#![deny(rustdoc::broken_intra_doc_links)]
use crate::{
Arithmetic, Conjugate, IntegerScalar, NeumaierAddable, ScalarCore,
core::{
errors::{
ErrorsRawRealToInteger, ErrorsValidationRawComplex, ErrorsValidationRawReal,
RawRealConversionError, capture_backtrace,
},
traits::validation::{FpChecks, sealed::NativeReal},
},
functions::{Rounding, Sign},
kernels::PrecisionBits,
};
#[cfg(feature = "rug")]
use duplicate::duplicate_item;
use std::{
cmp::Ordering,
hash::Hasher,
num::FpCategory,
ops::{Mul, MulAssign},
};
pub trait RawScalarTrigonometric {
fn unchecked_sin(self) -> Self;
fn unchecked_asin(self) -> Self;
fn unchecked_cos(self) -> Self;
fn unchecked_acos(self) -> Self;
fn unchecked_tan(self) -> Self;
fn unchecked_atan(self) -> Self;
}
pub trait RawScalarHyperbolic {
fn unchecked_sinh(self) -> Self;
fn unchecked_asinh(self) -> Self;
fn unchecked_cosh(self) -> Self;
fn unchecked_acosh(self) -> Self;
fn unchecked_tanh(self) -> Self;
fn unchecked_atanh(self) -> Self;
}
pub trait RawScalarPow {
fn unchecked_pow_exponent_i8(self, exponent: &i8) -> Self;
fn unchecked_pow_exponent_i16(self, exponent: &i16) -> Self;
fn unchecked_pow_exponent_i32(self, exponent: &i32) -> Self;
fn unchecked_pow_exponent_i64(self, exponent: &i64) -> Self;
fn unchecked_pow_exponent_i128(self, exponent: &i128) -> Self;
fn unchecked_pow_exponent_isize(self, exponent: &isize) -> Self;
fn unchecked_pow_exponent_u8(self, exponent: &u8) -> Self;
fn unchecked_pow_exponent_u16(self, exponent: &u16) -> Self;
fn unchecked_pow_exponent_u32(self, exponent: &u32) -> Self;
fn unchecked_pow_exponent_u64(self, exponent: &u64) -> Self;
fn unchecked_pow_exponent_u128(self, exponent: &u128) -> Self;
fn unchecked_pow_exponent_usize(self, exponent: &usize) -> Self;
}
pub trait RawScalarValidate: Sized {
type ValidationError: std::error::Error;
#[inline(always)]
fn validate_raw_scalar(self) -> Result<Self, Self::ValidationError> {
self.validate_raw_scalar_ref().map(|_| self)
}
#[inline(always)]
fn validate_in_debug_raw_scalar(self) -> Result<Self, Self::ValidationError> {
self.validate_in_debug_raw_scalar_ref().map(|_| self)
}
#[inline(always)]
fn validate_in_debug_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
#[cfg(debug_assertions)]
{
self.validate_raw_scalar_ref()
}
#[cfg(not(debug_assertions))]
{
Ok(())
}
}
fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError>;
}
pub trait RawScalarTrait:
ScalarCore
+ Arithmetic
+ NeumaierAddable
+ FpChecks
+ RawScalarTrigonometric
+ RawScalarHyperbolic
+ RawScalarPow
+ RawScalarValidate
{
fn is_zero(&self) -> bool;
fn raw_zero(precision: u32) -> Self;
fn raw_one(precision: u32) -> Self;
#[inline(always)]
fn raw_negative_one(precision: u32) -> Self {
Self::raw_one(precision).neg()
}
fn unchecked_reciprocal(self) -> Self;
fn unchecked_exp(self) -> Self;
fn unchecked_sqrt(self) -> Self;
fn unchecked_ln(self) -> Self;
fn unchecked_log2(self) -> Self;
fn unchecked_log10(self) -> Self;
fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self;
fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H);
}
pub trait RawRealTrait:
RawScalarTrait<ValidationError = ErrorsValidationRawReal<Self>>
+ PartialOrd
+ Sign
+ Rounding
+ PrecisionBits
{
type RawComplex: RawComplexTrait<RawReal = Self>;
fn unchecked_abs(self) -> Self;
fn unchecked_atan2(self, denominator: &Self) -> Self;
fn unchecked_pow_exponent_real(self, exponent: &Self) -> Self;
fn unchecked_hypot(self, other: &Self) -> Self;
fn unchecked_ln_1p(self) -> Self;
fn unchecked_exp_m1(self) -> Self;
fn unchecked_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self);
fn unchecked_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self);
fn raw_total_cmp(&self, other: &Self) -> Ordering;
fn raw_clamp(self, min: &Self, max: &Self) -> Self;
fn raw_classify(&self) -> FpCategory;
fn raw_two(precision: u32) -> Self;
fn raw_one_div_2(precision: u32) -> Self;
fn raw_pi(precision: u32) -> Self;
fn raw_two_pi(precision: u32) -> Self;
fn raw_pi_div_2(precision: u32) -> Self;
fn raw_max_finite(precision: u32) -> Self;
fn raw_min_finite(precision: u32) -> Self;
fn raw_epsilon(precision: u32) -> Self;
fn raw_ln_2(precision: u32) -> Self;
fn raw_ln_10(precision: u32) -> Self;
fn raw_log10_2(precision: u32) -> Self;
fn raw_log2_10(precision: u32) -> Self;
fn raw_log2_e(precision: u32) -> Self;
fn raw_log10_e(precision: u32) -> Self;
fn raw_e(precision: u32) -> Self;
fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<Self, usize>>;
#[inline(always)]
fn try_convert<RawRealOut>(
self,
precision_out: u32,
) -> Result<RawRealOut, RawRealConversionError<Self, RawRealOut>>
where
RawRealOut: RawRealTrait + TryConvertFromRawReal<Self>,
{
RawRealOut::try_convert_from_raw_real(self, precision_out)
}
fn unchecked_mul_assign_integer<I>(&mut self, integer: I)
where
I: IntegerScalar;
fn unchecked_div_assign_integer<I>(&mut self, integer: I)
where
I: IntegerScalar;
#[inline(always)]
fn unchecked_mul_integer<I>(mut self, integer: I) -> Self
where
I: IntegerScalar,
{
self.unchecked_mul_assign_integer(integer);
self
}
#[inline(always)]
fn unchecked_div_integer<I>(mut self, integer: I) -> Self
where
I: IntegerScalar,
{
self.unchecked_div_assign_integer(integer);
self
}
}
pub trait TryConvertFromRawReal<RawRealIn: RawRealTrait>: RawRealTrait {
fn try_convert_from_raw_real(
value_in: RawRealIn,
precision_out: u32,
) -> Result<Self, RawRealConversionError<RawRealIn, Self>>;
}
pub(crate) fn try_validity_precision_out<RealIn: RawRealTrait, RealOut: NativeReal>(
value_in: &RealIn,
precision_out: u32,
) -> Result<(), RawRealConversionError<RealIn, RealOut>> {
if precision_out != RealOut::MANTISSA_DIGITS {
return Err(RawRealConversionError::InvalidOutputPrecision {
value_in: value_in.clone(),
valid_precision_out: RealOut::MANTISSA_DIGITS,
precision_out,
backtrace: capture_backtrace(),
});
}
Ok(())
}
#[inline(always)]
fn try_validate_input_value<RealIn: RawRealTrait, RealOut: RawRealTrait>(
value_in: RealIn,
) -> Result<RealIn, RawRealConversionError<RealIn, RealOut>> {
value_in
.validate_raw_scalar()
.map_err(|e| RawRealConversionError::InputValidation { source: e })
}
#[inline(always)]
fn try_validate_output_value<RealIn: RawRealTrait, RealOut: RawRealTrait>(
value_out: RealOut,
) -> Result<RealOut, RawRealConversionError<RealIn, RealOut>> {
value_out
.validate_raw_scalar()
.map_err(|e| RawRealConversionError::OutputValidation { source: e })
}
impl<T: NativeReal> TryConvertFromRawReal<T> for T {
#[inline(always)]
fn try_convert_from_raw_real(
value: T,
precision_out: u32,
) -> Result<Self, RawRealConversionError<T, T>> {
try_validity_precision_out::<T, T>(&value, precision_out)?;
try_validate_input_value::<T, T>(value)
}
}
impl TryConvertFromRawReal<f64> for f32 {
fn try_convert_from_raw_real(
value_in: f64,
precision_out: u32,
) -> Result<Self, RawRealConversionError<f64, f32>> {
try_validity_precision_out::<f64, f32>(&value_in, precision_out)?;
let value_in = try_validate_input_value::<f64, f32>(value_in)?;
let value_out = try_validate_output_value::<f64, f32>(value_in as f32)?;
if (value_out as f64) != value_in {
let precision_out = value_out.precision_bits();
return Err(RawRealConversionError::NonRepresentableExactly {
value_in,
value_out,
precision_out,
backtrace: capture_backtrace(),
});
}
Ok(value_out)
}
}
impl TryConvertFromRawReal<f32> for f64 {
fn try_convert_from_raw_real(
value_in: f32,
precision_out: u32,
) -> Result<Self, RawRealConversionError<f32, f64>> {
try_validity_precision_out::<f32, f64>(&value_in, precision_out)?;
let value_in = try_validate_input_value::<f32, f64>(value_in)?;
Ok(value_in as f64)
}
}
#[duplicate_item(
RawRealOut func_rug_to_native;
[f64] [to_f64()];
[f32] [to_f32()];
)]
#[cfg(feature = "rug")]
impl TryConvertFromRawReal<rug::Float> for RawRealOut {
fn try_convert_from_raw_real(
value_in: rug::Float,
precision_out: u32,
) -> Result<Self, RawRealConversionError<rug::Float, Self>> {
try_validity_precision_out::<rug::Float, RawRealOut>(&value_in, precision_out)?;
let value_in = try_validate_input_value::<rug::Float, RawRealOut>(value_in)?;
let value_out =
try_validate_output_value::<rug::Float, RawRealOut>(value_in.func_rug_to_native)?;
let precision_in = value_in.precision_bits();
let value_roundtrip = rug::Float::with_val(precision_in, value_out);
if value_roundtrip != value_in {
let precision_out = value_out.precision_bits();
return Err(RawRealConversionError::NonRepresentableExactly {
value_in,
value_out,
precision_out,
backtrace: capture_backtrace(),
});
}
Ok(value_out)
}
}
#[cfg(feature = "rug")]
impl TryConvertFromRawReal<rug::Float> for rug::Float {
fn try_convert_from_raw_real(
value_in: rug::Float,
precision_out: u32,
) -> Result<Self, RawRealConversionError<rug::Float, Self>> {
let value_in = try_validate_input_value::<rug::Float, rug::Float>(value_in)?;
let precision_in = value_in.precision_bits();
if precision_out == precision_in {
Ok(value_in)
} else {
let value_out = try_validate_output_value::<rug::Float, rug::Float>(
rug::Float::with_val(precision_out, &value_in),
)?;
let value_roundtrip = rug::Float::with_val(precision_in, &value_out);
if value_roundtrip != value_in {
return Err(RawRealConversionError::NonRepresentableExactly {
value_in,
value_out,
precision_out,
backtrace: capture_backtrace(),
});
}
Ok(value_out)
}
}
}
#[duplicate_item(
RawRealIn func_rug_to_native;
[f64] [to_f64()];
[f32] [to_f32()];
)]
#[cfg(feature = "rug")]
impl TryConvertFromRawReal<RawRealIn> for rug::Float {
fn try_convert_from_raw_real(
value_in: RawRealIn,
precision_out: u32,
) -> Result<Self, RawRealConversionError<RawRealIn, Self>> {
let value_in = try_validate_input_value::<RawRealIn, rug::Float>(value_in)?;
let value_out = try_validate_output_value::<RawRealIn, rug::Float>(rug::Float::with_val(
precision_out,
value_in,
))?;
let value_roundtrip = value_out.func_rug_to_native;
if value_roundtrip != value_in {
return Err(RawRealConversionError::NonRepresentableExactly {
value_in,
value_out,
precision_out,
backtrace: capture_backtrace(),
});
}
Ok(value_out)
}
}
pub trait RawComplexTrait:
RawScalarTrait<
ValidationError = ErrorsValidationRawComplex<ErrorsValidationRawReal<Self::RawReal>>,
> + Conjugate
+ for<'a> Mul<&'a Self::RawReal, Output = Self>
+ for<'a> MulAssign<&'a Self::RawReal>
{
type RawReal: RawRealTrait<RawComplex = Self>;
fn new_unchecked_raw_complex(real: Self::RawReal, imag: Self::RawReal) -> Self;
fn unchecked_abs(self) -> Self::RawReal;
fn raw_real_part(&self) -> &Self::RawReal;
fn raw_imag_part(&self) -> &Self::RawReal;
fn mut_raw_real_part(&mut self) -> &mut Self::RawReal;
fn mut_raw_imag_part(&mut self) -> &mut Self::RawReal;
fn unchecked_arg(self) -> Self::RawReal;
fn unchecked_pow_exponent_real(self, exponent: &Self::RawReal) -> Self;
}
#[cfg(test)]
mod tests_try_convert_from_raw_real {
use super::*;
use crate::core::errors::RawRealConversionError;
mod native {
use super::*;
#[test]
fn identity_f32_success() {
let value = 1.5_f32;
let converted: f32 = value.try_convert(24).unwrap();
assert_eq!(converted, value);
}
#[test]
fn identity_f32_invalid_output_precision() {
let value = 1.5_f32;
let result = value.try_convert::<f32>(53);
assert!(matches!(
result,
Err(RawRealConversionError::InvalidOutputPrecision { .. })
));
}
#[test]
fn identity_f64_success() {
let value = 1.5_f64;
let converted: f64 = value.try_convert(53).unwrap();
assert_eq!(converted, value);
}
#[test]
fn identity_f64_invalid_output_precision() {
let value = 1.5_f64;
let result = value.try_convert::<f64>(24);
assert!(matches!(
result,
Err(RawRealConversionError::InvalidOutputPrecision { .. })
));
}
#[test]
fn f64_to_f32_success() {
let value = 1.5_f64;
let converted: f32 = value.try_convert(24).unwrap();
assert_eq!(converted, 1.5_f32);
}
#[test]
fn f64_to_f32_non_representable_exactly() {
let value = 0.1_f64;
let result = value.try_convert::<f32>(24);
assert!(matches!(
result,
Err(RawRealConversionError::NonRepresentableExactly { .. })
));
}
#[test]
fn f64_to_f32_invalid_output_precision() {
let value = 1.5_f64;
let result = value.try_convert::<f32>(53);
assert!(matches!(
result,
Err(RawRealConversionError::InvalidOutputPrecision { .. })
));
}
#[test]
fn f32_to_f64_success() {
let value = 1.5_f32;
let converted: f64 = value.try_convert(53).unwrap();
assert_eq!(converted, 1.5_f64);
}
#[test]
fn f32_to_f64_invalid_output_precision() {
let value = 1.5_f32;
let result = value.try_convert::<f64>(24);
assert!(matches!(
result,
Err(RawRealConversionError::InvalidOutputPrecision { .. })
));
}
}
#[cfg(feature = "rug")]
mod rug {
use super::*;
#[test]
fn rug_to_f64_success() {
let value = ::rug::Float::with_val(80, 1.5);
let converted: f64 = value.try_convert(53).unwrap();
assert_eq!(converted, 1.5_f64);
}
#[test]
fn rug_to_f32_success() {
let value = ::rug::Float::with_val(80, 1.5);
let converted: f32 = value.try_convert(24).unwrap();
assert_eq!(converted, 1.5_f32);
}
#[test]
fn rug_to_f64_invalid_output_precision() {
let value = ::rug::Float::with_val(80, 1.5);
let result = value.try_convert::<f64>(24);
assert!(matches!(
result,
Err(RawRealConversionError::InvalidOutputPrecision { .. })
));
}
#[test]
fn rug_to_f32_non_representable_exactly() {
let value = ::rug::Float::with_val(200, 0.1);
let result = value.try_convert::<f32>(24);
assert!(matches!(
result,
Err(RawRealConversionError::NonRepresentableExactly { .. })
));
}
#[test]
fn f64_to_rug_success() {
let value = 1.5_f64;
let converted: ::rug::Float = value.try_convert(80).unwrap();
assert_eq!(converted.to_f64(), 1.5_f64);
assert_eq!(converted.precision_bits(), 80);
}
#[test]
fn f32_to_rug_success() {
let value = 1.5_f32;
let converted: ::rug::Float = value.try_convert(80).unwrap();
assert_eq!(converted.to_f32(), 1.5_f32);
assert_eq!(converted.precision_bits(), 80);
}
#[test]
fn f64_to_rug_non_representable_exactly_with_low_precision() {
let value = std::f64::consts::PI;
let result = value.try_convert::<::rug::Float>(10);
assert!(matches!(
result,
Err(RawRealConversionError::NonRepresentableExactly { .. })
));
}
#[test]
fn rug_to_rug_same_precision_success() {
let value = ::rug::Float::with_val(80, 1.5);
let converted: ::rug::Float = value.try_convert(80).unwrap();
assert_eq!(converted.precision_bits(), 80);
assert_eq!(converted.to_f64(), 1.5_f64);
}
#[test]
fn rug_to_rug_higher_precision_success() {
let value = ::rug::Float::with_val(53, 1.5);
let converted: ::rug::Float = value.try_convert(200).unwrap();
assert_eq!(converted.precision_bits(), 200);
assert_eq!(converted.to_f64(), 1.5_f64);
}
#[test]
fn rug_to_rug_non_representable_exactly_with_low_precision() {
let value = ::rug::Float::with_val(200, std::f64::consts::PI);
let result = value.try_convert::<::rug::Float>(10);
assert!(matches!(
result,
Err(RawRealConversionError::NonRepresentableExactly { .. })
));
}
}
}