chiark / gitweb /
Stop allocating a Vec for a one-word nimber
authorSimon Tatham <anakin@pobox.com>
Sat, 12 Apr 2025 12:34:09 +0000 (13:34 +0100)
committerSimon Tatham <anakin@pobox.com>
Sat, 12 Apr 2025 12:49:33 +0000 (13:49 +0100)
src/finitenimber.rs

index 469cd061c95751a6851ffc6a9d06aed856d776af..cb76756c95fbfa93cb5537c8500a97f24c5c34a6 100644 (file)
@@ -9,32 +9,47 @@ const WORDLEVELS: usize = 6; // 2^{2^6} = 64 = size of Word
 
 /// A type representing a finite nimber.
 #[derive(Clone, PartialEq, Eq, Hash)]
-pub struct FiniteNimber(Vec<Word>);
+pub struct FiniteNimber(FiniteNimberEnum);
+
+/// A type representing a finite nimber.
+#[derive(Clone, PartialEq, Eq, Hash)]
+enum FiniteNimberEnum {
+    Single(Word),
+    Vector(Vec<Word>),
+}
 
 #[derive(Clone, Copy)]
 pub enum FiniteNimberRef<'a> {
-    OneWord(Word),
+    Single(Word),
     Slice(&'a [Word]),
 }
