diff --git a/src/generated/Rainterpreter.pointers.sol b/src/generated/Rainterpreter.pointers.sol index aa4cea960..5c392692d 100644 --- a/src/generated/Rainterpreter.pointers.sol +++ b/src/generated/Rainterpreter.pointers.sol @@ -10,11 +10,11 @@ pragma solidity =0.8.25; /// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0x74f4ab876f43b52d924dd67b52b3ca05890425f855a8a777e9814f4246011d05); +bytes32 constant BYTECODE_HASH = bytes32(0xd3ec06768e5585a39fad965972bb18e6cee56474fc8437c0fb372c58b807a14f); /// @dev The function pointers known to the interpreter for dynamic dispatch. /// By setting these as a constant they can be inlined into the interpreter /// and loaded at eval time for very low gas (~100) due to the compiler /// optimising it to a single `codecopy` to build the in memory bytes array. bytes constant OPCODE_FUNCTION_POINTERS = - hex"08050837085b09e70ab00ac20ad40aed0b110b450b560b670c090c280ce60d960e1a0f5c108f0ce61188123a12dc1354136513761376138713f214fd157c159515a916081621163a1673169e16b716d016f7170a176c17ba1808185618a418f219401971197f198d19db1a0c1a5a1a8b1ad91b271c1d"; + hex"080e0840086409f00ab90acb0add0af60b1a0b4e0b5f0b700c120c310cef0d9f0e230f6510980cef1191124312e5135d136e137f137f139013fb15061585159e15b21611162a1643167c16a716c016d917001713177517c31811185f18ad18fb1949197a1988199619a419f21a231a711aa21af01b3e1c34"; diff --git a/src/generated/RainterpreterExpressionDeployer.pointers.sol b/src/generated/RainterpreterExpressionDeployer.pointers.sol index ee23b9b35..9bfd37368 100644 --- a/src/generated/RainterpreterExpressionDeployer.pointers.sol +++ b/src/generated/RainterpreterExpressionDeployer.pointers.sol @@ -10,11 +10,11 @@ pragma solidity =0.8.25; /// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0xbed2d4ac5d5461f524b74c1685829181a4869af0e88d8acce1c56f4fa26b53b5); +bytes32 constant BYTECODE_HASH = bytes32(0xab56a079610ef653607393f6c76826a1388e7a6f796f716bc49abd7f63a33252); /// @dev The hash of the meta that describes the contract. -bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x87c8570956bb61fd43b455e10f4e3bc363513a130c0a37907112aa2957795480); +bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x64a9740f561b5caf83b3f1baf257c585309c9e6a0544bc9610a2a77e440cc96c); /// @dev The function pointers for the integrity check fns. bytes constant INTEGRITY_FUNCTION_POINTERS = - hex"0e900f0e0f7210ec10f610f611001109112411ca11ca1226129e12ab10f6110012ab10f6110010f610f610f6110010ec10ec10ec10ec12b512da12f410f610f612b510f610f612ab110010f610f612ab10ec12fe12fe12fe12fe12fe12fe131810ec1100110012fe10ec12fe10ec13181318110012f4"; + hex"0e980f160f7a10f410fe10fe11081111112c11d211d2122e12a612b310fe110812b310fe110810fe10fe10fe110810f410f410f410f412bd12e212fc10fe10fe12bd10fe10fe12b3110810fe10fe12b310f4130613061306130613061306132010f4110811081108130610f4130610f413201320110812fc"; diff --git a/src/generated/RainterpreterParser.pointers.sol b/src/generated/RainterpreterParser.pointers.sol index ea4324ce8..055809d84 100644 --- a/src/generated/RainterpreterParser.pointers.sol +++ b/src/generated/RainterpreterParser.pointers.sol @@ -10,7 +10,7 @@ pragma solidity =0.8.25; /// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0x5394e29197b822127aea4c685666b83280e3d4d2e05754107709bec524e5cb32); +bytes32 constant BYTECODE_HASH = bytes32(0xdb81993338674b138f59a4f8052a89f19ab1c45693590bce1b4233f0222010fa); /// @dev The parse meta that is used to lookup word definitions. /// The structure of the parse meta is: @@ -29,7 +29,7 @@ bytes32 constant BYTECODE_HASH = bytes32(0x5394e29197b822127aea4c685666b83280e3d /// bit count of the previous bloom filter. If we reach the end of the bloom /// filters then we have a miss. bytes constant PARSE_META = - hex"02120580000068a0a65201249030011040048204660b2023215058008c06080120210000000000000000000000000000000000080000000000000000000000000000402d8199df009d67d22c1d0f37036e3e2e0e7d8c5707d7fdec21100100143de58736ae590b3905464e09fa0f6d29e133af31ac46712e06de8018f697c502a7d9d3259988671deafd991c6afdc512aa165d057d9d9d19be782a30f7cb9a0f11ccad2a42ee8e267c88391191db7333ac16ad0a15ce750b56f94f0464fdb31ba3783a100f9a7f1ed9827e0c53405c3522c7531a79fcd03a90ed6506c827b92f19ae1324e4865308f7e23532ad38972b2a49ad1f17f5b838bb726f2881231d34e365890d1d863d0150056620b2eeff2772e4921742d7773722824116f1989e2354b15415a57077135ab9fa22782c84"; + hex"02120580000068a0a65201249030051040048204660b2023215058008c06080120210000000000000000000000000000000000080000000000000000000000000000402d8199df009d67d22c1d0f37036e3e2e0e7d8c5707d7fdec21100100143de58737ae590b3a05464e09fa0f6d29e133af32ac46712e06de8018f697c502a7d9d3259988671deafd991c6afdc512aa165d057d9d9d19be782a30f7cb9a0f11ccad2a42ee8e267c88391191db7334ac16ad0a15ce750b56f94f0464fdb31ba3783a100f9a7f1ed9827e0c53405c311d716a3622c7531a79fcd03b90ed6506c827b92f19ae1324e4865308f7e23533ad38972b2a49ad1f17f5b839bb726f2881231d35e365890d1d863d0150056620b2eeff2772e4921742d7773822824116f1989e2354b15415a57077135ab9fa22782c84"; /// @dev The build depth of the parser meta. @@ -39,11 +39,11 @@ uint8 constant PARSE_META_BUILD_DEPTH = 2; /// These positional indexes all map to the same indexes looked up in the parse /// meta. bytes constant OPERAND_HANDLER_FUNCTION_POINTERS = - hex"1a751a751a751b4a1c611c611c611b4a1b4a1a751a751a751c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611c611a751c611c61"; + hex"1a7d1a7d1a7d1b521c691c691c691b521b521a7d1a7d1a7d1c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691c691a7d1c691c69"; /// @dev Every two bytes is a function pointer for a literal parser. /// Literal dispatches are determined by the first byte(s) of the literal /// rather than a full word lookup, and are done with simple conditional /// jumps as the possibilities are limited compared to the number of words we /// have. -bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"15bd17ef183218d0"; +bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"15c517f7183a18d8"; diff --git a/src/lib/op/LibAllStandardOps.sol b/src/lib/op/LibAllStandardOps.sol index a27379832..ec62f6a34 100644 --- a/src/lib/op/LibAllStandardOps.sol +++ b/src/lib/op/LibAllStandardOps.sol @@ -77,7 +77,7 @@ import {LibOpDiv} from "./math/LibOpDiv.sol"; import {LibOpE} from "./math/LibOpE.sol"; // import {LibOpExp} from "./math/LibOpExp.sol"; // import {LibOpExp2} from "./math/LibOpExp2.sol"; -// import {LibOpFloor} from "./math/LibOpFloor.sol"; +import {LibOpFloor} from "./math/LibOpFloor.sol"; import {LibOpFrac} from "./math/LibOpFrac.sol"; // import {LibOpGm} from "./math/LibOpGm.sol"; // import {LibOpHeadroom} from "./math/LibOpHeadroom.sol"; @@ -109,7 +109,7 @@ import {LibParseLiteralHex} from "../parse/literal/LibParseLiteralHex.sol"; import {LibParseLiteralSubParseable} from "../parse/literal/LibParseLiteralSubParseable.sol"; /// @dev Number of ops currently provided by `AllStandardOps`. -uint256 constant ALL_STANDARD_OPS_LENGTH = 59; +uint256 constant ALL_STANDARD_OPS_LENGTH = 60; /// @title LibAllStandardOps /// @notice Every opcode available from the core repository laid out as a single @@ -271,7 +271,7 @@ library LibAllStandardOps { AuthoringMetaV2("e", "The mathematical constant e."), // AuthoringMetaV2("exp", "Natural exponential e^x. Errors if the exponentiation exceeds `max-value()`."), // AuthoringMetaV2("exp2", "Binary exponential 2^x where x. Errors if the exponentiation exceeds `max-value()`."), - // AuthoringMetaV2("floor", "Floor of a number."), + AuthoringMetaV2("floor", "Floor of a number."), AuthoringMetaV2("frac", "Fractional part of a number."), // AuthoringMetaV2("gm", "Geometric mean of all numbers. Errors if any number is zero."), // AuthoringMetaV2( @@ -465,8 +465,8 @@ library LibAllStandardOps { // LibParseOperand.handleOperandDisallowed, // // exp2 // LibParseOperand.handleOperandDisallowed, - // // floor - // LibParseOperand.handleOperandDisallowed, + // floor + LibParseOperand.handleOperandDisallowed, // frac LibParseOperand.handleOperandDisallowed, // // gm @@ -590,7 +590,7 @@ library LibAllStandardOps { LibOpE.integrity, // LibOpExp.integrity, // LibOpExp2.integrity, - // LibOpFloor.integrity, + LibOpFloor.integrity, LibOpFrac.integrity, // LibOpGm.integrity, // LibOpHeadroom.integrity, @@ -698,7 +698,7 @@ library LibAllStandardOps { LibOpE.run, // LibOpExp.run, // LibOpExp2.run, - // LibOpFloor.run, + LibOpFloor.run, LibOpFrac.run, // LibOpGm.run, // LibOpHeadroom.run, diff --git a/src/lib/op/math/LibOpFloor.sol b/src/lib/op/math/LibOpFloor.sol index e291dae7f..6743b8cfd 100644 --- a/src/lib/op/math/LibOpFloor.sol +++ b/src/lib/op/math/LibOpFloor.sol @@ -1,43 +1,46 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -// import {UD60x18, floor} from "prb-math/UD60x18.sol"; -// import {OperandV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; -// import {Pointer} from "rain.solmem/lib/LibPointer.sol"; -// import {InterpreterState} from "../../state/LibInterpreterState.sol"; -// import {IntegrityCheckState} from "../../integrity/LibIntegrityCheck.sol"; +import {OperandV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; +import {Pointer} from "rain.solmem/lib/LibPointer.sol"; +import {InterpreterState} from "../../state/LibInterpreterState.sol"; +import {IntegrityCheckState} from "../../integrity/LibIntegrityCheck.sol"; +import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; +import {Float, LibDecimalFloat} from "rain.math.float/lib/LibDecimalFloat.sol"; -// /// @title LibOpFloor -// /// @notice Opcode for the floor of an decimal 18 fixed point number. -// library LibOpFloor { -// function integrity(IntegrityCheckState memory, Operand) internal pure returns (uint256, uint256) { -// // There must be one input and one output. -// return (1, 1); -// } +/// @title LibOpFloor +/// @notice Opcode for the floor of an decimal floating point number. +library LibOpFloor { + using LibDecimalFloat for Float; -// /// floor -// /// 18 decimal fixed point floor of a number. -// function run(InterpreterState memory, Operand, Pointer stackTop) internal pure returns (Pointer) { -// uint256 a; -// assembly ("memory-safe") { -// a := mload(stackTop) -// } -// a = UD60x18.unwrap(floor(UD60x18.wrap(a))); + function integrity(IntegrityCheckState memory, OperandV2) internal pure returns (uint256, uint256) { + // There must be one input and one output. + return (1, 1); + } -// assembly ("memory-safe") { -// mstore(stackTop, a) -// } -// return stackTop; -// } + /// floor + /// decimal floating point floor of a number. + function run(InterpreterState memory, OperandV2, Pointer stackTop) internal pure returns (Pointer) { + Float a; + assembly ("memory-safe") { + a := mload(stackTop) + } + a = a.floor(); -// /// Gas intensive reference implementation of floor for testing. -// function referenceFn(InterpreterState memory, Operand, uint256[] memory inputs) -// internal -// pure -// returns (uint256[] memory) -// { -// uint256[] memory outputs = new uint256[](1); -// outputs[0] = UD60x18.unwrap(floor(UD60x18.wrap(inputs[0]))); -// return outputs; -// } -// } + assembly ("memory-safe") { + mstore(stackTop, a) + } + return stackTop; + } + + /// Gas intensive reference implementation of floor for testing. + function referenceFn(InterpreterState memory, OperandV2, StackItem[] memory inputs) + internal + pure + returns (StackItem[] memory) + { + StackItem[] memory outputs = new StackItem[](1); + outputs[0] = StackItem.wrap(Float.unwrap(Float.wrap(StackItem.unwrap(inputs[0])).floor())); + return outputs; + } +} diff --git a/test/src/lib/op/logic/LibOpConditions.t.sol b/test/src/lib/op/logic/LibOpConditions.t.sol index 27a1e5d36..42d0dd6b0 100644 --- a/test/src/lib/op/logic/LibOpConditions.t.sol +++ b/test/src/lib/op/logic/LibOpConditions.t.sol @@ -45,7 +45,7 @@ contract LibOpConditionsTest is OpTest { } /// Directly test the runtime logic of LibOpConditions. - function testOpConditionsRun(StackItem[] memory inputs, bytes32 finalNonZero) external view { + function testOpConditionsRun(StackItem[] memory inputs, Float finalNonZero) external view { InterpreterState memory state = opTestDefaultInterpreterState(); // Ensure that we have inputs that are a valid pairwise conditions. @@ -60,8 +60,8 @@ contract LibOpConditionsTest is OpTest { } // Ensure the final condition is nonzero so that we don't error. if (Float.wrap(StackItem.unwrap(inputs[inputs.length - 2])).isZero()) { - vm.assume(finalNonZero != 0); - inputs[inputs.length - 2] = StackItem.wrap(finalNonZero); + vm.assume(!finalNonZero.isZero()); + inputs[inputs.length - 2] = StackItem.wrap(Float.unwrap(finalNonZero)); } OperandV2 operand = LibOperand.build(uint8(inputs.length), 1, 0); opReferenceCheck( diff --git a/test/src/lib/op/math/LibOpFloor.t.sol b/test/src/lib/op/math/LibOpFloor.t.sol index c5bbc4dbd..8d7bf4f18 100644 --- a/test/src/lib/op/math/LibOpFloor.t.sol +++ b/test/src/lib/op/math/LibOpFloor.t.sol @@ -1,60 +1,61 @@ // SPDX-License-Identifier: CAL pragma solidity =0.8.25; -// import {OpTest, IntegrityCheckState, Operand, InterpreterState, UnexpectedOperand} from "test/abstract/OpTest.sol"; -// import {LibOpFloor} from "src/lib/op/math/LibOpFloor.sol"; -// import {LibOperand} from "test/lib/operand/LibOperand.sol"; - -// contract LibOpFloorTest is OpTest { -// /// Directly test the integrity logic of LibOpFloor. -// /// Inputs are always 1, outputs are always 1. -// function testOpFloorIntegrity(IntegrityCheckState memory state, Operand operand) external pure { -// (uint256 calcInputs, uint256 calcOutputs) = LibOpFloor.integrity(state, operand); -// assertEq(calcInputs, 1); -// assertEq(calcOutputs, 1); -// } - -// /// Directly test the runtime logic of LibOpFloor. -// function testOpFloorRun(uint256 a, uint16 operandData) public view { -// a = bound(a, 0, type(uint64).max - 1e18); -// InterpreterState memory state = opTestDefaultInterpreterState(); - -// Operand operand = LibOperand.build(1, 1, operandData); -// uint256[] memory inputs = new uint256[](1); -// inputs[0] = a; - -// opReferenceCheck(state, operand, LibOpFloor.referenceFn, LibOpFloor.integrity, LibOpFloor.run, inputs); -// } - -// /// Test the eval of `floor`. -// function testOpFloorEval() external view { -// checkHappy("_: floor(0);", 0, "0"); -// checkHappy("_: floor(1);", 1e18, "1"); -// checkHappy("_: floor(0.5);", 0, "0.5"); -// checkHappy("_: floor(2);", 2e18, "2"); -// checkHappy("_: floor(3);", 3e18, "3"); -// checkHappy("_: floor(3.8);", 3e18, "3.8"); -// } - -// /// Test the eval of `floor` for bad inputs. -// function testOpFloorZeroInputs() external { -// checkBadInputs("_: floor();", 0, 1, 0); -// } - -// function testOpFloorTwoInputs() external { -// checkBadInputs("_: floor(1 1);", 2, 1, 2); -// } - -// function testOpFloorZeroOutputs() external { -// checkBadOutputs(": floor(1);", 1, 1, 0); -// } - -// function testOpFloorTwoOutputs() external { -// checkBadOutputs("_ _: floor(1);", 1, 1, 2); -// } - -// /// Test that operand is disallowed. -// function testOpFloorEvalOperandDisallowed() external { -// checkUnhappyParse("_: floor<0>(1);", abi.encodeWithSelector(UnexpectedOperand.selector)); -// } -// } +import {OpTest, IntegrityCheckState, OperandV2, InterpreterState, UnexpectedOperand} from "test/abstract/OpTest.sol"; +import {LibOpFloor} from "src/lib/op/math/LibOpFloor.sol"; +import {LibOperand} from "test/lib/operand/LibOperand.sol"; +import {LibDecimalFloat, Float} from "rain.math.float/lib/LibDecimalFloat.sol"; +import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; + +contract LibOpFloorTest is OpTest { + /// Directly test the integrity logic of LibOpFloor. + /// Inputs are always 1, outputs are always 1. + function testOpFloorIntegrity(IntegrityCheckState memory state, OperandV2 operand) external pure { + (uint256 calcInputs, uint256 calcOutputs) = LibOpFloor.integrity(state, operand); + assertEq(calcInputs, 1); + assertEq(calcOutputs, 1); + } + + /// Directly test the runtime logic of LibOpFloor. + function testOpFloorRun(Float a, uint16 operandData) public view { + InterpreterState memory state = opTestDefaultInterpreterState(); + + OperandV2 operand = LibOperand.build(1, 1, operandData); + StackItem[] memory inputs = new StackItem[](1); + inputs[0] = StackItem.wrap(Float.unwrap(a)); + + opReferenceCheck(state, operand, LibOpFloor.referenceFn, LibOpFloor.integrity, LibOpFloor.run, inputs); + } + + /// Test the eval of `floor`. + function testOpFloorEval() external view { + checkHappy("_: floor(0);", 0, "0"); + checkHappy("_: floor(1);", Float.unwrap(LibDecimalFloat.packLossless(1, 0)), "1"); + checkHappy("_: floor(0.5);", Float.unwrap(LibDecimalFloat.packLossless(0, -1)), "0.5"); + checkHappy("_: floor(2);", Float.unwrap(LibDecimalFloat.packLossless(2, 0)), "2"); + checkHappy("_: floor(3);", Float.unwrap(LibDecimalFloat.packLossless(3, 0)), "3"); + checkHappy("_: floor(3.8);", Float.unwrap(LibDecimalFloat.packLossless(30, -1)), "3.8"); + } + + /// Test the eval of `floor` for bad inputs. + function testOpFloorZeroInputs() external { + checkBadInputs("_: floor();", 0, 1, 0); + } + + function testOpFloorTwoInputs() external { + checkBadInputs("_: floor(1 1);", 2, 1, 2); + } + + function testOpFloorZeroOutputs() external { + checkBadOutputs(": floor(1);", 1, 1, 0); + } + + function testOpFloorTwoOutputs() external { + checkBadOutputs("_ _: floor(1);", 1, 1, 2); + } + + /// Test that operand is disallowed. + function testOpFloorEvalOperandDisallowed() external { + checkUnhappyParse("_: floor<0>(1);", abi.encodeWithSelector(UnexpectedOperand.selector)); + } +}