Skip to main content

num_valid/core/traits/
raw.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! Raw trait definitions for unchecked scalar operations.
4//!
5//! This module defines the fundamental traits for raw number types (like `f64` or `rug::Float`).
6//! These traits provide `unchecked_*` methods that assume inputs are valid and do not perform
7//! any validation.
8//!
9//! # Trait Hierarchy
10//!
11//! ```text
12//! RawScalarTrigonometric ─┐
13//! RawScalarHyperbolic ────┼──> RawScalarTrait ──> RawRealTrait
14//! RawScalarPow ───────────┘                   └──> RawComplexTrait
15//! ```
16
17use crate::{
18    Arithmetic, Conjugate, IntegerScalar, NeumaierAddable, ScalarCore,
19    core::{
20        errors::{
21            ErrorsRawRealToInteger, ErrorsValidationRawComplex, ErrorsValidationRawReal,
22            RawRealConversionError, capture_backtrace,
23        },
24        traits::validation::{FpChecks, sealed::NativeReal},
25    },
26    functions::{Rounding, Sign},
27    kernels::PrecisionBits,
28};
29
30#[cfg(feature = "rug")]
31use duplicate::duplicate_item;
32
33use std::{
34    cmp::Ordering,
35    hash::Hasher,
36    num::FpCategory,
37    ops::{Mul, MulAssign},
38};
39//use try_create::IntoInner;
40
41// =============================================================================
42// Trigonometric Operations
43// =============================================================================
44
45/// Trait for unchecked trigonometric operations.
46///
47/// Provides sine, cosine, tangent and their inverse functions without validation.
48pub trait RawScalarTrigonometric {
49    /// Computes the sine without validation.
50    fn unchecked_sin(self) -> Self;
51
52    /// Computes the arcsine without validation.
53    fn unchecked_asin(self) -> Self;
54
55    /// Computes the cosine without validation.
56    fn unchecked_cos(self) -> Self;
57
58    /// Computes the arccosine without validation.
59    fn unchecked_acos(self) -> Self;
60
61    /// Computes the tangent without validation.
62    fn unchecked_tan(self) -> Self;
63
64    /// Computes the arctangent without validation.
65    fn unchecked_atan(self) -> Self;
66}
67
68// =============================================================================
69// Hyperbolic Operations
70// =============================================================================
71
72/// Trait for unchecked hyperbolic operations.
73///
74/// Provides hyperbolic sine, cosine, tangent and their inverse functions without validation.
75pub trait RawScalarHyperbolic {
76    /// Computes the hyperbolic sine without validation.
77    fn unchecked_sinh(self) -> Self;
78
79    /// Computes the inverse hyperbolic sine without validation.
80    fn unchecked_asinh(self) -> Self;
81
82    /// Computes the hyperbolic cosine without validation.
83    fn unchecked_cosh(self) -> Self;
84
85    /// Computes the inverse hyperbolic cosine without validation.
86    fn unchecked_acosh(self) -> Self;
87
88    /// Computes the hyperbolic tangent without validation.
89    fn unchecked_tanh(self) -> Self;
90
91    /// Computes the inverse hyperbolic tangent without validation.
92    fn unchecked_atanh(self) -> Self;
93}
94
95// =============================================================================
96// Power Operations
97// =============================================================================
98
99/// Trait for unchecked power operations with integer exponents.
100///
101/// Provides power functions for all standard integer types without validation.
102pub trait RawScalarPow {
103    /// Raises `self` to the power of an `i8` exponent without validation.
104    ///
105    /// **Contract:** The caller must ensure the inputs are valid.
106    fn unchecked_pow_exponent_i8(self, exponent: &i8) -> Self;
107
108    /// Raises `self` to the power of an `i16` exponent without validation.
109    ///
110    /// **Contract:** The caller must ensure the inputs are valid.
111    fn unchecked_pow_exponent_i16(self, exponent: &i16) -> Self;
112
113    /// Raises `self` to the power of an `i32` exponent without validation.
114    ///
115    /// **Contract:** The caller must ensure the inputs are valid.
116    fn unchecked_pow_exponent_i32(self, exponent: &i32) -> Self;
117
118    /// Raises `self` to the power of an `i64` exponent without validation.
119    ///
120    /// **Contract:** The caller must ensure the inputs are valid. For integer powers, this
121    /// includes ensuring the `exponent` is within the representable range of the underlying
122    /// power function (e.g., `i32` for `f64::powi`).
123    fn unchecked_pow_exponent_i64(self, exponent: &i64) -> Self;
124
125    /// Raises `self` to the power of an `i128` exponent without validation.
126    ///
127    /// **Contract:** The caller must ensure the inputs are valid. For integer powers, this
128    /// includes ensuring the `exponent` is within the representable range of the underlying
129    /// power function (e.g., `i32` for `f64::powi`).
130    fn unchecked_pow_exponent_i128(self, exponent: &i128) -> Self;
131
132    /// Raises `self` to the power of an `isize` exponent without validation.
133    ///
134    /// **Contract:** The caller must ensure the inputs are valid. For integer powers, this
135    /// includes ensuring the `exponent` is within the representable range of the underlying
136    /// power function (e.g., `i32` for `f64::powi`).
137    fn unchecked_pow_exponent_isize(self, exponent: &isize) -> Self;
138
139    /// Raises `self` to the power of a `u8` exponent without validation.
140    ///
141    /// **Contract:** The caller must ensure the inputs are valid.
142    fn unchecked_pow_exponent_u8(self, exponent: &u8) -> Self;
143
144    /// Raises `self` to the power of a `u16` exponent without validation.
145    ///
146    /// **Contract:** The caller must ensure the inputs are valid.
147    fn unchecked_pow_exponent_u16(self, exponent: &u16) -> Self;
148
149    /// Raises `self` to the power of a `u32` exponent without validation.
150    ///
151    /// **Contract:** The caller must ensure the inputs are valid. For integer powers, this
152    /// includes ensuring the `exponent` is within the representable range of the underlying
153    /// power function (e.g., `i32` for `f64::powi`).
154    fn unchecked_pow_exponent_u32(self, exponent: &u32) -> Self;
155
156    /// Raises `self` to the power of a `u64` exponent without validation.
157    ///
158    /// **Contract:** The caller must ensure the inputs are valid. For integer powers, this
159    /// includes ensuring the `exponent` is within the representable range of the underlying
160    /// power function (e.g., `i32` for `f64::powi`).
161    fn unchecked_pow_exponent_u64(self, exponent: &u64) -> Self;
162
163    /// Raises `self` to the power of a `u128` exponent without validation.
164    ///
165    /// **Contract:** The caller must ensure the inputs are valid. For integer powers, this
166    /// includes ensuring the `exponent` is within the representable range of the underlying
167    /// power function (e.g., `i32` for `f64::powi`).
168    fn unchecked_pow_exponent_u128(self, exponent: &u128) -> Self;
169
170    /// Raises `self` to the power of a `usize` exponent without validation.
171    ///
172    /// **Contract:** The caller must ensure the inputs are valid. For integer powers, this
173    /// includes ensuring the `exponent` is within the representable range of the underlying
174    /// power function (e.g., `i32` for `f64::powi`).
175    fn unchecked_pow_exponent_usize(self, exponent: &usize) -> Self;
176}
177
178pub trait RawScalarValidate: Sized {
179    type ValidationError: std::error::Error;
180
181    #[inline(always)]
182    fn validate_raw_scalar(self) -> Result<Self, Self::ValidationError> {
183        self.validate_raw_scalar_ref().map(|_| self)
184    }
185
186    #[inline(always)]
187    fn validate_in_debug_raw_scalar(self) -> Result<Self, Self::ValidationError> {
188        self.validate_in_debug_raw_scalar_ref().map(|_| self)
189    }
190
191    #[inline(always)]
192    fn validate_in_debug_raw_scalar_ref(&self) -> Result<(), Self::ValidationError> {
193        #[cfg(debug_assertions)]
194        {
195            self.validate_raw_scalar_ref()
196        }
197        #[cfg(not(debug_assertions))]
198        {
199            Ok(())
200        }
201    }
202
203    fn validate_raw_scalar_ref(&self) -> Result<(), Self::ValidationError>;
204}
205
206// =============================================================================
207// Core Scalar Trait
208// =============================================================================
209
210/// A baseline trait for raw scalar types, defining core operations and properties.
211///
212/// This trait must be implemented by the underlying number types used in a kernel,
213/// such as like [`f64`] or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
214/// It provides a standard interface for arithmetic
215/// and a suite of `unchecked_*` methods for mathematical functions.
216///
217/// ## Hashing Support
218///
219/// All raw scalar types (both real and complex) must implement [`compute_hash_raw_scalar()`](Self::compute_hash_raw_scalar),
220/// which provides a consistent hashing mechanism. The hash implementation must ensure:
221///
222/// - **Mathematical Equality**: Values that are mathematically equal produce identical hashes
223/// - **Signed Zero Handling**: Both `+0.0` and `-0.0` hash to the same value
224/// - **Finite Values Only**: The hash is only well-defined for finite values (not NaN or infinity)
225/// - **Consistency**: The same value always produces the same hash across multiple calls
226///
227/// For complex numbers, the hash is computed by sequentially hashing the real and imaginary
228/// parts, ensuring that different complex numbers produce different hashes while maintaining
229/// the equality invariant.
230///
231/// This enables validated wrapper types to implement [`Hash`](std::hash::Hash) when their validation
232/// policies guarantee finite values (via [`crate::core::traits::validation::GuaranteesFiniteRealValues`]),
233/// allowing them to be used as keys in [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
234///
235/// # Safety and Contracts
236///
237/// The `unchecked_*` methods are designed for performance and assume that the caller
238/// has already validated the inputs. Calling them with invalid data (e.g., `unchecked_sqrt`
239/// on a negative real number) may lead to panics, incorrect results, or undefined behavior,
240/// depending on the underlying type's implementation.
241pub trait RawScalarTrait:
242    ScalarCore
243//    + IntoInner<InnerType = Self>
244    + Arithmetic
245    + NeumaierAddable
246    + FpChecks
247    + RawScalarTrigonometric
248    + RawScalarHyperbolic
249    + RawScalarPow
250    + RawScalarValidate
251{
252    /// Returns `true` if `self` is zero.
253    fn is_zero(&self) -> bool;
254
255    /// Returns zero with the given precision.
256    fn raw_zero(precision: u32) -> Self;
257
258    /// Returns one with the given precision.
259    fn raw_one(precision: u32) -> Self;
260
261    /// Returns negative one with the given precision.
262    #[inline(always)]
263    fn raw_negative_one(precision: u32) -> Self {
264        Self::raw_one(precision).neg()
265    }
266
267    /// Computes the reciprocal (1/x) without validation.
268    ///
269    /// **Contract:** The caller must ensure `self` is not zero.
270    fn unchecked_reciprocal(self) -> Self;
271
272    /// Computes the exponential (e^x) without validation.
273    ///
274    /// **Contract:** The caller must ensure the input is valid.
275    fn unchecked_exp(self) -> Self;
276
277    /// Computes the square root without validation.
278    ///
279    /// **Contract:** For real numbers, the caller must ensure `self >= 0`.
280    fn unchecked_sqrt(self) -> Self;
281
282    /// Computes the natural logarithm without validation.
283    fn unchecked_ln(self) -> Self;
284
285    /// Computes the base-2 logarithm without validation.
286    fn unchecked_log2(self) -> Self;
287
288    /// Computes the base-10 logarithm without validation.
289    fn unchecked_log10(self) -> Self;
290
291    /// Multiplies and adds in one fused operation, rounding to the nearest with only one rounding error.
292    ///
293    /// `a.mul_add(b, c)` produces a result like `a * &b + &c`.
294    ///
295    /// **Contract:** The caller must ensure all inputs are valid.
296    fn unchecked_mul_add(self, b: &Self, c: &Self) -> Self;
297
298    /// Computes a hash value for this scalar (real or complex).
299    ///
300    /// This method must ensure that mathematically equal values produce
301    /// the same hash, even across different representations (e.g., +0.0 and -0.0).
302    ///
303    /// For complex numbers, implementations should hash the real and imaginary
304    /// parts sequentially to ensure distinct complex numbers have different hashes.
305    ///
306    /// # Implementation Notes
307    ///
308    /// - For real numbers: Hash the value directly, normalizing signed zeros
309    /// - For complex numbers: Call `compute_hash_raw_scalar()` on real part, then imaginary part
310    ///
311    /// # Debug Assertions
312    ///
313    /// Implementations should include a debug assertion that the value is finite,
314    /// as hashing non-finite values may lead to inconsistent results.
315    fn compute_hash_raw_scalar<H: Hasher>(&self, state: &mut H);
316}
317
318// =============================================================================
319// Real Trait
320// =============================================================================
321
322/// A trait for raw real scalar types, extending [`RawScalarTrait`] with real-specific operations.
323///
324/// This trait defines the fundamental operations for real number types without
325/// validation or safety checks. Implementations should assume inputs are valid
326/// and focus on computational efficiency.
327///
328/// This trait is implemented by the underlying real number types within a kernel, such as [`f64`]
329/// or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
330/// It builds upon [`RawScalarTrait`] by adding operations that are unique
331/// to real numbers, like `atan2`, and by enforcing an ordering relationship.
332///
333/// ## Hashing Support
334///
335/// Types implementing this trait must provide a `compute_hash` method that:
336/// - Produces consistent hash values for mathematically equal numbers
337/// - Handles floating-point edge cases (like signed zeros) correctly  
338/// - Maintains the contract that `a == b` implies `hash(a) == hash(b)`
339///
340/// This enables validated wrapper types to implement [`Hash`](std::hash::Hash) when appropriate
341/// validation policies guarantee finite values.
342///
343/// # Trait Bounds
344///
345/// - `RawScalarTrait<ValidationError = ErrorsValidationRawReal<Self>>`: Ensures that the type
346///   is a raw scalar and that its validation error type is the standard one for real numbers.
347/// - [`PartialOrd`]: Requires that the type can be partially ordered, which is fundamental for
348///   real numbers.
349///
350/// # Associated Types
351///
352/// - `type RawComplex: RawComplexTrait<RawReal = Self>`: This links the raw real type to its
353///   corresponding complex number representation. For example, for [`f64`], this would be
354///   [`num::Complex<f64>`]. This association is vital for operations that can transition
355///   from the real to the complex domain.
356pub trait RawRealTrait:
357    RawScalarTrait<ValidationError = ErrorsValidationRawReal<Self>>
358    + PartialOrd
359    + Sign
360    + Rounding
361    + PrecisionBits
362{
363    /// The associated complex type that uses this type as its real part.
364    type RawComplex: RawComplexTrait<RawReal = Self>;
365
366    /// Computes the absolute value of `self` without validation.
367    ///
368    /// **Contract:** The caller must ensure that the input value has already been validated
369    /// and is suitable for the operation.
370    fn unchecked_abs(self) -> Self;
371
372    /// Computes the four-quadrant arctangent of `self` (`y`) and `denominator` (`x`) without validation.
373    ///
374    /// **Contract:** The caller must ensure that `self` and `denominator` are not both zero.
375    fn unchecked_atan2(self, denominator: &Self) -> Self;
376
377    /// Raises `self` to the power of a real `exponent` without validation.
378    ///
379    /// **Contract:** The caller must ensure the inputs are valid for the power function. For
380    /// example, if `self` is negative, the `exponent` should be an integer, but this function
381    /// does not enforce this.
382    fn unchecked_pow_exponent_real(self, exponent: &Self) -> Self;
383
384    /// Computes the Euclidean distance `sqrt(self² + other²)` without validation.
385    fn unchecked_hypot(self, other: &Self) -> Self;
386
387    /// Computes `ln(1 + self)` accurately for small values without validation.
388    fn unchecked_ln_1p(self) -> Self;
389
390    /// Computes `exp(self) - 1` accurately for small values without validation.
391    fn unchecked_exp_m1(self) -> Self;
392
393    /// Multiplies two pairs and adds them, rounding to the nearest.
394    /// `a.unchecked_mul_add_mul_mut(&b, &c, &d)` produces a result like `&a * &b + &c * &d`, but stores the result in `a` using its precision.
395    ///
396    /// **Contract:** The caller must ensure all inputs are valid.
397    fn unchecked_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self);
398
399    /// Multiplies two pairs and subtracts them, rounding to the nearest.
400    /// `a.unchecked_mul_sub_mul_mut(&b, &c, &d)` produces a result like `&a * &b - &c * &d`, but stores the result in `a` using its precision.
401    ///
402    /// **Contract:** The caller must ensure all inputs are valid.
403    fn unchecked_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self);
404
405    /// Performs a total ordering comparison.
406    fn raw_total_cmp(&self, other: &Self) -> Ordering;
407
408    /// Clamps the value within the specified bounds.
409    fn raw_clamp(self, min: &Self, max: &Self) -> Self;
410
411    /// Classifies the floating-point value.
412    fn raw_classify(&self) -> FpCategory;
413
414    /// Returns two with the given precision.
415    fn raw_two(precision: u32) -> Self;
416
417    /// Returns 0.5 with the given precision.
418    fn raw_one_div_2(precision: u32) -> Self;
419
420    /// Returns π with the given precision.
421    fn raw_pi(precision: u32) -> Self;
422
423    /// Returns 2π with the given precision.
424    fn raw_two_pi(precision: u32) -> Self;
425
426    /// Returns π/2 with the given precision.
427    fn raw_pi_div_2(precision: u32) -> Self;
428
429    /// Returns the maximum finite value with the given precision.
430    fn raw_max_finite(precision: u32) -> Self;
431
432    /// Returns the minimum finite value with the given precision.
433    fn raw_min_finite(precision: u32) -> Self;
434
435    /// Returns the machine epsilon with the given precision.
436    fn raw_epsilon(precision: u32) -> Self;
437
438    /// Returns ln(2) with the given precision.
439    fn raw_ln_2(precision: u32) -> Self;
440
441    /// Returns ln(10) with the given precision.
442    fn raw_ln_10(precision: u32) -> Self;
443
444    /// Returns log₁₀(2) with the given precision.
445    fn raw_log10_2(precision: u32) -> Self;
446
447    /// Returns log₂(10) with the given precision.
448    fn raw_log2_10(precision: u32) -> Self;
449
450    /// Returns log₂(e) with the given precision.
451    fn raw_log2_e(precision: u32) -> Self;
452
453    /// Returns log₁₀(e) with the given precision.
454    fn raw_log10_e(precision: u32) -> Self;
455
456    /// Returns e (Euler's number) with the given precision.
457    fn raw_e(precision: u32) -> Self;
458
459    /// Safely converts the truncated value to `usize`.
460    ///
461    /// Truncates toward zero and validates the result is a valid `usize`.
462    ///
463    /// # Returns
464    ///
465    /// - `Ok(usize)`: If truncated value is in `0..=usize::MAX`
466    /// - `Err(ErrorsRawRealToInteger)`: If value is not finite or out of range
467    ///   - `NotFinite`: Value is `NaN` or `±∞`
468    ///   - `OutOfRange`: Value is negative or > `usize::MAX`
469    ///
470    /// # Note
471    ///
472    /// The `NotAnInteger` error variant is not returned because truncation
473    /// explicitly handles the fractional part.
474    ///
475    /// # Examples
476    ///
477    /// ```
478    /// # use num_valid::core::traits::raw::RawRealTrait;
479    /// let value = 42.9_f64;
480    /// assert_eq!(value.truncate_to_usize().unwrap(), 42);
481    ///
482    /// let zero = 0.0_f64;
483    /// assert_eq!(zero.truncate_to_usize().unwrap(), 0);
484    /// ```
485    ///
486    /// ## Error cases
487    ///
488    /// ```
489    /// # use num_valid::core::traits::raw::RawRealTrait;
490    /// # use num_valid::core::errors::ErrorsRawRealToInteger;
491    /// // Negative value
492    /// let neg = -10.5_f64;
493    /// assert!(matches!(neg.truncate_to_usize(), Err(ErrorsRawRealToInteger::OutOfRange { .. })));
494    ///
495    /// // Too large for usize
496    /// let large = (usize::MAX as f64) + 100.0;
497    /// assert!(matches!(large.truncate_to_usize(), Err(ErrorsRawRealToInteger::OutOfRange { .. })));
498    ///
499    /// // Not finite
500    /// let nan = f64::NAN;
501    /// assert!(matches!(nan.truncate_to_usize(), Err(ErrorsRawRealToInteger::NotFinite { .. })));
502    /// ```
503    fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<Self, usize>>;
504
505    /// Converts `self` into another raw real type.
506    ///
507    /// The caller provides `precision_out`, which is interpreted by the target
508    /// type `RawRealOut`:
509    ///
510    /// - fixed-precision targets (`f32`, `f64`) require exact mantissa widths
511    ///   (24 and 53 respectively), otherwise
512    ///   [`RawRealConversionError::InvalidOutputPrecision`] is returned;
513    /// - variable-precision targets (e.g. `rug::Float`) use `precision_out` as
514    ///   requested output precision.
515    ///
516    /// Implementations enforce exact representability via roundtrip checks and
517    /// return [`RawRealConversionError::NonRepresentableExactly`] when needed.
518    #[inline(always)]
519    fn try_convert<RawRealOut>(
520        self,
521        precision_out: u32,
522    ) -> Result<RawRealOut, RawRealConversionError<Self, RawRealOut>>
523    where
524        RawRealOut: RawRealTrait + TryConvertFromRawReal<Self>,
525    {
526        RawRealOut::try_convert_from_raw_real(self, precision_out)
527    }
528
529    /// Multiplies `self` in place by an integer value, without validation.
530    ///
531    /// **Contract:** For fixed-precision targets (`f32`, `f64`), the caller must ensure
532    /// `integer` is exactly representable in `Self` (no rounding). For arbitrary-precision
533    /// targets (e.g., `rug::Float`), any `integer` is exactly representable, so no such
534    /// restriction applies.
535    fn unchecked_mul_assign_integer<I>(&mut self, integer: I)
536    where
537        I: IntegerScalar;
538
539    /// Divides `self` in place by an integer value, without validation.
540    ///
541    /// **Contract:** For fixed-precision targets (`f32`, `f64`), the caller must ensure
542    /// `integer` is exactly representable in `Self` (no rounding) and non-zero. For
543    /// arbitrary-precision targets (e.g., `rug::Float`), any `integer` is exactly
544    /// representable, so only the non-zero restriction applies.
545    fn unchecked_div_assign_integer<I>(&mut self, integer: I)
546    where
547        I: IntegerScalar;
548
549    /// Multiplies `self` by an integer value, without validation, returning the result.
550    ///
551    /// See [`unchecked_mul_assign_integer`](Self::unchecked_mul_assign_integer) for the contract.
552    #[inline(always)]
553    fn unchecked_mul_integer<I>(mut self, integer: I) -> Self
554    where
555        I: IntegerScalar,
556    {
557        self.unchecked_mul_assign_integer(integer);
558        self
559    }
560
561    /// Divides `self` by an integer value, without validation, returning the result.
562    ///
563    /// See [`unchecked_div_assign_integer`](Self::unchecked_div_assign_integer) for the contract.
564    #[inline(always)]
565    fn unchecked_div_integer<I>(mut self, integer: I) -> Self
566    where
567        I: IntegerScalar,
568    {
569        self.unchecked_div_assign_integer(integer);
570        self
571    }
572}
573
574/// Conversion contract from one raw real type to another.
575///
576/// This trait is the low-level engine behind [`RawRealTrait::try_convert`].
577/// Implementors define how to convert from `RawRealIn` into `Self`, including
578/// precision handling and exact representability guarantees.
579///
580/// # Design
581///
582/// - `RawRealIn` is the input raw real type.
583/// - `Self` is the output raw real type.
584/// - `precision_out` is always interpreted by the output type (`Self`).
585///
586/// # Error Semantics
587///
588/// Implementations should map failures to [`RawRealConversionError`]:
589/// - input validation failures -> `InputValidation`
590/// - output validation failures -> `OutputValidation`
591/// - invalid requested precision for fixed-precision targets -> `InvalidOutputPrecision`
592/// - exactness loss after conversion -> `NonRepresentableExactly`
593pub trait TryConvertFromRawReal<RawRealIn: RawRealTrait>: RawRealTrait {
594    /// Performs raw-real conversion with explicit output precision.
595    ///
596    /// # Parameters
597    ///
598    /// - `value_in`: input raw value to be converted.
599    /// - `precision_out`: requested output precision interpreted by `Self`.
600    ///
601    /// # Returns
602    ///
603    /// - `Ok(Self)` when conversion is valid and exact under the trait contract.
604    /// - `Err(RawRealConversionError<RawRealIn, Self>)` otherwise.
605    fn try_convert_from_raw_real(
606        value_in: RawRealIn,
607        precision_out: u32,
608    ) -> Result<Self, RawRealConversionError<RawRealIn, Self>>;
609}
610
611pub(crate) fn try_validity_precision_out<RealIn: RawRealTrait, RealOut: NativeReal>(
612    value_in: &RealIn,
613    precision_out: u32,
614) -> Result<(), RawRealConversionError<RealIn, RealOut>> {
615    if precision_out != RealOut::MANTISSA_DIGITS {
616        return Err(RawRealConversionError::InvalidOutputPrecision {
617            value_in: value_in.clone(),
618            valid_precision_out: RealOut::MANTISSA_DIGITS,
619            precision_out,
620            backtrace: capture_backtrace(),
621        });
622    }
623    Ok(())
624}
625
626#[inline(always)]
627fn try_validate_input_value<RealIn: RawRealTrait, RealOut: RawRealTrait>(
628    value_in: RealIn,
629) -> Result<RealIn, RawRealConversionError<RealIn, RealOut>> {
630    value_in
631        .validate_raw_scalar()
632        .map_err(|e| RawRealConversionError::InputValidation { source: e })
633}
634
635#[inline(always)]
636fn try_validate_output_value<RealIn: RawRealTrait, RealOut: RawRealTrait>(
637    value_out: RealOut,
638) -> Result<RealOut, RawRealConversionError<RealIn, RealOut>> {
639    value_out
640        .validate_raw_scalar()
641        .map_err(|e| RawRealConversionError::OutputValidation { source: e })
642}
643
644impl<T: NativeReal> TryConvertFromRawReal<T> for T {
645    #[inline(always)]
646    fn try_convert_from_raw_real(
647        value: T,
648        precision_out: u32,
649    ) -> Result<Self, RawRealConversionError<T, T>> {
650        try_validity_precision_out::<T, T>(&value, precision_out)?;
651
652        try_validate_input_value::<T, T>(value)
653    }
654}
655
656impl TryConvertFromRawReal<f64> for f32 {
657    fn try_convert_from_raw_real(
658        value_in: f64,
659        precision_out: u32,
660    ) -> Result<Self, RawRealConversionError<f64, f32>> {
661        try_validity_precision_out::<f64, f32>(&value_in, precision_out)?;
662
663        let value_in = try_validate_input_value::<f64, f32>(value_in)?;
664
665        // convert to f32 and check if f32 can represent the value exactly
666        let value_out = try_validate_output_value::<f64, f32>(value_in as f32)?;
667
668        // Check if the conversion is exact by comparing the original f64 value with the f32 value converted back to f64
669        if (value_out as f64) != value_in {
670            let precision_out = value_out.precision_bits();
671
672            return Err(RawRealConversionError::NonRepresentableExactly {
673                value_in,
674                value_out,
675                precision_out,
676                backtrace: capture_backtrace(),
677            });
678        }
679
680        Ok(value_out)
681    }
682}
683
684impl TryConvertFromRawReal<f32> for f64 {
685    fn try_convert_from_raw_real(
686        value_in: f32,
687        precision_out: u32,
688    ) -> Result<Self, RawRealConversionError<f32, f64>> {
689        try_validity_precision_out::<f32, f64>(&value_in, precision_out)?;
690
691        let value_in = try_validate_input_value::<f32, f64>(value_in)?;
692
693        // convert f32 to f64 (this is always exact)
694        Ok(value_in as f64)
695    }
696}
697
698#[duplicate_item(
699    RawRealOut func_rug_to_native;
700    [f64]      [to_f64()];
701    [f32]      [to_f32()];
702)]
703#[cfg(feature = "rug")]
704impl TryConvertFromRawReal<rug::Float> for RawRealOut {
705    fn try_convert_from_raw_real(
706        value_in: rug::Float,
707        precision_out: u32,
708    ) -> Result<Self, RawRealConversionError<rug::Float, Self>> {
709        try_validity_precision_out::<rug::Float, RawRealOut>(&value_in, precision_out)?;
710
711        let value_in = try_validate_input_value::<rug::Float, RawRealOut>(value_in)?;
712
713        // convert to RawRealOut and check if RawRealOut can represent the value exactly
714        let value_out =
715            try_validate_output_value::<rug::Float, RawRealOut>(value_in.func_rug_to_native)?;
716
717        // Check if the conversion is exact by comparing the original rug::Float value with the RawRealOut value converted back to rug::Float
718        let precision_in = value_in.precision_bits();
719        let value_roundtrip = rug::Float::with_val(precision_in, value_out);
720        if value_roundtrip != value_in {
721            let precision_out = value_out.precision_bits();
722
723            return Err(RawRealConversionError::NonRepresentableExactly {
724                value_in,
725                value_out,
726                precision_out,
727                backtrace: capture_backtrace(),
728            });
729        }
730
731        Ok(value_out)
732    }
733}
734
735#[cfg(feature = "rug")]
736impl TryConvertFromRawReal<rug::Float> for rug::Float {
737    fn try_convert_from_raw_real(
738        value_in: rug::Float,
739        precision_out: u32,
740    ) -> Result<Self, RawRealConversionError<rug::Float, Self>> {
741        let value_in = try_validate_input_value::<rug::Float, rug::Float>(value_in)?;
742
743        let precision_in = value_in.precision_bits();
744        if precision_out == precision_in {
745            Ok(value_in)
746        } else {
747            // Convert to rug::Float at requested precision and validate output.
748            let value_out = try_validate_output_value::<rug::Float, rug::Float>(
749                rug::Float::with_val(precision_out, &value_in),
750            )?;
751
752            // Ensure exact representability at requested precision.
753            let value_roundtrip = rug::Float::with_val(precision_in, &value_out);
754            if value_roundtrip != value_in {
755                return Err(RawRealConversionError::NonRepresentableExactly {
756                    value_in,
757                    value_out,
758                    precision_out,
759                    backtrace: capture_backtrace(),
760                });
761            }
762
763            Ok(value_out)
764        }
765    }
766}
767
768#[duplicate_item(
769    RawRealIn  func_rug_to_native;
770    [f64]      [to_f64()];
771    [f32]      [to_f32()];
772)]
773#[cfg(feature = "rug")]
774impl TryConvertFromRawReal<RawRealIn> for rug::Float {
775    fn try_convert_from_raw_real(
776        value_in: RawRealIn,
777        precision_out: u32,
778    ) -> Result<Self, RawRealConversionError<RawRealIn, Self>> {
779        let value_in = try_validate_input_value::<RawRealIn, rug::Float>(value_in)?;
780
781        // convert to rug::Float and check if can represent the input value exactly
782        let value_out = try_validate_output_value::<RawRealIn, rug::Float>(rug::Float::with_val(
783            precision_out,
784            value_in,
785        ))?;
786
787        let value_roundtrip = value_out.func_rug_to_native;
788        if value_roundtrip != value_in {
789            return Err(RawRealConversionError::NonRepresentableExactly {
790                value_in,
791                value_out,
792                precision_out,
793                backtrace: capture_backtrace(),
794            });
795        }
796
797        Ok(value_out)
798    }
799}
800
801// =============================================================================
802// Complex Trait
803// =============================================================================
804
805/// A trait for raw complex scalar types, extending [`RawScalarTrait`].
806///
807/// This trait is implemented by the underlying complex number types within a kernel,
808/// such as [`num::Complex<f64>`]. It builds upon [`RawScalarTrait`] by adding operations
809/// that are unique to complex numbers.
810///
811/// # Trait Bounds
812///
813/// - `RawScalarTrait<...>`: Ensures the type is a raw scalar and fixes its validation
814///   error type to [`ErrorsValidationRawComplex`], which in turn wraps the validation
815///   error of the associated real type. This establishes a consistent error structure.
816/// - [`Conjugate`]: Requires that the complex number can be conjugated.
817///
818/// # Associated Types
819///
820/// - `type RawReal: RawRealTrait<RawComplex = Self>`: This is a critical part of the
821///   design. It links the complex type to its underlying real component type (e.g., [`f64`]
822///   for [`num::Complex<f64>`]). This associated real type must itself implement [`RawRealTrait`],
823///   creating a two-way link between the real and complex raw types.
824///
825/// # Methods
826///
827/// This trait provides methods for accessing components and performing complex-specific
828/// mathematical operations without validation checks.
829pub trait RawComplexTrait:
830    RawScalarTrait<
831        ValidationError = ErrorsValidationRawComplex<ErrorsValidationRawReal<Self::RawReal>>,
832    > + Conjugate
833    + for<'a> Mul<&'a Self::RawReal, Output = Self>
834    + for<'a> MulAssign<&'a Self::RawReal>
835{
836    /// The associated real type that makes up the components of this complex type.
837    type RawReal: RawRealTrait<RawComplex = Self>;
838
839    /// Creates a new complex number from real and imaginary parts without validation.
840    fn new_unchecked_raw_complex(real: Self::RawReal, imag: Self::RawReal) -> Self;
841
842    /// Computes the absolute value (modulus or norm) of `self` without validation.
843    ///
844    /// **Contract:** The caller must ensure the input is valid for this operation.
845    fn unchecked_abs(self) -> Self::RawReal;
846
847    /// Returns a reference to the real part of the complex number.
848    fn raw_real_part(&self) -> &Self::RawReal;
849
850    /// Returns a reference to the imaginary part of the complex number.
851    fn raw_imag_part(&self) -> &Self::RawReal;
852
853    /// Returns a mutable reference to the real part of the complex number.
854    fn mut_raw_real_part(&mut self) -> &mut Self::RawReal;
855
856    /// Returns a mutable reference to the imaginary part of the complex number.
857    fn mut_raw_imag_part(&mut self) -> &mut Self::RawReal;
858
859    /// Computes the argument (or phase) of `self` in radians, without validation.
860    ///
861    /// **Contract:** The caller must ensure that `self` is not zero.
862    fn unchecked_arg(self) -> Self::RawReal;
863
864    /// Raises `self` to the power of a real `exponent` without validation.
865    ///
866    /// **Contract:** The caller must ensure the inputs are valid for the power function.
867    fn unchecked_pow_exponent_real(self, exponent: &Self::RawReal) -> Self;
868}
869
870#[cfg(test)]
871mod tests_try_convert_from_raw_real {
872    use super::*;
873    use crate::core::errors::RawRealConversionError;
874
875    mod native {
876        use super::*;
877
878        #[test]
879        fn identity_f32_success() {
880            let value = 1.5_f32;
881            let converted: f32 = value.try_convert(24).unwrap();
882            assert_eq!(converted, value);
883        }
884
885        #[test]
886        fn identity_f32_invalid_output_precision() {
887            let value = 1.5_f32;
888            let result = value.try_convert::<f32>(53);
889            assert!(matches!(
890                result,
891                Err(RawRealConversionError::InvalidOutputPrecision { .. })
892            ));
893        }
894
895        #[test]
896        fn identity_f64_success() {
897            let value = 1.5_f64;
898            let converted: f64 = value.try_convert(53).unwrap();
899            assert_eq!(converted, value);
900        }
901
902        #[test]
903        fn identity_f64_invalid_output_precision() {
904            let value = 1.5_f64;
905            let result = value.try_convert::<f64>(24);
906            assert!(matches!(
907                result,
908                Err(RawRealConversionError::InvalidOutputPrecision { .. })
909            ));
910        }
911
912        #[test]
913        fn f64_to_f32_success() {
914            let value = 1.5_f64;
915            let converted: f32 = value.try_convert(24).unwrap();
916            assert_eq!(converted, 1.5_f32);
917        }
918
919        #[test]
920        fn f64_to_f32_non_representable_exactly() {
921            let value = 0.1_f64;
922            let result = value.try_convert::<f32>(24);
923            assert!(matches!(
924                result,
925                Err(RawRealConversionError::NonRepresentableExactly { .. })
926            ));
927        }
928
929        #[test]
930        fn f64_to_f32_invalid_output_precision() {
931            let value = 1.5_f64;
932            let result = value.try_convert::<f32>(53);
933            assert!(matches!(
934                result,
935                Err(RawRealConversionError::InvalidOutputPrecision { .. })
936            ));
937        }
938
939        #[test]
940        fn f32_to_f64_success() {
941            let value = 1.5_f32;
942            let converted: f64 = value.try_convert(53).unwrap();
943            assert_eq!(converted, 1.5_f64);
944        }
945
946        #[test]
947        fn f32_to_f64_invalid_output_precision() {
948            let value = 1.5_f32;
949            let result = value.try_convert::<f64>(24);
950            assert!(matches!(
951                result,
952                Err(RawRealConversionError::InvalidOutputPrecision { .. })
953            ));
954        }
955    }
956
957    #[cfg(feature = "rug")]
958    mod rug {
959        use super::*;
960
961        #[test]
962        fn rug_to_f64_success() {
963            let value = ::rug::Float::with_val(80, 1.5);
964            let converted: f64 = value.try_convert(53).unwrap();
965            assert_eq!(converted, 1.5_f64);
966        }
967
968        #[test]
969        fn rug_to_f32_success() {
970            let value = ::rug::Float::with_val(80, 1.5);
971            let converted: f32 = value.try_convert(24).unwrap();
972            assert_eq!(converted, 1.5_f32);
973        }
974
975        #[test]
976        fn rug_to_f64_invalid_output_precision() {
977            let value = ::rug::Float::with_val(80, 1.5);
978            let result = value.try_convert::<f64>(24);
979            assert!(matches!(
980                result,
981                Err(RawRealConversionError::InvalidOutputPrecision { .. })
982            ));
983        }
984
985        #[test]
986        fn rug_to_f32_non_representable_exactly() {
987            let value = ::rug::Float::with_val(200, 0.1);
988            let result = value.try_convert::<f32>(24);
989            assert!(matches!(
990                result,
991                Err(RawRealConversionError::NonRepresentableExactly { .. })
992            ));
993        }
994
995        #[test]
996        fn f64_to_rug_success() {
997            let value = 1.5_f64;
998            let converted: ::rug::Float = value.try_convert(80).unwrap();
999            assert_eq!(converted.to_f64(), 1.5_f64);
1000            assert_eq!(converted.precision_bits(), 80);
1001        }
1002
1003        #[test]
1004        fn f32_to_rug_success() {
1005            let value = 1.5_f32;
1006            let converted: ::rug::Float = value.try_convert(80).unwrap();
1007            assert_eq!(converted.to_f32(), 1.5_f32);
1008            assert_eq!(converted.precision_bits(), 80);
1009        }
1010
1011        #[test]
1012        fn f64_to_rug_non_representable_exactly_with_low_precision() {
1013            let value = std::f64::consts::PI;
1014            let result = value.try_convert::<::rug::Float>(10);
1015            assert!(matches!(
1016                result,
1017                Err(RawRealConversionError::NonRepresentableExactly { .. })
1018            ));
1019        }
1020
1021        #[test]
1022        fn rug_to_rug_same_precision_success() {
1023            let value = ::rug::Float::with_val(80, 1.5);
1024            let converted: ::rug::Float = value.try_convert(80).unwrap();
1025            assert_eq!(converted.precision_bits(), 80);
1026            assert_eq!(converted.to_f64(), 1.5_f64);
1027        }
1028
1029        #[test]
1030        fn rug_to_rug_higher_precision_success() {
1031            let value = ::rug::Float::with_val(53, 1.5);
1032            let converted: ::rug::Float = value.try_convert(200).unwrap();
1033            assert_eq!(converted.precision_bits(), 200);
1034            assert_eq!(converted.to_f64(), 1.5_f64);
1035        }
1036
1037        #[test]
1038        fn rug_to_rug_non_representable_exactly_with_low_precision() {
1039            let value = ::rug::Float::with_val(200, std::f64::consts::PI);
1040            let result = value.try_convert::<::rug::Float>(10);
1041            assert!(matches!(
1042                result,
1043                Err(RawRealConversionError::NonRepresentableExactly { .. })
1044            ));
1045        }
1046    }
1047}