computorv1 1.0.11

An educational computor project solving polynomial equations.
Documentation
use std::fmt;
use ft_lib::ft_abs::ft_abs;
#[derive(Debug, PartialEq, Clone)]
struct Fraction {
    numerator: i64,
    denominator: i64,
}

impl fmt::Display for Fraction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.denominator == 1 {
            write!(f, "{}", self.numerator)
        } else {
            write!(f, "{}/{}", self.numerator, self.denominator)
        }
    }
}

pub fn reduce_decimal(solution: f64) -> String {
    let mut fraction: Fraction = decimal_to_fraction(solution);
    reduce_fraction(&mut fraction);

    if is_interesting_fraction(&fraction) {
        fraction.to_string()
    } else {
        format!("{:.5}", solution)
    }
}

fn decimal_to_fraction(decimal: f64) -> Fraction {
    const MAX_DENOM: i64 = 1_000_000;
    const EPSILON: f64 = 1e-7;

    let negative: bool = decimal < 0.0;
    let x: f64 = ft_abs(decimal);

    let mut best_n: i64 = 0;
    let mut best_d: i64 = 1;
    let mut best_error: f64 = f64::MAX;

    for d in 1..=MAX_DENOM {
        let n: i64 = (x * d as f64).round() as i64;
        let approx: f64 = n as f64 / d as f64;
        let error: f64 = ft_abs(x - approx);

        if error < best_error - EPSILON {
            best_n = n;
            best_d = d;
            best_error = error;

            if error < EPSILON {
                break;
            }
        }
    }

    Fraction {
        numerator: if negative { -best_n } else { best_n },
        denominator: best_d,
    }
}

fn reduce_fraction(fraction: &mut Fraction) {
    let gcd_val = gcd(fraction.numerator.abs(), fraction.denominator);
    fraction.numerator /= gcd_val;
    fraction.denominator /= gcd_val;
}

