package ring // Variable-width serialization for bounded-norm polynomial coefficients. // // The fixed Serialize/Deserialize functions pack at ceil(log2(Q)) bits per // coefficient (14 for Falcon-512). When coefficients are known to be bounded // by a smaller range — as in sigma protocol responses (Gaussian, bound ~σ·T) // or gadget digit polynomials (bound ~base/2) — the bit width can be reduced. // // The packing order matches Serialize: coefficient 0 at bits [0:bitsPerCoeff), // coefficient 1 at bits [bitsPerCoeff:2*bitsPerCoeff), etc., LE within each // coefficient. // SerializeBounded packs polynomial coefficients at the given bit width. // Uses the same LE bitwise packing order as Serialize. // // If signed is true, coefficients are stored in two's complement: // - Positive coefficients are stored directly // - Negative coefficients (stored in centered form as Q - |v| in the Poly) // are converted to signed two's complement before packing // // The caller must ensure |coefficient| < 2^(bitsPerCoeff-1) when signed, // or 0 ≤ coefficient < 2^bitsPerCoeff when unsigned, or the result is // truncated. func SerializeBounded(a *Poly, bitsPerCoeff int, signed bool) []byte { q := a.params.Q half := q / 2 mask := uint32((1 << bitsPerCoeff) - 1) totalBits := bitsPerCoeff * len(a.Coeffs) out := make([]byte, (totalBits+7)/8) bitPos := 0 for _, c := range a.Coeffs { var v uint32 if signed && c > half { // Negative in centered form: c = Q - |v|. // Convert to two's complement: -|v| = (~|v| + 1) & mask. abs := q - c v = ((^abs) + 1) & mask } else { v = c & mask } for b := 0; b < bitsPerCoeff; b++ { if v&(1<= signBit { // Two's complement negative: extract magnitude. abs := ((^v) + 1) & mask poly.Coeffs[i] = q - abs if poly.Coeffs[i] >= q { poly.Coeffs[i] = 0 } } else { poly.Coeffs[i] = v } } return poly }