use crate::number::{
instances::int::Int,
traits::{fractional::Fractional, integral::Integral, number::Number, real::Real},
};
pub fn i8_div_mod(lhs: i8, rhs: i8) -> (i8, i8) {
let quot = lhs / rhs;
let rem = lhs % rhs;
if rem == 0i8 || (quot >= 0i8 && rem > 0i8) {
(quot, rem)
} else {
(quot - 1, lhs - (quot - 1) * rhs)
}
}
pub fn non_negative_integral_power<N: Number, I: Integral>(base: N, exponents: I) -> Option<N> {
fn inner_power<N: Number, I: Integral>(base: N, exponents: I) -> N {
if exponents.is_even() {
inner_power(base.clone() * base, exponents.quotient(I::one() + I::one()))
} else if exponents.is_one() {
base
} else {
inner_power_acc(
base.clone() * base.clone(),
exponents.quotient(I::one() + I::one()),
base,
)
}
}
fn inner_power_acc<N: Number, I: Integral>(base: N, exponents: I, acc: N) -> N {
if exponents.is_even() {
inner_power_acc(
base.clone() * base,
exponents.quotient(I::one() + I::one()),
acc,
)
} else if exponents.is_one() {
acc * base
} else {
inner_power_acc(
base.clone() * base.clone(),
exponents.quotient(I::one() + I::one()),
acc * base,
)
}
}
if exponents < I::zero() {
eprintln!(
"Error[number::utils::non_negative_integral_power]: Negative exponents ({}) are not allowed.",
exponents
);
None
} else if exponents == I::zero() {
Some(N::one())
} else if base == N::zero() {
Some(N::zero())
} else {
Some(inner_power(base, exponents))
}
}
pub fn clamp(first: Int, second: Int) -> Int {
(-first.clone()).max(first.min(second))
}
pub fn integral_power<F: Fractional, I: Integral>(base: F, exponents: I) -> Option<F> {
if exponents < I::zero() {
non_negative_integral_power(base, -exponents).map(|v| v.reciprocal())
} else {
non_negative_integral_power(base, exponents)
}
}
pub fn gcd<I: Integral>(lhs: I, rhs: I) -> I {
fn inner_gcd<I: Integral>(lhs: I, rhs: I) -> I {
if rhs == I::zero() || lhs == rhs {
lhs
} else {
inner_gcd(rhs.clone(), lhs.remainder(rhs))
}
}
inner_gcd(lhs.absolute_value(), rhs.absolute_value())
}
pub fn lcm<I: Integral>(lhs: I, rhs: I) -> I {
if lhs.is_zero() || rhs.is_zero() {
I::zero()
} else {
lhs.clone().quotient(gcd(lhs, rhs.clone())) * rhs
}
}
pub fn from_integral<N: Number, I: Integral>(integral_number: I) -> N {
N::from_integer(integral_number.to_integer())
}
pub fn real_to_frac<R: Real, F: Fractional>(real_number: R) -> F {
F::from_rational(real_number.to_rational())
}