computorv1/src/maths/solver.rs

401 lines
14 KiB
Rust

use super::*;
fn degree_zero(equation: Vec<GaussianRational>) {
println!("Polynomial degree: 0");
if equation[0] == zero() {
println!("Every real number is a solution ! Personally, I'd rather say that this equation is valid.")
} else {
println!("This equation has no solution ! Personally, I'd rather say that this equation is invalid.")
}
}
fn degree_one(equation: Vec<GaussianRational>) {
println!("Polynomial degree: 1");
let x = minus_one() * equation[0] / equation[1];
println!("The solution is");
let mut format = x.format(false);
if format == "" {
format = String::from("0");
}
println!("x = {}", format);
}
#[derive(Debug)]
struct MySqrt {
numerator_natural: i128,
numerator_irrational: i128,
denominator: i128,
}
#[derive(Debug)]
struct MyCompSqrt {
sqrt: MySqrt,
rational: Rational,
sign: i128,
}
fn simplify_sqrt(n: i128) -> (i128, i128) {
if n == 0 {
return (0, 0);
}
let mut prime_factors = get_prime_factors(n);
let (mut natural, mut irrational) = (1, 1);
prime_factors = prime_factors.into_iter().rev().collect();
while prime_factors.len() > 1 {
let pop = prime_factors.pop().unwrap();
if *prime_factors.last().unwrap() == pop {
natural *= pop;
prime_factors.pop();
} else {
irrational *= pop;
}
}
if let Some(n) = prime_factors.pop() {
irrational *= n;
}
(natural, irrational)
}
// Eric Naslund for the math https://math.stackexchange.com/questions/44406/how-do-i-get-the-square-root-of-a-complex-number
fn comp_sqrt(c: GaussianRational) -> MyCompSqrt {
let real = c.real();
let imag = c.imaginary();
let n = real * real + imag * imag;
let numerator_irrational = n.numerator * n.denominator;
let (mut numerator_natural, numerator_irrational) = simplify_sqrt(numerator_irrational);
let mut denominator = n.denominator;
let gcd_var = gcd(numerator_natural, denominator);
denominator /= gcd_var;
numerator_natural /= gcd_var;
let mut sqrt = MySqrt {
numerator_natural,
numerator_irrational,
denominator,
};
let mut sign = 1;
if c.imaginary.numerator < 0 {
sign = -1;
}
let mut rational = c.real;
rational = rational / 2;
if sqrt.numerator_natural % 2 == 0 {
sqrt.numerator_natural /= 2;
} else {
sqrt.denominator *= 2;
}
// Here we multiply by 4 to avoid dividing by 2 later and ease ascii art
let mem = sqrt.denominator;
sqrt.denominator *= rational.denominator * 4;
sqrt.numerator_natural *= rational.denominator;
rational.numerator *= mem;
rational.denominator *= mem * 4;
let gcd = gcd(gcd(sqrt.denominator, sqrt.numerator_natural), rational.numerator);
sqrt.denominator /= gcd;
sqrt.numerator_natural /= gcd;
rational.numerator /= gcd;
rational.denominator /= gcd;
MyCompSqrt {
sqrt,
rational,
sign,
}
}
fn get_strings(left_part: Rational, sqrt_delta: &MySqrt) -> (String, String, String, String, String, String) {
let mut a = left_part.numerator;
let mut b = sqrt_delta.numerator_natural;
if b < 0 {
b = -b;
}
let c = sqrt_delta.numerator_irrational;
let mut d = left_part.denominator;
if d < 0 {
a = -a;
d = -d;
}
let mut string = String::from("");
if b != 0 || c != 0 {
if b != 1 && c != 1 {
string = format!("{b} * sqrt({c})");
} else if b != 1 {
string = format!("{b}");
} else if c != 1 {
string = format!("sqrt({c})");
} else {
string = format!("1");
}
}
let mut string_a = format!("{string}");
let mut string_b = format!("{string}");
if a != 0 {
string_a = format!("{a} + {string}");
string_b = format!("{} + {string}", -a);
}
let len = std::cmp::max(string_a.len(), format!("{d}").len()) + 2;
let len_b = std::cmp::max(string_b.len(), format!("{d}").len()) + 2;
let len1 = len - string_a.len();
let len2 = len - d.to_string().len();
let len1b = len_b - string_b.len();
let len2b = len_b - d.to_string().len();
let string1;
let string2;
let string3;
let string4;
let string5;
let string6;
if d != 1 {
string1 = format!("{}{string_a}{}", " ".repeat(len1 / 2), " ".repeat(len1 / 2 + len1 % 2));
string2 = format!("{}", "-".repeat(len));
string3 = format!("{}{d}{}", " ".repeat(len2 / 2), " ".repeat(len2 / 2 + len2 % 2));
string4 = format!("{}{string_b}{}", " ".repeat(len1b / 2), " ".repeat(len1b / 2 + len1b % 2));
string5 = format!("{}", "-".repeat(len_b));
string6 = format!("{}{d}{}", " ".repeat(len2b / 2), " ".repeat(len2b / 2 + len2b % 2));
} else if string.len() > 0 {
string1 = format!("{}", string.len());
string2 = format!("{string_a}");
string3 = format!("{}", string.len());
string4 = format!("{}", string.len());
string5 = format!("{string_b}");
string6 = format!("{}", string.len());
} else {
string1 = format!(" ");
string2 = format!("0");
string3 = format!(" ");
string4 = format!(" ");
string5 = format!("0");
string6 = format!(" ");
}
(string1, string2, string3, string4, string5, string6)
}
fn degree_two_complex(a: GaussianRational, b: GaussianRational, c: GaussianRational) {
// Here we divide by a to avoid dividing by complex later and ease ascii art
let b = b / a;
let c = c / a;
let delta = b * b - 4 * c;
let sqrt_delta = comp_sqrt(delta);
let b = b / GaussianRational::new(Rational::new(-2, 1), Rational::new(0, 1));
let mut sign = '±';
if sqrt_delta.sign < 0 {
sign = '∓';
}
let (s1, s2, s3, s4, s5, s6) = get_strings(sqrt_delta.rational, &sqrt_delta.sqrt);
let mut string1 = String::from("");
let mut string2 = String::from("");
let mut string3 = String::from("");
if b.real.numerator != 0 {
if b.real.denominator != 1 {
let b_real_len = std::cmp::max(format!("{}", b.real.denominator).len(), format!("{}", b.real.numerator).len()) + 2;
let space_num = b_real_len - format!("{}", b.real.numerator).len();
let space_dem = b_real_len - format!("{}", b.real.denominator).len();
string1 = format!("{}{}{}", " ".repeat(space_num / 2), b.real.numerator, " ".repeat(space_num / 2 + space_num % 2));
string2 = format!("{}", "-".repeat(b_real_len));
string3 = format!("{}{}{}", " ".repeat(space_dem / 2), b.real.denominator, " ".repeat(space_dem / 2 + space_dem % 2));
} else {
let string = format!("{}", b.real.numerator);
string1 = format!("{}", " ".repeat(string.len()));
string2 = format!("{string}");
string3 = format!("{}", " ".repeat(string.len()));
}
}
string1 = format!("{string1} /{s1}\\ ");
string2 = format!("{string2} ± sqrt | {s2} | ");
string3 = format!("{string3} \\{s3}/ ");
if b.imaginary.numerator != 0 {
if b.imaginary.denominator != 1 {
let b_imag_len = std::cmp::max(format!("{}", b.imaginary.denominator).len(), format!("{}", b.imaginary.numerator).len()) + 2;
let space_num = b_imag_len - format!("{}", b.imaginary.numerator).len();
let space_dem = b_imag_len - format!("{}", b.imaginary.denominator).len();
string1 = format!("{string1} {}{}{} ", " ".repeat(space_num / 2), b.imaginary.numerator, " ".repeat(space_num / 2 + space_num % 2));
string2 = format!("{string2}+ {} i ", "-".repeat(b_imag_len));
string3 = format!("{string3} {}{}{} ", " ".repeat(space_dem / 2), b.imaginary.denominator, " ".repeat(space_dem / 2 + space_dem % 2));
} else {
let string = format!("{}", b.imaginary.numerator);
string1 = format!("{string1} {} ", " ".repeat(string.len()));
string2 = format!("{string2}+ {string}i ");
string3 = format!("{string3} {} ", " ".repeat(string.len()));
}
}
string1 = format!("{string1} /{s4}\\ ");
string2 = format!("{string2}{sign} sqrt | {s5} | i");
string3 = format!("{string3} \\{s6}/ ");
println!("The two solutions are:");
println!("{string1}");
println!("{string2}");
println!("{string3}");
println!("Approximations:");
let sqrt = sqrt_delta.sqrt.numerator_natural as f64 * approx_sqrt(sqrt_delta.sqrt.numerator_irrational);
let left = b.real.numerator as f64 / b.real.denominator as f64;
let left_sqrt = approx_sqrt_f64((sqrt_delta.rational.numerator as f64 + sqrt) / sqrt_delta.rational.denominator as f64);
let right = b.imaginary.numerator as f64 / b.imaginary.denominator as f64;
let right_sqrt = sqrt_delta.sign as f64 * approx_sqrt_f64((-sqrt_delta.rational.numerator as f64 + sqrt) / sqrt_delta.rational.denominator as f64);
println!("{}", format!("{} + {}i", left + left_sqrt, right + right_sqrt).split("+ -").collect::<Vec<_>>().join("- "));
println!("{}", format!("{} + {}i", left - left_sqrt, right - right_sqrt).split("+ -").collect::<Vec<_>>().join("- "));
}
fn sqrt(n: Rational, a: Rational) -> MySqrt {
let numerator_irrational = n.numerator * n.denominator;
let (mut numerator_natural, numerator_irrational) = simplify_sqrt(numerator_irrational);
numerator_natural *= a.denominator;
let mut denominator = n.denominator * 2 * a.numerator;
let gcd = gcd(numerator_natural, denominator);
denominator /= gcd;
numerator_natural /= gcd;
MySqrt {
numerator_natural,
numerator_irrational,
denominator,
}
}
fn print_degree_two_real(left_part: Rational, sqrt_delta: MySqrt, imaginary: bool) {
let mut a = left_part.numerator;
let mut b = sqrt_delta.numerator_natural;
if b < 0 {
b = -b;
}
let c = sqrt_delta.numerator_irrational;
let mut d = left_part.denominator;
if d < 0 {
a = -a;
d = -d;
}
let mut string = String::from("");
if a != 0 {
string = format!("{a} ");
}
if b != 0 || c != 0 {
if imaginary == true {
if b != 1 && c != 1 {
string = format!("{string}± ({b} * sqrt({c}))i");
} else if b != 1 {
string = format!("{string}± {b}i");
} else if c != 1 {
string = format!("{string}± sqrt({c})i");
} else {
string = format!("{string}± i");
}
} else {
if b != 1 && c != 1 {
string = format!("{string}± {b} * sqrt({c})");
} else if b != 1 {
string = format!("{string}± {b}");
} else if c != 1 {
string = format!("{string}± sqrt({c})");
} else {
string = format!("{string}± 1");
}
}
}
let len = std::cmp::max(string.chars().count(), format!("{d}").len()) + 2;
if b != 0 || c != 0 {
if imaginary == true {
println!("Discriminant is less than zero. The two complex solutions are:");
} else {
println!("Discriminant is more than zero. The two solutions are:");
}
} else {
println!("Discriminant is zero. The solution is:");
}
if d != 1 {
println!("{}{string}", " ".repeat(4 + (len - string.chars().count()) / 2));
println!("x = {}", "-".repeat(len));
println!("{}{d}", " ".repeat(4 + (len - d.to_string().len()) / 2));
let mut right = (sqrt_delta.numerator_natural as f64 * approx_sqrt(sqrt_delta.numerator_irrational)) / left_part.denominator as f64;
let left = left_part.numerator as f64 / left_part.denominator as f64;
if right < 0. {
right *= -1.;
}
println!("Approximations:");
if imaginary == true {
println!("{} - {}i", left, right);
println!("{} + {}i", left, right);
} else {
println!("{}", left - right);
println!("{}", left + right);
}
} else if string.len() > 0 {
println!("x = {string}");
} else {
println!("x = 0");
}
}
fn degree_two_real(a: Rational, b: Rational, c: Rational) {
let mut delta = b * b - 4 * a * c;
let mut imaginary = false;
if delta.numerator < 0 {
imaginary = true;
delta = -1 * delta;
}
let mut sqrt_delta = sqrt(delta, a);
let mut left_part = (-1 * b) / (2 * a);
let tmp = left_part.denominator;
left_part.denominator *= sqrt_delta.denominator;
left_part.numerator *= sqrt_delta.denominator;
sqrt_delta.numerator_natural *= tmp;
sqrt_delta.denominator *= tmp;
print_degree_two_real(left_part, sqrt_delta, imaginary);
}
fn degree_two(equation: Vec<GaussianRational>) {
println!("Polynomial degree: 2");
let (a, b, c) = (equation[2], equation[1], equation[0]);
if a.is_real() && b.is_real() && c.is_real() {
degree_two_real(a.real(), b.real(), c.real());
} else {
degree_two_complex(a, b, c);
}
}
pub fn solve(mut equation: Vec<GaussianRational>) -> Vec<GaussianRational> {
for i in (1..equation.len()).rev() {
if equation[i] == zero() {
equation.pop();
} else {
break;
}
}
match equation.len() {
0 => unreachable!(),
1 => degree_zero(equation),
2 => degree_one(equation),
3 => degree_two(equation),
_ => println!("Polynomial of degree greater than 2 detected, I can't solve that !"),
}
vec![]
}