use std::convert::TryFrom;
use std::error::Error as StdError;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FmtResult;
#[cfg(not(feature = "num-v02"))]
use std::num::FpCategory;
use std::ops::Add;
use std::ops::AddAssign;
use std::ops::Div;
use std::ops::DivAssign;
use std::ops::Mul;
use std::ops::MulAssign;
use std::ops::Neg;
use std::ops::Rem;
use std::ops::RemAssign;
use std::ops::Sub;
use std::ops::SubAssign;
use std::str::FromStr;
use num_traits::cast::ToPrimitive;
use num_traits::identities::Zero;
use num_traits::pow::Pow;
use num_traits::sign::Signed;
use crate::num_bigint::BigInt;
use crate::num_bigint::ParseBigIntError;
use crate::num_bigint::Sign;
use crate::num_rational::BigRational;
use crate::num_rational::Rational32;
use crate::num_rational::Rational64;
const MAX_PRECISION: usize = 8;
fn round_to_even(val: &BigRational) -> BigRational {
let zero = BigInt::from(0);
let one = BigInt::from(1);
let two = BigInt::from(2);
let zero_ = BigRational::new(zero, one.clone());
let half = BigRational::new(one.clone(), two.clone());
let mut fract = val.fract();
if fract < zero_ {
fract = &zero_ - fract
};
let trunc = val.trunc();
if fract == half {
if &trunc % two == zero_ {
trunc
} else if trunc >= zero_ {
trunc + one
} else {
trunc - one
}
} else {
val.round()
}
}
fn format_impl(
value: &BigRational,
mut result: String,
depth: usize,
min_precision: usize,
precision: Option<usize>,
) -> String {
debug_assert!(min_precision <= precision.unwrap_or(MAX_PRECISION));
let trunc = value.trunc().to_integer();
result += &trunc.to_string();
let numer = value.numer();
let denom = value.denom();
let value = numer - (trunc * denom);
let at_min = depth >= min_precision;
let at_max = depth >= precision.unwrap_or(MAX_PRECISION);
if (value.is_zero() && precision.is_none() && at_min) || at_max {
result
} else {
if depth == 0 {
result += ".";
}
let value = BigRational::new(value * 10, denom.clone());
format_impl(&value, result, depth + 1, min_precision, precision)
}
}
#[derive(Debug, PartialEq)]
pub enum ParseNumError {
InvalidStrError(String),
ParseIntError(ParseBigIntError),
}
impl From<ParseBigIntError> for ParseNumError {
#[inline]
fn from(e: ParseBigIntError) -> Self {
Self::ParseIntError(e)
}
}
impl Display for ParseNumError {
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
match self {
Self::InvalidStrError(s) => write!(fmt, "{}", s),
Self::ParseIntError(err) => write!(fmt, "{}", err),
}
}
}
impl StdError for ParseNumError {
#[inline]
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Self::InvalidStrError(..) => None,
Self::ParseIntError(err) => err.source(),
}
}
}
#[derive(Debug)]
pub struct CustomDisplay<'n> {
num: &'n Num,
min_precision: Option<usize>,
}
impl<'n> CustomDisplay<'n> {
#[inline]
fn new(num: &'n Num) -> Self {
Self {
num,
min_precision: None,
}
}
#[inline]
pub fn min_precision(&mut self, min_precision: usize) -> &mut Self {
self.min_precision = Some(min_precision);
self
}
}
impl<'n> Display for CustomDisplay<'n> {
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
self.num.format(fmt, self.min_precision.unwrap_or(0))
}
}
macro_rules! impl_num {
($(#[$meta:meta])* pub struct $name:ident($rational:ty), $int:ty) => {
$(#[$meta])*
pub struct $name(pub(crate) $rational);
impl $name {
#[inline]
pub fn new<T, U>(numer: T, denom: U) -> Self
where
$int: From<T>,
$int: From<U>,
{
let numer = <$int>::from(numer);
let denom = <$int>::from(denom);
Self(<$rational>::new(numer, denom))
}
}
impl Default for $name {
#[inline]
fn default() -> Self {
<$name>::from(0)
}
}
impl From<$name> for ($int, $int) {
#[inline]
fn from(other: $name) -> Self {
other.0.into()
}
}
impl Debug for $name {
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
debug_assert!(self.0.denom().is_positive());
write!(fmt, "{}/{}", self.0.numer(), self.0.denom())
}
}
};
}
impl_num! {
#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Num(BigRational), BigInt
}
impl Num {
#[inline]
pub fn round(&self) -> Self {
self.round_with(0)
}
pub fn round_with(&self, precision: usize) -> Self {
let factor = BigInt::from(10).pow(precision);
let value = &self.0 * &factor;
Num(round_to_even(&value).trunc() / factor)
}
#[inline]
pub fn trunc(&self) -> Self {
Num(self.0.trunc())
}
#[inline]
pub fn fract(&self) -> Self {
Num(self.0.fract())
}
#[inline]
pub fn to_integer(&self) -> BigInt {
self.0.to_integer()
}
#[inline]
pub fn to_i64(&self) -> Option<i64> {
self.to_integer().to_i64()
}
#[inline]
pub fn to_u64(&self) -> Option<u64> {
self.to_integer().to_u64()
}
pub fn to_f64(&self) -> Option<f64> {
let numer = self.0.numer().to_f64();
let denom = self.0.denom().to_f64();
match (numer, denom) {
#[cfg(feature = "num-v02")]
(Some(numer), Some(denom)) => Some(numer / denom),
#[cfg(not(feature = "num-v02"))]
(Some(numer), Some(denom)) => {
if !matches!(numer.classify(), FpCategory::Normal | FpCategory::Zero) {
return None
}
if !matches!(denom.classify(), FpCategory::Normal | FpCategory::Zero) {
return None
}
Some(numer / denom)
},
_ => None,
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.0.is_zero()
}
#[inline]
pub fn is_positive(&self) -> bool {
self.0.is_positive()
}
#[inline]
pub fn is_negative(&self) -> bool {
self.0.is_negative()
}
fn format(&self, fmt: &mut Formatter<'_>, min_precision: usize) -> FmtResult {
let non_negative = !self.0.is_negative();
let prefix = "";
let precision = fmt.precision();
let value = self.round_with(precision.unwrap_or(MAX_PRECISION)).0.abs();
let string = format_impl(&value, String::new(), 0, min_precision, precision);
fmt.pad_integral(non_negative, prefix, &string)
}
#[inline]
pub fn display(&self) -> CustomDisplay<'_> {
CustomDisplay::new(self)
}
}
impl Display for Num {
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
let min_precision = 0;
self.format(fmt, min_precision)
}
}
impl From<Num32> for Num {
fn from(other: Num32) -> Num {
let (numer, denom) = other.into();
Num::new(numer, denom)
}
}
impl From<Num64> for Num {
fn from(other: Num64) -> Num {
let (numer, denom) = other.into();
Num::new(numer, denom)
}
}
impl FromStr for Num {
type Err = ParseNumError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
fn parse_istr(s: &str) -> Result<(Sign, BigInt), ParseBigIntError> {
let val = BigInt::from_str(s)?;
let sign = val.sign();
let sign = if sign == Sign::NoSign {
if s.starts_with('-') {
Sign::Minus
} else {
Sign::Plus
}
} else {
sign
};
Ok((sign, val))
}
fn parse_str(s: &str, sign: Sign) -> Result<BigInt, ParseNumError> {
if s.starts_with('-') || s.starts_with('+') {
return Err(ParseNumError::InvalidStrError(s.to_owned()));
}
let num = BigInt::parse_bytes(s.as_bytes(), 10)
.ok_or_else(|| ParseNumError::InvalidStrError(s.to_owned()))?;
let (_, bytes) = num.to_bytes_le();
let num = BigInt::from_bytes_le(sign, &bytes);
Ok(num)
}
let mut splits = s.splitn(2, '.');
let numer = splits
.next()
.ok_or_else(|| ParseNumError::InvalidStrError(s.to_owned()))?;
let (sign, numer) = parse_istr(numer)?;
if let Some(s) = splits.next() {
let denom = parse_str(s, sign)?;
let power = BigInt::from(10).pow(s.len());
let numer = numer * &power + denom;
Ok(Num(BigRational::new(numer, power)))
} else {
Ok(Num(BigRational::from_integer(numer)))
}
}
}
macro_rules! impl_from {
($type:ty) => {
impl From<$type> for Num {
#[inline]
fn from(val: $type) -> Self {
Self(BigRational::from_integer(BigInt::from(val)))
}
}
};
}
impl_from!(i128);
impl_from!(i16);
impl_from!(i32);
impl_from!(i64);
impl_from!(i8);
impl_from!(isize);
impl_from!(u128);
impl_from!(u16);
impl_from!(u32);
impl_from!(u64);
impl_from!(u8);
impl_from!(usize);
impl_from!(BigInt);
macro_rules! impl_neg {
($lhs:ty) => {
impl Neg for $lhs {
type Output = Num;
#[inline]
fn neg(self) -> Self::Output {
let Num(int) = self;
Num(int.neg())
}
}
};
}
impl_neg!(Num);
impl_neg!(&Num);
macro_rules! impl_op {
(impl $imp:ident, $method:ident, $lhs:ty, $rhs:ty) => {
impl $imp<$rhs> for $lhs {
type Output = Num;
#[inline]
fn $method(self, rhs: $rhs) -> Self::Output {
let Num(lhs) = self;
let Num(rhs) = rhs;
Num(lhs.$method(rhs))
}
}
};
}
macro_rules! impl_int_op {
(impl $imp:ident, $method:ident, $lhs:ty) => {
impl<T> $imp<T> for $lhs
where
BigInt: From<T>,
{
type Output = Num;
#[inline]
fn $method(self, rhs: T) -> Self::Output {
let Num(lhs) = self;
let rhs = BigRational::from_integer(BigInt::from(rhs));
Num(lhs.$method(rhs))
}
}
};
}
macro_rules! impl_ops {
(impl $imp:ident, $method:ident) => {
impl_op!(impl $imp, $method, Num, Num);
impl_op!(impl $imp, $method, &Num, Num);
impl_op!(impl $imp, $method, Num, &Num);
impl_op!(impl $imp, $method, &Num, &Num);
impl_int_op!(impl $imp, $method, Num);
impl_int_op!(impl $imp, $method, &Num);
};
}
impl_ops!(impl Add, add);
impl_ops!(impl Sub, sub);
impl_ops!(impl Mul, mul);
impl_ops!(impl Div, div);
impl_ops!(impl Rem, rem);
macro_rules! impl_assign_op {
(impl $imp:ident, $method:ident, $lhs:ty, $rhs:ty) => {
impl $imp<$rhs> for $lhs {
#[inline]
fn $method(&mut self, rhs: $rhs) {
let Num(rhs) = rhs;
(self.0).$method(rhs)
}
}
};
}
macro_rules! impl_assign_ops {
(impl $imp:ident, $method:ident) => {
impl<T> $imp<T> for Num
where
BigInt: From<T>,
{
#[inline]
fn $method(&mut self, rhs: T) {
let rhs = BigRational::from_integer(BigInt::from(rhs));
(self.0).$method(rhs)
}
}
impl_assign_op!(impl $imp, $method, Num, Num);
impl_assign_op!(impl $imp, $method, Num, &Num);
};
}
impl_assign_ops!(impl AddAssign, add_assign);
impl_assign_ops!(impl SubAssign, sub_assign);
impl_assign_ops!(impl MulAssign, mul_assign);
impl_assign_ops!(impl DivAssign, div_assign);
impl_assign_ops!(impl RemAssign, rem_assign);
macro_rules! impl_try_from {
($lhs:ty, $rhs:ty, $to_int:ident) => {
impl TryFrom<$rhs> for $lhs {
type Error = ();
fn try_from(other: $rhs) -> Result<Self, Self::Error> {
let numer = other.0.numer().$to_int().ok_or(())?;
let denom = other.0.denom().$to_int().ok_or(())?;
Ok(Self::new(numer, denom))
}
}
};
}
impl_num! {
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Num32(Rational32), i32
}
impl Num32 {
pub fn approximate(num: Num) -> Self {
if let Ok(num32) = Num32::try_from(&num) {
return num32
}
let integer = num.to_integer();
if integer >= BigInt::from(i32::MAX) {
Num32::from(i32::MAX)
} else if integer <= BigInt::from(i32::MIN) {
Num32::from(i32::MIN)
} else {
match Self::continued_fractions(num, integer) {
Ok(num) | Err(num) => num,
}
}
}
fn continued_fractions(num: Num, integer: BigInt) -> Result<Num32, Num32> {
let mut q = num.0;
let mut a = integer;
let mut n0 = 0i32;
let mut d0 = 1i32;
let mut n1 = 1i32;
let mut d1 = 0i32;
loop {
if q.is_integer() {
break Ok(Num32::new(n1, d1))
}
let a32 = a.to_i32();
let n = a32
.and_then(|n| n.checked_mul(n1))
.and_then(|n| n.checked_add(n0))
.ok_or_else(|| Num32::new(n1, d1))?;
let d = a32
.and_then(|n| n.checked_mul(d1))
.and_then(|n| n.checked_add(d0))
.ok_or_else(|| Num32::new(n1, d1))?;
n0 = n1;
d0 = d1;
n1 = n;
d1 = d;
q = (q - a).recip();
a = q.to_integer();
}
}
}
impl<T> From<T> for Num32
where
i32: From<T>,
{
#[inline]
fn from(val: T) -> Self {
Self(Rational32::from(i32::from(val)))
}
}
impl_try_from!(Num32, Num, to_i32);
impl_try_from!(Num32, &Num, to_i32);
impl_num! {
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Num64(Rational64), i64
}
impl<T> From<T> for Num64
where
i64: From<T>,
{
#[inline]
fn from(val: T) -> Self {
Self(Rational64::from(i64::from(val)))
}
}
impl_try_from!(Num64, Num, to_i64);
impl_try_from!(Num64, &Num, to_i64);