diff --git a/src/ComplexHuff/Complex.huff b/src/ComplexHuff/Complex.huff index c270b9c..c34c60c 100644 --- a/src/ComplexHuff/Complex.huff +++ b/src/ComplexHuff/Complex.huff @@ -5,7 +5,7 @@ #define constant VALUE_LOCATION = FREE_STORAGE_POINTER() -#define macro ADD_Z() = takes (0) returns (0) { +#define macro ADD_Z() = takes (4) returns (2) { add // [Re(A)+Re(B),Im(A),Im(B)] swap2 // [Im(B),Im(A),Re(A)+Re(B)] add // [Im(B)+Im(A),Re(A)+Re(B)] @@ -103,16 +103,16 @@ #define macro TO_POLAR() = takes(2) returns(2) { // INPUT STACK => [RE(a),IM(a)] - dup2 //[i,r,i] - dup2 //[r,i,r,i] - CALC_R() - swap2 // [Im(a),Re(a),r] - [X3] - mul - sdiv // [1e18*Im(a)/Re(a),r] => [x1,r] - dup1 - [X3] - gt case1 jumpi + dup2 //[i,r,i] + dup2 //[r,i,r,i] + CALC_R() //[r,ra,ia] + swap2 // [Im(a),Re(a),r] + [X3] //[1e18,ia,ra,r] + mul //[1e18*ia,ra,r] + sdiv //[1e18*Im(a)/Re(a),r] => [x1,r] + dup1 //[x1,x1,r] + [X3] //[1e18,x1,x1,r] + gt case1 jumpi //[1e18>x1,x1,r] dup1 [X3] eq case2 jumpi @@ -156,11 +156,11 @@ swap1 finish jump - case1: + case1: // if y>x dup1 // [x1,x1,r] - [X11] - swap1 - sdiv // [a,x1,r] + [X11] //[1e12,x1,x1,r] + swap1 //[x1,1e12,x1,r] + sdiv // [a=x1/1e12,x1,r] 0x03 // [0x03,a,x1,r] swap1 // [a,3,x1,r] exp // [a**3,x1,r] @@ -170,9 +170,9 @@ 0x01 // [1,a**3/3,..] 0x00 // [0,1,a**3/3,..] sub // [-1,a**3/3,..] - mul // [x2,x1,r] + mul // [x2=-(a**3)/3,x1,r] dup2 // [x1,x2,x1,r] - [X12] + [X12] //[] swap1 sdiv //[b,x2,..] 0x05 //[5,b,..] @@ -345,7 +345,7 @@ } -// ///@notice e^(a+bi) calculation +///@notice e^(a+bi) calculation #define macro EXP_Z() = takes(2) returns(2) { //INPUT STACK => [Re(A),Im(A)] diff --git a/src/complexMath/Complex.sol b/src/complexMath/Complex.sol index 67d027b..00c69ed 100644 --- a/src/complexMath/Complex.sol +++ b/src/complexMath/Complex.sol @@ -69,8 +69,9 @@ contract Num_Complex { a.re = _a - _b; a.im = _c + _d; - a.re /= 1e18; - a.im /= 1e18; + // a.re /= 1e18; + // a.im /= 1e18; + // Various Fuzz Test were failing due the above two lines return a; } @@ -85,7 +86,8 @@ contract Num_Complex { int256 numB = a.im * b.re - a.re * b.im; a.re = (numA * 1e18) / den; - b.im = (numB * 1e18) / den; + //b.im = (numB * 1e18) / den; should be a instead of b + a.im = (numB * 1e18) / den; return a; } @@ -108,7 +110,9 @@ contract Num_Complex { /// @return r r /// @return T theta function toPolar(Complex memory a) public pure returns (int256, int256) { - int256 r = r2(a.re, a.im); + int256 r = r2(a.re, a.im); // not returning desirable output during fuzzing + // int256 r = (a.re * a.re + a.im * a.im).sqrt() / 1e9; // Fuzzing test were passing when devided by 1e9 + //int BdivA = re / im; if (r > 0) { // im/re or re/im ?? diff --git a/test/Complex.t.sol b/test/Complex.t.sol index 32cb28b..ee0b057 100644 --- a/test/Complex.t.sol +++ b/test/Complex.t.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.15; import "foundry-huff/HuffDeployer.sol"; import "forge-std/Test.sol"; import "forge-std/console.sol"; +import "../src/complexMath/Complex.sol"; +import {PRBMathSD59x18} from "../src/complexMath/prbMath/PRBMathSD59x18.sol"; contract ComplexTest is Test { WRAPPER public complex; @@ -34,13 +36,13 @@ contract ComplexTest is Test { function testDivZ() public { (int256 r, int256 i) = complex.divz(7 * scale, 1 * scale, 5 * scale, 2 * scale); - assertEq((r * 10) / scale, 2); // 17/74 - assertEq((i * 10) / scale, -1); // -8/74 + assertEq(r, 229729729729729729); // (17/74 = 0.229729729729729729..) + assertEq(i, -121621621621621621); // (-9/74=-0.121621621621621621) } function testCalcR() public { uint256 r = complex.calcR(4 * scale, 4 * scale); - assertEq(r / uint256(scale), 5); + assertEq(r, 5656854249492380195); // root(32)=5.656854249492380195 } function testToPolar() public { @@ -75,8 +77,8 @@ contract ComplexTest is Test { function testLnZ() public { (int256 r, int256 i) = complex.ln(30 * scale, 40 * scale); - assertEq(r * 100 / scale, 391); // ln(50) = 3.912.. - assertEq(i * 100 / scale, 65); + assertEq((r * 100) / scale, 391); // ln(50) = 3.912.. + assertEq((i * 100) / scale, 65); } function testAtan2() public { @@ -88,6 +90,198 @@ contract ComplexTest is Test { int256 r = complex.atan1to1(9 * 1e17); assertEq((r * 100) / scale, 73); } + + // bi,ai,br,ar + // We can say the Addition function is correct if + // it follows the properties of addition of complex numbers + // i.e. Closure,Commutative,Associative, + // Existance of Additive Identity and Additive Inverse + function testAddZFuzz(int256 bi, int256 ai, int256 ci, int256 br, int256 ar, int256 cr) public { + //bounded input to avoid underflow or overflow + bi = bound(bi, -1e40, 1e40); + ai = bound(ai, -1e40, 1e40); + ci = bound(ci, -1e40, 1e40); + br = bound(br, -1e40, 1e40); + ar = bound(ar, -1e40, 1e40); + cr = bound(cr, -1e40, 1e40); + (int256 complexA_Br, int256 complexA_Bi) = (complex.addz(bi, ai, br, ar)); //A+B + (int256 complexB_Ar, int256 complexB_Ai) = (complex.addz(ai, bi, ar, br)); //B+A + (int256 complexB_Cr, int256 complexB_Ci) = (complex.addz(ci, bi, cr, br)); //B+C + + //Commutative A + B = B + A + assertEq(complexA_Br, complexB_Ar); + assertEq(complexA_Bi, complexB_Ai); + + //Used same variables to avoid stacktoodeep error + (complexA_Br, complexA_Bi) = (complex.addz(ci, complexA_Bi, cr, complexA_Br)); //(A+B)+C + (complexB_Cr, complexB_Ci) = (complex.addz(complexB_Ci, ai, complexB_Cr, ar)); //A+(B+c) + //Associative (A+B)+C = A+(B+C) + assertEq(complexA_Br, complexB_Cr); + assertEq(complexA_Bi, complexB_Ci); + + (complexA_Br, complexA_Bi) = (complex.addz(0, ai, 0, ar)); //A + 0 + (complexB_Cr, complexB_Ci) = (complex.addz(-(ai), ai, -(ar), ar)); //A + (-A) + // Existance of additive identity A+0=A + assertEq(complexA_Br, ar); + assertEq(complexA_Bi, ai); + //Existance of additive inverse A + (-A)=0 + assertEq(complexB_Cr, 0); + assertEq(complexB_Ci, 0); + } + + function testSubZFuzz(int256 bi, int256 ai, int256 ci, int256 br, int256 ar, int256 cr) public { + //bounded input to avoid underflow or overflow + vm.assume(ai != bi); + vm.assume(bi != ci); + vm.assume(ai != ci); + vm.assume(ar != br); + vm.assume(br != cr); + vm.assume(ar != cr); + vm.assume(bi != 0); + vm.assume(ai != 0); + vm.assume(ci != 0); + vm.assume(ar != 0); + vm.assume(br != 0); + vm.assume(cr != 0); + bi = bound(bi, -1e40, 1e40); + ai = bound(ai, -1e40, 1e40); + ci = bound(ci, -1e40, 1e40); + br = bound(br, -1e40, 1e40); + ar = bound(ar, -1e40, 1e40); + cr = bound(cr, -1e40, 1e40); + + (int256 complexA_Br, int256 complexA_Bi) = (complex.subz(bi, ai, br, ar)); //A-B + (int256 complexB_Ar, int256 complexB_Ai) = (complex.subz(ai, bi, ar, br)); //B-A + (int256 complexB_Cr, int256 complexB_Ci) = (complex.subz(ci, bi, cr, br)); //B-C + //Commutative A - B != B - A + assertFalse(complexA_Br == complexB_Ar); + assertFalse(complexA_Bi == complexB_Ai); + + (complexA_Br, complexA_Bi) = (complex.subz(ci, complexA_Bi, cr, complexA_Br)); //(A-B)-C + (complexB_Ar, complexB_Ai) = (complex.subz(complexB_Ci, ai, complexB_Cr, ar)); //A-(B-c) + //Associative (A-B)-C != A-(B-C) + assertFalse(complexA_Br == complexB_Ar); + assertFalse(complexA_Bi == complexB_Ai); + + (complexA_Br, complexA_Bi) = (complex.subz(0, ai, 0, ar)); //A - 0 + // Existance of additive identity A-0=A + assertEq(complexA_Br, ar); + assertEq(complexA_Bi, ai); + } + + function testMulZFuzz(int256 bi, int256 ai, int256 ci, int256 br, int256 ar, int256 cr) public { + //bounded input to avoid underflow or overflow + vm.assume(ai != bi); + vm.assume(bi != ci); + vm.assume(ai != ci); + vm.assume(ar != br); + vm.assume(br != cr); + vm.assume(ar != cr); + vm.assume(bi != -1); + vm.assume(ai != -1); + vm.assume(ci != -1); + vm.assume(ar != -1); + vm.assume(br != -1); + vm.assume(cr != -1); + vm.assume(bi != 1); + vm.assume(ai != 1); + vm.assume(ci != 1); + vm.assume(ar != 1); + vm.assume(br != 1); + vm.assume(cr != 1); + vm.assume(bi != 0); + vm.assume(ai != 0); + vm.assume(ci != 0); + vm.assume(ar != 0); + vm.assume(br != 0); + vm.assume(cr != 0); + bi = bound(bi, -1e10, 1e10); + ai = bound(ai, -1e10, 1e10); + ci = bound(ci, -1e10, 1e10); + br = bound(br, -1e10, 1e10); + ar = bound(ar, -1e10, 1e10); + cr = bound(cr, -1e10, 1e10); + + (int256 complexA_Br, int256 complexA_Bi) = (complex.mulz(bi, ai, br, ar)); //A*B + (int256 complexB_Ar, int256 complexB_Ai) = (complex.mulz(ai, bi, ar, br)); //B*A + (int256 complexB_Cr, int256 complexB_Ci) = (complex.mulz(ci, bi, cr, br)); //B*C + (int256 complexA_Cr, int256 complexA_Ci) = (complex.mulz(ci, ai, cr, ar)); //A*C + //Commutative A*B = B*A + assertEq(complexA_Br, complexB_Ar); + assertEq(complexA_Bi, complexB_Ai); + + (complexA_Br, complexA_Bi) = (complex.mulz(ci, complexA_Bi, cr, complexA_Br)); //(AB)*C + (complexB_Ar, complexB_Ai) = (complex.mulz(complexB_Ci, ai, complexB_Cr, ar)); //A*(BC) + //Associative (AB)C=A(BC) + assertEq(complexA_Br, complexB_Ar); + assertEq(complexA_Bi, complexB_Ai); + + (complexA_Br, complexA_Bi) = (complex.addz(bi, ai, br, ar)); //A+B + (complexA_Br, complexA_Bi) = (complex.mulz(ci, complexA_Bi, cr, complexA_Br)); //(A+B)*C + (complexB_Ar, complexB_Ai) = (complex.addz(complexB_Ci, complexA_Ci, complexB_Cr, complexA_Cr)); // AC+BC + //Distributive (A+B)*C=AC+BC + assertEq(complexA_Br, complexB_Ar); + assertEq(complexA_Bi, complexB_Ai); + + (complexA_Br, complexA_Bi) = (complex.mulz(0, ai, 1, ar)); //A*1 + // Existance of additive identity A*1=A + assertEq(complexA_Br, ar); + assertEq(complexA_Bi, ai); + + (complexA_Br, complexA_Bi) = (complex.divz(ai * scale, 0 * scale, ar * scale, 1 * scale)); // 1/A + (complexA_Br, complexA_Bi) = (complex.mulz(complexA_Bi, ai, complexA_Br, ar)); // (1/A)*A + //Existance of additive inverse A*(1/A)= 1 + assertEq(complexA_Bi / scale, 0); + assertApproxEqAbs(complexA_Br * 10 / scale, 10, 5); + } + + // devision is basically and multiplication of complex numbers is tested above + function testDivZFuzz(int256 ar, int256 ai, int256 br, int256 bi) public { + vm.assume(ai != 0); + vm.assume(bi != 0); + vm.assume(ar != 0); + vm.assume(br != 0); + ai = bound(ai, -1e10, 1e10); + bi = bound(bi, -1e10, 1e10); + ar = bound(ar, -1e10, 1e10); + br = bound(br, -1e10, 1e10); + (int256 Nr, int256 Ni) = (complex.mulz(-bi, ai, br, ar)); + (int256 Dr,) = (complex.mulz(-bi, bi, br, br)); + int256 Rr = PRBMathSD59x18.div(Nr, Dr); + int256 Ri = PRBMathSD59x18.div(Ni, Dr); + (int256 Rhr, int256 Rhi) = (complex.divz(bi, ai, br, ar)); + assertEq(Rr, Rhr); + assertEq(Ri, Rhi); + } + + // function testCalc_RFuzz(int256 ar, int256 ai, int256 br, int256 bi, int256 k) public { + // ai = bound(ai, -1e9, 1e9); + // bi = bound(bi, -1e9, 1e9); + // ar = bound(ar, -1e9, 1e9); + // br = bound(br, -1e9, 1e9); + // k = bound(k, -1e9, 1e9); + + // (int256 mag1r,) = (complex.mulz(-ai, ai, ar, ar)); // ar^2 + ai^2 + // int256 mag1 = PRBMathSD59x18.sqrt(mag1r); // (ar^2+ai^2)^0.5 + // uint256 R1 = (complex.calcR(ai, ar)); // magnitude(A) + // uint256 R2 = (complex.calcR(bi, br)); // magnitude(B) + // (int256 A_Br, int256 A_Bi) = (complex.addz(bi, ai, br, ai)); // A+B + // uint256 R3 = (complex.calcR(A_Bi, A_Br)); // magnitude(A+B) + // uint256 R4 = (complex.calcR(k * ai, k * ar)); // magnitude(k*A) + // uint256 magk = (complex.calcR(0, k)); + // uint256 _R1 = (complex.calcR(-ai, ar)); + + // // Test by comparing + // assertEq(uint256(mag1) / 1e9, R1); + + // // Test by property + // // mag(A+B)<=mag(A)+mag(B) + // assert(R3 <= (R1 + R2)); + // // mag(kA)=kmag(A) + // assertEq(R4,(magk)*R1); + // // mag(A)=mag(A') + // assertEq(R1,_R1); + // } } interface WRAPPER {