use crate::scalar::Scalar;
use std::cmp::Ordering;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IntegerDivExactError {
DivisionByZero,
Remainder(Integer),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn integer_basic_ops() {
let a = Integer(5);
let b = Integer(3);
assert_eq!(a.add(&b), Integer(8));
assert_eq!(a.mul(&b), Integer(15));
assert_eq!(a.neg(), Integer(-5));
assert_eq!(Integer(i128::MAX).neg(), Integer(i128::MIN + 1));
}
#[test]
#[should_panic(expected = "Integer addition overflowed i128")]
fn integer_add_overflows_loudly() {
let _ = Integer(i128::MAX).add(&Integer(1));
}
#[test]
#[should_panic(expected = "Integer negation overflowed i128")]
fn integer_neg_overflows_loudly() {
let _ = Integer(i128::MIN).neg();
}
#[test]
#[should_panic(expected = "Integer multiplication overflowed i128")]
fn integer_mul_overflows_loudly() {
let _ = Integer(i128::MAX).mul(&Integer(2));
}
#[test]
fn euclidean_division_uses_nonnegative_remainders() {
assert_eq!(
Integer(7).divrem(&Integer(3)),
Some((Integer(2), Integer(1)))
);
assert_eq!(
Integer(-7).divrem(&Integer(3)),
Some((Integer(-3), Integer(2)))
);
assert_eq!(
Integer(7).divrem(&Integer(-3)),
Some((Integer(-2), Integer(1)))
);
assert_eq!(Integer(7).rem(&Integer(0)), None);
}
#[test]
fn exact_division_reports_the_remainder() {
assert_eq!(Integer(6).div_exact(&Integer(3)), Ok(Integer(2)));
assert_eq!(
Integer(7).div_exact(&Integer(3)),
Err(IntegerDivExactError::Remainder(Integer(1)))
);
assert_eq!(
Integer(7).div_exact(&Integer(0)),
Err(IntegerDivExactError::DivisionByZero)
);
}
#[test]
fn standard_order_is_the_integer_order() {
assert!(Integer(-2) < Integer(5));
assert_eq!(
std::cmp::Ord::cmp(&Integer(4), &Integer(4)),
Ordering::Equal
);
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Integer(pub i128);
impl Integer {
pub fn divrem(&self, divisor: &Self) -> Option<(Self, Self)> {
if divisor.0 == 0 {
return None;
}
let q = self
.0
.checked_div_euclid(divisor.0)
.expect("Integer Euclidean quotient overflowed i128");
let r = self
.0
.checked_rem_euclid(divisor.0)
.expect("Integer Euclidean remainder overflowed i128");
Some((Integer(q), Integer(r)))
}
pub fn rem(&self, divisor: &Self) -> Option<Self> {
self.divrem(divisor).map(|(_, r)| r)
}
pub fn div_exact(&self, divisor: &Self) -> Result<Self, IntegerDivExactError> {
let (q, r) = self
.divrem(divisor)
.ok_or(IntegerDivExactError::DivisionByZero)?;
if r.is_zero() {
Ok(q)
} else {
Err(IntegerDivExactError::Remainder(r))
}
}
}
impl From<i128> for Integer {
fn from(n: i128) -> Self {
Integer(n)
}
}
impl fmt::Display for Integer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Debug for Integer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl PartialOrd for Integer {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(std::cmp::Ord::cmp(self, other))
}
}
impl Ord for Integer {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl Scalar for Integer {
fn zero() -> Self {
Integer(0)
}
fn one() -> Self {
Integer(1)
}
fn add(&self, rhs: &Self) -> Self {
Integer(
self.0
.checked_add(rhs.0)
.expect("Integer addition overflowed i128"),
)
}
fn neg(&self) -> Self {
Integer(
self.0
.checked_neg()
.expect("Integer negation overflowed i128"),
)
}
fn mul(&self, rhs: &Self) -> Self {
Integer(
self.0
.checked_mul(rhs.0)
.expect("Integer multiplication overflowed i128"),
)
}
fn characteristic() -> u128 {
0
}
fn inv(&self) -> Option<Self> {
match self.0 {
1 | -1 => Some(*self),
_ => None, }
}
fn from_int(n: i128) -> Self {
Integer(n)
}
}