competitive/algorithm/
xorbasis.rs1#[derive(Debug, Clone, Default)]
3pub struct XorBasis {
4 bases: Vec<(u64, u64, u64)>,
6}
7impl XorBasis {
8 pub fn new() -> Self {
10 Default::default()
11 }
12 pub fn reduce(&self, mut x: u64) -> (u64, u64) {
15 let mut coord = 0u64;
16 for (i, (u, c, _)) in self.bases.iter().enumerate() {
17 if x > u ^ x {
18 coord ^= c ^ (1 << i);
19 x ^= u;
20 }
21 }
22 (x, coord)
23 }
24 pub fn insert(&mut self, x: u64) -> bool {
27 let (y, coord) = self.reduce(x);
28 if y != 0 {
29 self.bases.push((y, coord, x));
30 }
31 y != 0
32 }
33 pub fn find(&self, x: u64) -> Option<u64> {
35 let (y, coord) = self.reduce(x);
36 if y == 0 { Some(coord) } else { None }
37 }
38 pub fn basis(&self, x: u64) -> Option<Vec<u64>> {
40 let (y, coord) = self.reduce(x);
41 if y == 0 {
42 Some(
43 self.bases
44 .iter()
45 .enumerate()
46 .filter_map(|(i, (_, _, b))| {
47 if coord & (1 << i) != 0 {
48 Some(*b)
49 } else {
50 None
51 }
52 })
53 .collect(),
54 )
55 } else {
56 None
57 }
58 }
59}
60impl std::iter::FromIterator<u64> for XorBasis {
61 fn from_iter<T: IntoIterator<Item = u64>>(iter: T) -> Self {
62 let mut basis = XorBasis::default();
63 for x in iter {
64 basis.insert(x);
65 }
66 basis
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::{rand, tools::Xorshift};
74
75 #[test]
76 fn test_xor_basis() {
77 let mut rng = Xorshift::default();
78 const Q: usize = 200;
79
80 for _ in 0..Q {
81 let mut basis = XorBasis::new();
82 for x in rng.random_iter(0u64..).take(Q) {
83 if let Some(b) = basis.basis(x) {
84 assert_eq!(x, b.into_iter().fold(0, std::ops::BitXor::bitxor));
85 }
86 basis.insert(x);
87 }
88 }
89 }
90
91 fn consistables(b: &[u64]) -> std::collections::HashSet<u64> {
92 assert!(b.len() <= 20);
93 (0..1 << b.len())
94 .map(|i| {
95 b.iter()
96 .enumerate()
97 .map(|(j, &b)| if i & (1 << j) != 0 { b } else { 0 })
98 .fold(0, std::ops::BitXor::bitxor)
99 })
100 .collect()
101 }
102
103 #[test]
104 fn test_xor_basis_find() {
105 let mut rng = Xorshift::default();
106 const Q: usize = 100;
107 const L: usize = 12;
108
109 for _ in 0..Q {
110 rand!(rng, k: 0usize..=L + 2, b: [0u64..1 << L; k]);
111 let cons = consistables(&b);
112 let basis: XorBasis = b.into_iter().collect();
113 for x in rng.random_iter(0u64..1 << L).take(Q) {
114 assert_eq!(cons.contains(&x), basis.find(x).is_some());
115 }
116 }
117 }
118}