generated from auditless/cairo-template
-
Notifications
You must be signed in to change notification settings - Fork 3
/
u60f18.cairo
187 lines (161 loc) · 4.7 KB
/
u60f18.cairo
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//! Decimal fixed-point datatype based on u256.
use debug::PrintTrait;
use traits::Into;
use core::integer::BoundedInt;
use core::integer::U256Div;
use core::integer::U256Rem;
use core::zeroable::Zeroable;
use core::integer::U256Mul;
use suna::math::u256::U256Zeroable;
/// Rounding mode.
#[derive(Copy, Drop)]
enum Rounding {
/// Round toward negative infinity
Down: (),
/// Round toward infinity
Up: (),
/// Round toward zero
Zero: (),
}
/// Return a * b / c rounded down.
fn mul_div_down(lhs: u256, rhs: u256, denominator: u256) -> u256 {
let max_u128 = BoundedInt::max();
let max_u256 = u256 { low: max_u128, high: max_u128 };
// TODO: Change this back when u256 non-truncating division is implemented
let cond = Zeroable::is_non_zero(
denominator
) & (Zeroable::is_zero(rhs) | lhs <= Div::div(max_u256, rhs));
assert(cond, 'multiplication overflow');
lhs * rhs / denominator
}
/// Return a * b % denominator without checks.
fn unsafe_mul_mod(lhs: u256, rhs: u256, denominator: u256) -> u256 {
lhs * rhs % denominator
}
trait MulDiv<T> {
/// Return a * b / denominator with given rounding mode.
fn mul_div(lhs: T, rhs: T, denominator: T, rounding: Rounding) -> T;
}
impl U256MulDiv of MulDiv<u256> {
fn mul_div(lhs: u256, rhs: u256, denominator: u256, rounding: Rounding) -> u256 {
let result = mul_div_down(lhs, rhs, denominator);
match rounding {
Rounding::Down(_) => result,
Rounding::Up(_) => if unsafe_mul_mod(
lhs, rhs, denominator
) > 0_u256 {
result + 1_u256
} else {
result
},
Rounding::Zero(_) => result,
}
}
}
/// Unsigned 18-decimal fixed point representation.
/// 60-decimal unsigned part and 18-decimal fractional part
/// (nomenclature borrowed from the Rust crate `fixed`
/// but adapted to use decimals rather than bits).
#[derive(Copy, Drop)]
struct U60F18 {
/// A scaled value of x represents the fraction x / 10^18
scaled: u256,
}
impl U60F18Add of Add<U60F18> {
fn add(lhs: U60F18, rhs: U60F18) -> U60F18 {
U60F18 { scaled: lhs.scaled + rhs.scaled }
}
}
impl U60F18AddEq of AddEq<U60F18> {
#[inline(always)]
fn add_eq(ref self: U60F18, other: U60F18) {
self = Add::add(self, other);
}
}
impl U60F18Sub of Sub<U60F18> {
fn sub(lhs: U60F18, rhs: U60F18) -> U60F18 {
U60F18 { scaled: lhs.scaled - rhs.scaled }
}
}
impl U60F18SubEq of SubEq<U60F18> {
#[inline(always)]
fn sub_eq(ref self: U60F18, other: U60F18) {
self = Sub::sub(self, other);
}
}
impl U60F18Mul of Mul<U60F18> {
fn mul(lhs: U60F18, rhs: U60F18) -> U60F18 {
let base: u256 = 1000000000000000000_u256;
U60F18 { scaled: mul_div_down(lhs.scaled, rhs.scaled, base) }
}
}
impl U60F18MulEq of MulEq<U60F18> {
#[inline(always)]
fn mul_eq(ref self: U60F18, other: U60F18) {
self = Mul::mul(self, other);
}
}
impl U60F18Div of Div<U60F18> {
fn div(lhs: U60F18, rhs: U60F18) -> U60F18 {
let base: u256 = 1000000000000000000_u256;
U60F18 { scaled: mul_div_down(lhs.scaled, base, rhs.scaled) }
}
}
impl U60F18DivEq of DivEq<U60F18> {
#[inline(always)]
fn div_eq(ref self: U60F18, other: U60F18) {
self = Div::div(self, other);
}
}
impl U60F18PartialEq of PartialEq<U60F18> {
#[inline(always)]
fn eq(lhs: U60F18, rhs: U60F18) -> bool {
lhs.scaled == rhs.scaled
}
#[inline(always)]
fn ne(lhs: U60F18, rhs: U60F18) -> bool {
!(lhs.scaled == rhs.scaled)
}
}
impl U60F18PartialOrd of PartialOrd<U60F18> {
#[inline(always)]
fn le(lhs: U60F18, rhs: U60F18) -> bool {
lhs.scaled <= rhs.scaled
}
#[inline(always)]
fn ge(lhs: U60F18, rhs: U60F18) -> bool {
rhs.scaled <= lhs.scaled
}
#[inline(always)]
fn lt(lhs: U60F18, rhs: U60F18) -> bool {
lhs.scaled < rhs.scaled
}
#[inline(always)]
fn gt(lhs: U60F18, rhs: U60F18) -> bool {
rhs.scaled < lhs.scaled
}
}
impl U60F18ToU256 of Into<U60F18, u256> {
fn into(self: U60F18) -> u256 {
let base: u256 = 1000000000000000000_u256;
self.scaled / base
}
}
impl U256ToU60F18 of Into<u256, U60F18> {
fn into(self: u256) -> U60F18 {
let base: u256 = 1000000000000000000_u256;
U60F18 { scaled: self * base }
}
}
impl Felt252ToU60F18 of Into<felt252, U60F18> {
fn into(self: felt252) -> U60F18 {
let a: u256 = self.into();
a.into()
}
}
impl U60F18PrintImpl of PrintTrait<U60F18> {
fn print(self: U60F18) {
self.scaled.print();
}
}
// TODO: Implement StorageAccess once it's testable