Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzz test by using properties #28

Merged
merged 7 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions src/ComplexHuff/Complex.huff
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand All @@ -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,..]
Expand Down Expand Up @@ -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)]
Expand Down
12 changes: 8 additions & 4 deletions src/complexMath/Complex.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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 ??
Expand Down
204 changes: 199 additions & 5 deletions test/Complex.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
Loading