Skip to content

Commit aef5514

Browse files
fingolfincodex
andcommitted
Fix garbled result produced by CosetLeadersMatFFE
When invoked on compressed matrices over small finite fields, CosetLeadersMatFFE sometimes would produce incorrect output with gaps in it (i.e., undefined entries) AI assistance: Codex investigated the 8-bit fast path, added a regression test, and prepared the fix. Co-authored-by: Codex <codex@openai.com>
1 parent 96c73b4 commit aef5514

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

src/vec8bit.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2482,6 +2482,7 @@ static UInt CosetLeadersInner8Bits(Obj veclis,
24822482
UInt elts;
24832483
UInt1 * ptr, *ptrw;
24842484
const UInt1 * gettab;
2485+
const Obj * gapseq;
24852486
const UInt1 * feltffe;
24862487
Obj x;
24872488
Obj vp;
@@ -2491,20 +2492,29 @@ static UInt CosetLeadersInner8Bits(Obj veclis,
24912492
elts = ELS_BYTE_FIELDINFO_8BIT(info);
24922493
settab = SETELT_FIELDINFO_8BIT(info);
24932494
gettab = GETELT_FIELDINFO_8BIT(info);
2495+
gapseq = GAPSEQ_FELT_FIELDINFO_8BIT(info);
24942496
ptrw = BYTES_VEC8BIT(w);
24952497
if (weight == 1) {
24962498
for (i = pos; i <= len; i++) {
24972499
vp = ELM_PLIST(veclis, i);
24982500
u = ELM_PLIST(vp, 1);
24992501
AddVec8BitVec8BitInner(w, w, u, 1, lenw);
25002502
ptr = BYTES_VEC8BIT(v) + (i - 1) / elts;
2501-
*ptr = settab[*ptr + 256 * (elts + ((i - 1) % elts))];
2503+
// Keep the coefficient vector in the same sorted field-element
2504+
// order that NumberFFVector uses for list indices.
2505+
feltffe = FELT_FFE_FIELDINFO_8BIT(info);
2506+
x = ELM_PLIST(felts, 2);
2507+
*ptr = settab[*ptr + 256 * (elts * feltffe[VAL_FFE(x)] +
2508+
((i - 1) % elts))];
25022509
sy = 0;
25032510
for (j = 0; j < lenw; j++) {
25042511
UInt xxxx;
25052512
sy *= q;
25062513
xxxx = gettab[ptrw[j / elts] + 256 * (j % elts)];
2507-
sy += xxxx;
2514+
// The packed 8-bit representation uses a different internal
2515+
// field ordering, so translate back to GAP's sequence before
2516+
// using the syndrome number as a plain-list position.
2517+
sy += INT_INTOBJ(gapseq[xxxx]);
25082518
}
25092519
if ((Obj)0 == ELM_PLIST(leaders, sy + 1)) {
25102520
UInt k;
@@ -2517,22 +2527,26 @@ static UInt CosetLeadersInner8Bits(Obj veclis,
25172527
wc = ZeroVec8Bit(q, lenw, 1);
25182528
settab = SETELT_FIELDINFO_8BIT(info);
25192529
gettab = GETELT_FIELDINFO_8BIT(info);
2530+
gapseq = GAPSEQ_FELT_FIELDINFO_8BIT(info);
25202531
ptr = BYTES_VEC8BIT(v) + (i - 1) / elts;
25212532
ptrw = BYTES_VEC8BIT(w);
2522-
for (k = 2; k < q; k++) {
2523-
qk = FFE_FELT_FIELDINFO_8BIT(info, k);
2533+
for (k = 3; k <= q; k++) {
2534+
// Record scalar multiples in the same order as the GAP
2535+
// fallback, namely the sorted field elements in 'felts'.
2536+
qk = ELM_PLIST(felts, k);
25242537
MultVec8BitFFEInner(wc, w, qk, 1, lenw);
25252538
ptrw = BYTES_VEC8BIT(wc);
25262539
sy = 0;
25272540
for (j = 0; j < lenw; j++) {
25282541
UInt xxxx;
25292542
sy *= q;
25302543
xxxx = gettab[ptrw[j / elts] + 256 * (j % elts)];
2531-
sy += xxxx;
2544+
sy += INT_INTOBJ(gapseq[xxxx]);
25322545
}
25332546
vc = ZeroVec8Bit(q, len, 0);
25342547
settab = SETELT_FIELDINFO_8BIT(info);
25352548
gettab = GETELT_FIELDINFO_8BIT(info);
2549+
gapseq = GAPSEQ_FELT_FIELDINFO_8BIT(info);
25362550
ptr = BYTES_VEC8BIT(v) + (i - 1) / elts;
25372551
ptrw = BYTES_VEC8BIT(w);
25382552
MultVec8BitFFEInner(vc, v, qk, 1, len);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Regression test for issue #5923: the 8-bit kernel path for
2+
# CosetLeadersMatFFE must not leave holes in the result list.
3+
gap> F := GF(4);;
4+
gap> M := One(F)*[[1,0,Z(4)],[0,1,Z(4)^2]];;
5+
gap> L := CosetLeadersMatFFE(M, F);;
6+
gap> Length(L) = Size(F)^2;
7+
true
8+
gap> ForAll([1..Length(L)], i -> IsBound(L[i]));
9+
true
10+
gap> List(L, v -> NumberFFVector(M * v, Size(F))) = [0..Length(L)-1];
11+
true
12+
13+
# some additional tests "because we can" for other cases
14+
gap> F := GF(2);;
15+
gap> M := One(F)*[[1,0,1,0,1,0],[1,1,1,0,0,0]];;
16+
gap> L := CosetLeadersMatFFE(M, F);;
17+
gap> Length(L) = Size(F)^2;
18+
true
19+
gap> ForAll([1..Length(L)], i -> IsBound(L[i]));
20+
true
21+
gap> List(L, v -> NumberFFVector(M * v, Size(F))) = [0..Length(L)-1];
22+
true
23+
24+
# some additional tests "because we can" for other cases
25+
gap> F := GF(257);;
26+
gap> M := One(F)*[[1,0,1,0,1,0],[1,1,1,0,0,0]];;
27+
gap> L := CosetLeadersMatFFE(M, F);;
28+
gap> Length(L) = Size(F)^2;
29+
true
30+
gap> ForAll([1..Length(L)], i -> IsBound(L[i]));
31+
true
32+
gap> List(L, v -> NumberFFVector(M * v, Size(F))) = [0..Length(L)-1];
33+
true

0 commit comments

Comments
 (0)