From d2752d161bdb25890325a752cc71845ef655d27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Larsson=20H=C3=B6gfeldt?= Date: Wed, 9 Oct 2024 20:47:20 +0200 Subject: [PATCH 1/3] Improve JSDoc documentation --- README.md | 6 +- package-lock.json | 6 +- src/internal/floatOctets.ts | 2 +- src/namespace-std-numbers/index.ts | 14 +-- src/namespace-std/all-numbers/iota.ts | 9 +- src/namespace-std/all-numbers/signbit.ts | 11 +- src/namespace-std/doubles/copysign.ts | 17 ++- src/namespace-std/doubles/fabs.ts | 11 +- src/namespace-std/doubles/frexp.test.ts | 18 +-- src/namespace-std/doubles/frexp.ts | 42 ++++--- src/namespace-std/doubles/index.ts | 2 +- src/namespace-std/doubles/ldexp.ts | 15 ++- src/namespace-std/doubles/nan.ts | 44 ++++--- .../create_countl_zero.test.ts | 2 + .../create_countr_zero.test.ts | 2 + .../create_countr_zero.ts | 6 +- .../integers/{ => gcd}/gcd.test.ts | 0 src/namespace-std/integers/{ => gcd}/gcd.ts | 0 .../integers/{ => gcd}/gcd_bigint.ts | 0 .../integers/{ => gcd}/gcd_number.ts | 0 src/namespace-std/integers/gcd/index.ts | 3 + src/namespace-std/integers/index.ts | 4 +- src/namespace-std/integers/lcm/index.ts | 1 + .../integers/{ => lcm}/lcm.test.ts | 6 +- src/namespace-std/integers/{ => lcm}/lcm.ts | 0 .../integers/{ => lcm}/lcm_bigint.ts | 2 +- .../integers/{ => lcm}/lcm_number.ts | 2 +- tsconfig.json | 112 ++++++++++-------- 28 files changed, 204 insertions(+), 133 deletions(-) rename src/namespace-std/integers/{ => gcd}/gcd.test.ts (100%) rename src/namespace-std/integers/{ => gcd}/gcd.ts (100%) rename src/namespace-std/integers/{ => gcd}/gcd_bigint.ts (100%) rename src/namespace-std/integers/{ => gcd}/gcd_number.ts (100%) create mode 100644 src/namespace-std/integers/gcd/index.ts create mode 100644 src/namespace-std/integers/lcm/index.ts rename src/namespace-std/integers/{ => lcm}/lcm.test.ts (91%) rename src/namespace-std/integers/{ => lcm}/lcm.ts (100%) rename src/namespace-std/integers/{ => lcm}/lcm_bigint.ts (85%) rename src/namespace-std/integers/{ => lcm}/lcm_number.ts (87%) diff --git a/README.md b/README.md index c3da8c8..db3256c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # cmath-js -Implementation of parts of C's & C++'s numerics libraries in TypeScript/JavaScript. +Implementation of parts of C++'s numerics libraries in TypeScript/JavaScript, including functions C++ inherits from C. ## Floating-point functions - [`copysign`](https://en.cppreference.com/w/c/numeric/math/copysign) @@ -50,6 +50,6 @@ These functions accept either a `bigint` or an integer `number`: The test coverage is a perfect 100% and enforced by the publishing and pull request verification workflows. ## Contributing -Contributions are welcomed! Feel free to make a pull request. Please add your name to `contributors` in `package.json` and run `npm run build-and-verify` before submitting your PR. By making a pull request you agree to license your contribution under [the CC0 license](https://creativecommons.org/publicdomain/zero/1.0/legalcode.en#legal-code-title) unless otherwise specified. +Contributions are welcomed! Feel free to make a pull request. Please add your name to `contributors` in `package.json` and run `npm run build-and-verify` before submitting your PR. By opening a pull request you agree to license your contribution under [the CC0 license](https://creativecommons.org/publicdomain/zero/1.0/legalcode.en#legal-code-title) unless you specify otherwise. -ESLint is used to enforce code quality and consistent formatting (with the help of Prettier). If ESLint complains when you run `npm run build-and-verify`, you can run `npm run lint-fix` to apply automatic fixes and then fix the rest of the errors manually. I recommend configuring your IDE for ESLint and Prettier. If you are using Visual Studio Code, simply installing [Microsoft's ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [the official Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) takes care of that. +ESLint is used to enforce code quality and consistent formatting (with the help of Prettier). If ESLint complains when you run `npm run build-and-verify`, you can run `npm run lint-fix` to apply automatic fixes, and then fix the rest of the errors manually. It is recommend to configure your IDE for ESLint and Prettier. If you are using Visual Studio Code, simply installing [Microsoft's ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and [the official Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) takes care of that. diff --git a/package-lock.json b/package-lock.json index 483ac37..17f8dc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2618,9 +2618,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.33", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz", - "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==", + "version": "1.5.34", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.34.tgz", + "integrity": "sha512-/TZAiChbAflBNjCg+VvstbcwAtIL/VdMFO3NgRFIzBjpvPzWOTIbbO8kNb6RwU4bt9TP7K+3KqBKw/lOU+Y+GA==", "dev": true, "license": "ISC" }, diff --git a/src/internal/floatOctets.ts b/src/internal/floatOctets.ts index 72f5626..e93bdb3 100644 --- a/src/internal/floatOctets.ts +++ b/src/internal/floatOctets.ts @@ -1,6 +1,6 @@ // Constructing these can take a little bit of time so let's reuse them const floatArray = new Float64Array(1); -const octets = new Uint8Array(floatArray.buffer); +const octets: Uint8Array = new Uint8Array(floatArray.buffer); // DO NOT STORE THE Uint8Array RETURN VALUE FROM THIS CALL! // It will be mutated on the next call to this function diff --git a/src/namespace-std-numbers/index.ts b/src/namespace-std-numbers/index.ts index 44802f4..deda922 100644 --- a/src/namespace-std-numbers/index.ts +++ b/src/namespace-std-numbers/index.ts @@ -1,16 +1,16 @@ // Mathematical constants defined in the namespace std::numbers // cppreference: https://en.cppreference.com/w/cpp/numeric/constants -export const e = Math.E; +export const e: number = Math.E; export const egamma = 0.5772156649015329; // The Euler-Mascheroni constant export const inv_pi = 0.3183098861837907; // 1 / pi export const inv_sqrt3 = 0.5773502691896257; // 1 / sqrt(3) export const inv_sqrtpi = 0.5641895835477563; // 1 / sqrt(pi) -export const ln10 = Math.LN10; -export const ln2 = Math.LN2; -export const log10 = Math.LOG10E; -export const log2e = Math.LOG2E; +export const ln10: number = Math.LN10; +export const ln2: number = Math.LN2; +export const log10: number = Math.LOG10E; +export const log2e: number = Math.LOG2E; export const phi = 1.618033988749895; // The golden ratio -export const pi = Math.PI; -export const sqrt2 = Math.SQRT2; +export const pi: number = Math.PI; +export const sqrt2: number = Math.SQRT2; export const sqrt3 = 1.7320508075688772; // sqrt(3) diff --git a/src/namespace-std/all-numbers/iota.ts b/src/namespace-std/all-numbers/iota.ts index 21aeefc..9976cf8 100644 --- a/src/namespace-std/all-numbers/iota.ts +++ b/src/namespace-std/all-numbers/iota.ts @@ -1,6 +1,9 @@ -// Fills an array with sequentially increasing values -// Cppreference: https://en.cppreference.com/w/c/numeric/math/iota - +/** + * Fills an array with sequentially increasing values + * + * Read more about the original function on + * - {@link https://en.cppreference.com/w/c/numeric/math/iota|Cppreference} + */ export function iota(mut_arrayToFill: bigint[], startValue: bigint): void; export function iota(mut_arrayToFill: number[], startValue: number): void; export function iota(mut_arrayToFill: (bigint | number)[], startValue: bigint | number): void { diff --git a/src/namespace-std/all-numbers/signbit.ts b/src/namespace-std/all-numbers/signbit.ts index 118c529..6282b5e 100644 --- a/src/namespace-std/all-numbers/signbit.ts +++ b/src/namespace-std/all-numbers/signbit.ts @@ -1,6 +1,13 @@ import { floatOctets } from "../../internal/index.js"; - -// Cppreference: https://en.cppreference.com/w/c/numeric/math/signbit +/** + * Determines if a number is negative or NaN with the sign bit set. + * Note that the ECMAScript standard does not guarantee that +NaN and + * -NaN are different so this function may give unexpected results + * (such as always false or always true) in some JavaScript engines. + * + * Read more about the original function on + * - {@link https://en.cppreference.com/w/cpp/numeric/math/signbit|Cppreference} + */ export function signbit(num: bigint | number): boolean { return floatOctets(Number(num))[7] > 127; } diff --git a/src/namespace-std/doubles/copysign.ts b/src/namespace-std/doubles/copysign.ts index dec5c52..c1552d7 100644 --- a/src/namespace-std/doubles/copysign.ts +++ b/src/namespace-std/doubles/copysign.ts @@ -1,10 +1,15 @@ import { signbit } from "../index.js"; -// copysign produces a value with the magnitude of 'num' and the sign 'sign' -// C spec: https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf#subsection.7.12.11 -// Cppreference: https://en.cppreference.com/w/c/numeric/math/copysign -// This implementation handles positive and negative zero and positive and negative -// NaNs (in JS engines where that difference is observable when writing NaNs to a Float64Array) -export function copysign(/*double*/ num: number, /*double*/ sign: number): /*double*/ number { +/** + * Produces a value with the magnitude of 'num' and the sign 'sign'. + * This implementation handles positive and negative zero and positive and negative + * NaNs (in JS engines where that difference is observable when writing NaNs to a Float64Array). + * + * Read more about the original function on + * - {@link https://en.cppreference.com/w/cpp/numeric/math/copysign|Cppreference} + * - {@link https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf#subsection.7.12.11|The C23 final draft specification} + */ + +export function copysign(num: number, sign: number): number { return signbit(num) === signbit(sign) ? num : -num; } diff --git a/src/namespace-std/doubles/fabs.ts b/src/namespace-std/doubles/fabs.ts index 002a3ca..4bfa171 100644 --- a/src/namespace-std/doubles/fabs.ts +++ b/src/namespace-std/doubles/fabs.ts @@ -1,3 +1,8 @@ -// fabs is just like JavaScript's Math.abs -// Cppreference: https://en.cppreference.com/w/c/numeric/math/fabs -export const fabs = Math.abs; +/** + * fabs is just like JavaScript's Math.abs. + * + * Read more about the original function on + * - {@link https://en.cppreference.com/w/cpp/numeric/math/fabs|Cppreference} + */ + +export const fabs: (number: number) => number = Math.abs; diff --git a/src/namespace-std/doubles/frexp.test.ts b/src/namespace-std/doubles/frexp.test.ts index fcb15c4..8d44928 100644 --- a/src/namespace-std/doubles/frexp.test.ts +++ b/src/namespace-std/doubles/frexp.test.ts @@ -2,14 +2,14 @@ import { frexp } from "./index.js"; describe(frexp.name, () => { it("decomposes a number into a normalized fraction and an integral power of two", () => { - expect(frexp(1)).toStrictEqual([0.5, 1]); - expect(frexp(1.5)).toStrictEqual([0.75, 1]); - expect(frexp(3 * 2 ** 500)).toStrictEqual([0.75, 502]); - expect(frexp(-4)).toStrictEqual([-0.5, 3]); - expect(frexp(Number.MAX_VALUE)).toStrictEqual([0.9999999999999999, 1024]); - expect(frexp(Number.MIN_VALUE)).toStrictEqual([0.5, -1073]); - expect(frexp(-Infinity)).toStrictEqual([-Infinity, 0]); - expect(frexp(-0)).toStrictEqual([-0, 0]); - expect(frexp(NaN)).toStrictEqual([NaN, 0]); + expect(frexp(1)).toStrictEqual({ exponent: 1, fraction: 0.5 }); + expect(frexp(1.5)).toStrictEqual({ exponent: 1, fraction: 0.75 }); + expect(frexp(3 * 2 ** 500)).toStrictEqual({ exponent: 502, fraction: 0.75 }); + expect(frexp(-4)).toStrictEqual({ exponent: 3, fraction: -0.5 }); + expect(frexp(Number.MAX_VALUE)).toStrictEqual({ exponent: 1024, fraction: 0.9999999999999999 }); + expect(frexp(Number.MIN_VALUE)).toStrictEqual({ exponent: -1073, fraction: 0.5 }); + expect(frexp(-Infinity)).toStrictEqual({ exponent: 0, fraction: -Infinity }); + expect(frexp(-0)).toStrictEqual({ exponent: 0, fraction: -0 }); + expect(frexp(NaN)).toStrictEqual({ exponent: 0, fraction: NaN }); }); }); diff --git a/src/namespace-std/doubles/frexp.ts b/src/namespace-std/doubles/frexp.ts index e357b60..7a6533a 100644 --- a/src/namespace-std/doubles/frexp.ts +++ b/src/namespace-std/doubles/frexp.ts @@ -1,39 +1,45 @@ -// Note: Instead of "double frexp(double arg, int* exp)" this is built as "[double, int] frexp(double arg)" due to ECMAScripts's lack of pointers -// A hypothetical issue with this implementation is that the precision the ** operator is not defined in the ECMAScript standard, -// however, sane ECMAScript implementations should give precise results for 2** expressions -// Cppreference: https://en.cppreference.com/w/c/numeric/math/frexp for a more detailed description +export interface frexp_result { + exponent: number; + fraction: number; +} + +// Note: Instead of "double frexp(double arg, int* exp)" this is built as "{ exponent, fraction } frexp(double arg)" due to ECMAScripts's lack of pointers +// A hypothetical issue with this implementation is that the precision of the ** operator is not defined in the ECMAScript standard, +// however, it is hard to imagine a sane ECMAScript implementation would give imprecise results for 2** expressions. +// Cppreference: https://en.cppreference.com/w/cpp/numeric/math/frexp // Object.is(n, frexp(n)[0] * 2 ** frexp(n)[1]) for all number values of n except when Math.isFinite(n) && Math.abs(n) > 2**1023 // Object.is(n, (2 * frexp(n)[0]) * 2 ** (frexp(n)[1] - 1)) for all number values of n // Object.is(n, frexp(n)[0]) for these values of n: 0, -0, NaN, Infinity, -Infinity // Math.abs(frexp(n)[0]) is >= 0.5 and < 1.0 for any other number-type value of n -export function frexp( - /*double*/ num: number, -): [/*double*/ fraction: number, /*int*/ exponent: number] { +export function frexp(num: number): frexp_result { if (num === 0 || !Number.isFinite(num)) { - return [num, 0]; + return { exponent: 0, fraction: num }; } const absNum: number = Math.abs(num); - let exp: number = Math.max(-1023, Math.floor(Math.log2(absNum)) + 1); - let x: number = absNum * 2 ** -exp; + let exponent: number = Math.max(-1023, Math.floor(Math.log2(absNum)) + 1); + let fraction: number = absNum * 2 ** -exponent; // These while loops compensate for rounding errors that may occur because of ECMAScript's Math.log2's undefined precision // and the first one also helps work around the issue of 2 ** -exp === Infinity when exp <= -1024 - while (x < 0.5) { - x *= 2; - --exp; + while (fraction < 0.5) { + fraction *= 2; + --exponent; } // istanbul ignore next - This might never run and that's okay. See the above comment - while (x >= 1) { - x *= 0.5; - ++exp; + while (fraction >= 1) { + fraction *= 0.5; + ++exponent; } if (num < 0) { - x = -x; + fraction = -fraction; } - return [x, exp]; + return { + exponent, + fraction, + }; } diff --git a/src/namespace-std/doubles/index.ts b/src/namespace-std/doubles/index.ts index 719479b..c502010 100644 --- a/src/namespace-std/doubles/index.ts +++ b/src/namespace-std/doubles/index.ts @@ -1,6 +1,6 @@ export { copysign } from "./copysign.js"; export { fabs } from "./fabs.js"; -export { frexp } from "./frexp.js"; +export { frexp, type frexp_result } from "./frexp.js"; export { hypot } from "./hypot.js"; export { ldexp } from "./ldexp.js"; export { nan } from "./nan.js"; diff --git a/src/namespace-std/doubles/ldexp.ts b/src/namespace-std/doubles/ldexp.ts index e6acdb3..2b5e474 100644 --- a/src/namespace-std/doubles/ldexp.ts +++ b/src/namespace-std/doubles/ldexp.ts @@ -1,9 +1,20 @@ -// ldexp multiplies a floating-point number by an integral power of 2 // ldexp returns factor * 2**exponent // C spec: https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf#subsection.7.12.6 // Cppreference: https://en.cppreference.com/w/c/numeric/math/ldexp -// Implementation is complicated by the need to avoid underflow/overflow given a large exponent (-1075< >1023) + +/** + * Multiplies a floating-point number by an integral power of 2. + * + * + * Read more about the original function on + * - {@link https://en.cppreference.com/w/c/numeric/math/ldexp|Cppreference} + * - {@link https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf#subsection.7.12.6|The C23 final draft specification} + * + * @returns `factor * 2**exponent` but avoiding overflow and underflow + */ export function ldexp(/*double*/ factor: number, /*int*/ exponent: number): /*double*/ number { + // Implementation is complicated by the need to avoid underflow/overflow + // given a large exponent (less than -1075 or greater than 1023). const halfPowerRoundedTowardZero: number = 2 ** Math.trunc(exponent * 0.5); return ( factor * halfPowerRoundedTowardZero * halfPowerRoundedTowardZero * 2 ** Math.sign(exponent % 2) diff --git a/src/namespace-std/doubles/nan.ts b/src/namespace-std/doubles/nan.ts index db2e5da..17b4e76 100644 --- a/src/namespace-std/doubles/nan.ts +++ b/src/namespace-std/doubles/nan.ts @@ -1,24 +1,36 @@ import { floatFromBits } from "../../internal/index.js"; -// Converts the string arg into the corresponding quiet NaN value. -// https://en.cppreference.com/w/cpp/numeric/math/nan -// This is always true: Object.is(nan(), NaN). -// Important note: JavaScript engines do not have to keep the NaN value -// and at the time of writing (2024) at least one engine (Firefox's) only -// supports two different NaNs (+NaN and -NaN), so in Firefox this function -// will always return the exact same NaN. Chrome's JS engine supports all -// possible NaN values, though, which can be observed like this: -// ( -// new Uint8Array(new Float64Array([nan("0")]).buffer)[0] === 0 && -// new Uint8Array(new Float64Array([nan("92")]).buffer)[0] === 92 -// ) - +/** + * Converts the string argument into the corresponding quiet NaN value. + * + * The accepted format is not defined in the C23 standard, except that + * if a character other than [0-9a-zA-Z_] appears, the argument is ignored. + * This implementation accepts simple decimal and hexadecimal integer strings + * like `"1234"` or `"0xF00D"`. + * + * The following is always `true`: + * ```ts + * Object.is(nan(), NaN) + * ``` + * + * Important note: JavaScript engines do not have to keep the NaN value + * and at the time of writing (2024) at least one engine (Firefox's) only + * supports two different NaNs (+NaN and -NaN), so in Firefox this function + * will always return the exact same NaN. Chrome's JS engine supports all + * possible NaN values, though, which can be observed like this: + * ```ts + * ( + * new Uint8Array(new Float64Array([nan("0")]).buffer)[0] === 0 && + * new Uint8Array(new Float64Array([nan("92")]).buffer)[0] === 92 + * ) + * ``` + * + * Read more about the original function on + * - {@link https://en.cppreference.com/w/cpp/numeric/math/nan|Cppreference} + */ export function nan(arg: string): number { let bits = 0n; - // The accepted format is not defined in the C23 standard, except that - // if a character other than [0-9a-zA-Z_] appears, the argument is ignored. - // So let's just accept simple decimal and hexadecimal integers. const isIntegerString = /^(0|[1-9]\d*|0x[0-9a-zA-Z]+)$/.test(arg); if (isIntegerString) { diff --git a/src/namespace-std/integers/count-left-or-right-bits/create_countl_zero.test.ts b/src/namespace-std/integers/count-left-or-right-bits/create_countl_zero.test.ts index 798743e..26293af 100644 --- a/src/namespace-std/integers/count-left-or-right-bits/create_countl_zero.test.ts +++ b/src/namespace-std/integers/count-left-or-right-bits/create_countl_zero.test.ts @@ -49,6 +49,8 @@ describe(create_countl_zero.name, () => { expect(countl_zero_u32(0b0)).toStrictEqual(32); expect(countl_zero_u32(0xff_ff_ff_ff)).toStrictEqual(0); expect(countl_zero_u32(0xff_ff_ff_fe)).toStrictEqual(0); + expect(countl_zero_u32(-1)).toStrictEqual(0); + expect(countl_zero_u32(-2n)).toStrictEqual(0); expect(countl_zero_u32(0x7f_ff_ff_ff)).toStrictEqual(1); expect(countl_zero_u32(0x00_00_00_01)).toStrictEqual(31); diff --git a/src/namespace-std/integers/count-left-or-right-bits/create_countr_zero.test.ts b/src/namespace-std/integers/count-left-or-right-bits/create_countr_zero.test.ts index 6b03722..478d797 100644 --- a/src/namespace-std/integers/count-left-or-right-bits/create_countr_zero.test.ts +++ b/src/namespace-std/integers/count-left-or-right-bits/create_countr_zero.test.ts @@ -60,6 +60,8 @@ describe(create_countr_zero.name, () => { expect(countr_zero_u32(0xff_ff_ff_fe)).toStrictEqual(1); expect(countr_zero_u32(0x7f_ff_ff_ff)).toStrictEqual(0); expect(countr_zero_u32(0x80_00_00_00)).toStrictEqual(31); + expect(countr_zero_u32(-1)).toStrictEqual(0); + expect(countr_zero_u32(-2n)).toStrictEqual(1); expect(countr_zero_u64(0x0)).toStrictEqual(64); expect(countr_zero_u64(0x0n)).toStrictEqual(64); diff --git a/src/namespace-std/integers/count-left-or-right-bits/create_countr_zero.ts b/src/namespace-std/integers/count-left-or-right-bits/create_countr_zero.ts index 1aa9e07..e75251e 100644 --- a/src/namespace-std/integers/count-left-or-right-bits/create_countr_zero.ts +++ b/src/namespace-std/integers/count-left-or-right-bits/create_countr_zero.ts @@ -30,11 +30,15 @@ export function create_countr_zero({ bits }: { bits: number }): countr_zero_func const truncated = (typeof integer === "number" ? integer : Number(BigInt.asIntN(32, integer))) & mask; + if (truncated === 0) { + return bits; + } + // Turn the trailing 0s - plus the bit just to the left of them - to 1s, and the rest of the bits to 0s. const zeroesAsOnes = truncated ^ (truncated - 1); const trailLength = 31 - Math.clz32(zeroesAsOnes); - return truncated === 0 ? bits : trailLength; + return trailLength; }; } diff --git a/src/namespace-std/integers/gcd.test.ts b/src/namespace-std/integers/gcd/gcd.test.ts similarity index 100% rename from src/namespace-std/integers/gcd.test.ts rename to src/namespace-std/integers/gcd/gcd.test.ts diff --git a/src/namespace-std/integers/gcd.ts b/src/namespace-std/integers/gcd/gcd.ts similarity index 100% rename from src/namespace-std/integers/gcd.ts rename to src/namespace-std/integers/gcd/gcd.ts diff --git a/src/namespace-std/integers/gcd_bigint.ts b/src/namespace-std/integers/gcd/gcd_bigint.ts similarity index 100% rename from src/namespace-std/integers/gcd_bigint.ts rename to src/namespace-std/integers/gcd/gcd_bigint.ts diff --git a/src/namespace-std/integers/gcd_number.ts b/src/namespace-std/integers/gcd/gcd_number.ts similarity index 100% rename from src/namespace-std/integers/gcd_number.ts rename to src/namespace-std/integers/gcd/gcd_number.ts diff --git a/src/namespace-std/integers/gcd/index.ts b/src/namespace-std/integers/gcd/index.ts new file mode 100644 index 0000000..bd38ff3 --- /dev/null +++ b/src/namespace-std/integers/gcd/index.ts @@ -0,0 +1,3 @@ +export { gcd } from "./gcd.js"; +export { gcd_bigint } from "./gcd_bigint.js"; +export { gcd_number } from "./gcd_number.js"; diff --git a/src/namespace-std/integers/index.ts b/src/namespace-std/integers/index.ts index 602feaf..6cc8d28 100644 --- a/src/namespace-std/integers/index.ts +++ b/src/namespace-std/integers/index.ts @@ -1,6 +1,6 @@ export { abs } from "./abs.js"; export * from "./count-left-or-right-bits/index.js"; export { div, type div_t, type div_t_bigint, type div_t_number } from "./div/index.js"; -export { gcd } from "./gcd.js"; -export { lcm } from "./lcm.js"; +export { gcd } from "./gcd/index.js"; +export { lcm } from "./lcm/index.js"; export { popcount } from "./popcount.js"; diff --git a/src/namespace-std/integers/lcm/index.ts b/src/namespace-std/integers/lcm/index.ts new file mode 100644 index 0000000..ab93e9a --- /dev/null +++ b/src/namespace-std/integers/lcm/index.ts @@ -0,0 +1 @@ +export { lcm } from "./lcm.js"; diff --git a/src/namespace-std/integers/lcm.test.ts b/src/namespace-std/integers/lcm/lcm.test.ts similarity index 91% rename from src/namespace-std/integers/lcm.test.ts rename to src/namespace-std/integers/lcm/lcm.test.ts index 61dc454..a2df0e4 100644 --- a/src/namespace-std/integers/lcm.test.ts +++ b/src/namespace-std/integers/lcm/lcm.test.ts @@ -1,4 +1,4 @@ -import { lcm } from "./index.js"; +import { lcm } from "../index.js"; describe(lcm.name, () => { it("finds the least common multiple", () => { @@ -40,7 +40,7 @@ describe(lcm.name, () => { } }); - it(`returns 0 if at least one of the arguments are 0`, () => { + it("returns 0 if at least one of the arguments are 0", () => { expect(lcm(0, 0)).toStrictEqual(0); expect(lcm(-0, -0)).toStrictEqual(0); expect(lcm(-4, 0)).toStrictEqual(0); @@ -50,7 +50,7 @@ describe(lcm.name, () => { expect(lcm(0n, 4n)).toStrictEqual(0n); }); - it(`returns 0 if one or both parameters are non-integers`, () => { + it("returns 0 if one or both parameters are non-integers", () => { expect(lcm(9, 4.5)).toStrictEqual(0); expect(lcm(4.5, Infinity)).toStrictEqual(0); expect(lcm(-Infinity, 4)).toStrictEqual(0); diff --git a/src/namespace-std/integers/lcm.ts b/src/namespace-std/integers/lcm/lcm.ts similarity index 100% rename from src/namespace-std/integers/lcm.ts rename to src/namespace-std/integers/lcm/lcm.ts diff --git a/src/namespace-std/integers/lcm_bigint.ts b/src/namespace-std/integers/lcm/lcm_bigint.ts similarity index 85% rename from src/namespace-std/integers/lcm_bigint.ts rename to src/namespace-std/integers/lcm/lcm_bigint.ts index 4854e94..cf20699 100644 --- a/src/namespace-std/integers/lcm_bigint.ts +++ b/src/namespace-std/integers/lcm/lcm_bigint.ts @@ -1,4 +1,4 @@ -import { gcd_bigint } from "./gcd_bigint.js"; +import { gcd_bigint } from "../gcd/index.js"; export function lcm_bigint(aInteger: bigint, bInteger: bigint): bigint { const a = aInteger < 0n ? -aInteger : aInteger; diff --git a/src/namespace-std/integers/lcm_number.ts b/src/namespace-std/integers/lcm/lcm_number.ts similarity index 87% rename from src/namespace-std/integers/lcm_number.ts rename to src/namespace-std/integers/lcm/lcm_number.ts index cef0a57..19ba233 100644 --- a/src/namespace-std/integers/lcm_number.ts +++ b/src/namespace-std/integers/lcm/lcm_number.ts @@ -1,4 +1,4 @@ -import { gcd_number } from "./gcd_number.js"; +import { gcd_number } from "../gcd/index.js"; export function lcm_number(aInteger: number, bInteger: number): number { if (!Number.isSafeInteger(aInteger) || !Number.isSafeInteger(bInteger)) { diff --git a/tsconfig.json b/tsconfig.json index 796191a..c239c48 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,102 +1,112 @@ { "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ + /* Visit https://aka.ms/tsconfig to read more about this file */ /* Projects */ - // "incremental": false, // Enable incremental compilation - // "composite": true, // Enable constraints that allow a TypeScript project to be used with project references. - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "ESNEXT", // Set the JavaScript language version for emitted JavaScript and include compatible library declarations. + "target": "ESNEXT", // /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "NodeNext", // Specify what module code is generated. - "rootDir": ".", // Specify the root folder within your source files. - "moduleResolution": "NodeNext", // Specify how TypeScript looks up a file from a given module specifier. - // "baseUrl": "./src", // Specify the base directory to resolve non-relative module names. + "module": "NodeNext", // /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "NodeNext", // /* Specify how TypeScript looks up a file from a given module specifier. */, + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ /* Emit */ - "declaration": true, // Generate .d.ts files from TypeScript and JavaScript files in your project. - // "declarationMap": true, // Create sourcemaps for d.ts files. + "declaration": true, // /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true, // Create source map files for emitted JavaScript files. - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./dist", // Specify an output folder for all emitted files. - "removeComments": true, // Disable emitting comments. - "noEmit": true, /* Disable emitting files from a compilation. */ + "sourceMap": true, // /* Create source map files for emitted JavaScript files. */, + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + "noEmit": true, // /* Disable emitting files from a compilation. */, + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + // "outDir": "./", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - "newLine": "lf", // Set the newline character for emitting files. - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ + "newLine": "lf", // /* Set the newline character for emitting files. */, + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ /* Interop Constraints */ - "isolatedModules": true, // Ensure that each file can be safely transpiled without relying on other imports. + "isolatedModules": true, // /* Ensure that each file can be safely transpiled without relying on other imports. */, + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + "isolatedDeclarations": true, // /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */, // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, // Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. + "esModuleInterop": true, // /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, // Ensure that casing is correct in imports. + "forceConsistentCasingInFileNames": true, // /* Ensure that casing is correct in imports. */, /* Type Checking */ - "strict": true, // Enable all strict type-checking options. - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ + "strict": true, // /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - "alwaysStrict": true, // Ensure 'use strict' is always emitted. - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - "exactOptionalPropertyTypes": true, // Interpret optional property types as written, rather than adding 'undefined'. + // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": true, // /* Ensure 'use strict' is always emitted. */, + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + "exactOptionalPropertyTypes": true, // /* Interpret optional property types as written, rather than adding 'undefined'. */, // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - "noImplicitOverride": true, // Ensure overriding members in derived classes are marked with an override modifier. - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + "noImplicitOverride": true, // /* Ensure overriding members in derived classes are marked with an override modifier. */, + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true // Skip type checking all .d.ts files. + "skipLibCheck": true, // /* Skip type checking all .d.ts files. */ }, "exclude": ["dist"], - "include": ["*.js", "src"], + "include": ["*.js", "src"] } From 2043dc555141e472b3f9340f3010c87d46928e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Larsson=20H=C3=B6gfeldt?= Date: Wed, 9 Oct 2024 20:49:57 +0200 Subject: [PATCH 2/3] Enable declaration maps --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index c239c48..606a4e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -51,7 +51,7 @@ /* Emit */ "declaration": true, // /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + "declarationMap": true, // /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ "sourceMap": true, // /* Create source map files for emitted JavaScript files. */, // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ From d716f6242bc5a8281e80ae79e8e45645d8a011d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Larsson=20H=C3=B6gfeldt?= Date: Wed, 9 Oct 2024 20:51:59 +0200 Subject: [PATCH 3/3] 100% test coverage --- src/namespace-std/integers/gcd/gcd.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/namespace-std/integers/gcd/gcd.test.ts b/src/namespace-std/integers/gcd/gcd.test.ts index f588703..515cf77 100644 --- a/src/namespace-std/integers/gcd/gcd.test.ts +++ b/src/namespace-std/integers/gcd/gcd.test.ts @@ -1,4 +1,4 @@ -import { gcd } from "./index.js"; +import { gcd } from "../index.js"; describe(gcd.name, () => { it("finds the greatest common divisor", () => {