extern crate maths_traits;
extern crate num_traits;
extern crate rug;
#[path = "macros.rs"]
#[macro_use]
mod macros;
use crate::integer;
use integer::Integer;
use maths_traits::{
algebra,
algebra::{additive::*, multiplicative::*, EuclideanDiv},
analysis,
};
use num_traits::{ops::checked::*, FromPrimitive, ToPrimitive};
use std::{
fmt::{Display, Formatter, Result as FmtResult},
mem, ops,
ops::{Rem, RemAssign},
};
const FLOAT_DEFAULT_PREC: u32 = 53;
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Float {
pub val: rug::Float,
}
impl Float {
pub fn with_val<T>(prec: u32, val: T) -> Self
where
rug::Float: rug::Assign<T>,
{
Self {
val: rug::Float::with_val(prec, val),
}
}
pub fn to_integer(&self) -> Option<Integer> {
Some(Integer {
val: self.val.to_integer()?,
})
}
}
impl Display for Float {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
self.val.fmt(f)
}
}
impl From<rug::Float> for Float {
fn from(src: rug::Float) -> Self {
Self { val: src }
}
}
macro_rules! float_impl_from {
($T:ident) => {
impl From<$T> for Float {
fn from(src: $T) -> Self {
Self {
val: rug::Float::with_val((mem::size_of::<$T>() as u32) * 8, src),
}
}
}
};
}
float_impl_from! {isize}
float_impl_from! {i8}
float_impl_from! {i16}
float_impl_from! {i32}
float_impl_from! {i64}
float_impl_from! {i128}
float_impl_from! {usize}
float_impl_from! {u8}
float_impl_from! {u16}
float_impl_from! {u32}
float_impl_from! {u64}
float_impl_from! {u128}
float_impl_from! {f32}
float_impl_from! {f64}
impl num_traits::cast::ToPrimitive for Float {
fn to_i64(&self) -> Option<i64> {
self.val.to_integer()?.to_i64()
}
fn to_u64(&self) -> Option<u64> {
self.val.to_integer()?.to_u64()
}
fn to_isize(&self) -> Option<isize> {
self.val.to_integer()?.to_isize()
}
fn to_i8(&self) -> Option<i8> {
self.val.to_integer()?.to_i8()
}
fn to_i16(&self) -> Option<i16> {
self.val.to_integer()?.to_i16()
}
fn to_i32(&self) -> Option<i32> {
self.val.to_integer()?.to_i32()
}
fn to_i128(&self) -> Option<i128> {
self.val.to_integer()?.to_i128()
}
fn to_usize(&self) -> Option<usize> {
self.val.to_integer()?.to_usize()
}
fn to_u8(&self) -> Option<u8> {
self.val.to_integer()?.to_u8()
}
fn to_u16(&self) -> Option<u16> {
self.val.to_integer()?.to_u16()
}
fn to_u32(&self) -> Option<u32> {
self.val.to_integer()?.to_u32()
}
fn to_u128(&self) -> Option<u128> {
self.val.to_integer()?.to_u128()
}
fn to_f32(&self) -> Option<f32> {
Some(self.val.to_f32())
}
fn to_f64(&self) -> Option<f64> {
Some(self.val.to_f64())
}
}
impl num_traits::cast::FromPrimitive for Float {
fn from_i64(n: i64) -> Option<Self> {
Some(Self::from(n))
}
fn from_u64(n: u64) -> Option<Self> {
Some(Self::from(n))
}
fn from_isize(n: isize) -> Option<Self> {
Some(Self::from(n))
}
fn from_i8(n: i8) -> Option<Self> {
Some(Self::from(n))
}
fn from_i16(n: i16) -> Option<Self> {
Some(Self::from(n))
}
fn from_i32(n: i32) -> Option<Self> {
Some(Self::from(n))
}
fn from_i128(n: i128) -> Option<Self> {
Some(Self::from(n))
}
fn from_usize(n: usize) -> Option<Self> {
Some(Self::from(n))
}
fn from_u8(n: u8) -> Option<Self> {
Some(Self::from(n))
}
fn from_u16(n: u16) -> Option<Self> {
Some(Self::from(n))
}
fn from_u32(n: u32) -> Option<Self> {
Some(Self::from(n))
}
fn from_u128(n: u128) -> Option<Self> {
Some(Self::from(n))
}
fn from_f32(n: f32) -> Option<Self> {
Some(Self::from(n))
}
fn from_f64(n: f64) -> Option<Self> {
Some(Self::from(n))
}
}
impl AddAssociative for Float {}
impl AddCommutative for Float {}
impl MulAssociative for Float {}
impl MulCommutative for Float {}
impl analysis::ordered::AddOrdered for Float {}
impl analysis::ordered::MulOrdered for Float {}
impl algebra::ring_like::EuclideanDiv for Float {
type Naturals = Integer;
fn euclid_norm(&self) -> Self::Naturals {
Self::Naturals {
val: self.val.to_integer().unwrap().abs(),
}
}
fn div_euc(self, rhs: Self) -> Self {
Self {
val: (self.val / &rhs.val).trunc(),
}
}
fn rem_euc(self, rhs: Self) -> Self {
self % rhs
}
fn div_alg(self, rhs: Self) -> (Self, Self) {
(self.clone().div_euc(rhs.clone()), self.rem_euc(rhs))
}
}
impl analysis::ordered::ArchimedeanProperty for Float {}
impl analysis::ordered::ArchimedeanDiv for Float {
fn embed_nat<N: algebra::integer::Natural>(n: N) -> Self {
Self::from_u128(n.to_u128().unwrap()).unwrap()
}
fn div_arch(self, rhs: Self) -> Self {
self.div_euc(rhs)
}
fn rem_arch(self, rhs: Self) -> Self {
self.rem_euc(rhs)
}
fn div_alg_arch(self, rhs: Self) -> (Self, Self) {
self.div_alg(rhs)
}
}
impl_arith! { Float, Add { add }, AddAssign {add_assign} }
impl_arith! { Float, Sub { sub }, SubAssign {sub_assign} }
impl_arith! { Float, Mul { mul }, MulAssign {mul_assign} }
impl_arith! { Float, Div { div }, DivAssign {div_assign} }
impl_arith! { Float, Rem { rem }, RemAssign {rem_assign} }
impl CheckedAdd for Float {
fn checked_add(&self, v: &Self) -> Option<Self> {
Some(self + v)
}
}
impl CheckedSub for Float {
fn checked_sub(&self, v: &Self) -> Option<Self> {
Some(self - v)
}
}
impl CheckedNeg for Float {
fn checked_neg(&self) -> Option<Self> {
Some(Self {
val: -self.val.clone(),
})
}
}
impl CheckedMul for Float {
fn checked_mul(&self, v: &Self) -> Option<Self> {
Some(self * v)
}
}
impl CheckedDiv for Float {
fn checked_div(&self, v: &Self) -> Option<Self> {
if v.is_zero() {
None
} else {
Some(self / v)
}
}
}
impl CheckedRem for Float {
fn checked_rem(&self, v: &Self) -> Option<Self> {
if v.is_zero() {
None
} else {
Some(self % v)
}
}
}
impl algebra::group_like::additive::Neg for Float {
type Output = Float;
fn neg(self) -> Self::Output {
Self::Output { val: -self.val }
}
}
impl algebra::group_like::additive::Zero for Float {
fn zero() -> Self {
Self {
val: rug::Float::new(FLOAT_DEFAULT_PREC),
}
}
fn is_zero(&self) -> bool {
self.val.is_zero()
}
fn set_zero(&mut self) {
self.val *= 0;
}
}
impl algebra::group_like::multiplicative::One for Float {
fn one() -> Self {
Self {
val: rug::Float::with_val(FLOAT_DEFAULT_PREC, 1),
}
}
fn is_one(&self) -> bool {
self.val == 1
}
fn set_one(&mut self) {
self.val *= 0;
self.val += 1;
}
}
impl algebra::group_like::multiplicative::Inv for Float {
type Output = Float;
fn inv(self) -> Self::Output {
Self::Output::one() / self
}
}
impl algebra::ring_like::Distributive for Float {}
impl algebra::ring_like::Divisibility for Float {
fn divides(self, rhs: Self) -> bool {
(rhs % self).is_zero()
}
fn divide(self, rhs: Self) -> Option<Self> {
match self.clone().divides(rhs.clone()) {
false => None,
true => Some(rhs / self),
}
}
fn unit(&self) -> bool {
!self.is_zero()
}
fn inverse(self) -> Option<Self> {
match self.unit() {
false => None,
true => Some(self.inv()),
}
}
}
impl analysis::ordered::Sign for Float {
fn signum(self) -> Self {
Self {
val: self.val.signum(),
}
}
fn abs(self) -> Self {
Self {
val: self.val.abs(),
}
}
}
impl algebra::ring_like::NoZeroDivisors for Float {}
macro_rules! float_fn_trig {
($fn:ident) => {
fn $fn(self) -> Self {
Float {
val: self.val.$fn(),
}
}
};
}
macro_rules! float_fn_trig_try {
($fn:ident $try_fn:ident) => {
fn $try_fn(self) -> Option<Self> {
let result = self.val.$fn();
if result.is_nan() {
return None;
}
Some(Float { val: result })
}
};
}
impl analysis::real::Trig for Float {
float_fn_trig! {sin}
float_fn_trig! {cos}
float_fn_trig! {tan}
float_fn_trig! {sinh}
float_fn_trig! {cosh}
float_fn_trig! {tanh}
float_fn_trig! {asin}
float_fn_trig! {acos}
float_fn_trig! {atan}
float_fn_trig! {asinh}
float_fn_trig! {acosh}
float_fn_trig! {atanh}
float_fn_trig_try! {asin try_asin}
float_fn_trig_try! {acos try_acos}
float_fn_trig_try! {asinh try_asinh}
float_fn_trig_try! {acosh try_acosh}
float_fn_trig_try! {atanh try_atanh}
fn atan2(y: Self, x: Self) -> Self {
Self {
val: rug::Float::with_val(FLOAT_DEFAULT_PREC, rug::Float::atan2(y.val, &x.val)),
}
}
fn pi() -> Self {
Self {
val: rug::Float::with_val(FLOAT_DEFAULT_PREC, rug::float::Constant::Pi),
}
}
}
impl algebra::ring_like::Exponential for Float {
fn exp(self) -> Self {
Self {
val: self.val.exp(),
}
}
fn try_ln(self) -> Option<Self> {
let result = self.val.ln();
if result.is_nan() {
return None;
}
Some(Self { val: result })
}
}
impl analysis::real::RealExponential for Float {}
impl analysis::real::Real for Float {
fn approx(self) -> f64 {
self.to_f64().unwrap()
}
fn repr(f: f64) -> Self {
Self::from_f64(f).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use maths_traits::algebra::{Divisibility, Exponential};
use maths_traits::analysis::{Real, Trig};
use num_traits::ToPrimitive;
#[test]
fn test_ops() {
let a = Float::from(16);
let b = Float::from(10);
let c = Float::from(-6);
assert_eq!(b.clone() - a.clone(), c);
assert_eq!(&b - a.clone(), c);
assert_eq!(b.clone() - &a, c);
assert_eq!(&b - &a, c);
assert_eq!(b.clone() - a.to_integer().unwrap().to_i16().unwrap(), c);
assert_eq!(&b - a.to_integer().unwrap().to_i16().unwrap(), c);
assert_eq!(&a * b.clone().pow_z(80), ((a - 1) * 10 + 10) * b.pow_z(79));
}
#[test]
fn test_divisibility() {
assert_eq!(
Float::from(11.55).divide(Float::from(23.1)),
Some(Float::from(2))
);
assert_eq!(Float::from(11.5333).divide(Float::from(23.6661)), None);
assert_eq!(
Float::from(-11.55).divide(Float::from(23.1)),
Some(Float::from(-2))
);
assert_eq!(
Float::from(11.55).divide(Float::from(-23.1)),
Some(Float::from(-2))
);
assert_eq!(
Float::from(-11.55).divide(Float::from(-23.1)),
Some(Float::from(2))
);
assert_eq!(Float::zero().inverse(), None);
assert_eq!(Float::one().inverse(), Some(Float::one()));
assert_eq!(Float::from(2).inverse(), Some(Float::from(0.5)));
}
#[test]
fn test_trig() {
let a = Float {
val: rug::Float::with_val(FLOAT_DEFAULT_PREC, 123),
};
assert_eq!(Float::atan2(a.clone(), Float::zero()), Float::pi() / 2);
assert_eq!(Float::atan2(Float::zero(), a.clone()), Float::zero());
assert_eq!(Float::atan2(a.clone(), a), Float::pi() / 4);
}
#[test]
fn test_exponential() {
assert_eq!(Float::zero().exp(), Float::one());
assert_eq!(Float::from(12.3).exp().try_ln(), Some(Float::from(12.3)));
assert_eq!(Float::from(-1).try_ln(), None);
}
#[test]
fn test_real() {
assert_eq!(123456.789f64, Float::repr(123456.789).approx());
}
}