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, 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
530/// Conversion contract from one raw real type to another.
531///
532/// This trait is the low-level engine behind [`RawRealTrait::try_convert`].
533/// Implementors define how to convert from `RawRealIn` into `Self`, including
534/// precision handling and exact representability guarantees.
535///
536/// # Design
537///
538/// - `RawRealIn` is the input raw real type.
539/// - `Self` is the output raw real type.
540/// - `precision_out` is always interpreted by the output type (`Self`).
541///
542/// # Error Semantics
543///
544/// Implementations should map failures to [`RawRealConversionError`]:
545/// - input validation failures -> `InputValidation`
546/// - output validation failures -> `OutputValidation`
547/// - invalid requested precision for fixed-precision targets -> `InvalidOutputPrecision`
548/// - exactness loss after conversion -> `NonRepresentableExactly`
549pub trait TryConvertFromRawReal<RawRealIn: RawRealTrait>: RawRealTrait {
550 /// Performs raw-real conversion with explicit output precision.
551 ///
552 /// # Parameters
553 ///
554 /// - `value_in`: input raw value to be converted.
555 /// - `precision_out`: requested output precision interpreted by `Self`.
556 ///
557 /// # Returns
558 ///
559 /// - `Ok(Self)` when conversion is valid and exact under the trait contract.
560 /// - `Err(RawRealConversionError<RawRealIn, Self>)` otherwise.
561 fn try_convert_from_raw_real(
562 value_in: RawRealIn,
563 precision_out: u32,
564 ) -> Result<Self, RawRealConversionError<RawRealIn, Self>>;
565}
566
567fn try_validity_precision_out<RealIn: RawRealTrait, RealOut: NativeReal>(
568 value_in: &RealIn,
569 precision_out: u32,
570) -> Result<(), RawRealConversionError<RealIn, RealOut>> {
571 if precision_out != RealOut::MANTISSA_DIGITS {
572 return Err(RawRealConversionError::InvalidOutputPrecision {
573 value_in: value_in.clone(),
574 valid_precision_out: RealOut::MANTISSA_DIGITS,
575 precision_out,
576 backtrace: capture_backtrace(),
577 });
578 }
579 Ok(())
580}
581
582#[inline(always)]
583fn try_validate_input_value<RealIn: RawRealTrait, RealOut: RawRealTrait>(
584 value_in: RealIn,
585) -> Result<RealIn, RawRealConversionError<RealIn, RealOut>> {
586 value_in
587 .validate_raw_scalar()
588 .map_err(|e| RawRealConversionError::InputValidation { source: e })
589}
590
591#[inline(always)]
592fn try_validate_output_value<RealIn: RawRealTrait, RealOut: RawRealTrait>(
593 value_out: RealOut,
594) -> Result<RealOut, RawRealConversionError<RealIn, RealOut>> {
595 value_out
596 .validate_raw_scalar()
597 .map_err(|e| RawRealConversionError::OutputValidation { source: e })
598}
599
600impl<T: NativeReal> TryConvertFromRawReal<T> for T {
601 #[inline(always)]
602 fn try_convert_from_raw_real(
603 value: T,
604 precision_out: u32,
605 ) -> Result<Self, RawRealConversionError<T, T>> {
606 try_validity_precision_out::<T, T>(&value, precision_out)?;
607
608 try_validate_input_value::<T, T>(value)
609 }
610}
611
612impl TryConvertFromRawReal<f64> for f32 {
613 fn try_convert_from_raw_real(
614 value_in: f64,
615 precision_out: u32,
616 ) -> Result<Self, RawRealConversionError<f64, f32>> {
617 try_validity_precision_out::<f64, f32>(&value_in, precision_out)?;
618
619 let value_in = try_validate_input_value::<f64, f32>(value_in)?;
620
621 // convert to f32 and check if f32 can represent the value exactly
622 let value_out = try_validate_output_value::<f64, f32>(value_in as f32)?;
623
624 // Check if the conversion is exact by comparing the original f64 value with the f32 value converted back to f64
625 if (value_out as f64) != value_in {
626 let precision_out = value_out.precision_bits();
627
628 return Err(RawRealConversionError::NonRepresentableExactly {
629 value_in,
630 value_out,
631 precision_out,
632 backtrace: capture_backtrace(),
633 });
634 }
635
636 Ok(value_out)
637 }
638}
639
640impl TryConvertFromRawReal<f32> for f64 {
641 fn try_convert_from_raw_real(
642 value_in: f32,
643 precision_out: u32,
644 ) -> Result<Self, RawRealConversionError<f32, f64>> {
645 try_validity_precision_out::<f32, f64>(&value_in, precision_out)?;
646
647 let value_in = try_validate_input_value::<f32, f64>(value_in)?;
648
649 // convert f32 to f64 (this is always exact)
650 Ok(value_in as f64)
651 }
652}
653
654#[duplicate_item(
655 RawRealOut func_rug_to_native;
656 [f64] [to_f64()];
657 [f32] [to_f32()];
658)]
659#[cfg(feature = "rug")]
660impl TryConvertFromRawReal<rug::Float> for RawRealOut {
661 fn try_convert_from_raw_real(
662 value_in: rug::Float,
663 precision_out: u32,
664 ) -> Result<Self, RawRealConversionError<rug::Float, Self>> {
665 try_validity_precision_out::<rug::Float, RawRealOut>(&value_in, precision_out)?;
666
667 let value_in = try_validate_input_value::<rug::Float, RawRealOut>(value_in)?;
668
669 // convert to RawRealOut and check if RawRealOut can represent the value exactly
670 let value_out =
671 try_validate_output_value::<rug::Float, RawRealOut>(value_in.func_rug_to_native)?;
672
673 // Check if the conversion is exact by comparing the original rug::Float value with the RawRealOut value converted back to rug::Float
674 let precision_in = value_in.precision_bits();
675 let value_roundtrip = rug::Float::with_val(precision_in, value_out);
676 if value_roundtrip != value_in {
677 let precision_out = value_out.precision_bits();
678
679 return Err(RawRealConversionError::NonRepresentableExactly {
680 value_in,
681 value_out,
682 precision_out,
683 backtrace: capture_backtrace(),
684 });
685 }
686
687 Ok(value_out)
688 }
689}
690
691#[cfg(feature = "rug")]
692impl TryConvertFromRawReal<rug::Float> for rug::Float {
693 fn try_convert_from_raw_real(
694 value_in: rug::Float,
695 precision_out: u32,
696 ) -> Result<Self, RawRealConversionError<rug::Float, Self>> {
697 let value_in = try_validate_input_value::<rug::Float, rug::Float>(value_in)?;
698
699 let precision_in = value_in.precision_bits();
700 if precision_out == precision_in {
701 Ok(value_in)
702 } else {
703 // Convert to rug::Float at requested precision and validate output.
704 let value_out = try_validate_output_value::<rug::Float, rug::Float>(
705 rug::Float::with_val(precision_out, &value_in),
706 )?;
707
708 // Ensure exact representability at requested precision.
709 let value_roundtrip = rug::Float::with_val(precision_in, &value_out);
710 if value_roundtrip != value_in {
711 return Err(RawRealConversionError::NonRepresentableExactly {
712 value_in,
713 value_out,
714 precision_out,
715 backtrace: capture_backtrace(),
716 });
717 }
718
719 Ok(value_out)
720 }
721 }
722}
723
724#[duplicate_item(
725 RawRealIn func_rug_to_native;
726 [f64] [to_f64()];
727 [f32] [to_f32()];
728)]
729#[cfg(feature = "rug")]
730impl TryConvertFromRawReal<RawRealIn> for rug::Float {
731 fn try_convert_from_raw_real(
732 value_in: RawRealIn,
733 precision_out: u32,
734 ) -> Result<Self, RawRealConversionError<RawRealIn, Self>> {
735 let value_in = try_validate_input_value::<RawRealIn, rug::Float>(value_in)?;
736
737 // convert to rug::Float and check if can represent the input value exactly
738 let value_out = try_validate_output_value::<RawRealIn, rug::Float>(rug::Float::with_val(
739 precision_out,
740 value_in,
741 ))?;
742
743 let value_roundtrip = value_out.func_rug_to_native;
744 if value_roundtrip != value_in {
745 return Err(RawRealConversionError::NonRepresentableExactly {
746 value_in,
747 value_out,
748 precision_out,
749 backtrace: capture_backtrace(),
750 });
751 }
752
753 Ok(value_out)
754 }
755}
756
757// =============================================================================
758// Complex Trait
759// =============================================================================
760
761/// A trait for raw complex scalar types, extending [`RawScalarTrait`].
762///
763/// This trait is implemented by the underlying complex number types within a kernel,
764/// such as [`num::Complex<f64>`]. It builds upon [`RawScalarTrait`] by adding operations
765/// that are unique to complex numbers.
766///
767/// # Trait Bounds
768///
769/// - `RawScalarTrait<...>`: Ensures the type is a raw scalar and fixes its validation
770/// error type to [`ErrorsValidationRawComplex`], which in turn wraps the validation
771/// error of the associated real type. This establishes a consistent error structure.
772/// - [`Conjugate`]: Requires that the complex number can be conjugated.
773///
774/// # Associated Types
775///
776/// - `type RawReal: RawRealTrait<RawComplex = Self>`: This is a critical part of the
777/// design. It links the complex type to its underlying real component type (e.g., [`f64`]
778/// for [`num::Complex<f64>`]). This associated real type must itself implement [`RawRealTrait`],
779/// creating a two-way link between the real and complex raw types.
780///
781/// # Methods
782///
783/// This trait provides methods for accessing components and performing complex-specific
784/// mathematical operations without validation checks.
785pub trait RawComplexTrait:
786 RawScalarTrait<
787 ValidationError = ErrorsValidationRawComplex<ErrorsValidationRawReal<Self::RawReal>>,
788 > + Conjugate
789 + for<'a> Mul<&'a Self::RawReal, Output = Self>
790 + for<'a> MulAssign<&'a Self::RawReal>
791{
792 /// The associated real type that makes up the components of this complex type.
793 type RawReal: RawRealTrait<RawComplex = Self>;
794
795 /// Creates a new complex number from real and imaginary parts without validation.
796 fn new_unchecked_raw_complex(real: Self::RawReal, imag: Self::RawReal) -> Self;
797
798 /// Computes the absolute value (modulus or norm) of `self` without validation.
799 ///
800 /// **Contract:** The caller must ensure the input is valid for this operation.
801 fn unchecked_abs(self) -> Self::RawReal;
802
803 /// Returns a reference to the real part of the complex number.
804 fn raw_real_part(&self) -> &Self::RawReal;
805
806 /// Returns a reference to the imaginary part of the complex number.
807 fn raw_imag_part(&self) -> &Self::RawReal;
808
809 /// Returns a mutable reference to the real part of the complex number.
810 fn mut_raw_real_part(&mut self) -> &mut Self::RawReal;
811
812 /// Returns a mutable reference to the imaginary part of the complex number.
813 fn mut_raw_imag_part(&mut self) -> &mut Self::RawReal;
814
815 /// Computes the argument (or phase) of `self` in radians, without validation.
816 ///
817 /// **Contract:** The caller must ensure that `self` is not zero.
818 fn unchecked_arg(self) -> Self::RawReal;
819
820 /// Raises `self` to the power of a real `exponent` without validation.
821 ///
822 /// **Contract:** The caller must ensure the inputs are valid for the power function.
823 fn unchecked_pow_exponent_real(self, exponent: &Self::RawReal) -> Self;
824}
825
826#[cfg(test)]
827mod tests_try_convert_from_raw_real {
828 use super::*;
829 use crate::core::errors::RawRealConversionError;
830
831 mod native {
832 use super::*;
833
834 #[test]
835 fn identity_f32_success() {
836 let value = 1.5_f32;
837 let converted: f32 = value.try_convert(24).unwrap();
838 assert_eq!(converted, value);
839 }
840
841 #[test]
842 fn identity_f32_invalid_output_precision() {
843 let value = 1.5_f32;
844 let result = value.try_convert::<f32>(53);
845 assert!(matches!(
846 result,
847 Err(RawRealConversionError::InvalidOutputPrecision { .. })
848 ));
849 }
850
851 #[test]
852 fn identity_f64_success() {
853 let value = 1.5_f64;
854 let converted: f64 = value.try_convert(53).unwrap();
855 assert_eq!(converted, value);
856 }
857
858 #[test]
859 fn identity_f64_invalid_output_precision() {
860 let value = 1.5_f64;
861 let result = value.try_convert::<f64>(24);
862 assert!(matches!(
863 result,
864 Err(RawRealConversionError::InvalidOutputPrecision { .. })
865 ));
866 }
867
868 #[test]
869 fn f64_to_f32_success() {
870 let value = 1.5_f64;
871 let converted: f32 = value.try_convert(24).unwrap();
872 assert_eq!(converted, 1.5_f32);
873 }
874
875 #[test]
876 fn f64_to_f32_non_representable_exactly() {
877 let value = 0.1_f64;
878 let result = value.try_convert::<f32>(24);
879 assert!(matches!(
880 result,
881 Err(RawRealConversionError::NonRepresentableExactly { .. })
882 ));
883 }
884
885 #[test]
886 fn f64_to_f32_invalid_output_precision() {
887 let value = 1.5_f64;
888 let result = value.try_convert::<f32>(53);
889 assert!(matches!(
890 result,
891 Err(RawRealConversionError::InvalidOutputPrecision { .. })
892 ));
893 }
894
895 #[test]
896 fn f32_to_f64_success() {
897 let value = 1.5_f32;
898 let converted: f64 = value.try_convert(53).unwrap();
899 assert_eq!(converted, 1.5_f64);
900 }
901
902 #[test]
903 fn f32_to_f64_invalid_output_precision() {
904 let value = 1.5_f32;
905 let result = value.try_convert::<f64>(24);
906 assert!(matches!(
907 result,
908 Err(RawRealConversionError::InvalidOutputPrecision { .. })
909 ));
910 }
911 }
912
913 #[cfg(feature = "rug")]
914 mod rug {
915 use super::*;
916
917 #[test]
918 fn rug_to_f64_success() {
919 let value = ::rug::Float::with_val(80, 1.5);
920 let converted: f64 = value.try_convert(53).unwrap();
921 assert_eq!(converted, 1.5_f64);
922 }
923
924 #[test]
925 fn rug_to_f32_success() {
926 let value = ::rug::Float::with_val(80, 1.5);
927 let converted: f32 = value.try_convert(24).unwrap();
928 assert_eq!(converted, 1.5_f32);
929 }
930
931 #[test]
932 fn rug_to_f64_invalid_output_precision() {
933 let value = ::rug::Float::with_val(80, 1.5);
934 let result = value.try_convert::<f64>(24);
935 assert!(matches!(
936 result,
937 Err(RawRealConversionError::InvalidOutputPrecision { .. })
938 ));
939 }
940
941 #[test]
942 fn rug_to_f32_non_representable_exactly() {
943 let value = ::rug::Float::with_val(200, 0.1);
944 let result = value.try_convert::<f32>(24);
945 assert!(matches!(
946 result,
947 Err(RawRealConversionError::NonRepresentableExactly { .. })
948 ));
949 }
950
951 #[test]
952 fn f64_to_rug_success() {
953 let value = 1.5_f64;
954 let converted: ::rug::Float = value.try_convert(80).unwrap();
955 assert_eq!(converted.to_f64(), 1.5_f64);
956 assert_eq!(converted.precision_bits(), 80);
957 }
958
959 #[test]
960 fn f32_to_rug_success() {
961 let value = 1.5_f32;
962 let converted: ::rug::Float = value.try_convert(80).unwrap();
963 assert_eq!(converted.to_f32(), 1.5_f32);
964 assert_eq!(converted.precision_bits(), 80);
965 }
966
967 #[test]
968 fn f64_to_rug_non_representable_exactly_with_low_precision() {
969 let value = std::f64::consts::PI;
970 let result = value.try_convert::<::rug::Float>(10);
971 assert!(matches!(
972 result,
973 Err(RawRealConversionError::NonRepresentableExactly { .. })
974 ));
975 }
976
977 #[test]
978 fn rug_to_rug_same_precision_success() {
979 let value = ::rug::Float::with_val(80, 1.5);
980 let converted: ::rug::Float = value.try_convert(80).unwrap();
981 assert_eq!(converted.precision_bits(), 80);
982 assert_eq!(converted.to_f64(), 1.5_f64);
983 }
984
985 #[test]
986 fn rug_to_rug_higher_precision_success() {
987 let value = ::rug::Float::with_val(53, 1.5);
988 let converted: ::rug::Float = value.try_convert(200).unwrap();
989 assert_eq!(converted.precision_bits(), 200);
990 assert_eq!(converted.to_f64(), 1.5_f64);
991 }
992
993 #[test]
994 fn rug_to_rug_non_representable_exactly_with_low_precision() {
995 let value = ::rug::Float::with_val(200, std::f64::consts::PI);
996 let result = value.try_convert::<::rug::Float>(10);
997 assert!(matches!(
998 result,
999 Err(RawRealConversionError::NonRepresentableExactly { .. })
1000 ));
1001 }
1002 }
1003}