Skip to content
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
416 changes: 208 additions & 208 deletions .gas-snapshot

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/rain.interpreter.interface
2 changes: 1 addition & 1 deletion src/generated/Rainterpreter.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x12101f163b9f03997261cb8dbba0d24350308e2583d2456ffecf3761813ca62a);
bytes32 constant BYTECODE_HASH = bytes32(0xdd79e1c7cbdede5eab86e9ebc7f0753e183f110d6b88bce72daf19beef42d9cf);

/// @dev The function pointers known to the interpreter for dynamic dispatch.
/// By setting these as a constant they can be inlined into the interpreter
Expand Down
2 changes: 1 addition & 1 deletion src/generated/RainterpreterParser.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x994bd986f9fbac0f25488fe3a5ea2c7d039d0f9465e2db37c9321a046a74ed8f);
bytes32 constant BYTECODE_HASH = bytes32(0xfd32658ad9d8f388f2a1d28c608df58461cf38dafbd22f029594a5d069d1295c);

/// @dev The parse meta that is used to lookup word definitions.
/// The structure of the parse meta is:
Expand Down
2 changes: 1 addition & 1 deletion src/generated/RainterpreterReferenceExtern.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x921c56ace3b8670c36bba13dc2e7ae2b002283ebbb87947f147780114604ba73);
bytes32 constant BYTECODE_HASH = bytes32(0xb1f8988baacdb0efa30560d8b2bf6b5fd83d5f68c91fc1adad86ca2d153e5919);

/// @dev The hash of the meta that describes the contract.
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0xadf71693c6ecf3fd560904bc46973d1b6e651440d15366673f9b3984749e7c16);
Expand Down
8 changes: 4 additions & 4 deletions test/src/lib/op/math/LibOpAdd.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ contract LibOpAddTest is OpTest {
}

