+use itertools::Itertools;
+use std::borrow::Borrow;
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+use std::fmt::{Display, Formatter};
+use std::ops::Add;
+
use nimber::FiniteNimber;
+fn lowest_set_bit(n: &FiniteNimber) -> Option<usize> {
+ let words: &[nimber::Word] = n.borrow();
+
+ for (i, word) in words.iter().cloned().enumerate() {
+ if word != 0 {
+ let tz: usize = word.trailing_zeros().try_into().expect(
+ "a number of leading zeros can't be too big to fit in a usize",
+ );
+ return Some(i * 64 + tz);
+ }
+ }
+
+ None
+}
+
+#[derive(Clone, Debug)]
+struct Polynomial(Vec<u64>);
+
+impl Polynomial {
+ fn monomial(index: usize) -> Self {
+ Self(
+ std::iter::repeat(0)
+ .take(index / 64)
+ .chain([1u64 << (index % 64)].iter().cloned())
+ .collect(),
+ )
+ }
+}
+
+impl Add<&Polynomial> for Polynomial {
+ type Output = Polynomial;
+
+ fn add(self, other: &Polynomial) -> Self {
+ Self(
+ self.0
+ .into_iter()
+ .zip_longest(other.0.iter().cloned())
+ .map(|pair| pair.reduce(|a, b| a ^ b))
+ .collect(),
+ )
+ }
+}
+
+impl Display for Polynomial {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
+ write!(f, "*0x")?;
+ let mut started = false;
+ for v in self.0.iter().rev() {
+ if started {
+ write!(f, "{:016x}", v)?;
+ } else if *v != 0 {
+ write!(f, "{:x}", v)?;
+ started = true;
+ }
+ }
+ if !started {
+ write!(f, "0")?;
+ }
+ Ok(())
+ }
+}
+
+fn minimal_polynomial(n: &FiniteNimber) -> Polynomial {
+ // Maps (lowest set bit) -> (nimber, combination of powers it's made of)
+ let mut map = HashMap::new();
+
+ let mut exp = 0;
+ let mut val = FiniteNimber::from(1);
+
+ loop {
+ let mut comb = Polynomial::monomial(exp);
+ loop {
+ let bit = match lowest_set_bit(&val) {
+ Some(bit) => bit,
+ None => return comb,
+ };
+ match map.entry(bit) {
+ Entry::Occupied(e) => {
+ let e: &(FiniteNimber, Polynomial) = e.get();
+ val = val + &e.0;
+ comb = comb + &e.1;
+ }
+ Entry::Vacant(e) => {
+ e.insert((val.clone(), comb));
+ break;
+ }
+ }
+ }
+ val = val * n;
+ exp += 1;
+ }
+}
+
fn main() {
- let n = FiniteNimber::from(1234);
- println!("{}", n);
+ let n = FiniteNimber::from(0xff);
+ println!("{} -> {}", &n, minimal_polynomial(&n));
}