#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
IntegerScalar, ScalarCore,
core::{
errors::{
ErrorsRawRealToInteger, ErrorsValidationRawComplex, ErrorsValidationRawReal,
capture_backtrace,
},
traits::{
raw::{
RawComplexTrait, RawRealTrait, RawScalarHyperbolic, RawScalarPow, RawScalarTrait,
RawScalarTrigonometric, RawScalarValidate,
},
validation::FpChecks,
},
},
functions::{Conjugate, NegAssign, Rounding, Sign},
kernels::PrecisionBits,
};
use duplicate::duplicate_item;
use rug::{
float::Constant as MpfrConstant,
ops::{CompleteRound, Pow},
};
use std::{
cmp::Ordering,
hash::{Hash, Hasher},
num::FpCategory,
ops::Neg,
};
#[duplicate_item(
T;
[rug::Float];
[rug::Complex];
)]
impl RawScalarTrigonometric for T {
#[duplicate_item(
unchecked_method method;
[unchecked_sin] [sin];
[unchecked_asin] [asin];
[unchecked_cos] [cos];
[unchecked_acos] [acos];
[unchecked_tan] [tan];
[unchecked_atan] [atan];)]
#[inline(always)]
fn unchecked_method(self) -> Self {
T::method(self)
}
}
#[duplicate_item(
T;
[rug::Float];
[rug::Complex];
)]
impl RawScalarHyperbolic for T {
#[duplicate_item(
unchecked_method method;
[unchecked_sinh] [sinh];
[unchecked_asinh] [asinh];
[unchecked_cosh] [cosh];
[unchecked_acosh] [acosh];
[unchecked_tanh] [tanh];
[unchecked_atanh] [atanh];)]
#[inline(always)]
fn unchecked_method(self) -> Self {
T::method(self)
}
}
#[duplicate_item(
T;
[rug::Float];
[rug::Complex];
)]
impl RawScalarPow for T {
#[duplicate_item(
unchecked_method exponent_type;
[unchecked_pow_exponent_i8] [i8];
[unchecked_pow_exponent_i16] [i16];
[unchecked_pow_exponent_i32] [i32];
[unchecked_pow_exponent_i64] [i64];
[unchecked_pow_exponent_i128] [i128];
[unchecked_pow_exponent_isize] [isize];
[unchecked_pow_exponent_u8] [u8];
[unchecked_pow_exponent_u16] [u16];
[unchecked_pow_exponent_u32] [u32];
[unchecked_pow_exponent_u64] [u64];
[unchecked_pow_exponent_u128] [u128];
[unchecked_pow_exponent_usize] [usize];
)]
#[inline(always)]
fn unchecked_method(self, exponent: &exponent_type) -> Self {
T::pow(self, exponent)
}
}
impl RawScalarValidate for rug::Float {
type ValidationError = ErrorsValidationRawReal<rug::Float>;
#[inline(always)]
fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
match self.raw_classify() {
FpCategory::Nan => Err(ErrorsValidationRawReal::<rug::Float>::IsNaN {
value: self.clone(),
backtrace: capture_backtrace(),
}),
FpCategory::Infinite => {
if self.is_sign_positive() {
Err(ErrorsValidationRawReal::<rug::Float>::IsPosInfinity {
backtrace: capture_backtrace(),
})
} else {
Err(ErrorsValidationRawReal::<rug::Float>::IsNegInfinity {
backtrace: capture_backtrace(),
})
}
}
FpCategory::Subnormal => {
unreachable!("rug::Float should not produce subnormal values");
}
FpCategory::Zero | FpCategory::Normal => {
Ok(())
}
}
}
}
#[duplicate_item(
T;
[rug::Float];
[rug::Complex];
)]
impl ScalarCore for T {
type RawScalarType = Self;
fn as_raw_scalar_ref(&self) -> &Self::RawScalarType {
self
}
fn into_raw_scalar(self) -> Self::RawScalarType {
self
}
}
impl RawScalarTrait for rug::Float {
fn raw_zero(precision: u32) -> Self {
rug::Float::with_val(precision, 0.)
}
fn is_zero(&self) -> bool {
rug::Float::is_zero(self)
}
fn raw_one(precision: u32) -> Self {
rug::Float::with_val(precision, 1.)
}
#[duplicate_item(
unchecked_method method;
[unchecked_reciprocal] [recip];
[unchecked_exp] [exp];
[unchecked_sqrt] [sqrt];
[unchecked_ln] [ln];
[unchecked_log2] [log2];
[unchecked_log10] [log10];
)]
#[inline(always)]
fn unchecked_method(self) -> Self {
rug::Float::method(self)
}
#[inline(always)]
fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
rug::Float::mul_add(self, b, c)
}
#[inline(always)]
fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H) {
debug_assert!(
self.is_finite(),
"Hashing a non-finite rug::Float value (i.e., NaN or Infinity) may lead to inconsistent results."
);
self.prec().hash(state);
if self.is_zero() {
rug::Float::with_val(self.prec(), 0.0)
.to_string_radix(10, None)
.hash(state);
} else {
self.to_string_radix(10, None).hash(state)
}
}
}
impl PrecisionBits for rug::Float {
#[inline(always)]
fn precision_bits(&self) -> u32 {
self.prec()
}
}
impl RawRealTrait for rug::Float {
type RawComplex = rug::Complex;
#[inline(always)]
fn unchecked_abs(self) -> rug::Float {
rug::Float::abs(self)
}
#[inline(always)]
fn unchecked_atan2(self, denominator: &Self) -> Self {
rug::Float::atan2(self, denominator)
}
#[inline(always)]
fn unchecked_pow_exponent_real(self, exponent: &Self) -> Self {
rug::Float::pow(self, exponent)
}
#[inline(always)]
fn unchecked_hypot(self, other: &Self) -> Self {
rug::Float::hypot(self, other)
}
#[inline(always)]
fn unchecked_ln_1p(self) -> Self {
rug::Float::ln_1p(self)
}
#[inline(always)]
fn unchecked_exp_m1(self) -> Self {
rug::Float::exp_m1(self)
}
#[inline(always)]
fn unchecked_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self) {
self.mul_add_mul_mut(mul, add_mul1, add_mul2);
}
#[inline(always)]
fn unchecked_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self) {
self.mul_sub_mul_mut(mul, sub_mul1, sub_mul2);
}
#[inline(always)]
fn raw_total_cmp(&self, other: &Self) -> Ordering {
rug::Float::total_cmp(self, other)
}
#[inline(always)]
fn raw_clamp(self, min: &Self, max: &Self) -> Self {
rug::Float::clamp(self, min, max)
}
#[inline(always)]
fn raw_classify(&self) -> FpCategory {
rug::Float::classify(self)
}
#[inline(always)]
fn raw_two(precision: u32) -> Self {
rug::Float::with_val(precision, 2.)
}
#[inline(always)]
fn raw_one_div_2(precision: u32) -> Self {
rug::Float::with_val(precision, 0.5)
}
#[inline(always)]
fn raw_pi(precision: u32) -> Self {
rug::Float::with_val(precision, MpfrConstant::Pi)
}
#[inline(always)]
fn raw_two_pi(precision: u32) -> Self {
rug::Float::with_val(precision, MpfrConstant::Pi) * 2
}
#[inline(always)]
fn raw_pi_div_2(precision: u32) -> Self {
rug::Float::with_val(precision, MpfrConstant::Pi) / 2
}
#[inline(always)]
fn raw_max_finite(precision: u32) -> Self {
let one = rug::Float::with_val(precision, 1);
let eps = rug::Float::with_val(precision, rug::Float::u_pow_u(2, precision)).recip();
let significand = one - &eps;
let max_exp = rug::float::exp_max() - 1;
significand * rug::Float::with_val(precision, rug::Float::u_pow_u(2, max_exp as u32))
}
#[inline(always)]
fn raw_min_finite(precision: u32) -> Self {
Self::raw_max_finite(precision).neg()
}
#[inline(always)]
fn raw_epsilon(precision: u32) -> Self {
rug::Float::u_pow_u(2, precision - 1)
.complete(precision)
.recip()
}
#[inline(always)]
fn raw_ln_2(precision: u32) -> Self {
rug::Float::with_val(precision, MpfrConstant::Log2)
}
#[inline(always)]
fn raw_ln_10(precision: u32) -> Self {
let ln2 = rug::Float::with_val(precision, MpfrConstant::Log2);
let log2_10 = rug::Float::with_val(precision, 10).log2();
ln2 * log2_10
}
#[inline(always)]
fn raw_log10_2(precision: u32) -> Self {
rug::Float::with_val(precision, 2.).log10()
}
#[inline(always)]
fn raw_log2_10(precision: u32) -> Self {
rug::Float::with_val(precision, 10.).log2()
}
#[inline(always)]
fn raw_log2_e(precision: u32) -> Self {
rug::Float::with_val(precision, MpfrConstant::Log2).recip()
}
#[inline(always)]
fn raw_log10_e(precision: u32) -> Self {
let ln2 = rug::Float::with_val(precision, MpfrConstant::Log2);
let log2_10 = rug::Float::with_val(precision, 10).log2();
(ln2 * log2_10).recip()
}
#[inline(always)]
fn raw_e(precision: u32) -> Self {
rug::Float::with_val(precision, 1.).exp()
}
#[inline(always)]
fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<rug::Float, usize>> {
if !self.is_finite() {
return Err(ErrorsRawRealToInteger::NotFinite {
value: self,
backtrace: capture_backtrace(),
});
}
let truncation = self.clone().trunc();
let out_of_range = || ErrorsRawRealToInteger::OutOfRange {
value: self,
min: usize::MIN,
max: usize::MAX,
backtrace: capture_backtrace(),
};
let truncation_as_rug_integer = truncation.to_integer().ok_or_else(out_of_range.clone())?;
truncation_as_rug_integer
.to_usize()
.ok_or_else(out_of_range)
}
fn unchecked_mul_assign_integer<I>(&mut self, integer: I)
where
I: IntegerScalar,
{
let as_rug_integer = match integer.to_i128() {
Some(value) => rug::Integer::from(value),
None => rug::Integer::from(
integer
.to_u128()
.expect("IntegerScalar values always fit in i128 or u128"),
),
};
*self *= as_rug_integer;
}
fn unchecked_div_assign_integer<I>(&mut self, integer: I)
where
I: IntegerScalar,
{
let as_rug_integer = match integer.to_i128() {
Some(value) => rug::Integer::from(value),
None => rug::Integer::from(
integer
.to_u128()
.expect("IntegerScalar values always fit in i128 or u128"),
),
};
*self /= as_rug_integer;
}
}
impl RawScalarValidate for rug::Complex {
type ValidationError = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;
fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
let real_validation = self.real().validate_raw_scalar_ref();
let imag_validation = self.imag().validate_raw_scalar_ref();
match (real_validation, imag_validation) {
(Ok(()), Ok(())) => Ok(()),
(Ok(()), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
source: Box::new(imag_err),
}),
(Err(real_err), Ok(())) => Err(ErrorsValidationRawComplex::InvalidRealPart {
source: Box::new(real_err),
}),
(Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
real_error: Box::new(real_err),
imag_error: Box::new(imag_err),
}),
}
}
}
impl RawScalarTrait for rug::Complex {
fn raw_zero(precision: u32) -> Self {
rug::Complex::with_val(precision, (0., 0.))
}
fn is_zero(&self) -> bool {
rug::Complex::is_zero(self)
}
fn raw_one(precision: u32) -> Self {
rug::Complex::with_val(precision, (1., 0.))
}
#[duplicate_item(
unchecked_method method;
[unchecked_reciprocal] [recip];
[unchecked_exp] [exp];
[unchecked_sqrt] [sqrt];
[unchecked_ln] [ln];
[unchecked_log10] [log10];
)]
#[inline(always)]
fn unchecked_method(self) -> Self {
rug::Complex::method(self)
}
#[inline(always)]
fn unchecked_log2(self) -> Self {
let ln_2 = rug::Float::with_val(self.real().prec(), 2.).ln();
rug::Complex::ln(self) / ln_2
}
#[inline(always)]
fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self {
rug::Complex::mul_add(self, b, c)
}
fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H) {
self.raw_real_part().compute_hash_raw_scalar(state);
self.raw_imag_part().compute_hash_raw_scalar(state);
}
}
impl Conjugate for rug::Complex {
#[inline(always)]
fn conjugate(self) -> Self {
rug::Complex::conj(self)
}
}
impl RawComplexTrait for rug::Complex {
type RawReal = rug::Float;
fn new_unchecked_raw_complex(real: rug::Float, imag: rug::Float) -> Self {
debug_assert_eq!(
real.prec(),
imag.prec(),
"Different precision between real and imaginary part!"
);
rug::Complex::with_val(real.prec(), (real, imag))
}
fn mut_raw_real_part(&mut self) -> &mut rug::Float {
self.mut_real()
}
fn mut_raw_imag_part(&mut self) -> &mut rug::Float {
self.mut_imag()
}
#[inline(always)]
fn unchecked_abs(self) -> rug::Float {
rug::Complex::abs(self).into_real_imag().0
}
#[inline(always)]
fn raw_real_part(&self) -> &rug::Float {
self.real()
}
#[inline(always)]
fn raw_imag_part(&self) -> &rug::Float {
self.imag()
}
#[inline(always)]
fn unchecked_arg(self) -> rug::Float {
rug::Complex::arg(self).into_real_imag().0
}
#[inline(always)]
fn unchecked_pow_exponent_real(self, exponent: &rug::Float) -> Self {
rug::Complex::pow(self, exponent)
}
}
impl FpChecks for rug::Float {
fn is_finite(&self) -> bool {
rug::Float::is_finite(self)
}
fn is_infinite(&self) -> bool {
rug::Float::is_infinite(self)
}
fn is_nan(&self) -> bool {
rug::Float::is_nan(self)
}
fn is_normal(&self) -> bool {
rug::Float::is_normal(self)
}
}
impl FpChecks for rug::Complex {
#[inline(always)]
fn is_finite(&self) -> bool {
self.real().is_finite() && self.imag().is_finite()
}
#[inline(always)]
fn is_infinite(&self) -> bool {
!self.is_nan() && (self.real().is_infinite() || self.imag().is_infinite())
}
#[inline(always)]
fn is_nan(&self) -> bool {
self.real().is_nan() || self.imag().is_nan()
}
#[inline(always)]
fn is_normal(&self) -> bool {
self.real().is_normal() && self.imag().is_normal()
}
}
#[duplicate_item(
T;
[rug::Float];
[rug::Complex];
)]
impl NegAssign for T {
fn neg_assign(&mut self) {
<T as rug::ops::NegAssign>::neg_assign(self);
}
}
impl Sign for rug::Float {
#[inline(always)]
fn kernel_copysign(self, sign: &Self) -> Self {
self.copysign(sign)
}
#[inline(always)]
fn kernel_is_sign_negative(&self) -> bool {
self.is_sign_negative()
}
#[inline(always)]
fn kernel_is_sign_positive(&self) -> bool {
self.is_sign_positive()
}
#[inline(always)]
fn kernel_signum(self) -> Self {
self.signum()
}
}
impl Rounding for rug::Float {
#[inline(always)]
fn kernel_ceil(self) -> Self {
self.ceil()
}
#[inline(always)]
fn kernel_floor(self) -> Self {
self.floor()
}
#[inline(always)]
fn kernel_fract(self) -> Self {
self.fract()
}
#[inline(always)]
fn kernel_round(self) -> Self {
self.round()
}
#[inline(always)]
fn kernel_round_ties_even(self) -> Self {
self.round_even()
}
#[inline(always)]
fn kernel_trunc(self) -> Self {
self.trunc()
}
}
#[cfg(test)]
mod tests {
use crate::kernels::RawRealTrait;
mod mul_assign_integer {
use super::*;
#[test]
fn multiplies_by_signed_integer() {
let mut real = rug::Float::with_val(100, 3.5);
real.unchecked_mul_assign_integer(-4_i32);
assert_eq!(real, rug::Float::with_val(100, -14));
}
#[test]
fn multiplies_by_unsigned_integer() {
let mut real = rug::Float::with_val(100, 3.5);
real.unchecked_mul_assign_integer(4_u16);
assert_eq!(real, rug::Float::with_val(100, 14));
}
#[test]
fn multiplies_by_i128_beyond_f64_precision() {
let mut real = rug::Float::with_val(200, 1);
real.unchecked_mul_assign_integer((1_i128 << 100) + 1);
assert_eq!(real, rug::Float::with_val(200, (1_i128 << 100) + 1));
}
#[test]
fn multiplies_by_u128_beyond_i128_range() {
let mut real = rug::Float::with_val(200, 1);
real.unchecked_mul_assign_integer(u128::MAX);
assert_eq!(
real,
rug::Float::with_val(200, rug::Integer::from(u128::MAX))
);
}
}
mod div_assign_integer {
use super::*;
#[test]
fn divides_by_signed_integer() {
let mut real = rug::Float::with_val(100, 14);
real.unchecked_div_assign_integer(-4_i32);
assert_eq!(real, rug::Float::with_val(100, -3.5));
}
#[test]
fn divides_by_unsigned_integer() {
let mut real = rug::Float::with_val(100, 14);
real.unchecked_div_assign_integer(4_u16);
assert_eq!(real, rug::Float::with_val(100, 3.5));
}
#[test]
fn divides_by_i128_beyond_f64_precision() {
let mut real = rug::Float::with_val(200, (1_i128 << 100) + 1);
real.unchecked_div_assign_integer((1_i128 << 100) + 1);
assert_eq!(real, rug::Float::with_val(200, 1));
}
#[test]
fn divides_by_u128_beyond_i128_range() {
let mut real = rug::Float::with_val(200, rug::Integer::from(u128::MAX));
real.unchecked_div_assign_integer(u128::MAX);
assert_eq!(real, rug::Float::with_val(200, 1));
}
}
mod mul_integer {
use super::*;
#[test]
fn returns_product_without_mutating_in_place() {
let real = rug::Float::with_val(100, 3.5);
assert_eq!(
real.unchecked_mul_integer(-4_i32),
rug::Float::with_val(100, -14)
);
}
}
mod div_integer {
use super::*;
#[test]
fn returns_quotient_without_mutating_in_place() {
let real = rug::Float::with_val(100, 14);
assert_eq!(
real.unchecked_div_integer(-4_i32),
rug::Float::with_val(100, -3.5)
);
}
}
}