├── .gitignore ├── .github ├── dependabot.yml └── workflows │ ├── release.yml │ └── ci.yml ├── LICENSE ├── Cargo.toml ├── CHANGELOG.md ├── tests └── integration_test.rs ├── examples ├── demo.rs ├── optimization.rs └── scientific_computing.rs ├── CONTRIBUTING.md ├── benches └── expression_bench.rs ├── src ├── calculus │ ├── limits.rs │ └── mod.rs ├── types │ └── mod.rs ├── parser │ └── mod.rs ├── transforms │ └── mod.rs ├── lib.rs ├── precision │ └── mod.rs ├── solver │ └── mod.rs ├── matrix │ └── mod.rs ├── ml │ └── mod.rs ├── engine │ └── mod.rs └── differential │ └── mod.rs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *.pdb 4 | Cargo.lock 5 | .DS_Store 6 | *.swp 7 | *.swo 8 | *~ 9 | .idea/ 10 | .vscode/ 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | day: "monday" 8 | time: "09:00" 9 | open-pull-requests-limit: 10 10 | reviewers: 11 | - "Nonanti" 12 | assignees: 13 | - "Nonanti" 14 | commit-message: 15 | prefix: "deps" 16 | include: "scope" 17 | labels: 18 | - "dependencies" 19 | - "rust" 20 | 21 | - package-ecosystem: "github-actions" 22 | directory: "/" 23 | schedule: 24 | interval: "weekly" 25 | day: "monday" 26 | time: "09:00" 27 | open-pull-requests-limit: 5 28 | reviewers: 29 | - "Nonanti" 30 | commit-message: 31 | prefix: "ci" 32 | include: "scope" 33 | labels: 34 | - "ci" 35 | - "github-actions" -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | publish: 10 | name: Publish to crates.io 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: dtolnay/rust-toolchain@stable 15 | 16 | - name: Check if version exists on crates.io 17 | id: check 18 | run: | 19 | VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version') 20 | if cargo search mathcore --limit 1 | grep -q "mathcore = \"$VERSION\""; then 21 | echo "Version $VERSION already exists on crates.io" 22 | echo "exists=true" >> $GITHUB_OUTPUT 23 | else 24 | echo "Version $VERSION not found on crates.io" 25 | echo "exists=false" >> $GITHUB_OUTPUT 26 | fi 27 | 28 | - name: Publish to crates.io 29 | if: steps.check.outputs.exists == 'false' 30 | run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} 31 | env: 32 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Nonanti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mathcore" 3 | version = "0.3.1" 4 | edition = "2021" 5 | authors = ["Mehmet Yilmaz "] 6 | description = "Symbolic math library and computer algebra system for Rust" 7 | license = "MIT" 8 | repository = "https://github.com/Nonanti/mathcore" 9 | keywords = ["math", "symbolic", "algebra", "calculus", "cas"] 10 | categories = ["mathematics", "science"] 11 | 12 | [dependencies] 13 | nom = "7.1" 14 | num-complex = "0.4" 15 | num-traits = "0.2" 16 | num-bigint = "0.4" 17 | num-rational = "0.4" 18 | nalgebra = "0.32" 19 | thiserror = "1.0" 20 | indexmap = "2.0" 21 | ndarray = "0.15" 22 | rayon = { version = "1.7", optional = true } 23 | rustfft = { version = "6.1", optional = true } 24 | wasm-bindgen = { version = "0.2", optional = true } 25 | getrandom = { version = "0.2", features = ["js"], optional = true } 26 | 27 | [target.'cfg(target_arch = "wasm32")'.dependencies] 28 | getrandom = { version = "0.2", features = ["js"] } 29 | 30 | [features] 31 | default = ["parallel", "fft"] 32 | parallel = ["rayon"] 33 | fft = ["rustfft"] 34 | wasm = ["wasm-bindgen", "getrandom"] 35 | full = ["parallel", "fft"] 36 | 37 | [dev-dependencies] 38 | criterion = "0.5" 39 | approx = "0.5" 40 | proptest = "1.4" 41 | 42 | 43 | [profile.release] 44 | lto = true 45 | codegen-units = 1 46 | opt-level = 3 47 | 48 | [profile.bench] 49 | inherits = "release" 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.3.1] - 2025-08-30 4 | ### Changed 5 | - Repository maintenance and documentation improvements 6 | - Clean commit history for better maintainability 7 | 8 | ## [0.3.0] - 2025-08-28 9 | 10 | ### Added 11 | - Differential equations solver (ODEs and PDEs) 12 | - RK4 method 13 | - Euler method 14 | - Stiff solver (implicit) 15 | - 2nd order ODEs 16 | - System of ODEs 17 | - Heat equation 18 | - Wave equation 19 | - Laplace equation 20 | 21 | ### Changed 22 | - Renamed ML module to Optimization 23 | - Better docs 24 | - PDE solvers more stable now 25 | 26 | ### Fixed 27 | - Type errors in matrix ops 28 | - Convergence issues 29 | 30 | ## [0.2.0] - 2025-07-15 31 | 32 | ### Added 33 | - Limits (one-sided, at infinity) 34 | - Matrix operations: 35 | - Basic ops (multiply, add, transpose) 36 | - Determinant, trace, eigenvalues 37 | - LU/QR/SVD decomposition 38 | - Linear system solver 39 | - Arbitrary precision (BigInt/BigRational) 40 | - Exact rational math 41 | - Compute pi and e to arbitrary digits 42 | - Optimization stuff: 43 | - Gradients and Hessians 44 | - Autodiff 45 | - Gradient descent 46 | - Taylor series 47 | - Newton's method 48 | - Lagrange multipliers 49 | - Better integration: 50 | - Integration by parts 51 | - u-substitution 52 | - Partial fractions 53 | 54 | ### Improved 55 | - Complex numbers work better 56 | - Error handling 57 | 58 | ## [0.1.0] - 2025-05-20 59 | 60 | Initial release! 61 | 62 | - Expression parser (handles precedence correctly) 63 | - Symbolic engine 64 | - Basic math ops 65 | - Differentiation/integration (symbolic) 66 | - Equation solver 67 | - Complex numbers 68 | - ASCII plots 69 | - Simplification 70 | - Variables -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | pull_request: 7 | branches: [ master, main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | test: 14 | name: Test 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | rust: 19 | - stable 20 | - beta 21 | - nightly 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: dtolnay/rust-toolchain@master 25 | with: 26 | toolchain: ${{ matrix.rust }} 27 | - uses: Swatinem/rust-cache@v2 28 | - name: Run tests 29 | run: cargo test --verbose 30 | - name: Run tests (no default features) 31 | run: cargo test --verbose --no-default-features 32 | - name: Run tests (all features) 33 | run: cargo test --verbose --all-features 34 | 35 | fmt: 36 | name: Rustfmt 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v4 40 | - uses: dtolnay/rust-toolchain@stable 41 | with: 42 | components: rustfmt 43 | - name: Check formatting 44 | run: cargo fmt -- --check 45 | 46 | clippy: 47 | name: Clippy 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@v4 51 | - uses: dtolnay/rust-toolchain@stable 52 | with: 53 | components: clippy 54 | - uses: Swatinem/rust-cache@v2 55 | - name: Run clippy 56 | run: cargo clippy -- -D warnings 57 | 58 | coverage: 59 | name: Code coverage 60 | runs-on: ubuntu-latest 61 | steps: 62 | - uses: actions/checkout@v4 63 | - uses: dtolnay/rust-toolchain@stable 64 | - uses: Swatinem/rust-cache@v2 65 | - name: Install tarpaulin 66 | run: cargo install cargo-tarpaulin 67 | - name: Generate coverage 68 | run: cargo tarpaulin --verbose --workspace -------------------------------------------------------------------------------- /tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | use mathcore::{Expr, MathCore}; 2 | use std::collections::HashMap; 3 | 4 | #[test] 5 | fn test_complete_workflow() { 6 | let math = MathCore::new(); 7 | 8 | // Parse expression 9 | let _expr = MathCore::parse("x^2 + 2*x + 1").unwrap(); 10 | 11 | // Evaluate with variables 12 | let mut vars = HashMap::new(); 13 | vars.insert("x".to_string(), 3.0); 14 | let result = math.evaluate_with_vars("x^2 + 2*x + 1", &vars).unwrap(); 15 | assert_eq!(result, 16.0); 16 | 17 | // Differentiate 18 | let _derivative = MathCore::differentiate("x^2 + 2*x + 1", "x").unwrap(); 19 | 20 | // Integrate 21 | let _integral = MathCore::integrate("2*x + 2", "x").unwrap(); 22 | 23 | // Solve equation 24 | let roots = MathCore::solve("x^2 + 2*x + 1", "x").unwrap(); 25 | assert_eq!(roots.len(), 1); // Perfect square has one root 26 | } 27 | 28 | #[test] 29 | fn test_complex_calculations() { 30 | let math = MathCore::new(); 31 | 32 | // Composite functions 33 | let result = math.calculate("sin(pi/2) + cos(0) + exp(0)").unwrap(); 34 | assert!((result - 3.0).abs() < 1e-10); 35 | 36 | // Nested operations 37 | let result = math.calculate("sqrt(16) * log(100, 10)").unwrap(); 38 | assert!((result - 8.0).abs() < 1e-10); 39 | } 40 | 41 | #[test] 42 | fn test_error_handling() { 43 | let math = MathCore::new(); 44 | 45 | // Division by zero 46 | assert!(math.calculate("1/0").is_err()); 47 | 48 | // Undefined variable 49 | assert!(math.calculate("undefined_var + 1").is_err()); 50 | 51 | // Invalid syntax 52 | assert!(MathCore::parse("2 ++ 3").is_err()); 53 | } 54 | 55 | #[test] 56 | fn test_scientific_constants() { 57 | let math = MathCore::new(); 58 | 59 | let pi = math.calculate("pi").unwrap(); 60 | assert!((pi - std::f64::consts::PI).abs() < 1e-10); 61 | 62 | let e = math.calculate("e").unwrap(); 63 | assert!((e - std::f64::consts::E).abs() < 1e-10); 64 | 65 | let tau = math.calculate("tau").unwrap(); 66 | assert!((tau - std::f64::consts::TAU).abs() < 1e-10); 67 | } 68 | 69 | #[test] 70 | fn test_factorials_and_powers() { 71 | let math = MathCore::new(); 72 | 73 | assert_eq!(math.calculate("5!").unwrap(), 120.0); 74 | assert_eq!(math.calculate("2^10").unwrap(), 1024.0); 75 | assert_eq!(math.calculate("27^(1/3)").unwrap(), 3.0); 76 | } 77 | 78 | #[test] 79 | fn test_equation_solving() { 80 | // Linear equation 81 | let roots = MathCore::solve("3*x - 9", "x").unwrap(); 82 | assert_eq!(roots.len(), 1); 83 | if let Expr::Number(n) = &roots[0] { 84 | assert!((n - 3.0).abs() < 1e-10); 85 | } 86 | 87 | // Quadratic equation 88 | let roots = MathCore::solve("x^2 - 5*x + 6", "x").unwrap(); 89 | assert_eq!(roots.len(), 2); 90 | 91 | // Complex roots 92 | let roots = MathCore::solve("x^2 + 1", "x").unwrap(); 93 | assert_eq!(roots.len(), 2); 94 | assert!(matches!(&roots[0], Expr::Complex(_))); 95 | } 96 | 97 | #[test] 98 | fn test_symbolic_operations() { 99 | // Differentiation chain rule 100 | let deriv = MathCore::differentiate("sin(x^2)", "x").unwrap(); 101 | println!("d/dx(sin(x^2)) = {}", deriv); 102 | 103 | // Integration by substitution 104 | let integral = MathCore::integrate("2*x", "x").unwrap(); 105 | println!("∫2x dx = {}", integral); 106 | 107 | // Simplification 108 | let simplified = MathCore::simplify("x + x + x").unwrap(); 109 | println!("x + x + x = {}", simplified); 110 | } 111 | 112 | #[test] 113 | fn test_numerical_methods() { 114 | // Numerical integration (Simpson's rule) 115 | let area = MathCore::numerical_integrate("x^2", "x", 0.0, 1.0).unwrap(); 116 | assert!((area - 1.0 / 3.0).abs() < 1e-6); 117 | 118 | // Numerical integration of trig function 119 | let area = MathCore::numerical_integrate("sin(x)", "x", 0.0, 3.14159).unwrap(); 120 | assert!((area - 2.0).abs() < 1e-4); 121 | } 122 | -------------------------------------------------------------------------------- /examples/demo.rs: -------------------------------------------------------------------------------- 1 | use mathcore::MathCore; 2 | use std::collections::HashMap; 3 | 4 | fn main() { 5 | println!("MathCore Demonstration\n"); 6 | 7 | let math = MathCore::new(); 8 | 9 | println!("=== Basic Arithmetic ==="); 10 | println!("2 + 3 * 4 = {}", math.calculate("2 + 3 * 4").unwrap()); 11 | println!("(2 + 3) * 4 = {}", math.calculate("(2 + 3) * 4").unwrap()); 12 | println!("2^8 = {}", math.calculate("2^8").unwrap()); 13 | println!("5! = {}", math.calculate("5!").unwrap()); 14 | 15 | println!("\n=== Trigonometric Functions ==="); 16 | println!("sin(0) = {}", math.calculate("sin(0)").unwrap()); 17 | println!("cos(pi) = {}", math.calculate("cos(pi)").unwrap()); 18 | println!("tan(pi/4) = {:.4}", math.calculate("tan(pi/4)").unwrap()); 19 | 20 | println!("\n=== Complex Numbers ==="); 21 | let complex_result = MathCore::parse("3+4i").unwrap(); 22 | println!("Complex number parsed: {}", complex_result); 23 | 24 | println!("\n=== Variables ==="); 25 | let mut vars = HashMap::new(); 26 | vars.insert("x".to_string(), 3.0); 27 | vars.insert("y".to_string(), 4.0); 28 | let pythagoras = math.evaluate_with_vars("sqrt(x^2 + y^2)", &vars).unwrap(); 29 | println!("sqrt(3² + 4²) = {}", pythagoras); 30 | 31 | println!("\n=== Differentiation ==="); 32 | let derivatives = vec![ 33 | ("x^2", "x"), 34 | ("x^3 + 2*x^2 + x + 1", "x"), 35 | ("sin(x)", "x"), 36 | ("x * sin(x)", "x"), 37 | ("e^x", "x"), 38 | ]; 39 | 40 | for (expr, var) in derivatives { 41 | let deriv = MathCore::differentiate(expr, var).unwrap(); 42 | println!("d/d{}({}) = {}", var, expr, deriv); 43 | } 44 | 45 | println!("\n=== Integration ==="); 46 | let integrals = vec![ 47 | ("x", "x"), 48 | ("x^2", "x"), 49 | ("2*x + 3", "x"), 50 | ("sin(x)", "x"), 51 | ("cos(x)", "x"), 52 | ]; 53 | 54 | for (expr, var) in integrals { 55 | let integral = MathCore::integrate(expr, var).unwrap(); 56 | println!("∫{} d{} = {}", expr, var, integral); 57 | } 58 | 59 | println!("\n=== Numerical Integration ==="); 60 | let area = MathCore::numerical_integrate("x^2", "x", 0.0, 1.0).unwrap(); 61 | println!("∫₀¹ x² dx = {:.6}", area); 62 | 63 | let area = MathCore::numerical_integrate("sin(x)", "x", 0.0, 3.14159).unwrap(); 64 | println!("∫₀^π sin(x) dx = {:.6}", area); 65 | 66 | println!("\n=== Equation Solving ==="); 67 | let equations = vec!["x^2 - 4", "x^2 + x - 6", "x^2 + 1", "2*x - 10"]; 68 | 69 | for eq in equations { 70 | let roots = MathCore::solve(eq, "x").unwrap(); 71 | println!("{} = 0, solutions: {:?}", eq, roots); 72 | } 73 | 74 | println!("\n=== Expression Simplification ==="); 75 | let expressions = vec!["x - x", "0 * x", "1 * x", "x^0", "x^1"]; 76 | 77 | for expr in expressions { 78 | let simplified = MathCore::simplify(expr).unwrap(); 79 | println!("{} simplifies to: {}", expr, simplified); 80 | } 81 | 82 | println!("\n=== Function Plotting ==="); 83 | let plot = MathCore::plot_ascii("x^2", "x", -2.0, 2.0, 40, 15).unwrap(); 84 | println!("{}", plot); 85 | 86 | println!("\n=== Advanced Example: Taylor Series ==="); 87 | println!("Taylor series expansion of e^x around x=0:"); 88 | let mut taylor = "1".to_string(); 89 | for n in 1..=5 { 90 | taylor.push_str(&format!(" + x^{}/{}", n, factorial(n))); 91 | let expr = taylor.replace("/", " / "); 92 | let value = math 93 | .evaluate_with_vars(&expr, &HashMap::from([("x".to_string(), 1.0)])) 94 | .unwrap(); 95 | println!("n={}: e ≈ {:.6}", n, value); 96 | } 97 | 98 | let actual_e = math.calculate("e").unwrap(); 99 | println!("Actual e = {:.6}", actual_e); 100 | } 101 | 102 | fn factorial(n: u32) -> u32 { 103 | match n { 104 | 0 | 1 => 1, 105 | _ => n * factorial(n - 1), 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /examples/optimization.rs: -------------------------------------------------------------------------------- 1 | use mathcore::ml::Optimization; 2 | use mathcore::parser::Parser; 3 | use std::collections::HashMap; 4 | 5 | fn main() { 6 | println!("Optimization Examples\n"); 7 | 8 | gradient_descent_example(); 9 | newton_method_example(); 10 | lagrange_multipliers_example(); 11 | taylor_series_example(); 12 | } 13 | 14 | fn gradient_descent_example() { 15 | println!("=== Gradient Descent Optimization ==="); 16 | 17 | // Minimize f(x,y) = (x-3)^2 + (y-2)^2 18 | let loss_fn = Parser::parse("(x-3)^2 + (y-2)^2").unwrap(); 19 | 20 | // Starting point 21 | let mut initial_params = HashMap::new(); 22 | initial_params.insert("x".to_string(), 0.0); 23 | initial_params.insert("y".to_string(), 0.0); 24 | 25 | println!("Minimizing f(x,y) = (x-3)² + (y-2)²"); 26 | println!("Starting point: x=0, y=0"); 27 | 28 | let optimized = Optimization::gradient_descent( 29 | &loss_fn, 30 | initial_params, 31 | 0.1, // learning rate 32 | 100, // iterations 33 | ) 34 | .unwrap(); 35 | 36 | println!( 37 | "Optimized point: x={:.4}, y={:.4}", 38 | optimized["x"], optimized["y"] 39 | ); 40 | println!("Expected minimum: x=3, y=2\n"); 41 | } 42 | 43 | fn newton_method_example() { 44 | println!("=== Newton's Method for Optimization ==="); 45 | 46 | // Find minimum of f(x) = x^4 - 2x^2 + x 47 | let func = Parser::parse("x^4 - 2*x^2 + x").unwrap(); 48 | 49 | println!("Finding critical points of f(x) = x⁴ - 2x² + x"); 50 | 51 | let critical_points = vec![-1.5, 0.0, 1.0]; 52 | 53 | for initial in critical_points { 54 | let result = Optimization::optimize_newton( 55 | &func, "x", initial, 1e-8, // tolerance 56 | 50, // max iterations 57 | ) 58 | .unwrap(); 59 | 60 | println!( 61 | "Starting from x={:.1}: converged to x={:.6}", 62 | initial, result 63 | ); 64 | } 65 | println!(); 66 | } 67 | 68 | fn lagrange_multipliers_example() { 69 | println!("=== Lagrange Multipliers ==="); 70 | 71 | // Maximize f(x,y) = x*y subject to x^2 + y^2 = 1 72 | let objective = Parser::parse("x*y").unwrap(); 73 | let constraint = Parser::parse("x^2 + y^2 - 1").unwrap(); 74 | 75 | println!("Maximize f(x,y) = xy"); 76 | println!("Subject to: x² + y² = 1"); 77 | 78 | let gradient_eqs = Optimization::lagrange_multipliers( 79 | &objective, 80 | &[constraint], 81 | &["x".to_string(), "y".to_string()], 82 | ) 83 | .unwrap(); 84 | 85 | println!("\nLagrangian gradient equations:"); 86 | for (i, eq) in gradient_eqs.iter().enumerate() { 87 | println!(" ∂L/∂var_{} = {}", i, eq); 88 | } 89 | 90 | println!("\nAnalytical solution: x = ±1/√2, y = ±1/√2"); 91 | println!("Maximum value: f = ±0.5\n"); 92 | } 93 | 94 | fn taylor_series_example() { 95 | println!("=== Taylor Series Expansion ==="); 96 | 97 | let functions = vec![ 98 | ("sin(x)", 0.0, 7), 99 | ("exp(x)", 0.0, 5), 100 | ("ln(1+x)", 0.0, 5), 101 | ("1/(1-x)", 0.0, 5), 102 | ]; 103 | 104 | for (func_str, center, order) in functions { 105 | let func = Parser::parse(func_str).unwrap(); 106 | let taylor = Optimization::taylor_series(&func, "x", center, order).unwrap(); 107 | 108 | println!("Taylor series of {} around x={}:", func_str, center); 109 | println!(" {}", taylor); 110 | println!(); 111 | } 112 | 113 | // Verify accuracy 114 | println!("=== Taylor Series Accuracy ==="); 115 | let sin_expr = Parser::parse("sin(x)").unwrap(); 116 | let sin_taylor = Optimization::taylor_series(&sin_expr, "x", 0.0, 9).unwrap(); 117 | 118 | println!("sin(x) Taylor series (order 9) vs actual:"); 119 | println!("x\tTaylor\t\tActual\t\tError"); 120 | 121 | use mathcore::engine::Engine; 122 | let engine = Engine::new(); 123 | 124 | for x in [0.1, 0.5, 1.0, 1.5] { 125 | let mut vars = HashMap::new(); 126 | vars.insert("x".to_string(), x); 127 | 128 | let taylor_val = engine.evaluate_with_vars(&sin_taylor, &vars).unwrap(); 129 | let actual = x.sin(); 130 | 131 | if let mathcore::Expr::Number(t) = taylor_val { 132 | let error = (t - actual).abs(); 133 | println!("{:.1}\t{:.6}\t{:.6}\t{:.2e}", x, t, actual, error); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /examples/scientific_computing.rs: -------------------------------------------------------------------------------- 1 | use mathcore::differential::{DifferentialEquations, PDESolver}; 2 | use mathcore::parser::Parser; 3 | 4 | fn main() { 5 | println!("Scientific Computing Examples\n"); 6 | 7 | population_dynamics(); 8 | heat_diffusion(); 9 | wave_propagation(); 10 | pendulum_motion(); 11 | } 12 | 13 | fn population_dynamics() { 14 | println!("=== Population Dynamics (Logistic Growth) ==="); 15 | 16 | // dP/dt = r*P*(1 - P/K) 17 | // where r = growth rate, K = carrying capacity 18 | let r = 0.5; 19 | let k = 1000.0; 20 | 21 | // Parse the logistic growth equation 22 | let expr = Parser::parse(&format!("{}*P*(1 - P/{})", r, k)).unwrap(); 23 | 24 | let solution = DifferentialEquations::solve_ode_first_order( 25 | &expr, 26 | "t", 27 | "P", 28 | (0.0, 10.0), // Initial population of 10 29 | 20.0, // Time span of 20 units 30 | 200, // 200 time steps 31 | ) 32 | .unwrap(); 33 | 34 | // Print selected points 35 | println!("Time\tPopulation"); 36 | for i in (0..solution.t.len()).step_by(20) { 37 | println!("{:.1}\t{:.1}", solution.t[i], solution.y[i][0]); 38 | } 39 | 40 | println!("\nPopulation stabilizes at carrying capacity K = {}\n", k); 41 | } 42 | 43 | fn heat_diffusion() { 44 | println!("=== Heat Diffusion in a Rod ==="); 45 | 46 | // Initial temperature distribution: hot spot in the middle 47 | let initial_temp = |x: f64| { 48 | if (x - 0.5).abs() < 0.1 { 49 | 100.0 // Hot spot 50 | } else { 51 | 20.0 // Room temperature 52 | } 53 | }; 54 | 55 | let solution = PDESolver::solve_heat_equation( 56 | 0.01, // Thermal diffusivity 57 | &initial_temp, 58 | 20.0, // Left boundary temperature 59 | 20.0, // Right boundary temperature 60 | (0.0, 1.0), // Rod from x=0 to x=1 61 | 0.5, // Time = 0.5 62 | 50, // 50 spatial points 63 | 100, // 100 time steps 64 | ) 65 | .unwrap(); 66 | 67 | println!("Temperature distribution after heat diffusion:"); 68 | println!("Position\tInitial\t\tFinal"); 69 | 70 | for i in (0..50).step_by(5) { 71 | let x = i as f64 / 49.0; 72 | println!( 73 | "{:.2}\t\t{:.1}\t\t{:.1}", 74 | x, 75 | solution[0][i], // Initial 76 | solution[99][i] // Final 77 | ); 78 | } 79 | println!(); 80 | } 81 | 82 | fn wave_propagation() { 83 | println!("=== Wave Propagation ==="); 84 | 85 | // Initial displacement: Gaussian pulse 86 | let initial_position = |x: f64| { 87 | let center = 0.5; 88 | let width = 0.05; 89 | (-(((x - center) / width).powi(2) as f64)).exp() 90 | }; 91 | 92 | // Initially at rest 93 | let initial_velocity = |_: f64| 0.0; 94 | 95 | let solution = PDESolver::solve_wave_equation( 96 | 0.5, // Wave speed 97 | &initial_position, 98 | &initial_velocity, 99 | (0.0, 1.0), // Domain 100 | 1.0, // Time 101 | 100, // Spatial points 102 | 200, // Time steps 103 | ) 104 | .unwrap(); 105 | 106 | println!("Wave amplitude at different times:"); 107 | println!("Position\tt=0\t\tt=0.5\t\tt=1.0"); 108 | 109 | for i in (0..100).step_by(10) { 110 | let x = i as f64 / 99.0; 111 | println!( 112 | "{:.2}\t\t{:.4}\t\t{:.4}\t\t{:.4}", 113 | x, 114 | solution[0][i], // t=0 115 | solution[100][i], // t=0.5 116 | solution[199][i] // t=1.0 117 | ); 118 | } 119 | println!(); 120 | } 121 | 122 | fn pendulum_motion() { 123 | println!("=== Nonlinear Pendulum ==="); 124 | 125 | // Second-order ODE: θ'' + (g/L)*sin(θ) = 0 126 | // Convert to system: θ' = ω, ω' = -(g/L)*sin(θ) 127 | 128 | let g_over_l = 9.81; // g/L ratio 129 | 130 | // System of equations 131 | let theta_dot = Parser::parse("omega").unwrap(); 132 | let omega_dot = Parser::parse(&format!("-{}*sin(theta)", g_over_l)).unwrap(); 133 | 134 | let solution = DifferentialEquations::solve_ode_system( 135 | &[theta_dot, omega_dot], 136 | "t", 137 | &["theta".to_string(), "omega".to_string()], 138 | (0.0, vec![1.0, 0.0]), // Initial angle = 1 rad, initial velocity = 0 139 | 10.0, // 10 seconds 140 | 1000, // 1000 time steps 141 | ) 142 | .unwrap(); 143 | 144 | println!("Pendulum motion (nonlinear):"); 145 | println!("Time\tAngle (rad)\tAngular Velocity"); 146 | 147 | for i in (0..solution.t.len()).step_by(100) { 148 | println!( 149 | "{:.1}\t{:.4}\t\t{:.4}", 150 | solution.t[i], 151 | solution.y[i][0], // theta 152 | solution.y[i][1] // omega 153 | ); 154 | } 155 | 156 | // Calculate period 157 | let mut crossings = Vec::new(); 158 | for i in 1..solution.y.len() { 159 | if solution.y[i - 1][0] > 0.0 && solution.y[i][0] <= 0.0 { 160 | crossings.push(solution.t[i]); 161 | } 162 | } 163 | 164 | if crossings.len() >= 2 { 165 | let period = 2.0 * (crossings[1] - crossings[0]); 166 | println!("\nEstimated period: {:.3} seconds", period); 167 | 168 | let linear_period = 2.0 * std::f64::consts::PI / (g_over_l as f64).sqrt(); 169 | println!("Linear approximation: {:.3} seconds", linear_period); 170 | println!( 171 | "Difference: {:.1}%", 172 | 100.0 * (period - linear_period).abs() / linear_period 173 | ); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to MathCore 2 | 3 | First off, thank you for considering contributing to MathCore! It's people like you that make MathCore such a great tool. 4 | 5 | ## Code of Conduct 6 | 7 | This project and everyone participating in it is governed by our Code of Conduct. By participating, you are expected to uphold this code. 8 | 9 | ## How Can I Contribute? 10 | 11 | ### Reporting Bugs 12 | 13 | Before creating bug reports, please check existing issues as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible using the issue template. 14 | 15 | **Great Bug Reports** tend to have: 16 | - A quick summary and/or background 17 | - Steps to reproduce 18 | - What you expected would happen 19 | - What actually happens 20 | - Notes (possibly including why you think this might be happening) 21 | 22 | ### Suggesting Enhancements 23 | 24 | Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please include: 25 | - Use a clear and descriptive title 26 | - Provide a step-by-step description of the suggested enhancement 27 | - Provide specific examples to demonstrate the steps 28 | - Describe the current behavior and explain which behavior you expected to see instead 29 | - Explain why this enhancement would be useful 30 | 31 | ### Your First Code Contribution 32 | 33 | Unsure where to begin contributing? You can start by looking through these issues: 34 | - `good first issue` - issues which should only require a few lines of code 35 | - `help wanted` - issues which should be a bit more involved than beginner issues 36 | 37 | ### Pull Requests 38 | 39 | 1. Fork the repo and create your branch from `main` 40 | 2. If you've added code that should be tested, add tests 41 | 3. If you've changed APIs, update the documentation 42 | 4. Ensure the test suite passes (`cargo test`) 43 | 5. Make sure your code follows the style guidelines (`cargo fmt` and `cargo clippy`) 44 | 6. Issue that pull request! 45 | 46 | ## Development Process 47 | 48 | ### Setting Up Your Environment 49 | 50 | ```bash 51 | # Clone your fork 52 | git clone https://github.com/YOUR_USERNAME/mathcore.git 53 | cd mathcore 54 | 55 | # Add upstream remote 56 | git remote add upstream https://github.com/Nonanti/mathcore.git 57 | 58 | # Install Rust (if not already installed) 59 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 60 | 61 | # Build the project 62 | cargo build 63 | 64 | # Run tests 65 | cargo test 66 | 67 | # Run benchmarks 68 | cargo bench 69 | ``` 70 | 71 | ### Code Style 72 | 73 | - Use `cargo fmt` to format your code 74 | - Use `cargo clippy` to catch common mistakes 75 | - Follow Rust naming conventions: 76 | - Types: `PascalCase` 77 | - Functions/methods: `snake_case` 78 | - Constants: `SCREAMING_SNAKE_CASE` 79 | - Modules: `snake_case` 80 | 81 | ### Testing 82 | 83 | - Write unit tests for new functionality 84 | - Ensure all tests pass before submitting PR 85 | - Add integration tests for complex features 86 | - Include doc tests in your documentation 87 | 88 | Example test: 89 | ```rust 90 | #[cfg(test)] 91 | mod tests { 92 | use super::*; 93 | 94 | #[test] 95 | fn test_new_feature() { 96 | // Test implementation 97 | assert_eq!(2 + 2, 4); 98 | } 99 | } 100 | ``` 101 | 102 | ### Documentation 103 | 104 | - Add documentation comments to all public items 105 | - Include examples in documentation 106 | - Update README.md if adding major features 107 | - Update CHANGELOG.md following the Keep a Changelog format 108 | 109 | Example documentation: 110 | ```rust 111 | /// Computes the factorial of a number. 112 | /// 113 | /// # Examples 114 | /// 115 | /// ``` 116 | /// use mathcore::factorial; 117 | /// 118 | /// assert_eq!(factorial(5), 120); 119 | /// ``` 120 | pub fn factorial(n: u32) -> u32 { 121 | // Implementation 122 | } 123 | ``` 124 | 125 | ### Commit Messages 126 | 127 | - Use the present tense ("Add feature" not "Added feature") 128 | - Use the imperative mood ("Move cursor to..." not "Moves cursor to...") 129 | - Limit the first line to 72 characters or less 130 | - Reference issues and pull requests liberally after the first line 131 | 132 | Format: 133 | ``` 134 | (): 135 | 136 | 137 | 138 |