From 42711d4f6c51cf57248e2ff2f3d26d322939f30c Mon Sep 17 00:00:00 2001 From: Sebastian Lorenz Date: Sat, 1 Aug 2026 19:27:05 +0000 Subject: [PATCH 1/2] Add reproduction for BigInt issue --- packages/effect/test/BigIntGcdLcm.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 packages/effect/test/BigIntGcdLcm.test.ts diff --git a/packages/effect/test/BigIntGcdLcm.test.ts b/packages/effect/test/BigIntGcdLcm.test.ts new file mode 100644 index 00000000000..7bd37e7782e --- /dev/null +++ b/packages/effect/test/BigIntGcdLcm.test.ts @@ -0,0 +1,17 @@ +import * as BigInt from "effect/BigInt" +import { describe, it } from "vitest" +import { strictEqual } from "./utils/assert.ts" + +describe("BigInt gcd and lcm", () => { + it("returns a non-negative greatest common divisor", () => { + strictEqual(BigInt.gcd(-6n, 4n), 2n) + }) + + it("returns a non-negative least common multiple", () => { + strictEqual(BigInt.lcm(6n, -4n), 12n) + }) + + it("returns zero for two zero operands", () => { + strictEqual(BigInt.lcm(0n, 0n), 0n) + }) +}) From 392b148587291c24cbdd8c1522a3dcac5829c3fd Mon Sep 17 00:00:00 2001 From: Giulio Canti Date: Sun, 2 Aug 2026 10:07:58 +0200 Subject: [PATCH 2/2] Fix BigInt gcd and lcm edge cases --- .changeset/fix-bigint-gcd-lcm.md | 5 +++ packages/effect/src/BigInt.ts | 8 +++-- packages/effect/test/BigInt.test.ts | 38 +++++++++++++++-------- packages/effect/test/BigIntGcdLcm.test.ts | 17 ---------- 4 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 .changeset/fix-bigint-gcd-lcm.md delete mode 100644 packages/effect/test/BigIntGcdLcm.test.ts diff --git a/.changeset/fix-bigint-gcd-lcm.md b/.changeset/fix-bigint-gcd-lcm.md new file mode 100644 index 00000000000..bfffa048479 --- /dev/null +++ b/.changeset/fix-bigint-gcd-lcm.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Ensure `BigInt.gcd` and `BigInt.lcm` return non-negative values and handle zero operands in `BigInt.lcm`. diff --git a/packages/effect/src/BigInt.ts b/packages/effect/src/BigInt.ts index fa67d020e94..18ac6ba68b3 100644 --- a/packages/effect/src/BigInt.ts +++ b/packages/effect/src/BigInt.ts @@ -609,7 +609,7 @@ export const gcd: { that = self % that self = t } - return self + return abs(self) }) /** @@ -637,7 +637,11 @@ export const gcd: { export const lcm: { (that: bigint): (self: bigint) => bigint (self: bigint, that: bigint): bigint -} = dual(2, (self: bigint, that: bigint): bigint => (self * that) / gcd(self, that)) +} = dual( + 2, + (self: bigint, that: bigint): bigint => + self === bigint0 || that === bigint0 ? bigint0 : abs((self * that) / gcd(self, that)) +) /** * Returns the integer square root of a non-negative `bigint`. diff --git a/packages/effect/test/BigInt.test.ts b/packages/effect/test/BigInt.test.ts index 28f7e6b2868..64e83168a99 100644 --- a/packages/effect/test/BigInt.test.ts +++ b/packages/effect/test/BigInt.test.ts @@ -1,17 +1,17 @@ +import { assert, describe, it } from "@effect/vitest" import * as BigInt from "effect/BigInt" -import { describe, it } from "vitest" -import { assertNone, assertSome, strictEqual } from "./utils/assert.ts" +import { assertNone, assertSome } from "./utils/assert.ts" describe("BigInt", () => { it("re-exports the global BigInt constructor", () => { - strictEqual(BigInt.Equivalence(1n, 1n), true) - strictEqual(BigInt.Equivalence(1n, 2n), false) + assert.strictEqual(BigInt.Equivalence(1n, 1n), true) + assert.strictEqual(BigInt.Equivalence(1n, 2n), false) }) it("divide returns some for non-zero divisors in data-first and data-last forms", () => { assertSome(BigInt.divide(6n, 3n), 2n) assertNone(BigInt.divide(6n, 0n)) - strictEqual(BigInt.divideUnsafe(6n, 3n), 2n) + assert.strictEqual(BigInt.divideUnsafe(6n, 3n), 2n) }) it("sqrt returns integer square roots", () => { @@ -36,22 +36,34 @@ describe("BigInt", () => { }) it("fromNumber returns none for unsafe or non-integral numbers", () => { - strictEqual(BigInt.ReducerSum.combine(1n, 2n), 3n) - strictEqual(BigInt.ReducerSum.combine(BigInt.ReducerSum.initialValue, 2n), 2n) - strictEqual(BigInt.ReducerSum.combine(2n, BigInt.ReducerSum.initialValue), 2n) + assert.strictEqual(BigInt.ReducerSum.combine(1n, 2n), 3n) + assert.strictEqual(BigInt.ReducerSum.combine(BigInt.ReducerSum.initialValue, 2n), 2n) + assert.strictEqual(BigInt.ReducerSum.combine(2n, BigInt.ReducerSum.initialValue), 2n) }) it("ReducerMultiply combines values with one as the identity", () => { - strictEqual(BigInt.ReducerMultiply.combine(2n, 3n), 6n) - strictEqual(BigInt.ReducerMultiply.combine(BigInt.ReducerMultiply.initialValue, 2n), 2n) - strictEqual(BigInt.ReducerMultiply.combine(2n, BigInt.ReducerMultiply.initialValue), 2n) + assert.strictEqual(BigInt.ReducerMultiply.combine(2n, 3n), 6n) + assert.strictEqual(BigInt.ReducerMultiply.combine(BigInt.ReducerMultiply.initialValue, 2n), 2n) + assert.strictEqual(BigInt.ReducerMultiply.combine(2n, BigInt.ReducerMultiply.initialValue), 2n) }) it("CombinerMax returns the larger bigint", () => { - strictEqual(BigInt.CombinerMax.combine(1n, 2n), 2n) + assert.strictEqual(BigInt.CombinerMax.combine(1n, 2n), 2n) }) it("CombinerMin returns the smaller bigint", () => { - strictEqual(BigInt.CombinerMin.combine(1n, 2n), 1n) + assert.strictEqual(BigInt.CombinerMin.combine(1n, 2n), 1n) + }) + + it("returns a non-negative greatest common divisor", () => { + assert.strictEqual(BigInt.gcd(-6n, 4n), 2n) + }) + + it("returns a non-negative least common multiple", () => { + assert.strictEqual(BigInt.lcm(6n, -4n), 12n) + }) + + it("returns zero for two zero operands", () => { + assert.strictEqual(BigInt.lcm(0n, 0n), 0n) }) }) diff --git a/packages/effect/test/BigIntGcdLcm.test.ts b/packages/effect/test/BigIntGcdLcm.test.ts deleted file mode 100644 index 7bd37e7782e..00000000000 --- a/packages/effect/test/BigIntGcdLcm.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as BigInt from "effect/BigInt" -import { describe, it } from "vitest" -import { strictEqual } from "./utils/assert.ts" - -describe("BigInt gcd and lcm", () => { - it("returns a non-negative greatest common divisor", () => { - strictEqual(BigInt.gcd(-6n, 4n), 2n) - }) - - it("returns a non-negative least common multiple", () => { - strictEqual(BigInt.lcm(6n, -4n), 12n) - }) - - it("returns zero for two zero operands", () => { - strictEqual(BigInt.lcm(0n, 0n), 0n) - }) -})