1const PREALLOC_CAP: usize = 4096;
25
26const PERIOD_CAP: usize = 512;
28
29pub const MAX_POLY_DEGREE: usize = 4;
31
32pub const MAX_RECUR_ORDER: usize = 4;
35
36const LFSR_MAX_BYTES: usize = 512;
39
40const FCSR_MAX_BYTES: usize = 256;
42
43const RLE_MAX_TOTAL: usize = 1 << 28;
45
46pub const MAX_GEN_NODES: u32 = 256;
48
49pub const MAX_GEN_DEPTH: u32 = 32;
51
52const DECODE_MAX_DEPTH: u32 = 32;
54
55const DEFAULT_MAX_ELEMENTS: usize = 1 << 28;
58
59pub const T_INTS: u8 = 19; pub const T_INTS_AFFINE: u8 = 32; pub const T_INTS_DELTA: u8 = 39; pub const T_INTS_DOD: u8 = 40; pub const T_INTS_FOR: u8 = 41; pub const T_INTS_RLE: u8 = 42; pub const T_INTS_DICT: u8 = 43; pub const T_INTS_POLY: u8 = 50; pub const T_GEN: u8 = 51; pub const T_BYTES: u8 = 53; pub const T_INTS_GEOMETRIC: u8 = 61; pub const T_INTS_PERIODIC: u8 = 62; pub const T_INTS_SPARSE: u8 = 67; pub const T_INTS_LRECUR: u8 = 82; pub const T_INTS_LFSR: u8 = 83; pub const T_INTS_FCSR: u8 = 84; #[inline]
82pub fn write_uvarint(mut x: u64, out: &mut Vec<u8>) {
83 while x >= 0x80 {
84 out.push((x as u8) | 0x80);
85 x >>= 7;
86 }
87 out.push(x as u8);
88}
89
90#[inline]
92pub fn read_uvarint(buf: &[u8], pos: &mut usize) -> Option<u64> {
93 let mut result = 0u64;
94 let mut shift = 0u32;
95 loop {
96 let b = *buf.get(*pos)?;
97 *pos += 1;
98 if shift >= 64 {
99 return None; }
101 result |= u64::from(b & 0x7f) << shift;
102 if b & 0x80 == 0 {
103 return Some(result);
104 }
105 shift += 7;
106 }
107}
108
109#[inline]
111pub fn zigzag(x: i64) -> u64 {
112 ((x << 1) ^ (x >> 63)) as u64
113}
114
115#[inline]
117pub fn unzigzag(x: u64) -> i64 {
118 ((x >> 1) as i64) ^ -((x & 1) as i64)
119}
120
121pub fn uvarint_byte_len(x: u64) -> usize {
123 (((64 - x.leading_zeros()).max(1) + 6) / 7) as usize
124}
125
126pub fn leb128_encode<I: Iterator<Item = i64> + Clone>(out: &mut Vec<u8>, vals: I, n: usize) {
129 let signed = vals.clone().any(|x| x < 0);
130 write_uvarint(((n as u64) << 1) | signed as u64, out);
131 out.reserve(n * 2);
132 if signed {
133 for x in vals {
134 write_uvarint(zigzag(x), out);
135 }
136 } else {
137 for x in vals {
138 write_uvarint(x as u64, out);
139 }
140 }
141}
142
143pub fn bitpack(vals: &[u64], width: u8) -> Vec<u8> {
147 if width == 0 {
148 return Vec::new();
149 }
150 let total_bits = vals.len().saturating_mul(width as usize);
151 let mut out = vec![0u8; total_bits.div_ceil(8)];
152 let mut bitpos = 0usize;
153 for &val in vals {
154 let mut bits = val;
155 let mut remaining = width as usize;
156 while remaining > 0 {
157 let byte = bitpos / 8;
158 let off = bitpos % 8;
159 let take = remaining.min(8 - off);
160 let mask = (1u64 << take) - 1;
161 out[byte] |= ((bits & mask) as u8) << off;
162 bits >>= take;
163 bitpos += take;
164 remaining -= take;
165 }
166 }
167 out
168}
169
170pub fn bitunpack(bytes: &[u8], count: usize, width: u8) -> Option<Vec<u64>> {
173 if width == 0 || width > 64 {
174 return None;
175 }
176 let total_bits = count.checked_mul(width as usize)?;
177 if bytes.len() < total_bits.div_ceil(8) {
178 return None;
179 }
180 let mut out = Vec::with_capacity(count.min(PREALLOC_CAP));
181 let mut bitpos = 0usize;
182 for _ in 0..count {
183 let mut val = 0u64;
184 let mut got = 0usize;
185 while got < width as usize {
186 let byte = bitpos / 8;
187 let off = bitpos % 8;
188 let take = (width as usize - got).min(8 - off);
189 let mask = (1u64 << take) - 1;
190 val |= (((bytes[byte] >> off) as u64) & mask) << got;
191 got += take;
192 bitpos += take;
193 }
194 out.push(val);
195 }
196 Some(out)
197}
198
199#[derive(Clone, Copy, Debug, PartialEq, Eq)]
202pub enum GenCmp {
203 Eq,
204 Ne,
205 Lt,
206 Le,
207 Gt,
208 Ge,
209}
210
211#[derive(Clone, Debug, PartialEq, Eq)]
215pub enum GenExpr {
216 Index,
217 Const(i64),
218 Add(Box<GenExpr>, Box<GenExpr>),
219 Sub(Box<GenExpr>, Box<GenExpr>),
220 Mul(Box<GenExpr>, Box<GenExpr>),
221 Div(Box<GenExpr>, Box<GenExpr>),
222 Mod(Box<GenExpr>, Box<GenExpr>),
223 Select { op: GenCmp, lhs: Box<GenExpr>, rhs: Box<GenExpr>, then: Box<GenExpr>, els: Box<GenExpr> },
224}
225
226pub fn gen_eval(e: &GenExpr, i: i64) -> i64 {
228 match e {
229 GenExpr::Index => i,
230 GenExpr::Const(c) => *c,
231 GenExpr::Add(a, b) => gen_eval(a, i).wrapping_add(gen_eval(b, i)),
232 GenExpr::Sub(a, b) => gen_eval(a, i).wrapping_sub(gen_eval(b, i)),
233 GenExpr::Mul(a, b) => gen_eval(a, i).wrapping_mul(gen_eval(b, i)),
234 GenExpr::Div(a, b) => {
235 let d = gen_eval(b, i);
236 if d == 0 { 0 } else { gen_eval(a, i).wrapping_div(d) }
237 }
238 GenExpr::Mod(a, b) => {
239 let d = gen_eval(b, i);
240 if d == 0 { 0 } else { gen_eval(a, i).wrapping_rem(d) }
241 }
242 GenExpr::Select { op, lhs, rhs, then, els } => {
243 let (l, r) = (gen_eval(lhs, i), gen_eval(rhs, i));
244 let c = match op {
245 GenCmp::Eq => l == r,
246 GenCmp::Ne => l != r,
247 GenCmp::Lt => l < r,
248 GenCmp::Le => l <= r,
249 GenCmp::Gt => l > r,
250 GenCmp::Ge => l >= r,
251 };
252 if c { gen_eval(then, i) } else { gen_eval(els, i) }
253 }
254 }
255}
256
257pub fn serialize_gen(e: &GenExpr, out: &mut Vec<u8>) {
259 match e {
260 GenExpr::Index => out.push(0),
261 GenExpr::Const(c) => {
262 out.push(1);
263 write_uvarint(zigzag(*c), out);
264 }
265 GenExpr::Add(a, b) => { out.push(2); serialize_gen(a, out); serialize_gen(b, out); }
266 GenExpr::Sub(a, b) => { out.push(3); serialize_gen(a, out); serialize_gen(b, out); }
267 GenExpr::Mul(a, b) => { out.push(4); serialize_gen(a, out); serialize_gen(b, out); }
268 GenExpr::Div(a, b) => { out.push(5); serialize_gen(a, out); serialize_gen(b, out); }
269 GenExpr::Mod(a, b) => { out.push(6); serialize_gen(a, out); serialize_gen(b, out); }
270 GenExpr::Select { op, lhs, rhs, then, els } => {
271 out.push(7);
272 out.push(*op as u8);
273 serialize_gen(lhs, out);
274 serialize_gen(rhs, out);
275 serialize_gen(then, out);
276 serialize_gen(els, out);
277 }
278 }
279}
280
281pub fn deserialize_gen(buf: &[u8], pos: &mut usize, budget: &mut u32, depth: u32) -> Option<GenExpr> {
283 if depth > MAX_GEN_DEPTH || *budget == 0 {
284 return None;
285 }
286 *budget -= 1;
287 let tag = *buf.get(*pos)?;
288 *pos += 1;
289 Some(match tag {
290 0 => GenExpr::Index,
291 1 => GenExpr::Const(unzigzag(read_uvarint(buf, pos)?)),
292 2..=6 => {
293 let a = Box::new(deserialize_gen(buf, pos, budget, depth + 1)?);
294 let b = Box::new(deserialize_gen(buf, pos, budget, depth + 1)?);
295 match tag {
296 2 => GenExpr::Add(a, b),
297 3 => GenExpr::Sub(a, b),
298 4 => GenExpr::Mul(a, b),
299 5 => GenExpr::Div(a, b),
300 _ => GenExpr::Mod(a, b),
301 }
302 }
303 7 => {
304 let op = match *buf.get(*pos)? {
305 0 => GenCmp::Eq,
306 1 => GenCmp::Ne,
307 2 => GenCmp::Lt,
308 3 => GenCmp::Le,
309 4 => GenCmp::Gt,
310 5 => GenCmp::Ge,
311 _ => return None,
312 };
313 *pos += 1;
314 let lhs = Box::new(deserialize_gen(buf, pos, budget, depth + 1)?);
315 let rhs = Box::new(deserialize_gen(buf, pos, budget, depth + 1)?);
316 let then = Box::new(deserialize_gen(buf, pos, budget, depth + 1)?);
317 let els = Box::new(deserialize_gen(buf, pos, budget, depth + 1)?);
318 GenExpr::Select { op, lhs, rhs, then, els }
319 }
320 _ => return None,
321 })
322}
323
324pub fn detect_affine(v: &[i64]) -> Option<(i64, i64)> {
328 if v.len() < 2 {
329 return None;
330 }
331 let base = v[0];
332 let stride = v[1].wrapping_sub(v[0]);
333 for (i, &x) in v.iter().enumerate() {
334 if base.wrapping_add((i as i64).wrapping_mul(stride)) != x {
335 return None;
336 }
337 }
338 Some((base, stride))
339}
340
341pub fn detect_geometric(v: &[i64]) -> Option<(i64, i64)> {
344 if v.len() < 3 {
345 return None;
346 }
347 let base = v[0];
348 if base == 0 || v[1].checked_rem(base)? != 0 {
349 return None;
350 }
351 let ratio = v[1].checked_div(base)?;
352 if ratio == 0 || ratio == 1 {
353 return None;
354 }
355 let mut cur = base;
356 for &x in v {
357 if cur != x {
358 return None;
359 }
360 cur = cur.wrapping_mul(ratio);
361 }
362 Some((base, ratio))
363}
364
365pub fn detect_period(v: &[i64]) -> Option<usize> {
368 let n = v.len();
369 if n < 4 {
370 return None;
371 }
372 let cap = (n / 2).min(PERIOD_CAP);
373 'p: for p in 2..=cap {
374 for i in p..n {
375 if v[i] != v[i - p] {
376 continue 'p;
377 }
378 }
379 return Some(p);
380 }
381 None
382}
383
384pub fn detect_sparse(v: &[i64]) -> Option<(i64, Vec<(usize, i64)>)> {
387 if v.len() < 8 {
388 return None;
389 }
390 let mut cand = v[0];
391 let mut count: i64 = 0;
392 for &x in v {
393 if count == 0 {
394 cand = x;
395 count = 1;
396 } else if x == cand {
397 count += 1;
398 } else {
399 count -= 1;
400 }
401 }
402 let occ = v.iter().filter(|&&x| x == cand).count();
403 if v.len() - occ > v.len() / 4 {
404 return None; }
406 let exceptions: Vec<(usize, i64)> =
407 v.iter().enumerate().filter(|(_, &x)| x != cand).map(|(i, &x)| (i, x)).collect();
408 Some((cand, exceptions))
409}
410
411pub fn detect_poly_generator(v: &[i64]) -> Option<(u8, Vec<i64>)> {
414 if v.len() < 3 {
415 return None;
416 }
417 let mut levels: Vec<Vec<i64>> = Vec::with_capacity(MAX_POLY_DEGREE + 1);
418 levels.push(v.to_vec());
419 for d in 0..MAX_POLY_DEGREE {
420 let prev = &levels[d];
421 if prev.len() < 2 {
422 break;
423 }
424 let mut next = Vec::with_capacity(prev.len() - 1);
425 for w in prev.windows(2) {
426 next.push(w[1].checked_sub(w[0])?); }
428 if next.len() >= 2 && next.iter().all(|&x| x == next[0]) {
429 let degree = (d + 1) as u8;
430 let mut seeds: Vec<i64> = levels.iter().map(|lvl| lvl[0]).collect();
431 seeds.push(next[0]);
432 if reconstruct_poly(&seeds, v.len()) == v {
433 return Some((degree, seeds));
434 }
435 return None;
436 }
437 levels.push(next);
438 }
439 None
440}
441
442pub fn reconstruct_poly(seeds: &[i64], n: usize) -> Vec<i64> {
444 let mut diffs = seeds.to_vec();
445 let mut out = Vec::with_capacity(n.min(PREALLOC_CAP));
446 for _ in 0..n {
447 out.push(diffs[0]);
448 for j in 0..diffs.len().saturating_sub(1) {
449 diffs[j] = diffs[j].wrapping_add(diffs[j + 1]);
450 }
451 }
452 out
453}
454
455pub fn detect_linear_recurrence(v: &[i64]) -> Option<(Vec<i64>, Vec<i64>)> {
463 let n = v.len();
464 for k in 1..=MAX_RECUR_ORDER.min(n / 2) {
465 if let Some(coeffs) = solve_recurrence(v, k) {
466 if reconstruct_recurrence(&coeffs, &v[..k], n) == v {
467 return Some((coeffs, v[..k].to_vec()));
468 }
469 }
470 }
471 None
472}
473
474fn solve_recurrence(v: &[i64], k: usize) -> Option<Vec<i64>> {
478 let mut m = vec![vec![0i128; k]; k];
479 let mut b = vec![0i128; k];
480 for r in 0..k {
481 for j in 0..k {
482 m[r][j] = v[k + r - 1 - j] as i128; }
484 b[r] = v[k + r] as i128;
485 }
486 let det_m = det_i128(&m)?;
487 if det_m == 0 {
488 return None;
489 }
490 let mut coeffs = Vec::with_capacity(k);
491 for j in 0..k {
492 let mut mj = m.clone();
493 for (r, br) in b.iter().enumerate() {
494 mj[r][j] = *br;
495 }
496 let det_j = det_i128(&mj)?;
497 if det_j % det_m != 0 {
498 return None; }
500 let c = det_j / det_m;
501 if c < i64::MIN as i128 || c > i64::MAX as i128 {
502 return None;
503 }
504 coeffs.push(c as i64);
505 }
506 Some(coeffs)
507}
508
509fn det_i128(m: &[Vec<i128>]) -> Option<i128> {
512 let k = m.len();
513 match k {
514 1 => Some(m[0][0]),
515 2 => m[0][0].checked_mul(m[1][1])?.checked_sub(m[0][1].checked_mul(m[1][0])?),
516 _ => {
517 let mut acc: i128 = 0;
518 for j in 0..k {
519 let minor: Vec<Vec<i128>> =
520 (1..k).map(|r| (0..k).filter(|&c| c != j).map(|c| m[r][c]).collect()).collect();
521 let cof = m[0][j].checked_mul(det_i128(&minor)?)?;
522 acc = if j % 2 == 0 { acc.checked_add(cof)? } else { acc.checked_sub(cof)? };
523 }
524 Some(acc)
525 }
526 }
527}
528
529fn reconstruct_recurrence(coeffs: &[i64], seeds: &[i64], n: usize) -> Vec<i64> {
532 let k = coeffs.len();
533 if seeds.len() < k {
536 return seeds[..seeds.len().min(n)].to_vec();
537 }
538 let mut out = Vec::with_capacity(n.min(PREALLOC_CAP));
539 out.extend_from_slice(&seeds[..k.min(n)]);
540 for i in k..n {
541 let mut acc = 0i64;
542 for j in 0..k {
543 acc = acc.wrapping_add(coeffs[j].wrapping_mul(out[i - 1 - j]));
544 }
545 out.push(acc);
546 }
547 out.truncate(n);
548 out
549}
550
551pub trait FieldElem: Copy + PartialEq {
561 fn zero() -> Self;
562 fn one() -> Self;
563 fn add(self, o: Self) -> Self;
564 fn sub(self, o: Self) -> Self;
565 fn mul(self, o: Self) -> Self;
566 fn inv(self) -> Self;
568}
569
570#[derive(Clone, Copy, PartialEq, Eq, Debug)]
572pub struct Gf2(pub bool);
573impl FieldElem for Gf2 {
574 fn zero() -> Self { Gf2(false) }
575 fn one() -> Self { Gf2(true) }
576 fn add(self, o: Self) -> Self { Gf2(self.0 ^ o.0) }
577 fn sub(self, o: Self) -> Self { Gf2(self.0 ^ o.0) }
578 fn mul(self, o: Self) -> Self { Gf2(self.0 && o.0) }
579 fn inv(self) -> Self { self } }
581
582#[derive(Clone, Copy, PartialEq, Eq, Debug)]
584pub struct Gf256(pub u8);
585
586fn gf256_xtime(a: u8) -> u8 {
587 (a << 1) ^ (if a & 0x80 != 0 { 0x1B } else { 0 })
588}
589fn gf256_tables() -> &'static (Vec<u8>, Vec<u8>) {
592 static T: std::sync::OnceLock<(Vec<u8>, Vec<u8>)> = std::sync::OnceLock::new();
593 T.get_or_init(|| {
594 let mut exp = vec![0u8; 512];
595 let mut log = vec![0u8; 256];
596 let mut x = 1u8;
597 for i in 0..255usize {
598 exp[i] = x;
599 log[x as usize] = i as u8;
600 x ^= gf256_xtime(x); }
602 for i in 255..512 {
603 exp[i] = exp[i - 255];
604 }
605 (log, exp)
606 })
607}
608impl FieldElem for Gf256 {
609 fn zero() -> Self { Gf256(0) }
610 fn one() -> Self { Gf256(1) }
611 fn add(self, o: Self) -> Self { Gf256(self.0 ^ o.0) }
612 fn sub(self, o: Self) -> Self { Gf256(self.0 ^ o.0) }
613 fn mul(self, o: Self) -> Self {
614 if self.0 == 0 || o.0 == 0 {
615 return Gf256(0);
616 }
617 let (log, exp) = gf256_tables();
618 Gf256(exp[log[self.0 as usize] as usize + log[o.0 as usize] as usize])
619 }
620 fn inv(self) -> Self {
621 let (log, exp) = gf256_tables();
622 Gf256(exp[255 - log[self.0 as usize] as usize])
623 }
624}
625
626pub fn berlekamp_massey_field<F: FieldElem>(s: &[F]) -> (usize, Vec<F>) {
629 let n = s.len();
630 let mut c = vec![F::zero(); n + 1];
631 let mut b = vec![F::zero(); n + 1];
632 c[0] = F::one();
633 b[0] = F::one();
634 let mut l = 0usize;
635 let mut m = 1usize;
636 let mut b_disc = F::one(); for nn in 0..n {
638 let mut d = s[nn];
639 for i in 1..=l {
640 d = d.add(c[i].mul(s[nn - i]));
641 }
642 if d == F::zero() {
643 m += 1;
644 } else {
645 let coef = d.mul(b_disc.inv());
646 let t = c.clone();
647 for i in 0..=n {
648 if i + m <= n && b[i] != F::zero() {
649 c[i + m] = c[i + m].sub(coef.mul(b[i]));
650 }
651 }
652 if 2 * l <= nn {
653 l = nn + 1 - l;
654 b = t;
655 b_disc = d;
656 m = 1;
657 } else {
658 m += 1;
659 }
660 }
661 }
662 let taps = if l == 0 { Vec::new() } else { c[1..=l].to_vec() };
663 (l, taps)
664}
665
666pub fn lfsr_generate_field<F: FieldElem>(taps: &[F], seed: &[F], total: usize) -> Vec<F> {
668 let l = taps.len();
669 let mut out = Vec::with_capacity(total);
670 out.extend_from_slice(&seed[..l.min(seed.len()).min(total)]);
671 for i in l..total {
672 let mut acc = F::zero();
673 for j in 0..l {
674 acc = acc.add(taps[j].mul(out[i - 1 - j]));
675 }
676 out.push(F::zero().sub(acc));
677 }
678 out.truncate(total);
679 out
680}
681
682pub fn berlekamp_massey_gf2(s: &[bool]) -> (usize, Vec<bool>) {
685 let elems: Vec<Gf2> = s.iter().map(|&b| Gf2(b)).collect();
686 let (l, taps) = berlekamp_massey_field(&elems);
687 (l, taps.into_iter().map(|g| g.0).collect())
688}
689
690pub fn lfsr_generate(taps: &[bool], seed: &[bool], total: usize) -> Vec<bool> {
692 let t: Vec<Gf2> = taps.iter().map(|&b| Gf2(b)).collect();
693 let sd: Vec<Gf2> = seed.iter().map(|&b| Gf2(b)).collect();
694 lfsr_generate_field(&t, &sd, total).into_iter().map(|g| g.0).collect()
695}
696
697fn detect_lfsr_bytes(v: &[i64]) -> Option<(usize, Vec<bool>, Vec<bool>)> {
701 let bits = bytes_to_bits(v);
702 let (l, taps) = berlekamp_massey_gf2(&bits);
703 if l == 0 || l >= bits.len() {
704 return None;
705 }
706 let seed = bits[..l].to_vec();
707 if lfsr_generate(&taps, &seed, bits.len()) != bits {
708 return None; }
710 Some((l, taps, seed))
711}
712
713pub fn bytes_to_bits(v: &[i64]) -> Vec<bool> {
715 let mut bits = Vec::with_capacity(v.len().saturating_mul(8));
716 for &x in v {
717 let byte = x as u8;
718 for j in 0..8 {
719 bits.push((byte >> j) & 1 == 1);
720 }
721 }
722 bits
723}
724
725pub fn bits_to_bytes(bits: &[bool]) -> Vec<i64> {
728 bits.chunks(8)
729 .map(|chunk| {
730 let mut byte = 0u8;
731 for (j, &bit) in chunk.iter().enumerate() {
732 if bit {
733 byte |= 1 << j;
734 }
735 }
736 byte as i64
737 })
738 .collect()
739}
740
741fn bigint_gcd_local(a: &crate::numeric::BigInt, b: &crate::numeric::BigInt) -> crate::numeric::BigInt {
752 let (mut a, mut b) = (a.abs(), b.abs());
753 while !b.is_zero() {
754 let r = a.div_rem(&b).map(|(_, r)| r).unwrap_or_else(crate::numeric::BigInt::zero);
755 a = b;
756 b = r;
757 }
758 a
759}
760
761fn bigint_bitlen(x: &crate::numeric::BigInt) -> usize {
763 let (_, bytes) = x.to_le_bytes();
764 for (i, &b) in bytes.iter().enumerate().rev() {
765 if b != 0 {
766 return i * 8 + (8 - b.leading_zeros() as usize);
767 }
768 }
769 0
770}
771
772pub fn two_adic_reconstruct(bits: &[bool]) -> Option<(crate::numeric::BigInt, crate::numeric::BigInt)> {
776 use crate::numeric::BigInt;
777 let n = bits.len();
778 if n == 0 {
779 return None;
780 }
781 let two = BigInt::from_i64(2);
782 let mut alpha = BigInt::zero();
784 let mut pow2 = BigInt::from_i64(1);
785 for &bit in bits {
786 if bit {
787 alpha = alpha.add(&pow2);
788 }
789 pow2 = pow2.mul(&two);
790 }
791 let n_big = pow2; let (mut r0, mut t0) = (n_big.clone(), BigInt::zero());
794 let (mut r1, mut t1) = (alpha, BigInt::from_i64(1));
795 while !r1.is_zero() && r1.mul(&r1) > n_big {
796 let (quot, rem) = r0.div_rem(&r1)?;
797 let t2 = t0.sub(".mul(&t1));
798 r0 = r1;
799 t0 = t1;
800 r1 = rem;
801 t1 = t2;
802 }
803 let (mut p, mut q) = (r1, t1);
804 if q.is_zero() {
805 return None;
806 }
807 if q.is_negative() {
808 p = BigInt::zero().sub(&p);
809 q = BigInt::zero().sub(&q);
810 }
811 let g = bigint_gcd_local(&p, &q);
815 if !g.is_zero() && g != BigInt::from_i64(1) {
816 p = p.div_rem(&g).map(|(quot, _)| quot)?;
817 q = q.div_rem(&g).map(|(quot, _)| quot)?;
818 }
819 if !q.is_odd() {
820 return None; }
822 Some((p, q))
823}
824
825pub fn fcsr_generate(p: &crate::numeric::BigInt, q: &crate::numeric::BigInt, n: usize) -> Vec<bool> {
828 use crate::numeric::BigInt;
829 let two = BigInt::from_i64(2);
830 let mut r = p.clone();
831 let mut bits = Vec::with_capacity(n);
832 for _ in 0..n {
833 let bit = r.is_odd();
834 bits.push(bit);
835 if bit {
836 r = r.sub(q);
837 }
838 r = r.div_rem(&two).map(|(quot, _)| quot).unwrap_or_else(BigInt::zero);
840 }
841 bits
842}
843
844pub fn two_adic_complexity(bits: &[bool]) -> usize {
847 match two_adic_reconstruct(bits) {
848 Some((p, q)) => bigint_bitlen(&p).max(bigint_bitlen(&q)),
849 None => bits.len(),
850 }
851}
852
853pub fn maximal_order_complexity(bits: &[bool]) -> usize {
861 let n = bits.len();
862 if n == 0 {
863 return 0;
864 }
865 let consistent = |l: usize| -> bool {
867 if l >= n {
868 return true;
869 }
870 let mut succ: std::collections::HashMap<&[bool], bool> = std::collections::HashMap::new();
871 for i in 0..(n - l) {
872 match succ.insert(&bits[i..i + l], bits[i + l]) {
873 Some(prev) if prev != bits[i + l] => return false,
874 _ => {}
875 }
876 }
877 true
878 };
879 let (mut lo, mut hi) = (0usize, n);
881 while lo < hi {
882 let mid = (lo + hi) / 2;
883 if consistent(mid) {
884 hi = mid;
885 } else {
886 lo = mid + 1;
887 }
888 }
889 lo
890}
891
892fn monomials(l: usize, d: usize) -> Vec<Vec<usize>> {
909 fn combos(start: usize, l: usize, k: usize, cur: &mut Vec<usize>, out: &mut Vec<Vec<usize>>) {
910 if cur.len() == k {
911 out.push(cur.clone());
912 return;
913 }
914 for v in start..l {
915 cur.push(v);
916 combos(v + 1, l, k, cur, out);
917 cur.pop();
918 }
919 }
920 let mut out = vec![Vec::new()];
921 for k in 1..=d.min(l) {
922 combos(0, l, k, &mut Vec::new(), &mut out);
923 }
924 out
925}
926
927fn eval_monomial(mono: &[usize], window: &[bool]) -> bool {
929 mono.iter().all(|&v| window[v])
930}
931
932fn solve_gf2_system(rows: &[(Vec<u64>, bool)], ncols: usize) -> Option<Vec<bool>> {
937 let words = ncols.div_ceil(64).max(1);
938 let mut mat: Vec<(Vec<u64>, bool)> = rows.to_vec();
939 let mut pivot_row_of_col = vec![usize::MAX; ncols];
940 let mut r = 0usize;
941 for c in 0..ncols {
942 let (w, bit) = (c / 64, 1u64 << (c % 64));
943 let Some(pr) = (r..mat.len()).find(|&i| mat[i].0[w] & bit != 0) else {
944 continue;
945 };
946 mat.swap(r, pr);
947 for i in 0..mat.len() {
948 if i != r && mat[i].0[w] & bit != 0 {
949 for k in 0..words {
950 mat[i].0[k] ^= mat[r].0[k];
951 }
952 mat[i].1 ^= mat[r].1;
953 }
954 }
955 pivot_row_of_col[c] = r;
956 r += 1;
957 }
958 if mat.iter().any(|(coeff, rhs)| *rhs && coeff.iter().all(|&w| w == 0)) {
960 return None;
961 }
962 let mut c = vec![false; ncols];
965 for col in 0..ncols {
966 if pivot_row_of_col[col] != usize::MAX {
967 c[col] = mat[pivot_row_of_col[col]].1;
968 }
969 }
970 Some(c)
971}
972
973pub fn detect_algebraic_recurrence(bits: &[bool], l: usize, d: usize) -> Option<Vec<bool>> {
980 if l == 0 || bits.len() <= l {
981 return None;
982 }
983 let monos = monomials(l, d);
984 let m = monos.len();
985 let words = m.div_ceil(64).max(1);
986 let mut rows: Vec<(Vec<u64>, bool)> = Vec::with_capacity(bits.len() - l);
987 for i in l..bits.len() {
988 let window: Vec<bool> = (0..l).map(|v| bits[i - 1 - v]).collect();
990 let mut coeff = vec![0u64; words];
991 for (mi, mono) in monos.iter().enumerate() {
992 if eval_monomial(mono, &window) {
993 coeff[mi / 64] |= 1u64 << (mi % 64);
994 }
995 }
996 rows.push((coeff, bits[i]));
997 }
998 let coeffs = solve_gf2_system(&rows, m)?;
999 if algebraic_generate(l, d, &coeffs, &bits[..l], bits.len()) != bits {
1000 return None; }
1002 Some(coeffs)
1003}
1004
1005pub fn algebraic_generate(l: usize, d: usize, coeffs: &[bool], seed: &[bool], total: usize) -> Vec<bool> {
1009 let monos = monomials(l, d);
1010 let mut out: Vec<bool> = seed.iter().take(l).copied().collect();
1011 for i in l..total {
1012 let window: Vec<bool> = (0..l).map(|v| out[i - 1 - v]).collect();
1013 let mut bit = false;
1014 for (mi, mono) in monos.iter().enumerate() {
1015 if coeffs.get(mi).copied().unwrap_or(false) && eval_monomial(mono, &window) {
1016 bit ^= true;
1017 }
1018 }
1019 out.push(bit);
1020 }
1021 out.truncate(total);
1022 out
1023}
1024
1025pub fn algebraic_complexity(bits: &[bool], max_degree: usize) -> Option<(usize, usize)> {
1030 for l in 1..=(bits.len() / 2) {
1031 if let Some(coeffs) = detect_algebraic_recurrence(bits, l, max_degree) {
1032 let used = coeffs.iter().filter(|&&c| c).count();
1033 return Some((l, used.max(1)));
1034 }
1035 }
1036 None
1037}
1038
1039#[derive(Clone, Debug, PartialEq)]
1054pub struct CorrelationAttack {
1055 pub register_len: usize,
1057 pub init_state: Vec<bool>,
1059 pub agreement: f64,
1061 pub bias: f64,
1063 pub samples: usize,
1065}
1066
1067pub fn correlation_attack(keystream: &[bool], taps: &[bool]) -> Option<CorrelationAttack> {
1074 let l = taps.len();
1075 let n = keystream.len();
1076 if l == 0 || l > 20 || n <= l {
1077 return None;
1078 }
1079 let mut best: Option<CorrelationAttack> = None;
1080 for code in 1u64..(1u64 << l) {
1081 let seed: Vec<bool> = (0..l).map(|k| (code >> k) & 1 == 1).collect();
1082 let stream = lfsr_generate(taps, &seed, n);
1083 let agree = stream.iter().zip(keystream).filter(|(a, b)| a == b).count();
1084 let bias = (agree as f64 / n as f64 - 0.5).abs();
1085 if best.as_ref().is_none_or(|b| bias > b.bias) {
1086 best = Some(CorrelationAttack {
1087 register_len: l,
1088 init_state: seed,
1089 agreement: agree as f64 / n as f64,
1090 bias,
1091 samples: n,
1092 });
1093 }
1094 }
1095 best
1096}
1097
1098pub fn spurious_bias_floor(register_len: usize, samples: usize) -> f64 {
1103 if samples == 0 {
1104 return 1.0;
1105 }
1106 ((register_len as f64) * std::f64::consts::LN_2 / (2.0 * samples as f64)).sqrt()
1107}
1108
1109pub fn fast_walsh_hadamard(a: &mut [i64]) {
1126 let n = a.len();
1127 debug_assert!(n.is_power_of_two(), "Walsh–Hadamard length must be a power of two");
1128 let mut len = 1;
1129 while len < n {
1130 let mut i = 0;
1131 while i < n {
1132 for j in i..i + len {
1133 let (x, y) = (a[j], a[j + len]);
1134 a[j] = x + y;
1135 a[j + len] = x - y;
1136 }
1137 i += 2 * len;
1138 }
1139 len <<= 1;
1140 }
1141}
1142
1143pub fn walsh_spectrum(truth: &[bool]) -> Option<Vec<i64>> {
1147 if truth.is_empty() || !truth.len().is_power_of_two() {
1148 return None;
1149 }
1150 let mut a: Vec<i64> = truth.iter().map(|&b| if b { -1 } else { 1 }).collect();
1151 fast_walsh_hadamard(&mut a);
1152 Some(a)
1153}
1154
1155pub fn best_linear_approximation(truth: &[bool], skip_zero: bool) -> Option<(usize, f64)> {
1159 let spec = walsh_spectrum(truth)?;
1160 let denom = 2.0 * truth.len() as f64; spec.iter()
1162 .enumerate()
1163 .skip(usize::from(skip_zero))
1164 .max_by_key(|(_, &c)| c.unsigned_abs())
1165 .map(|(w, &c)| (w, c.unsigned_abs() as f64 / denom))
1166}
1167
1168pub fn nonlinearity(truth: &[bool]) -> Option<u64> {
1172 let spec = walsh_spectrum(truth)?;
1173 let max_abs = spec.iter().map(|&c| c.unsigned_abs()).max()?;
1174 Some((truth.len() as u64) / 2 - max_abs / 2)
1175}
1176
1177pub fn correlation_immunity_order(truth: &[bool]) -> Option<usize> {
1182 let spec = walsh_spectrum(truth)?;
1183 let n = truth.len().trailing_zeros() as usize;
1184 for m in 1..=n {
1185 let vanishes = spec
1186 .iter()
1187 .enumerate()
1188 .filter(|(w, _)| w.count_ones() as usize == m)
1189 .all(|(_, &c)| c == 0);
1190 if !vanishes {
1191 return Some(m - 1);
1192 }
1193 }
1194 Some(n)
1195}
1196
1197pub fn anf(truth: &[bool]) -> Option<Vec<bool>> {
1202 if truth.is_empty() || !truth.len().is_power_of_two() {
1203 return None;
1204 }
1205 let n = truth.len().trailing_zeros();
1206 let mut a = truth.to_vec();
1207 for i in 0..n {
1208 let step = 1usize << i;
1209 let mut j = 0;
1210 while j < a.len() {
1211 for k in j..j + step {
1212 let lo = a[k];
1213 a[k + step] ^= lo;
1214 }
1215 j += step << 1;
1216 }
1217 }
1218 Some(a)
1219}
1220
1221pub fn algebraic_degree(truth: &[bool]) -> Option<usize> {
1224 let a = anf(truth)?;
1225 Some(a.iter().enumerate().filter(|(_, &c)| c).map(|(m, _)| (m as u64).count_ones() as usize).max().unwrap_or(0))
1226}
1227
1228pub fn autocorrelation(truth: &[bool]) -> Option<Vec<i64>> {
1234 let mut w = walsh_spectrum(truth)?;
1235 for c in w.iter_mut() {
1236 *c *= *c;
1237 }
1238 fast_walsh_hadamard(&mut w);
1239 let scale = truth.len() as i64;
1240 for c in w.iter_mut() {
1241 *c /= scale;
1242 }
1243 Some(w)
1244}
1245
1246#[derive(Clone, Debug, PartialEq, Eq)]
1264pub struct AnnihilatorWitness {
1265 pub n_vars: usize,
1266 pub degree: usize,
1267 pub coeffs: Vec<bool>,
1268 pub annihilates_complement: bool,
1269}
1270
1271fn gf2_kernel_basis(rows: &[Vec<u64>], ncols: usize) -> Vec<Vec<bool>> {
1275 let words = ncols.div_ceil(64).max(1);
1276 let mut mat: Vec<Vec<u64>> = rows.to_vec();
1277 let mut pivot_row_of_col = vec![usize::MAX; ncols];
1278 let mut pivot_cols: Vec<usize> = Vec::new();
1279 let mut r = 0usize;
1280 for c in 0..ncols {
1281 let (w, bit) = (c / 64, 1u64 << (c % 64));
1282 let Some(pr) = (r..mat.len()).find(|&i| mat[i][w] & bit != 0) else {
1283 continue;
1284 };
1285 mat.swap(r, pr);
1286 for i in 0..mat.len() {
1287 if i != r && mat[i][w] & bit != 0 {
1288 for k in 0..words {
1289 mat[i][k] ^= mat[r][k];
1290 }
1291 }
1292 }
1293 pivot_row_of_col[c] = r;
1294 pivot_cols.push(c);
1295 r += 1;
1296 }
1297 (0..ncols)
1300 .filter(|&c| pivot_row_of_col[c] == usize::MAX)
1301 .map(|free| {
1302 let mut c = vec![false; ncols];
1303 c[free] = true;
1304 for &pc in &pivot_cols {
1305 let row = &mat[pivot_row_of_col[pc]];
1306 if (row[free / 64] >> (free % 64)) & 1 == 1 {
1307 c[pc] = true;
1308 }
1309 }
1310 c
1311 })
1312 .collect()
1313}
1314
1315fn annihilator_basis(n: usize, d: usize, zero_set: &[usize]) -> Vec<Vec<bool>> {
1319 let monos = monomials(n, d);
1320 let m = monos.len();
1321 let words = m.div_ceil(64).max(1);
1322 let rows: Vec<Vec<u64>> = zero_set
1323 .iter()
1324 .map(|&x| {
1325 let window: Vec<bool> = (0..n).map(|i| (x >> i) & 1 == 1).collect();
1326 let mut coeff = vec![0u64; words];
1327 for (mi, mono) in monos.iter().enumerate() {
1328 if eval_monomial(mono, &window) {
1329 coeff[mi / 64] |= 1u64 << (mi % 64);
1330 }
1331 }
1332 coeff
1333 })
1334 .collect();
1335 gf2_kernel_basis(&rows, m)
1336}
1337
1338fn annihilator(n: usize, d: usize, zero_set: &[usize]) -> Option<Vec<bool>> {
1340 annihilator_basis(n, d, zero_set).into_iter().next()
1341}
1342
1343pub fn algebraic_immunity(truth: &[bool]) -> Option<(usize, AnnihilatorWitness)> {
1348 if truth.is_empty() || !truth.len().is_power_of_two() {
1349 return None;
1350 }
1351 let n = truth.len().trailing_zeros() as usize;
1352 let support: Vec<usize> = (0..truth.len()).filter(|&x| truth[x]).collect();
1353 let zeros: Vec<usize> = (0..truth.len()).filter(|&x| !truth[x]).collect();
1354 for d in 0..=n {
1355 if let Some(g) = annihilator(n, d, &support) {
1356 return Some((d, AnnihilatorWitness { n_vars: n, degree: d, coeffs: g, annihilates_complement: false }));
1357 }
1358 if let Some(g) = annihilator(n, d, &zeros) {
1359 return Some((d, AnnihilatorWitness { n_vars: n, degree: d, coeffs: g, annihilates_complement: true }));
1360 }
1361 }
1362 None
1363}
1364
1365fn eval_anf(coeffs: &[bool], n_vars: usize, degree: usize, x: usize) -> bool {
1367 let window: Vec<bool> = (0..n_vars).map(|i| (x >> i) & 1 == 1).collect();
1368 monomials(n_vars, degree)
1369 .iter()
1370 .enumerate()
1371 .filter(|(mi, _)| coeffs.get(*mi).copied().unwrap_or(false))
1372 .fold(false, |acc, (_, mono)| acc ^ eval_monomial(mono, &window))
1373}
1374
1375pub fn verify_annihilator(truth: &[bool], w: &AnnihilatorWitness) -> bool {
1379 if w.coeffs.iter().all(|&c| !c) || monomials(w.n_vars, w.degree).len() != w.coeffs.len() {
1380 return false;
1381 }
1382 (0..truth.len()).all(|x| {
1383 let must_vanish = if w.annihilates_complement { !truth[x] } else { truth[x] };
1384 !must_vanish || !eval_anf(&w.coeffs, w.n_vars, w.degree, x)
1385 })
1386}
1387
1388pub fn algebraic_filter_attack(keystream: &[bool], taps: &[bool], filter_truth: &[bool]) -> Option<Vec<bool>> {
1396 let l = taps.len();
1397 let n = keystream.len();
1398 if l == 0 || l > 64 || !filter_truth.len().is_power_of_two() {
1399 return None;
1400 }
1401 let m = filter_truth.len().trailing_zeros() as usize;
1402 if m == 0 || n < l + m {
1403 return None;
1404 }
1405 let (ai, _) = algebraic_immunity(filter_truth)?;
1406 if ai == 0 {
1407 return None; }
1409 let g_monos = monomials(m, ai);
1410 let support: Vec<usize> = (0..filter_truth.len()).filter(|&x| filter_truth[x]).collect();
1414 let zeros: Vec<usize> = (0..filter_truth.len()).filter(|&x| !filter_truth[x]).collect();
1415 let ann_when_one = annihilator_basis(m, ai, &support);
1416 let ann_when_zero = annihilator_basis(m, ai, &zeros);
1417
1418 let need = n + m;
1420 let mut r: Vec<u64> = Vec::with_capacity(need);
1421 for k in 0..need {
1422 if k < l {
1423 r.push(1u64 << k);
1424 } else {
1425 let mut acc = 0u64;
1426 for (j, &t) in taps.iter().enumerate() {
1427 if t {
1428 acc ^= r[k - 1 - j];
1429 }
1430 }
1431 r.push(acc);
1432 }
1433 }
1434
1435 let s_monos = monomials(l, ai);
1437 let ncols = s_monos.len();
1438 let col_of: std::collections::HashMap<u64, usize> = s_monos
1439 .iter()
1440 .enumerate()
1441 .map(|(i, mono)| (mono.iter().fold(0u64, |b, &v| b | (1u64 << v)), i))
1442 .collect();
1443 let words = ncols.div_ceil(64).max(1);
1444
1445 let expand = |g: &[bool], t: usize| -> Option<(Vec<u64>, bool)> {
1448 let mut acc: std::collections::HashSet<u64> = std::collections::HashSet::new();
1449 for (gi, mono) in g_monos.iter().enumerate() {
1450 if !g[gi] {
1451 continue;
1452 }
1453 let mut poly: std::collections::HashSet<u64> = std::collections::HashSet::from([0u64]);
1454 for &i in mono {
1455 let form = r[t + i];
1456 let mut next: std::collections::HashSet<u64> = std::collections::HashSet::new();
1457 for &pm in &poly {
1458 let mut bits = form;
1459 while bits != 0 {
1460 let v = bits.trailing_zeros();
1461 bits &= bits - 1;
1462 let term = pm | (1u64 << v);
1463 if !next.insert(term) {
1464 next.remove(&term); }
1466 }
1467 }
1468 poly = next;
1469 }
1470 for pm in poly {
1471 if !acc.insert(pm) {
1472 acc.remove(&pm);
1473 }
1474 }
1475 }
1476 let mut coeff = vec![0u64; words];
1477 let mut rhs = false;
1478 for mask in acc {
1479 if mask == 0 {
1480 rhs = true;
1481 } else {
1482 coeff[*col_of.get(&mask)? / 64] |= 1u64 << (col_of[&mask] % 64);
1483 }
1484 }
1485 Some((coeff, rhs))
1486 };
1487
1488 let mut rows: Vec<(Vec<u64>, bool)> = Vec::new();
1489 let mut pin = vec![0u64; words];
1490 pin[0] = 1;
1491 rows.push((pin, true)); for t in 0..n.saturating_sub(m - 1) {
1494 let anns = if keystream[t] { &ann_when_one } else { &ann_when_zero };
1495 for g in anns {
1496 rows.push(expand(g, t)?);
1497 }
1498 }
1499
1500 let sol = solve_gf2_system(&rows, ncols)?;
1501 let state: Vec<bool> = (0..l)
1503 .map(|i| col_of.get(&(1u64 << i)).map(|&ci| sol[ci]).unwrap_or(false))
1504 .collect();
1505
1506 let seq = lfsr_generate(taps, &state, need);
1508 let regen: Vec<bool> = (0..n)
1509 .map(|t| {
1510 let idx = (0..m).fold(0usize, |a, i| a | (usize::from(seq[t + i]) << i));
1511 filter_truth[idx]
1512 })
1513 .collect();
1514 (regen == keystream).then_some(state)
1515}
1516
1517pub fn fast_correlation_attack(keystream: &[bool], taps: &[bool], max_iters: usize) -> Option<Vec<bool>> {
1539 let (n, l) = (keystream.len(), taps.len());
1540 if l == 0 || n <= 2 * l {
1541 return None;
1542 }
1543 let mut base: Vec<usize> = vec![0];
1545 for (j, &t) in taps.iter().enumerate() {
1546 if t {
1547 base.push(1 + j);
1548 }
1549 }
1550 let mut checks: Vec<Vec<usize>> = Vec::new();
1553 let mut scale = 1usize;
1554 for _ in 0..4 {
1555 let span = base.iter().map(|&o| o * scale).max().unwrap_or(0);
1556 if span >= n {
1557 break;
1558 }
1559 for i in span..n {
1560 checks.push(base.iter().map(|&o| i - o * scale).collect());
1561 }
1562 scale *= 2;
1563 }
1564 if checks.is_empty() {
1565 return None;
1566 }
1567 let mut per_bit = vec![0usize; n];
1568 for c in &checks {
1569 for &p in c {
1570 per_bit[p] += 1;
1571 }
1572 }
1573
1574 let mut y = keystream.to_vec();
1575 for _ in 0..max_iters {
1576 let mut votes = vec![0usize; n];
1577 let mut all_satisfied = true;
1578 for c in &checks {
1579 if c.iter().fold(false, |a, &p| a ^ y[p]) {
1580 all_satisfied = false;
1581 for &p in c {
1582 votes[p] += 1;
1583 }
1584 }
1585 }
1586 if all_satisfied {
1587 break;
1588 }
1589 let mut flipped = false;
1590 for i in 0..n {
1591 if per_bit[i] > 0 && votes[i] * 2 > per_bit[i] {
1592 y[i] ^= true;
1593 flipped = true;
1594 }
1595 }
1596 if !flipped {
1597 break; }
1599 }
1600
1601 let state = y[..l].to_vec();
1603 let regen = lfsr_generate(taps, &state, n);
1604 if regen != y {
1605 return None;
1606 }
1607 let agree = regen.iter().zip(keystream).filter(|(a, b)| a == b).count() as f64 / n as f64;
1608 (agree > 0.6).then_some(state)
1609}
1610
1611pub fn shrinking_generator(
1628 a_taps: &[bool],
1629 a_seed: &[bool],
1630 s_taps: &[bool],
1631 s_seed: &[bool],
1632 out_len: usize,
1633) -> Vec<bool> {
1634 let clocks = out_len.saturating_mul(4) + 64; let a = lfsr_generate(a_taps, a_seed, clocks);
1636 let s = lfsr_generate(s_taps, s_seed, clocks);
1637 let mut out = Vec::with_capacity(out_len);
1638 for i in 0..clocks {
1639 if a[i] {
1640 out.push(s[i]);
1641 if out.len() == out_len {
1642 break;
1643 }
1644 }
1645 }
1646 out
1647}
1648
1649pub fn attack_shrinking_generator(
1657 output: &[bool],
1658 a_taps: &[bool],
1659 s_taps: &[bool],
1660) -> Option<(Vec<bool>, Vec<bool>)> {
1661 let (la, ls) = (a_taps.len(), s_taps.len());
1662 let m = output.len();
1663 if la == 0 || la > 22 || ls == 0 || ls > 64 || m < ls {
1664 return None;
1665 }
1666 let clocks = m.saturating_mul(4) + 64;
1667 let mut r: Vec<u64> = Vec::with_capacity(clocks);
1669 for k in 0..clocks {
1670 if k < ls {
1671 r.push(1u64 << k);
1672 } else {
1673 let mut acc = 0u64;
1674 for (j, &t) in s_taps.iter().enumerate() {
1675 if t {
1676 acc ^= r[k - 1 - j];
1677 }
1678 }
1679 r.push(acc);
1680 }
1681 }
1682 let words = ls.div_ceil(64).max(1);
1683 for code in 1u64..(1u64 << la) {
1684 let a_seed: Vec<bool> = (0..la).map(|k| (code >> k) & 1 == 1).collect();
1685 let a = lfsr_generate(a_taps, &a_seed, clocks);
1686 let mut positions = Vec::with_capacity(m);
1687 for (i, &bit) in a.iter().enumerate() {
1688 if bit {
1689 positions.push(i);
1690 if positions.len() == m {
1691 break;
1692 }
1693 }
1694 }
1695 if positions.len() < m {
1696 continue; }
1698 let rows: Vec<(Vec<u64>, bool)> = positions
1699 .iter()
1700 .enumerate()
1701 .map(|(k, &pos)| {
1702 let mut coeff = vec![0u64; words];
1703 coeff[0] = r[pos];
1704 (coeff, output[k])
1705 })
1706 .collect();
1707 if let Some(sol) = solve_gf2_system(&rows, ls) {
1708 let s_seed: Vec<bool> = sol[..ls].to_vec();
1709 if shrinking_generator(a_taps, &a_seed, s_taps, &s_seed, m) == output {
1710 return Some((a_seed, s_seed));
1711 }
1712 }
1713 }
1714 None
1715}
1716
1717#[derive(Clone, Debug, PartialEq, Eq)]
1732pub enum PolySolveResult {
1733 Solved(Vec<bool>),
1735 Refuted,
1737 Undetermined,
1739}
1740
1741fn monomial_masks(n: usize, deg: usize) -> Vec<u64> {
1743 monomials(n, deg).iter().map(|mono| mono.iter().fold(0u64, |b, &v| b | (1u64 << v))).collect()
1744}
1745
1746fn eval_mask(mask: u64, x: &[bool]) -> bool {
1748 let mut m = mask;
1749 let mut v = true;
1750 while m != 0 {
1751 v &= x[m.trailing_zeros() as usize];
1752 m &= m - 1;
1753 }
1754 v
1755}
1756
1757fn verify_poly_system(eqs: &[Vec<u64>], x: &[bool]) -> bool {
1759 eqs.iter().all(|eq| !eq.iter().fold(false, |a, &m| a ^ eval_mask(m, x)))
1760}
1761
1762pub fn solve_polynomial_system_gf2(eqs: &[Vec<u64>], n_vars: usize, max_degree: usize) -> PolySolveResult {
1774 for d in 1..=max_degree {
1775 let cols = monomial_masks(n_vars, d);
1776 let col_of: std::collections::HashMap<u64, usize> =
1777 cols.iter().enumerate().map(|(i, &m)| (m, i)).collect();
1778 let ncols = cols.len();
1779 let words = ncols.div_ceil(64).max(1);
1780
1781 let mut rows: Vec<(Vec<u64>, bool)> = Vec::new();
1782 let mut pin = vec![0u64; words];
1784 let c0 = col_of[&0];
1785 pin[c0 / 64] |= 1u64 << (c0 % 64);
1786 rows.push((pin, true));
1787
1788 for eq in eqs {
1789 let deg_eq = eq.iter().map(|m| m.count_ones() as usize).max().unwrap_or(0);
1790 if deg_eq > d {
1791 continue; }
1793 for mu in monomial_masks(n_vars, d - deg_eq) {
1794 let mut acc: std::collections::HashSet<u64> = std::collections::HashSet::new();
1796 for &m in eq {
1797 let prod = m | mu;
1798 if !acc.insert(prod) {
1799 acc.remove(&prod); }
1801 }
1802 if acc.is_empty() {
1803 continue;
1804 }
1805 let mut coeff = vec![0u64; words];
1806 let mut rhs = false;
1807 for mask in acc {
1808 if mask == 0 {
1809 rhs = true;
1810 } else if let Some(&ci) = col_of.get(&mask) {
1811 coeff[ci / 64] |= 1u64 << (ci % 64);
1812 }
1813 }
1814 rows.push((coeff, rhs));
1815 }
1816 }
1817
1818 match solve_gf2_system(&rows, ncols) {
1819 None => return PolySolveResult::Refuted,
1820 Some(sol) => {
1821 let x: Vec<bool> = (0..n_vars)
1822 .map(|i| col_of.get(&(1u64 << i)).map(|&ci| sol[ci]).unwrap_or(false))
1823 .collect();
1824 if verify_poly_system(eqs, &x) {
1825 return PolySolveResult::Solved(x);
1826 }
1827 }
1828 }
1829 }
1830 PolySolveResult::Undetermined
1831}
1832
1833fn write_bigint(x: &crate::numeric::BigInt, out: &mut Vec<u8>) {
1836 let (neg, mut bytes) = x.to_le_bytes();
1837 while bytes.last() == Some(&0) {
1838 bytes.pop();
1839 }
1840 out.push(neg as u8);
1841 write_uvarint(bytes.len() as u64, out);
1842 out.extend_from_slice(&bytes);
1843}
1844
1845fn read_bigint(buf: &[u8], pos: &mut usize) -> Option<crate::numeric::BigInt> {
1847 let neg = *buf.get(*pos)? != 0;
1848 *pos += 1;
1849 let len = read_uvarint(buf, pos)? as usize;
1850 let bytes = buf.get(*pos..pos.checked_add(len)?)?;
1851 *pos += len;
1852 Some(crate::numeric::BigInt::from_le_bytes(neg, bytes))
1853}
1854
1855fn detect_fcsr_bytes(v: &[i64]) -> Option<(crate::numeric::BigInt, crate::numeric::BigInt)> {
1859 let bits = bytes_to_bits(v);
1860 let (p, q) = two_adic_reconstruct(&bits)?;
1861 if fcsr_generate(&p, &q, bits.len()) != bits {
1862 return None;
1863 }
1864 Some((p, q))
1865}
1866
1867pub fn detect_modular_affine(v: &[i64]) -> Option<GenExpr> {
1869 const MAX_PERIOD: usize = 16;
1870 if v.len() < 4 {
1871 return None;
1872 }
1873 for p in 2..=MAX_PERIOD.min(v.len() / 2) {
1874 let a = v[0];
1875 let b = v[1].wrapping_sub(v[0]);
1876 if b != 0 && (0..v.len()).all(|i| v[i] == a.wrapping_add(b.wrapping_mul((i % p) as i64))) {
1877 return Some(GenExpr::Add(
1878 Box::new(GenExpr::Const(a)),
1879 Box::new(GenExpr::Mul(
1880 Box::new(GenExpr::Const(b)),
1881 Box::new(GenExpr::Mod(Box::new(GenExpr::Index), Box::new(GenExpr::Const(p as i64)))),
1882 )),
1883 ));
1884 }
1885 }
1886 None
1887}
1888
1889pub fn delta_encode(out: &mut Vec<u8>, v: &[i64]) {
1893 out.push(T_INTS_DELTA);
1894 write_uvarint(v.len() as u64, out);
1895 if let Some(&first) = v.first() {
1896 write_uvarint(zigzag(first), out);
1897 let mut prev = first;
1898 for &x in &v[1..] {
1899 write_uvarint(zigzag(x.wrapping_sub(prev)), out);
1900 prev = x;
1901 }
1902 }
1903}
1904
1905pub fn dod_encode(out: &mut Vec<u8>, v: &[i64]) {
1908 out.push(T_INTS_DOD);
1909 write_uvarint(v.len() as u64, out);
1910 if v.is_empty() {
1911 return;
1912 }
1913 write_uvarint(zigzag(v[0]), out);
1914 if v.len() == 1 {
1915 return;
1916 }
1917 let mut prev_delta = v[1].wrapping_sub(v[0]);
1918 write_uvarint(zigzag(prev_delta), out);
1919 let mut prev = v[1];
1920 for &x in &v[2..] {
1921 let d = x.wrapping_sub(prev);
1922 write_uvarint(zigzag(d.wrapping_sub(prev_delta)), out);
1923 prev_delta = d;
1924 prev = x;
1925 }
1926}
1927
1928pub fn for_encode(out: &mut Vec<u8>, v: &[i64]) {
1931 out.push(T_INTS_FOR);
1932 write_uvarint(v.len() as u64, out);
1933 let min = v.iter().copied().min().unwrap_or(0);
1934 write_uvarint(zigzag(min), out);
1935 if v.is_empty() {
1936 out.push(0);
1937 return;
1938 }
1939 let max = v.iter().copied().max().unwrap();
1940 let range = (max as u64).wrapping_sub(min as u64);
1941 let width = if range == 0 { 0 } else { (64 - range.leading_zeros()) as u8 };
1942 out.push(width);
1943 if width > 0 {
1944 let residuals: Vec<u64> = v.iter().map(|&x| (x as u64).wrapping_sub(min as u64)).collect();
1945 out.extend_from_slice(&bitpack(&residuals, width));
1946 }
1947}
1948
1949pub fn rle_encode(out: &mut Vec<u8>, v: &[i64]) {
1951 let mut runs: Vec<(i64, u64)> = Vec::new();
1952 for &x in v {
1953 match runs.last_mut() {
1954 Some(last) if last.0 == x => last.1 += 1,
1955 _ => runs.push((x, 1)),
1956 }
1957 }
1958 out.push(T_INTS_RLE);
1959 write_uvarint(runs.len() as u64, out);
1960 for (val, len) in runs {
1961 write_uvarint(zigzag(val), out);
1962 write_uvarint(len, out);
1963 }
1964}
1965
1966pub fn dict_encode(v: &[i64]) -> Vec<u8> {
1969 let mut dict: Vec<i64> = Vec::new();
1970 let mut index_of: std::collections::HashMap<i64, u64> = std::collections::HashMap::new();
1971 let mut indices: Vec<u64> = Vec::with_capacity(v.len());
1972 for &x in v {
1973 let idx = *index_of.entry(x).or_insert_with(|| {
1974 dict.push(x);
1975 (dict.len() - 1) as u64
1976 });
1977 indices.push(idx);
1978 }
1979 let mut out = vec![T_INTS_DICT];
1980 write_uvarint(dict.len() as u64, &mut out);
1981 for &d in &dict {
1982 write_uvarint(zigzag(d), &mut out);
1983 }
1984 write_uvarint(v.len() as u64, &mut out);
1985 let iw = if dict.len() <= 1 { 0 } else { (64 - ((dict.len() - 1) as u64).leading_zeros()) as u8 };
1986 out.push(iw);
1987 if iw > 0 {
1988 out.extend_from_slice(&bitpack(&indices, iw));
1989 }
1990 out
1991}
1992
1993pub fn consider(best: &mut Vec<u8>, cand: Vec<u8>) {
1995 if cand.len() < best.len() {
1996 *best = cand;
1997 }
1998}
1999
2000pub fn emit_best_int_column(v: &[i64], out: &mut Vec<u8>) {
2005 let mut best = Vec::new();
2006 best.push(T_INTS);
2007 leb128_encode(&mut best, v.iter().copied(), v.len());
2008
2009 if let Some((base, stride)) = detect_affine(v) {
2010 let mut c = vec![T_INTS_AFFINE];
2011 write_uvarint(zigzag(base), &mut c);
2012 write_uvarint(zigzag(stride), &mut c);
2013 write_uvarint(v.len() as u64, &mut c);
2014 consider(&mut best, c);
2015 }
2016 if let Some((base, ratio)) = detect_geometric(v) {
2017 let mut c = vec![T_INTS_GEOMETRIC];
2018 write_uvarint(zigzag(base), &mut c);
2019 write_uvarint(zigzag(ratio), &mut c);
2020 write_uvarint(v.len() as u64, &mut c);
2021 consider(&mut best, c);
2022 }
2023 if let Some(p) = detect_period(v) {
2024 let mut c = vec![T_INTS_PERIODIC];
2025 write_uvarint(v.len() as u64, &mut c);
2026 emit_best_int_column(&v[..p], &mut c);
2027 consider(&mut best, c);
2028 }
2029 if let Some((dom, exc)) = detect_sparse(v) {
2030 let mut c = vec![T_INTS_SPARSE];
2031 write_uvarint(zigzag(dom), &mut c);
2032 write_uvarint(v.len() as u64, &mut c);
2033 write_uvarint(exc.len() as u64, &mut c);
2034 let mut prev = 0usize;
2035 for (i, x) in &exc {
2036 write_uvarint((i - prev) as u64, &mut c);
2037 prev = *i;
2038 write_uvarint(zigzag(*x), &mut c);
2039 }
2040 consider(&mut best, c);
2041 }
2042 if let Some((degree, seeds)) = detect_poly_generator(v) {
2043 let mut c = vec![T_INTS_POLY, degree];
2044 write_uvarint(v.len() as u64, &mut c);
2045 for &s in &seeds {
2046 write_uvarint(zigzag(s), &mut c);
2047 }
2048 consider(&mut best, c);
2049 }
2050 if let Some((coeffs, seeds)) = detect_linear_recurrence(v) {
2054 let mut c = vec![T_INTS_LRECUR, coeffs.len() as u8];
2055 write_uvarint(v.len() as u64, &mut c);
2056 for &x in &coeffs {
2057 write_uvarint(zigzag(x), &mut c);
2058 }
2059 for &s in &seeds {
2060 write_uvarint(zigzag(s), &mut c);
2061 }
2062 consider(&mut best, c);
2063 }
2064 if let Some(expr) = detect_modular_affine(v) {
2065 let mut c = vec![T_GEN];
2066 serialize_gen(&expr, &mut c);
2067 write_uvarint(v.len() as u64, &mut c);
2068 consider(&mut best, c);
2069 }
2070 let mut delta = Vec::new();
2071 delta_encode(&mut delta, v);
2072 consider(&mut best, delta);
2073
2074 let mut dod = Vec::new();
2075 dod_encode(&mut dod, v);
2076 consider(&mut best, dod);
2077
2078 if !v.is_empty() && v.iter().all(|&x| (0..256).contains(&x)) {
2079 let mut b = vec![T_BYTES];
2080 write_uvarint(v.len() as u64, &mut b);
2081 b.extend(v.iter().map(|&x| x as u8));
2082 consider(&mut best, b);
2083 }
2084
2085 let mut for_c = Vec::new();
2086 for_encode(&mut for_c, v);
2087 consider(&mut best, for_c);
2088
2089 let mut rle = Vec::new();
2090 rle_encode(&mut rle, v);
2091 consider(&mut best, rle);
2092
2093 consider(&mut best, dict_encode(v));
2094
2095 if !v.is_empty()
2100 && v.len() <= LFSR_MAX_BYTES
2101 && best.len() >= v.len()
2102 && v.iter().all(|&x| (0..256).contains(&x))
2103 {
2104 if let Some((l, taps, seed)) = detect_lfsr_bytes(v) {
2105 let mut c = vec![T_INTS_LFSR];
2106 write_uvarint(v.len() as u64, &mut c);
2107 write_uvarint(l as u64, &mut c);
2108 let tap_vals: Vec<u64> = taps.iter().map(|&b| b as u64).collect();
2109 c.extend_from_slice(&bitpack(&tap_vals, 1));
2110 let seed_vals: Vec<u64> = seed.iter().map(|&b| b as u64).collect();
2111 c.extend_from_slice(&bitpack(&seed_vals, 1));
2112 consider(&mut best, c);
2113 }
2114 }
2115
2116 if !v.is_empty()
2121 && v.len() <= FCSR_MAX_BYTES
2122 && best.len() >= v.len()
2123 && v.iter().all(|&x| (0..256).contains(&x))
2124 {
2125 if let Some((p, q)) = detect_fcsr_bytes(v) {
2126 let mut c = vec![T_INTS_FCSR];
2127 write_uvarint(v.len() as u64, &mut c);
2128 write_bigint(&p, &mut c);
2129 write_bigint(&q, &mut c);
2130 consider(&mut best, c);
2131 }
2132 }
2133
2134 out.extend_from_slice(&best);
2135}
2136
2137pub fn describe_int_seq(v: &[i64]) -> Vec<u8> {
2141 let mut out = Vec::new();
2142 emit_best_int_column(v, &mut out);
2143 out
2144}
2145
2146#[inline]
2151fn bounded(n: u64, max_elements: usize) -> Option<usize> {
2152 let n = n as usize;
2153 (n <= max_elements).then_some(n)
2154}
2155
2156pub fn decode_int_column_body(
2160 tag: u8,
2161 buf: &[u8],
2162 pos: &mut usize,
2163 max_elements: usize,
2164 depth: u32,
2165) -> Option<Vec<i64>> {
2166 Some(match tag {
2167 T_INTS => {
2169 let header = read_uvarint(buf, pos)?;
2170 let signed = header & 1 == 1;
2171 let n = bounded(header >> 1, max_elements)?;
2172 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2173 for _ in 0..n {
2174 let u = read_uvarint(buf, pos)?;
2175 v.push(if signed { unzigzag(u) } else { u as i64 });
2176 }
2177 v
2178 }
2179 T_INTS_AFFINE => {
2181 let base = unzigzag(read_uvarint(buf, pos)?);
2182 let stride = unzigzag(read_uvarint(buf, pos)?);
2183 let n = bounded(read_uvarint(buf, pos)?, max_elements)?;
2184 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2185 for i in 0..n {
2186 v.push(base.wrapping_add((i as i64).wrapping_mul(stride)));
2187 }
2188 v
2189 }
2190 T_INTS_GEOMETRIC => {
2192 let base = unzigzag(read_uvarint(buf, pos)?);
2193 let ratio = unzigzag(read_uvarint(buf, pos)?);
2194 let n = bounded(read_uvarint(buf, pos)?, max_elements)?;
2195 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2196 let mut cur = base;
2197 for _ in 0..n {
2198 v.push(cur);
2199 cur = cur.wrapping_mul(ratio);
2200 }
2201 v
2202 }
2203 T_INTS_PERIODIC => {
2205 let n = bounded(read_uvarint(buf, pos)?, max_elements)?;
2206 if depth >= DECODE_MAX_DEPTH {
2207 return None;
2208 }
2209 let block_tag = *buf.get(*pos)?;
2210 *pos += 1;
2211 let block = decode_int_column_body(block_tag, buf, pos, max_elements, depth + 1)?;
2212 let p = block.len();
2213 if p == 0 {
2214 return None; }
2216 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2217 for i in 0..n {
2218 v.push(block[i % p]);
2219 }
2220 v
2221 }
2222 T_INTS_SPARSE => {
2224 let dom = unzigzag(read_uvarint(buf, pos)?);
2225 let n = bounded(read_uvarint(buf, pos)?, max_elements)?;
2226 let num_exc = bounded(read_uvarint(buf, pos)?, max_elements)?;
2227 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2228 v.resize(n, dom);
2229 let mut idx = 0usize;
2230 for _ in 0..num_exc {
2231 idx = idx.checked_add(read_uvarint(buf, pos)? as usize)?;
2232 let val = unzigzag(read_uvarint(buf, pos)?);
2233 *v.get_mut(idx)? = val; }
2235 v
2236 }
2237 T_INTS_POLY => {
2239 let degree = *buf.get(*pos)? as usize;
2240 *pos += 1;
2241 if degree > MAX_POLY_DEGREE {
2242 return None;
2243 }
2244 let n = bounded(read_uvarint(buf, pos)?, max_elements)?;
2245 let mut seeds = Vec::with_capacity(degree + 1);
2246 for _ in 0..=degree {
2247 seeds.push(unzigzag(read_uvarint(buf, pos)?));
2248 }
2249 reconstruct_poly(&seeds, n)
2250 }
2251 T_INTS_LRECUR => {
2253 let k = *buf.get(*pos)? as usize;
2254 *pos += 1;
2255 if k == 0 || k > MAX_RECUR_ORDER {
2256 return None; }
2258 let n = bounded(read_uvarint(buf, pos)?, max_elements)?;
2259 let mut coeffs = Vec::with_capacity(k);
2260 for _ in 0..k {
2261 coeffs.push(unzigzag(read_uvarint(buf, pos)?));
2262 }
2263 let mut seeds = Vec::with_capacity(k);
2264 for _ in 0..k {
2265 seeds.push(unzigzag(read_uvarint(buf, pos)?));
2266 }
2267 reconstruct_recurrence(&coeffs, &seeds, n)
2268 }
2269 T_INTS_LFSR => {
2272 let n = bounded(read_uvarint(buf, pos)?, max_elements)?; let l = read_uvarint(buf, pos)? as usize;
2274 let total_bits = n.checked_mul(8)?;
2275 if l > total_bits {
2276 return None;
2277 }
2278 let nbytes = l.div_ceil(8);
2279 let tap_bytes = buf.get(*pos..pos.checked_add(nbytes)?)?;
2280 *pos += nbytes;
2281 let taps: Vec<bool> = bitunpack(tap_bytes, l, 1)?.into_iter().map(|x| x == 1).collect();
2282 let seed_bytes = buf.get(*pos..pos.checked_add(nbytes)?)?;
2283 *pos += nbytes;
2284 let seed: Vec<bool> = bitunpack(seed_bytes, l, 1)?.into_iter().map(|x| x == 1).collect();
2285 bits_to_bytes(&lfsr_generate(&taps, &seed, total_bits))
2286 }
2287 T_INTS_FCSR => {
2289 let n = bounded(read_uvarint(buf, pos)?, max_elements)?; let p = read_bigint(buf, pos)?;
2291 let q = read_bigint(buf, pos)?;
2292 if !q.is_odd() {
2293 return None; }
2295 let total_bits = n.checked_mul(8)?;
2296 bits_to_bytes(&fcsr_generate(&p, &q, total_bits))
2297 }
2298 T_GEN => {
2300 let mut budget = MAX_GEN_NODES;
2301 let expr = deserialize_gen(buf, pos, &mut budget, 0)?;
2302 let n = bounded(read_uvarint(buf, pos)?, max_elements)?;
2303 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2304 for i in 0..n {
2305 v.push(gen_eval(&expr, i as i64));
2306 }
2307 v
2308 }
2309 T_BYTES => {
2311 let n = read_uvarint(buf, pos)? as usize;
2312 let raw = buf.get(*pos..pos.checked_add(n)?)?;
2313 *pos += n;
2314 raw.iter().map(|&b| b as i64).collect()
2315 }
2316 T_INTS_DELTA => {
2318 let n = read_uvarint(buf, pos)? as usize;
2319 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2320 if n > 0 {
2321 let mut cur = unzigzag(read_uvarint(buf, pos)?);
2322 v.push(cur);
2323 for _ in 1..n {
2324 cur = cur.wrapping_add(unzigzag(read_uvarint(buf, pos)?));
2325 v.push(cur);
2326 }
2327 }
2328 v
2329 }
2330 T_INTS_DOD => {
2332 let n = read_uvarint(buf, pos)? as usize;
2333 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2334 if n > 0 {
2335 let first = unzigzag(read_uvarint(buf, pos)?);
2336 v.push(first);
2337 if n > 1 {
2338 let mut prev_delta = unzigzag(read_uvarint(buf, pos)?);
2339 let mut prev = first.wrapping_add(prev_delta);
2340 v.push(prev);
2341 for _ in 2..n {
2342 prev_delta = prev_delta.wrapping_add(unzigzag(read_uvarint(buf, pos)?));
2343 prev = prev.wrapping_add(prev_delta);
2344 v.push(prev);
2345 }
2346 }
2347 }
2348 v
2349 }
2350 T_INTS_FOR => {
2352 let n = read_uvarint(buf, pos)? as usize;
2353 let min = unzigzag(read_uvarint(buf, pos)?);
2354 let width = *buf.get(*pos)?;
2355 *pos += 1;
2356 if width > 64 {
2357 return None;
2358 }
2359 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2360 if width == 0 {
2361 for _ in 0..n {
2362 v.push(min);
2363 }
2364 } else {
2365 let nbytes = n.checked_mul(width as usize)?.div_ceil(8);
2366 let bytes = buf.get(*pos..pos.checked_add(nbytes)?)?;
2367 *pos += nbytes;
2368 for r in bitunpack(bytes, n, width)? {
2369 v.push(r.wrapping_add(min as u64) as i64);
2370 }
2371 }
2372 v
2373 }
2374 T_INTS_RLE => {
2376 let runs = read_uvarint(buf, pos)? as usize;
2377 let mut v: Vec<i64> = Vec::new();
2378 for _ in 0..runs {
2379 let val = unzigzag(read_uvarint(buf, pos)?);
2380 let len = read_uvarint(buf, pos)? as usize;
2381 if v.len().checked_add(len)? > RLE_MAX_TOTAL {
2382 return None;
2383 }
2384 v.resize(v.len() + len, val);
2385 }
2386 v
2387 }
2388 T_INTS_DICT => {
2390 let d = read_uvarint(buf, pos)? as usize;
2391 let mut dict = Vec::with_capacity(d.min(PREALLOC_CAP));
2392 for _ in 0..d {
2393 dict.push(unzigzag(read_uvarint(buf, pos)?));
2394 }
2395 let n = read_uvarint(buf, pos)? as usize;
2396 let iw = *buf.get(*pos)?;
2397 *pos += 1;
2398 if iw > 64 {
2399 return None;
2400 }
2401 let mut v = Vec::with_capacity(n.min(PREALLOC_CAP));
2402 if iw == 0 {
2403 if n > 0 {
2404 let val = *dict.first()?;
2405 v.resize(n, val);
2406 }
2407 } else {
2408 let nbytes = n.checked_mul(iw as usize)?.div_ceil(8);
2409 let bytes = buf.get(*pos..pos.checked_add(nbytes)?)?;
2410 *pos += nbytes;
2411 for ix in bitunpack(bytes, n, iw)? {
2412 v.push(*dict.get(ix as usize)?);
2413 }
2414 }
2415 v
2416 }
2417 _ => return None,
2418 })
2419}
2420
2421pub fn decode_int_seq(bytes: &[u8]) -> Option<Vec<i64>> {
2424 let mut pos = 0usize;
2425 let tag = *bytes.get(pos)?;
2426 pos += 1;
2427 let v = decode_int_column_body(tag, bytes, &mut pos, DEFAULT_MAX_ELEMENTS, 0)?;
2428 (pos == bytes.len()).then_some(v)
2429}
2430
2431#[cfg(test)]
2432mod tests {
2433 use super::*;
2434
2435 fn roundtrips(v: &[i64]) {
2436 let enc = describe_int_seq(v);
2437 let dec = decode_int_seq(&enc);
2438 assert_eq!(dec.as_deref(), Some(v), "round-trip failed for {v:?} (enc {enc:?})");
2439 }
2440
2441 #[test]
2442 fn affine_round_trips_and_beats_varint() {
2443 let v: Vec<i64> = (0..1000).map(|i| 10 + 7 * i).collect();
2444 roundtrips(&v);
2445 assert!(describe_int_seq(&v).len() < 20, "affine must ship as a generator, not data");
2447 }
2448
2449 #[test]
2450 fn geometric_round_trips() {
2451 let v: Vec<i64> = (0..40).map(|i| 3i64.wrapping_mul(2i64.wrapping_pow(i))).collect();
2452 roundtrips(&v);
2453 }
2454
2455 #[test]
2456 fn polynomial_round_trips() {
2457 let v: Vec<i64> = (0..500).map(|i| i * i - 3 * i + 5).collect();
2458 roundtrips(&v);
2459 assert!(describe_int_seq(&v).len() < 30, "poly must ship as finite-difference seeds");
2460 }
2461
2462 #[test]
2463 fn linear_recurrence_ships_the_generator() {
2464 let mut fib = vec![0i64, 1];
2467 while fib.len() < 60 {
2468 let n = fib.len();
2469 fib.push(fib[n - 1].wrapping_add(fib[n - 2]));
2470 }
2471 roundtrips(&fib);
2472 assert!(describe_int_seq(&fib).len() < 15, "Fibonacci ships as (order, coeffs, seeds), not data");
2473
2474 let mut lucas = vec![2i64, 1];
2476 while lucas.len() < 60 {
2477 let n = lucas.len();
2478 lucas.push(lucas[n - 1].wrapping_add(lucas[n - 2]));
2479 }
2480 roundtrips(&lucas);
2481 assert!(describe_int_seq(&lucas).len() < 15);
2482
2483 let mut pell = vec![0i64, 1];
2485 while pell.len() < 50 {
2486 let n = pell.len();
2487 pell.push(2i64.wrapping_mul(pell[n - 1]).wrapping_add(pell[n - 2]));
2488 }
2489 roundtrips(&pell);
2490 assert!(describe_int_seq(&pell).len() < 15);
2491
2492 let mut r3 = vec![1i64, 2, 3];
2494 while r3.len() < 40 {
2495 let n = r3.len();
2496 r3.push(r3[n - 1].wrapping_add(r3[n - 2]).wrapping_sub(r3[n - 3]));
2497 }
2498 roundtrips(&r3);
2499 assert!(describe_int_seq(&r3).len() < 18);
2500 }
2501
2502 #[test]
2503 fn berlekamp_massey_recovers_the_shortest_lfsr() {
2504 let taps = vec![true, false, true];
2506 let seed = vec![true, false, false];
2507 let seq = lfsr_generate(&taps, &seed, 40);
2508 let (l, recovered) = berlekamp_massey_gf2(&seq);
2509 assert_eq!(l, 3, "a length-3 LFSR has linear complexity 3");
2510 assert_eq!(lfsr_generate(&recovered, &seq[..l], seq.len()), seq);
2512 let mut st = 0x1234_5678u64;
2516 let rnd: Vec<bool> = (0..200)
2517 .map(|_| {
2518 st = st.wrapping_add(0x9E37_79B9_7F4A_7C15);
2519 let mut z = st;
2520 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
2521 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
2522 z ^= z >> 31;
2523 z & 1 == 1
2524 })
2525 .collect();
2526 let (lr, _) = berlekamp_massey_gf2(&rnd);
2527 assert!((80..=120).contains(&lr), "high-quality random complexity ≈ n/2, got {lr}");
2528 let bytes = vec![0x41i64, 0x00, 0xFF, 0x7E];
2530 assert_eq!(bits_to_bytes(&bytes_to_bits(&bytes)), bytes);
2531 }
2532
2533 #[test]
2534 fn lfsr_keystream_bytes_compress_to_the_register() {
2535 let taps = vec![false, false, true, false, false, false, true]; let seed = vec![true, false, true, true, false, false, true];
2541 let bits = lfsr_generate(&taps, &seed, 200 * 8);
2542 let bytes = bits_to_bytes(&bits);
2543 roundtrips(&bytes);
2544 let enc = describe_int_seq(&bytes);
2545 assert_eq!(enc[0], T_INTS_LFSR, "the keystream ships as its LFSR register (tag), got {}", enc[0]);
2546 assert!(enc.len() < 20, "200 bytes collapse to the 7-bit register, got {} bytes", enc.len());
2547 }
2548
2549 #[test]
2550 fn berlekamp_massey_over_gf256_recovers_a_word_lfsr() {
2551 assert_eq!(Gf256(0x53).mul(Gf256(0xCA)), Gf256(0x53).mul(Gf256(0xCA)));
2553 assert_eq!(Gf256(0x53).mul(Gf256(0x53).inv()), Gf256::one());
2554 assert_eq!(Gf256(0xFF).mul(Gf256(0xFF).inv()), Gf256::one());
2555 let taps = vec![Gf256(0x02), Gf256(0x8d), Gf256(0x1f)];
2559 let seed = vec![Gf256(0x41), Gf256(0x9c), Gf256(0x07)];
2560 let seq = lfsr_generate_field(&taps, &seed, 60);
2561 let (l, recovered) = berlekamp_massey_field(&seq);
2562 assert_eq!(l, 3, "order-3 word-LFSR has GF(256) linear complexity 3, got {l}");
2563 assert_eq!(lfsr_generate_field(&recovered, &seq[..l], seq.len()), seq, "recovered register regenerates it");
2564 }
2565
2566 #[test]
2567 fn two_adic_complexity_detects_fcsr_keystreams() {
2568 use crate::numeric::BigInt;
2569 let bits = fcsr_generate(&BigInt::from_i64(3), &BigInt::from_i64(19), 120);
2572 let (rp, rq) = two_adic_reconstruct(&bits).expect("a clean 2-adic rational");
2573 assert_eq!(fcsr_generate(&rp, &rq, bits.len()), bits, "the recovered FCSR regenerates the keystream");
2574 let tac = two_adic_complexity(&bits);
2575 assert!(tac < 12, "a small FCSR has low 2-adic complexity, got {tac}");
2576 assert!(berlekamp_massey_gf2(&bits).0 > tac, "the FCSR is simpler 2-adically than linearly");
2578 let mut st = 0xABCD_1234u64;
2580 let rnd: Vec<bool> = (0..200)
2581 .map(|_| {
2582 st = st.wrapping_add(0x9E37_79B9_7F4A_7C15);
2583 let mut z = st;
2584 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
2585 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
2586 z ^= z >> 31;
2587 z & 1 == 1
2588 })
2589 .collect();
2590 assert!(two_adic_complexity(&rnd) > 60, "random ≈ n/2 2-adic complexity, got {}", two_adic_complexity(&rnd));
2591 }
2592
2593 #[test]
2594 fn fcsr_keystream_bytes_compress_to_the_rational() {
2595 use crate::numeric::BigInt;
2596 let bits = fcsr_generate(&BigInt::from_i64(7), &BigInt::from_i64(1_000_003), 100 * 8);
2599 let bytes = bits_to_bytes(&bits);
2600 roundtrips(&bytes);
2601 let enc = describe_int_seq(&bytes);
2602 assert_eq!(enc[0], T_INTS_FCSR, "the FCSR keystream ships as its rational p/q (tag), got {}", enc[0]);
2603 assert!(enc.len() < 20, "100 bytes collapse to a small rational, got {} bytes", enc.len());
2604 }
2605
2606 #[test]
2607 fn maximal_order_complexity_catches_nonlinear_feedback() {
2608 let order = 6;
2613 let period = 1usize << order;
2614 let mut db: Vec<bool> = vec![false; order];
2615 let mut seen: std::collections::HashSet<Vec<bool>> = std::collections::HashSet::new();
2616 seen.insert(db[..order].to_vec());
2617 while db.len() < period {
2618 let mut w: Vec<bool> = db[db.len() - (order - 1)..].to_vec();
2619 w.push(true);
2620 if seen.contains(&w) {
2621 w.pop();
2622 w.push(false);
2623 }
2624 seen.insert(w.clone());
2625 db.push(*w.last().unwrap());
2626 }
2627 let bits: Vec<bool> = [db.as_slice(), db.as_slice(), db.as_slice()].concat(); let moc = maximal_order_complexity(&bits);
2629 let lin = berlekamp_massey_gf2(&bits).0;
2630 assert!((5..=7).contains(&moc), "the order-6 De Bruijn register has MOC ≈ 6, got {moc}");
2631 assert!(lin > moc, "nonlinear feedback fools linear complexity (BM {lin} > MOC {moc})");
2633 assert!(two_adic_complexity(&bits) > moc, "nonlinear feedback fools 2-adic complexity too");
2634 let mut st = 0x2468_ace0_1357_9bdfu64;
2636 let rnd: Vec<bool> = (0..300)
2637 .map(|_| {
2638 st = st.wrapping_add(0x9E37_79B9_7F4A_7C15);
2639 let mut z = st;
2640 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
2641 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
2642 z ^= z >> 31;
2643 z & 1 == 1
2644 })
2645 .collect();
2646 assert!(maximal_order_complexity(&rnd) <= berlekamp_massey_gf2(&rnd).0, "MOC ≤ linear complexity");
2647 }
2648
2649 #[test]
2650 fn algebraic_recurrence_recovers_low_degree_nonlinear_feedback() {
2651 let seed: Vec<bool> = (0..8).map(|k| (0x9E37u64 >> k) & 1 == 1).collect();
2659 let mut bits = seed.clone();
2660 while bits.len() < 600 {
2661 let i = bits.len();
2662 let s = |k: usize| bits[i - k];
2663 bits.push(s(1) ^ s(5) ^ s(6) ^ s(8) ^ (s(1) & s(6)));
2664 }
2665
2666 let coeffs = detect_algebraic_recurrence(&bits, 8, 2).expect("recovers the degree-2 feedback");
2667 assert_eq!(
2668 algebraic_generate(8, 2, &coeffs, &bits[..8], bits.len()),
2669 bits,
2670 "the recovered ANF regenerates the whole nonlinear sequence"
2671 );
2672
2673 assert_eq!(coeffs.len(), 37, "degree-2 ANF over 8 vars has 37 monomials");
2676 assert!(coeffs.len() < (1usize << 8), "the ANF is far sparser than the full truth table");
2677
2678 let lin = berlekamp_massey_gf2(&bits).0;
2681 assert!(lin > 200, "the nonlinear term drives linear complexity to 246, got {lin}");
2682 assert!(two_adic_complexity(&bits) > 8, "and 2-adic complexity is high too — carry tools miss it");
2683
2684 assert!(detect_algebraic_recurrence(&bits, 8, 1).is_none(), "no affine order-8 recurrence fits");
2686
2687 let (l, m) = algebraic_complexity(&bits, 2).expect("degree-2 algebraic complexity exists");
2689 assert!(l <= 8, "the algebraic recovery finds an order-≤8 register, got {l}");
2690 assert!(m <= 37, "and describes it in at most its 37 ANF coefficients, got {m}");
2691 }
2692
2693 #[test]
2694 fn algebraic_recovery_is_a_strict_generalization_of_berlekamp_massey() {
2695 let taps = vec![true, false, false, true, false, true]; let seed = vec![true, false, true, true, false, false];
2701 let bits = lfsr_generate(&taps, &seed, 200);
2702 let coeffs = detect_algebraic_recurrence(&bits, 6, 1).expect("degree-1 recovery = Berlekamp–Massey");
2703 assert_eq!(
2704 algebraic_generate(6, 1, &coeffs, &bits[..6], bits.len()),
2705 bits,
2706 "the degree-1 ANF regenerates the linear keystream"
2707 );
2708 assert!(berlekamp_massey_gf2(&bits).0 <= 6, "an LFSR keystream has linear complexity ≤ its order");
2710 }
2711
2712 #[test]
2713 fn correlation_attack_breaks_the_geffe_combiner_register_by_register() {
2714 let n = 2000;
2720 let taps1 = [false, false, true, false, false, false, true]; let taps2 = [false, false, true, false, true]; let taps3 = [false, false, false, false, true, false, false, false, true]; let seed1 = [true, false, true, true, false, false, true];
2724 let seed2 = [true, true, false, false, true];
2725 let seed3 = [true, false, false, true, false, true, true, false, true];
2726 let x1 = lfsr_generate(&taps1, &seed1, n);
2727 let x2 = lfsr_generate(&taps2, &seed2, n);
2728 let x3 = lfsr_generate(&taps3, &seed3, n);
2729 let z: Vec<bool> = (0..n).map(|i| if x2[i] { x1[i] } else { x3[i] }).collect();
2730
2731 let a1 = correlation_attack(&z, &taps1).expect("x1 register attackable");
2733 assert_eq!(a1.init_state, seed1.to_vec(), "recovers x1's exact initial state");
2734 assert!((a1.agreement - 0.75).abs() < 0.05, "x1 leaks at ~¾ agreement, got {}", a1.agreement);
2735 assert!(a1.bias > 3.0 * spurious_bias_floor(7, n), "x1's correlation clears the spurious floor");
2736 assert_eq!(lfsr_generate(&taps1, &a1.init_state, n), x1, "the witness regenerates the x1 register");
2738
2739 let a3 = correlation_attack(&z, &taps3).expect("x3 register attackable");
2741 assert_eq!(a3.init_state, seed3.to_vec(), "recovers x3's exact initial state");
2742 assert!((a3.agreement - 0.75).abs() < 0.05, "x3 leaks at ~¾ agreement, got {}", a3.agreement);
2743 assert!(a3.bias > 3.0 * spurious_bias_floor(9, n), "x3's correlation clears the spurious floor");
2744 assert_eq!(lfsr_generate(&taps3, &a3.init_state, n), x3, "the witness regenerates the x3 register");
2745
2746 let a2 = correlation_attack(&z, &taps2).expect("x2 register scanned");
2748 assert!(a2.bias < 3.0 * spurious_bias_floor(5, n), "x2 does not measurably leak, bias {}", a2.bias);
2749 assert!(a1.bias > 3.0 * a2.bias, "the leaking registers are starkly separated from the protected one");
2750 }
2751
2752 #[test]
2753 fn walsh_spectrum_sees_the_linear_approximation_correlation_is_blind_to() {
2754 let f: Vec<bool> = (0..16)
2759 .map(|x| {
2760 let b = |i: usize| (x >> i) & 1 == 1;
2761 b(0) ^ b(1) ^ (b(2) & b(3))
2762 })
2763 .collect();
2764 let spec = walsh_spectrum(&f).expect("well-formed truth table");
2765 for m in [1usize, 2, 4, 8] {
2766 assert_eq!(spec[m], 0, "weight-1 mask {m} vanishes — Rung E is blind here");
2767 }
2768 assert_eq!(correlation_immunity_order(&f), Some(1), "f is first-order correlation-immune");
2769 let (mask, bias) = best_linear_approximation(&f, true).expect("a best approximation exists");
2770 assert_eq!(bias, 0.25, "the exploitable approximation has bias ¼");
2771 assert!(mask.count_ones() >= 2, "and it lives at weight ≥ 2 — beyond E's single-register view");
2772 assert_eq!(nonlinearity(&f), Some(4), "nonlinearity 2³ − 8/2 = 4");
2773
2774 let g: Vec<bool> = (0..16)
2777 .map(|x| {
2778 let b = |i: usize| (x >> i) & 1 == 1;
2779 (b(0) & b(1)) ^ (b(2) & b(3))
2780 })
2781 .collect();
2782 let spec_g = walsh_spectrum(&g).expect("well-formed");
2783 assert!(spec_g.iter().all(|&c| c.unsigned_abs() == 4), "bent ⇒ perfectly flat spectrum");
2784 assert_eq!(nonlinearity(&g), Some(6), "bent nonlinearity 2³ − 2¹ = 6 (maximal for n=4)");
2785 assert_eq!(best_linear_approximation(&g, true).unwrap().1, 0.125, "no approximation beats 1/8");
2786 }
2787
2788 #[test]
2789 fn anf_is_its_own_inverse_and_reads_the_degree() {
2790 for n in 1..=5usize {
2792 for seed in 0..8u64 {
2793 let f: Vec<bool> = (0..1u64 << n)
2794 .map(|i| {
2795 let mut z = 0x9E37_79B9_7F4A_7C15u64.wrapping_mul(i.wrapping_add(seed * 131 + 1));
2796 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
2797 (z ^ (z >> 27)) & 1 == 1
2798 })
2799 .collect();
2800 let a = anf(&f).expect("well-formed");
2801 assert_eq!(anf(&a).as_deref(), Some(&f[..]), "anf∘anf = identity (n={n}, seed={seed})");
2802 }
2803 }
2804 let n = 4;
2806 let parity: Vec<bool> = (0..1usize << n).map(|x| (x as u32).count_ones() % 2 == 1).collect();
2807 assert_eq!(algebraic_degree(&parity), Some(1), "parity is affine");
2808 let bent: Vec<bool> = (0..1usize << n)
2809 .map(|x| {
2810 let b = |i: usize| x & (1 << i) != 0;
2811 (b(0) && b(1)) ^ (b(2) && b(3))
2812 })
2813 .collect();
2814 assert_eq!(algebraic_degree(&bent), Some(2), "x0x1 ⊕ x2x3 is quadratic");
2815 assert_eq!(algebraic_degree(&vec![true; 8]), Some(0), "a constant has degree 0");
2816 }
2817
2818 #[test]
2819 fn autocorrelation_reads_the_derivative_symmetry() {
2820 let n = 4;
2821 let parity: Vec<bool> = (0..1usize << n).map(|x| (x as u32).count_ones() % 2 == 1).collect();
2823 let rp = autocorrelation(&parity).expect("well-formed");
2824 assert_eq!(rp[0], 16, "r(0) = 2ⁿ always");
2825 assert!(rp.iter().all(|&c| c.unsigned_abs() == 16), "a linear function is flat: |r(a)| = 2ⁿ ∀a");
2826
2827 let bent: Vec<bool> = (0..1usize << n)
2829 .map(|x| {
2830 let b = |i: usize| x & (1 << i) != 0;
2831 (b(0) && b(1)) ^ (b(2) && b(3))
2832 })
2833 .collect();
2834 let rb = autocorrelation(&bent).expect("well-formed");
2835 assert_eq!(rb[0], 16);
2836 assert!(rb[1..].iter().all(|&c| c == 0), "bent ⇒ no nonzero linear structure");
2837
2838 let f: Vec<bool> = (0..1usize << n)
2840 .map(|x| {
2841 let b = |i: usize| x & (1 << i) != 0;
2842 ((b(0) && b(1)) ^ b(2)) ^ b(3)
2843 })
2844 .collect();
2845 let rf = autocorrelation(&f).expect("well-formed");
2846 assert_eq!(rf[1 << 3], -16, "flipping x3 always flips f ⇒ r(e3) = −2ⁿ (a linear structure)");
2847 }
2848
2849 #[test]
2850 fn walsh_found_approximation_breaks_a_correlation_immune_combiner() {
2851 let n = 4000;
2855 let taps1 = [false, false, true, false, false, false, true]; let taps2 = [false, false, true, false, true]; let taps3 = [false, false, false, false, true, false, false, false, true]; let taps4 = [false, false, false, false, false, false, false, false, true, false, true]; let a1 = lfsr_generate(&taps1, &[true, false, true, true, false, false, true], n);
2860 let a2 = lfsr_generate(&taps2, &[true, true, false, false, true], n);
2861 let a3 = lfsr_generate(&taps3, &[true, false, false, true, false, true, true, false, true], n);
2862 let a4 = lfsr_generate(&taps4, &[true, false, true, false, false, true, true, false, false, true, false], n);
2863 let z: Vec<bool> = (0..n).map(|i| a1[i] ^ a2[i] ^ (a3[i] & a4[i])).collect();
2864
2865 let agree1 = z.iter().zip(&a1).filter(|(x, y)| x == y).count() as f64 / n as f64;
2867 assert!((agree1 - 0.5).abs() < 0.04, "single register a1 is invisible to first-order correlation, {agree1}");
2868
2869 let combined: Vec<bool> = (0..n).map(|i| a1[i] ^ a2[i]).collect();
2871 let agree_r = z.iter().zip(&combined).filter(|(x, y)| x == y).count() as f64 / n as f64;
2872 assert!((agree_r - 0.75).abs() < 0.04, "the weight-2 approximation leaks at ¾ — E could not see it, {agree_r}");
2873 }
2874
2875 #[test]
2876 fn fast_correlation_attack_decodes_a_noisy_lfsr_without_exhaustive_search() {
2877 let mut taps = vec![false; 17];
2880 taps[13] = true; taps[16] = true; let seed: Vec<bool> = (0..17).map(|k| (0xACE1u64 >> k) & 1 == 1).collect();
2883 let n = 4000;
2884 let a = lfsr_generate(&taps, &seed, n);
2885
2886 let mut st = 0x1234_5678_9abc_def0u64;
2888 let z: Vec<bool> = a
2889 .iter()
2890 .map(|&bit| {
2891 st = st.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
2892 bit ^ ((st >> 40) % 100 < 12)
2893 })
2894 .collect();
2895 let agree = a.iter().zip(&z).filter(|(x, y)| x == y).count() as f64 / n as f64;
2896 assert!((agree - 0.88).abs() < 0.04, "the channel is genuinely ~12% noisy, got {agree}");
2897
2898 let state = fast_correlation_attack(&z, &taps, 400).expect("the decoder recovers the register");
2899 assert_eq!(state, seed, "fast correlation recovers the exact initial state — no 2¹⁷ search");
2900
2901 let mut st2 = 0xdead_beef_0bad_c0deu64;
2903 let pure: Vec<bool> = (0..n)
2904 .map(|_| {
2905 st2 = st2.wrapping_mul(6364136223846793005).wrapping_add(1);
2906 (st2 >> 40) & 1 == 1
2907 })
2908 .collect();
2909 assert!(fast_correlation_attack(&pure, &taps, 400).is_none(), "pure noise leaks no register — the ceiling");
2910 }
2911
2912 #[test]
2913 fn shrinking_generator_falls_to_a_clock_guess_the_linear_rungs_cannot_touch() {
2914 let a_taps = [false, false, true, false, false, false, true]; let s_taps = [false, false, false, false, true, false, false, false, true]; let a_seed = [true, false, true, true, false, false, true];
2921 let s_seed = [true, true, false, true, false, true, true, false, true];
2922 let m = 300;
2923 let output = shrinking_generator(&a_taps, &a_seed, &s_taps, &s_seed, m);
2924
2925 assert!(
2927 berlekamp_massey_gf2(&output).0 > 40,
2928 "the shrinking generator's output has high linear complexity, got {}",
2929 berlekamp_massey_gf2(&output).0
2930 );
2931
2932 let (a_rec, s_rec) = attack_shrinking_generator(&output, &a_taps, &s_taps).expect("the generator falls");
2933 assert_eq!(
2938 shrinking_generator(&a_taps, &a_rec, &s_taps, &s_rec, m),
2939 output,
2940 "the recovered pair regenerates the keystream — the certified break"
2941 );
2942 assert_eq!(
2943 shrinking_generator(&a_taps, &a_rec, &s_taps, &s_rec, 1000),
2944 shrinking_generator(&a_taps, &a_seed, &s_taps, &s_seed, 1000),
2945 "and stays identical to the true generator well past the attacked length — a full break"
2946 );
2947 }
2948
2949 #[test]
2950 fn xl_solves_a_quadratic_system_by_degree_escalation() {
2951 let eqs: Vec<Vec<u64>> = vec![
2954 vec![0b0011], vec![0b0101, 0b0000], vec![0b0110], vec![0b1100], vec![0b0001, 0b0100], vec![0b0010, 0b1000], ];
2961 assert_eq!(
2964 solve_polynomial_system_gf2(&eqs, 4, 1),
2965 PolySolveResult::Undetermined,
2966 "degree 1 cannot represent the quadratic constraints"
2967 );
2968 assert_eq!(
2971 solve_polynomial_system_gf2(&eqs, 4, 2),
2972 PolySolveResult::Solved(vec![true, false, true, false]),
2973 "XL recovers the unique root"
2974 );
2975 }
2976
2977 #[test]
2978 fn xl_refutes_an_unsatisfiable_system_linearization_misses() {
2979 let eqs: Vec<Vec<u64>> = vec![
2984 vec![0b11, 0b00], vec![0b01], ];
2987 assert_eq!(
2988 solve_polynomial_system_gf2(&eqs, 2, 1),
2989 PolySolveResult::Undetermined,
2990 "at degree 1 the quadratic contradiction is invisible"
2991 );
2992 assert_eq!(
2993 solve_polynomial_system_gf2(&eqs, 2, 2),
2994 PolySolveResult::Refuted,
2995 "XL manufactures x0·x1 = 0, refuting the system"
2996 );
2997 }
2998
2999 #[test]
3000 fn algebraic_immunity_computes_and_verifies_known_values() {
3001 let b = |x: usize, i: usize| (x >> i) & 1 == 1;
3002
3003 let affine: Vec<bool> = (0..8).map(|x| b(x, 0) ^ b(x, 1) ^ b(x, 2)).collect();
3005 let (ai, w) = algebraic_immunity(&affine).expect("well-formed");
3006 assert_eq!(ai, 1, "affine functions have AI 1");
3007 assert!(verify_annihilator(&affine, &w), "the affine annihilator re-checks");
3008
3009 let maj3: Vec<bool> = (0..8).map(|x| (x as u32).count_ones() >= 2).collect();
3011 let (ai, w) = algebraic_immunity(&maj3).expect("well-formed");
3012 assert_eq!(ai, 2, "majority-3 has AI 2 (the maximum for n=3)");
3013 assert!(verify_annihilator(&maj3, &w), "the degree-2 annihilator re-checks");
3014
3015 let and3: Vec<bool> = (0..8).map(|x| b(x, 0) & b(x, 1) & b(x, 2)).collect();
3017 assert_eq!(algebraic_immunity(&and3).unwrap().0, 1, "a single product term has AI 1");
3018
3019 let (_, w) = algebraic_immunity(&maj3).unwrap();
3021 let mut zeroed = w.clone();
3022 zeroed.coeffs = vec![false; zeroed.coeffs.len()];
3023 assert!(!verify_annihilator(&maj3, &zeroed), "the zero function is not a valid annihilator");
3024 let mut flipped = w.clone();
3025 flipped.annihilates_complement = !flipped.annihilates_complement;
3026 assert!(!verify_annihilator(&maj3, &flipped), "a nonzero g cannot vanish on both sides");
3027 }
3028
3029 #[test]
3030 fn algebraic_immunity_never_exceeds_half_n() {
3031 let mut st = 0x00C0_FFEE_1234_5678u64;
3033 for _ in 0..24 {
3034 let truth: Vec<bool> = (0..16)
3035 .map(|_| {
3036 st = st.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
3037 (st >> 33) & 1 == 1
3038 })
3039 .collect();
3040 let (ai, w) = algebraic_immunity(&truth).expect("well-formed");
3041 assert!(ai <= 2, "AI ≤ ⌈4/2⌉ = 2, got {ai}");
3042 assert!(verify_annihilator(&truth, &w), "every witness re-checks");
3043 }
3044 }
3045
3046 #[test]
3047 fn algebraic_filter_attack_recovers_the_filter_generator_state() {
3048 let taps = [false, false, false, false, false, false, true, false, false, true]; let s0 = [true, false, true, true, false, false, true, false, true, true];
3054 let filter_truth: Vec<bool> = (0..8).map(|x| (x as u32).count_ones() >= 2).collect(); let m = 3;
3056 let n = 400;
3057 let seq = lfsr_generate(&taps, &s0, n + m);
3058 let keystream: Vec<bool> = (0..n)
3059 .map(|t| {
3060 let idx = (0..m).fold(0usize, |a, i| a | (usize::from(seq[t + i]) << i));
3061 filter_truth[idx]
3062 })
3063 .collect();
3064
3065 let recovered = algebraic_filter_attack(&keystream, &taps, &filter_truth)
3066 .expect("the algebraic attack recovers the state");
3067 assert_eq!(recovered, s0.to_vec(), "the recovered initial state IS the secret key");
3068 }
3069
3070 #[test]
3071 fn periodic_round_trips() {
3072 let block = [4i64, 1, 1, 5, 9, 2, 6];
3073 let v: Vec<i64> = (0..300).map(|i| block[i % block.len()]).collect();
3074 roundtrips(&v);
3075 }
3076
3077 #[test]
3078 fn modular_affine_round_trips() {
3079 let v: Vec<i64> = (0..200).map(|i| 100 + 3 * ((i % 8) as i64)).collect();
3080 roundtrips(&v);
3081 }
3082
3083 #[test]
3084 fn sparse_round_trips() {
3085 let mut v = vec![42i64; 500];
3086 v[17] = -1;
3087 v[300] = 999;
3088 v[499] = 7;
3089 roundtrips(&v);
3090 }
3091
3092 #[test]
3093 fn delta_and_dod_shapes_round_trip() {
3094 let monotone: Vec<i64> = (0..200).map(|i| i * i / 7 + i).collect();
3095 roundtrips(&monotone);
3096 let jittered: Vec<i64> = (0..200).map(|i| 1_000_000 + 60 * i + (i % 3)).collect();
3097 roundtrips(&jittered);
3098 }
3099
3100 #[test]
3101 fn runs_and_low_cardinality_round_trip() {
3102 let mut runs = vec![5i64; 100];
3103 runs.extend(std::iter::repeat(9).take(80));
3104 runs.extend(std::iter::repeat(-2).take(120));
3105 roundtrips(&runs);
3106 let categorical: Vec<i64> = (0..600).map(|i| [10, 20, 30][i % 3]).collect();
3107 roundtrips(&categorical);
3108 }
3109
3110 #[test]
3111 fn byte_column_round_trips() {
3112 let v: Vec<i64> = (0..500).map(|i| ((i * 37) % 256) as i64).collect();
3113 roundtrips(&v);
3114 }
3115
3116 #[test]
3117 fn edge_cases_round_trip() {
3118 roundtrips(&[]);
3119 roundtrips(&[42]);
3120 roundtrips(&[-1]);
3121 roundtrips(&[i64::MIN, i64::MAX, 0, -1, 1]);
3122 roundtrips(&[7, 7, 7, 7]);
3123 }
3124
3125 #[test]
3126 fn pseudorandom_round_trips_and_is_never_larger_than_varint() {
3127 let mut state = 0x1234_5678_9abc_def0u64;
3130 let v: Vec<i64> = (0..400)
3131 .map(|_| {
3132 state ^= state << 13;
3133 state ^= state >> 7;
3134 state ^= state << 17;
3135 (state >> 1) as i64
3136 })
3137 .collect();
3138 roundtrips(&v);
3139 let mut baseline = vec![T_INTS];
3140 leb128_encode(&mut baseline, v.iter().copied(), v.len());
3141 assert!(describe_int_seq(&v).len() <= baseline.len(), "never worse than the varint baseline");
3142 }
3143}