use crate::error::IntegerError;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IntegerSign {
Negative,
Zero,
Positive,
}
#[must_use]
pub const fn classify_sign(value: i128) -> IntegerSign {
if value < 0 {
IntegerSign::Negative
} else if value > 0 {
IntegerSign::Positive
} else {
IntegerSign::Zero
}
}
#[must_use]
pub const fn is_even(value: i128) -> bool {
value % 2 == 0
}
#[must_use]
pub const fn is_odd(value: i128) -> bool {
!is_even(value)
}
pub const fn is_divisible_by(value: i128, divisor: i128) -> Result<bool, IntegerError> {
if divisor == 0 {
Err(IntegerError::DivisionByZero)
} else {
Ok(value % divisor == 0)
}
}
#[must_use]
pub const fn gcd(left: i128, right: i128) -> u128 {
gcd_u128(left.unsigned_abs(), right.unsigned_abs())
}
#[must_use]
pub const fn are_coprime(left: i128, right: i128) -> bool {
gcd(left, right) == 1
}
pub const fn lcm(left: i128, right: i128) -> Result<u128, IntegerError> {
if left == 0 || right == 0 {
return Ok(0);
}
let divisor = gcd(left, right);
let scaled = left.unsigned_abs() / divisor;
match scaled.checked_mul(right.unsigned_abs()) {
Some(result) => Ok(result),
None => Err(IntegerError::ArithmeticOverflow { operation: "lcm" }),
}
}
const fn gcd_u128(mut left: u128, mut right: u128) -> u128 {
while right != 0 {
let remainder = left % right;
left = right;
right = remainder;
}
left
}
#[cfg(test)]
mod tests {
use super::{
IntegerSign, are_coprime, classify_sign, gcd, is_divisible_by, is_even, is_odd, lcm,
};
use crate::IntegerError;
#[test]
fn classifies_sign_and_parity() {
assert_eq!(classify_sign(-1), IntegerSign::Negative);
assert_eq!(classify_sign(0), IntegerSign::Zero);
assert_eq!(classify_sign(1), IntegerSign::Positive);
assert!(is_even(-8));
assert!(is_odd(-7));
}
#[test]
fn computes_divisibility_and_common_divisors() -> Result<(), IntegerError> {
assert!(is_divisible_by(81, 9)?);
assert!(!is_divisible_by(81, 8)?);
assert_eq!(gcd(-54, 24), 6);
assert_eq!(gcd(0, 0), 0);
assert_eq!(lcm(-6, 15)?, 30);
assert_eq!(lcm(0, 15)?, 0);
assert!(are_coprime(35, 64));
Ok(())
}
#[test]
fn rejects_invalid_divisors_and_reports_overflow() {
assert_eq!(is_divisible_by(10, 0), Err(IntegerError::DivisionByZero));
assert!(matches!(
lcm(i128::MAX, i128::MAX - 1),
Err(IntegerError::ArithmeticOverflow { operation: "lcm" })
));
}
}