Skip to main content

rain_math_float/
lib.rs

1use alloy::hex::FromHex;
2use alloy::primitives::{Bytes, B256};
3use alloy::{sol, sol_types::SolCall};
4use revm::primitives::{fixed_bytes, U256};
5use serde::{Deserialize, Serialize};
6use std::ops::{Add, Div, Mul, Neg, Sub};
7use wasm_bindgen_utils::prelude::*;
8
9#[cfg(test)]
10use alloy::primitives::aliases::I224;
11
12pub mod error;
13mod evm;
14mod fuzz_ops;
15pub mod js_api;
16pub mod tables;
17
18use error::DecimalFloatErrorSelector;
19pub use error::FloatError;
20use evm::execute_call;
21#[cfg(test)]
22use evm::execute_test_call;
23
24sol!(
25    #![sol(all_derives)]
26    DecimalFloat,
27    concat!(env!("CARGO_MANIFEST_DIR"), "/abi/DecimalFloat.json")
28);
29
30#[cfg(test)]
31sol!(
32    #![sol(all_derives)]
33    TestDecimalFloat,
34    concat!(env!("CARGO_MANIFEST_DIR"), "/abi/TestDecimalFloat.json")
35);
36
37#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize, Hash)]
38#[wasm_bindgen]
39pub struct Float(B256);
40
41impl Float {
42    /// Creates a new `Float` from the given 32-byte value `B256`.
43    pub const fn from_raw(value: B256) -> Self {
44        Float(value)
45    }
46
47    /// Getter for inner 32-bytes value of this Float instance as `B256`.
48    pub fn get_inner(&self) -> B256 {
49        self.0
50    }
51
52    /// Sets the inner 32-byte value of this float from the given `B256`.
53    pub fn set_inner(&mut self, value: B256) {
54        self.0 = value;
55    }
56
57    /// Converts a fixed-point decimal value to a `Float` using the specified number of decimals.
58    ///
59    /// # Arguments
60    ///
61    /// * `value` - The fixed-point decimal value as a `U256`.
62    /// * `decimals` - The number of decimals in the fixed-point representation.
63    ///
64    /// # Returns
65    ///
66    /// * `Ok(Float)` - The resulting `Float` value.
67    /// * `Err(FloatError)` - If the conversion fails.
68    ///
69    /// # Example
70    ///
71    /// ```
72    /// use rain_math_float::Float;
73    /// use alloy::primitives::U256;
74    ///
75    /// // 123.45 with 2 decimals is represented as 12345
76    /// let value = U256::from(12345u64);
77    /// let decimals = 2u8;
78    /// let float = Float::from_fixed_decimal(value, decimals)?;
79    /// assert_eq!(float.format()?, "123.45");
80    ///
81    /// anyhow::Ok(())
82    /// ```
83    pub fn from_fixed_decimal(value: U256, decimals: u8) -> Result<Self, FloatError> {
84        let calldata = DecimalFloat::fromFixedDecimalLosslessCall { value, decimals }.abi_encode();
85
86        execute_call(Bytes::from(calldata), |output| {
87            let decoded =
88                DecimalFloat::fromFixedDecimalLosslessCall::abi_decode_returns(output.as_ref())?;
89            Ok(Float(decoded))
90        })
91    }
92
93    /// Converts a `Float` to a fixed-point decimal value using the specified number of decimals.
94    ///
95    /// # Arguments
96    ///
97    /// * `decimals` - The number of decimals in the fixed-point representation.
98    ///
99    /// # Returns
100    ///
101    /// * `Ok(U256)` - The resulting fixed-point decimal value.
102    /// * `Err(FloatError)` - If the conversion fails.
103    ///
104    /// # Example
105    ///
106    /// ```
107    /// use rain_math_float::Float;
108    /// use alloy::primitives::U256;
109    ///
110    /// // 123.45 with 2 decimals becomes 12345
111    /// let float = Float::parse("123.45".to_string())?;
112    /// let fixed = float.to_fixed_decimal(2)?;
113    /// assert_eq!(fixed, U256::from(12345u64));
114    ///
115    /// anyhow::Ok(())
116    /// ```
117    pub fn to_fixed_decimal(self, decimals: u8) -> Result<U256, FloatError> {
118        let Float(float) = self;
119        let calldata = DecimalFloat::toFixedDecimalLosslessCall { float, decimals }.abi_encode();
120
121        execute_call(Bytes::from(calldata), |output| {
122            let decoded =
123                DecimalFloat::toFixedDecimalLosslessCall::abi_decode_returns(output.as_ref())?;
124            Ok(decoded)
125        })
126    }
127
128    /// Converts a fixed-point decimal value to a `Float` using the specified number of decimals lossy.
129    ///
130    /// # Arguments
131    ///
132    /// * `value` - The fixed-point decimal value as a `U256`.
133    /// * `decimals` - The number of decimals in the fixed-point representation.
134    ///
135    /// # Returns
136    ///
137    /// * `Ok((Float, bool))` - The resulting `Float` value and a boolean indicating if the conversion was lossless.
138    /// * `Err(FloatError)` - If the conversion fails.
139    ///
140    /// # Example
141    ///
142    /// ```
143    /// use rain_math_float::Float;
144    /// use alloy::primitives::U256;
145    ///
146    /// // 123.45 with 2 decimals is represented as 12345
147    /// let value = U256::from(12345u64);
148    /// let decimals = 2u8;
149    /// let (float, lossless) = Float::from_fixed_decimal_lossy(value, decimals)?;
150    /// assert_eq!(float.format()?, "123.45");
151    /// assert!(lossless);
152    ///
153    /// anyhow::Ok(())
154    /// ```
155    pub fn from_fixed_decimal_lossy(value: U256, decimals: u8) -> Result<(Self, bool), FloatError> {
156        let calldata = DecimalFloat::fromFixedDecimalLossyCall { value, decimals }.abi_encode();
157
158        execute_call(Bytes::from(calldata), |output| {
159            let decoded =
160                DecimalFloat::fromFixedDecimalLossyCall::abi_decode_returns(output.as_ref())?;
161            Ok((Float(decoded._0), decoded._1))
162        })
163    }
164
165    /// Converts a `Float` to a fixed-point decimal value using the specified number of decimals lossy.
166    ///
167    /// # Arguments
168    ///
169    /// * `decimals` - The number of decimals in the fixed-point representation.
170    ///
171    /// # Returns
172    ///
173    /// * `Ok((U256, bool))` - The resulting fixed-point decimal value and a boolean indicating if the conversion was lossless.
174    /// * `Err(FloatError)` - If the conversion fails.
175    ///
176    /// # Example
177    ///
178    /// ```
179    /// use rain_math_float::Float;
180    /// use alloy::primitives::U256;
181    ///
182    /// // 123.45 with 2 decimals becomes 12345
183    /// let float = Float::from_fixed_decimal(U256::from(12345), 3)?;
184    /// let (fixed, lossless) = float.to_fixed_decimal_lossy(2)?;
185    /// assert_eq!(fixed, U256::from(1234u64));
186    /// assert!(!lossless);
187    ///
188    /// anyhow::Ok(())
189    /// ```
190    pub fn to_fixed_decimal_lossy(self, decimals: u8) -> Result<(U256, bool), FloatError> {
191        let Float(float) = self;
192        let calldata = DecimalFloat::toFixedDecimalLossyCall { float, decimals }.abi_encode();
193
194        execute_call(Bytes::from(calldata), |output| {
195            let decoded =
196                DecimalFloat::toFixedDecimalLossyCall::abi_decode_returns(output.as_ref())?;
197            Ok((decoded._0, decoded._1))
198        })
199    }
200
201    /// Packs a coefficient and exponent into a `Float` in a lossless manner.
202    ///
203    /// # Arguments
204    ///
205    /// * `coefficient` - The coefficient as an `I224`.
206    /// * `exponent` - The exponent as an `i32`.
207    ///
208    /// # Returns
209    ///
210    /// * `Ok(Float)` - The packed float.
211    /// * `Err(FloatError)` - If the packing fails (e.g., overflow).
212    ///
213    /// # Example
214    ///
215    /// ```
216    /// use std::str::FromStr;
217    /// use alloy::primitives::aliases::I224;
218    /// use rain_math_float::{Float, FloatError};
219    ///
220    /// let coefficient = I224::from_str("314")?;
221    /// let exponent = -2;
222    /// let float = Float::pack_lossless(coefficient, exponent)?;
223    /// assert_eq!(float.format()?, "3.14");
224    ///
225    /// anyhow::Ok(())
226    /// ```
227    #[cfg(test)]
228    pub fn pack_lossless(coefficient: I224, exponent: i32) -> Result<Self, FloatError> {
229        let calldata = TestDecimalFloat::packLosslessCall {
230            coefficient,
231            exponent,
232        }
233        .abi_encode();
234
235        execute_test_call(Bytes::from(calldata), |output| {
236            let decoded = TestDecimalFloat::packLosslessCall::abi_decode_returns(output.as_ref())?;
237            Ok(Float(decoded))
238        })
239    }
240
241    #[cfg(test)]
242    fn unpack(self) -> Result<(alloy::primitives::I256, alloy::primitives::I256), FloatError> {
243        let Float(float) = self;
244        let calldata = TestDecimalFloat::unpackCall { float }.abi_encode();
245
246        execute_test_call(Bytes::from(calldata), |output| {
247            let TestDecimalFloat::unpackReturn {
248                _0: coefficient,
249                _1: exponent,
250            } = TestDecimalFloat::unpackCall::abi_decode_returns(output.as_ref())?;
251
252            Ok((coefficient, exponent))
253        })
254    }
255
256    #[cfg(test)]
257    fn show_unpacked(self) -> Result<String, FloatError> {
258        let (coefficient, exponent) = self.unpack()?;
259        Ok(format!("{coefficient}e{exponent}"))
260    }
261
262    /// Parses a decimal string into a `Float`.
263    ///
264    /// # Arguments
265    ///
266    /// * `str` - The string to parse.
267    ///
268    /// # Returns
269    ///
270    /// * `Ok(Float)` - The parsed float.
271    /// * `Err(FloatError)` - If parsing fails.
272    ///
273    /// # Example
274    ///
275    /// ```
276    /// use rain_math_float::Float;
277    ///
278    /// let float = Float::parse("3.1415".to_string())?;
279    /// assert_eq!(float.format()?, "3.1415");
280    ///
281    /// anyhow::Ok(())
282    /// ```
283    pub fn parse(str: String) -> Result<Self, FloatError> {
284        let calldata = DecimalFloat::parseCall { str }.abi_encode();
285
286        execute_call(Bytes::from(calldata), |output| {
287            let DecimalFloat::parseReturn {
288                _0: error_selector,
289                _1: parsed_float,
290            } = DecimalFloat::parseCall::abi_decode_returns(output.as_ref())?;
291
292            if error_selector != fixed_bytes!("00000000") {
293                let selector = DecimalFloatErrorSelector::try_from(error_selector);
294                return Err(FloatError::DecimalFloatSelector(selector));
295            }
296
297            Ok(Float(parsed_float))
298        })
299    }
300
301    /// Returns the 32-byte hexadecimal string representation of the float.
302    ///
303    /// # Returns
304    ///
305    /// * `String` - The 32-byte hex string.
306    ///
307    /// # Example
308    ///
309    /// ```
310    /// use rain_math_float::Float;
311    /// let float = Float::from_hex("0x0000000000000000000000000000000000000000000000000000000000000005").unwrap();
312    /// assert_eq!(float.as_hex(), "0x0000000000000000000000000000000000000000000000000000000000000005");
313    /// ```
314    pub fn as_hex(self) -> String {
315        alloy::hex::encode_prefixed(self.0)
316    }
317
318    /// Constructs a `Float` from a 32-byte hexadecimal string.
319    ///
320    /// # Arguments
321    ///
322    /// * `hex` - The 32-byte hex string to parse.
323    ///
324    /// # Returns
325    ///
326    /// * `Ok(Float)` - The float parsed from the hex string.
327    /// * `Err(FloatError)` - If the hex string is not valid or not 32 bytes.
328    ///
329    /// # Example
330    ///
331    /// ```
332    /// use rain_math_float::Float;
333    /// let float = Float::from_hex("0x0000000000000000000000000000000000000000000000000000000000000005")?;
334    /// assert_eq!(float.as_hex(), "0x0000000000000000000000000000000000000000000000000000000000000005");
335    /// anyhow::Ok(())
336    /// ```
337    pub fn from_hex(hex: &str) -> Result<Self, FloatError> {
338        let bytes = B256::from_hex(hex).map_err(|_| FloatError::InvalidHex(hex.to_string()))?;
339        Ok(Float(bytes))
340    }
341
342    /// Returns the maximum positive value that can be represented as a `Float`.
343    ///
344    /// # Returns
345    ///
346    /// * `Ok(Float)` - The maximum positive value.
347    /// * `Err(FloatError)` - If the EVM call fails.
348    ///
349    /// # Example
350    ///
351    /// ```
352    /// use rain_math_float::Float;
353    ///
354    /// let max_pos = Float::max_positive_value()?;
355    /// let zero = Float::parse("0".to_string())?;
356    ///
357    /// // Max positive is greater than zero
358    /// assert!(max_pos.gt(zero)?);
359    ///
360    /// // Max positive is greater than any normal large number
361    /// let big_number = Float::parse("999999999999999999999".to_string())?;
362    /// assert!(max_pos.gt(big_number)?);
363    ///
364    /// anyhow::Ok(())
365    /// ```
366    pub fn max_positive_value() -> Result<Self, FloatError> {
367        let calldata = DecimalFloat::maxPositiveValueCall {}.abi_encode();
368
369        execute_call(Bytes::from(calldata), |output| {
370            let decoded = DecimalFloat::maxPositiveValueCall::abi_decode_returns(output.as_ref())?;
371            Ok(Float(decoded))
372        })
373    }
374
375    /// Returns the minimum positive value that can be represented as a `Float`.
376    ///
377    /// # Returns
378    ///
379    /// * `Ok(Float)` - The minimum positive value.
380    /// * `Err(FloatError)` - If the EVM call fails.
381    ///
382    /// # Example
383    ///
384    /// ```
385    /// use rain_math_float::Float;
386    ///
387    /// let min_pos = Float::min_positive_value()?;
388    /// let zero = Float::parse("0".to_string())?;
389    ///
390    /// // Min positive is greater than zero but smaller than any other positive number
391    /// assert!(min_pos.gt(zero)?);
392    ///
393    /// let small_number = Float::parse("0.000000000000000001".to_string())?;
394    /// assert!(min_pos.lt(small_number)?);
395    ///
396    /// anyhow::Ok(())
397    /// ```
398    pub fn min_positive_value() -> Result<Self, FloatError> {
399        let calldata = DecimalFloat::minPositiveValueCall {}.abi_encode();
400
401        execute_call(Bytes::from(calldata), |output| {
402            let decoded = DecimalFloat::minPositiveValueCall::abi_decode_returns(output.as_ref())?;
403            Ok(Float(decoded))
404        })
405    }
406
407    /// Returns the maximum negative value that can be represented as a `Float`.
408    ///
409    /// # Returns
410    ///
411    /// * `Ok(Float)` - The maximum negative value (closest to zero).
412    /// * `Err(FloatError)` - If the EVM call fails.
413    ///
414    /// # Example
415    ///
416    /// ```
417    /// use rain_math_float::Float;
418    ///
419    /// let max_neg = Float::max_negative_value()?;
420    /// let zero = Float::parse("0".to_string())?;
421    ///
422    /// // Max negative is less than zero but greater than any other negative number
423    /// assert!(max_neg.lt(zero)?);
424    ///
425    /// let small_negative = Float::parse("-0.000000000000000001".to_string())?;
426    /// assert!(max_neg.gt(small_negative)?);
427    ///
428    /// anyhow::Ok(())
429    /// ```
430    pub fn max_negative_value() -> Result<Self, FloatError> {
431        let calldata = DecimalFloat::maxNegativeValueCall {}.abi_encode();
432
433        execute_call(Bytes::from(calldata), |output| {
434            let decoded = DecimalFloat::maxNegativeValueCall::abi_decode_returns(output.as_ref())?;
435            Ok(Float(decoded))
436        })
437    }
438
439    /// Returns the minimum negative value that can be represented as a `Float`.
440    ///
441    /// # Returns
442    ///
443    /// * `Ok(Float)` - The minimum negative value (furthest from zero).
444    /// * `Err(FloatError)` - If the EVM call fails.
445    ///
446    /// # Example
447    ///
448    /// ```
449    /// use rain_math_float::Float;
450    ///
451    /// let min_neg = Float::min_negative_value()?;
452    /// let zero = Float::parse("0".to_string())?;
453    ///
454    /// // Min negative is less than zero
455    /// assert!(min_neg.lt(zero)?);
456    ///
457    /// // Min negative is less than any normal negative number
458    /// let big_negative = Float::parse("-999999999999999999999".to_string())?;
459    /// assert!(min_neg.lt(big_negative)?);
460    ///
461    /// anyhow::Ok(())
462    /// ```
463    pub fn min_negative_value() -> Result<Self, FloatError> {
464        let calldata = DecimalFloat::minNegativeValueCall {}.abi_encode();
465
466        execute_call(Bytes::from(calldata), |output| {
467            let decoded = DecimalFloat::minNegativeValueCall::abi_decode_returns(output.as_ref())?;
468            Ok(Float(decoded))
469        })
470    }
471
472    /// Returns the zero value of a `Float` in its maximized representation.
473    ///
474    /// # Returns
475    ///
476    /// * `Ok(Float)` - The zero value.
477    /// * `Err(FloatError)` - If the EVM call fails.
478    ///
479    /// # Example
480    ///
481    /// ```
482    /// use rain_math_float::Float;
483    ///
484    /// let zero = Float::zero()?;
485    /// assert!(zero.is_zero()?);
486    /// assert_eq!(zero.format()?, "0");
487    ///
488    /// // Should be equal to parsed zero
489    /// let parsed_zero = Float::parse("0".to_string())?;
490    /// assert!(zero.eq(parsed_zero)?);
491    ///
492    /// anyhow::Ok(())
493    /// ```
494    pub fn zero() -> Result<Self, FloatError> {
495        let calldata = DecimalFloat::zeroCall {}.abi_encode();
496
497        execute_call(Bytes::from(calldata), |output| {
498            let decoded = DecimalFloat::zeroCall::abi_decode_returns(output.as_ref())?;
499            Ok(Float(decoded))
500        })
501    }
502
503    /// Returns the default minimum value for scientific notation formatting (1e-4).
504    ///
505    /// Values smaller than this (in absolute value) will be formatted in scientific notation.
506    ///
507    /// # Returns
508    ///
509    /// * `Ok(Float)` - The default minimum (1e-4).
510    /// * `Err(FloatError)` - If the EVM call fails.
511    ///
512    /// # Example
513    ///
514    /// ```
515    /// use rain_math_float::Float;
516    ///
517    /// let min = Float::format_default_scientific_min()?;
518    /// assert_eq!(min.format()?, "0.0001");
519    ///
520    /// anyhow::Ok(())
521    /// ```
522    pub fn format_default_scientific_min() -> Result<Self, FloatError> {
523        let calldata = DecimalFloat::FORMAT_DEFAULT_SCIENTIFIC_MINCall {}.abi_encode();
524
525        execute_call(Bytes::from(calldata), |output| {
526            let decoded = DecimalFloat::FORMAT_DEFAULT_SCIENTIFIC_MINCall::abi_decode_returns(
527                output.as_ref(),
528            )?;
529            Ok(Float(decoded))
530        })
531    }
532
533    /// Returns the default maximum value for scientific notation formatting (1e9).
534    ///
535    /// Values larger than this (in absolute value) will be formatted in scientific notation.
536    ///
537    /// # Returns
538    ///
539    /// * `Ok(Float)` - The default maximum (1e9).
540    /// * `Err(FloatError)` - If the EVM call fails.
541    ///
542    /// # Example
543    ///
544    /// ```
545    /// use rain_math_float::Float;
546    ///
547    /// let max = Float::format_default_scientific_max()?;
548    /// assert_eq!(max.format()?, "1000000000");
549    ///
550    /// anyhow::Ok(())
551    /// ```
552    pub fn format_default_scientific_max() -> Result<Self, FloatError> {
553        let calldata = DecimalFloat::FORMAT_DEFAULT_SCIENTIFIC_MAXCall {}.abi_encode();
554
555        execute_call(Bytes::from(calldata), |output| {
556            let decoded = DecimalFloat::FORMAT_DEFAULT_SCIENTIFIC_MAXCall::abi_decode_returns(
557                output.as_ref(),
558            )?;
559            Ok(Float(decoded))
560        })
561    }
562
563    /// Formats the float as a decimal string using default scientific notation range (1e-4 to 1e9).
564    ///
565    /// Values within the range [1e-4, 1e9] will use decimal notation.
566    /// Values outside this range will use scientific notation.
567    ///
568    /// # Returns
569    ///
570    /// * `Ok(String)` - The formatted string.
571    /// * `Err(FloatError)` - If formatting fails.
572    ///
573    /// # Examples
574    ///
575    /// Values within the default range use decimal notation:
576    /// ```
577    /// use rain_math_float::Float;
578    ///
579    /// // At the boundaries (inclusive)
580    /// assert_eq!(Float::parse("0.0001".to_string())?.format()?, "0.0001");  // 1e-4
581    /// assert_eq!(Float::parse("1000000000".to_string())?.format()?, "1000000000");  // 1e9
582    ///
583    /// // Within range
584    /// assert_eq!(Float::parse("2.5".to_string())?.format()?, "2.5");
585    /// assert_eq!(Float::parse("123.456".to_string())?.format()?, "123.456");
586    /// assert_eq!(Float::parse("0.001".to_string())?.format()?, "0.001");
587    /// assert_eq!(Float::parse("1000000".to_string())?.format()?, "1000000");
588    ///
589    /// anyhow::Ok(())
590    /// ```
591    ///
592    /// Values outside the default range use scientific notation:
593    /// ```
594    /// use rain_math_float::Float;
595    ///
596    /// // Smaller than 1e-4
597    /// assert_eq!(Float::parse("0.00001".to_string())?.format()?, "1e-5");
598    /// assert_eq!(Float::parse("0.000001".to_string())?.format()?, "1e-6");
599    ///
600    /// // Larger than 1e9
601    /// assert_eq!(Float::parse("10000000000".to_string())?.format()?, "1e10");
602    /// assert_eq!(Float::parse("123000000000".to_string())?.format()?, "1.23e11");
603    ///
604    /// anyhow::Ok(())
605    /// ```
606    pub fn format(self) -> Result<String, FloatError> {
607        let Float(a) = self;
608        let calldata = DecimalFloat::format_1Call { a }.abi_encode();
609
610        execute_call(Bytes::from(calldata), |output| {
611            let decoded = DecimalFloat::format_1Call::abi_decode_returns(output.as_ref())?;
612            Ok(decoded)
613        })
614    }
615
616    /// Formats the float as a decimal string with explicit scientific notation control.
617    ///
618    /// # Arguments
619    ///
620    /// * `scientific` - If true, always use scientific notation. If false, use decimal notation.
621    ///
622    /// # Returns
623    ///
624    /// * `Ok(String)` - The formatted string.
625    /// * `Err(FloatError)` - If formatting fails.
626    ///
627    /// # Example
628    ///
629    /// ```
630    /// use rain_math_float::Float;
631    ///
632    /// let float = Float::parse("3.14".to_string())?;
633    /// assert_eq!(float.format_with_scientific(false)?, "3.14");
634    /// assert_eq!(float.format_with_scientific(true)?, "3.14");
635    ///
636    /// anyhow::Ok(())
637    /// ```
638    pub fn format_with_scientific(self, scientific: bool) -> Result<String, FloatError> {
639        let Float(a) = self;
640        let calldata = DecimalFloat::format_0Call { a, scientific }.abi_encode();
641
642        execute_call(Bytes::from(calldata), |output| {
643            let decoded = DecimalFloat::format_0Call::abi_decode_returns(output.as_ref())?;
644            Ok(decoded)
645        })
646    }
647
648    /// Formats the float as a decimal string with a custom scientific notation range.
649    ///
650    /// # Arguments
651    ///
652    /// * `scientific_min` - Values smaller than this (in absolute value) use scientific notation.
653    /// * `scientific_max` - Values larger than this (in absolute value) use scientific notation.
654    ///
655    /// # Returns
656    ///
657    /// * `Ok(String)` - The formatted string.
658    /// * `Err(FloatError)` - If formatting fails.
659    ///
660    /// # Example
661    ///
662    /// ```
663    /// use rain_math_float::Float;
664    ///
665    /// let float = Float::parse("0.001".to_string())?;
666    /// let min = Float::parse("0.01".to_string())?;
667    /// let max = Float::parse("100".to_string())?;
668    /// assert_eq!(float.format_with_range(min, max)?, "1e-3");
669    ///
670    /// anyhow::Ok(())
671    /// ```
672    pub fn format_with_range(
673        self,
674        scientific_min: Self,
675        scientific_max: Self,
676    ) -> Result<String, FloatError> {
677        let Float(a) = self;
678        let Float(scientific_min_inner) = scientific_min;
679        let Float(scientific_max_inner) = scientific_max;
680        let calldata = DecimalFloat::format_2Call {
681            a,
682            scientificMin: scientific_min_inner,
683            scientificMax: scientific_max_inner,
684        }
685        .abi_encode();
686
687        execute_call(Bytes::from(calldata), |output| {
688            let decoded = DecimalFloat::format_2Call::abi_decode_returns(output.as_ref())?;
689            Ok(decoded)
690        })
691    }
692
693    /// Returns `true` if `self` is less than `b`.
694    ///
695    /// # Arguments
696    ///
697    /// * `b` - The `Float` value to compare with `self`.
698    ///
699    /// # Returns
700    ///
701    /// * `Ok(true)` if `self` is less than `b`.
702    /// * `Ok(false)` if `self` is not less than `b`.
703    /// * `Err(FloatError)` if the comparison fails due to an error in the underlying EVM call or decoding.
704    ///
705    /// # Example
706    ///
707    /// ```
708    /// use rain_math_float::Float;
709    ///
710    /// let a = Float::parse("1.0".to_string())?;
711    /// let b = Float::parse("2.0".to_string())?;
712    /// assert!(a.lt(b)?);
713    ///
714    /// anyhow::Ok(())
715    /// ```
716    pub fn lt(self, b: Self) -> Result<bool, FloatError> {
717        let Float(a) = self;
718        let Float(b) = b;
719        let calldata = DecimalFloat::ltCall { a, b }.abi_encode();
720
721        execute_call(Bytes::from(calldata), |output| {
722            let decoded = DecimalFloat::ltCall::abi_decode_returns(output.as_ref())?;
723            Ok(decoded)
724        })
725    }
726
727    /// Returns `true` if `self` is equal to `b`.
728    ///
729    /// # Arguments
730    ///
731    /// * `b` - The `Float` value to compare with `self`.
732    ///
733    /// # Returns
734    ///
735    /// * `Ok(true)` if `self` is equal to `b`.
736    /// * `Ok(false)` if `self` is not equal to `b`.
737    /// * `Err(FloatError)` if the comparison fails due to an error in the underlying EVM call or decoding.
738    ///
739    /// # Example
740    ///
741    /// ```
742    /// use rain_math_float::Float;
743    ///
744    /// let a = Float::parse("3.14".to_string())?;
745    /// let b = Float::parse("3.14".to_string())?;
746    /// assert!(a.eq(b)?);
747    ///
748    /// anyhow::Ok(())
749    /// ```
750    pub fn eq(self, b: Self) -> Result<bool, FloatError> {
751        let Float(a) = self;
752        let Float(b) = b;
753        let calldata = DecimalFloat::eqCall { a, b }.abi_encode();
754
755        execute_call(Bytes::from(calldata), |output| {
756            let decoded = DecimalFloat::eqCall::abi_decode_returns(output.as_ref())?;
757            Ok(decoded)
758        })
759    }
760
761    /// Returns `true` if `self` is greater than `b`.
762    ///
763    /// # Arguments
764    ///
765    /// * `b` - The `Float` value to compare with `self`.
766    ///
767    /// # Returns
768    ///
769    /// * `Ok(true)` if `self` is greater than `b`.
770    /// * `Ok(false)` if `self` is not greater than `b`.
771    /// * `Err(FloatError)` if the comparison fails due to an error in the underlying EVM call or decoding.
772    ///
773    /// # Example
774    ///
775    /// ```
776    /// use rain_math_float::Float;
777    ///
778    /// let a = Float::parse("5.0".to_string())?;
779    /// let b = Float::parse("2.0".to_string())?;
780    /// assert!(a.gt(b)?);
781    ///
782    /// anyhow::Ok(())
783    /// ```
784    pub fn gt(self, b: Self) -> Result<bool, FloatError> {
785        let Float(a) = self;
786        let Float(b) = b;
787        let calldata = DecimalFloat::gtCall { a, b }.abi_encode();
788
789        execute_call(Bytes::from(calldata), |output| {
790            let decoded = DecimalFloat::gtCall::abi_decode_returns(output.as_ref())?;
791            Ok(decoded)
792        })
793    }
794
795    /// Returns the multiplicative inverse of the float.
796    ///
797    /// # Returns
798    ///
799    /// * `Ok(Float)` - The inverse.
800    /// * `Err(FloatError)` - If inversion fails.
801    ///
802    /// # Example
803    ///
804    /// ```
805    /// use rain_math_float::Float;
806    ///
807    /// let x = Float::parse("2.0".to_string())?;
808    /// let inv = x.inv()?;
809    /// assert!(inv.format()?.starts_with("0.5"));
810    ///
811    /// anyhow::Ok(())
812    /// ```
813    pub fn inv(self) -> Result<Self, FloatError> {
814        let Float(a) = self;
815        let calldata = DecimalFloat::invCall { a }.abi_encode();
816
817        execute_call(Bytes::from(calldata), |output| {
818            let decoded = DecimalFloat::invCall::abi_decode_returns(output.as_ref())?;
819            Ok(Float(decoded))
820        })
821    }
822
823    /// Returns the absolute value of the float.
824    ///
825    /// # Returns
826    ///
827    /// * `Ok(Float)` - The absolute value.
828    /// * `Err(FloatError)` - If the operation fails.
829    ///
830    /// # Example
831    ///
832    /// ```
833    /// use rain_math_float::Float;
834    ///
835    /// let x = Float::parse("-3.14".to_string())?;
836    /// let abs = x.abs()?;
837    /// assert_eq!(abs.format()?, "3.14");
838    ///
839    /// anyhow::Ok(())
840    /// ```
841    pub fn abs(self) -> Result<Float, FloatError> {
842        let Float(a) = self;
843        let calldata = DecimalFloat::absCall { a }.abi_encode();
844
845        execute_call(Bytes::from(calldata), |output| {
846            let decoded = DecimalFloat::absCall::abi_decode_returns(output.as_ref())?;
847            Ok(Float(decoded))
848        })
849    }
850
851    /// Returns `true` if `self` is less than or equal to `b`.
852    ///
853    /// # Arguments
854    ///
855    /// * `b` - The `Float` value to compare with `self`.
856    ///
857    /// # Returns
858    ///
859    /// * `Ok(true)` if `self` is less than or equal to `b`.
860    /// * `Ok(false)` if `self` is not less than or equal to `b`.
861    /// * `Err(FloatError)` if the comparison fails due to an error in the underlying EVM call or decoding.
862    ///
863    /// # Example
864    ///
865    /// ```
866    /// use rain_math_float::Float;
867    ///
868    /// let a = Float::parse("1.0".to_string())?;
869    /// let b = Float::parse("2.0".to_string())?;
870    /// assert!(a.lte(b)?);
871    ///
872    /// anyhow::Ok(())
873    /// ```
874    pub fn lte(self, b: Self) -> Result<bool, FloatError> {
875        let Float(a) = self;
876        let Float(b) = b;
877        let calldata = DecimalFloat::lteCall { a, b }.abi_encode();
878
879        execute_call(Bytes::from(calldata), |output| {
880            let decoded = DecimalFloat::lteCall::abi_decode_returns(output.as_ref())?;
881            Ok(decoded)
882        })
883    }
884
885    /// Returns `true` if `self` is greater than or equal to `b`.
886    ///
887    /// # Arguments
888    ///
889    /// * `b` - The `Float` value to compare with `self`.
890    ///
891    /// # Returns
892    ///
893    /// * `Ok(true)` if `self` is greater than or equal to `b`.
894    /// * `Ok(false)` if `self` is not greater than or equal to `b`.
895    /// * `Err(FloatError)` if the comparison fails due to an error in the underlying EVM call or decoding.
896    ///
897    /// # Example
898    ///
899    /// ```
900    /// use rain_math_float::Float;
901    ///
902    /// let a = Float::parse("2.0".to_string())?;
903    /// let b = Float::parse("1.0".to_string())?;
904    /// assert!(a.gte(b)?);
905    ///
906    /// anyhow::Ok(())
907    /// ```
908    pub fn gte(self, b: Self) -> Result<bool, FloatError> {
909        let Float(a) = self;
910        let Float(b) = b;
911        let calldata = DecimalFloat::gteCall { a, b }.abi_encode();
912
913        execute_call(Bytes::from(calldata), |output| {
914            let decoded = DecimalFloat::gteCall::abi_decode_returns(output.as_ref())?;
915            Ok(decoded)
916        })
917    }
918}
919
920impl Add for Float {
921    type Output = Result<Self, FloatError>;
922
923    /// Adds two floats.
924    ///
925    /// # Returns
926    ///
927    /// * `Ok(Float)` - The sum.
928    /// * `Err(FloatError)` - If addition fails.
929    ///
930    /// # Example
931    ///
932    /// ```
933    /// use rain_math_float::Float;
934    ///
935    /// let a = Float::parse("1.5".to_string())?;
936    /// let b = Float::parse("2.5".to_string())?;
937    /// let sum = (a + b)?;
938    /// assert_eq!(sum.format()?, "4");
939    ///
940    /// anyhow::Ok(())
941    /// ```
942    fn add(self, b: Self) -> Self::Output {
943        let Float(a) = self;
944        let Float(b) = b;
945        let calldata = DecimalFloat::addCall { a, b }.abi_encode();
946
947        execute_call(Bytes::from(calldata), |output| {
948            let decoded = DecimalFloat::addCall::abi_decode_returns(output.as_ref())?;
949            Ok(Float(decoded))
950        })
951    }
952}
953
954impl Sub for Float {
955    type Output = Result<Self, FloatError>;
956
957    /// Subtracts `b` from `self`.
958    ///
959    /// # Returns
960    ///
961    /// * `Ok(Float)` - The difference.
962    /// * `Err(FloatError)` - If subtraction fails.
963    ///
964    /// # Example
965    ///
966    /// ```
967    /// use rain_math_float::Float;
968    ///
969    /// let a = Float::parse("5.0".to_string())?;
970    /// let b = Float::parse("2.0".to_string())?;
971    /// let diff = (a - b)?;
972    /// assert_eq!(diff.format()?, "3");
973    ///
974    /// anyhow::Ok(())
975    /// ```
976    fn sub(self, b: Self) -> Self::Output {
977        let Float(a) = self;
978        let Float(b) = b;
979        let calldata = DecimalFloat::subCall { a, b }.abi_encode();
980
981        execute_call(Bytes::from(calldata), |output| {
982            let decoded = DecimalFloat::subCall::abi_decode_returns(output.as_ref())?;
983            Ok(Float(decoded))
984        })
985    }
986}
987
988impl Mul for Float {
989    type Output = Result<Self, FloatError>;
990
991    /// Multiplies two floats.
992    ///
993    /// # Returns
994    ///
995    /// * `Ok(Float)` - The product.
996    /// * `Err(FloatError)` - If multiplication fails.
997    ///
998    /// # Example
999    ///
1000    /// ```
1001    /// use rain_math_float::Float;
1002    ///
1003    /// let a = Float::parse("2.0".to_string())?;
1004    /// let b = Float::parse("3.0".to_string())?;
1005    /// let product = (a * b)?;
1006    /// assert_eq!(product.format()?, "6");
1007    ///
1008    /// anyhow::Ok(())
1009    /// ```
1010    fn mul(self, b: Self) -> Self::Output {
1011        let Float(a) = self;
1012        let Float(b) = b;
1013        let calldata = DecimalFloat::mulCall { a, b }.abi_encode();
1014
1015        execute_call(Bytes::from(calldata), |output| {
1016            let decoded = DecimalFloat::mulCall::abi_decode_returns(output.as_ref())?;
1017            Ok(Float(decoded))
1018        })
1019    }
1020}
1021
1022impl Div for Float {
1023    type Output = Result<Self, FloatError>;
1024
1025    /// Divides `self` by `b`.
1026    ///
1027    /// # Returns
1028    ///
1029    /// * `Ok(Float)` - The quotient.
1030    /// * `Err(FloatError)` - If division fails.
1031    ///
1032    /// # Example
1033    ///
1034    /// ```
1035    /// use rain_math_float::Float;
1036    ///
1037    /// let a = Float::parse("6.0".to_string())?;
1038    /// let b = Float::parse("2.0".to_string())?;
1039    /// let quotient = (a / b)?;
1040    /// assert_eq!(quotient.format()?, "3");
1041    ///
1042    /// anyhow::Ok(())
1043    /// ```
1044    fn div(self, b: Self) -> Self::Output {
1045        let Float(a) = self;
1046        let Float(b) = b;
1047        let calldata = DecimalFloat::divCall { a, b }.abi_encode();
1048
1049        execute_call(Bytes::from(calldata), |output| {
1050            let decoded = DecimalFloat::divCall::abi_decode_returns(output.as_ref())?;
1051            Ok(Float(decoded))
1052        })
1053    }
1054}
1055
1056impl Float {
1057    /// Returns the integer part of the float (truncation toward zero).
1058    ///
1059    /// # Returns
1060    ///
1061    /// * `Ok(Float)` - The integer part.
1062    /// * `Err(FloatError)` - If the operation fails.
1063    ///
1064    /// # Example
1065    ///
1066    /// ```
1067    /// use rain_math_float::Float;
1068    ///
1069    /// let x = Float::parse("3.75".to_string())?;
1070    /// let int = x.integer()?;
1071    /// assert_eq!(int.format()?, "3");
1072    ///
1073    /// let y = Float::parse("-3.75".to_string())?;
1074    /// let int_y = y.integer()?;
1075    /// assert_eq!(int_y.format()?, "-3");
1076    ///
1077    /// anyhow::Ok(())
1078    /// ```
1079    pub fn integer(self) -> Result<Float, FloatError> {
1080        let Float(a) = self;
1081        let calldata = DecimalFloat::integerCall { a }.abi_encode();
1082
1083        execute_call(Bytes::from(calldata), |output| {
1084            let decoded = DecimalFloat::integerCall::abi_decode_returns(output.as_ref())?;
1085            Ok(Float(decoded))
1086        })
1087    }
1088
1089    /// Returns the fractional part of the float.
1090    ///
1091    /// # Returns
1092    ///
1093    /// * `Ok(Float)` - The fractional part.
1094    /// * `Err(FloatError)` - If the operation fails.
1095    ///
1096    /// # Example
1097    ///
1098    /// ```
1099    /// use rain_math_float::Float;
1100    ///
1101    /// let x = Float::parse("3.75".to_string())?;
1102    /// let frac = x.frac()?;
1103    /// assert_eq!(frac.format()?, "0.75");
1104    ///
1105    /// anyhow::Ok(())
1106    /// ```
1107    pub fn frac(self) -> Result<Float, FloatError> {
1108        let Float(a) = self;
1109        let calldata = DecimalFloat::fracCall { a }.abi_encode();
1110
1111        execute_call(Bytes::from(calldata), |output| {
1112            let decoded = DecimalFloat::fracCall::abi_decode_returns(output.as_ref())?;
1113            Ok(Float(decoded))
1114        })
1115    }
1116
1117    /// Returns the floor of the float.
1118    ///
1119    /// # Returns
1120    ///
1121    /// * `Ok(Float)` - The floored value.
1122    /// * `Err(FloatError)` - If the operation fails.
1123    ///
1124    /// # Example
1125    ///
1126    /// ```
1127    /// use rain_math_float::Float;
1128    ///
1129    /// let x = Float::parse("3.75".to_string())?;
1130    /// let floor = x.floor()?;
1131    /// assert_eq!(floor.format()?, "3");
1132    ///
1133    /// anyhow::Ok(())
1134    /// ```
1135    pub fn floor(self) -> Result<Float, FloatError> {
1136        let Float(a) = self;
1137        let calldata = DecimalFloat::floorCall { a }.abi_encode();
1138
1139        execute_call(Bytes::from(calldata), |output| {
1140            let decoded = DecimalFloat::floorCall::abi_decode_returns(output.as_ref())?;
1141            Ok(Float(decoded))
1142        })
1143    }
1144
1145    /// Returns the minimum of `self` and `b`.
1146    ///
1147    /// # Arguments
1148    ///
1149    /// * `b` - The other `Float` to compare with.
1150    ///
1151    /// # Returns
1152    ///
1153    /// * `Ok(Float)` - The minimum value.
1154    /// * `Err(FloatError)` - If the operation fails.
1155    ///
1156    /// # Example
1157    ///
1158    /// ```
1159    /// use rain_math_float::Float;
1160    ///
1161    /// let a = Float::parse("1.0".to_string())?;
1162    /// let b = Float::parse("2.0".to_string())?;
1163    /// let min = a.min(b)?;
1164    /// assert_eq!(min.format()?, "1");
1165    ///
1166    /// anyhow::Ok(())
1167    /// ```
1168    pub fn min(self, b: Self) -> Result<Self, FloatError> {
1169        let Float(a) = self;
1170        let Float(b) = b;
1171        let calldata = DecimalFloat::minCall { a, b }.abi_encode();
1172
1173        execute_call(Bytes::from(calldata), |output| {
1174            let decoded = DecimalFloat::minCall::abi_decode_returns(output.as_ref())?;
1175            Ok(Float(decoded))
1176        })
1177    }
1178
1179    /// Returns the maximum of `self` and `b`.
1180    ///
1181    /// # Arguments
1182    ///
1183    /// * `b` - The other `Float` to compare with.
1184    ///
1185    /// # Returns
1186    ///
1187    /// * `Ok(Float)` - The maximum value.
1188    /// * `Err(FloatError)` - If the operation fails.
1189    ///
1190    /// # Example
1191    ///
1192    /// ```
1193    /// use rain_math_float::Float;
1194    ///
1195    /// let a = Float::parse("1.0".to_string())?;
1196    /// let b = Float::parse("2.0".to_string())?;
1197    /// let max = a.max(b)?;
1198    /// assert_eq!(max.format()?, "2");
1199    ///
1200    /// anyhow::Ok(())
1201    /// ```
1202    pub fn max(self, b: Self) -> Result<Self, FloatError> {
1203        let Float(a) = self;
1204        let Float(b) = b;
1205        let calldata = DecimalFloat::maxCall { a, b }.abi_encode();
1206
1207        execute_call(Bytes::from(calldata), |output| {
1208            let decoded = DecimalFloat::maxCall::abi_decode_returns(output.as_ref())?;
1209            Ok(Float(decoded))
1210        })
1211    }
1212
1213    /// Checks if the float is zero.
1214    ///
1215    /// # Returns
1216    ///
1217    /// * `Ok(true)` if the float is zero.
1218    /// * `Ok(false)` if the float is not zero.
1219    /// * `Err(FloatError)` if the operation fails.
1220    ///
1221    /// # Example
1222    ///
1223    /// ```
1224    /// use rain_math_float::Float;
1225    ///
1226    /// let zero = Float::parse("0".to_string())?;
1227    /// assert!(zero.is_zero()?);
1228    /// let nonzero = Float::parse("1.23".to_string())?;
1229    /// assert!(!nonzero.is_zero()?);
1230    ///
1231    /// anyhow::Ok(())
1232    /// ```
1233    pub fn is_zero(self) -> Result<bool, FloatError> {
1234        let Float(a) = self;
1235        let calldata = DecimalFloat::isZeroCall { a }.abi_encode();
1236
1237        execute_call(Bytes::from(calldata), |output| {
1238            let decoded = DecimalFloat::isZeroCall::abi_decode_returns(output.as_ref())?;
1239            Ok(decoded)
1240        })
1241    }
1242}
1243
1244impl Neg for Float {
1245    type Output = Result<Self, FloatError>;
1246
1247    /// Returns the negation of the float.
1248    ///
1249    /// # Returns
1250    ///
1251    /// * `Ok(Float)` - The negated value.
1252    /// * `Err(FloatError)` - If the operation fails.
1253    ///
1254    /// # Example
1255    ///
1256    /// ```
1257    /// use rain_math_float::Float;
1258    ///
1259    /// let x = Float::parse("3.14".to_string())?;
1260    /// let neg = (-x)?;
1261    /// assert_eq!(neg.format()?, "-3.14");
1262    ///
1263    /// anyhow::Ok(())
1264    /// ```
1265    fn neg(self) -> Self::Output {
1266        let Float(a) = self;
1267        let calldata = DecimalFloat::minusCall { a }.abi_encode();
1268
1269        execute_call(Bytes::from(calldata), |output| {
1270            let decoded = DecimalFloat::minusCall::abi_decode_returns(output.as_ref())?;
1271            Ok(Float(decoded))
1272        })
1273    }
1274}
1275
1276impl From<B256> for Float {
1277    fn from(value: B256) -> Self {
1278        Float(value)
1279    }
1280}
1281
1282impl From<Float> for B256 {
1283    fn from(value: Float) -> Self {
1284        value.0
1285    }
1286}
1287
1288#[cfg(test)]
1289mod tests {
1290    use crate::DecimalFloat::DecimalFloatErrors;
1291
1292    use super::*;
1293    use core::str::FromStr;
1294    use proptest::prelude::*;
1295    use serde_json::json;
1296
1297    /// Float::default() equals parsed "0".
1298    #[test]
1299    fn test_default() {
1300        let zero = Float::parse("0".to_string()).unwrap();
1301        assert!(zero.eq(Float::default()).unwrap());
1302    }
1303
1304    /// Float::zero() is_zero, formats as "0", equals parsed "0" and default.
1305    #[test]
1306    fn test_zero() {
1307        let zero = Float::zero().unwrap();
1308        assert!(zero.is_zero().unwrap());
1309        assert_eq!(zero.format().unwrap(), "0");
1310
1311        // Test that zero equals parsed zero
1312        let parsed_zero = Float::parse("0".to_string()).unwrap();
1313        assert!(zero.eq(parsed_zero).unwrap());
1314
1315        // Test that zero equals default
1316        assert!(zero.eq(Float::default()).unwrap());
1317    }
1318
1319    prop_compose! {
1320        fn arb_float()(
1321            coefficient in any::<I224>(),
1322            exponent in any::<i32>(),
1323        ) -> Float {
1324            Float::pack_lossless(coefficient, exponent).unwrap()
1325        }
1326    }
1327
1328    prop_compose! {
1329        fn reasonable_float()(
1330            int_part in -10i128.pow(18)..10i128.pow(18),
1331            decimal_part in 0u128..10u128.pow(18u32)
1332        ) -> Float {
1333            let num_str = if decimal_part == 0 {
1334                format!("{int_part}")
1335            } else {
1336                format!("{int_part}.{decimal_part}")
1337            };
1338
1339            Float::parse(num_str).unwrap()
1340        }
1341    }
1342
1343    /// JSON serialize then deserialize preserves equality and hex representation.
1344    #[test]
1345    fn test_serde() {
1346        let float = Float::parse("1.1341234234625468391".to_string()).unwrap();
1347        let serialized = serde_json::to_string(&float).unwrap();
1348        assert_eq!(
1349            serialized,
1350            json!("0xffffffed00000000000000000000000000000000000000009d642872ad59a7e7").to_string()
1351        );
1352        let deserialized: Float = serde_json::from_str(&serialized).unwrap();
1353        assert!(float.eq(deserialized).unwrap());
1354    }
1355
1356    proptest! {
1357        #[test]
1358        /// JSON round-trip preserves equality and serialized form for all floats.
1359        fn proptest_serde(float in arb_float()) {
1360            let serialized = serde_json::to_string(&float).unwrap();
1361            let deserialized: Float = serde_json::from_str(&serialized).unwrap();
1362            prop_assert!(float.eq(deserialized).unwrap());
1363            let re_serialized = serde_json::to_string(&deserialized).unwrap();
1364            prop_assert_eq!(serialized, re_serialized);
1365        }
1366    }
1367
1368    /// Parsing an empty string returns a DecimalFloatSelector error.
1369    #[test]
1370    fn test_parse_empty_string_error() {
1371        let err = Float::parse("".to_string()).unwrap_err();
1372        // We don't know the exact selector here, just ensure the error path is hit.
1373        assert!(matches!(err, FloatError::DecimalFloatSelector(_)));
1374    }
1375
1376    #[test]
1377    fn test_parse_exponent_overflow_error() {
1378        // Extremely large exponent expected to overflow (exponent >> i32::MAX).
1379        let err = Float::parse("1e3000000000".to_string()).unwrap_err();
1380        assert!(matches!(
1381            err,
1382            FloatError::DecimalFloat(e) if matches!(*e, DecimalFloatErrors::ExponentOverflow(_))
1383        ));
1384    }
1385
1386    /// Malformed inputs ("1.2.3", "abc") return specific error selectors.
1387    #[test]
1388    fn test_parse_edge_cases() {
1389        let err = Float::parse("1.2.3".to_string()).unwrap_err();
1390        assert!(matches!(
1391            err,
1392            FloatError::DecimalFloatSelector(Err(selector))
1393            if selector == fixed_bytes!("ad384e87")
1394        ));
1395
1396        let err = Float::parse("abc".to_string()).unwrap_err();
1397        assert!(matches!(
1398            err,
1399            FloatError::DecimalFloatSelector(Err(selector))
1400            if selector == fixed_bytes!("34bd2069")
1401        ));
1402    }
1403
1404    /// Boundary constants are distinct, correctly signed, correctly ordered,
1405    /// and bound normal values like 1 and -1.
1406    #[test]
1407    fn test_float_constants() {
1408        // Test that all constant methods return valid floats
1409        let max_pos = Float::max_positive_value().unwrap();
1410        let min_pos = Float::min_positive_value().unwrap();
1411        let max_neg = Float::max_negative_value().unwrap();
1412        let min_neg = Float::min_negative_value().unwrap();
1413
1414        let zero = Float::parse("0".to_string()).unwrap();
1415
1416        // Test mathematical properties without exposing binary representation
1417
1418        // All constants should be distinct
1419        assert!(!max_pos.eq(min_pos).unwrap());
1420        assert!(!max_neg.eq(min_neg).unwrap());
1421        assert!(!max_pos.eq(max_neg).unwrap());
1422        assert!(!min_pos.eq(min_neg).unwrap());
1423
1424        // Test sign properties
1425        assert!(min_pos.gt(zero).unwrap()); // min positive should be > 0
1426        assert!(max_pos.gt(zero).unwrap()); // max positive should be > 0
1427        assert!(max_neg.lt(zero).unwrap()); // max negative should be < 0
1428        assert!(min_neg.lt(zero).unwrap()); // min negative should be < 0
1429
1430        // Test ordering relationships
1431        assert!(min_pos.lt(max_pos).unwrap()); // min positive < max positive
1432        assert!(min_neg.lt(max_neg).unwrap()); // min negative < max negative
1433
1434        // Test boundary properties
1435        let one = Float::parse("1".to_string()).unwrap();
1436        let neg_one = Float::parse("-1".to_string()).unwrap();
1437
1438        // Positive constants should be greater than normal values
1439        assert!(max_pos.gt(one).unwrap());
1440        assert!(min_pos.lt(one).unwrap());
1441
1442        // Negative constants should be more extreme than normal negative values
1443        assert!(max_neg.gt(neg_one).unwrap());
1444        assert!(min_neg.lt(neg_one).unwrap());
1445    }
1446
1447    proptest! {
1448        #[test]
1449        /// format() then parse() round-trips to an equal value.
1450        fn test_format_parse(float in reasonable_float()) {
1451            let formatted = float.format().unwrap();
1452            let parsed = Float::parse(formatted.clone()).unwrap();
1453            prop_assert!(float.eq(parsed).unwrap());
1454        }
1455    }
1456
1457    proptest! {
1458        #[test]
1459        /// as_hex() then from_hex() round-trips to identical hex.
1460        fn test_as_from_hex(float in arb_float()) {
1461            let hex = float.as_hex();
1462            let parsed = Float::from_hex(&hex).unwrap();
1463            prop_assert_eq!(parsed.as_hex(), hex);
1464        }
1465    }
1466
1467    /// Adding two max-exponent floats overflows with ExponentOverflow.
1468    #[test]
1469    fn test_add_exponent_overflow_error() {
1470        let max_coeff_str = "13479973333575319897333507543509815336818572211270286240551805124607";
1471        let large_coeff_i224 = I224::from_str(max_coeff_str).unwrap();
1472        let exponent_max = i32::MAX;
1473
1474        let a = Float::pack_lossless(large_coeff_i224, exponent_max).unwrap();
1475
1476        let err = (a + a).unwrap_err();
1477
1478        assert!(matches!(
1479            err,
1480            FloatError::DecimalFloat(e) if matches!(*e, DecimalFloatErrors::ExponentOverflow(_))
1481        ));
1482    }
1483
1484    /// Subtracting opposite-sign max-exponent floats overflows.
1485    #[test]
1486    fn test_sub_exponent_overflow_error() {
1487        let max_coeff_str = "13479973333575319897333507543509815336818572211270286240551805124607";
1488        let large_coeff_i224 = I224::from_str(max_coeff_str).unwrap();
1489        let exponent_max = i32::MAX;
1490
1491        let a = Float::pack_lossless(large_coeff_i224, exponent_max).unwrap();
1492        let b = Float::pack_lossless(-large_coeff_i224, exponent_max).unwrap();
1493
1494        let err = (b - a).unwrap_err();
1495
1496        assert!(matches!(
1497            err,
1498            FloatError::DecimalFloat(e) if matches!(*e, DecimalFloatErrors::ExponentOverflow(_))
1499        ));
1500    }
1501
1502    proptest! {
1503        #[test]
1504        /// Addition does not panic for reasonable inputs.
1505        fn test_add(a in reasonable_float(), b in reasonable_float()) {
1506            (a + b).unwrap();
1507        }
1508    }
1509
1510    proptest! {
1511        #[test]
1512        /// Subtraction does not panic for reasonable inputs.
1513        fn test_sub(a in reasonable_float(), b in reasonable_float()) {
1514            (a - b).unwrap();
1515        }
1516    }
1517
1518    proptest! {
1519        #[test]
1520        /// (a + b) - b == a: subtraction inverts addition.
1521        fn test_add_sub(a in reasonable_float(), b in reasonable_float()) {
1522            let sum = (a + b).unwrap();
1523            let diff = (sum - b).unwrap();
1524            prop_assert_eq!(
1525                a.format().unwrap(),
1526                diff.format().unwrap(),
1527                "a: {}, b: {}",
1528                a.format().unwrap(),
1529                b.format().unwrap(),
1530            );
1531        }
1532    }
1533
1534    /// Manual check: -1 < 0 < 3, with correct lt/eq/gt for each pair.
1535    #[test]
1536    fn test_lt_eq_gt() {
1537        let negone = Float::parse("-1".to_string()).unwrap();
1538        let zero = Float::parse("0".to_string()).unwrap();
1539        let three = Float::parse("3".to_string()).unwrap();
1540
1541        assert!(negone.lt(zero).unwrap());
1542        assert!(!negone.eq(zero).unwrap());
1543        assert!(!negone.gt(zero).unwrap());
1544
1545        assert!(!three.lt(zero).unwrap());
1546        assert!(!three.eq(zero).unwrap());
1547        assert!(three.gt(zero).unwrap());
1548
1549        assert!(zero.lt(three).unwrap());
1550        assert!(!zero.eq(three).unwrap());
1551        assert!(!zero.gt(three).unwrap());
1552    }
1553
1554    proptest! {
1555        #[test]
1556        /// a == a, a-1 < a, a+1 > a for all reasonable floats.
1557        fn test_lt_eq_gt_with_add(a in reasonable_float()) {
1558            let b = a;
1559            let eq = a.eq(b).unwrap();
1560            prop_assert!(eq);
1561
1562            let one = Float::parse("1".to_string()).unwrap();
1563
1564            let a = (a - one).unwrap();
1565            let lt = a.lt(b).unwrap();
1566            prop_assert!(lt);
1567
1568            let a = (a + one).unwrap();
1569            let eq = a.eq(b).unwrap();
1570            prop_assert!(eq);
1571
1572            let a = (a + one).unwrap();
1573            let gt = a.gt(b).unwrap();
1574            prop_assert!(gt);
1575        }
1576
1577        #[test]
1578        /// Trichotomy: exactly one of lt, eq, gt is true for any two floats.
1579        fn test_exactly_one_lt_eq_gt(a in arb_float(), b in arb_float()) {
1580            let eq = a.eq(b).unwrap();
1581            let lt = a.lt(b).unwrap();
1582            let gt = a.gt(b).unwrap();
1583
1584            let a_str = a.show_unpacked().unwrap();
1585            let b_str = b.show_unpacked().unwrap();
1586
1587            prop_assert!(lt || eq || gt, "a: {a_str}, b: {b_str}");
1588            prop_assert!(!(lt && eq), "both less than and equal: a: {a_str}, b: {b_str}");
1589            prop_assert!(!(eq && gt), "both equal and greater than: a: {a_str}, b: {b_str}");
1590            prop_assert!(!(lt && gt), "both less than and greater than: a: {a_str}, b: {b_str}");
1591        }
1592    }
1593
1594    /// abs(-x) == abs(x) == |x| for manual positive, negative, and zero cases.
1595    #[test]
1596    fn test_abs() {
1597        let float = Float::parse("-3613.1324123".to_string()).unwrap();
1598        let abs = float.abs().unwrap();
1599        let formatted = abs.format().unwrap();
1600        assert_eq!(formatted, "3613.1324123");
1601
1602        let float = Float::parse("3613.1324123".to_string()).unwrap();
1603        let abs = float.abs().unwrap();
1604        let formatted = abs.format().unwrap();
1605        assert_eq!(formatted, "3613.1324123");
1606
1607        let float = Float::parse("0".to_string()).unwrap();
1608        let abs = float.abs().unwrap();
1609        let formatted = abs.format().unwrap();
1610        assert_eq!(formatted, "0");
1611    }
1612
1613    proptest! {
1614        #[test]
1615        /// Multiplication does not panic for reasonable inputs.
1616        fn test_mul(a in reasonable_float(), b in reasonable_float()) {
1617            (a * b).unwrap();
1618        }
1619    }
1620
1621    /// Negating a negative produces positive format; negating zero stays "0".
1622    #[test]
1623    fn test_minus_format() {
1624        let float = Float::parse("-123.1234234625468391".to_string()).unwrap();
1625        let negated = float.neg().unwrap();
1626
1627        let formatted_decimal = negated.format_with_scientific(false).unwrap();
1628        assert_eq!(formatted_decimal, "123.1234234625468391");
1629
1630        let float = Float::parse("0".to_string()).unwrap();
1631        let negated = float.neg().unwrap();
1632        let formatted = negated.format().unwrap();
1633        assert_eq!(formatted, "0");
1634    }
1635
1636    proptest! {
1637        #[test]
1638        /// Double negation is identity: -(-a) == a.
1639        fn test_minus_minus(float in arb_float()) {
1640            let negated = float.neg().unwrap();
1641            let renegated = negated.neg().unwrap();
1642            prop_assert!(float.eq(renegated).unwrap());
1643        }
1644    }
1645
1646    proptest! {
1647        #[test]
1648        /// a * inv(a) ≈ 1 within ±1e-37 for nonzero a.
1649        fn test_inv_prod(float in reasonable_float()) {
1650            let zero = Float::parse("0".to_string()).unwrap();
1651            prop_assume!(!float.eq(zero).unwrap());
1652
1653            let inv = float.inv().unwrap();
1654            let product = (float * inv).unwrap();
1655            let one = Float::parse("1".to_string()).unwrap();
1656
1657            // Allow for minor rounding errors introduced by the lossy
1658            // `inv` implementation. We consider the property to
1659            // hold if the product is within `±1e-37` of 1.
1660
1661            let eps = Float::parse("1e-37".to_string()).unwrap();
1662            let one_plus_eps = (one + eps).unwrap();
1663            let one_minus_eps = (one - eps).unwrap();
1664
1665            let within_upper = !product.gt(one_plus_eps).unwrap();
1666            let within_lower = !product.lt(one_minus_eps).unwrap();
1667
1668            prop_assert!(
1669                within_upper && within_lower,
1670                "float: {}, inv: {}, product: {} (not within ±ε)",
1671                float.show_unpacked().unwrap(),
1672                inv.show_unpacked().unwrap(),
1673                product.show_unpacked().unwrap(),
1674            );
1675        }
1676    }
1677
1678    proptest! {
1679        #[test]
1680        /// abs() never produces a string starting with "-".
1681        fn test_abs_no_minus_sign(float in reasonable_float()) {
1682            let abs = float.abs().unwrap();
1683            let formatted = abs.format().unwrap();
1684            prop_assert!(!formatted.starts_with("-"));
1685        }
1686
1687        #[test]
1688        /// abs is idempotent: abs(abs(a)) == abs(a).
1689        fn test_abs_abs(float in arb_float()) {
1690            let abs = float.abs().unwrap();
1691            let abs_abs = abs.abs().unwrap();
1692            prop_assert!(abs.eq(abs_abs).unwrap());
1693        }
1694    }
1695
1696    proptest! {
1697        #[test]
1698        /// Division does not panic for nonzero divisor.
1699        fn test_div(a in reasonable_float(), b in reasonable_float()) {
1700            let zero = Float::parse("0".to_string()).unwrap();
1701            prop_assume!(!b.eq(zero).unwrap());
1702
1703            (a / b).unwrap();
1704        }
1705    }
1706
1707    prop_compose! {
1708        fn small_int_float()(int_part in -1_000_000_000_000i128..1_000_000_000_000i128) -> Float {
1709            Float::parse(int_part.to_string()).unwrap()
1710        }
1711    }
1712
1713    proptest! {
1714        #[test]
1715        /// (a * b) / b == a: division inverts multiplication for small integers.
1716        fn test_mul_div_int(a in small_int_float(), b in small_int_float()) {
1717            let zero = Float::parse("0".to_string()).unwrap();
1718            prop_assume!(!b.eq(zero).unwrap());
1719
1720            let product = (a * b).unwrap();
1721            let quotient = (product / b).unwrap();
1722
1723            prop_assert!(
1724                a.eq(quotient).unwrap(),
1725                "a: {}, quotient: {}, b: {}",
1726                a.show_unpacked().unwrap(),
1727                quotient.show_unpacked().unwrap(),
1728                b.show_unpacked().unwrap()
1729            );
1730        }
1731    }
1732
1733    /// 6/3 == 2 and 2*3 == 6.
1734    #[test]
1735    fn test_mul_div_manual() {
1736        let two = Float::parse("2".to_string()).unwrap();
1737        let three = Float::parse("3".to_string()).unwrap();
1738        let six = Float::parse("6".to_string()).unwrap();
1739
1740        assert!(two.eq((six / three).unwrap()).unwrap());
1741        assert!(six.eq((two * three).unwrap()).unwrap());
1742    }
1743
1744    /// 1/0 returns DivisionByZero error.
1745    #[test]
1746    fn test_divide_by_zero_error() {
1747        let one = Float::parse("1".to_string()).unwrap();
1748        let zero = Float::parse("0".to_string()).unwrap();
1749        let err = (one / zero).unwrap_err();
1750
1751        assert!(matches!(
1752            err,
1753            FloatError::DecimalFloat(e) if matches!(*e, DecimalFloatErrors::DivisionByZero(_))
1754        ));
1755    }
1756
1757    /// Multiplying near-max exponents overflows.
1758    #[test]
1759    fn test_mul_exponent_overflow_error() {
1760        let near_max_exp = Float::parse("1e2147483646".to_string()).unwrap();
1761        let one_e_two = Float::parse("1e2".to_string()).unwrap();
1762
1763        let err = (near_max_exp * one_e_two).unwrap_err();
1764        assert!(matches!(
1765            err,
1766            FloatError::DecimalFloat(e) if matches!(*e, DecimalFloatErrors::ExponentOverflow(_))
1767        ));
1768    }
1769
1770    /// Dividing near-max exponent by small exponent overflows.
1771    #[test]
1772    fn test_div_exponent_overflow_error() {
1773        let near_max_exp = Float::parse("1e2147483646".to_string()).unwrap();
1774        let one_e_neg_hundred = Float::parse("1e-100".to_string()).unwrap();
1775
1776        let err = (near_max_exp / one_e_neg_hundred).unwrap_err();
1777        assert!(matches!(
1778            err,
1779            FloatError::DecimalFloat(e) if matches!(*e, DecimalFloatErrors::ExponentOverflow(_))
1780        ));
1781    }
1782
1783    /// Multiplying near-min exponents underflows; the public arithmetic
1784    /// surface reverts with `ExponentUnderflow` rather than silently
1785    /// producing zero.
1786    #[test]
1787    fn test_mul_exponent_underflow_error() {
1788        let near_min_exp = Float::parse("1e-2147483646".to_string()).unwrap();
1789        let one_e_neg_three = Float::parse("1e-3".to_string()).unwrap();
1790
1791        let err = (near_min_exp * one_e_neg_three).unwrap_err();
1792        assert!(matches!(
1793            err,
1794            FloatError::DecimalFloat(e) if matches!(*e, DecimalFloatErrors::ExponentUnderflow(_))
1795        ));
1796    }
1797
1798    /// from_fixed_decimal for known value/decimals pairs matches parsed strings.
1799    #[test]
1800    fn test_from_fixed_decimal() {
1801        let cases = vec![
1802            (U256::from(0u128), 0u8, "0"),
1803            (U256::from(0u128), 18u8, "0"),
1804            (U256::from(1u128), 18u8, "1e-18"),
1805            (U256::from(123456789u128), 0u8, "123456789"),
1806            (U256::from(123456789u128), 2u8, "123456789e-2"),
1807            (U256::from(1000000000000000000u128), 18u8, "1"),
1808        ];
1809
1810        for (amount, decimals, expected) in cases {
1811            let float = Float::from_fixed_decimal(amount, decimals).expect("should convert");
1812            let expected = Float::parse(expected.to_string()).unwrap();
1813            assert!(float.eq(expected).unwrap());
1814        }
1815    }
1816
1817    /// U256::MAX with 1 decimal overflows (LossyConversionToFloat).
1818    #[test]
1819    fn test_from_fixed_decimal_err() {
1820        let err = Float::from_fixed_decimal(U256::MAX, 1).unwrap_err();
1821        assert!(matches!(
1822            err,
1823            FloatError::DecimalFloat(e) if matches!(*e, DecimalFloatErrors::LossyConversionToFloat(_))
1824        ));
1825    }
1826
1827    /// to_fixed_decimal for known inputs matches expected U256 values.
1828    #[test]
1829    fn test_to_fixed_decimal() {
1830        let cases = vec![
1831            ("0", 0u8, 0u128),
1832            ("0", 18u8, 0u128),
1833            ("1e-18", 18u8, 1u128),
1834            ("123456789", 0u8, 123456789u128),
1835            ("123456789e-2", 2u8, 123456789u128),
1836            ("1", 18u8, 1000000000000000000u128),
1837        ];
1838
1839        for (input, decimals, expected) in cases {
1840            let float = Float::parse(input.to_string()).unwrap();
1841            let fixed = float.to_fixed_decimal(decimals).unwrap();
1842            assert_eq!(fixed, U256::from(expected));
1843        }
1844    }
1845
1846    /// For integers: floor == self, frac == 0, and floor + frac == self.
1847    #[test]
1848    fn test_frac_and_floor_integers() {
1849        let int_float = Float::parse("12345".to_string()).unwrap();
1850        let floor = int_float.floor().unwrap();
1851        let frac = int_float.frac().unwrap();
1852        let zero = Float::parse("0".to_string()).unwrap();
1853
1854        assert!(int_float.eq(floor).unwrap());
1855        assert!(frac.eq(zero).unwrap());
1856
1857        let int_float = Float::parse("-98765".to_string()).unwrap();
1858        let floor = int_float.floor().unwrap();
1859        let frac = int_float.frac().unwrap();
1860        let zero = Float::parse("0".to_string()).unwrap();
1861
1862        assert!(int_float.eq(floor).unwrap());
1863        assert!(frac.eq(zero).unwrap());
1864
1865        let recombined = (floor + frac).unwrap();
1866        assert!(int_float.eq(recombined).unwrap());
1867    }
1868
1869    /// floor(12345.6789) == 12345, frac(12345.6789) == 0.6789.
1870    #[test]
1871    fn test_frac_and_floor_floats() {
1872        let float = Float::parse("12345.6789".to_string()).unwrap();
1873        let floor = float.floor().unwrap();
1874        let frac = float.frac().unwrap();
1875
1876        let expected_floor = Float::parse("12345".to_string()).unwrap();
1877        let expected_frac = Float::parse("0.6789".to_string()).unwrap();
1878
1879        assert!(floor.eq(expected_floor).unwrap());
1880        assert!(frac.eq(expected_frac).unwrap());
1881    }
1882
1883    /// integer(12345.6789) == 12345, and integer + frac == original.
1884    #[test]
1885    fn test_integer_positive() {
1886        let float = Float::parse("12345.6789".to_string()).unwrap();
1887        let int = float.integer().unwrap();
1888        let expected = Float::parse("12345".to_string()).unwrap();
1889        assert!(int.eq(expected).unwrap());
1890
1891        let frac = float.frac().unwrap();
1892        let recombined = (int + frac).unwrap();
1893        assert!(float.eq(recombined).unwrap());
1894    }
1895
1896    /// integer truncates toward zero: integer(-12345.6789) == -12345.
1897    #[test]
1898    fn test_integer_negative() {
1899        let float = Float::parse("-12345.6789".to_string()).unwrap();
1900        let int = float.integer().unwrap();
1901        let frac = float.frac().unwrap();
1902
1903        // integer truncates toward zero, so -12345.6789 -> -12345
1904        let expected_int = Float::parse("-12345".to_string()).unwrap();
1905        let expected_frac = Float::parse("-0.6789".to_string()).unwrap();
1906
1907        assert!(int.eq(expected_int).unwrap());
1908        assert!(frac.eq(expected_frac).unwrap());
1909
1910        // integer + frac == original
1911        let recombined = (int + frac).unwrap();
1912        assert!(float.eq(recombined).unwrap());
1913    }
1914
1915    /// integer(42) == 42, frac(42) == 0 for positive and negative whole numbers.
1916    #[test]
1917    fn test_integer_whole_numbers() {
1918        let pos = Float::parse("42".to_string()).unwrap();
1919        assert!(pos.integer().unwrap().eq(pos).unwrap());
1920        let zero = Float::parse("0".to_string()).unwrap();
1921        assert!(pos.frac().unwrap().eq(zero).unwrap());
1922
1923        let neg = Float::parse("-42".to_string()).unwrap();
1924        assert!(neg.integer().unwrap().eq(neg).unwrap());
1925        assert!(neg.frac().unwrap().eq(zero).unwrap());
1926    }
1927
1928    proptest! {
1929        #[test]
1930        /// from_fixed_decimal then to_fixed_decimal round-trips for any non-negative I224.
1931        fn test_from_to_fixed_decimal_valid_range(coeff in any::<I224>(), decimals in 0u8..=66u8) {
1932            prop_assume!(coeff >= I224::ZERO);
1933
1934            let exponent = -(decimals as i32);
1935            let value = U256::from(coeff);
1936
1937            let float = Float::from_fixed_decimal(value, decimals).unwrap();
1938            let expected = Float::pack_lossless(coeff, exponent).unwrap();
1939            prop_assert!(float.eq(expected).unwrap());
1940
1941            let fixed = float.to_fixed_decimal(decimals).unwrap();
1942            assert_eq!(fixed, value);
1943        }
1944    }
1945
1946    proptest! {
1947        #[test]
1948        /// integer(a) + frac(a) == a, frac has no integer part, integer has
1949        /// no fractional part, and |frac| < 1.
1950        fn test_int_frac_properties(float in arb_float()) {
1951            let int = float.integer().unwrap();
1952            let frac = float.frac().unwrap();
1953
1954            let zero = Float::parse("0".to_string()).unwrap();
1955
1956            prop_assert!(
1957                int.frac().unwrap().eq(zero).unwrap(),
1958                "int.frac() is not zero: {}",
1959                int.show_unpacked().unwrap()
1960            );
1961
1962            prop_assert!(
1963                frac.integer().unwrap().eq(zero).unwrap(),
1964                "frac.integer() is not zero: {}",
1965                frac.show_unpacked().unwrap()
1966            );
1967
1968            let recombined = (int + frac).unwrap();
1969            prop_assert!(
1970                float.eq(recombined).unwrap(),
1971                "original: {}, int: {}, frac: {}, recombined: {}",
1972                float.show_unpacked().unwrap(),
1973                int.show_unpacked().unwrap(),
1974                frac.show_unpacked().unwrap(),
1975                recombined.show_unpacked().unwrap()
1976            );
1977
1978            let one = Float::parse("1".to_string()).unwrap();
1979            let neg_one = one.neg().unwrap();
1980            prop_assert!(
1981                frac.lt(one).unwrap(),
1982                "frac not < 1: {}",
1983                frac.show_unpacked().unwrap()
1984            );
1985            prop_assert!(
1986                frac.gt(neg_one).unwrap(),
1987                "frac not > -1: {}",
1988                frac.show_unpacked().unwrap()
1989            );
1990        }
1991    }
1992
1993    /// min/max for known value pairs, including identical arguments.
1994    #[test]
1995    fn test_min_max_manual() {
1996        let negone = Float::parse("-1".to_string()).unwrap();
1997        let zero = Float::parse("0".to_string()).unwrap();
1998        let three = Float::parse("3".to_string()).unwrap();
1999        let seven = Float::parse("7".to_string()).unwrap();
2000
2001        // --- min ---
2002        assert!(negone.eq(negone.min(zero).unwrap()).unwrap());
2003        assert!(negone.eq(negone.min(three).unwrap()).unwrap());
2004        assert!(zero.eq(zero.min(three).unwrap()).unwrap());
2005        // min with identical arguments should return that argument
2006        assert!(seven.eq(seven.min(seven).unwrap()).unwrap());
2007
2008        // --- max ---
2009        assert!(zero.eq(negone.max(zero).unwrap()).unwrap());
2010        assert!(three.eq(negone.max(three).unwrap()).unwrap());
2011        assert!(three.eq(zero.max(three).unwrap()).unwrap());
2012        // max with identical arguments should return that argument
2013        assert!(seven.eq(seven.max(seven).unwrap()).unwrap());
2014    }
2015
2016    /// is_zero for "0", "-0", "0.0" (all true) and "1" (false).
2017    #[test]
2018    fn test_is_zero_manual() {
2019        let zero = Float::parse("0".to_string()).unwrap();
2020        assert!(zero.is_zero().unwrap());
2021
2022        // Alternative zero representations that should also be considered zero.
2023        let neg_zero = Float::parse("-0".to_string()).unwrap();
2024        assert!(neg_zero.is_zero().unwrap());
2025        let zero_point = Float::parse("0.0".to_string()).unwrap();
2026        assert!(zero_point.is_zero().unwrap());
2027
2028        let one = Float::parse("1".to_string()).unwrap();
2029        assert!(!one.is_zero().unwrap());
2030    }
2031
2032    proptest! {
2033        #[test]
2034        /// min(a,b) <= both, max(a,b) >= both, each equals one operand,
2035        /// and min <= max.
2036        fn test_min_max_properties(a in reasonable_float(), b in reasonable_float()) {
2037            let min = a.min(b).unwrap();
2038            let max = a.max(b).unwrap();
2039
2040            prop_assert!(
2041                !min.gt(a).unwrap(),
2042                "min > a: min={}, a={}",
2043                min.show_unpacked().unwrap(),
2044                a.show_unpacked().unwrap()
2045            );
2046            prop_assert!(
2047                !min.gt(b).unwrap(),
2048                "min > b: min={}, b={}",
2049                min.show_unpacked().unwrap(),
2050                b.show_unpacked().unwrap()
2051            );
2052
2053            prop_assert!(
2054                !max.lt(a).unwrap(),
2055                "max < a: max={}, a={}",
2056                max.show_unpacked().unwrap(),
2057                a.show_unpacked().unwrap()
2058            );
2059            prop_assert!(
2060                !max.lt(b).unwrap(),
2061                "max < b: max={}, b={}",
2062                max.show_unpacked().unwrap(),
2063                b.show_unpacked().unwrap()
2064            );
2065
2066            let min_is_a = min.eq(a).unwrap();
2067            let min_is_b = min.eq(b).unwrap();
2068            prop_assert!(
2069                min_is_a || min_is_b,
2070                "min is not equal to either operand: a={}, b={}, min={}",
2071                a.show_unpacked().unwrap(),
2072                b.show_unpacked().unwrap(),
2073                min.show_unpacked().unwrap()
2074            );
2075
2076            let max_is_a = max.eq(a).unwrap();
2077            let max_is_b = max.eq(b).unwrap();
2078            prop_assert!(
2079                max_is_a || max_is_b,
2080                "max is not equal to either operand: a={}, b={}, max={}",
2081                a.show_unpacked().unwrap(),
2082                b.show_unpacked().unwrap(),
2083                max.show_unpacked().unwrap()
2084            );
2085
2086            prop_assert!(
2087                !min.gt(max).unwrap(),
2088                "min > max: min={}, max={}",
2089                min.show_unpacked().unwrap(),
2090                max.show_unpacked().unwrap()
2091            );
2092        }
2093    }
2094
2095    /// Manual lte/gte checks: -1 <= 0 <= 3, 0 >= -1, 3 >= 0.
2096    #[test]
2097    fn test_lte_gte() {
2098        let negone = Float::parse("-1".to_string()).unwrap();
2099        let zero = Float::parse("0".to_string()).unwrap();
2100        let three = Float::parse("3".to_string()).unwrap();
2101
2102        assert!(negone.lte(zero).unwrap());
2103        assert!(zero.lte(three).unwrap());
2104        assert!(negone.lte(three).unwrap());
2105
2106        assert!(zero.gte(negone).unwrap());
2107        assert!(three.gte(zero).unwrap());
2108        assert!(three.gte(negone).unwrap());
2109    }
2110
2111    proptest! {
2112        #[test]
2113        /// a-1 lte a, a lte a and gte a, a+1 gte a.
2114        fn test_lte_gte_fuzz(a in reasonable_float()) {
2115            let b = a;
2116            let one = Float::parse("1".to_string()).unwrap();
2117
2118            let a = (a - one).unwrap();
2119            let lte = a.lte(b).unwrap();
2120            prop_assert!(lte); // lt
2121
2122            let a = (a + one).unwrap();
2123            let gte = a.gte(b).unwrap();
2124            let lte = a.lte(b).unwrap();
2125            prop_assert!(gte); // eq
2126            prop_assert!(lte); // eq
2127
2128            let a = (a + one).unwrap();
2129            let gte = a.gte(b).unwrap();
2130            prop_assert!(gte); // gt
2131        }
2132    }
2133
2134    /// from_fixed_decimal_lossy: lossless for small values, lossy for U256::MAX.
2135    #[test]
2136    fn test_from_fixed_decimal_lossy() {
2137        // Test lossless conversions (values that fit in Float's precision)
2138        let lossless_cases = vec![
2139            (U256::from(0u128), 0u8, "0"),
2140            (U256::from(0u128), 18u8, "0"),
2141            (U256::from(1u128), 18u8, "1e-18"),
2142            (U256::from(123456789u128), 0u8, "123456789"),
2143            (U256::from(123456789u128), 2u8, "123456789e-2"),
2144            (U256::from(1000000000000000000u128), 18u8, "1"),
2145        ];
2146
2147        for (amount, decimals, expected) in lossless_cases {
2148            let (float, lossless) =
2149                Float::from_fixed_decimal_lossy(amount, decimals).expect("should convert");
2150            let expected = Float::parse(expected.to_string()).unwrap();
2151            assert!(float.eq(expected).unwrap());
2152            assert!(
2153                lossless,
2154                "conversion should be lossless for amount={}, decimals={}",
2155                amount, decimals
2156            );
2157        }
2158
2159        // Test lossy conversion with U256::MAX (too large to fit in Float's 224-bit coefficient)
2160        let (float, lossless) = Float::from_fixed_decimal_lossy(U256::MAX, 1).unwrap();
2161        assert!(!lossless, "U256::MAX conversion should be lossy");
2162        assert!(!float.is_zero().unwrap(), "result should not be zero");
2163    }
2164
2165    /// to_fixed_decimal_lossy: correctly reports lossy/lossless for precision loss.
2166    #[test]
2167    fn test_to_fixed_decimal_lossy() {
2168        // Test lossy conversions (loss of precision)
2169        let lossy_cases = vec![
2170            (U256::from(1), 18u8, 0u128),
2171            (U256::from(123456789), 0u8, 12345678u128),
2172            (U256::from(123456789), 2u8, 12345678u128),
2173        ];
2174
2175        for (input, decimals, expected) in lossy_cases {
2176            let float = Float::from_fixed_decimal(input, decimals + 1).unwrap();
2177            let (fixed, lossless) = float.to_fixed_decimal_lossy(decimals).unwrap();
2178            assert_eq!(
2179                fixed,
2180                U256::from(expected),
2181                "wrong value for input={}, decimals={}",
2182                input,
2183                decimals
2184            );
2185            assert!(
2186                !lossless,
2187                "should be lossy for input={}, decimals={}",
2188                input, decimals
2189            );
2190        }
2191
2192        // Test lossless conversions (no loss of precision)
2193        let lossless_cases = vec![
2194            // Zero is always lossless
2195            (U256::from(0), 0u8, 0u128),
2196            (U256::from(0), 18u8, 0u128),
2197            // Converting 12340 with 3 decimals (12.340) to 2 decimals (12.34) is lossless
2198            (U256::from(12340), 3u8, 1234u128),
2199        ];
2200
2201        for (input, decimals, expected) in lossless_cases {
2202            let float = Float::from_fixed_decimal(input, decimals + 1).unwrap();
2203            let (fixed, lossless) = float.to_fixed_decimal_lossy(decimals).unwrap();
2204            assert_eq!(
2205                fixed,
2206                U256::from(expected),
2207                "wrong value for input={}, decimals={}",
2208                input,
2209                decimals
2210            );
2211            assert!(
2212                lossless,
2213                "should be lossless for input={}, decimals={}",
2214                input, decimals
2215            );
2216        }
2217    }
2218
2219    proptest! {
2220        #[test]
2221        /// Lossy fixed-decimal round-trip: from(decimals+1) then to(decimals) is
2222        /// lossy iff the last digit is nonzero.
2223        fn test_from_to_fixed_decimal_lossy_valid_range(coeff in any::<I224>(), decimals in 0u8..=66u8) {
2224            prop_assume!(coeff >= I224::ZERO);
2225
2226            let exponent = -(decimals as i32 + 1);
2227            let value = U256::from(coeff);
2228
2229            let (float, from_lossless) = Float::from_fixed_decimal_lossy(value, decimals + 1).unwrap();
2230            let expected = Float::pack_lossless(coeff, exponent).unwrap();
2231            prop_assert!(float.eq(expected).unwrap());
2232
2233            // from_fixed_decimal_lossy should be lossless for values that fit in Float's precision
2234            prop_assert!(from_lossless, "from_fixed_decimal_lossy should be lossless for coeff={coeff}");
2235
2236            let (fixed, to_lossless) = float.to_fixed_decimal_lossy(decimals).unwrap();
2237            assert_eq!(fixed, value / U256::from(10));
2238
2239            // Converting from decimals+1 to decimals should be lossy unless the value is zero or
2240            // the last digit is zero (divisible by 10)
2241            if value == U256::ZERO || value % U256::from(10) == U256::ZERO {
2242                prop_assert!(to_lossless, "to_fixed_decimal_lossy should be lossless when last digit is 0: value={}", value);
2243            } else {
2244                prop_assert!(!to_lossless, "to_fixed_decimal_lossy should be lossy when losing precision: value={}", value);
2245            }
2246        }
2247    }
2248
2249    proptest! {
2250        #[test]
2251        /// All reasonable positive floats are bounded by min/max_positive_value,
2252        /// all negative by min/max_negative_value.
2253        fn test_constants_relationships(float in reasonable_float()) {
2254            let max_pos = Float::max_positive_value().unwrap();
2255            let min_pos = Float::min_positive_value().unwrap();
2256            let max_neg = Float::max_negative_value().unwrap();
2257            let min_neg = Float::min_negative_value().unwrap();
2258            let zero = Float::parse("0".to_string()).unwrap();
2259
2260            // Test that constants are the extremes
2261            // Any reasonable positive float should be <= max_positive and >= min_positive
2262            if float.gt(zero).unwrap() {
2263                prop_assert!(float.lte(max_pos).unwrap());
2264                prop_assert!(float.gte(min_pos).unwrap());
2265            }
2266
2267            // Any reasonable negative float should be <= max_negative and >= min_negative
2268            // (max_negative is closest to zero, min_negative is furthest from zero)
2269            if float.lt(zero).unwrap() {
2270                prop_assert!(float.lte(max_neg).unwrap());
2271                prop_assert!(float.gte(min_neg).unwrap());
2272            }
2273
2274            // Constants should be consistent regardless of arbitrary float
2275            prop_assert!(max_pos.gt(zero).unwrap());
2276            prop_assert!(min_pos.gt(zero).unwrap());
2277            prop_assert!(max_neg.lt(zero).unwrap());
2278            prop_assert!(min_neg.lt(zero).unwrap());
2279
2280            // Verify constants maintain their ordering
2281            prop_assert!(min_pos.lt(max_pos).unwrap());
2282            prop_assert!(min_neg.lt(max_neg).unwrap());
2283            prop_assert!(max_neg.lt(zero).unwrap());
2284            prop_assert!(min_pos.gt(zero).unwrap());
2285        }
2286    }
2287
2288    proptest! {
2289        #[test]
2290        /// No arbitrary float exceeds max_positive or is below min_negative.
2291        fn test_constants_edge_cases(float in arb_float()) {
2292            let max_pos = Float::max_positive_value().unwrap();
2293            let min_pos = Float::min_positive_value().unwrap();
2294            let max_neg = Float::max_negative_value().unwrap();
2295            let min_neg = Float::min_negative_value().unwrap();
2296
2297            // Constants should always be distinct
2298            prop_assert!(!max_pos.eq(min_pos).unwrap());
2299            prop_assert!(!max_neg.eq(min_neg).unwrap());
2300            prop_assert!(!max_pos.eq(max_neg).unwrap());
2301            prop_assert!(!min_pos.eq(min_neg).unwrap());
2302
2303            // Test that constants are at the boundaries
2304            // (Note: We can't test arithmetic operations that would overflow/underflow
2305            // since those would fail, but we can test comparisons)
2306
2307            // No arbitrary float should be greater than max_pos or less than min_neg
2308            if !float.eq(max_pos).unwrap() {
2309                prop_assert!(!float.gt(max_pos).unwrap());
2310            }
2311            if !float.eq(min_neg).unwrap() {
2312                prop_assert!(!float.lt(min_neg).unwrap());
2313            }
2314        }
2315    }
2316}