/// Test the eval of `add` opcode parsed from a string. Tests two inputs.
function testOpAddEval2InputsHappy() external view {
function testOpAddEval2InputsHappyExamples() external view {
checkHappy("_: add(5 6);", Float.unwrap(LibDecimalFloat.packLossless(11e66, -66)), "5 + 6");
checkHappy("_: add(6 5);", Float.unwrap(LibDecimalFloat.packLossless(11e66, -66)), "6 + 5");

Expand All @@ -89,8 +89,8 @@ contract LibOpAddTest is OpTest {
checkHappy("_: add(6 -5);", Float.unwrap(LibDecimalFloat.packLossless(1e67, -67)), "6 + -5");

// Mixed-sign cancellation to zero should canonicalize to the zero encoding.
checkHappy("_: add(5 -5);", Float.unwrap(LibDecimalFloat.packLossless(0, -75)), "5 + -5");
checkHappy("_: add(-5 5);", Float.unwrap(LibDecimalFloat.packLossless(0, -75)), "-5 + 5");
checkHappy("_: add(5 -5);", Float.unwrap(LibDecimalFloat.packLossless(0, -76)), "5 + -5");
checkHappy("_: add(-5 5);", Float.unwrap(LibDecimalFloat.packLossless(0, -76)), "-5 + 5");
}
Comment thread
thedavidmeister marked this conversation as resolved.

/// Test the eval of `add` opcode parsed from a string. Tests two inputs.
Expand Down Expand Up @@ -131,7 +131,7 @@ contract LibOpAddTest is OpTest {
checkHappy("_: add(7 6 5);", Float.unwrap(LibDecimalFloat.packLossless(18e65, -65)), "7 + 6 + 5");
checkHappy("_: add(5 7 6);", Float.unwrap(LibDecimalFloat.packLossless(18e65, -65)), "5 + 7 + 6");
checkHappy("_: add(7 5 6);", Float.unwrap(LibDecimalFloat.packLossless(18e65, -65)), "7 + 5 + 6");
checkHappy("_: add(5 -6 1);", Float.unwrap(LibDecimalFloat.packLossless(0, -75)), "5 + -6 + 1");
checkHappy("_: add(5 -6 1);", Float.unwrap(LibDecimalFloat.packLossless(0, -76)), "5 + -6 + 1");
}

/// Test the eval of `add` opcode parsed from a string. Tests three inputs.
Expand Down
20 changes: 10 additions & 10 deletions test/src/lib/op/math/LibOpAvg.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ contract LibOpAvgTest is OpTest {
}

/// Test the eval of `avg`.
function testOpAvgEval() external view {
checkHappy("_: avg(0 0);", Float.unwrap(LibDecimalFloat.packLossless(0, -1)), "0 0");
checkHappy("_: avg(0 1);", Float.unwrap(LibDecimalFloat.packLossless(5e37, -38)), "0 1");
checkHappy("_: avg(1 0);", Float.unwrap(LibDecimalFloat.packLossless(5e37, -38)), "1 0");
checkHappy("_: avg(1 1);", Float.unwrap(LibDecimalFloat.packLossless(1e38, -38)), "1 1");
checkHappy("_: avg(1 2);", Float.unwrap(LibDecimalFloat.packLossless(15e37, -38)), "1 2");
checkHappy("_: avg(2 2);", Float.unwrap(LibDecimalFloat.packLossless(2e38, -38)), "2 2");
checkHappy("_: avg(2 3);", Float.unwrap(LibDecimalFloat.packLossless(25e37, -38)), "2 3");
checkHappy("_: avg(2 4);", Float.unwrap(LibDecimalFloat.packLossless(3e38, -38)), "2 4");
checkHappy("_: avg(4 0.5);", Float.unwrap(LibDecimalFloat.packLossless(225e36, -38)), "4 5");
function testOpAvgEvalExamples() external view {
checkHappy("_: avg(0 0);", Float.unwrap(LibDecimalFloat.packLossless(0, 0)), "0 0");
checkHappy("_: avg(0 1);", Float.unwrap(LibDecimalFloat.packLossless(5e66, -67)), "0 1");
checkHappy("_: avg(1 0);", Float.unwrap(LibDecimalFloat.packLossless(5e66, -67)), "1 0");
checkHappy("_: avg(1 1);", Float.unwrap(LibDecimalFloat.packLossless(1e67, -67)), "1 1");
checkHappy("_: avg(1 2);", Float.unwrap(LibDecimalFloat.packLossless(1.5e66, -66)), "1 2");
checkHappy("_: avg(2 2);", Float.unwrap(LibDecimalFloat.packLossless(2e66, -66)), "2 2");
checkHappy("_: avg(2 3);", Float.unwrap(LibDecimalFloat.packLossless(2.5e66, -66)), "2 3");
checkHappy("_: avg(2 4);", Float.unwrap(LibDecimalFloat.packLossless(3e66, -66)), "2 4");
checkHappy("_: avg(4 0.5);", Float.unwrap(LibDecimalFloat.packLossless(2.25e66, -66)), "4 5");
}
Comment thread
thedavidmeister marked this conversation as resolved.

/// Test the eval of `avg` for bad inputs.
Expand Down
83 changes: 57 additions & 26 deletions test/src/lib/op/math/LibOpDiv.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {LibOpDiv} from "src/lib/op/math/LibOpDiv.sol";
import {LibOperand} from "test/lib/operand/LibOperand.sol";
import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {Float, LibDecimalFloat} from "rain.math.float/lib/LibDecimalFloat.sol";
import {LibDecimalFloatImplementation} from "rain.math.float/lib/implementation/LibDecimalFloatImplementation.sol";
import {
LibDecimalFloatImplementation,
MulDivOverflow
} from "rain.math.float/lib/implementation/LibDecimalFloatImplementation.sol";

contract LibOpDivTest is OpTest {
using LibDecimalFloat for Float;
Expand Down Expand Up @@ -112,63 +115,91 @@ contract LibOpDivTest is OpTest {
/// Tests two inputs.
/// Tests the happy path where we do not divide by zero or overflow.
function testOpDivEvalTwoInputsHappy() external view {
checkHappy("_: div(0 1);", Float.unwrap(LibDecimalFloat.packLossless(0, -1)), "0 1");
checkHappy("_: div(1 1);", Float.unwrap(LibDecimalFloat.packLossless(1e38, -38)), "1 1");
checkHappy("_: div(1 2);", Float.unwrap(LibDecimalFloat.packLossless(5e37, -38)), "1 2");
checkHappy("_: div(2 1);", Float.unwrap(LibDecimalFloat.packLossless(2e38, -38)), "2 1");
checkHappy("_: div(2 2);", Float.unwrap(LibDecimalFloat.packLossless(1e38, -38)), "2 2");
checkHappy("_: div(2 0.1);", Float.unwrap(LibDecimalFloat.packLossless(2e38, -37)), "2 0.1");
// https://github.com/rainlanguage/rain.math.float/issues/71
// checkHappy("_: div(max-positive-value() 1);", Float.unwrap(LibDecimalFloat.packLossless(1, 1)), "max-positive-value() 1");
checkHappy("_: div(0 1);", Float.unwrap(LibDecimalFloat.packLossless(0, 0)), "0 1");
checkHappy("_: div(1 1);", Float.unwrap(LibDecimalFloat.packLossless(1e67, -67)), "1 1");
checkHappy("_: div(1 2);", Float.unwrap(LibDecimalFloat.packLossless(0.5e67, -67)), "1 2");
checkHappy("_: div(2 1);", Float.unwrap(LibDecimalFloat.packLossless(2e66, -66)), "2 1");
checkHappy("_: div(2 2);", Float.unwrap(LibDecimalFloat.packLossless(1e67, -67)), "2 2");
checkHappy("_: div(2 0.1);", Float.unwrap(LibDecimalFloat.packLossless(20e65, -65)), "2 0.1");
checkHappy(
"_: div(max-positive-value() 1);",
Float.unwrap(LibDecimalFloat.FLOAT_MAX_POSITIVE_VALUE),
"max-positive-value() 1"
);
}

/// Test the eval of `div` opcode parsed from a string.
/// Tests two inputs.
/// Tests the unhappy path where we divide by zero.
function testOpDivEvalTwoInputsUnhappy() external {
function testOpDivEvalTwoInputsUnhappyDivZero() external {
checkUnhappy("_: div(0 0);", stdError.divisionError);
checkUnhappy("_: div(1 0);", stdError.divisionError);
checkUnhappy("_: div(max-positive-value() 0);", stdError.divisionError);
checkUnhappy("_: div(1 0);", abi.encodeWithSelector(MulDivOverflow.selector, 1e76, 1e75, 0));
checkUnhappy(
"_: div(max-positive-value() 0);",
abi.encodeWithSelector(
MulDivOverflow.selector,
13479973333575319897333507543509815336818572211270286240551805124607000000000,
1e75,
0
)
);
}
Comment thread
thedavidmeister marked this conversation as resolved.

/// Test the eval of `div` opcode parsed from a string.
/// Tests two inputs.
/// Tests the unhappy path where the final result overflows.
function testOpDivEvalTwoInputsUnhappyOverflow() external {
checkUnhappyOverflow("_: div(max-positive-value() 1e-18);", 134799733335753198973335075435098153360, 2147483694);
checkUnhappyOverflow(
"_: div(max-positive-value() 1e-18);",
13479973333575319897333507543509815336818572211270286240551805124607,
2147483665
);
Comment on lines +152 to +156

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Replace magic numbers with named constants for overflow payloads.

The coefficient and exponent sentinel literals hurt readability and are error-prone.

Example (outside this hunk):

uint256 constant COEFF_MAX = 13479973333575319897333507543509815336818572211270286240551805124607;
int256 constant EXP_OVERFLOW_SENTINEL = 2147483665;

Then:

checkUnhappyOverflow("_: div(max-positive-value() 1e-18);", COEFF_MAX, EXP_OVERFLOW_SENTINEL);
🤖 Prompt for AI Agents
In test/src/lib/op/math/LibOpDiv.t.sol around lines 152 to 156, the literal
coefficient and exponent sentinel values passed into checkUnhappyOverflow are
magic numbers; define descriptive constants (e.g., COEFF_MAX and
EXP_OVERFLOW_SENTINEL) near the top of the test file or in a shared test
constants area and replace the numeric literals in the call with those constants
(update the checkUnhappyOverflow invocation to use COEFF_MAX and
EXP_OVERFLOW_SENTINEL).

// checkUnhappy("_: div(1e52 1e-8);", abi.encodeWithSelector(PRBMath_MulDiv_Overflow.selector, 1e70, 1e18, 1e10));
}

/// Test the eval of `div` opcode parsed from a string.
/// Tests three inputs.
/// Tests the happy path where we do not divide by zero or overflow.
function testOpDivEvalThreeInputsHappy() external view {
checkHappy("_: div(0 1 1);", Float.unwrap(LibDecimalFloat.packLossless(0, -1)), "0 1 1");
checkHappy("_: div(1 1 1);", Float.unwrap(LibDecimalFloat.packLossless(1e38, -38)), "1 1 1");
checkHappy("_: div(1 1 2);", Float.unwrap(LibDecimalFloat.packLossless(5e37, -38)), "1 1 2");
checkHappy("_: div(1 2 1);", Float.unwrap(LibDecimalFloat.packLossless(5e38, -39)), "1 2 1");
checkHappy("_: div(1 2 2);", Float.unwrap(LibDecimalFloat.packLossless(25e37, -39)), "1 2 2");
checkHappy("_: div(1 2 0.1);", Float.unwrap(LibDecimalFloat.packLossless(5e38, -38)), "1 2 0.1");
// https://github.com/rainlanguage/rain.math.float/issues/71
// checkHappy("_: div(max-positive-value() 1 1);", type(uint256).max, "max-positive-value() 1 1");
checkHappy("_: div(0 1 1);", Float.unwrap(LibDecimalFloat.packLossless(0, 0)), "0 1 1");
checkHappy("_: div(1 1 1);", Float.unwrap(LibDecimalFloat.packLossless(1e67, -67)), "1 1 1");
checkHappy("_: div(1 1 2);", Float.unwrap(LibDecimalFloat.packLossless(5e66, -67)), "1 1 2");
checkHappy("_: div(1 2 1);", Float.unwrap(LibDecimalFloat.packLossless(5e66, -67)), "1 2 1");
checkHappy("_: div(1 2 2);", Float.unwrap(LibDecimalFloat.packLossless(0.25e67, -67)), "1 2 2");
checkHappy("_: div(1 2 0.1);", Float.unwrap(LibDecimalFloat.packLossless(5e66, -66)), "1 2 0.1");
checkHappy(
"_: div(max-positive-value() 1 1);",
Float.unwrap(LibDecimalFloat.FLOAT_MAX_POSITIVE_VALUE),
"max-positive-value() 1 1"
);
}

/// Test the eval of `div` opcode parsed from a string.
/// Tests three inputs.
/// Tests the unhappy path where we divide by zero.
function testOpDivEvalThreeInputsUnhappy() external {
function testOpDivEvalThreeInputsUnhappyExamples() external {
checkUnhappy("_: div(0 0 0);", stdError.divisionError);
checkUnhappy("_: div(1 0 0);", stdError.divisionError);
checkUnhappy("_: div(1 1 0);", stdError.divisionError);
checkUnhappy("_: div(max-positive-value() 0 0);", stdError.divisionError);
checkUnhappy("_: div(1 0 0);", abi.encodeWithSelector(MulDivOverflow.selector, 1e76, 1e75, 0));
checkUnhappy("_: div(1 1 0);", abi.encodeWithSelector(MulDivOverflow.selector, 1e76, 1e75, 0));
checkUnhappy(
"_: div(max-positive-value() 0 0);",
abi.encodeWithSelector(
MulDivOverflow.selector,
13479973333575319897333507543509815336818572211270286240551805124607000000000,
1e75,
0
)
);
}
Comment on lines +180 to 193

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

DRY the div-by-zero payload construction.

Consider a small helper that builds the expected MulDivOverflow payload from inputs to avoid repeating long literals and reduce drift if encoding changes again.

🤖 Prompt for AI Agents
In test/src/lib/op/math/LibOpDiv.t.sol around lines 180 to 193, the test repeats
constructing the same abi.encodeWithSelector(MulDivOverflow.selector, a, b, c)
payload multiple times which duplicates long numeric literals; introduce a small
helper function (e.g., expectMulDivOverflowPayload or buildMulDivOverflow) that
takes the three numeric inputs and returns
abi.encodeWithSelector(MulDivOverflow.selector, a, b, c), then replace the
repeated abi.encodeWithSelector calls in these test cases with calls to that
helper to DRY the construction and centralize encoding logic.


/// Test the eval of `div` opcode parsed from a string.
/// Tests three inputs.
/// Tests the unhappy path where the final result overflows.
function testOpDivEvalThreeInputsUnhappyOverflow() external {
checkUnhappyOverflow(
"_: div(max-positive-value() 1e-18 1e-18);", 134799733335753198973335075435098153360, 2147483694
"_: div(max-positive-value() 1e-18 1e-18);",
13479973333575319897333507543509815336818572211270286240551805124607,
2147483665
);
// checkUnhappyOverflow("_: div(1e900000000 1 1e-900000000);", 1, -8000000000000000000000000000);
// checkUnhappy("_: div(1e52 1e-8 1);", abi.encodeWithSelector(PRBMath_MulDiv_Overflow.selector, 1e70, 1e18, 1e10));
Expand Down
26 changes: 21 additions & 5 deletions test/src/lib/op/math/LibOpExp.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,42 @@ contract LibOpExpTest is OpTest {
}

/// Test the eval of `exp`.
function testOpExpEval() external view {
function testOpExpEvalExample() external view {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Broaden fuzz domain to include negative inputs for exp.

Current bound excludes negatives; consider [-10000, 10000] to cover e^x for x < 0.

Example (outside this hunk):

signedCoefficientA = int224(bound(signedCoefficientA, -10000, 10000));
🤖 Prompt for AI Agents
In test/src/lib/op/math/LibOpExp.t.sol around line 40, the fuzz bounds for the
exponent test currently exclude negative inputs; update the bound(s) used for
the exponent/coefficient variables in this test (e.g., signedCoefficientA or
whatever variable is passed to exp) to use a range of -10000 to 10000 instead of
a non-negative-only range, ensuring you re-cast to the original type (e.g.,
int224(bound(var, -10000, 10000))) so negative values are included in the fuzz
domain.

checkHappy("_: exp(0);", Float.unwrap(LibDecimalFloat.packLossless(1, 0)), "e^0");
checkHappy(
"_: exp(1);",
Float.unwrap(LibDecimalFloat.packLossless(2.7182818284590452353602874713526624977e66, -66)),
Float.unwrap(
LibDecimalFloat.packLossless(
2.718281828459045235360287471352662497757247093699959574966967627724e66, -66
)
),
"e^1"
);
checkHappy(
"_: exp(0.5);",
Float.unwrap(LibDecimalFloat.packLossless(1.64864091422952261768014373567633124885e66, -66)),
Float.unwrap(
LibDecimalFloat.packLossless(
1.648640914229522617680143735676331248878623546849979787483483813862e66, -66
)
),
"e^0.5"
);
checkHappy(
"_: exp(2);",
Float.unwrap(LibDecimalFloat.packLossless(7.3901273138361809414411498854106499908e66, -66)),
Float.unwrap(
LibDecimalFloat.packLossless(
7.390127313836180941441149885410649991028988374799838299867870510896e66, -66
)
),
"e^2"
);
checkHappy(
"_: exp(3);",
Float.unwrap(LibDecimalFloat.packLossless(20.088454853771357060808624140579874931e65, -65)),
Float.unwrap(
LibDecimalFloat.packLossless(
20.08845485377135706080862414057987493271741281099878724900902883172e65, -65
)
),
"e^3"
);
}
Expand Down
4 changes: 2 additions & 2 deletions test/src/lib/op/math/LibOpExp2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ contract LibOpExp2Test is OpTest {
}

/// Test the eval of `exp2`.
function testOpExp2Eval() external view {
function testOpExp2EvalExample() external view {
checkHappy("_: exp2(0);", Float.unwrap(LibDecimalFloat.packLossless(1, 0)), "2^0");
checkHappy("_: exp2(1);", Float.unwrap(LibDecimalFloat.packLossless(2000, -3)), "2^1");
checkHappy("_: exp2(1);", Float.unwrap(LibDecimalFloat.packLossless(2, 0)), "2^1");
checkHappy("_: exp2(0.5);", Float.unwrap(LibDecimalFloat.packLossless(1415, -3)), "2^0.5");
checkHappy("_: exp2(2);", Float.unwrap(LibDecimalFloat.packLossless(3999, -3)), "2^2");
checkHappy("_: exp2(3);", Float.unwrap(LibDecimalFloat.packLossless(7998, -3)), "2^3");
Expand Down
12 changes: 8 additions & 4 deletions test/src/lib/op/math/LibOpInv.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,16 @@ contract LibOpInvTest is OpTest {

/// Test the eval of `inv`.
function testOpInvEval() external view {
checkHappy("_: inv(1);", Float.unwrap(LibDecimalFloat.packLossless(1e38, -38)), "1");
checkHappy("_: inv(0.5);", Float.unwrap(LibDecimalFloat.packLossless(2e37, -37)), "0.5");
checkHappy("_: inv(2);", Float.unwrap(LibDecimalFloat.packLossless(0.5e38, -38)), "2");
checkHappy("_: inv(1);", Float.unwrap(LibDecimalFloat.packLossless(1e67, -67)), "1");
checkHappy("_: inv(0.5);", Float.unwrap(LibDecimalFloat.packLossless(2e66, -66)), "0.5");
checkHappy("_: inv(2);", Float.unwrap(LibDecimalFloat.packLossless(0.5e67, -67)), "2");
checkHappy(
"_: inv(3);",
Float.unwrap(LibDecimalFloat.packLossless(0.33333333333333333333333333333333333333e38, -38)),
Float.unwrap(
LibDecimalFloat.packLossless(
0.3333333333333333333333333333333333333333333333333333333333333333333e67, -67
)
),
"3"
);
}
Expand Down
6 changes: 3 additions & 3 deletions test/src/lib/op/math/LibOpPow.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ contract LibOpPowTest is OpTest {
// 1 ^ 0
checkHappy("_: power(1 0);", Float.unwrap(LibDecimalFloat.packLossless(1, 0)), "1 0");
// 1 ^ 1
checkHappy("_: power(1 1);", Float.unwrap(LibDecimalFloat.packLossless(1e3, -3)), "1 1");
checkHappy("_: power(1 1);", Float.unwrap(LibDecimalFloat.packLossless(1, 0)), "1 1");
// 1 ^ 2
checkHappy("_: power(1 2);", Float.unwrap(LibDecimalFloat.packLossless(1e3, -3)), "1 2");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider canonicalizing other exact-1 cases

1^2 still expects 1e3,-3. If the pow path now normalizes exact integers, switch to packLossless(1, 0) for consistency (only if the reference fn returns canonical 1).

Proposed diff:

-        checkHappy("_: power(1 2);", Float.unwrap(LibDecimalFloat.packLossless(1e3, -3)), "1 2");
+        checkHappy("_: power(1 2);", Float.unwrap(LibDecimalFloat.packLossless(1, 0)), "1 2");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
checkHappy("_: power(1 2);", Float.unwrap(LibDecimalFloat.packLossless(1e3, -3)), "1 2");
checkHappy("_: power(1 2);", Float.unwrap(LibDecimalFloat.packLossless(1, 0)), "1 2");
🤖 Prompt for AI Agents
In test/src/lib/op/math/LibOpPow.t.sol around line 58, the test expects
Float.unwrap(LibDecimalFloat.packLossless(1e3, -3)) for the result of power(1 2)
but the pow path may now canonicalize exact integer 1; verify the reference
implementation's return for 1^2 and if it returns the canonical integer 1 update
the expected value to Float.unwrap(LibDecimalFloat.packLossless(1, 0)) instead,
otherwise leave the current expectation; adjust the test assertion accordingly
to match the reference function's canonicalization.

// 2 ^ 2
Expand All @@ -70,9 +70,9 @@ contract LibOpPowTest is OpTest {

function testOpPowNegativeBaseError() external {
// Negative base with positive exponent.
checkUnhappy("_: power(-1 2);", abi.encodeWithSelector(Log10Negative.selector, -1e37, -37));
checkUnhappy("_: power(-1 2);", abi.encodeWithSelector(Log10Negative.selector, -1, 0));
// Negative base with negative exponent.
checkUnhappy("_: power(-1 -2);", abi.encodeWithSelector(Log10Negative.selector, -1e37, -37));
checkUnhappy("_: power(-1 -2);", abi.encodeWithSelector(Log10Negative.selector, -1, 0));
}
Comment on lines 71 to 76

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add fractional-negative-base unhappy example

Add power(-4 0.5) unhappy to lock in the log-domain error behaviour for non-integer exponents with negative bases.

Proposed snippet (within this test):

+        // Negative base with fractional exponent (domain error).
+        checkUnhappy("_: power(-4 0.5);", abi.encodeWithSelector(Log10Negative.selector, -4, 0));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function testOpPowNegativeBaseError() external {
// Negative base with positive exponent.
checkUnhappy("_: power(-1 2);", abi.encodeWithSelector(Log10Negative.selector, -1e37, -37));
checkUnhappy("_: power(-1 2);", abi.encodeWithSelector(Log10Negative.selector, -1, 0));
// Negative base with negative exponent.
checkUnhappy("_: power(-1 -2);", abi.encodeWithSelector(Log10Negative.selector, -1e37, -37));
checkUnhappy("_: power(-1 -2);", abi.encodeWithSelector(Log10Negative.selector, -1, 0));
}
function testOpPowNegativeBaseError() external {
// Negative base with positive exponent.
checkUnhappy("_: power(-1 2);", abi.encodeWithSelector(Log10Negative.selector, -1, 0));
// Negative base with negative exponent.
checkUnhappy("_: power(-1 -2);", abi.encodeWithSelector(Log10Negative.selector, -1, 0));
// Negative base with fractional exponent (domain error).
checkUnhappy("_: power(-4 0.5);", abi.encodeWithSelector(Log10Negative.selector, -4, 0));
}
🤖 Prompt for AI Agents
In test/src/lib/op/math/LibOpPow.t.sol around lines 71 to 76, add an unhappy
test asserting that a negative base with a fractional exponent triggers the
log-domain error: insert a new checkUnhappy call inside
testOpPowNegativeBaseError, e.g. call checkUnhappy("_: power(-4 0.5);",
abi.encodeWithSelector(Log10Negative.selector, -4, 0)); so the test covers
non-integer exponents with negative bases and locks in the expected
Log10Negative behaviour.


/// Test the eval of `power` for bad inputs.
Expand Down
6 changes: 4 additions & 2 deletions test/src/lib/op/math/LibOpSqrt.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ contract LibOpSqrtTest is OpTest {
}

/// Test the eval of `sqrt`.
function testOpSqrtEval() external view {
function testOpSqrtEvalExamples() external view {
checkHappy("_: sqrt(0);", 0, "0");
checkHappy("_: sqrt(1);", Float.unwrap(LibDecimalFloat.packLossless(1e3, -3)), "1");
checkHappy(
"_: sqrt(0.5);",
Float.unwrap(LibDecimalFloat.packLossless(70671378091872791519434628975265017667, -38)),
Float.unwrap(
LibDecimalFloat.packLossless(7067137809187279151943462897526501766784452296819787985865724381625, -67)
),
Comment on lines +44 to +46

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Verify sqrt(0.5) canonical encoding

The packed value for sqrt(0.5) changed to a very long mantissa with exponent -67. Please double-check this is the canonical packLossless encoding produced by the parser + evaluator for 0.5 to avoid future drift. Consider adding a brief comment noting how this constant was derived.

🤖 Prompt for AI Agents
In test/src/lib/op/math/LibOpSqrt.t.sol around lines 44-46, the packed constant
used for sqrt(0.5) appears to be an unexpectedly long mantissa with exponent
-67; verify this is the canonical packLossless encoding produced by the parser +
evaluator for 0.5 by re-running the same parser/evaluator that produced other
constants and comparing outputs, update the packed tuple to the canonical value
if it differs, and add a brief inline comment explaining how the
constant<codegen_instructions>
In test/src/lib/op/math/LibOpSqrt.t.sol around lines 44-46, the packed constant
used for sqrt(0.5) appears to be an unexpectedly long mantissa with exponent
-67; re-run the parser+evaluator that generates packLossless encodings for
literals to confirm the canonical mantissa/exponent pair for sqrt(0.5), update
the literal here to match that canonical output if it differs, and add a short
inline comment indicating the constant was generated by the parser+evaluator
(include the command or script used) so future changes can be validated the same
way.

"0.5"
);
checkHappy("_: sqrt(2);", Float.unwrap(LibDecimalFloat.packLossless(1415, -3)), "2");
Expand Down
Loading
Loading