chiark / gitweb /
Implement fast squaring, and use it in Div
authorSimon Tatham <anakin@pobox.com>
Sat, 12 Apr 2025 11:57:55 +0000 (12:57 +0100)
committerSimon Tatham <anakin@pobox.com>
Sat, 12 Apr 2025 11:57:55 +0000 (12:57 +0100)
src/finitenimber.rs

index c74e3a1d98763896cd5d8d57ae10a3741b41abe4..974152aa9e9e8b3334ecd1a96bebf34a82635a11 100644 (file)
@@ -356,6 +356,22 @@ impl<'a> FiniteNimberRef<'a> {
         }
     }
 
+    fn square_recurse(self, level: usize) -> FiniteNimber {
+        match level.checked_sub(1) {
+            Some(sublevel) => {
+                let (lo, hi) = self.split(sublevel);
+                let lo2 = lo.square_recurse(sublevel);
+                let hi2 = hi.square_recurse(sublevel);
+                (lo2 + hi2.to_ref().mul_by_h(sublevel))
+                    .to_ref()
+                    .join(hi2.to_ref(), sublevel)
+            }
+
+            // At level 0, both elements of GF(2) square to themselves.
+            None => self.into(),
+        }
+    }
+
     fn mul_recurse(
         self,
         other: FiniteNimberRef<'a>,
@@ -380,6 +396,16 @@ impl<'a> FiniteNimberRef<'a> {
     }
 }
 
+impl FiniteNimber {
+    /// Compute the square of a nimber, faster than the general
+    /// multiplication algorithm performed by the `Mul` trait can do
+    /// it.
+    pub fn square(&self) -> FiniteNimber {
+        let r = self.to_ref();
+        r.square_recurse(r.level())
+    }
+}
+
 impl<'a, 'b> Mul<FiniteNimberRef<'a>> for FiniteNimberRef<'b> {
     type Output = FiniteNimber;
     fn mul(self, other: FiniteNimberRef<'a>) -> FiniteNimber {
@@ -392,14 +418,14 @@ impl<'a> FiniteNimberRef<'a> {
     fn inverse_recurse(self, level: usize) -> FiniteNimber {
         match level.checked_sub(1) {
             Some(sublevel) => {
-                let (alo, ahi) = self.split(sublevel);
-                let sum = alo + ahi;
-                let sq = ahi * ahi; // todo! dedicated squaring function
-                let det = alo * sum.to_ref() + sq.to_ref().mul_by_h(sublevel);
+                let (lo, hi) = self.split(sublevel);
+                let sum = lo + hi;
+                let sq = hi.square_recurse(sublevel);
+                let det = lo * sum.to_ref() + sq.to_ref().mul_by_h(sublevel);
                 let detinv = det.to_ref().inverse_recurse(sublevel);
                 (detinv.to_ref() * sum)
                     .to_ref()
-                    .join((detinv.to_ref() * ahi).to_ref(), sublevel)
+                    .join((detinv.to_ref() * hi).to_ref(), sublevel)
             }
 
             // At level 0, we're in GF(2), so the only invertible
@@ -702,6 +728,42 @@ mod tests {
         );
     }
 
+    #[test]
+    fn square() {
+        assert_eq!(
+            FiniteNimber::from(0x80).square(),
+            FiniteNimber::from(0xde)
+        );
+        assert_eq!(
+            FiniteNimber::from(0x8000).square(),
+            FiniteNimber::from(0xde4a)
+        );
+
+        assert_eq!(
+            FiniteNimber::from(vec![
+                0x3f84d5b5b5470917,
+                0xc0ac29b7c97c50dd,
+                0xbe5466cf34e90c6c,
+                0x452821e638d01377,
+                0x082efa98ec4e6c89,
+                0xa4093822299f31d0,
+                0x13198a2e03707344,
+                0x243f6a8885a308d3,
+            ])
+            .square(),
+            FiniteNimber::from(vec![
+                0xd0e74a0945d35342,
+                0xfa91473032b5438a,
+                0xe17bdc047300f99d,
+                0x1a51cba4adb8ddb9,
+                0xdc06e76f54f372c0,
+                0xe74d7b7c65542a19,
+                0xffe69bcb391c628b,
+                0x32ae6e49dcd65156,
+            ])
+        );
+    }
+
     #[test]
     fn div() {
         assert_eq!(