├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "numerical-rs" 5 | version = "0.1.0" 6 | 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "numerical-rs" 3 | version = "0.1.0" 4 | authors = ["Damian Gryski "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::f64; 2 | 3 | pub fn trapezoid(start: f64, end: f64, steps: i64, fp: fn(f64) -> f64) -> f64 { 4 | let h = (end - start) / (steps as f64); 5 | 6 | let mut total = 0.5 * (fp(end) - fp(start)); 7 | 8 | for i in 0..steps { 9 | total += fp(start + h * (i as f64)); 10 | } 11 | 12 | return total * h; 13 | } 14 | 15 | pub fn trapezoid_iterative(start: f64, end: f64, _steps: i64, fp: fn(f64) -> f64) -> f64 { 16 | // adaptive method -- ignore provided 'steps' 17 | let mut steps = 1; 18 | 19 | let mut old_estimate = 0.0; 20 | let mut new_estimate = trapezoid_iterative_helper(start, end, steps, fp, old_estimate); 21 | 22 | while (old_estimate - new_estimate) * (old_estimate - new_estimate) > 1e-18 { 23 | steps += 1; 24 | old_estimate = new_estimate; 25 | new_estimate = trapezoid_iterative_helper(start, end, steps, fp, old_estimate); 26 | } 27 | 28 | return new_estimate; 29 | } 30 | 31 | pub fn trapezoid_iterative_helper( 32 | start: f64, 33 | end: f64, 34 | steps: i64, 35 | fp: fn(f64) -> f64, 36 | old_estimate: f64, 37 | ) -> f64 { 38 | if steps == 1 { 39 | return (fp(start) + fp(end)) * (end - start) / 2.0; 40 | } 41 | 42 | let n = 1 << (steps - 2); // number of new points 43 | 44 | let h = (end - start) / (n as f64); // spacing of new points 45 | let x = start + h / 2.0; // coord of first new point 46 | let mut total = 0.0; 47 | 48 | for i in 0..n { 49 | total += fp(x + (i as f64) * h); 50 | } 51 | 52 | return (old_estimate + h * total) / 2.0; 53 | } 54 | 55 | pub fn romberg(start: f64, end: f64, _steps: i64, fp: fn(f64) -> f64) -> f64 { 56 | let max: usize = 21; // iterations 57 | 58 | let mut area = 0.0; 59 | let mut s = vec![0.0; max]; 60 | 61 | for k in 1..max { 62 | let mut oldval = s[1]; 63 | s[1] = trapezoid_iterative_helper(start, end, k as i64, fp, oldval); 64 | 65 | for i in 2..(k + 1) { 66 | let p = ((1 as i64) << (2 * (i - 1))) as f64; 67 | s[k] = (p * s[i - 1] - oldval) / (p - 1.0); 68 | oldval = s[i]; 69 | s[i] = s[k]; 70 | } 71 | 72 | if (area - s[k]).abs() < 1e-9 { 73 | return s[k]; 74 | } 75 | 76 | area = s[k]; 77 | } 78 | 79 | // max iterations reached; return what we have 80 | return s[max - 1]; 81 | } 82 | 83 | pub fn simpsons(start: f64, end: f64, _steps: i64, fp: fn(f64) -> f64) -> f64 { 84 | let total = fp(start) + 4.0 * fp((start + end) / 2.0) + fp(end); 85 | return (end - start) / 6.0 * total; 86 | } 87 | 88 | pub fn simpsons38(start: f64, end: f64, _steps: i64, fp: fn(f64) -> f64) -> f64 { 89 | let total = fp(start) 90 | + 3.0 * fp((2.0 * start + end) / 3.0) 91 | + 3.0 * fp((2.0 * end + start) / 3.0) 92 | + fp(end); 93 | return (end - start) / 8.0 * total; 94 | } 95 | 96 | pub fn simpsons_composite(start: f64, end: f64, steps: i64, fp: fn(f64) -> f64) -> f64 { 97 | let steps = steps + (steps & 1); 98 | let h = (end - start) / (steps as f64); 99 | let mut totals = [0.0; 2]; 100 | 101 | for i in 1..steps { 102 | totals[(i & 1) as usize] += fp(start + h * (i as f64)); 103 | } 104 | 105 | let total = fp(start) + totals[0] * 2.0 + totals[1] * 4.0 + fp(end); 106 | 107 | return h * total / 3.0; 108 | } 109 | 110 | pub fn simpsons38_composite(start: f64, end: f64, steps: i64, fp: fn(f64) -> f64) -> f64 { 111 | let steps = steps * 3; 112 | 113 | let sections = steps / 3; 114 | 115 | let h = (end - start) / (steps as f64); 116 | let mut totals = [0.0; 4]; 117 | 118 | for i in 0..sections { 119 | let mut x0 = start + 3.0 * (i as f64) * h; 120 | totals[0] += fp(x0); 121 | x0 += h; 122 | totals[1] += fp(x0); 123 | x0 += h; 124 | totals[2] += fp(x0); 125 | x0 += h; 126 | totals[3] += fp(x0); 127 | } 128 | 129 | let total = totals[0] + 3.0 * totals[1] + 3.0 * totals[2] + totals[3]; 130 | 131 | return 3.0 * h / 8.0 * total; 132 | } 133 | 134 | pub fn bisect(fp: fn(f64) -> f64, mut x0: f64, mut x1: f64) -> f64 { 135 | let eps = 10e-8; 136 | let mut fx0 = fp(x0); 137 | let mut root = 0.0; 138 | 139 | while x1 - x0 > eps { 140 | root = (x0 + x1) / 2.0; 141 | let froot = fp(root); 142 | if froot * fx0 < 0.0 { 143 | x1 = root; 144 | } else { 145 | x0 = root; 146 | fx0 = froot; 147 | } 148 | } 149 | 150 | return root; 151 | } 152 | 153 | pub fn secant(fp: fn(f64) -> f64, mut x0: f64, mut x1: f64) -> f64 { 154 | let eps = 10e-8; 155 | 156 | let mut fx0 = fp(x0); 157 | while (x1 - x0).abs() > eps { 158 | let fx1 = fp(x1); 159 | let x2 = x1 - fx1 * ((x1 - x0) / (fx1 - fx0)); 160 | x0 = x1; 161 | x1 = x2; 162 | fx0 = fx1; 163 | } 164 | 165 | return x1; 166 | } 167 | 168 | pub fn falsi(fp: fn(f64) -> f64, mut s: f64, mut t: f64) -> f64 { 169 | let mut side = 0; 170 | 171 | let mut fs = fp(s); 172 | let mut ft = fp(t); 173 | 174 | let m = 100; // max iterations 175 | 176 | let mut r = 0.0; 177 | let e = 5e-15; // eps 178 | 179 | for _ in 0..m { 180 | r = (fs * t - ft * s) / (fs - ft); 181 | if (t - s).abs() < (e * (t + s).abs()) { 182 | break; 183 | } 184 | 185 | let fr = fp(r); 186 | 187 | if fr * ft > 0.0 { 188 | t = r; 189 | ft = fr; 190 | if side == -1 { 191 | fs /= 2.0; 192 | } 193 | side = -1; 194 | } else if fs * fr > 0.0 { 195 | s = r; 196 | fs = fr; 197 | if side == 1 { 198 | ft /= 2.0; 199 | } 200 | side = 1; 201 | } else { 202 | break; 203 | } 204 | } 205 | return r; 206 | } 207 | 208 | pub fn ridder(fp: fn(f64) -> f64, mut a: f64, mut b: f64) -> f64 { 209 | let tol = 1.0e-9; 210 | 211 | let mut fa = fp(a); 212 | if fa == 0.0 { 213 | return a; 214 | } 215 | let mut fb = fp(b); 216 | if fb == 0.0 { 217 | return b; 218 | } 219 | 220 | // if fa*fb > 0.0: errror.err('Root is not bracketed') 221 | 222 | let mut xOld = 0.0; 223 | 224 | for i in 0..30 { 225 | // Compute the improved root x from Ridder's formula 226 | let c = 0.5 * (a + b); 227 | let fc = fp(c); 228 | let s = (fc * fc - fa * fb).sqrt(); 229 | if s == 0.0 { 230 | return 0.0; 231 | } 232 | let mut dx = (c - a) * fc / s; 233 | if (fa - fb) < 0.0 { 234 | dx = -dx; 235 | } 236 | let x = c + dx; 237 | let fx = fp(x); 238 | // Test for convergence 239 | if i > 0 && (x - xOld).abs() < tol * x.abs().max(1.0) { 240 | return x; 241 | } 242 | xOld = x; 243 | // Re-bracket the root as tightly as possible 244 | if fc * fx > 0.0 { 245 | if fa * fx < 0.0 { 246 | b = x; 247 | fb = fx; 248 | } else { 249 | a = x; 250 | fa = fx; 251 | } 252 | } else { 253 | a = c; 254 | b = x; 255 | fa = fc; 256 | fb = fx; 257 | } 258 | } 259 | 260 | // too many iterations 261 | return 0.0; 262 | } 263 | 264 | #[cfg(test)] 265 | mod tests { 266 | 267 | use super::*; 268 | use std::f64::consts::PI; 269 | 270 | #[test] 271 | fn runall_integrate() { 272 | let algs = [ 273 | trapezoid, 274 | trapezoid_iterative, 275 | romberg, 276 | simpsons_composite, 277 | simpsons38_composite, 278 | // These two algorithms are not accurate enough 279 | // simpsons, 280 | // simpsons38 281 | ]; 282 | 283 | for alg in algs.iter() { 284 | let r = alg(0.0, PI / 2.0, 100, f_integrate); 285 | let t = (r - 2.0).abs(); 286 | assert!(t < 0.0001); 287 | } 288 | } 289 | 290 | fn f_integrate(x: f64) -> f64 { 291 | x.sin() + x.cos() 292 | } 293 | 294 | #[test] 295 | fn runall_roots() { 296 | let algs = [bisect, secant, falsi, ridder]; 297 | 298 | for alg in algs.iter() { 299 | let r = alg(f_root, 1.0, 4.0); 300 | let t = (r - 2.0).abs(); 301 | println!("t = {}, r = {}", t, r); 302 | assert!(t < 0.00001); 303 | } 304 | } 305 | 306 | fn f_root(x: f64) -> f64 { 307 | (x * x) - 4.0 308 | } 309 | } 310 | --------------------------------------------------------------------------------