use bit_vec::BitVec;
use visioncortex::PointF64;
pub const EPSILON: f64 = std::f64::EPSILON;
pub fn f64_approximately(a: f64, b: f64) -> bool {
(a - b).abs() <= EPSILON
}
pub fn normalize_point_f64(p: &PointF64) -> PointF64 {
let norm = p.norm();
PointF64::new(p.x / norm, p.y / norm)
}
pub fn normalize_vec_f64(v: &[f64]) -> Vec<f64> {
let norm = v.iter().fold(0.0, |acc, element| acc + element * element).sqrt();
v.iter().map(|element| element / norm).collect()
}
pub fn euclid_dist_f64(p1: &PointF64, p2: &PointF64) -> f64 {
(*p1-*p2).norm()
}
pub fn euclid_dist_vec_f64(v1: &[f64], v2: &[f64]) -> f64 {
if v1.len() != v2.len() {
panic!("Lengths of vectors do not agree.");
}
v1.iter().enumerate()
.fold(0.0, |acc, (i, element)| acc + (element - v2[i])*(element - v2[i]))
.sqrt()
}
pub fn clockwise_points_f64(p1: &PointF64, p2: &PointF64, p3: &PointF64) -> bool {
let cross_product_z_component = |a: PointF64, b: PointF64| { a.x * b.y - a.y * b.x };
let p1p2 = *p2 - *p1;
let p1p3 = *p3 - *p1;
let cross_z = cross_product_z_component(p1p2, p1p3);
cross_z > EPSILON && cross_z.is_sign_positive()
}
pub fn num_bits_to_store(n: usize) -> usize {
if n == 0 {
return 0; } else if n == 1 {
return 1; }
let n = n-1; num_significant_bits(n)
}
pub fn num_significant_bits(n: usize) -> usize {
(0_usize.leading_zeros() - n.leading_zeros()) as usize
}
pub fn into_bitvec(mut n: usize, len: usize) -> BitVec {
if len < num_significant_bits(n) {
panic!(format!("Not enough bits to store {}", n));
}
let mut bitvec = BitVec::from_elem(len, false);
for i in (0..len).rev() {
bitvec.set(i, n % 2 == 1);
n >>= 1;
}
bitvec
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn math_euclid_dist_unit() {
let p1 = PointF64::new(1.0, 0.0);
let p2 = PointF64::new(0.0, 1.0);
let dist = euclid_dist_f64(&p1, &p2);
assert!(f64_approximately(dist, 2.0_f64.sqrt()))
}
#[test]
fn math_euclid_dist_zero() {
let p1 = PointF64::new(0.0, 0.0);
let p2 = PointF64::new(0.0, 0.0);
let dist = euclid_dist_f64(&p1, &p2);
assert!(f64_approximately(dist, 0.0))
}
#[test]
fn math_euclid_dist_regular() {
let p1 = PointF64::new(3.0, 4.0);
let p2 = PointF64::new(1.0, 5.0);
let dist = euclid_dist_f64(&p1, &p2);
assert!(f64_approximately(dist, 5.0_f64.sqrt()))
}
#[test]
fn math_clockwise_points() {
assert!(clockwise_points_f64(&PointF64::new(0.0, 0.0), &PointF64::new(1.0, 0.0), &PointF64::new(0.0, 1.0)));
assert!(!clockwise_points_f64(&PointF64::new(0.0, 0.0), &PointF64::new(0.0, 1.0), &PointF64::new(1.0, 0.0)));
assert!(!clockwise_points_f64(&PointF64::new(0.0, 0.0), &PointF64::new(1.0, 1.0), &PointF64::new(2.0, 2.0)));
assert!(!clockwise_points_f64(&PointF64::new(1.0, 1.0), &PointF64::new(1.0, 1.0), &PointF64::new(1.0, 1.0)));
assert!(clockwise_points_f64(&PointF64::new(7.3, 5.2), &PointF64::new(10.5, 2.5), &PointF64::new(15.6, 4.2)));
assert!(!clockwise_points_f64(&PointF64::new(23.3, 6.8), &PointF64::new(30.1, 14.7), &PointF64::new(27.5, 11.4)));
}
#[test]
fn math_num_bits_to_store() {
assert_eq!(num_bits_to_store(0), 0); assert_eq!(num_bits_to_store(1), 1); assert_eq!(num_bits_to_store(2), 1); assert_eq!(num_bits_to_store(3), 2); assert_eq!(num_bits_to_store(32), 5); assert_eq!(num_bits_to_store(33), 6); }
#[test]
fn math_euclid_dist_vec() {
let v1 = vec![1.0, 1.0, 1.0];
let v2 = vec![1.0, 1.0, 1.0];
assert!(f64_approximately(euclid_dist_vec_f64(&v1, &v2), 0.0));
let v1 = vec![1.0, 0.5, 1.0];
let v2 = vec![2.0, 1.0, 3.0];
println!("{}", euclid_dist_vec_f64(&v1, &v2));
assert!(f64_approximately(euclid_dist_vec_f64(&v1, &v2), 2.29128784747792));
}
#[test]
fn math_into_bitvec() {
let n = 0;
assert!(into_bitvec(n, 1).eq_vec(&[false]));
let n = 1;
assert!(into_bitvec(n, 1).eq_vec(&[true]));
let n = 9;
assert!(into_bitvec(n, 4).eq_vec(&[true, false, false, true]));
let n = 14;
assert!(into_bitvec(n, 4).eq_vec(&[true, true, true, false]));
let n = 20;
assert!(into_bitvec(n, 5).eq_vec(&[true, false, true, false, false]));
}
}