@@ -1941,6 +1941,14 @@ pub(crate) unsafe fn align_offset<T: Sized>(p: *const T, a: usize) -> usize {
1941
1941
/// * `x < m`; (if `x ≥ m`, pass in `x % m` instead)
1942
1942
///
1943
1943
/// Implementation of this function shall not panic. Ever.
1944
+ #[ safety:: requires( m. is_power_of_two( ) ) ]
1945
+ #[ safety:: requires( x < m) ]
1946
+ // TODO: add ensures contract to check that the answer is indeed correct
1947
+ // This will require quantifiers (https://model-checking.github.io/kani/rfc/rfcs/0010-quantifiers.html)
1948
+ // so that we can add a precondition that gcd(x, m) = 1 like so:
1949
+ // ∀d, d > 0 ∧ x % d = 0 ∧ m % d = 0 → d = 1
1950
+ // With this precondition, we can then write this postcondition to check the correctness of the answer:
1951
+ // #[safety::ensures(|result| wrapping_mul(*result, x) % m == 1)]
1944
1952
#[ inline]
1945
1953
const unsafe fn mod_inv ( x : usize , m : usize ) -> usize {
1946
1954
/// Multiplicative modular inverse table modulo 2⁴ = 16.
@@ -2549,67 +2557,4 @@ mod verify {
2549
2557
let p = kani:: any :: < usize > ( ) as * const [ char ; 5 ] ;
2550
2558
check_align_offset ( p) ;
2551
2559
}
2552
-
2553
- // This function lives inside align_offset, so it is not publicly accessible (hence this copy).
2554
- #[ safety:: requires( m. is_power_of_two( ) ) ]
2555
- #[ safety:: requires( x < m) ]
2556
- // TODO: add ensures contract to check that the answer is indeed correct
2557
- // This will require quantifiers (https://model-checking.github.io/kani/rfc/rfcs/0010-quantifiers.html)
2558
- // so that we can add a precondition that gcd(x, m) = 1 like so:
2559
- // ∀d, d > 0 ∧ x % d = 0 ∧ m % d = 0 → d = 1
2560
- // With this precondition, we can then write this postcondition to check the correctness of the answer:
2561
- // #[safety::ensures(|result| wrapping_mul(*result, x) % m == 1)]
2562
- const unsafe fn mod_inv_copy ( x : usize , m : usize ) -> usize {
2563
- /// Multiplicative modular inverse table modulo 2⁴ = 16.
2564
- ///
2565
- /// Note, that this table does not contain values where inverse does not exist (i.e., for
2566
- /// `0⁻¹ mod 16`, `2⁻¹ mod 16`, etc.)
2567
- const INV_TABLE_MOD_16 : [ u8 ; 8 ] = [ 1 , 11 , 13 , 7 , 9 , 3 , 5 , 15 ] ;
2568
- /// Modulo for which the `INV_TABLE_MOD_16` is intended.
2569
- const INV_TABLE_MOD : usize = 16 ;
2570
-
2571
- // SAFETY: `m` is required to be a power-of-two, hence non-zero.
2572
- let m_minus_one = unsafe { unchecked_sub ( m, 1 ) } ;
2573
- let mut inverse = INV_TABLE_MOD_16 [ ( x & ( INV_TABLE_MOD - 1 ) ) >> 1 ] as usize ;
2574
- let mut mod_gate = INV_TABLE_MOD ;
2575
- // We iterate "up" using the following formula:
2576
- //
2577
- // $$ xy ≡ 1 (mod 2ⁿ) → xy (2 - xy) ≡ 1 (mod 2²ⁿ) $$
2578
- //
2579
- // This application needs to be applied at least until `2²ⁿ ≥ m`, at which point we can
2580
- // finally reduce the computation to our desired `m` by taking `inverse mod m`.
2581
- //
2582
- // This computation is `O(log log m)`, which is to say, that on 64-bit machines this loop
2583
- // will always finish in at most 4 iterations.
2584
- loop {
2585
- // y = y * (2 - xy) mod n
2586
- //
2587
- // Note, that we use wrapping operations here intentionally – the original formula
2588
- // uses e.g., subtraction `mod n`. It is entirely fine to do them `mod
2589
- // usize::MAX` instead, because we take the result `mod n` at the end
2590
- // anyway.
2591
- if mod_gate >= m {
2592
- break ;
2593
- }
2594
- inverse = wrapping_mul ( inverse, wrapping_sub ( 2usize , wrapping_mul ( x, inverse) ) ) ;
2595
- let ( new_gate, overflow) = mul_with_overflow ( mod_gate, mod_gate) ;
2596
- if overflow {
2597
- break ;
2598
- }
2599
- mod_gate = new_gate;
2600
- }
2601
- inverse & m_minus_one
2602
- }
2603
-
2604
- // The specification for mod_inv states that it cannot ever panic.
2605
- // Verify that is the case, given that the function's safety preconditions are met.
2606
-
2607
- // TODO: Once https://github.com/model-checking/kani/issues/3467 is fixed,
2608
- // move this harness inside `align_offset` and delete `mod_inv_copy`
2609
- #[ kani:: proof_for_contract( mod_inv_copy) ]
2610
- fn check_mod_inv ( ) {
2611
- let x = kani:: any :: < usize > ( ) ;
2612
- let m = kani:: any :: < usize > ( ) ;
2613
- unsafe { mod_inv_copy ( x, m) } ;
2614
- }
2615
2560
}
0 commit comments