ospf_rust_math/algebra/ordinary/
log.rs

1use std::ops::{Mul, Sub};
2
3use paste::paste;
4
5use crate::algebra::concept::{Arithmetic, FloatingNumber, Precision, RealNumber, SemiArithmetic};
6use crate::algebra::operator::Reciprocal;
7
8pub fn ln<T: FloatingNumber>(x: &T) -> Option<T>
9where
10    for<'a> &'a T: Sub<Output = T> + Mul<Output = T> + Reciprocal<Output = T>,
11{
12    if x <= T::ZERO {
13        (*<T as RealNumber>::NAN).clone()
14    } else {
15        let frac_e = T::E.reciprocal().unwrap();
16
17        let mut val = (T::ZERO).clone();
18        let mut xp = x.clone();
19        if &xp < T::ONE {
20            while xp <= frac_e {
21                xp *= T::E;
22                val -= T::ONE;
23            }
24        } else if &xp > T::ONE {
25            while &xp >= T::E {
26                xp /= T::E;
27                val += T::ONE;
28            }
29        }
30        let mut base = &xp - T::ONE;
31        let mut signed = (*T::ONE).clone();
32        let mut i = (*T::ONE).clone();
33        loop {
34            let this_item = &signed * &base / &i;
35            val += this_item.clone();
36            base *= &xp - T::ONE;
37            signed = -signed;
38            i += T::ONE;
39
40            if &this_item <= &<T as Precision>::EPSILON {
41                break;
42            }
43        }
44        Some(val)
45    }
46}
47
48pub fn log<T: FloatingNumber>(nature: &T, x: &T) -> Option<T>
49where
50    for<'a> &'a T: Sub<Output = T> + Mul<Output = T> + Reciprocal<Output = T>,
51{
52    if let (Some(ln_nature), Some(ln_x)) = (ln(nature), ln(x)) {
53        Some(ln_x / ln_nature)
54    } else {
55        None
56    }
57}
58
59pub fn lg10<T: FloatingNumber>(x: &T) -> Option<T>
60where
61    for<'a> &'a T: Sub<Output = T> + Mul<Output = T> + Reciprocal<Output = T>,
62{
63    log(T::TEN, x)
64}
65
66pub fn lg2<T: FloatingNumber>(x: &T) -> Option<T>
67where
68    for<'a> &'a T: Sub<Output = T> + Mul<Output = T> + Reciprocal<Output = T>,
69{
70    log(T::TWO, x)
71}
72
73macro_rules! ln_template {
74    ($($type:ident)*) => ($(
75        paste! {
76            pub fn [<ln_ $type>](x: &$type) -> Option<$type> {
77                if x <= $type::ZERO {
78                    (*<$type as RealNumber>::NAN).clone()
79                } else {
80                    let frac_e = $type::E.reciprocal().unwrap();
81
82                    let mut val = (*$type::ZERO).clone();
83                    let mut xp = x.clone();
84                    if &xp < $type::ONE {
85                        while xp <= frac_e {
86                            xp *= $type::E;
87                            val -= $type::ONE;
88                        }
89                    } else if &xp > $type::ONE {
90                        while &xp >= $type::E {
91                            xp /= $type::E;
92                            val += $type::ONE;
93                        }
94                    }
95                    let mut base = &xp - $type::ONE;
96                    let mut signed = (*$type::ONE).clone();
97                    let mut i = (*$type::ONE).clone();
98                    loop {
99                        let this_item = &signed * &base / &i;
100                        val += this_item.clone();
101                        base *= &xp - $type::ONE;
102                        signed = -signed;
103                        i += $type::ONE;
104
105                        if &this_item <= &<$type as Precision>::EPSILON {
106                            break;
107                        }
108                    }
109                    Some(val)
110                }
111            }
112        }
113    )*)
114}
115ln_template! { f32 f64 }
116
117macro_rules! log_template {
118    ($($type:ident)*) => ($(
119        paste! {
120            pub fn [<log_ $type>](nature: &$type, x: &$type) -> Option<$type> {
121                if let (Some(ln_nature), Some(ln_x)) = ([<ln_ $type>](nature), [<ln_ $type>](x)) {
122                    Some(ln_x / ln_nature)
123                } else {
124                    None
125                }
126            }
127
128            pub fn [<lg10_ $type>](x: &$type) -> Option<$type> {
129                [<log_ $type>]($type::TEN, x)
130            }
131
132            pub fn [<lg2_ $type>](x: &$type) -> Option<$type> {
133                [<log_ $type>]($type::TWO, x)
134            }
135        }
136    )*)
137}
138log_template! { f32 f64 }