rust/fpnum/src/lib.rs
changeset 14143 c27461e6a9eb
parent 14102 5d42204ac35e
child 14160 37c99587825d
equal deleted inserted replaced
14142:69db1d2e4cec 14143:c27461e6a9eb
     1 use std::cmp;
     1 use std::cmp;
     2 use std::ops;
     2 use std::ops;
       
     3 use std::ops::Shl;
     3 
     4 
     4 #[derive(Clone, Debug, Copy)]
     5 #[derive(Clone, Debug, Copy)]
     5 pub struct FPNum {
     6 pub struct FPNum {
     6     is_negative: bool,
     7     is_negative: bool,
     7     value: u64,
     8     value: u64,
    91         }
    92         }
    92     }
    93     }
    93 
    94 
    94     #[inline]
    95     #[inline]
    95     pub fn with_sign(&self, is_negative: bool) -> FPNum {
    96     pub fn with_sign(&self, is_negative: bool) -> FPNum {
    96         FPNum{ is_negative, ..*self}
    97         FPNum {
       
    98             is_negative,
       
    99             ..*self
       
   100         }
    97     }
   101     }
    98 
   102 
    99     #[inline]
   103     #[inline]
   100     pub fn with_sign_as(self, other: FPNum) -> FPNum {
   104     pub fn with_sign_as(self, other: FPNum) -> FPNum {
   101         self.with_sign(other.is_negative)
   105         self.with_sign(other.is_negative)
   291     }
   295     }
   292 }
   296 }
   293 
   297 
   294 #[macro_export]
   298 #[macro_export]
   295 macro_rules! fp {
   299 macro_rules! fp {
   296     (-$n: tt / $d: tt) => { FPNum::new(-$n, $d) };
   300     (-$n: tt / $d: tt) => {
   297     ($n: tt / $d: tt) => { FPNum::new($n, $d) };
   301         FPNum::new(-$n, $d)
   298     (-$n: tt) => { FPNum::from(-$n) };
   302     };
   299     ($n: tt) => { FPNum::from($n) };
   303     ($n: tt / $d: tt) => {
       
   304         FPNum::new($n, $d)
       
   305     };
       
   306     (-$n: tt) => {
       
   307         FPNum::from(-$n)
       
   308     };
       
   309     ($n: tt) => {
       
   310         FPNum::from($n)
       
   311     };
   300 }
   312 }
   301 
   313 
   302 const LINEARIZE_TRESHOLD: u64 = 0x1_0000;
   314 const LINEARIZE_TRESHOLD: u64 = 0x1_0000;
   303 
   315 
   304 #[derive(Clone, Copy, Debug)]
   316 #[derive(Clone, Copy, Debug)]
   314     pub fn new(x: FPNum, y: FPNum) -> Self {
   326     pub fn new(x: FPNum, y: FPNum) -> Self {
   315         Self {
   327         Self {
   316             x_is_negative: x.is_negative,
   328             x_is_negative: x.is_negative,
   317             y_is_negative: y.is_negative,
   329             y_is_negative: y.is_negative,
   318             x_value: x.value,
   330             x_value: x.value,
   319             y_value: y.value
   331             y_value: y.value,
   320         }
   332         }
   321     }
   333     }
   322 
   334 
   323     #[inline]
   335     #[inline]
   324     pub fn zero() -> FPPoint { FPPoint::new(fp!(0), fp!(0)) }
   336     pub fn zero() -> FPPoint {
   325 
   337         FPPoint::new(fp!(0), fp!(0))
   326     #[inline]
   338     }
   327     pub fn unit_x() -> FPPoint { FPPoint::new(fp!(1), fp!(0)) }
   339 
   328 
   340     #[inline]
   329     #[inline]
   341     pub fn unit_x() -> FPPoint {
   330     pub fn unit_y() -> FPPoint { FPPoint::new(fp!(0), fp!(1)) }
   342         FPPoint::new(fp!(1), fp!(0))
       
   343     }
       
   344 
       
   345     #[inline]
       
   346     pub fn unit_y() -> FPPoint {
       
   347         FPPoint::new(fp!(0), fp!(1))
       
   348     }
   331 
   349 
   332     #[inline]
   350     #[inline]
   333     pub fn x(&self) -> FPNum {
   351     pub fn x(&self) -> FPNum {
   334         FPNum {
   352         FPNum {
   335             is_negative: self.x_is_negative,
   353             is_negative: self.x_is_negative,
   336             value: self.x_value
   354             value: self.x_value,
   337         }
   355         }
   338     }
   356     }
   339 
   357 
   340     #[inline]
   358     #[inline]
   341     pub fn y(&self) -> FPNum {
   359     pub fn y(&self) -> FPNum {
   342         FPNum {
   360         FPNum {
   343             is_negative: self.y_is_negative,
   361             is_negative: self.y_is_negative,
   344             value: self.y_value
   362             value: self.y_value,
   345         }
   363         }
   346     }
   364     }
   347 
   365 
   348     #[inline]
   366     #[inline]
   349     pub fn max_norm(&self) -> FPNum {
   367     pub fn max_norm(&self) -> FPNum {
   359     pub fn distance(&self) -> FPNum {
   377     pub fn distance(&self) -> FPNum {
   360         let r = self.x_value | self.y_value;
   378         let r = self.x_value | self.y_value;
   361         if r < LINEARIZE_TRESHOLD {
   379         if r < LINEARIZE_TRESHOLD {
   362             FPNum::from(r as u32)
   380             FPNum::from(r as u32)
   363         } else {
   381         } else {
   364             self.sqr_distance().sqrt()
   382             let mut sqr: u128 = (self.x_value as u128).pow(2) + (self.y_value as u128).pow(2);
       
   383 
       
   384             let mut t: u128 = 0x40000000_00000000_00000000_00000000;
       
   385             let mut r: u128 = 0;
       
   386 
       
   387             for _ in 0..64 {
       
   388                 let s = r + t;
       
   389                 r >>= 1;
       
   390 
       
   391                 if s <= sqr {
       
   392                     sqr -= s;
       
   393                     r += t;
       
   394                 }
       
   395                 t >>= 2;
       
   396             }
       
   397 
       
   398             FPNum {
       
   399                 is_negative: false,
       
   400                 value: r as u64,
       
   401             }
   365         }
   402         }
   366     }
   403     }
   367 
   404 
   368     #[inline]
   405     #[inline]
   369     pub fn is_in_range(&self, radius: FPNum) -> bool {
   406     pub fn is_in_range(&self, radius: FPNum) -> bool {
   399         impl $op for FPPoint {
   436         impl $op for FPPoint {
   400             type Output = Self;
   437             type Output = Self;
   401 
   438 
   402             #[inline]
   439             #[inline]
   403             fn $name(self, rhs: Self) -> Self {
   440             fn $name(self, rhs: Self) -> Self {
   404                 Self::new(self.x().$name(rhs.x()),
   441                 Self::new(self.x().$name(rhs.x()), self.y().$name(rhs.y()))
   405                           self.y().$name(rhs.y()))
   442             }
   406             }
   443         }
   407         }
   444     };
   408     }
       
   409 }
   445 }
   410 
   446 
   411 macro_rules! right_scalar_bin_op_impl {
   447 macro_rules! right_scalar_bin_op_impl {
   412     ($($op: tt)::+, $name: tt) => {
   448     ($($op: tt)::+, $name: tt) => {
   413         impl $($op)::+<FPNum> for FPPoint {
   449         impl $($op)::+<FPNum> for FPPoint {
   475 bin_assign_op_impl!(FPPoint, ops::AddAssign<FPNum>, add_assign, +);
   511 bin_assign_op_impl!(FPPoint, ops::AddAssign<FPNum>, add_assign, +);
   476 bin_assign_op_impl!(FPPoint, ops::SubAssign<FPNum>, sub_assign, -);
   512 bin_assign_op_impl!(FPPoint, ops::SubAssign<FPNum>, sub_assign, -);
   477 bin_assign_op_impl!(FPPoint, ops::MulAssign<FPNum>, mul_assign, *);
   513 bin_assign_op_impl!(FPPoint, ops::MulAssign<FPNum>, mul_assign, *);
   478 bin_assign_op_impl!(FPPoint, ops::DivAssign<FPNum>, div_assign, /);
   514 bin_assign_op_impl!(FPPoint, ops::DivAssign<FPNum>, div_assign, /);
   479 
   515 
   480 #[inline]
       
   481 pub fn distance<T>(x: T, y: T) -> FPNum
   516 pub fn distance<T>(x: T, y: T) -> FPNum
   482     where T: ops::Add + ops::Mul + Copy +
   517 where
   483              From<<T as ops::Add>::Output> +
   518     T: Into<i64> + std::fmt::Debug,
   484              From<<T as ops::Mul>::Output> +
       
   485              Into<FPNum>
       
   486 {
   519 {
   487     let sqr: FPNum = T::from(T::from(x * x) + T::from(y * y)).into();
   520     let mut sqr: u128 = (x.into().pow(2) as u128).shl(64) + (y.into().pow(2) as u128).shl(64);
   488     sqr.sqrt()
   521 
       
   522     let mut t: u128 = 0x40000000_00000000_00000000_00000000;
       
   523     let mut r: u128 = 0;
       
   524 
       
   525     for _ in 0..64 {
       
   526         let s = r + t;
       
   527         r >>= 1;
       
   528 
       
   529         if s <= sqr {
       
   530             sqr -= s;
       
   531             r += t;
       
   532         }
       
   533         t >>= 2;
       
   534     }
       
   535 
       
   536     FPNum {
       
   537         is_negative: false,
       
   538         value: r as u64,
       
   539     }
   489 }
   540 }
   490 
   541 
   491 /* TODO:
   542 /* TODO:
   492  AngleSin
   543  AngleSin
   493  AngleCos
   544  AngleCos
   494 */
   545 */
   495 
   546 
   496 #[cfg(test)]
   547 #[cfg(test)]
   497 #[test]
   548 #[test]
   498 fn basics() {
   549 fn basics() {
   499     let n = fp!(15/2);
   550     let n = fp!(15 / 2);
   500     assert!(n.is_positive());
   551     assert!(n.is_positive());
   501     assert!(!n.is_negative());
   552     assert!(!n.is_negative());
   502 
   553 
   503     assert!(!(-n).is_positive());
   554     assert!(!(-n).is_positive());
   504     assert!((-n).is_negative());
   555     assert!((-n).is_negative());
   505 
   556 
   506     assert_eq!(-(-n), n);
   557     assert_eq!(-(-n), n);
   507     assert_eq!((-n).abs(), n);
   558     assert_eq!((-n).abs(), n);
   508     assert_eq!(-n, fp!(-15/2));
   559     assert_eq!(-n, fp!(-15 / 2));
   509 
   560 
   510     assert_eq!(n.round(), 7);
   561     assert_eq!(n.round(), 7);
   511     assert_eq!((-n).round(), -7);
   562     assert_eq!((-n).round(), -7);
   512 }
   563 }
   513 
   564 
   514 #[test]
   565 #[test]
   515 fn zero() {
   566 fn zero() {
   516     let z = fp!(0);
   567     let z = fp!(0);
   517     let n = fp!(15/2);
   568     let n = fp!(15 / 2);
   518 
   569 
   519     assert!(z.is_zero());
   570     assert!(z.is_zero());
   520     assert!(z.is_positive());
   571     assert!(z.is_positive());
   521     assert!((-z).is_negative);
   572     assert!((-z).is_negative);
   522     assert_eq!(n - n, z);
   573     assert_eq!(n - n, z);
   525 }
   576 }
   526 
   577 
   527 #[test]
   578 #[test]
   528 fn ord() {
   579 fn ord() {
   529     let z = fp!(0);
   580     let z = fp!(0);
   530     let n1_5 = fp!(3/2);
   581     let n1_5 = fp!(3 / 2);
   531     let n2_25 = fp!(9/4);
   582     let n2_25 = fp!(9 / 4);
   532 
   583 
   533     assert!(!(z > z));
   584     assert!(!(z > z));
   534     assert!(!(z < z));
   585     assert!(!(z < z));
   535     assert!(n2_25 > n1_5);
   586     assert!(n2_25 > n1_5);
   536     assert!(-n2_25 < n1_5);
   587     assert!(-n2_25 < n1_5);
   537     assert!(-n2_25 < -n1_5);
   588     assert!(-n2_25 < -n1_5);
   538 }
   589 }
   539 
   590 
   540 #[test]
   591 #[test]
   541 fn arith() {
   592 fn arith() {
   542     let n1_5 = fp!(3/2);
   593     let n1_5 = fp!(3 / 2);
   543     let n2_25 = fp!(9/4);
   594     let n2_25 = fp!(9 / 4);
   544     let n_0_15 = fp!(-15/100);
   595     let n_0_15 = fp!(-15 / 100);
   545 
   596 
   546     assert_eq!(n1_5 + n1_5, fp!(3));
   597     assert_eq!(n1_5 + n1_5, fp!(3));
   547     assert_eq!(-n1_5 - n1_5, fp!(-3));
   598     assert_eq!(-n1_5 - n1_5, fp!(-3));
   548 
   599 
   549     assert_eq!(n1_5 * n1_5, n2_25);
   600     assert_eq!(n1_5 * n1_5, n2_25);
   561 
   612 
   562     assert_eq!((n1_5 * n1_5 * n1_5.sqr()).sqrt(), n2_25);
   613     assert_eq!((n1_5 * n1_5 * n1_5.sqr()).sqrt(), n2_25);
   563 
   614 
   564     let mut m = fp!(1);
   615     let mut m = fp!(1);
   565     m += n1_5;
   616     m += n1_5;
   566     assert_eq!(m, fp!(5/2));
   617     assert_eq!(m, fp!(5 / 2));
       
   618 }
       
   619 
       
   620 #[test]
       
   621 fn test_distance_high_values() {
       
   622     assert_eq!(distance(1_000_000i32, 0), fp!(1_000_000));
       
   623     assert_eq!(
       
   624         FPPoint::new(fp!(1_000_000), fp!(0)).distance(),
       
   625         fp!(1_000_000)
       
   626     );
   567 }
   627 }
   568 
   628 
   569 #[test]
   629 #[test]
   570 fn point() {
   630 fn point() {
   571     let z = FPPoint::zero();
   631     let z = FPPoint::zero();
   572     let n = fp!(16/9);
   632     let n = fp!(16 / 9);
   573     let p = FPPoint::new(fp!(1), fp!(-2));
   633     let p = FPPoint::new(fp!(1), fp!(-2));
   574 
   634 
   575     assert_eq!(p.sqr_distance(), fp!(5));
   635     assert_eq!(p.sqr_distance(), fp!(5));
   576     assert_eq!(p + -p, FPPoint::zero());
   636     assert_eq!(p + -p, FPPoint::zero());
   577     assert_eq!(p * z, z);
   637     assert_eq!(p * z, z);