-
Notifications
You must be signed in to change notification settings - Fork 310
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
139 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { U128 } from './u128.js'; | ||
|
||
describe('U128', () => { | ||
describe('constructor', () => { | ||
it('accepts valid number inputs', () => { | ||
const small = new U128(42); | ||
expect(small.toInteger()).toBe(42n); | ||
|
||
const large = new U128(Number.MAX_SAFE_INTEGER); | ||
expect(large.toInteger()).toBe(BigInt(Number.MAX_SAFE_INTEGER)); | ||
}); | ||
|
||
it('accepts valid bigint inputs', () => { | ||
const small = new U128(42n); | ||
expect(small.toInteger()).toBe(42n); | ||
|
||
const max = new U128(2n ** 128n - 1n); | ||
expect(max.toInteger()).toBe(2n ** 128n - 1n); | ||
}); | ||
|
||
it('throws for negative values', () => { | ||
expect(() => new U128(-1)).toThrow('Value -1 is not within 128 bits'); | ||
expect(() => new U128(-1n)).toThrow('Value -1 is not within 128 bits'); | ||
}); | ||
|
||
it('throws for values >= 2^128', () => { | ||
const tooLarge = 2n ** 128n; | ||
expect(() => new U128(tooLarge)).toThrow(`Value ${tooLarge} is not within 128 bits`); | ||
}); | ||
}); | ||
|
||
describe('fromU64sLE', () => { | ||
it('correctly combines valid limbs', () => { | ||
const lo = 0xdeadbeefn; | ||
const hi = 0xcafebaben; | ||
const combined = U128.fromU64sLE(lo, hi); | ||
|
||
expect(combined.lo).toBe(lo); | ||
expect(combined.hi).toBe(hi); | ||
expect(combined.toInteger()).toBe((hi << 64n) | lo); | ||
}); | ||
|
||
it('accepts maximum valid limb values', () => { | ||
const maxLimb = 2n ** 64n - 1n; | ||
const value = U128.fromU64sLE(maxLimb, maxLimb); | ||
|
||
expect(value.lo).toBe(maxLimb); | ||
expect(value.hi).toBe(maxLimb); | ||
expect(value.toInteger()).toBe(2n ** 128n - 1n); | ||
}); | ||
|
||
it('throws for invalid lower limb', () => { | ||
const invalid = 2n ** 64n; | ||
expect(() => U128.fromU64sLE(invalid, 0n)).toThrow(`Lower limb ${invalid} is not within valid range`); | ||
|
||
expect(() => U128.fromU64sLE(-1n, 0n)).toThrow('Lower limb -1 is not within valid range'); | ||
}); | ||
|
||
it('throws for invalid higher limb', () => { | ||
const invalid = 2n ** 64n; | ||
expect(() => U128.fromU64sLE(0n, invalid)).toThrow(`Higher limb ${invalid} is not within valid range`); | ||
|
||
expect(() => U128.fromU64sLE(0n, -1n)).toThrow('Higher limb -1 is not within valid range'); | ||
}); | ||
}); | ||
|
||
describe('getters', () => { | ||
it('correctly extracts lo and hi components', () => { | ||
const testCases = [ | ||
{ lo: 0xdeadbeefn, hi: 0xcafebaben }, | ||
{ lo: 0n, hi: 1n }, | ||
{ lo: 1n, hi: 0n }, | ||
{ lo: 2n ** 64n - 1n, hi: 2n ** 64n - 1n }, | ||
]; | ||
|
||
for (const { lo, hi } of testCases) { | ||
const value = U128.fromU64sLE(lo, hi); | ||
expect(value.lo).toBe(lo); | ||
expect(value.hi).toBe(hi); | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// A typescript version of noir::std::U128 | ||
export class U128 { | ||
private readonly value: bigint; | ||
|
||
constructor(value: bigint | number) { | ||
if (typeof value === 'number') { | ||
value = BigInt(value); | ||
} | ||
|
||
// Check value is within 128 bits | ||
if (value < 0n || value >= 2n ** 128n) { | ||
throw new Error(`Value ${value} is not within 128 bits and hence cannot be converted to U128.`); | ||
} | ||
|
||
this.value = value; | ||
} | ||
|
||
static fromU64sLE(lo: bigint, hi: bigint): U128 { | ||
// Validate limbs are within valid ranges | ||
if (lo < 0n || lo >= 2n ** 64n) { | ||
throw new Error(`Lower limb ${lo} is not within valid range (0 to 2^64-1)`); | ||
} | ||
if (hi < 0n || hi >= 2n ** 64n) { | ||
throw new Error(`Higher limb ${hi} is not within valid range (0 to 2^64-1)`); | ||
} | ||
|
||
// Combine limbs into full value and create new instance | ||
const value = (hi << 64n) | lo; | ||
return new U128(value); | ||
} | ||
|
||
get lo(): bigint { | ||
return this.value & 0xffffffffffffffffn; | ||
} | ||
|
||
get hi(): bigint { | ||
return this.value >> 64n; | ||
} | ||
|
||
toInteger(): bigint { | ||
return this.value; | ||
} | ||
} |