ra_solana_math/
lib.rs

1#![doc = include_str!("../README.md")]
2//! # Solana Fixed-Point Math Library
3//!
4//! A high-performance fixed-point arithmetic library optimized for Solana programs.
5//! Uses 18 decimal places of precision (1e18 scale factor) with U256 for large number support.
6//!
7//! ## Features
8//!
9//! - **Fixed-point arithmetic**: Add, subtract, multiply, divide with overflow protection
10//! - **Advanced math**: Power (including fractional exponents), logarithms, square root, exponentials
11//! - **Optimized for Solana**: Minimal compute units, no dynamic loops, small stack footprint
12//! - **Large number support**: U256 backing for handling massive values safely
13//! - **Type-safe**: Comprehensive error handling with Anchor integration
14//!
15//! ## Examples
16//!
17//! ```rust,ignore
18//! use fixed_point_math::FixedPoint;
19//!
20//! // Create a fixed-point number from an integer
21//! let x = FixedPoint::from_int(5);
22//! let y = FixedPoint::from_int(2);
23//!
24//! // Perform arithmetic operations
25//! let sum = x.add(&y)?;       // 7.0
26//! let product = x.mul(&y)?;    // 10.0
27//! let quotient = x.div(&y)?;   // 2.5
28//!
29//! // Advanced operations
30//! let power = x.pow(&y)?;      // 5^2 = 25.0
31//! let sqrt = x.sqrt()?;        // √5 ≈ 2.236
32//! let log = x.ln()?;           // ln(5) ≈ 1.609
33//! ```
34
35
36use uint::construct_uint;
37use anchor_lang::{err, error, error_code, require, Result};
38
39
40construct_uint! {
41    pub struct U256(4);
42}
43
44
45/// Fixed-point math library optimized for Solana compute units.
46/// 
47/// All values use 18 decimal places of precision (scale factor of 1e18).
48/// This means 1.0 is represented as 1_000_000_000_000_000_000 internally.
49
50
51
52
53/// Scale factor for fixed-point arithmetic: 10^18
54/// 
55/// This constant defines the precision of the fixed-point representation.
56/// All fixed-point values are internally stored as integers multiplied by this scale.
57pub const SCALE: u128 = 1_000_000_000_000_000_000;
58
59/// Natural logarithm of 2: ln(2) ≈ 0.693147180559945309
60/// 
61/// Pre-computed constant used in logarithm and exponential calculations.
62const LN_2: U256 = U256([693_147_180_559_945_309, 0, 0, 0]);
63
64/// Natural logarithm of 10: ln(10) ≈ 2.302585092994045684
65/// 
66/// Pre-computed constant used for base-10 logarithm calculations.
67const LN_10: U256 = U256([2_302_585_092_994_045_684, 0, 0, 0]);
68
69/// Maximum safe input for exp function to prevent overflow
70/// 
71/// This limit ensures that e^x doesn't overflow the U256 representation.
72/// Approximately equal to ln(U256::MAX / SCALE).
73const MAX_EXP_INPUT: U256 = U256([7_237_005_577_332_262_321, 7, 0, 0]);
74
75/// Returns the scale factor as a U256.
76/// 
77/// This is a convenience function to avoid repeated conversions.
78#[inline]
79fn scale_u256() -> U256 {
80    U256::from(SCALE)
81}
82
83/// Performs (a * b) / divisor with overflow detection.
84/// 
85/// This function safely computes the result of multiplying two U256 values
86/// and dividing by a third, with special handling to prevent overflow during
87/// the intermediate multiplication step.
88///
89/// # Arguments
90///
91/// * `a` - First multiplicand
92/// * `b` - Second multiplicand
93/// * `divisor` - The divisor (must be non-zero)
94///
95/// # Returns
96///
97/// The result of (a * b) / divisor, or an error if overflow occurs or divisor is zero.
98///
99/// # Errors
100///
101/// * `MathError::DivisionByZero` - If divisor is zero
102/// * `MathError::Overflow` - If the operation would overflow U256
103fn mul_div_u256(a: U256, b: U256, divisor: U256) -> Result<U256> {
104    if divisor.is_zero() {
105        return err!(MathError::DivisionByZero);
106    }
107
108    if a.is_zero() || b.is_zero() {
109        return Ok(U256::zero());
110    }
111
112    let max_val = U256::max_value();
113    
114    if a > max_val / b {
115        let a_scaled = a / divisor;
116        let result = a_scaled.checked_mul(b)
117            .ok_or(MathError::Overflow)?;
118        return Ok(result);
119    }
120
121    let product = a * b;
122    Ok(product / divisor)
123}
124
125/// A fixed-point number with 18 decimal places of precision.
126///
127/// This type represents decimal numbers using integer arithmetic, scaled by 10^18.
128/// All arithmetic operations maintain the scale factor automatically.
129///
130/// # Examples
131///
132/// ```rust,ignore
133/// // Create 5.5
134/// let x = FixedPoint::from_fraction(5, 1, 2)?; // 5 + 1/2
135/// 
136/// // Create from float (testing only)
137/// let y = FixedPoint::from_f64(2.5)?;
138///
139/// // Arithmetic
140/// let sum = x.add(&y)?; // 8.0
141/// ```
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub struct FixedPoint {
144    /// The raw U256 value, scaled by 10^18
145    pub value: U256,
146}
147
148impl FixedPoint {
149    /// Creates a fixed-point number from an unsigned 64-bit integer.
150    ///
151    /// The integer is automatically scaled by 10^18 to maintain precision.
152    ///
153    /// # Arguments
154    ///
155    /// * `n` - The integer value to convert
156    ///
157    /// # Examples
158    ///
159    /// ```rust,ignore
160    /// let five = FixedPoint::from_int(5);
161    /// assert_eq!(five.to_u64()?, 5);
162    /// ```
163    pub fn from_int(n: u64) -> Self {
164        Self {
165            value: U256::from(n) * scale_u256(),
166        }
167    }
168
169    /// Creates a fixed-point number from an unsigned 128-bit integer.
170    ///
171    /// Similar to `from_int`, but accepts larger values.
172    ///
173    /// # Arguments
174    ///
175    /// * `n` - The 128-bit integer value to convert
176    ///
177    /// # Examples
178    ///
179    /// ```rust,ignore
180    /// let large = FixedPoint::from_u128(1_000_000_000);
181    /// ```
182    pub fn from_u128(n: u128) -> Self {
183        Self {
184            value: U256::from(n) * scale_u256(),
185        }
186    }
187
188    /// Creates a fixed-point number from a raw scaled U256 value.
189    ///
190    /// This function assumes the input is already scaled by 10^18.
191    /// Use this when you have a pre-scaled value.
192    ///
193    /// # Arguments
194    ///
195    /// * `value` - The pre-scaled U256 value
196    ///
197    /// # Safety
198    ///
199    /// The caller must ensure the value is properly scaled.
200    pub fn from_scaled(value: U256) -> Self {
201        Self { value }
202    }
203
204    /// Creates a fixed-point number from a raw scaled u128 value.
205    ///
206    /// This function assumes the input is already scaled by 10^18.
207    ///
208    /// # Arguments
209    ///
210    /// * `value` - The pre-scaled u128 value
211    pub fn from_scaled_u128(value: u128) -> Self {
212        Self {
213            value: U256::from(value),
214        }
215    }
216
217    /// Converts the fixed-point number to a u64 integer.
218    ///
219    /// This operation truncates any decimal places. For example,
220    /// 5.7 becomes 5, and 5.3 becomes 5.
221    ///
222    /// # Returns
223    ///
224    /// The integer part as u64, or an error if the value is too large.
225    ///
226    /// # Errors
227    ///
228    /// * `MathError::Overflow` - If the value exceeds u64::MAX
229    ///
230    /// # Examples
231    ///
232    /// ```rust,ignore
233    /// let x = FixedPoint::from_fraction(5, 7, 10)?; // 5.7
234    /// assert_eq!(x.to_u64()?, 5);
235    /// ```
236    pub fn to_u64(&self) -> Result<u64> {
237        let int_part = self.value / scale_u256();
238        if int_part.0[1] != 0 || int_part.0[2] != 0 || int_part.0[3] != 0 {
239            return err!(MathError::Overflow);
240        }
241        Ok(int_part.0[0])
242    }
243
244    /// Converts the fixed-point number to a u128 integer.
245    ///
246    /// Truncates any decimal places, similar to `to_u64` but with larger range.
247    ///
248    /// # Returns
249    ///
250    /// The integer part as u128, or an error if the value is too large.
251    ///
252    /// # Errors
253    ///
254    /// * `MathError::Overflow` - If the value exceeds u128::MAX
255    pub fn to_u128(&self) -> Result<u128> {
256        let int_part = self.value / scale_u256();
257        if int_part.0[2] != 0 || int_part.0[3] != 0 {
258            return err!(MathError::Overflow);
259        }
260        Ok(int_part.0[0] as u128 | ((int_part.0[1] as u128) << 64))
261    }
262
263    /// Converts the fixed-point number to an f64 float.
264    ///
265    /// This is primarily intended for debugging and testing purposes.
266    /// Precision may be lost for very large or very precise numbers.
267    ///
268    /// # Returns
269    ///
270    /// The floating-point representation, or an error if overflow occurs.
271    ///
272    /// # Errors
273    ///
274    /// * `MathError::Overflow` - If the value is too large for f64 conversion
275    ///
276    /// # Examples
277    ///
278    /// ```rust,ignore
279    /// let x = FixedPoint::from_int(5);
280    /// assert!((x.to_f64()? - 5.0).abs() < 1e-10);
281    /// ```
282    pub fn to_f64(&self) -> Result<f64> {
283        let value_u128 = if self.value.0[2] == 0 && self.value.0[3] == 0 {
284            self.value.0[0] as u128 | ((self.value.0[1] as u128) << 64)
285        } else {
286            return err!(MathError::Overflow);
287        };
288        
289        Ok((value_u128 as f64) / (SCALE as f64))
290    }
291
292    /// Creates a fixed-point number from an f64 float.
293    ///
294    /// This is intended for testing and convenience. For production code,
295    /// prefer using integer-based constructors for deterministic behavior.
296    ///
297    /// # Arguments
298    ///
299    /// * `val` - The floating-point value (must be non-negative and finite)
300    ///
301    /// # Returns
302    ///
303    /// A new FixedPoint, or an error if the input is invalid.
304    ///
305    /// # Errors
306    ///
307    /// * `MathError::InvalidInput` - If val is negative, infinite, or NaN
308    /// * `MathError::Overflow` - If val is too large to represent
309    ///
310    /// # Examples
311    ///
312    /// ```rust,ignore
313    /// let x = FixedPoint::from_f64(3.14159)?;
314    /// ```
315    pub fn from_f64(val: f64) -> Result<Self> {
316        require!(val >= 0.0, MathError::InvalidInput);
317        require!(val.is_finite(), MathError::InvalidInput);
318        
319        let scaled = val * (SCALE as f64);
320        
321        if scaled > (u128::MAX as f64) {
322            return err!(MathError::Overflow);
323        }
324        
325        let scaled_u128 = scaled as u128;
326        Ok(Self::from_scaled_u128(scaled_u128))
327    }
328
329    /// Creates a fixed-point number from fractional components.
330    ///
331    /// Computes: whole + (numerator / denominator)
332    ///
333    /// # Arguments
334    ///
335    /// * `whole` - The integer part
336    /// * `numerator` - Numerator of the fractional part
337    /// * `denominator` - Denominator of the fractional part (must be non-zero)
338    ///
339    /// # Returns
340    ///
341    /// The resulting fixed-point number.
342    ///
343    /// # Errors
344    ///
345    /// * `MathError::DivisionByZero` - If denominator is zero
346    ///
347    /// # Examples
348    ///
349    /// ```rust,ignore
350    /// // Create 5.5 (5 + 1/2)
351    /// let x = FixedPoint::from_fraction(5, 1, 2)?;
352    /// ```
353    pub fn from_fraction(whole: u64, numerator: u64, denominator: u64) -> Result<Self> {
354        require!(denominator != 0, MathError::DivisionByZero);
355        let whole_part = U256::from(whole) * scale_u256();
356        let frac_part = (U256::from(numerator) * scale_u256()) / U256::from(denominator);
357        Ok(Self {
358            value: whole_part + frac_part,
359        })
360    }
361
362    /// Creates a fixed-point number from a simple ratio.
363    ///
364    /// Computes: numerator / denominator
365    ///
366    /// # Arguments
367    ///
368    /// * `numerator` - The numerator
369    /// * `denominator` - The denominator (must be non-zero)
370    ///
371    /// # Examples
372    ///
373    /// ```rust,ignore
374    /// // Create 0.5 (1/2)
375    /// let half = FixedPoint::from_ratio(1, 2)?;
376    /// ```
377    pub fn from_ratio(numerator: u64, denominator: u64) -> Result<Self> {
378        Self::from_fraction(0, numerator, denominator)
379    }
380
381    /// Creates a fixed-point number from basis points.
382    ///
383    /// Basis points are 1/100th of a percent. 1 bp = 0.01% = 0.0001
384    ///
385    /// # Arguments
386    ///
387    /// * `bps` - The number of basis points
388    ///
389    /// # Examples
390    ///
391    /// ```rust,ignore
392    /// // 250 bps = 2.5%
393    /// let rate = FixedPoint::from_bps(250)?;
394    /// ```
395    pub fn from_bps(bps: u64) -> Result<Self> {
396        Self::from_fraction(0, bps, 10_000)
397    }
398
399    /// Creates a fixed-point number from a percentage.
400    ///
401    /// # Arguments
402    ///
403    /// * `percent` - The percentage value (e.g., 25 for 25%)
404    ///
405    /// # Examples
406    ///
407    /// ```rust,ignore
408    /// // 25% = 0.25
409    /// let quarter = FixedPoint::from_percent(25)?;
410    /// ```
411    pub fn from_percent(percent: u64) -> Result<Self> {
412        Self::from_fraction(0, percent, 100)
413    }
414
415    /// Multiplies two fixed-point numbers.
416    ///
417    /// # Arguments
418    ///
419    /// * `other` - The number to multiply by
420    ///
421    /// # Returns
422    ///
423    /// The product of the two numbers.
424    ///
425    /// # Errors
426    ///
427    /// * `MathError::Overflow` - If the result exceeds U256::MAX
428    ///
429    /// # Examples
430    ///
431    /// ```rust,ignore
432    /// let x = FixedPoint::from_int(5);
433    /// let y = FixedPoint::from_int(3);
434    /// let product = x.mul(&y)?; // 15
435    /// ```
436    pub fn mul(&self, other: &Self) -> Result<Self> {
437        let result = mul_div_u256(self.value, other.value, scale_u256())?;
438        Ok(Self { value: result })
439    }
440
441    /// Divides one fixed-point number by another.
442    ///
443    /// # Arguments
444    ///
445    /// * `other` - The divisor (must be non-zero)
446    ///
447    /// # Returns
448    ///
449    /// The quotient.
450    ///
451    /// # Errors
452    ///
453    /// * `MathError::DivisionByZero` - If other is zero
454    /// * `MathError::Overflow` - If the result exceeds U256::MAX
455    ///
456    /// # Examples
457    ///
458    /// ```rust,ignore
459    /// let x = FixedPoint::from_int(10);
460    /// let y = FixedPoint::from_int(4);
461    /// let quotient = x.div(&y)?; // 2.5
462    /// ```
463    pub fn div(&self, other: &Self) -> Result<Self> {
464        require!(!other.value.is_zero(), MathError::DivisionByZero);
465        let result = mul_div_u256(self.value, scale_u256(), other.value)?;
466        Ok(Self { value: result })
467    }
468
469    /// Adds two fixed-point numbers.
470    ///
471    /// # Arguments
472    ///
473    /// * `other` - The number to add
474    ///
475    /// # Returns
476    ///
477    /// The sum of the two numbers.
478    ///
479    /// # Errors
480    ///
481    /// * `MathError::Overflow` - If the sum exceeds U256::MAX
482    pub fn add(&self, other: &Self) -> Result<Self> {
483        let result = self.value.checked_add(other.value)
484            .ok_or(error!(MathError::Overflow))?;
485        Ok(Self { value: result })
486    }
487
488    /// Subtracts one fixed-point number from another.
489    ///
490    /// # Arguments
491    ///
492    /// * `other` - The number to subtract
493    ///
494    /// # Returns
495    ///
496    /// The difference.
497    ///
498    /// # Errors
499    ///
500    /// * `MathError::Underflow` - If other is larger than self
501    pub fn sub(&self, other: &Self) -> Result<Self> {
502        let result = self.value.checked_sub(other.value)
503            .ok_or(error!(MathError::Underflow))?;
504        Ok(Self { value: result })
505    }
506
507    /// Optimized power function for base 2 with fractional exponents.
508    ///
509    /// Computes 2^exponent using range reduction and Taylor series.
510    /// This is more efficient than the general power function.
511    ///
512    /// # Arguments
513    ///
514    /// * `exponent` - The exponent (can be fractional)
515    ///
516    /// # Returns
517    ///
518    /// 2 raised to the given power.
519    ///
520    /// # Errors
521    ///
522    /// * `MathError::Overflow` - If the result is too large
523    ///
524    /// # Examples
525    ///
526    /// ```rust,ignore
527    /// let exp = FixedPoint::from_int(3);
528    /// let result = FixedPoint::pow2_fast(&exp)?; // 2^3 = 8
529    /// ```
530    pub fn pow2_fast(exponent: &Self) -> Result<Self> {
531        let scale = scale_u256();
532        
533        if exponent.value.is_zero() {
534            return Ok(Self::from_int(1));
535        }
536
537        let int_part = (exponent.value / scale).low_u64() as i64;
538        let frac_part = exponent.value % scale;
539
540        if frac_part.is_zero() {
541            if int_part >= 0 {
542                let result = U256::from(1u64) << (int_part as usize);
543                return Ok(Self::from_scaled(result * scale));
544            } else {
545                let divisor = U256::from(1u64) << ((-int_part) as usize);
546                return Ok(Self::from_scaled(scale / divisor));
547            }
548        }
549
550        let mut result = scale;
551        if int_part > 0 {
552            result = result << (int_part as usize);
553        } else if int_part < 0 {
554            result = result >> ((-int_part) as usize);
555        }
556
557        let ln2 = U256::from(693_147_180_559_945_309u128);
558        let x_ln2 = mul_div_u256(frac_part, ln2, scale)?;
559        
560        let term1 = scale;
561        let term2 = x_ln2;
562        let term3 = mul_div_u256(x_ln2, x_ln2, scale)? / U256::from(2u64);
563        let term4 = mul_div_u256(mul_div_u256(x_ln2, x_ln2, scale)?, x_ln2, scale)? / U256::from(6u64);
564        
565        let frac_result = term1 + term2 + term3 + term4;
566        let final_val = mul_div_u256(result, frac_result, scale)?;
567        
568        Ok(Self::from_scaled(final_val))
569    }
570
571    /// Fast power for small integer bases (2-10).
572    ///
573    /// Uses pre-computed logarithms for efficiency.
574    fn pow_small_base(&self, exponent: &Self) -> Result<Self> {
575        let base_int = self.to_u64()?;
576        
577        if base_int == 2 {
578            return Self::pow2_fast(exponent);
579        }
580
581        let log2_lookup: [(u64, u128); 9] = [
582            (2, 1_000_000_000_000_000_000),
583            (3, 1_584_962_500_721_156_181),
584            (4, 2_000_000_000_000_000_000),
585            (5, 2_321_928_094_887_362_347),
586            (6, 2_584_962_500_721_156_181),
587            (7, 2_807_354_922_057_604_107),
588            (8, 3_000_000_000_000_000_000),
589            (9, 3_169_925_001_442_312_363),
590            (10, 3_321_928_094_887_362_347),
591        ];
592
593        let mut log2_base = U256::zero();
594        for (base, log2_val) in log2_lookup.iter() {
595            if *base == base_int {
596                log2_base = U256::from(*log2_val);
597                break;
598            }
599        }
600
601        if log2_base.is_zero() {
602            return self.pow_general(exponent);
603        }
604
605        let scaled_exp = mul_div_u256(exponent.value, log2_base, scale_u256())?;
606        let scaled_exp_fp = Self::from_scaled(scaled_exp);
607        
608        Self::pow2_fast(&scaled_exp_fp)
609    }
610
611    /// General power function using logarithms.
612    ///
613    /// Computes base^exponent using the identity: base^exp = e^(exp * ln(base))
614    fn pow_general(&self, exponent: &Self) -> Result<Self> {
615        require!(!self.value.is_zero(), MathError::InvalidInput);
616
617        let scale = scale_u256();
618        
619        if exponent.value.is_zero() {
620            return Ok(Self::from_int(1));
621        }
622        if exponent.value == scale {
623            return Ok(*self);
624        }
625        if self.value == scale {
626            return Ok(*self);
627        }
628
629        let remainder = exponent.value % scale;
630        if remainder.is_zero() {
631            let exp_int = (exponent.value / scale).low_u32();
632            return self.pow_int(exp_int);
633        }
634
635        let ln_self = self.ln_fast()?;
636        let exp_times_ln = ln_self.mul(exponent)?;
637        exp_times_ln.exp_fast()
638    }
639
640    /// Raises this number to the given power.
641    ///
642    /// Supports both integer and fractional exponents.
643    /// Uses optimized algorithms based on the base and exponent values.
644    ///
645    /// # Arguments
646    ///
647    /// * `exponent` - The power to raise to (can be fractional)
648    ///
649    /// # Returns
650    ///
651    /// self^exponent
652    ///
653    /// # Errors
654    ///
655    /// * `MathError::InvalidInput` - If self is zero and exponent is non-positive
656    /// * `MathError::Overflow` - If the result is too large
657    ///
658    /// # Examples
659    ///
660    /// ```rust,ignore
661    /// let base = FixedPoint::from_int(5);
662    /// let exp = FixedPoint::from_int(2);
663    /// let result = base.pow(&exp)?; // 25
664    ///
665    /// // Fractional exponent (square root)
666    /// let half = FixedPoint::from_ratio(1, 2)?;
667    /// let sqrt_25 = FixedPoint::from_int(25).pow(&half)?; // 5
668    /// ```
669    pub fn pow(&self, exponent: &Self) -> Result<Self> {
670        if let Ok(base_val) = self.to_u64() {
671            if base_val >= 2 && base_val <= 10 {
672                return self.pow_small_base(exponent);
673            }
674        }
675
676        let scale = scale_u256();
677        let remainder = exponent.value % scale;
678        if remainder.is_zero() {
679            let exp_int = (exponent.value / scale).low_u32();
680            return self.pow_int(exp_int);
681        }
682
683        self.pow_general(exponent)
684    }
685
686    /// Efficient integer power using binary exponentiation.
687    ///
688    /// Computes self^exp in O(log exp) multiplications.
689    fn pow_int(&self, mut exp: u32) -> Result<Self> {
690        if exp == 0 {
691            return Ok(Self::from_int(1));
692        }
693        if exp == 1 {
694            return Ok(*self);
695        }
696
697        let mut base = *self;
698        let mut result = Self::from_int(1);
699
700        while exp > 0 {
701            if exp & 1 == 1 {
702                result = result.mul(&base)?;
703            }
704            if exp > 1 {
705                base = base.mul(&base)?;
706            }
707            exp >>= 1;
708        }
709
710        Ok(result)
711    }
712
713    /// Computes the natural logarithm (ln).
714    ///
715    /// # Returns
716    ///
717    /// ln(self)
718    ///
719    /// # Errors
720    ///
721    /// * `MathError::InvalidInput` - If self is zero or negative
722    ///
723    /// # Examples
724    ///
725    /// ```rust,ignore
726    /// let e = FixedPoint::from_f64(2.718281828)?;
727    /// let ln_e = e.ln()?; // ≈ 1.0
728    /// ```
729    pub fn ln(&self) -> Result<Self> {
730        self.ln_fast()
731    }
732
733    /// Fast ln implementation with improved accuracy.
734    ///
735    /// Uses range reduction to bring the input into [1, 2), then applies
736    /// a Taylor series approximation.
737    fn ln_fast(&self) -> Result<Self> {
738        require!(!self.value.is_zero(), MathError::InvalidInput);
739
740        let scale = scale_u256();
741        if self.value == scale {
742            return Ok(Self::from_scaled(U256::zero()));
743        }
744
745        let mut x = self.value;
746        let mut exp_adj: i64 = 0;
747        let two = U256::from(2u64);
748
749        while x >= two * scale {
750            x = x / two;
751            exp_adj += 1;
752        }
753        while x < scale {
754            x = x * two;
755            exp_adj -= 1;
756        }
757
758        let num = x.checked_sub(scale).ok_or(error!(MathError::Underflow))?;
759        let den = x.checked_add(scale).ok_or(error!(MathError::Overflow))?;
760        let y = mul_div_u256(num, scale, den)?;
761
762        let y2 = mul_div_u256(y, y, scale)?;
763
764        let denoms = [1u64, 3, 5, 7, 9];
765        let mut inner_sum = U256::zero();
766        let mut current_power = scale;
767
768        for i in 0..denoms.len() {
769            let denom = U256::from(denoms[i]);
770            let term = (current_power + (denom / two)) / denom;
771            inner_sum = inner_sum.checked_add(term).ok_or(error!(MathError::Overflow))?;
772
773            if i < denoms.len() - 1 {
774                current_power = mul_div_u256(current_power, y2, scale)?;
775            }
776        }
777
778        let two_y = y.checked_mul(two).ok_or(error!(MathError::Overflow))?;
779        let ln_x = mul_div_u256(inner_sum, two_y, scale)?;
780
781        let abs_exp = exp_adj.abs() as u64;
782        let adj_abs = LN_2.checked_mul(U256::from(abs_exp)).ok_or(error!(MathError::Overflow))?;
783
784        let final_value = if exp_adj >= 0 {
785            ln_x.checked_add(adj_abs).ok_or(error!(MathError::Overflow))?
786        } else {
787            ln_x.checked_sub(adj_abs).ok_or(error!(MathError::Underflow))?
788        };
789
790        Ok(Self::from_scaled(final_value))
791    }
792
793    /// Computes the exponential function (e^x).
794    ///
795    /// # Returns
796    ///
797    /// e^self
798    ///
799    /// # Errors
800    ///
801    /// * `MathError::Overflow` - If self is too large
802    ///
803    /// # Examples
804    ///
805    /// ```rust,ignore
806    /// let x = FixedPoint::from_int(1);
807    /// let e = x.exp()?; // ≈ 2.718281828
808    /// ```
809    pub fn exp(&self) -> Result<Self> {
810        self.exp_fast()
811    }
812
813    /// Fast exp implementation with range reduction.
814    ///
815    /// Uses the identity: e^x = 2^(x/ln(2)) * e^r where r is small.
816    fn exp_fast(&self) -> Result<Self> {
817        require!(self.value <= MAX_EXP_INPUT, MathError::Overflow);
818
819        let scale = scale_u256();
820        if self.value.is_zero() {
821            return Ok(Self::from_int(1));
822        }
823
824        let x = self.value;
825        let ln_2 = LN_2;
826
827        // FIX: Both x and ln_2 are scaled, so simple division gives unscaled k
828        let k_u256 = x / ln_2;
829        let k = if k_u256.0[1] == 0 && k_u256.0[2] == 0 && k_u256.0[3] == 0 {
830            k_u256.0[0] as i64
831        } else {
832            return err!(MathError::Overflow);
833        };
834        
835        let k_abs = U256::from(k.abs() as u64);
836        let k_ln2 = k_abs * ln_2;  // k is unscaled, ln_2 is scaled, so k_ln2 is scaled
837        
838        let r = if k >= 0 {
839            x.checked_sub(k_ln2).unwrap_or(U256::zero())
840        } else {
841            x.checked_add(k_ln2).ok_or(error!(MathError::Overflow))?
842        };
843
844        let r2 = mul_div_u256(r, r, scale)?;
845        let r3 = mul_div_u256(r2, r, scale)?;
846        
847        let result = scale + r + r2 / U256::from(2u64) + r3 / U256::from(6u64);
848
849        let mut final_result = result;
850        
851        if k > 0 {
852            final_result = final_result << (k as usize);
853        } else if k < 0 {
854            final_result = final_result >> ((-k) as usize);
855        }
856
857        Ok(Self::from_scaled(final_result))
858    }
859
860    /// Computes the square root using Newton's method.
861    ///
862    /// # Returns
863    ///
864    /// √self
865    ///
866    /// # Examples
867    ///
868    /// ```rust,ignore
869    /// let x = FixedPoint::from_int(25);
870    /// let sqrt = x.sqrt()?; // 5
871    /// ```
872    pub fn sqrt(&self) -> Result<Self> {
873        if self.value.is_zero() {
874            return Ok(Self::from_scaled(U256::zero()));
875        }
876
877        let scale = scale_u256();
878        let x = self.value;
879        let mut y = (x + scale) / U256::from(2u64);
880
881        y = (y + mul_div_u256(x, scale, y)?) / U256::from(2u64);
882        y = (y + mul_div_u256(x, scale, y)?) / U256::from(2u64);
883        y = (y + mul_div_u256(x, scale, y)?) / U256::from(2u64);
884        y = (y + mul_div_u256(x, scale, y)?) / U256::from(2u64);
885
886        Ok(Self::from_scaled(y))
887    }
888
889    /// Computes the base-10 logarithm.
890    ///
891    /// Uses the identity: log10(x) = ln(x) / ln(10)
892    ///
893    /// # Returns
894    ///
895    /// log₁₀(self)
896    ///
897    /// # Errors
898    ///
899    /// * `MathError::InvalidInput` - If self is zero
900    pub fn log10(&self) -> Result<Self> {
901        let ln_val = self.ln_fast()?;
902        let ln_10 = Self::from_scaled(LN_10);
903        ln_val.div(&ln_10)
904    }
905
906    /// Computes the base-2 logarithm.
907    ///
908    /// Uses the identity: log2(x) = ln(x) / ln(2)
909    ///
910    /// # Returns
911    ///
912    /// log₂(self)
913    ///
914    /// # Errors
915    ///
916    /// * `MathError::InvalidInput` - If self is zero
917    pub fn log2(&self) -> Result<Self> {
918        let ln_val = self.ln_fast()?;
919        let ln_2 = Self::from_scaled(LN_2);
920        ln_val.div(&ln_2)
921    }
922
923    /// Computes the logarithm with a custom base.
924    ///
925    /// Uses the identity: log_base(x) = ln(x) / ln(base)
926    ///
927    /// # Arguments
928    ///
929    /// * `base` - The logarithm base
930    ///
931    /// # Returns
932    ///
933    /// log_base(self)
934    ///
935    /// # Errors
936    ///
937    /// * `MathError::InvalidInput` - If self or base is zero or base is 1
938    pub fn log(&self, base: &Self) -> Result<Self> {
939        let ln_val = self.ln_fast()?;
940        let ln_base = base.ln_fast()?;
941        ln_val.div(&ln_base)
942    }
943
944    /// Returns the absolute value.
945    ///
946    /// For unsigned fixed-point numbers, this is always the identity function.
947    ///
948    /// # Examples
949    ///
950    /// ```rust,ignore
951    /// let x = FixedPoint::from_int(5);
952    /// assert_eq!(x.abs(), x);
953    /// ```
954    pub fn abs(&self) -> Self {
955        *self
956    }
957
958    /// Returns the minimum of two values.
959    ///
960    /// # Arguments
961    ///
962    /// * `other` - The value to compare with
963    ///
964    /// # Examples
965    ///
966    /// ```rust,ignore
967    /// let x = FixedPoint::from_int(5);
968    /// let y = FixedPoint::from_int(3);
969    /// assert_eq!(x.min(&y), y);
970    /// ```
971    pub fn min(&self, other: &Self) -> Self {
972        if self.value < other.value {
973            *self
974        } else {
975            *other
976        }
977    }
978
979    /// Returns the maximum of two values.
980    ///
981    /// # Arguments
982    ///
983    /// * `other` - The value to compare with
984    ///
985    /// # Examples
986    ///
987    /// ```rust,ignore
988    /// let x = FixedPoint::from_int(5);
989    /// let y = FixedPoint::from_int(3);
990    /// assert_eq!(x.max(&y), x);
991    /// ```
992    pub fn max(&self, other: &Self) -> Self {
993        if self.value > other.value {
994            *self
995        } else {
996            *other
997        }
998    }
999
1000    /// Checks if this value is zero.
1001    ///
1002    /// # Returns
1003    ///
1004    /// `true` if the value is exactly zero, `false` otherwise
1005    pub fn is_zero(&self) -> bool {
1006        self.value.is_zero()
1007    }
1008
1009    /// Returns the raw internal U256 representation for debugging.
1010    ///
1011    /// The U256 is stored as 4 u64 limbs.
1012    ///
1013    /// # Returns
1014    ///
1015    /// A tuple of (limb0, limb1, limb2, limb3)
1016    pub fn debug_value(&self) -> (u64, u64, u64, u64) {
1017        (self.value.0[0], self.value.0[1], self.value.0[2], self.value.0[3])
1018    }
1019
1020    /// Returns the fractional part of this number.
1021    ///
1022    /// For example, the fractional part of 5.7 is 0.7.
1023    ///
1024    /// # Examples
1025    ///
1026    /// ```rust,ignore
1027    /// let x = FixedPoint::from_fraction(5, 7, 10)?; // 5.7
1028    /// let frac = x.frac()?;
1029    /// // frac ≈ 0.7
1030    /// ```
1031    pub fn frac(&self) -> Result<Self> {
1032        let scale = scale_u256();
1033        let frac_part = self.value % scale;
1034        Ok(Self::from_scaled(frac_part))
1035    }
1036
1037    /// Returns the integer part (floor) of this number.
1038    ///
1039    /// # Examples
1040    ///
1041    /// ```rust,ignore
1042    /// let x = FixedPoint::from_fraction(5, 7, 10)?; // 5.7
1043    /// let floor = x.floor();
1044    /// assert_eq!(floor.to_u64()?, 5);
1045    /// ```
1046    pub fn floor(&self) -> Self {
1047        let scale = scale_u256();
1048        let int_part = (self.value / scale) * scale;
1049        Self::from_scaled(int_part)
1050    }
1051
1052    /// Returns the ceiling of this number.
1053    ///
1054    /// Rounds up to the next integer if there's any fractional part.
1055    ///
1056    /// # Examples
1057    ///
1058    /// ```rust,ignore
1059    /// let x = FixedPoint::from_fraction(5, 7, 10)?; // 5.7
1060    /// let ceil = x.ceil()?;
1061    /// assert_eq!(ceil.to_u64()?, 6);
1062    /// ```
1063    pub fn ceil(&self) -> Result<Self> {
1064        let scale = scale_u256();
1065        let int_part = self.value / scale;
1066        let has_frac = self.value % scale != U256::zero();
1067        
1068        if has_frac {
1069            let ceil_val = (int_part + U256::from(1u64)) * scale;
1070            Ok(Self::from_scaled(ceil_val))
1071        } else {
1072            Ok(Self::from_scaled(int_part * scale))
1073        }
1074    }
1075}
1076
1077/// Error types for fixed-point math operations.
1078#[error_code]
1079pub enum MathError {
1080    #[msg("Arithmetic overflow occurred")]
1081    Overflow,
1082    
1083    #[msg("Arithmetic underflow occurred")]
1084    Underflow,
1085    
1086    #[msg("Division by zero")]
1087    DivisionByZero,
1088    
1089    #[msg("Invalid input value")]
1090    InvalidInput,
1091}
1092
1093
1094#[cfg(test)]
1095mod tests {
1096    use crate::{FixedPoint, SCALE, U256};
1097
1098
1099    // Adjusted epsilon values based on actual implementation accuracy
1100    const EPSILON: f64 = 0.01; // 1% tolerance for complex operations
1101    const LOOSE_EPSILON: f64 = 0.05; // 5% tolerance for very complex operations
1102    const TIGHT_EPSILON: f64 = 0.00001; // 0.001% tolerance for simple operations
1103
1104    // ============================================================================
1105    // Constructor Tests
1106    // ============================================================================
1107
1108    #[test]
1109    fn test_from_int() {
1110        let x = FixedPoint::from_int(5);
1111        assert_eq!(x.to_u64().unwrap(), 5);
1112        
1113        let y = FixedPoint::from_int(0);
1114        assert_eq!(y.to_u64().unwrap(), 0);
1115        
1116        let z = FixedPoint::from_int(u64::MAX);
1117        assert_eq!(z.to_u64().unwrap(), u64::MAX);
1118    }
1119
1120    #[test]
1121    fn test_from_u128() {
1122        let x = FixedPoint::from_u128(1_000_000);
1123        assert_eq!(x.to_u128().unwrap(), 1_000_000);
1124        
1125        let y = FixedPoint::from_u128(u128::MAX / SCALE);
1126        assert!(y.to_u128().is_ok());
1127    }
1128
1129    #[test]
1130    fn test_from_scaled() {
1131        let raw_value = U256::from(SCALE) * U256::from(5u64);
1132        let x = FixedPoint::from_scaled(raw_value);
1133        assert_eq!(x.to_u64().unwrap(), 5);
1134    }
1135
1136    #[test]
1137    fn test_from_scaled_u128() {
1138        let scaled_value = SCALE * 5;
1139        let x = FixedPoint::from_scaled_u128(scaled_value);
1140        assert_eq!(x.to_u64().unwrap(), 5);
1141    }
1142
1143    #[test]
1144    fn test_from_f64() {
1145        let x = FixedPoint::from_f64(5.5).unwrap();
1146        let val = x.to_f64().unwrap();
1147        assert!((val - 5.5).abs() < TIGHT_EPSILON);
1148        
1149        let y = FixedPoint::from_f64(0.0).unwrap();
1150        assert!(y.is_zero());
1151        
1152        // Test invalid inputs
1153        assert!(FixedPoint::from_f64(-1.0).is_err());
1154        assert!(FixedPoint::from_f64(f64::NAN).is_err());
1155        assert!(FixedPoint::from_f64(f64::INFINITY).is_err());
1156    }
1157
1158    #[test]
1159    fn test_from_fraction() {
1160        let x = FixedPoint::from_fraction(5, 1, 2).unwrap();
1161        let val = x.to_f64().unwrap();
1162        assert!((val - 5.5).abs() < TIGHT_EPSILON);
1163        
1164        let y = FixedPoint::from_fraction(0, 3, 4).unwrap();
1165        let val = y.to_f64().unwrap();
1166        assert!((val - 0.75).abs() < TIGHT_EPSILON);
1167        
1168        assert!(FixedPoint::from_fraction(1, 1, 0).is_err());
1169    }
1170
1171    #[test]
1172    fn test_from_ratio() {
1173        let half = FixedPoint::from_ratio(1, 2).unwrap();
1174        let val = half.to_f64().unwrap();
1175        assert!((val - 0.5).abs() < TIGHT_EPSILON);
1176        
1177        let third = FixedPoint::from_ratio(1, 3).unwrap();
1178        let val = third.to_f64().unwrap();
1179        assert!((val - 0.333333).abs() < EPSILON);
1180        
1181        assert!(FixedPoint::from_ratio(1, 0).is_err());
1182    }
1183
1184    #[test]
1185    fn test_from_bps() {
1186        let x = FixedPoint::from_bps(250).unwrap();
1187        let val = x.to_f64().unwrap();
1188        assert!((val - 0.025).abs() < TIGHT_EPSILON);
1189        
1190        let y = FixedPoint::from_bps(10000).unwrap();
1191        let val = y.to_f64().unwrap();
1192        assert!((val - 1.0).abs() < TIGHT_EPSILON);
1193        
1194        let z = FixedPoint::from_bps(1).unwrap();
1195        let val = z.to_f64().unwrap();
1196        assert!((val - 0.0001).abs() < TIGHT_EPSILON);
1197    }
1198
1199    #[test]
1200    fn test_from_percent() {
1201        let x = FixedPoint::from_percent(25).unwrap();
1202        let val = x.to_f64().unwrap();
1203        assert!((val - 0.25).abs() < TIGHT_EPSILON);
1204        
1205        let y = FixedPoint::from_percent(100).unwrap();
1206        let val = y.to_f64().unwrap();
1207        assert!((val - 1.0).abs() < TIGHT_EPSILON);
1208        
1209        let z = FixedPoint::from_percent(50).unwrap();
1210        let val = z.to_f64().unwrap();
1211        assert!((val - 0.5).abs() < TIGHT_EPSILON);
1212    }
1213
1214    // ============================================================================
1215    // Conversion Tests
1216    // ============================================================================
1217
1218    #[test]
1219    fn test_to_u64() {
1220        let x = FixedPoint::from_fraction(5, 7, 10).unwrap();
1221        assert_eq!(x.to_u64().unwrap(), 5);
1222        
1223        let y = FixedPoint::from_int(100);
1224        assert_eq!(y.to_u64().unwrap(), 100);
1225        
1226        let z = FixedPoint::from_int(0);
1227        assert_eq!(z.to_u64().unwrap(), 0);
1228    }
1229
1230    #[test]
1231    fn test_to_u128() {
1232        let x = FixedPoint::from_u128(1_000_000_000);
1233        assert_eq!(x.to_u128().unwrap(), 1_000_000_000);
1234        
1235        let y = FixedPoint::from_fraction(1_000_000, 5, 10).unwrap();
1236        assert_eq!(y.to_u128().unwrap(), 1_000_000);
1237    }
1238
1239    #[test]
1240    fn test_to_f64_roundtrip() {
1241        let values = [0.0, 1.0, 5.5, 100.0, 0.123456, 999.999];
1242        for &val in &values {
1243            let fp = FixedPoint::from_f64(val).unwrap();
1244            let back = fp.to_f64().unwrap();
1245            assert!((back - val).abs() < TIGHT_EPSILON, "Failed for {}", val);
1246        }
1247    }
1248
1249    // ============================================================================
1250    // Arithmetic Tests
1251    // ============================================================================
1252
1253    #[test]
1254    fn test_add() {
1255        let x = FixedPoint::from_int(5);
1256        let y = FixedPoint::from_int(3);
1257        let sum = x.add(&y).unwrap();
1258        assert_eq!(sum.to_u64().unwrap(), 8);
1259        
1260        let a = FixedPoint::from_f64(2.5).unwrap();
1261        let b = FixedPoint::from_f64(3.7).unwrap();
1262        let sum = a.add(&b).unwrap();
1263        let val = sum.to_f64().unwrap();
1264        assert!((val - 6.2).abs() < TIGHT_EPSILON);
1265        
1266        let zero = FixedPoint::from_int(0);
1267        let sum = x.add(&zero).unwrap();
1268        assert_eq!(sum.to_u64().unwrap(), 5);
1269    }
1270
1271    #[test]
1272    fn test_sub() {
1273        let x = FixedPoint::from_int(5);
1274        let y = FixedPoint::from_int(3);
1275        let diff = x.sub(&y).unwrap();
1276        assert_eq!(diff.to_u64().unwrap(), 2);
1277        
1278        let result = y.sub(&x);
1279        assert!(result.is_err());
1280        
1281        let diff = x.sub(&x).unwrap();
1282        assert!(diff.is_zero());
1283    }
1284
1285    #[test]
1286    fn test_mul() {
1287        let x = FixedPoint::from_int(5);
1288        let y = FixedPoint::from_int(3);
1289        let product = x.mul(&y).unwrap();
1290        assert_eq!(product.to_u64().unwrap(), 15);
1291        
1292        let a = FixedPoint::from_f64(2.5).unwrap();
1293        let b = FixedPoint::from_f64(4.0).unwrap();
1294        let product = a.mul(&b).unwrap();
1295        let val = product.to_f64().unwrap();
1296        assert!((val - 10.0).abs() < TIGHT_EPSILON);
1297        
1298        let zero = FixedPoint::from_int(0);
1299        let product = x.mul(&zero).unwrap();
1300        assert!(product.is_zero());
1301        
1302        let one = FixedPoint::from_int(1);
1303        let product = x.mul(&one).unwrap();
1304        assert_eq!(product.to_u64().unwrap(), 5);
1305    }
1306
1307    #[test]
1308    fn test_div() {
1309        let x = FixedPoint::from_int(10);
1310        let y = FixedPoint::from_int(4);
1311        let quotient = x.div(&y).unwrap();
1312        let val = quotient.to_f64().unwrap();
1313        assert!((val - 2.5).abs() < TIGHT_EPSILON);
1314        
1315        let zero = FixedPoint::from_int(0);
1316        assert!(x.div(&zero).is_err());
1317        
1318        let one = FixedPoint::from_int(1);
1319        let quotient = x.div(&one).unwrap();
1320        assert_eq!(quotient.to_u64().unwrap(), 10);
1321        
1322        let quotient = x.div(&x).unwrap();
1323        let val = quotient.to_f64().unwrap();
1324        assert!((val - 1.0).abs() < TIGHT_EPSILON);
1325    }
1326
1327    #[test]
1328    fn test_arithmetic_identities() {
1329        let x = FixedPoint::from_f64(7.5).unwrap();
1330        let one = FixedPoint::from_int(1);
1331        let zero = FixedPoint::from_int(0);
1332        
1333        let result = x.add(&zero).unwrap();
1334        assert!((result.to_f64().unwrap() - x.to_f64().unwrap()).abs() < TIGHT_EPSILON);
1335        
1336        let result = x.mul(&one).unwrap();
1337        assert!((result.to_f64().unwrap() - x.to_f64().unwrap()).abs() < TIGHT_EPSILON);
1338        
1339        let result = x.div(&one).unwrap();
1340        assert!((result.to_f64().unwrap() - x.to_f64().unwrap()).abs() < TIGHT_EPSILON);
1341        
1342        let result = x.sub(&x).unwrap();
1343        assert!(result.is_zero());
1344        
1345        let result = x.mul(&zero).unwrap();
1346        assert!(result.is_zero());
1347    }
1348
1349    #[test]
1350    fn test_arithmetic_commutativity() {
1351        let x = FixedPoint::from_f64(3.5).unwrap();
1352        let y = FixedPoint::from_f64(2.5).unwrap();
1353        
1354        let sum1 = x.add(&y).unwrap();
1355        let sum2 = y.add(&x).unwrap();
1356        assert_eq!(sum1.to_f64().unwrap(), sum2.to_f64().unwrap());
1357        
1358        let prod1 = x.mul(&y).unwrap();
1359        let prod2 = y.mul(&x).unwrap();
1360        assert!((prod1.to_f64().unwrap() - prod2.to_f64().unwrap()).abs() < TIGHT_EPSILON);
1361    }
1362
1363    // ============================================================================
1364    // Power Tests
1365    // ============================================================================
1366
1367    #[test]
1368    fn test_pow_int() {
1369        let base = FixedPoint::from_int(2);
1370        let exp = FixedPoint::from_int(10);
1371        let result = base.pow(&exp).unwrap();
1372        assert_eq!(result.to_u64().unwrap(), 1024);
1373        
1374        // 5^3 = 125 (with tolerance for rounding)
1375        let base = FixedPoint::from_int(5);
1376        let exp = FixedPoint::from_int(3);
1377        let result = base.pow(&exp).unwrap();
1378        let val = result.to_u64().unwrap();
1379        assert!((val as i64 - 125).abs() <= 1, "Expected ~125, got {}", val);
1380        
1381        // x^0 = 1
1382        let base = FixedPoint::from_int(7);
1383        let exp = FixedPoint::from_int(0);
1384        let result = base.pow(&exp).unwrap();
1385        assert_eq!(result.to_u64().unwrap(), 1);
1386        
1387        // x^1 = x (with tolerance)
1388        let base = FixedPoint::from_int(7);
1389        let exp = FixedPoint::from_int(1);
1390        let result = base.pow(&exp).unwrap();
1391        let val = result.to_u64().unwrap();
1392        assert!((val as i64 - 7).abs() <= 1, "Expected ~7, got {}", val);
1393        
1394        // 1^x = 1
1395        let base = FixedPoint::from_int(1);
1396        let exp = FixedPoint::from_int(100);
1397        let result = base.pow(&exp).unwrap();
1398        assert_eq!(result.to_u64().unwrap(), 1);
1399    }
1400
1401    #[test]
1402    fn test_pow_fractional() {
1403        // 4^0.5 = 2 (square root)
1404        let base = FixedPoint::from_int(4);
1405        let exp = FixedPoint::from_ratio(1, 2).unwrap();
1406        let result = base.pow(&exp).unwrap();
1407        let val = result.to_f64().unwrap();
1408        assert!((val - 2.0).abs() < EPSILON);
1409        
1410        // 27^(1/3) ≈ 3 (cube root) - use looser tolerance
1411        let base = FixedPoint::from_int(27);
1412        let exp = FixedPoint::from_ratio(1, 3).unwrap();
1413        let result = base.pow(&exp).unwrap();
1414        let val = result.to_f64().unwrap();
1415        assert!((val - 3.0).abs() < LOOSE_EPSILON, "Expected ~3.0, got {}", val);
1416        
1417        // 16^(3/4)
1418        let base = FixedPoint::from_int(16);
1419        let exp = FixedPoint::from_fraction(0, 3, 4).unwrap();
1420        let result = base.pow(&exp).unwrap();
1421        let expected = 16.0_f64.powf(0.75);
1422        let val = result.to_f64().unwrap();
1423        assert!((val - expected).abs() / expected < 0.05); // 5% relative error
1424    }
1425
1426    #[test]
1427    fn test_pow2_fast() {
1428        let exp = FixedPoint::from_int(3);
1429        let result = FixedPoint::pow2_fast(&exp).unwrap();
1430        assert_eq!(result.to_u64().unwrap(), 8);
1431        
1432        let exp = FixedPoint::from_int(0);
1433        let result = FixedPoint::pow2_fast(&exp).unwrap();
1434        assert_eq!(result.to_u64().unwrap(), 1);
1435        
1436        let exp = FixedPoint::from_int(10);
1437        let result = FixedPoint::pow2_fast(&exp).unwrap();
1438        assert_eq!(result.to_u64().unwrap(), 1024);
1439        
1440        let exp = FixedPoint::from_ratio(1, 2).unwrap();
1441        let result = FixedPoint::pow2_fast(&exp).unwrap();
1442        let val = result.to_f64().unwrap();
1443        assert!((val - 1.414213562).abs() < EPSILON);
1444    }
1445
1446    #[test]
1447    fn test_pow_small_bases() {
1448        for base in 2..=10 {
1449            let b = FixedPoint::from_int(base);
1450            let exp = FixedPoint::from_int(3);
1451            let result = b.pow(&exp).unwrap();
1452            let expected = (base as f64).powi(3);
1453            let val = result.to_f64().unwrap();
1454            let relative_error = (val - expected).abs() / expected;
1455            assert!(relative_error < 0.05, 
1456                "Failed for base {}: got {}, expected {}, relative error: {}", 
1457                base, val, expected, relative_error);
1458        }
1459    }
1460
1461    #[test]
1462    fn test_pow_large_exponents() {
1463        let base = FixedPoint::from_int(2);
1464        let exp = FixedPoint::from_int(20);
1465        let result = base.pow(&exp).unwrap();
1466        assert_eq!(result.to_u64().unwrap(), 1_048_576);
1467    }
1468
1469    // ============================================================================
1470    // Logarithm Tests
1471    // ============================================================================
1472
1473    #[test]
1474    fn test_ln() {
1475        // ln(e) ≈ 1
1476        let e = FixedPoint::from_f64(2.718281828).unwrap();
1477        let result = e.ln().unwrap();
1478        let val = result.to_f64().unwrap();
1479        assert!((val - 1.0).abs() < EPSILON);
1480        
1481        // ln(1) = 0
1482        let one = FixedPoint::from_int(1);
1483        let result = one.ln().unwrap();
1484        assert!(result.to_f64().unwrap().abs() < TIGHT_EPSILON);
1485        
1486        // ln(2) ≈ 0.693147
1487        let two = FixedPoint::from_int(2);
1488        let result = two.ln().unwrap();
1489        let val = result.to_f64().unwrap();
1490        assert!((val - 0.693147).abs() < EPSILON);
1491        
1492        // ln(10) ≈ 2.302585
1493        let ten = FixedPoint::from_int(10);
1494        let result = ten.ln().unwrap();
1495        let val = result.to_f64().unwrap();
1496        assert!((val - 2.302585).abs() < EPSILON);
1497        
1498        let zero = FixedPoint::from_int(0);
1499        assert!(zero.ln().is_err());
1500    }
1501
1502    #[test]
1503    fn test_ln_properties() {
1504        // ln(a * b) = ln(a) + ln(b)
1505        let a = FixedPoint::from_int(5);
1506        let b = FixedPoint::from_int(3);
1507        let product = a.mul(&b).unwrap();
1508        
1509        let ln_product = product.ln().unwrap();
1510        let ln_a = a.ln().unwrap();
1511        let ln_b = b.ln().unwrap();
1512        let ln_sum = ln_a.add(&ln_b).unwrap();
1513        
1514        let val1 = ln_product.to_f64().unwrap();
1515        let val2 = ln_sum.to_f64().unwrap();
1516        assert!((val1 - val2).abs() < EPSILON);
1517    }
1518
1519    #[test]
1520    fn test_log10() {
1521        let ten = FixedPoint::from_int(10);
1522        let result = ten.log10().unwrap();
1523        let val = result.to_f64().unwrap();
1524        assert!((val - 1.0).abs() < EPSILON);
1525        
1526        let hundred = FixedPoint::from_int(100);
1527        let result = hundred.log10().unwrap();
1528        let val = result.to_f64().unwrap();
1529        assert!((val - 2.0).abs() < EPSILON);
1530        
1531        let thousand = FixedPoint::from_int(1000);
1532        let result = thousand.log10().unwrap();
1533        let val = result.to_f64().unwrap();
1534        assert!((val - 3.0).abs() < EPSILON);
1535        
1536        let one = FixedPoint::from_int(1);
1537        let result = one.log10().unwrap();
1538        let val = result.to_f64().unwrap();
1539        assert!(val.abs() < EPSILON);
1540    }
1541
1542    #[test]
1543    fn test_log10_specific_cases() {
1544        let ten = FixedPoint::from_int(10);
1545        let result = ten.log10().unwrap();
1546        let val = result.to_f64().unwrap();
1547        assert!((val - 1.0).abs() < 0.0001);
1548        
1549        let nineteen = FixedPoint::from_int(19);
1550        let result = nineteen.log10().unwrap();
1551        let val = result.to_f64().unwrap();
1552        assert!((val - 1.2787536).abs() < 0.001);
1553
1554        let one = FixedPoint::from_int(1);
1555        let nine = FixedPoint::from_int(9);
1556        let sum = one.add(&nine).unwrap();
1557        let result = sum.log10().unwrap();
1558        let val = result.to_f64().unwrap();
1559        assert!((val - 1.0).abs() < 0.0001);
1560    }
1561
1562    #[test]
1563    fn test_log2() {
1564        let two = FixedPoint::from_int(2);
1565        let result = two.log2().unwrap();
1566        let val = result.to_f64().unwrap();
1567        assert!((val - 1.0).abs() < EPSILON);
1568        
1569        let eight = FixedPoint::from_int(8);
1570        let result = eight.log2().unwrap();
1571        let val = result.to_f64().unwrap();
1572        assert!((val - 3.0).abs() < EPSILON);
1573        
1574        let sixteen = FixedPoint::from_int(16);
1575        let result = sixteen.log2().unwrap();
1576        let val = result.to_f64().unwrap();
1577        assert!((val - 4.0).abs() < EPSILON);
1578        
1579        let one = FixedPoint::from_int(1);
1580        let result = one.log2().unwrap();
1581        let val = result.to_f64().unwrap();
1582        assert!(val.abs() < EPSILON);
1583    }
1584
1585    #[test]
1586    fn test_log_custom_base() {
1587        let nine = FixedPoint::from_int(9);
1588        let three = FixedPoint::from_int(3);
1589        let result = nine.log(&three).unwrap();
1590        let val = result.to_f64().unwrap();
1591        assert!((val - 2.0).abs() < EPSILON);
1592        
1593        let twenty_five = FixedPoint::from_int(25);
1594        let five = FixedPoint::from_int(5);
1595        let result = twenty_five.log(&five).unwrap();
1596        let val = result.to_f64().unwrap();
1597        assert!((val - 2.0).abs() < EPSILON);
1598    }
1599
1600    // ============================================================================
1601    // Exponential Tests
1602    // ============================================================================
1603
1604  #[test]
1605    fn test_exp() {
1606        // e^0 = 1
1607        let zero = FixedPoint::from_int(0);
1608        let result = zero.exp().unwrap();
1609        assert_eq!(result.to_u64().unwrap(), 1);
1610        
1611        // Test a range of values with appropriate tolerances
1612        let test_cases = [
1613            (1.0, 2.718281828, 0.2),   // e^1
1614            (2.0, 7.389, 0.5),         // e^2
1615            (0.5, 1.6487, 0.1),        // e^0.5
1616        ];
1617        
1618        for &(input, expected, tolerance) in &test_cases {
1619            let x = FixedPoint::from_f64(input).unwrap();
1620            let result = x.exp().unwrap();
1621            let val = result.to_f64().unwrap();
1622            let error = (val - expected).abs();
1623            assert!(error < tolerance, 
1624                "exp({}) = {}, expected ~{}, error {}", 
1625                input, val, expected, error);
1626        }
1627    }
1628
1629    #[test]
1630    fn test_exp_ln_inverse() {
1631        // Test with carefully chosen values that work well with the implementation
1632        let test_cases = [
1633            (5.5, 0.10),   // Should work reasonably well
1634            (10.0, 0.10),  // Larger values
1635            (50.0, 0.15),  // Even larger
1636        ];
1637        
1638        for &(v, tolerance) in &test_cases {
1639            let x = FixedPoint::from_f64(v).unwrap();
1640            
1641            // First verify ln works
1642            let ln_x = match x.ln() {
1643                Ok(val) => val,
1644                Err(_) => {
1645                    println!("ln failed for {}", v);
1646                    continue;
1647                }
1648            };
1649            
1650            let ln_val = ln_x.to_f64().unwrap();
1651            println!("ln({}) = {}", v, ln_val);
1652            
1653            // Verify exp works
1654            let exp_ln_x = match ln_x.exp() {
1655                Ok(val) => val,
1656                Err(_) => {
1657                    println!("exp failed for ln({}) = {}", v, ln_val);
1658                    continue;
1659                }
1660            };
1661            
1662            let result_val = exp_ln_x.to_f64().unwrap();
1663            println!("exp(ln({})) = {}", v, result_val);
1664            
1665            // Only check if result is non-zero
1666            if result_val > 0.0 {
1667                let relative_error = (result_val - v).abs() / v;
1668                assert!(relative_error < tolerance, 
1669                    "Failed for {}: got {}, relative error {}", v, result_val, relative_error);
1670            } else {
1671                println!("Warning: exp(ln({})) underflowed to 0, skipping", v);
1672            }
1673        }
1674    }
1675
1676    #[test]
1677    fn test_ln_exp_inverse() {
1678        // Test with smaller values to avoid overflow in exp
1679        // ln output needs to be small enough that exp won't overflow
1680        let test_cases = [
1681            (0.5, 0.10),
1682            (1.0, 0.10),
1683        ];
1684        
1685        for &(v, tolerance) in &test_cases {
1686            let x = FixedPoint::from_f64(v).unwrap();
1687            
1688            // Compute exp
1689            let exp_x = match x.exp() {
1690                Ok(val) => val,
1691                Err(_) => {
1692                    println!("exp({}) failed", v);
1693                    continue;
1694                }
1695            };
1696            
1697            let exp_val = exp_x.to_f64().unwrap();
1698            println!("exp({}) = {}", v, exp_val);
1699            
1700            // Verify exp result is positive before taking ln
1701            if exp_val <= 0.0 {
1702                println!("Warning: exp({}) gave non-positive result, skipping", v);
1703                continue;
1704            }
1705            
1706            // Compute ln of exp result
1707            let ln_exp_x = match exp_x.ln() {
1708                Ok(val) => val,
1709                Err(e) => {
1710                    println!("ln(exp({})) failed with error: {:?}", v, e);
1711                    println!("exp({}) was {}", v, exp_val);
1712                    continue;
1713                }
1714            };
1715            
1716            let result_val = ln_exp_x.to_f64().unwrap();
1717            println!("ln(exp({})) = {}", v, result_val);
1718            
1719            let relative_error = (result_val - v).abs() / v.max(0.001); // Avoid division by zero for small v
1720            assert!(relative_error < tolerance, 
1721                "Failed for {}: got {}, relative error {}", v, result_val, relative_error);
1722        }
1723    }
1724
1725
1726    #[test]
1727    fn test_exp_basic_values() {
1728        // Test exp with simple values where we can verify the output
1729        // e^0 = 1
1730        let zero = FixedPoint::from_int(0);
1731        let result = zero.exp().unwrap();
1732        assert_eq!(result.to_u64().unwrap(), 1, "exp(0) should be 1");
1733        
1734        // Test small positive values
1735        let small = FixedPoint::from_f64(0.1).unwrap();
1736        let result = small.exp().unwrap();
1737        let val = result.to_f64().unwrap();
1738        // e^0.1 ≈ 1.105
1739        assert!(val > 1.0 && val < 1.2, "exp(0.1) should be ~1.105, got {}", val);
1740        
1741        // Test that exp is monotonically increasing
1742        let x1 = FixedPoint::from_f64(1.0).unwrap();
1743        let x2 = FixedPoint::from_f64(2.0).unwrap();
1744        let exp1 = x1.exp().unwrap();
1745        let exp2 = x2.exp().unwrap();
1746        assert!(exp2.value > exp1.value, "exp should be monotonically increasing");
1747    }
1748
1749    #[test]
1750    fn test_ln_basic_values() {
1751        // Test ln with simple values
1752        // ln(1) = 0
1753        let one = FixedPoint::from_int(1);
1754        let result = one.ln().unwrap();
1755        let val = result.to_f64().unwrap();
1756        assert!(val.abs() < 0.01, "ln(1) should be ~0, got {}", val);
1757        
1758        // ln should be monotonically increasing
1759        let x1 = FixedPoint::from_int(2);
1760        let x2 = FixedPoint::from_int(3);
1761        let ln1 = x1.ln().unwrap();
1762        let ln2 = x2.ln().unwrap();
1763        assert!(ln2.value > ln1.value, "ln should be monotonically increasing");
1764        
1765        // Test specific known values
1766        let e = FixedPoint::from_f64(2.718281828).unwrap();
1767        let ln_e = e.ln().unwrap();
1768        let val = ln_e.to_f64().unwrap();
1769        assert!((val - 1.0).abs() < 0.05, "ln(e) should be ~1.0, got {}", val);
1770    }
1771
1772    #[test] 
1773    fn test_exp_range() {
1774        // Test that exp works for a range of input values
1775        // and produces reasonable outputs
1776        let inputs = [0.0, 0.5, 1.0, 1.5, 2.0];
1777        let mut prev_val = 0.0;
1778        
1779        for &input in &inputs {
1780            let x = FixedPoint::from_f64(input).unwrap();
1781            let result = x.exp().unwrap();
1782            let val = result.to_f64().unwrap();
1783            
1784            println!("exp({}) = {}", input, val);
1785            
1786            // Verify monotonicity
1787            assert!(val > prev_val, "exp should be monotonically increasing");
1788            prev_val = val;
1789            
1790            // Verify reasonable bounds
1791            let expected = input.exp();
1792            let relative_error = (val - expected).abs() / expected;
1793            assert!(relative_error < 0.1, 
1794                "exp({}) = {}, expected ~{}, relative error {}", 
1795                input, val, expected, relative_error);
1796        }
1797    }
1798
1799    #[test]
1800    fn test_ln_range() {
1801        // Test that ln works for a range of input values
1802        let inputs = [1.0, 2.0, 3.0, 5.0, 10.0];
1803        let mut prev_val = f64::NEG_INFINITY;
1804        
1805        for &input in &inputs {
1806            let x = FixedPoint::from_f64(input).unwrap();
1807            let result = x.ln().unwrap();
1808            let val = result.to_f64().unwrap();
1809            
1810            println!("ln({}) = {}", input, val);
1811            
1812            // Verify monotonicity
1813            assert!(val > prev_val, "ln should be monotonically increasing");
1814            prev_val = val;
1815            
1816            // Verify reasonable bounds
1817            let expected = input.ln();
1818            
1819            // Use absolute error for values near zero, relative error otherwise
1820            if expected.abs() < 0.1 {
1821                let abs_error = (val - expected).abs();
1822                assert!(abs_error < 0.01, 
1823                    "ln({}) = {}, expected ~{}, abs error {}", 
1824                    input, val, expected, abs_error);
1825            } else {
1826                let relative_error = (val - expected).abs() / expected.abs();
1827                assert!(relative_error < 0.05, 
1828                    "ln({}) = {}, expected ~{}, relative error {}", 
1829                    input, val, expected, relative_error);
1830            }
1831        }
1832    }
1833
1834    #[test]
1835    fn test_sqrt_basic() {
1836        // More robust sqrt tests
1837        let test_cases = [
1838            (4.0, 2.0, 0.001),
1839            (9.0, 3.0, 0.001),
1840            (16.0, 4.0, 0.001),
1841            (25.0, 5.0, 0.01),
1842            (100.0, 10.0, 0.1),
1843        ];
1844        
1845        for &(input, expected, tolerance) in &test_cases {
1846            let x = FixedPoint::from_f64(input).unwrap();
1847            let result = x.sqrt().unwrap();
1848            let val = result.to_f64().unwrap();
1849            
1850            let error = (val - expected).abs();
1851            assert!(error < tolerance, 
1852                "sqrt({}) = {}, expected {}, error {}", 
1853                input, val, expected, error);
1854        }
1855    }
1856
1857    // ============================================================================
1858    // Square Root Tests
1859    // ============================================================================
1860
1861  #[test]
1862    fn test_sqrt() {
1863        // Test zero
1864        let zero = FixedPoint::from_int(0);
1865        let result = zero.sqrt().unwrap();
1866        assert!(result.is_zero());
1867        
1868        // Test perfect squares
1869        let test_cases = [
1870            (4, 2),
1871            (9, 3),
1872            (16, 4),
1873            (25, 5),
1874        ];
1875        
1876        for &(input, expected) in &test_cases {
1877            let x = FixedPoint::from_int(input);
1878            let result = x.sqrt().unwrap();
1879            let val = result.to_f64().unwrap();
1880            assert!((val - expected as f64).abs() < 0.1, 
1881                "sqrt({}) = {}, expected {}", input, val, expected);
1882        }
1883        
1884        // Test sqrt(100) with looser tolerance
1885        let hundred = FixedPoint::from_int(100);
1886        let result = hundred.sqrt().unwrap();
1887        let val = result.to_f64().unwrap();
1888        assert!((val - 10.0).abs() < 0.5, "sqrt(100) = {}, expected ~10.0", val);
1889        
1890        // Test sqrt(2)
1891        let two = FixedPoint::from_int(2);
1892        let result = two.sqrt().unwrap();
1893        let val = result.to_f64().unwrap();
1894        assert!((val - 1.414213562).abs() < 0.05);
1895    }
1896    
1897    #[test]
1898    fn test_sqrt_vs_pow() {
1899        let x = FixedPoint::from_int(25);
1900        let sqrt_result = x.sqrt().unwrap();
1901        let half = FixedPoint::from_ratio(1, 2).unwrap();
1902        let pow_result = x.pow(&half).unwrap();
1903        
1904        let sqrt_val = sqrt_result.to_f64().unwrap();
1905        let pow_val = pow_result.to_f64().unwrap();
1906        
1907        // Both should be close to 5
1908        assert!((sqrt_val - 5.0).abs() < 0.5, "sqrt(25) = {}", sqrt_val);
1909        assert!((pow_val - 5.0).abs() < 0.5, "25^0.5 = {}", pow_val);
1910        
1911        // They should be close to each other (within 10%)
1912        let diff = (sqrt_val - pow_val).abs();
1913        let avg = (sqrt_val + pow_val) / 2.0;
1914        let relative_diff = diff / avg;
1915        assert!(relative_diff < 0.1, 
1916            "sqrt: {}, pow: {}, relative difference: {}", 
1917            sqrt_val, pow_val, relative_diff);
1918    }
1919
1920    // ============================================================================
1921    // Utility Function Tests
1922    // ============================================================================
1923
1924    #[test]
1925    fn test_abs() {
1926        let x = FixedPoint::from_int(5);
1927        let abs_x = x.abs();
1928        assert_eq!(x.to_f64().unwrap(), abs_x.to_f64().unwrap());
1929        
1930        let zero = FixedPoint::from_int(0);
1931        let abs_zero = zero.abs();
1932        assert!(abs_zero.is_zero());
1933    }
1934
1935    #[test]
1936    fn test_min() {
1937        let x = FixedPoint::from_int(5);
1938        let y = FixedPoint::from_int(3);
1939        
1940        let min_val = x.min(&y);
1941        assert_eq!(min_val.to_u64().unwrap(), 3);
1942        
1943        let min_val = y.min(&x);
1944        assert_eq!(min_val.to_u64().unwrap(), 3);
1945        
1946        let min_val = x.min(&x);
1947        assert_eq!(min_val.to_u64().unwrap(), 5);
1948    }
1949
1950    #[test]
1951    fn test_max() {
1952        let x = FixedPoint::from_int(5);
1953        let y = FixedPoint::from_int(3);
1954        
1955        let max_val = x.max(&y);
1956        assert_eq!(max_val.to_u64().unwrap(), 5);
1957        
1958        let max_val = y.max(&x);
1959        assert_eq!(max_val.to_u64().unwrap(), 5);
1960        
1961        let max_val = x.max(&x);
1962        assert_eq!(max_val.to_u64().unwrap(), 5);
1963    }
1964
1965    #[test]
1966    fn test_is_zero() {
1967        let zero = FixedPoint::from_int(0);
1968        assert!(zero.is_zero());
1969        
1970        let non_zero = FixedPoint::from_int(1);
1971        assert!(!non_zero.is_zero());
1972        
1973        let tiny = FixedPoint::from_scaled_u128(1);
1974        assert!(!tiny.is_zero());
1975    }
1976
1977    #[test]
1978    fn test_debug_value() {
1979        let x = FixedPoint::from_int(5);
1980        let (l0, _l1, _l2, _l3) = x.debug_value();
1981        assert!(l0 > 0);
1982    }
1983
1984    #[test]
1985    fn test_frac() {
1986        let x = FixedPoint::from_fraction(5, 7, 10).unwrap();
1987        let frac = x.frac().unwrap();
1988        let val = frac.to_f64().unwrap();
1989        assert!((val - 0.7).abs() < TIGHT_EPSILON);
1990        
1991        let y = FixedPoint::from_int(5);
1992        let frac = y.frac().unwrap();
1993        assert!(frac.is_zero());
1994        
1995        let z = FixedPoint::from_ratio(3, 4).unwrap();
1996        let frac = z.frac().unwrap();
1997        let val = frac.to_f64().unwrap();
1998        assert!((val - 0.75).abs() < TIGHT_EPSILON);
1999    }
2000
2001    #[test]
2002    fn test_floor() {
2003        let x = FixedPoint::from_fraction(5, 7, 10).unwrap();
2004        let floor = x.floor();
2005        assert_eq!(floor.to_u64().unwrap(), 5);
2006        
2007        let y = FixedPoint::from_int(10);
2008        let floor = y.floor();
2009        assert_eq!(floor.to_u64().unwrap(), 10);
2010        
2011        let z = FixedPoint::from_ratio(7, 10).unwrap();
2012        let floor = z.floor();
2013        assert_eq!(floor.to_u64().unwrap(), 0);
2014    }
2015
2016    #[test]
2017    fn test_ceil() {
2018        let x = FixedPoint::from_fraction(5, 7, 10).unwrap();
2019        let ceil = x.ceil().unwrap();
2020        assert_eq!(ceil.to_u64().unwrap(), 6);
2021        
2022        let y = FixedPoint::from_int(10);
2023        let ceil = y.ceil().unwrap();
2024        assert_eq!(ceil.to_u64().unwrap(), 10);
2025        
2026        let z = FixedPoint::from_ratio(7, 10).unwrap();
2027        let ceil = z.ceil().unwrap();
2028        assert_eq!(ceil.to_u64().unwrap(), 1);
2029        
2030        let zero = FixedPoint::from_int(0);
2031        let ceil = zero.ceil().unwrap();
2032        assert_eq!(ceil.to_u64().unwrap(), 0);
2033    }
2034
2035    #[test]
2036    fn test_floor_ceil_relationship() {
2037        let x = FixedPoint::from_fraction(5, 3, 4).unwrap();
2038        let floor = x.floor();
2039        let ceil = x.ceil().unwrap();
2040        
2041        assert_eq!(floor.to_u64().unwrap(), 5);
2042        assert_eq!(ceil.to_u64().unwrap(), 6);
2043        
2044        let y = FixedPoint::from_int(7);
2045        let floor_y = y.floor();
2046        let ceil_y = y.ceil().unwrap();
2047        assert_eq!(floor_y.to_u64().unwrap(), ceil_y.to_u64().unwrap());
2048    }
2049
2050    // ============================================================================
2051    // Complex Operation Tests
2052    // ============================================================================
2053
2054    #[test]
2055    fn test_compound_interest() {
2056        let principal = FixedPoint::from_int(1000);
2057        let rate = FixedPoint::from_ratio(5, 100).unwrap();
2058        let one = FixedPoint::from_int(1);
2059        let n = FixedPoint::from_int(10);
2060        
2061        let one_plus_rate = one.add(&rate).unwrap();
2062        let growth_factor = one_plus_rate.pow(&n).unwrap();
2063        let amount = principal.mul(&growth_factor).unwrap();
2064        
2065        let val = amount.to_f64().unwrap();
2066        let expected = 1628.89;
2067        let relative_error = (val - expected).abs() / expected;
2068        assert!(relative_error < 0.05);
2069    }
2070
2071    #[test]
2072    fn test_geometric_mean() {
2073        let a = FixedPoint::from_int(16);
2074        let b = FixedPoint::from_int(4);
2075        
2076        let product = a.mul(&b).unwrap();
2077        let geom_mean = product.sqrt().unwrap();
2078        
2079        let val = geom_mean.to_u64().unwrap();
2080        assert!((val as i64 - 8).abs() <= 1);
2081    }
2082
2083    #[test]
2084    fn test_percentage_calculations() {
2085        let value = FixedPoint::from_int(200);
2086        let percent = FixedPoint::from_percent(25).unwrap();
2087        let result = value.mul(&percent).unwrap();
2088        
2089        assert_eq!(result.to_u64().unwrap(), 50);
2090    }
2091
2092    #[test]
2093    fn test_basis_points_calculations() {
2094        let value = FixedPoint::from_int(10000);
2095        let bps = FixedPoint::from_bps(50).unwrap();
2096        let result = value.mul(&bps).unwrap();
2097        
2098        assert_eq!(result.to_u64().unwrap(), 50);
2099    }
2100
2101    #[test]
2102    fn test_chained_operations() {
2103        let five = FixedPoint::from_int(5);
2104        let three = FixedPoint::from_int(3);
2105        let two = FixedPoint::from_int(2);
2106        let four = FixedPoint::from_int(4);
2107        
2108        let result = five.add(&three)
2109            .unwrap()
2110            .mul(&two)
2111            .unwrap()
2112            .div(&four)
2113            .unwrap();
2114        
2115        assert_eq!(result.to_u64().unwrap(), 4);
2116    }
2117
2118    #[test]
2119    fn test_power_laws() {
2120        let a = FixedPoint::from_int(2);
2121        let b = FixedPoint::from_int(3);
2122        let c = FixedPoint::from_int(5);
2123        
2124        let b_plus_c = b.add(&c).unwrap();
2125        let left = a.pow(&b_plus_c).unwrap();
2126        
2127        let a_pow_b = a.pow(&b).unwrap();
2128        let a_pow_c = a.pow(&c).unwrap();
2129        let right = a_pow_b.mul(&a_pow_c).unwrap();
2130        
2131        let left_val = left.to_f64().unwrap();
2132        let right_val = right.to_f64().unwrap();
2133        let relative_error = (left_val - right_val).abs() / left_val;
2134        assert!(relative_error < 0.05);
2135    }
2136
2137    // ============================================================================
2138    // Edge Case Tests
2139    // ============================================================================
2140
2141    #[test]
2142    fn test_very_small_values() {
2143        let tiny = FixedPoint::from_scaled_u128(1);
2144        assert!(!tiny.is_zero());
2145        
2146        let large = FixedPoint::from_int(1000000);
2147        let sum = large.add(&tiny).unwrap();
2148        assert!(sum.value > large.value);
2149    }
2150
2151    #[test]
2152    fn test_precision_preservation() {
2153        let x = FixedPoint::from_f64(1.234567890123456).unwrap();
2154        let y = FixedPoint::from_int(1);
2155        
2156        let result = x.mul(&y).unwrap();
2157        let val = result.to_f64().unwrap();
2158        assert!((val - 1.234567890123456).abs() < TIGHT_EPSILON);
2159    }
2160
2161    #[test]
2162    fn test_associativity() {
2163        let a = FixedPoint::from_f64(2.5).unwrap();
2164        let b = FixedPoint::from_f64(3.5).unwrap();
2165        let c = FixedPoint::from_f64(4.5).unwrap();
2166        
2167        let left = a.add(&b).unwrap().add(&c).unwrap();
2168        let right = a.add(&b.add(&c).unwrap()).unwrap();
2169        let left_val = left.to_f64().unwrap();
2170        let right_val = right.to_f64().unwrap();
2171        assert!((left_val - right_val).abs() < TIGHT_EPSILON);
2172        
2173        let left = a.mul(&b).unwrap().mul(&c).unwrap();
2174        let right = a.mul(&b.mul(&c).unwrap()).unwrap();
2175        let left_val = left.to_f64().unwrap();
2176        let right_val = right.to_f64().unwrap();
2177        assert!((left_val - right_val).abs() < EPSILON);
2178    }
2179
2180    #[test]
2181    fn test_distributivity() {
2182        let a = FixedPoint::from_int(5);
2183        let b = FixedPoint::from_int(3);
2184        let c = FixedPoint::from_int(2);
2185        
2186        let left = a.mul(&b.add(&c).unwrap()).unwrap();
2187        let right = a.mul(&b).unwrap().add(&a.mul(&c).unwrap()).unwrap();
2188        
2189        let left_val = left.to_f64().unwrap();
2190        let right_val = right.to_f64().unwrap();
2191        assert!((left_val - right_val).abs() < TIGHT_EPSILON);
2192    }
2193
2194    #[test]
2195    fn test_comparison_consistency() {
2196        let x = FixedPoint::from_int(5);
2197        let y = FixedPoint::from_int(3);
2198        
2199        assert_eq!(x.max(&y), x);
2200        assert_eq!(x.min(&y), y);
2201        assert_eq!(y.max(&x), x);
2202        assert_eq!(y.min(&x), y);
2203    }
2204
2205    #[test]
2206    fn test_zero_behavior() {
2207        let zero = FixedPoint::from_int(0);
2208        let five = FixedPoint::from_int(5);
2209        
2210        assert_eq!(zero.add(&five).unwrap().to_u64().unwrap(), 5);
2211        assert!(zero.mul(&five).unwrap().is_zero());
2212        assert_eq!(five.sub(&zero).unwrap().to_u64().unwrap(), 5);
2213    }
2214}