Skip to content

Commit 126bd49

Browse files
authored
Power function (#505)
1 parent 815008e commit 126bd49

13 files changed

Lines changed: 2003 additions & 1243 deletions

File tree

packages/contract-utils/src/math/fixed_point.rs

Lines changed: 0 additions & 21 deletions
This file was deleted.

packages/contract-utils/src/math/i128_fixed_point.rs

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,9 @@
1-
/*
2-
MIT License
3-
4-
Copyright (c) 2023 Script3 Ltd. and contributors
5-
6-
Permission is hereby granted, free of charge, to any person obtaining a copy
7-
of this software and associated documentation files (the "Software"), to deal
8-
in the Software without restriction, including without limitation the rights
9-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10-
copies of the Software, and to permit persons to whom the Software is
11-
furnished to do so, subject to the following conditions:
12-
13-
The above copyright notice and this permission notice shall be included in all
14-
copies or substantial portions of the Software.
15-
16-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22-
SOFTWARE.
23-
*/
241
// Based on the Soroban fixed-point mathematics library
252
// Original implementation: https://github.com/script3/soroban-fixed-point-math
263

274
use soroban_sdk::{panic_with_error, Env, I256};
285

29-
use crate::math::{soroban_fixed_point::SorobanFixedPoint, SorobanFixedPointError};
6+
use crate::math::{SorobanFixedPoint, SorobanFixedPointError};
307

318
/// Performs floor(r / z)
329
fn div_floor(r: i128, z: i128) -> Option<i128> {
@@ -60,9 +37,17 @@ impl SorobanFixedPoint for i128 {
6037
fn fixed_mul_ceil(&self, env: &Env, y: &i128, denominator: &i128) -> i128 {
6138
scaled_mul_div_ceil(self, env, y, denominator)
6239
}
40+
41+
fn checked_fixed_mul_floor(&self, env: &Env, y: &i128, denominator: &i128) -> Option<i128> {
42+
checked_scaled_mul_div_floor(self, env, y, denominator)
43+
}
44+
45+
fn checked_fixed_mul_ceil(&self, env: &Env, y: &i128, denominator: &i128) -> Option<i128> {
46+
checked_scaled_mul_div_ceil(self, env, y, denominator)
47+
}
6348
}
6449

65-
/// Performs floor(x * y / z)
50+
/// Performs floor(x * y / z), panics on overflow or division by zero
6651
fn scaled_mul_div_floor(x: &i128, env: &Env, y: &i128, z: &i128) -> i128 {
6752
match x.checked_mul(*y) {
6853
Some(r) => div_floor(r, *z)
@@ -81,7 +66,7 @@ fn scaled_mul_div_floor(x: &i128, env: &Env, y: &i128, z: &i128) -> i128 {
8166
}
8267
}
8368

84-
/// Performs floor(x * y / z)
69+
/// Performs ceil(x * y / z)
8570
fn scaled_mul_div_ceil(x: &i128, env: &Env, y: &i128, z: &i128) -> i128 {
8671
match x.checked_mul(*y) {
8772
Some(r) => div_ceil(r, *z)
@@ -99,3 +84,37 @@ fn scaled_mul_div_ceil(x: &i128, env: &Env, y: &i128, z: &i128) -> i128 {
9984
}
10085
}
10186
}
87+
88+
/// Checked version of floor(x * y / z)
89+
fn checked_scaled_mul_div_floor(x: &i128, env: &Env, y: &i128, z: &i128) -> Option<i128> {
90+
match x.checked_mul(*y) {
91+
Some(r) => div_floor(r, *z),
92+
None => {
93+
// scale to i256 and retry
94+
let res = crate::math::i256_fixed_point::checked_mul_div_floor(
95+
env,
96+
&I256::from_i128(env, *x),
97+
&I256::from_i128(env, *y),
98+
&I256::from_i128(env, *z),
99+
);
100+
res.map(|r| r.to_i128())?
101+
}
102+
}
103+
}
104+
105+
/// Checked version of ceil(x * y / z)
106+
fn checked_scaled_mul_div_ceil(x: &i128, env: &Env, y: &i128, z: &i128) -> Option<i128> {
107+
match x.checked_mul(*y) {
108+
Some(r) => div_ceil(r, *z),
109+
None => {
110+
// scale to i256 and retry
111+
let res = crate::math::i256_fixed_point::checked_mul_div_ceil(
112+
env,
113+
&I256::from_i128(env, *x),
114+
&I256::from_i128(env, *y),
115+
&I256::from_i128(env, *z),
116+
);
117+
res.map(|r| r.to_i128())?
118+
}
119+
}
120+
}
Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,9 @@
1-
/*
2-
MIT License
3-
4-
Copyright (c) 2023 Script3 Ltd. and contributors
5-
6-
Permission is hereby granted, free of charge, to any person obtaining a copy
7-
of this software and associated documentation files (the "Software"), to deal
8-
in the Software without restriction, including without limitation the rights
9-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10-
copies of the Software, and to permit persons to whom the Software is
11-
furnished to do so, subject to the following conditions:
12-
13-
The above copyright notice and this permission notice shall be included in all
14-
copies or substantial portions of the Software.
15-
16-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22-
SOFTWARE.
23-
*/
241
// Based on the Soroban fixed-point mathematics library
252
// Original implementation: https://github.com/script3/soroban-fixed-point-math
263

274
use soroban_sdk::{panic_with_error, Env, I256};
285

29-
use crate::math::{soroban_fixed_point::SorobanFixedPoint, SorobanFixedPointError};
6+
use crate::math::{SorobanFixedPoint, SorobanFixedPointError};
307

318
impl SorobanFixedPoint for I256 {
329
fn fixed_mul_floor(&self, env: &Env, y: &I256, denominator: &I256) -> I256 {
@@ -36,18 +13,26 @@ impl SorobanFixedPoint for I256 {
3613
fn fixed_mul_ceil(&self, env: &Env, y: &I256, denominator: &I256) -> I256 {
3714
mul_div_ceil(env, self, y, denominator)
3815
}
16+
17+
fn checked_fixed_mul_floor(&self, env: &Env, y: &I256, denominator: &I256) -> Option<I256> {
18+
checked_mul_div_floor(env, self, y, denominator)
19+
}
20+
21+
fn checked_fixed_mul_ceil(&self, env: &Env, y: &I256, denominator: &I256) -> Option<I256> {
22+
checked_mul_div_ceil(env, self, y, denominator)
23+
}
3924
}
4025

4126
/// Performs floor(x * y / z)
4227
pub(crate) fn mul_div_floor(env: &Env, x: &I256, y: &I256, z: &I256) -> I256 {
4328
let zero = I256::from_i32(env, 0);
4429
let r = x.mul(y);
4530

46-
if z.clone() == zero {
31+
if *z == zero {
4732
panic_with_error!(env, SorobanFixedPointError::DivisionByZero);
4833
}
4934

50-
if r < zero || (r > zero && z.clone() < zero) {
35+
if r < zero || (r > zero && *z < zero) {
5136
// ceil is taken by default for a negative result
5237
let remainder = r.rem_euclid(z);
5338
let one = I256::from_i32(env, 1);
@@ -63,11 +48,11 @@ pub(crate) fn mul_div_ceil(env: &Env, x: &I256, y: &I256, z: &I256) -> I256 {
6348
let zero = I256::from_i32(env, 0);
6449
let r = x.mul(y);
6550

66-
if z.clone() == zero {
51+
if *z == zero {
6752
panic_with_error!(env, SorobanFixedPointError::DivisionByZero);
6853
}
6954

70-
if z.clone() < zero || r <= zero {
55+
if *z < zero || r <= zero {
7156
// ceil is taken by default for a negative or zero result
7257
r.div(z)
7358
} else {
@@ -77,3 +62,43 @@ pub(crate) fn mul_div_ceil(env: &Env, x: &I256, y: &I256, z: &I256) -> I256 {
7762
r.div(z).add(if remainder > zero { &one } else { &zero })
7863
}
7964
}
65+
66+
/// Checked version of floor(x * y / z)
67+
pub(crate) fn checked_mul_div_floor(env: &Env, x: &I256, y: &I256, z: &I256) -> Option<I256> {
68+
let zero = I256::from_i32(env, 0);
69+
let r = x.mul(y);
70+
71+
if *z == zero {
72+
return None;
73+
}
74+
75+
if r < zero || (r > zero && *z < zero) {
76+
// ceil is taken by default for a negative result
77+
let remainder = r.rem_euclid(z);
78+
let one = I256::from_i32(env, 1);
79+
Some(r.div(z).sub(if remainder > zero { &one } else { &zero }))
80+
} else {
81+
// floor is taken by default for a positive or zero result
82+
Some(r.div(z))
83+
}
84+
}
85+
86+
/// Checked version of ceil(x * y / z)
87+
pub(crate) fn checked_mul_div_ceil(env: &Env, x: &I256, y: &I256, z: &I256) -> Option<I256> {
88+
let zero = I256::from_i32(env, 0);
89+
let r = x.mul(y);
90+
91+
if *z == zero {
92+
return None;
93+
}
94+
95+
if *z < zero || r <= zero {
96+
// ceil is taken by default for a negative or zero result
97+
Some(r.div(z))
98+
} else {
99+
// floor is taken by default for a positive result
100+
let remainder = r.rem_euclid(z);
101+
let one = I256::from_i32(env, 1);
102+
Some(r.div(z).add(if remainder > zero { &one } else { &zero }))
103+
}
104+
}

0 commit comments

Comments
 (0)