| import { describe, it, expect } from 'vitest'; |
| import { |
| HASH_KEY, |
| kirchenbauerSeed, |
| isGreenToken, |
| greenUniform, |
| textsealR, |
| keyFromString, |
| ksemstampSeed, |
| } from '../../src/lib/utils/hashing'; |
|
|
| describe('kirchenbauerSeed', () => { |
| it('matches the reference formula salt * prev mod 2^64-1', () => { |
| expect(kirchenbauerSeed(1)).toBe(15485863n); |
| expect(kirchenbauerSeed(1000)).toBe(15485863000n); |
| const big = 2n ** 64n - 1n; |
| expect(kirchenbauerSeed(123456)).toBe((HASH_KEY * 123456n) % big); |
| }); |
| }); |
|
|
| describe('green partition', () => { |
| it('is deterministic', () => { |
| const seed = kirchenbauerSeed(777); |
| for (let v = 0; v < 50; v++) { |
| expect(isGreenToken(seed, v, 0.25)).toBe(isGreenToken(seed, v, 0.25)); |
| } |
| }); |
|
|
| it('has approximately gamma green fraction over the vocab', () => { |
| const seed = kirchenbauerSeed(42); |
| const V = 50000; |
| const gamma = 0.25; |
| let green = 0; |
| for (let v = 0; v < V; v++) if (isGreenToken(seed, v, gamma)) green++; |
| const frac = green / V; |
| |
| expect(Math.abs(frac - gamma)).toBeLessThan(0.01); |
| }); |
|
|
| it('partitions differ across seeds (context-dependence)', () => { |
| const s1 = kirchenbauerSeed(10); |
| const s2 = kirchenbauerSeed(11); |
| let diff = 0; |
| for (let v = 0; v < 2000; v++) { |
| if (isGreenToken(s1, v, 0.25) !== isGreenToken(s2, v, 0.25)) diff++; |
| } |
| expect(diff).toBeGreaterThan(200); |
| }); |
|
|
| it('greenUniform in [0,1)', () => { |
| const seed = kirchenbauerSeed(5); |
| for (let v = 0; v < 100; v++) { |
| const u = greenUniform(seed, v); |
| expect(u).toBeGreaterThanOrEqual(0); |
| expect(u).toBeLessThan(1); |
| } |
| }); |
| }); |
|
|
| describe('textsealR', () => { |
| const key = keyFromString('secret'); |
| it('is deterministic per (key, context, token)', () => { |
| expect(textsealR(key, [1, 2, 3], 42)).toBe(textsealR(key, [1, 2, 3], 42)); |
| }); |
| it('changes with context', () => { |
| expect(textsealR(key, [1, 2, 3], 42)).not.toBe(textsealR(key, [1, 2, 4], 42)); |
| }); |
| it('changes with key', () => { |
| const key2 = keyFromString('other'); |
| expect(textsealR(key, [1, 2, 3], 42)).not.toBe(textsealR(key2, [1, 2, 3], 42)); |
| }); |
| it('is uniform-ish: mean near 0.5', () => { |
| let sum = 0; |
| const n = 20000; |
| for (let v = 0; v < n; v++) sum += textsealR(key, [9, 8, 7], v); |
| expect(Math.abs(sum / n - 0.5)).toBeLessThan(0.01); |
| }); |
| it('stays strictly inside (0,1)', () => { |
| for (let v = 0; v < 1000; v++) { |
| const r = textsealR(key, [], v); |
| expect(r).toBeGreaterThan(0); |
| expect(r).toBeLessThan(1); |
| } |
| }); |
| }); |
|
|
| describe('keyFromString / ksemstampSeed', () => { |
| it('derives stable distinct keys', () => { |
| expect(keyFromString('a')).toBe(keyFromString('a')); |
| expect(keyFromString('a')).not.toBe(keyFromString('b')); |
| }); |
| it('ksemstamp seed differs per cluster and avoids zero for cluster 0', () => { |
| expect(ksemstampSeed(0)).not.toBe(0n); |
| expect(ksemstampSeed(0)).not.toBe(ksemstampSeed(1)); |
| }); |
| }); |
|
|