-use FiniteNimberRef::*;
 
 impl Debug for FiniteNimber {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
-        write!(f, "FiniteNimber[")?;
-        let mut sep: &str = "";
-        for v in &self.0 {
-            write!(f, "{}0x{:016x}", sep, v)?;
-            sep = ", ";
+        match &self.0 {
+            FiniteNimberEnum::Single(v) => {
+                write!(f, "FiniteNimberEnum::Single(0x{:16x})", v)
+            }
+            FiniteNimberEnum::Vector(s) => {
+                write!(f, "FiniteNimberEnum::Vector[")?;
+                let mut sep: &str = "";
+                for v in s {
+                    write!(f, "{}0x{:016x}", sep, v)?;
+                    sep = ", ";
+                }
+                write!(f, "]")
+            }
         }
-        write!(f, "]")
     }
 }
 
 impl Debug for FiniteNimberRef<'_> {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
         match self {
-            OneWord(v) => write!(f, "FiniteNimberRef::OneWord(0x{:16x})", v),
-            Slice(s) => {
+            FiniteNimberRef::Single(v) => {
+                write!(f, "FiniteNimberRef::Single(0x{:16x})", v)
+            }
+            FiniteNimberRef::Slice(s) => {
                 write!(f, "FiniteNimberRef::Slice[")?;
                 let mut sep: &str = "";
                 for v in *s {
@@ -49,15 +64,13 @@ impl Debug for FiniteNimberRef<'_> {
 
 impl Display for FiniteNimber {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
-        if !f.alternate()
-            && self.to_ref().level() < WORDLEVELS
-            && self.0[0] < 10
-        {
-            write!(f, "*{}", self.0[0])
+        let r = self.to_ref();
+        if !f.alternate() && r.level() < WORDLEVELS && r.low_word() < 10 {
+            write!(f, "*{}", r.low_word())
         } else {
             write!(f, "*0x")?;
             let mut started = false;
-            for v in self.0.iter().rev() {
+            for v in r.as_slice().iter().rev() {
                 if started {
                     write!(f, "{:016x}", v)?;
                 } else if *v != 0 {
@@ -75,19 +88,19 @@ impl Display for FiniteNimber {
 
 impl Borrow<[Word]> for FiniteNimber {
     fn borrow(&self) -> &[Word] {
-        &self.0
+        self.as_slice()
     }
 }
 
 impl<'a> FiniteNimberRef<'a> {
     fn zero() -> Self {
-        OneWord(0)
+        FiniteNimberRef::Single(0)
     }
 
     fn low_word(self) -> Word {
         match self {
-            OneWord(v) => v,
-            Slice(s) => {
+            FiniteNimberRef::Single(v) => v,
+            FiniteNimberRef::Slice(s) => {
                 *(s.first().expect("FiniteNimberRef::Slice is never empty"))
             }
         }
@@ -99,8 +112,8 @@ impl<'a> FiniteNimberRef<'a> {
         'b: 'c,
     {
         match self {
-            OneWord(v) => core::slice::from_ref(v),
-            Slice(s) => s,
+            FiniteNimberRef::Single(v) => core::slice::from_ref(v),
+            FiniteNimberRef::Slice(s) => s,
         }
     }
 
@@ -117,17 +130,12 @@ impl<'a> FiniteNimberRef<'a> {
         }
 
         match self {
-            OneWord(v) => word_level(v),
-            Slice(s) => {
-                match s
-                    .iter()
-                    .enumerate()
-                    .rev()
-                    .find(|(_index, word)| **word != 0)
-                {
-                    None => 0,
-                    Some((0, w)) => word_level(*w),
-                    Some((i, _)) => usize_level(i) + (WORDLEVELS + 1),
+            FiniteNimberRef::Single(v) => word_level(v),
+            FiniteNimberRef::Slice(s) => {
+                let w = s.len().checked_sub(1).expect("slice representation must always be non-empty");
+                match w {
+                    0 => word_level(s[0]),
+                    w => usize_level(w) + (WORDLEVELS + 1),
                 }
             }
         }
@@ -143,11 +151,16 @@ impl<'a> FiniteNimberRef<'a> {
                 let bits = 1 << (level as Word);
                 let mask = (1 << bits) - 1;
                 let v = self.low_word();
-                return (OneWord(v & mask), OneWord((v >> bits) & mask));
+                return (
+                    FiniteNimberRef::Single(v & mask),
+                    FiniteNimberRef::Single((v >> bits) & mask),
+                );
             }
             Some(wordlevel) => match self {
-                OneWord(v) => (OneWord(v), Self::zero()),
-                Slice(s) => {
+                FiniteNimberRef::Single(v) => {
+                    (FiniteNimberRef::Single(v), Self::zero())
+                }
+                FiniteNimberRef::Slice(s) => {
                     match wordlevel
                         .try_into()
                         .ok()
@@ -159,8 +172,13 @@ impl<'a> FiniteNimberRef<'a> {
                                 "FiniteNimberRef::Slice is never empty",
                             );
                             match iter.next() {
-                                Some(hi) => (Slice(lo), Slice(hi)),
-                                None => (Slice(lo), Self::zero()),
+                                Some(hi) => (
+                                    FiniteNimberRef::Slice(lo),
+                                    FiniteNimberRef::Slice(hi),
+                                ),
+                                None => {
+                                    (FiniteNimberRef::Slice(lo), Self::zero())
+                                }
                             }
                         }
                         None => (self.clone(), Self::zero()),
@@ -190,16 +208,16 @@ impl<'a> FiniteNimberRef<'a> {
                         let slo = self.as_slice();
                         let shi = hi.as_slice();
                         let padding = words.saturating_sub(slo.len());
-                        FiniteNimber(
-                            slo.iter()
-                                .cloned()
-                                .take(words)
-                                .chain(core::iter::repeat(0).take(padding))
-                                .chain(shi.iter().cloned().take(words))
-                                .collect(),
-                        )
+                        let vec: Vec<_> = slo
+                            .iter()
+                            .cloned()
+                            .take(words)
+                            .chain(core::iter::repeat(0).take(padding))
+                            .chain(shi.iter().cloned().take(words))
+                            .collect();
+                        FiniteNimber::from(vec)
                     }
-                    None => FiniteNimber(self.as_slice().into()),
+                    None => FiniteNimber::from(self.as_slice()),
                 }
             }
         }
@@ -208,9 +226,19 @@ impl<'a> FiniteNimberRef<'a> {
 
 impl FiniteNimber {
     fn to_ref(&self) -> FiniteNimberRef {
-        match self.0.len() {
-            0 => FiniteNimberRef::zero(),
-            _ => Slice(&self.0),
+        match &self.0 {
+            FiniteNimberEnum::Single(w) => FiniteNimberRef::Single(*w),
+            FiniteNimberEnum::Vector(v) => match v.len() {
+                0 => FiniteNimberRef::zero(),
+                _ => FiniteNimberRef::Slice(&v),
+            },
+        }
+    }
+
+    fn as_slice(&self) -> &[Word] {
+        match &self.0 {
+            FiniteNimberEnum::Single(w) => core::slice::from_ref(w),
+            FiniteNimberEnum::Vector(v) => &v,
         }
     }
 }
@@ -223,25 +251,49 @@ impl<'a> From<&'a FiniteNimber> for FiniteNimberRef<'a> {
 
 impl From<FiniteNimberRef<'_>> for FiniteNimber {
     fn from(n: FiniteNimberRef) -> Self {
-        FiniteNimber(n.as_slice().into())
+        match n {
+            FiniteNimberRef::Single(w) => Self(FiniteNimberEnum::Single(w)),
+            FiniteNimberRef::Slice(v) => FiniteNimber::from(v),
+        }
     }
 }
 
 impl From<Word> for FiniteNimber {
     fn from(val: Word) -> FiniteNimber {
-        FiniteNimber(vec![val])
+        Self(FiniteNimberEnum::Single(val))
     }
 }
 
 impl From<Vec<Word>> for FiniteNimber {
-    fn from(val: Vec<Word>) -> FiniteNimber {
-        FiniteNimber(val)
+    fn from(mut vec: Vec<Word>) -> FiniteNimber {
+        match vec
+            .iter()
+            .enumerate()
+            .rev()
+            .find(|(_index, word)| **word != 0)
+        {
+            None => Self(FiniteNimberEnum::Single(0)),
+            Some((0, w)) => Self(FiniteNimberEnum::Single(*w)),
+            Some((pos, _)) => {
+                vec.truncate(pos + 1);
+                Self(FiniteNimberEnum::Vector(vec))
+            }
+        }
     }
 }
 
 impl From<&[Word]> for FiniteNimber {
-    fn from(val: &[Word]) -> FiniteNimber {
-        FiniteNimber(val.into())
+    fn from(slice: &[Word]) -> FiniteNimber {
+        match slice
+            .iter()
+            .enumerate()
+            .rev()
+            .find(|(_index, word)| **word != 0)
+        {
+            None => Self(FiniteNimberEnum::Single(0)),
+            Some((0, w)) => Self(FiniteNimberEnum::Single(*w)),
+            Some(_) => Self(FiniteNimberEnum::Vector(slice.into()))
+        }
     }
 }
 
@@ -322,14 +374,13 @@ macro_rules! impl_binop_wrappers {
 impl<'a, 'b> Add<FiniteNimberRef<'a>> for FiniteNimberRef<'b> {
     type Output = FiniteNimber;
     fn add(self, other: FiniteNimberRef<'a>) -> FiniteNimber {
-        FiniteNimber(
-            self.as_slice()
-                .iter()
-                .cloned()
-                .zip_longest(other.as_slice().iter().cloned())
-                .map(|pair| pair.reduce(|a, b| a ^ b))
-                .collect(),
-        )
+        let vec: Vec<_> = self.as_slice()
+            .iter()
+            .cloned()
+            .zip_longest(other.as_slice().iter().cloned())
+            .map(|pair| pair.reduce(|a, b| a ^ b))
+            .collect();
+        FiniteNimber::from(vec)
     }
 }