tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

maths.spec.ts (100858B)


      1 export const description = `
      2 Util math unit tests.
      3 `;
      4 
      5 import { makeTestGroup } from '../common/framework/test_group.js';
      6 import { objectEquals } from '../common/util/util.js';
      7 import { kBit, kValue } from '../webgpu/util/constants.js';
      8 import {
      9  f16,
     10  f32,
     11  f64,
     12  float16ToUint16,
     13  float32ToUint32,
     14  uint16ToFloat16,
     15  uint32ToFloat32,
     16 } from '../webgpu/util/conversion.js';
     17 import {
     18  biasedRange,
     19  calculatePermutations,
     20  cartesianProduct,
     21  correctlyRoundedF16,
     22  correctlyRoundedF32,
     23  FlushMode,
     24  frexp,
     25  scalarF16Range,
     26  scalarF32Range,
     27  fullI32Range,
     28  lerp,
     29  linearRange,
     30  nextAfterF16,
     31  nextAfterF32,
     32  nextAfterF64,
     33  NextDirection,
     34  oneULPF16,
     35  oneULPF32,
     36  oneULPF64,
     37  lerpBigInt,
     38  linearRangeBigInt,
     39  biasedRangeBigInt,
     40 } from '../webgpu/util/math.js';
     41 import {
     42  reinterpretU16AsF16,
     43  reinterpretU32AsF32,
     44  reinterpretU64AsF64,
     45 } from '../webgpu/util/reinterpret.js';
     46 
     47 import { UnitTest } from './unit_test.js';
     48 
     49 export const g = makeTestGroup(UnitTest);
     50 
     51 /**
     52 * Utility wrapper around oneULP to test if a value is within 1 ULP(x)
     53 *
     54 * @param got number to test
     55 * @param expected number to be within 1 ULP of
     56 * @param mode should oneULP FTZ
     57 * @returns if got is within 1 ULP of expected
     58 */
     59 function withinOneULPF32(got: number, expected: number, mode: FlushMode): boolean {
     60  const ulp = oneULPF32(expected, mode);
     61  return got >= expected - ulp && got <= expected + ulp;
     62 }
     63 
     64 /**
     65 * @returns true if arrays are equal within 1ULP, doing element-wise comparison
     66 *               as needed, and considering NaNs to be equal.
     67 *
     68 * Depends on the correctness of oneULP, which is tested in this file.
     69 **
     70 * @param got array of numbers to compare for equality
     71 * @param expect array of numbers to compare against
     72 * @param mode should different subnormals be considered the same, i.e. should
     73 *              FTZ occur during comparison
     74 **/
     75 function compareArrayOfNumbersF32(
     76  got: readonly number[],
     77  expect: readonly number[],
     78  mode: FlushMode = 'flush'
     79 ): boolean {
     80  return (
     81    got.length === expect.length &&
     82    got.every((value, index) => {
     83      const expected = expect[index];
     84      return (
     85        (Number.isNaN(value) && Number.isNaN(expected)) || withinOneULPF32(value, expected, mode)
     86      );
     87    })
     88  );
     89 }
     90 
     91 /** @returns the hex value representation of a f64, from is numeric representation */
     92 function float64ToUint64(value: number): bigint {
     93  return new BigUint64Array(new Float64Array([value]).buffer)[0];
     94 }
     95 
     96 /** @returns the numeric representation of a f64, from its hex value representation */
     97 function uint64ToFloat64(bits: bigint): number {
     98  return new Float64Array(new BigUint64Array([bits]).buffer)[0];
     99 }
    100 
    101 interface nextAfterCase {
    102  val: number;
    103  dir: NextDirection;
    104  result: number;
    105 }
    106 
    107 g.test('nextAfterF64FlushToZero')
    108  .paramsSubcasesOnly<nextAfterCase>(
    109    // prettier-ignore
    110    [
    111      // Edge Cases
    112      { val: Number.NaN, dir: 'positive', result: Number.NaN },
    113      { val: Number.NaN, dir: 'negative', result: Number.NaN },
    114      { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f64.positive.infinity },
    115      { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f64.positive.infinity },
    116      { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f64.negative.infinity },
    117      { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f64.negative.infinity },
    118 
    119      // Zeroes
    120      { val: +0, dir: 'positive', result: kValue.f64.positive.min },
    121      { val: +0, dir: 'negative', result: kValue.f64.negative.max },
    122      { val: -0, dir: 'positive', result: kValue.f64.positive.min },
    123      { val: -0, dir: 'negative', result: kValue.f64.negative.max },
    124 
    125      // Subnormals
    126      { val: kValue.f64.positive.subnormal.min, dir: 'positive', result: kValue.f64.positive.min },
    127      { val: kValue.f64.positive.subnormal.min, dir: 'negative', result: kValue.f64.negative.max },
    128      { val: kValue.f64.positive.subnormal.max, dir: 'positive', result: kValue.f64.positive.min },
    129      { val: kValue.f64.positive.subnormal.max, dir: 'negative', result: kValue.f64.negative.max },
    130      { val: kValue.f64.negative.subnormal.min, dir: 'positive', result: kValue.f64.positive.min },
    131      { val: kValue.f64.negative.subnormal.min, dir: 'negative', result: kValue.f64.negative.max },
    132      { val: kValue.f64.negative.subnormal.max, dir: 'positive', result: kValue.f64.positive.min },
    133      { val: kValue.f64.negative.subnormal.max, dir: 'negative', result: kValue.f64.negative.max },
    134 
    135      // Normals
    136      { val: kValue.f64.positive.max, dir: 'positive', result: kValue.f64.positive.infinity },
    137      { val: kValue.f64.positive.max, dir: 'negative', result: kValue.f64.positive.nearest_max },
    138      { val: kValue.f64.positive.min, dir: 'positive', result: reinterpretU64AsF64(0x0010_0000_0000_0001n ) },
    139      { val: kValue.f64.positive.min, dir: 'negative', result: 0 },
    140      { val: kValue.f64.negative.max, dir: 'positive', result: 0 },
    141      { val: kValue.f64.negative.max, dir: 'negative', result: reinterpretU64AsF64(0x8010_0000_0000_0001n) },
    142      { val: kValue.f64.negative.min, dir: 'positive', result: kValue.f64.negative.nearest_min },
    143      { val: kValue.f64.negative.min, dir: 'negative', result: kValue.f64.negative.infinity },
    144      { val: reinterpretU64AsF64(0x0380_0000_0000_0000n), dir: 'positive', result: reinterpretU64AsF64(0x0380_0000_0000_0001n) },
    145      { val: reinterpretU64AsF64(0x0380_0000_0000_0000n), dir: 'negative', result: reinterpretU64AsF64(0x037f_ffff_ffff_ffffn) },
    146      { val: reinterpretU64AsF64(0x8380_0000_0000_0000n), dir: 'positive', result: reinterpretU64AsF64(0x837f_ffff_ffff_ffffn) },
    147      { val: reinterpretU64AsF64(0x8380_0000_0000_0000n), dir: 'negative', result: reinterpretU64AsF64(0x8380_0000_0000_0001n) },
    148    ]
    149  )
    150  .fn(t => {
    151    const val = t.params.val;
    152    const dir = t.params.dir;
    153    const expect = t.params.result;
    154    const got = nextAfterF64(val, dir, 'flush');
    155    t.expect(
    156      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    157      `nextAfterF64(${f64(val)}, '${dir}', 'flush') returned ${f64(got)}. Expected ${f64(expect)}`
    158    );
    159  });
    160 
    161 g.test('nextAfterF64NoFlush')
    162  .paramsSubcasesOnly<nextAfterCase>(
    163    // prettier-ignore
    164    [
    165      // Edge Cases
    166      { val: Number.NaN, dir: 'positive', result: Number.NaN },
    167      { val: Number.NaN, dir: 'negative', result: Number.NaN },
    168      { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f64.positive.infinity },
    169      { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f64.positive.infinity },
    170      { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f64.negative.infinity },
    171      { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f64.negative.infinity },
    172 
    173      // Zeroes
    174      { val: +0, dir: 'positive', result: kValue.f64.positive.subnormal.min },
    175      { val: +0, dir: 'negative', result: kValue.f64.negative.subnormal.max },
    176      { val: -0, dir: 'positive', result: kValue.f64.positive.subnormal.min },
    177      { val: -0, dir: 'negative', result: kValue.f64.negative.subnormal.max },
    178 
    179      // Subnormals
    180      { val: kValue.f64.positive.subnormal.min, dir: 'positive', result: reinterpretU64AsF64(0x0000_0000_0000_0002n) },
    181      { val: kValue.f64.positive.subnormal.min, dir: 'negative', result: 0 },
    182      { val: kValue.f64.positive.subnormal.max, dir: 'positive', result: kValue.f64.positive.min },
    183      { val: kValue.f64.positive.subnormal.max, dir: 'negative', result: reinterpretU64AsF64(0x000f_ffff_ffff_fffen) },
    184      { val: kValue.f64.negative.subnormal.min, dir: 'positive', result: reinterpretU64AsF64(0x800f_ffff_ffff_fffen) },
    185      { val: kValue.f64.negative.subnormal.min, dir: 'negative', result: kValue.f64.negative.max },
    186      { val: kValue.f64.negative.subnormal.max, dir: 'positive', result: 0 },
    187      { val: kValue.f64.negative.subnormal.max, dir: 'negative', result: reinterpretU64AsF64(0x8000_0000_0000_0002n) },
    188 
    189      // Normals
    190      { val: kValue.f64.positive.max, dir: 'positive', result: kValue.f64.positive.infinity },
    191      { val: kValue.f64.positive.max, dir: 'negative', result: kValue.f64.positive.nearest_max },
    192      { val: kValue.f64.positive.min, dir: 'positive', result: reinterpretU64AsF64(0x0010_0000_0000_0001n ) },
    193      { val: kValue.f64.positive.min, dir: 'negative', result: reinterpretU64AsF64(0x000f_ffff_ffff_ffffn) },
    194      { val: kValue.f64.negative.max, dir: 'positive', result: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn) },
    195      { val: kValue.f64.negative.max, dir: 'negative', result: reinterpretU64AsF64(0x8010_0000_0000_0001n) },
    196      { val: kValue.f64.negative.min, dir: 'positive', result: kValue.f64.negative.nearest_min },
    197      { val: kValue.f64.negative.min, dir: 'negative', result: kValue.f64.negative.infinity },
    198      { val: reinterpretU64AsF64(0x0380_0000_0000_0000n), dir: 'positive', result: reinterpretU64AsF64(0x0380_0000_0000_0001n) },
    199      { val: reinterpretU64AsF64(0x0380_0000_0000_0000n), dir: 'negative', result: reinterpretU64AsF64(0x037f_ffff_ffff_ffffn) },
    200      { val: reinterpretU64AsF64(0x8380_0000_0000_0000n), dir: 'positive', result: reinterpretU64AsF64(0x837f_ffff_ffff_ffffn) },
    201      { val: reinterpretU64AsF64(0x8380_0000_0000_0000n), dir: 'negative', result: reinterpretU64AsF64(0x8380_0000_0000_0001n) },
    202    ]
    203  )
    204  .fn(t => {
    205    const val = t.params.val;
    206    const dir = t.params.dir;
    207    const expect = t.params.result;
    208    const got = nextAfterF64(val, dir, 'no-flush');
    209    t.expect(
    210      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    211      `nextAfterF64(${f64(val)}, '${dir}', 'no-flush') returned ${f64(got)}. Expected ${f64(
    212        expect
    213      )}`
    214    );
    215  });
    216 
    217 g.test('nextAfterF32FlushToZero')
    218  .paramsSubcasesOnly<nextAfterCase>(
    219    // prettier-ignore
    220    [
    221    // Edge Cases
    222    { val: Number.NaN, dir: 'positive', result: Number.NaN },
    223    { val: Number.NaN, dir: 'negative', result: Number.NaN },
    224    { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f32.positive.infinity },
    225    { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f32.positive.infinity },
    226    { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f32.negative.infinity },
    227    { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f32.negative.infinity },
    228 
    229    // Zeroes
    230    { val: +0, dir: 'positive', result: kValue.f32.positive.min },
    231    { val: +0, dir: 'negative', result: kValue.f32.negative.max },
    232    { val: -0, dir: 'positive', result: kValue.f32.positive.min },
    233    { val: -0, dir: 'negative', result: kValue.f32.negative.max },
    234 
    235    // Subnormals
    236    { val: kValue.f32.positive.subnormal.min, dir: 'positive', result: kValue.f32.positive.min },
    237    { val: kValue.f32.positive.subnormal.min, dir: 'negative', result: kValue.f32.negative.max },
    238    { val: kValue.f32.positive.subnormal.max, dir: 'positive', result: kValue.f32.positive.min },
    239    { val: kValue.f32.positive.subnormal.max, dir: 'negative', result: kValue.f32.negative.max },
    240    { val: kValue.f32.negative.subnormal.min, dir: 'positive', result: kValue.f32.positive.min },
    241    { val: kValue.f32.negative.subnormal.min, dir: 'negative', result: kValue.f32.negative.max },
    242    { val: kValue.f32.negative.subnormal.max, dir: 'positive', result: kValue.f32.positive.min },
    243    { val: kValue.f32.negative.subnormal.max, dir: 'negative', result: kValue.f32.negative.max },
    244 
    245    // Normals
    246    { val: kValue.f32.positive.max, dir: 'positive', result: kValue.f32.positive.infinity },
    247    { val: kValue.f32.positive.max, dir: 'negative', result: kValue.f32.positive.nearest_max },
    248    { val: kValue.f32.positive.min, dir: 'positive', result: reinterpretU32AsF32(0x00800001) },
    249    { val: kValue.f32.positive.min, dir: 'negative', result: 0 },
    250    { val: kValue.f32.negative.max, dir: 'positive', result: 0 },
    251    { val: kValue.f32.negative.max, dir: 'negative', result: reinterpretU32AsF32(0x80800001) },
    252    { val: kValue.f32.negative.min, dir: 'positive', result: reinterpretU32AsF32(0xff7ffffe) },
    253    { val: kValue.f32.negative.min, dir: 'negative', result: kValue.f32.negative.infinity },
    254    { val: reinterpretU32AsF32(0x03800000), dir: 'positive', result: reinterpretU32AsF32(0x03800001) },
    255    { val: reinterpretU32AsF32(0x03800000), dir: 'negative', result: reinterpretU32AsF32(0x037fffff) },
    256    { val: reinterpretU32AsF32(0x83800000), dir: 'positive', result: reinterpretU32AsF32(0x837fffff) },
    257    { val: reinterpretU32AsF32(0x83800000), dir: 'negative', result: reinterpretU32AsF32(0x83800001) },
    258 
    259    // Not precisely expressible as f32
    260    { val: 0.001, dir: 'positive', result: reinterpretU32AsF32(0x3a83126f) }, // positive normal
    261    { val: 0.001, dir: 'negative', result: reinterpretU32AsF32(0x3a83126e) }, // positive normal
    262    { val: -0.001, dir: 'positive', result: reinterpretU32AsF32(0xba83126e) }, // negative normal
    263    { val: -0.001, dir: 'negative', result: reinterpretU32AsF32(0xba83126f) }, // negative normal
    264    { val: 2.82E-40, dir: 'positive', result: kValue.f32.positive.min }, // positive subnormal
    265    { val: 2.82E-40, dir: 'negative', result: kValue.f32.negative.max }, // positive subnormal
    266    { val: -2.82E-40, dir: 'positive', result: kValue.f32.positive.min }, // negative subnormal
    267    { val: -2.82E-40, dir: 'negative', result: kValue.f32.negative.max }, // negative subnormal
    268    ]
    269  )
    270  .fn(t => {
    271    const val = t.params.val;
    272    const dir = t.params.dir;
    273    const expect = t.params.result;
    274    const got = nextAfterF32(val, dir, 'flush');
    275    t.expect(
    276      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    277      `nextAfterF32(${f64(val)}, '${dir}', 'flush') returned ${f32(got)}. Expected ${f32(expect)}`
    278    );
    279  });
    280 
    281 g.test('nextAfterF32NoFlush')
    282  .paramsSubcasesOnly<nextAfterCase>(
    283    // prettier-ignore
    284    [
    285    // Edge Cases
    286    { val: Number.NaN, dir: 'positive', result: Number.NaN },
    287    { val: Number.NaN, dir: 'negative', result: Number.NaN },
    288    { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f32.positive.infinity },
    289    { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f32.positive.infinity },
    290    { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f32.negative.infinity },
    291    { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f32.negative.infinity },
    292 
    293    // Zeroes
    294    { val: +0, dir: 'positive', result: kValue.f32.positive.subnormal.min },
    295    { val: +0, dir: 'negative', result: kValue.f32.negative.subnormal.max },
    296    { val: -0, dir: 'positive', result: kValue.f32.positive.subnormal.min },
    297    { val: -0, dir: 'negative', result: kValue.f32.negative.subnormal.max },
    298 
    299    // Subnormals
    300    { val:kValue.f32.positive.subnormal.min, dir: 'positive', result: reinterpretU32AsF32(0x00000002) },
    301    { val:kValue.f32.positive.subnormal.min, dir: 'negative', result: 0 },
    302    { val:kValue.f32.positive.subnormal.max, dir: 'positive', result: kValue.f32.positive.min },
    303    { val:kValue.f32.positive.subnormal.max, dir: 'negative', result: reinterpretU32AsF32(0x007ffffe) },
    304    { val:kValue.f32.negative.subnormal.min, dir: 'positive', result: reinterpretU32AsF32(0x807ffffe) },
    305    { val:kValue.f32.negative.subnormal.min, dir: 'negative', result: kValue.f32.negative.max },
    306    { val:kValue.f32.negative.subnormal.max, dir: 'positive', result: 0 },
    307    { val:kValue.f32.negative.subnormal.max, dir: 'negative', result: reinterpretU32AsF32(0x80000002) },
    308 
    309    // Normals
    310    { val: kValue.f32.positive.max, dir: 'positive', result: kValue.f32.positive.infinity },
    311    { val: kValue.f32.positive.max, dir: 'negative', result: kValue.f32.positive.nearest_max },
    312    { val: kValue.f32.positive.min, dir: 'positive', result: reinterpretU32AsF32(0x00800001) },
    313    { val: kValue.f32.positive.min, dir: 'negative', result: kValue.f32.positive.subnormal.max },
    314    { val: kValue.f32.negative.max, dir: 'positive', result: kValue.f32.negative.subnormal.min },
    315    { val: kValue.f32.negative.max, dir: 'negative', result: reinterpretU32AsF32(0x80800001) },
    316    { val: kValue.f32.negative.min, dir: 'positive', result: kValue.f32.negative.nearest_min },
    317    { val: kValue.f32.negative.min, dir: 'negative', result: kValue.f32.negative.infinity },
    318    { val: reinterpretU32AsF32(0x03800000), dir: 'positive', result: reinterpretU32AsF32(0x03800001) },
    319    { val: reinterpretU32AsF32(0x03800000), dir: 'negative', result: reinterpretU32AsF32(0x037fffff) },
    320    { val: reinterpretU32AsF32(0x83800000), dir: 'positive', result: reinterpretU32AsF32(0x837fffff) },
    321    { val: reinterpretU32AsF32(0x83800000), dir: 'negative', result: reinterpretU32AsF32(0x83800001) },
    322 
    323    // Not precisely expressible as f32
    324    { val: 0.001, dir: 'positive', result: reinterpretU32AsF32(0x3a83126f) }, // positive normal
    325    { val: 0.001, dir: 'negative', result: reinterpretU32AsF32(0x3a83126e) }, // positive normal
    326    { val: -0.001, dir: 'positive', result: reinterpretU32AsF32(0xba83126e) }, // negative normal
    327    { val: -0.001, dir: 'negative', result: reinterpretU32AsF32(0xba83126f) }, // negative normal
    328    { val: 2.82E-40, dir: 'positive', result: reinterpretU32AsF32(0x0003121a) }, // positive subnormal
    329    { val: 2.82E-40, dir: 'negative', result: reinterpretU32AsF32(0x00031219) }, // positive subnormal
    330    { val: -2.82E-40, dir: 'positive', result: reinterpretU32AsF32(0x80031219) }, // negative subnormal
    331    { val: -2.82E-40, dir: 'negative', result: reinterpretU32AsF32(0x8003121a) }, // negative subnormal
    332  ]
    333  )
    334  .fn(t => {
    335    const val = t.params.val;
    336    const dir = t.params.dir;
    337    const expect = t.params.result;
    338    const got = nextAfterF32(val, dir, 'no-flush');
    339    t.expect(
    340      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    341      `nextAfterF32(${f64(val)}, '${dir}', 'no-flush') returned ${f32(got)}. Expected ${f32(
    342        expect
    343      )}`
    344    );
    345  });
    346 
    347 g.test('nextAfterF16FlushToZero')
    348  .paramsSubcasesOnly<nextAfterCase>(
    349    // prettier-ignore
    350    [
    351      // Edge Cases
    352      { val: Number.NaN, dir: 'positive', result: Number.NaN },
    353      { val: Number.NaN, dir: 'negative', result: Number.NaN },
    354      { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f16.positive.infinity },
    355      { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f16.positive.infinity },
    356      { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f16.negative.infinity },
    357      { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f16.negative.infinity },
    358 
    359      // Zeroes
    360      { val: +0, dir: 'positive', result: kValue.f16.positive.min },
    361      { val: +0, dir: 'negative', result: kValue.f16.negative.max },
    362      { val: -0, dir: 'positive', result: kValue.f16.positive.min },
    363      { val: -0, dir: 'negative', result: kValue.f16.negative.max },
    364 
    365      // Subnormals
    366      { val: kValue.f16.positive.subnormal.min, dir: 'positive', result: kValue.f16.positive.min },
    367      { val: kValue.f16.positive.subnormal.min, dir: 'negative', result: kValue.f16.negative.max },
    368      { val: kValue.f16.positive.subnormal.max, dir: 'positive', result: kValue.f16.positive.min },
    369      { val: kValue.f16.positive.subnormal.max, dir: 'negative', result: kValue.f16.negative.max },
    370      { val: kValue.f16.negative.subnormal.min, dir: 'positive', result: kValue.f16.positive.min },
    371      { val: kValue.f16.negative.subnormal.min, dir: 'negative', result: kValue.f16.negative.max },
    372      { val: kValue.f16.negative.subnormal.max, dir: 'positive', result: kValue.f16.positive.min },
    373      { val: kValue.f16.negative.subnormal.max, dir: 'negative', result: kValue.f16.negative.max },
    374 
    375      // Normals
    376      { val: kValue.f16.positive.max, dir: 'positive', result: kValue.f16.positive.infinity },
    377      { val: kValue.f16.positive.max, dir: 'negative', result: reinterpretU16AsF16(0x7bfe) },
    378      { val: kValue.f16.positive.min, dir: 'positive', result: reinterpretU16AsF16(0x0401) },
    379      { val: kValue.f16.positive.min, dir: 'negative', result: 0 },
    380      { val: kValue.f16.negative.max, dir: 'positive', result: 0 },
    381      { val: kValue.f16.negative.max, dir: 'negative', result: reinterpretU16AsF16(0x8401) },
    382      { val: kValue.f16.negative.min, dir: 'positive', result: reinterpretU16AsF16(0xfbfe) },
    383      { val: kValue.f16.negative.min, dir: 'negative', result: kValue.f16.negative.infinity },
    384      { val: reinterpretU16AsF16(0x1380), dir: 'positive', result: reinterpretU16AsF16(0x1381) },
    385      { val: reinterpretU16AsF16(0x1380), dir: 'negative', result: reinterpretU16AsF16(0x137f) },
    386      { val: reinterpretU16AsF16(0x9380), dir: 'positive', result: reinterpretU16AsF16(0x937f) },
    387      { val: reinterpretU16AsF16(0x9380), dir: 'negative', result: reinterpretU16AsF16(0x9381) },
    388 
    389      // Not precisely expressible as f16
    390      { val: 0.01, dir: 'positive', result: reinterpretU16AsF16(0x211f) }, // positive normal
    391      { val: 0.01, dir: 'negative', result: reinterpretU16AsF16(0x211e) }, // positive normal
    392      { val: -0.01, dir: 'positive', result: reinterpretU16AsF16(0xa11e) }, // negative normal
    393      { val: -0.01, dir: 'negative', result: reinterpretU16AsF16(0xa11f) }, // negative normal
    394      { val: 2.82E-40, dir: 'positive', result: kValue.f16.positive.min }, // positive subnormal
    395      { val: 2.82E-40, dir: 'negative', result: kValue.f16.negative.max }, // positive subnormal
    396      { val: -2.82E-40, dir: 'positive', result: kValue.f16.positive.min }, // negative subnormal
    397      { val: -2.82E-40, dir: 'negative', result: kValue.f16.negative.max }, // negative subnormal
    398    ]
    399  )
    400  .fn(t => {
    401    const val = t.params.val;
    402    const dir = t.params.dir;
    403    const expect = t.params.result;
    404    const got = nextAfterF16(val, dir, 'flush');
    405    t.expect(
    406      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    407      `nextAfterF16(${f64(val)}, '${dir}', 'flush') returned ${f16(got)}. Expected ${f16(expect)}`
    408    );
    409  });
    410 
    411 g.test('nextAfterF16NoFlush')
    412  .paramsSubcasesOnly<nextAfterCase>(
    413    // prettier-ignore
    414    [
    415      // Edge Cases
    416      { val: Number.NaN, dir: 'positive', result: Number.NaN },
    417      { val: Number.NaN, dir: 'negative', result: Number.NaN },
    418      { val: Number.POSITIVE_INFINITY, dir: 'positive', result: kValue.f16.positive.infinity },
    419      { val: Number.POSITIVE_INFINITY, dir: 'negative', result: kValue.f16.positive.infinity },
    420      { val: Number.NEGATIVE_INFINITY, dir: 'positive', result: kValue.f16.negative.infinity },
    421      { val: Number.NEGATIVE_INFINITY, dir: 'negative', result: kValue.f16.negative.infinity },
    422 
    423      // Zeroes
    424      { val: +0, dir: 'positive', result: kValue.f16.positive.subnormal.min },
    425      { val: +0, dir: 'negative', result: kValue.f16.negative.subnormal.max },
    426      { val: -0, dir: 'positive', result: kValue.f16.positive.subnormal.min },
    427      { val: -0, dir: 'negative', result: kValue.f16.negative.subnormal.max },
    428 
    429      // Subnormals
    430      { val: kValue.f16.positive.subnormal.min, dir: 'positive', result: reinterpretU16AsF16(0x0002) },
    431      { val: kValue.f16.positive.subnormal.min, dir: 'negative', result: 0 },
    432      { val: kValue.f16.positive.subnormal.max, dir: 'positive', result: kValue.f16.positive.min },
    433      { val: kValue.f16.positive.subnormal.max, dir: 'negative', result: reinterpretU16AsF16(0x03fe) },
    434      { val: kValue.f16.negative.subnormal.min, dir: 'positive', result: reinterpretU16AsF16(0x83fe) },
    435      { val: kValue.f16.negative.subnormal.min, dir: 'negative', result: kValue.f16.negative.max },
    436      { val: kValue.f16.negative.subnormal.max, dir: 'positive', result: 0 },
    437      { val: kValue.f16.negative.subnormal.max, dir: 'negative', result: reinterpretU16AsF16(0x8002) },
    438 
    439      // Normals
    440      { val: kValue.f16.positive.max, dir: 'positive', result: kValue.f16.positive.infinity },
    441      { val: kValue.f16.positive.max, dir: 'negative', result: reinterpretU16AsF16(0x7bfe) },
    442      { val: kValue.f16.positive.min, dir: 'positive', result: reinterpretU16AsF16(0x0401) },
    443      { val: kValue.f16.positive.min, dir: 'negative', result: kValue.f16.positive.subnormal.max },
    444      { val: kValue.f16.negative.max, dir: 'positive', result: kValue.f16.negative.subnormal.min },
    445      { val: kValue.f16.negative.max, dir: 'negative', result: reinterpretU16AsF16(0x8401) },
    446      { val: kValue.f16.negative.min, dir: 'positive', result: reinterpretU16AsF16(0xfbfe) },
    447      { val: kValue.f16.negative.min, dir: 'negative', result: kValue.f16.negative.infinity },
    448      { val: reinterpretU16AsF16(0x1380), dir: 'positive', result: reinterpretU16AsF16(0x1381) },
    449      { val: reinterpretU16AsF16(0x1380), dir: 'negative', result: reinterpretU16AsF16(0x137f) },
    450      { val: reinterpretU16AsF16(0x9380), dir: 'positive', result: reinterpretU16AsF16(0x937f) },
    451      { val: reinterpretU16AsF16(0x9380), dir: 'negative', result: reinterpretU16AsF16(0x9381) },
    452 
    453      // Not precisely expressible as f16
    454      { val: 0.01, dir: 'positive', result: reinterpretU16AsF16(0x211f) }, // positive normal
    455      { val: 0.01, dir: 'negative', result: reinterpretU16AsF16(0x211e) }, // positive normal
    456      { val: -0.01, dir: 'positive', result: reinterpretU16AsF16(0xa11e) }, // negative normal
    457      { val: -0.01, dir: 'negative', result: reinterpretU16AsF16(0xa11f) }, // negative normal
    458      { val: 2.82E-40, dir: 'positive', result: kValue.f16.positive.subnormal.min }, // positive subnormal
    459      { val: 2.82E-40, dir: 'negative', result: 0 }, // positive subnormal
    460      { val: -2.82E-40, dir: 'positive', result: 0 }, // negative subnormal
    461      { val: -2.82E-40, dir: 'negative', result: kValue.f16.negative.subnormal.max }, // negative subnormal
    462    ]
    463  )
    464  .fn(t => {
    465    const val = t.params.val;
    466    const dir = t.params.dir;
    467    const expect = t.params.result;
    468    const got = nextAfterF16(val, dir, 'no-flush');
    469    t.expect(
    470      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    471      `nextAfterF16(${f64(val)}, '${dir}', 'no-flush') returned ${f16(got)}. Expected ${f16(
    472        expect
    473      )}`
    474    );
    475  });
    476 
    477 interface OneULPCase {
    478  target: number;
    479  expect: number;
    480 }
    481 
    482 g.test('oneULPF64FlushToZero')
    483  .paramsSimple<OneULPCase>([
    484    // Edge Cases
    485    { target: Number.NaN, expect: Number.NaN },
    486    { target: Number.POSITIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    487    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    488 
    489    // Zeroes
    490    { target: +0, expect: reinterpretU64AsF64(0x0010_0000_0000_0000n) },
    491    { target: -0, expect: reinterpretU64AsF64(0x0010_0000_0000_0000n) },
    492 
    493    // Subnormals
    494    {
    495      target: kValue.f64.positive.subnormal.min,
    496      expect: reinterpretU64AsF64(0x0010_0000_0000_0000n),
    497    },
    498    {
    499      target: kValue.f64.positive.subnormal.max,
    500      expect: reinterpretU64AsF64(0x0010_0000_0000_0000n),
    501    },
    502    {
    503      target: kValue.f64.negative.subnormal.min,
    504      expect: reinterpretU64AsF64(0x0010_0000_0000_0000n),
    505    },
    506    {
    507      target: kValue.f64.negative.subnormal.max,
    508      expect: reinterpretU64AsF64(0x0010_0000_0000_0000n),
    509    },
    510 
    511    // Normals
    512    { target: kValue.f64.positive.min, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) },
    513    { target: 1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) },
    514    { target: 2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) },
    515    { target: 4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) },
    516    { target: 1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) },
    517    { target: kValue.f64.positive.max, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    518    { target: kValue.f64.negative.max, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) },
    519    { target: -1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) },
    520    { target: -2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) },
    521    { target: -4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) },
    522    { target: -1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) },
    523    { target: kValue.f64.negative.min, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    524  ])
    525  .fn(t => {
    526    const target = t.params.target;
    527    const got = oneULPF64(target, 'flush');
    528    const expect = t.params.expect;
    529    t.expect(
    530      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    531      `oneULPF64(${f64(target)}, 'flush') returned ${f64(got)}. Expected ${f64(expect)}`
    532    );
    533  });
    534 
    535 g.test('oneULPF64NoFlush')
    536  .paramsSimple<OneULPCase>([
    537    // Edge Cases
    538    { target: Number.NaN, expect: Number.NaN },
    539    { target: Number.POSITIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    540    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    541 
    542    // Zeroes
    543    { target: +0, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) },
    544    { target: -0, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) },
    545 
    546    // Subnormals
    547    {
    548      target: kValue.f64.positive.subnormal.min,
    549      expect: reinterpretU64AsF64(0x0000_0000_0000_0001n),
    550    },
    551    {
    552      target: kValue.f64.positive.subnormal.max,
    553      expect: reinterpretU64AsF64(0x0000_0000_0000_0001n),
    554    },
    555    {
    556      target: kValue.f64.negative.subnormal.min,
    557      expect: reinterpretU64AsF64(0x0000_0000_0000_0001n),
    558    },
    559    {
    560      target: kValue.f64.negative.subnormal.max,
    561      expect: reinterpretU64AsF64(0x0000_0000_0000_0001n),
    562    },
    563 
    564    // Normals
    565    { target: kValue.f64.positive.min, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) },
    566    { target: 1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) },
    567    { target: 2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) },
    568    { target: 4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) },
    569    { target: 1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) },
    570    { target: kValue.f64.positive.max, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    571    { target: kValue.f64.negative.max, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) },
    572    { target: -1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) },
    573    { target: -2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) },
    574    { target: -4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) },
    575    { target: -1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) },
    576    { target: kValue.f64.negative.min, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    577  ])
    578  .fn(t => {
    579    const target = t.params.target;
    580    const got = oneULPF64(target, 'no-flush');
    581    const expect = t.params.expect;
    582    t.expect(
    583      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    584      `oneULPF64(${f64(target)}, 'no-flush') returned ${f64(got)}. Expected ${f64(expect)}`
    585    );
    586  });
    587 
    588 g.test('oneULPF64')
    589  .paramsSimple<OneULPCase>([
    590    // Edge Cases
    591    { target: Number.NaN, expect: Number.NaN },
    592    { target: Number.POSITIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    593    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    594 
    595    // Zeroes
    596    { target: +0, expect: reinterpretU64AsF64(0x0010_0000_0000_0000n) },
    597    { target: -0, expect: reinterpretU64AsF64(0x0010_0000_0000_0000n) },
    598 
    599    // Subnormals
    600    {
    601      target: kValue.f64.positive.subnormal.min,
    602      expect: reinterpretU64AsF64(0x0010_0000_0000_0000n),
    603    },
    604    {
    605      target: kValue.f64.positive.subnormal.max,
    606      expect: reinterpretU64AsF64(0x0010_0000_0000_0000n),
    607    },
    608    {
    609      target: kValue.f64.negative.subnormal.min,
    610      expect: reinterpretU64AsF64(0x0010_0000_0000_0000n),
    611    },
    612    {
    613      target: kValue.f64.negative.subnormal.max,
    614      expect: reinterpretU64AsF64(0x0010_0000_0000_0000n),
    615    },
    616 
    617    // Normals
    618    { target: kValue.f64.positive.min, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) },
    619    { target: 1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) },
    620    { target: 2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) },
    621    { target: 4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) },
    622    { target: 1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) },
    623    { target: kValue.f64.positive.max, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    624    { target: kValue.f64.negative.max, expect: reinterpretU64AsF64(0x0000_0000_0000_0001n) },
    625    { target: -1, expect: reinterpretU64AsF64(0x3ca0_0000_0000_0000n) },
    626    { target: -2, expect: reinterpretU64AsF64(0x3cb0_0000_0000_0000n) },
    627    { target: -4, expect: reinterpretU64AsF64(0x3cc0_0000_0000_0000n) },
    628    { target: -1000000, expect: reinterpretU64AsF64(0x3de0_0000_0000_0000n) },
    629    { target: kValue.f64.negative.min, expect: reinterpretU64AsF64(0x7ca0_0000_0000_0000n) },
    630  ])
    631  .fn(t => {
    632    const target = t.params.target;
    633    const got = oneULPF64(target);
    634    const expect = t.params.expect;
    635    t.expect(
    636      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    637      `oneULPF64(${f64(target)}) returned ${f64(got)}. Expected ${f64(expect)}`
    638    );
    639  });
    640 
    641 g.test('oneULPF32FlushToZero')
    642  .paramsSimple<OneULPCase>([
    643    // Edge Cases
    644    { target: Number.NaN, expect: Number.NaN },
    645    { target: Number.POSITIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) },
    646    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) },
    647 
    648    // Zeroes
    649    { target: +0, expect: reinterpretU32AsF32(0x00800000) },
    650    { target: -0, expect: reinterpretU32AsF32(0x00800000) },
    651 
    652    // Subnormals
    653    { target: kValue.f32.positive.subnormal.min, expect: reinterpretU32AsF32(0x00800000) },
    654    { target: 2.82e-40, expect: reinterpretU32AsF32(0x00800000) }, // positive subnormal
    655    { target: kValue.f32.positive.subnormal.max, expect: reinterpretU32AsF32(0x00800000) },
    656    { target: kValue.f32.negative.subnormal.min, expect: reinterpretU32AsF32(0x00800000) },
    657    { target: -2.82e-40, expect: reinterpretU32AsF32(0x00800000) }, // negative subnormal
    658    { target: kValue.f32.negative.subnormal.max, expect: reinterpretU32AsF32(0x00800000) },
    659 
    660    // Normals
    661    { target: kValue.f32.positive.min, expect: reinterpretU32AsF32(0x00000001) },
    662    { target: 1, expect: reinterpretU32AsF32(0x33800000) },
    663    { target: 2, expect: reinterpretU32AsF32(0x34000000) },
    664    { target: 4, expect: reinterpretU32AsF32(0x34800000) },
    665    { target: 1000000, expect: reinterpretU32AsF32(0x3d800000) },
    666    { target: kValue.f32.positive.max, expect: reinterpretU32AsF32(0x73800000) },
    667    { target: kValue.f32.negative.max, expect: reinterpretU32AsF32(0x00000001) },
    668    { target: -1, expect: reinterpretU32AsF32(0x33800000) },
    669    { target: -2, expect: reinterpretU32AsF32(0x34000000) },
    670    { target: -4, expect: reinterpretU32AsF32(0x34800000) },
    671    { target: -1000000, expect: reinterpretU32AsF32(0x3d800000) },
    672    { target: kValue.f32.negative.min, expect: reinterpretU32AsF32(0x73800000) },
    673 
    674    // No precise f32 value
    675    { target: 0.001, expect: reinterpretU32AsF32(0x2f000000) }, // positive normal
    676    { target: -0.001, expect: reinterpretU32AsF32(0x2f000000) }, // negative normal
    677    { target: 1e40, expect: reinterpretU32AsF32(0x73800000) }, // positive out of range
    678    { target: -1e40, expect: reinterpretU32AsF32(0x73800000) }, // negative out of range
    679  ])
    680  .fn(t => {
    681    const target = t.params.target;
    682    const got = oneULPF32(target, 'flush');
    683    const expect = t.params.expect;
    684    t.expect(
    685      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    686      `oneULPF32(${target}, 'flush') returned ${got}. Expected ${expect}`
    687    );
    688  });
    689 
    690 g.test('oneULPF32NoFlush')
    691  .paramsSimple<OneULPCase>([
    692    // Edge Cases
    693    { target: Number.NaN, expect: Number.NaN },
    694    { target: Number.POSITIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) },
    695    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) },
    696 
    697    // Zeroes
    698    { target: +0, expect: reinterpretU32AsF32(0x00000001) },
    699    { target: -0, expect: reinterpretU32AsF32(0x00000001) },
    700 
    701    // Subnormals
    702    { target: kValue.f32.positive.subnormal.min, expect: reinterpretU32AsF32(0x00000001) },
    703    { target: -2.82e-40, expect: reinterpretU32AsF32(0x00000001) }, // negative subnormal
    704    { target: kValue.f32.positive.subnormal.max, expect: reinterpretU32AsF32(0x00000001) },
    705    { target: kValue.f32.negative.subnormal.min, expect: reinterpretU32AsF32(0x00000001) },
    706    { target: 2.82e-40, expect: reinterpretU32AsF32(0x00000001) }, // positive subnormal
    707    { target: kValue.f32.negative.subnormal.max, expect: reinterpretU32AsF32(0x00000001) },
    708 
    709    // Normals
    710    { target: kValue.f32.positive.min, expect: reinterpretU32AsF32(0x00000001) },
    711    { target: 1, expect: reinterpretU32AsF32(0x33800000) },
    712    { target: 2, expect: reinterpretU32AsF32(0x34000000) },
    713    { target: 4, expect: reinterpretU32AsF32(0x34800000) },
    714    { target: 1000000, expect: reinterpretU32AsF32(0x3d800000) },
    715    { target: kValue.f32.positive.max, expect: reinterpretU32AsF32(0x73800000) },
    716    { target: kValue.f32.negative.max, expect: reinterpretU32AsF32(0x00000001) },
    717    { target: -1, expect: reinterpretU32AsF32(0x33800000) },
    718    { target: -2, expect: reinterpretU32AsF32(0x34000000) },
    719    { target: -4, expect: reinterpretU32AsF32(0x34800000) },
    720    { target: -1000000, expect: reinterpretU32AsF32(0x3d800000) },
    721    { target: kValue.f32.negative.min, expect: reinterpretU32AsF32(0x73800000) },
    722 
    723    // No precise f32 value
    724    { target: 0.001, expect: reinterpretU32AsF32(0x2f000000) }, // positive normal
    725    { target: -0.001, expect: reinterpretU32AsF32(0x2f000000) }, // negative normal
    726    { target: 1e40, expect: reinterpretU32AsF32(0x73800000) }, // positive out of range
    727    { target: -1e40, expect: reinterpretU32AsF32(0x73800000) }, // negative out of range
    728  ])
    729  .fn(t => {
    730    const target = t.params.target;
    731    const got = oneULPF32(target, 'no-flush');
    732    const expect = t.params.expect;
    733    t.expect(
    734      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    735      `oneULPF32(${target}, no-flush) returned ${got}. Expected ${expect}`
    736    );
    737  });
    738 
    739 g.test('oneULPF32')
    740  .paramsSimple<OneULPCase>([
    741    // Edge Cases
    742    { target: Number.NaN, expect: Number.NaN },
    743    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) },
    744    { target: Number.POSITIVE_INFINITY, expect: reinterpretU32AsF32(0x73800000) },
    745 
    746    // Zeroes
    747    { target: +0, expect: reinterpretU32AsF32(0x00800000) },
    748    { target: -0, expect: reinterpretU32AsF32(0x00800000) },
    749 
    750    // Subnormals
    751    { target: kValue.f32.negative.subnormal.max, expect: reinterpretU32AsF32(0x00800000) },
    752    { target: -2.82e-40, expect: reinterpretU32AsF32(0x00800000) },
    753    { target: kValue.f32.negative.subnormal.min, expect: reinterpretU32AsF32(0x00800000) },
    754    { target: kValue.f32.positive.subnormal.max, expect: reinterpretU32AsF32(0x00800000) },
    755    { target: 2.82e-40, expect: reinterpretU32AsF32(0x00800000) },
    756    { target: kValue.f32.positive.subnormal.min, expect: reinterpretU32AsF32(0x00800000) },
    757 
    758    // Normals
    759    { target: kValue.f32.positive.min, expect: reinterpretU32AsF32(0x00000001) },
    760    { target: 1, expect: reinterpretU32AsF32(0x33800000) },
    761    { target: 2, expect: reinterpretU32AsF32(0x34000000) },
    762    { target: 4, expect: reinterpretU32AsF32(0x34800000) },
    763    { target: 1000000, expect: reinterpretU32AsF32(0x3d800000) },
    764    { target: kValue.f32.positive.max, expect: reinterpretU32AsF32(0x73800000) },
    765    { target: kValue.f32.negative.max, expect: reinterpretU32AsF32(0x000000001) },
    766    { target: -1, expect: reinterpretU32AsF32(0x33800000) },
    767    { target: -2, expect: reinterpretU32AsF32(0x34000000) },
    768    { target: -4, expect: reinterpretU32AsF32(0x34800000) },
    769    { target: -1000000, expect: reinterpretU32AsF32(0x3d800000) },
    770    { target: kValue.f32.negative.min, expect: reinterpretU32AsF32(0x73800000) },
    771 
    772    // No precise f32 value
    773    { target: -0.001, expect: reinterpretU32AsF32(0x2f000000) }, // negative normal
    774    { target: -1e40, expect: reinterpretU32AsF32(0x73800000) }, // negative out of range
    775    { target: 0.001, expect: reinterpretU32AsF32(0x2f000000) }, // positive normal
    776    { target: 1e40, expect: reinterpretU32AsF32(0x73800000) }, // positive out of range
    777  ])
    778  .fn(t => {
    779    const target = t.params.target;
    780    const got = oneULPF32(target);
    781    const expect = t.params.expect;
    782    t.expect(
    783      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    784      `oneULPF32(${target}) returned ${got}. Expected ${expect}`
    785    );
    786  });
    787 
    788 g.test('oneULPF16FlushToZero')
    789  .paramsSubcasesOnly<OneULPCase>([
    790    // Edge Cases
    791    { target: Number.NaN, expect: Number.NaN },
    792    { target: Number.POSITIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) },
    793    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) },
    794 
    795    // Zeroes, expect positive.min in flush mode
    796    { target: +0, expect: reinterpretU16AsF16(0x0400) },
    797    { target: -0, expect: reinterpretU16AsF16(0x0400) },
    798 
    799    // Subnormals
    800    { target: kValue.f16.positive.subnormal.min, expect: reinterpretU16AsF16(0x0400) },
    801    { target: 1.91e-6, expect: reinterpretU16AsF16(0x0400) }, // positive subnormal
    802    { target: kValue.f16.positive.subnormal.max, expect: reinterpretU16AsF16(0x0400) },
    803    { target: kValue.f16.negative.subnormal.min, expect: reinterpretU16AsF16(0x0400) },
    804    { target: -1.91e-6, expect: reinterpretU16AsF16(0x0400) }, // negative subnormal
    805    { target: kValue.f16.negative.subnormal.max, expect: reinterpretU16AsF16(0x0400) },
    806 
    807    // Normals
    808    { target: kValue.f16.positive.min, expect: reinterpretU16AsF16(0x0001) },
    809    { target: 1, expect: reinterpretU16AsF16(0x1000) },
    810    { target: 2, expect: reinterpretU16AsF16(0x1400) },
    811    { target: 4, expect: reinterpretU16AsF16(0x1800) },
    812    { target: 1000, expect: reinterpretU16AsF16(0x3800) },
    813    { target: kValue.f16.positive.max, expect: reinterpretU16AsF16(0x5000) },
    814    { target: kValue.f16.negative.max, expect: reinterpretU16AsF16(0x0001) },
    815    { target: -1, expect: reinterpretU16AsF16(0x1000) },
    816    { target: -2, expect: reinterpretU16AsF16(0x1400) },
    817    { target: -4, expect: reinterpretU16AsF16(0x1800) },
    818    { target: -1000, expect: reinterpretU16AsF16(0x3800) },
    819    { target: kValue.f16.negative.min, expect: reinterpretU16AsF16(0x5000) },
    820 
    821    // No precise f16 value
    822    { target: 0.001, expect: reinterpretU16AsF16(0x0010) }, // positive normal
    823    { target: -0.001, expect: reinterpretU16AsF16(0x0010) }, // negative normal
    824    { target: 1e8, expect: reinterpretU16AsF16(0x5000) }, // positive out of range
    825    { target: -1e8, expect: reinterpretU16AsF16(0x5000) }, // negative out of range
    826  ])
    827  .fn(t => {
    828    const target = t.params.target;
    829    const got = oneULPF16(target, 'flush');
    830    const expect = t.params.expect;
    831    t.expect(
    832      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    833      `oneULPF16(${target}, 'flush') returned ${got}. Expected ${expect}`
    834    );
    835  });
    836 
    837 g.test('oneULPF16NoFlush')
    838  .paramsSubcasesOnly<OneULPCase>([
    839    // Edge Cases
    840    { target: Number.NaN, expect: Number.NaN },
    841    { target: Number.POSITIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) },
    842    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) },
    843 
    844    // Zeroes, expect positive.min in flush mode
    845    { target: +0, expect: reinterpretU16AsF16(0x0001) },
    846    { target: -0, expect: reinterpretU16AsF16(0x0001) },
    847 
    848    // Subnormals
    849    { target: kValue.f16.positive.subnormal.min, expect: reinterpretU16AsF16(0x0001) },
    850    { target: 1.91e-6, expect: reinterpretU16AsF16(0x0001) }, // positive subnormal
    851    { target: kValue.f16.positive.subnormal.max, expect: reinterpretU16AsF16(0x0001) },
    852    { target: kValue.f16.negative.subnormal.min, expect: reinterpretU16AsF16(0x0001) },
    853    { target: -1.91e-6, expect: reinterpretU16AsF16(0x0001) }, // negative subnormal
    854    { target: kValue.f16.negative.subnormal.max, expect: reinterpretU16AsF16(0x0001) },
    855 
    856    // Normals
    857    { target: kValue.f16.positive.min, expect: reinterpretU16AsF16(0x0001) },
    858    { target: 1, expect: reinterpretU16AsF16(0x1000) },
    859    { target: 2, expect: reinterpretU16AsF16(0x1400) },
    860    { target: 4, expect: reinterpretU16AsF16(0x1800) },
    861    { target: 1000, expect: reinterpretU16AsF16(0x3800) },
    862    { target: kValue.f16.positive.max, expect: reinterpretU16AsF16(0x5000) },
    863    { target: kValue.f16.negative.max, expect: reinterpretU16AsF16(0x0001) },
    864    { target: -1, expect: reinterpretU16AsF16(0x1000) },
    865    { target: -2, expect: reinterpretU16AsF16(0x1400) },
    866    { target: -4, expect: reinterpretU16AsF16(0x1800) },
    867    { target: -1000, expect: reinterpretU16AsF16(0x3800) },
    868    { target: kValue.f16.negative.min, expect: reinterpretU16AsF16(0x5000) },
    869 
    870    // No precise f16 value
    871    { target: 0.001, expect: reinterpretU16AsF16(0x0010) }, // positive normal
    872    { target: -0.001, expect: reinterpretU16AsF16(0x0010) }, // negative normal
    873    { target: 1e8, expect: reinterpretU16AsF16(0x5000) }, // positive out of range
    874    { target: -1e8, expect: reinterpretU16AsF16(0x5000) }, // negative out of range
    875  ])
    876  .fn(t => {
    877    const target = t.params.target;
    878    const got = oneULPF16(target, 'no-flush');
    879    const expect = t.params.expect;
    880    t.expect(
    881      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    882      `oneULPF16(${target}, no-flush) returned ${got}. Expected ${expect}`
    883    );
    884  });
    885 
    886 g.test('oneULPF16')
    887  .paramsSubcasesOnly<OneULPCase>([
    888    // Edge Cases
    889    { target: Number.NaN, expect: Number.NaN },
    890    { target: Number.POSITIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) },
    891    { target: Number.NEGATIVE_INFINITY, expect: reinterpretU16AsF16(0x5000) },
    892 
    893    // Zeroes, expect positive.min in flush mode
    894    { target: +0, expect: reinterpretU16AsF16(0x0400) },
    895    { target: -0, expect: reinterpretU16AsF16(0x0400) },
    896 
    897    // Subnormals
    898    { target: kValue.f16.positive.subnormal.min, expect: reinterpretU16AsF16(0x0400) },
    899    { target: 1.91e-6, expect: reinterpretU16AsF16(0x0400) }, // positive subnormal
    900    { target: kValue.f16.positive.subnormal.max, expect: reinterpretU16AsF16(0x0400) },
    901    { target: kValue.f16.negative.subnormal.min, expect: reinterpretU16AsF16(0x0400) },
    902    { target: -1.91e-6, expect: reinterpretU16AsF16(0x0400) }, // negative subnormal
    903    { target: kValue.f16.negative.subnormal.max, expect: reinterpretU16AsF16(0x0400) },
    904 
    905    // Normals
    906    { target: kValue.f16.positive.min, expect: reinterpretU16AsF16(0x0001) },
    907    { target: 1, expect: reinterpretU16AsF16(0x1000) },
    908    { target: 2, expect: reinterpretU16AsF16(0x1400) },
    909    { target: 4, expect: reinterpretU16AsF16(0x1800) },
    910    { target: 1000, expect: reinterpretU16AsF16(0x3800) },
    911    { target: kValue.f16.positive.max, expect: reinterpretU16AsF16(0x5000) },
    912    { target: kValue.f16.negative.max, expect: reinterpretU16AsF16(0x0001) },
    913    { target: -1, expect: reinterpretU16AsF16(0x1000) },
    914    { target: -2, expect: reinterpretU16AsF16(0x1400) },
    915    { target: -4, expect: reinterpretU16AsF16(0x1800) },
    916    { target: -1000, expect: reinterpretU16AsF16(0x3800) },
    917    { target: kValue.f16.negative.min, expect: reinterpretU16AsF16(0x5000) },
    918 
    919    // No precise f16 value
    920    { target: 0.001, expect: reinterpretU16AsF16(0x0010) }, // positive normal
    921    { target: -0.001, expect: reinterpretU16AsF16(0x0010) }, // negative normal
    922    { target: 1e8, expect: reinterpretU16AsF16(0x5000) }, // positive out of range
    923    { target: -1e8, expect: reinterpretU16AsF16(0x5000) }, // negative out of range
    924  ])
    925  .fn(t => {
    926    const target = t.params.target;
    927    const got = oneULPF16(target, 'flush');
    928    const expect = t.params.expect;
    929    t.expect(
    930      got === expect || (Number.isNaN(got) && Number.isNaN(expect)),
    931      `oneULPF16(${target}, 'flush') returned ${got}. Expected ${expect}`
    932    );
    933  });
    934 
    935 interface correctlyRoundedCase {
    936  value: number;
    937  expected: Array<number>;
    938 }
    939 
    940 g.test('correctlyRoundedF32')
    941  .paramsSubcasesOnly<correctlyRoundedCase>(
    942    // prettier-ignore
    943    [
    944      // Edge Cases
    945      { value: kValue.f32.positive.max, expected: [kValue.f32.positive.max] },
    946      { value: kValue.f32.negative.min, expected: [kValue.f32.negative.min] },
    947      { value: kValue.f32.positive.max + oneULPF64(kValue.f32.positive.max), expected: [kValue.f32.positive.max, Number.POSITIVE_INFINITY] },
    948      { value: kValue.f32.negative.min - oneULPF64(kValue.f32.negative.min), expected: [Number.NEGATIVE_INFINITY, kValue.f32.negative.min] },
    949      { value: 2 ** (kValue.f32.emax + 1) - oneULPF64(kValue.f32.positive.max), expected: [kValue.f32.positive.max, Number.POSITIVE_INFINITY] },
    950      { value: -(2 ** (kValue.f32.emax + 1)) + oneULPF64(kValue.f32.positive.max), expected: [Number.NEGATIVE_INFINITY, kValue.f32.negative.min] },
    951      { value: 2 ** (kValue.f32.emax + 1), expected: [Number.POSITIVE_INFINITY] },
    952      { value: -(2 ** (kValue.f32.emax + 1)), expected: [Number.NEGATIVE_INFINITY] },
    953      { value: kValue.f32.positive.infinity, expected: [Number.POSITIVE_INFINITY] },
    954      { value: kValue.f32.negative.infinity, expected: [Number.NEGATIVE_INFINITY] },
    955 
    956      // 32-bit subnormals
    957      { value: kValue.f32.positive.subnormal.min, expected: [kValue.f32.positive.subnormal.min] },
    958      { value: kValue.f32.positive.subnormal.max, expected: [kValue.f32.positive.subnormal.max] },
    959      { value: kValue.f32.negative.subnormal.min, expected: [kValue.f32.negative.subnormal.min] },
    960      { value: kValue.f32.negative.subnormal.max, expected: [kValue.f32.negative.subnormal.max] },
    961 
    962      // 64-bit subnormals
    963      { value: reinterpretU64AsF64(0x0000_0000_0000_0001n), expected: [0, kValue.f32.positive.subnormal.min] },
    964      { value: reinterpretU64AsF64(0x0000_0000_0000_0002n), expected: [0, kValue.f32.positive.subnormal.min] },
    965      { value: reinterpretU64AsF64(0x800f_ffff_ffff_ffffn), expected: [kValue.f32.negative.subnormal.max, 0] },
    966      { value: reinterpretU64AsF64(0x800f_ffff_ffff_fffen), expected: [kValue.f32.negative.subnormal.max, 0] },
    967 
    968      // 32-bit normals
    969      { value: 0, expected: [0] },
    970      { value: kValue.f32.positive.min, expected: [kValue.f32.positive.min] },
    971      { value: kValue.f32.negative.max, expected: [kValue.f32.negative.max] },
    972      { value: reinterpretU32AsF32(0x03800000), expected: [reinterpretU32AsF32(0x03800000)] },
    973      { value: reinterpretU32AsF32(0x03800001), expected: [reinterpretU32AsF32(0x03800001)] },
    974      { value: reinterpretU32AsF32(0x83800000), expected: [reinterpretU32AsF32(0x83800000)] },
    975      { value: reinterpretU32AsF32(0x83800001), expected: [reinterpretU32AsF32(0x83800001)] },
    976 
    977      // 64-bit normals
    978      { value: reinterpretU64AsF64(0x3ff0_0000_0000_0001n), expected: [reinterpretU32AsF32(0x3f800000), reinterpretU32AsF32(0x3f800001)] },
    979      { value: reinterpretU64AsF64(0x3ff0_0000_0000_0002n), expected: [reinterpretU32AsF32(0x3f800000), reinterpretU32AsF32(0x3f800001)] },
    980      { value: reinterpretU64AsF64(0x3ff0_0010_0000_0010n), expected: [reinterpretU32AsF32(0x3f800080), reinterpretU32AsF32(0x3f800081)] },
    981      { value: reinterpretU64AsF64(0x3ff0_0020_0000_0020n), expected: [reinterpretU32AsF32(0x3f800100), reinterpretU32AsF32(0x3f800101)] },
    982      { value: reinterpretU64AsF64(0xbff0_0000_0000_0001n), expected: [reinterpretU32AsF32(0xbf800001), reinterpretU32AsF32(0xbf800000)] },
    983      { value: reinterpretU64AsF64(0xbff0_0000_0000_0002n), expected: [reinterpretU32AsF32(0xbf800001), reinterpretU32AsF32(0xbf800000)] },
    984      { value: reinterpretU64AsF64(0xbff0_0010_0000_0010n), expected: [reinterpretU32AsF32(0xbf800081), reinterpretU32AsF32(0xbf800080)] },
    985      { value: reinterpretU64AsF64(0xbff0_0020_0000_0020n), expected: [reinterpretU32AsF32(0xbf800101), reinterpretU32AsF32(0xbf800100)] },
    986    ]
    987  )
    988  .fn(t => {
    989    const value = t.params.value;
    990    const expected = t.params.expected;
    991 
    992    const got = correctlyRoundedF32(value);
    993    t.expect(
    994      objectEquals(expected, got),
    995      `correctlyRoundedF32(${f64(value)}) returned [${got.map(f32)}]. Expected [${expected.map(
    996        f32
    997      )}]`
    998    );
    999  });
   1000 
   1001 g.test('correctlyRoundedF16')
   1002  .paramsSubcasesOnly<correctlyRoundedCase>(
   1003    // prettier-ignore
   1004    [
   1005      // Edge Cases
   1006      { value: kValue.f16.positive.max, expected: [kValue.f16.positive.max] },
   1007      { value: kValue.f16.negative.min, expected: [kValue.f16.negative.min] },
   1008      { value: kValue.f16.positive.max + oneULPF64(kValue.f16.positive.max), expected: [kValue.f16.positive.max, Number.POSITIVE_INFINITY] },
   1009      { value: kValue.f16.negative.min - oneULPF64(kValue.f16.negative.min), expected: [Number.NEGATIVE_INFINITY, kValue.f16.negative.min] },
   1010      { value: 2 ** (kValue.f16.emax + 1) - oneULPF64(kValue.f16.positive.max), expected: [kValue.f16.positive.max, Number.POSITIVE_INFINITY] },
   1011      { value: -(2 ** (kValue.f16.emax + 1)) + oneULPF64(kValue.f16.positive.max), expected: [Number.NEGATIVE_INFINITY, kValue.f16.negative.min] },
   1012      { value: 2 ** (kValue.f16.emax + 1), expected: [Number.POSITIVE_INFINITY] },
   1013      { value: -(2 ** (kValue.f16.emax + 1)), expected: [Number.NEGATIVE_INFINITY] },
   1014      { value: kValue.f16.positive.infinity, expected: [Number.POSITIVE_INFINITY] },
   1015      { value: kValue.f16.negative.infinity, expected: [Number.NEGATIVE_INFINITY] },
   1016 
   1017      // 16-bit subnormals
   1018      { value: kValue.f16.positive.subnormal.min, expected: [kValue.f16.positive.subnormal.min] },
   1019      { value: kValue.f16.positive.subnormal.max, expected: [kValue.f16.positive.subnormal.max] },
   1020      { value: kValue.f16.negative.subnormal.min, expected: [kValue.f16.negative.subnormal.min] },
   1021      { value: kValue.f16.negative.subnormal.max, expected: [kValue.f16.negative.subnormal.max] },
   1022 
   1023      // 32-bit subnormals
   1024      { value: kValue.f32.positive.subnormal.min, expected: [0, kValue.f16.positive.subnormal.min] },
   1025      { value: kValue.f32.positive.subnormal.max, expected: [0, kValue.f16.positive.subnormal.min] },
   1026      { value: kValue.f32.negative.subnormal.max, expected: [kValue.f16.negative.subnormal.max, 0] },
   1027      { value: kValue.f32.negative.subnormal.min, expected: [kValue.f16.negative.subnormal.max, 0] },
   1028 
   1029      // 16-bit normals
   1030      { value: 0, expected: [0] },
   1031      { value: kValue.f16.positive.min, expected: [kValue.f16.positive.min] },
   1032      { value: kValue.f16.negative.max, expected: [kValue.f16.negative.max] },
   1033      { value: reinterpretU16AsF16(0x1380), expected: [reinterpretU16AsF16(0x1380)] },
   1034      { value: reinterpretU16AsF16(0x1381), expected: [reinterpretU16AsF16(0x1381)] },
   1035      { value: reinterpretU16AsF16(0x9380), expected: [reinterpretU16AsF16(0x9380)] },
   1036      { value: reinterpretU16AsF16(0x9381), expected: [reinterpretU16AsF16(0x9381)] },
   1037 
   1038      // 32-bit normals
   1039      { value: reinterpretU32AsF32(0x3a700001), expected: [reinterpretU16AsF16(0x1380), reinterpretU16AsF16(0x1381)] },
   1040      { value: reinterpretU32AsF32(0x3a700002), expected: [reinterpretU16AsF16(0x1380), reinterpretU16AsF16(0x1381)] },
   1041      { value: reinterpretU32AsF32(0xba700001), expected: [reinterpretU16AsF16(0x9381), reinterpretU16AsF16(0x9380)] },
   1042      { value: reinterpretU32AsF32(0xba700002), expected: [reinterpretU16AsF16(0x9381), reinterpretU16AsF16(0x9380)] },
   1043    ]
   1044  )
   1045  .fn(t => {
   1046    const value = t.params.value;
   1047    const expected = t.params.expected;
   1048 
   1049    const got = correctlyRoundedF16(value);
   1050    t.expect(
   1051      objectEquals(expected, got),
   1052      `correctlyRoundedF16(${f64(value)}) returned [${got.map(f16)}]. Expected [${expected.map(
   1053        f16
   1054      )}]`
   1055    );
   1056  });
   1057 
   1058 interface frexpCase {
   1059  input: number;
   1060  fract: number;
   1061  exp: number;
   1062 }
   1063 
   1064 // prettier-ignore
   1065 const kFrexpCases = {
   1066  f32: [
   1067    { input: kValue.f32.positive.max, fract: 0.9999999403953552, exp: 128 },
   1068    { input: kValue.f32.positive.min, fract: 0.5, exp: -125 },
   1069    { input: kValue.f32.negative.max, fract: -0.5, exp: -125 },
   1070    { input: kValue.f32.negative.min, fract: -0.9999999403953552, exp: 128 },
   1071    { input: kValue.f32.positive.subnormal.max, fract: 0.9999998807907104, exp: -126 },
   1072    { input: kValue.f32.positive.subnormal.min, fract: 0.5, exp: -148 },
   1073    { input: kValue.f32.negative.subnormal.max, fract: -0.5, exp: -148 },
   1074    { input: kValue.f32.negative.subnormal.min, fract: -0.9999998807907104, exp: -126 },
   1075  ] as frexpCase[],
   1076  f16: [
   1077    { input: kValue.f16.positive.max, fract: 0.99951171875, exp: 16 },
   1078    { input: kValue.f16.positive.min, fract: 0.5, exp: -13 },
   1079    { input: kValue.f16.negative.max, fract: -0.5, exp: -13 },
   1080    { input: kValue.f16.negative.min, fract: -0.99951171875, exp: 16 },
   1081    { input: kValue.f16.positive.subnormal.max, fract: 0.9990234375, exp: -14 },
   1082    { input: kValue.f16.positive.subnormal.min, fract: 0.5, exp: -23 },
   1083    { input: kValue.f16.negative.subnormal.max, fract: -0.5, exp: -23 },
   1084    { input: kValue.f16.negative.subnormal.min, fract: -0.9990234375, exp: -14 },
   1085  ] as frexpCase[],
   1086  f64: [
   1087    { input: kValue.f64.positive.max, fract: reinterpretU64AsF64(0x3fef_ffff_ffff_ffffn) /* ~0.9999999999999999 */, exp: 1024 },
   1088    { input: kValue.f64.positive.min, fract: 0.5, exp: -1021 },
   1089    { input: kValue.f64.negative.max, fract: -0.5, exp: -1021 },
   1090    { input: kValue.f64.negative.min, fract: reinterpretU64AsF64(0xbfef_ffff_ffff_ffffn) /* ~-0.9999999999999999 */, exp: 1024 },
   1091    { input: kValue.f64.positive.subnormal.max, fract: reinterpretU64AsF64(0x3fef_ffff_ffff_fffen) /* ~0.9999999999999998 */, exp: -1022 },
   1092    { input: kValue.f64.positive.subnormal.min, fract: 0.5, exp: -1073 },
   1093    { input: kValue.f64.negative.subnormal.max, fract: -0.5, exp: -1073 },
   1094    { input: kValue.f64.negative.subnormal.min, fract: reinterpretU64AsF64(0xbfef_ffff_ffff_fffen) /* ~-0.9999999999999998 */, exp: -1022 },
   1095  ] as frexpCase[],
   1096 } as const;
   1097 
   1098 g.test('frexp')
   1099  .params(u =>
   1100    u
   1101      .combine('trait', ['f32', 'f16', 'f64'] as const)
   1102      .beginSubcases()
   1103      .expandWithParams<frexpCase>(p => {
   1104        // prettier-ignore
   1105        return [
   1106          // +/- 0.0
   1107          { input: 0, fract: 0, exp: 0 },
   1108          { input: -0, fract: -0, exp: 0 },
   1109          // Normal float values that can be exactly represented by all float types
   1110          { input: 0.171875, fract: 0.6875, exp: -2 },
   1111          { input: -0.171875, fract: -0.6875, exp: -2 },
   1112          { input: 0.5, fract: 0.5, exp: 0 },
   1113          { input: -0.5, fract: -0.5, exp: 0 },
   1114          { input: 1, fract: 0.5, exp: 1 },
   1115          { input: -1, fract: -0.5, exp: 1 },
   1116          { input: 2, fract: 0.5, exp: 2 },
   1117          { input: -2, fract: -0.5, exp: 2 },
   1118          { input: 10000, fract: 0.6103515625, exp: 14 },
   1119          { input: -10000, fract: -0.6103515625, exp: 14 },
   1120          // Normal ans subnormal cases that are different for each type
   1121          ...kFrexpCases[p.trait],
   1122          // Inf and NaN
   1123          { input: Number.POSITIVE_INFINITY, fract: Number.POSITIVE_INFINITY, exp: 0 },
   1124          { input: Number.NEGATIVE_INFINITY, fract: Number.NEGATIVE_INFINITY, exp: 0 },
   1125          { input: Number.NaN, fract: Number.NaN, exp: 0 },
   1126        ];
   1127      })
   1128  )
   1129  .fn(test => {
   1130    const input = test.params.input;
   1131    const got = frexp(input, test.params.trait);
   1132    const expect = { fract: test.params.fract, exp: test.params.exp };
   1133 
   1134    test.expect(
   1135      objectEquals(got, expect),
   1136      `frexp(${input}, ${test.params.trait}) returned { fract: ${got.fract}, exp: ${got.exp} }. Expected { fract: ${expect.fract}, exp: ${expect.exp} }`
   1137    );
   1138  });
   1139 
   1140 interface lerpCase {
   1141  a: number;
   1142  b: number;
   1143  t: number;
   1144  result: number;
   1145 }
   1146 
   1147 g.test('lerp')
   1148  .paramsSimple<lerpCase>([
   1149    // Infinite cases
   1150    { a: 0.0, b: Number.POSITIVE_INFINITY, t: 0.5, result: Number.NaN },
   1151    { a: Number.POSITIVE_INFINITY, b: 0.0, t: 0.5, result: Number.NaN },
   1152    { a: Number.NEGATIVE_INFINITY, b: 1.0, t: 0.5, result: Number.NaN },
   1153    { a: 1.0, b: Number.NEGATIVE_INFINITY, t: 0.5, result: Number.NaN },
   1154    { a: Number.NEGATIVE_INFINITY, b: Number.POSITIVE_INFINITY, t: 0.5, result: Number.NaN },
   1155    { a: Number.POSITIVE_INFINITY, b: Number.NEGATIVE_INFINITY, t: 0.5, result: Number.NaN },
   1156    { a: 0.0, b: 1.0, t: Number.NEGATIVE_INFINITY, result: Number.NaN },
   1157    { a: 1.0, b: 0.0, t: Number.NEGATIVE_INFINITY, result: Number.NaN },
   1158    { a: 0.0, b: 1.0, t: Number.POSITIVE_INFINITY, result: Number.NaN },
   1159    { a: 1.0, b: 0.0, t: Number.POSITIVE_INFINITY, result: Number.NaN },
   1160 
   1161    // [0.0, 1.0] cases
   1162    { a: 0.0, b: 1.0, t: -1.0, result: -1.0 },
   1163    { a: 0.0, b: 1.0, t: 0.0, result: 0.0 },
   1164    { a: 0.0, b: 1.0, t: 0.1, result: 0.1 },
   1165    { a: 0.0, b: 1.0, t: 0.01, result: 0.01 },
   1166    { a: 0.0, b: 1.0, t: 0.001, result: 0.001 },
   1167    { a: 0.0, b: 1.0, t: 0.25, result: 0.25 },
   1168    { a: 0.0, b: 1.0, t: 0.5, result: 0.5 },
   1169    { a: 0.0, b: 1.0, t: 0.9, result: 0.9 },
   1170    { a: 0.0, b: 1.0, t: 0.99, result: 0.99 },
   1171    { a: 0.0, b: 1.0, t: 0.999, result: 0.999 },
   1172    { a: 0.0, b: 1.0, t: 1.0, result: 1.0 },
   1173    { a: 0.0, b: 1.0, t: 2.0, result: 2.0 },
   1174 
   1175    // [1.0, 0.0] cases
   1176    { a: 1.0, b: 0.0, t: -1.0, result: 2.0 },
   1177    { a: 1.0, b: 0.0, t: 0.0, result: 1.0 },
   1178    { a: 1.0, b: 0.0, t: 0.1, result: 0.9 },
   1179    { a: 1.0, b: 0.0, t: 0.01, result: 0.99 },
   1180    { a: 1.0, b: 0.0, t: 0.001, result: 0.999 },
   1181    { a: 1.0, b: 0.0, t: 0.25, result: 0.75 },
   1182    { a: 1.0, b: 0.0, t: 0.5, result: 0.5 },
   1183    { a: 1.0, b: 0.0, t: 0.9, result: 0.1 },
   1184    { a: 1.0, b: 0.0, t: 0.99, result: 0.01 },
   1185    { a: 1.0, b: 0.0, t: 0.999, result: 0.001 },
   1186    { a: 1.0, b: 0.0, t: 1.0, result: 0.0 },
   1187    { a: 1.0, b: 0.0, t: 2.0, result: -1.0 },
   1188 
   1189    // [0.0, 10.0] cases
   1190    { a: 0.0, b: 10.0, t: -1.0, result: -10.0 },
   1191    { a: 0.0, b: 10.0, t: 0.0, result: 0.0 },
   1192    { a: 0.0, b: 10.0, t: 0.1, result: 1.0 },
   1193    { a: 0.0, b: 10.0, t: 0.01, result: 0.1 },
   1194    { a: 0.0, b: 10.0, t: 0.001, result: 0.01 },
   1195    { a: 0.0, b: 10.0, t: 0.25, result: 2.5 },
   1196    { a: 0.0, b: 10.0, t: 0.5, result: 5.0 },
   1197    { a: 0.0, b: 10.0, t: 0.9, result: 9.0 },
   1198    { a: 0.0, b: 10.0, t: 0.99, result: 9.9 },
   1199    { a: 0.0, b: 10.0, t: 0.999, result: 9.99 },
   1200    { a: 0.0, b: 10.0, t: 1.0, result: 10.0 },
   1201    { a: 0.0, b: 10.0, t: 2.0, result: 20.0 },
   1202 
   1203    // [10.0, 0.0] cases
   1204    { a: 10.0, b: 0.0, t: -1.0, result: 20.0 },
   1205    { a: 10.0, b: 0.0, t: 0.0, result: 10.0 },
   1206    { a: 10.0, b: 0.0, t: 0.1, result: 9 },
   1207    { a: 10.0, b: 0.0, t: 0.01, result: 9.9 },
   1208    { a: 10.0, b: 0.0, t: 0.001, result: 9.99 },
   1209    { a: 10.0, b: 0.0, t: 0.25, result: 7.5 },
   1210    { a: 10.0, b: 0.0, t: 0.5, result: 5.0 },
   1211    { a: 10.0, b: 0.0, t: 0.9, result: 1.0 },
   1212    { a: 10.0, b: 0.0, t: 0.99, result: 0.1 },
   1213    { a: 10.0, b: 0.0, t: 0.999, result: 0.01 },
   1214    { a: 10.0, b: 0.0, t: 1.0, result: 0.0 },
   1215    { a: 10.0, b: 0.0, t: 2.0, result: -10.0 },
   1216 
   1217    // [2.0, 10.0] cases
   1218    { a: 2.0, b: 10.0, t: -1.0, result: -6.0 },
   1219    { a: 2.0, b: 10.0, t: 0.0, result: 2.0 },
   1220    { a: 2.0, b: 10.0, t: 0.1, result: 2.8 },
   1221    { a: 2.0, b: 10.0, t: 0.01, result: 2.08 },
   1222    { a: 2.0, b: 10.0, t: 0.001, result: 2.008 },
   1223    { a: 2.0, b: 10.0, t: 0.25, result: 4.0 },
   1224    { a: 2.0, b: 10.0, t: 0.5, result: 6.0 },
   1225    { a: 2.0, b: 10.0, t: 0.9, result: 9.2 },
   1226    { a: 2.0, b: 10.0, t: 0.99, result: 9.92 },
   1227    { a: 2.0, b: 10.0, t: 0.999, result: 9.992 },
   1228    { a: 2.0, b: 10.0, t: 1.0, result: 10.0 },
   1229    { a: 2.0, b: 10.0, t: 2.0, result: 18.0 },
   1230 
   1231    // [10.0, 2.0] cases
   1232    { a: 10.0, b: 2.0, t: -1.0, result: 18.0 },
   1233    { a: 10.0, b: 2.0, t: 0.0, result: 10.0 },
   1234    { a: 10.0, b: 2.0, t: 0.1, result: 9.2 },
   1235    { a: 10.0, b: 2.0, t: 0.01, result: 9.92 },
   1236    { a: 10.0, b: 2.0, t: 0.001, result: 9.992 },
   1237    { a: 10.0, b: 2.0, t: 0.25, result: 8.0 },
   1238    { a: 10.0, b: 2.0, t: 0.5, result: 6.0 },
   1239    { a: 10.0, b: 2.0, t: 0.9, result: 2.8 },
   1240    { a: 10.0, b: 2.0, t: 0.99, result: 2.08 },
   1241    { a: 10.0, b: 2.0, t: 0.999, result: 2.008 },
   1242    { a: 10.0, b: 2.0, t: 1.0, result: 2.0 },
   1243    { a: 10.0, b: 2.0, t: 2.0, result: -6.0 },
   1244 
   1245    // [-1.0, 1.0] cases
   1246    { a: -1.0, b: 1.0, t: -2.0, result: -5.0 },
   1247    { a: -1.0, b: 1.0, t: 0.0, result: -1.0 },
   1248    { a: -1.0, b: 1.0, t: 0.1, result: -0.8 },
   1249    { a: -1.0, b: 1.0, t: 0.01, result: -0.98 },
   1250    { a: -1.0, b: 1.0, t: 0.001, result: -0.998 },
   1251    { a: -1.0, b: 1.0, t: 0.25, result: -0.5 },
   1252    { a: -1.0, b: 1.0, t: 0.5, result: 0.0 },
   1253    { a: -1.0, b: 1.0, t: 0.9, result: 0.8 },
   1254    { a: -1.0, b: 1.0, t: 0.99, result: 0.98 },
   1255    { a: -1.0, b: 1.0, t: 0.999, result: 0.998 },
   1256    { a: -1.0, b: 1.0, t: 1.0, result: 1.0 },
   1257    { a: -1.0, b: 1.0, t: 2.0, result: 3.0 },
   1258 
   1259    // [1.0, -1.0] cases
   1260    { a: 1.0, b: -1.0, t: -2.0, result: 5.0 },
   1261    { a: 1.0, b: -1.0, t: 0.0, result: 1.0 },
   1262    { a: 1.0, b: -1.0, t: 0.1, result: 0.8 },
   1263    { a: 1.0, b: -1.0, t: 0.01, result: 0.98 },
   1264    { a: 1.0, b: -1.0, t: 0.001, result: 0.998 },
   1265    { a: 1.0, b: -1.0, t: 0.25, result: 0.5 },
   1266    { a: 1.0, b: -1.0, t: 0.5, result: 0.0 },
   1267    { a: 1.0, b: -1.0, t: 0.9, result: -0.8 },
   1268    { a: 1.0, b: -1.0, t: 0.99, result: -0.98 },
   1269    { a: 1.0, b: -1.0, t: 0.999, result: -0.998 },
   1270    { a: 1.0, b: -1.0, t: 1.0, result: -1.0 },
   1271    { a: 1.0, b: -1.0, t: 2.0, result: -3.0 },
   1272 
   1273    // [-1.0, 0.0] cases
   1274    { a: -1.0, b: 0.0, t: -1.0, result: -2.0 },
   1275    { a: -1.0, b: 0.0, t: 0.0, result: -1.0 },
   1276    { a: -1.0, b: 0.0, t: 0.1, result: -0.9 },
   1277    { a: -1.0, b: 0.0, t: 0.01, result: -0.99 },
   1278    { a: -1.0, b: 0.0, t: 0.001, result: -0.999 },
   1279    { a: -1.0, b: 0.0, t: 0.25, result: -0.75 },
   1280    { a: -1.0, b: 0.0, t: 0.5, result: -0.5 },
   1281    { a: -1.0, b: 0.0, t: 0.9, result: -0.1 },
   1282    { a: -1.0, b: 0.0, t: 0.99, result: -0.01 },
   1283    { a: -1.0, b: 0.0, t: 0.999, result: -0.001 },
   1284    { a: -1.0, b: 0.0, t: 1.0, result: 0.0 },
   1285    { a: -1.0, b: 0.0, t: 2.0, result: 1.0 },
   1286 
   1287    // [0.0, -1.0] cases
   1288    { a: 0.0, b: -1.0, t: -1.0, result: 1.0 },
   1289    { a: 0.0, b: -1.0, t: 0.0, result: 0.0 },
   1290    { a: 0.0, b: -1.0, t: 0.1, result: -0.1 },
   1291    { a: 0.0, b: -1.0, t: 0.01, result: -0.01 },
   1292    { a: 0.0, b: -1.0, t: 0.001, result: -0.001 },
   1293    { a: 0.0, b: -1.0, t: 0.25, result: -0.25 },
   1294    { a: 0.0, b: -1.0, t: 0.5, result: -0.5 },
   1295    { a: 0.0, b: -1.0, t: 0.9, result: -0.9 },
   1296    { a: 0.0, b: -1.0, t: 0.99, result: -0.99 },
   1297    { a: 0.0, b: -1.0, t: 0.999, result: -0.999 },
   1298    { a: 0.0, b: -1.0, t: 1.0, result: -1.0 },
   1299    { a: 0.0, b: -1.0, t: 2.0, result: -2.0 },
   1300  ])
   1301  .fn(test => {
   1302    const a = test.params.a;
   1303    const b = test.params.b;
   1304    const t = test.params.t;
   1305    const got = lerp(a, b, t);
   1306    const expect = test.params.result;
   1307 
   1308    test.expect(
   1309      (Number.isNaN(got) && Number.isNaN(expect)) || withinOneULPF32(got, expect, 'flush'),
   1310      `lerp(${a}, ${b}, ${t}) returned ${got}. Expected ${expect}`
   1311    );
   1312  });
   1313 
   1314 interface lerpBigIntCase {
   1315  a: bigint;
   1316  b: bigint;
   1317  idx: number;
   1318  steps: number;
   1319  result: bigint;
   1320 }
   1321 
   1322 g.test('lerpBigInt')
   1323  .paramsSimple<lerpBigIntCase>([
   1324    // [0n, 1000n] cases
   1325    { a: 0n, b: 1000n, idx: 0, steps: 1, result: 0n },
   1326    { a: 0n, b: 1000n, idx: 0, steps: 2, result: 0n },
   1327    { a: 0n, b: 1000n, idx: 1, steps: 2, result: 1000n },
   1328    { a: 0n, b: 1000n, idx: 0, steps: 1000, result: 0n },
   1329    { a: 0n, b: 1000n, idx: 500, steps: 1000, result: 500n },
   1330    { a: 0n, b: 1000n, idx: 999, steps: 1000, result: 1000n },
   1331 
   1332    // [1000n, 0n] cases
   1333    { a: 1000n, b: 0n, idx: 0, steps: 1, result: 1000n },
   1334    { a: 1000n, b: 0n, idx: 0, steps: 2, result: 1000n },
   1335    { a: 1000n, b: 0n, idx: 1, steps: 2, result: 0n },
   1336    { a: 1000n, b: 0n, idx: 0, steps: 1000, result: 1000n },
   1337    { a: 1000n, b: 0n, idx: 500, steps: 1000, result: 500n },
   1338    { a: 1000n, b: 0n, idx: 999, steps: 1000, result: 0n },
   1339 
   1340    // [0n, -1000n] cases
   1341    { a: 0n, b: -1000n, idx: 0, steps: 1, result: 0n },
   1342    { a: 0n, b: -1000n, idx: 0, steps: 2, result: 0n },
   1343    { a: 0n, b: -1000n, idx: 1, steps: 2, result: -1000n },
   1344    { a: 0n, b: -1000n, idx: 0, steps: 1000, result: 0n },
   1345    { a: 0n, b: -1000n, idx: 500, steps: 1000, result: -500n },
   1346    { a: 0n, b: -1000n, idx: 999, steps: 1000, result: -1000n },
   1347 
   1348    // [-1000n, 0n] cases
   1349    { a: -1000n, b: 0n, idx: 0, steps: 1, result: -1000n },
   1350    { a: -1000n, b: 0n, idx: 0, steps: 2, result: -1000n },
   1351    { a: -1000n, b: 0n, idx: 1, steps: 2, result: 0n },
   1352    { a: -1000n, b: 0n, idx: 0, steps: 1000, result: -1000n },
   1353    { a: -1000n, b: 0n, idx: 500, steps: 1000, result: -500n },
   1354    { a: -1000n, b: 0n, idx: 999, steps: 1000, result: 0n },
   1355 
   1356    // [100n, 1000n] cases
   1357    { a: 100n, b: 1000n, idx: 0, steps: 1, result: 100n },
   1358    { a: 100n, b: 1000n, idx: 0, steps: 2, result: 100n },
   1359    { a: 100n, b: 1000n, idx: 1, steps: 2, result: 1000n },
   1360    { a: 100n, b: 1000n, idx: 0, steps: 9, result: 100n },
   1361    { a: 100n, b: 1000n, idx: 4, steps: 9, result: 550n },
   1362    { a: 100n, b: 1000n, idx: 8, steps: 9, result: 1000n },
   1363 
   1364    // [1000n, 100n] cases
   1365    { a: 1000n, b: 100n, idx: 0, steps: 1, result: 1000n },
   1366    { a: 1000n, b: 100n, idx: 0, steps: 2, result: 1000n },
   1367    { a: 1000n, b: 100n, idx: 1, steps: 2, result: 100n },
   1368    { a: 1000n, b: 100n, idx: 0, steps: 9, result: 1000n },
   1369    { a: 1000n, b: 100n, idx: 4, steps: 9, result: 550n },
   1370    { a: 1000n, b: 100n, idx: 8, steps: 9, result: 100n },
   1371 
   1372    // [01000n, 1000n] cases
   1373    { a: -1000n, b: 1000n, idx: 0, steps: 1, result: -1000n },
   1374    { a: -1000n, b: 1000n, idx: 0, steps: 2, result: -1000n },
   1375    { a: -1000n, b: 1000n, idx: 1, steps: 2, result: 1000n },
   1376    { a: -1000n, b: 1000n, idx: 0, steps: 9, result: -1000n },
   1377    { a: -1000n, b: 1000n, idx: 4, steps: 9, result: 0n },
   1378    { a: -1000n, b: 1000n, idx: 8, steps: 9, result: 1000n },
   1379  ])
   1380  .fn(test => {
   1381    const a = test.params.a;
   1382    const b = test.params.b;
   1383    const idx = test.params.idx;
   1384    const steps = test.params.steps;
   1385    const got = lerpBigInt(a, b, idx, steps);
   1386    const expect = test.params.result;
   1387 
   1388    test.expect(
   1389      got === expect,
   1390      `lerpBigInt(${a}, ${b}, ${idx}, ${steps}) returned ${got}. Expected ${expect}`
   1391    );
   1392  });
   1393 
   1394 interface rangeCase {
   1395  a: number;
   1396  b: number;
   1397  num_steps: number;
   1398  result: number[];
   1399 }
   1400 
   1401 g.test('linearRange')
   1402  .paramsSimple<rangeCase>(
   1403    // prettier-ignore
   1404    [
   1405    { a: 0.0, b: Number.POSITIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1406    { a: Number.POSITIVE_INFINITY, b: 0.0, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1407    { a: Number.NEGATIVE_INFINITY, b: 1.0, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1408    { a: 1.0, b: Number.NEGATIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1409    { a: Number.NEGATIVE_INFINITY, b: Number.POSITIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1410    { a: Number.POSITIVE_INFINITY, b: Number.NEGATIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1411    { a: 0.0, b: 0.0, num_steps: 10, result: new Array<number>(10).fill(0.0) },
   1412    { a: 10.0, b: 10.0, num_steps: 10, result: new Array<number>(10).fill(10.0) },
   1413    { a: 0.0, b: 10.0, num_steps: 1, result: [0.0] },
   1414    { a: 10.0, b: 0.0, num_steps: 1, result: [10] },
   1415    { a: 0.0, b: 10.0, num_steps: 11, result: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] },
   1416    { a: 10.0, b: 0.0, num_steps: 11, result: [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0] },
   1417    { a: 0.0, b: 1000.0, num_steps: 11, result: [0.0, 100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.0] },
   1418    { a: 1000.0, b: 0.0, num_steps: 11, result: [1000.0, 900.0, 800.0, 700.0, 600.0, 500.0, 400.0, 300.0, 200.0, 100.0, 0.0] },
   1419    { a: 1.0, b: 5.0, num_steps: 5, result: [1.0, 2.0, 3.0, 4.0, 5.0] },
   1420    { a: 5.0, b: 1.0, num_steps: 5, result: [5.0, 4.0, 3.0, 2.0, 1.0] },
   1421    { a: 0.0, b: 1.0, num_steps: 11, result: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] },
   1422    { a: 1.0, b: 0.0, num_steps: 11, result: [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0] },
   1423    { a: 0.0, b: 1.0, num_steps: 5, result: [0.0, 0.25, 0.5, 0.75, 1.0] },
   1424    { a: 1.0, b: 0.0, num_steps: 5, result: [1.0, 0.75, 0.5, 0.25, 0.0] },
   1425    { a: -1.0, b: 1.0, num_steps: 11, result: [-1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0] },
   1426    { a: 1.0, b: -1.0, num_steps: 11, result: [1.0, 0.8, 0.6, 0.4, 0.2, 0.0, -0.2, -0.4, -0.6, -0.8, -1.0] },
   1427    { a: -1.0, b: 0, num_steps: 11, result: [-1.0, -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0] },
   1428    { a: 0.0, b: -1.0, num_steps: 11, result: [0.0, -0.1, -0.2, -0.3, -0.4, -0.5, -0.6, -0.7, -0.8, -0.9, -1.0] },
   1429  ]
   1430  )
   1431  .fn(test => {
   1432    const a = test.params.a;
   1433    const b = test.params.b;
   1434    const num_steps = test.params.num_steps;
   1435    const got = linearRange(a, b, num_steps);
   1436    const expect = test.params.result;
   1437 
   1438    test.expect(
   1439      compareArrayOfNumbersF32(got, expect, 'no-flush'),
   1440      `linearRange(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}`
   1441    );
   1442  });
   1443 
   1444 g.test('biasedRange')
   1445  .paramsSimple<rangeCase>(
   1446    // prettier-ignore
   1447    [
   1448      { a: 0.0, b: Number.POSITIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1449      { a: Number.POSITIVE_INFINITY, b: 0.0, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1450      { a: Number.NEGATIVE_INFINITY, b: 1.0, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1451      { a: 1.0, b: Number.NEGATIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1452      { a: Number.NEGATIVE_INFINITY, b: Number.POSITIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1453      { a: Number.POSITIVE_INFINITY, b: Number.NEGATIVE_INFINITY, num_steps: 10, result: new Array<number>(10).fill(Number.NaN) },
   1454      { a: 0.0, b: 0.0, num_steps: 10, result: new Array<number>(10).fill(0.0) },
   1455      { a: 10.0, b: 10.0, num_steps: 10, result: new Array<number>(10).fill(10.0) },
   1456      { a: 0.0, b: 10.0, num_steps: 1, result: [0.0] },
   1457      { a: 10.0, b: 0.0, num_steps: 1, result: [10.0] },
   1458      { a: 0.0, b: 10.0, num_steps: 11, result: [0.0, 0.1, 0.4, 0.9, 1.6, 2.5, 3.6, 4.9, 6.4, 8.1, 10.0] },
   1459      { a: 10.0, b: 0.0, num_steps: 11, result: [10.0, 9.9, 9.6, 9.1, 8.4, 7.5, 6.4, 5.1, 3.6, 1.9, 0.0] },
   1460      { a: 0.0, b: 1000.0, num_steps: 11, result: [0.0, 10.0, 40.0, 90.0, 160.0, 250.0, 360.0, 490.0, 640.0, 810.0, 1000.0] },
   1461      { a: 1000.0, b: 0.0, num_steps: 11, result: [1000.0, 990.0, 960.0, 910.0, 840.0, 750.0, 640.0, 510.0, 360.0, 190.0, 0.0] },
   1462      { a: 1.0, b: 5.0, num_steps: 5, result: [1.0, 1.25, 2.0, 3.25, 5.0] },
   1463      { a: 5.0, b: 1.0, num_steps: 5, result: [5.0, 4.75, 4.0, 2.75, 1.0] },
   1464      { a: 0.0, b: 1.0, num_steps: 11, result: [0.0, 0.01, 0.04, 0.09, 0.16, 0.25, 0.36, 0.49, 0.64, 0.81, 1.0] },
   1465      { a: 1.0, b: 0.0, num_steps: 11, result: [1.0, 0.99, 0.96, 0.91, 0.84, 0.75, 0.64, 0.51, 0.36, 0.19, 0.0] },
   1466      { a: 0.0, b: 1.0, num_steps: 5, result: [0.0, 0.0625, 0.25, 0.5625, 1.0] },
   1467      { a: 1.0, b: 0.0, num_steps: 5, result: [1.0, 0.9375, 0.75, 0.4375, 0.0] },
   1468      { a: -1.0, b: 1.0, num_steps: 11, result: [-1.0, -0.98, -0.92, -0.82, -0.68, -0.5, -0.28 ,-0.02, 0.28, 0.62, 1.0] },
   1469      { a: 1.0, b: -1.0, num_steps: 11, result: [1.0, 0.98, 0.92, 0.82, 0.68, 0.5, 0.28 ,0.02, -0.28, -0.62, -1.0] },
   1470      { a: -1.0, b: 0, num_steps: 11, result: [-1.0 , -0.99, -0.96, -0.91, -0.84, -0.75, -0.64, -0.51, -0.36, -0.19, 0.0] },
   1471      { a: 0.0, b: -1.0, num_steps: 11, result: [0.0, -0.01, -0.04, -0.09, -0.16, -0.25, -0.36, -0.49, -0.64, -0.81, -1.0] },
   1472    ]
   1473  )
   1474  .fn(test => {
   1475    const a = test.params.a;
   1476    const b = test.params.b;
   1477    const num_steps = test.params.num_steps;
   1478    const got = biasedRange(a, b, num_steps);
   1479    const expect = test.params.result;
   1480 
   1481    test.expect(
   1482      compareArrayOfNumbersF32(got, expect, 'no-flush'),
   1483      `biasedRange(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}`
   1484    );
   1485  });
   1486 
   1487 interface rangeBigIntCase {
   1488  a: bigint;
   1489  b: bigint;
   1490  num_steps: number;
   1491  result: bigint[];
   1492 }
   1493 
   1494 g.test('linearRangeBigInt')
   1495  .paramsSimple<rangeBigIntCase>(
   1496    // prettier-ignore
   1497    [
   1498      { a: 0n, b: 0n, num_steps: 10, result: new Array<bigint>(10).fill(0n) },
   1499      { a: 10n, b: 10n, num_steps: 10, result: new Array<bigint>(10).fill(10n) },
   1500      { a: 0n, b: 10n, num_steps: 1, result: [0n] },
   1501      { a: 10n, b: 0n, num_steps: 1, result: [10n] },
   1502      { a: 0n, b: 10n, num_steps: 11, result: [0n, 1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 10n] },
   1503      { a: 10n, b: 0n, num_steps: 11, result: [10n, 9n, 8n, 7n, 6n, 5n, 4n, 3n, 2n, 1n, 0n] },
   1504      { a: 0n, b: 1000n, num_steps: 11, result: [0n, 100n, 200n, 300n, 400n, 500n, 600n, 700n, 800n, 900n, 1000n] },
   1505      { a: 1000n, b: 0n, num_steps: 11, result: [1000n, 900n, 800n, 700n, 600n, 500n, 400n, 300n, 200n, 100n, 0n] },
   1506      { a: 1n, b: 5n, num_steps: 5, result: [1n, 2n, 3n, 4n, 5n] },
   1507      { a: 5n, b: 1n, num_steps: 5, result: [5n, 4n, 3n, 2n, 1n] },
   1508      { a: 0n, b: 10n, num_steps: 5, result: [0n, 2n, 5n, 7n, 10n] },
   1509      { a: 10n, b: 0n, num_steps: 5, result: [10n, 8n, 5n, 3n, 0n] },
   1510      { a: -10n, b: 10n, num_steps: 11, result: [-10n, -8n, -6n, -4n, -2n, 0n, 2n, 4n, 6n, 8n, 10n] },
   1511      { a: 10n, b: -10n, num_steps: 11, result: [10n, 8n, 6n, 4n, 2n, 0n, -2n, -4n, -6n, -8n, -10n] },
   1512      { a: -10n, b: 0n, num_steps: 11, result: [-10n, -9n, -8n, -7n, -6n, -5n, -4n, -3n, -2n, -1n, 0n] },
   1513      { a: 0n, b: -10n, num_steps: 11, result: [0n, -1n, -2n, -3n, -4n, -5n, -6n, -7n, -8n, -9n, -10n] },
   1514    ]
   1515  )
   1516  .fn(test => {
   1517    const a = test.params.a;
   1518    const b = test.params.b;
   1519    const num_steps = test.params.num_steps;
   1520    const got = linearRangeBigInt(a, b, num_steps);
   1521    const expect = test.params.result;
   1522 
   1523    test.expect(
   1524      objectEquals(got, expect),
   1525      `linearRangeBigInt(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}`
   1526    );
   1527  });
   1528 
   1529 g.test('biasedRangeBigInt')
   1530  .paramsSimple<rangeBigIntCase>(
   1531    // prettier-ignore
   1532    [
   1533      { a: 0n, b: 0n, num_steps: 10, result: new Array<bigint>(10).fill(0n) },
   1534      { a: 10n, b: 10n, num_steps: 10, result: new Array<bigint>(10).fill(10n) },
   1535      { a: 0n, b: 10n, num_steps: 1, result: [0n] },
   1536      { a: 10n, b: 0n, num_steps: 1, result: [10n] },
   1537      { a: 0n, b: 10n, num_steps: 11, result: [0n, 0n, 0n, 0n, 1n, 2n, 3n, 4n, 6n, 8n, 10n] },
   1538      { a: 10n, b: 0n, num_steps: 11, result: [10n, 10n, 10n, 10n, 9n, 8n, 7n, 6n, 4n, 2n, 0n] },
   1539      { a: 0n, b: 1000n, num_steps: 11, result: [0n, 9n, 39n, 89n, 159n, 249n, 359n, 489n, 639n, 809n, 1000n] },
   1540      { a: 1000n, b: 0n, num_steps: 11, result: [1000n, 991n, 961n, 911n, 841n, 751n, 641n, 511n, 361n, 191n, 0n] },
   1541      { a: 1n, b: 5n, num_steps: 5, result: [1n, 1n, 2n, 3n, 5n] },
   1542      { a: 5n, b: 1n, num_steps: 5, result: [5n, 5n, 4n, 3n, 1n] },
   1543      { a: 0n, b: 10n, num_steps: 5, result: [0n, 0n, 2n, 5n, 10n] },
   1544      { a: 10n, b: 0n, num_steps: 5, result: [10n, 10n, 8n, 5n, 0n] },
   1545      { a: -10n, b: 10n, num_steps: 11, result: [-10n, -10n, -10n, -10n, -8n, -6n, -4n, -2n, 2n, 6n, 10n] },
   1546      { a: 10n, b: -10n, num_steps: 11, result: [10n, 10n, 10n, 10n, 8n, 6n, 4n, 2n, -2n, -6n, -10n] },
   1547      { a: -10n, b: 0n, num_steps: 11, result: [-10n, -10n, -10n, -10n, -9n, -8n, -7n, -6n, -4n, -2n, -0n] },
   1548      { a: 0n, b: -10n, num_steps: 11, result: [0n, 0n, 0n, 0n, -1n, -2n, -3n, -4n, -6n, -8n, -10n] },
   1549    ]
   1550  )
   1551  .fn(test => {
   1552    const a = test.params.a;
   1553    const b = test.params.b;
   1554    const num_steps = test.params.num_steps;
   1555    const got = biasedRangeBigInt(a, b, num_steps);
   1556    const expect = test.params.result;
   1557 
   1558    test.expect(
   1559      objectEquals(got, expect),
   1560      `biasedRangeBigInt(${a}, ${b}, ${num_steps}) returned ${got}. Expected ${expect}`
   1561    );
   1562  });
   1563 
   1564 interface fullF32RangeCase {
   1565  neg_norm: number;
   1566  neg_sub: number;
   1567  pos_sub: number;
   1568  pos_norm: number;
   1569  expect: Array<number>;
   1570 }
   1571 
   1572 g.test('fullF32Range')
   1573  .paramsSimple<fullF32RangeCase>(
   1574    // prettier-ignore
   1575    [
   1576      { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ -0.0, 0.0 ] },
   1577      { neg_norm: 1, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.min, -0.0, 0.0] },
   1578      { neg_norm: 2, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.min, kValue.f32.negative.max, -0.0, 0.0 ] },
   1579      { neg_norm: 3, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.min, -1.9999998807907104, kValue.f32.negative.max, -0.0, 0.0 ] },
   1580      { neg_norm: 0, neg_sub: 1, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.subnormal.min, -0.0, 0.0 ] },
   1581      { neg_norm: 0, neg_sub: 2, pos_sub: 0, pos_norm: 0, expect: [ kValue.f32.negative.subnormal.min, kValue.f32.negative.subnormal.max, -0.0, 0.0 ] },
   1582      { neg_norm: 0, neg_sub: 0, pos_sub: 1, pos_norm: 0, expect: [ -0.0, 0.0, kValue.f32.positive.subnormal.min ] },
   1583      { neg_norm: 0, neg_sub: 0, pos_sub: 2, pos_norm: 0, expect: [ -0.0, 0.0, kValue.f32.positive.subnormal.min, kValue.f32.positive.subnormal.max ] },
   1584      { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 1, expect: [ -0.0, 0.0, kValue.f32.positive.min ] },
   1585      { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 2, expect: [ -0.0, 0.0, kValue.f32.positive.min, kValue.f32.positive.max ] },
   1586      { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 3, expect: [ -0.0, 0.0, kValue.f32.positive.min, 1.9999998807907104, kValue.f32.positive.max ] },
   1587      { neg_norm: 1, neg_sub: 1, pos_sub: 1, pos_norm: 1, expect: [ kValue.f32.negative.min, kValue.f32.negative.subnormal.min, -0.0, 0.0, kValue.f32.positive.subnormal.min, kValue.f32.positive.min ] },
   1588      { neg_norm: 2, neg_sub: 2, pos_sub: 2, pos_norm: 2, expect: [ kValue.f32.negative.min, kValue.f32.negative.max, kValue.f32.negative.subnormal.min, kValue.f32.negative.subnormal.max, -0.0, 0.0, kValue.f32.positive.subnormal.min, kValue.f32.positive.subnormal.max, kValue.f32.positive.min, kValue.f32.positive.max ] },
   1589    ]
   1590  )
   1591  .fn(test => {
   1592    const neg_norm = test.params.neg_norm;
   1593    const neg_sub = test.params.neg_sub;
   1594    const pos_sub = test.params.pos_sub;
   1595    const pos_norm = test.params.pos_norm;
   1596    const got = scalarF32Range({ neg_norm, neg_sub, pos_sub, pos_norm });
   1597    const expect = test.params.expect;
   1598 
   1599    test.expect(
   1600      compareArrayOfNumbersF32(got, expect, 'no-flush'),
   1601      `fullF32Range(${neg_norm}, ${neg_sub}, ${pos_sub}, ${pos_norm}) returned [${got}]. Expected [${expect}]`
   1602    );
   1603  });
   1604 
   1605 interface fullF16RangeCase {
   1606  neg_norm: number;
   1607  neg_sub: number;
   1608  pos_sub: number;
   1609  pos_norm: number;
   1610  expect: Array<number>;
   1611 }
   1612 
   1613 g.test('fullF16Range')
   1614  .paramsSimple<fullF16RangeCase>(
   1615    // prettier-ignore
   1616    [
   1617      { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ -0.0, 0.0 ] },
   1618      { neg_norm: 1, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.min, -0.0, 0.0] },
   1619      { neg_norm: 2, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.min, kValue.f16.negative.max, -0.0, 0.0 ] },
   1620      { neg_norm: 3, neg_sub: 0, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.min, -1.9990234375, kValue.f16.negative.max, -0.0, 0.0 ] },
   1621      { neg_norm: 0, neg_sub: 1, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.subnormal.min, -0.0, 0.0 ] },
   1622      { neg_norm: 0, neg_sub: 2, pos_sub: 0, pos_norm: 0, expect: [ kValue.f16.negative.subnormal.min, kValue.f16.negative.subnormal.max, -0.0, 0.0 ] },
   1623      { neg_norm: 0, neg_sub: 0, pos_sub: 1, pos_norm: 0, expect: [ -0.0, 0.0, kValue.f16.positive.subnormal.min ] },
   1624      { neg_norm: 0, neg_sub: 0, pos_sub: 2, pos_norm: 0, expect: [ -0.0, 0.0, kValue.f16.positive.subnormal.min, kValue.f16.positive.subnormal.max ] },
   1625      { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 1, expect: [ -0.0, 0.0, kValue.f16.positive.min ] },
   1626      { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 2, expect: [ -0.0, 0.0, kValue.f16.positive.min, kValue.f16.positive.max ] },
   1627      { neg_norm: 0, neg_sub: 0, pos_sub: 0, pos_norm: 3, expect: [ -0.0, 0.0, kValue.f16.positive.min, 1.9990234375, kValue.f16.positive.max ] },
   1628      { neg_norm: 1, neg_sub: 1, pos_sub: 1, pos_norm: 1, expect: [ kValue.f16.negative.min, kValue.f16.negative.subnormal.min, -0.0, 0.0, kValue.f16.positive.subnormal.min, kValue.f16.positive.min ] },
   1629      { neg_norm: 2, neg_sub: 2, pos_sub: 2, pos_norm: 2, expect: [ kValue.f16.negative.min, kValue.f16.negative.max, kValue.f16.negative.subnormal.min, kValue.f16.negative.subnormal.max, -0.0, 0.0, kValue.f16.positive.subnormal.min, kValue.f16.positive.subnormal.max, kValue.f16.positive.min, kValue.f16.positive.max ] },
   1630    ]
   1631  )
   1632  .fn(test => {
   1633    const neg_norm = test.params.neg_norm;
   1634    const neg_sub = test.params.neg_sub;
   1635    const pos_sub = test.params.pos_sub;
   1636    const pos_norm = test.params.pos_norm;
   1637    const got = scalarF16Range({ neg_norm, neg_sub, pos_sub, pos_norm });
   1638    const expect = test.params.expect;
   1639 
   1640    test.expect(
   1641      compareArrayOfNumbersF32(got, expect),
   1642      `fullF16Range(${neg_norm}, ${neg_sub}, ${pos_sub}, ${pos_norm}) returned [${got}]. Expected [${expect}]`
   1643    );
   1644  });
   1645 
   1646 interface fullI32RangeCase {
   1647  neg_count: number;
   1648  pos_count: number;
   1649  expect: Array<number>;
   1650 }
   1651 
   1652 g.test('fullI32Range')
   1653  .paramsSimple<fullI32RangeCase>(
   1654    // prettier-ignore
   1655    [
   1656      { neg_count: 0, pos_count: 0, expect: [0] },
   1657      { neg_count: 1, pos_count: 0, expect: [kValue.i32.negative.min, 0] },
   1658      { neg_count: 2, pos_count: 0, expect: [kValue.i32.negative.min, -1, 0] },
   1659      { neg_count: 3, pos_count: 0, expect: [kValue.i32.negative.min, -1610612736, -1, 0] },
   1660      { neg_count: 0, pos_count: 1, expect: [0, 1] },
   1661      { neg_count: 0, pos_count: 2, expect: [0, 1, kValue.i32.positive.max] },
   1662      { neg_count: 0, pos_count: 3, expect: [0, 1, 536870912, kValue.i32.positive.max] },
   1663      { neg_count: 1, pos_count: 1, expect: [kValue.i32.negative.min, 0, 1] },
   1664      { neg_count: 2, pos_count: 2, expect: [kValue.i32.negative.min, -1, 0, 1, kValue.i32.positive.max ] },
   1665    ]
   1666  )
   1667  .fn(test => {
   1668    const neg_count = test.params.neg_count;
   1669    const pos_count = test.params.pos_count;
   1670    const got = fullI32Range({ negative: neg_count, positive: pos_count });
   1671    const expect = test.params.expect;
   1672 
   1673    test.expect(
   1674      compareArrayOfNumbersF32(got, expect),
   1675      `fullI32Range(${neg_count}, ${pos_count}) returned [${got}]. Expected [${expect}]`
   1676    );
   1677  });
   1678 
   1679 interface limitsBigIntBitsF64Case {
   1680  bits: bigint;
   1681  value: number;
   1682 }
   1683 
   1684 // Test to confirm kBit and kValue constants are equivalent for f64
   1685 g.test('f64LimitsEquivalency')
   1686  .paramsSimple<limitsBigIntBitsF64Case>([
   1687    { bits: kBit.f64.positive.max, value: kValue.f64.positive.max },
   1688    { bits: kBit.f64.positive.min, value: kValue.f64.positive.min },
   1689    { bits: kBit.f64.positive.nearest_max, value: kValue.f64.positive.nearest_max },
   1690    { bits: kBit.f64.positive.less_than_one, value: kValue.f64.positive.less_than_one },
   1691    { bits: kBit.f64.positive.pi.whole, value: kValue.f64.positive.pi.whole },
   1692    { bits: kBit.f64.positive.pi.three_quarters, value: kValue.f64.positive.pi.three_quarters },
   1693    { bits: kBit.f64.positive.pi.half, value: kValue.f64.positive.pi.half },
   1694    { bits: kBit.f64.positive.pi.third, value: kValue.f64.positive.pi.third },
   1695    { bits: kBit.f64.positive.pi.quarter, value: kValue.f64.positive.pi.quarter },
   1696    { bits: kBit.f64.positive.pi.sixth, value: kValue.f64.positive.pi.sixth },
   1697    { bits: kBit.f64.positive.e, value: kValue.f64.positive.e },
   1698    { bits: kBit.f64.max_ulp, value: kValue.f64.max_ulp },
   1699    { bits: kBit.f64.negative.max, value: kValue.f64.negative.max },
   1700    { bits: kBit.f64.negative.min, value: kValue.f64.negative.min },
   1701    { bits: kBit.f64.negative.nearest_min, value: kValue.f64.negative.nearest_min },
   1702    { bits: kBit.f64.negative.pi.whole, value: kValue.f64.negative.pi.whole },
   1703    { bits: kBit.f64.negative.pi.three_quarters, value: kValue.f64.negative.pi.three_quarters },
   1704    { bits: kBit.f64.negative.pi.half, value: kValue.f64.negative.pi.half },
   1705    { bits: kBit.f64.negative.pi.third, value: kValue.f64.negative.pi.third },
   1706    { bits: kBit.f64.negative.pi.quarter, value: kValue.f64.negative.pi.quarter },
   1707    { bits: kBit.f64.negative.pi.sixth, value: kValue.f64.negative.pi.sixth },
   1708    { bits: kBit.f64.positive.subnormal.max, value: kValue.f64.positive.subnormal.max },
   1709    { bits: kBit.f64.positive.subnormal.min, value: kValue.f64.positive.subnormal.min },
   1710    { bits: kBit.f64.negative.subnormal.max, value: kValue.f64.negative.subnormal.max },
   1711    { bits: kBit.f64.negative.subnormal.min, value: kValue.f64.negative.subnormal.min },
   1712    { bits: kBit.f64.positive.infinity, value: kValue.f64.positive.infinity },
   1713    { bits: kBit.f64.negative.infinity, value: kValue.f64.negative.infinity },
   1714  ])
   1715  .fn(test => {
   1716    const bits = test.params.bits;
   1717    const value = test.params.value;
   1718 
   1719    const val_to_bits = bits === float64ToUint64(value);
   1720    const bits_to_val = value === uint64ToFloat64(bits);
   1721    test.expect(
   1722      val_to_bits && bits_to_val,
   1723      `bits = ${bits}, value = ${value}, returned val_to_bits as ${val_to_bits}, and bits_to_val as ${bits_to_val}, they are expected to be equivalent`
   1724    );
   1725  });
   1726 
   1727 interface limitsNumberBitsCase {
   1728  bits: number;
   1729  value: number;
   1730 }
   1731 
   1732 // Test to confirm kBit and kValue constants are equivalent for f32
   1733 g.test('f32LimitsEquivalency')
   1734  .paramsSimple<limitsNumberBitsCase>([
   1735    { bits: kBit.f32.positive.max, value: kValue.f32.positive.max },
   1736    { bits: kBit.f32.positive.min, value: kValue.f32.positive.min },
   1737    { bits: kBit.f32.positive.nearest_max, value: kValue.f32.positive.nearest_max },
   1738    { bits: kBit.f32.positive.less_than_one, value: kValue.f32.positive.less_than_one },
   1739    { bits: kBit.f32.positive.pi.whole, value: kValue.f32.positive.pi.whole },
   1740    { bits: kBit.f32.positive.pi.three_quarters, value: kValue.f32.positive.pi.three_quarters },
   1741    { bits: kBit.f32.positive.pi.half, value: kValue.f32.positive.pi.half },
   1742    { bits: kBit.f32.positive.pi.third, value: kValue.f32.positive.pi.third },
   1743    { bits: kBit.f32.positive.pi.quarter, value: kValue.f32.positive.pi.quarter },
   1744    { bits: kBit.f32.positive.pi.sixth, value: kValue.f32.positive.pi.sixth },
   1745    { bits: kBit.f32.positive.e, value: kValue.f32.positive.e },
   1746    { bits: kBit.f32.max_ulp, value: kValue.f32.max_ulp },
   1747    { bits: kBit.f32.negative.max, value: kValue.f32.negative.max },
   1748    { bits: kBit.f32.negative.min, value: kValue.f32.negative.min },
   1749    { bits: kBit.f32.negative.nearest_min, value: kValue.f32.negative.nearest_min },
   1750    { bits: kBit.f32.negative.pi.whole, value: kValue.f32.negative.pi.whole },
   1751    { bits: kBit.f32.negative.pi.three_quarters, value: kValue.f32.negative.pi.three_quarters },
   1752    { bits: kBit.f32.negative.pi.half, value: kValue.f32.negative.pi.half },
   1753    { bits: kBit.f32.negative.pi.third, value: kValue.f32.negative.pi.third },
   1754    { bits: kBit.f32.negative.pi.quarter, value: kValue.f32.negative.pi.quarter },
   1755    { bits: kBit.f32.negative.pi.sixth, value: kValue.f32.negative.pi.sixth },
   1756    { bits: kBit.f32.positive.subnormal.max, value: kValue.f32.positive.subnormal.max },
   1757    { bits: kBit.f32.positive.subnormal.min, value: kValue.f32.positive.subnormal.min },
   1758    { bits: kBit.f32.negative.subnormal.max, value: kValue.f32.negative.subnormal.max },
   1759    { bits: kBit.f32.negative.subnormal.min, value: kValue.f32.negative.subnormal.min },
   1760    { bits: kBit.f32.positive.infinity, value: kValue.f32.positive.infinity },
   1761    { bits: kBit.f32.negative.infinity, value: kValue.f32.negative.infinity },
   1762  ])
   1763  .fn(test => {
   1764    const bits = test.params.bits;
   1765    const value = test.params.value;
   1766 
   1767    const val_to_bits = bits === float32ToUint32(value);
   1768    const bits_to_val = value === uint32ToFloat32(bits);
   1769    test.expect(
   1770      val_to_bits && bits_to_val,
   1771      `bits = ${bits}, value = ${value}, returned val_to_bits as ${val_to_bits}, and bits_to_val as ${bits_to_val}, they are expected to be equivalent`
   1772    );
   1773  });
   1774 
   1775 // Test to confirm kBit and kValue constants are equivalent for f16
   1776 g.test('f16LimitsEquivalency')
   1777  .paramsSimple<limitsNumberBitsCase>([
   1778    { bits: kBit.f16.positive.max, value: kValue.f16.positive.max },
   1779    { bits: kBit.f16.positive.min, value: kValue.f16.positive.min },
   1780    { bits: kBit.f16.positive.nearest_max, value: kValue.f16.positive.nearest_max },
   1781    { bits: kBit.f16.positive.less_than_one, value: kValue.f16.positive.less_than_one },
   1782    { bits: kBit.f16.positive.pi.whole, value: kValue.f16.positive.pi.whole },
   1783    { bits: kBit.f16.positive.pi.three_quarters, value: kValue.f16.positive.pi.three_quarters },
   1784    { bits: kBit.f16.positive.pi.half, value: kValue.f16.positive.pi.half },
   1785    { bits: kBit.f16.positive.pi.third, value: kValue.f16.positive.pi.third },
   1786    { bits: kBit.f16.positive.pi.quarter, value: kValue.f16.positive.pi.quarter },
   1787    { bits: kBit.f16.positive.pi.sixth, value: kValue.f16.positive.pi.sixth },
   1788    { bits: kBit.f16.positive.e, value: kValue.f16.positive.e },
   1789    { bits: kBit.f16.max_ulp, value: kValue.f16.max_ulp },
   1790    { bits: kBit.f16.negative.max, value: kValue.f16.negative.max },
   1791    { bits: kBit.f16.negative.min, value: kValue.f16.negative.min },
   1792    { bits: kBit.f16.negative.nearest_min, value: kValue.f16.negative.nearest_min },
   1793    { bits: kBit.f16.negative.pi.whole, value: kValue.f16.negative.pi.whole },
   1794    { bits: kBit.f16.negative.pi.three_quarters, value: kValue.f16.negative.pi.three_quarters },
   1795    { bits: kBit.f16.negative.pi.half, value: kValue.f16.negative.pi.half },
   1796    { bits: kBit.f16.negative.pi.third, value: kValue.f16.negative.pi.third },
   1797    { bits: kBit.f16.negative.pi.quarter, value: kValue.f16.negative.pi.quarter },
   1798    { bits: kBit.f16.negative.pi.sixth, value: kValue.f16.negative.pi.sixth },
   1799    { bits: kBit.f16.positive.subnormal.max, value: kValue.f16.positive.subnormal.max },
   1800    { bits: kBit.f16.positive.subnormal.min, value: kValue.f16.positive.subnormal.min },
   1801    { bits: kBit.f16.negative.subnormal.max, value: kValue.f16.negative.subnormal.max },
   1802    { bits: kBit.f16.negative.subnormal.min, value: kValue.f16.negative.subnormal.min },
   1803    { bits: kBit.f16.positive.infinity, value: kValue.f16.positive.infinity },
   1804    { bits: kBit.f16.negative.infinity, value: kValue.f16.negative.infinity },
   1805  ])
   1806  .fn(test => {
   1807    const bits = test.params.bits;
   1808    const value = test.params.value;
   1809 
   1810    const val_to_bits = bits === float16ToUint16(value);
   1811    const bits_to_val = value === uint16ToFloat16(bits);
   1812    test.expect(
   1813      val_to_bits && bits_to_val,
   1814      `bits = ${bits}, value = ${value}, returned val_to_bits as ${val_to_bits}, and bits_to_val as ${bits_to_val}, they are expected to be equivalent`
   1815    );
   1816  });
   1817 
   1818 interface cartesianProductCase<T> {
   1819  inputs: T[][];
   1820  result: T[][];
   1821 }
   1822 
   1823 g.test('cartesianProductNumber')
   1824  .paramsSimple<cartesianProductCase<number>>(
   1825    // prettier-ignore
   1826    [
   1827      { inputs: [[0], [1]], result: [[0, 1]] },
   1828      { inputs: [[0, 1], [2]], result: [[0, 2],
   1829                                        [1, 2]] },
   1830      { inputs: [[0], [1, 2]], result: [[0, 1],
   1831                                        [0, 2]] },
   1832      { inputs: [[0, 1], [2, 3]], result: [[0,2],
   1833                                           [1, 2],
   1834                                           [0, 3],
   1835                                           [1, 3]] },
   1836      { inputs: [[0, 1, 2], [3, 4, 5]], result: [[0, 3],
   1837                                                 [1, 3],
   1838                                                 [2, 3],
   1839                                                 [0, 4],
   1840                                                 [1, 4],
   1841                                                 [2, 4],
   1842                                                 [0, 5],
   1843                                                 [1, 5],
   1844                                                 [2, 5]] },
   1845      { inputs: [[0, 1], [2, 3], [4, 5]], result: [[0, 2, 4],
   1846                                                   [1, 2, 4],
   1847                                                   [0, 3, 4],
   1848                                                   [1, 3, 4],
   1849                                                   [0, 2, 5],
   1850                                                   [1, 2, 5],
   1851                                                   [0, 3, 5],
   1852                                                   [1, 3, 5]] },
   1853 
   1854  ]
   1855  )
   1856  .fn(test => {
   1857    const inputs = test.params.inputs;
   1858    const got = cartesianProduct(...inputs);
   1859    const expect = test.params.result;
   1860 
   1861    test.expect(
   1862      objectEquals(got, expect),
   1863      `cartesianProduct(${JSON.stringify(inputs)}) returned ${JSON.stringify(
   1864        got
   1865      )}. Expected ${JSON.stringify(expect)} `
   1866    );
   1867  });
   1868 
   1869 g.test('cartesianProductArray')
   1870  .paramsSimple<cartesianProductCase<number[]>>(
   1871    // prettier-ignore
   1872    [
   1873      { inputs: [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], result: [[[0, 1], [4, 5]],
   1874                                                               [[2, 3], [4, 5]],
   1875                                                               [[0, 1], [6, 7]],
   1876                                                               [[2, 3], [6, 7]]]},
   1877      { inputs: [[[0, 1], [2, 3]], [[4, 5], [6, 7]], [[8, 9]]], result: [[[0, 1], [4, 5], [8, 9]],
   1878                                                                         [[2, 3], [4, 5], [8, 9]],
   1879                                                                         [[0, 1], [6, 7], [8, 9]],
   1880                                                                         [[2, 3], [6, 7], [8, 9]]]},
   1881      { inputs: [[[0, 1, 2], [3, 4, 5], [6, 7, 8]], [[2, 1, 0], [5, 4, 3], [8, 7, 6]]], result:  [[[0, 1, 2], [2, 1, 0]],
   1882                                                                                                  [[3, 4, 5], [2, 1, 0]],
   1883                                                                                                  [[6, 7, 8], [2, 1, 0]],
   1884                                                                                                  [[0, 1, 2], [5, 4, 3]],
   1885                                                                                                  [[3, 4, 5], [5, 4, 3]],
   1886                                                                                                  [[6, 7, 8], [5, 4, 3]],
   1887                                                                                                  [[0, 1, 2], [8, 7, 6]],
   1888                                                                                                  [[3, 4, 5], [8, 7, 6]],
   1889                                                                                                  [[6, 7, 8], [8, 7, 6]]]}
   1890 
   1891    ]
   1892  )
   1893  .fn(test => {
   1894    const inputs = test.params.inputs;
   1895    const got = cartesianProduct(...inputs);
   1896    const expect = test.params.result;
   1897 
   1898    test.expect(
   1899      objectEquals(got, expect),
   1900      `cartesianProduct(${JSON.stringify(inputs)}) returned ${JSON.stringify(
   1901        got
   1902      )}. Expected ${JSON.stringify(expect)} `
   1903    );
   1904  });
   1905 
   1906 interface calculatePermutationsCase<T> {
   1907  input: T[];
   1908  result: T[][];
   1909 }
   1910 
   1911 g.test('calculatePermutations')
   1912  .paramsSimple<calculatePermutationsCase<number>>(
   1913    // prettier-ignore
   1914    [
   1915      { input: [0, 1], result: [[0, 1],
   1916                                [1, 0]] },
   1917      { input: [0, 1, 2], result: [[0, 1, 2],
   1918                                   [0, 2, 1],
   1919                                   [1, 0, 2],
   1920                                   [1, 2, 0],
   1921                                   [2, 0, 1],
   1922                                   [2, 1, 0]] },
   1923        { input: [0, 1, 2, 3], result: [[0, 1, 2, 3],
   1924                                        [0, 1, 3, 2],
   1925                                        [0, 2, 1, 3],
   1926                                        [0, 2, 3, 1],
   1927                                        [0, 3, 1, 2],
   1928                                        [0, 3, 2, 1],
   1929                                        [1, 0, 2, 3],
   1930                                        [1, 0, 3, 2],
   1931                                        [1, 2, 0, 3],
   1932                                        [1, 2, 3, 0],
   1933                                        [1, 3, 0, 2],
   1934                                        [1, 3, 2, 0],
   1935                                        [2, 0, 1, 3],
   1936                                        [2, 0, 3, 1],
   1937                                        [2, 1, 0, 3],
   1938                                        [2, 1, 3, 0],
   1939                                        [2, 3, 0, 1],
   1940                                        [2, 3, 1, 0],
   1941                                        [3, 0, 1, 2],
   1942                                        [3, 0, 2, 1],
   1943                                        [3, 1, 0, 2],
   1944                                        [3, 1, 2, 0],
   1945                                        [3, 2, 0, 1],
   1946                                        [3, 2, 1, 0]] },
   1947    ]
   1948  )
   1949  .fn(test => {
   1950    const input = test.params.input;
   1951    const got = calculatePermutations(input);
   1952    const expect = test.params.result;
   1953 
   1954    test.expect(
   1955      objectEquals(got, expect),
   1956      `calculatePermutations(${JSON.stringify(input)}) returned ${JSON.stringify(
   1957        got
   1958      )}. Expected ${JSON.stringify(expect)} `
   1959    );
   1960  });