-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfull_math.fe
More file actions
66 lines (57 loc) · 2.01 KB
/
Copy pathfull_math.fe
File metadata and controls
66 lines (57 loc) · 2.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use std::evm::crypto::{mulmod}
/// 512-bit mulDiv: floor(a * b / denominator) with full precision.
/// Semantically equivalent to Uniswap V3 FullMath.sol.
///
/// The algorithm is Remco Bloemen's: CRT for 512-bit product,
/// Newton-Raphson modular inverse. Needs wrapping arithmetic
/// throughout since intermediate values intentionally overflow.
#[arithmetic(unchecked)]
pub fn mul_div(a: u256, b: u256, denominator: u256) -> u256 {
// 512-bit multiply [prod1 prod0] = a * b
let mm = mulmod(a, b, 0 - 1)
let prod0 = a * b
let prod1 = mm - prod0 - if mm < prod0 { 1 } else { 0 }
// Simple case: result fits in 256 bits
if prod1 == 0 {
if denominator == 0 {
assert!(false)
}
return prod0 / denominator
}
// Overflow check
if denominator <= prod1 {
assert!(false)
}
// Subtract remainder to make division exact
let remainder = mulmod(a, b, denominator)
let prod1 = prod1 - if remainder > prod0 { 1 } else { 0 }
let prod0 = prod0 - remainder
// Factor powers of two out of denominator
let twos = (0 - denominator) & denominator
let denominator = denominator / twos
let prod0 = prod0 / twos
// Shift prod1 bits into prod0
let twos_inv = (0 - twos) / twos + 1
let prod0 = prod0 | (prod1 * twos_inv)
// Modular inverse via Newton-Raphson (seed correct for 4 bits, double 6x)
let mut inv = (3 * denominator) ^ 2
inv = inv * (2 - denominator * inv)
inv = inv * (2 - denominator * inv)
inv = inv * (2 - denominator * inv)
inv = inv * (2 - denominator * inv)
inv = inv * (2 - denominator * inv)
inv = inv * (2 - denominator * inv)
prod0 * inv
}
/// ceil(a * b / denominator) with full precision.
#[arithmetic(unchecked)]
pub fn mul_div_rounding_up(a: u256, b: u256, denominator: u256) -> u256 {
let mut result = mul_div(a, b, denominator)
if mulmod(a, b, denominator) > 0 {
if result == 0 - 1 {
assert!(false)
}
result = result + 1
}
result
}