added num module, implemented basic biguint functionality

This commit is contained in:
Egor 2024-06-11 11:55:38 +03:00
parent b53ec32c7d
commit 067ad2217f
3 changed files with 211 additions and 0 deletions

View file

@ -1,2 +1,3 @@
pub mod expr; pub mod expr;
pub mod matrix; pub mod matrix;
pub mod num;

1
src/num.rs Normal file
View file

@ -0,0 +1 @@
pub mod bigint;

209
src/num/bigint.rs Normal file
View file

@ -0,0 +1,209 @@
use std::{mem, ops::{Add, AddAssign}};
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct BigUInt {
digits: Vec<u8>
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct BigInt {
sign: bool,
num: BigUInt
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct ParseError;
impl BigUInt {
pub fn new() -> Self {
Self { digits: vec![0] }
}
pub fn parse(str: impl Into<String>) -> Result<Self, ParseError> {
let str = str.into();
let str = str.trim();
if str.is_empty() {
return Ok(Self::new())
}
let mut digits = Vec::with_capacity(str.len());
for b in str.bytes().rev() {
if !b.is_ascii_digit() {
return Err(ParseError)
}
digits.push(b - b'0');
}
Ok(Self { digits })
}
pub fn as_str(&self) -> String {
self.clone().into()
}
}
impl From<u128> for BigUInt {
fn from(mut value: u128) -> Self {
if value == 0 {
return Self::new()
}
let mut digits = Vec::with_capacity(39);
while value > 0 {
digits.push((value % 10) as u8);
value /= 10;
}
Self { digits }
}
}
impl From<u64> for BigUInt {
fn from(value: u64) -> Self {
Self::from(value as u128)
}
}
impl From<u32> for BigUInt {
fn from(value: u32) -> Self {
Self::from(value as u128)
}
}
impl From<u16> for BigUInt {
fn from(value: u16) -> Self {
Self::from(value as u128)
}
}
impl From<u8> for BigUInt {
fn from(value: u8) -> Self {
Self::from(value as u128)
}
}
impl TryFrom<String> for BigUInt {
type Error = ParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
BigUInt::parse(value)
}
}
impl Into<String> for BigUInt {
fn into(self) -> String {
self.digits.into_iter().rev().map(|b|(b + b'0') as char).collect()
}
}
impl Default for BigUInt {
fn default() -> Self {
Self::new()
}
}
impl Add for BigUInt {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let (small, mut big) = if self.digits.len() < rhs.digits.len() {
(self, rhs)
} else {
(rhs, self)
};
big.digits.resize(big.digits.len() + 1, 0);
for i in 0..small.digits.len() {
let new_digit = small.digits[i] + big.digits[i];
big.digits[i] = new_digit % 10;
big.digits[i + 1] += new_digit / 10;
}
for i in small.digits.len()..big.digits.len() {
if big.digits[i] < 10 {
break;
}
big.digits[i] -= 10;
big.digits[i + 1] += 1;
}
if big.digits.len() > 1 && big.digits[big.digits.len() - 1] == 0 {
big.digits.pop();
}
big
}
}
impl AddAssign for BigUInt {
fn add_assign(&mut self, rhs: Self) {
*self = mem::take(self) + rhs;
}
}
#[cfg(test)]
mod tests {
use super::*;
fn bigint(s: impl Into<String>) -> BigUInt {
BigUInt::parse(s).unwrap()
}
#[test]
fn from_num() {
assert_eq!(BigUInt::new(), BigUInt { digits: vec![0] });
assert_eq!(BigUInt::from(0u8), BigUInt { digits: vec![0] });
assert_eq!(BigUInt::from(1234u16), BigUInt { digits: vec![4,3,2,1] });
assert_eq!(BigUInt::from(12345678u32), BigUInt { digits: vec![8,7,6,5,4,3,2,1] });
assert_eq!(
BigUInt::from(1234567890123456u64),
BigUInt { digits: vec![6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1] }
);
assert_eq!(
BigUInt::from(12345678901234561234567890123456u128),
BigUInt { digits: vec![6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1,6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1] }
);
}
#[test]
fn parse_str() {
assert_eq!(BigUInt::parse(""), Ok(BigUInt { digits: vec![0] }));
assert_eq!(BigUInt::parse(" "), Ok(BigUInt { digits: vec![0] }));
assert_eq!(BigUInt::parse("0"), Ok(BigUInt { digits: vec![0] }));
assert_eq!(BigUInt::parse("1234"), Ok(BigUInt { digits: vec![4,3,2,1] }));
assert_eq!(BigUInt::parse(" 1234 "), Ok(BigUInt { digits: vec![4,3,2,1] }));
assert_eq!(
BigUInt::parse("1234567890123456789012345678901234567890"),
Ok(BigUInt { digits: vec![0,9,8,7,6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1] })
);
assert_eq!(BigUInt::parse("wawa"), Err(ParseError))
}
#[test]
fn to_str() {
assert_eq!(BigUInt { digits: vec![0] }.as_str(), "0");
assert_eq!(BigUInt { digits: vec![4,3,2,1] }.as_str(), "1234");
assert_eq!(
BigUInt { digits: vec![0,9,8,7,6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1,0,9,8,7,6,5,4,3,2,1] }.as_str(),
"1234567890123456789012345678901234567890");
}
#[test]
fn add() {
assert_eq!(bigint("0") + bigint("0"), bigint("0"));
assert_eq!(bigint("1234") + bigint("0"), bigint("1234"));
assert_eq!(bigint("0") + bigint("1234"), bigint("1234"));
assert_eq!(bigint("1234") + bigint("1234"), bigint("2468"));
assert_eq!(bigint("123456") + bigint("123"), bigint("123579"));
assert_eq!(bigint("123") + bigint("123456"), bigint("123579"));
assert_eq!(bigint("9999999") + bigint("1"), bigint("10000000"));
assert_eq!(bigint("1") + bigint("9999999"), bigint("10000000"));
assert_eq!(bigint("9999801") + bigint("999999"), bigint("10999800"));
assert_eq!(bigint("999") + bigint("999"), bigint("1998"));
}
#[test]
fn add_assign() {
let mut n = bigint("1732");
n += bigint("1234");
assert_eq!(n, bigint("2966"));
n += bigint("746283");
assert_eq!(n, bigint("749249"));
n += bigint("9999");
assert_eq!(n, bigint("759248"));
n += bigint("0");
assert_eq!(n, bigint("759248"));
}
}