├── assets └── blueprint.png ├── .env.example ├── .gitmodules ├── foundry.toml ├── .gitignore ├── script └── Deploy.s.sol ├── LICENSE ├── .github └── workflows │ └── ci.yaml ├── README.md ├── test └── BitMagic.t.sol └── src └── BitMagic.huff /assets/blueprint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devtooligan/bit-magic-huff-speedrun/HEAD/assets/blueprint.png -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ETHERSCAN_API_KEY=ABC123ABC123ABC123ABC123ABC123ABC1 2 | ROPSTEN_URL=https://eth-ropsten.alchemyapi.io/v2/ 3 | PRIVATE_KEY=0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/foundry-huff"] 2 | path = lib/foundry-huff 3 | url = https://github.com/huff-language/foundry-huff 4 | [submodule "lib/forge-std"] 5 | path = lib/forge-std 6 | url = https://github.com/foundry-rs/forge-std 7 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | 2 | [profile.default] 3 | solc_version = '0.8.15' 4 | auto_detect_solc = false 5 | optimizer = true 6 | optimizer_runs = 200 # Default amount 7 | ffi = true 8 | fuzz_runs = 1_000 9 | remappings = [ 10 | "forge-std=lib/forge-std/src/", 11 | "foundry-huff=lib/foundry-huff/src/", 12 | ] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Any Node Modules 2 | node_modules/ 3 | 4 | # Environment Variables 5 | .env 6 | .env.prod 7 | 8 | # Cached Build 9 | cache/ 10 | out/ 11 | 12 | # Vscode 13 | .vscode/ 14 | 15 | # huffc artifacts 16 | artifacts 17 | 18 | # Ignore flattened files 19 | flattened.txt 20 | 21 | # Ignore Broadcasts 22 | broadcast -------------------------------------------------------------------------------- /script/Deploy.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.15; 3 | 4 | import "foundry-huff/HuffDeployer.sol"; 5 | import "forge-std/Script.sol"; 6 | 7 | interface SimpleStore { 8 | function setValue(uint256) external; 9 | function getValue() external returns (uint256); 10 | } 11 | 12 | contract Deploy is Script { 13 | function run() public returns (SimpleStore simpleStore) { 14 | simpleStore = SimpleStore(HuffDeployer.deploy("SimpleStore")); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push] 4 | 5 | jobs: 6 | tests: 7 | name: Tests with Foundry 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | with: 13 | submodules: recursive 14 | 15 | - name: Install Foundry 16 | uses: foundry-rs/foundry-toolchain@v1 17 | with: 18 | version: nightly 19 | 20 | - name: Install Huff 21 | uses: huff-language/huff-toolchain@v2 22 | with: 23 | version: nightly 24 | 25 | - name: Run Tests 26 | run: forge test -vvv 27 | 28 | scripts: 29 | strategy: 30 | fail-fast: true 31 | name: Run Scripts 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v3 35 | with: 36 | submodules: recursive 37 | 38 | - name: Install Foundry 39 | uses: foundry-rs/foundry-toolchain@v1 40 | with: 41 | version: nightly 42 | 43 | - name: Install Huff 44 | uses: huff-language/huff-toolchain@v2 45 | with: 46 | version: nightly 47 | 48 | - name: Run Forge build 49 | run: | 50 | forge --version 51 | forge build --sizes 52 | id: build 53 | continue-on-error: true 54 | 55 | - name: Run scripts 56 | run: | 57 | ls -lsa 58 | ls script/ 59 | for file in script/*; do 60 | forge script $file -vvvv 61 | done -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bit-magic-huff-speedrun 2 | 3 | Speedrun of [@saxenism](@saxenism)'s [Solidity - Bit Magic](https://saxenism.com/web3/solidity/language-tricks/bit-magic/intermediate/2022/09/06/Bit-Magic-Solidity.html) using [Huff](huff.sh). 4 | 5 | ## Getting Started 6 | 7 | ### Requirements 8 | 9 | The following will need to be installed. Please follow the links and instructions. 10 | 11 | - [Foundry / Foundryup](https://github.com/gakonst/foundry) 12 | - This will install `forge`, `cast`, and `anvil` 13 | - You can test you've installed them right by running `forge --version` and get an output like: `forge 0.2.0 (92f8951 2022-08-06T00:09:32.96582Z)` 14 | - To get the latest of each, just run `foundryup` 15 | - [Huff Compiler](https://docs.huff.sh/get-started/installing/) 16 | - You'll know you've done it right if you can run `huffc --version` and get an output like: `huffc 0.3.0` 17 | 18 | ### Quickstart 19 | 20 | 1. Clone this repo or use template 21 | 22 | ``` 23 | git clone git@github.com:devtooligan/bit-magic-huff-speedrun.git 24 | cd bit-magic-huff-speedrun 25 | ``` 26 | 27 | 2. Install dependencies 28 | 29 | Once you've cloned and entered into your repository, you need to install the necessary dependencies. In order to do so, simply run: 30 | 31 | ```shell 32 | forge install 33 | ``` 34 | 35 | 3. Build & Test 36 | 37 | To build and test your contracts, you can run: 38 | 39 | ```shell 40 | forge build 41 | forge test 42 | ``` 43 | 44 | For more information on how to use Foundry, check out the [Foundry Github Repository](https://github.com/foundry-rs/foundry/tree/master/forge) and the [foundry-huff library repository](https://github.com/huff-language/foundry-huff). 45 | 46 | -------------------------------------------------------------------------------- /test/BitMagic.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.15; 3 | 4 | import "foundry-huff/HuffDeployer.sol"; 5 | import "forge-std/Test.sol"; 6 | import "forge-std/console.sol"; 7 | 8 | contract BitMagicTest is Test { 9 | /// @dev Address of the BitMagic contract. 10 | BitMagic public bm; 11 | 12 | /// @dev Setup the testing environment. 13 | function setUp() public { 14 | bm = BitMagic(HuffDeployer.deploy("BitMagic")); 15 | } 16 | 17 | // Bitwise Basics 18 | 19 | /// @dev Test and_ 20 | function testAnd_(uint256 x, uint256 y) public { 21 | assert(bm.and_(x, y) == x & y); 22 | } 23 | 24 | /// @dev Test or_ 25 | function testOr_(uint256 x, uint256 y) public { 26 | assert(bm.or_(x, y) == x | y); 27 | } 28 | 29 | /// @dev Test xor_ 30 | function testXor_(uint256 x, uint256 y) public { 31 | assert(bm.xor_(x, y) == x ^ y); 32 | } 33 | 34 | ///@dev Test not_ 35 | function testNot_(uint256 x) public { 36 | assert(bm.not_(x) == ~x); 37 | } 38 | 39 | /// @dev Test shl_ 40 | function testShl_(uint256 x, uint256 y) public { 41 | assert(bm.shl_(x, y) == x << y); 42 | } 43 | 44 | /// @dev Test shr_ 45 | function testShr_(uint256 x, uint256 y) public { 46 | assert(bm.shr_(x, y) == x >> y); 47 | } 48 | 49 | // @dev Get Last N bits 50 | function testGetLastNBits(uint256 x, uint256 n) public { 51 | vm.assume((1 << n) >= 1); 52 | assert(bm.getLastNBits(x, n) == x & ((1 << n) - 1)); 53 | } 54 | 55 | // @dev Get most significant Bit 56 | function testGetMostSignificantBit(uint256 x) public { 57 | uint256 expected; 58 | uint256 x_ = x; 59 | while (x_ > 0) { 60 | ++expected; 61 | x_ >>= 1; 62 | } 63 | console.log("expected", expected); 64 | console.log("actual", bm.getMostSignificantBit(x)); 65 | assert(bm.getMostSignificantBit(x) == expected); 66 | } 67 | 68 | // @dev Get First N bits 69 | function testGetFirstNBits(uint256 x, uint256 n) public { 70 | uint256 x = uint256(1000); 71 | uint256 n = 0x2; 72 | uint256 len = bm.getMostSignificantBit(x); 73 | vm.assume((1 << n) >= 1); 74 | vm.assume(n <= len); 75 | uint256 mask = ((1 << n) - 1) << (len - n); 76 | uint256 expected = x & mask; 77 | assert(bm.getFirstNBits(x, n) == expected); 78 | } 79 | 80 | // @dev Is power of 2 81 | function testIsPowerOfTwo(uint256 x) public { 82 | bool expected = (x == 0) || ((x & (x - 1)) == 0); 83 | assert(bm.isPowerOfTwo(x) == expected); 84 | } 85 | 86 | function countSetBits(uint256 x) public returns (uint256) { 87 | uint256 count; 88 | while (x != 0) { 89 | x = x & (x - 1); 90 | ++count; 91 | } 92 | return count; 93 | } 94 | 95 | // @dev Count set bits 96 | function testCountSetBits(uint256 x) public { 97 | assert(bm.countSetBits(x) == countSetBits(x)); 98 | } 99 | 100 | // BitMap 101 | 102 | function findNthBool(uint256 packedBool, uint256 position) public returns (bool) { 103 | return ((packedBool >> position) & 1 == 1); 104 | } 105 | 106 | // @dev test findNthBool 107 | function testFindNthBool(uint256 packedBool, uint256 position) public { 108 | assert(bm.findNthBool(packedBool, position) == findNthBool(packedBool, position)); 109 | } 110 | 111 | function setNthBool(uint256 packedBool, uint256 position, bool value) public returns (uint256) { 112 | if (value) { 113 | packedBool |= (1 << position); 114 | } else { 115 | packedBool &= ~(1 << position); 116 | } 117 | return packedBool; 118 | } 119 | 120 | function testSetNthBool(uint256 packedBool, uint256 position, bool value) public { 121 | bound(position, 0, 256); 122 | assert(bm.setNthBool(packedBool, position, value) == setNthBool(packedBool, position, value)); 123 | } 124 | 125 | // Extreme Basics - Part 1 126 | 127 | /// @dev Test sameSign 128 | // function testSameSign(int256 x, int256 y) public { 129 | function testSameSign() public { 130 | // vm.assume(x != 0 && y != 0); 131 | int256 x = -1; 132 | int256 y = -1; 133 | assert(bm.sameSign(x, y) == ((x ^ y) >= 0)); 134 | } 135 | 136 | /// @dev Test isEven 137 | function testIsEven(uint256 x) public { 138 | assert(bm.isEven(x) == (x % 2 == 0)); 139 | } 140 | 141 | /// @dev test invertSign using 2's complement 142 | function testInvertSign(int256 x) public { 143 | vm.assume(x != type(int256).min); 144 | assert(bm.invertSign(x) == -x); 145 | } 146 | 147 | // add1ToInteger with -(~x) is not so interesting in Huff since we use 2's complement to invert the sign 148 | 149 | // swap2Numbers with xor: a = a ^ b; b = b ^ a; a = a ^ b; also not interesting (overly complex) to do with Huff 150 | 151 | // turnOffNthBit, checkNthBit, toggleNthBit etc, done above w bitmaps findNthBool, setNthBool 152 | 153 | /// @dev test rightMostUnsetBit 154 | function testUnsetRightMostSetBit(uint256 x) public { 155 | uint256 expected; 156 | unchecked { 157 | expected = x & (x - 1); 158 | } 159 | assert(bm.rightMostUnsetBit(x) == expected); 160 | } 161 | 162 | /// @dev test findPositionOfRightmostSetBit 163 | function testFindPositionOfRightmostSetBit(uint256 x) public { 164 | uint256 rightMostSetBit = x ^ bm.rightMostUnsetBit(x); 165 | uint256 expected = bm.getMostSignificantBit(rightMostSetBit); 166 | assert(bm.findPositionOfRightmostSetBit(x) == expected); 167 | } 168 | 169 | function findPositionOfRightmostSetBit_Negation(uint256 x) public returns (uint256) { 170 | if (x & 1 == 1) { 171 | return 1; // Number is odd 172 | } 173 | 174 | uint256 num; 175 | unchecked { 176 | num = uint256(int256(x) & -int256(x)); 177 | } 178 | return bm.getMostSignificantBit(num); 179 | } 180 | 181 | /// @dev test findPositionOfRightmostSetBit_Negation 182 | function testFindPositionOfRightmostSetBit_Negation(int256 x) public { 183 | vm.assume(x != type(int256).min); 184 | uint256 expected = findPositionOfRightmostSetBit_Negation(uint256(x)); 185 | console.log( 186 | "+ + file: BitMagic.t.sol + line 182 + testFindPositionOfRightmostSetBit_Negation + expected", expected 187 | ); 188 | console.log( 189 | "+ + file: BitMagic.t.sol + line 184 + testFindPositionOfRightmostSetBit_Negation + bm.findPositionOfRightmostSetBit_Negation(uint(x))", 190 | bm.findPositionOfRightmostSetBit_Negation(uint256(x)) 191 | ); 192 | assert(bm.findPositionOfRightmostSetBit_Negation(uint256(x)) == expected); 193 | } 194 | 195 | // Puzzles (Incorporating multiple tricks) 196 | 197 | function bitsToFlip(uint256 x, uint256 y) public pure returns (uint256 counter) { 198 | uint256 xoredNumber = x ^ y; 199 | while (xoredNumber != 0) { 200 | xoredNumber = xoredNumber & (xoredNumber - 1); 201 | ++counter; 202 | } 203 | } 204 | 205 | /// @dev test bitsToFlip 206 | function testBitsToFlip(uint256 x, uint256 y) public { 207 | assert(bm.bitsToFlip(x, y) == bitsToFlip(x, y)); 208 | } 209 | 210 | function calculateXorToN(uint256 n) public pure returns (uint256 result) { 211 | for (uint256 i = 1; i <= n;) { 212 | result ^= i; 213 | unchecked { 214 | ++i; 215 | } 216 | } 217 | } 218 | 219 | /// @dev test calculateXorToN 220 | function testCalculateXorToN(uint8 n) public { 221 | assert(bm.calculateXorToN(n) == calculateXorToN(n)); 222 | } 223 | 224 | /// @dev calculateXorToNEfficient 225 | function testCalculateXorToNEfficient(uint8 n) public { 226 | assert(bm.calculateXorToNEfficient(n) == calculateXorToN(n)); 227 | } 228 | 229 | function findSumEqualToXor(uint8 n) public returns (uint256 counter) { 230 | for (uint256 i; i <= n; ++i) { 231 | if ((n ^ i) == (n + i)) { 232 | console.log(n, i); 233 | ++counter; 234 | } 235 | } 236 | } 237 | 238 | /// @dev test findSumEqualToXor 239 | function testFindSumEqualToXor(uint8 n) public { 240 | assert(uint256(bm.findSumEqualToXor(n)) == uint256(findSumEqualToXor(n))); 241 | } 242 | 243 | /// @dev test findSumEqualToXorEfficient 244 | function testFindSumEqualToXorEfficient(uint8 n) public { 245 | // function testFindSumEqualToXorEfficient() public { 246 | // uint8 n = 2; 247 | console.log("findSumEqualToXor(n)", findSumEqualToXor(n)); 248 | console.log("bm.findSumEqualToXorEfficient(n)", bm.findSumEqualToXorEfficient(n)); 249 | assert(uint256(bm.findSumEqualToXorEfficient(n)) == uint256(findSumEqualToXor(n))); 250 | } 251 | 252 | function findMSB(uint256 n) public pure returns (uint256) { 253 | // Since this is uint256, this will have 256 bits. So we will have to take appropriate number of steps. 254 | // The number of ORs we do would be based on the bits present in number n. 255 | 256 | n |= (n >> 1); // Now starting 2 bits are set in n 257 | n |= (n >> 2); // Now starting 4 bits are set in n 258 | n |= (n >> 4); // Now starting 8 bits are set in n 259 | n |= (n >> 8); // Now starting 16 bits are set in n 260 | n |= (n >> 16); // Now starting 32 bits are set in n 261 | n |= (n >> 32); // Now starting 64 bits are set in n 262 | n |= (n >> 64); // Now starting 128 bits are set in n 263 | n |= (n >> 128); // Now starting 256 bits are set in n 264 | 265 | n += 1; // Now it's 1 set bit (higher than the original MSB) and rest are 0s 266 | 267 | return (n >> 1); 268 | } 269 | 270 | /// @dev test findMSB -- OPTIMIZED VERSION 271 | function testFindMSB(uint128 x) public { 272 | assert(bm.findMSB(x) == findMSB(x)); 273 | } 274 | 275 | // Advanced problems solved via bit manipulations 276 | 277 | /// @dev test addTwoNumbers 278 | function testAddTwoNumbers(uint128 x, uint128 y) public { 279 | vm.assume((uint256(x) + uint(y)) <= type(uint64).max); 280 | uint256 actual = bm.addTwoNumbers(x, y); 281 | uint expected = uint256(x) + uint(y); 282 | assertEq(actual, expected); 283 | } 284 | } 285 | 286 | interface BitMagic { 287 | function and_(uint256, uint256) external returns (uint256); 288 | function or_(uint256, uint256) external returns (uint256); 289 | function xor_(uint256, uint256) external returns (uint256); 290 | function not_(uint256) external returns (uint256); 291 | function shl_(uint256, uint256) external returns (uint256); 292 | function shr_(uint256, uint256) external returns (uint256); 293 | function getLastNBits(uint256, uint256) external returns (uint256); 294 | function getFirstNBits(uint256, uint256) external returns (uint256); 295 | function getMostSignificantBit(uint256) external returns (uint256); 296 | function isPowerOfTwo(uint256) external returns (bool); 297 | function countSetBits(uint256) external returns (uint256); 298 | function findNthBool(uint256, uint256) external returns (bool); 299 | function setNthBool(uint256, uint256, bool) external returns (uint256); 300 | function sameSign(int256, int256) external returns (bool); 301 | function isEven(uint256) external returns (bool); 302 | function invertSign(int256) external returns (int256); 303 | function rightMostUnsetBit(uint256) external returns (uint256); 304 | function findPositionOfRightmostSetBit(uint256) external returns (uint256); 305 | function findPositionOfRightmostSetBit_Negation(uint256) external returns (uint256); 306 | function bitsToFlip(uint256, uint256) external returns (uint256); 307 | function calculateXorToN(uint256) external returns (uint256); 308 | function calculateXorToNEfficient(uint256) external returns (uint256); 309 | function findSumEqualToXor(uint256) external returns (uint256); 310 | function findSumEqualToXorEfficient(uint256) external returns (uint256); 311 | function findMSB(uint256) external returns (uint256); 312 | function addTwoNumbers(uint256, uint256) external returns (uint256); 313 | } 314 | -------------------------------------------------------------------------------- /src/BitMagic.huff: -------------------------------------------------------------------------------- 1 | /* Interface */ 2 | #define function and_(uint256, uint256) pure returns (uint256) 3 | #define function or_(uint256, uint256) pure returns (uint256) 4 | #define function xor_(uint256, uint256) pure returns (uint256) 5 | #define function not_(uint256) pure returns (uint256) 6 | #define function shl_(uint256, uint256) pure returns (uint256) 7 | #define function shr_(uint256, uint256) pure returns (uint256) 8 | #define function getLastNBits(uint256, uint256) pure returns (uint256) 9 | #define function getFirstNBits(uint256, uint256) pure returns (uint256) 10 | #define function getMostSignificantBit(uint256) pure returns (uint256) 11 | #define function isPowerOfTwo(uint256) pure returns (bool) 12 | #define function countSetBits(uint256) pure returns (uint256) 13 | #define function findNthBool(uint256, uint256) pure returns (bool) 14 | #define function setNthBool(uint256, uint256, bool) pure returns (uint256) 15 | #define function sameSign(int256, int256) pure returns (bool) 16 | #define function invertSign(int256) pure returns (bool) 17 | #define function invertSign2(int256) pure returns (bool) 18 | #define function isEven(uint256) pure returns (bool) 19 | #define function rightMostUnsetBit(uint256) pure returns (uint256) 20 | #define function findPositionOfRightmostSetBit(uint256) pure returns (uint256) 21 | #define function findPositionOfRightmostSetBit_Negation(uint256) pure returns (uint256) 22 | #define function bitsToFlip(uint256, uint256) pure returns (uint256) 23 | #define function calculateXorToN(uint256) pure returns (uint256) 24 | #define function calculateXorToNEfficient(uint256) pure returns (uint256) 25 | #define function findSumEqualToXor(uint256) pure returns (uint256) 26 | #define function findSumEqualToXorEfficient(uint256) pure returns (uint256) 27 | #define function findMSB(uint256) pure returns (uint256) 28 | #define function addTwoNumbers(uint256, uint256) pure returns (uint256) 29 | 30 | /* Storage Slots */ 31 | 32 | /* Methods */ 33 | #define macro AND() = takes (0) returns (0) { 34 | 0x04 calldataload // [a] 35 | 0x24 calldataload // [b, a] 36 | and // [a & b] 37 | 0x00 mstore // [] 38 | 0x20 0x00 return // [] 39 | } 40 | 41 | #define macro OR() = takes (0) returns (0) { 42 | 0x04 calldataload // [a] 43 | 0x24 calldataload // [b, a] 44 | or // [a | b] 45 | 0x00 mstore // [] 46 | 0x20 0x00 return // [] 47 | } 48 | 49 | #define macro XOR() = takes (0) returns (0) { 50 | 0x04 calldataload // [a] 51 | 0x24 calldataload // [b, a] 52 | xor // [a ^ b] 53 | 0x00 mstore // [] 54 | 0x20 0x00 return // [] 55 | } 56 | 57 | #define macro NOT() = takes (0) returns (0) { 58 | 0x04 calldataload // [a] 59 | not // [~a] 60 | 0x00 mstore // [] 61 | 0x20 0x00 return // [] 62 | } 63 | 64 | #define macro SHL() = takes (0) returns (0) { 65 | 0x04 calldataload // [a] 66 | 0x24 calldataload // [b, a] 67 | shl // [a << b] 68 | 0x00 mstore // [] 69 | 0x20 0x00 return // [] 70 | } 71 | 72 | #define macro SHR() = takes (0) returns (0) { 73 | 0x04 calldataload // [a] 74 | 0x24 calldataload // [b, a] 75 | shr // [a >> b] 76 | 0x00 mstore // [] 77 | 0x20 0x00 return // [] 78 | } 79 | 80 | #define macro GET_LAST_N_BITS() = takes (0) returns (0) { 81 | 0x04 calldataload // [x] 82 | 0x1 // [1, x] 83 | 0x1 // [1, 1, x] 84 | 0x24 calldataload // [n, 1, 1, x] 85 | shl // [1 << n, 1, x] 86 | sub // [1 << n - 1 == mask, x] 87 | and // [x & mask] 88 | 0x00 mstore // [] 89 | 0x20 0x00 return // [] 90 | } 91 | 92 | #define macro GET_FIRST_N_BITS() = takes (0) returns (0) { 93 | //todo: REFACTOR 94 | 0x04 calldataload // [x] 95 | 0x24 calldataload // [n, x] 96 | 0x04 calldataload // [x, n, x] 97 | _GET_MOST_SIGNIFICANT_BIT() // [msb, n, x] 98 | sub // [msb - n, x] 99 | 0x1 // [1, msb - n, x] 100 | 0x1 // [1, 1, msb - n, x] 101 | 0x24 calldataload // [n, 1, 1, msb - n, x] 102 | shl // [1 << n, 1, msb - n, x] 103 | sub // [1 << n - 1, msb - n, x] 104 | swap1 // [msb - n, 1 << n - 1, x] 105 | shl // [(1 << n) - 1 << (msb - n), x] 106 | and // [x & mask] 107 | 0x00 mstore 108 | 0x20 0x00 return 109 | } 110 | 111 | 112 | // see optimized solution https://ethereum.stackexchange.com/questions/8086/logarithm-math-operation-in-solidity/73682#73682 113 | #define macro _GET_MOST_SIGNIFICANT_BIT() = takes (1) returns (1) { 114 | // [x] 115 | 0x00 // [counter, x] 116 | swap1 // [x, counter] 117 | begin_while: // [x, counter] 118 | dup1 // [x, x, counter] 119 | iszero // [x == 0, x, counter] 120 | end_while jumpi // [x, counter] 121 | swap1 // [counter, x] 122 | 0x01 add // [counter + 1, x] 123 | swap1 // [x, counter + 1] 124 | 0x1 shr // [x >> 1, counter + 1] 125 | begin_while jump 126 | end_while: // [x, counter] 127 | pop // [counter] 128 | } 129 | 130 | #define macro GET_MOST_SIGNIFICANT_BIT() = takes (0) returns (0) { 131 | 0x04 calldataload // [x] 132 | _GET_MOST_SIGNIFICANT_BIT() 133 | 0x00 mstore 134 | 0x20 0x00 return 135 | } 136 | 137 | #define macro IS_POWER_OF_TWO() = takes (0) returns (0) { 138 | // return (x == 0) || ((x & (x - 1)) == 0); 139 | // todo: REFACTOR! 140 | 0x04 calldataload // [x] 141 | dup1 // [x, x] 142 | iszero // [x == 0, x] 143 | iszero // [x !=0, x] 144 | notzero jumpi // [x] 145 | iszero 0x00 mstore // [true] 146 | ret jumpi // [true] 147 | notzero: 148 | 0x1 // [1, x] 149 | dup2 // [x, 1, x] 150 | sub // [x - 1, x] 151 | and // [x & (x - 1)] 152 | iszero // [x & (x - 1) == 0] 153 | 0x00 mstore 154 | 155 | ret: 156 | 0x20 0x00 return 157 | } 158 | 159 | #define macro _COUNT_SET_BITS() = takes (1) returns (1) { 160 | // [x] 161 | // uint count; 162 | // while (x != 0) { 163 | // x = x & (x-1); 164 | // ++count; 165 | // } 166 | // return count; 167 | 0x00 // [counter, x] 168 | swap1 // [x, counter] 169 | begin_while: // [x, counter] 170 | dup1 // [x, x, counter] 171 | iszero ret jumpi // [x, counter] 172 | swap1 // [counter, x] 173 | 0x01 add // [counter + 1, x] 174 | 0x01 // [1, counter + 1, x] 175 | dup3 // [x, 1, counter + 1, x] 176 | sub // [x - 1, counter + 1, x] 177 | dup3 // [x, x - 1, counter + 1, x] 178 | and // [x & (x - 1) == newX, counter + 1, x] 179 | dup1 // [newX, newX, counter + 1, x] 180 | begin_while jumpi // [newX, counter + 1, x] 181 | pop // [counter + 1 == counter, x] 182 | swap1 // [x, counter] 183 | ret: // [x, counter] 184 | pop // [counter] 185 | } 186 | 187 | #define macro COUNT_SET_BITS() = takes (0) returns (0) { 188 | 0x00 // [counter] 189 | 0x04 calldataload // [x, counter] 190 | _COUNT_SET_BITS() // [counter] 191 | 0x00 mstore // [counter] 192 | 0x20 0x00 return 193 | } 194 | 195 | #define macro FIND_NTH_BOOL() = takes(0) returns (0) { 196 | //return ((packedBool >> position) & 1 == 1); 197 | 0x04 calldataload // [packedBool] 198 | 0x24 calldataload // [position, packedBool] 199 | shr // [packedBool >> position] 200 | 0x01 and // [(packedBool >> position) & 1] 201 | 0x00 mstore // [] 202 | 0x20 0x00 return // [] 203 | } 204 | 205 | #define macro SET_NTH_BOOL() = takes(0) returns (0) { 206 | // if (value) { 207 | // packedBool |= (1 << position); 208 | // } else { 209 | // packedBool &= ~(1 << position); 210 | // } 211 | // return packedBool; 212 | 0x04 calldataload // [packedBool] 213 | 0x01 // [1, packedBool] 214 | 0x24 calldataload // [position, 1, packedBool] 215 | shl // [1 << position, packedBool] 216 | 0x44 calldataload // [value, 1 << position, packedBool] 217 | one jumpi // [1 << position, packedBool] 218 | not // [~(1 << position), packedBool] 219 | and // [packedBool & ~(1 << position)] 220 | ret jump // [packedBool & ~(1 << position)] 221 | one: // [1 << position, packedBool] 222 | or // [packedBool | (1 << position)] 223 | ret: // [result] 224 | 0x00 mstore // [] 225 | 0x20 0x00 return // [] 226 | } 227 | 228 | #define constant INT256_MAX = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 229 | #define constant INT256_MIN = 0x8000000000000000000000000000000000000000000000000000000000000000 230 | 231 | #define macro SAME_SIGN() = takes (0) returns (0) { 232 | // return (x ^ y) >= 0; 233 | 0x04 calldataload // [x] 234 | 0x24 calldataload // [y, x] 235 | xor // [x ^ y] 236 | [INT256_MIN] // [INT256_MAX, x ^ y] 237 | gt // [x ^ y >= INT256_MAX] 238 | 0x00 mstore // [] 239 | 0x20 0x00 return // [] 240 | } 241 | 242 | #define macro _INVERT_SIGN() = takes (1) returns (1) { 243 | // [x] 244 | not // [~x] 245 | 0x01 add // [~x + 1] two's complement 246 | } 247 | 248 | #define macro INVERT_SIGN() = takes (0) returns (0) { 249 | // return -x; 250 | 0x04 calldataload // [x] 251 | _INVERT_SIGN() // [-x] 252 | 0x00 mstore // [] 253 | 0x20 0x00 return // [] 254 | } 255 | 256 | #define macro IS_EVEN() = takes (0) returns (0) { 257 | // return (x & 1) == 0; 258 | 0x04 calldataload // [x] 259 | 0x01 and // [x & 1] 260 | iszero // [x & 1 == 0] 261 | 0x00 mstore // [] 262 | 0x20 0x00 return // [] 263 | } 264 | 265 | #define macro _RIGHT_MOST_UNSET_BIT() = takes (1) returns (1) { 266 | // [x] 267 | 0x01 // [1, x] 268 | dup2 sub // [x - 1, x] 269 | and // [x & (x - 1)] 270 | } 271 | 272 | #define macro RIGHT_MOST_UNSET_BIT() = takes (0) returns (0) { 273 | // returns x & (x - 1); 274 | 0x04 calldataload // [x] 275 | _RIGHT_MOST_UNSET_BIT() // [rightMostUnsetBit] 276 | 0x00 mstore // [] 277 | 0x20 0x00 return // [] 278 | } 279 | 280 | #define macro FIND_POSITION_OF_RIGHTMOST_SET_BIT() = takes (0) returns (0) { 281 | 0x04 calldataload // [x] 282 | dup1 // [x, x] 283 | _RIGHT_MOST_UNSET_BIT() // [rightMostUnsetBit, x] 284 | xor // [x ^ rightMostUnsetBit == rightMostSetBit] 285 | _GET_MOST_SIGNIFICANT_BIT() // [positionOfRightMostSetBit] 286 | 0x00 mstore // [] 287 | 0x20 0x00 return // [] 288 | } 289 | 290 | #define macro FIND_POSITION_OF_RIGHTMOST_SET_BIT_NEGATION() = takes (0) returns (0) { 291 | 0x04 calldataload // [x] 292 | 0x01 // [1, x] 293 | and // [x & 1] 294 | iszero // [x & 1 == 0] 295 | even jumpi // [] 296 | 0x01 // [1] 297 | ret jump // [1] 298 | even: 299 | 0x04 calldataload // [x] 300 | dup1 // [x, x] 301 | _INVERT_SIGN() // [-x, x] 302 | and // [x & -x] 303 | _GET_MOST_SIGNIFICANT_BIT() // [positionOfRightMostSetBit] 304 | 305 | ret: 306 | 0x00 mstore // [] 307 | 0x20 0x00 return // [] 308 | } 309 | 310 | #define macro BITS_TO_FLIP() = takes (0) returns (0) { 311 | // return (x ^ y) & ~(x & y); 312 | 0x04 calldataload // [x] 313 | 0x24 calldataload // [y, x] 314 | xor // [x ^ y] 315 | _COUNT_SET_BITS() 316 | 0x00 mstore // [] 317 | 0x20 0x00 return // [] 318 | } 319 | 320 | #define macro CALCULATE_XOR_TO_N() = takes (0) returns (0) { 321 | 0x0 // [val] 322 | 0x04 calldataload // [n, val] 323 | 0x1 // [counter, n, val] 324 | begin_while: // [counter, n, val] 325 | dup2 // [n, counter, n, val] 326 | dup2 // [counter, n, counter, n, val] 327 | gt // [counter > n, counter, n, val] 328 | ret jumpi // [counter, n, val] 329 | dup1 // [counter, counter, n, val] 330 | swap3 // [val, counter, n, counter] 331 | xor // [val ^ counter == newVal, n, counter] 332 | swap2 // [counter, n, newVal] 333 | 0x01 add // [counter + 1, n, newVal] 334 | begin_while jump // [counter + 1, n, newVal] 335 | 336 | ret: // [counter, n, val] 337 | pop // [n, val] 338 | pop // [val] 339 | 0x00 mstore // [] 340 | 0x20 0x00 return // [] 341 | } 342 | 343 | #define macro CALCULATE_XOR_TO_N_EFFICIENT() = takes(0) returns (0) { 344 | // uint moduloN = N%4; 345 | // if(moduloN == 0) { 346 | // result = N; 347 | // } else if(moduloN == 1) { 348 | // result = 1; 349 | // } else if(moduloN == 2) { 350 | // result = N + 1; 351 | // } else { 352 | // result = 0; 353 | // } 354 | 0x04 calldataload // [n] 355 | 0x04 // [4, n] 356 | dup2 // [n, 4, n] 357 | mod // [n % 4, n] 358 | dup1 // [n % 4, n % 4, n] 359 | iszero // [n % 4 == 0, n % 4, n] 360 | ret jumpi // [n % 4, n] 361 | 0x01 // [1, n % 4, n] 362 | swap2 // [n, n % 4, 1] 363 | pop // [n % 4, 1] 364 | dup1 // [n % 4, n % 4, 1] 365 | dup3 // [1, n % 4, n % 4, 1] 366 | eq // [n % 4 == 1, n % 4, 1] 367 | ret jumpi // [n % 4, 1] 368 | swap1 // [1, n % 4] 369 | 0x04 calldataload // [n, 1, n % 4] 370 | add // [n + 1, n % 4] 371 | swap1 // [n % 4, n + 1] 372 | dup1 // [n % 4, n % 4, n + 1] 373 | 0x02 // [2, n % 4, n % 4, n + 1] 374 | eq // [n % 4 == 2, n % 4, n + 1] 375 | ret jumpi // [n % 4, n + 1] 376 | swap1 // [n + 1, n % 4] 377 | pop // [n % 4] 378 | returndatasize // [0, n % 4] 379 | swap1 // [n % 4, 0] 380 | ret: // [n % 4, result] 381 | pop // [result] 382 | 0x00 mstore // [] 383 | 0x20 0x00 return // [] 384 | } 385 | 386 | #define macro FIND_SUM_EQUAL_TO_XOR() = takes (0) returns (0) { 387 | // for (uint i; i <= n; ++i) { 388 | // if ((n ^ i) == (n + i)) { 389 | // ++counter; 390 | // } 391 | // } 392 | 0x04 calldataload // [n] 393 | returndatasize // [i, n] 394 | returndatasize // [counter, i, n] 395 | swap2 // [n, i, counter] 396 | begin_while: // [n, i, counter] 397 | dup2 // [i, n, i, counter] 398 | dup2 // [n, i, n, i, counter] 399 | dup1 // [n, n, i, n, i, counter] 400 | dup3 // [i, n, i, n, i, n, i, counter] 401 | gt // [i > n, n, i, n, i, counter] 402 | ret jumpi // [n, i, n, i, counter] 403 | dup2 // [i, n, i, n, i, counter] 404 | dup2 // [n, i, n, i, n, i, counter] 405 | xor // [n ^ i, n, i, n, i, counter] 406 | swap2 // [i, n, n ^ i, n, i, counter] 407 | add // [n + i, n ^ i, n, i, counter] 408 | eq // [n + i == n ^ i, n, i, counter] 409 | iszero // [n + i != n ^ i, n, i, counter] 410 | not_equal jumpi // [n, i, counter] 411 | swap2 // [counter, i, n] 412 | 0x01 add // [oldCounter + 1 == counter, i, n] 413 | swap2 // [n, i, counter] 414 | not_equal: // [n, i, counter] 415 | swap1 // [i, n, counter] 416 | 0x01 add // [i + 1, n, counter] 417 | swap1 // [n, i + 1, counter] 418 | begin_while jump // [n, i + 1, counter] 419 | ret: // [n, i, n, i, counter] 420 | pop // [i, n, i, counter] 421 | pop // [n, i, counter] 422 | pop // [i, counter] 423 | pop // [counter] 424 | 0x00 mstore // [] 425 | 0x20 0x00 return // [] 426 | } 427 | 428 | #define macro FIND_SUM_EQUAL_TO_XOR_EFFICIENT() = takes (0) returns (0) { 429 | // uint unsetBits; 430 | // while(n != 0) { 431 | // if(n & 1 == 0) { 432 | // ++unsetBits; 433 | // } 434 | // n >>= 1; 435 | // } 436 | // counter = (1 << unsetBits); 437 | 0x04 calldataload // [n] 438 | returndatasize // [unsetBits, n] 439 | begin_while: // [unsetBits, n] 440 | dup2 // [n, unsetBits, n] 441 | iszero // [n == 0, unsetBits, n] 442 | ret jumpi // [unsetBits, n] 443 | dup2 // [n, unsetBits, n] 444 | 0x01 and // [n & 1, unsetBits, n] 445 | iszero // [n & 1 == 0, unsetBits, n] 446 | iszero // [n & 1 != 0, unsetBits, n] 447 | notzero jumpi // [unsetBits, n] 448 | 0x01 add // [unsetBits + 1, n] 449 | notzero: // [unsetBits, n] 450 | swap1 // [n, unsetBits] 451 | 0x01 shr // [n >> 1, unsetBits] 452 | swap1 // [unsetBits, n >> 1] 453 | begin_while jump // [unsetBits, n] 454 | ret: // [unsetBits, n] 455 | 0x01 // [1, unsetBits, n] 456 | swap1 // [unsetBits, 1, n] 457 | shl // [1 << unsetBits, n] 458 | swap1 // [n, 1 << unsetBits] 459 | pop // [1 << unsetBits] 460 | 0x00 mstore // [] 461 | 0x20 0x00 return // [] 462 | } 463 | 464 | #define macro FIND_MSB() = takes(0) returns (0) { 465 | // n = n | (n >> 1); // Now starting 2 bits are set in n 466 | // n = n | (n >> 2); // Now starting 4 bits are set in n 467 | // n = n | (n >> 4); // Now starting 8 bits are set in n 468 | // n = n | (n >> 8); // Now starting 16 bits are set in n 469 | // n = n | (n >> 16); // Now starting 32 bits are set in n 470 | // n = n | (n >> 32); // Now starting 64 bits are set in n 471 | // n = n | (n >> 64); // Now starting 128 bits are set in n 472 | // n = n | (n >> 128); // Now starting 256 bits are set in n 473 | 474 | // n += 1; // Now it's 1 set bit (higher than the original MSB) and rest are 0s 475 | 476 | // return (n >> 1); 477 | 0x04 calldataload // [n] 478 | dup1 // [n, n] 479 | 0x01 shr // [n >> 1, n] 480 | or // [n | (n >> 1) == n] 481 | dup1 // [n, n] 482 | 0x02 shr // [n >> 2, n] 483 | or // [n | (n >> 2) == n] 484 | dup1 // [n, n] 485 | 0x04 shr // [n >> 4, n] 486 | or // [n | (n >> 4) == n] 487 | dup1 // [n, n] 488 | 0x08 shr // [n >> 8, n] 489 | or // [n | (n >> 8) == n] 490 | dup1 // [n, n] 491 | 0x10 shr // [n >> 16, n] 492 | or // [n | (n >> 16) == n] 493 | dup1 // [n, n] 494 | 0x20 shr // [n >> 32, n] 495 | or // [n | (n >> 32) == n] 496 | dup1 // [n, n] 497 | 0x40 shr // [n >> 64, n] 498 | or // [n | (n >> 64) == n] 499 | dup1 // [n, n] 500 | 0x80 shr // [n >> 128, n] 501 | or // [n | (n >> 128) == n] 502 | 0x01 add // [n + 1 == n] 503 | 0x01 shr // [n >> 1] 504 | 0x00 mstore // [] 505 | 0x20 0x00 return // [] 506 | } 507 | 508 | #define macro ADD_TWO_NUMBERS() = takes (0) returns (0) { 509 | 0x04 calldataload // [x] 510 | 0x24 calldataload // [y, x] 511 | dup2 // [x, y, x] 512 | dup2 // [y, x, y, x] 513 | and // [x & y, x, y] 514 | swap2 // [y, x, x & y] 515 | or // [x | y, x & y] 516 | add // [x | y + x & y] 517 | 0x00 mstore // [] 518 | 0x20 0x00 return // [] 519 | } 520 | 521 | 522 | 523 | #define macro MAIN() = takes (0) returns (0) { 524 | // Identify which function is being called. 525 | 0x00 calldataload 0xE0 shr 526 | dup1 __FUNC_SIG(and_) eq and_ jumpi 527 | dup1 __FUNC_SIG(or_) eq or_ jumpi 528 | dup1 __FUNC_SIG(xor_) eq xor_ jumpi 529 | dup1 __FUNC_SIG(not_) eq not_ jumpi 530 | dup1 __FUNC_SIG(shl_) eq shl_ jumpi 531 | dup1 __FUNC_SIG(shr_) eq shr_ jumpi 532 | dup1 __FUNC_SIG(getLastNBits) eq getLastNBits jumpi 533 | dup1 __FUNC_SIG(getFirstNBits) eq getFirstNBits jumpi 534 | dup1 __FUNC_SIG(getMostSignificantBit) eq getMostSignificantBit jumpi 535 | dup1 __FUNC_SIG(isPowerOfTwo) eq isPowerOfTwo jumpi 536 | dup1 __FUNC_SIG(countSetBits) eq countSetBits jumpi 537 | dup1 __FUNC_SIG(findNthBool) eq findNthBool jumpi 538 | dup1 __FUNC_SIG(setNthBool) eq setNthBool jumpi 539 | dup1 __FUNC_SIG(sameSign) eq sameSign jumpi 540 | dup1 __FUNC_SIG(invertSign) eq invertSign jumpi 541 | dup1 __FUNC_SIG(invertSign2) eq invertSign2 jumpi 542 | dup1 __FUNC_SIG(isEven) eq isEven jumpi 543 | dup1 __FUNC_SIG(rightMostUnsetBit) eq rightMostUnsetBit jumpi 544 | dup1 __FUNC_SIG(findPositionOfRightmostSetBit) eq findPositionOfRightmostSetBit jumpi 545 | dup1 __FUNC_SIG(findPositionOfRightmostSetBit_Negation) eq findPositionOfRightmostSetBit_Negation jumpi 546 | dup1 __FUNC_SIG(bitsToFlip) eq bitsToFlip jumpi 547 | dup1 __FUNC_SIG(calculateXorToN) eq calculateXorToN jumpi 548 | dup1 __FUNC_SIG(calculateXorToNEfficient) eq calculateXorToNEfficient jumpi 549 | dup1 __FUNC_SIG(findSumEqualToXor) eq findSumEqualToXor jumpi 550 | dup1 __FUNC_SIG(findSumEqualToXorEfficient) eq findSumEqualToXorEfficient jumpi 551 | dup1 __FUNC_SIG(findMSB) eq findMSB jumpi 552 | dup1 __FUNC_SIG(addTwoNumbers) eq addTwoNumbers jumpi 553 | 554 | 0x00 0x00 revert 555 | 556 | and_: 557 | AND() 558 | 559 | or_: 560 | OR() 561 | 562 | xor_: 563 | XOR() 564 | 565 | not_: 566 | NOT() 567 | 568 | shl_: 569 | SHL() 570 | 571 | shr_: 572 | SHR() 573 | 574 | getLastNBits: 575 | GET_LAST_N_BITS() 576 | 577 | getFirstNBits: 578 | GET_FIRST_N_BITS() 579 | 580 | getMostSignificantBit: 581 | GET_MOST_SIGNIFICANT_BIT() 582 | 583 | isPowerOfTwo: 584 | IS_POWER_OF_TWO() 585 | 586 | countSetBits: 587 | COUNT_SET_BITS() 588 | 589 | findNthBool: 590 | FIND_NTH_BOOL() 591 | 592 | setNthBool: 593 | SET_NTH_BOOL() 594 | 595 | sameSign: 596 | SAME_SIGN() 597 | 598 | invertSign: 599 | INVERT_SIGN() 600 | 601 | invertSign2: 602 | INVERT_SIGN() 603 | 604 | isEven: 605 | IS_EVEN() 606 | 607 | rightMostUnsetBit: 608 | RIGHT_MOST_UNSET_BIT() 609 | 610 | findPositionOfRightmostSetBit: 611 | FIND_POSITION_OF_RIGHTMOST_SET_BIT() 612 | 613 | findPositionOfRightmostSetBit_Negation: 614 | FIND_POSITION_OF_RIGHTMOST_SET_BIT_NEGATION() 615 | 616 | bitsToFlip: 617 | BITS_TO_FLIP() 618 | 619 | calculateXorToN: 620 | CALCULATE_XOR_TO_N() 621 | 622 | calculateXorToNEfficient: 623 | CALCULATE_XOR_TO_N_EFFICIENT() 624 | 625 | findSumEqualToXor: 626 | FIND_SUM_EQUAL_TO_XOR() 627 | 628 | findSumEqualToXorEfficient: 629 | FIND_SUM_EQUAL_TO_XOR_EFFICIENT() 630 | 631 | findMSB: 632 | FIND_MSB() 633 | 634 | addTwoNumbers: 635 | ADD_TWO_NUMBERS() 636 | 637 | } --------------------------------------------------------------------------------