diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs b/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs index 6b9773958..7405f9686 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs @@ -320,7 +320,7 @@ private static EDecimal Hypot(EDecimal a, EDecimal b, EContext context) else { var ratio = small.Divide(large, context); - return ratio.MultiplyAndAdd(ratio, EDecimal.One, context).Sqrt(context).Multiply(large, context); + return ratio.MultiplyAndAdd(ratio, EDecimal.One, context).SqrtByIntegerRoot(context).Multiply(large, context); } } @@ -332,8 +332,8 @@ public static Complex Sqrt(Complex num) // From https://source.dot.net/#System.Runtime.Numerics/System/Numerics/Complex.cs,7dc9c2ee4f99814a if (num is Real { EDecimal: var real }) if (real.IsNegative) - return Complex.Create(0, real.Negate().Sqrt(context)); - else return Real.Create(real.Sqrt(context)); + return Complex.Create(0, real.Negate().SqrtByIntegerRoot(context)); + else return Real.Create(real.SqrtByIntegerRoot(context)); else { @@ -369,12 +369,12 @@ public static Complex Sqrt(Complex num) EDecimal x, y; if (!re.IsNegative) { - x = Hypot(re, im, context).Add(re, context).Divide(2, context).Sqrt(context); + x = Hypot(re, im, context).Add(re, context).Divide(2, context).SqrtByIntegerRoot(context); y = im.Divide(x.Multiply(2, context), context); } else { - y = Hypot(re, im, context).Subtract(re, context).Divide(2, context).Sqrt(context); + y = Hypot(re, im, context).Subtract(re, context).Divide(2, context).SqrtByIntegerRoot(context); if (im.IsNegative) y = -y; x = im.Divide(y.Multiply(2, context), context); } @@ -451,7 +451,7 @@ static Complex BinaryIntPow(Complex num, EInteger val) && halfPower.Denominator.Equals(EInteger.FromInt32(2)) && halfPower.Numerator.Abs().CompareTo(EInteger.FromInt32(1 << 20)) <= 0) { var halfContext = MathS.Settings.DecimalPrecisionContext; - var root = rootBase.Sqrt(halfContext); + var root = rootBase.SqrtByIntegerRoot(halfContext); var n = halfPower.Numerator.Abs().ToInt32Checked(); var raised = n == 1 ? root : root.Pow(n, halfContext); return Real.Create(halfPower.Numerator.Sign < 0 ? EDecimal.One.Divide(raised, halfContext) : raised); @@ -737,11 +737,11 @@ public static Complex Cotan(Complex num) var (x, y) = (num.RealPart.EDecimal, num.ImaginaryPart.EDecimal); var xp1 = x.Increment(); var xm1 = x.Decrement(); - var rho = xp1.MultiplyAndAdd(xp1, y.Multiply(y, context), context).Sqrt(context); - var sigma = xm1.MultiplyAndAdd(xm1, y.Multiply(y, context), context).Sqrt(context); + var rho = xp1.MultiplyAndAdd(xp1, y.Multiply(y, context), context).SqrtByIntegerRoot(context); + var sigma = xm1.MultiplyAndAdd(xm1, y.Multiply(y, context), context).SqrtByIntegerRoot(context); var alpha = rho.Add(sigma, context).Divide(2, context); return (rho.Subtract(sigma, context).Divide(2, context), - alpha.MultiplyAndSubtract(alpha, EDecimal.One, context).Sqrt(context).Add(alpha, context).NaturalLogarithm(context).Multiply((y.IsNegative || y.IsZero) ? -1 : 1, context)); + alpha.MultiplyAndSubtract(alpha, EDecimal.One, context).SqrtByIntegerRoot(context).Add(alpha, context).NaturalLogarithm(context).Multiply((y.IsNegative || y.IsZero) ? -1 : 1, context)); } /// Calculates the exact value of arcsine of num diff --git a/Sources/AngouriMath/Docs/WhatsNew/version_performance_control.md b/Sources/AngouriMath/Docs/WhatsNew/version_performance_control.md index e08a16d85..9dd586011 100644 --- a/Sources/AngouriMath/Docs/WhatsNew/version_performance_control.md +++ b/Sources/AngouriMath/Docs/WhatsNew/version_performance_control.md @@ -256,6 +256,40 @@ cent — its measured run-to-run spread on mean time is up to 51.8%. A move of 8 outside that band by a wide margin and agrees in sign and rough size with the allocation column beside it. The small rows from the same run are still not worth reading, and are not quoted. +## The 2074th: the logarithm and the arctangent reduced by a table, the cosine from the sine, the square root an integer one + +After the 2072nd the remaining factor to mpmath was the term count: eighty artanh terms for a +logarithm at a hundred digits, forty-seven for an arctangent after three square-root +halvings, and two series -- the sine's and the cosine's -- for either. The logarithm's +mantissa, already within `[1/sqrt 2, sqrt 2]`, is divided by the nearest `1 + j/64`, whose +logarithm the constant cache holds (built outward from `ln 1 = 0` when first asked, one short +series a step), so the series runs on a quotient within 1/128 of one: twenty-five terms. The +arctangent's argument, within `[0, 1]`, is brought within 1/128 of zero by the nearest `j/64` +the same way, `arctan x = arctan c + arctan((x - c)/(1 + xc))`: thirty terms and no square +root. The cosine at the sine's reduced argument, which is within a twentieth of zero, is +`sqrt(1 - sin^2)` and cancels nothing; the other way round is what lost half the digits in the +2050th. And the square root of a decimal is the integer square root of its mantissa padded to +twice the digits, with a sticky digit so the rounding to the context is the correct one in +every mode, as PeterO's is -- Newton from the root of the top half of the bits, three +divisions at the full width where a power of two above the root took one per bit of the +exponent. The hundred-digit values are the ones pinned before, to the last digit. + +| benchmark | 2073rd | 2074th | allocation | time | +|---|--:|--:|--:|--:| +| `EvalTranscendentalFresh` | 59,136 | **34,608** | **−41.5%** | 33 → **16 µs** | +| `EvalTrig` | 82,104 | **69,792** | **−15.0%** | 37 → **32 µs** | +| `EvalTrigPrecise` | 525,289 | **336,360** | **−36.0%** | 617 → **406 µs** | +| `SolveEasy` | 981,996 | **783,622** | **−20.2%** | 491 → **390 µs** | +| `SolveHard` | 11,708,080 | **10,053,640** | **−14.1%** | 68 → 70 ms | +| every other entry | | | within 1.3% | | + +Bytes allocated per call, same machine, both columns by the gate in one session; the gate's +baseline was taken from this run. Alone, one probe on both builds: at a hundred digits `ln` +18.0 → 8.4 µs, `arctan` 24.6 → 7.7, `sin` 15.2 → 13.1; at five hundred `ln` 777 → 241 µs, +`sin` 228 → 141, and the square root 41 → 22 (PeterO's against the integer one); at two +thousand digits the square root 441 → 174 µs. mpmath at a hundred digits is `ln` 4.5, `arctan` +3.9, `sin` 5.6. + ## The 2073rd: the rational pre-check confirms a maybe, and reads two thousand digits `EvalTrig` is `sin 1 + cos 1 + tan 1`, and after the 2072nd fifty of its eighty-three diff --git a/Sources/AngouriMath/Functions/InternalAMExtensions.cs b/Sources/AngouriMath/Functions/InternalAMExtensions.cs index d6d5c6877..558c950ac 100644 --- a/Sources/AngouriMath/Functions/InternalAMExtensions.cs +++ b/Sources/AngouriMath/Functions/InternalAMExtensions.cs @@ -8,6 +8,7 @@ using System; using System.Numerics; using System.Collections.Concurrent; +using System.Collections.Generic; using AngouriMath.Core.Exceptions; using PeterO.Numbers; using AngouriMath.Extensions; @@ -228,6 +229,45 @@ public BigInteger TenTo(int power) return result; } private readonly Dictionary powersOfTen = new(); + /// + /// ln(1 + j/64) in fixed point, for = j + /// from -19 to 27, which covers [1/sqrt 2, sqrt 2]: the logarithm's + /// argument is divided by the nearest of these before its series, so that the + /// series' argument is within 1/256. Built outward from ln 1 = 0 when first + /// asked, one short series a step -- ln((64 + j)/(63 + j)) = 2 artanh(1/(127 + 2j)) + /// above one and ln((64 - k)/(65 - k)) = -2 artanh(1/(129 - 2k)) below it. + /// + public BigInteger FixedLnOfOnePlus(int sixtyFourths) + { + if (sixtyFourths >= 0) + { + while (lnAbove.Count <= sixtyFourths) + lnAbove.Add(lnAbove[lnAbove.Count - 1] + TwiceArtanh(FixedOne / (127 + 2 * lnAbove.Count), FixedBits)); + return lnAbove[sixtyFourths]; + } + while (lnBelow.Count <= -sixtyFourths) + lnBelow.Add(lnBelow[lnBelow.Count - 1] - TwiceArtanh(FixedOne / (129 - 2 * lnBelow.Count), FixedBits)); + return lnBelow[-sixtyFourths]; + } + private readonly List lnAbove = new() { BigInteger.Zero }; + private readonly List lnBelow = new() { BigInteger.Zero }; + /// + /// arctan(j/64) in fixed point for = j from + /// 0 to 64: the arctangent's argument is brought within 1/128 of zero by the + /// nearest of these, arctan x = arctan c + arctan((x - c)/(1 + xc)). Built + /// up from arctan 0 = 0 when first asked, one short series a step: + /// arctan(j/64) - arctan((j - 1)/64) = arctan(64/(4096 + j(j - 1))). + /// + public BigInteger FixedArctanOf(int sixtyFourths) + { + while (arctans.Count <= sixtyFourths) + { + var j = arctans.Count; + arctans.Add(arctans[j - 1] + ArctanSeries((FixedOne << 6) / (4096 + j * (j - 1)), FixedBits)); + } + return arctans[sixtyFourths]; + } + private readonly List arctans = new() { BigInteger.Zero }; /// The square root of 2, in fixed point, to fifty digits public BigInteger FixedSqrt2 { get; } /// Pi in fixed point, to the context's digits @@ -461,20 +501,19 @@ internal static (EDecimal Sin, EDecimal Cos) SinAndCos(EDecimal x, EContext cont } var square = MultiplyFixed(fixedX, fixedX, bits); - // sin: x - x^3/3! + ...; cos: 1 - x^2/2! + ... + // sin: x - x^3/3! + ...; and the cosine is sqrt(1 - sin^2), which at |x| < 1/20 + // is within 1/800 of one and cancels nothing -- the other way round, the sine + // from the cosine, is what lost half the digits (see the remarks). var sinTerm = fixedX; var sin = fixedX; - var cosTerm = one; - var cos = one; for (var i = 1; i < 100000; i++) { - cosTerm = -MultiplyFixed(cosTerm, square, bits) / ((2 * i - 1) * (2 * i)); sinTerm = -MultiplyFixed(sinTerm, square, bits) / ((2 * i) * (2 * i + 1)); - if (cosTerm.IsZero && sinTerm.IsZero) + if (sinTerm.IsZero) break; - cos += cosTerm; sin += sinTerm; } + var cos = IntegerSquareRoot((one - MultiplyFixed(sin, sin, bits)) << bits); for (var i = 0; i < halvings; i++) { // sin(2y) = 2 sin(y) cos(y), cos(2y) = 2 cos(y)^2 - 1 @@ -574,18 +613,20 @@ public static EDecimal Arcsin(this EDecimal x, EContext context) var working = WithGuardDigits(context, 5); var oneMinusSquare = EDecimal.One.Subtract(x, working).Multiply(EDecimal.One.Add(x, working), working); - return Arctan(x.Divide(oneMinusSquare.Sqrt(working), working), working).RoundToPrecision(context); + return Arctan(x.Divide(oneMinusSquare.SqrtByIntegerRoot(working), working), working).RoundToPrecision(context); } /// Analogy of /// - /// Reduced to [0, 1] by arctan(x) = pi/2 - arctan(1/x), then halved -- - /// arctan(x) = 2 arctan(x/(1 + sqrt(1 + x^2))) -- until the argument is below a - /// twentieth, where the series x - x^3/3 + x^5/5 - ... is forty terms at a hundred - /// digits, each a multiplication and a division by a small integer; the halvings come - /// back as a power of two, which costs no digits. It was the arcsine of - /// x/sqrt(1 + x^2), two milliseconds at a hundred digits for an argument of a - /// third. https://github.com/asc-community/AngouriMath/issues/1338 + /// Reduced to [0, 1] by arctan(x) = pi/2 - arctan(1/x), then to within + /// 1/128 of zero by the nearest c = j/64 -- arctan x = arctan c + + /// arctan((x - c)/(1 + xc)), with arctan c from the constant cache -- where + /// the series y - y^3/3 + y^5/5 - ... is thirty terms at a hundred digits, each + /// a multiplication and a division by a small integer. It was halved to a twentieth + /// by arctan(x) = 2 arctan(x/(1 + sqrt(1 + x^2))), three square roots and + /// forty-seven terms; before that the arcsine of x/sqrt(1 + x^2), two + /// milliseconds at a hundred digits for an argument of a third. + /// https://github.com/asc-community/AngouriMath/issues/1338 /// public static EDecimal Arctan(this EDecimal x, EContext context) { @@ -599,24 +640,28 @@ public static EDecimal Arctan(this EDecimal x, EContext context) if (x.GreaterThan(EDecimal.One)) return ConstantCache.Lookup(working).HalfPi.Subtract(Arctan(EDecimal.One.Divide(x, working), working), working).RoundToPrecision(context); - // In fixed point from here: the square root of a fixed-point number is the - // integer square root of it shifted up, exactly floored, and the halvings come - // back as a shift. + // In fixed point from here. The nearest sixty-fourth is exact in fixed point + // (FixedBits is at least six), so the table's entry is the arctangent of it. var workingConsts = ConstantCache.Lookup(working); var bits = workingConsts.FixedBits; var one = workingConsts.FixedOne; var fixedX = ToFixed(x, bits); - var halvings = 0; - var twentieth = one / 20; - while (fixedX > twentieth) - { - var root = IntegerSquareRoot((one + MultiplyFixed(fixedX, fixedX, bits)) << bits); - fixedX = (fixedX << bits) / (one + root); - halvings++; - } - var square = MultiplyFixed(fixedX, fixedX, bits); - var power = fixedX; - var sum = fixedX; + var j = (int)(((fixedX << 6) + (one >> 1)) >> bits); + var c = (one >> 6) * j; + var y = ((fixedX - c) << bits) / (one + MultiplyFixed(fixedX, c, bits)); + var sum = ArctanSeries(y, bits) + workingConsts.FixedArctanOf(j); + return FromFixed(sum, workingConsts, working).RoundToPrecision(context); + } + + /// + /// y - y^3/3 + y^5/5 - ... in fixed point, which is arctan y; for + /// |y| within 1/128, thirty terms at a hundred digits. + /// + private static BigInteger ArctanSeries(BigInteger y, int bits) + { + var square = MultiplyFixed(y, y, bits); + var power = y; + var sum = y; for (var i = 1; i < 100000; i++) { power = -MultiplyFixed(power, square, bits); @@ -625,7 +670,7 @@ public static EDecimal Arctan(this EDecimal x, EContext context) break; sum += term; } - return FromFixed(sum << halvings, workingConsts, working).RoundToPrecision(context); + return sum; } /// Analogy of public static EDecimal Acos(this EDecimal x, EContext context) @@ -750,15 +795,36 @@ private static BigInteger MultiplyFixed(BigInteger a, BigInteger b, int bits) private static BigInteger HalveFixed(BigInteger a) => a.Sign < 0 ? -((-a) >> 1) : a >> 1; - /// The integer square root, floored: Newton's iteration from a power of two above it. + /// + /// The integer square root, floored. The root of the top half of the bits, shifted + /// back, is right to a quarter of them; one Newton step from a value on either side + /// lands at or above the root ((r + n/r)/2 >= sqrt(n)) with the error + /// squared, the next one squares it again, and the iteration then descends to the + /// floor and stops -- three divisions at the full width and three at a quarter of it, + /// where a power of two above the root took one division per bit of the exponent. + /// private static BigInteger IntegerSquareRoot(BigInteger n) { if (n.Sign <= 0) return BigInteger.Zero; - // From a power of two at or above the root, so that the iteration descends: - // the byte count is a bound on the bit length that netstandard2.0's BigInteger + // The byte count is a bound on the bit length that netstandard2.0's BigInteger // can give without a logarithm. - var root = BigInteger.One << (n.ToByteArray().Length * 4 + 1); + var bytes = n.ToByteArray().Length; + BigInteger root; + if (bytes <= 8) + { + // Fits a double's exponent; its square root is within one of the floor. + root = new BigInteger(Math.Sqrt((double)n)); + while (root * root > n) + root -= 1; + while ((root + 1) * (root + 1) <= n) + root += 1; + return root; + } + // A multiple of sixteen, so that its half is a whole number of bits. + var shift = (bytes / 2) * 8; + root = IntegerSquareRoot(n >> shift) << (shift / 2); + root = (root + n / root) >> 1; while (true) { var next = (root + n / root) >> 1; @@ -768,10 +834,48 @@ private static BigInteger IntegerSquareRoot(BigInteger n) } } + /// + /// The square root of a nonnegative finite to + /// , correctly rounded in its rounding mode, as PeterO's + /// is: the mantissa, made a whole number of + /// twice the digits and more by an even power of ten, has its integer square root + /// taken -- exactly floored, so the true root lies within one unit above it -- and a + /// sticky digit appended, 1 where the root was inexact, so that the rounding to the + /// context decides every tie as the true root would. PeterO's is Newton's iteration + /// in decimal; this is the integer one, faster by the same factor as the series above + /// it are. Zero, a negative, an infinity and NaN are PeterO's answers. + /// https://github.com/asc-community/AngouriMath/issues/1338 + /// + public static EDecimal SqrtByIntegerRoot(this EDecimal x, EContext context) + { + if (!x.IsFinite || x.IsNegative || x.IsZero) + return x.Sqrt(context); + var exponent = x.Exponent.ToInt32Checked(); + var mantissa = ToBig(x.Mantissa); + var digits = x.Precision().ToInt32Checked(); + if ((exponent & 1) != 0) + { + // sqrt(m 10^e) = sqrt(10 m) 10^((e - 1)/2): the power taken out must be even. + mantissa *= BigTen; + exponent -= 1; + digits += 1; + } + // Padded so that the root has two digits past the context's precision, for the + // rounding to read, and then one more for the sticky digit. + var precision = context.Precision.ToInt32Checked(); + var padding = Math.Max(0, (2 * (precision + 2) - digits + 1) / 2); + if (padding > 0) + mantissa *= BigInteger.Pow(BigTen, 2 * padding); + var root = IntegerSquareRoot(mantissa); + var sticky = root * root == mantissa ? BigInteger.Zero : BigInteger.One; + return EDecimal.Create(ToEInteger(root * BigTen + sticky), exponent / 2 - padding - 1).RoundToPrecision(context); + } + /// /// 2 artanh(y) as its series 2 (y + y^3/3 + y^5/5 + ...), which is - /// ln((1 + y)/(1 - y)), in fixed point; for |y| below 0.18, - /// seventy terms at a hundred digits. + /// ln((1 + y)/(1 - y)), in fixed point: twenty-five terms at a hundred digits + /// for |y| within 1/256, which the logarithm's reduction reaches, and a + /// hundred and thirty for the third that gives ln 2. /// private static BigInteger TwiceArtanh(BigInteger y, int bits) { @@ -795,12 +899,16 @@ private static BigInteger TwiceArtanh(BigInteger y, int bits) /// The natural logarithm of to the precision of /// : the argument written as a mantissa times a power of ten /// and of two, the mantissa brought between 1/sqrt(2) and sqrt(2), and - /// 2 artanh((m - 1)/(m + 1)) there, a series in a square below 0.03, in - /// fixed point. PeterO's is seven hundred - /// microseconds at a hundred digits; this is about twenty. An argument within - /// [1/sqrt(2), sqrt(2)] goes to the series as it is, so a value near 1 keeps - /// every digit rather than losing them to ln 10 - 3 ln 2 - ... cancelling. - /// Zero, a negative, an infinity and NaN are PeterO's answers. + /// the mantissa divided by the nearest t = 1 + j/64, whose logarithm the + /// constant cache holds, and 2 artanh((q - 1)/(q + 1)) for the quotient, a + /// series in a square below 1/65536 -- twenty-five terms at a hundred digits + /// where the series straight from [1/sqrt(2), sqrt(2)] was eighty -- in fixed + /// point. PeterO's is seven hundred microseconds + /// at a hundred digits; this is under ten. An argument within + /// [1/sqrt(2), sqrt(2)] goes to the reduction as it is, and one within 1/128 of + /// 1 to the series as it is, so a value near 1 keeps every digit rather than losing + /// them to ln 10 - 3 ln 2 - ... cancelling. Zero, a negative, an infinity and + /// NaN are PeterO's answers. /// https://github.com/asc-community/AngouriMath/issues/1338 /// public static EDecimal NaturalLogarithm(this EDecimal x, EContext context) @@ -827,8 +935,16 @@ public static EDecimal NaturalLogarithm(this EDecimal x, EContext context) twos++; } } - var y = ((mantissa - consts.FixedOne) << bits) / (mantissa + consts.FixedOne); + // The nearest 1 + j/64, exact in fixed point (FixedBits is at least six), and the + // mantissa over it, within 1/128 of one. + var one = consts.FixedOne; + var j = (int)((((mantissa - one) << 6) + (one >> 1)) >> bits); + if (j != 0) + mantissa = (mantissa << bits) / (one + (one >> 6) * j); + var y = ((mantissa - one) << bits) / (mantissa + one); var log = TwiceArtanh(y, bits); + if (j != 0) + log += consts.FixedLnOfOnePlus(j); if (twos != 0) log += consts.FixedLn2 * twos; if (tens != 0) diff --git a/Sources/Tests/DotnetBenchmark/performance-baseline.json b/Sources/Tests/DotnetBenchmark/performance-baseline.json index 36cf877bd..cb18b3976 100644 --- a/Sources/Tests/DotnetBenchmark/performance-baseline.json +++ b/Sources/Tests/DotnetBenchmark/performance-baseline.json @@ -1,94 +1,94 @@ { "comment": "Allocated bytes and mean nanoseconds per operation for the popular use cases of https://github.com/asc-community/AngouriMath/issues/746. Checked by PerformanceGate.cs; see Sources/AngouriMath/Docs/WhatsNew/version_performance_control.md for when updating it is legitimate.", "benchmark": "CommonFunctionsInterVersion", - "commit": "0f0124f64aa6f8357c19308053e888acc85e5619", - "measuredOn": "2026-09-15", + "commit": "bc083427dd093d78034672bee0970b7a5fb09307", + "measuredOn": "2026-09-16", "runtime": ".NET 10.0.10", "machine": "Ubuntu 26.04 LTS, X64, 8 logical cores", "cases": { "CompileEasy": { "allocatedBytes": 11031, - "meanNanoseconds": 186281.22169596356 + "meanNanoseconds": 187607.61580984932 }, "CompileHard": { - "allocatedBytes": 20434, - "meanNanoseconds": 311388.99150390626 + "allocatedBytes": 20705, + "meanNanoseconds": 313574.67550223216 }, "Derivate": { "allocatedBytes": 53111, - "meanNanoseconds": 14184.764560154506 + "meanNanoseconds": 14356.708233642577 }, "EvalEasy": { "allocatedBytes": 0, - "meanNanoseconds": 1.8897320133234774 + "meanNanoseconds": 1.8836873841605015 }, "EvalPolynomialFresh": { "allocatedBytes": 12328, - "meanNanoseconds": 4152.384998614972 + "meanNanoseconds": 4256.926479633038 }, "EvalPolynomialFresh15Digits": { "allocatedBytes": 11656, - "meanNanoseconds": 4151.804206339518 + "meanNanoseconds": 4187.276914978027 }, "EvalTranscendentalFresh": { - "allocatedBytes": 59136, - "meanNanoseconds": 32736.61446126302 + "allocatedBytes": 34608, + "meanNanoseconds": 16325.007756159855 }, "EvalTrig": { - "allocatedBytes": 82104, - "meanNanoseconds": 37378.113830566406 + "allocatedBytes": 69792, + "meanNanoseconds": 32238.372596153848 }, "EvalTrigPrecise": { - "allocatedBytes": 525289, - "meanNanoseconds": 617474.7988978794 + "allocatedBytes": 336360, + "meanNanoseconds": 405945.4066731771 }, "ParseEasy": { "allocatedBytes": 18125, - "meanNanoseconds": 5856.118166410006 + "meanNanoseconds": 5873.22529703776 }, "ParseHard": { "allocatedBytes": 3555058, - "meanNanoseconds": 1384331.1709735577 + "meanNanoseconds": 1394554.0330636161 }, "RunEasy": { "allocatedBytes": 0, - "meanNanoseconds": 20.13024140092043 + "meanNanoseconds": 20.067236744440518 }, "RunHard": { "allocatedBytes": 0, - "meanNanoseconds": 294.4334234169551 + "meanNanoseconds": 294.3466989994049 }, "RunMedium": { "allocatedBytes": 0, - "meanNanoseconds": 163.92867718140283 + "meanNanoseconds": 163.25927019119263 }, "SimplifyEasy": { "allocatedBytes": 78578, - "meanNanoseconds": 44438.70051574707 + "meanNanoseconds": 44333.93706868489 }, "SimplifyHard": { - "allocatedBytes": 182278816, - "meanNanoseconds": 118444605.13333334 + "allocatedBytes": 179367888, + "meanNanoseconds": 105143696.61538461 }, "SolveEasy": { - "allocatedBytes": 981996, - "meanNanoseconds": 491438.64869791665 + "allocatedBytes": 783622, + "meanNanoseconds": 390027.2749399039 }, "SolveEasyMedium": { "allocatedBytes": 65184, - "meanNanoseconds": 18787.276129586357 + "meanNanoseconds": 18794.714174543107 }, "SolveHard": { - "allocatedBytes": 11708080, - "meanNanoseconds": 68061885.4090909 + "allocatedBytes": 10053640, + "meanNanoseconds": 70218525.77419356 }, "SolveMedium": { "allocatedBytes": 446413, - "meanNanoseconds": 271954.6346529447 + "meanNanoseconds": 277422.89869791665 }, "SolveMediumHard": { "allocatedBytes": 1367804, - "meanNanoseconds": 8358186.242708334 + "meanNanoseconds": 8754956.344791668 } }, "ungated": {