namespace Just.PreciseMath.Benchmarks; // Input construction stays outside timed benchmark methods. internal sealed class ArithmeticInputs { internal const int Count = 64; internal DoubleDouble[] Left { get; } = new DoubleDouble[Count]; internal DoubleDouble[] Right { get; } = new DoubleDouble[Count]; internal static ArithmeticInputs Create(string scenario, bool chain) { if (chain && scenario is not ("BothResidual" or "Mixed")) { throw new ArgumentOutOfRangeException(nameof(scenario)); } if (scenario is not ("BinaryExact" or "DecimalResidual" or "BothResidual" or "Mixed" or "SignsAndScales" or "Cancellation")) { throw new ArgumentOutOfRangeException(nameof(scenario)); } ArithmeticInputs inputs = new(); for (int i = 0; i < Count; i++) { // Exact dyadic construction, not decimal-to-double residual estimation. double high = 1.25 + ((i % 7) / 32.0); double low = Math.ScaleB(1.0, -56); DoubleDouble left = DoubleDouble.FromComponents(high, low); DoubleDouble right = DoubleDouble.FromComponents(1.0 + (((i % 5) - 2) / 128.0), -low); if (!chain) { switch (scenario) { case "BinaryExact": left = new DoubleDouble(high); right = new DoubleDouble(0.75); break; case "DecimalResidual": left = (DoubleDouble)1.1m; right = new DoubleDouble(0.75); break; case "SignsAndScales": int exponent = ((i % 7) - 3) * 40; double sign = i % 2 == 0 ? 1.0 : -1.0; left = DoubleDouble.FromComponents(Math.ScaleB(sign * high, exponent), Math.ScaleB(low, exponent)); right = DoubleDouble.FromComponents(Math.ScaleB(1.125, -exponent), Math.ScaleB(-low, -exponent)); break; case "Cancellation": // Alternate near cancellation in addition and subtraction. right = DoubleDouble.FromComponents(i % 2 == 0 ? -high : high, -low / 2.0); break; } } if (scenario == "Mixed") { // Alternate both residuals, right zero-low, left zero-low, both zero-low. if (i % 4 is 2 or 3) { left = new DoubleDouble(left.High); } if (i % 4 is 1 or 3) { right = new DoubleDouble(right.High); } } inputs.Left[i] = left; inputs.Right[i] = right; } inputs.Validate(scenario, chain); return inputs; } private void Validate(string scenario, bool chain) { for (int i = 0; i < Count; i++) { ValidateOrdinary(Left[i]); ValidateOrdinary(Right[i]); bool expectedLeftLow = scenario != "BinaryExact" && (scenario != "Mixed" || i % 4 < 2); bool expectedRightLow = scenario is not ("BinaryExact" or "DecimalResidual") && (scenario != "Mixed" || i % 2 == 0); if ((Left[i].Low != 0.0) != expectedLeftLow || (Right[i].Low != 0.0) != expectedRightLow) { throw new InvalidOperationException("Benchmark residual shape does not match its scenario."); } } if (!chain) { return; } // Setup-only guard against a chain drifting into special or exponent-boundary // paths. This is fixture validation, not an independent accuracy oracle. for (int operation = 0; operation < 12; operation++) { DoubleDouble value = Left[0]; for (int i = 0; i < Count; i++) { DoubleDouble right = Right[i]; value = operation switch { 0 => value + right, 1 => value - right, 2 => value * right, 3 => value / right, 4 => value + right.High, 5 => value - right.High, 6 => value * right.High, 7 => value / right.High, 8 => right.High + value, 9 => right.High - value, 10 => right.High * value, 11 => right.High / value, _ => throw new InvalidOperationException() }; ValidateOrdinary(value); } } } private static void ValidateOrdinary(DoubleDouble value) { if (!DoubleDouble.IsFinite(value) || value.High == 0.0 || Math.Abs(Math.ILogB(value.High)) > 200 || DoubleDouble.FromComponents(value.High, value.Low) != value || (value.Low == 0.0 && BitConverter.DoubleToInt64Bits(value.Low) != 0)) { throw new InvalidOperationException("Benchmark input/chain left the normalized ordinary finite range."); } } }