add more fpnum functions
authoralfadur
Tue, 16 Oct 2018 04:43:02 +0300
changeset 13930 2ee07e751171
parent 13929 a140f28decc4
child 13931 2717a5289d88
add more fpnum functions
rust/fpnum/src/lib.rs
--- a/rust/fpnum/src/lib.rs	Mon Oct 15 23:10:03 2018 +0200
+++ b/rust/fpnum/src/lib.rs	Tue Oct 16 04:43:02 2018 +0300
@@ -84,6 +84,15 @@
             value: r << 16,
         }
     }
+
+    fn with_sign(&self, is_negative: bool) -> FPNum {
+        FPNum{ is_negative, ..*self}
+    }
+
+    #[inline]
+    fn with_sign_as(self, other: FPNum) -> FPNum {
+        self.with_sign(other.is_negative)
+    }
 }
 
 impl From<i32> for FPNum {
@@ -277,10 +286,127 @@
     ($n: tt) => { FPNum::from($n) };
 }
 
+const LINEARIZE_TRESHOLD: u64 = 0x1_0000;
+
+#[derive(Clone, Copy, Debug)]
+struct FPPoint {
+    x_is_negative: bool,
+    y_is_negative: bool,
+    x_value: u64,
+    y_value: u64,
+}
+
+impl FPPoint {
+    #[inline]
+    fn new(x: FPNum, y: FPNum) -> Self {
+        Self {
+            x_is_negative: x.is_negative,
+            y_is_negative: y.is_negative,
+            x_value: x.value,
+            y_value: y.value
+        }
+    }
+
+    #[inline]
+    fn zero() -> FPPoint { FPPoint::new(fp!(0), fp!(0)) }
+
+    #[inline]
+    fn unit_x() -> FPPoint { FPPoint::new(fp!(1), fp!(0)) }
+
+    #[inline]
+    fn unit_y() -> FPPoint { FPPoint::new(fp!(0), fp!(1)) }
+
+    #[inline]
+    fn x(&self) -> FPNum {
+        FPNum {
+            is_negative: self.x_is_negative,
+            value: self.x_value
+        }
+    }
+
+    #[inline]
+    fn y(&self) -> FPNum {
+        FPNum {
+            is_negative: self.y_is_negative,
+            value: self.y_value
+        }
+    }
+
+    #[inline]
+    fn sqr_distance(&self) -> FPNum {
+        self.x().sqr() + self.y().sqr()
+    }
+
+    #[inline]
+    fn distance(&self) -> FPNum {
+        let r = self.x_value | self.y_value;
+        if r < LINEARIZE_TRESHOLD {
+            FPNum::from(r as u32)
+        } else {
+            self.sqr_distance().sqrt()
+        }
+    }
+
+    #[inline]
+    fn is_in_range(&self, radius: FPNum) -> bool {
+        std::cmp::max(self.x(), self.y()) < radius
+            && self.sqr_distance() < radius.sqr()
+    }
+
+    #[inline]
+    fn dot(&self, other: &FPPoint) -> FPNum {
+        self.x() * other.x() + self.y() * other.y()
+    }
+}
+
+impl PartialEq for FPPoint {
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        self.x() == other.x() && self.y() == other.y()
+    }
+}
+
+impl Eq for FPPoint {}
+
+impl ops::Neg for FPPoint {
+    type Output = Self;
+
+    #[inline]
+    fn neg(self) -> Self {
+        Self::new(-self.x(), -self.y())
+    }
+}
+
+macro_rules! bin_op_impl {
+    ($op: path, $name: tt) => {
+        impl $op for FPPoint {
+            type Output = Self;
+
+            #[inline]
+            fn $name(self, rhs: Self) -> Self {
+                Self::new(self.x().$name(rhs.x()),
+                          self.y().$name(rhs.y()))
+            }
+        }
+    }
+}
+
+bin_op_impl!(ops::Add, add);
+bin_op_impl!(ops::Sub, sub);
+bin_op_impl!(ops::Mul, mul);
+
+#[inline]
+fn distance<T>(x: T, y: T) -> FPNum
+    where T: ops::Add + ops::Mul + Copy +
+             From<<T as ops::Add>::Output> +
+             From<<T as ops::Mul>::Output> +
+             Into<FPNum>
+{
+    let sqr: FPNum = T::from(T::from(x * x) + T::from(y * y)).into();
+    sqr.sqrt()
+}
+
 /* TODO:
- Distance
- DistanceI
- SignAs
  AngleSin
  AngleCos
 */
@@ -313,6 +439,7 @@
     assert!((-z).is_negative);
     assert_eq!(n - n, z);
     assert_eq!(-n + n, z);
+    assert_eq!(z.with_sign_as(-n), -z);
 }
 
 #[test]
@@ -352,3 +479,15 @@
 
     assert_eq!((n1_5 * n1_5 * n1_5.sqr()).sqrt(), n2_25);
 }
+
+#[test]
+fn point() {
+    let z = FPPoint::zero();
+    let p = FPPoint::new(fp!(1), fp!(-2));
+
+    assert_eq!(p.sqr_distance(), fp!(5));
+    assert_eq!(p + -p, FPPoint::zero());
+    assert_eq!(p * z, z);
+    assert_eq!(p.dot(&z), fp!(0));
+    assert_eq!(distance(4, 3), fp!(5));
+}
\ No newline at end of file