use rust_physics_engine::exact::rational::Rational;
use rust_physics_engine::exact::symbolic::Expr;
use rust_physics_engine::units::dimensional::{buckingham_pi, dimensional_check_formula};
use rust_physics_engine::units::quantity::{parse_quantity, unit_convert, Dim, Quantity};
fn main() {
let force = Quantity::newtons(4.45);
let distance = Quantity::meters(2.0);
let work = force.mul(&distance).expect("force times distance");
println!("dimensions");
println!(" 4.45 N x 2 m = {:.2} {}", work.value, work.dim);
assert_eq!(work.dim, Dim::new(2, 1, -2, 0, 0, 0, 0));
let time = Quantity::seconds(3.0);
println!(" adding a force to a time -> {}", force.add(&time).unwrap_err());
assert!(force.add(&time).is_err());
let area = Quantity::new(9.0, Dim::new(2, 0, 0, 0, 0, 0, 0));
println!(" sqrt(9 m^2) = {:.1} {}", area.sqrt().unwrap().value, area.sqrt().unwrap().dim);
assert!(Quantity::meters(9.0).sqrt().is_err());
let g = parse_quantity("9.81 m/s^2").expect("a readable quantity");
println!(" \"9.81 m/s^2\" parses to {} {}", g.value, g.dim);
let mps = unit_convert(3.6, "km/h", "m/s").expect("same dimension");
println!(" 3.6 km/h = {mps:.1} m/s");
assert!((mps - 1.0).abs() < 1e-12);
let vars = [
("l", Dim::LENGTH),
("g", Dim::new(1, 0, -2, 0, 0, 0, 0)),
("t", Dim::TIME),
("omega", Dim::new(0, 0, -1, 0, 0, 0, 0)),
];
let over_g = Expr::pow(Expr::var("g"), Expr::c(-1.0));
let pendulum = Expr::Sqrt(Box::new(Expr::mul(vec![Expr::var("l"), over_g])));
println!("\nformula checking");
println!(" sqrt(l/g) has dimension {}", dimensional_check_formula(&pendulum, &vars).unwrap());
assert_eq!(dimensional_check_formula(&pendulum, &vars).unwrap(), Dim::TIME);
let good = Expr::Sin(Box::new(Expr::mul(vec![Expr::var("omega"), Expr::var("t")])));
let bad = Expr::Sin(Box::new(Expr::var("t")));
println!(" sin(omega*t) checks out; sin(t) does not");
assert!(dimensional_check_formula(&good, &vars).is_ok());
assert!(dimensional_check_formula(&bad, &vars).is_err());
let pipe = [
Dim::new(-3, 1, 0, 0, 0, 0, 0), Dim::new(1, 0, -1, 0, 0, 0, 0), Dim::LENGTH, Dim::new(-1, 1, -1, 0, 0, 0, 0), ];
let groups = buckingham_pi(&pipe).expect("a well-posed problem");
println!("\nBuckingham's theorem");
println!(" 4 quantities, rank 3 -> {} group", groups.len());
let exponents: Vec<String> = groups[0].iter().map(|r| format!("{r}")).collect();
println!(" exponents (rho, u, d, mu): {}", exponents.join(", "));
println!(" that is rho^-1 u^-1 d^-1 mu, which is 1/Re -- the theorem finds");
println!(" a basis for the null space, not the name anybody gave it");
assert_eq!(groups.len(), 1);
println!(" computed over Rational, so the cancellation is exact, not 1e-16");
assert!(groups[0].iter().all(|r| *r == Rational::from_i64(-1, 1) || *r == Rational::one()));
let tenth = Rational::from_i64(1, 10);
let fifth = Rational::from_i64(1, 5);
let sum = tenth.add(&fifth);
println!("\nexact arithmetic");
println!(" 0.1 + 0.2 in f64 = {:.17}", 0.1 + 0.2);
println!(" 1/10 + 1/5 exact = {sum}");
assert_ne!(0.1 + 0.2, 0.3);
assert_eq!(sum, Rational::from_i64(3, 10));
let as_stored = Rational::from_f64_exact(0.1).expect("finite");
println!(" and 0.1 as an f64 is really {as_stored}");
assert_ne!(as_stored, Rational::from_i64(1, 10));
}