From 46fcb7048a956869e7222ff3d7e46f3e69c29f73 Mon Sep 17 00:00:00 2001 From: David Dallaire Date: Tue, 11 Aug 2026 16:20:47 -0400 Subject: [PATCH] Cheaper expWad and lnWad via symmetric rationals --- src/utils/FixedPointMathLib.sol | 193 ++++++++++++------------- test/FixedPointMathLib.t.sol | 243 ++++++++++++++++---------------- 2 files changed, 221 insertions(+), 215 deletions(-) diff --git a/src/utils/FixedPointMathLib.sol b/src/utils/FixedPointMathLib.sol index 37103d59f..a619da8e3 100644 --- a/src/utils/FixedPointMathLib.sol +++ b/src/utils/FixedPointMathLib.sol @@ -206,67 +206,53 @@ library FixedPointMathLib { /// Note: This function is an approximation. Monotonically increasing. function expWad(int256 x) internal pure returns (int256 r) { unchecked { - // When the result is less than 0.5 we return zero. - // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`. - if (x <= -41446531673892822313) return r; + // Accept `-41446531673892822313 < x < 135305999368893231589` with a + // single unsigned comparison; sort out the two edges on the cold path. + if (uint256(x) + 41446531673892822312 >= 176752531042786053901) { + // When the true result is less than 1 wei we return zero. + // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`. + if (x <= -41446531673892822313) return r; - /// @solidity memory-safe-assembly - assembly { - // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as - // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`. - if iszero(slt(x, 135305999368893231589)) { + /// @solidity memory-safe-assembly + assembly { + // When the result is greater than `(2**255 - 1) / 1e18` we can not + // represent it as an int. This happens when + // `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`. mstore(0x00, 0xa37bfec9) // `ExpOverflow()`. revert(0x1c, 0x04) } } - // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96` - // for more intermediate precision and a binary basis. This base conversion - // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. + // Convert `x` from `10**18` fixed point to `2**96` fixed point. x = (x << 78) / 5 ** 18; - // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers - // of two such that exp(x) = exp(x') * 2**k, where k is an integer. - // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). - int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96; + // Reduce to `x' in (-½ ln 2, ½ ln 2) * 2**96` with `exp(x) = 2**k * exp(x')`. + // `6196328019 = round(2**128 / (ln 2 * 2**96))`; `k` is in the range `[-60, 195]`. + int256 k = (x * 6196328019 + 2 ** 127) >> 128; x = x - k * 54916777467707473351141471128; - // `k` is in the range `[-61, 195]`. - - // Evaluate using a (6, 7)-term rational approximation. - // `p` is made monic, we'll multiply by a scale factor later. - int256 y = x + 1346386616545796478920950773328; - y = ((y * x) >> 96) + 57155421227552351082224309758442; - int256 p = y + x - 94201549194550492254356042504812; - p = ((p * y) >> 96) + 28719021644029726153956944680412240; - p = p * x + (4385272521454847904659076985693276 << 96); - - // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. - int256 q = x - 2855989394907223263936484059900; - q = ((q * x) >> 96) + 50020603652535783019961831881945; - q = ((q * x) >> 96) - 533845033583426703283633433725380; - q = ((q * x) >> 96) + 3604857256930695427073651918091429; - q = ((q * x) >> 96) - 14423608567350463180887372962807573; - q = ((q * x) >> 96) + 26449188498355588339934803723976023; + // `exp(x') = (E + x' * O) / (E - x' * O)`, a (5, 5)-term symmetric + // rational with `E`, `O` polynomials in `x'^2`. `E` is monic, so its + // single Horner stage needs no `>> 96`: with the constant term + // pre-shifted, `e` and `t` are in `2**192` basis. + int256 u = (x * x) >> 96; + int256 e = (u + 8876005618932925505308977557156) * u + + (79886213883764523772906264239809 << 96); + int256 o = ((2639738311906822815584886674 * u) >> 96) + 1109410564309688178690540877381; + o = ((o * u) >> 96) + 39943106941882261691307222498689; + int256 t = x * o; /// @solidity memory-safe-assembly assembly { // Div in assembly because solidity adds a zero check despite the unchecked. - // The q polynomial won't have zeros in the domain as all its roots are complex. - // No scaling is necessary because p is already `2**96` too large. - r := sdiv(p, q) + // The denominator is positive on the whole reduced domain. + r := sdiv(add(e, t), sar(96, sub(e, t))) } - // r should be in the range `(0.09, 0.25) * 2**96`. - - // We now need to multiply r by: - // - The scale factor `s ≈ 6.031367120`. - // - The `2**k` factor from the range reduction. - // - The `1e18 / 2**96` factor for base conversion. - // We do this all at once, with an intermediate result in `2**213` - // basis, so the final right shift is always by a positive amount. + // Multiply by `2**k * 1e18 / 2**96`. `r < 1.5 * 2**96`, so the + // product cannot overflow, and the shift amount is never negative. r = int256( - (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k) + (uint256(r) * 633825300114114700748351602688000000000000000000) >> uint256(195 - k) ); } } @@ -277,11 +263,6 @@ library FixedPointMathLib { function lnWad(int256 x) internal pure returns (int256 r) { /// @solidity memory-safe-assembly assembly { - // We want to convert `x` from `10**18` fixed point to `2**96` fixed point. - // We do this by multiplying by `2**96 / 10**18`. But since - // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here - // and add `ln(2**96 / 10**18)` at the end. - // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`. r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) @@ -301,47 +282,44 @@ library FixedPointMathLib { // ln(2^k * x) = k * ln(2) + ln(x) x := shr(159, shl(r, x)) - // Evaluate using a (8, 8)-term rational approximation. - // `p` is made monic, we will multiply by a scale factor later. - // forgefmt: disable-next-item - let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir. - sar(96, mul(add(43456485725739037958740375743393, - sar(96, mul(add(24828157081833163892658089445524, - sar(96, mul(add(3273285459638523848632254066296, - x), x))), x))), x)), 11111509109440967052023855526967) - p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857) - p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526) - p := sub(mul(p, x), shl(96, 795164235651350426258249787498)) - // We leave `p` in `2**192` basis so we don't need to scale it back up for the division. - - // `q` is monic by convention. - let q := add(5573035233440673466300451813936, x) - q := add(71694874799317883764090561454958, sar(96, mul(x, q))) - q := add(283447036172924575727196451306956, sar(96, mul(x, q))) - q := add(401686690394027663651624208769553, sar(96, mul(x, q))) - q := add(204048457590392012362485061816622, sar(96, mul(x, q))) - q := add(31853899698501571402653359427138, sar(96, mul(x, q))) - q := add(909429971244387300277376558375, sar(96, mul(x, q))) - - // `p / q` is in the range `(0, 0.125) * 2**96`. - - // Finalization, we need to: - // - Multiply by the scale factor `s = 5.549…`. - // - Add `ln(2**96 / 10**18)`. - // - Add `k * ln(2)`. - // - Multiply by `10**18 / 2**96 = 5**18 >> 78`. - - // The q polynomial is known not to have zeros in the domain. - // No scaling required because p is already `2**96` too large. - p := sdiv(p, q) - // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`. - p := mul(1677202110996718588342820967067443963516166, p) - // Add `ln(2) * k * 5**18 * 2**192`. - // forgefmt: disable-next-item - p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p) - // Add `ln(2**96 / 10**18) * 5**18 * 2**192`. - p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p) - // Base conversion: mul `2**18 / 2**192`. + // `s = (x - sqrt(2)) * 2**96 / (x + sqrt(2))`, so that + // `ln(x) = ln(2)/2 + 2 * atanh(s)`. + let s := + sdiv( + shl(96, sub(x, 112045541949572279837463876455)), + add(x, 112045541949572279837463876455) + ) + + // `2 * atanh(s) = s * A(w) / B(w)`, a (3, 3)-term odd rational in `w = s^2`. + let w := sar(96, mul(s, s)) + let a := + add( + sar(96, mul(sub(w, 1813347344949966953757847210329), w)), + 5824670411451500986303020460168 + ) + a := sub(sar(96, mul(a, w)), 4518264490991587979207438354337) + let b := + sub( + sar(96, mul(188151507788160136135094921663, w)), + 1676640319226537252003611223372 + ) + b := add(sar(96, mul(b, w)), 3665379287557676720634158507137) + b := sub(sar(96, mul(b, w)), 2259132245495793985525851698055) + + // `B` is bounded away from zero on the whole domain. + let p := sdiv(mul(s, a), b) + + // Add `(2k + 1) * ln(2)/2` and `ln(2**96 / 10**18)`, then convert to `WAD`, + // all in `5**18 * 2**192` basis. + p := mul(302231454903657293676544000000000000000000, p) + p := add( + mul( + 8298788776342807110743642979096973734596910279609939088954046749604186, + sub(319, shl(1, r)) + ), + p + ) + p := add(600920179829731861736750627322249724520361163382248881493645412721105578, p) r := sar(174, p) } } @@ -428,12 +406,35 @@ library FixedPointMathLib { int256 t = w | 1; /// @solidity memory-safe-assembly assembly { - x := sdiv(mul(x, wad), t) - } - x = (t * (wad + lnWad(x))); - /// @solidity memory-safe-assembly - assembly { - w := sdiv(x, add(wad, t)) + x := sdiv(add(mul(x, wad), shr(1, t)), t) + // Inline the `lnWad` core at `2**96` precision, so that the final + // step rounds to nearest regardless of `lnWad`'s wad rounding. + let v := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) + v := or(v, shl(6, lt(0xffffffffffffffff, shr(v, x)))) + v := or(v, shl(5, lt(0xffffffff, shr(v, x)))) + v := or(v, shl(4, lt(0xffff, shr(v, x)))) + v := or(v, shl(3, lt(0xff, shr(v, x)))) + // forgefmt: disable-next-item + v := xor(v, byte(and(0x1f, shr(shr(v, x), 0x8421084210842108cc6318c6db6d54be)), + 0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)) + x := shr(159, shl(v, x)) + let s := sdiv(shl(96, sub(x, 112045541949572279837463876455)), + add(x, 112045541949572279837463876455)) + let z := sar(96, mul(s, s)) + let a := add(sar(96, mul(sub(z, 1813347344949966953757847210329), z)), + 5824670411451500986303020460168) + a := sub(sar(96, mul(a, z)), 4518264490991587979207438354337) + let b := sub(sar(96, mul(188151507788160136135094921663, z)), + 1676640319226537252003611223372) + b := add(sar(96, mul(b, z)), 3665379287557676720634158507137) + b := sub(sar(96, mul(b, z)), 2259132245495793985525851698055) + // `l = ln(x' / 1e18) * 2**96`. + let l := add(sdiv(mul(s, a), b), + add(mul(27458388733853736675570735564, sub(319, shl(1, v))), + 1988278089788132588087242333381)) + // `w = t * (2**96 + l) * 1e18 / (2**96 * (1e18 + t))`, rounded to nearest. + let d := mul(shl(96, 1), add(wad, t)) + w := sdiv(add(mul(mul(t, add(shl(96, 1), l)), wad), shr(1, d)), d) } } } diff --git a/test/FixedPointMathLib.t.sol b/test/FixedPointMathLib.t.sol index edc37f066..d8f0d7077 100644 --- a/test/FixedPointMathLib.t.sol +++ b/test/FixedPointMathLib.t.sol @@ -9,41 +9,41 @@ contract FixedPointMathLibTest is SoladyTest { assertEq(FixedPointMathLib.expWad(-41446531673892822312), 1); assertEq(FixedPointMathLib.expWad(-41446531673892822313), 0); - assertEq(FixedPointMathLib.expWad(-3e18), 49787068367863942); + assertEq(FixedPointMathLib.expWad(-3e18), 49787068367863943); assertEq(FixedPointMathLib.expWad(-2e18), 135335283236612691); assertEq(FixedPointMathLib.expWad(-1e18), 367879441171442321); assertEq(FixedPointMathLib.expWad(-0.5e18), 606530659712633423); - assertEq(FixedPointMathLib.expWad(-0.3e18), 740818220681717866); + assertEq(FixedPointMathLib.expWad(-0.3e18), 740818220681717865); assertEq(FixedPointMathLib.expWad(0), 1000000000000000000); - assertEq(FixedPointMathLib.expWad(0.3e18), 1349858807576003103); - assertEq(FixedPointMathLib.expWad(0.5e18), 1648721270700128146); + assertEq(FixedPointMathLib.expWad(0.3e18), 1349858807576003104); + assertEq(FixedPointMathLib.expWad(0.5e18), 1648721270700128148); assertEq(FixedPointMathLib.expWad(1e18), 2718281828459045235); - assertEq(FixedPointMathLib.expWad(2e18), 7389056098930650227); - assertEq(FixedPointMathLib.expWad(3e18), 20085536923187667741); + assertEq(FixedPointMathLib.expWad(2e18), 7389056098930650225); + assertEq(FixedPointMathLib.expWad(3e18), 20085536923187667726); // True value: 20085536923187667740.92 - assertEq(FixedPointMathLib.expWad(10e18), 220264657948067165169_80); + assertEq(FixedPointMathLib.expWad(10e18), 22026465794806716538705); // True value: 22026465794806716516957.90 // Relative error 9.987984547746668e-22 - assertEq(FixedPointMathLib.expWad(50e18), 5184705528587072464_148529318587763226117); + assertEq(FixedPointMathLib.expWad(50e18), 5184705528587072466219726447701454162597); // True value: 5184705528587072464_087453322933485384827.47 // Relative error: 1.1780031733243328e-20 assertEq( FixedPointMathLib.expWad(100e18), - 268811714181613544841_34666106240937146178367581647816351662017 + 26881171418161354463938235454088674598191104000000000000000000 ); // True value: 268811714181613544841_26255515800135873611118773741922415191608 // Relative error: 3.128803544297531e-22 assertEq( FixedPointMathLib.expWad(135305999368893231588), - 578960446186580976_50144101621524338577433870140581303254786265309376407432913 + 57896044618658097668148077575359153358666366564063646842880000000000000000000 ); // True value: 578960446186580976_49816762928942336782129491980154662247847962410455084893091 // Relative error: 5.653904247484822e-21 @@ -51,10 +51,15 @@ contract FixedPointMathLibTest is SoladyTest { // Notes on lambertW0Wad: // - // If you want to attempt finding a better approximation, look at + // The historical `expWad` / `lnWad` constants came from // https://github.com/recmo/experiment-solexp/blob/main/approximate_mpmath.ipynb - // I somehow can't get it to reproduce the approximation constants for `lnWad`. - // Let me know if you can get the code to reproduce the approximation constants for `lnWad`. + // and were not reproducible from the published notebook. + // The current constants are derived from scratch instead: symmetric rational + // approximations (exp: `q(v) = p(-v)` even/odd split; ln: `2 * atanh(s)` with + // `s = (x - sqrt(2)) / (x + sqrt(2))`), fitted with Lawson IRLS at 50-digit + // precision, quantized to the `2**96` grid, and ulp-tuned against the exact + // integer pipeline. The generator scripts reproduce every constant + // deterministically (see the pull request that introduced them). event TestingLambertW0WadMonotonicallyIncreasing( int256 a, int256 b, int256 w0a, int256 w0b, bool success, uint256 gasUsed @@ -72,82 +77,82 @@ contract FixedPointMathLibTest is SoladyTest { _checkLambertW0Wad(3, 2); _checkLambertW0Wad(131071, 131070); _checkLambertW0Wad(17179869183, 17179868887); - _checkLambertW0Wad(1000000000000000000, 567143290409783872); + _checkLambertW0Wad(1000000000000000000, 567143290409783873); _checkLambertW0Wad(-3678794411715, -3678807945318); - _checkLambertW0Wad(_LAMBERT_W0_MIN, -999999999741585709); + _checkLambertW0Wad(_LAMBERT_W0_MIN, -999999997926232316); // These are exact values. - _checkLambertW0Wad(2 ** 255 - 1, 130435123404408416612); - _checkLambertW0Wad(2 ** 254 - 1, 129747263755102316133); - _checkLambertW0Wad(2 ** 253 - 1, 129059431996357330139); + _checkLambertW0Wad(2 ** 255 - 1, 130435123404408416611); + _checkLambertW0Wad(2 ** 254 - 1, 129747263755102316132); + _checkLambertW0Wad(2 ** 253 - 1, 129059431996357330138); _checkLambertW0Wad(2 ** 252 - 1, 128371628422812486425); _checkLambertW0Wad(2 ** 251 - 1, 127683853333788079721); - _checkLambertW0Wad(2 ** 250 - 1, 126996107033385166927); + _checkLambertW0Wad(2 ** 250 - 1, 126996107033385166926); _checkLambertW0Wad(2 ** 249 - 1, 126308389830587715420); _checkLambertW0Wad(2 ** 248 - 1, 125620702039367489656); _checkLambertW0Wad(2 ** 247 - 1, 124933043978791764502); - _checkLambertW0Wad(2 ** 246 - 1, 124245415973133957088); - _checkLambertW0Wad(2 ** 245 - 1, 123557818351987272451); - _checkLambertW0Wad(2 ** 244 - 1, 122870251450381461880); - _checkLambertW0Wad(2 ** 243 - 1, 122182715608902796703); - _checkLambertW0Wad(2 ** 242 - 1, 121495211173817364188); - _checkLambertW0Wad(2 ** 241 - 1, 120807738497197796422); - _checkLambertW0Wad(2 ** 240 - 1, 120120297937053547320); - _checkLambertW0Wad(2 ** 239 - 1, 119432889857464837488); - _checkLambertW0Wad(2 ** 238 - 1, 118745514628720391363); - _checkLambertW0Wad(2 ** 237 - 1, 118058172627459096009); + _checkLambertW0Wad(2 ** 246 - 1, 124245415973133957089); + _checkLambertW0Wad(2 ** 245 - 1, 123557818351987272452); + _checkLambertW0Wad(2 ** 244 - 1, 122870251450381461881); + _checkLambertW0Wad(2 ** 243 - 1, 122182715608902796704); + _checkLambertW0Wad(2 ** 242 - 1, 121495211173817364189); + _checkLambertW0Wad(2 ** 241 - 1, 120807738497197796423); + _checkLambertW0Wad(2 ** 240 - 1, 120120297937053547321); + _checkLambertW0Wad(2 ** 239 - 1, 119432889857464837489); + _checkLambertW0Wad(2 ** 238 - 1, 118745514628720391364); + _checkLambertW0Wad(2 ** 237 - 1, 118058172627459096010); _checkLambertW0Wad(2 ** 236 - 1, 117370864236815716134); _checkLambertW0Wad(2 ** 235 - 1, 116683589846570805279); _checkLambertW0Wad(2 ** 234 - 1, 115996349853304958814); _checkLambertW0Wad(2 ** 233 - 1, 115309144660557560280); - _checkLambertW0Wad(2 ** 232 - 1, 114621974678990178815); + _checkLambertW0Wad(2 ** 232 - 1, 114621974678990178814); _checkLambertW0Wad(2 ** 231 - 1, 113934840326554781918); - _checkLambertW0Wad(2 ** 230 - 1, 113247742028666934564); - _checkLambertW0Wad(2 ** 229 - 1, 112560680218384162820); - _checkLambertW0Wad(2 ** 228 - 1, 111873655336589667598); - _checkLambertW0Wad(2 ** 227 - 1, 111186667832181581935); - _checkLambertW0Wad(2 ** 226 - 1, 110499718162267973459); - _checkLambertW0Wad(2 ** 225 - 1, 109812806792367802251); - _checkLambertW0Wad(2 ** 224 - 1, 109125934196618053331); + _checkLambertW0Wad(2 ** 230 - 1, 113247742028666934563); + _checkLambertW0Wad(2 ** 229 - 1, 112560680218384162819); + _checkLambertW0Wad(2 ** 228 - 1, 111873655336589667597); + _checkLambertW0Wad(2 ** 227 - 1, 111186667832181581934); + _checkLambertW0Wad(2 ** 226 - 1, 110499718162267973458); + _checkLambertW0Wad(2 ** 225 - 1, 109812806792367802250); + _checkLambertW0Wad(2 ** 224 - 1, 109125934196618053330); _checkLambertW0Wad(2 ** 223 - 1, 108439100857987272488); _checkLambertW0Wad(2 ** 222 - 1, 107752307268495744067); - _checkLambertW0Wad(2 ** 221 - 1, 107065553929442559763); - _checkLambertW0Wad(2 ** 220 - 1, 106378841351639838444); - _checkLambertW0Wad(2 ** 219 - 1, 105692170055654368478); - _checkLambertW0Wad(2 ** 218 - 1, 105005540572056956171); - _checkLambertW0Wad(2 ** 217 - 1, 104318953441679776592); + _checkLambertW0Wad(2 ** 221 - 1, 107065553929442559764); + _checkLambertW0Wad(2 ** 220 - 1, 106378841351639838445); + _checkLambertW0Wad(2 ** 219 - 1, 105692170055654368479); + _checkLambertW0Wad(2 ** 218 - 1, 105005540572056956172); + _checkLambertW0Wad(2 ** 217 - 1, 104318953441679776593); _checkLambertW0Wad(2 ** 216 - 1, 103632409215882036434); - _checkLambertW0Wad(2 ** 215 - 1, 102945908456824272609); - _checkLambertW0Wad(2 ** 214 - 1, 102259451737751625038); - _checkLambertW0Wad(2 ** 213 - 1, 101573039643286437675); + _checkLambertW0Wad(2 ** 215 - 1, 102945908456824272608); + _checkLambertW0Wad(2 ** 214 - 1, 102259451737751625037); + _checkLambertW0Wad(2 ** 213 - 1, 101573039643286437674); _checkLambertW0Wad(2 ** 212 - 1, 100886672769730558166); _checkLambertW0Wad(2 ** 211 - 1, 100200351725377723788); _checkLambertW0Wad(2 ** 210 - 1, 99514077130836439501); - _checkLambertW0Wad(2 ** 209 - 1, 98827849619363773067); - _checkLambertW0Wad(2 ** 208 - 1, 98141669837210512407); - _checkLambertW0Wad(2 ** 207 - 1, 97455538443978151616); - _checkLambertW0Wad(2 ** 206 - 1, 96769456112988194563); - _checkLambertW0Wad(2 ** 205 - 1, 96083423531664288650); - _checkLambertW0Wad(2 ** 204 - 1, 95397441401927726359); - _checkLambertW0Wad(2 ** 203 - 1, 94711510440606878644); + _checkLambertW0Wad(2 ** 209 - 1, 98827849619363773068); + _checkLambertW0Wad(2 ** 208 - 1, 98141669837210512408); + _checkLambertW0Wad(2 ** 207 - 1, 97455538443978151617); + _checkLambertW0Wad(2 ** 206 - 1, 96769456112988194565); + _checkLambertW0Wad(2 ** 205 - 1, 96083423531664288651); + _checkLambertW0Wad(2 ** 204 - 1, 95397441401927726360); + _checkLambertW0Wad(2 ** 203 - 1, 94711510440606878645); _checkLambertW0Wad(2 ** 202 - 1, 94025631379861152095); _checkLambertW0Wad(2 ** 201 - 1, 93339804967620091367); - _checkLambertW0Wad(2 ** 200 - 1, 92654031968038279517); - _checkLambertW0Wad(2 ** 199 - 1, 91968313161966721893); - _checkLambertW0Wad(2 ** 198 - 1, 91282649347441434152); - _checkLambertW0Wad(2 ** 197 - 1, 90597041340189991908); - _checkLambertW0Wad(2 ** 196 - 1, 89911489974156838659); - _checkLambertW0Wad(2 ** 195 - 1, 89225996102048190100); + _checkLambertW0Wad(2 ** 200 - 1, 92654031968038279516); + _checkLambertW0Wad(2 ** 199 - 1, 91968313161966721892); + _checkLambertW0Wad(2 ** 198 - 1, 91282649347441434151); + _checkLambertW0Wad(2 ** 197 - 1, 90597041340189991907); + _checkLambertW0Wad(2 ** 196 - 1, 89911489974156838658); + _checkLambertW0Wad(2 ** 195 - 1, 89225996102048190099); _checkLambertW0Wad(2 ** 194 - 1, 88540560595897416858); - _checkLambertW0Wad(2 ** 193 - 1, 87855184347651834275); + _checkLambertW0Wad(2 ** 193 - 1, 87855184347651834274); _checkLambertW0Wad(2 ** 192 - 1, 87169868269781877263); _checkLambertW0Wad(2 ** 191 - 1, 86484613295913690725); _checkLambertW0Wad(2 ** 190 - 1, 85799420381486221653); _checkLambertW0Wad(2 ** 189 - 1, 85114290504433958190); - _checkLambertW0Wad(2 ** 188 - 1, 84429224665896523735); + _checkLambertW0Wad(2 ** 188 - 1, 84429224665896523736); _checkLambertW0Wad(2 ** 187 - 1, 83744223890956400983); _checkLambertW0Wad(2 ** 186 - 1, 83059289229406131801); _checkLambertW0Wad(2 ** 185 - 1, 82374421756546414467); - _checkLambertW0Wad(2 ** 184 - 1, 81689622574016600237); + _checkLambertW0Wad(2 ** 184 - 1, 81689622574016600238); _checkLambertW0Wad(2 ** 183 - 1, 81004892810659176931); _checkLambertW0Wad(2 ** 182 - 1, 80320233623419918558); _checkLambertW0Wad(2 ** 181 - 1, 79635646198285477393); @@ -157,7 +162,7 @@ contract FixedPointMathLibTest is SoladyTest { _checkLambertW0Wad(2 ** 177 - 1, 76898038910840689756); _checkLambertW0Wad(2 ** 176 - 1, 76213829173218558571); _checkLambertW0Wad(2 ** 175 - 1, 75529698981200547567); - _checkLambertW0Wad(2 ** 174 - 1, 74845649753881648207); + _checkLambertW0Wad(2 ** 174 - 1, 74845649753881648206); _checkLambertW0Wad(2 ** 173 - 1, 74161682948497332759); _checkLambertW0Wad(2 ** 172 - 1, 73477800061797780656); _checkLambertW0Wad(2 ** 171 - 1, 72794002631484376331); @@ -177,99 +182,99 @@ contract FixedPointMathLibTest is SoladyTest { _checkLambertW0Wad(2 ** 157 - 1, 63230784396575459844); _checkLambertW0Wad(2 ** 156 - 1, 62548486454213176429); _checkLambertW0Wad(2 ** 155 - 1, 61866305617161244980); - _checkLambertW0Wad(2 ** 154 - 1, 61184244420081220067); - _checkLambertW0Wad(2 ** 153 - 1, 60502305480354769865); - _checkLambertW0Wad(2 ** 152 - 1, 59820491501706673077); - _checkLambertW0Wad(2 ** 151 - 1, 59138805278027624755); + _checkLambertW0Wad(2 ** 154 - 1, 61184244420081220068); + _checkLambertW0Wad(2 ** 153 - 1, 60502305480354769866); + _checkLambertW0Wad(2 ** 152 - 1, 59820491501706673078); + _checkLambertW0Wad(2 ** 151 - 1, 59138805278027624756); _checkLambertW0Wad(2 ** 150 - 1, 58457249697410179101); _checkLambertW0Wad(2 ** 149 - 1, 57775827746412203235); - _checkLambertW0Wad(2 ** 148 - 1, 57094542514563356374); - _checkLambertW0Wad(2 ** 147 - 1, 56413397199131353678); - _checkLambertW0Wad(2 ** 146 - 1, 55732395110166133991); - _checkLambertW0Wad(2 ** 145 - 1, 55051539675841537897); + _checkLambertW0Wad(2 ** 148 - 1, 57094542514563356373); + _checkLambertW0Wad(2 ** 147 - 1, 56413397199131353677); + _checkLambertW0Wad(2 ** 146 - 1, 55732395110166133989); + _checkLambertW0Wad(2 ** 145 - 1, 55051539675841537895); _checkLambertW0Wad(2 ** 144 - 1, 54370834448115730535); _checkLambertW0Wad(2 ** 143 - 1, 53690283108733387465); _checkLambertW0Wad(2 ** 142 - 1, 53009889475594618649); _checkLambertW0Wad(2 ** 141 - 1, 52329657509517754228); _checkLambertW0Wad(2 ** 140 - 1, 51649591321425477661); - _checkLambertW0Wad(2 ** 139 - 1, 50969695179986390948); - _checkLambertW0Wad(2 ** 138 - 1, 50289973519746960243); - _checkLambertW0Wad(2 ** 137 - 1, 49610430949791948630); - _checkLambertW0Wad(2 ** 136 - 1, 48931072262974930811); + _checkLambertW0Wad(2 ** 139 - 1, 50969695179986390949); + _checkLambertW0Wad(2 ** 138 - 1, 50289973519746960244); + _checkLambertW0Wad(2 ** 137 - 1, 49610430949791948631); + _checkLambertW0Wad(2 ** 136 - 1, 48931072262974930812); _checkLambertW0Wad(2 ** 135 - 1, 48251902445764340905); _checkLambertW0Wad(2 ** 134 - 1, 47572926688754773801); _checkLambertW0Wad(2 ** 133 - 1, 46894150397897992742); - _checkLambertW0Wad(2 ** 132 - 1, 46215579206513348095); + _checkLambertW0Wad(2 ** 132 - 1, 46215579206513348096); _checkLambertW0Wad(2 ** 131 - 1, 45537218988143149666); - _checkLambertW0Wad(2 ** 130 - 1, 44859075870325031417); - _checkLambertW0Wad(2 ** 129 - 1, 44181156249360587882); - _checkLambertW0Wad(2 ** 128 - 1, 43503466806167642613); - _checkLambertW0Wad(2 ** 127 - 1, 42826014523312541917); - _checkLambertW0Wad(2 ** 126 - 1, 42148806703328979292); - _checkLambertW0Wad(2 ** 125 - 1, 41471850988441194251); - _checkLambertW0Wad(2 ** 124 - 1, 40795155381822122767); - _checkLambertW0Wad(2 ** 123 - 1, 40118728270531400808); + _checkLambertW0Wad(2 ** 130 - 1, 44859075870325031418); + _checkLambertW0Wad(2 ** 129 - 1, 44181156249360587883); + _checkLambertW0Wad(2 ** 128 - 1, 43503466806167642614); + _checkLambertW0Wad(2 ** 127 - 1, 42826014523312541918); + _checkLambertW0Wad(2 ** 126 - 1, 42148806703328979293); + _checkLambertW0Wad(2 ** 125 - 1, 41471850988441194252); + _checkLambertW0Wad(2 ** 124 - 1, 40795155381822122768); + _checkLambertW0Wad(2 ** 123 - 1, 40118728270531400809); _checkLambertW0Wad(2 ** 122 - 1, 39442578450294263667); - _checkLambertW0Wad(2 ** 121 - 1, 38766715152300604375); + _checkLambertW0Wad(2 ** 121 - 1, 38766715152300604376); _checkLambertW0Wad(2 ** 120 - 1, 38091148072224059569); _checkLambertW0Wad(2 ** 119 - 1, 37415887401684336100); - _checkLambertW0Wad(2 ** 118 - 1, 36740943862402491609); - _checkLambertW0Wad(2 ** 117 - 1, 36066328743329022902); - _checkLambertW0Wad(2 ** 116 - 1, 35392053941058967434); + _checkLambertW0Wad(2 ** 118 - 1, 36740943862402491610); + _checkLambertW0Wad(2 ** 117 - 1, 36066328743329022903); + _checkLambertW0Wad(2 ** 116 - 1, 35392053941058967435); _checkLambertW0Wad(2 ** 115 - 1, 34718132003887455986); _checkLambertW0Wad(2 ** 114 - 1, 34044576179904059477); _checkLambertW0Wad(2 ** 113 - 1, 33371400469575784902); - _checkLambertW0Wad(2 ** 112 - 1, 32698619683327803297); - _checkLambertW0Wad(2 ** 111 - 1, 32026249504699254799); - _checkLambertW0Wad(2 ** 110 - 1, 31354306559730344521); + _checkLambertW0Wad(2 ** 112 - 1, 32698619683327803298); + _checkLambertW0Wad(2 ** 111 - 1, 32026249504699254800); + _checkLambertW0Wad(2 ** 110 - 1, 31354306559730344522); _checkLambertW0Wad(2 ** 109 - 1, 30682808493328298780); - _checkLambertW0Wad(2 ** 108 - 1, 30011774053465850808); - _checkLambertW0Wad(2 ** 107 - 1, 29341223184189485097); - _checkLambertW0Wad(2 ** 106 - 1, 28671177128558970924); - _checkLambertW0Wad(2 ** 105 - 1, 28001658542808735364); + _checkLambertW0Wad(2 ** 108 - 1, 30011774053465850809); + _checkLambertW0Wad(2 ** 107 - 1, 29341223184189485098); + _checkLambertW0Wad(2 ** 106 - 1, 28671177128558970925); + _checkLambertW0Wad(2 ** 105 - 1, 28001658542808735365); _checkLambertW0Wad(2 ** 104 - 1, 27332691623220201135); - _checkLambertW0Wad(2 ** 103 - 1, 26664302247428250682); - _checkLambertW0Wad(2 ** 102 - 1, 25996518132161712657); + _checkLambertW0Wad(2 ** 103 - 1, 26664302247428250683); + _checkLambertW0Wad(2 ** 102 - 1, 25996518132161712658); _checkLambertW0Wad(2 ** 101 - 1, 25329369009746106264); _checkLambertW0Wad(2 ** 100 - 1, 24662886826087826761); _checkLambertW0Wad(2 ** 99 - 1, 23997105963326166352); - _checkLambertW0Wad(2 ** 98 - 1, 23332063490900058530); - _checkLambertW0Wad(2 ** 97 - 1, 22667799449451523321); + _checkLambertW0Wad(2 ** 98 - 1, 23332063490900058531); + _checkLambertW0Wad(2 ** 97 - 1, 22667799449451523322); _checkLambertW0Wad(2 ** 96 - 1, 22004357172804292983); - _checkLambertW0Wad(2 ** 95 - 1, 21341783654247925671); - _checkLambertW0Wad(2 ** 94 - 1, 20680129964567978803); - _checkLambertW0Wad(2 ** 93 - 1, 20019451730746615034); + _checkLambertW0Wad(2 ** 95 - 1, 21341783654247925672); + _checkLambertW0Wad(2 ** 94 - 1, 20680129964567978804); + _checkLambertW0Wad(2 ** 93 - 1, 20019451730746615035); _checkLambertW0Wad(2 ** 92 - 1, 19359809686086176343); - _checkLambertW0Wad(2 ** 91 - 1, 18701270304772358157); - _checkLambertW0Wad(2 ** 90 - 1, 18043906536712772323); - _checkLambertW0Wad(2 ** 89 - 1, 17387798662016868795); + _checkLambertW0Wad(2 ** 91 - 1, 18701270304772358158); + _checkLambertW0Wad(2 ** 90 - 1, 18043906536712772324); + _checkLambertW0Wad(2 ** 89 - 1, 17387798662016868796); _checkLambertW0Wad(2 ** 88 - 1, 16733035288929945451); - _checkLambertW0Wad(2 ** 87 - 1, 16079714524670107222 + 1); - _checkLambertW0Wad(2 ** 86 - 1, 15427945355807184379); + _checkLambertW0Wad(2 ** 87 - 1, 16079714524670107223); + _checkLambertW0Wad(2 ** 86 - 1, 15427945355807184380); _checkLambertW0Wad(2 ** 85 - 1, 14777849284057868231); - _checkLambertW0Wad(2 ** 84 - 1, 14129562275318189632); - _checkLambertW0Wad(2 ** 83 - 1, 13483237095324880705); - _checkLambertW0Wad(2 ** 82 - 1, 12839046125789215063); + _checkLambertW0Wad(2 ** 84 - 1, 14129562275318189633); + _checkLambertW0Wad(2 ** 83 - 1, 13483237095324880706); + _checkLambertW0Wad(2 ** 82 - 1, 12839046125789215064); _checkLambertW0Wad(2 ** 81 - 1, 12197184781931118579); - _checkLambertW0Wad(2 ** 80 - 1, 11557875688514566228 - 1); - _checkLambertW0Wad(2 ** 79 - 1, 10921373820226202580); + _checkLambertW0Wad(2 ** 80 - 1, 11557875688514566228); + _checkLambertW0Wad(2 ** 79 - 1, 10921373820226202581); _checkLambertW0Wad(2 ** 78 - 1, 10287972878516218499); _checkLambertW0Wad(2 ** 77 - 1, 9658013267990184319); _checkLambertW0Wad(2 ** 76 - 1, 9031892161491509531); - _checkLambertW0Wad(2 ** 75 - 1, 8410076319328428686); + _checkLambertW0Wad(2 ** 75 - 1, 8410076319328428687); _checkLambertW0Wad(2 ** 74 - 1, 7793118576966979948); - _checkLambertW0Wad(2 ** 73 - 1, 7181679269695846234); - _checkLambertW0Wad(2 ** 72 - 1, 6576554370186862926); - _checkLambertW0Wad(2 ** 71 - 1, 5978712844468804878 - 1); - _checkLambertW0Wad(2 ** 70 - 1, 5389346779005776683); + _checkLambertW0Wad(2 ** 73 - 1, 7181679269695846235); + _checkLambertW0Wad(2 ** 72 - 1, 6576554370186862927); + _checkLambertW0Wad(2 ** 71 - 1, 5978712844468804878); + _checkLambertW0Wad(2 ** 70 - 1, 5389346779005776684); _checkLambertW0Wad(2 ** 69 - 1, 4809939316762921936); - _checkLambertW0Wad(2 ** 68 - 1, 4242357480017482271); + _checkLambertW0Wad(2 ** 68 - 1, 4242357480017482272); _checkLambertW0Wad(2 ** 67 - 1, 3688979548845126287); _checkLambertW0Wad(2 ** 66 - 1, 3152869312105232629); _checkLambertW0Wad(2 ** 65 - 1, 2638010157689274059); - _checkLambertW0Wad(2 ** 64 - 1, 2149604165721149566); + _checkLambertW0Wad(2 ** 64 - 1, 2149604165721149567); _checkLambertW0Wad(2 ** 63 - 1, 1694407549795038335); - _checkLambertW0Wad(2 ** 62 - 1, 1280973323147500590); + _checkLambertW0Wad(2 ** 62 - 1, 1280973323147500591); _checkLambertW0Wad(2 ** 61 - 1, 919438481612859603); _checkLambertW0Wad(2 ** 60 - 1, 620128202996354327); _checkLambertW0Wad(2 ** 59 - 1, 390213425026895126);