use num_traits::{One, Zero};
use core::ops::{Add, Div, Mul, Sub};
#[inline]
pub fn archimedes<T>(q_1: &T, q_2: &T, q_3: &T) -> T
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + One + Zero,
{
let temp = *q_1 + *q_2 - *q_3;
let four = T::one() + T::one() + T::one() + T::one();
four * *q_1 * *q_2 - temp * temp
}
#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
/// ```svgbob
/// .───────────. .───────────────.
/// │ (x1, y1) │───► dx²──► │
/// │ (x2, y2) │───► dy²──► Q = dx² + dy²│
/// '───────────' '───────────────'
/// ```
))]
#[inline]
pub fn quadrance<T>(p_1: (T, T), p_2: (T, T)) -> T
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
let dx = p_1.0 - p_2.0;
let dy = p_1.1 - p_2.1;
dx * dx + dy * dy
}
#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
/// ```svgbob
/// .───────────. .───────────────.
/// │ (v1x,v1y) │───► dot──► │
/// │ (v2x,v2y) │───► |v|²─► s = 1 - │
/// '───────────' │ (dot/|v|)² │
/// '───────────────'
/// ```
))]
#[inline]
pub fn spread<T>(v_1: (T, T), v_2: (T, T)) -> T
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + One + Zero,
{
let dot_product = v_1.0 * v_2.0 + v_1.1 * v_2.1;
let q_1 = quadrance(v_1, (T::zero(), T::zero()));
let q_2 = quadrance(v_2, (T::zero(), T::zero()));
T::one() - dot_product * dot_product / (q_1 * q_2)
}
#[inline]
pub fn safe_spread<T>(v_1: (T, T), v_2: (T, T)) -> Result<T, crate::error::MathError>
where
T: Copy
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ One
+ Zero
+ PartialEq,
{
let dot_product = v_1.0 * v_2.0 + v_1.1 * v_2.1;
let q_1 = quadrance(v_1, (T::zero(), T::zero()));
let q_2 = quadrance(v_2, (T::zero(), T::zero()));
if q_1 == T::zero() || q_2 == T::zero() {
return Err(crate::error::MathError::DivisionByZero);
}
Ok(T::one() - dot_product * dot_product / (q_1 * q_2))
}
#[inline]
pub fn cross<T>(v_1: (T, T), v_2: (T, T)) -> T
where
T: Copy + Sub<Output = T> + Mul<Output = T>,
{
v_1.0 * v_2.1 - v_1.1 * v_2.0
}
#[inline]
pub fn quadrance_from_line<T>(p: (T, T), l: (T, T, T)) -> T
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + Zero,
{
let temp = l.0 * p.0 + l.1 * p.1 + l.2;
temp * temp / quadrance((l.0, l.1), (T::zero(), T::zero()))
}
#[inline]
pub fn safe_quadrance_from_line<T>(p: (T, T), l: (T, T, T)) -> Result<T, crate::error::MathError>
where
T: Copy
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ Zero
+ PartialEq,
{
let temp = l.0 * p.0 + l.1 * p.1 + l.2;
let q = quadrance((l.0, l.1), (T::zero(), T::zero()));
if q == T::zero() {
return Err(crate::error::MathError::DivisionByZero);
}
Ok(temp * temp / q)
}
#[inline]
pub fn spread_from_line<T>(l_1: (T, T, T), l_2: (T, T, T)) -> T
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + Zero,
{
let temp = cross((l_1.0, l_1.1), (l_2.0, l_2.1));
temp * temp
/ (quadrance((l_1.0, l_1.1), (T::zero(), T::zero()))
* quadrance((l_2.0, l_2.1), (T::zero(), T::zero())))
}
#[inline]
pub fn safe_spread_from_line<T>(
l_1: (T, T, T),
l_2: (T, T, T),
) -> Result<T, crate::error::MathError>
where
T: Copy
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ Zero
+ PartialEq,
{
let temp = cross((l_1.0, l_1.1), (l_2.0, l_2.1));
let q_1 = quadrance((l_1.0, l_1.1), (T::zero(), T::zero()));
let q_2 = quadrance((l_2.0, l_2.1), (T::zero(), T::zero()));
if q_1 == T::zero() || q_2 == T::zero() {
return Err(crate::error::MathError::DivisionByZero);
}
Ok(temp * temp / (q_1 * q_2))
}
#[inline]
pub fn cross_from_line<T>(l_1: (T, T, T), l_2: (T, T, T)) -> T
where
T: Copy + Sub<Output = T> + Mul<Output = T>,
{
cross((l_1.0, l_1.1), (l_2.0, l_2.1))
}
#[inline]
pub fn quadrance_from_three_points<T>(p_1: (T, T), p_2: (T, T), p_3: (T, T)) -> (T, T, T)
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
(
quadrance(p_2, p_3),
quadrance(p_1, p_3),
quadrance(p_1, p_2),
)
}
#[inline]
pub fn spread_from_three_points<T>(p_1: (T, T), p_2: (T, T), p_3: (T, T)) -> (T, T, T)
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + One + Zero,
{
let q_1 = quadrance(p_2, p_3);
let q_2 = quadrance(p_1, p_3);
let q_3 = quadrance(p_1, p_2);
let four = T::one() + T::one() + T::one() + T::one();
let s_1 = T::one() - (q_2 + q_3 - q_1) * (q_2 + q_3 - q_1) / (four * q_2 * q_3);
let s_2 = T::one() - (q_1 + q_3 - q_2) * (q_1 + q_3 - q_2) / (four * q_1 * q_3);
let s_3 = T::one() - (q_1 + q_2 - q_3) * (q_1 + q_2 - q_3) / (four * q_1 * q_2);
(s_1, s_2, s_3)
}
#[inline]
pub fn cross_from_three_points<T>(p_1: (T, T), p_2: (T, T), p_3: (T, T)) -> T
where
T: Copy + Sub<Output = T> + Mul<Output = T>,
{
cross(
(p_2.0 - p_1.0, p_2.1 - p_1.1),
(p_3.0 - p_1.0, p_3.1 - p_1.1),
)
}
#[inline]
pub fn quadrance3d<T>(p_1: (T, T, T), p_2: (T, T, T)) -> T
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T>,
{
let dx = p_1.0 - p_2.0;
let dy = p_1.1 - p_2.1;
let dz = p_1.2 - p_2.2;
dx * dx + dy * dy + dz * dz
}
#[inline]
pub fn cross3d<T>(v_1: (T, T, T), v_2: (T, T, T)) -> (T, T, T)
where
T: Copy + Sub<Output = T> + Mul<Output = T> + Add<Output = T>,
{
(
v_1.1 * v_2.2 - v_1.2 * v_2.1,
v_1.2 * v_2.0 - v_1.0 * v_2.2,
v_1.0 * v_2.1 - v_1.1 * v_2.0,
)
}
#[inline]
pub fn spread3d<T>(v_1: (T, T, T), v_2: (T, T, T)) -> T
where
T: Copy + Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + One + Zero,
{
let dot_product = v_1.0 * v_2.0 + v_1.1 * v_2.1 + v_1.2 * v_2.2;
let q_1 = quadrance3d(v_1, (T::zero(), T::zero(), T::zero()));
let q_2 = quadrance3d(v_2, (T::zero(), T::zero(), T::zero()));
T::one() - dot_product * dot_product / (q_1 * q_2)
}
#[inline]
pub fn twist<T>(p_1: (T, T), p_2: (T, T), p_3: (T, T)) -> T
where
T: Copy + Sub<Output = T> + Mul<Output = T>,
{
cross_from_three_points(p_1, p_2, p_3)
}
#[inline]
pub fn turn<T>(p_1: (T, T), p_2: (T, T), p_3: (T, T)) -> (T, bool)
where
T: Copy
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ One
+ Zero
+ PartialOrd,
{
let v1 = (p_2.0 - p_1.0, p_2.1 - p_1.1);
let v2 = (p_3.0 - p_2.0, p_3.1 - p_2.1);
let s = spread(v1, v2);
let sign = cross(v1, v2) >= T::zero();
(s, sign)
}
#[inline]
pub fn dilatation<T>(v_1: (T, T), v_2: (T, T)) -> T
where
T: Copy
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ Zero
+ PartialEq,
{
let q_1 = quadrance(v_1, (T::zero(), T::zero()));
let q_2 = quadrance(v_2, (T::zero(), T::zero()));
if q_1 == T::zero() {
T::zero()
} else {
q_2 / q_1
}
}
#[inline]
pub fn safe_dilatation<T>(v_1: (T, T), v_2: (T, T)) -> Result<T, crate::error::MathError>
where
T: Copy
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ Zero
+ PartialEq,
{
let q_1 = quadrance(v_1, (T::zero(), T::zero()));
let q_2 = quadrance(v_2, (T::zero(), T::zero()));
if q_1 == T::zero() {
return Err(crate::error::MathError::DivisionByZero);
}
Ok(q_2 / q_1)
}
#[inline]
pub fn sine_law_product<T>(q: T, s: T) -> T
where
T: Copy + Mul<Output = T>,
{
q * s
}
#[inline]
pub fn cosine_law<T>(q_1: T, q_2: T, q_3: T) -> T
where
T: Copy
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ One
+ Zero
+ PartialEq,
{
let four = T::one() + T::one() + T::one() + T::one();
if q_2 == T::zero() || q_3 == T::zero() {
T::zero()
} else {
T::one() - (q_2 + q_3 - q_1) * (q_2 + q_3 - q_1) / (four * q_2 * q_3)
}
}
#[inline]
pub fn safe_cosine_law<T>(q_1: T, q_2: T, q_3: T) -> Result<T, crate::error::MathError>
where
T: Copy
+ Add<Output = T>
+ Sub<Output = T>
+ Mul<Output = T>
+ Div<Output = T>
+ One
+ Zero
+ PartialEq,
{
let four = T::one() + T::one() + T::one() + T::one();
if q_2 == T::zero() || q_3 == T::zero() {
return Err(crate::error::MathError::DivisionByZero);
}
Ok(T::one() - (q_2 + q_3 - q_1) * (q_2 + q_3 - q_1) / (four * q_2 * q_3))
}
#[cfg(test)]
mod tests {
use super::*;
use num_rational::Ratio;
#[test]
fn test_archimedes2() {
let q_1: i64 = 1;
let q_2: i64 = 2;
let q_3: i64 = 3;
assert_eq!(archimedes(&q_1, &q_2, &q_3), 8);
}
#[test]
fn test_archimedes3() {
let q_1 = 1.0;
let q_2 = 2.0;
let q_3 = 3.0;
assert_eq!(archimedes(&q_1, &q_2, &q_3), 8.0);
}
#[test]
fn test_archimedes() {
let q_1 = Ratio::<i32>::new(1, 2);
let q_2 = Ratio::<i32>::new(1, 4);
let q_3 = Ratio::<i32>::new(1, 6);
assert_eq!(archimedes(&q_1, &q_2, &q_3), Ratio::<i32>::new(23, 144));
}
#[test]
fn test_archimedes_zero() {
let q_1 = Ratio::<i64>::new(0, 1);
let q_2 = Ratio::<i64>::new(0, 1);
let q_3 = Ratio::<i64>::new(0, 1);
assert_eq!(archimedes(&q_1, &q_2, &q_3), Ratio::<i64>::new(0, 1));
}
#[test]
fn test_archimedes_negative() {
let q_1 = Ratio::<i64>::new(-1, 2);
let q_2 = Ratio::<i64>::new(-1, 4);
let q_3 = Ratio::<i64>::new(-1, 6);
assert_eq!(archimedes(&q_1, &q_2, &q_3), Ratio::<i64>::new(23, 144));
}
#[test]
fn test_archimedes_i32() {
let q_1: i32 = 1;
let q_2: i32 = 2;
let q_3: i32 = 3;
assert_eq!(archimedes(&q_1, &q_2, &q_3), 8);
}
#[test]
fn test_quadrance() {
let p1 = (1, 1);
let p2 = (4, 5);
assert_eq!(quadrance(p1, p2), 25);
}
#[test]
fn test_spread() {
let v1 = (1.0, 1.0);
let v2 = (1.0, 0.0);
assert_eq!(spread(v1, v2), 0.5);
}
#[test]
fn test_cross() {
let v1 = (1, 1);
let v2 = (1, 0);
assert_eq!(cross(v1, v2), -1);
}
#[test]
fn test_quadrance_from_line() {
let p1 = (1.0, 1.0);
let l1 = (1.0, 1.0, 1.0);
assert_eq!(quadrance_from_line(p1, l1), 4.5);
}
#[test]
fn test_spread_from_line() {
let l1 = (1.0, 1.0, 1.0);
let l2 = (1.0, 0.0, 0.0);
assert_eq!(spread_from_line(l1, l2), 0.5);
}
#[test]
fn test_cross_from_line() {
let l1 = (1, 1, 1);
let l2 = (1, 0, 0);
assert_eq!(cross_from_line(l1, l2), -1);
}
#[test]
fn test_quadrance_from_three_points() {
let p1 = (0, 0);
let p2 = (1, 0);
let p3 = (0, 1);
assert_eq!(quadrance_from_three_points(p1, p2, p3), (2, 1, 1));
}
#[test]
fn test_spread_from_three_points() {
let p1 = (0.0, 0.0);
let p2 = (1.0, 0.0);
let p3 = (0.0, 1.0);
assert_eq!(spread_from_three_points(p1, p2, p3), (1.0, 0.5, 0.5));
}
#[test]
fn test_cross_from_three_points() {
let p1 = (0, 0);
let p2 = (1, 0);
let p3 = (0, 1);
assert_eq!(cross_from_three_points(p1, p2, p3), 1);
}
#[test]
fn test_quadrance3d() {
let p1 = (0, 0, 0);
let p2 = (1, 2, 2);
assert_eq!(quadrance3d(p1, p2), 9);
}
#[test]
fn test_cross3d() {
let v1 = (1, 0, 0);
let v2 = (0, 1, 0);
assert_eq!(cross3d(v1, v2), (0, 0, 1));
}
#[test]
fn test_spread3d() {
let v1 = (1.0, 0.0, 0.0);
let v2 = (0.0, 1.0, 0.0);
assert_eq!(spread3d(v1, v2), 1.0);
}
#[test]
fn test_quadrance3d_rational() {
let p1 = (
Ratio::<i32>::new(0, 1),
Ratio::<i32>::new(0, 1),
Ratio::<i32>::new(0, 1),
);
let p2 = (
Ratio::<i32>::new(1, 1),
Ratio::<i32>::new(2, 1),
Ratio::<i32>::new(2, 1),
);
assert_eq!(quadrance3d(p1, p2), Ratio::<i32>::new(9, 1));
}
#[test]
fn test_twist() {
let p1 = (0, 0);
let p2 = (1, 0);
let p3 = (0, 1);
assert_eq!(twist(p1, p2, p3), 1);
}
#[test]
fn test_turn() {
let p1 = (0.0, 0.0);
let p2 = (1.0, 0.0);
let p3 = (1.0, 1.0);
let (s, sign) = turn(p1, p2, p3);
assert_eq!(s, 1.0);
assert!(sign);
}
#[test]
fn test_dilatation() {
let v1 = (1.0, 0.0);
let v2 = (2.0, 0.0);
assert_eq!(dilatation(v1, v2), 4.0);
}
#[test]
fn test_sine_law_product() {
let q = 4.0;
let s = 0.5;
assert_eq!(sine_law_product(q, s), 2.0);
}
#[test]
fn test_cosine_law() {
let q1 = 2.0;
let q2 = 1.0;
let q3 = 1.0;
assert_eq!(cosine_law(q1, q2, q3), 1.0);
}
#[test]
fn test_twist_negative() {
let p1 = (0, 0);
let p2 = (1, 0);
let p3 = (0, -1);
assert_eq!(twist(p1, p2, p3), -1);
}
}
#[cfg(test)]
mod quickcheck_tests {
}