diff --git a/src/generated/Rainterpreter.pointers.sol b/src/generated/Rainterpreter.pointers.sol index e89580f68..17e7fd750 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(0x9a2b0c327fc4c41a99ab23f8e3c53a561d1e036de4f3fdfd40101cd261d86cc5); +bytes32 constant BYTECODE_HASH = bytes32(0x508e2859cbe04ffcf9a4b8f7b03ad763a7b6de5b8d5def61dffb3f671fc1ec9a); /// @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"085a088c08b00a3c0b050b170b290b420b660b9a0bab0bbc0c5e0c7d0d3b0deb0e6f0fb110e40d3b11dd128f133113a913ba13cb13cb13dc1447155215d115ea15fe165d1676168f16c816f3170c1725176e179517a8180a185818a618f419421950199e19c11a0f1a401a841a921aa01aae1afc1b2d1b5e1bac1bdd1c0e1c5c1c891cac1cfa1df0"; + hex"0863089508b90a450b0e0b200b320b4b0b6f0ba30bb40bc50c670c860d440df40e780fba10ed0d4411e61298133a13b213c313d413d413e51450155b15da15f316071666167f169816d116fc1715172e1777179e17b11813186118af18fd194b195919a719ca1a181a491a8d1ab21ac01ace1adc1b2a1b5b1b8c1bda1c0b1c3c1c8a1cb71cda1d281e1e"; diff --git a/src/generated/RainterpreterExpressionDeployer.pointers.sol b/src/generated/RainterpreterExpressionDeployer.pointers.sol index bbfac0f6d..8457a88a0 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(0xb13338c000917efed437eb17269fc4ed086e147c98ad73ab3ce7ef90a97bbc70); +bytes32 constant BYTECODE_HASH = bytes32(0xc09e77bdd405bd4351220b8e812a90e7679db941dcf6cf394bc7cee82ec7cab0); /// @dev The hash of the meta that describes the contract. -bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x1856b55328ed5bd3f5e2390acf6d07222a82c5d0f834c320c0b7ce538c069c1e); +bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0xed7c88b7711baa439b37f2f59e1a2eed9fdab8bd2b58f123d84b7b13a1331e73); /// @dev The function pointers for the integrity check fns. bytes constant INTEGRITY_FUNCTION_POINTERS = - hex"0ed80f560fba1134113e113e11481151116c12121212126e12e612f3113e114812f3113e1148113e113e113e1148113411341134113412fd1322133c113e113e12fd113e113e12f31148113e113e12f312f311341346134613461346134611481346113e1360113411481148114811481346113411341346113411341360113e114813601148133c"; + hex"0ee00f5e0fc2113c11461146115011591174121a121a127612ee12fb1146115012fb114611501146114611461150113c113c113c113c1305132a13441146114613051146114612fb11501146114612fb12fb113c134e134e134e134e134e1150134e11461368113c11501150115011501150134e113c113c134e113c113c136811461150136811501344"; diff --git a/src/generated/RainterpreterParser.pointers.sol b/src/generated/RainterpreterParser.pointers.sol index c5bfe66d6..dafe74cb2 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(0x61765f108a4eafb7e39d931d12e91c4239dc9b5fd31d5dcd388366c10ed0d7bc); +bytes32 constant BYTECODE_HASH = bytes32(0x54c1723ff28ef16ba6f24e3ff5b598ae8da7ce0bee7f2b3e2ee3cd1548b190af); /// @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(0x61765f108a4eafb7e39d931d12e91c4239dc9 /// 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"027360018812c0058112300804a1a02000120192e89864d1586010788a0800310122000002000000000000000000000000000000000005000000000000000000000000253bfdf302d3ac012ac8268505b44edf35182f2a14690c630d83a7eb32c5853a08ccb48c3358350a1c21b6884098a6e5262057e32f7e09af17b43369161b9cec2b5ab5e8243c910831738e091e8a3fa7150113a32e22720213c659b9225ac7c1285da1bd42cc18770aa63c19048eb6fa018179d300dfb6a91fde8a882cb772ea03596d3111fd7ab9437b42922d4396833f58726b1d2330703794005b21f438852081752c3dff19581ac1a49e30ffeff038e1eec73924bdfe2760978f232d6a9a09d520ad061480a0294c65fb344763440ecb8d4d0b8b587210e7c1b518e4b09907152b041261f729198ab9053af8942b1b45c1270f22a49a3ca6838c4160cc7c0c668e383b10da5036d6d3e33ee7d2ff"; + hex"027360018812c0058112300804a1a02000120192e89864d1786010788a0800310122000002000000000000000000000000000000000005000000000000000000000000253bfdf302d3ac012ac8268505b44edf36182f2a14690c630d83a7eb32c5853a08ccb48c3358350a1c21b6884198a6e5262057e32f7e09af17b43369161b9cec2b5ab5e8243c910831738e093542f25d1e8a3fa7150113a32e22720213c659b9225ac7c1285da1bd43cc18770aa63c19048eb6fa018179d300dfb6a91fde8a882cb772ea03596d3111fd7ab9447b42922d4396834058726b1d2330703894005b21f438852081752c3eff19581ac1a49e30ffeff039e1eec73a24bdfe2760978f232d6a9a09d520ad061480a0294c65fb344763440ecb8d4d0b8b587210e7c1b518e4b09907152b041261f729198ab9053bf8942b1b45c1270f22a49a3da6838c4260cc7c0c668e383c10da5037d6d3e33fe7d2ff"; /// @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"1abd1abd1abd1b921ca91ca91ca91b921b921abd1abd1abd1ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91ca91abd1ca91ca9"; + hex"1ac51ac51ac51b9a1cb11cb11cb11b9a1b9a1ac51ac51ac51cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11cb11ac51cb11cb1"; /// @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"16051837187a1918"; +bytes constant LITERAL_PARSER_FUNCTION_POINTERS = hex"160d183f18821920"; diff --git a/src/lib/op/LibAllStandardOps.sol b/src/lib/op/LibAllStandardOps.sol index 43fa7fda5..b3e9cfc05 100644 --- a/src/lib/op/LibAllStandardOps.sol +++ b/src/lib/op/LibAllStandardOps.sol @@ -77,7 +77,7 @@ import {LibOpMul} from "./math/LibOpMul.sol"; 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 {LibOpExp2} from "./math/LibOpExp2.sol"; import {LibOpFloor} from "./math/LibOpFloor.sol"; import {LibOpFrac} from "./math/LibOpFrac.sol"; // import {LibOpGm} from "./math/LibOpGm.sol"; @@ -112,7 +112,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 = 68; +uint256 constant ALL_STANDARD_OPS_LENGTH = 69; /// @title LibAllStandardOps /// @notice Every opcode available from the core repository laid out as a single @@ -274,7 +274,7 @@ library LibAllStandardOps { AuthoringMetaV2("div", "Divides the first number by all other numbers. Errors if any divisor is zero."), AuthoringMetaV2("e", "The mathematical constant e."), AuthoringMetaV2("exp", "Natural exponential e^x."), - // AuthoringMetaV2("exp2", "Binary exponential 2^x where x. Errors if the exponentiation exceeds `max-value()`."), + AuthoringMetaV2("exp2", "Binary exponential 2^x."), 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."), @@ -474,8 +474,8 @@ library LibAllStandardOps { LibParseOperand.handleOperandDisallowed, // exp LibParseOperand.handleOperandDisallowed, - // // exp2 - // LibParseOperand.handleOperandDisallowed, + // exp2 + LibParseOperand.handleOperandDisallowed, // floor LibParseOperand.handleOperandDisallowed, // frac @@ -605,7 +605,7 @@ library LibAllStandardOps { LibOpDiv.integrity, LibOpE.integrity, LibOpExp.integrity, - // LibOpExp2.integrity, + LibOpExp2.integrity, LibOpFloor.integrity, LibOpFrac.integrity, // LibOpGm.integrity, @@ -716,7 +716,7 @@ library LibAllStandardOps { LibOpDiv.run, LibOpE.run, LibOpExp.run, - // LibOpExp2.run, + LibOpExp2.run, LibOpFloor.run, LibOpFrac.run, // LibOpGm.run, diff --git a/src/lib/op/math/LibOpExp2.sol b/src/lib/op/math/LibOpExp2.sol index f78990c9b..7711bad6e 100644 --- a/src/lib/op/math/LibOpExp2.sol +++ b/src/lib/op/math/LibOpExp2.sol @@ -1,43 +1,50 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -// import {UD60x18, exp2} 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"; - -// /// @title LibOpExp2 -// /// @notice Opcode for the binary exponential 2^x as decimal 18 fixed point. -// library LibOpExp2 { -// function integrity(IntegrityCheckState memory, Operand) internal pure returns (uint256, uint256) { -// // There must be one inputs and one output. -// return (1, 1); -// } - -// /// exp2 -// /// 18 decimal fixed point binary exponent 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(exp2(UD60x18.wrap(a))); - -// assembly ("memory-safe") { -// mstore(stackTop, a) -// } -// return stackTop; -// } - -// /// Gas intensive reference implementation of exp 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(exp2(UD60x18.wrap(inputs[0]))); -// return outputs; -// } -// } +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 {LibDecimalFloat, Float} from "rain.math.float/lib/LibDecimalFloat.sol"; +import {StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol"; + +/// @title LibOpExp2 +/// @notice Opcode for the binary exponential 2^x as decimal floating point. +library LibOpExp2 { + using LibDecimalFloat for Float; + + function integrity(IntegrityCheckState memory, OperandV2) internal pure returns (uint256, uint256) { + // There must be one inputs and one output. + return (1, 1); + } + + /// exp2 + /// decimal floating point binary exponent of a number. + function run(InterpreterState memory, OperandV2, Pointer stackTop) internal view returns (Pointer) { + Float a; + assembly ("memory-safe") { + a := mload(stackTop) + } + + a = LibDecimalFloat.FLOAT_TWO.pow(a, LibDecimalFloat.LOG_TABLES_ADDRESS); + + assembly ("memory-safe") { + mstore(stackTop, a) + } + return stackTop; + } + + /// Gas intensive reference implementation of exp for testing. + function referenceFn(InterpreterState memory, OperandV2, StackItem[] memory inputs) + internal + view + returns (StackItem[] memory) + { + Float a = Float.wrap(StackItem.unwrap(inputs[0])); + a = LibDecimalFloat.FLOAT_TWO.pow(a, LibDecimalFloat.LOG_TABLES_ADDRESS); + + StackItem[] memory outputs = new StackItem[](1); + outputs[0] = StackItem.wrap(Float.unwrap(a)); + return outputs; + } +} diff --git a/test/src/lib/op/math/LibOpExp2.t.sol b/test/src/lib/op/math/LibOpExp2.t.sol index 73d089c1c..603dad936 100644 --- a/test/src/lib/op/math/LibOpExp2.t.sol +++ b/test/src/lib/op/math/LibOpExp2.t.sol @@ -1,56 +1,64 @@ // SPDX-License-Identifier: CAL pragma solidity =0.8.25; -// import {OpTest, IntegrityCheckState, Operand, InterpreterState, UnexpectedOperand} from "test/abstract/OpTest.sol"; -// import {LibOpExp2} from "src/lib/op/math/LibOpExp2.sol"; -// import {LibOperand} from "test/lib/operand/LibOperand.sol"; - -// contract LibOpExp2Test is OpTest { -// /// Directly test the integrity logic of LibOpExp2. -// /// Inputs are always 1, outputs are always 1. -// function testOpExp2Integrity(IntegrityCheckState memory state, Operand operand) external pure { -// (uint256 calcInputs, uint256 calcOutputs) = LibOpExp2.integrity(state, operand); -// assertEq(calcInputs, 1); -// assertEq(calcOutputs, 1); -// } - -// /// Directly test the runtime logic of LibOpExp2. -// function testOpExp2Run(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, LibOpExp2.referenceFn, LibOpExp2.integrity, LibOpExp2.run, inputs); -// } - -// /// Test the eval of `exp2`. -// function testOpExp2Eval() external view { -// checkHappy("_: exp2(0);", 1e18, "2^0"); -// checkHappy("_: exp2(1);", 2e18, "2^1"); -// checkHappy("_: exp2(0.5);", 1414213562373095048, "2^0.5"); -// checkHappy("_: exp2(2);", 4e18, "2^2"); -// checkHappy("_: exp2(3);", 8e18, "2^3"); -// } - -// /// Test the eval of `exp2` for bad inputs. -// function testOpExp2EvalBad() external { -// checkBadInputs("_: exp2();", 0, 1, 0); -// checkBadInputs("_: exp2(1 1);", 2, 1, 2); -// } - -// /// Test that operand is disallowed. -// function testOpExp2EvalOperandDisallowed() external { -// checkUnhappyParse("_: exp2<0>(1);", abi.encodeWithSelector(UnexpectedOperand.selector)); -// } - -// function testOpExp2ZeroOutputs() external { -// checkBadOutputs(": exp2(1);", 1, 1, 0); -// } - -// function testOpExp2TwoOutputs() external { -// checkBadOutputs("_ _: exp2(1);", 1, 1, 2); -// } -// } +import {OpTest, IntegrityCheckState, OperandV2, InterpreterState, UnexpectedOperand} from "test/abstract/OpTest.sol"; +import {LibOpExp2} from "src/lib/op/math/LibOpExp2.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 LibOpExp2Test is OpTest { + function beforeOpTestConstructor() internal virtual override { + vm.createSelectFork(vm.envString("ETH_RPC_URL")); + } + + /// Directly test the integrity logic of LibOpExp2. + /// Inputs are always 1, outputs are always 1. + function testOpExp2Integrity(IntegrityCheckState memory state, OperandV2 operand) external pure { + (uint256 calcInputs, uint256 calcOutputs) = LibOpExp2.integrity(state, operand); + assertEq(calcInputs, 1); + assertEq(calcOutputs, 1); + } + + /// Directly test the runtime logic of LibOpExp2. + function testOpExp2Run(int224 signedCoefficientA, int32 exponentA, uint16 operandData) public view { + signedCoefficientA = int224(bound(signedCoefficientA, 0, 10000)); + exponentA = int32(bound(exponentA, -10, 5)); + InterpreterState memory state = opTestDefaultInterpreterState(); + Float a = LibDecimalFloat.packLossless(signedCoefficientA, exponentA); + + OperandV2 operand = LibOperand.build(1, 1, operandData); + StackItem[] memory inputs = new StackItem[](1); + inputs[0] = StackItem.wrap(Float.unwrap(a)); + + opReferenceCheck(state, operand, LibOpExp2.referenceFn, LibOpExp2.integrity, LibOpExp2.run, inputs); + } + + /// Test the eval of `exp2`. + function testOpExp2Eval() 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(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"); + } + + /// Test the eval of `exp2` for bad inputs. + function testOpExp2EvalBad() external { + checkBadInputs("_: exp2();", 0, 1, 0); + checkBadInputs("_: exp2(1 1);", 2, 1, 2); + } + + /// Test that operand is disallowed. + function testOpExp2EvalOperandDisallowed() external { + checkUnhappyParse("_: exp2<0>(1);", abi.encodeWithSelector(UnexpectedOperand.selector)); + } + + function testOpExp2ZeroOutputs() external { + checkBadOutputs(": exp2(1);", 1, 1, 0); + } + + function testOpExp2TwoOutputs() external { + checkBadOutputs("_ _: exp2(1);", 1, 1, 2); + } +}