From c93eda91566437be3b6ac40e58d9cdc1efebbdd8 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Mon, 4 May 2026 15:32:25 +0000 Subject: [PATCH] Fix parityCount to use Math.floor, neededPackets to use totalGenerations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parityCount: Math.ceil → Math.floor - Small files (G ≤ 33) now truly get 0 parity generations - Large files (G ≥ 34) still get proportional parity (≈3%) - Previously Math.ceil always gave ≥1 parity for any G ≥ 1 neededPackets: K * sourceGenerations → K * totalGenerations - Aligns with the round-robin scheduler: symbols are interleaved across ALL generations, so practical minimum is K × totalGens - Fixes the mismatch where 'needed' showed 32 but actual decode required ~48 frames (for 2 source + 1 parity case) Tests: Updated assembly test comments and totalGenerations params to match new parityCount behavior. Closes the long-standing 'needed frames count is off because of outer error correction' issue. --- src/core/protocol/constants.ts | 2 +- src/tests/complete.test.ts | 10 +++++----- src/workers/decode.worker.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/protocol/constants.ts b/src/core/protocol/constants.ts index 053385e..1562593 100644 --- a/src/core/protocol/constants.ts +++ b/src/core/protocol/constants.ts @@ -67,7 +67,7 @@ export const OUTER_EC_OVERHEAD = 0.03; /** Compute number of parity generations for G source generations. */ export function parityCount(sourceGenerations: number): number { - return Math.ceil(sourceGenerations * OUTER_EC_OVERHEAD); + return Math.floor(sourceGenerations * OUTER_EC_OVERHEAD); } /** Compute source generation count from total generations. */ diff --git a/src/tests/complete.test.ts b/src/tests/complete.test.ts index 416c528..bfe8352 100644 --- a/src/tests/complete.test.ts +++ b/src/tests/complete.test.ts @@ -355,10 +355,10 @@ describe('Payload Assembly', () => { solved.set(0, g0); solved.set(1, g1); - // 2 source gens + 1 parity = 3 total. Both source gens present. + // 2 source gens + 0 parity = 2 total (small file, no outer RS). Both source gens present. // dataLength spans into second generation so we can verify cross-gen assembly const dataLength = K * MAX_PAYLOAD_SIZE + 4; - const data = assemblePayload(solved, 3, dataLength); + const data = assemblePayload(solved, 2, dataLength); expect(data.length).toBe(dataLength); expect(data[0]).toBe(1); expect(data[1]).toBe(2); @@ -385,8 +385,8 @@ describe('Payload Assembly', () => { const solved = new Map(); solved.set(0, g0); - // 1 source gen + 1 parity = 2 total. Source gen present. - const data = assemblePayload(solved, 2, 3); + // 1 source gen + 0 parity = 1 total (small file, no outer RS). Source gen present. + const data = assemblePayload(solved, 1, 3); expect(data.length).toBe(3); expect(data[0]).toBe(1); expect(data[1]).toBe(2); @@ -403,7 +403,7 @@ describe('Payload Assembly', () => { const solved = new Map(); solved.set(0, g0); - // 3 source gens + 1 parity = 4 total, but only 1 solved + // 4 source gens + 0 parity = 4 total, but only 1 solved expect(() => assemblePayload(solved, 4, 3)).toThrow('only 1 generations solved'); }); }); diff --git a/src/workers/decode.worker.ts b/src/workers/decode.worker.ts index 0ad9930..11fd1ac 100644 --- a/src/workers/decode.worker.ts +++ b/src/workers/decode.worker.ts @@ -251,7 +251,7 @@ function reconstructData(state: DecodeState): void { function reportProgress(state: DecodeState): void { const totalGens = state.totalGenerations; const solvedGens = state.solvedGenerations.size; - const needed = state.sourceGenerations > 0 ? K * state.sourceGenerations : 0; + const needed = state.totalGenerations > 0 ? K * state.totalGenerations : 0; self.postMessage({ type: 'progress',