frame_suite/
fixedpoint.rs

1// SPDX-License-Identifier: MPL-2.0
2//
3// Part of Auguth Labs open-source softwares.
4// Built for the Substrate framework.
5//
6// This Source Code Form is subject to the terms of the Mozilla Public
7// License, v. 2.0. If a copy of the MPL was not distributed with this
8// file, You can obtain one at https://mozilla.org/MPL/2.0/.
9//
10// Copyright (c) 2026 Auguth Labs (OPC) Pvt Ltd, India
11
12// ===============================================================================
13// `````````````````````````````````` FIXED-POINT `````````````````````````````````
14// ===============================================================================
15 
16//! Deterministic, `no_std`-compatible mathematical primitives for Substrate's
17//! fixed-point numeric tower ([`FixedU64`], [`FixedU128`], [`FixedI64`], [`FixedI128`]).
18//!
19//! All arithmetic is implemented without floating-point instructions, making
20//! every operation fully deterministic and suitable for on-chain execution where
21//! bit-identical results across heterogeneous validator hardware are required.
22//!
23//! ## Type System
24//!
25//! Three layered abstractions bridge raw integers and fixed-point values:
26//!
27//! | Trait                | Role                                                        |
28//! |----------------------|-------------------------------------------------------------|
29//! | [`FixedForInteger`]  | Associates each primitive integer with its natural fixed-point counterpart |
30//! | [`IntegerToFixed`]   | Round-trip `to_fixed` / `from_fixed` with saturation at type boundaries   |
31//! | [`FixedSignedCast`]  | Lifts unsigned types into signed arithmetic space for operations that require negative intermediates, then projects the result back |
32//!
33//! ## Operations
34//!
35//! | Function        | Description                                              |
36//! |-----------------|----------------------------------------------------------|
37//! | `fixed_sqrt`    | Square root - real domain; returns `None` for negatives  |
38//! | `complex_sqrt`  | Square root - complex domain; imaginary output for `x<0` |
39//! | `fixed_exp`     | Natural exponential `e^x`                                 |
40//! | `fixed_ln`      | Natural logarithm `ln(x)`, defined for `x > 0`           |
41//! | `fixed_pow`     | General power `x^p` - integer and fractional exponents    |
42//!
43//! Operations are exposed through the [`FixedOp`] and [`FixedComplexOp`] trait
44//! facades, so generic code can be written against a single trait bound and
45//! work across all four fixed-point types without specialisation.
46//!
47//! ## Design Notes
48//!
49//! - **No panics.** All public entry-points return `Option<T>` so that undefined
50//!   inputs (negative logarithm, zero base with negative exponent, etc.) are
51//!   expressed as `None` rather than a runtime abort.
52//! - **Saturating internal arithmetic.** Intermediate overflow clamps to the
53//!   type's representable range rather than wrapping or panicking.
54//! - **Convergence guarantees.** Every iterative algorithm is hard-capped at
55//!   `MAX_ITERATIONS` and also checks for stagnation, so no function can loop
56//!   indefinitely regardless of input.
57//!
58//! ## Planned Extensions
59//!
60//! Trigonometric, hyperbolic, special (gamma, erf), and additional root / power
61//! functions are outlined in the `PLANNED EXTENSIONS` section at the bottom of
62//! this file. New operations should implement the corresponding method on
63//! [`FixedOp`] (or a new companion trait) and follow the same `Option`-returning,
64//! saturation-safe conventions established here.
65
66// ===============================================================================
67// ``````````````````````````````````` IMPORTS ```````````````````````````````````
68// ===============================================================================
69
70// --- Core ---
71use core::ops::Shr;
72use core::convert::TryInto;
73
74// --- Substrate crates ---
75use sp_arithmetic::{FixedI128, FixedI64, FixedU128, FixedU64};
76use sp_runtime::{
77    FixedPointNumber,
78    traits::Bounded
79};
80
81// ===============================================================================
82// ```````````````````````````` INTEGER-FIXED MAPPING ````````````````````````````
83// ===============================================================================
84
85/// Trait mapping **primitive integer types** to an appropriate **fixed-point type**.
86///
87/// This is useful in generic algorithms where a numeric type might need to be converted
88/// to a fixed-point representation for deterministic arithmetic, scaling, or computations.
89pub trait FixedForInteger {
90    /// The fixed-point type corresponding to the integer type.
91    ///
92    ///   - Small unsigned integers (u8, u16, u32) map to `FixedU64`
93    ///   - Large unsigned integers (u64, u128, usize) map to `FixedU128`
94    ///   - Signed integers follow a similar mapping with `FixedI64` or `FixedI128`.
95    type FixedPoint: FixedPointNumber;
96}
97
98/// Macro to conveniently implement [`FixedForInteger`] for multiple integer types at once.
99///
100macro_rules! int_best_fixed {
101    // Accepts pairs of integer type => fixed-point type
102    ($($t:ty => $fixed:ty),* $(,)?) => {
103        $(
104            // Implement the FixedForInteger trait for the integer type
105            impl FixedForInteger for $t {
106                // Associate the chosen fixed-point type with this integer
107                type FixedPoint = $fixed;
108            }
109        )*
110    };
111}
112
113// Implement [`FixedForInteger`] for all primitive integer types.
114//
115// Provides sensible defaults:
116// - **Unsigned small integers (u8, u16, u32)** -> `FixedU64`
117// - **Unsigned large integers (u64, u128, usize)** -> `FixedU128`
118// - **Signed small integers (i8, i16, i32)** -> `FixedI64`
119// - **Signed large integers (i64, i128, isize)** -> `FixedI128`
120//
121// This ensures consistent fixed-point conversions across different integer sizes,
122// particularly in algorithms involving weighting, or normalized calculations.
123int_best_fixed! {
124    u8   => FixedU64,
125    u16  => FixedU64,
126    u32  => FixedU64,
127    u64  => FixedU128,
128    u128 => FixedU128,
129    usize => FixedU128,
130    i8   => FixedI64,
131    i16  => FixedI64,
132    i32  => FixedI64,
133    i64  => FixedI128,
134    i128 => FixedI128,
135    isize => FixedI128,
136}
137
138// ===============================================================================
139// ``````````````````````````` INTEGER-FIXED CONVERSION ``````````````````````````
140// ===============================================================================
141
142/// Trait for converting a numeric type to and from its **associated fixed-point type**.
143///
144/// This is intended for integer types that implement [`FixedForInteger`],
145/// allowing deterministic fixed-point arithmetic while preserving the original type.
146pub trait IntegerToFixed: Sized + FixedForInteger {
147    /// Convert the current value to the mapped fixed-point type.
148    fn to_fixed(&self) -> <Self as FixedForInteger>::FixedPoint;
149
150    /// Convert a value in the mapped fixed-point type back to the original type.
151    fn from_fixed(f: &<Self as FixedForInteger>::FixedPoint) -> Self;
152}
153
154/// Implements `IntegerToFixed` conversion for **all unsigned integer types** in the list.
155///
156/// - `to_fixed`: Converts the integer into the corresponding fixed-point type using
157///   saturating conversion to prevent overflow.
158/// - `from_fixed`: Converts back from fixed-point to the integer, clamping values to
159///   the integer's max if the fixed-point inner value exceeds it.
160///
161/// Usage: `impl_fixed_convert_unsigned!(u8, u16, u32 => FixedU64);`
162macro_rules! impl_fixed_convert_unsigned {
163    // Accepts a comma-separated list of unsigned types ($t) and a fixed-point type ($fixed)
164    ($($t:ty),* => $fixed:ty) => {
165        $(
166            impl IntegerToFixed for $t {
167                /// Convert integer to fixed-point
168                fn to_fixed(&self) -> <$t as FixedForInteger>::FixedPoint {
169                    // Saturating conversion ensures no overflow when casting integer to fixed
170                    <$fixed>::saturating_from_integer(*self as $t)
171                }
172
173                /// Convert fixed-point back to integer
174                fn from_fixed(f: &<$t as FixedForInteger>::FixedPoint) -> Self {
175                    // Extract the underlying integer from the fixed-point type
176                    let inner = f.into_inner().saturating_div(<$fixed>::DIV);
177                    // Clamp to the maximum value of the integer type
178                    if inner > <$t>::MAX as _ {
179                        <$t>::MAX
180                    } else {
181                        // Safe cast for unsigned integers
182                        inner as $t
183                    }
184                }
185            }
186        )*
187    };
188}
189
190/// Implements `IntegerToFixed` conversion for **all signed integer types** in the list.
191///
192/// - `to_fixed`: Converts the integer into the corresponding fixed-point type using
193///   saturating conversion.
194/// - `from_fixed`: Converts back from fixed-point to the integer, clamping to
195///   both the integer's min and max if the fixed-point inner value is out of bounds.
196///
197/// Usage: `impl_fixed_convert_signed!(i8, i16, i32 => FixedI64);`
198macro_rules! impl_fixed_convert_signed {
199    // Accepts a comma-separated list of signed types ($t) and a fixed-point type ($fixed)
200    ($($t:ty),* => $fixed:ty) => {
201        $(
202            impl IntegerToFixed for $t {
203                /// Convert signed integer to fixed-point
204                fn to_fixed(&self) -> <$t as FixedForInteger>::FixedPoint {
205                    // Saturating conversion prevents overflow when converting signed integer to fixed
206                    <$fixed>::saturating_from_integer(*self as $t)
207                }
208
209                /// Convert fixed-point back to signed integer
210                fn from_fixed(f: &<$t as FixedForInteger>::FixedPoint) -> Self {
211                    // Extract the underlying integer from the fixed-point type
212                    let inner = f.into_inner().saturating_div(<$fixed>::DIV);
213
214                    // Clamp to the maximum value of the integer type
215                    if inner > <$t>::MAX as _ {
216                        <$t>::MAX
217                    }
218                    // Clamp to the minimum value of the integer type
219                    else if inner < <$t>::MIN as _ {
220                        <$t>::MIN
221                    }
222                    // Safe cast for values within the integer range
223                    else {
224                        inner as $t
225                    }
226                }
227            }
228        )*
229    };
230}
231
232// Apply conversions for small unsigned integers
233impl_fixed_convert_unsigned!(u8, u16, u32 => FixedU64);
234// Apply conversions for large unsigned integers
235impl_fixed_convert_unsigned!(u64, u128, usize => FixedU128);
236// Apply conversions for small signed integers
237impl_fixed_convert_signed!(i8, i16, i32 => FixedI64);
238// Apply conversions for large signed integers
239impl_fixed_convert_signed!(i64, i128, isize => FixedI128);
240
241// ===============================================================================
242// ```````````````````````````` SIGNED CAST BRIDGE ```````````````````````````````
243// ===============================================================================
244
245/// A bridge that allows any [`FixedPointNumber`] type - including unsigned ones -
246/// to perform arithmetic in a signed intermediate space, then project the result
247/// back to the original type.
248///
249/// ## Motivation
250///
251/// Several mathematical operations (logarithm of a fraction, negative exponents,
252/// complex-domain arithmetic) require signed intermediates even when the input
253/// and final result are both representable as unsigned values. Rather than
254/// duplicating signed-aware implementations for every function, `FixedSignedCast`
255/// provides a single seam:
256///
257/// - **Signed types** (`FixedI64`, `FixedI128`) implement this trait as a
258///   pure identity: the associated `Signed` type is `Self`, and every conversion
259///   is a no-op.
260/// - **Unsigned types** (`FixedU64`, `FixedU128`) map to a wider signed
261///   counterpart (`FixedI128`) that can represent the full unsigned range as
262///   non-negative values. Conversions to/from `Signed` clamp or fail gracefully
263///   when a result is negative (i.e. not representable by the unsigned type).
264///
265/// ## Associated Type
266///
267/// - `Signed` - the signed fixed-point type used as the arithmetic workspace.
268///   For signed types this is `Self`, for unsigned types it is `FixedI128`.
269///
270/// ## Methods
271///
272/// | Method             | Behaviour on error / out-of-range              |
273/// |--------------------|------------------------------------------------|
274/// | `saturating`       | Clamps the result to the target type's bounds  |
275/// | `checked`          | Returns `None` when the result is out-of-range |
276/// | `checked_into`     | `Self -> Option<Signed>`                        |
277/// | `saturated_into`   | `Self -> Signed` (clamping on overflow)         |
278/// | `checked_from`     | `Signed -> Option<Self>`                        |
279/// | `saturated_from`   | `Signed -> Self` (clamping on underflow/overflow)|
280///
281/// ## Usage
282///
283/// Prefer [`FixedSignedCast::saturating`] for operations where out-of-range
284/// results should clamp silently, and [`FixedSignedCast::checked`] where
285/// out-of-range results must be propagated to the caller as `None`.
286pub trait FixedSignedCast : FixedPointNumber {
287    /// The signed fixed-point workspace type for intermediate arithmetic.
288    ///
289    /// - Signed types (`FixedI64`, `FixedI128`): `type Signed = Self`.
290    /// - Unsigned types (`FixedU64`, `FixedU128`): `type Signed = FixedI128`.
291    type Signed: FixedPointNumber;
292
293    /// Applies the closure `f` in `Signed` space and converts the result back
294    /// to `Self`, **clamping** at the type's representable bounds on overflow
295    /// or underflow.
296    ///
297    /// Useful when signed arithmetic may produce a value outside the target
298    /// range but a best-effort saturated answer is acceptable.
299    fn saturating<F>(x: Self, f: F) -> Self where F: FnOnce(Self::Signed)->Self::Signed;
300
301    /// Applies the closure `f` in `Signed` space and converts the result back
302    /// to `Self`, returning `None` when the result cannot be represented.
303    ///
304    /// The closure receives an `Option<Signed>` - `None` signals that the
305    /// initial conversion from `Self` into `Signed` already failed (only
306    /// possible for `FixedU128` values exceeding `i128::MAX`).
307    fn checked<F>(x: Self, f: F) -> Option<Self> where F: FnOnce(Option<Self::Signed>)->Self::Signed;
308
309    /// Converts `Self` into `Signed`, returning `None` if the value cannot
310    /// be represented in `Signed`.
311    ///
312    /// For signed types this is always `Some(x)`. For unsigned types, this
313    /// fails only when `x.into_inner() > i128::MAX` (only reachable with
314    /// `FixedU128` values in the upper half of its range).
315    fn checked_into(x: Self) -> Option<Self::Signed>;
316
317    /// Converts `Self` into `Signed`, clamping to `Signed::max_value()` on
318    /// overflow.
319    ///
320    /// For signed types this is a zero-cost identity. For unsigned types the
321    /// inner `u64`/`u128` value is reinterpreted as `i128`; values that exceed
322    /// `i128::MAX` clamp to `i128::MAX`.
323    fn saturated_into(x: Self) -> Self::Signed;
324
325    /// Converts a `Signed` value into `Self`, returning `None` if the value
326    /// falls outside the representable range of `Self`.
327    ///
328    /// For signed types this is always `Some(x)`. For unsigned types, a
329    /// negative `Signed` inner value means the result is negative and therefore
330    /// unrepresentable - `None` is returned.
331    fn checked_from(x: Self::Signed) -> Option<Self>;
332
333    /// Converts a `Signed` value into `Self`, clamping at the type bounds.
334    ///
335    /// For signed types this is a zero-cost identity. For unsigned types,
336    /// negative values clamp to `0`. No upper clamp is needed: a non-negative
337    /// `i128` inner value is at most `i128::MAX = 2^127 - 1`, which is always
338    /// less than `u128::MAX = 2^128 - 1`, so it always fits in the unsigned
339    /// inner type without loss.
340    fn saturated_from(x: Self::Signed) -> Self;
341}
342
343/// Identity implementation for `FixedI64`.
344///
345/// `FixedI64` is already signed, so `checked_into`, `saturated_into`,
346/// `checked_from`, and `saturated_from` are all zero-cost identity operations.
347///
348/// `saturating` delegates directly to `f` - any saturation that occurs inside
349/// the closure is the closure's own saturating arithmetic, which is the
350/// expected behaviour for this variant.
351///
352/// `checked` delegates to `f` and returns `None` only when the result has
353/// saturated to `min_value()` or `max_value()`, which are the two sentinel
354/// values that saturating arithmetic produces on overflow. If the closure
355/// legitimately computes exactly `min_value()` or `max_value()`, `None` is
356/// returned conservatively. For cases where that distinction matters, prefer
357/// the saturating variant and handle clamping at the call site.
358impl FixedSignedCast for FixedI64 {
359    type Signed = FixedI64;
360
361    fn saturating<F>(x: Self, f: F) -> Self
362    where
363        F: FnOnce(Self::Signed) -> Self::Signed,
364    {
365        f(x)
366    }
367
368    fn checked<F>(x: Self, f: F) -> Option<Self>
369    where
370        F: FnOnce(Option<Self::Signed>) -> Self::Signed,
371    {
372        let result = f(Some(x));
373        // Detect saturation: saturating arithmetic clamps to min/max on overflow.
374        // Treat either sentinel as evidence that the result is out of range.
375        if result == Self::min_value() || result == Self::max_value() {
376            None
377        } else {
378            Some(result)
379        }
380    }
381
382    fn checked_into(x: Self) -> Option<Self::Signed> {
383        Some(x)
384    }
385
386    fn saturated_into(x: Self) -> Self::Signed {
387        x
388    }
389
390    fn checked_from(x: Self::Signed) -> Option<Self> {
391        Some(x)
392    }
393
394    fn saturated_from(x: Self::Signed) -> Self {
395        x
396    }
397}
398
399/// Identity implementation for `FixedI128`.
400///
401/// `FixedI128` is already signed, so `checked_into`, `saturated_into`,
402/// `checked_from`, and `saturated_from` are all zero-cost identity operations.
403///
404/// `saturating` delegates directly to `f` - any saturation that occurs inside
405/// the closure is the closure's own saturating arithmetic, which is the
406/// expected behaviour for this variant.
407///
408/// `checked` delegates to `f` and returns `None` only when the result has
409/// saturated to `min_value()` or `max_value()`, which are the two sentinel
410/// values that saturating arithmetic produces on overflow. If the closure
411/// legitimately computes exactly `min_value()` or `max_value()`, `None` is
412/// returned conservatively. For cases where that distinction matters, prefer
413/// the saturating variant and handle clamping at the call site.
414impl FixedSignedCast for FixedI128 {
415    type Signed = FixedI128;
416
417    fn saturating<F>(x: Self, f: F) -> Self
418    where
419        F: FnOnce(Self::Signed) -> Self::Signed,
420    {
421        f(x)
422    }
423
424    fn checked<F>(x: Self, f: F) -> Option<Self>
425    where
426        F: FnOnce(Option<Self::Signed>) -> Self::Signed,
427    {
428        let result = f(Some(x));
429        // Detect saturation: saturating arithmetic clamps to min/max on overflow.
430        // Treat either sentinel as evidence that the result is out of range.
431        if result == Self::min_value() || result == Self::max_value() {
432            None
433        } else {
434            Some(result)
435        }
436    }
437
438    fn checked_into(x: Self) -> Option<Self::Signed> {
439        Some(x)
440    }
441
442    fn saturated_into(x: Self) -> Self::Signed {
443        x
444    }
445
446    fn checked_from(x: Self::Signed) -> Option<Self> {
447        Some(x)
448    }
449
450    fn saturated_from(x: Self::Signed) -> Self {
451        x
452    }
453}
454
455/// Unsigned-to-signed bridge for `FixedU64`.
456///
457/// Uses `FixedI128` as the signed workspace. A `FixedU64` inner value is a
458/// `u64`, which always fits in `i128`, so `checked_into` / `saturated_into`
459/// are infallible. The reverse (`checked_from` / `saturated_from`) can fail
460/// or clamp when the signed result is negative or exceeds `u64::MAX`.
461impl FixedSignedCast for FixedU64 {
462    type Signed = FixedI128;
463
464    fn saturating<F>(x: Self, f: F) -> Self
465    where
466        F: FnOnce(Self::Signed) -> Self::Signed,
467    {
468        // u64 inner always fits in i128 - cast is infallible.
469        let signed = FixedI128::from_inner(x.into_inner() as i128);
470        let result = f(signed);
471        Self::saturated_from(result)
472    }
473
474    fn checked<F>(x: Self, f: F) -> Option<Self>
475    where
476        F: FnOnce(Option<Self::Signed>) -> Self::Signed,
477    {   
478        // u64 inner always fits in i128 - cast is infallible.
479        let signed = FixedI128::from_inner(x.into_inner() as i128);
480        let result = f(Some(signed));
481        Self::checked_from(result)
482    }
483
484    fn checked_into(x: Self) -> Option<Self::Signed> {
485        Some(FixedI128::from_inner(x.into_inner() as i128))
486    }
487
488    fn saturated_into(x: Self) -> Self::Signed {
489        FixedI128::from_inner(x.into_inner() as i128)
490    }
491
492    fn checked_from(x: Self::Signed) -> Option<Self> {
493        let inner = x.into_inner();
494        // Negative values are not representable as FixedU64.
495        // Values above u64::MAX cannot fit in the u64 inner type.
496        match inner < 0 || inner > u64::MAX as i128 {
497            true => None,
498            false => Some(FixedU64::from_inner(inner as u64)),
499        }
500    }
501
502    fn saturated_from(x: Self::Signed) -> Self {
503        let inner = x.into_inner();
504
505        let clamped = match inner {
506            b if b < 0 => 0,
507            b if b > u64::MAX as i128 => u64::MAX,
508            b => b as u64,
509        };
510
511        FixedU64::from_inner(clamped)
512    }
513}
514
515/// Unsigned-to-signed bridge for `FixedU128`.
516///
517/// Uses `FixedI128` as the signed workspace. Unlike `FixedU64`, a `FixedU128`
518/// inner value is a `u128` whose upper half (`> i128::MAX`) cannot be
519/// represented in `i128`. `checked_into` therefore returns `None` for those
520/// values, and `saturated_into` clamps them to `i128::MAX`.
521impl FixedSignedCast for FixedU128 {
522    type Signed = FixedI128;
523
524    fn saturating<F>(x: Self, f: F) -> Self
525    where
526        F: FnOnce(Self::Signed) -> Self::Signed,
527    {
528        let signed = Self::saturated_into(x);
529        let result = f(signed);
530        Self::saturated_from(result)
531    }
532
533    fn checked<F>(x: Self, f: F) -> Option<Self>
534    where
535        F: FnOnce(Option<Self::Signed>) -> Self::Signed,
536    {
537        let signed = Self::checked_into(x);
538        let result = f(signed);
539        Self::checked_from(result)
540    }
541
542    fn checked_into(x: Self) -> Option<Self::Signed> {
543        let inner = x.into_inner();
544        // u128 values above i128::MAX cannot be represented in FixedI128.
545        match inner > i128::MAX as u128 {
546            true => None,
547            false => Some(FixedI128::from_inner(inner as i128)),
548        }
549    }
550
551    fn saturated_into(x: Self) -> Self::Signed {
552        let inner = x.into_inner();
553        // Values in the upper half of u128 clamp to i128::MAX.
554        match inner > i128::MAX as u128 {
555            true => FixedI128::from_inner(i128::MAX),
556            false => FixedI128::from_inner(inner as i128),
557        }
558    }
559
560    fn checked_from(x: Self::Signed) -> Option<Self> {
561        let inner = x.into_inner();
562        // Negative values are not representable as FixedU128.
563        match inner < 0 {   
564            true => None,
565            false => Some(FixedU128::from_inner(inner as u128))
566        }
567    }
568
569    fn saturated_from(x: Self::Signed) -> Self {
570        let inner = x.into_inner();
571        // Negative signed results clamp to zero, non-negative values fit in u128.
572        let clamped = match inner < 0 {
573            true => 0,
574            false => inner as u128
575        };
576
577        FixedU128::from_inner(clamped)
578    }
579}
580
581
582// ===============================================================================
583// ```````````````````````````````` COMPLEX NUMBER ```````````````````````````````
584// ===============================================================================
585
586/// A simple, generic **complex number** representation.
587///
588/// Holds a **real** and an **imaginary** (`imgn`) component of any numeric type `T`.
589///
590/// This structure is lightweight and can be used for mathematical, financial, or
591/// signal-processing computations that require complex arithmetic.
592///
593/// ### Type Parameters
594/// - `T`: A numeric type (e.g. `f32`, `f64`, or a custom numeric type)
595#[derive(Debug, Clone, Copy, PartialEq, Eq)]
596pub struct Complex<T> {
597    /// The **real component** of the complex number.
598    pub real: T,
599
600    /// The **imaginary component** of the complex number.
601    pub imgn: T,
602}
603
604impl<T> Complex<T> {
605    fn new(real: T, imgn: T) -> Self {
606        Self { real, imgn }
607    }
608}
609
610// ===============================================================================
611// ``````````````````````````````` PRECISION MODEL ```````````````````````````````
612// ===============================================================================
613
614/// Provides precision metadata for fixed-point types used in numerical
615/// series computations.
616///
617/// # Constants
618///
619/// - `DECIMAL_PLACES`: the number of decimal digits after the point that
620///   the type can represent, derived from its `DIV` value. Used to compute
621///   underflow thresholds and convergence bounds in `fixed_exp` and
622///   related functions.
623///
624/// # Note
625///
626/// These types use **decimal** fixed-point representation, not binary.
627/// `DIV` is a power of 10, so precision is measured in decimal places
628/// rather than fractional bits. `INNER_BITS` records the total bit width
629/// of the underlying integer storage and is reserved for potential future
630/// use with binary fixed-point types; it is not used internally.
631pub trait FixedPointInfo {
632    /// Total bit width of the inner integer storage type.
633    ///
634    /// For example, `FixedU64` wraps a `u64`, so `INNER_BITS = 64`.
635    ///
636    /// This constant is reserved for potential future
637    /// binary fixed-point support and is not used internally.
638    const INNER_BITS: u32;
639
640    /// Number of representable decimal places, equal to `log10(DIV)`.
641    ///
642    /// | Type        | DIV     | DECIMAL_PLACES |
643    /// |-------------|---------|----------------|
644    /// | `FixedU64`  | `10^9`  | `9`            |
645    /// | `FixedI64`  | `10^9`  | `9`            |
646    /// | `FixedU128` | `10^18` | `18`           |
647    /// | `FixedI128` | `10^18` | `18`           |
648    const DECIMAL_PLACES: u32;
649}
650
651/// `FixedU64`: inner type `u64` (64-bit), `DIV = 10^9` - 9 decimal places.
652impl FixedPointInfo for FixedU64 {
653    const INNER_BITS: u32 = 64; // bit width of u64
654    const DECIMAL_PLACES: u32 = 9;
655}
656
657/// `FixedI64`: inner type `i64` (64-bit), `DIV = 10^9` - 9 decimal places.
658impl FixedPointInfo for FixedI64 {
659    const INNER_BITS: u32 = 64; // bit width of i64, sign bit included
660    const DECIMAL_PLACES: u32 = 9;
661}
662
663/// `FixedU128`: inner type `u128` (128-bit), `DIV = 10^18` - 18 decimal places.
664impl FixedPointInfo for FixedU128 {
665    const INNER_BITS: u32 = 128; // bit width of u128
666    const DECIMAL_PLACES: u32 = 18;
667}
668
669/// `FixedI128`: inner type `i128` (128-bit), `DIV = 10^18` - 18 decimal places.
670impl FixedPointInfo for FixedI128 {
671    const INNER_BITS: u32 = 128; // bit width of i128, sign bit included
672    const DECIMAL_PLACES: u32 = 18;
673}
674
675// ===============================================================================
676// ````````````````````````````` NUMERICAL UTILITIES `````````````````````````````
677// ===============================================================================
678
679/// Returns the smallest positive increment representable by the fixed-point generic
680/// [`FixedPointNumber`].
681///
682/// For a fixed-point number defined as:
683/// ```text
684/// value = inner / DIV
685/// ```
686/// the ULP equals `1 / DIV`.
687///
688/// ## Behavior
689/// - Uses `FixedPoint::from_inner(1)` to construct the smallest step value.
690/// - Useful as a numerical tolerance in convergence or rounding checks.
691///
692/// ## Example
693/// ```ignore
694/// let ulp_val = ulp::<FixedU128>();
695/// assert_eq!(ulp_val, FixedU128::from_inner(1)); // represents 1e-18 if DIV = 1e18
696/// ```
697///
698/// ## Notes
699/// - The exact decimal size of the ULP depends on `FixedPoint::DIV`.
700/// - If `DIV = 10^6`, then `ULP = 1e-6`.
701/// - Works for all fixed-point types whose `Inner` implements `From<u8>`.
702fn ulp<F: FixedPointNumber>() -> F
703where
704    F::Inner: From<u8>,
705{
706    F::from_inner(F::Inner::from(1u8))
707}
708
709/// Maximum allowed number of iterations for iterative numerical methods.
710///
711/// This constant caps the number of iterations in functions to:
712/// - Prevent infinite or excessively long loops during convergence.
713/// - Provide a reasonable trade-off between accuracy and computation time.
714///
715/// Typical value (50) is chosen empirically to balance precision and performance,
716/// but can be adjusted depending on application requirements.
717///
718/// ## Note
719/// - Functions also should implement early stopping conditions based on tolerance or stagnation,
720///   so the actual number of iterations is often fewer than this maximum.
721const MAX_ITERATIONS: u32 = 50;
722
723
724/// Extracts the integer part of a fixed-point number as a `u32`,
725/// truncating the fractional component toward zero.
726///
727/// Fixed-point numbers store their value as `inner / DIV`, where `DIV`
728/// is a power of 10 (`10^9` for 64-bit types, `10^18` for 128-bit types).
729/// Dividing `inner` by `DIV` removes the fractional portion, leaving
730/// the integer part.
731///
732/// ## Behavior
733///
734/// | Condition              | Returns      |
735/// |------------------------|--------------|
736/// | Integer part negative  | `0`          |
737/// | Integer part > u32::MAX| `u32::MAX`   |
738/// | Otherwise              | Integer part |
739///
740/// ## Arguments
741///
742/// * `x` - The fixed-point value to truncate.
743///
744/// ## Returns
745///
746/// The integer portion of `x`, clamped to `[0, u32::MAX]`.
747///
748/// ## Examples
749///
750/// ```ignore
751/// // FixedU64 with DIV = 10^9: inner value 300_750_000_000 represents 300.75
752/// let x = FixedU64::from_inner(300_750_000_000);
753/// assert_eq!(to_u32_floor(&x), 300);
754///
755/// // Negative values clamp to 0
756/// let x = FixedI64::saturating_from_integer(-5);
757/// assert_eq!(to_u32_floor(&x), 0);
758/// ```
759fn to_u32_floor<T>(x: &T) -> u32
760where
761    T: FixedPointNumber + Copy + FixedPointInfo,
762    // TryInto<i128> required to work with the fixed-point inner value
763    // in a common signed type regardless of whether T is u64 or i128 based.
764    T::Inner: Copy + PartialOrd + TryInto<i128>,
765{
766    // Extract the raw inner integer representation.
767    let inner = x.into_inner();
768
769    // Convert inner to i128 for arithmetic. For unsigned inner types (u64, u128),
770    // try_into() fails only if the value exceeds i128::MAX - astronomically large,
771    // treated as overflow and clamped to u32::MAX.
772    let inner_i128: i128 = match inner.try_into() {
773        Ok(val) => val,
774        Err(_) => return u32::MAX,
775    };
776
777    // Convert DIV to i128. For sp_arithmetic types, DIV is at most 10^18
778    // which fits comfortably in i128 (max ~1.7 * 10^38). Failure here is
779    // unreachable in practice, but we return 0 conservatively rather than panic.
780    let div: i128 = match T::DIV.try_into() {
781        Ok(val) => val,
782        Err(_) => return 0,
783    };
784
785    // Integer part = inner / DIV, truncated toward zero.
786    let int_part = inner_i128 / div;
787
788    if int_part < 0 {
789        // Negative integer part - not representable as u32, clamp to 0.
790        0
791    } else if int_part > u32::MAX as i128 {
792        // Exceeds u32 range - clamp to maximum.
793        u32::MAX
794    } else {
795        // Safe cast: int_part is in [0, u32::MAX].
796        int_part as u32
797    }
798}
799
800/// Extracts the exact integer value of a fixed-point number as `i128`,
801/// returning `None` if the value has a non-zero fractional component.
802///
803/// A fixed-point number represents `inner / DIV`. This function returns
804/// `inner / DIV` only when `inner` is exactly divisible by `DIV` -
805/// i.e. when the fixed-point value is a whole number with no fractional part.
806///
807/// ## Arguments
808///
809/// * `x` - The fixed-point number to inspect.
810///
811/// ## Returns
812///
813/// * `Some(n)` if `x` represents the exact integer `n`
814/// * `None` if `x` has a fractional component, or if internal conversion fails
815///
816/// ## Examples
817///
818/// ```ignore
819/// let x = FixedU64::saturating_from_integer(5);
820/// assert_eq!(fixed_to_i128(&x), Some(5));
821///
822/// let x = FixedU64::saturating_from_rational(3, 2); // 1.5
823/// assert_eq!(fixed_to_i128(&x), None);
824/// ```
825fn fixed_to_i128<T>(x: &T) -> Option<i128>
826where
827    T: FixedPointNumber,
828    T::Inner: TryInto<i128> + Copy,
829{
830    // Convert inner representation and DIV to i128 for arithmetic.
831    // Both conversions fail only in pathological cases - DIV for sp_arithmetic
832    // types is at most 10^18, well within i128 range.
833    let inner: i128 = x.into_inner().try_into().ok()?;
834    let div: i128 = T::DIV.try_into().ok()?;
835
836    // Exact integer check: inner must be perfectly divisible by DIV.
837    // Any remainder means the value has a fractional component.
838    if inner % div == 0 {
839        Some(inner / div)
840    } else {
841        None
842    }
843}
844
845#[allow(dead_code)]
846fn fixed_pi<T>() -> T
847where
848    T: FixedPointNumber,
849{
850    T::saturating_from_rational(355, 113)
851}
852
853/// Computes an adaptive iteration count for series expansions based on
854/// the magnitude of `x` and the precision of the fixed-point type.
855///
856/// Larger inputs converge more slowly in series expansions, and higher
857/// precision types require more terms to reach their representable accuracy.
858/// This function combines both factors into a single iteration budget:
859///
860/// ```text
861/// iterations = floor(|x|) * DECIMAL_PLACES + 1
862/// ```
863///
864/// The `+ 1` guarantees at least one iteration for any input, including
865/// `x = 0`.
866///
867/// ## Arguments
868///
869/// * `x` - The fixed-point value whose magnitude drives the iteration count.
870///
871/// ## Returns
872///
873/// A `u32` iteration count, always `>= 1`. Uses saturating arithmetic
874/// throughout so overflow on very large inputs produces `u32::MAX` rather
875/// than wrapping.
876#[allow(dead_code)]
877fn dynamic_max_iterations<T>(x: &T) -> u32
878where
879    T: FixedPointNumber + Copy + FixedPointInfo,
880    T::Inner: Shr<u32, Output = T::Inner> + TryInto<i128> + Copy,
881{
882    // Work with |x| so negative inputs produce the same iteration count
883    // as their positive equivalents.
884    let abs_x = x.saturating_abs();
885
886    // Integer part of |x| - the fractional component does not affect
887    // convergence speed meaningfully.
888    let int_part = to_u32_floor(&abs_x);
889
890    // Scale by DECIMAL_PLACES to account for type precision, then add 1
891    // to ensure at least one iteration. saturating_mul and saturating_add
892    // prevent overflow on extreme inputs.
893    int_part
894        .saturating_mul(T::DECIMAL_PLACES)
895        .saturating_add(1)
896}
897
898// ===============================================================================
899// ```````````````````````````` SQRT - NEWTON-RAPHSON ````````````````````````````
900// ===============================================================================
901
902/// Approximates the square root of a fixed-point number using the
903/// Newton-Raphson method.
904///
905/// This is the core computational primitive for square root operations.
906/// The public API is [`fixed_sqrt`], which adds domain checking and exact
907/// fast paths before delegating here.
908///
909/// ## Algorithm
910///
911/// Newton-Raphson iteration for square roots:
912/// ```text
913/// guess_{n+1} = (guess_n + x / guess_n) / 2
914/// ```
915///
916/// Converges quadratically - the number of correct digits roughly doubles
917/// each iteration. For fixed-point types, convergence is detected when the
918/// change between iterations falls within `2 * ULP`, which is the tightest
919/// meaningful threshold: the Newton step cannot improve beyond `1 ULP` on
920/// each side, so tighter tolerances would cause infinite oscillation between
921/// adjacent representable values.
922///
923/// Iteration stops early on stagnation (improvement stops or reverses),
924/// and is hard-capped at `MAX_ITERATIONS` to guarantee termination.
925///
926/// ## Initial Guess Strategy
927///
928/// | Input range | Initial guess         | Reason                              |
929/// |-------------|-----------------------|-------------------------------------|
930/// | `x > 1`     | `(x + 1) / 2`         | Midpoint above 1, closer to result  |
931/// | `x = 1`     | `1`                   | Exact, no iteration needed          |
932/// | `x in (0.25, 1)` | `x`            | Already a reasonable approximation  |
933/// | `x <= 0.25` | `0.25`                | Avoids starting too close to zero   |
934///
935/// ## Arguments
936///
937/// * `x` - A non-negative fixed-point number to compute the square root of.
938///         Caller is responsible for ensuring `x >= 0`. Negative inputs
939///         return zero - use [`fixed_sqrt`] for proper domain handling.
940///
941/// ## Returns
942///
943/// An approximation of `sqrt(x)`, accurate to within `2 * ULP` of the
944/// true value for well-behaved inputs.
945fn fixed_sqrt_newton<F: FixedPointNumber>(x: &F) -> F
946where
947    F::Inner: From<u8>,
948{
949    let zero = F::zero();
950
951    if *x <= zero {
952        return zero;
953    }
954
955    let one = F::one();
956    let two = one.saturating_add(one);
957
958    // 2 * ULP is the principled convergence bound: the Newton step cannot
959    // improve beyond 1 ULP on either side, so anything tighter causes
960    // oscillation between adjacent representable values.
961    let tol = ulp::<F>().saturating_add(ulp::<F>());
962
963    let mut guess = match x.cmp(&one) {
964        // x > 1: midpoint of [1, x] is above sqrt(x), a safe starting point.
965        core::cmp::Ordering::Greater => {
966            x.saturating_add(one).checked_div(&two).unwrap_or(one)
967        }
968        // x = 1: sqrt(1) = 1 exactly, no iteration needed.
969        core::cmp::Ordering::Equal => return one,
970        // x < 1: use x itself if it's above 0.25, otherwise use 0.25.
971        core::cmp::Ordering::Less => {
972            let quarter = F::saturating_from_rational(1, 4);
973            if *x > quarter { *x } else { quarter }
974        }
975    };
976
977    let mut prev_diff: Option<F> = None;
978
979    for _ in 0..MAX_ITERATIONS {
980        // Compute x / guess. If this fails (degenerate state at fixed-point
981        // boundaries), return the best approximation computed so far.
982        let div = match x.checked_div(&guess) {
983            Some(d) => d,
984            None => return guess,
985        };
986
987        // Next guess: average of current guess and x/guess.
988        // Falls back to current guess if the addition overflows.
989        let next = guess.saturating_add(div)
990            .checked_div(&two)
991            .unwrap_or(guess);
992
993        // Absolute difference between successive guesses.
994        let diff = if next > guess {
995            next.saturating_sub(guess)
996        } else {
997            guess.saturating_sub(next)
998        };
999
1000        // Converged: improvement is within 2 * ULP.
1001        // Return `next`, not `guess` - next is the result of this iteration
1002        // and is always at least as accurate as guess.
1003        if diff <= tol {
1004            return next;
1005        }
1006
1007        // Stagnation: improvement has stopped or reversed.
1008        // Return next for the same reason as above.
1009        if let Some(pd) = prev_diff {
1010            if diff >= pd {
1011                return next;
1012            }
1013        }
1014
1015        prev_diff = Some(diff);
1016        guess = next;
1017    }
1018
1019    // Iteration limit reached - return best approximation found.
1020    guess
1021}
1022
1023// ===============================================================================
1024// ````````````````````````````` LN - RANGE REDUCTION ````````````````````````````
1025// ===============================================================================
1026
1027/// Reduces a fixed-point value `y` toward `1` by repeatedly taking its
1028/// square root, returning the reduced value and the number of reductions applied.
1029///
1030/// ## Purpose
1031///
1032/// Series expansions for `ln(y)` converge fastest when `y` is close to `1`.
1033/// This function brings `y` into the band `[0.5, 1.5]` where [`ln_near_one`]
1034/// is both accurate and efficient.
1035///
1036/// ## Algorithm
1037///
1038/// Each iteration replaces `y` with `sqrt(y)`, halving the distance to `1`
1039/// in logarithmic space. After `k` reductions:
1040/// 
1041/// ```text
1042/// y_original = y_reduced ^ (2^k)
1043/// ln(y_original) = 2^k * ln(y_reduced)
1044/// ```
1045///
1046/// The caller uses `k` to undo the reduction after computing `ln(y_reduced)`.
1047///
1048/// ## Stopping Conditions
1049///
1050/// Iteration stops when any of the following occur:
1051/// - `|y - 1| <= 0.5` - `y` is in `[0.5, 1.5]`, close enough for [`ln_near_one`]
1052/// - `ny == y` - Newton-Raphson stagnated, no further reduction is possible
1053/// - `ny == 0` - degenerate input at fixed-point boundaries; result will be approximate
1054/// - [`MAX_ITERATIONS`] reached - hard cap to guarantee termination
1055///
1056/// ## Arguments
1057///
1058/// * `y` - A positive fixed-point value to reduce. Behaviour for `y <= 0`
1059///         is undefined - caller is responsible for domain validation.
1060///
1061/// ## Returns
1062///
1063/// A tuple `(y_reduced, k)` where:
1064/// - `y_reduced` is in `[0.5, 1.5]` (or as close as the stopping conditions allow)
1065/// - `k` is the number of square root reductions applied
1066fn range_reduce_sqrt<T>(mut y: T) -> (T, u32)
1067where
1068    T: FixedPointNumber + Copy + PartialOrd,
1069    T::Inner: From<u8> + Shr<u32, Output = T::Inner> + TryInto<i128> + Copy,
1070{
1071    let one = T::one();
1072
1073    let half = T::saturating_from_rational(1, 2);
1074
1075    let mut k: u32 = 0;
1076
1077    for _ in 0..MAX_ITERATIONS {
1078        let diff = if y > one {
1079            y.saturating_sub(one)
1080        } else {
1081            one.saturating_sub(y)
1082        };
1083
1084        // y is within [0.5, 1.5] - close enough for ln_near_one.
1085        if diff <= half {
1086            break;
1087        }
1088
1089        let ny = fixed_sqrt_newton::<T>(&y);
1090
1091        // Stagnation: Newton-Raphson could not improve further.
1092        if ny == y {
1093            break;
1094        }
1095
1096        // Degenerate: sqrt collapsed to zero at fixed-point boundaries.
1097        if ny == T::zero() {
1098            break;
1099        }
1100
1101        y = ny;
1102        k += 1;
1103    }
1104
1105    (y, k)
1106}
1107
1108/// Computes `ln(y)` for a fixed-point value `y` near `1` using the
1109/// arctanh series identity:
1110///
1111/// ```text
1112/// ln(y) = 2 * sum_{k=0}^{inf} t^(2k+1) / (2k+1)
1113///
1114/// where t = (y - 1) / (y + 1)
1115/// ```
1116///
1117/// Converges for all `y > 0`, with convergence rate determined by `|t|`.
1118/// The closer `y` is to `1`, the smaller `|t|` and the faster convergence.
1119/// [`range_reduce_sqrt`] ensures `y` is in `[0.5, 1.5]` before calling
1120/// this function, keeping `|t| <= 1/3` for fast, reliable convergence.
1121///
1122/// ## Arguments
1123///
1124/// * `y` - A fixed-point value near `1`. Caller must ensure `y > 0`.
1125///         Results are inaccurate for `y` far from `1`.
1126///
1127/// ## Returns
1128///
1129/// An approximation of `ln(y)`, accurate to within the type's ULP for
1130/// inputs in `[0.5, 1.5]`.
1131///
1132/// ## Note
1133///
1134/// On unsigned types, `y < 1` produces `t = 0` (since `y - 1` saturates
1135/// to zero), returning `ln(y) = 0`. This is incorrect for `y < 1`, but
1136/// the unsigned type guard in [`fixed_ln`] ensures this branch is never
1137/// reached for unsigned types with `y < 1`.
1138fn ln_near_one<T>(y: T) -> T
1139where
1140    T: FixedPointNumber + Copy + PartialOrd + FixedPointInfo,
1141    T::Inner: From<u8> + Shr<u32, Output = T::Inner> + TryInto<i128> + Copy,
1142{
1143    let one = T::one();
1144    let two = one.saturating_add(one);
1145    let eps = ulp::<T>();
1146
1147    // t = (y - 1) / (y + 1)
1148    // For signed types: saturating_sub produces a negative result when y < 1,
1149    // giving a negative t - correct.
1150    // For unsigned types: saturating_sub returns 0 when y < 1 - guarded in fixed_ln.
1151    let num = y.saturating_sub(one);   // y - 1
1152    let denom = y.saturating_add(one); // y + 1, always positive for y > 0
1153    let t = num.checked_div(&denom).unwrap_or(T::zero());
1154
1155    // t^2, used to advance the power each iteration: t, t^3, t^5, ...
1156    let t_sq = t.checked_mul(&t).unwrap_or(T::zero());
1157
1158    let mut sum = T::zero();
1159    let mut power = t;
1160
1161    for i in 0u32..MAX_ITERATIONS {
1162        let denom_fp = T::saturating_from_integer(2 * i + 1);
1163
1164        // Current term: t^(2k+1) / (2k+1)
1165        let term = power.checked_div(&denom_fp).unwrap_or(T::zero());
1166        if term.saturating_abs() <= eps {
1167            break;
1168        }
1169
1170        let new_sum = sum.saturating_add(term);
1171
1172        // Stagnation: sum is no longer changing at ULP level.
1173        if new_sum == sum {
1174            break;
1175        }
1176
1177        sum = new_sum;
1178        power = power.checked_mul(&t_sq).unwrap_or(T::zero());
1179    }
1180
1181    // ln(y) = 2 * sum. 
1182    sum.saturating_mul(two)
1183}
1184
1185// ===============================================================================
1186// ``````````````````````````` POWER - INTEGER & BINARY ``````````````````````````
1187// ===============================================================================
1188
1189/// Raises a fixed-point number `x` to an integer power `n` using
1190/// binary exponentiation.
1191///
1192/// ## Behavior
1193///
1194/// | Case               | Result                          |
1195/// |--------------------|---------------------------------|
1196/// | `n = 0`            | `Some(1)` - `x^0 = 1` always   |
1197/// | `n > 0`            | `Some(x^n)`                     |
1198/// | `n < 0, x != 0`    | `Some(1 / x^|n|)`               |
1199/// | `n < 0, x = 0`     | `None` - division by zero       |
1200///
1201/// Uses saturating arithmetic throughout, so intermediate overflow clamps
1202/// to the type's maximum rather than wrapping or panicking. Returns `None`
1203/// only for division-by-zero or when `1 / x^|n|` is unrepresentable.
1204///
1205/// ## Arguments
1206///
1207/// * `x` - The fixed-point base.
1208/// * `n` - The integer exponent, including `i128::MIN`.
1209fn fixed_powi<T>(x: T, n: i128) -> Option<T>
1210where
1211    T: FixedPointNumber + Copy,
1212{
1213    let one = T::one();
1214    let zero = T::zero();
1215
1216    // x^0 = 1 for all x, including x = 0.
1217    // The caller (fixed_pow) guards 0^0 before reaching here
1218    if n == 0 {
1219        return Some(one);
1220    }
1221
1222    if n < 0 {
1223        // 0^(-n) is division by zero - undefined.
1224        if x == zero {
1225            return None;
1226        }
1227
1228        // x^(-n) = 1 / x^|n|.
1229        // unsigned_abs() handles n = i128::MIN without overflow.
1230        let pos = fixed_powi_positive(x, n.unsigned_abs());
1231        return one.checked_div(&pos);
1232    }
1233
1234    Some(fixed_powi_positive(x, n as u128))
1235}
1236
1237/// Core binary exponentiation for non-negative integer powers.
1238///
1239/// Computes `x^n` in `O(log n)` multiplications using the
1240/// square-and-multiply algorithm. Extracted as a separate function
1241/// so both the positive and negative paths of [`fixed_powi`] can
1242/// share the same implementation.
1243///
1244/// Uses saturating arithmetic - intermediate overflow clamps to the
1245/// type's maximum rather than wrapping or panicking.
1246///
1247/// ## Arguments
1248///
1249/// * `x` - The fixed-point base.
1250/// * `n` - The non-negative exponent as `u128`.
1251fn fixed_powi_positive<T>(x: T, mut n: u128) -> T
1252where
1253    T: FixedPointNumber + Copy,
1254{
1255    let one = T::one();
1256    let mut result = one;
1257    let mut base = x;
1258
1259    while n > 0 {
1260        // If the current bit is set, multiply result by the current base power.
1261        if (n & 1) == 1 {
1262            result = result.saturating_mul(base);
1263        }
1264        n >>= 1;
1265        // Square the base for the next bit position.
1266        // Guard avoids a redundant squaring on the final iteration.
1267        if n > 0 {
1268            base = base.saturating_mul(base);
1269        }
1270    }
1271
1272    result
1273}
1274
1275// ===============================================================================
1276// `````````````````````````````````` FIXED-SQRT `````````````````````````````````
1277// ===============================================================================
1278
1279/// Computes the square root of a fixed-point number [`FixedPointNumber`] `x`.
1280///
1281/// Uses the Newton-Raphson method internally via [`fixed_sqrt_newton`] for
1282/// the general case, with exact fast paths for the common values `0` and `1`.
1283///
1284/// ## Domain
1285///
1286/// Defined only for `x >= 0`. Returns `None` for negative inputs, as the square
1287/// root of a negative number is not real-valued.
1288///
1289/// # Arguments
1290///
1291/// * `x` - The fixed-point number to compute the square root of.
1292///
1293/// # Returns
1294///
1295/// * `Some(sqrt(x))` for `x >= 0`
1296/// * `None` for `x < 0`
1297///
1298/// # Examples
1299///
1300/// ```ignore
1301/// let x = FixedU64::saturating_from_integer(4);
1302/// assert_eq!(fixed_sqrt(&x), Some(FixedU64::saturating_from_integer(2)));
1303///
1304/// let x = FixedI64::saturating_from_integer(-1);
1305/// assert_eq!(fixed_sqrt(&x), None);
1306/// ```
1307fn fixed_sqrt<F: FixedPointNumber>(x: &F) -> Option<F>
1308where
1309    // Require the inner integer type to be constructible from u8 literals,
1310    // which is common for fixed-point arithmetic types.
1311    F::Inner: From<u8>,
1312{
1313    let zero = F::zero();
1314    let one = F::one();
1315
1316    // --- DOMAIN CHECK ---
1317    // sqrt(x) is undefined for x < 0 in real arithmetic.
1318    if *x < zero {
1319        return None;
1320    }
1321
1322    // --- FAST PATHS ---
1323    // Exact results for boundary values, avoids unnecessary Newton iterations.
1324
1325    // sqrt(0) = 0 exactly.
1326    if *x == zero {
1327        return Some(zero);
1328    }
1329
1330    // sqrt(1) = 1 exactly.
1331    if *x == one {
1332        return Some(one);
1333    }
1334
1335    // Delegates to Newton-Raphson for all other values.
1336    // See [`fixed_sqrt_newton`] for convergence details.
1337    Some(fixed_sqrt_newton::<F>(x))
1338}
1339
1340// ===============================================================================
1341// ````````````````````````````````` COMPLEX-SQRT ````````````````````````````````
1342// ===============================================================================
1343
1344/// Computes the principal square root of a fixed-point number, returning
1345/// a [`Complex`] result.
1346///
1347/// Unlike [`fixed_sqrt`], this function is defined for all inputs including
1348/// negative numbers. For negative inputs, the result is a purely imaginary
1349/// number representing the principal square root in the complex plane.
1350///
1351/// Internally delegates to [`fixed_sqrt_newton`] for the real square root
1352/// computation, which is only valid for non-negative inputs. The sign of `x`
1353/// is handled here before dispatching.
1354///
1355/// ## Domain
1356///
1357/// Defined for all fixed-point values. Never returns `None`.
1358///
1359/// ## Arguments
1360///
1361/// * `x` - The fixed-point number to compute the complex square root of.
1362///
1363/// ## Returns
1364///
1365/// | Input    | Result                   |
1366/// |----------|--------------------------|
1367/// | `x > 0`  | `sqrt(x) + 0i`           |
1368/// | `x = 0`  | `0 + 0i`                 |
1369/// | `x < 0`  | `0 + sqrt(|x|)i`         |
1370///
1371/// ## Note
1372///
1373/// On unsigned types (`FixedU64`, `FixedU128`), negative values are not
1374/// representable, so the imaginary branch is never reached. Only the real
1375/// and zero branches apply.
1376///
1377/// ## Examples
1378///
1379/// ```ignore
1380/// // Positive input - purely real result
1381/// let x = FixedI64::saturating_from_integer(4);
1382/// assert_eq!(complex_sqrt(&x), Some(Complex { real: FixedI64::saturating_from_integer(2), imgn: FixedI64::zero() }));
1383///
1384/// // Negative input - purely imaginary result
1385/// let x = FixedI64::saturating_from_integer(-4);
1386/// assert_eq!(complex_sqrt(&x), Some(Complex { real: FixedI64::zero(), imgn: FixedI64::saturating_from_integer(2) }));
1387/// ```
1388fn complex_sqrt<F: FixedPointNumber>(x: &F) -> Option<Complex<F>>
1389where
1390    // Require the inner integer type to be constructible from u8 literals,
1391    // which is common for fixed-point arithmetic types.
1392    F::Inner: From<u8>,
1393{
1394    let zero = F::zero();
1395
1396    // --- FAST PATH ---
1397    // sqrt(0) = 0 + 0i exactly.
1398    if *x == zero {
1399        return Some(Complex::new(zero, zero));
1400    }
1401
1402    // --- NEGATIVE INPUT ---
1403    // sqrt(x) for x < 0 is purely imaginary: sqrt(x) = 0 + sqrt(|x|)i.
1404    // Take the magnitude first since fixed_sqrt_newton requires a non-negative input.
1405    if *x < zero {
1406        let mag = x.saturating_abs();
1407        let imgn = fixed_sqrt_newton::<F>(&mag);
1408        return Some(Complex::new(zero, imgn));
1409    }
1410
1411    // sqrt(x) for x > 0 is purely real: sqrt(x) = sqrt(x) + 0i.
1412    let real = fixed_sqrt_newton::<F>(x);
1413    Some(Complex::new(real, zero))
1414}
1415
1416// ===============================================================================
1417// `````````````````````````````````` FIXED-EXP ``````````````````````````````````
1418// ===============================================================================
1419
1420/// Computes `e^x` for a fixed-point number using argument reduction and
1421/// a Taylor series expansion.
1422///
1423/// ## Algorithm
1424///
1425/// Splits `x = n + r` where `n` is the integer part and `|r| <= 0.5`:
1426///
1427/// ```text
1428/// exp(x) = exp(n) * exp(r)
1429/// ```
1430///
1431/// `exp(r)` is computed via Taylor series (fast for small `|r|`).
1432/// `exp(n)` is computed by raising `e ~= 2.718281828459045235` to integer
1433/// power `n` via binary exponentiation ([`fixed_powi`]).
1434///
1435/// `e` is approximated as `2_718_281_828_459_045_235 / 10^18`, giving
1436/// 18 significant figures - matching the full precision of `FixedU128`
1437/// and over-specified but harmless for the other three types.
1438///
1439/// ## Domain
1440///
1441/// Defined for all fixed-point values, but:
1442/// - Large positive `x` overflows the fixed-point range - returns `None`.
1443/// - Large negative `x` underflows to zero - returns `Some(0)`.
1444///   Threshold: `x < -(DECIMAL_PLACES * 10)`.
1445///
1446/// ## Arguments
1447///
1448/// * `x` - The fixed-point exponent value.
1449///
1450/// ## Returns
1451///
1452/// * `Some(exp(x))` on success
1453/// * `Some(0)` when `x` is below the underflow threshold
1454/// * `None` on overflow, or if internal arithmetic fails
1455///
1456/// # Examples
1457///
1458/// ```ignore
1459/// let x = FixedU64::saturating_from_integer(1);
1460/// let result = fixed_exp(&x).unwrap();
1461/// // result ~= 2.718281828
1462/// ```
1463fn fixed_exp<T>(x: &T) -> Option<T>
1464where
1465    T: FixedPointNumber + Copy + PartialOrd + FixedPointInfo,
1466    T::Inner: From<u8> + Shr<u32, Output = T::Inner> + TryInto<i128> + Copy,
1467{
1468    let zero = T::zero();
1469    let one = T::one();
1470
1471    // --- FAST PATH ---
1472    // exp(0) = 1 exactly.
1473    if *x == zero {
1474        return Some(one);
1475    }
1476
1477    // Underflow guard: for sufficiently large negative x, exp(x) is below
1478    // the smallest representable value. Return zero rather than iterating
1479    // toward an unrepresentable result.
1480    let neg_threshold = T::saturating_from_integer(
1481        -((T::DECIMAL_PLACES as i32).saturating_mul(10))
1482    );
1483    if *x < neg_threshold {
1484        return Some(zero);
1485    }
1486
1487    // Argument reduction: split x = n + r, |r| <= 0.5.
1488    // The Taylor series for exp(r) converges much faster for small |r|.
1489    let n_i128: i128 = {
1490        let inner: i128 = x.into_inner().try_into().ok()?;
1491        let div: i128 = T::DIV.try_into().ok()?;
1492        // Truncate toward zero - standard Rust integer division semantics.
1493        inner / div
1494    };
1495    let n = n_i128.clamp(i32::MIN as i128, i32::MAX as i128) as i32;
1496    let n_fixed = T::saturating_from_integer(n);
1497
1498    // r = x - n, guaranteed |r| <= 0.5 by construction.
1499    let r = x.saturating_sub(n_fixed);
1500
1501    // Taylor series: exp(r) = 1 + r + r^2/2! + r^3/3! + ...
1502    // Terms are computed incrementally: term_i = term_{i-1} * r / i.
1503    let epsilon = ulp::<T>();
1504    let mut sum = one;  // Accumulates the series result, starts at the i=0 term (1).
1505    let mut term = one; // Tracks the current series term, starts at 1.
1506
1507    for i in 1u32..=MAX_ITERATIONS {
1508        let i_fixed = T::saturating_from_integer(i);
1509
1510        // next_term = term * r / i.
1511        // Both operations fall back to zero on failure rather than propagating
1512        // None - a failed multiply or divide means the term is negligibly small.
1513        let next_term = term.checked_mul(&r)
1514            .unwrap_or(zero)
1515            .checked_div(&i_fixed)
1516            .unwrap_or(zero);
1517
1518        // saturating_abs() correctly handles negative terms (negative x alternates
1519        // term signs). A plain comparison without abs() would miss converged
1520        // negative terms entirely.
1521        if next_term.saturating_abs() <= epsilon {
1522            break;
1523        }
1524
1525        let new_sum = sum.saturating_add(next_term);
1526
1527        // Sum is no longer changing - saturated or converged at ULP boundary.
1528        if new_sum == sum {
1529            break;
1530        }
1531
1532        sum = new_sum;
1533        term = next_term;
1534    }
1535
1536    // exp(r) is now in `sum`.
1537
1538    // No integer scaling needed when the integer part is zero.
1539    if n == 0 {
1540        return Some(sum);
1541    }
1542
1543    // Scale back: exp(x) = exp(r) * exp(n) = sum * e^n.
1544    let e = T::saturating_from_rational(
1545        2_718_281_828_459_045_235u128,
1546        1_000_000_000_000_000_000u128,
1547    );
1548
1549    let exp_n = fixed_powi(e, n as i128)?;
1550
1551    // If exp_n has already saturated to max_value, multiplying by sum (>= 1
1552    // for positive x) would overflow. Return None rather than a silent saturated result.
1553    if exp_n >= T::max_value() {
1554        return None;
1555    }
1556
1557    // Final result: exp(x) = exp(r) * exp(n).
1558    // checked_mul returns None on overflow, propagating cleanly to the caller.
1559    sum.checked_mul(&exp_n)
1560}
1561
1562// ===============================================================================
1563// ``````````````````````````````````` FIXED-LN ``````````````````````````````````
1564// ===============================================================================
1565
1566/// Computes the natural logarithm `ln(x)` for a fixed-point number.
1567///
1568/// ## Algorithm
1569///
1570/// Uses repeated square root range reduction to bring `x` near `1`,
1571/// then evaluates `ln` via the series expansion in `ln_near_one`:
1572///
1573/// ```text
1574/// ln(x) = 2^k * ln(y)
1575/// ```
1576///
1577/// where `y` is the range-reduced value near `1` and `k` is the number
1578/// of square root reductions applied. The scaling back is done with a
1579/// single multiply by `2^k` to avoid accumulated rounding error from
1580/// repeated multiplication.
1581///
1582/// ## Domain
1583///
1584/// - Defined only for `x > 0`. Returns `None` for `x <= 0`.
1585/// - On unsigned types (`FixedU64`, `FixedU128`), `ln(x)` for `x < 1`
1586///   produces a negative result which is unrepresentable. Returns `None`
1587///   in this case rather than silently returning a wrong answer.
1588///   Use a signed type (`FixedI64`, `FixedI128`) if `ln` of fractional
1589///   values is needed.
1590///
1591/// ## Arguments
1592///
1593/// * `x` - The fixed-point number to compute the natural logarithm of.
1594///
1595/// ## Returns
1596///
1597/// * `Some(ln(x))` for valid inputs
1598/// * `None` for `x <= 0`
1599/// * `None` for unsigned types where `x < 1` (result not representable)
1600///
1601/// ## Examples
1602///
1603/// ```ignore
1604/// let x = FixedU64::saturating_from_integer(1);
1605/// assert_eq!(fixed_ln(&x), Some(FixedU64::zero())); // ln(1) = 0
1606///
1607/// let x = FixedU64::saturating_from_integer(2);
1608/// let result = fixed_ln(&x).unwrap();
1609/// // result ~= 0.693147180
1610///
1611/// // Negative input - always None
1612/// let x = FixedI64::saturating_from_integer(-1);
1613/// assert_eq!(fixed_ln(&x), None);
1614///
1615/// // Fractional input on unsigned type - None (unrepresentable result)
1616/// let x = FixedU64::saturating_from_rational(1, 2);
1617/// assert_eq!(fixed_ln(&x), None);
1618///
1619/// // Fractional input on signed type - correct negative result
1620/// let x = FixedI64::saturating_from_rational(1, 2);
1621/// let result = fixed_ln(&x).unwrap();
1622/// // result ~= -0.693147180
1623/// ```
1624fn fixed_ln<T>(x: &T) -> Option<T>
1625where
1626    T: FixedPointNumber + Copy + PartialOrd + FixedPointInfo ,
1627    T::Inner: From<u8> + Shr<u32, Output = T::Inner> + TryInto<i128> + Copy,
1628{
1629    let zero = T::zero();
1630    let one = T::one();
1631
1632    // --- DOMAIN CHECK ---
1633    // ln(x) is undefined for x <= 0 in real arithmetic.
1634    if *x <= zero {
1635        return None;
1636    }
1637
1638    // --- FAST PATH ---
1639    // ln(1) = 0 exactly.
1640    if *x == one {
1641        return Some(zero);
1642    }
1643
1644    // Detect unsigned types: on unsigned types, `0 - 1` saturates to `0`
1645    // rather than wrapping to `-1`. For such types, ln(x < 1) would return
1646    // the incorrect `Some(0)` from the series - return None instead.
1647    let is_unsigned = zero.saturating_sub(one) == zero;
1648    if is_unsigned && *x < one {
1649        return None;
1650    }
1651
1652    let (y_reduced, k) = range_reduce_sqrt(*x);
1653    let mut ln_val = ln_near_one(y_reduced);
1654
1655    // Recover ln(x) = 2^k * ln(y_reduced).
1656    // k can reach up to MAX_ITERATIONS (50). Shifting by more than 31 would
1657    // panic in debug mode (1u32 << 32 is UB). When k > 31 the true result
1658    // is 2^k * ln(y_reduced) with k >= 32, meaning the result exceeds
1659    // 2^32 * ln(y_reduced) - astronomically large for any fixed-point type.
1660    // Return None rather than a silently wrong clamped value.
1661    if k > 31 {
1662        return None;
1663    }
1664
1665    if k > 0 {
1666        let scale = T::saturating_from_integer(1u32 << k);
1667        ln_val = ln_val.saturating_mul(scale);
1668    }
1669
1670    Some(ln_val)
1671}
1672
1673// ===============================================================================
1674// ``````````````````````````````````` FIXED-POW `````````````````````````````````
1675// ===============================================================================
1676
1677/// Computes `x^p` for fixed-point numbers.
1678///
1679/// ## Algorithm
1680///
1681/// Three computation paths depending on the inputs:
1682///
1683/// - **Integer exponent**: uses binary exponentiation via `fixed_powi`
1684///   for exact, efficient results.
1685/// - **Fractional exponent**: uses the identity `x^p = exp(p * ln(x))`
1686///   via [`fixed_exp`] and [`fixed_ln`].
1687/// - **Special cases**: handled directly with exact results.
1688///
1689/// ## Domain
1690///
1691/// | Input condition              | Result              | Reason                              |
1692/// |------------------------------|---------------------|-------------------------------------|
1693/// | `x = 0, p > 0`              | `Some(0)`           | Mathematical limit                  |
1694/// | `x = 0, p = 0`              | `None`              | Indeterminate form                  |
1695/// | `x = 0, p < 0`              | `None`              | Division by zero                    |
1696/// | `x < 0, p` non-integer      | `None`              | Not real-valued                     |
1697/// | `x < 0, p` integer          | `Some(x^p)`         | Real-valued, handled by `fixed_powi` |
1698/// | `p = 0`                     | `Some(1)`           | `x^0 = 1` for all non-zero `x`     |
1699/// | `x = 1`                     | `Some(1)`           | `1^p = 1` for all `p`              |
1700///
1701/// ## Arguments
1702///
1703/// * `x` - The base as a fixed-point number.
1704/// * `p` - The exponent as a fixed-point number.
1705///
1706/// ## Returns
1707///
1708/// * `Some(x^p)` on success
1709/// * `None` for indeterminate or undefined inputs (see domain table above)
1710/// * `None` on overflow
1711///
1712/// ## Examples
1713///
1714/// ```ignore
1715/// // Integer exponent
1716/// let x = FixedU64::saturating_from_integer(2);
1717/// let p = FixedU64::saturating_from_integer(3);
1718/// assert_eq!(fixed_pow(&x, &p), Some(FixedU64::saturating_from_integer(8)));
1719///
1720/// // Fractional exponent
1721/// let x = FixedU64::saturating_from_integer(4);
1722/// let p = FixedU64::saturating_from_rational(1, 2);
1723/// let result = fixed_pow(&x, &p).unwrap();
1724/// // result ~= 2.0 (square root of 4)
1725///
1726/// // Undefined cases
1727/// let zero = FixedU64::zero();
1728/// assert_eq!(fixed_pow(&zero, &zero), None); // 0^0 indeterminate
1729/// ```
1730fn fixed_pow<T>(x: &T, p: &T) -> Option<T>
1731where
1732    T: FixedPointNumber + Copy + PartialOrd + FixedPointInfo,
1733    T::Inner: From<u8> + Shr<u32, Output = T::Inner> + TryInto<i128> + Copy,
1734{
1735    let zero = T::zero();
1736    let one = T::one();
1737
1738    // --- DOMAIN VALIDATION ---
1739    // 0^0 is indeterminate; 0^(negative) is division by zero.
1740    if *x == zero && *p <= zero {
1741        return None;
1742    }
1743
1744    // 0^(positive) = 0. Handles both integer and fractional positive p,
1745    // consistent with the mathematical limit. Guarded explicitly because
1746    // the general path would call ln(0) which is undefined.
1747    if *x == zero {
1748        return Some(zero);
1749    }
1750
1751    // Negative base with a fractional exponent is not real-valued.
1752    // Integer exponents are handled below by fixed_powi.
1753    let int_exp = fixed_to_i128(p);
1754    if *x < zero && int_exp.is_none() {
1755        return None;
1756    }
1757
1758    // --- FAST PATHS ---
1759    // x^0 = 1 for all non-zero x (zero case already handled above).
1760    if *p == zero {
1761        return Some(one);
1762    }
1763
1764    // 1^p = 1 for all p.
1765    if *x == one {
1766        return Some(one);
1767    }
1768
1769    // Binary exponentiation is exact and significantly cheaper than
1770    // the general exp(p * ln(x)) path. Also the only valid path for
1771    // negative bases, where ln(x) is undefined.
1772    if let Some(n) = int_exp {
1773        return fixed_powi(*x, n);
1774    }
1775
1776    // General case: x^p = exp(p * ln(x)).
1777    // Requires x > 0, which is guaranteed at this point:
1778    // - x = 0 was handled above
1779    // - x < 0 with fractional p was rejected above
1780    //
1781    // Overflow: if p * ln(x) exceeds the fixed-point range, saturating_mul
1782    // clamps it. fixed_exp then receives a saturated value and returns either
1783    // None (overflow guard) or Some(0) (underflow guard), both of which
1784    // propagate correctly to the caller.
1785    let ln_x = fixed_ln(x)?;
1786    let exponent = p.saturating_mul(ln_x);
1787    fixed_exp(&exponent)
1788}
1789
1790// ===============================================================================
1791// ````````````````````````````````` TRAIT FACADES ```````````````````````````````
1792// ===============================================================================
1793
1794/// Unified interface for core fixed-point mathematical operations.
1795///
1796/// Implemented for all four fixed-point types: [`FixedU64`], [`FixedU128`],
1797/// [`FixedI64`], [`FixedI128`]. Enables generic code that works across the
1798/// entire fixed-point family through a single trait bound.
1799pub trait FixedOp
1800where
1801    Self: Sized,
1802{   
1803    /// Square root (real domain).
1804    fn fixed_sqrt(f: &Self) -> Option<Self>;
1805    /// General power `x^p` (integer and fractional exponents).
1806    fn fixed_pow(f: &Self, p: &Self) -> Option<Self>;
1807    /// Natural exponential ( e^x ).
1808    fn fixed_exp(f: &Self) -> Option<Self>;
1809    /// Natural logarithm ( ln(x) ).
1810    fn fixed_ln(f: &Self) -> Option<Self>;
1811}
1812
1813/// Interface for complex-valued fixed-point operations.
1814///
1815/// Extends the real-domain operations in [`FixedOp`] with functions whose
1816/// results may be complex-valued.
1817pub trait FixedComplexOp
1818where   
1819    Self: Sized,
1820{   
1821    /// Square root in complex domain.
1822    fn complex_sqrt(f: &Self) -> Option<Complex<Self>>;
1823}
1824
1825// --- FixedOp Implementations ---
1826
1827/// FixedOp implementation for FixedU64.
1828impl FixedOp for FixedU64 {
1829    fn fixed_sqrt(f: &Self) -> Option<Self> {
1830        fixed_sqrt(f)
1831    }
1832    fn fixed_pow(f: &Self, p: &Self) -> Option<Self> {
1833        fixed_pow(f, p)
1834    }
1835    fn fixed_exp(f: &Self) -> Option<Self> {
1836        fixed_exp(f)
1837    }
1838    fn fixed_ln(f: &Self) -> Option<Self> {
1839        fixed_ln(f)
1840    }
1841}
1842
1843/// FixedOp implementation for FixedU128.
1844impl FixedOp for FixedU128 {
1845    fn fixed_sqrt(f: &Self) -> Option<Self> {
1846        fixed_sqrt(f)
1847    }
1848    fn fixed_pow(f: &Self, p: &Self) -> Option<Self> {
1849        fixed_pow(f, p)
1850    }
1851    fn fixed_exp(f: &Self) -> Option<Self> {
1852        fixed_exp(f)
1853    }
1854    fn fixed_ln(f: &Self) -> Option<Self> {
1855        fixed_ln(f)
1856    }
1857}
1858
1859/// FixedOp implementation for FixedI64.
1860impl FixedOp for FixedI64 {
1861    fn fixed_sqrt(f: &Self) -> Option<Self> {
1862        fixed_sqrt(f)
1863    }
1864    fn fixed_pow(f: &Self, p: &Self) -> Option<Self> {
1865        fixed_pow(f, p)
1866    }
1867    fn fixed_exp(f: &Self) -> Option<Self> {
1868        fixed_exp(f)
1869    }
1870    fn fixed_ln(f: &Self) -> Option<Self> {
1871        fixed_ln(f)
1872    }
1873}
1874
1875/// FixedOp implementation for FixedI128.
1876impl FixedOp for FixedI128 {
1877    fn fixed_sqrt(f: &Self) -> Option<Self> {
1878        fixed_sqrt(f)
1879    }
1880    fn fixed_pow(f: &Self, p: &Self) -> Option<Self> {
1881        fixed_pow(f, p)
1882    }
1883    fn fixed_exp(f: &Self) -> Option<Self> {
1884        fixed_exp(f)
1885    }
1886    fn fixed_ln(f: &Self) -> Option<Self> {
1887        fixed_ln(f)
1888    }
1889}
1890
1891// --- FixedComplexOp Implementations ---
1892
1893/// FixedComplexOp implementation for FixedU64.
1894impl FixedComplexOp for FixedU64 {
1895    fn complex_sqrt(f: &Self) -> Option<Complex<Self>> {
1896        complex_sqrt(f)
1897    }
1898}
1899
1900/// FixedComplexOp implementation for FixedI64.
1901impl FixedComplexOp for FixedI64 {
1902    fn complex_sqrt(f: &Self) -> Option<Complex<Self>> {
1903        complex_sqrt(f)
1904    }
1905}
1906
1907/// FixedComplexOp implementation for FixedU128.
1908impl FixedComplexOp for FixedU128 {
1909    fn complex_sqrt(f: &Self) -> Option<Complex<Self>> {
1910        complex_sqrt(f)
1911    }
1912}
1913
1914/// FixedComplexOp implementation for FixedI128.
1915impl FixedComplexOp for FixedI128 {
1916    fn complex_sqrt(f: &Self) -> Option<Complex<Self>> {
1917        complex_sqrt(f)
1918    }
1919}
1920
1921// ===============================================================================
1922// ```````````````````````````````` PLANNED EXTENSIONS ```````````````````````````
1923// ===============================================================================
1924
1925// pub trait FixedOp
1926// where
1927//     Self: Sized,
1928// {
1929//     // ------------------------
1930//     // Roots & Powers
1931//     // ------------------------
1932//     // Cube root
1933//     // fn fixed_cbrt(f: &Self) -> Self;
1934//     // Integer powers
1935//     // fn fixed_powi(f: &Self, n: i32) -> Self;
1936//     // n-th root
1937//     // fn fixed_root(f: &Self, n: &Self) -> Self;
1938//     // Square of a number
1939//     // fn fixed_square(f: &Self) -> Self;
1940//     // Reciprocal
1941//     // fn fixed_recip(f: &Self) -> Self;
1942
1943//     // ------------------------
1944//     // Exponential & Logarithmic Variants
1945//     // ------------------------
1946//     // 2^x
1947//     // fn fixed_exp2(f: &Self) -> Self;
1948//     // 10^x
1949//     // fn fixed_exp10(f: &Self) -> Self;
1950//     // Natural log
1951//     // fn fixed_ln(f: &Self) -> Self;
1952//     // log base 2
1953//     // fn fixed_log2(f: &Self) -> Self;
1954//     // log base 10
1955//     // fn fixed_log10(f: &Self) -> Self;
1956//     // Exponential minus 1 (exp(x) - 1)
1957//     // fn fixed_expm1(f: &Self) -> Self;
1958//     // Logarithm of 1+x (ln(1+x))
1959//     // fn fixed_ln1p(f: &Self) -> Self;
1960//     // Logarithmic gamma function
1961//     // fn fixed_lgamma(f: &Self) -> Self;
1962
1963//     // ------------------------
1964//     // Trigonometric Functions
1965//     // ------------------------
1966//     // Sine
1967//     // fn fixed_sin(f: &Self) -> Self;
1968//     // Cosine
1969//     // fn fixed_cos(f: &Self) -> Self;
1970//     // Tangent
1971//     // fn fixed_tan(f: &Self) -> Self;
1972//     // Arc sine
1973//     // fn fixed_asin(f: &Self) -> Self;
1974//     // Arc cosine
1975//     // fn fixed_acos(f: &Self) -> Self;
1976//     // Arc tangent
1977//     // fn fixed_atan(f: &Self) -> Self;
1978//     // Arc tangent of y/x
1979//     // fn fixed_atan2(y: &Self, x: &Self) -> Self;
1980
1981//     // ------------------------
1982//     // Hyperbolic Functions
1983//     // ------------------------
1984//     // Hyperbolic sine
1985//     // fn fixed_sinh(f: &Self) -> Self;
1986//     // Hyperbolic cosine
1987//     // fn fixed_cosh(f: &Self) -> Self;
1988//     // Hyperbolic tangent
1989//     // fn fixed_tanh(f: &Self) -> Self;
1990//     // Hyperbolic arc sine
1991//     // fn fixed_asinh(f: &Self) -> Self;
1992//     // Hyperbolic arc cosine
1993//     // fn fixed_acosh(f: &Self) -> Self;
1994//     // Hyperbolic arc tangent
1995//     // fn fixed_atanh(f: &Self) -> Self;
1996
1997//     // ------------------------
1998//     // Special Functions
1999//     // ------------------------
2000//     // Error function
2001//     // fn fixed_erf(f: &Self) -> Self;
2002//     // Complementary error function
2003//     // fn fixed_erfc(f: &Self) -> Self;
2004//     // Gamma function
2005//     // fn fixed_gamma(f: &Self) -> Self;
2006//     // Factorial for integer values
2007//     // fn fixed_fact(n: u32) -> Self;
2008//     // Factorial for floating point (gamma variant)
2009//     // fn fixed_factf(f: &Self) -> Self;
2010//     // Binomial coefficient (n choose k)
2011//     // fn fixed_binom(n: u32, k: u32) -> Self;
2012//     // Signum function
2013//     // fn fixed_sign(f: &Self) -> Self;
2014//     // Clamp value between min and max
2015//     // fn fixed_clamp(f: &Self, min: &Self, max: &Self) -> Self;
2016//     // Floor
2017//     // fn fixed_floor(f: &Self) -> Self;
2018//     // Ceil
2019//     // fn fixed_ceil(f: &Self) -> Self;
2020//     // Round
2021//     // fn fixed_round(f: &Self) -> Self;
2022//     // Fractional part
2023//     // fn fixed_frac(f: &Self) -> Self;
2024
2025//     // ------------------------
2026//     // Numeric & Scientific Utilities
2027//     // ------------------------
2028//     // Absolute value
2029//     // fn fixed_abs(f: &Self) -> Self;
2030//     // Euclidean norm for 2D or 3D (sqrt(x^2 + y^2))
2031//     // fn fixed_hypot(x: &Self, y: &Self) -> Self;
2032//     // Complex modulus squared
2033//     // fn fixed_modsq(f: &Self) -> Self;
2034//     // Power of 2 rounding (next_pow2)
2035//     // fn fixed_next_pow2(f: &Self) -> Self;
2036//     // Logarithm with arbitrary base
2037//     // fn fixed_logb(f: &Self, base: &Self) -> Self;
2038//     // Reciprocal square root (1/sqrt(x))
2039//     // fn fixed_rsqrt(f: &Self) -> Self;
2040// }
2041
2042
2043// ===============================================================================
2044// `````````````````````````````````` UNIT TESTS `````````````````````````````````
2045// ===============================================================================
2046
2047#[cfg(test)]
2048mod tests {
2049    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2050    // ``````````````````````````````````` IMPORTS ```````````````````````````````````
2051    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2052
2053    // --- Module import ---
2054    use super::*;
2055
2056    // --- Substrate crates ---
2057    use sp_runtime::traits::{Bounded, One, Saturating, Zero};
2058
2059    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2060    // `````````````````````````````````` FIXED_SQRT `````````````````````````````````
2061    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2062    
2063    #[test]
2064    fn fixed_sqrt_perfect_cases() {
2065        // case 1: sqrt(4) -> 2
2066        let x: FixedU64 = 4.into();
2067        let result = fixed_sqrt(&x).unwrap();
2068        let expected = FixedU64::saturating_from_integer(2);
2069        assert_eq!(result, expected);
2070
2071        // case 2: sqrt(36) -> 6
2072        let x: FixedU64 = 36.into();
2073        let result = fixed_sqrt(&x).unwrap();
2074        let expected = FixedU64::saturating_from_integer(6);
2075        assert_eq!(result, expected);
2076
2077        // case 3: sqrt(81) -> 9
2078        let x: FixedU64 = 81.into();
2079        let result = fixed_sqrt(&x).unwrap();
2080        let expected = FixedU64::saturating_from_integer(9);
2081        assert_eq!(result, expected);
2082
2083        // case 4: sqrt(1) -> 1
2084        let x: FixedU64 = 1.into();
2085        let result = fixed_sqrt(&x).unwrap();
2086        let expected = FixedU64::saturating_from_integer(1);
2087        assert_eq!(result, expected);
2088    }
2089
2090    #[test]
2091    fn fixed_sqrt_negative_perfect_cases() {
2092        // case 1: sqrt(-1) -> None
2093        let x: FixedI64 = (-1).into();
2094        let result = fixed_sqrt(&x);
2095        assert!(result.is_none());
2096
2097        // case 2: sqrt(-16) -> None
2098        let x: FixedI64 = (-16).into();
2099        let result = fixed_sqrt(&x);
2100        assert!(result.is_none());
2101
2102        // case 3: sqrt(-9) -> None
2103        let x: FixedI64 = (-9).into();
2104        let result = fixed_sqrt(&x);
2105        assert!(result.is_none());
2106
2107        // case 4: sqrt(-100) -> None
2108        let x: FixedI64 = (-100).into();
2109        let result = fixed_sqrt(&x);
2110        assert!(result.is_none());
2111
2112        // case 5: sqrt(-4) -> None
2113        let x: FixedI64 = (-4).into();
2114        let result = fixed_sqrt(&x);
2115        assert!(result.is_none());
2116    }
2117
2118    #[test]
2119    fn fixed_sqrt_large_numbers() {
2120        // case 1: sqrt(10000) -> 100
2121        let x: FixedU64 = 10000.into();
2122        let result = fixed_sqrt(&x).unwrap();
2123        let expected = FixedU64::saturating_from_integer(100);
2124        assert_eq!(result, expected);
2125
2126        // case 2: sqrt(1000000) -> 1000
2127        let x: FixedU64 = 1000000.into();
2128        let result = fixed_sqrt(&x).unwrap();
2129        let expected = FixedU64::saturating_from_integer(1000);
2130        assert_eq!(result, expected);
2131
2132        // case 3: sqrt(u32::MAX) -> 65535.999992370
2133        let x: FixedU64 = (u32::MAX as u64).into();
2134        let result = fixed_sqrt(&x).unwrap();
2135        let expected = FixedU64::from_inner(65535999992370);
2136        assert_eq!(result, expected);
2137
2138        // case 4: sqrt(u64::MAX) -> 135818.791312945
2139        let x: FixedU64 = (u64::MAX).into();
2140        let result = fixed_sqrt(&x).unwrap();
2141        let expected = FixedU64::from_inner(135818791312945);
2142        assert_eq!(result, expected);
2143    }
2144
2145    #[test]
2146    fn fixed_sqrt_non_perfect_cases() {
2147        // case 1: sqrt(2) ~= 1.414213562
2148        let x: FixedU64 = 2.into();
2149        let result = fixed_sqrt(&x).unwrap();
2150        let expected = FixedU64::from_inner(1414213562);
2151        assert_eq!(result, expected);
2152
2153        // case 2: sqrt(5) -> 2.236067977
2154        let x: FixedU64 = 5.into();
2155        let result = fixed_sqrt(&x).unwrap();
2156        let expected = FixedU64::from_inner(2236067977);
2157        assert_eq!(result, expected);
2158
2159        // case 3: sqrt(10) -> 3.162277660
2160        let x: FixedU64 = 10.into();
2161        let result = fixed_sqrt(&x).unwrap();
2162        let expected: FixedU64 = FixedU64::from_inner(3162277660);
2163        assert_eq!(result, expected);
2164
2165        // case 4: sqrt(125) -> 11.180339887
2166        let x: FixedU64 = 125.into();
2167        let result = fixed_sqrt(&x).unwrap();
2168        let expected: FixedU64 = FixedU64::from_inner(11180339887);
2169        assert_eq!(result, expected);
2170    }
2171
2172    #[test]
2173    fn fixed_sqrt_negative_non_perfect_squares() {
2174        // case 1: sqrt(-2) -> None
2175        let x: FixedI64 = (-2).into();
2176        let result = fixed_sqrt(&x);
2177        assert!(result.is_none());
2178
2179        // case 2: sqrt(-35) -> None
2180        let x: FixedI64 = (-35).into();
2181        let result = fixed_sqrt(&x);
2182        assert!(result.is_none());
2183
2184        // case 3: sqrt(-50) -> None
2185        let x: FixedI64 = (-50).into();
2186        let result = fixed_sqrt(&x);
2187        assert!(result.is_none());
2188    }
2189
2190    #[test]
2191    fn fixed_sqrt_fractional_cases() {
2192        // case 1: sqrt(0.25) -> 0.5
2193        let x = FixedU64::saturating_from_rational(1, 4);
2194        let result = fixed_sqrt(&x).unwrap();
2195        let expected = FixedU64::saturating_from_rational(1, 2);
2196        assert_eq!(result, expected);
2197
2198        // case 2: sqrt(0.01) -> 0.1
2199        let x = FixedU64::saturating_from_rational(1, 100);
2200        let result = fixed_sqrt(&x).unwrap();
2201        let expected = FixedU64::saturating_from_rational(1, 10);
2202        assert_eq!(result, expected);
2203
2204        // case 3: sqrt(0.5) -> 0.707106781
2205        let x = FixedU64::saturating_from_rational(1, 2);
2206        let result = fixed_sqrt(&x).unwrap();
2207        let expected = FixedU64::from_inner(707106781);
2208        assert_eq!(result, expected);
2209    }
2210
2211    #[test]
2212    fn fixed_sqrt_edge_cases() {
2213        // case 1: sqrt(0) -> 0
2214        let x: FixedU64 = 0.into();
2215        let result = fixed_sqrt(&x).unwrap();
2216        let expected = FixedU64::zero();
2217        assert_eq!(result, expected);
2218
2219        // case 2: sqrt(1) -> 1
2220        let x: FixedU64 = 1.into();
2221        let result = fixed_sqrt(&x).unwrap();
2222        let expected = FixedU64::one();
2223        assert_eq!(result, expected);
2224
2225        // case 3
2226        // sqrt(i64::MIN) -> saturating_abs gives i64::MAX
2227        let x: FixedI64 = (i64::MIN).into();
2228        let result = fixed_sqrt(&x);
2229        assert!(result.is_none());
2230
2231        // case 4
2232        // sqrt(i64::MAX) -> 96038.388349944
2233        let x  = FixedI64::max_value();
2234        let result = fixed_sqrt(&x).unwrap();
2235        let expected = FixedI64::from_inner(96038388349944);
2236        assert_eq!(result, expected);
2237    }
2238
2239    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2240    // ````````````````````````````````` COMPLEX_SQRT ````````````````````````````````
2241    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2242
2243    #[test]
2244    fn complex_sqrt_perfect_cases() {
2245        // case 1: sqrt(4) -> 2
2246        let x: FixedU64 = 4.into();
2247        let result = complex_sqrt(&x).unwrap();
2248        let expected: Complex<FixedU64> = Complex {
2249            real: 2.into(),
2250            imgn: 0.into(),
2251        };
2252        assert_eq!(result, expected);
2253
2254        // case 2: sqrt(36) -> 6
2255        let x: FixedU64 = 36.into();
2256        let result = complex_sqrt(&x).unwrap();
2257        let expected: Complex<FixedU64> = Complex {
2258            real: 6.into(),
2259            imgn: 0.into(),
2260        };
2261        assert_eq!(result, expected);
2262
2263        // case 3: sqrt(81) -> 9
2264        let x: FixedU64 = 81.into();
2265        let result = complex_sqrt(&x).unwrap();
2266        let expected: Complex<FixedU64> = Complex {
2267            real: 9.into(),
2268            imgn: 0.into(),
2269        };
2270        assert_eq!(result, expected);
2271
2272        // case 4: sqrt(1) -> 1
2273        let x: FixedU64 = 1.into();
2274        let result = complex_sqrt(&x).unwrap();
2275        let expected = Complex {
2276            real: 1.into(),
2277            imgn: 0.into(),
2278        };
2279        assert_eq!(result, expected);
2280    }
2281
2282    #[test]
2283    fn complex_sqrt_negative_perfect_cases() {
2284        // case 1: sqrt(-1) -> 1i (imaginary)
2285        let x: FixedI64 = (-1).into();
2286        let result = complex_sqrt(&x).unwrap();
2287        let expected = Complex {
2288            real: 0.into(),
2289            imgn: 1.into(),
2290        };
2291        assert_eq!(result, expected);
2292
2293        // case 2:sqrt(-16) -> 4i (imaginary)
2294        let x: FixedI64 = (-16).into();
2295        let result = complex_sqrt(&x).unwrap();
2296        let expected = Complex {
2297            real: 0.into(),
2298            imgn: 4.into(),
2299        };
2300        assert_eq!(result, expected);
2301
2302        // case 3: sqrt(-9) -> 3i (imaginary)
2303        let x: FixedI64 = (-9).into();
2304        let result = complex_sqrt(&x).unwrap();
2305        let expected = Complex {
2306            real: 0.into(),
2307            imgn: 3.into(),
2308        };
2309        assert_eq!(result, expected);
2310
2311        // case 4: sqrt(-100) -> 10i (imaginary)
2312        let x: FixedI64 = (-100).into();
2313        let result = complex_sqrt(&x).unwrap();
2314        let expected = Complex {
2315            real: 0.into(),
2316            imgn: 10.into(),
2317        };
2318        assert_eq!(result, expected);
2319
2320        // case 5: sqrt(-4) -> 2i (imaginary)
2321        let x: FixedI64 = (-4).into();
2322        let result = complex_sqrt(&x).unwrap();
2323        let expected = Complex {
2324            real: 0.into(),
2325            imgn: 2.into(),
2326        };
2327        assert_eq!(result, expected);
2328    }
2329
2330    #[test]
2331    fn complex_sqrt_large_numbers() {
2332        // case 1: sqrt(10000) -> 100
2333        let x: FixedU64 = 10000.into();
2334        let result = complex_sqrt(&x).unwrap();
2335        let expected = Complex {
2336            real: 100.into(),
2337            imgn: 0.into(),
2338        };
2339        assert_eq!(result, expected);
2340
2341        // case 2: sqrt(1000000) -> 1000
2342        let x: FixedU64 = 1000000.into();
2343        let result = complex_sqrt(&x).unwrap();
2344        let expected = Complex {
2345            real: 1000.into(),
2346            imgn: 0.into(),
2347        };
2348        assert_eq!(result, expected);
2349
2350        // case 3: sqrt(u32::MAX) -> 65535.999992370
2351        let x: FixedU64 = (u32::MAX as u64).into();
2352        let result = complex_sqrt(&x).unwrap();
2353        let expected = Complex {
2354            real: FixedU64::from_inner(65535999992370),
2355            imgn: 0.into(),
2356        };
2357        assert_eq!(result, expected);
2358
2359        // case 4: sqrt(u64::MAX) -> 135818.791312945
2360        let x: FixedU64 = (u64::MAX).into();
2361        let result = complex_sqrt(&x).unwrap();
2362        let expected = Complex {
2363            real: FixedU64::from_inner(135818791312945),
2364            imgn: 0.into(),
2365        };
2366        assert_eq!(result, expected);
2367    }
2368
2369    #[test]
2370    fn complex_sqrt_non_perfect_cases() {
2371        // case 1: sqrt(2) ~= 1.414213562
2372        let x: FixedU64 = 2.into();
2373        let result = complex_sqrt(&x).unwrap();
2374        let expected = Complex {
2375            real: FixedU64::from_inner(1414213562),
2376            imgn: 0.into(),
2377        };
2378        assert_eq!(result, expected);
2379
2380        // case 2: sqrt(5) -> 2.236067977
2381        let x: FixedU64 = 5.into();
2382        let result = complex_sqrt(&x).unwrap();
2383        let expected = Complex {
2384            real: FixedU64::from_inner(2236067977),
2385            imgn: 0.into(),
2386        };
2387        assert_eq!(result, expected);
2388
2389        // case 3: sqrt(10) -> 3.162277660
2390        let x: FixedU64 = 10.into();
2391        let result = complex_sqrt(&x).unwrap();
2392        let expected: Complex<FixedU64> = Complex {
2393            real: FixedU64::from_inner(3162277660),
2394            imgn: 0.into(),
2395        };
2396        assert_eq!(result, expected);
2397
2398        // case 4: sqrt(125) -> 11.180339887
2399        let x: FixedU64 = 125.into();
2400        let result = complex_sqrt(&x).unwrap();
2401        let expected: Complex<FixedU64> = Complex {
2402            real: FixedU64::from_inner(11180339887),
2403            imgn: 0.into(),
2404        };
2405        assert_eq!(result, expected);
2406    }
2407
2408    #[test]
2409    fn complex_sqrt_negative_non_perfect_squares() {
2410        // case 1: sqrt(-2) -> 1.414213562i
2411        let x: FixedI64 = (-2).into();
2412        let result = complex_sqrt(&x).unwrap();
2413        let expected = Complex {
2414            real: 0.into(),
2415            imgn: FixedI64::from_inner(1414213562),
2416        };
2417        assert_eq!(result, expected);
2418
2419        // case 2: sqrt(-35) -> 5.916079783i (imaginary)
2420        let x: FixedI64 = (-35).into();
2421        let result = complex_sqrt(&x).unwrap();
2422        let expected = Complex {
2423            real: 0.into(),
2424            imgn: FixedI64::from_inner(5916079783),
2425        };
2426        assert_eq!(result, expected);
2427
2428        // case 3: sqrt(-50) -> 7.071067811i
2429        let x: FixedI64 = (-50).into();
2430        let result = complex_sqrt(&x).unwrap();
2431        let expected = Complex {
2432            real: 0.into(),
2433            imgn: FixedI64::from_inner(7071067811),
2434        };
2435        assert_eq!(result, expected);
2436    }
2437
2438    #[test]
2439    fn complex_sqrt_fractional_cases() {
2440        // case 1: sqrt(0.25) -> 0.5
2441        let x = FixedU64::saturating_from_rational(1, 4);
2442        let result = complex_sqrt(&x).unwrap();
2443        let expected = Complex {
2444            real: FixedU64::saturating_from_rational(1, 2),
2445            imgn: 0.into(),
2446        };
2447        assert_eq!(result, expected);
2448
2449        // case 2: sqrt(0.01) -> 0.1
2450        let x = FixedU64::saturating_from_rational(1, 100);
2451        let result = complex_sqrt(&x).unwrap();
2452        let expected = Complex {
2453            real: FixedU64::saturating_from_rational(1, 10),
2454            imgn: 0.into(),
2455        };
2456        assert_eq!(result, expected);
2457
2458        // case 3: sqrt(0.5) -> 0.707106781
2459        let x = FixedU64::saturating_from_rational(1, 2);
2460        let result = complex_sqrt(&x).unwrap();
2461        let expected = Complex {
2462            real: FixedU64::from_inner(707106781),
2463            imgn: 0.into(),
2464        };
2465        assert_eq!(result, expected);
2466    }
2467
2468    #[test]
2469    fn complex_sqrt_edge_cases() {
2470        // case 1: sqrt(0) -> 0
2471        let x: FixedU64 = 0.into();
2472        let result = complex_sqrt(&x).unwrap();
2473        let expected = Complex {
2474            real: 0.into(),
2475            imgn: 0.into(),
2476        };
2477        assert_eq!(result, expected);
2478
2479        // case 2: sqrt(1) -> 1
2480        let x: FixedU64 = 1.into();
2481        let result = complex_sqrt(&x).unwrap();
2482        let expected = Complex {
2483            real: 1.into(),
2484            imgn: 0.into(),
2485        };
2486        assert_eq!(result, expected);
2487
2488        // case 3
2489        // sqrt(i64::MAX) -> 96038.388349944
2490        let x  = FixedI64::max_value();
2491        let result = complex_sqrt(&x).unwrap();
2492        let expected = Complex {
2493            real: FixedI64::from_inner(96038388349944),
2494            imgn: 0.into(),
2495        };
2496        assert_eq!(result, expected);
2497
2498        // case 4
2499        // sqrt(i64::MIN) -> saturating_abs gives i64::MAX
2500        let x: FixedI64 = (i64::MIN).into();
2501        let result = complex_sqrt(&x).unwrap();
2502        let expected = Complex {
2503            real: 0.into(),
2504            imgn: FixedI64::from_inner(96038388349944),
2505        };
2506        assert_eq!(result, expected);
2507    }
2508
2509    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2510    // `````````````````````````````````` FIXED_EXP ``````````````````````````````````
2511    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2512
2513    #[test]
2514    fn fixed_exp_normal_cases() {
2515        // case 1: exp(0) -> 1
2516        let x: FixedU64 = 0.into();
2517        let result = fixed_exp(&x).unwrap();
2518        let expected = 1.into();
2519        assert_eq!(result, expected);
2520
2521        // case 2: exp(1) -> 2.718281828
2522        let x: FixedU64 = 1.into();
2523        let result = fixed_exp(&x).unwrap();
2524        let expected = FixedU64::from_inner(2718281828);
2525        assert_eq!(result, expected);
2526
2527        // case 3: exp(2) -> 7.389056096
2528        let x: FixedU64 = 2.into();
2529        let result = fixed_exp(&x).unwrap();
2530        let expected = FixedU64::from_inner(7389056096);
2531        assert_eq!(result, expected);
2532
2533        // case 4: exp(3) -> 20.085536911
2534        let x: FixedU64 = 3.into();
2535        let result = fixed_exp(&x).unwrap();
2536        let expected = FixedU64::from_inner(20085536911);
2537        assert_eq!(result, expected);
2538
2539        // case 5: exp(5) -> 148.413158957
2540        let x: FixedU64 = 5.into();
2541        let result = fixed_exp(&x).unwrap();
2542        let expected = FixedU64::from_inner(148413158957);
2543        assert_eq!(result, expected);
2544    }
2545
2546    #[test]
2547    fn fixed_exp_positive_fractional_edge_cases() {
2548        // case 1: exp(0.5) -> 1.648721267
2549        let x = FixedU64::saturating_from_rational(1, 2);
2550        let result = fixed_exp(&x).unwrap();
2551        let expected = FixedU64::from_inner(1648721267);
2552        assert_eq!(result, expected);
2553
2554        // case 2: exp(0.1) -> 1.105170915
2555        let x = FixedU64::saturating_from_rational(1, 10);
2556        let result = fixed_exp(&x).unwrap();
2557        let expected = FixedU64::from_inner(1105170915);
2558        assert_eq!(result, expected);
2559
2560        // case 3: exp(0.001) -> 1.001000500
2561        let x = FixedU64::saturating_from_rational(1, 1000);
2562        let result = fixed_exp(&x).unwrap();
2563        let expected = FixedU64::from_inner(1001000500);
2564        assert_eq!(result, expected);
2565
2566        // case 4: exp(2.5) -> 12.182493928
2567        let x = FixedU64::saturating_from_rational(5, 2);
2568        let result = fixed_exp(&x).unwrap();
2569        let expected = FixedU64::from_inner(12182493928);
2570        assert_eq!(result, expected);
2571
2572        // case 5: exp(1.5) -> 4.481689059
2573        let x = FixedU64::saturating_from_rational(3, 2);
2574        let result = fixed_exp(&x).unwrap();
2575        let expected = FixedU64::from_inner(4481689059);
2576        assert_eq!(result, expected);
2577    }
2578
2579    #[test]
2580    fn fixed_exp_negative_edge_cases() {
2581        // case 1: exp(-1) -> 0.367879441
2582        let x: FixedI64 = (-1).into();
2583        let result = fixed_exp(&x).unwrap();
2584        let expected = FixedI64::from_inner(367879441);
2585        assert_eq!(result, expected);
2586
2587        // case 2: exp(-2) -> 0.135335383
2588        let x: FixedI64 = (-2).into();
2589        let result = fixed_exp(&x).unwrap();
2590        let expected = FixedI64::from_inner(135335283);
2591        assert_eq!(result, expected);
2592
2593        // case 3: exp(-0.5) -> 0.606530659
2594        let x = FixedI64::saturating_from_rational(-1, 2);
2595        let result = fixed_exp(&x).unwrap();
2596        let expected = FixedI64::from_inner(606530659);
2597        assert_eq!(result, expected);
2598
2599        // case 4: exp(-15) -> 0.000000305
2600        let x: FixedI64 = (-15).into();
2601        let result = fixed_exp(&x).unwrap();
2602        let expected = FixedI64::from_inner(000000305);
2603        assert_eq!(result, expected);
2604    }
2605
2606    #[test]
2607    fn fixed_exp_mathematical_constants() {
2608        // case 1:
2609        // exp(ln(2)) should be close to 2
2610        // ln(2) ~= 0.693147181
2611        let ln2 = FixedU64::from_inner(693147181);
2612        let result = fixed_exp(&ln2).unwrap();
2613        let expected: FixedU64 = 2.into();
2614
2615        // Allow small tolerance due to precision
2616        let diff = if result > expected {
2617            result.saturating_sub(expected)
2618        } else {
2619            expected.saturating_sub(result)
2620        };
2621        let tolerance = FixedU64::from_inner(1_000_000); // 0.001
2622        assert!(diff < tolerance);
2623
2624        // case 2:
2625        // exp(ln(10)) should be close to 10
2626        // ln(10) ~= 2.302585093
2627        let ln10 = FixedU64::from_inner(2302585093);
2628        let result = fixed_exp(&ln10).unwrap();
2629        let expected: FixedU64 = 10.into();
2630
2631        let diff = if result > expected {
2632            result.saturating_sub(expected)
2633        } else {
2634            expected.saturating_sub(result)
2635        };
2636        assert!(diff < tolerance);
2637    }
2638
2639    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2640    // ``````````````````````````````````` FIXED_LN ``````````````````````````````````
2641    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2642
2643    #[test]
2644    fn fixed_ln_at_one() {
2645        // case 1: ln(1) = 0 (real part), 0 (imaginary)
2646        let x: FixedU64 = 1.into();
2647        let result = fixed_ln(&x).unwrap();
2648        let expected = FixedU64::from_inner(0);
2649        assert_eq!(result, expected);
2650    }
2651
2652    #[test]
2653    fn fixed_ln_at_zero() {
2654        // case 1: ln(0) -> None
2655        let x: FixedU64 = 0.into();
2656        let result = fixed_ln(&x);
2657        assert!(result.is_none());
2658    }
2659
2660    #[test]
2661    fn fixed_ln_positive_integers() {
2662        // case 1: ln(2) ~= 0.693147172
2663        let x: FixedU64 = 2.into();
2664        let result = fixed_ln(&x).unwrap();
2665        let expected = FixedU64::from_inner(693147172);
2666        assert_eq!(result, expected);
2667
2668        // case 2: ln(3) ~= 1.098612248
2669        let x: FixedU64 = 3.into();
2670        let result = fixed_ln(&x).unwrap();
2671        let expected = FixedU64::from_inner(1098612248);
2672        assert_eq!(result, expected);
2673
2674        // case 3: ln(10) ~= 2.302585040
2675        let x: FixedU64 = 10.into();
2676        let result = fixed_ln(&x).unwrap();
2677        let expected = FixedU64::from_inner(2302585040);
2678        assert_eq!(result, expected);
2679
2680        // case 4: ln(100) ~= 4.605170180
2681        let x: FixedU64 = 100.into();
2682        let result = fixed_ln(&x).unwrap();
2683        let expected = FixedU64::from_inner(4605170080);
2684        assert_eq!(result, expected);
2685    }
2686
2687    #[test]
2688    fn fixed_ln_fractional_values() {
2689        // case 1: ln(0.5) ~= -0.693147174
2690        let x = FixedI64::saturating_from_rational(1, 2);
2691        let result = fixed_ln(&x).unwrap();
2692        let expected = FixedI64::from_inner(-693147174);
2693        assert_eq!(result, expected);
2694
2695        // case 2: ln(0.1) ~= -2.302585064
2696        let x = FixedI64::saturating_from_rational(1, 10);
2697        let result = fixed_ln(&x).unwrap();
2698        let expected = FixedI64::from_inner(-2302585064);
2699        assert_eq!(result, expected);
2700
2701        // case 3: ln(1.5) ~= 0.405465100
2702        let x = FixedU64::saturating_from_rational(3, 2);
2703        let result = fixed_ln(&x).unwrap();
2704        let expected = FixedU64::from_inner(405465100);
2705        assert_eq!(result, expected);
2706
2707        // case 4: ln(2.5) ~= 0.916290712
2708        let x = FixedU64::saturating_from_rational(5, 2);
2709        let result = fixed_ln(&x).unwrap();
2710        let expected = FixedU64::from_inner(916290712);
2711        assert_eq!(result, expected);
2712
2713        // case 5: ln(0.5) -> None
2714        let x = FixedU64::saturating_from_rational(1, 2);
2715        let result = fixed_ln(&x);
2716        assert!(result.is_none());
2717
2718        // case 6: ln(0.1) -> None
2719        let x = FixedU64::saturating_from_rational(1, 10);
2720        let result = fixed_ln(&x);
2721        assert!(result.is_none());
2722
2723    }
2724
2725    #[test]
2726    fn fixed_ln_negative_values() {
2727        // case 1: ln(-1) -> None
2728        let x: FixedI64 = (-1).into();
2729        let result = fixed_ln(&x);
2730        assert!(result.is_none());
2731
2732        // case 2: ln(-2) ~= 0.6931471872 
2733        let x: FixedI64 = (-2).into();
2734        let result = fixed_ln(&x);
2735        assert!(result.is_none());
2736
2737        // case 3: ln(-10) ~= 2.302585040 
2738        let x: FixedI64 = (-10).into();
2739        let result = fixed_ln(&x);
2740        assert!(result.is_none());
2741
2742        // case 4: ln(-0.5) ~= -0.693147174
2743        let x = FixedI64::saturating_from_rational(-1, 2);
2744        let result = fixed_ln(&x);
2745        assert!(result.is_none());
2746    }
2747
2748    #[test]
2749    fn fixed_ln_mathematical_constants() {
2750        // case 1: ln(e) should be ~= 1
2751        // e ~= 2.718281828
2752        let e = FixedU64::from_inner(2718281828);
2753        let result = fixed_ln(&e).unwrap();
2754        let expected = FixedU64::from_inner(1000000000);
2755
2756        let tolerance = FixedU64::from_inner(1_000_000); // 0.001
2757        let diff = if result > expected {
2758            result.saturating_sub(expected)
2759        } else {
2760            expected.saturating_sub(result)
2761        };
2762        assert!(diff < tolerance);
2763    }
2764
2765    #[test]
2766    fn fixed_ln_inverse_of_exp() {
2767        // case 1: x = 1
2768        let x: FixedU64 = 1.into();
2769        let exp_x = fixed_exp(&x).unwrap();
2770        let result = fixed_ln(&exp_x).unwrap();
2771
2772        let tolerance = FixedU64::from_inner(1_000_000); // 0.001
2773        let diff = if result > x {
2774            result.saturating_sub(x)
2775        } else {
2776            x.saturating_sub(result)
2777        };
2778        assert!(diff < tolerance);
2779
2780        // case 2: x = 2
2781        let x: FixedU64 = 2.into();
2782        let exp_x = fixed_exp(&x).unwrap();
2783        let result = fixed_ln(&exp_x).unwrap();
2784
2785        let diff = if result > x {
2786            result.saturating_sub(x)
2787        } else {
2788            x.saturating_sub(result)
2789        };
2790        assert!(diff < tolerance);
2791    }
2792
2793    #[test]
2794    fn fixed_ln_properties_verification() {
2795        // case 1: ln(a*b) = ln(a) + ln(b)
2796        // ln(2*3) = ln(6) should equal ln(2) + ln(3)
2797        let x: FixedU64 = 6.into();
2798        let ln_6 = fixed_ln(&x).unwrap();
2799
2800        let two: FixedU64 = 2.into();
2801        let three: FixedU64 = 3.into();
2802        let ln_2 = fixed_ln(&two).unwrap();
2803        let ln_3 = fixed_ln(&three).unwrap();
2804        let sum = ln_2.saturating_add(ln_3);
2805
2806        let tolerance = FixedU64::from_inner(1_000_000); // 0.001
2807        let diff = if ln_6 > sum {
2808            ln_6.saturating_sub(sum)
2809        } else {
2810            sum.saturating_sub(ln_6)
2811        };
2812        assert!(diff < tolerance);
2813    }
2814
2815    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2816    // `````````````````````````````````` FIXED_POW ``````````````````````````````````
2817    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2818
2819    #[test]
2820    fn fixed_pow_edge_cases() {
2821        // case 1: x = 1, p = 0
2822        let x: FixedU64 = 1.into();
2823        let p: FixedU64 = 0.into();
2824        let result = fixed_pow(&x, &p).unwrap();
2825        let expected: FixedU64 = 1.into();
2826        assert_eq!(result, expected);
2827
2828        // case 2: x = 0, p = 1
2829        let x: FixedU64 = 0.into();
2830        let p: FixedU64 = 1.into();
2831        let result = fixed_pow(&x, &p).unwrap();
2832        let expected: FixedU64 = 0.into();
2833        assert_eq!(result, expected);
2834
2835        // case 3: x = 1, p = 1
2836        let x: FixedU64 = 1.into();
2837        let p: FixedU64 = 1.into();
2838        let result = fixed_pow(&x, &p).unwrap();
2839        let expected: FixedU64 = 1.into();
2840        assert_eq!(result, expected);
2841
2842        // case 4: pow(0, -1) = 1 / 0 -> None
2843        let x: FixedI64 = 0.into();
2844        let p: FixedI64 = (-1).into();
2845        let result = fixed_pow(&x, &p);
2846        assert!(result.is_none());
2847    }
2848
2849    #[test]
2850    fn fixed_pow_normal_cases() {
2851        // case 1: x = 2, p = 3
2852        let x: FixedU64 = 2.into();
2853        let p: FixedU64 = 3.into();
2854        let result = fixed_pow(&x, &p).unwrap();
2855        let expected: FixedU64 = 8.into();
2856        assert_eq!(result, expected);
2857
2858        // case 2: x = 4, p = 5
2859        let x: FixedU64 = 4.into();
2860        let p: FixedU64 = 5.into();
2861        let result = fixed_pow(&x, &p).unwrap();
2862        let expected: FixedU64 = 1024.into();
2863        assert_eq!(result, expected);
2864
2865        // case 3: x = 8, p = 6
2866        let x: FixedU64 = 8.into();
2867        let p: FixedU64 = 6.into();
2868        let result = fixed_pow(&x, &p).unwrap();
2869        let expected: FixedU64 = 262144.into();
2870        assert_eq!(result, expected);
2871    }
2872
2873    #[test]
2874    fn fixed_pow_fractional_exponents() {
2875        let tolerance: FixedU64 = FixedU64::from_inner(1_000_000); // 0.001
2876
2877        // case 1: pow(4, 0.5) = 2
2878        let x: FixedU64 = 4.into();
2879        let p = FixedU64::saturating_from_rational(1, 2);
2880        let result = fixed_pow(&x, &p).unwrap();
2881        let expected: FixedU64 = 2.into();
2882        let diff = if result > expected {
2883            result.saturating_sub(expected)
2884        } else {
2885            expected.saturating_sub(result)
2886        };
2887        assert!(diff < tolerance);
2888
2889        // case 2: pow(9, 0.25) = sqrt(3) ~= 1.7320508
2890        let x: FixedU64 = 9.into();
2891        let p = FixedU64::saturating_from_rational(1, 4);
2892        let result = fixed_pow(&x, &p).unwrap();
2893        let expected = FixedU64::saturating_from_rational(1732051, 1_000_000); // ~= sqrt(3)
2894        let diff = if result > expected {
2895            result.saturating_sub(expected)
2896        } else {
2897            expected.saturating_sub(result)
2898        };
2899        assert!(diff < tolerance);
2900
2901        // case 3: pow(12, 0.35) ~= 2.386876
2902        let x: FixedU64 = 12.into();
2903        let p = FixedU64::saturating_from_rational(35, 100); // 0.35
2904        let result = fixed_pow(&x, &p).unwrap();
2905        let expected = FixedU64::saturating_from_rational(2_386_876, 1_000_000);
2906
2907        let diff = if result > expected {
2908            result.saturating_sub(expected)
2909        } else {
2910            expected.saturating_sub(result)
2911        };
2912        assert!(diff < tolerance);
2913    }
2914
2915    #[test]
2916    fn fixed_pow_fractional_base_integer_exp() {
2917        let tolerance = FixedU64::from_inner(1_000_000);
2918
2919        // case 1: pow(1.5, 2) = 2.25
2920        let x = FixedU64::saturating_from_rational(3, 2);
2921        let p: FixedU64 = 2.into();
2922        let result = fixed_pow(&x, &p).unwrap();
2923        let expected = FixedU64::saturating_from_rational(9, 4);
2924
2925        let diff = if result > expected {
2926            result.saturating_sub(expected)
2927        } else {
2928            expected.saturating_sub(result)
2929        };
2930        assert!(diff < tolerance);
2931
2932        // case 2: pow(3.5, 4) = 150.0625
2933        let x = FixedU64::saturating_from_rational(7, 2); // 3.5
2934        let p: FixedU64 = 4.into();
2935        let result = fixed_pow(&x, &p).unwrap();
2936        let expected = FixedU64::from_inner(150062500000);
2937        assert_eq!(result, expected);
2938    }
2939
2940    #[test]
2941    fn fixed_pow_negative_base_integer_exp() {
2942        // case 1: pow(-2, 3) = -8
2943        let x: FixedI64 = (-2).into();
2944        let p: FixedI64 = 3.into();
2945        let result = fixed_pow(&x, &p).unwrap();
2946        let expected: FixedI64 = (-8).into();
2947        assert_eq!(result, expected);
2948
2949        // case 2: pow(-6, 5) = -7776
2950        let x: FixedI64 = (-6).into();
2951        let p: FixedI64 = 5.into();
2952        let result = fixed_pow(&x, &p).unwrap();
2953        let expected: FixedI64 = (-7776).into();
2954        assert_eq!(result, expected);
2955
2956        // case 3: pow(-4, 2) = 16
2957        let x: FixedI64 = (-4).into();
2958        let p: FixedI64 = 2.into();
2959        let result = fixed_pow(&x, &p).unwrap();
2960        let expected: FixedI64 = (16).into();
2961        assert_eq!(result, expected);
2962    }
2963
2964    #[test]
2965    fn fixed_pow_negative_base_fractional_exp() {
2966        // case 1: pow(-4, 1/2) -> None
2967        let x: FixedI64 = (-4).into();
2968        let p = FixedI64::saturating_from_rational(1, 2);
2969        let result = fixed_pow(&x, &p);
2970        assert!(result.is_none());
2971
2972        // case 2: pow(-8, 1/3) -> None
2973        let x: FixedI64 = (-8).into();
2974        let p = FixedI64::saturating_from_rational(1, 3);
2975
2976        let result = fixed_pow(&x, &p);
2977        assert!(result.is_none())
2978    }
2979
2980    #[test]
2981    fn fixed_pow_large_integer_overflow_saturates() {
2982        // 2^128 overflows FixedU64
2983        let x: FixedU64 = 2.into();
2984        let p: FixedU64 = 128.into();
2985
2986        let result = fixed_pow(&x, &p).unwrap();
2987        let expected = u64::MAX.into();
2988        assert_eq!(result, expected);
2989    }
2990
2991    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2992    // `````````````````````````````` FIXED_SQRT_NEWTON ``````````````````````````````
2993    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2994
2995    #[test]
2996    fn fixed_sqrt_newton_standard_cases() {
2997        // case 1: x = 81 -> 9
2998        let x: FixedU64 = (81).into();
2999        let result = fixed_sqrt_newton(&x);
3000        let expected = 9.into();
3001        assert_eq!(result, expected);
3002
3003        // case 2: x = 49 -> 7
3004        let x: FixedU64 = (49).into();
3005        let result = fixed_sqrt_newton(&x);
3006        let expected = 7.into();
3007        assert_eq!(result, expected);
3008
3009        // case 3: x = 10 -> 3.162277660
3010        let x: FixedU64 = 10.into();
3011        let result = fixed_sqrt_newton(&x);
3012        let expected: FixedU64 = FixedU64::from_inner(3162277660);
3013        assert_eq!(result, expected);
3014
3015        // case 4: x = 7 -> 2.2645751311
3016        let x: FixedU64 = 7.into();
3017        let result = fixed_sqrt_newton(&x);
3018        let expected: FixedU64 = FixedU64::from_inner(2645751311);
3019        assert_eq!(result, expected);
3020    }
3021
3022    #[test]
3023    fn fixed_sqrt_newton_edge_cases() {
3024        // case 1: x = 0 -> 0
3025        let x: FixedU64 = (0).into();
3026        let result = fixed_sqrt_newton(&x);
3027        let expected = 0.into();
3028        assert_eq!(result, expected);
3029
3030        // case 2: x < 0 : -9 -> 0
3031        let x: FixedI64 = (-9).into();
3032        let result = fixed_sqrt_newton(&x);
3033        let expected = 0.into();
3034        assert_eq!(result, expected);
3035    }
3036
3037    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3038    // ``````````````````````````````````` FIXED_PI ``````````````````````````````````
3039    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3040
3041    #[test]
3042    fn fixed_pi_value_check() {
3043        // fixed_pi() -> 3.141592920
3044        let actual: FixedU64 = fixed_pi();
3045        let expected = FixedU64::from_inner(3141592920);
3046        assert_eq!(actual, expected);
3047    }
3048
3049    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3050    // `````````````````````````````` RANGE_REDUCES_SQRT `````````````````````````````
3051    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3052
3053    #[test]
3054    fn range_reduce_sqrt_already_in_range() {
3055        // case 1: y = 1 (already at target, no reduction needed)
3056        let y: FixedU64 = 1.into();
3057        let (reduced, k) = range_reduce_sqrt(y);
3058        assert_eq!(reduced, y);
3059        assert_eq!(k, 0);
3060
3061        // case 2: y = 1.5 (within [0.5, 1.5], no reduction)
3062        let y = FixedU64::saturating_from_rational(3, 2);
3063        let (reduced, k) = range_reduce_sqrt(y);
3064        assert_eq!(reduced, y);
3065        assert_eq!(k, 0);
3066
3067        // case 3: y = 0.5 (at lower bound, no reduction)
3068        let y = FixedU64::saturating_from_rational(1, 2);
3069        let (reduced, k) = range_reduce_sqrt(y);
3070        assert_eq!(reduced, y);
3071        assert_eq!(k, 0);
3072
3073        // case 4: y = 0.75 (within range)
3074        let y = FixedU64::saturating_from_rational(3, 4);
3075        let (reduced, k) = range_reduce_sqrt(y);
3076        assert_eq!(reduced, y);
3077        assert_eq!(k, 0);
3078    }
3079
3080    #[test]
3081    fn range_reduce_sqrt_needs_reduction_above_range() {
3082        // Reduced value should be in range [0.5, 1.5]
3083        let half = FixedU64::saturating_from_rational(1, 2);
3084        let one_half = FixedU64::saturating_from_rational(3, 2);
3085
3086        // case 1: y = 4 (needs reduction)
3087        // sqrt(4) = 2, sqrt(2) = 1.414 (within range)
3088        let y: FixedU64 = 4.into();
3089        let (reduced, k) = range_reduce_sqrt(y);
3090        // 2 reductions needed
3091        assert_eq!(k, 2);
3092        assert!(reduced >= half && reduced <= one_half);
3093
3094        // case 2: y = 16 (needs multiple reductions)
3095        // sqrt(16) = 4, sqrt(4) = 2, sqrt(2) = 1.414
3096        let y: FixedU64 = 16.into();
3097        let (reduced, k) = range_reduce_sqrt(y);
3098        assert_eq!(k, 3); // 3 reductions needed
3099        assert!(reduced >= half && reduced <= one_half);
3100
3101        // case 3: y = 100 (large value)
3102        let y: FixedU64 = 100.into();
3103        let (reduced, k) = range_reduce_sqrt(y);
3104        assert!(k > 0);
3105        assert!(reduced >= half && reduced <= one_half);
3106    }
3107
3108    #[test]
3109    fn range_reduce_sqrt_needs_reduction_below_range() {
3110        let half = FixedU64::saturating_from_rational(1, 2);
3111        let one_half = FixedU64::saturating_from_rational(3, 2);
3112
3113        // case 1: y = 0.25 (below range, needs reduction)
3114        // sqrt(0.25) = 0.5 (now in range)
3115        let y = FixedU64::saturating_from_rational(1, 4);
3116        let (reduced, k) = range_reduce_sqrt(y);
3117        assert_eq!(k, 1);
3118        assert!(reduced >= half && reduced <= one_half);
3119
3120        // case 2: y = 0.01 (very small, needs multiple reductions)
3121        let y = FixedU64::saturating_from_rational(1, 100);
3122        let (reduced, k) = range_reduce_sqrt(y);
3123        assert!(k > 0);
3124        assert_eq!(k, 3);
3125        assert!(reduced >= half && reduced <= one_half);
3126
3127        // case 3: y = 0.0001 (very small, needs multiple reductions)
3128        let y = FixedU64::saturating_from_rational(1, 10000);
3129        let (reduced, k) = range_reduce_sqrt(y);
3130        assert!(k > 0);
3131        assert_eq!(k, 4);
3132        assert!(reduced >= half && reduced <= one_half);
3133    }
3134
3135    #[test]
3136    fn range_reduce_sqrt_edge_cases() {
3137        // case 1: y = 0 (zero input)
3138        let y: FixedU64 = 0.into();
3139        let (reduced, k) = range_reduce_sqrt(y);
3140
3141        // sqrt(0) = 0, which is outside [0.5, 1.5]
3142        // But the function should handle this gracefully
3143        assert_eq!(reduced, y);
3144        assert_eq!(k, 0);
3145
3146        // case 2: y very close to 1 (minimal difference)
3147        let y = FixedU64::from_inner(1000000001); // 1.000000001
3148        let (reduced, k) = range_reduce_sqrt(y);
3149
3150        // Should not need reduction (within tolerance)
3151        assert_eq!(k, 0);
3152        assert_eq!(reduced, y);
3153
3154        // case 3: y = u32::MAX (very large value)
3155        let y: FixedU64 = (u32::MAX as u64).into();
3156        let (reduced, k) = range_reduce_sqrt(y);
3157
3158        // Should reduce many times
3159        assert!(k > 0);
3160        let half = FixedU64::saturating_from_rational(1, 2);
3161        let one_half = FixedU64::saturating_from_rational(3, 2);
3162        assert!(reduced >= half && reduced <= one_half);
3163    }
3164
3165    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3166    // ````````````````````````````````` LN_NEAR_ONE `````````````````````````````````
3167    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3168
3169    #[test]
3170    fn ln_near_one_at_one() {
3171        // case 1: y = 1, ln(1) = 0
3172        let y: FixedU64 = 1.into();
3173        let result = ln_near_one(y);
3174        let expected: FixedU64 = 0.into();
3175        assert_eq!(result, expected);
3176    }
3177
3178    #[test]
3179    fn ln_near_one_slightly_above_one() {
3180        // case 1: y = 1.1, ln(1.1) ~= 0.095310176
3181        let y = FixedU64::saturating_from_rational(11, 10);
3182        let result = ln_near_one(y);
3183        let expected = FixedU64::from_inner(95310176);
3184        assert_eq!(result, expected);
3185
3186        // case 2: y = 1.2, ln(1.2) ~= 0.182321552
3187        let y = FixedU64::saturating_from_rational(6, 5);
3188        let result = ln_near_one(y);
3189        let expected = FixedU64::from_inner(182321552);
3190        assert_eq!(result, expected);
3191
3192        // case 3: y = 1.5, ln(1.5) ~= 0.405465100
3193        let y = FixedU64::saturating_from_rational(3, 2);
3194        let result = ln_near_one(y);
3195        let expected = FixedU64::from_inner(405465100);
3196        assert_eq!(result, expected);
3197    }
3198
3199    #[test]
3200    fn ln_near_one_slightly_below_one() {
3201        // case 1: y = 0.9, ln(0.9) ~= -0.105360510
3202        let y = FixedU64::saturating_from_rational(9, 10);
3203        let _result = ln_near_one(y);
3204        // Result will be negative (but FixedU64 is unsigned, so this might underflow)
3205        // For signed types:
3206        let y_signed = FixedI64::saturating_from_rational(9, 10);
3207        let result_signed = ln_near_one(y_signed);
3208        let expected = FixedI64::from_inner(-105360510);
3209        assert_eq!(result_signed, expected);
3210
3211        // case 2: y = 0.8, ln(0.8) ~= -0.223143548
3212        let y = FixedI64::saturating_from_rational(4, 5);
3213        let result = ln_near_one(y);
3214        let expected = FixedI64::from_inner(-223143548);
3215        assert_eq!(result, expected);
3216
3217        // case 3: y = 0.5, ln(0.5) ~= -0.693147174
3218        let y = FixedI64::saturating_from_rational(1, 2);
3219        let result = ln_near_one(y);
3220        let expected = FixedI64::from_inner(-693147174);
3221        assert_eq!(result, expected);
3222    }
3223
3224    #[test]
3225    fn ln_near_one_very_close_to_one() {
3226        // case 1: y = 1.01, ln(1.01) ~= 0.009950330
3227        let y = FixedU64::saturating_from_rational(101, 100);
3228        let result = ln_near_one(y);
3229        let expected = FixedU64::from_inner(9950330);
3230        assert_eq!(result, expected);
3231
3232        // case 2: y = 1.001, ln(1.001) ~= 0.000999500
3233        let y = FixedU64::saturating_from_rational(1001, 1000);
3234        let result = ln_near_one(y);
3235        let expected = FixedU64::from_inner(999500);
3236        assert_eq!(result, expected);
3237
3238        // case 3: y = 0.99, ln(0.99) ~= -0.010050334
3239        let y = FixedI64::saturating_from_rational(99, 100);
3240        let result = ln_near_one(y);
3241        let expected = FixedI64::from_inner(-10050334);
3242        assert_eq!(result, expected);
3243
3244        // case 4: y = 0.999, ln(0.999) ~= -0.001000500
3245        let y = FixedI64::saturating_from_rational(999, 1000);
3246        let result = ln_near_one(y);
3247        let expected = FixedI64::from_inner(-1000500);
3248        assert_eq!(result, expected);
3249    }
3250
3251    #[test]
3252    fn ln_near_one_edge_cases() {
3253        // case 1: Very small positive deviation
3254        // y = 1.0001, ln(1.0001) ~= 0.000099994
3255        let y = FixedU64::saturating_from_rational(10001, 10000);
3256        let result = ln_near_one(y);
3257        let expected = FixedU64::from_inner(99994);
3258        assert_eq!(result, expected);
3259
3260        // case 2: Very small negative deviation
3261        // y = 0.9999, ln(0.9999) ~= -0.000100004
3262        let y = FixedI64::saturating_from_rational(9999, 10000);
3263        let result = ln_near_one(y);
3264        let expected = FixedI64::from_inner(-100004);
3265        assert_eq!(result, expected);
3266    }
3267
3268    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3269    // ```````````````````````````` DYNAMIC_MAX_ITERATIONS ```````````````````````````
3270    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3271    
3272    #[test]
3273    fn dynamic_max_iterations_zero_input() {
3274        // case 1: x = 0, should return minimum of 1 iteration
3275        let x: FixedU64 = 0.into();
3276        let result = dynamic_max_iterations(&x);
3277        assert_eq!(result, 1);
3278    }
3279
3280    #[test]
3281    fn dynamic_max_iterations_one_input() {
3282        // case 1: x = 1, int_part = 1
3283        // iterations = 1 * DECIMAL_PLACES + 1 = 1 * 9 + 1 = 10
3284        let x: FixedU64 = 1.into();
3285        let result = dynamic_max_iterations(&x);
3286        assert_eq!(result, 10); // 1 * 9 + 1
3287    }
3288
3289    #[test]
3290    fn dynamic_max_iterations_small_integers() {
3291        // case 1: x = 2, int_part = 2
3292        // iterations = 2 * 9 + 1 = 19
3293        let x: FixedU64 = 2.into();
3294        let result = dynamic_max_iterations(&x);
3295        assert_eq!(result, 19);
3296
3297        // case 2: x = 5, int_part = 5
3298        // iterations = 5 * 9 + 1 = 46
3299        let x: FixedU64 = 5.into();
3300        let result = dynamic_max_iterations(&x);
3301        assert_eq!(result, 46);
3302
3303        // case 3: x = 10, int_part = 10
3304        // iterations = 10 * 9 + 1 = 91
3305        let x: FixedU64 = 10.into();
3306        let result = dynamic_max_iterations(&x);
3307        assert_eq!(result, 91);
3308    }
3309
3310    #[test]
3311    fn dynamic_max_iterations_fractional_values() {
3312        // case 1: x = 0.5, int_part = 0
3313        // iterations = 0 * 9 + 1 = 1
3314        let x = FixedU64::saturating_from_rational(1, 2);
3315        let result = dynamic_max_iterations(&x);
3316        assert_eq!(result, 1);
3317
3318        // case 2: x = 0.9, int_part = 0
3319        // iterations = 0 * 99 + 1 = 1
3320        let x = FixedU64::saturating_from_rational(9, 10);
3321        let result = dynamic_max_iterations(&x);
3322        assert_eq!(result, 1);
3323
3324        // case 3: x = 1.5, int_part = 1
3325        // iterations = 1 * 9 + 1 = 10
3326        let x = FixedU64::saturating_from_rational(3, 2);
3327        let result = dynamic_max_iterations(&x);
3328        assert_eq!(result, 10);
3329
3330        // case 4: x = 2.7, int_part = 2
3331        // iterations = 2 * 9 + 1 = 19
3332        let x = FixedU64::saturating_from_rational(27, 10);
3333        let result = dynamic_max_iterations(&x);
3334        assert_eq!(result, 19);
3335    }
3336
3337    #[test]
3338    fn dynamic_max_iterations_negative_values() {
3339        // case 1: x = -1, abs = 1, int_part = 1
3340        // iterations = 1 * 9 + 1 = 10
3341        let x: FixedI64 = (-1).into();
3342        let result = dynamic_max_iterations(&x);
3343        assert_eq!(result, 10);
3344
3345        // case 2: x = -5, abs = 5, int_part = 5
3346        // iterations = 5 * 9 + 1 = 46
3347        let x: FixedI64 = (-5).into();
3348        let result = dynamic_max_iterations(&x);
3349        assert_eq!(result, 46);
3350
3351        // case 3: x = -10, abs = 10, int_part = 10
3352        // iterations = 10 * 9 + 1 = 91
3353        let x: FixedI64 = (-10).into();
3354        let result = dynamic_max_iterations(&x);
3355        assert_eq!(result, 91);
3356
3357        // case 4: x = -0.5, abs = 0.5, int_part = 0
3358        // iterations = 0 * 9 + 1 = 1
3359        let x = FixedI64::saturating_from_rational(-1, 2);
3360        let result = dynamic_max_iterations(&x);
3361        assert_eq!(result, 1);
3362    }
3363
3364    #[test]
3365    fn dynamic_max_iterations_large_values() {
3366        // case 1: x = 100, int_part = 100
3367        // iterations = 100 * 9 + 1 = 901
3368        let x: FixedU64 = 100.into();
3369        let result = dynamic_max_iterations(&x);
3370        assert_eq!(result, 901);
3371
3372        // case 2: x = 1000, int_part = 1000
3373        // iterations = 1000 * 9 + 1 = 9001
3374        let x: FixedU64 = 1000.into();
3375        let result = dynamic_max_iterations(&x);
3376        assert_eq!(result, 9001);
3377
3378        // case 3: x = 10000, int_part = 10000
3379        // iterations = 10000 * 9 + 1 = 90001
3380        let x: FixedU64 = 10000.into();
3381        let result = dynamic_max_iterations(&x);
3382        assert_eq!(result, 90001);
3383    }
3384
3385    #[test]
3386    fn dynamic_max_iterations_saturation_check() {
3387        // case 1: Overflow values
3388        // This depends on the max value of the fixed-point type
3389        let x: FixedU64 = (u32::MAX as u64).into();
3390        let result = dynamic_max_iterations(&x);
3391
3392        // Should return a valid value without panicking
3393        assert!(result >= 1);
3394
3395        // Result should be reasonable (not u32::MAX due to saturation)
3396        // int_part = u32::MAX, iterations = u32::MAX * 5 + 1
3397        // This will saturate to u32::MAX
3398        assert_eq!(result, u32::MAX);
3399    }
3400
3401    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3402    // ````````````````````````````````` TO_U32_FLOOR ````````````````````````````````
3403    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3404
3405    #[test]
3406    fn to_u32_floor_zero_input() {
3407        // case 1: x = 0 -> 0
3408        let x: FixedU64 = 0.into();
3409        let result = to_u32_floor(&x);
3410        assert_eq!(result, 0);
3411
3412        // case 2: x = 0 (signed)
3413        let x: FixedI64 = 0.into();
3414        let result = to_u32_floor(&x);
3415        assert_eq!(result, 0);
3416    }
3417
3418    #[test]
3419    fn to_u32_floor_positive_integers() {
3420        // case 1: x = 1 -> 1
3421        let x: FixedU64 = 1.into();
3422        let result = to_u32_floor(&x);
3423        assert_eq!(result, 1);
3424
3425        // case 2: x = 10 -> 10
3426        let x: FixedU64 = 10.into();
3427        let result = to_u32_floor(&x);
3428        assert_eq!(result, 10);
3429
3430        // case 3: x = 100 -> 100
3431        let x: FixedU64 = 100.into();
3432        let result = to_u32_floor(&x);
3433        assert_eq!(result, 100);
3434
3435        // case 4: x = 1000 -> 1000
3436        let x: FixedU64 = 1000.into();
3437        let result = to_u32_floor(&x);
3438        assert_eq!(result, 1000);
3439
3440        // case 5: x = 42 -> 42
3441        let x: FixedU64 = 42.into();
3442        let result = to_u32_floor(&x);
3443        assert_eq!(result, 42);
3444    }
3445
3446    #[test]
3447    fn to_u32_floor_fractional_values() {
3448        // case 1: x = 1.5 -> 1 (floor)
3449        let x = FixedU64::saturating_from_rational(3, 2);
3450        let result = to_u32_floor(&x);
3451        assert_eq!(result, 1);
3452
3453        // case 2: x = 2.7 -> 2 (floor)
3454        let x = FixedU64::saturating_from_rational(27, 10);
3455        let result = to_u32_floor(&x);
3456        assert_eq!(result, 2);
3457
3458        // case 3: x = 9.999 -> 9 (floor)
3459        let x = FixedU64::saturating_from_rational(9999, 1000);
3460        let result = to_u32_floor(&x);
3461        assert_eq!(result, 9);
3462
3463        // case 4: x = 100.1 -> 100 (floor)
3464        let x = FixedU64::saturating_from_rational(1001, 10);
3465        let result = to_u32_floor(&x);
3466        assert_eq!(result, 100);
3467
3468        // case 5: x = 0.5 -> 0 (floor)
3469        let x = FixedU64::saturating_from_rational(1, 2);
3470        let result = to_u32_floor(&x);
3471        assert_eq!(result, 0);
3472
3473        // case 6: x = 0.9 -> 0 (floor)
3474        let x = FixedU64::saturating_from_rational(9, 10);
3475        let result = to_u32_floor(&x);
3476        assert_eq!(result, 0);
3477    }
3478
3479    #[test]
3480    fn to_u32_floor_negative_values() {
3481        // case 1: x = -1 -> 0 (clamped)
3482        let x: FixedI64 = (-1).into();
3483        let result = to_u32_floor(&x);
3484        assert_eq!(result, 0);
3485
3486        // case 2: x = -10 -> 0 (clamped)
3487        let x: FixedI64 = (-10).into();
3488        let result = to_u32_floor(&x);
3489        assert_eq!(result, 0);
3490
3491        // case 3: x = -0.5 -> 0 (clamped)
3492        let x = FixedI64::saturating_from_rational(-1, 2);
3493        let result = to_u32_floor(&x);
3494        assert_eq!(result, 0);
3495
3496        // case 4: x = -100.5 -> 0 (clamped)
3497        let x = FixedI64::saturating_from_rational(-201, 2);
3498        let result = to_u32_floor(&x);
3499        assert_eq!(result, 0);
3500
3501        // case 5: x = i64::MIN -> 0 (clamped)
3502        let x: FixedI64 = i64::MIN.into();
3503        let result = to_u32_floor(&x);
3504        assert_eq!(result, 0);
3505    }
3506
3507    #[test]
3508    fn to_u32_floor_boundary_values() {
3509        // case 1: x = u32::MAX (exactly at boundary)
3510        let x: FixedU64 = (u32::MAX as u64).into();
3511        let result = to_u32_floor(&x);
3512        assert_eq!(result, u32::MAX);
3513
3514        // case 2: x = u32::MAX + 1 (overflow, should saturate)
3515        let x: FixedU64 = ((u32::MAX as u64) + 1).into();
3516        let result = to_u32_floor(&x);
3517        assert_eq!(result, u32::MAX);
3518
3519        // case 3: x = u32::MAX + 100 (overflow, should saturate)
3520        let x: FixedU64 = ((u32::MAX as u64) + 100).into();
3521        let result = to_u32_floor(&x);
3522        assert_eq!(result, u32::MAX);
3523    }
3524
3525    #[test]
3526    fn to_u32_floor_large_values() {
3527        // case 1: x = 1000000 -> 1000000
3528        let x: FixedU64 = 1000000.into();
3529        let result = to_u32_floor(&x);
3530        assert_eq!(result, 1000000);
3531
3532        // case 2: x = 10000000 -> 10000000
3533        let x: FixedU64 = 10000000.into();
3534        let result = to_u32_floor(&x);
3535        assert_eq!(result, 10000000);
3536
3537        // case 3: x = u64::MAX (overflow, should saturate)
3538        let x: FixedU64 = u64::MAX.into();
3539        let result = to_u32_floor(&x);
3540        assert_eq!(result, u32::MAX);
3541    }
3542
3543    #[test]
3544    fn to_u32_floor_from_inner_representation() {
3545        // case 1: FixedU64 with DIV = 1_000_000_000
3546        // inner = 5_000_000_000 represents 5.0
3547        let x = FixedU64::from_inner(5_000_000_000);
3548        let result = to_u32_floor(&x);
3549        assert_eq!(result, 5);
3550
3551        // case 2: inner = 5_500_000_000 represents 5.5
3552        let x = FixedU64::from_inner(5_500_000_000);
3553        let result = to_u32_floor(&x);
3554        assert_eq!(result, 5);
3555
3556        // case 3: inner = 999_999_999 represents 0.999999999
3557        let x = FixedU64::from_inner(999_999_999);
3558        let result = to_u32_floor(&x);
3559        assert_eq!(result, 0);
3560
3561        // case 4: inner = 1_000_000_000 represents 1.0
3562        let x = FixedU64::from_inner(1_000_000_000);
3563        let result = to_u32_floor(&x);
3564        assert_eq!(result, 1);
3565    }
3566
3567    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3568    // ````````````````````````` FIXED_SIGNED_CAST - FixedI64 ````````````````````````
3569    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3570    //
3571    // FixedI64 is an identity implementation: Signed = Self.
3572    // Every conversion is a no-op - the value passes through unchanged.
3573 
3574    #[test]
3575    fn fixed_signed_cast_i64_checked_into() {
3576        // Any FixedI64 value should always yield Some(itself).
3577        let x = FixedI64::saturating_from_integer(5);
3578        assert_eq!(FixedI64::checked_into(x), Some(x));
3579 
3580        let x = FixedI64::saturating_from_integer(-7);
3581        assert_eq!(FixedI64::checked_into(x), Some(x));
3582 
3583        let x = FixedI64::zero();
3584        assert_eq!(FixedI64::checked_into(x), Some(x));
3585 
3586        let x = FixedI64::max_value();
3587        assert_eq!(FixedI64::checked_into(x), Some(x));
3588 
3589        let x = FixedI64::min_value();
3590        assert_eq!(FixedI64::checked_into(x), Some(x));
3591    }
3592 
3593    #[test]
3594    fn fixed_signed_cast_i64_saturated_into() {
3595        // Identity: saturated_into on a signed type is always a no-op.
3596        let x = FixedI64::saturating_from_integer(42);
3597        assert_eq!(FixedI64::saturated_into(x), x);
3598 
3599        let x = FixedI64::saturating_from_integer(-42);
3600        assert_eq!(FixedI64::saturated_into(x), x);
3601 
3602        let x = FixedI64::max_value();
3603        assert_eq!(FixedI64::saturated_into(x), x);
3604    }
3605 
3606    #[test]
3607    fn fixed_signed_cast_i64_checked_from() {
3608        // Identity: checked_from on a signed type always yields Some(itself).
3609        let x = FixedI64::saturating_from_integer(3);
3610        assert_eq!(FixedI64::checked_from(x), Some(x));
3611 
3612        let x = FixedI64::saturating_from_integer(-3);
3613        assert_eq!(FixedI64::checked_from(x), Some(x));
3614 
3615        let x = FixedI64::min_value();
3616        assert_eq!(FixedI64::checked_from(x), Some(x));
3617    }
3618 
3619    #[test]
3620    fn fixed_signed_cast_i64_saturated_from() {
3621        // Identity: saturated_from on a signed type is always a no-op.
3622        let x = FixedI64::saturating_from_integer(10);
3623        assert_eq!(FixedI64::saturated_from(x), x);
3624 
3625        let x = FixedI64::saturating_from_integer(-10);
3626        assert_eq!(FixedI64::saturated_from(x), x);
3627    }
3628 
3629    #[test]
3630    fn fixed_signed_cast_i64_saturating_closure() {
3631        // saturating wraps a signed closure - on FixedI64 it is a pure pass-through.
3632        let x = FixedI64::saturating_from_integer(4);
3633        let result = FixedI64::saturating(x, |v| v.saturating_add(FixedI64::saturating_from_integer(1)));
3634        assert_eq!(result, FixedI64::saturating_from_integer(5));
3635 
3636        // Closure that negates: result is negative but still representable.
3637        let x = FixedI64::saturating_from_integer(3);
3638        let result = FixedI64::saturating(x, |v| v.saturating_mul(FixedI64::saturating_from_integer(-1)));
3639        assert_eq!(result, FixedI64::saturating_from_integer(-3));
3640    }
3641 
3642    #[test]
3643    fn fixed_signed_cast_i64_checked_closure() {
3644        // checked wraps a signed closure - returns Some for in-range results,
3645        // None when the result saturates to min_value() or max_value().
3646        let x = FixedI64::saturating_from_integer(7);
3647        let result = FixedI64::checked(x, |v| {
3648            v.unwrap_or(FixedI64::zero()).saturating_add(FixedI64::saturating_from_integer(1))
3649        });
3650        assert_eq!(result, Some(FixedI64::saturating_from_integer(8)));
3651    }
3652
3653    #[test]
3654    fn fixed_signed_cast_i64_checked_closure_overflow_is_none() {
3655        // Closure overflows to max_value via saturating arithmetic - checked returns None.
3656        let x = FixedI64::max_value();
3657        let result = FixedI64::checked(x, |v| {
3658            v.unwrap_or(FixedI64::zero()).saturating_add(FixedI64::one())
3659        });
3660        assert_eq!(result, None);
3661    }
3662 
3663    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3664    // ````````````````````````` FIXED_SIGNED_CAST - FixedI128 ```````````````````````
3665    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3666    //
3667    // FixedI128 is also an identity implementation: Signed = Self.
3668 
3669    #[test]
3670    fn fixed_signed_cast_i128_checked_into() {
3671        let x = FixedI128::saturating_from_integer(100);
3672        assert_eq!(FixedI128::checked_into(x), Some(x));
3673 
3674        let x = FixedI128::saturating_from_integer(-100);
3675        assert_eq!(FixedI128::checked_into(x), Some(x));
3676 
3677        let x = FixedI128::zero();
3678        assert_eq!(FixedI128::checked_into(x), Some(x));
3679 
3680        let x = FixedI128::max_value();
3681        assert_eq!(FixedI128::checked_into(x), Some(x));
3682 
3683        let x = FixedI128::min_value();
3684        assert_eq!(FixedI128::checked_into(x), Some(x));
3685    }
3686 
3687    #[test]
3688    fn fixed_signed_cast_i128_saturated_into() {
3689        let x = FixedI128::saturating_from_integer(999);
3690        assert_eq!(FixedI128::saturated_into(x), x);
3691 
3692        let x = FixedI128::saturating_from_integer(-999);
3693        assert_eq!(FixedI128::saturated_into(x), x);
3694    }
3695 
3696    #[test]
3697    fn fixed_signed_cast_i128_checked_from() {
3698        let x = FixedI128::saturating_from_integer(50);
3699        assert_eq!(FixedI128::checked_from(x), Some(x));
3700 
3701        let x = FixedI128::saturating_from_integer(-50);
3702        assert_eq!(FixedI128::checked_from(x), Some(x));
3703    }
3704 
3705    #[test]
3706    fn fixed_signed_cast_i128_saturated_from() {
3707        let x = FixedI128::saturating_from_integer(77);
3708        assert_eq!(FixedI128::saturated_from(x), x);
3709 
3710        let x = FixedI128::saturating_from_integer(-77);
3711        assert_eq!(FixedI128::saturated_from(x), x);
3712    }
3713 
3714    #[test]
3715    fn fixed_signed_cast_i128_saturating_closure() {
3716        let x = FixedI128::saturating_from_integer(10);
3717        let result = FixedI128::saturating(x, |v| v.saturating_add(FixedI128::saturating_from_integer(5)));
3718        assert_eq!(result, FixedI128::saturating_from_integer(15));
3719 
3720        let x = FixedI128::saturating_from_integer(-10);
3721        let result = FixedI128::saturating(x, |v| v.saturating_mul(FixedI128::saturating_from_integer(2)));
3722        assert_eq!(result, FixedI128::saturating_from_integer(-20));
3723    }
3724 
3725    #[test]
3726    fn fixed_signed_cast_i128_checked_closure() {
3727        let x = FixedI128::saturating_from_integer(3);
3728        let result = FixedI128::checked(x, |v| {
3729            v.unwrap_or(FixedI128::zero()).saturating_add(FixedI128::saturating_from_integer(2))
3730        });
3731        assert_eq!(result, Some(FixedI128::saturating_from_integer(5)));
3732    }
3733 
3734    #[test]
3735    fn fixed_signed_cast_i128_checked_closure_overflow_is_none() {
3736        // Closure overflows to max_value via saturating arithmetic - checked returns None.
3737        let x = FixedI128::max_value();
3738        let result = FixedI128::checked(x, |v| {
3739            v.unwrap_or(FixedI128::zero()).saturating_add(FixedI128::one())
3740        });
3741        assert_eq!(result, None);
3742    }
3743
3744    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3745    // ````````````````````````` FIXED_SIGNED_CAST - FixedU64 ````````````````````````
3746    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3747    //
3748    // FixedU64 bridges to FixedI128. u64 inner always fits in i128,
3749    // so checked_into / saturated_into are always infallible.
3750    // The reverse direction can fail when the signed result is negative
3751    // or exceeds u64::MAX.
3752 
3753    #[test]
3754    fn fixed_signed_cast_u64_checked_into_always_some() {
3755        // u64 inner always representable in i128.
3756        let x = FixedU64::saturating_from_integer(0);
3757        assert!(FixedU64::checked_into(x).is_some());
3758 
3759        let x = FixedU64::saturating_from_integer(1);
3760        let signed = FixedU64::checked_into(x).unwrap();
3761        // The signed inner should equal the unsigned inner cast to i128.
3762        assert_eq!(signed.into_inner(), x.into_inner() as i128);
3763 
3764        let x = FixedU64::max_value();
3765        assert!(FixedU64::checked_into(x).is_some());
3766    }
3767 
3768    #[test]
3769    fn fixed_signed_cast_u64_saturated_into_round_trip() {
3770        // saturated_into then checked_from should recover the original value
3771        // for any representable FixedU64.
3772        let values = [
3773            FixedU64::saturating_from_integer(0),
3774            FixedU64::saturating_from_integer(1),
3775            FixedU64::saturating_from_integer(1000),
3776            FixedU64::saturating_from_rational(3, 2), // 1.5
3777            FixedU64::max_value(),
3778        ];
3779        for x in values {
3780            let signed = FixedU64::saturated_into(x);
3781            let back = FixedU64::checked_from(signed).unwrap();
3782            assert_eq!(back, x, "round-trip failed for {x:?}");
3783        }
3784    }
3785 
3786    #[test]
3787    fn fixed_signed_cast_u64_checked_from_negative_is_none() {
3788        // Negative FixedI128 values cannot be represented as FixedU64.
3789        let neg = FixedI128::saturating_from_integer(-1);
3790        assert_eq!(FixedU64::checked_from(neg), None);
3791 
3792        let neg = FixedI128::saturating_from_integer(-100);
3793        assert_eq!(FixedU64::checked_from(neg), None);
3794 
3795        // Minimum i128 value - strongly negative.
3796        let neg = FixedI128::min_value();
3797        assert_eq!(FixedU64::checked_from(neg), None);
3798    }
3799 
3800    #[test]
3801    fn fixed_signed_cast_u64_checked_from_above_u64_max_is_none() {
3802        // FixedI128 inner > u64::MAX cannot fit in FixedU64.
3803        // Construct an i128 inner that represents a value just above u64::MAX.
3804        // FixedU64::DIV = 10^9; FixedI128::DIV = 10^18.
3805        // We need inner_i128 > u64::MAX (as a raw inner value of FixedI128).
3806        // u64::MAX as i128 = 18_446_744_073_709_551_615.
3807        let too_large = FixedI128::from_inner(u64::MAX as i128 + 1);
3808        assert_eq!(FixedU64::checked_from(too_large), None);
3809    }
3810 
3811    #[test]
3812    fn fixed_signed_cast_u64_checked_from_zero_is_some() {
3813        let zero = FixedI128::zero();
3814        assert_eq!(FixedU64::checked_from(zero), Some(FixedU64::zero()));
3815    }
3816 
3817    #[test]
3818    fn fixed_signed_cast_u64_saturated_from_negative_clamps_to_zero() {
3819        // Negative values saturate to zero (the unsigned floor).
3820        let neg = FixedI128::saturating_from_integer(-5);
3821        assert_eq!(FixedU64::saturated_from(neg), FixedU64::zero());
3822 
3823        let neg = FixedI128::min_value();
3824        assert_eq!(FixedU64::saturated_from(neg), FixedU64::zero());
3825    }
3826 
3827    #[test]
3828    fn fixed_signed_cast_u64_saturated_from_above_max_clamps_to_max() {
3829        // Values above u64::MAX saturate to u64::MAX.
3830        let too_large = FixedI128::from_inner(u64::MAX as i128 + 1);
3831        assert_eq!(FixedU64::saturated_from(too_large), FixedU64::from_inner(u64::MAX));
3832    }
3833 
3834    #[test]
3835    fn fixed_signed_cast_u64_saturating_closure_with_positive_result() {
3836        // Closure doubles the value - result stays within FixedU64 range.
3837        let x = FixedU64::saturating_from_integer(3);
3838        let result = FixedU64::saturating(x, |v| v.saturating_add(v));
3839        assert_eq!(result, FixedU64::saturating_from_integer(6));
3840    }
3841 
3842    #[test]
3843    fn fixed_signed_cast_u64_saturating_closure_negative_result_clamps() {
3844        // Closure produces a negative signed result - saturated_from clamps to 0.
3845        let x = FixedU64::saturating_from_integer(2);
3846        let result = FixedU64::saturating(x, |v| v.saturating_mul(FixedI128::saturating_from_integer(-1)));
3847        assert_eq!(result, FixedU64::zero());
3848    }
3849 
3850    #[test]
3851    fn fixed_signed_cast_u64_checked_closure_negative_result_is_none() {
3852        // Closure produces a negative signed result - checked_from returns None.
3853        let x = FixedU64::saturating_from_integer(5);
3854        let result = FixedU64::checked(x, |v| {
3855        // saturating_mul by the fixed-point -1.0 correctly negates because
3856        // fixed-point multiplication divides by DIV, cancelling the scale difference.
3857        // (Unlike saturating_add, which operates on raw inner values and would be wrong
3858        // with a FixedI128 integer constant - see the checked_closure_valid test.)
3859            v.unwrap().saturating_mul(FixedI128::saturating_from_integer(-1))
3860        });
3861        assert_eq!(result, None);
3862    }
3863 
3864    #[test]
3865    fn fixed_signed_cast_u64_checked_closure_valid_result_is_some() {
3866        // Closure adds one unit in FixedU64 scale.
3867        // The bridge casts raw inner bits into FixedI128, so arithmetic inside
3868        // the closure must use FixedU64::DIV (10^9) as "one unit", not
3869        // FixedI128::saturating_from_integer(1) which uses FixedI128::DIV (10^18).
3870        let x = FixedU64::saturating_from_integer(10);
3871        let one_unit = FixedI128::from_inner(FixedU64::DIV as i128); // = 1 in FixedU64 scale
3872        let result = FixedU64::checked(x, |v| {
3873            v.unwrap().saturating_add(one_unit)
3874        });
3875        assert_eq!(result, Some(FixedU64::saturating_from_integer(11)));
3876    }
3877 
3878    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3879    // ````````````````````````` FIXED_SIGNED_CAST - FixedU128 ```````````````````````
3880    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3881    //
3882    // FixedU128 bridges to FixedI128. Unlike FixedU64, the inner u128 value
3883    // can exceed i128::MAX, making checked_into fallible for large values.
3884 
3885    #[test]
3886    fn fixed_signed_cast_u128_checked_into_small_values_are_some() {
3887        let x = FixedU128::saturating_from_integer(0);
3888        assert!(FixedU128::checked_into(x).is_some());
3889 
3890        let x = FixedU128::saturating_from_integer(1);
3891        let signed = FixedU128::checked_into(x).unwrap();
3892        assert_eq!(signed.into_inner(), x.into_inner() as i128);
3893 
3894        let x = FixedU128::saturating_from_integer(1_000_000);
3895        assert!(FixedU128::checked_into(x).is_some());
3896    }
3897 
3898    #[test]
3899    fn fixed_signed_cast_u128_checked_into_above_i128_max_is_none() {
3900        // Inner value just above i128::MAX is unrepresentable in FixedI128.
3901        let too_large = FixedU128::from_inner(i128::MAX as u128 + 1);
3902        assert_eq!(FixedU128::checked_into(too_large), None);
3903 
3904        // Maximum FixedU128 value is definitely unrepresentable.
3905        assert_eq!(FixedU128::checked_into(FixedU128::max_value()), None);
3906    }
3907 
3908    #[test]
3909    fn fixed_signed_cast_u128_saturated_into_large_clamps_to_i128_max() {
3910        // Values above i128::MAX saturate to i128::MAX in the signed workspace.
3911        let too_large = FixedU128::from_inner(i128::MAX as u128 + 1);
3912        let signed = FixedU128::saturated_into(too_large);
3913        assert_eq!(signed, FixedI128::from_inner(i128::MAX));
3914 
3915        let signed_max = FixedU128::saturated_into(FixedU128::max_value());
3916        assert_eq!(signed_max, FixedI128::from_inner(i128::MAX));
3917    }
3918 
3919    #[test]
3920    fn fixed_signed_cast_u128_saturated_into_small_values_exact() {
3921        let x = FixedU128::saturating_from_integer(42);
3922        let signed = FixedU128::saturated_into(x);
3923        assert_eq!(signed.into_inner(), x.into_inner() as i128);
3924    }
3925 
3926    #[test]
3927    fn fixed_signed_cast_u128_checked_from_negative_is_none() {
3928        let neg = FixedI128::saturating_from_integer(-1);
3929        assert_eq!(FixedU128::checked_from(neg), None);
3930 
3931        let neg = FixedI128::min_value();
3932        assert_eq!(FixedU128::checked_from(neg), None);
3933    }
3934 
3935    #[test]
3936    fn fixed_signed_cast_u128_checked_from_non_negative_is_some() {
3937        let zero = FixedI128::zero();
3938        assert_eq!(FixedU128::checked_from(zero), Some(FixedU128::zero()));
3939 
3940        let pos = FixedI128::saturating_from_integer(100);
3941        let result = FixedU128::checked_from(pos).unwrap();
3942        assert_eq!(result.into_inner(), pos.into_inner() as u128);
3943    }
3944 
3945    #[test]
3946    fn fixed_signed_cast_u128_saturated_from_negative_clamps_to_zero() {
3947        let neg = FixedI128::saturating_from_integer(-99);
3948        assert_eq!(FixedU128::saturated_from(neg), FixedU128::zero());
3949 
3950        let neg = FixedI128::min_value();
3951        assert_eq!(FixedU128::saturated_from(neg), FixedU128::zero());
3952    }
3953 
3954    #[test]
3955    fn fixed_signed_cast_u128_saturated_from_non_negative_exact() {
3956        let pos = FixedI128::saturating_from_integer(500);
3957        let result = FixedU128::saturated_from(pos);
3958        assert_eq!(result.into_inner(), pos.into_inner() as u128);
3959 
3960        let zero = FixedI128::zero();
3961        assert_eq!(FixedU128::saturated_from(zero), FixedU128::zero());
3962    }
3963 
3964    #[test]
3965    fn fixed_signed_cast_u128_round_trip_for_representable_values() {
3966        let values = [
3967            FixedU128::saturating_from_integer(0),
3968            FixedU128::saturating_from_integer(1),
3969            FixedU128::saturating_from_integer(1_000_000),
3970            FixedU128::saturating_from_rational(7, 3),
3971        ];
3972        for x in values {
3973            let signed = FixedU128::saturated_into(x);
3974            let back = FixedU128::checked_from(signed).unwrap();
3975            assert_eq!(back, x, "round-trip failed for {x:?}");
3976        }
3977    }
3978 
3979    #[test]
3980    fn fixed_signed_cast_u128_saturating_closure_valid() {
3981        let x = FixedU128::saturating_from_integer(4);
3982        let result = FixedU128::saturating(x, |v| v.saturating_add(FixedI128::saturating_from_integer(6)));
3983        assert_eq!(result, FixedU128::saturating_from_integer(10));
3984    }
3985 
3986    #[test]
3987    fn fixed_signed_cast_u128_saturating_closure_negative_clamps_to_zero() {
3988        let x = FixedU128::saturating_from_integer(3);
3989        let result = FixedU128::saturating(x, |v| v.saturating_mul(FixedI128::saturating_from_integer(-2)));
3990        assert_eq!(result, FixedU128::zero());
3991    }
3992 
3993    #[test]
3994    fn fixed_signed_cast_u128_checked_closure_negative_is_none() {
3995        let x = FixedU128::saturating_from_integer(5);
3996        let result = FixedU128::checked(x, |v| {
3997            v.unwrap_or(FixedI128::zero())
3998                .saturating_mul(FixedI128::saturating_from_integer(-1))
3999        });
4000        assert_eq!(result, None);
4001    }
4002 
4003    #[test]
4004    fn fixed_signed_cast_u128_checked_closure_valid_is_some() {
4005        let x = FixedU128::saturating_from_integer(20);
4006        let result = FixedU128::checked(x, |v| {
4007            v.unwrap_or(FixedI128::zero())
4008                .saturating_add(FixedI128::saturating_from_integer(5))
4009        });
4010        assert_eq!(result, Some(FixedU128::saturating_from_integer(25)));
4011    }
4012 
4013    #[test]
4014    fn fixed_signed_cast_u128_checked_into_none_propagated_through_closure() {
4015        // When checked_into returns None, the closure receives None.
4016        // The closure must handle this case - here it falls back to zero.
4017        let too_large = FixedU128::from_inner(i128::MAX as u128 + 1);
4018        let result = FixedU128::checked(too_large, |v| {
4019            v.unwrap_or(FixedI128::zero()) // None -> zero -> not representable? No: zero is fine.
4020        });
4021        // zero is representable as FixedU128, so we get Some(0).
4022        assert_eq!(result, Some(FixedU128::zero()));
4023    }
4024 
4025}