VYPR
High severityOSV Advisory· Published Jan 27, 2026· Updated Jan 28, 2026

soroban-fixed-point-math has Incorrect Rounding and Overflow Handling in Signed Fixed-Point Math with Negatives

CVE-2026-24783

Description

soroban-fixed-point-math is a fixed-point math library for Soroban smart contacts. In versions 1.3.0 and 1.4.0, the mulDiv(x, y, z) function incorrectly handled cases where both the intermediate product $x * y$ and the divisor $z$ were negative. The logic assumed that if the intermediate product was negative, the final result must also be negative, neglecting the sign of $z$. This resulted in rounding being applied in the wrong direction for cases where both $x * y$ and $z$ were negative. The functions most at risk are fixed_div_floor and fixed_div_ceil, as they often use non-constant numbers as the divisor $z$ in mulDiv. This error is present in all signed FixedPoint and SorobanFixedPoint implementations, including i64, i128, and I256. Versions 1.3.1 and 1.4.1 contain a patch. No known workarounds for this issue are available.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
soroban-fixed-point-mathcrates.io
>= 1.4.0, < 1.4.11.4.1
soroban-fixed-point-mathcrates.io
>= 1.3.0, < 1.3.11.3.1

Affected products

1

Patches

1
c9233f709419

fix: better edge case handling and test suite improvements (#9)

7 files changed · +2762 638
  • Cargo.toml+1 1 modified
    @@ -12,4 +12,4 @@ keywords = ["no_std", "wasm"]
     rust-version = "1.89"
     
     [dependencies]
    -soroban-sdk = { version = "23.0.2" }
    +soroban-sdk = { version = "23" }
    
  • src/i128.rs+906 158 modified
    @@ -28,10 +28,12 @@ pub(crate) fn mul_div_floor(x: i128, y: i128, z: i128) -> Option<i128> {
     
     /// Performs floor(r / z)
     fn div_floor(r: i128, z: i128) -> Option<i128> {
    -    if r < 0 || (r > 0 && z < 0) {
    +    if (r < 0 && z > 0) || (r > 0 && z < 0) {
             // ceiling is taken by default for a negative result
    +        // if there is any remainder, sub 1 to round floor
             let remainder = r.checked_rem_euclid(z)?;
    -        (r / z).checked_sub(if remainder > 0 { 1 } else { 0 })
    +        r.checked_div(z)?
    +            .checked_sub(if remainder > 0 { 1 } else { 0 })
         } else {
             // floor taken by default for a positive or zero result
             r.checked_div(z)
    @@ -46,13 +48,15 @@ pub(crate) fn mul_div_ceil(x: i128, y: i128, z: i128) -> Option<i128> {
     
     /// Performs ceil(r / z)
     fn div_ceil(r: i128, z: i128) -> Option<i128> {
    -    if r <= 0 || (r > 0 && z < 0) {
    +    if r == 0 || (r < 0 && z > 0) || (r > 0 && z < 0) {
             // ceiling is taken by default for a negative or zero result
             r.checked_div(z)
         } else {
             // floor taken by default for a positive result
    +        // if there is any remainder, add 1 to round ceil
             let remainder = r.checked_rem_euclid(z)?;
    -        (r / z).checked_add(if remainder > 0 { 1 } else { 0 })
    +        r.checked_div(z)?
    +            .checked_add(if remainder > 0 { 1 } else { 0 })
         }
     }
     
    @@ -112,372 +116,1116 @@ fn scaled_mul_div_ceil(x: &i128, env: &Env, y: &i128, z: &i128) -> i128 {
     
     #[cfg(test)]
     mod test_fixed_point {
    +    use core::i128;
     
    -    /********** fixed_mul_floor **********/
    -
    +    use super::{mul_div_ceil, mul_div_floor};
         use crate::FixedPoint;
     
    +    /********** mul_div_floor **********/
    +
         #[test]
    -    fn test_fixed_mul_floor_rounds_down() {
    +    fn test_mul_div_floor_rounds_down() {
    +        // Real result = 483_5313675.8
             let x: i128 = 1_5391283;
             let y: i128 = 314_1592653;
    -        let denominator: i128 = 1_0000001;
    +        let z: i128 = 1_0000001;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_exact() {
    +        // Real result = 12
    +        let x: i128 = 8;
    +        let y: i128 = 3;
    +        let z: i128 = 2;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 12);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_negative_exact() {
    +        // Real result = -12
    +        let x: i128 = 8;
    +        let y: i128 = -3;
    +        let z: i128 = 2;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
     
    -        assert_eq!(result, 483_5313675)
    +        assert_eq!(result, -12);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_negative_rounds_down() {
    +    fn test_mul_div_floor_mul_negative_rounds_down() {
    +        // Real result = -483_5313675.8
             let x: i128 = -1_5391283;
             let y: i128 = 314_1592653;
    -        let denominator: i128 = 1_0000001;
    +        let z: i128 = 1_0000001;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_div_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = -1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_x_y_negative_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let x: i128 = -1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = 1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
     
    -        assert_eq!(result, -483_5313676)
    +        assert_eq!(result, 483_5313675);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_large_number() {
    +    fn test_mul_div_floor_y_z_negative_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let x: i128 = 1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = -1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_all_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let x: i128 = -1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = -1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_mul_zero() {
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 0;
    +        let z: i128 = 1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_div_zero() {
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = 0;
    +
    +        let result = mul_div_floor(x, y, z);
    +
    +        assert_eq!(result, None);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_large_number() {
             let x: i128 = 170_141_183_460_469_231_731;
             let y: i128 = 1_000_000_000_000_000_000;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 170_141_183_460_469_231_731);
    +    }
     
    -        assert_eq!(result, 170_141_183_460_469_231_731)
    +    #[test]
    +    fn test_mul_div_floor_negative_large_number() {
    +        let x: i128 = -170_141_183_460_469_231_731;
    +        let y: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -170_141_183_460_469_231_731);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_small_number() {
    +        let x: i128 = 1;
    +        let y: i128 = 2;
    +        let z: i128 = 3;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_negative_small_number() {
    +        let x: i128 = -1;
    +        let y: i128 = 2;
    +        let z: i128 = 3;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -1);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_phantom_overflow() {
    +    fn test_mul_div_floor_phantom_overflow() {
             let x: i128 = 170_141_183_460_469_231_731;
             let y: i128 = 1_000_000_000_000_000_001;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_mul_floor(y, denominator);
    +        let result = mul_div_floor(x, y, z);
     
    -        assert_eq!(None, result);
    +        assert_eq!(result, None);
         }
     
    -    /********** fixed_mul_ceil **********/
    +    #[test]
    +    fn test_mul_div_floor_negative_phantom_overflow() {
    +        let x: i128 = -170_141_183_460_469_231_731;
    +        let y: i128 = 1_000_000_000_000_000_001;
    +        let z: i128 = 1_000_000_000_000_000_000;
    +
    +        let result = mul_div_floor(x, y, z);
    +
    +        assert_eq!(result, None);
    +    }
    +
    +    /********** mul_div_ceil **********/
     
         #[test]
    -    fn test_fixed_mul_ceil_rounds_up() {
    +    fn test_mul_div_ceil_rounds_up() {
    +        // Real result = 483_5313675.8
             let x: i128 = 1_5391283;
             let y: i128 = 314_1592653;
    -        let denominator: i128 = 1_0000001;
    +        let z: i128 = 1_0000001;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_exact() {
    +        // Real result = 12
    +        let x: i128 = 8;
    +        let y: i128 = 3;
    +        let z: i128 = 2;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 12);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_exact() {
    +        // Real result = -12
    +        let x: i128 = 8;
    +        let y: i128 = -3;
    +        let z: i128 = 2;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 483_5313676)
    +        assert_eq!(result, -12);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_negative_rounds_up() {
    +    fn test_mul_div_ceil_mul_negative_rounds_up() {
    +        // Real result = -483_5313675.8
             let x: i128 = -1_5391283;
             let y: i128 = 314_1592653;
    -        let denominator: i128 = 1_0000001;
    +        let z: i128 = 1_0000001;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_div_negative_rounds_up() {
    +        // Real result = -483_5313675.8
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = -1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_x_y_negative_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let x: i128 = -1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = 1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_y_z_negative_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let x: i128 = 1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = -1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_all_negative_rounds_up() {
    +        // Real result = -483_5313675.8
    +        let x: i128 = -1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = -1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_mul_zero() {
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 0;
    +        let z: i128 = 1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_div_zero() {
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = 0;
     
    -        assert_eq!(result, -483_5313675)
    +        let result = mul_div_ceil(x, y, z);
    +
    +        assert_eq!(result, None);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_large_number() {
    +    fn test_mul_div_ceil_large_number() {
             let x: i128 = 170_141_183_460_469_231_731;
             let y: i128 = 1_000_000_000_000_000_000;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 170_141_183_460_469_231_731);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_large_number() {
    +        let x: i128 = -170_141_183_460_469_231_731;
    +        let y: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, -170_141_183_460_469_231_731);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_small_number() {
    +        let x: i128 = 1;
    +        let y: i128 = 2;
    +        let z: i128 = 3;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 1);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_small_number() {
    +        let x: i128 = -1;
    +        let y: i128 = 2;
    +        let z: i128 = 3;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 170_141_183_460_469_231_731)
    +        assert_eq!(result, 0);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_phantom_overflow() {
    +    fn test_mul_div_ceil_phantom_overflow() {
             let x: i128 = 170_141_183_460_469_231_731;
             let y: i128 = 1_000_000_000_000_000_001;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
    +
    +        let result = mul_div_ceil(x, y, z);
    +
    +        assert_eq!(result, None);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_phantom_overflow() {
    +        let x: i128 = -170_141_183_460_469_231_731;
    +        let y: i128 = 1_000_000_000_000_000_001;
    +        let z: i128 = 1_000_000_000_000_000_000;
    +
    +        let result = mul_div_ceil(x, y, z);
    +
    +        assert_eq!(result, None);
    +    }
    +
    +    /********** fixed_mul_floor **********/
    +
    +    #[test]
    +    fn test_fixed_mul_floor() {
    +        // Real result = 104_9522835.2
    +        let x: i128 = 3_1423141;
    +        let y: i128 = 4_1234142;
    +        let denominator: i128 = 0_1234567;
    +
    +        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        assert_eq!(result, 104_9522835);
    +
    +        let result = (-x).fixed_mul_floor(y, denominator).unwrap();
    +        assert_eq!(result, -104_9522836);
    +
    +        let result = (-x).fixed_mul_floor(-y, denominator).unwrap();
    +        assert_eq!(result, 104_9522835);
    +
    +        let invalid = x.fixed_mul_floor(y, 0);
    +        assert_eq!(invalid, None);
    +    }
    +
    +    /********** fixed_mul_ceil **********/
    +
    +    #[test]
    +    fn test_fixed_mul_ceil() {
    +        // Real result = 104_9522835.2
    +        let x: i128 = 3_1423141;
    +        let y: i128 = 4_1234142;
    +        let denominator: i128 = 0_1234567;
    +
    +        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        assert_eq!(result, 104_9522836);
    +
    +        let result = (-x).fixed_mul_ceil(y, denominator).unwrap();
    +        assert_eq!(result, -104_9522835);
     
    -        let result = x.fixed_mul_ceil(y, denominator);
    +        let result = (-x).fixed_mul_ceil(-y, denominator).unwrap();
    +        assert_eq!(result, 104_9522836);
     
    -        assert_eq!(None, result);
    +        let invalid = x.fixed_mul_ceil(y, 0);
    +        assert_eq!(invalid, None);
         }
     
         /********** fixed_div_floor **********/
     
         #[test]
    -    fn test_fixed_div_floor_rounds_down() {
    +    fn test_fixed_div_floor() {
    +        // Real result = 204_1150997.8
             let x: i128 = 314_1592653;
             let y: i128 = 1_5391280;
             let denominator: i128 = 1_0000000;
     
             let result = x.fixed_div_floor(y, denominator).unwrap();
    +        assert_eq!(result, 204_1150997);
    +
    +        let result = (-x).fixed_div_floor(y, denominator).unwrap();
    +        assert_eq!(result, -204_1150998);
    +
    +        let result = (-x).fixed_div_floor(-y, denominator).unwrap();
    +        assert_eq!(result, 204_1150997);
     
    -        assert_eq!(result, 204_1150997)
    +        let invalid = x.fixed_div_floor(0, denominator);
    +        assert_eq!(invalid, None);
         }
     
    +    /********** fixed_div_ceil **********/
    +
         #[test]
    -    fn test_fixed_div_floor_negative_rounds_down() {
    +    fn test_fixed_div_ceil() {
    +        // Real result = 204_1150997.8
             let x: i128 = 314_1592653;
    -        let y: i128 = -1_5391280;
    +        let y: i128 = 1_5391280;
             let denominator: i128 = 1_0000000;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        assert_eq!(result, 204_1150998);
    +
    +        let result = (-x).fixed_div_ceil(y, denominator).unwrap();
    +        assert_eq!(result, -204_1150997);
    +
    +        let result = (-x).fixed_div_ceil(-y, denominator).unwrap();
    +        assert_eq!(result, 204_1150998);
     
    -        assert_eq!(result, -204_1150998)
    +        let invalid = x.fixed_div_ceil(0, denominator);
    +        assert_eq!(invalid, None);
         }
    +}
    +
    +#[cfg(test)]
    +mod test_soroban_fixed_point {
    +    use core::i128;
    +
    +    use super::{scaled_mul_div_ceil, scaled_mul_div_floor};
    +    use crate::SorobanFixedPoint;
    +    use soroban_sdk::Env;
    +
    +    /********** scaled_mul_div_floor **********/
     
         #[test]
    -    fn test_fixed_div_floor_large_number() {
    -        let x: i128 = 170_141_183_460_469_231_731;
    -        let y: i128 = 1_000_000_000_000_000_000;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +    fn test_scaled_mul_div_floor_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = 1_0000001;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
     
    -        assert_eq!(result, 170_141_183_460_469_231_731)
    +        assert_eq!(result, 483_5313675);
         }
     
         #[test]
    -    fn test_fixed_div_floor_phantom_overflow() {
    -        let x: i128 = 170_141_183_460_469_231_732;
    -        let y: i128 = 1_000_000_000_000_000_000;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +    fn test_scaled_mul_div_floor_exact() {
    +        // Real result = 12
    +        let env = Env::default();
    +        let x: i128 = 8;
    +        let y: i128 = 3;
    +        let z: i128 = 2;
     
    -        let result = x.fixed_div_floor(y, denominator);
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
     
    -        assert_eq!(None, result);
    +        assert_eq!(result, 12);
         }
     
    -    /********** fixed_div_ceil **********/
    +    #[test]
    +    fn test_scaled_mul_div_floor_negative_exact() {
    +        // Real result = -12
    +        let env = Env::default();
    +        let x: i128 = 8;
    +        let y: i128 = -3;
    +        let z: i128 = 2;
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, -12);
    +    }
     
         #[test]
    -    fn test_fixed_div_ceil_rounds_down() {
    -        let x: i128 = 314_1592653;
    -        let y: i128 = 1_5391280;
    -        let denominator: i128 = 1_0000000;
    +    fn test_scaled_mul_div_floor_mul_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let env = Env::default();
    +        let x: i128 = -1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = 1_0000001;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
     
    -        assert_eq!(result, 204_1150998)
    +        assert_eq!(result, -483_5313676);
         }
     
         #[test]
    -    fn test_fixed_div_ceil_negative_rounds_down() {
    -        let x: i128 = 314_1592653;
    -        let y: i128 = -1_5391280;
    -        let denominator: i128 = 1_0000000;
    +    fn test_scaled_mul_div_floor_div_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let env = Env::default();
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = -1_0000001;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, -483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_x_y_negative_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x: i128 = -1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = 1_0000001;
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
     
    -        assert_eq!(result, -204_1150997)
    +        assert_eq!(result, 483_5313675);
         }
     
         #[test]
    -    fn test_fixed_div_ceil_large_number() {
    +    fn test_scaled_mul_div_floor_y_z_negative_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x: i128 = 1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = -1_0000001;
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_all_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let env = Env::default();
    +        let x: i128 = -1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = -1_0000001;
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, -483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_mul_zero() {
    +        let env = Env::default();
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 0;
    +        let z: i128 = 1_0000001;
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_scaled_mul_div_floor_div_zero() {
    +        let env = Env::default();
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = 0;
    +
    +        scaled_mul_div_floor(&x, &env, &y, &z);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_large_number() {
    +        let env = Env::default();
             let x: i128 = 170_141_183_460_469_231_731;
             let y: i128 = 1_000_000_000_000_000_000;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
     
    -        assert_eq!(result, 170_141_183_460_469_231_731)
    +        assert_eq!(result, 170_141_183_460_469_231_731);
         }
     
         #[test]
    -    fn test_fixed_div_ceil_phantom_overflow() {
    -        let x: i128 = 170_141_183_460_469_231_732;
    +    fn test_scaled_mul_div_floor_negative_large_number() {
    +        let env = Env::default();
    +        let x: i128 = -170_141_183_460_469_231_731;
             let y: i128 = 1_000_000_000_000_000_000;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_div_ceil(y, denominator);
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
     
    -        assert_eq!(None, result);
    +        assert_eq!(result, -170_141_183_460_469_231_731);
         }
    -}
     
    -#[cfg(test)]
    -mod test_soroban_fixed_point {
    -    use crate::SorobanFixedPoint;
    -    use soroban_sdk::Env;
    +    #[test]
    +    fn test_scaled_mul_div_floor_small_number() {
    +        let env = Env::default();
    +        let x: i128 = 1;
    +        let y: i128 = 2;
    +        let z: i128 = 3;
     
    -    /********** fixed_mul_floor **********/
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_negative_small_number() {
    +        let env = Env::default();
    +        let x: i128 = -1;
    +        let y: i128 = 2;
    +        let z: i128 = 3;
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, -1)
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_phantom_overflow_uses_i256() {
    +        // i128::MAX is odd
    +        let env = Env::default();
    +        let x: i128 = (i128::MAX - 1) / 2;
    +        let y: i128 = 2 * 10i128.pow(18);
    +        let z: i128 = 10i128.pow(18);
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, i128::MAX - 1);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_negative_phantom_overflow_uses_i256() {
    +        let env = Env::default();
    +        let x: i128 = i128::MIN / 2;
    +        let y: i128 = 2 * 10i128.pow(18);
    +        let z: i128 = 10i128.pow(18);
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, i128::MIN);
    +    }
     
         #[test]
    -    fn test_fixed_mul_floor_rounds_down() {
    +    fn test_scaled_mul_div_floor_phantom_overflow_rounds_down() {
    +        // Real Result = 333_3x18.3..
    +        let env = Env::default();
    +        let x: i128 = 100 * 10i128.pow(18);
    +        let y: i128 = 10 * 10i128.pow(18);
    +        let z: i128 = 3 * 10i128.pow(18);
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 333_333_333_333_333_333_333);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_scaled_mul_div_floor_result_overflow() {
    +        let env = Env::default();
    +        let x: i128 = (i128::MAX - 1) / 2 + 1;
    +        let y: i128 = 2 * 10i128.pow(18);
    +        let z: i128 = 10i128.pow(18);
    +
    +        scaled_mul_div_floor(&x, &env, &y, &z);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_scaled_mul_div_floor_result_negative_overflow() {
    +        let env = Env::default();
    +        let x: i128 = i128::MIN / 2 - 1;
    +        let y: i128 = 2 * 10i128.pow(18);
    +        let z: i128 = 10i128.pow(18);
    +
    +        scaled_mul_div_floor(&x, &env, &y, &z);
    +    }
    +
    +    /********** scaled_mul_div_ceil **********/
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_rounds_up() {
    +        // Real result = 483_5313675.8
             let env = Env::default();
             let x: i128 = 1_5391283;
             let y: i128 = 314_1592653;
    -        let denominator: i128 = 1_0000001;
    +        let z: i128 = 1_0000001;
     
    -        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_exact() {
    +        // Real result = 12
    +        let env = Env::default();
    +        let x: i128 = 8;
    +        let y: i128 = 3;
    +        let z: i128 = 2;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 12);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_negative_exact() {
    +        // Real result = -12
    +        let env = Env::default();
    +        let x: i128 = 8;
    +        let y: i128 = -3;
    +        let z: i128 = 2;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
     
    -        assert_eq!(result, 483_5313675)
    +        assert_eq!(result, -12);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_negative_rounds_down() {
    +    fn test_scaled_mul_div_ceil_mul_negative_rounds_up() {
    +        // Real result = -483_5313675.8
             let env = Env::default();
             let x: i128 = -1_5391283;
             let y: i128 = 314_1592653;
    -        let denominator: i128 = 1_0000001;
    +        let z: i128 = 1_0000001;
     
    -        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
     
    -        assert_eq!(result, -483_5313676)
    +        assert_eq!(result, -483_5313675);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_phantom_overflow_scales() {
    +    fn test_scaled_mul_div_ceil_div_negative_rounds_up() {
    +        // Real result = -483_5313675.8
             let env = Env::default();
    -        let x: i128 = 170_141_183_460_469_231_731;
    -        let y: i128 = 10i128.pow(27);
    -        let denominator: i128 = 10i128.pow(18);
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = -1_0000001;
     
    -        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
     
    -        assert_eq!(result, 170_141_183_460_469_231_731 * 10i128.pow(9));
    +        assert_eq!(result, -483_5313675);
         }
     
    -    /********** fixed_mul_ceil **********/
    +    #[test]
    +    fn test_scaled_mul_div_ceil_x_y_negative_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x: i128 = -1_5391283;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = 1_0000001;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 483_5313676);
    +    }
     
         #[test]
    -    fn test_fixed_mul_ceil_rounds_up() {
    +    fn test_scaled_mul_div_ceil_y_z_negative_rounds_up() {
    +        // Real result = 483_5313675.8
             let env = Env::default();
             let x: i128 = 1_5391283;
    -        let y: i128 = 314_1592653;
    -        let denominator: i128 = 1_0000001;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = -1_0000001;
     
    -        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
     
    -        assert_eq!(result, 483_5313676)
    +        assert_eq!(result, 483_5313676);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_negative_rounds_up() {
    +    fn test_scaled_mul_div_ceil_all_negative_rounds_up() {
    +        // Real result = -483_5313675.8
             let env = Env::default();
             let x: i128 = -1_5391283;
    -        let y: i128 = 314_1592653;
    -        let denominator: i128 = 1_0000001;
    +        let y: i128 = -314_1592653;
    +        let z: i128 = -1_0000001;
     
    -        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, -483_5313675);
    +    }
     
    -        assert_eq!(result, -483_5313675)
    +    #[test]
    +    fn test_scaled_mul_div_ceil_mul_zero() {
    +        let env = Env::default();
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 0;
    +        let z: i128 = 1_0000001;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 0);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_large_number() {
    +    #[should_panic]
    +    fn test_scaled_mul_div_ceil_div_zero() {
    +        let env = Env::default();
    +        let x: i128 = 1_5391283;
    +        let y: i128 = 314_1592653;
    +        let z: i128 = 0;
    +
    +        scaled_mul_div_ceil(&x, &env, &y, &z);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_large_number() {
             let env = Env::default();
             let x: i128 = 170_141_183_460_469_231_731;
             let y: i128 = 1_000_000_000_000_000_000;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 170_141_183_460_469_231_731);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_negative_large_number() {
    +        let env = Env::default();
    +        let x: i128 = -170_141_183_460_469_231_731;
    +        let y: i128 = 1_000_000_000_000_000_000;
    +        let z: i128 = 1_000_000_000_000_000_000;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, -170_141_183_460_469_231_731);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_small_number() {
    +        let env = Env::default();
    +        let x: i128 = 1;
    +        let y: i128 = 2;
    +        let z: i128 = 3;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 1);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_negative_small_number() {
    +        let env = Env::default();
    +        let x: i128 = -1;
    +        let y: i128 = 2;
    +        let z: i128 = 3;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_phantom_overflow_uses_i256() {
    +        // i128::MAX is odd
    +        let env = Env::default();
    +        let x: i128 = (i128::MAX - 1) / 2;
    +        let y: i128 = 2 * 10i128.pow(18);
    +        let z: i128 = 10i128.pow(18);
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, i128::MAX - 1);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_negative_phantom_overflow_uses_i256() {
    +        let env = Env::default();
    +        let x: i128 = i128::MIN / 2;
    +        let y: i128 = 2 * 10i128.pow(18);
    +        let z: i128 = 10i128.pow(18);
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, i128::MIN);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_phantom_overflow_rounds_up() {
    +        // Real Result = 333_3x18.3..
    +        let env = Env::default();
    +        let x: i128 = 100 * 10i128.pow(18);
    +        let y: i128 = 10 * 10i128.pow(18);
    +        let z: i128 = 3 * 10i128.pow(18);
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 333_333_333_333_333_333_334);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_scaled_mul_div_ceil_result_overflow() {
    +        // i128::MAX is odd
    +        let env = Env::default();
    +        let x: i128 = (i128::MAX - 1) / 2 + 1;
    +        let y: i128 = 2 * 10i128.pow(18);
    +        let z: i128 = 10i128.pow(18);
    +
    +        scaled_mul_div_ceil(&x, &env, &y, &z);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_scaled_mul_div_ceil_result_negative_overflow() {
    +        let env = Env::default();
    +        let x: i128 = i128::MIN / 2 - 1;
    +        let y: i128 = 2 * 10i128.pow(18);
    +        let z: i128 = 10i128.pow(18);
    +
    +        scaled_mul_div_ceil(&x, &env, &y, &z);
    +    }
    +
    +    /********** fixed_mul_floor **********/
    +
    +    #[test]
    +    fn test_fixed_mul_floor() {
    +        // Real result = 104_9522835.2
    +        let env = Env::default();
    +        let x: i128 = 3_1423141;
    +        let y: i128 = 4_1234142;
    +        let denominator: i128 = 0_1234567;
    +
    +        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, 104_9522835);
    +
    +        let result = (-x).fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, -104_9522836);
    +
    +        let result = (-x).fixed_mul_floor(&env, &(-y), &denominator);
    +        assert_eq!(result, 104_9522835);
    +    }
    +
    +    #[test]
    +    fn test_fixed_mul_floor_uses_i256() {
    +        // Real result = 246800e18
    +        let env = Env::default();
    +        let x: i128 = 1234 * 10i128.pow(18);
    +        let y: i128 = 200 * 10i128.pow(18);
    +        let denominator: i128 = 10i128.pow(18);
    +
    +        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, 246800 * 10i128.pow(18));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_fixed_mul_floor_panics() {
    +        let env = Env::default();
    +        let x: i128 = 10i128.pow(7);
    +
    +        x.fixed_mul_floor(&env, &x, &0);
    +    }
    +
    +    /********** fixed_mul_ceil **********/
    +
    +    #[test]
    +    fn test_fixed_mul_ceil() {
    +        // Real result = 104_9522835.2
    +        let env = Env::default();
    +        let x: i128 = 3_1423141;
    +        let y: i128 = 4_1234142;
    +        let denominator: i128 = 0_1234567;
     
             let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        assert_eq!(result, 104_9522836);
    +
    +        let result = (-x).fixed_mul_ceil(&env, &y, &denominator);
    +        assert_eq!(result, -104_9522835);
     
    -        assert_eq!(result, 170_141_183_460_469_231_731)
    +        let result = (-x).fixed_mul_ceil(&env, &(-y), &denominator);
    +        assert_eq!(result, 104_9522836);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_phantom_overflow_scales() {
    +    fn test_fixed_mul_ceil_uses_i256() {
    +        // Real result = 246800e18
             let env = Env::default();
    -        let x: i128 = 170_141_183_460_469_231_731;
    -        let y: i128 = 10i128.pow(27);
    +        let x: i128 = 1234 * 10i128.pow(18);
    +        let y: i128 = 200 * 10i128.pow(18);
             let denominator: i128 = 10i128.pow(18);
     
             let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        assert_eq!(result, 246800 * 10i128.pow(18));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_fixed_mul_ceil_panics() {
    +        let env = Env::default();
    +        let x: i128 = 10i128.pow(7);
     
    -        assert_eq!(result, 170_141_183_460_469_231_731 * 10i128.pow(9));
    +        x.fixed_mul_ceil(&env, &x, &0);
         }
     
         /********** fixed_div_floor **********/
     
         #[test]
    -    fn test_fixed_div_floor_rounds_down() {
    +    fn test_fixed_div_floor() {
    +        // Real result = 204_1150997.8
             let env = Env::default();
             let x: i128 = 314_1592653;
             let y: i128 = 1_5391280;
             let denominator: i128 = 1_0000000;
     
             let result = x.fixed_div_floor(&env, &y, &denominator);
    +        assert_eq!(result, 204_1150997);
     
    -        assert_eq!(result, 204_1150997)
    +        let result = (-x).fixed_div_floor(&env, &y, &denominator);
    +        assert_eq!(result, -204_1150998);
    +
    +        let result = (-x).fixed_div_floor(&env, &(-y), &denominator);
    +        assert_eq!(result, 204_1150997);
         }
     
         #[test]
    -    fn test_fixed_div_floor_negative_rounds_down() {
    +    fn test_fixed_div_floor_uses_i256() {
    +        // Real result = 5000e18
             let env = Env::default();
    -        let x: i128 = 314_1592653;
    -        let y: i128 = -1_5391280;
    -        let denominator: i128 = 1_0000000;
    +        let x: i128 = 1000 * 10i128.pow(18);
    +        let y: i128 = 2 * 10i128.pow(17);
    +        let denominator: i128 = 10i128.pow(18);
     
             let result = x.fixed_div_floor(&env, &y, &denominator);
    -
    -        assert_eq!(result, -204_1150998)
    +        assert_eq!(result, 5000 * 10i128.pow(18));
         }
     
         #[test]
    -    fn test_fixed_div_floor_phantom_overflow_scales() {
    +    #[should_panic]
    +    fn test_fixed_div_floor_panics() {
             let env = Env::default();
    -        let x: i128 = 170_141_183_460_469_231_731;
    -        let y: i128 = 10i128.pow(18);
    -        let denominator: i128 = 10i128.pow(27);
    +        let x: i128 = 10i128.pow(7);
     
    -        let result = x.fixed_div_floor(&env, &y, &denominator);
    -
    -        assert_eq!(result, 170_141_183_460_469_231_731 * 10i128.pow(9));
    +        x.fixed_div_floor(&env, &0, &x);
         }
     
         /********** fixed_div_ceil **********/
     
         #[test]
    -    fn test_fixed_div_ceil_rounds_down() {
    +    fn test_fixed_div_ceil() {
    +        // Real result = 204_1150997.8
             let env = Env::default();
             let x: i128 = 314_1592653;
             let y: i128 = 1_5391280;
             let denominator: i128 = 1_0000000;
     
             let result = x.fixed_div_ceil(&env, &y, &denominator);
    +        assert_eq!(result, 204_1150998);
     
    -        assert_eq!(result, 204_1150998)
    -    }
    +        let result = (-x).fixed_div_ceil(&env, &y, &denominator);
    +        assert_eq!(result, -204_1150997);
     
    -    #[test]
    -    fn test_fixed_div_ceil_negative_rounds_down() {
    -        let env = Env::default();
    -        let x: i128 = 314_1592653;
    -        let y: i128 = -1_5391280;
    -        let denominator: i128 = 1_0000000;
    -
    -        let result = x.fixed_div_ceil(&env, &y, &denominator);
    -
    -        assert_eq!(result, -204_1150997)
    +        let result = (-x).fixed_div_ceil(&env, &(-y), &denominator);
    +        assert_eq!(result, 204_1150998);
         }
     
         #[test]
    -    fn test_fixed_div_ceil_large_number() {
    +    fn test_fixed_div_ceil_uses_i256() {
    +        // Real result = 5000e18
             let env = Env::default();
    -        let x: i128 = 170_141_183_460_469_231_731;
    -        let y: i128 = 1_000_000_000_000_000_000;
    -        let denominator: i128 = 1_000_000_000_000_000_000;
    +        let x: i128 = 1000 * 10i128.pow(18);
    +        let y: i128 = 2 * 10i128.pow(17);
    +        let denominator: i128 = 10i128.pow(18);
     
             let result = x.fixed_div_ceil(&env, &y, &denominator);
    -
    -        assert_eq!(result, 170_141_183_460_469_231_731)
    +        assert_eq!(result, 5000 * 10i128.pow(18));
         }
     
         #[test]
    -    fn test_fixed_div_ceil_phantom_overflow_scales() {
    +    #[should_panic]
    +    fn test_fixed_div_ceil_panics() {
             let env = Env::default();
    -        let x: i128 = 170_141_183_460_469_231_731;
    -        let y: i128 = 10i128.pow(18);
    -        let denominator: i128 = 10i128.pow(27);
    -
    -        let result = x.fixed_div_floor(&env, &y, &denominator);
    +        let x: i128 = 10i128.pow(7);
     
    -        assert_eq!(result, 170_141_183_460_469_231_731 * 10i128.pow(9));
    +        x.fixed_div_ceil(&env, &0, &x);
         }
     }
    
  • src/i256.rs+523 105 modified
    @@ -23,23 +23,23 @@ impl SorobanFixedPoint for I256 {
     /// Performs floor(x * y / z)
     pub(crate) fn mul_div_floor(env: &Env, x: &I256, y: &I256, z: &I256) -> I256 {
         let zero = I256::from_i32(env, 0);
    -    let r = x.mul(&y);
    -    if r < zero || (r > zero && z.clone() < zero) {
    +    let r = x.mul(y);
    +    if (r < zero && z > &zero) || (r > zero && z < &zero) {
             // ceiling is taken by default for a negative result
    -        let remainder = r.rem_euclid(&z);
    +        let remainder = r.rem_euclid(z);
             let one = I256::from_i32(env, 1);
    -        r.div(&z).sub(if remainder > zero { &one } else { &zero })
    +        r.div(z).sub(if remainder > zero { &one } else { &zero })
         } else {
             // floor taken by default for a positive or zero result
    -        r.div(&z)
    +        r.div(z)
         }
     }
     
     /// Performs ceil(x * y / z)
     pub(crate) fn mul_div_ceil(env: &Env, x: &I256, y: &I256, z: &I256) -> I256 {
         let zero = I256::from_i32(env, 0);
         let r = x.mul(&y);
    -    if r <= zero || (r > zero && z.clone() < zero) {
    +    if r == zero || (r < zero && z > &zero) || (r > zero && z < &zero) {
             // ceiling is taken by default for a negative or zero result
             r.div(&z)
         } else {
    @@ -52,209 +52,627 @@ pub(crate) fn mul_div_ceil(env: &Env, x: &I256, y: &I256, z: &I256) -> I256 {
     
     #[cfg(test)]
     mod tests {
    +    use soroban_sdk::Bytes;
    +
         use super::*;
     
    -    /********** fixed_mul_floor **********/
    +    /// Helper to create I256::MAX (0x7F followed by 31 0xFF bytes)
    +    fn i256_max(env: &Env) -> I256 {
    +        let mut bytes = [0xFFu8; 32];
    +        bytes[0] = 0x7F;
    +        I256::from_be_bytes(env, &Bytes::from_array(env, &bytes))
    +    }
    +
    +    /// Helper to create I256::MIN (0x80 followed by 31 0x00 bytes)
    +    fn i256_min(env: &Env) -> I256 {
    +        let mut bytes = [0x00u8; 32];
    +        bytes[0] = 0x80;
    +        I256::from_be_bytes(env, &Bytes::from_array(env, &bytes))
    +    }
    +
    +    /********** mul_div_floor **********/
     
         #[test]
    -    fn test_fixed_mul_floor_rounds_down() {
    +    fn test_mul_div_floor_rounds_down() {
    +        // Real result = 483_5313675.8
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, 1_5391283);
    -        let y: I256 = I256::from_i128(&env, 314_1592653);
    -        let denominator: I256 = I256::from_i128(&env, 1_0000001);
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, 314_1592653);
    +        let z = I256::from_i128(&env, 1_0000001);
     
    -        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        let result = mul_div_floor(&env, &x, &y, &z);
     
             assert_eq!(result, I256::from_i128(&env, 483_5313675));
         }
     
         #[test]
    -    fn test_fixed_mul_floor_negative_rounds_down() {
    +    fn test_mul_div_floor_exact() {
    +        // Real result = 12
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, -1_5391283);
    -        let y: I256 = I256::from_i128(&env, 314_1592653);
    -        let denominator: I256 = I256::from_i128(&env, 1_0000001);
    +        let x = I256::from_i32(&env, 8);
    +        let y = I256::from_i32(&env, 3);
    +        let z = I256::from_i32(&env, 2);
     
    -        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i32(&env, 12));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_negative_exact() {
    +        // Real result = -12
    +        let env = Env::default();
    +        let x = I256::from_i32(&env, 8);
    +        let y = I256::from_i32(&env, -3);
    +        let z = I256::from_i32(&env, 2);
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i32(&env, -12));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_mul_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, -1_5391283);
    +        let y = I256::from_i128(&env, 314_1592653);
    +        let z = I256::from_i128(&env, 1_0000001);
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
     
             assert_eq!(result, I256::from_i128(&env, -483_5313676));
         }
     
         #[test]
    -    fn test_fixed_mul_floor_large_number() {
    +    fn test_mul_div_floor_div_negative_rounds_down() {
    +        // Real result = -483_5313675.8
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, i128::MAX);
    -        let y: I256 = I256::from_i128(&env, 10i128.pow(38));
    -        let denominator: I256 = I256::from_i128(&env, 10i128.pow(18));
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, 314_1592653);
    +        let z = I256::from_i128(&env, -1_0000001);
     
    -        let result = x.clone().fixed_mul_floor(&env, &y, &denominator);
    +        let result = mul_div_floor(&env, &x, &y, &z);
     
    -        let expected_result = x.mul(&I256::from_i128(&env, 10i128.pow(20)));
    -        assert_eq!(result, expected_result);
    +        assert_eq!(result, I256::from_i128(&env, -483_5313676));
         }
     
         #[test]
    -    #[should_panic(expected = "attempt to multiply with overflow")]
    -    fn test_fixed_mul_floor_phantom_overflow() {
    +    fn test_mul_div_floor_x_y_negative_rounds_down() {
    +        // Real result = 483_5313675.8
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, i128::MAX);
    -        // 256 bit max ~= 5.8e76, 128 bit max ~= 1.7e38, need to multiply by at least 10^39
    -        let y: I256 = I256::from_i128(&env, 10i128.pow(39));
    -        let denominator: I256 = I256::from_i128(&env, 10i128.pow(18));
    +        let x = I256::from_i128(&env, -1_5391283);
    +        let y = I256::from_i128(&env, -314_1592653);
    +        let z = I256::from_i128(&env, 1_0000001);
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
     
    -        x.fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, 483_5313675));
         }
     
    -    /********** fixed_mul_ceil **********/
    +    #[test]
    +    fn test_mul_div_floor_y_z_negative_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, -314_1592653);
    +        let z = I256::from_i128(&env, -1_0000001);
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i128(&env, 483_5313675));
    +    }
     
         #[test]
    -    fn test_fixed_mul_ceil_rounds_up() {
    +    fn test_mul_div_floor_all_negative_rounds_down() {
    +        // Real result = -483_5313675.8
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, 1_5391283);
    -        let y: I256 = I256::from_i128(&env, 314_1592653);
    -        let denominator: I256 = I256::from_i128(&env, 1_0000001);
    +        let x = I256::from_i128(&env, -1_5391283);
    +        let y = I256::from_i128(&env, -314_1592653);
    +        let z = I256::from_i128(&env, -1_0000001);
     
    -        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i128(&env, -483_5313676));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_mul_zero() {
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, 0);
    +        let z = I256::from_i128(&env, 1_0000001);
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i128(&env, 0));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_floor_div_zero() {
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, 314_1592653);
    +        let z = I256::from_i128(&env, 0);
    +
    +        mul_div_floor(&env, &x, &y, &z);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_large_number() {
    +        let env = Env::default();
    +        let x = i256_max(&env).div(&I256::from_i128(&env, 10i128.pow(36)));
    +        let y = I256::from_i128(&env, 10i128.pow(36));
    +        let z = I256::from_i128(&env, 10i128.pow(36));
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, x);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_negative_large_number() {
    +        let env = Env::default();
    +        let x = i256_min(&env).div(&I256::from_i128(&env, 10i128.pow(36)));
    +        let y = I256::from_i128(&env, 10i128.pow(36));
    +        let z = I256::from_i128(&env, 10i128.pow(36));
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, x);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_small_number() {
    +        let env = Env::default();
    +        let x = I256::from_i32(&env, 1);
    +        let y = I256::from_i32(&env, 2);
    +        let z = I256::from_i32(&env, 3);
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i32(&env, 0));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_negative_small_number() {
    +        let env = Env::default();
    +        let x = I256::from_i32(&env, -1);
    +        let y = I256::from_i32(&env, 2);
    +        let z = I256::from_i32(&env, 3);
    +
    +        let result = mul_div_floor(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i32(&env, -1));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_floor_overflow() {
    +        let env = Env::default();
    +        let x = i256_max(&env)
    +            .div(&I256::from_i128(&env, 10i128.pow(36)))
    +            .add(&I256::from_i32(&env, 1));
    +        let y = I256::from_i128(&env, 10i128.pow(36));
    +        let z = I256::from_i128(&env, 10i128.pow(36));
    +
    +        mul_div_floor(&env, &x, &y, &z);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_floor_negative_overflow() {
    +        let env = Env::default();
    +        let x = i256_min(&env)
    +            .div(&I256::from_i128(&env, 10i128.pow(36)))
    +            .add(&I256::from_i32(&env, -1));
    +        let y = I256::from_i128(&env, 10i128.pow(36));
    +        let z = I256::from_i128(&env, 10i128.pow(36));
    +
    +        mul_div_floor(&env, &x, &y, &z);
    +    }
    +
    +    /********** mul_div_ceil **********/
    +
    +    #[test]
    +    fn test_mul_div_ceil_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, 314_1592653);
    +        let z = I256::from_i128(&env, 1_0000001);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
     
             assert_eq!(result, I256::from_i128(&env, 483_5313676));
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_negative_rounds_up() {
    +    fn test_mul_div_ceil_exact() {
    +        // Real result = 12
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, -1_5391283);
    -        let y: I256 = I256::from_i128(&env, 314_1592653);
    -        let denominator: I256 = I256::from_i128(&env, 1_0000001);
    +        let x = I256::from_i32(&env, 8);
    +        let y = I256::from_i32(&env, 3);
    +        let z = I256::from_i32(&env, 2);
     
    -        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i32(&env, 12));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_exact() {
    +        // Real result = -12
    +        let env = Env::default();
    +        let x = I256::from_i32(&env, 8);
    +        let y = I256::from_i32(&env, -3);
    +        let z = I256::from_i32(&env, 2);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i32(&env, -12));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_mul_negative_rounds_up() {
    +        // Real result = -483_5313675.8
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, -1_5391283);
    +        let y = I256::from_i128(&env, 314_1592653);
    +        let z = I256::from_i128(&env, 1_0000001);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
     
             assert_eq!(result, I256::from_i128(&env, -483_5313675));
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_large_number() {
    +    fn test_mul_div_ceil_div_negative_rounds_up() {
    +        // Real result = -483_5313675.8
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, i128::MAX);
    -        let y: I256 = I256::from_i128(&env, 10i128.pow(38));
    -        let denominator: I256 = I256::from_i128(&env, 10i128.pow(18));
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, 314_1592653);
    +        let z = I256::from_i128(&env, -1_0000001);
     
    -        let result = x.clone().fixed_mul_ceil(&env, &y, &denominator);
    +        let result = mul_div_ceil(&env, &x, &y, &z);
     
    -        let expected_result = x.mul(&I256::from_i128(&env, 10i128.pow(20)));
    -        assert_eq!(result, expected_result);
    +        assert_eq!(result, I256::from_i128(&env, -483_5313675));
         }
     
         #[test]
    -    #[should_panic(expected = "attempt to multiply with overflow")]
    -    fn test_fixed_mul_ceil_phantom_overflow() {
    +    fn test_mul_div_ceil_x_y_negative_rounds_up() {
    +        // Real result = 483_5313675.8
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, i128::MAX);
    -        // 256 bit max ~= 5.8e76, 128 bit max ~= 1.7e38, need to multiply by at least 10^39
    -        let y: I256 = I256::from_i128(&env, 10i128.pow(39));
    -        let denominator: I256 = I256::from_i128(&env, 10i128.pow(18));
    +        let x = I256::from_i128(&env, -1_5391283);
    +        let y = I256::from_i128(&env, -314_1592653);
    +        let z = I256::from_i128(&env, 1_0000001);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
     
    -        x.fixed_mul_ceil(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, 483_5313676));
         }
     
    -    /********** fixed_div_floor **********/
    +    #[test]
    +    fn test_mul_div_ceil_y_z_negative_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, -314_1592653);
    +        let z = I256::from_i128(&env, -1_0000001);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i128(&env, 483_5313676));
    +    }
     
         #[test]
    -    fn test_fixed_div_floor_rounds_down() {
    +    fn test_mul_div_ceil_all_negative_rounds_up() {
    +        // Real result = -483_5313675.8
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, 314_1592653);
    -        let y: I256 = I256::from_i128(&env, 1_5391280);
    -        let denominator: I256 = I256::from_i128(&env, 1_0000000);
    +        let x = I256::from_i128(&env, -1_5391283);
    +        let y = I256::from_i128(&env, -314_1592653);
    +        let z = I256::from_i128(&env, -1_0000001);
     
    -        let result = x.fixed_div_floor(&env, &y, &denominator);
    +        let result = mul_div_ceil(&env, &x, &y, &z);
     
    -        assert_eq!(result, I256::from_i128(&env, 204_1150997));
    +        assert_eq!(result, I256::from_i128(&env, -483_5313675));
         }
     
         #[test]
    -    fn test_fixed_div_floor_negative_rounds_down() {
    +    fn test_mul_div_ceil_mul_zero() {
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, 314_1592653);
    -        let y: I256 = I256::from_i128(&env, -1_5391280);
    -        let denominator: I256 = I256::from_i128(&env, 1_0000000);
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i32(&env, 0);
    +        let z = I256::from_i128(&env, 1_0000001);
     
    -        let result = x.fixed_div_floor(&env, &y, &denominator);
    +        let result = mul_div_ceil(&env, &x, &y, &z);
     
    -        assert_eq!(result, I256::from_i128(&env, -204_1150998));
    +        assert_eq!(result, I256::from_i32(&env, 0));
         }
     
         #[test]
    -    fn test_fixed_div_floor_large_number() {
    +    #[should_panic]
    +    fn test_mul_div_ceil_div_zero() {
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 1_5391283);
    +        let y = I256::from_i128(&env, 314_1592653);
    +        let z = I256::from_i32(&env, 0);
    +
    +        mul_div_ceil(&env, &x, &y, &z);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_large_number() {
    +        let env = Env::default();
    +        let x = i256_max(&env).div(&I256::from_i128(&env, 10i128.pow(36)));
    +        let y = I256::from_i128(&env, 10i128.pow(36));
    +        let z = I256::from_i128(&env, 10i128.pow(36));
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, x);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_large_number() {
    +        let env = Env::default();
    +        let x = i256_min(&env).div(&I256::from_i128(&env, 10i128.pow(36)));
    +        let y = I256::from_i128(&env, 10i128.pow(36));
    +        let z = I256::from_i128(&env, 10i128.pow(36));
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, x);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_small_number() {
    +        let env = Env::default();
    +        let x = I256::from_i32(&env, 1);
    +        let y = I256::from_i32(&env, 2);
    +        let z = I256::from_i32(&env, 3);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i32(&env, 1));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_small_number() {
    +        let env = Env::default();
    +        let x = I256::from_i32(&env, -1);
    +        let y = I256::from_i32(&env, 2);
    +        let z = I256::from_i32(&env, 3);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, I256::from_i32(&env, 0));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_ceil_overflow() {
    +        let env = Env::default();
    +        let x = i256_max(&env)
    +            .div(&I256::from_i128(&env, 10i128.pow(36)))
    +            .add(&I256::from_i32(&env, 1));
    +        let y = I256::from_i128(&env, 10i128.pow(36));
    +        let z = I256::from_i128(&env, 10i128.pow(36));
    +
    +        mul_div_ceil(&env, &x, &y, &z);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_ceil_negative_overflow() {
    +        let env = Env::default();
    +        let x = i256_min(&env)
    +            .div(&I256::from_i128(&env, 10i128.pow(36)))
    +            .add(&I256::from_i32(&env, -1));
    +        let y = I256::from_i128(&env, 10i128.pow(36));
    +        let z = I256::from_i128(&env, 10i128.pow(36));
    +
    +        mul_div_ceil(&env, &x, &y, &z);
    +    }
    +
    +    /********** fixed_mul_floor **********/
    +
    +    #[test]
    +    fn test_fixed_mul_floor() {
    +        // Real result = 104_9522835.2
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 3_1423141);
    +        let y = I256::from_i128(&env, 4_1234142);
    +        let denominator = I256::from_i128(&env, 0_1234567);
    +
    +        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, 104_9522835));
    +
    +        let result = x
    +            .mul(&I256::from_i32(&env, -1))
    +            .fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, -104_9522836));
    +
    +        let result = x.mul(&I256::from_i32(&env, -1)).fixed_mul_floor(
    +            &env,
    +            &y.mul(&I256::from_i32(&env, -1)),
    +            &denominator,
    +        );
    +        assert_eq!(result, I256::from_i128(&env, 104_9522835));
    +    }
    +
    +    #[test]
    +    fn test_fixed_mul_floor_large_number() {
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, i128::MAX);
    -        let y: I256 = I256::from_i128(&env, 10i128.pow(27));
    -        let denominator: I256 = I256::from_i128(&env, 10i128.pow(38));
    +        let x = I256::from_i128(&env, i128::MAX);
    +        let y = I256::from_i128(&env, 10i128.pow(38));
    +        let denominator = I256::from_i128(&env, 10i128.pow(18));
     
    -        let result = x.clone().fixed_div_floor(&env, &y, &denominator);
    +        let result = x.fixed_mul_floor(&env, &y, &denominator);
     
    -        let expected_result = x.mul(&I256::from_i128(&env, 10i128.pow(11)));
    +        let expected_result =
    +            I256::from_i128(&env, i128::MAX).mul(&I256::from_i128(&env, 10i128.pow(20)));
             assert_eq!(result, expected_result);
         }
     
         #[test]
    -    #[should_panic(expected = "attempt to multiply with overflow")]
    -    fn test_fixed_div_floor_phantom_overflow() {
    +    #[should_panic]
    +    fn test_fixed_mul_floor_panics() {
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, i128::MAX);
    -        let y: I256 = I256::from_i128(&env, 10i128.pow(27));
    -        // 256 bit max ~= 5.8e76, 128 bit max ~= 1.7e38, need to multiply by at least 10^39
    -        let denominator: I256 = I256::from_i128(&env, 10i128.pow(39));
    +        let x = I256::from_i128(&env, 10i128.pow(7));
    +        let zero = I256::from_i32(&env, 0);
     
    -        x.fixed_div_floor(&env, &y, &denominator);
    +        x.fixed_mul_floor(&env, &x, &zero);
         }
     
    -    /********** fixed_div_ceil **********/
    +    /********** fixed_mul_ceil **********/
     
         #[test]
    -    fn test_fixed_div_ceil_rounds_down() {
    +    fn test_fixed_mul_ceil() {
    +        // Real result = 104_9522835.2
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, 314_1592653);
    -        let y: I256 = I256::from_i128(&env, 1_5391280);
    -        let denominator: I256 = I256::from_i128(&env, 1_0000000);
    +        let x = I256::from_i128(&env, 3_1423141);
    +        let y = I256::from_i128(&env, 4_1234142);
    +        let denominator = I256::from_i128(&env, 0_1234567);
     
    -        let result = x.fixed_div_ceil(&env, &y, &denominator);
    +        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, 104_9522836));
    +
    +        let result = x
    +            .mul(&I256::from_i32(&env, -1))
    +            .fixed_mul_ceil(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, -104_9522835));
    +
    +        let result = x.mul(&I256::from_i32(&env, -1)).fixed_mul_ceil(
    +            &env,
    +            &y.mul(&I256::from_i32(&env, -1)),
    +            &denominator,
    +        );
    +        assert_eq!(result, I256::from_i128(&env, 104_9522836));
    +    }
     
    -        assert_eq!(result, I256::from_i128(&env, 204_1150998));
    +    #[test]
    +    fn test_fixed_mul_ceil_large_number() {
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, i128::MAX);
    +        let y = I256::from_i128(&env, 10i128.pow(38));
    +        let denominator = I256::from_i128(&env, 10i128.pow(18));
    +
    +        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +
    +        let expected_result =
    +            I256::from_i128(&env, i128::MAX).mul(&I256::from_i128(&env, 10i128.pow(20)));
    +        assert_eq!(result, expected_result);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_fixed_mul_ceil_panics() {
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 10i128.pow(7));
    +        let zero = I256::from_i32(&env, 0);
    +
    +        x.fixed_mul_ceil(&env, &x, &zero);
         }
     
    +    /********** fixed_div_floor **********/
    +
         #[test]
    -    fn test_fixed_div_ceil_negative_rounds_down() {
    +    fn test_fixed_div_floor() {
    +        // Real result = 204_1150997.8
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, 314_1592653);
    -        let y: I256 = I256::from_i128(&env, -1_5391280);
    -        let denominator: I256 = I256::from_i128(&env, 1_0000000);
    +        let x = I256::from_i128(&env, 314_1592653);
    +        let y = I256::from_i128(&env, 1_5391280);
    +        let denominator = I256::from_i128(&env, 1_0000000);
    +
    +        let result = x.fixed_div_floor(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, 204_1150997));
    +
    +        let result = x
    +            .mul(&I256::from_i32(&env, -1))
    +            .fixed_div_floor(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, -204_1150998));
    +
    +        let result = x.mul(&I256::from_i32(&env, -1)).fixed_div_floor(
    +            &env,
    +            &y.mul(&I256::from_i32(&env, -1)),
    +            &denominator,
    +        );
    +        assert_eq!(result, I256::from_i128(&env, 204_1150997));
    +    }
    +
    +    #[test]
    +    fn test_fixed_div_floor_large_number() {
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, i128::MAX);
    +        let y = I256::from_i128(&env, 10i128.pow(27));
    +        let denominator = I256::from_i128(&env, 10i128.pow(38));
    +
    +        let result = x.fixed_div_floor(&env, &y, &denominator);
    +
    +        let expected_result =
    +            I256::from_i128(&env, i128::MAX).mul(&I256::from_i128(&env, 10i128.pow(11)));
    +        assert_eq!(result, expected_result);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_fixed_div_floor_panics() {
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 10i128.pow(7));
    +        let zero = I256::from_i32(&env, 0);
    +
    +        x.fixed_div_floor(&env, &zero, &x);
    +    }
    +
    +    /********** fixed_div_ceil **********/
    +
    +    #[test]
    +    fn test_fixed_div_ceil() {
    +        // Real result = 204_1150997.8
    +        let env = Env::default();
    +        let x = I256::from_i128(&env, 314_1592653);
    +        let y = I256::from_i128(&env, 1_5391280);
    +        let denominator = I256::from_i128(&env, 1_0000000);
     
             let result = x.fixed_div_ceil(&env, &y, &denominator);
    +        assert_eq!(result, I256::from_i128(&env, 204_1150998));
     
    +        let result = x
    +            .mul(&I256::from_i32(&env, -1))
    +            .fixed_div_ceil(&env, &y, &denominator);
             assert_eq!(result, I256::from_i128(&env, -204_1150997));
    +
    +        let result = x.mul(&I256::from_i32(&env, -1)).fixed_div_ceil(
    +            &env,
    +            &y.mul(&I256::from_i32(&env, -1)),
    +            &denominator,
    +        );
    +        assert_eq!(result, I256::from_i128(&env, 204_1150998));
         }
     
         #[test]
         fn test_fixed_div_ceil_large_number() {
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, i128::MAX);
    -        let y: I256 = I256::from_i128(&env, 10i128.pow(27));
    -        let denominator: I256 = I256::from_i128(&env, 10i128.pow(38));
    +        let x = I256::from_i128(&env, i128::MAX);
    +        let y = I256::from_i128(&env, 10i128.pow(27));
    +        let denominator = I256::from_i128(&env, 10i128.pow(38));
     
    -        let result = x.clone().fixed_div_ceil(&env, &y, &denominator);
    +        let result = x.fixed_div_ceil(&env, &y, &denominator);
     
    -        let expected_result = x.mul(&I256::from_i128(&env, 10i128.pow(11)));
    +        let expected_result =
    +            I256::from_i128(&env, i128::MAX).mul(&I256::from_i128(&env, 10i128.pow(11)));
             assert_eq!(result, expected_result);
         }
     
         #[test]
    -    #[should_panic(expected = "attempt to multiply with overflow")]
    -    fn test_fixed_div_ceil_phantom_overflow() {
    +    #[should_panic]
    +    fn test_fixed_div_ceil_panics() {
             let env = Env::default();
    -        let x: I256 = I256::from_i128(&env, i128::MAX);
    -        let y: I256 = I256::from_i128(&env, 10i128.pow(27));
    -        // 256 bit max ~= 5.8e76, 128 bit max ~= 1.7e38, need to multiply by at least 10^39
    -        let denominator: I256 = I256::from_i128(&env, 10i128.pow(39));
    +        let x = I256::from_i128(&env, 10i128.pow(7));
    +        let zero = I256::from_i32(&env, 0);
     
    -        x.fixed_div_ceil(&env, &y, &denominator);
    +        x.fixed_div_ceil(&env, &zero, &x);
         }
     }
    
  • src/i64.rs+451 95 modified
    @@ -22,21 +22,24 @@ impl FixedPoint for i64 {
     fn mul_div_floor(x: i64, y: i64, z: i64) -> Option<i64> {
         return match x.checked_mul(y) {
             Some(r) => {
    -            if r < 0 || (r > 0 && z < 0) {
    +            if (r < 0 && z > 0) || (r > 0 && z < 0) {
                     // ceiling is taken by default for a negative result
    +                // if there is any remainder, sub 1 to round floor
                     let remainder = r.checked_rem_euclid(z)?;
    -                (r / z).checked_sub(if remainder > 0 { 1 } else { 0 })
    +                r.checked_div(z)?
    +                    .checked_sub(if remainder > 0 { 1 } else { 0 })
                 } else {
                     // floor taken by default for a positive or zero result
                     r.checked_div(z)
                 }
             }
             None => {
                 let res_i128 = crate::i128::mul_div_floor(x as i128, y as i128, z as i128)?;
    -            if res_i128 > i64::MAX as i128 {
    -                return None;
    +            if let Ok(res_i64) = i64::try_from(res_i128) {
    +                Some(res_i64)
    +            } else {
    +                None
                 }
    -            Some(res_i128 as i64)
             }
         };
     }
    @@ -45,210 +48,563 @@ fn mul_div_floor(x: i64, y: i64, z: i64) -> Option<i64> {
     fn mul_div_ceil(x: i64, y: i64, z: i64) -> Option<i64> {
         return match x.checked_mul(y) {
             Some(r) => {
    -            if r <= 0 || (r > 0 && z < 0) {
    +            if r == 0 || (r < 0 && z > 0) || (r > 0 && z < 0) {
                     // ceiling is taken by default for a negative or zero result
                     r.checked_div(z)
                 } else {
                     // floor taken by default for a positive result
    +                // if there is any remainder, add 1 to round ceiling
                     let remainder = r.checked_rem_euclid(z)?;
    -                (r / z).checked_add(if remainder > 0 { 1 } else { 0 })
    +                r.checked_div(z)?
    +                    .checked_add(if remainder > 0 { 1 } else { 0 })
                 }
             }
             None => {
                 let res_i128 = crate::i128::mul_div_ceil(x as i128, y as i128, z as i128)?;
    -            if res_i128 > i64::MAX as i128 {
    -                return None;
    +            if let Ok(res_i64) = i64::try_from(res_i128) {
    +                Some(res_i64)
    +            } else {
    +                None
                 }
    -            Some(res_i128 as i64)
             }
         };
     }
     
     #[cfg(test)]
     mod tests {
    +    use core::i64;
    +
         use super::*;
     
    -    /********** fixed_mul_floor **********/
    +    /********** mul_div_floor **********/
     
         #[test]
    -    fn test_fixed_mul_floor_rounds_down() {
    +    fn test_mul_div_floor_rounds_down() {
    +        // Real result = 483_5313675.8
             let x: i64 = 1_5391283;
             let y: i64 = 314_1592653;
    -        let denominator: i64 = 1_0000001;
    +        let z: i64 = 1_0000001;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
     
    -        assert_eq!(result, 483_5313675)
    +        assert_eq!(result, 483_5313675);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_large_number() {
    -        let x: i64 = 9_223_372_036;
    -        let y: i64 = 1_000_000_000;
    -        let denominator: i64 = 1_000_000_000;
    +    fn test_mul_div_floor_exact() {
    +        // Real result = 12
    +        let x: i64 = 8;
    +        let y: i64 = 3;
    +        let z: i64 = 2;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
     
    -        assert_eq!(result, 9_223_372_036)
    +        assert_eq!(result, 12);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_phantom_overflow_uses_i128() {
    -        let x: i64 = 9_223_372_036;
    -        let y: i64 = 2_000_000_000;
    -        let denominator: i64 = 1_000_000_000;
    +    fn test_mul_div_floor_negative_exact() {
    +        // Real result = -12
    +        let x: i64 = 8;
    +        let y: i64 = -3;
    +        let z: i64 = 2;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
     
    -        assert_eq!(result, 18_446_744_072);
    +        assert_eq!(result, -12);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_result_overflow() {
    -        let x: i64 = 9_223_372_036_000_000_000;
    -        let y: i64 = 2_000_000_000;
    -        let denominator: i64 = 1_000_000_000;
    +    fn test_mul_div_floor_mul_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let x: i64 = -1_5391283;
    +        let y: i64 = 314_1592653;
    +        let z: i64 = 1_0000001;
     
    -        let result = x.fixed_mul_floor(y, denominator);
    +        let result = mul_div_floor(x, y, z).unwrap();
     
    -        assert_eq!(result, None);
    +        assert_eq!(result, -483_5313676);
         }
     
    -    /********** fixed_mul_ceil **********/
    +    #[test]
    +    fn test_mul_div_floor_div_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let x: i64 = 1_5391283;
    +        let y: i64 = 314_1592653;
    +        let z: i64 = -1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_x_y_negative_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let x: i64 = -1_5391283;
    +        let y: i64 = -314_1592653;
    +        let z: i64 = 1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_y_z_negative_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let x: i64 = 1_5391283;
    +        let y: i64 = -314_1592653;
    +        let z: i64 = -1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_all_negative_rounds_down() {
    +        // Real result = -483_5313675.8
    +        let x: i64 = -1_5391283;
    +        let y: i64 = -314_1592653;
    +        let z: i64 = -1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_mul_zero() {
    +        let x: i64 = 1_5391283;
    +        let y: i64 = 0;
    +        let z: i64 = 1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
     
         #[test]
    -    fn test_fixed_mul_ceil_rounds_up() {
    +    fn test_mul_div_floor_div_zero() {
             let x: i64 = 1_5391283;
             let y: i64 = 314_1592653;
    -        let denominator: i64 = 1_0000001;
    +        let z: i64 = 0;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z);
     
    -        assert_eq!(result, 483_5313676)
    +        assert_eq!(result, None);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_large_number() {
    +    fn test_mul_div_floor_large_number() {
             let x: i64 = 9_223_372_036;
             let y: i64 = 1_000_000_000;
    -        let denominator: i64 = 1_000_000_000;
    +        let z: i64 = 1_000_000_000;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
     
             assert_eq!(result, 9_223_372_036)
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_phantom_overflow_uses_i128() {
    -        let x: i64 = 9_223_372_036;
    +    fn test_mul_div_floor_negative_large_number() {
    +        let x: i64 = -9_223_372_036;
    +        let y: i64 = 1_000_000_000;
    +        let z: i64 = 1_000_000_000;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -9_223_372_036)
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_small_number() {
    +        let x: i64 = 1;
    +        let y: i64 = 2;
    +        let z: i64 = 3;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0)
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_negative_small_number() {
    +        let x: i64 = -1;
    +        let y: i64 = 2;
    +        let z: i64 = 3;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, -1)
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_phantom_overflow_uses_i128() {
    +        // i64::MAX is odd
    +        let x: i64 = (i64::MAX - 1) / 2;
             let y: i64 = 2_000_000_000;
    -        let denominator: i64 = 1_000_000_000;
    +        let z: i64 = 1_000_000_000;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, i64::MAX - 1);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_negative_phantom_overflow_uses_i128() {
    +        let x: i64 = i64::MIN / 2;
    +        let y: i64 = 2_000_000_000;
    +        let z: i64 = 1_000_000_000;
     
    -        assert_eq!(result, 18446744072);
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, i64::MIN);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_result_overflow() {
    -        let x: i64 = 9_223_372_036_000_000_000;
    +    fn test_mul_div_floor_phantom_overflow_rounds_down() {
    +        // Real Result = 333_333_333_333.3..
    +        let x: i64 = 100 * 10i64.pow(9);
    +        let y: i64 = 10 * 10i64.pow(9);
    +        let z: i64 = 3 * 10i64.pow(9);
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 333_333_333_333);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_result_overflow() {
    +        // i64::MAX is odd
    +        let x: i64 = (i64::MAX - 1) / 2 + 1;
             let y: i64 = 2_000_000_000;
    -        let denominator: i64 = 1_000_000_000;
    +        let z: i64 = 1_000_000_000;
     
    -        let result = x.fixed_mul_ceil(y, denominator);
    +        let result = mul_div_floor(x, y, z);
     
             assert_eq!(result, None);
         }
     
    -    /********** fixed_div_floor **********/
    +    #[test]
    +    fn test_mul_div_floor_result_negative_overflow() {
    +        let x: i64 = i64::MIN / 2 - 1;
    +        let y: i64 = 2_000_000_000;
    +        let z: i64 = 1_000_000_000;
    +
    +        let result = mul_div_floor(x, y, z);
    +
    +        assert_eq!(result, None);
    +    }
    +
    +    /********** mul_div_ceil **********/
     
         #[test]
    -    fn test_fixed_div_floor_rounds_down() {
    -        let x: i64 = 314_1592653;
    -        let y: i64 = 1_5391280;
    -        let denominator: i64 = 1_0000000;
    +    fn test_mul_div_ceil_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let x: i64 = 1_5391283;
    +        let y: i64 = 314_1592653;
    +        let z: i64 = 1_0000001;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 204_1150997)
    +        assert_eq!(result, 483_5313676);
         }
     
         #[test]
    -    fn test_fixed_div_floor_large_number() {
    -        let x: i64 = 9_223_372_036;
    -        let y: i64 = 1_000_000_000;
    -        let denominator: i64 = 1_000_000_000;
    +    fn test_mul_div_ceil_exact() {
    +        // Real result = 12
    +        let x: i64 = 8;
    +        let y: i64 = 3;
    +        let z: i64 = 2;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 9_223_372_036)
    +        assert_eq!(result, 12);
         }
     
         #[test]
    -    fn test_fixed_div_floor_phantom_overflow_uses_i128() {
    -        let x: i64 = 9_223_372_036;
    -        let y: i64 = 1_000_000_000;
    -        let denominator: i64 = 2_000_000_000;
    +    fn test_mul_div_ceil_negative_exact() {
    +        // Real result = -12
    +        let x: i64 = 8;
    +        let y: i64 = -3;
    +        let z: i64 = 2;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 18_446_744_072);
    +        assert_eq!(result, -12);
         }
     
         #[test]
    -    fn test_fixed_div_floor_result_overflow() {
    -        let x: i64 = 9_223_372_036_000_000_000;
    -        let y: i64 = 1_000_000_000;
    -        let denominator: i64 = 2_000_000_000;
    +    fn test_mul_div_ceil_mul_negative_rounds_up() {
    +        // Real result = -483_5313675.8
    +        let x: i64 = -1_5391283;
    +        let y: i64 = 314_1592653;
    +        let z: i64 = 1_0000001;
     
    -        let result = x.fixed_div_floor(y, denominator);
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, None);
    +        assert_eq!(result, -483_5313675);
         }
     
    -    /********** fixed_div_ceil **********/
    +    #[test]
    +    fn test_mul_div_ceil_div_negative_rounds_up() {
    +        // Real result = -483_5313675.8
    +        let x: i64 = 1_5391283;
    +        let y: i64 = 314_1592653;
    +        let z: i64 = -1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313675);
    +    }
     
         #[test]
    -    fn test_fixed_div_ceil_rounds_up() {
    -        let x: i64 = 314_1592653;
    -        let y: i64 = 1_5391280;
    -        let denominator: i64 = 1_0000000;
    +    fn test_mul_div_ceil_x_y_negative_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let x: i64 = -1_5391283;
    +        let y: i64 = -314_1592653;
    +        let z: i64 = 1_0000001;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 204_1150998)
    +        assert_eq!(result, 483_5313676);
         }
     
         #[test]
    -    fn test_fixed_div_ceil_large_number() {
    +    fn test_mul_div_ceil_y_z_negative_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let x: i64 = 1_5391283;
    +        let y: i64 = -314_1592653;
    +        let z: i64 = -1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_all_negative_rounds_up() {
    +        // Real result = -483_5313675.8
    +        let x: i64 = -1_5391283;
    +        let y: i64 = -314_1592653;
    +        let z: i64 = -1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, -483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_mul_zero() {
    +        let x: i64 = 1_5391283;
    +        let y: i64 = 0;
    +        let z: i64 = 1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_div_zero() {
    +        let x: i64 = 1_5391283;
    +        let y: i64 = 314_1592653;
    +        let z: i64 = 0;
    +
    +        let result = mul_div_ceil(x, y, z);
    +
    +        assert_eq!(result, None);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_large_number() {
             let x: i64 = 9_223_372_036;
             let y: i64 = 1_000_000_000;
    -        let denominator: i64 = 1_000_000_000;
    +        let z: i64 = 1_000_000_000;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
             assert_eq!(result, 9_223_372_036)
         }
     
         #[test]
    -    fn test_fixed_div_ceil_phantom_overflow_uses_i128() {
    -        let x: i64 = 9_223_372_036;
    +    fn test_mul_div_ceil_negative_large_number() {
    +        let x: i64 = -9_223_372_036;
             let y: i64 = 1_000_000_000;
    -        let denominator: i64 = 2_000_000_000;
    +        let z: i64 = 1_000_000_000;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 18_446_744_072);
    +        assert_eq!(result, -9_223_372_036)
         }
     
         #[test]
    -    fn test_fixed_div_ceil_result_overflow() {
    -        let x: i64 = 9_223_372_036_000_000_000;
    -        let y: i64 = 1_000_000_000;
    -        let denominator: i64 = 2_000_000_000;
    +    fn test_mul_div_ceil_small_number() {
    +        let x: i64 = 1;
    +        let y: i64 = 2;
    +        let z: i64 = 3;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 1)
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_small_number() {
    +        let x: i64 = -1;
    +        let y: i64 = 2;
    +        let z: i64 = 3;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0)
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_phantom_overflow_uses_i128() {
    +        // i64::MAX is odd
    +        let x: i64 = (i64::MAX - 1) / 2;
    +        let y: i64 = 2_000_000_000;
    +        let z: i64 = 1_000_000_000;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, i64::MAX - 1);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_negative_phantom_overflow_uses_i128() {
    +        let x: i64 = i64::MIN / 2;
    +        let y: i64 = 2_000_000_000;
    +        let z: i64 = 1_000_000_000;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, i64::MIN);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_phantom_overflow_rounds_up() {
    +        // Real Result = 333_333_333_333.3..
    +        let x: i64 = 100 * 10i64.pow(9);
    +        let y: i64 = 10 * 10i64.pow(9);
    +        let z: i64 = 3 * 10i64.pow(9);
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 333_333_333_334);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_result_overflow() {
    +        // i64::MAX is odd
    +        let x: i64 = (i64::MAX - 1) / 2 + 1;
    +        let y: i64 = 2_000_000_000;
    +        let z: i64 = 1_000_000_000;
    +
    +        let result = mul_div_ceil(x, y, z);
    +
    +        assert_eq!(result, None);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_result_negative_overflow() {
    +        let x: i64 = i64::MIN / 2 - 1;
    +        let y: i64 = 2_000_000_000;
    +        let z: i64 = 1_000_000_000;
     
    -        let result = x.fixed_div_ceil(y, denominator);
    +        let result = mul_div_ceil(x, y, z);
     
             assert_eq!(result, None);
         }
    +
    +    /********** fixed_mul_floor **********/
    +
    +    #[test]
    +    fn test_fixed_mul_floor() {
    +        // Real result = 104_9522835.2
    +        let x: i64 = 3_1423141;
    +        let y: i64 = 4_1234142;
    +        let denominator: i64 = 0_1234567;
    +
    +        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        assert_eq!(result, 104_9522835);
    +
    +        let result = (-x).fixed_mul_floor(y, denominator).unwrap();
    +        assert_eq!(result, -104_9522836);
    +
    +        let result = (-x).fixed_mul_floor(-y, denominator).unwrap();
    +        assert_eq!(result, 104_9522835);
    +
    +        let invalid = x.fixed_mul_floor(y, 0);
    +        assert_eq!(invalid, None);
    +    }
    +
    +    /********** fixed_mul_ceil **********/
    +
    +    #[test]
    +    fn test_fixed_mul_ceil() {
    +        // Real result = 104_9522835.2
    +        let x: i64 = 3_1423141;
    +        let y: i64 = 4_1234142;
    +        let denominator: i64 = 0_1234567;
    +
    +        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        assert_eq!(result, 104_9522836);
    +
    +        let result = (-x).fixed_mul_ceil(y, denominator).unwrap();
    +        assert_eq!(result, -104_9522835);
    +
    +        let result = (-x).fixed_mul_ceil(-y, denominator).unwrap();
    +        assert_eq!(result, 104_9522836);
    +
    +        let invalid = x.fixed_mul_ceil(y, 0);
    +        assert_eq!(invalid, None);
    +    }
    +
    +    /********** fixed_div_floor **********/
    +
    +    #[test]
    +    fn test_fixed_div_floor() {
    +        // Real result = 204_1150997.8
    +        let x: i64 = 314_1592653;
    +        let y: i64 = 1_5391280;
    +        let denominator: i64 = 1_0000000;
    +
    +        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        assert_eq!(result, 204_1150997);
    +
    +        let result = (-x).fixed_div_floor(y, denominator).unwrap();
    +        assert_eq!(result, -204_1150998);
    +
    +        let result = (-x).fixed_div_floor(-y, denominator).unwrap();
    +        assert_eq!(result, 204_1150997);
    +
    +        let invalid = x.fixed_div_floor(0, denominator);
    +        assert_eq!(invalid, None);
    +    }
    +
    +    /********** fixed_div_ceil **********/
    +
    +    #[test]
    +    fn test_fixed_div_ceil() {
    +        // Real result = 204_1150997.8
    +        let x: i64 = 314_1592653;
    +        let y: i64 = 1_5391280;
    +        let denominator: i64 = 1_0000000;
    +
    +        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        assert_eq!(result, 204_1150998);
    +
    +        let result = (-x).fixed_div_ceil(y, denominator).unwrap();
    +        assert_eq!(result, -204_1150997);
    +
    +        let result = (-x).fixed_div_ceil(-y, denominator).unwrap();
    +        assert_eq!(result, 204_1150998);
    +
    +        let invalid = x.fixed_div_ceil(0, denominator);
    +        assert_eq!(invalid, None);
    +    }
     }
    
  • src/u128.rs+440 113 modified
    @@ -35,8 +35,10 @@ pub(crate) fn mul_div_ceil(x: u128, y: u128, z: u128) -> Option<u128> {
     /// Performs ceil(r / z)
     fn div_ceil(r: u128, z: u128) -> Option<u128> {
         // floor taken by default for a positive result
    +    // if there is any remainder, add 1 to round ceil
         let remainder = r.checked_rem_euclid(z)?;
    -    (r / z).checked_add(if remainder > 0 { 1 } else { 0 })
    +    r.checked_div(z)?
    +        .checked_add(if remainder > 0 { 1 } else { 0 })
     }
     
     impl SorobanFixedPoint for u128 {
    @@ -94,280 +96,605 @@ fn scaled_mul_div_ceil(x: &u128, env: &Env, y: &u128, z: &u128) -> u128 {
     
     #[cfg(test)]
     mod test_fixed_point {
    +    use super::{mul_div_ceil, mul_div_floor};
    +    use crate::FixedPoint;
     
    -    /********** fixed_mul_floor **********/
    +    /********** mul_div_floor **********/
     
    -    use crate::FixedPoint;
    +    #[test]
    +    fn test_mul_div_floor_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let x: u128 = 1_5391283;
    +        let y: u128 = 314_1592653;
    +        let z: u128 = 1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_exact() {
    +        // Real result = 12
    +        let x: u128 = 8;
    +        let y: u128 = 3;
    +        let z: u128 = 2;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 12);
    +    }
     
         #[test]
    -    fn test_fixed_mul_floor_rounds_down() {
    +    fn test_mul_div_floor_mul_zero() {
    +        let x: u128 = 1_5391283;
    +        let y: u128 = 0;
    +        let z: u128 = 1_0000001;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_div_zero() {
             let x: u128 = 1_5391283;
             let y: u128 = 314_1592653;
    -        let denominator: u128 = 1_0000001;
    +        let z: u128 = 0;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z);
     
    -        assert_eq!(result, 483_5313675)
    +        assert_eq!(result, None);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_large_number() {
    +    fn test_mul_div_floor_large_number() {
             let x: u128 = 340_282_366_920_938_463_463;
             let y: u128 = 1_000_000_000_000_000_000;
    -        let denominator: u128 = 1_000_000_000_000_000_000;
    +        let z: u128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
     
    -        assert_eq!(result, 340_282_366_920_938_463_463)
    +        assert_eq!(result, 340_282_366_920_938_463_463);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_phantom_overflow() {
    +    fn test_mul_div_floor_small_number() {
    +        let x: u128 = 1;
    +        let y: u128 = 2;
    +        let z: u128 = 3;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_phantom_overflow() {
             let x: u128 = 340_282_366_920_938_463_463;
             let y: u128 = 1_000_000_000_000_000_001;
    -        let denominator: u128 = 1_000_000_000_000_000_000;
    +        let z: u128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_mul_floor(y, denominator);
    +        let result = mul_div_floor(x, y, z);
     
    -        assert_eq!(None, result);
    +        assert_eq!(result, None);
         }
     
    -    /********** fixed_mul_ceil **********/
    +    /********** mul_div_ceil **********/
     
         #[test]
    -    fn test_fixed_mul_ceil_rounds_up() {
    +    fn test_mul_div_ceil_rounds_up() {
    +        // Real result = 483_5313675.8
             let x: u128 = 1_5391283;
             let y: u128 = 314_1592653;
    -        let denominator: u128 = 1_0000001;
    +        let z: u128 = 1_0000001;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313676);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_exact() {
    +        // Real result = 12
    +        let x: u128 = 8;
    +        let y: u128 = 3;
    +        let z: u128 = 2;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 12);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_mul_zero() {
    +        let x: u128 = 1_5391283;
    +        let y: u128 = 0;
    +        let z: u128 = 1_0000001;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
     
    -        assert_eq!(result, 483_5313676)
    +    #[test]
    +    fn test_mul_div_ceil_div_zero() {
    +        let x: u128 = 1_5391283;
    +        let y: u128 = 314_1592653;
    +        let z: u128 = 0;
    +
    +        let result = mul_div_ceil(x, y, z);
    +
    +        assert_eq!(result, None);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_large_number() {
    +    fn test_mul_div_ceil_large_number() {
             let x: u128 = 340_282_366_920_938_463_463;
             let y: u128 = 1_000_000_000_000_000_000;
    -        let denominator: u128 = 1_000_000_000_000_000_000;
    +        let z: u128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 340_282_366_920_938_463_463);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_small_number() {
    +        let x: u128 = 1;
    +        let y: u128 = 2;
    +        let z: u128 = 3;
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 340_282_366_920_938_463_463)
    +        assert_eq!(result, 1);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_phantom_overflow() {
    +    fn test_mul_div_ceil_phantom_overflow() {
             let x: u128 = 340_282_366_920_938_463_463;
             let y: u128 = 1_000_000_000_000_000_001;
    -        let denominator: u128 = 1_000_000_000_000_000_000;
    +        let z: u128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_mul_ceil(y, denominator);
    +        let result = mul_div_ceil(x, y, z);
     
    -        assert_eq!(None, result);
    +        assert_eq!(result, None);
         }
     
    -    /********** fixed_div_floor **********/
    +    /********** fixed_mul_floor **********/
     
         #[test]
    -    fn test_fixed_div_floor_rounds_down() {
    -        let x: u128 = 314_1592653;
    -        let y: u128 = 1_5391280;
    -        let denominator: u128 = 1_0000000;
    +    fn test_fixed_mul_floor() {
    +        // Real result = 104_9522835.2
    +        let x: u128 = 3_1423141;
    +        let y: u128 = 4_1234142;
    +        let denominator: u128 = 0_1234567;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        assert_eq!(result, 104_9522835);
     
    -        assert_eq!(result, 204_1150997)
    +        let invalid = x.fixed_mul_floor(y, 0);
    +        assert_eq!(invalid, None);
         }
     
    +    /********** fixed_mul_ceil **********/
    +
         #[test]
    -    fn test_fixed_div_floor_large_number() {
    -        let x: u128 = 340_282_366_920_938_463_463;
    -        let y: u128 = 1_000_000_000_000_000_000;
    -        let denominator: u128 = 1_000_000_000_000_000_000;
    +    fn test_fixed_mul_ceil() {
    +        // Real result = 104_9522835.2
    +        let x: u128 = 3_1423141;
    +        let y: u128 = 4_1234142;
    +        let denominator: u128 = 0_1234567;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        assert_eq!(result, 104_9522836);
     
    -        assert_eq!(result, 340_282_366_920_938_463_463)
    +        let invalid = x.fixed_mul_ceil(y, 0);
    +        assert_eq!(invalid, None);
         }
     
    +    /********** fixed_div_floor **********/
    +
         #[test]
    -    fn test_fixed_div_floor_phantom_overflow() {
    -        let x: u128 = 340_282_366_920_938_463_463;
    -        let y: u128 = 1_000_000_000_000_000_000;
    -        let denominator: u128 = 1_000_000_000_000_000_001;
    +    fn test_fixed_div_floor() {
    +        // Real result = 204_1150997.8
    +        let x: u128 = 314_1592653;
    +        let y: u128 = 1_5391280;
    +        let denominator: u128 = 1_0000000;
     
    -        let result = x.fixed_div_floor(y, denominator);
    +        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        assert_eq!(result, 204_1150997);
     
    -        assert_eq!(None, result);
    +        let invalid = x.fixed_div_floor(0, denominator);
    +        assert_eq!(invalid, None);
         }
     
         /********** fixed_div_ceil **********/
     
         #[test]
    -    fn test_fixed_div_ceil_rounds_down() {
    +    fn test_fixed_div_ceil() {
    +        // Real result = 204_1150997.8
             let x: u128 = 314_1592653;
             let y: u128 = 1_5391280;
             let denominator: u128 = 1_0000000;
     
             let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        assert_eq!(result, 204_1150998);
     
    -        assert_eq!(result, 204_1150998)
    +        let invalid = x.fixed_div_ceil(0, denominator);
    +        assert_eq!(invalid, None);
         }
    +}
    +
    +#[cfg(test)]
    +mod test_soroban_fixed_point {
    +    use super::{scaled_mul_div_ceil, scaled_mul_div_floor};
    +    use crate::SorobanFixedPoint;
    +    use soroban_sdk::Env;
    +
    +    /********** scaled_mul_div_floor **********/
     
         #[test]
    -    fn test_fixed_div_ceil_large_number() {
    -        let x: u128 = 340_282_366_920_938_463_463;
    -        let y: u128 = 1_000_000_000_000_000_000;
    -        let denominator: u128 = 1_000_000_000_000_000_000;
    +    fn test_scaled_mul_div_floor_rounds_down() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x: u128 = 1_5391283;
    +        let y: u128 = 314_1592653;
    +        let z: u128 = 1_0000001;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_exact() {
    +        // Real result = 12
    +        let env = Env::default();
    +        let x: u128 = 8;
    +        let y: u128 = 3;
    +        let z: u128 = 2;
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
     
    -        assert_eq!(result, 340_282_366_920_938_463_463)
    +        assert_eq!(result, 12);
         }
     
         #[test]
    -    fn test_fixed_div_ceil_phantom_overflow() {
    +    fn test_scaled_mul_div_floor_mul_zero() {
    +        let env = Env::default();
    +        let x: u128 = 1_5391283;
    +        let y: u128 = 0;
    +        let z: u128 = 1_0000001;
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_scaled_mul_div_floor_div_zero() {
    +        let env = Env::default();
    +        let x: u128 = 1_5391283;
    +        let y: u128 = 314_1592653;
    +        let z: u128 = 0;
    +
    +        scaled_mul_div_floor(&x, &env, &y, &z);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_large_number() {
    +        let env = Env::default();
             let x: u128 = 340_282_366_920_938_463_463;
             let y: u128 = 1_000_000_000_000_000_000;
    -        let denominator: u128 = 1_000_000_000_000_000_001;
    +        let z: u128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_div_ceil(y, denominator);
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
     
    -        assert_eq!(None, result);
    +        assert_eq!(result, 340_282_366_920_938_463_463);
         }
    -}
     
    -#[cfg(test)]
    -mod test_soroban_fixed_point {
    -    use crate::SorobanFixedPoint;
    -    use soroban_sdk::Env;
    +    #[test]
    +    fn test_scaled_mul_div_floor_small_number() {
    +        let env = Env::default();
    +        let x: u128 = 1;
    +        let y: u128 = 2;
    +        let z: u128 = 3;
     
    -    /********** fixed_mul_floor **********/
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_phantom_overflow_uses_u256() {
    +        // u128::MAX is odd
    +        let env = Env::default();
    +        let x: u128 = (u128::MAX - 1) / 2;
    +        let y: u128 = 2 * 10u128.pow(18);
    +        let z: u128 = 10u128.pow(18);
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, u128::MAX - 1);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_floor_phantom_overflow_rounds_down() {
    +        // Real Result = 333_3x18.3..
    +        let env = Env::default();
    +        let x: u128 = 100 * 10u128.pow(18);
    +        let y: u128 = 10 * 10u128.pow(18);
    +        let z: u128 = 3 * 10u128.pow(18);
    +
    +        let result = scaled_mul_div_floor(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 333_333_333_333_333_333_333);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_scaled_mul_div_floor_result_overflow() {
    +        // u128::MAX is odd
    +        let env = Env::default();
    +        let x: u128 = (u128::MAX - 1) / 2 + 1;
    +        let y: u128 = 2 * 10u128.pow(18);
    +        let z: u128 = 10u128.pow(18);
    +
    +        scaled_mul_div_floor(&x, &env, &y, &z);
    +    }
    +
    +    /********** scaled_mul_div_ceil **********/
     
         #[test]
    -    fn test_fixed_mul_floor_rounds_down() {
    +    fn test_scaled_mul_div_ceil_rounds_up() {
    +        // Real result = 483_5313675.8
             let env = Env::default();
             let x: u128 = 1_5391283;
             let y: u128 = 314_1592653;
    -        let denominator: u128 = 1_0000001;
    +        let z: u128 = 1_0000001;
     
    -        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
     
    -        assert_eq!(result, 483_5313675)
    +        assert_eq!(result, 483_5313676);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_phantom_overflow_scales() {
    +    fn test_scaled_mul_div_ceil_exact() {
    +        // Real result = 12
             let env = Env::default();
    -        let x: u128 = 340_282_366_920_938_463_463;
    -        let y: u128 = 10u128.pow(27);
    -        let denominator: u128 = 10u128.pow(18);
    +        let x: u128 = 8;
    +        let y: u128 = 3;
    +        let z: u128 = 2;
     
    -        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
     
    -        assert_eq!(result, 340_282_366_920_938_463_463 * 10u128.pow(9));
    +        assert_eq!(result, 12);
         }
     
    -    /********** fixed_mul_ceil **********/
    +    #[test]
    +    fn test_scaled_mul_div_ceil_mul_zero() {
    +        let env = Env::default();
    +        let x: u128 = 1_5391283;
    +        let y: u128 = 0;
    +        let z: u128 = 1_0000001;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 0);
    +    }
     
         #[test]
    -    fn test_fixed_mul_ceil_rounds_up() {
    +    #[should_panic]
    +    fn test_scaled_mul_div_ceil_div_zero() {
             let env = Env::default();
             let x: u128 = 1_5391283;
             let y: u128 = 314_1592653;
    -        let denominator: u128 = 1_0000001;
    -
    -        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        let z: u128 = 0;
     
    -        assert_eq!(result, 483_5313676)
    +        scaled_mul_div_ceil(&x, &env, &y, &z);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_large_number() {
    +    fn test_scaled_mul_div_ceil_large_number() {
             let env = Env::default();
             let x: u128 = 340_282_366_920_938_463_463;
             let y: u128 = 1_000_000_000_000_000_000;
    -        let denominator: u128 = 1_000_000_000_000_000_000;
    +        let z: u128 = 1_000_000_000_000_000_000;
     
    -        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
     
    -        assert_eq!(result, 340_282_366_920_938_463_463)
    +        assert_eq!(result, 340_282_366_920_938_463_463);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_phantom_overflow_scales() {
    +    fn test_scaled_mul_div_ceil_small_number() {
             let env = Env::default();
    -        let x: u128 = 340_282_366_920_938_463_463;
    -        let y: u128 = 10u128.pow(27);
    +        let x: u128 = 1;
    +        let y: u128 = 2;
    +        let z: u128 = 3;
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 1);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_phantom_overflow_uses_u256() {
    +        // u128::MAX is odd
    +        let env = Env::default();
    +        let x: u128 = (u128::MAX - 1) / 2;
    +        let y: u128 = 2 * 10u128.pow(18);
    +        let z: u128 = 10u128.pow(18);
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, u128::MAX - 1);
    +    }
    +
    +    #[test]
    +    fn test_scaled_mul_div_ceil_phantom_overflow_rounds_up() {
    +        // Real Result = 333_3x18.3..
    +        let env = Env::default();
    +        let x: u128 = 100 * 10u128.pow(18);
    +        let y: u128 = 10 * 10u128.pow(18);
    +        let z: u128 = 3 * 10u128.pow(18);
    +
    +        let result = scaled_mul_div_ceil(&x, &env, &y, &z);
    +
    +        assert_eq!(result, 333_333_333_333_333_333_334);
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_scaled_mul_div_ceil_result_overflow() {
    +        // u128::MAX is odd
    +        let env = Env::default();
    +        let x: u128 = (u128::MAX - 1) / 2 + 1;
    +        let y: u128 = 2 * 10u128.pow(18);
    +        let z: u128 = 10u128.pow(18);
    +
    +        scaled_mul_div_ceil(&x, &env, &y, &z);
    +    }
    +
    +    /********** fixed_mul_floor **********/
    +
    +    #[test]
    +    fn test_fixed_mul_floor() {
    +        // Real result = 104_9522835.2
    +        let env = Env::default();
    +        let x: u128 = 3_1423141;
    +        let y: u128 = 4_1234142;
    +        let denominator: u128 = 0_1234567;
    +
    +        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, 104_9522835);
    +    }
    +
    +    #[test]
    +    fn test_fixed_mul_floor_uses_u256() {
    +        // Real result = 246800e18
    +        let env = Env::default();
    +        let x: u128 = 1234 * 10u128.pow(18);
    +        let y: u128 = 200 * 10u128.pow(18);
    +        let denominator: u128 = 10u128.pow(18);
    +
    +        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, 246800 * 10u128.pow(18));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_fixed_mul_floor_panics() {
    +        let env = Env::default();
    +        let x: u128 = 10u128.pow(7);
    +
    +        x.fixed_mul_floor(&env, &x, &0);
    +    }
    +
    +    /********** fixed_mul_ceil **********/
    +
    +    #[test]
    +    fn test_fixed_mul_ceil() {
    +        // Real result = 104_9522835.2
    +        let env = Env::default();
    +        let x: u128 = 3_1423141;
    +        let y: u128 = 4_1234142;
    +        let denominator: u128 = 0_1234567;
    +
    +        let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        assert_eq!(result, 104_9522836);
    +    }
    +
    +    #[test]
    +    fn test_fixed_mul_ceil_uses_u256() {
    +        // Real result = 246800e18
    +        let env = Env::default();
    +        let x: u128 = 1234 * 10u128.pow(18);
    +        let y: u128 = 200 * 10u128.pow(18);
             let denominator: u128 = 10u128.pow(18);
     
             let result = x.fixed_mul_ceil(&env, &y, &denominator);
    +        assert_eq!(result, 246800 * 10u128.pow(18));
    +    }
     
    -        assert_eq!(result, 340_282_366_920_938_463_463 * 10u128.pow(9));
    +    #[test]
    +    #[should_panic]
    +    fn test_fixed_mul_ceil_panics() {
    +        let env = Env::default();
    +        let x: u128 = 10u128.pow(7);
    +
    +        x.fixed_mul_ceil(&env, &x, &0);
         }
     
         /********** fixed_div_floor **********/
     
         #[test]
    -    fn test_fixed_div_floor_rounds_down() {
    +    fn test_fixed_div_floor() {
    +        // Real result = 204_1150997.8
             let env = Env::default();
             let x: u128 = 314_1592653;
             let y: u128 = 1_5391280;
             let denominator: u128 = 1_0000000;
     
             let result = x.fixed_div_floor(&env, &y, &denominator);
    -
    -        assert_eq!(result, 204_1150997)
    +        assert_eq!(result, 204_1150997);
         }
     
         #[test]
    -    fn test_fixed_div_floor_phantom_overflow_scales() {
    +    fn test_fixed_div_floor_uses_u256() {
    +        // Real result = 5000e18
             let env = Env::default();
    -        let x: u128 = 340_282_366_920_938_463_463;
    -        let y: u128 = 10u128.pow(18);
    -        let denominator: u128 = 10u128.pow(27);
    +        let x: u128 = 1000 * 10u128.pow(18);
    +        let y: u128 = 2 * 10u128.pow(17);
    +        let denominator: u128 = 10u128.pow(18);
     
             let result = x.fixed_div_floor(&env, &y, &denominator);
    +        assert_eq!(result, 5000 * 10u128.pow(18));
    +    }
     
    -        assert_eq!(result, 340_282_366_920_938_463_463 * 10u128.pow(9));
    +    #[test]
    +    #[should_panic]
    +    fn test_fixed_div_floor_panics() {
    +        let env = Env::default();
    +        let x: u128 = 10u128.pow(7);
    +
    +        x.fixed_div_floor(&env, &0, &x);
         }
     
         /********** fixed_div_ceil **********/
     
         #[test]
    -    fn test_fixed_div_ceil_rounds_down() {
    +    fn test_fixed_div_ceil() {
    +        // Real result = 204_1150997.8
             let env = Env::default();
             let x: u128 = 314_1592653;
             let y: u128 = 1_5391280;
             let denominator: u128 = 1_0000000;
     
             let result = x.fixed_div_ceil(&env, &y, &denominator);
    -
    -        assert_eq!(result, 204_1150998)
    +        assert_eq!(result, 204_1150998);
         }
     
         #[test]
    -    fn test_fixed_div_ceil_large_number() {
    +    fn test_fixed_div_ceil_uses_u256() {
    +        // Real result = 5000e18
             let env = Env::default();
    -        let x: u128 = 340_282_366_920_938_463_463;
    -        let y: u128 = 1_000_000_000_000_000_000;
    -        let denominator: u128 = 1_000_000_000_000_000_000;
    +        let x: u128 = 1000 * 10u128.pow(18);
    +        let y: u128 = 2 * 10u128.pow(17);
    +        let denominator: u128 = 10u128.pow(18);
     
             let result = x.fixed_div_ceil(&env, &y, &denominator);
    -
    -        assert_eq!(result, 340_282_366_920_938_463_463)
    +        assert_eq!(result, 5000 * 10u128.pow(18));
         }
     
         #[test]
    -    fn test_fixed_div_ceil_phantom_overflow_scales() {
    +    #[should_panic]
    +    fn test_fixed_div_ceil_panics() {
             let env = Env::default();
    -        let x: u128 = 340_282_366_920_938_463_463;
    -        let y: u128 = 10u128.pow(18);
    -        let denominator: u128 = 10u128.pow(27);
    -
    -        let result = x.fixed_div_floor(&env, &y, &denominator);
    +        let x: u128 = 10u128.pow(7);
     
    -        assert_eq!(result, 340_282_366_920_938_463_463 * 10u128.pow(9));
    +        x.fixed_div_ceil(&env, &0, &x);
         }
     }
    
  • src/u256.rs+251 70 modified
    @@ -28,6 +28,8 @@ pub(crate) fn mul_div_floor(x: &U256, y: &U256, z: &U256) -> U256 {
     
     /// Performs ceil(x * y / z)
     pub(crate) fn mul_div_ceil(env: &Env, x: &U256, y: &U256, z: &U256) -> U256 {
    +    // floor taken by default
    +    // if there is any remainder, add 1 to round ceil
         let r = x.mul(&y);
         let remainder = r.rem_euclid(&z);
         let zero = U256::from_u32(env, 0);
    @@ -37,161 +39,340 @@ pub(crate) fn mul_div_ceil(env: &Env, x: &U256, y: &U256, z: &U256) -> U256 {
     
     #[cfg(test)]
     mod tests {
    +    use soroban_sdk::Bytes;
    +
         use super::*;
     
    -    /********** fixed_mul_floor **********/
    +    /// Helper to create U256::MAX (all bits set to 1)
    +    fn u256_max(env: &Env) -> U256 {
    +        U256::from_be_bytes(env, &Bytes::from_array(env, &[0xFF; 32]))
    +    }
    +
    +    /********** mul_div_floor **********/
     
         #[test]
    -    fn test_fixed_mul_floor_rounds_down() {
    +    fn test_mul_div_floor_rounds_down() {
    +        // Real result = 483_5313675.8
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, 1_5391283);
    -        let y: U256 = U256::from_u128(&env, 314_1592653);
    -        let denominator: U256 = U256::from_u128(&env, 1_0000001);
    +        let x = U256::from_u128(&env, 1_5391283);
    +        let y = U256::from_u128(&env, 314_1592653);
    +        let z = U256::from_u128(&env, 1_0000001);
     
    -        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        let result = mul_div_floor(&x, &y, &z);
     
             assert_eq!(result, U256::from_u128(&env, 483_5313675));
         }
     
    +    #[test]
    +    fn test_mul_div_floor_exact() {
    +        // Real result = 12
    +        let env = Env::default();
    +        let x = U256::from_u32(&env, 8);
    +        let y = U256::from_u32(&env, 3);
    +        let z = U256::from_u32(&env, 2);
    +
    +        let result = mul_div_floor(&x, &y, &z);
    +
    +        assert_eq!(result, U256::from_u32(&env, 12));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_mul_zero() {
    +        let env = Env::default();
    +        let x = U256::from_u128(&env, 1_5391283);
    +        let y = U256::from_u128(&env, 0);
    +        let z = U256::from_u128(&env, 1_0000001);
    +
    +        let result = mul_div_floor(&x, &y, &z);
    +
    +        assert_eq!(result, U256::from_u128(&env, 0));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_floor_div_zero() {
    +        let env = Env::default();
    +        let x = U256::from_u128(&env, 1_5391283);
    +        let y = U256::from_u128(&env, 314_1592653);
    +        let z = U256::from_u128(&env, 0);
    +
    +        mul_div_floor(&x, &y, &z);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_large_number() {
    +        let env = Env::default();
    +        let x = u256_max(&env).div(&U256::from_u128(&env, 10u128.pow(36)));
    +        let y = U256::from_u128(&env, 10u128.pow(36));
    +        let z = U256::from_u128(&env, 10u128.pow(36));
    +
    +        let result = mul_div_floor(&x, &y, &z);
    +
    +        assert_eq!(result, x);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_small_number() {
    +        let env = Env::default();
    +        let x = U256::from_u32(&env, 1);
    +        let y = U256::from_u32(&env, 2);
    +        let z = U256::from_u32(&env, 3);
    +
    +        let result = mul_div_floor(&x, &y, &z);
    +
    +        assert_eq!(result, U256::from_u32(&env, 0));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_floor_overflow() {
    +        let env = Env::default();
    +        let x = u256_max(&env)
    +            .div(&U256::from_u128(&env, 10u128.pow(36)))
    +            .add(&U256::from_u32(&env, 1));
    +        let y = U256::from_u128(&env, 10u128.pow(36));
    +        let z = U256::from_u128(&env, 10u128.pow(36));
    +
    +        mul_div_floor(&x, &y, &z);
    +    }
    +
    +    /********** mul_div_ceil **********/
    +
    +    #[test]
    +    fn test_mul_div_ceil_rounds_up() {
    +        // Real result = 483_5313675.8
    +        let env = Env::default();
    +        let x = U256::from_u128(&env, 1_5391283);
    +        let y = U256::from_u128(&env, 314_1592653);
    +        let z = U256::from_u128(&env, 1_0000001);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, U256::from_u128(&env, 483_5313676));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_exact() {
    +        // Real result = 12
    +        let env = Env::default();
    +        let x = U256::from_u32(&env, 8);
    +        let y = U256::from_u32(&env, 3);
    +        let z = U256::from_u32(&env, 2);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, U256::from_u32(&env, 12));
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_mul_zero() {
    +        let env = Env::default();
    +        let x = U256::from_u128(&env, 1_5391283);
    +        let y = U256::from_u32(&env, 0);
    +        let z = U256::from_u128(&env, 1_0000001);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, U256::from_u32(&env, 0));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_ceil_div_zero() {
    +        let env = Env::default();
    +        let x = U256::from_u128(&env, 1_5391283);
    +        let y = U256::from_u128(&env, 314_1592653);
    +        let z = U256::from_u32(&env, 0);
    +
    +        mul_div_ceil(&env, &x, &y, &z);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_large_number() {
    +        let env = Env::default();
    +        let x = u256_max(&env).div(&U256::from_u128(&env, 10u128.pow(36)));
    +        let y = U256::from_u128(&env, 10u128.pow(36));
    +        let z = U256::from_u128(&env, 10u128.pow(36));
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, x);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_small_number() {
    +        let env = Env::default();
    +        let x = U256::from_u32(&env, 1);
    +        let y = U256::from_u32(&env, 2);
    +        let z = U256::from_u32(&env, 3);
    +
    +        let result = mul_div_ceil(&env, &x, &y, &z);
    +
    +        assert_eq!(result, U256::from_u32(&env, 1));
    +    }
    +
    +    #[test]
    +    #[should_panic]
    +    fn test_mul_div_ceil_overflow() {
    +        let env = Env::default();
    +        let x = u256_max(&env)
    +            .div(&U256::from_u128(&env, 10u128.pow(36)))
    +            .add(&U256::from_u32(&env, 1));
    +        let y = U256::from_u128(&env, 10u128.pow(36));
    +        let z = U256::from_u128(&env, 10u128.pow(36));
    +
    +        mul_div_ceil(&env, &x, &y, &z);
    +    }
    +
    +    /********** fixed_mul_floor **********/
    +
    +    #[test]
    +    fn test_fixed_mul_floor() {
    +        // Real result = 104_9522835.2
    +        let env = Env::default();
    +        let x = U256::from_u128(&env, 3_1423141);
    +        let y = U256::from_u128(&env, 4_1234142);
    +        let denominator = U256::from_u128(&env, 0_1234567);
    +
    +        let result = x.fixed_mul_floor(&env, &y, &denominator);
    +        assert_eq!(result, U256::from_u128(&env, 104_9522835));
    +    }
    +
         #[test]
         fn test_fixed_mul_floor_large_number() {
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, u128::MAX);
    -        let y: U256 = U256::from_u128(&env, 10u128.pow(38));
    -        let denominator: U256 = U256::from_u128(&env, 10u128.pow(18));
    +        let x = U256::from_u128(&env, u128::MAX);
    +        let y = U256::from_u128(&env, 10u128.pow(38));
    +        let denominator = U256::from_u128(&env, 10u128.pow(18));
     
    -        let result = x.clone().fixed_mul_floor(&env, &y, &denominator);
    +        let result = x.fixed_mul_floor(&env, &y, &denominator);
     
    -        let expected_result = x.mul(&U256::from_u128(&env, 10u128.pow(20)));
    +        let expected_result =
    +            U256::from_u128(&env, u128::MAX).mul(&U256::from_u128(&env, 10u128.pow(20)));
             assert_eq!(result, expected_result);
         }
     
         #[test]
    -    #[should_panic(expected = "attempt to multiply with overflow")]
    -    fn test_fixed_mul_floor_phantom_overflow() {
    +    #[should_panic]
    +    fn test_fixed_mul_floor_panics() {
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, u128::MAX);
    -        // 256 bit max ~= 1.2e77, 128 bit max ~= 3.4e38, need to multiply by at least 10^39
    -        let y: U256 = U256::from_u128(&env, 10u128.pow(39));
    -        let denominator: U256 = U256::from_u128(&env, 10u128.pow(18));
    +        let x = U256::from_u128(&env, 10u128.pow(7));
    +        let zero = U256::from_u32(&env, 0);
     
    -        x.fixed_mul_floor(&env, &y, &denominator);
    +        x.fixed_mul_floor(&env, &x, &zero);
         }
     
         /********** fixed_mul_ceil **********/
     
         #[test]
    -    fn test_fixed_mul_ceil_rounds_up() {
    +    fn test_fixed_mul_ceil() {
    +        // Real result = 104_9522835.2
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, 1_5391283);
    -        let y: U256 = U256::from_u128(&env, 314_1592653);
    -        let denominator: U256 = U256::from_u128(&env, 1_0000001);
    +        let x = U256::from_u128(&env, 3_1423141);
    +        let y = U256::from_u128(&env, 4_1234142);
    +        let denominator = U256::from_u128(&env, 0_1234567);
     
             let result = x.fixed_mul_ceil(&env, &y, &denominator);
    -
    -        assert_eq!(result, U256::from_u128(&env, 483_5313676));
    +        assert_eq!(result, U256::from_u128(&env, 104_9522836));
         }
     
         #[test]
         fn test_fixed_mul_ceil_large_number() {
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, u128::MAX);
    -        let y: U256 = U256::from_u128(&env, 10u128.pow(38));
    -        let denominator: U256 = U256::from_u128(&env, 10u128.pow(18));
    +        let x = U256::from_u128(&env, u128::MAX);
    +        let y = U256::from_u128(&env, 10u128.pow(38));
    +        let denominator = U256::from_u128(&env, 10u128.pow(18));
     
    -        let result = x.clone().fixed_mul_ceil(&env, &y, &denominator);
    +        let result = x.fixed_mul_ceil(&env, &y, &denominator);
     
    -        let expected_result = x.mul(&U256::from_u128(&env, 10u128.pow(20)));
    +        let expected_result =
    +            U256::from_u128(&env, u128::MAX).mul(&U256::from_u128(&env, 10u128.pow(20)));
             assert_eq!(result, expected_result);
         }
     
         #[test]
    -    #[should_panic(expected = "attempt to multiply with overflow")]
    -    fn test_fixed_mul_ceil_phantom_overflow() {
    +    #[should_panic]
    +    fn test_fixed_mul_ceil_panics() {
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, u128::MAX);
    -        // 256 bit max ~= 1.2e77, 128 bit max ~= 3.4e38, need to multiply by at least 10^39
    -        let y: U256 = U256::from_u128(&env, 10u128.pow(39));
    -        let denominator: U256 = U256::from_u128(&env, 10u128.pow(18));
    +        let x = U256::from_u128(&env, 10u128.pow(7));
    +        let zero = U256::from_u32(&env, 0);
     
    -        x.fixed_mul_ceil(&env, &y, &denominator);
    +        x.fixed_mul_ceil(&env, &x, &zero);
         }
     
         /********** fixed_div_floor **********/
     
         #[test]
    -    fn test_fixed_div_floor_rounds_down() {
    +    fn test_fixed_div_floor() {
    +        // Real result = 204_1150997.8
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, 314_1592653);
    -        let y: U256 = U256::from_u128(&env, 1_5391280);
    -        let denominator: U256 = U256::from_u128(&env, 1_0000000);
    +        let x = U256::from_u128(&env, 314_1592653);
    +        let y = U256::from_u128(&env, 1_5391280);
    +        let denominator = U256::from_u128(&env, 1_0000000);
     
             let result = x.fixed_div_floor(&env, &y, &denominator);
    -
             assert_eq!(result, U256::from_u128(&env, 204_1150997));
         }
     
         #[test]
         fn test_fixed_div_floor_large_number() {
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, u128::MAX);
    -        let y: U256 = U256::from_u128(&env, 10u128.pow(27));
    -        let denominator: U256 = U256::from_u128(&env, 10u128.pow(38));
    +        let x = U256::from_u128(&env, u128::MAX);
    +        let y = U256::from_u128(&env, 10u128.pow(27));
    +        let denominator = U256::from_u128(&env, 10u128.pow(38));
     
    -        let result = x.clone().fixed_div_floor(&env, &y, &denominator);
    +        let result = x.fixed_div_floor(&env, &y, &denominator);
     
    -        let expected_result = x.mul(&U256::from_u128(&env, 10u128.pow(11)));
    +        let expected_result =
    +            U256::from_u128(&env, u128::MAX).mul(&U256::from_u128(&env, 10u128.pow(11)));
             assert_eq!(result, expected_result);
         }
     
         #[test]
    -    #[should_panic(expected = "attempt to multiply with overflow")]
    -    fn test_fixed_div_floor_phantom_overflow() {
    +    #[should_panic]
    +    fn test_fixed_div_floor_panics() {
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, u128::MAX);
    -        let y: U256 = U256::from_u128(&env, 10u128.pow(27));
    -        // 256 bit max ~= 1.2e77, 128 bit max ~= 3.4e38, need to multiply by at least 10^39
    -        let denominator: U256 = U256::from_u128(&env, 10u128.pow(39));
    +        let x = U256::from_u128(&env, 10u128.pow(7));
    +        let zero = U256::from_u32(&env, 0);
     
    -        x.fixed_div_floor(&env, &y, &denominator);
    +        x.fixed_div_floor(&env, &zero, &x);
         }
     
         /********** fixed_div_ceil **********/
     
         #[test]
    -    fn test_fixed_div_ceil_rounds_down() {
    +    fn test_fixed_div_ceil() {
    +        // Real result = 204_1150997.8
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, 314_1592653);
    -        let y: U256 = U256::from_u128(&env, 1_5391280);
    -        let denominator: U256 = U256::from_u128(&env, 1_0000000);
    +        let x = U256::from_u128(&env, 314_1592653);
    +        let y = U256::from_u128(&env, 1_5391280);
    +        let denominator = U256::from_u128(&env, 1_0000000);
     
             let result = x.fixed_div_ceil(&env, &y, &denominator);
    -
             assert_eq!(result, U256::from_u128(&env, 204_1150998));
         }
     
         #[test]
         fn test_fixed_div_ceil_large_number() {
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, u128::MAX);
    -        let y: U256 = U256::from_u128(&env, 10u128.pow(27));
    -        let denominator: U256 = U256::from_u128(&env, 10u128.pow(38));
    +        let x = U256::from_u128(&env, u128::MAX);
    +        let y = U256::from_u128(&env, 10u128.pow(27));
    +        let denominator = U256::from_u128(&env, 10u128.pow(38));
     
    -        let result = x.clone().fixed_div_ceil(&env, &y, &denominator);
    +        let result = x.fixed_div_ceil(&env, &y, &denominator);
     
    -        let expected_result = x.mul(&U256::from_u128(&env, 10u128.pow(11)));
    +        let expected_result =
    +            U256::from_u128(&env, u128::MAX).mul(&U256::from_u128(&env, 10u128.pow(11)));
             assert_eq!(result, expected_result);
         }
     
         #[test]
    -    #[should_panic(expected = "attempt to multiply with overflow")]
    -    fn test_fixed_div_ceil_phantom_overflow() {
    +    #[should_panic]
    +    fn test_fixed_div_ceil_panics() {
             let env = Env::default();
    -        let x: U256 = U256::from_u128(&env, u128::MAX);
    -        let y: U256 = U256::from_u128(&env, 10u128.pow(27));
    -        // 256 bit max ~= 1.2e77, 128 bit max ~= 3.4e38, need to multiply by at least 10^39
    -        let denominator: U256 = U256::from_u128(&env, 10u128.pow(39));
    +        let x = U256::from_u128(&env, 10u128.pow(7));
    +        let zero = U256::from_u32(&env, 0);
     
    -        x.fixed_div_ceil(&env, &y, &denominator);
    +        x.fixed_div_ceil(&env, &zero, &x);
         }
     }
    
  • src/u64.rs+190 96 modified
    @@ -24,10 +24,11 @@ fn mul_div_floor(x: u64, y: u64, z: u64) -> Option<u64> {
             Some(r) => r.checked_div(z),
             None => {
                 let res_u128 = crate::u128::mul_div_floor(x as u128, y as u128, z as u128)?;
    -            if res_u128 > u64::MAX as u128 {
    -                return None;
    +            if let Ok(res_u64) = u64::try_from(res_u128) {
    +                Some(res_u64)
    +            } else {
    +                None
                 }
    -            Some(res_u128 as u64)
             }
         };
     }
    @@ -37,15 +38,16 @@ fn mul_div_ceil(x: u64, y: u64, z: u64) -> Option<u64> {
         return match x.checked_mul(y) {
             Some(r) => {
                 let remainder = r.checked_rem_euclid(z)?;
    -            // div overflow will be caught by checked_rem_euclid
    -            (r / z).checked_add(if remainder > 0 { 1 } else { 0 })
    +            r.checked_div(z)?
    +                .checked_add(if remainder > 0 { 1 } else { 0 })
             }
             None => {
                 let res_u128 = crate::u128::mul_div_ceil(x as u128, y as u128, z as u128)?;
    -            if res_u128 > u64::MAX as u128 {
    -                return None;
    +            if let Ok(res_u64) = u64::try_from(res_u128) {
    +                Some(res_u64)
    +            } else {
    +                None
                 }
    -            Some(res_u128 as u64)
             }
         };
     }
    @@ -54,187 +56,279 @@ fn mul_div_ceil(x: u64, y: u64, z: u64) -> Option<u64> {
     mod tests {
         use super::*;
     
    -    /********** fixed_mul_floor **********/
    +    /********** mul_div_floor **********/
     
         #[test]
    -    fn test_fixed_mul_floor_rounds_down() {
    +    fn test_mul_div_floor_rounds_down() {
    +        // Real result = 483_5313675.8
             let x: u64 = 1_5391283;
             let y: u64 = 314_1592653;
    -        let denominator: u64 = 1_0000001;
    +        let z: u64 = 1_0000001;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 483_5313675);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_exact() {
    +        // Real result = 12
    +        let x: u64 = 8;
    +        let y: u64 = 3;
    +        let z: u64 = 2;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 12);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_mul_zero() {
    +        let x: u64 = 1_5391283;
    +        let y: u64 = 0;
    +        let z: u64 = 1_0000001;
     
    -        assert_eq!(result, 483_5313675)
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_div_zero() {
    +        let x: u64 = 1_5391283;
    +        let y: u64 = 314_1592653;
    +        let z: u64 = 0;
    +
    +        let result = mul_div_floor(x, y, z);
    +
    +        assert_eq!(result, None);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_large_number() {
    +    fn test_mul_div_floor_large_number() {
             let x: u64 = 18_446_744_073;
             let y: u64 = 1_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +        let z: u64 = 1_000_000_000;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
     
    -        assert_eq!(result, 18_446_744_073)
    +        assert_eq!(result, 18_446_744_073);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_phantom_overflow_uses_u128() {
    -        let x: u64 = 18_446_744_073;
    +    fn test_mul_div_floor_small_number() {
    +        let x: u64 = 1;
    +        let y: u64 = 2;
    +        let z: u64 = 3;
    +
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 0);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_phantom_overflow_uses_u128() {
    +        // u64::MAX is odd
    +        let x: u64 = (u64::MAX - 1) / 2;
             let y: u64 = 2_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +        let z: u64 = 1_000_000_000;
     
    -        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, u64::MAX - 1);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_floor_phantom_overflow_rounds_up() {
    +        // Real Result = 333_333_333_333.3..
    +        let x: u64 = 100 * 10u64.pow(9);
    +        let y: u64 = 10 * 10u64.pow(9);
    +        let z: u64 = 3 * 10u64.pow(9);
     
    -        assert_eq!(result, 36_893_488_146);
    +        let result = mul_div_floor(x, y, z).unwrap();
    +
    +        assert_eq!(result, 333_333_333_333);
         }
     
         #[test]
    -    fn test_fixed_mul_floor_result_overflow() {
    -        let x: u64 = 18_446_744_073_000_000_000;
    +    fn test_mul_div_floor_result_overflow() {
    +        // u64::MAX is odd
    +        let x: u64 = (u64::MAX - 1) / 2 + 1;
             let y: u64 = 2_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +        let z: u64 = 1_000_000_000;
     
    -        let result = x.fixed_mul_floor(y, denominator);
    +        let result = mul_div_floor(x, y, z);
     
             assert_eq!(result, None);
         }
     
    -    /********** fixed_mul_ceil **********/
    +    /********** mul_div_ceil **********/
     
         #[test]
    -    fn test_fixed_mul_ceil_rounds_up() {
    +    fn test_mul_div_ceil_rounds_up() {
    +        // Real result = 483_5313675.8
             let x: u64 = 1_5391283;
             let y: u64 = 314_1592653;
    -        let denominator: u64 = 1_0000001;
    +        let z: u64 = 1_0000001;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 483_5313676)
    +        assert_eq!(result, 483_5313676);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_large_number() {
    -        let x: u64 = 18_446_744_073;
    -        let y: u64 = 1_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +    fn test_mul_div_ceil_exact() {
    +        // Real result = 12
    +        let x: u64 = 8;
    +        let y: u64 = 3;
    +        let z: u64 = 2;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 18_446_744_073)
    +        assert_eq!(result, 12);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_phantom_overflow_uses_u128() {
    -        let x: u64 = 18_446_744_073;
    -        let y: u64 = 2_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +    fn test_mul_div_ceil_mul_zero() {
    +        let x: u64 = 1_5391283;
    +        let y: u64 = 0;
    +        let z: u64 = 1_0000001;
     
    -        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 36_893_488_146);
    +        assert_eq!(result, 0);
         }
     
         #[test]
    -    fn test_fixed_mul_ceil_result_overflow() {
    -        let x: u64 = 18_446_744_073_000_000_000;
    -        let y: u64 = 2_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +    fn test_mul_div_ceil_div_zero() {
    +        let x: u64 = 1_5391283;
    +        let y: u64 = 314_1592653;
    +        let z: u64 = 0;
     
    -        let result = x.fixed_mul_ceil(y, denominator);
    +        let result = mul_div_ceil(x, y, z);
     
             assert_eq!(result, None);
         }
     
    -    /********** fixed_div_floor **********/
    -
         #[test]
    -    fn test_fixed_div_floor_rounds_down() {
    -        let x: u64 = 314_1592653;
    -        let y: u64 = 1_5391280;
    -        let denominator: u64 = 1_0000000;
    +    fn test_mul_div_ceil_large_number() {
    +        let x: u64 = 18_446_744_073;
    +        let y: u64 = 1_000_000_000;
    +        let z: u64 = 1_000_000_000;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 204_1150997)
    +        assert_eq!(result, 18_446_744_073);
         }
     
         #[test]
    -    fn test_fixed_div_floor_large_number() {
    -        let x: u64 = 18_446_744_073;
    -        let y: u64 = 1_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +    fn test_mul_div_ceil_small_number() {
    +        let x: u64 = 1;
    +        let y: u64 = 2;
    +        let z: u64 = 3;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 18_446_744_073)
    +        assert_eq!(result, 1);
         }
     
         #[test]
    -    fn test_fixed_div_floor_phantom_overflow_uses_u128() {
    -        let x: u64 = 18_446_744_073;
    +    fn test_mul_div_ceil_phantom_overflow_uses_u128() {
    +        // u64::MAX is odd
    +        let x: u64 = (u64::MAX - 1) / 2;
             let y: u64 = 2_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +        let z: u64 = 1_000_000_000;
     
    -        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        let result = mul_div_ceil(x, y, z).unwrap();
     
    -        assert_eq!(result, 9_223_372_036);
    +        assert_eq!(result, u64::MAX - 1);
         }
     
         #[test]
    -    fn test_fixed_div_floor_result_overflow() {
    -        let x: u64 = 18_446_744_073_000_000_000;
    +    fn test_mul_div_ceil_phantom_overflow_rounds_up() {
    +        // Real Result = 333_333_333_333.3..
    +        let x: u64 = 100 * 10u64.pow(9);
    +        let y: u64 = 10 * 10u64.pow(9);
    +        let z: u64 = 3 * 10u64.pow(9);
    +
    +        let result = mul_div_ceil(x, y, z).unwrap();
    +
    +        assert_eq!(result, 333_333_333_334);
    +    }
    +
    +    #[test]
    +    fn test_mul_div_ceil_result_overflow() {
    +        // u64::MAX is odd
    +        let x: u64 = (u64::MAX - 1) / 2 + 1;
             let y: u64 = 2_000_000_000;
    -        let denominator: u64 = 4_000_000_000;
    +        let z: u64 = 1_000_000_000;
     
    -        let result = x.fixed_div_floor(y, denominator);
    +        let result = mul_div_ceil(x, y, z);
     
             assert_eq!(result, None);
         }
     
    -    /********** fixed_div_ceil **********/
    +    /********** fixed_mul_floor **********/
     
         #[test]
    -    fn test_fixed_div_ceil_rounds_up() {
    -        let x: u64 = 314_1592653;
    -        let y: u64 = 1_5391280;
    -        let denominator: u64 = 1_0000000;
    +    fn test_fixed_mul_floor() {
    +        // Real result = 104_9522835.2
    +        let x: u64 = 3_1423141;
    +        let y: u64 = 4_1234142;
    +        let denominator: u64 = 0_1234567;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = x.fixed_mul_floor(y, denominator).unwrap();
    +        assert_eq!(result, 104_9522835);
     
    -        assert_eq!(result, 204_1150998)
    +        let invalid = x.fixed_mul_floor(y, 0);
    +        assert_eq!(invalid, None);
         }
     
    +    /********** fixed_mul_ceil **********/
    +
         #[test]
    -    fn test_fixed_div_ceil_large_number() {
    -        let x: u64 = 18_446_744_073;
    -        let y: u64 = 1_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +    fn test_fixed_mul_ceil() {
    +        // Real result = 104_9522835.2
    +        let x: u64 = 3_1423141;
    +        let y: u64 = 4_1234142;
    +        let denominator: u64 = 0_1234567;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = x.fixed_mul_ceil(y, denominator).unwrap();
    +        assert_eq!(result, 104_9522836);
     
    -        assert_eq!(result, 18_446_744_073)
    +        let invalid = x.fixed_mul_ceil(y, 0);
    +        assert_eq!(invalid, None);
         }
     
    +    /********** fixed_div_floor **********/
    +
         #[test]
    -    fn test_fixed_div_ceil_phantom_overflow_uses_u128() {
    -        let x: u64 = 18_446_744_073;
    -        let y: u64 = 2_000_000_000;
    -        let denominator: u64 = 1_000_000_000;
    +    fn test_fixed_div_floor() {
    +        // Real result = 204_1150997.8
    +        let x: u64 = 314_1592653;
    +        let y: u64 = 1_5391280;
    +        let denominator: u64 = 1_0000000;
     
    -        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        let result = x.fixed_div_floor(y, denominator).unwrap();
    +        assert_eq!(result, 204_1150997);
     
    -        assert_eq!(result, 9_223_372_037);
    +        let invalid = x.fixed_div_floor(0, denominator);
    +        assert_eq!(invalid, None);
         }
     
    +    /********** fixed_div_ceil **********/
    +
         #[test]
    -    fn test_fixed_div_ceil_result_overflow() {
    -        let x: u64 = 18_446_744_073_000_000_000;
    -        let y: u64 = 2_000_000_000;
    -        let denominator: u64 = 4_000_000_000;
    +    fn test_fixed_div_ceil() {
    +        // Real result = 204_1150997.8
    +        let x: u64 = 314_1592653;
    +        let y: u64 = 1_5391280;
    +        let denominator: u64 = 1_0000000;
     
    -        let result = x.fixed_div_ceil(y, denominator);
    +        let result = x.fixed_div_ceil(y, denominator).unwrap();
    +        assert_eq!(result, 204_1150998);
     
    -        assert_eq!(result, None);
    +        let invalid = x.fixed_div_ceil(0, denominator);
    +        assert_eq!(invalid, None);
         }
     }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.