use rug::{Float, Integer};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RoundingMode {
HalfToEven = 0,
HalfAwayFromZero = 1,
HalfTowardZero = 2,
HalfUp = 3,
HalfDown = 4,
Up = 5,
Down = 6,
TowardZero = 7,
AwayFromZero = 8,
}
impl Default for RoundingMode {
fn default() -> Self {
Self::HalfToEven
}
}
pub fn abs(numerator: i64, denominator: i64) -> (i64, i64) {
(numerator.abs(), denominator.abs())
}
pub fn abs_integer(n: &Integer) -> Integer {
n.clone().abs()
}
pub fn ceil(numerator: i64, denominator: i64) -> i64 {
let mut result = numerator / denominator;
let remainder = numerator % denominator;
if remainder > 0 && denominator > 0 || remainder < 0 && denominator < 0 {
result += 1;
} else if remainder < 0 && denominator > 0 || remainder > 0 && denominator < 0 {
}
result
}
pub fn ceil_float(f: &Float) -> Integer {
let s = f.to_string_radix(10, None);
let float_val: f64 = s.parse().unwrap_or(0.0);
let mut result = Integer::from(float_val as i64);
if float_val > 0.0 && float_val.fract() != 0.0 {
result += 1;
}
result
}
pub fn floor(numerator: i64, denominator: i64) -> i64 {
let mut result = numerator / denominator;
let remainder = numerator % denominator;
if remainder < 0 && denominator > 0 || remainder > 0 && denominator < 0 {
result -= 1;
}
result
}
pub fn floor_float(f: &Float) -> Integer {
let s = f.to_string_radix(10, None);
let float_val: f64 = s.parse().unwrap_or(0.0);
let mut result = Integer::from(float_val as i64);
if float_val < 0.0 && float_val.fract() != 0.0 {
result -= 1;
}
result
}
pub fn trunc(numerator: i64, denominator: i64) -> i64 {
numerator / denominator
}
pub fn trunc_float(f: &Float) -> Integer {
let s = f.to_string_radix(10, None);
let float_val: f64 = s.parse().unwrap_or(0.0);
Integer::from(float_val as i64)
}
pub fn round(numerator: i64, denominator: i64, mode: RoundingMode) -> i64 {
let integer_part = numerator / denominator;
let remainder = numerator.abs() % denominator.abs();
if remainder == 0 {
return integer_part;
}
let fraction = (remainder as f64) / (denominator.abs() as f64);
match mode {
RoundingMode::HalfToEven => {
if fraction > 0.5 {
integer_part + integer_part.signum()
} else if fraction < 0.5 {
integer_part
} else {
if integer_part % 2 == 0 {
integer_part
} else {
integer_part + integer_part.signum()
}
}
}
RoundingMode::HalfAwayFromZero => {
if fraction >= 0.5 {
integer_part + integer_part.signum()
} else {
integer_part
}
}
RoundingMode::HalfTowardZero => {
if fraction > 0.5 {
integer_part + integer_part.signum()
} else {
integer_part
}
}
RoundingMode::HalfUp => {
if fraction >= 0.5 {
integer_part + 1
} else {
integer_part
}
}
RoundingMode::HalfDown => {
if fraction > 0.5 {
integer_part + 1
} else {
integer_part
}
}
RoundingMode::Up => ceil(numerator, denominator),
RoundingMode::Down => floor(numerator, denominator),
RoundingMode::TowardZero => trunc(numerator, denominator),
RoundingMode::AwayFromZero => {
if integer_part >= 0 {
ceil(numerator, denominator)
} else {
floor(numerator, denominator)
}
}
}
}
pub fn signum(n: i64) -> i64 {
n.signum()
}
pub fn signum_integer(n: &Integer) -> Integer {
if n.is_zero() {
Integer::from(0)
} else if n.is_positive() {
Integer::from(1)
} else {
Integer::from(-1)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_abs() {
assert_eq!(abs(7, 3), (7, 3));
assert_eq!(abs(-7, 3), (7, 3));
assert_eq!(abs(7, -3), (7, 3));
}
#[test]
fn test_ceil() {
assert_eq!(ceil(7, 3), 3);
assert_eq!(ceil(-7, 3), -2);
assert_eq!(ceil(6, 3), 2);
assert_eq!(ceil(-6, 3), -2);
assert_eq!(ceil(1, 2), 1);
assert_eq!(ceil(-1, 2), 0);
}
#[test]
fn test_floor() {
assert_eq!(floor(7, 3), 2);
assert_eq!(floor(-7, 3), -3);
assert_eq!(floor(6, 3), 2);
assert_eq!(floor(-6, 3), -2);
assert_eq!(floor(1, 2), 0);
assert_eq!(floor(-1, 2), -1);
}
#[test]
fn test_trunc() {
assert_eq!(trunc(7, 3), 2);
assert_eq!(trunc(-7, 3), -2);
assert_eq!(trunc(6, 3), 2);
assert_eq!(trunc(1, 2), 0);
assert_eq!(trunc(-1, 2), 0);
}
#[test]
fn test_round_half_to_even() {
assert_eq!(round(7, 3, RoundingMode::HalfToEven), 2);
assert_eq!(round(8, 3, RoundingMode::HalfToEven), 3);
assert_eq!(round(5, 2, RoundingMode::HalfToEven), 2);
assert_eq!(round(7, 2, RoundingMode::HalfToEven), 4);
assert_eq!(round(9, 2, RoundingMode::HalfToEven), 4);
assert_eq!(round(11, 2, RoundingMode::HalfToEven), 6);
}
#[test]
fn test_round_half_away_from_zero() {
assert_eq!(round(7, 3, RoundingMode::HalfAwayFromZero), 2);
assert_eq!(round(8, 3, RoundingMode::HalfAwayFromZero), 3);
assert_eq!(round(5, 2, RoundingMode::HalfAwayFromZero), 3);
assert_eq!(round(-5, 2, RoundingMode::HalfAwayFromZero), -3);
}
#[test]
fn test_signum() {
assert_eq!(signum(-5), -1);
assert_eq!(signum(0), 0);
assert_eq!(signum(5), 1);
}
}