1#![doc = include_str!("../README.md")]
2use uint::construct_uint;
37use anchor_lang::{err, error, error_code, require, Result};
38
39
40construct_uint! {
41 pub struct U256(4);
42}
43
44
45pub const SCALE: u128 = 1_000_000_000_000_000_000;
58
59const LN_2: U256 = U256([693_147_180_559_945_309, 0, 0, 0]);
63
64const LN_10: U256 = U256([2_302_585_092_994_045_684, 0, 0, 0]);
68
69const MAX_EXP_INPUT: U256 = U256([7_237_005_577_332_262_321, 7, 0, 0]);
74
75#[inline]
79fn scale_u256() -> U256 {
80 U256::from(SCALE)
81}
82
83fn 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub struct FixedPoint {
144 pub value: U256,
146}
147
148impl FixedPoint {
149 pub fn from_int(n: u64) -> Self {
164 Self {
165 value: U256::from(n) * scale_u256(),
166 }
167 }
168
169 pub fn from_u128(n: u128) -> Self {
183 Self {
184 value: U256::from(n) * scale_u256(),
185 }
186 }
187
188 pub fn from_scaled(value: U256) -> Self {
201 Self { value }
202 }
203
204 pub fn from_scaled_u128(value: u128) -> Self {
212 Self {
213 value: U256::from(value),
214 }
215 }
216
217 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 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 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 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 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 pub fn from_ratio(numerator: u64, denominator: u64) -> Result<Self> {
378 Self::from_fraction(0, numerator, denominator)
379 }
380
381 pub fn from_bps(bps: u64) -> Result<Self> {
396 Self::from_fraction(0, bps, 10_000)
397 }
398
399 pub fn from_percent(percent: u64) -> Result<Self> {
412 Self::from_fraction(0, percent, 100)
413 }
414
415 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 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 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 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 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 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 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 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 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 pub fn ln(&self) -> Result<Self> {
730 self.ln_fast()
731 }
732
733 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 pub fn exp(&self) -> Result<Self> {
810 self.exp_fast()
811 }
812
813 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 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; 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 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 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 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 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 pub fn abs(&self) -> Self {
955 *self
956 }
957
958 pub fn min(&self, other: &Self) -> Self {
972 if self.value < other.value {
973 *self
974 } else {
975 *other
976 }
977 }
978
979 pub fn max(&self, other: &Self) -> Self {
993 if self.value > other.value {
994 *self
995 } else {
996 *other
997 }
998 }
999
1000 pub fn is_zero(&self) -> bool {
1006 self.value.is_zero()
1007 }
1008
1009 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 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 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 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_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 const EPSILON: f64 = 0.01; const LOOSE_EPSILON: f64 = 0.05; const TIGHT_EPSILON: f64 = 0.00001; #[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 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 #[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 #[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 #[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 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 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 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 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 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 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 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); }
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 #[test]
1474 fn test_ln() {
1475 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 let one = FixedPoint::from_int(1);
1483 let result = one.ln().unwrap();
1484 assert!(result.to_f64().unwrap().abs() < TIGHT_EPSILON);
1485
1486 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 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 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 #[test]
1605 fn test_exp() {
1606 let zero = FixedPoint::from_int(0);
1608 let result = zero.exp().unwrap();
1609 assert_eq!(result.to_u64().unwrap(), 1);
1610
1611 let test_cases = [
1613 (1.0, 2.718281828, 0.2), (2.0, 7.389, 0.5), (0.5, 1.6487, 0.1), ];
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 let test_cases = [
1633 (5.5, 0.10), (10.0, 0.10), (50.0, 0.15), ];
1637
1638 for &(v, tolerance) in &test_cases {
1639 let x = FixedPoint::from_f64(v).unwrap();
1640
1641 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 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 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 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 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 if exp_val <= 0.0 {
1702 println!("Warning: exp({}) gave non-positive result, skipping", v);
1703 continue;
1704 }
1705
1706 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); 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 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 let small = FixedPoint::from_f64(0.1).unwrap();
1736 let result = small.exp().unwrap();
1737 let val = result.to_f64().unwrap();
1738 assert!(val > 1.0 && val < 1.2, "exp(0.1) should be ~1.105, got {}", val);
1740
1741 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 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 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 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 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 assert!(val > prev_val, "exp should be monotonically increasing");
1788 prev_val = val;
1789
1790 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 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 assert!(val > prev_val, "ln should be monotonically increasing");
1814 prev_val = val;
1815
1816 let expected = input.ln();
1818
1819 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 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 #[test]
1862 fn test_sqrt() {
1863 let zero = FixedPoint::from_int(0);
1865 let result = zero.sqrt().unwrap();
1866 assert!(result.is_zero());
1867
1868 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 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 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 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 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 #[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 #[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 #[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}