diff --git a/Cargo.toml b/Cargo.toml index d62cd4affa7..1d6f2b3584d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ num-bigint = { version = "0.4", optional = true } num-traits = { version = "0.2", optional = true } rand = "0.8" rand_chacha = "0.3" +nalgebra = "0.32.3" [dev-dependencies] quickcheck = "1.0" @@ -17,4 +18,4 @@ quickcheck_macros = "1.0" [features] default = ["big-math"] -big-math = ["dep:num-bigint", "dep:num-traits"] \ No newline at end of file +big-math = ["dep:num-bigint", "dep:num-traits"] diff --git a/src/math/least_square_approx.rs b/src/math/least_square_approx.rs new file mode 100644 index 00000000000..424bd8c3f07 --- /dev/null +++ b/src/math/least_square_approx.rs @@ -0,0 +1,101 @@ +/// Least Square Approximation
+/// Function that returns a polynomial which very closely passes through the given points (in 2D)
+///
+/// The result is made of coeficients, in descending order (from x^degree to free term)
+///
+/// Parameters:
+///
+/// points -> coordinates of given points
+///
+/// degree -> degree of the polynomial
+///
+pub fn least_square_approx(points: &[(f64, f64)], degree: i32) -> Vec -> base: base of log
+/// -> x: value for which log shall be evaluated
+/// -> tol: tolerance; the precision of the approximation
+///
+/// Advisable to use **std::f64::consts::*** for specific bases (like 'e')
+pub fn log(base: f64, mut x: f64, tol: f64) -> f64 {
+ let mut rez: f64 = 0f64;
+
+ if x <= 0f64 || base <= 0f64 {
+ println!("Log does not support negative argument or negative base.");
+ f64::NAN
+ } else if x < 1f64 && base == E {
+ /*
+ For x in (0, 1) and base 'e', the function is using MacLaurin Series:
+ ln(|1 + x|) = Σ "(-1)^n-1 * x^n / n", for n = 1..inf
+ Substituting x with x-1 yields:
+ ln(|x|) = Σ "(-1)^n-1 * (x-1)^n / n"
+ */
+ x -= 1f64;
+
+ let mut prev_rez = 1f64;
+ let mut step: i32 = 1;
+
+ while (prev_rez - rez).abs() > tol {
+ prev_rez = rez;
+ rez += (-1f64).powi(step - 1) * x.powi(step) / step as f64;
+ step += 1;
+ }
+
+ rez
+ } else {
+ let ln_x = x.ln();
+ let ln_base = base.ln();
+
+ ln_x / ln_base
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn basic() {
+ assert_eq!(log(E, E, 0.0), 1.0);
+ assert_eq!(log(E, E.powi(100), 0.0), 100.0);
+ assert_eq!(log(10.0, 10000.0, 0.0), 4.0);
+ assert_eq!(log(234501.0, 1.0, 1.0), 0.0);
+ }
+
+ #[test]
+ fn test_log_positive_base() {
+ assert_eq!(log(10.0, 100.0, 0.00001), 2.0);
+ assert_eq!(log(2.0, 8.0, 0.00001), 3.0);
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_log_zero_base() {
+ assert_eq!(log(0.0, 100.0, 0.00001), f64::NAN);
+ }
+
+ #[test]
+ #[should_panic] // Should panic because can't compare NAN to NAN
+ fn test_log_negative_base() {
+ assert_eq!(log(-1.0, 100.0, 0.00001), f64::NAN);
+ }
+
+ #[test]
+ fn test_log_tolerance() {
+ assert_eq!(log(10.0, 100.0, 1e-10), 2.0);
+ }
+}
diff --git a/src/math/mod.rs b/src/math/mod.rs
index 5d292c437aa..f27d1a26ca9 100644
--- a/src/math/mod.rs
+++ b/src/math/mod.rs
@@ -37,7 +37,9 @@ mod interquartile_range;
mod karatsuba_multiplication;
mod lcm_of_n_numbers;
mod leaky_relu;
+mod least_square_approx;
mod linear_sieve;
+mod logarithm;
mod lucas_series;
mod matrix_ops;
mod mersenne_primes;
@@ -118,7 +120,9 @@ pub use self::interquartile_range::interquartile_range;
pub use self::karatsuba_multiplication::multiply;
pub use self::lcm_of_n_numbers::lcm;
pub use self::leaky_relu::leaky_relu;
+pub use self::least_square_approx::least_square_approx;
pub use self::linear_sieve::LinearSieve;
+pub use self::logarithm::log;
pub use self::lucas_series::dynamic_lucas_number;
pub use self::lucas_series::recursive_lucas_number;
pub use self::matrix_ops::Matrix;