PuzzleUtils/src/number.rs

558 lines
16 KiB
Rust
Raw Normal View History

use std::cmp::{min, Ordering};
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::iter::zip;
use std::ops::{Add, Div, Mul, Rem, Sub};
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Sign {
Positif,
Negatif,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Number {
pub digits: Vec<isize>,
pub sign: Sign,
}
impl Number {
pub fn get_digit(n: isize, pos: usize) -> isize {
let modulo = 10isize.pow(pos as u32 + 1);
let divisor = modulo / 10;
(n % modulo) / divisor
}
pub const fn byte_to_digit(b: u8) -> isize {
// wrapping_sub('0' as u32) same as - 48 but less magical
(b as isize).wrapping_sub('0' as isize)
}
fn handle_overflows(&mut self) {
let new_digits = &mut self.digits;
let digits_len = new_digits.len();
let mut digits_idx = digits_len - 1;
loop {
let digit_or_num = new_digits[digits_idx];
let digit_len = if digit_or_num != 0 {
(digit_or_num.abs() as f64 + 1.0).log10().ceil() as usize
} else {
1
};
for i in 0..digit_len {
let new_digit = Self::get_digit(digit_or_num, i);
let (digit_idx, is_overflow) = digits_idx.overflowing_sub(i);
if is_overflow {
new_digits.insert(0, new_digit);
digits_idx += 1;
} else {
let digit = new_digits.get_mut(digit_idx).unwrap();
if i == 0 {
*digit = new_digit;
} else {
*digit += new_digit;
}
}
}
if digits_idx == 0 {
break;
}
digits_idx -= 1;
}
}
fn handle_underflows(&mut self) {
let new_digits = &mut self.digits;
let mut digits_len = new_digits.len();
for digit in new_digits.clone() {
match digit.cmp(&0) {
Ordering::Equal => {
if digits_len == 1 {
return;
}
digits_len -= 1;
new_digits.remove(0);
}
Ordering::Less => {
self.sign = Sign::Negatif;
break;
}
_ => break,
};
}
let mut digits_idx = digits_len - 1;
loop {
let digit = new_digits[digits_idx];
if self.sign == Sign::Positif && digit < 0 && digits_idx > 0 {
let mut_digit = new_digits.get_mut(digits_idx).unwrap();
*mut_digit = 10 - digit.abs();
let mut_digit = new_digits.get_mut(digits_idx - 1).unwrap();
*mut_digit -= 1;
} else {
let mut_digit = new_digits.get_mut(digits_idx).unwrap();
*mut_digit = digit.abs();
}
if digits_idx == 0 {
break;
}
digits_idx -= 1;
}
for digit in new_digits.clone() {
match digit.cmp(&0) {
Ordering::Equal => {
new_digits.remove(0);
}
_ => break,
};
}
}
pub fn pow(self, n: u32) -> Self {
let mut result = self.clone();
if ((self.digits.len() * 8) as u32) < isize::BITS {
let number = isize::try_from(self).unwrap();
for _i in 1..n {
result = result * number;
}
result
} else {
let number = self.clone();
for _i in 1..n {
result = result * number.clone();
}
result
}
}
pub fn fact(self) -> Self {
let mut fact = Number::from(1);
if ((self.digits.len() * 8) as u32) < isize::BITS {
let max = isize::try_from(self).unwrap();
for n in 1..=max {
fact = fact * n;
}
fact
} else {
panic!("starting number too big")
}
}
pub fn fib(n: u64) -> Self {
let mut last_two = (Self::from(1), Self::from(1));
let mut iteration = 1;
while iteration < n {
last_two = (last_two.1.clone(), last_two.0 + last_two.1);
iteration += 1;
}
last_two.0
}
pub fn div_with_rem(n1: Number, n2: Number) -> Result<(Number, Number), Box<dyn Error>> {
if n2 == 0.into() {
return Err(Box::from("Division by 0"));
}
let n1_len = n1.digits.len();
let n2_len = n2.digits.len();
if n2_len > n1_len {
return Ok((Number::from(0), n2));
}
let dividend = n1.digits[..n2_len].to_vec();
let mut quotient = vec![];
let mut remainder = Number {
digits: dividend.clone(),
sign: Sign::Positif,
};
let mut iteration = 1;
loop {
let mut factor = 0;
loop {
let temp_remainder = remainder.clone() - n2.clone();
if temp_remainder.sign == Sign::Negatif {
quotient.push(factor);
break;
}
remainder = temp_remainder;
factor += 1;
}
if n1_len == n2_len + iteration - 1 {
break;
}
remainder.digits.push(n1.digits[n2_len + iteration - 1]);
iteration += 1;
}
let mut res = Number {
digits: quotient,
sign: Sign::Positif,
};
res.handle_overflows();
for digit in res.clone().digits {
if digit != 0 || res.digits.len() == 1 {
break;
}
res.digits.remove(0);
}
for digit in remainder.clone().digits {
if digit != 0 || remainder.digits.len() == 1 {
break;
}
remainder.digits.remove(0);
}
Ok((res, remainder))
}
2024-11-18 14:30:58 +01:00
pub fn gcd(mut a: Number, mut b: Number) -> Number {
loop {
let t = b.clone();
b = a % b;
a = t;
if b == Number::from(0) {
return a;
}
}
}
}
impl TryFrom<Number> for isize {
type Error = Box<dyn Error>;
fn try_from(value: Number) -> Result<Self, Self::Error> {
let mut num = 0;
for (pos, &digit) in value.digits.iter().rev().enumerate() {
let mul = digit.checked_mul(10isize.pow(pos as u32));
if mul.is_none() {
return Err(Box::from("Cannot convert Number to isize. Too big."));
}
num += mul.unwrap();
}
Ok(num)
}
}
impl From<Number> for String {
fn from(value: Number) -> Self {
let string_vec: Vec<String> = value
.digits
.iter()
.map(|&digit| digit.to_string())
.collect();
string_vec.concat()
}
}
impl From<&str> for Number {
fn from(value: &str) -> Self {
let bytes = value.as_bytes();
let (sign, idx_start) = match bytes[0] {
b'-' => (Sign::Negatif, 1),
_ => (Sign::Positif, 0),
};
let mut digits = vec![];
for &byte in &bytes[idx_start..] {
let digit = Self::byte_to_digit(byte);
digits.push(digit);
}
Self { digits, sign }
}
}
impl From<isize> for Number {
2024-11-18 14:30:58 +01:00
fn from(mut value: isize) -> Self {
let mut sign = Sign::Positif;
if value < 0 {
sign = Sign::Negatif;
2024-11-18 14:30:58 +01:00
value *= -1;
}
if value == 0 {
return Self {
digits: vec![0],
sign: Sign::Positif,
};
}
let num_len = (value as f64 + 1.0).log10().ceil() as usize;
let mut digits = vec![];
for digit_idx in 0..num_len {
let digit = Self::get_digit(value, digit_idx);
digits.push(digit);
}
let digits = digits.iter().rev().copied().collect();
Self { digits, sign }
}
}
impl Display for Number {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let number_string = self
.digits
.iter()
.map(|&digit| digit.to_string())
.collect::<Vec<String>>()
.join("");
match self.sign {
Sign::Positif => write!(f, "{number_string}"),
Sign::Negatif => write!(f, "-{number_string}"),
}
}
}
impl Add for Number {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let self_len = self.digits.len();
let rhs_len = rhs.digits.len();
let mut self_digits = self.digits.clone();
let mut rhs_digits = rhs.digits.clone();
if self_len != rhs_len {
let difference = (self_len).abs_diff(rhs_len);
let pad = vec![0isize; difference];
if min(self_len, rhs_len) == self_len {
self_digits = [pad, self.digits].concat();
} else {
rhs_digits = [pad, rhs.digits].concat();
}
}
let zipped = zip(self_digits.iter(), rhs_digits.iter());
let added = zipped
.map(|(self_digit, rhs_digit)| self_digit + rhs_digit)
.collect();
let mut overflown_number = Self {
digits: added,
sign: Sign::Positif,
};
overflown_number.handle_overflows();
overflown_number
}
}
impl Sub for Number {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
let self_len = self.digits.len();
let rhs_len = rhs.digits.len();
let mut self_digits = self.digits.clone();
let mut rhs_digits = rhs.digits.clone();
if self_len != rhs_len {
let difference = (self_len).abs_diff(rhs_len);
let pad = vec![0isize; difference];
if min(self_len, rhs_len) == self_len {
self_digits = [pad, self.digits].concat();
} else {
rhs_digits = [pad, rhs.digits].concat();
}
}
let zipped = zip(self_digits.iter(), rhs_digits.iter());
let added = zipped
.map(|(self_digit, rhs_digit)| self_digit - rhs_digit)
.collect();
let mut underflown_number = Self {
digits: added,
sign: Sign::Positif,
};
underflown_number.handle_underflows();
underflown_number
}
}
impl Mul for Number {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let multiplied = self.digits.iter().rev().enumerate().map(|(pos, &digit)| {
let mut mult = digit * rhs.clone();
mult.digits = [mult.digits, vec![0; pos]].concat();
mult
});
let mut overflown_number = multiplied.reduce(|acc, num| acc + num).unwrap();
overflown_number.handle_overflows();
overflown_number
}
}
impl Mul<Number> for isize {
type Output = Number;
fn mul(self, rhs: Number) -> Self::Output {
let multiplied = rhs.digits.iter().map(|digit| digit * self).collect();
let mut overflown_number = Number {
digits: multiplied,
sign: Sign::Positif,
};
overflown_number.handle_overflows();
overflown_number
}
}
impl Mul<isize> for Number {
type Output = Self;
fn mul(self, rhs: isize) -> Self::Output {
let multiplied = self.digits.iter().map(|digit| digit * rhs).collect();
let mut overflown_number = Self {
digits: multiplied,
sign: Sign::Positif,
};
overflown_number.handle_overflows();
overflown_number
}
}
impl Div for Number {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self::div_with_rem(self, rhs).unwrap().0
}
}
impl Rem for Number {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
Self::div_with_rem(self, rhs).unwrap().1
}
}
impl PartialOrd for Number {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Number {
fn cmp(&self, other: &Self) -> Ordering {
if self.sign == Sign::Negatif && other.sign == Sign::Positif {
return Ordering::Less;
} else if self.sign == Sign::Positif && other.sign == Sign::Negatif {
return Ordering::Greater;
}
2024-11-22 14:17:04 +01:00
match self.digits.len().cmp(&other.digits.len()) {
Ordering::Less => match self.sign {
Sign::Positif => Ordering::Less,
Sign::Negatif => Ordering::Greater,
},
Ordering::Greater => match self.sign {
Sign::Positif => Ordering::Greater,
Sign::Negatif => Ordering::Less,
},
Ordering::Equal => {
for pair in zip(&self.digits, &other.digits) {
return match pair.0.cmp(pair.1) {
Ordering::Less => match self.sign {
Sign::Positif => Ordering::Less,
Sign::Negatif => Ordering::Greater,
},
Ordering::Greater => match self.sign {
Sign::Positif => Ordering::Greater,
Sign::Negatif => Ordering::Less,
},
Ordering::Equal => continue,
};
}
2024-11-22 14:17:04 +01:00
Ordering::Equal
}
}
}
}
#[cfg(test)]
mod number_tests {
use crate::number::{Number, Sign};
#[test]
fn test_from_isize() {
let number = Number::from(-1234);
assert_eq!(
number,
Number {
2024-11-18 14:30:58 +01:00
digits: vec![1, 2, 3, 4],
sign: Sign::Negatif
}
);
}
#[test]
fn test_from_0isize() {
let number = Number::from(0);
assert_eq!(
number,
Number {
digits: vec![0],
sign: Sign::Positif
}
);
}
#[test]
fn test_get_digit() {
let num = 12345;
let digit_1 = Number::get_digit(num, 0);
let digit_2 = Number::get_digit(num, 1);
let digit_3 = Number::get_digit(num, 2);
let digit_4 = Number::get_digit(num, 3);
let digit_5 = Number::get_digit(num, 4);
assert_eq!(digit_1, 5);
assert_eq!(digit_2, 4);
assert_eq!(digit_3, 3);
assert_eq!(digit_4, 2);
assert_eq!(digit_5, 1);
}
#[test]
fn test_cmp_eq_pos() {
let a = Number::from(1);
let b = Number::from(1);
assert_eq!(a, b);
}
#[test]
fn test_cmp_eq_neg() {
let a = Number::from(-1);
let b = Number::from(-1);
assert_eq!(a, b);
}
#[test]
fn test_cmp_pos_neg() {
let a = Number::from(1);
let b = Number::from(-1);
assert!(a > b);
}
#[test]
fn test_cmp_neg_pos() {
let a = Number::from(-1);
let b = Number::from(1);
assert!(a < b);
}
#[test]
fn test_cmp_short_neg() {
let a = Number::from(-1);
let b = Number::from(-10);
assert!(a > b);
}
#[test]
fn test_cmp_short_pos() {
let a = Number::from(1);
let b = Number::from(10);
assert!(a < b);
}
#[test]
fn test_cmp_long_neg() {
let a = Number::from(-10);
let b = Number::from(-1);
assert!(a < b);
}
#[test]
fn test_cmp_long_pos() {
let a = Number::from(10);
let b = Number::from(1);
assert!(a > b);
}
}