diff --git a/fjs/basen/proof.f.mjs b/fjs/basen/proof.f.mjs index b65da0af8..3e0a94582 100644 --- a/fjs/basen/proof.f.mjs +++ b/fjs/basen/proof.f.mjs @@ -4,15 +4,10 @@ import { baseN } from './module.f.mjs' const hex = baseN(4n, '0123456789abcdef') -const cb32 = baseN(5n, '0123456789abcdefghjkmnpqrstvwxyz', c => { - const lower = c.toLowerCase() - switch (lower) { - case 'i': { return '1' } - case 'l': { return '1' } - case 'o': { return '0' } - default: { return lower } - } -}) +// A synthetic normalizer keeps this proof focused on `baseN`'s mechanism +// rather than duplicating the rules owned by a concrete codec. +const normalizedHex = baseN(4n, '0123456789abcdef', c => + c === 'x' ? 'a' : c === 'y' ? 'z' : c.toLowerCase()) // Sample input for the `big` proof below: 262 144 `f` characters decode into a // 1 Mibit (`maxLength`) vector. @@ -48,16 +43,13 @@ export const proof = { }, normalizeHit: () => { // 'A' lowercases to 'a' — same vector as the lowercase input. - const a = cb32.stringToVec('A') - const b = cb32.stringToVec('a') + const a = normalizedHex.stringToVec('A') + const b = normalizedHex.stringToVec('a') assertEq(a, b, [a, b]) - // Crockford folds: i,l → 1 and o → 0. - assertEq(cb32.stringToVec('I'), cb32.stringToVec('1'), 'I→1') - assertEq(cb32.stringToVec('l'), cb32.stringToVec('1'), 'l→1') - assertEq(cb32.stringToVec('o'), cb32.stringToVec('0'), 'o→0') + assertEq(normalizedHex.stringToVec('x'), a, 'x→a') }, normalizeMiss: () => { - assertEq(cb32.stringToVec('u'), null, 'unknown char should return null') + assertEq(normalizedHex.stringToVec('y'), null, 'normalizing to an unknown char should return null') }, // Decodes a 1 Mibit hex string. With the O(n log n) `listToVec` builder this // runs in well under a second (was ~13 s node / ~43 s bun under the old diff --git a/fjs/basen/todo/proof-crockford-copy.md b/fjs/basen/todo/proof-crockford-copy.md deleted file mode 100644 index 27656126f..000000000 --- a/fjs/basen/todo/proof-crockford-copy.md +++ /dev/null @@ -1,57 +0,0 @@ -## proof-crockford-copy. `basen/proof.f.mjs` copies cbase32's alphabet and normalizer - -**Priority:** P4 -**Status:** open - -### Problem - -`fjs/basen/proof.f.mjs:7-15` re-declares the Crockford Base32 codec — -character-for-character the alphabet and fold table that -`fjs/basen/cbase32/module.f.mjs` owns: - -```js -// fjs/basen/proof.f.mjs:7-15 -const cb32 = baseN(5n, '0123456789abcdefghjkmnpqrstvwxyz', c => { - const lower = c.toLowerCase() - switch (lower) { - case 'i': { return '1' } - case 'l': { return '1' } - case 'o': { return '0' } - default: { return lower } - } -}) - -// fjs/basen/cbase32/module.f.mjs:14, :21-29 — the owner -const m = '0123456789abcdefghjkmnpqrstvwxyz' -const normalizeChar = c => { - const lower = c.toLowerCase() - switch (lower) { - case 'i': { return '1' } - ... -``` - -The Crockford i/l→1, o→0 rule now has two definitions. If `cbase32` changes, -`basen`'s `normalizeHit`/`normalizeMiss` proofs keep silently testing the old -one and stay green. - -### Proposal - -Either direction works; pick one: - -1. Export the codec parameters (`alphabet`, `normalizeChar`) from `cbase32` - and build `basen/proof`'s `cb32` from the imports. Note the layering: a - lower module's proof importing from a child package — acceptable for a - proof, since `cbase32` already depends on `basen` only at runtime, not the - reverse. -2. If that layering is unwanted: replace the copy with a deliberately - synthetic normalizing alphabet (e.g. 4-bit with one fold rule) so the - `basen` normalization proofs test the *mechanism* without mirroring a real - codec's data. - -Option 2 is simpler and keeps `basen`'s proof self-contained; the proof only -needs *a* normalizer, not Crockford's. - -### Tasks - -- [ ] Replace `basen/proof.f.mjs`'s `cb32` per one of the options above. -- [ ] `fjs t` — basen and cbase32 proofs pass.