chiark / gitweb /
decrement compiles
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 10 Oct 2020 22:53:24 +0000 (23:53 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 10 Oct 2020 22:53:24 +0000 (23:53 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
zcoord/zcoord.rs

index bf3a9bfb02b02332faaae1b7bf4043bff73e6161..6e85c9801eb340e6b7f4aab5b161e1798c5a297e 100644 (file)
 //    Pick a value earlier than a specified value.
 //
 //       Try to subtract delta from rightmost nonzero limb, with
-//       borrow.  If this would underflow, or would leave leftmost
-//       limb equal to 0000000000, start again: decrement rightmost
-//       nonzero limb by 1, with borrow, then add two limbs
-//       vvvvvvvvvv, and redo.
+//       borrow.  If this would underflow, or would change leftmost
+//       limb to 0000000000, start again: decrement rightmost nonzero
+//       limb by 1, with borrow, then add two limbs vvvvvvvvvv, and
+//       redo.
 
 use std::cmp::{Ordering, max};
 use std::convert::{TryFrom, TryInto};
@@ -77,23 +77,25 @@ use thiserror::Error;
 const BITS_PER_DIGIT : usize = 5;
 const DIGITS_PER_LIMB : usize = 10;
 
+type RawLimbVal = u64;
+type LimbVal = Wrapping<RawLimbVal>;
+
 const DELTA : LimbVal = Wrapping(0x4000_0000);
 const ZERO : LimbVal = Wrapping(0);
 const ONE : LimbVal = Wrapping(1);
 
+const RAW_LIMB_MODULUS : RawLimbVal = 1u64 << BITS_PER_LIMB;
+
 const BITS_PER_LIMB : usize = BITS_PER_DIGIT * DIGITS_PER_LIMB;
 const DIGIT_MASK : LimbVal = Wrapping((1u64 << BITS_PER_DIGIT) - 1);
 const TEXT_PER_LIMB : usize = DIGITS_PER_LIMB + 1;
-const LIMB_MODULUS : LimbVal = Wrapping(1u64 << BITS_PER_LIMB);
-const LIMB_MASK    : LimbVal = Wrapping((1u64 << BITS_PER_LIMB)-1);
+const LIMB_MODULUS : LimbVal = Wrapping(RAW_LIMB_MODULUS);
+const LIMB_MASK    : LimbVal = Wrapping(RAW_LIMB_MODULUS-1);
 
 #[derive(Deserialize)]
 #[serde(try_from="&str")]
 pub struct ZCoord(innards::Innards);
 
-type RawLimbVal = u64;
-type LimbVal = Wrapping<RawLimbVal>;
-
 #[derive(Error,Clone,Copy,Debug)]
 #[error("error parsing zcoord (z value)")]
 pub struct ParseError;
@@ -127,38 +129,71 @@ impl From<TryFromIntError> for Overflow {
   fn from(_: TryFromIntError) -> Overflow { Overflow }
 }
 
+trait IncDecOffset {
+  const INIT_DELTA  : LimbVal;
+  const CARRY_DELTA : LimbVal;
+  const NEW_LIMBS   : LimbVal;
+  fn check_underflow(m: &Mutable, i: usize, nv: LimbVal) -> Option<()>;
+}
+
+struct IncDecInc;
+impl IncDecOffset for IncDecInc {
+  const INIT_DELTA  : LimbVal = DELTA;
+  const CARRY_DELTA : LimbVal = ONE;
+  const NEW_LIMBS   : LimbVal = ZERO;
+  #[throws(as Option)]
+  fn check_underflow(_: &Mutable, _: usize, _: LimbVal) { }
+}
+
+struct IncDecDec;
+impl IncDecOffset for IncDecDec {
+  const INIT_DELTA  : LimbVal = Wrapping(DELTA.0.wrapping_neg());
+  const CARRY_DELTA : LimbVal = Wrapping(ONE  .0.wrapping_neg());
+  const NEW_LIMBS   : LimbVal = LIMB_MASK;
+  #[throws(as Option)]
+  fn check_underflow(_: &Mutable, i: usize, nv: LimbVal) {
+    if i == 0 && nv == ZERO { throw!() }
+  }
+}
+
 impl Mutable {
   #[throws(Overflow)]
-  pub fn increment(&mut self) -> ZCoord {
+  fn incdec<ID:IncDecOffset>(&mut self, _: ID) -> ZCoord {
     'attempt: loop {
       let mut i = self.limbs.len() - 1;
-      let mut delta = DELTA;
+      let mut delta = ID::INIT_DELTA;
 
       if (||{
         loop {
           let nv = self.limbs[i] + delta;
           self.limbs[i] = nv & LIMB_MASK;
+          ID::check_underflow(self, i, nv)?;
           if nv < LIMB_MODULUS { return Some(()) }
           if i == 0 { return None }
           i -= 1;
-          delta = ONE;
+          delta = ID::CARRY_DELTA;
         }
       })() == Some(()) { break 'attempt }
 
       // undo
       loop {
         if i >= self.limbs.len() { break }
-        else if i == self.limbs.len()-1 { delta = DELTA; }
+        else if i == self.limbs.len()-1 { delta = ID::INIT_DELTA; }
         let nv = self.limbs[i] - delta;
         self.limbs[i] = nv & LIMB_MASK;
         i += 1;
       }
-      self.limbs.push(ZERO);
-      self.limbs.push(ZERO);
+      self.limbs.push(ID::NEW_LIMBS);
+      self.limbs.push(ID::NEW_LIMBS);
     }
     self.repack()?
   }
 
+  #[throws(Overflow)]
+  pub fn increment(&mut self) -> ZCoord { self.incdec(IncDecInc)? }
+  #[throws(Overflow)]
+  pub fn decrement(&mut self) -> ZCoord { self.incdec(IncDecDec)? }
+
   #[throws(Overflow)]
   pub fn repack(&self) -> ZCoord { self.try_into()? }
 }