use crate::Rational;
pub fn r(n: i128, d: i128) -> Rational {
Rational::new(n, d)
}
pub fn gcd(a: i128, b: i128) -> i128 {
gcd_checked(a, b)
.unwrap_or_else(|| panic!("the gcd of {} and {} is equal to i128::MAX+1, which does not fit in the i128 primitive type", a, b))
}
pub fn gcd_checked(mut a: i128, mut b: i128) -> Option<i128> {
if a == 0 || b == 0 {
return return_gcd_checked(a | b);
}
let factors_of_two = (a | b).trailing_zeros();
if a == i128::MIN || b == i128::MIN {
return return_gcd_checked(1 << factors_of_two);
}
a = a.abs() >> a.trailing_zeros();
b = b.abs() >> b.trailing_zeros();
while a != b {
if a > b {
a -= b;
a >>= a.trailing_zeros();
} else {
b -= a;
b >>= b.trailing_zeros();
}
}
Some(a << factors_of_two)
}
fn return_gcd_checked(g: i128) -> Option<i128> {
if g == i128::MIN {
None
} else {
Some(g.abs())
}
}
pub fn lcm(a: i128, b: i128) -> i128 {
let g = gcd(a, b);
a.abs() * (b.abs() / g)
}
pub fn lcm_checked(a: i128, b: i128) -> Option<i128> {
let g = gcd(a, b);
a.abs().checked_mul(b.abs().checked_div(g)?)
}
pub fn is_coprime(l: i128, r: i128) -> bool {
gcd(l, r) == 1
}
pub fn continued_fraction(init: u64, cont: &[u64]) -> Rational {
let mut r = Rational::new(0, 1);
for &d in cont.iter().rev() {
r = Rational::new(1, r + d);
}
r + init
}
pub fn continued_fraction_iter(init: u64, cont: &[u64]) -> ContinuedFractionIter<'_> {
ContinuedFractionIter::new(Rational::new(init, 1), cont)
}
pub struct ContinuedFractionIter<'a> {
init: Rational,
fraction: Rational,
idx: usize,
cont: &'a [u64],
}
impl<'a> ContinuedFractionIter<'a> {
fn new(init: Rational, cont: &'a [u64]) -> Self {
Self {
init,
fraction: Rational::zero(),
idx: 0,
cont,
}
}
pub fn decimals(self) -> impl Iterator<Item = f64> + 'a {
self.map(|r| r.decimal_value())
}
}
impl Iterator for ContinuedFractionIter<'_> {
type Item = Rational;
fn next(&mut self) -> Option<Self::Item> {
if self.cont.is_empty() {
if self.idx == 0 {
self.idx += 1;
Some(self.init)
} else {
None
}
} else if self.idx > self.cont.len() {
None
} else if self.idx == self.cont.len() {
self.idx += 1;
Some(self.init + self.fraction)
} else {
let curr = self.init + self.fraction;
self.fraction =
Rational::new(1, self.fraction + self.cont[self.cont.len() - self.idx - 1]);
self.idx += 1;
Some(curr)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gcd_test() {
let eq = |a: i128, b: i128, g: i128| assert_eq!(gcd(a, b), g, "a: {}, b: {}", a, b);
eq(1, 2, 1);
eq(5, 4, 1);
eq(12, 4, 4);
eq(-74, 44, 2);
eq(-2, -4, 2);
eq(i128::MIN, 1, 1);
}
#[test]
#[should_panic]
fn gcd_should_panic_test_1() {
dbg!(gcd(i128::MIN, 0));
}
#[test]
#[should_panic]
fn gcd_should_panic_test_2() {
dbg!(gcd(0, i128::MIN));
}
#[test]
#[should_panic]
fn gcd_should_panic_test_3() {
dbg!(gcd(i128::MIN, i128::MIN));
}
#[test]
fn lcm_test() {
assert_eq!(lcm(2, 6), 6);
assert_eq!(lcm(1, 6), 6);
}
#[test]
fn repeated_test() {
use std::f64::consts::*;
let assert = |init: u64, cont: &[u64], expected: f64| {
let actual = continued_fraction(init, cont).decimal_value();
assert!(
(actual - expected).abs() < 0.000000001,
"actual: {}, expected: {}",
actual,
expected
);
};
assert(1, &[2; 15], 2.0_f64.sqrt());
assert(1, &[1; 100], 1.6180339887498);
assert(4, &[2, 1, 3, 1, 2, 8, 2, 1, 3, 1, 2, 8], 19.0_f64.sqrt());
assert(1, &[], 1.0);
assert(4, &[], 4.0);
assert(2, &[1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, 1, 1, 10], E);
assert(3, &[7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1], PI);
}
#[test]
fn continued_fraction_iter_test() {
let decimal_approx: Vec<_> = continued_fraction_iter(2, &[1, 2, 1, 1, 4, 1, 1, 6])
.decimals()
.collect();
assert_eq!(
decimal_approx,
vec![
2.0,
2.1666666666666665,
2.857142857142857,
2.5384615384615383,
2.2203389830508473,
2.8194444444444446,
2.549618320610687,
2.3922155688622753,
2.718279569892473,
]
);
}
}