use crate::{digit::*, Index, BigFixed, BigFixedError, macros::*};
use std::{
ops::{
Add, AddAssign,
BitAnd, BitAndAssign,
BitOr, BitOrAssign,
BitXor, BitXorAssign,
Mul, MulAssign,
Neg, Not,
Shl, ShlAssign,
Shr, ShrAssign,
Sub, SubAssign
},
cmp::{max, min, Ordering}
};
impl BigFixed {
pub fn add_digit(&mut self, d: Digit, position: Index) -> Result<(), BigFixedError> {
assert!(self.properly_positioned());
if let Index::Bit(_) = position {
let diff = position.bit_position_excess();
if diff == 0 {
return self.add_digit(d, position.cast_to_position());
}
let as_position = position.cast_to_position();
self.add_digit(d >> (DIGITBITS as isize - diff), (as_position + Index::Position(1))?)?;
self.add_digit(d << diff, as_position)?;
return Ok(())
}
self.ensure_valid_position(position)?;
let mut res;
let mut carry;
add!(self[position], d, res, carry);
self[position] = res;
let high = self.body_high()?;
let mut on_position = (position + 1isize)?;
while carry == 1 && on_position < high {
add!(self[on_position], 1, res, carry);
self[on_position] = res;
on_position += 1isize;
}
if carry == 1 {
if self.is_neg() {
self.head = 0;
} else {
self[high] = 1;
}
};
Ok(())
}
pub fn add_digit_drop_overflow(&mut self, d: Digit, position: Index) -> Result<(), BigFixedError> {
assert!(self.properly_positioned());
if position >= self.body_high()? {
return Ok(());
}
if let Index::Bit(_b) = position {
if let Index::Bit(_) = position {
let diff = position.bit_position_excess();
let as_position = position.cast_to_position();
self.add_digit_drop_overflow(d >> (DIGITBITS as isize - diff), (as_position + Index::Position(1))?)?;
self.add_digit_drop_overflow(d << diff, as_position)?;
return Ok(())
}
}
self.ensure_valid_position(position)?;
let mut res;
let mut carry;
add!(self[position], d, res, carry);
self[position] = res;
let high = self.body_high()?;
let mut on_position = (position + 1isize)?;
while carry == 1 && on_position < high {
add!(self[on_position], 1, res, carry);
self[on_position] = res;
on_position += 1isize;
}
Ok(())
}
pub fn negate(&mut self) -> Result<(), BigFixedError> {
self.head = !self.head;
for i in 0..self.body.len() {
self.body[i] = !self.body[i];
}
self.add_digit(1, self.position)?;
self.format()?;
Ok(())
}
pub fn abs(&self) -> Result<BigFixed, BigFixedError> {
if self.is_neg() {
let mut copy = self.clone();
copy.negate()?;
Ok(copy)
} else {
Ok(self.clone())
}
}
pub fn add_assign(&mut self, other: &BigFixed) -> Result<(), BigFixedError> {
self.fix_position()?;
let position = min(self.position, other.position);
let high = (max(self.body_high()?, other.body_high()?) + Index::Position(1))?;
self.ensure_valid_range(position, high)?;
let other_low = other.position.cast_to_position();
for i in other_low.value()..high.value() {
let p = Index::Position(i);
self.add_digit_drop_overflow(other[p], p)?;
}
self.head = if self[(high - Index::Position(1))?] >= GREATESTBIT {
ALLONES
} else {
0
};
self.format()
}
pub fn bitand_assign(&mut self, other: &BigFixed) -> Result<(), BigFixedError> {
self.fix_position()?;
let position = min(self.position, other.position);
let high = max(self.body_high()?, other.body_high()?);
self.ensure_valid_range(position, high)?;
for i in 0..self.body.len() {
self.body[i] &= other[(self.position + i)?];
}
self.head &= other.head;
self.format()
}
pub fn bitor_assign(&mut self, other: &BigFixed) -> Result<(), BigFixedError> {
self.fix_position()?;
let position = min(self.position, other.position);
let high = max(self.body_high()?, other.body_high()?);
self.ensure_valid_range(position, high)?;
for i in 0..self.body.len() {
self.body[i] |= other[(self.position + i)?];
}
self.head |= other.head;
self.format()
}
pub fn bitxor_assign(&mut self, other: &BigFixed) -> Result<(), BigFixedError> {
let position = min(self.position, other.position);
let high = max(self.body_high()?, other.body_high()?);
self.ensure_valid_range(position, high)?;
for i in 0..self.body.len() {
self.body[i] ^= other[(self.position + i)?];
}
self.head ^= other.head;
self.format()
}
pub fn mul_assign(&mut self, other: &BigFixed) -> Result<(), BigFixedError> {
self.fix_position()?;
assert!(other.properly_positioned());
if self.is_zero() {
return Ok(());
}
if other.is_zero() {
self.overwrite(other);
return Ok(());
}
let low = (self.position + other.position)?;
self.position = Index::Position(0);
let mut self_len = self.body.len();
if self.is_neg() {
self_len += 1;
}
let mut other_len = other.body.len();
if other.is_neg() {
other_len += 1;
}
let len = 2*max(self_len, other_len);
self.body.resize(len, self.head);
let mut other_base;
let mut prod_res;
let mut prod_carry;
let mut sum_res;
let mut sum_carry;
let mut total_carry;
let mut summ_res;
let mut summ_carry;
let mut totall_carry;
for i in (0..len).rev() {
sum_res = 0;
total_carry = 0;
summ_res = 0;
totall_carry = 0;
for j in 0..=i {
other_base = (other.position + i)?;
mul!(self.body[j], other[(other_base - j)?], prod_res, prod_carry);
add!(sum_res, prod_res, sum_res, sum_carry);
total_carry += sum_carry;
add!(summ_res, prod_carry, summ_res, summ_carry);
totall_carry += summ_carry;
}
self.body[i] = 0;
self.add_digit_drop_overflow(sum_res, Index::Position(Index::castsize(i)?))?;
self.add_digit_drop_overflow(total_carry, Index::Position(Index::castsize(i + 1)?))?;
self.add_digit_drop_overflow(summ_res, Index::Position(Index::castsize(i + 1)?))?;
self.add_digit_drop_overflow(totall_carry, Index::Position(Index::castsize(i + 2)?))?;
}
self.head = self.head ^ other.head;
self.position = low;
self.format()
}
pub fn shl_assign(&mut self, amount: &usize) -> Result<(), BigFixedError> {
self.position = (self.position + Index::Bit(Index::castsize(*amount)?))?;
self.fix_position()?;
Ok(())
}
pub fn shr_assign(&mut self, amount: &usize) -> Result<(), BigFixedError> {
self.position = (self.position - Index::Bit(Index::castsize(*amount)?))?;
self.fix_position()?;
Ok(())
}
pub fn sub_assign(&mut self, other: &BigFixed) -> Result<(), BigFixedError> {
self.negate()?;
self.add_assign(other)?;
self.negate()?;
Ok(())
}
}
op_assign_to_op!(Add, add, AddAssign, add_assign, BigFixed, BigFixed, BigFixed, BigFixedError);
op_assign_to_op!(BitAnd, bitand, BitAndAssign, bitand_assign, BigFixed, BigFixed, BigFixed, BigFixedError);
op_assign_to_op!(BitOr, bitor, BitOrAssign, bitor_assign, BigFixed, BigFixed, BigFixed, BigFixedError);
op_assign_to_op!(BitXor, bitxor, BitXorAssign, bitxor_assign, BigFixed, BigFixed, BigFixed, BigFixedError);
op_assign_to_op!(Mul, mul, MulAssign, mul_assign, BigFixed, BigFixed, BigFixed, BigFixedError);
op_assign_to_op!(Shl, shl, ShlAssign, shl_assign, BigFixed, usize, BigFixed, BigFixedError);
op_assign_to_op!(Shr, shr, ShrAssign, shr_assign, BigFixed, usize, BigFixed, BigFixedError);
op_assign_to_op!(Sub, sub, SubAssign, sub_assign, BigFixed, BigFixed, BigFixed, BigFixedError);
unary!(Neg, neg, BigFixed, negate, BigFixed, BigFixedError);
unary!(Not, not, BigFixed, negate, BigFixed, BigFixedError);
impl BigFixed {
pub fn combined_div(num: &mut BigFixed, denom: &BigFixed, to: usize) -> Result<BigFixed, BigFixedError> {
let mut quotient = BigFixed::from(0);
if &*num < "ient {
num.negate()?;
quotient = BigFixed::combined_div(num, denom, to)?;
num.negate()?;
quotient.negate()?;
return Ok(quotient);
}
if denom < "ient {
quotient = BigFixed::combined_div(num, &(-denom)?, to)?;
num.negate()?;
quotient.negate()?;
return Ok(quotient);
}
assert!(!denom.is_zero(), "divide by zero");
assert!(&*num >= "ient && denom >= "ient, "sign issue");
let mut cutoff = BigFixed::from(1).shift((-Index::Position(Index::castsize(to)?))?)?;
cutoff *= denom;
let denom_tail_len = denom.body.len() - 1;
let denom_high_position = (denom.body_high()? - 1isize)?;
let mut shifted_denom = denom.clone();
let mut position = (num.body_high()? - 1isize)?;
while !num.is_zero() && &*num >= &cutoff {
shifted_denom.position = (position - denom_tail_len)?;
let mut quot;
div!(num[(position + 1isize)?], num[position], shifted_denom[position], quot);
let mut prod = BigFixed::from(quot);
prod *= &shifted_denom;
while &prod < num && quot < ALLONES {
quot += 1;
prod += &shifted_denom;
}
while &prod > num && quot > 0 {
quot -= 1;
prod -= &shifted_denom;
}
quotient[(position - denom_high_position)?] = quot;
*num -= ∏
position -= 1isize;
}
quotient.format()?;
Ok(quotient)
}
pub fn to_digits(&self, base: &BigFixed) -> Result<(Vec<BigFixed>, isize), BigFixedError> {
let mut shifting = self.abs()?.clone();
let mut neg_count: isize = 0;
while shifting.position < 0isize {
shifting *= base;
neg_count += 1;
}
let mut digits = vec![];
while !shifting.is_zero() {
let quot = BigFixed::combined_div(&mut shifting, base, 0)?;
digits.push(shifting.clone());
shifting = quot;
}
Ok((digits, neg_count))
}
pub fn to_digits_10(&self) -> Result<(Vec<i32>, isize), BigFixedError> {
let (big_digits, point) = self.to_digits(&BigFixed::from(10))?;
let digits: Vec<i32> = big_digits.iter().map(|x| i32::from(x)).collect();
Ok((digits, point))
}
}
impl PartialOrd for BigFixed {
fn partial_cmp(&self, other: &BigFixed) -> Option<Ordering> {
let mut step_result = self.head.cmp(&other.head);
match step_result {
Ordering::Equal => {
for i in (
min(self.position.cast_to_position(), other.position.cast_to_position()).value()..
(max(self.body_high().ok()?.cast_to_position(), other.body_high().ok()?.cast_to_position())).value()
).rev() {
step_result = self[i].cmp(&other[i]);
match step_result {
Ordering::Equal => continue,
x => return Some(x)
}
}
Some(Ordering::Equal)
},
Ordering::Less => Some(Ordering::Greater),
Ordering::Greater => Some(Ordering::Less)
}
}
}