fn gcd(mut a: i64, mut b: i64) -> i64 {
    while b != 0 {
        let temp = b;
        b = a % b;
        a = temp;
    }
    a
}
fn is_interesting_fraction(fraction: &Fraction) -> bool {
    (fraction.denominator <= 10 && fraction.denominator >= -10)
        && (fraction.numerator <= 10 && fraction.numerator >= -10)
        || fraction.denominator == 1
        || (fraction.numerator % 2 == 0 && fraction.denominator % 2 == 0)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_simple_fractions() {
        assert_eq!(
            decimal_to_fraction(0.5),
            Fraction {
                numerator: 1,
                denominator: 2
            }
        );
        assert_eq!(
            decimal_to_fraction(0.25),
            Fraction {
                numerator: 1,
                denominator: 4
            }
        );
        assert_eq!(
            decimal_to_fraction(1.25),
            Fraction {
                numerator: 5,
                denominator: 4
            }
        );
        assert_eq!(
            decimal_to_fraction(0.75),
            Fraction {
                numerator: 3,
                denominator: 4
            }
        );
        assert_eq!(
            decimal_to_fraction(1.00),
            Fraction {
                numerator: 1,
                denominator: 1
            }
        );
        assert_eq!(
            decimal_to_fraction(5.0),
            Fraction {
                numerator: 5,
                denominator: 1
            }
        );
    }

    #[test]
    fn test_repeating_fractions() {
        assert_eq!(
            decimal_to_fraction(0.3333333),
            Fraction {
                numerator: 1,
                denominator: 3
            }
        );
        assert_eq!(
            decimal_to_fraction(0.6666667),
            Fraction {
                numerator: 2,
                denominator: 3
            }
        );
        assert_eq!(
            decimal_to_fraction(0.2),
            Fraction {
                numerator: 1,
                denominator: 5
            }
        );
    }

    #[test]
    fn test_large_numbers() {
        assert_eq!(
            decimal_to_fraction(123.75),
            Fraction {
                numerator: 495,
                denominator: 4
            }
        );
    }

    #[test]
    fn test_negative_values() {
        assert_eq!(
            decimal_to_fraction(-0.5),
            Fraction {
                numerator: -1,
                denominator: 2
            }
        );
        assert_eq!(
            decimal_to_fraction(-1.3333333),
            Fraction {
                numerator: -4,
                denominator: 3
            }
        );
    }

    #[test]
    fn test_rounding_edge_case() {
        assert_eq!(
            decimal_to_fraction(2.00000001),
            Fraction {
                numerator: 2,
                denominator: 1
            }
        );
    }

    #[test]
    fn test_zero() {
        assert_eq!(
            decimal_to_fraction(0.0),
            Fraction {
                numerator: 0,
                denominator: 1
            }
        );
    }

    #[test]
    fn test_one() {
        assert_eq!(
            decimal_to_fraction(1.0),
            Fraction {
                numerator: 1,
                denominator: 1
            }
        );
    }

    #[test]
    fn test_large_fraction() {
        assert_eq!(
            decimal_to_fraction(0.000123),
            Fraction {
                numerator: 1,
                denominator: 8128
            }
        );
    }

    #[test]
    fn test_close_to_whole_number() {
        assert_eq!(
            decimal_to_fraction(4.9999999),
            Fraction {
                numerator: 5,
                denominator: 1
            }
        );
        assert_eq!(
            decimal_to_fraction(5.0000001),
            Fraction {
                numerator: 5,
                denominator: 1
            }
        );
    }

    #[test]
    fn test_half_plus_small() {
        assert_eq!(
            decimal_to_fraction(0.5000001),
            Fraction {
                numerator: 1,
                denominator: 2
            }
        );
    }

    #[test]
    fn test_negative_large_fraction() {
        assert_eq!(
            decimal_to_fraction(-12.75),
            Fraction {
                numerator: -51,
                denominator: 4
            }
        );
    }

    #[test]
    fn test_pi_approx() {
        // π ≈ 3.14159
        let frac = decimal_to_fraction(3.14159);
        assert!((frac.numerator as f64 / frac.denominator as f64 - 3.14159).abs() < 1e-6);
    }

    #[test]
    fn test_extreme_precision() {
        assert_eq!(
            decimal_to_fraction(0.0000001),
            Fraction {
                numerator: 0,
                denominator: 1
            }
        );
    }

    #[test]
    fn test_fraction_reduction() {
        let mut frac = Fraction {
            numerator: 2,
            denominator: 4,
        };
        reduce_fraction(&mut frac);
        assert_eq!(
            frac,
            Fraction {
                numerator: 1,
                denominator: 2
            }
        );

        let mut frac = Fraction {
            numerator: 100,
            denominator: 400,
        };
        reduce_fraction(&mut frac);
        assert_eq!(
            frac,
            Fraction {
                numerator: 1,
                denominator: 4
            }
        );

        let mut frac = Fraction {
            numerator: 10,
            denominator: 10,
        };
        reduce_fraction(&mut frac);
        assert_eq!(
            frac,
            Fraction {
                numerator: 1,
                denominator: 1
            }
        );

        let mut frac = Fraction {
            numerator: 10,
            denominator: 2,
        };
        reduce_fraction(&mut frac);
        assert_eq!(
            frac,
            Fraction {
                numerator: 5,
                denominator: 1
            }
        );
    }
    #[test]
    fn test_is_interesting_fraction() {
        assert_eq!(
            is_interesting_fraction(&Fraction {
                numerator: 1,
                denominator: 3
            }),
            true
        );
        assert_eq!(
            is_interesting_fraction(&Fraction {
                numerator: 2,
                denominator: 5
            }),
            true
        );
        assert_eq!(
            is_interesting_fraction(&Fraction {
                numerator: 5,
                denominator: 1
            }),
            true
        );
        assert_eq!(
            is_interesting_fraction(&Fraction {
                numerator: 10,
                denominator: 2
            }),
            true
        );
        assert_eq!(
            is_interesting_fraction(&Fraction {
                numerator: 7,
                denominator: 6
            }),
            true
        );
        assert_eq!(
            is_interesting_fraction(&Fraction {
                numerator: 0,
                denominator: 1
            }),
            true
        );
        assert_eq!(
            is_interesting_fraction(&Fraction {
                numerator: 495,
                denominator: 4
            }),
            false
        )
    }
}