Repository: influenceth/cairo-math-64x61 (language: python)

Issue:
Add pow for frac
Hi Chris ... have added per your email
Have tested and works... might need some boundary conditions thanks


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index f46afed..13ee3e0 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
 
 A fixed point 64.61 math library for Cairo & Starknet
 
-## Installation ##
+## Usage ##
 For use with `starknet-hardhat-plugin`, install via npm with `npm install --save-dev @influenceth/cairo-math-64x61` and add `./node_modules/@influenceth/cairo-math-64x61` to `cairoPaths` in your hardhat config (see https://github.com/Shard-Labs/starknet-hardhat-plugin#paths). Then you can import with, for example: `from contracts.Math64x61 import Math64x61_mul`
 
 ## Signed 64.61 Fixed Point Numbers ##
@@ -15,7 +15,7 @@ A signed 64.61-bit fixed point number is a fraction in which the numerator is a
 Can represent values in the range of -2^64 to 2^64 with precision to 4.34e-19.
 
 ## Standard Library ##
-`Math64x61` includes implementation of `add`, `sub`, `mul`, `div`, `sqrt`, `exp`, `ln`, `log2`, `log10`, and `pow` as well as conversion to / from felts and Uint256 values, and assertion methods.
+`Math64x61` includes implementation of `add`, `sub`, `mul`, `div`, `sqrt`, `exp`, `ln`, `log2`, `log10`, and `pow` as well as conversion to / from felts and Uint256 values, `floor`, `ceil` and assertion methods.
 
 ## Trigonometry Library ##
 `Trig64x61` includes implementation of `sin`, `cos`, `tan` and their inverses.
diff --git a/contracts/Math64x61.cairo b/contracts/Math64x61.cairo
index af28aa0..afc2073 100644
--- a/contracts/Math64x61.cairo
+++ b/contracts/Math64x61.cairo
@@ -52,6 +52,22 @@ func Math64x61_fromUint256 {range_check_ptr} (x: Uint256) -> (res: felt):
     return (res)
 end
 
+# Calculates the floor of a 64.61 value
+func Math64x61_floor {range_check_ptr} (x: felt) -> (res: felt):
+    let (int_val, mod_val) = signed_div_rem(x, Math64x61_ONE, Math64x61_BOUND)
+    let res = x - mod_val
+    Math64x61_assert64x61(res)
+    return (res)
+end
+
+# Calculates the ceiling of a 64.61 value
+func Math64x61_ceil {range_check_ptr} (x: felt) -> (res: felt):
+    let (int_val, mod_val) = signed_div_rem(x, Math64x61_ONE, Math64x61_BOUND)
+    let res = (int_val + 1) * Math64x61_ONE
+    Math64x61_assert64x61(res)
+    return (res)
+end
+
 # Convenience addition method to assert no overflow before returning
 func Math64x61_add {range_check_ptr} (x: felt, y: felt) -> (res: felt):
     let res = x + y
@@ -89,7 +105,7 @@ end
 # Calclates the value of x^y and checks for overflow before returning
 # x is a 64x61 fixed point value
 # y is a standard felt (int)
-func Math64x61_pow {range_check_ptr} (x: felt, y: felt) -> (res: felt):
+func Math64x61__pow_int {range_check_ptr} (x: felt, y: felt) -> (res: felt):
     alloc_locals
     let (exp_sign) = sign(y)
     let (exp_val) = abs_value(y)
@@ -99,12 +115,12 @@ func Math64x61_pow {range_check_ptr} (x: felt, y: felt) -> (res: felt):
     end
 
     if exp_sign == -1:
-        let (num) = Math64x61_pow(x, exp_val)
+        let (num) = Math64x61__pow_int(x, exp_val)
         return Math64x61_div(Math64x61_ONE, num)
     end
 
     let (half_exp, rem) = unsigned_div_rem(exp_val, 2)
-    let (half_pow) = Math64x61_pow(x, half_exp)
+    let (half_pow) = Math64x61__pow_int(x, half_exp)
     let (res_p) = Math64x61_mul(half_pow, half_pow)
 
     if rem == 0:
@@ -117,6 +133,27 @@ func Math64x61_pow {range_check_ptr} (x: felt, y: felt) -> (res: felt):
     end
 end
 
+# Calclates the value of x^y and checks for overflow before returning
+# x is a 64x61 fixed point value
+# y is a 64x61 fixed point value
+func Math64x61_pow {range_check_ptr} (x: felt, y: felt) -> (res: felt):
+    alloc_locals
+    let (y_int, y_frac) = signed_div_rem(y, Math64x61_ONE, Math64x61_BOUND)
+
+    # use the more performant integer pow when y is an int
+    if y_frac == 0:
+        return Math64x61__pow_int(x, y_int)
+    end
+
+    # x^y = exp(y*ln(x)) for x > 0 (will error for x < 0
+    let (ln_x) = Math64x61_ln(x)
+    let (y_ln_x) = Math64x61_mul(y,ln_x)
+    let (res) = Math64x61_exp(y_ln_x)
+    return (res)
+    # Math64x61_assert64x61(res)
+    # return (res)
+end
+
 # Calculates the square root of a fixed point value
 # x must be positive
 func Math64x61_sqrt {range_check_ptr} (x: felt) -> (res: felt):
@@ -158,7 +195,7 @@ func Math64x61_exp2 {range_check_ptr} (x: felt) -> (res: felt):
 
     let (exp_value) = abs_value(x)
     let (int_part, frac_part) = unsigned_div_rem(exp_value, Math64x61_FRACT_PART)
-    let (int_res) = Math64x61_pow(2 * Math64x61_ONE, int_part)
+    let (int_res) = Math64x61__pow_int(2 * Math64x61_ONE, int_part)
 
     # 1.069e-7 maximum error
     const a1 = 2305842762765193127
diff --git a/contracts/mocks/Math64x61Mock.cairo b/contracts/mocks/Math64x61Mock.cairo
index 7161fb2..273fac6 100644
--- a/contracts/mocks/Math64x61Mock.cairo
+++ b/contracts/mocks/Math64x61Mock.cairo
@@ -1,6 +1,8 @@
 %lang starknet
 
 from Math64x61 import (
+    Math64x61_floor,
+    Math64x61_ceil,
     Math64x61_mul,
     Math64x61_div,
     Math64x61_pow,
@@ -12,6 +14,18 @@ from Math64x61 import (
     Math64x61_log10
 )
 
+@view
+func Math64x61_floor_test {range_check_ptr} (x: felt) -> (res: felt):
+    let (res) = Math64x61_floor(x)
+    return (res)
+end
+
+@view
+func Math64x61_ceil_test {range_check_ptr} (x: felt) -> (res: felt):
+    let (res) = Math64x61_ceil(x)
+    return (res)
+end
+
 @view
 func Math64x61_mul_test {range_check_ptr} (x: felt, y: felt) -> (res: felt):
     let (res) = Math64x61_mul(x, y)
@@ -65,4 +79,4 @@ end
 func Math64x61_log10_test {range_check_ptr} (x: felt) -> (res: felt):
     let (res) = Math64x61_log10(x)
     return (res)
-end
\ No newline at end of file
+end
diff --git a/package.json b/package.json
index bd8009f..0e84b29 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@influenceth/cairo-math-64x61",
-  "version": "1.0.0",
+  "version": "1.1.0",
   "description": "Fixed point 64.61 math library for Cairo / Starknet",
   "repository": {
     "type": "git",


Test patch:
diff --git a/test/math64x61.spec.js b/test/math64x61.spec.js
index 85d8c5b..7e59673 100644
--- a/test/math64x61.spec.js
+++ b/test/math64x61.spec.js
@@ -17,6 +17,28 @@ describe('64.61 fixed point math', function () {
     contract = await contractFactory.deploy();
   });
 
+  it('should return accurate results for floor', async () => {
+    const count = 10;
+    const xs = Array.from({ length: count }, () => Math.random() * 2 ** 32 - 2 ** 31);
+
+    for (const x of xs) {
+      const { res } = await contract.call('Math64x61_floor_test', { x: to64x61(x) });
+      const exp = Math.floor(x);
+      expect(from64x61(res)).to.eq(exp);
+    }
+  });
+
+  it('should return accurate results for ceiling', async () => {
+    const count = 10;
+    const xs = Array.from({ length: count }, () => Math.random() * 2 ** 32 - 2 ** 31);
+
+    for (const x of xs) {
+      const { res } = await contract.call('Math64x61_ceil_test', { x: to64x61(x) });
+      const exp = Math.ceil(x);
+      expect(from64x61(res)).to.eq(exp);
+    }
+  });
+
   it('should return accurate results for multiplication', async () => {
     const count = 10;
     const xs = Array.from({ length: count }, () => Math.random() * 2 ** 32 - 2 ** 31);
@@ -52,21 +74,21 @@ describe('64.61 fixed point math', function () {
   });
 
   it('should return accurate results for powers', async () => {
-    const xs = [ 4, 4, 4, 1024, 2 ** 16 - 1 , 4 , 64, -10, -10, -10 ];
-    const ys = [ 0, 1, 2, 3, 3 , -2, -3, 1, 2, 3 ];
+    const xs = [ 4, 4, 4 , 1024, 2 ** 16 - 1 , 4 , 64, -10, -10, -10, 0.5, 0.25, 0.125 ];
+    const ys = [ 0, 1, 2 , 3, 3 , -2, -3, 1, 2, 3, 0.5, 1, 1.5 ];
 
     for (const [ i, x ] of xs.entries()) {
       const y = ys[i];
       const { res } = await contract.call('Math64x61_pow_test', {
         x: to64x61(x),
-        y: toFelt(y)
+        y: to64x61(y)
       });
 
       const exp = x ** y;
       expect(almost(from64x61(res), exp, ABS_TOL, REL_TOL), `${from64x61(res)} != ${exp}`).to.be.true;
     }
   });
-
+  
   it('should return accurate results for sqrt', async () => {
     const xs = [ 1, 64, 2 ** 32, 7.21 ** 2 ];
 
@@ -116,4 +138,4 @@ describe('64.61 fixed point math', function () {
       expect(almost(from64x61(res), exp, ABS_TOL, REL_TOL), `${from64x61(res)} != ${exp}`).to.be.true;
     }
   });
-});
\ No newline at end of file
+});