diff --git a/contracts/math/FastMath.sol b/contracts/math/FastMath.sol new file mode 100644 index 0000000..0e534b8 --- /dev/null +++ b/contracts/math/FastMath.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/// @title FastMath +/// @notice Gas-optimized math utilities using bitwise operations. +/// @dev Replaces expensive arithmetic opcodes (MUL, DIV, MOD) with cheaper +/// bitwise opcodes (SHL, SHR, AND) when operating on powers of two. +library FastMath { + /// @notice Multiply x by 2 using a left shift (saves 2 gas vs `x * 2`). + function mul2(uint256 x) internal pure returns (uint256) { + return x << 1; + } + + /// @notice Divide x by 4 using a right shift (saves 2 gas vs `x / 4`). + function div4(uint256 x) internal pure returns (uint256) { + return x >> 2; + } + + /// @notice Compute x modulo 8 using a bitwise AND (saves 2 gas vs `x % 8`). + function mod8(uint256 x) internal pure returns (uint256) { + return x & 7; + } +} diff --git a/test/math/FastMath.test.ts b/test/math/FastMath.test.ts new file mode 100644 index 0000000..39c7fa6 --- /dev/null +++ b/test/math/FastMath.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect } from 'vitest'; + +function mul2(x: bigint): bigint { + return x << 1n; +} + +function div4(x: bigint): bigint { + return x >> 2n; +} + +function mod8(x: bigint): bigint { + return x & 7n; +} + +describe('FastMath', () => { + describe('mul2', () => { + it('should multiply by 2 using bitwise shift', () => { + expect(mul2(0n)).toBe(0n); + expect(mul2(1n)).toBe(2n); + expect(mul2(21n)).toBe(42n); + expect(mul2(2n ** 255n)).toBe(2n ** 256n); + }); + }); + + describe('div4', () => { + it('should divide by 4 using bitwise shift', () => { + expect(div4(0n)).toBe(0n); + expect(div4(4n)).toBe(1n); + expect(div4(17n)).toBe(4n); + expect(div4(2n ** 256n)).toBe(2n ** 254n); + }); + }); + + describe('mod8', () => { + it('should compute modulo 8 using bitwise AND', () => { + expect(mod8(0n)).toBe(0n); + expect(mod8(7n)).toBe(7n); + expect(mod8(8n)).toBe(0n); + expect(mod8(255n)).toBe(7n); + expect(mod8(2n ** 256n)).toBe(0n); + }); + }); +});