Repository: dubzzz/fast-check
Pull request #2089 (2021-07-08 18:23:56)

✨ Add built-in unmapper support onto object arbitraries
<!-- Context of the PR: short description and potentially linked issues -->

Partial support requires float and double to be compatible
Full support requires sparse arrays and typed arrays to be compatible
<!-- ...a few words to describe the content of this PR...               -->
<!-- ... -->

<!-- Type of PR: [ ] unchecked / [ ] checked -->
**_Category:_**

- [x] ✨ Introduce new features
- [ ] 📝 Add or update documentation
- [ ] ✅ Add or update tests
- [ ] 🐛 Fix a bug
- [ ] 🏷️ Add or update types
- [ ] ⚡️ Improve performance
- [ ] _Other(s):_ ...
<!-- Don't forget to add the gitmoji icon in the name of the PR -->
<!-- See: https://gitmoji.dev/                                  -->

<!-- Fixing bugs, adding features... may impact existing ones           -->
<!-- in order to track potential issues that could be related to your PR -->
<!-- please check the impacts and describe more precisely what to expect -->

**_Potential impacts:_**

<!-- Generated values: Can your change impact any of the existing generators in terms of generated values, if so which ones? when? -->
<!-- Shrink values:    Can your change impact any of the existing generators in terms of shrink values, if so which ones? when? -->
<!-- Performance:      Can it require some typings changes on user side? Please give more details -->
<!-- Typings:          Is there a potential performance impact? In which cases? -->
- [ ] Generated values
- [ ] Shrink values
- [ ] Performance
- [ ] Typings
- [ ] _Other(s):_ ...


codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/dubzzz/fast-check/pr/2089/builds/148319) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 6d997fbf922b0709c7f0581275b4ca617427f037:

|Sandbox| Source |
|--|--|
|[Vanilla](https://codesandbox.io/s/vanilla-fm0io)| Configuration |
|[fast-check-examples](https://codesandbox.io/s/fast-check-examples-qvkx2)| Configuration |

codecov[bot]: # [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/2089?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) Report
> Merging [#2089](https://codecov.io/gh/dubzzz/fast-check/pull/2089?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) (6d997fb) into [main](https://codecov.io/gh/dubzzz/fast-check/commit/dbf7d08b174857103d73b678fc0031f12de0e812?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) (dbf7d08) will **decrease** coverage by `0.03%`.
> The diff coverage is `95.52%`.

[![Impacted file tree graph](https://codecov.io/gh/dubzzz/fast-check/pull/2089/graphs/tree.svg?width=650&height=150&src=pr&token=fRi2fJN730&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN)](https://codecov.io/gh/dubzzz/fast-check/pull/2089?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN)

```diff
@@            Coverage Diff             @@
##             main    #2089      +/-   ##
==========================================
- Coverage   96.48%   96.45%   -0.04%     
==========================================
  Files         187      191       +4     
  Lines        4443     4483      +40     
  Branches      875      884       +9     
==========================================
+ Hits         4287     4324      +37     
- Misses        152      155       +3     
  Partials        4        4              
```

| Flag | Coverage Δ | |
|---|---|---|
| unit-tests | `96.45% <95.52%> (-0.04%)` | :arrow_down: |
| unit-tests-12.x-Linux | `?` | |
| unit-tests-14.x-Linux | `?` | |
| unit-tests-16.x-Linux | `96.45% <95.52%> (-0.04%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/dubzzz/fast-check/pull/2089?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) | Coverage Δ | |
|---|---|---|
| [src/arbitrary/\_internals/mappers/ArrayToMap.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2089/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL21hcHBlcnMvQXJyYXlUb01hcC50cw==) | `87.50% <87.50%> (ø)` | |
| [src/arbitrary/\_internals/mappers/ArrayToSet.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2089/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL21hcHBlcnMvQXJyYXlUb1NldC50cw==) | `87.50% <87.50%> (ø)` | |
| [...itrary/\_internals/mappers/ObjectToPrototypeLess.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2089/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL21hcHBlcnMvT2JqZWN0VG9Qcm90b3R5cGVMZXNzLnRz) | `87.50% <87.50%> (ø)` | |
| [...bitrary/\_internals/builders/AnyArbitraryBuilder.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2089/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL2J1aWxkZXJzL0FueUFyYml0cmFyeUJ1aWxkZXIudHM=) | `100.00% <100.00%> (ø)` | |
| [...trary/\_internals/builders/BoxedArbitraryBuilder.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2089/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL2J1aWxkZXJzL0JveGVkQXJiaXRyYXJ5QnVpbGRlci50cw==) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/\_internals/mappers/UnboxedToBoxed.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2089/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL21hcHBlcnMvVW5ib3hlZFRvQm94ZWQudHM=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/2089?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/2089?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN). Last update [dbf7d08...6d997fb](https://codecov.io/gh/dubzzz/fast-check/pull/2089?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN).


Patch:
diff --git a/src/check/arbitrary/helpers/ArrayInt64Arbitrary.ts b/src/arbitrary/_internals/ArrayInt64Arbitrary.ts
similarity index 92%
rename from src/check/arbitrary/helpers/ArrayInt64Arbitrary.ts
rename to src/arbitrary/_internals/ArrayInt64Arbitrary.ts
index 97a0cb03..0a2e73de 100644
--- a/src/check/arbitrary/helpers/ArrayInt64Arbitrary.ts
+++ b/src/arbitrary/_internals/ArrayInt64Arbitrary.ts
@@ -1,9 +1,9 @@
-import { Random } from '../../../random/generator/Random';
-import { stream, Stream } from '../../../stream/Stream';
-import { ArbitraryWithContextualShrink } from '../definition/ArbitraryWithContextualShrink';
-import { convertFromNextWithShrunkOnce } from '../definition/Converters';
-import { NextArbitrary } from '../definition/NextArbitrary';
-import { NextValue } from '../definition/NextValue';
+import { Random } from '../../random/generator/Random';
+import { stream, Stream } from '../../stream/Stream';
+import { ArbitraryWithContextualShrink } from '../../check/arbitrary/definition/ArbitraryWithContextualShrink';
+import { convertFromNextWithShrunkOnce } from '../../check/arbitrary/definition/Converters';
+import { NextArbitrary } from '../../check/arbitrary/definition/NextArbitrary';
+import { NextValue } from '../../check/arbitrary/definition/NextValue';
 import {
   add64,
   ArrayInt64,
@@ -17,7 +17,7 @@ import {
   substract64,
   Unit64,
   Zero64,
-} from './ArrayInt64';
+} from './helpers/ArrayInt64';
 
 /** @internal */
 class ArrayInt64Arbitrary extends NextArbitrary<ArrayInt64> {
diff --git a/src/arbitrary/_internals/builders/AnyArbitraryBuilder.ts b/src/arbitrary/_internals/builders/AnyArbitraryBuilder.ts
index e093a4c9..0cf9fa49 100644
--- a/src/arbitrary/_internals/builders/AnyArbitraryBuilder.ts
+++ b/src/arbitrary/_internals/builders/AnyArbitraryBuilder.ts
@@ -19,8 +19,59 @@ import { uint32Array } from '../../uint32Array';
 import { uint8Array } from '../../uint8Array';
 import { uint8ClampedArray } from '../../uint8ClampedArray';
 import { sparseArray } from '../../sparseArray';
-import { keyValuePairsToObjectMapper } from '../mappers/KeyValuePairsToObject';
+import { keyValuePairsToObjectMapper, keyValuePairsToObjectUnmapper } from '../mappers/KeyValuePairsToObject';
 import { QualifiedObjectConstraints } from '../helpers/QualifiedObjectConstraints';
+import { convertFromNext, convertToNext } from '../../../check/arbitrary/definition/Converters';
+import { arrayToMapMapper, arrayToMapUnmapper } from '../mappers/ArrayToMap';
+import { arrayToSetMapper, arrayToSetUnmapper } from '../mappers/ArrayToSet';
+import { objectToPrototypeLessMapper, objectToPrototypeLessUnmapper } from '../mappers/ObjectToPrototypeLess';
+
+/** @internal */
+function entriesOf<T, U>(keyArb: Arbitrary<T>, valueArb: Arbitrary<U>, maxKeys: number) {
+  // TODO - Depending on the situation, the selected compare function might not be appropriate
+  // eg.: in the case of Map, NaN is NaN but NaN !== NaN
+  return convertToNext(set(tuple(keyArb, valueArb), { maxLength: maxKeys, compare: (t1, t2) => t1[0] === t2[0] }));
+}
+
+/** @internal */
+function mapOf<T, U>(ka: Arbitrary<T>, va: Arbitrary<U>, maxKeys: number) {
+  return convertFromNext(entriesOf(ka, va, maxKeys).map(arrayToMapMapper, arrayToMapUnmapper));
+}
+
+/** @internal */
+function dictOf<U>(ka: Arbitrary<string>, va: Arbitrary<U>, maxKeys: number) {
+  return convertFromNext(entriesOf(ka, va, maxKeys).map(keyValuePairsToObjectMapper, keyValuePairsToObjectUnmapper));
+}
+
+/** @internal */
+function setOf<U>(va: Arbitrary<U>, maxKeys: number) {
+  // TODO - The default compare function provided by the set is not appropriate (today) as it distintish NaN from NaN
+  // While the Set does not and consider them to be the same values.
+  return convertFromNext(convertToNext(set(va, { maxLength: maxKeys })).map(arrayToSetMapper, arrayToSetUnmapper));
+}
+
+/** @internal */
+// eslint-disable-next-line @typescript-eslint/ban-types
+function prototypeLessOf(objectArb: Arbitrary<object>) {
+  // TODO - The default compare function provided by the set is not appropriate (today) as it distintish NaN from NaN
+  // While the Set does not and consider them to be the same values.
+  return convertFromNext(convertToNext(objectArb).map(objectToPrototypeLessMapper, objectToPrototypeLessUnmapper));
+}
+
+/** @internal */
+function typedArray() {
+  return oneof(
+    int8Array(),
+    uint8Array(),
+    uint8ClampedArray(),
+    int16Array(),
+    uint16Array(),
+    int32Array(),
+    uint32Array(),
+    float32Array(),
+    float64Array()
+  );
+}
 
 /** @internal */
 export function anyArbitraryBuilder(constraints: QualifiedObjectConstraints): Arbitrary<unknown> {
@@ -37,19 +88,11 @@ export function anyArbitraryBuilder(constraints: QualifiedObjectConstraints): Ar
   const maxDepth = constraints.maxDepth;
   const maxKeys = constraints.maxKeys;
 
-  const entriesOf = <T, U>(keyArb: Arbitrary<T>, valueArb: Arbitrary<U>) =>
-    set(tuple(keyArb, valueArb), { maxLength: maxKeys, compare: (t1, t2) => t1[0] === t2[0] });
-
-  const mapOf = <T, U>(ka: Arbitrary<T>, va: Arbitrary<U>) => entriesOf(ka, va).map((v) => new Map(v));
-  const dictOf = <U>(ka: Arbitrary<string>, va: Arbitrary<U>) =>
-    entriesOf(ka, va).map((v) => keyValuePairsToObjectMapper(v));
-
   const baseArb = oneof(...arbitrariesForBase);
   const arrayBaseArb = oneof(...arbitrariesForBase.map((arb) => array(arb, { maxLength: maxKeys })));
-  const objectBaseArb = (n: number) => oneof(...arbitrariesForBase.map((arb) => dictOf(arbKeys(n), arb)));
-  const setBaseArb = () =>
-    oneof(...arbitrariesForBase.map((arb) => set(arb, { maxLength: maxKeys }).map((v) => new Set(v))));
-  const mapBaseArb = (n: number) => oneof(...arbitrariesForBase.map((arb) => mapOf(arbKeys(n), arb)));
+  const objectBaseArb = (n: number) => oneof(...arbitrariesForBase.map((arb) => dictOf(arbKeys(n), arb, maxKeys)));
+  const setBaseArb = () => oneof(...arbitrariesForBase.map((arb) => setOf(arb, maxKeys)));
+  const mapBaseArb = (n: number) => oneof(...arbitrariesForBase.map((arb) => mapOf(arbKeys(n), arb, maxKeys)));
 
   // base[] | anything[]
   // eslint-disable-next-line @typescript-eslint/no-use-before-define
@@ -58,19 +101,21 @@ export function anyArbitraryBuilder(constraints: QualifiedObjectConstraints): Ar
   const setArb = memo((n) =>
     oneof(
       setBaseArb(),
-
       // eslint-disable-next-line @typescript-eslint/no-use-before-define
-      set(anythingArb(n), { maxLength: maxKeys }).map((v) => new Set(v))
+      setOf(anythingArb(n), maxKeys)
     )
   );
   // Map<key, base> | (Map<key, anything> | Map<anything, anything>)
   const mapArb = memo((n) =>
-    // eslint-disable-next-line @typescript-eslint/no-use-before-define
-    oneof(mapBaseArb(n), oneof(mapOf(arbKeys(n), anythingArb(n)), mapOf(anythingArb(n), anythingArb(n))))
+    oneof(
+      mapBaseArb(n),
+      // eslint-disable-next-line @typescript-eslint/no-use-before-define
+      oneof(mapOf(arbKeys(n), anythingArb(n), maxKeys), mapOf(anythingArb(n), anythingArb(n), maxKeys))
+    )
   );
   // {[key:string]: base} | {[key:string]: anything}
   // eslint-disable-next-line @typescript-eslint/no-use-before-define
-  const objectArb = memo((n) => oneof(objectBaseArb(n), dictOf(arbKeys(n), anythingArb(n))));
+  const objectArb = memo((n) => oneof(objectBaseArb(n), dictOf(arbKeys(n), anythingArb(n), maxKeys)));
 
   const anythingArb: Memo<unknown> = memo((n) => {
     if (n <= 0) return oneof(baseArb);
@@ -81,24 +126,10 @@ export function anyArbitraryBuilder(constraints: QualifiedObjectConstraints): Ar
       ...(constraints.withMap ? [mapArb()] : []),
       ...(constraints.withSet ? [setArb()] : []),
       ...(constraints.withObjectString ? [anythingArb().map((o) => stringify(o))] : []),
-      ...(constraints.withNullPrototype ? [objectArb().map((o) => Object.assign(Object.create(null), o))] : []),
+      ...(constraints.withNullPrototype ? [prototypeLessOf(objectArb())] : []),
       ...(constraints.withBigInt ? [bigInt()] : []),
       ...(constraints.withDate ? [date()] : []),
-      ...(constraints.withTypedArray
-        ? [
-            oneof(
-              int8Array(),
-              uint8Array(),
-              uint8ClampedArray(),
-              int16Array(),
-              uint16Array(),
-              int32Array(),
-              uint32Array(),
-              float32Array(),
-              float64Array()
-            ),
-          ]
-        : []),
+      ...(constraints.withTypedArray ? [typedArray()] : []),
       ...(constraints.withSparseArray ? [sparseArray(anythingArb())] : [])
     );
   });
diff --git a/src/arbitrary/_internals/builders/BoxedArbitraryBuilder.ts b/src/arbitrary/_internals/builders/BoxedArbitraryBuilder.ts
index 552e28f7..128bc302 100644
--- a/src/arbitrary/_internals/builders/BoxedArbitraryBuilder.ts
+++ b/src/arbitrary/_internals/builders/BoxedArbitraryBuilder.ts
@@ -1,23 +1,8 @@
 import { Arbitrary } from '../../../check/arbitrary/definition/Arbitrary';
-
-/** @internal */
-function toBoxedMapper(v: unknown): unknown {
-  switch (typeof v) {
-    case 'boolean':
-      // tslint:disable-next-line:no-construct
-      return new Boolean(v);
-    case 'number':
-      // tslint:disable-next-line:no-construct
-      return new Number(v);
-    case 'string':
-      // tslint:disable-next-line:no-construct
-      return new String(v);
-    default:
-      return v;
-  }
-}
+import { convertFromNext, convertToNext } from '../../../check/arbitrary/definition/Converters';
+import { unboxedToBoxedMapper, unboxedToBoxedUnmapper } from '../mappers/UnboxedToBoxed';
 
 /** @internal */
 export function boxedArbitraryBuilder(arb: Arbitrary<unknown>): Arbitrary<unknown> {
-  return arb.map(toBoxedMapper);
+  return convertFromNext(convertToNext(arb).map(unboxedToBoxedMapper, unboxedToBoxedUnmapper));
 }
diff --git a/src/check/arbitrary/helpers/ArrayInt64.ts b/src/arbitrary/_internals/helpers/ArrayInt64.ts
similarity index 100%
rename from src/check/arbitrary/helpers/ArrayInt64.ts
rename to src/arbitrary/_internals/helpers/ArrayInt64.ts
diff --git a/src/check/arbitrary/DoubleNextArbitrary.ts b/src/arbitrary/_internals/helpers/DoubleHelpers.ts
similarity index 63%
rename from src/check/arbitrary/DoubleNextArbitrary.ts
rename to src/arbitrary/_internals/helpers/DoubleHelpers.ts
index 8aea0d6b..683e4856 100644
--- a/src/check/arbitrary/DoubleNextArbitrary.ts
+++ b/src/arbitrary/_internals/helpers/DoubleHelpers.ts
@@ -1,15 +1,4 @@
-import { Arbitrary } from './definition/Arbitrary';
-import {
-  add64,
-  ArrayInt64,
-  clone64,
-  isEqual64,
-  isStrictlyPositive64,
-  isStrictlySmaller64,
-  substract64,
-  Unit64,
-} from './helpers/ArrayInt64';
-import { arrayInt64 } from './helpers/ArrayInt64Arbitrary';
+import { ArrayInt64, clone64, isEqual64 } from './ArrayInt64';
 
 /** @internal */
 const INDEX_POSITIVE_INFINITY: ArrayInt64 = { sign: 1, data: [2146435072, 0] }; // doubleToIndex(Number.MAX_VALUE) + 1;
@@ -152,91 +141,3 @@ export function indexToDouble(index: ArrayInt64): number {
   const significand = 1 + ((postIndexHigh & 0xfffff) * 2 ** 32 + index.data[1]) * Number.EPSILON;
   return significand * 2 ** exponent;
 }
-
-/**
- * Constraints to be applied on {@link doubleNext}
- * @remarks Since 2.8.1
- * @public
- */
-export interface DoubleNextConstraints {
-  /**
-   * Lower bound for the generated 64-bit floats (included)
-   * @defaultValue Number.NEGATIVE_INFINITY, -1.7976931348623157e+308 when noDefaultInfinity is true
-   * @remarks Since 2.8.0
-   */
-  min?: number;
-  /**
-   * Upper bound for the generated 64-bit floats (included)
-   * @defaultValue Number.POSITIVE_INFINITY, 1.7976931348623157e+308 when noDefaultInfinity is true
-   * @remarks Since 2.8.0
-   */
-  max?: number;
-  /**
-   * By default, lower and upper bounds are -infinity and +infinity.
-   * By setting noDefaultInfinity to true, you move those defaults to minimal and maximal finite values.
-   * @defaultValue false
-   * @remarks Since 2.8.0
-   */
-  noDefaultInfinity?: boolean;
-  /**
-   * When set to true, no more Number.NaN can be generated.
-   * @defaultValue false
-   * @remarks Since 2.8.0
-   */
-  noNaN?: boolean;
-}
-
-/**
- * Same as {@link doubleToIndex} except it throws in case of invalid double
- *
- * @internal
- */
-function safeDoubleToIndex(d: number, constraintsLabel: keyof DoubleNextConstraints) {
-  if (Number.isNaN(d)) {
-    // Number.NaN does not have any associated index in the current implementation
-    throw new Error('fc.doubleNext constraints.' + constraintsLabel + ' must be a 32-bit float');
-  }
-  return doubleToIndex(d);
-}
-
-/**
- * For 64-bit floating point numbers:
- * - sign: 1 bit
- * - significand: 52 bits
- * - exponent: 11 bits
- *
- * @param constraints - Constraints to apply when building instances
- *
- * @public
- */
-export function doubleNext(constraints: DoubleNextConstraints = {}): Arbitrary<number> {
-  const {
-    noDefaultInfinity = false,
-    noNaN = false,
-    min = noDefaultInfinity ? -Number.MAX_VALUE : Number.NEGATIVE_INFINITY,
-    max = noDefaultInfinity ? Number.MAX_VALUE : Number.POSITIVE_INFINITY,
-  } = constraints;
-  const minIndex = safeDoubleToIndex(min, 'min');
-  const maxIndex = safeDoubleToIndex(max, 'max');
-  if (isStrictlySmaller64(maxIndex, minIndex)) {
-    // In other words: minIndex > maxIndex
-    // Comparing min and max might be problematic in case min=+0 and max=-0
-    // For that reason, we prefer to compare computed index to be safer
-    throw new Error('fc.doubleNext constraints.min must be smaller or equal to constraints.max');
-  }
-  if (noNaN) {
-    return arrayInt64(minIndex, maxIndex).map(indexToDouble);
-  }
-  // In case maxIndex > 0 or in other words max > 0,
-  //   values will be [min, ..., +0, ..., max, NaN]
-  //               or [min, ..., max, NaN] if min > +0
-  // Otherwise,
-  //   values will be [NaN, min, ..., max] with max <= +0
-  const positiveMaxIdx = isStrictlyPositive64(maxIndex);
-  const minIndexWithNaN = positiveMaxIdx ? minIndex : substract64(minIndex, Unit64);
-  const maxIndexWithNaN = positiveMaxIdx ? add64(maxIndex, Unit64) : maxIndex;
-  return arrayInt64(minIndexWithNaN, maxIndexWithNaN).map((index) => {
-    if (isStrictlySmaller64(maxIndex, index) || isStrictlySmaller64(index, minIndex)) return Number.NaN;
-    else return indexToDouble(index);
-  });
-}
diff --git a/src/check/arbitrary/FloatNextArbitrary.ts b/src/arbitrary/_internals/helpers/FloatHelpers.ts
similarity index 53%
rename from src/check/arbitrary/FloatNextArbitrary.ts
rename to src/arbitrary/_internals/helpers/FloatHelpers.ts
index 38522964..a43800e6 100644
--- a/src/check/arbitrary/FloatNextArbitrary.ts
+++ b/src/arbitrary/_internals/helpers/FloatHelpers.ts
@@ -1,6 +1,3 @@
-import { Arbitrary } from './definition/Arbitrary';
-import { integer } from '../../arbitrary/integer';
-
 /** @internal */
 export const MIN_VALUE_32 = 2 ** -126 * 2 ** -23;
 /** @internal */
@@ -118,100 +115,3 @@ export function indexToFloat(index: number): number {
   const significand = 1 + (postIndex & 0x7fffff) / 0x800000;
   return significand * 2 ** exponent;
 }
-
-/**
- * Constraints to be applied on {@link floatNext}
- * @remarks Since 2.8.0
- * @public
- */
-export interface FloatNextConstraints {
-  /**
-   * Lower bound for the generated 32-bit floats (included)
-   * @defaultValue Number.NEGATIVE_INFINITY, -3.4028234663852886e+38 when noDefaultInfinity is true
-   * @remarks Since 2.8.0
-   */
-  min?: number;
-  /**
-   * Upper bound for the generated 32-bit floats (included)
-   * @defaultValue Number.POSITIVE_INFINITY, 3.4028234663852886e+38 when noDefaultInfinity is true
-   * @remarks Since 2.8.0
-   */
-  max?: number;
-  /**
-   * By default, lower and upper bounds are -infinity and +infinity.
-   * By setting noDefaultInfinity to true, you move those defaults to minimal and maximal finite values.
-   * @defaultValue false
-   * @remarks Since 2.8.0
-   */
-  noDefaultInfinity?: boolean;
-  /**
-   * When set to true, no more Number.NaN can be generated.
-   * @defaultValue false
-   * @remarks Since 2.8.0
-   */
-  noNaN?: boolean;
-}
-
-/**
- * Same as {@link floatToIndex} except it throws in case of invalid float 32
- *
- * @internal
- */
-function safeFloatToIndex(f: number, constraintsLabel: keyof FloatNextConstraints) {
-  const conversionTrick = 'you can convert any double to a 32-bit float by using `new Float32Array([myDouble])[0]`';
-  const errorMessage = 'fc.floatNext constraints.' + constraintsLabel + ' must be a 32-bit float - ' + conversionTrick;
-  if (Number.isNaN(f) || (Number.isFinite(f) && (f < -MAX_VALUE_32 || f > MAX_VALUE_32))) {
-    // Number.NaN does not have any associated index in the current implementation
-    // Finite values outside of the 32-bit range for floats cannot be 32-bit floats
-    throw new Error(errorMessage);
-  }
-  const index = floatToIndex(f);
-  if (!Number.isInteger(index)) {
-    // Index not being an integer means that original value was not a valid 32-bit float
-    throw new Error(errorMessage);
-  }
-  return index;
-}
-
-/**
- * For 32-bit floating point numbers:
- * - sign: 1 bit
- * - significand: 23 bits
- * - exponent: 8 bits
- *
- * The smallest non-zero value (in absolute value) that can be represented by such float is: 2 ** -126 * 2 ** -23.
- * And the largest one is: 2 ** 127 * (1 + (2 ** 23 - 1) / 2 ** 23).
- *
- * @param constraints - Constraints to apply when building instances
- *
- * @public
- */
-export function floatNext(constraints: FloatNextConstraints = {}): Arbitrary<number> {
-  const {
-    noDefaultInfinity = false,
-    noNaN = false,
-    min = noDefaultInfinity ? -MAX_VALUE_32 : Number.NEGATIVE_INFINITY,
-    max = noDefaultInfinity ? MAX_VALUE_32 : Number.POSITIVE_INFINITY,
-  } = constraints;
-  const minIndex = safeFloatToIndex(min, 'min');
-  const maxIndex = safeFloatToIndex(max, 'max');
-  if (minIndex > maxIndex) {
-    // Comparing min and max might be problematic in case min=+0 and max=-0
-    // For that reason, we prefer to compare computed index to be safer
-    throw new Error('fc.floatNext constraints.min must be smaller or equal to constraints.max');
-  }
-  if (noNaN) {
-    return integer({ min: minIndex, max: maxIndex }).map(indexToFloat);
-  }
-  // In case maxIndex > 0 or in other words max > 0,
-  //   values will be [min, ..., +0, ..., max, NaN]
-  //               or [min, ..., max, NaN] if min > +0
-  // Otherwise,
-  //   values will be [NaN, min, ..., max] with max <= +0
-  const minIndexWithNaN = maxIndex > 0 ? minIndex : minIndex - 1;
-  const maxIndexWithNaN = maxIndex > 0 ? maxIndex + 1 : maxIndex;
-  return integer({ min: minIndexWithNaN, max: maxIndexWithNaN }).map((index) => {
-    if (index > maxIndex || index < minIndex) return Number.NaN;
-    else return indexToFloat(index);
-  });
-}
diff --git a/src/arbitrary/_internals/helpers/JsonConstraintsBuilder.ts b/src/arbitrary/_internals/helpers/JsonConstraintsBuilder.ts
index 0314ccf6..a47c52b5 100644
--- a/src/arbitrary/_internals/helpers/JsonConstraintsBuilder.ts
+++ b/src/arbitrary/_internals/helpers/JsonConstraintsBuilder.ts
@@ -1,7 +1,7 @@
 import { Arbitrary } from '../../../check/arbitrary/definition/Arbitrary';
 import { boolean } from '../../boolean';
 import { constant } from '../../constant';
-import { double } from '../../../check/arbitrary/FloatingPointArbitrary';
+import { double } from '../../double';
 import { maxSafeInteger } from '../../maxSafeInteger';
 import { ObjectConstraints } from './QualifiedObjectConstraints';
 
diff --git a/src/arbitrary/_internals/helpers/QualifiedObjectConstraints.ts b/src/arbitrary/_internals/helpers/QualifiedObjectConstraints.ts
index ba198c19..4a80e4cf 100644
--- a/src/arbitrary/_internals/helpers/QualifiedObjectConstraints.ts
+++ b/src/arbitrary/_internals/helpers/QualifiedObjectConstraints.ts
@@ -1,7 +1,7 @@
 import { Arbitrary } from '../../../check/arbitrary/definition/Arbitrary';
 import { boolean } from '../../boolean';
 import { constant } from '../../constant';
-import { double } from '../../../check/arbitrary/FloatingPointArbitrary';
+import { double } from '../../double';
 import { maxSafeInteger } from '../../maxSafeInteger';
 import { oneof } from '../../oneof';
 import { string } from '../../string';
diff --git a/src/arbitrary/_internals/mappers/ArrayToMap.ts b/src/arbitrary/_internals/mappers/ArrayToMap.ts
new file mode 100644
index 00000000..12196cec
--- /dev/null
+++ b/src/arbitrary/_internals/mappers/ArrayToMap.ts
@@ -0,0 +1,18 @@
+/** @internal */
+export function arrayToMapMapper<TKey, TValue>(data: [TKey, TValue][]): Map<TKey, TValue> {
+  return new Map(data);
+}
+
+/** @internal */
+export function arrayToMapUnmapper<TKey, TValue>(value: unknown): [TKey, TValue][] {
+  if (typeof value !== 'object' || value === null) {
+    throw new Error('Incompatible instance received: should be a non-null object');
+  }
+  if (!('constructor' in value) || value.constructor !== Map) {
+    throw new Error('Incompatible instance received: should be of exact type Map');
+  }
+  // Remark: We do not really check for TValue, this will be the responsability of our caller
+  // Unmappers are not supposed to inspect to far into the data they receive, they just have to check if the value
+  // could have been produced using them: here Array -> Map so if it is a Map then we just return the array.
+  return Array.from(value as Map<TKey, TValue>);
+}
diff --git a/src/arbitrary/_internals/mappers/ArrayToSet.ts b/src/arbitrary/_internals/mappers/ArrayToSet.ts
new file mode 100644
index 00000000..6b06d041
--- /dev/null
+++ b/src/arbitrary/_internals/mappers/ArrayToSet.ts
@@ -0,0 +1,18 @@
+/** @internal */
+export function arrayToSetMapper<TValue>(data: TValue[]): Set<TValue> {
+  return new Set(data);
+}
+
+/** @internal */
+export function arrayToSetUnmapper<TValue>(value: unknown): TValue[] {
+  if (typeof value !== 'object' || value === null) {
+    throw new Error('Incompatible instance received: should be a non-null object');
+  }
+  if (!('constructor' in value) || value.constructor !== Set) {
+    throw new Error('Incompatible instance received: should be of exact type Set');
+  }
+  // Remark: We do not really check for TValue, this will be the responsability of our caller
+  // Unmappers are not supposed to inspect to far into the data they receive, they just have to check if the value
+  // could have been produced using them: here Array -> Set so if it is a Set then we just return the array.
+  return Array.from(value as Set<TValue>);
+}
diff --git a/src/arbitrary/_internals/mappers/ObjectToPrototypeLess.ts b/src/arbitrary/_internals/mappers/ObjectToPrototypeLess.ts
new file mode 100644
index 00000000..6ff404cd
--- /dev/null
+++ b/src/arbitrary/_internals/mappers/ObjectToPrototypeLess.ts
@@ -0,0 +1,22 @@
+/**
+ * The received instance MUST be an exact instance of Object, not a sub-class
+ * @internal
+ */
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+// eslint-disable-next-line @typescript-eslint/ban-types
+export function objectToPrototypeLessMapper(o: object): object {
+  return Object.assign(Object.create(null), o);
+}
+
+/** @internal */
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+// eslint-disable-next-line @typescript-eslint/ban-types
+export function objectToPrototypeLessUnmapper(value: unknown): object {
+  if (typeof value !== 'object' || value === null) {
+    throw new Error('Incompatible instance received: should be a non-null object');
+  }
+  if ('__proto__' in value) {
+    throw new Error('Incompatible instance received: should not have any __proto__');
+  }
+  return Object.assign({}, value);
+}
diff --git a/src/arbitrary/_internals/mappers/UnboxedToBoxed.ts b/src/arbitrary/_internals/mappers/UnboxedToBoxed.ts
new file mode 100644
index 00000000..d1b5f88a
--- /dev/null
+++ b/src/arbitrary/_internals/mappers/UnboxedToBoxed.ts
@@ -0,0 +1,27 @@
+/** @internal */
+export function unboxedToBoxedMapper(value: unknown): unknown {
+  switch (typeof value) {
+    case 'boolean':
+      // tslint:disable-next-line:no-construct
+      return new Boolean(value);
+    case 'number':
+      // tslint:disable-next-line:no-construct
+      return new Number(value);
+    case 'string':
+      // tslint:disable-next-line:no-construct
+      return new String(value);
+    default:
+      return value;
+  }
+}
+
+/** @internal */
+export function unboxedToBoxedUnmapper(value: unknown): unknown {
+  if (typeof value !== 'object' || value === null || !('constructor' in value)) {
+    return value;
+  }
+  return value.constructor === Boolean || value.constructor === Number || value.constructor === String
+    ? // eslint-disable-next-line @typescript-eslint/ban-types
+      (value as Boolean | Number | String).valueOf()
+    : value;
+}
diff --git a/src/arbitrary/_next/doubleNext.ts b/src/arbitrary/_next/doubleNext.ts
new file mode 100644
index 00000000..a9e67143
--- /dev/null
+++ b/src/arbitrary/_next/doubleNext.ts
@@ -0,0 +1,98 @@
+import {
+  add64,
+  isStrictlyPositive64,
+  isStrictlySmaller64,
+  substract64,
+  Unit64,
+} from '../_internals/helpers/ArrayInt64';
+import { arrayInt64 } from '../_internals/ArrayInt64Arbitrary';
+import { Arbitrary } from '../../check/arbitrary/definition/Arbitrary';
+import { doubleToIndex, indexToDouble } from '../_internals/helpers/DoubleHelpers';
+
+/**
+ * Constraints to be applied on {@link doubleNext}
+ * @remarks Since 2.8.1
+ * @public
+ */
+export interface DoubleNextConstraints {
+  /**
+   * Lower bound for the generated 64-bit floats (included)
+   * @defaultValue Number.NEGATIVE_INFINITY, -1.7976931348623157e+308 when noDefaultInfinity is true
+   * @remarks Since 2.8.0
+   */
+  min?: number;
+  /**
+   * Upper bound for the generated 64-bit floats (included)
+   * @defaultValue Number.POSITIVE_INFINITY, 1.7976931348623157e+308 when noDefaultInfinity is true
+   * @remarks Since 2.8.0
+   */
+  max?: number;
+  /**
+   * By default, lower and upper bounds are -infinity and +infinity.
+   * By setting noDefaultInfinity to true, you move those defaults to minimal and maximal finite values.
+   * @defaultValue false
+   * @remarks Since 2.8.0
+   */
+  noDefaultInfinity?: boolean;
+  /**
+   * When set to true, no more Number.NaN can be generated.
+   * @defaultValue false
+   * @remarks Since 2.8.0
+   */
+  noNaN?: boolean;
+}
+
+/**
+ * Same as {@link doubleToIndex} except it throws in case of invalid double
+ *
+ * @internal
+ */
+function safeDoubleToIndex(d: number, constraintsLabel: keyof DoubleNextConstraints) {
+  if (Number.isNaN(d)) {
+    // Number.NaN does not have any associated index in the current implementation
+    throw new Error('fc.doubleNext constraints.' + constraintsLabel + ' must be a 32-bit float');
+  }
+  return doubleToIndex(d);
+}
+
+/**
+ * For 64-bit floating point numbers:
+ * - sign: 1 bit
+ * - significand: 52 bits
+ * - exponent: 11 bits
+ *
+ * @param constraints - Constraints to apply when building instances
+ *
+ * @public
+ */
+export function doubleNext(constraints: DoubleNextConstraints = {}): Arbitrary<number> {
+  const {
+    noDefaultInfinity = false,
+    noNaN = false,
+    min = noDefaultInfinity ? -Number.MAX_VALUE : Number.NEGATIVE_INFINITY,
+    max = noDefaultInfinity ? Number.MAX_VALUE : Number.POSITIVE_INFINITY,
+  } = constraints;
+  const minIndex = safeDoubleToIndex(min, 'min');
+  const maxIndex = safeDoubleToIndex(max, 'max');
+  if (isStrictlySmaller64(maxIndex, minIndex)) {
+    // In other words: minIndex > maxIndex
+    // Comparing min and max might be problematic in case min=+0 and max=-0
+    // For that reason, we prefer to compare computed index to be safer
+    throw new Error('fc.doubleNext constraints.min must be smaller or equal to constraints.max');
+  }
+  if (noNaN) {
+    return arrayInt64(minIndex, maxIndex).map(indexToDouble);
+  }
+  // In case maxIndex > 0 or in other words max > 0,
+  //   values will be [min, ..., +0, ..., max, NaN]
+  //               or [min, ..., max, NaN] if min > +0
+  // Otherwise,
+  //   values will be [NaN, min, ..., max] with max <= +0
+  const positiveMaxIdx = isStrictlyPositive64(maxIndex);
+  const minIndexWithNaN = positiveMaxIdx ? minIndex : substract64(minIndex, Unit64);
+  const maxIndexWithNaN = positiveMaxIdx ? add64(maxIndex, Unit64) : maxIndex;
+  return arrayInt64(minIndexWithNaN, maxIndexWithNaN).map((index) => {
+    if (isStrictlySmaller64(maxIndex, index) || isStrictlySmaller64(index, minIndex)) return Number.NaN;
+    else return indexToDouble(index);
+  });
+}
diff --git a/src/arbitrary/_next/floatNext.ts b/src/arbitrary/_next/floatNext.ts
new file mode 100644
index 00000000..bdae483a
--- /dev/null
+++ b/src/arbitrary/_next/floatNext.ts
@@ -0,0 +1,100 @@
+import { integer } from '../integer';
+import { Arbitrary } from '../../check/arbitrary/definition/Arbitrary';
+import { floatToIndex, indexToFloat, MAX_VALUE_32 } from '../_internals/helpers/FloatHelpers';
+
+/**
+ * Constraints to be applied on {@link floatNext}
+ * @remarks Since 2.8.0
+ * @public
+ */
+export interface FloatNextConstraints {
+  /**
+   * Lower bound for the generated 32-bit floats (included)
+   * @defaultValue Number.NEGATIVE_INFINITY, -3.4028234663852886e+38 when noDefaultInfinity is true
+   * @remarks Since 2.8.0
+   */
+  min?: number;
+  /**
+   * Upper bound for the generated 32-bit floats (included)
+   * @defaultValue Number.POSITIVE_INFINITY, 3.4028234663852886e+38 when noDefaultInfinity is true
+   * @remarks Since 2.8.0
+   */
+  max?: number;
+  /**
+   * By default, lower and upper bounds are -infinity and +infinity.
+   * By setting noDefaultInfinity to true, you move those defaults to minimal and maximal finite values.
+   * @defaultValue false
+   * @remarks Since 2.8.0
+   */
+  noDefaultInfinity?: boolean;
+  /**
+   * When set to true, no more Number.NaN can be generated.
+   * @defaultValue false
+   * @remarks Since 2.8.0
+   */
+  noNaN?: boolean;
+}
+
+/**
+ * Same as {@link floatToIndex} except it throws in case of invalid float 32
+ *
+ * @internal
+ */
+function safeFloatToIndex(f: number, constraintsLabel: keyof FloatNextConstraints) {
+  const conversionTrick = 'you can convert any double to a 32-bit float by using `new Float32Array([myDouble])[0]`';
+  const errorMessage = 'fc.floatNext constraints.' + constraintsLabel + ' must be a 32-bit float - ' + conversionTrick;
+  if (Number.isNaN(f) || (Number.isFinite(f) && (f < -MAX_VALUE_32 || f > MAX_VALUE_32))) {
+    // Number.NaN does not have any associated index in the current implementation
+    // Finite values outside of the 32-bit range for floats cannot be 32-bit floats
+    throw new Error(errorMessage);
+  }
+  const index = floatToIndex(f);
+  if (!Number.isInteger(index)) {
+    // Index not being an integer means that original value was not a valid 32-bit float
+    throw new Error(errorMessage);
+  }
+  return index;
+}
+
+/**
+ * For 32-bit floating point numbers:
+ * - sign: 1 bit
+ * - significand: 23 bits
+ * - exponent: 8 bits
+ *
+ * The smallest non-zero value (in absolute value) that can be represented by such float is: 2 ** -126 * 2 ** -23.
+ * And the largest one is: 2 ** 127 * (1 + (2 ** 23 - 1) / 2 ** 23).
+ *
+ * @param constraints - Constraints to apply when building instances
+ *
+ * @public
+ */
+export function floatNext(constraints: FloatNextConstraints = {}): Arbitrary<number> {
+  const {
+    noDefaultInfinity = false,
+    noNaN = false,
+    min = noDefaultInfinity ? -MAX_VALUE_32 : Number.NEGATIVE_INFINITY,
+    max = noDefaultInfinity ? MAX_VALUE_32 : Number.POSITIVE_INFINITY,
+  } = constraints;
+  const minIndex = safeFloatToIndex(min, 'min');
+  const maxIndex = safeFloatToIndex(max, 'max');
+  if (minIndex > maxIndex) {
+    // Comparing min and max might be problematic in case min=+0 and max=-0
+    // For that reason, we prefer to compare computed index to be safer
+    throw new Error('fc.floatNext constraints.min must be smaller or equal to constraints.max');
+  }
+  if (noNaN) {
+    return integer({ min: minIndex, max: maxIndex }).map(indexToFloat);
+  }
+  // In case maxIndex > 0 or in other words max > 0,
+  //   values will be [min, ..., +0, ..., max, NaN]
+  //               or [min, ..., max, NaN] if min > +0
+  // Otherwise,
+  //   values will be [NaN, min, ..., max] with max <= +0
+  const minIndexWithNaN = maxIndex > 0 ? minIndex : minIndex - 1;
+  const maxIndexWithNaN = maxIndex > 0 ? maxIndex + 1 : maxIndex;
+  return integer({ min: minIndexWithNaN, max: maxIndexWithNaN }).map((index) => {
+    if (index > maxIndex || index < minIndex) return Number.NaN;
+    else return indexToFloat(index);
+  });
+}
diff --git a/src/arbitrary/double.ts b/src/arbitrary/double.ts
new file mode 100644
index 00000000..ba4041b6
--- /dev/null
+++ b/src/arbitrary/double.ts
@@ -0,0 +1,125 @@
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { integer } from './integer';
+import { tuple } from './tuple';
+import { doubleNext, DoubleNextConstraints } from './_next/doubleNext';
+
+/** @internal */
+function next(n: number): Arbitrary<number> {
+  return integer(0, (1 << n) - 1);
+}
+
+/** @internal */ const doubleFactor = Math.pow(2, 27);
+/** @internal */ const doubleDivisor = Math.pow(2, -53);
+
+/** @internal */
+const doubleInternal = (): Arbitrary<number> => {
+  // uniformaly in the range 0 (inc.), 1 (exc.)
+  return tuple(next(26), next(27)).map((v) => (v[0] * doubleFactor + v[1]) * doubleDivisor);
+};
+
+/**
+ * Constraints to be applied on {@link double}
+ * @remarks Since 2.6.0
+ * @public
+ */
+export type DoubleConstraints =
+  | {
+      /**
+       * Enable new version of fc.double
+       * @remarks Since 2.8.0
+       */
+      next?: false;
+      /**
+       * Lower bound for the generated doubles (included)
+       * @remarks Since 2.6.0
+       */
+      min?: number;
+      /**
+       * Upper bound for the generated doubles (excluded)
+       * @remarks Since 2.6.0
+       */
+      max?: number;
+    }
+  | ({
+      /**
+       * Enable new version of fc.double
+       * @remarks Since 2.8.0
+       */
+      next: true;
+    } & DoubleNextConstraints);
+
+/**
+ * For floating point numbers between 0.0 (included) and 1.0 (excluded) - accuracy of `1 / 2**53`
+ * @remarks Since 0.0.6
+ * @public
+ */
+function double(): Arbitrary<number>;
+/**
+ * For floating point numbers between 0.0 (included) and max (excluded) - accuracy of `max / 2**53`
+ *
+ * @param max - Upper bound of the generated floating point
+ *
+ * @deprecated
+ * Superceded by `fc.double({max})` - see {@link https://github.com/dubzzz/fast-check/issues/992 | #992}.
+ * Ease the migration with {@link https://github.com/dubzzz/fast-check/tree/main/codemods/unify-signatures | our codemod script}.
+ *
+ * @remarks Since 1.0.0
+ * @public
+ */
+function double(max: number): Arbitrary<number>;
+/**
+ * For floating point numbers between min (included) and max (excluded) - accuracy of `(max - min) / 2**53`
+ *
+ * @param min - Lower bound of the generated floating point
+ * @param max - Upper bound of the generated floating point
+ *
+ * @remarks You may prefer to use `fc.double({min, max})` instead.
+ * @remarks Since 1.0.0
+ * @public
+ */
+function double(min: number, max: number): Arbitrary<number>;
+/**
+ * For floating point numbers in range defined by constraints - accuracy of `(max - min) / 2**53`
+ *
+ * @param constraints - Constraints to apply when building instances
+ *
+ * @remarks Since 2.6.0
+ * @public
+ */
+function double(constraints: DoubleConstraints): Arbitrary<number>;
+function double(...args: [] | [number] | [number, number] | [DoubleConstraints]): Arbitrary<number> {
+  if (typeof args[0] === 'object') {
+    if (args[0].next) {
+      return doubleNext(args[0]);
+    }
+    const min = args[0].min !== undefined ? args[0].min : 0;
+    const max = args[0].max !== undefined ? args[0].max : 1;
+    return (
+      doubleInternal()
+        .map((v) => min + v * (max - min))
+        // For v = 0.9999999999999999, min: 0, max: 5e-324
+        // we have `min + v * (max - min)` equal to `max`
+        .filter((g) => g !== max || g === min)
+    );
+  } else {
+    const a = args[0];
+    const b = args[1];
+    if (a === undefined) return doubleInternal();
+    if (b === undefined)
+      return (
+        doubleInternal()
+          .map((v) => v * a)
+          // For v = 0.9999999999999999, a: 5e-324
+          // we have `v * a` equal to `a`
+          .filter((g) => g !== a || g === 0)
+      );
+    return (
+      doubleInternal()
+        .map((v) => a + v * (b - a))
+        // For v = 0.9999999999999999, a: 0, b: 5e-324
+        // we have `a + v * (b - a)` equal to `b`
+        .filter((g) => g !== b || g === a)
+    );
+  }
+}
+export { double };
diff --git a/src/arbitrary/float.ts b/src/arbitrary/float.ts
new file mode 100644
index 00000000..271bbc12
--- /dev/null
+++ b/src/arbitrary/float.ts
@@ -0,0 +1,121 @@
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { floatNext, FloatNextConstraints } from './_next/floatNext';
+import { integer } from './integer';
+
+/** @internal */
+function next(n: number): Arbitrary<number> {
+  return integer(0, (1 << n) - 1);
+}
+
+/** @internal */
+const floatInternal = (): Arbitrary<number> => {
+  // uniformaly in the range 0 (inc.), 1 (exc.)
+  return next(24).map((v) => v / (1 << 24));
+};
+
+/**
+ * Constraints to be applied on {@link float}
+ * @remarks Since 2.6.0
+ * @public
+ */
+export type FloatConstraints =
+  | {
+      /**
+       * Enable new version of fc.float
+       * @remarks Since 2.8.0
+       */
+      next?: false;
+      /**
+       * Lower bound for the generated floats (included)
+       * @remarks Since 2.6.0
+       */
+      min?: number;
+      /**
+       * Upper bound for the generated floats (excluded)
+       * @remarks Since 2.6.0
+       */
+      max?: number;
+    }
+  | ({
+      /**
+       * Enable new version of fc.float
+       * @remarks Since 2.8.0
+       */
+      next: true;
+    } & FloatNextConstraints);
+
+/**
+ * For floating point numbers between 0.0 (included) and 1.0 (excluded) - accuracy of `1 / 2**24`
+ * @remarks Since 0.0.6
+ * @public
+ */
+function float(): Arbitrary<number>;
+/**
+ * For floating point numbers between 0.0 (included) and max (excluded) - accuracy of `max / 2**24`
+ *
+ * @param max - Upper bound of the generated floating point
+ *
+ * @deprecated
+ * Superceded by `fc.float({max})` - see {@link https://github.com/dubzzz/fast-check/issues/992 | #992}.
+ * Ease the migration with {@link https://github.com/dubzzz/fast-check/tree/main/codemods/unify-signatures | our codemod script}.
+ *
+ * @remarks Since 1.0.0
+ * @public
+ */
+function float(max: number): Arbitrary<number>;
+/**
+ * For floating point numbers between min (included) and max (excluded) - accuracy of `(max - min) / 2**24`
+ *
+ * @param min - Lower bound of the generated floating point
+ * @param max - Upper bound of the generated floating point
+ *
+ * @remarks You may prefer to use `fc.float({min, max})` instead.
+ * @remarks Since 1.0.0
+ * @public
+ */
+function float(min: number, max: number): Arbitrary<number>;
+/**
+ * For floating point numbers in range defined by constraints - accuracy of `(max - min) / 2**24`
+ *
+ * @param constraints - Constraints to apply when building instances
+ *
+ * @remarks Since 2.6.0
+ * @public
+ */
+function float(constraints: FloatConstraints): Arbitrary<number>;
+function float(...args: [] | [number] | [number, number] | [FloatConstraints]): Arbitrary<number> {
+  if (typeof args[0] === 'object') {
+    if (args[0].next) {
+      return floatNext(args[0]);
+    }
+    const min = args[0].min !== undefined ? args[0].min : 0;
+    const max = args[0].max !== undefined ? args[0].max : 1;
+    return (
+      floatInternal()
+        .map((v) => min + v * (max - min))
+        // For v = 0.9999999403953552, min: 0, max: 5e-324
+        // we have `min + v * (max - min)` equal to `max`
+        .filter((g) => g !== max || g === min)
+    );
+  } else {
+    const a = args[0];
+    const b = args[1];
+    if (a === undefined) return floatInternal();
+    if (b === undefined)
+      return (
+        floatInternal()
+          .map((v) => v * a)
+          // For v = 0.9999999403953552, a: 5e-324
+          // we have `v * a` equal to `a`
+          .filter((g) => g !== a || g === 0)
+      );
+    return (
+      floatInternal()
+        .map((v) => a + v * (b - a))
+        // For v = 0.9999999403953552, a: 0, b: 5e-324
+        // we have `a + v * (b - a)` equal to `b`
+        .filter((g) => g !== b || g === a)
+    );
+  }
+}
+export { float };
diff --git a/src/arbitrary/float32Array.ts b/src/arbitrary/float32Array.ts
index 25335eb5..e2103cd0 100644
--- a/src/arbitrary/float32Array.ts
+++ b/src/arbitrary/float32Array.ts
@@ -1,6 +1,6 @@
 import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
-import { float } from '../check/arbitrary/FloatingPointArbitrary';
-import { FloatNextConstraints } from '../check/arbitrary/FloatNextArbitrary';
+import { float } from './float';
+import { FloatNextConstraints } from './_next/floatNext';
 import { array } from './array';
 
 /**
diff --git a/src/arbitrary/float64Array.ts b/src/arbitrary/float64Array.ts
index 971546a7..4c9ef4ec 100644
--- a/src/arbitrary/float64Array.ts
+++ b/src/arbitrary/float64Array.ts
@@ -1,6 +1,6 @@
 import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
-import { DoubleNextConstraints } from '../check/arbitrary/DoubleNextArbitrary';
-import { double } from '../check/arbitrary/FloatingPointArbitrary';
+import { DoubleNextConstraints } from './_next/doubleNext';
+import { double } from './double';
 import { array } from './array';
 
 /**
diff --git a/src/check/arbitrary/FloatingPointArbitrary.ts b/src/check/arbitrary/FloatingPointArbitrary.ts
deleted file mode 100644
index 50be354f..00000000
--- a/src/check/arbitrary/FloatingPointArbitrary.ts
+++ /dev/null
@@ -1,239 +0,0 @@
-import { Arbitrary } from './definition/Arbitrary';
-import { doubleNext, DoubleNextConstraints } from './DoubleNextArbitrary';
-import { floatNext, FloatNextConstraints } from './FloatNextArbitrary';
-import { integer } from '../../arbitrary/integer';
-import { tuple } from '../../arbitrary/tuple';
-
-/** @internal */
-function next(n: number): Arbitrary<number> {
-  return integer(0, (1 << n) - 1);
-}
-
-/** @internal */
-const floatInternal = (): Arbitrary<number> => {
-  // uniformaly in the range 0 (inc.), 1 (exc.)
-  return next(24).map((v) => v / (1 << 24));
-};
-
-/**
- * Constraints to be applied on {@link float}
- * @remarks Since 2.6.0
- * @public
- */
-export type FloatConstraints =
-  | {
-      /**
-       * Enable new version of fc.float
-       * @remarks Since 2.8.0
-       */
-      next?: false;
-      /**
-       * Lower bound for the generated floats (included)
-       * @remarks Since 2.6.0
-       */
-      min?: number;
-      /**
-       * Upper bound for the generated floats (excluded)
-       * @remarks Since 2.6.0
-       */
-      max?: number;
-    }
-  | ({
-      /**
-       * Enable new version of fc.float
-       * @remarks Since 2.8.0
-       */
-      next: true;
-    } & FloatNextConstraints);
-
-/**
- * For floating point numbers between 0.0 (included) and 1.0 (excluded) - accuracy of `1 / 2**24`
- * @remarks Since 0.0.6
- * @public
- */
-function float(): Arbitrary<number>;
-/**
- * For floating point numbers between 0.0 (included) and max (excluded) - accuracy of `max / 2**24`
- *
- * @param max - Upper bound of the generated floating point
- *
- * @deprecated
- * Superceded by `fc.float({max})` - see {@link https://github.com/dubzzz/fast-check/issues/992 | #992}.
- * Ease the migration with {@link https://github.com/dubzzz/fast-check/tree/main/codemods/unify-signatures | our codemod script}.
- *
- * @remarks Since 1.0.0
- * @public
- */
-function float(max: number): Arbitrary<number>;
-/**
- * For floating point numbers between min (included) and max (excluded) - accuracy of `(max - min) / 2**24`
- *
- * @param min - Lower bound of the generated floating point
- * @param max - Upper bound of the generated floating point
- *
- * @remarks You may prefer to use `fc.float({min, max})` instead.
- * @remarks Since 1.0.0
- * @public
- */
-function float(min: number, max: number): Arbitrary<number>;
-/**
- * For floating point numbers in range defined by constraints - accuracy of `(max - min) / 2**24`
- *
- * @param constraints - Constraints to apply when building instances
- *
- * @remarks Since 2.6.0
- * @public
- */
-function float(constraints: FloatConstraints): Arbitrary<number>;
-function float(...args: [] | [number] | [number, number] | [FloatConstraints]): Arbitrary<number> {
-  if (typeof args[0] === 'object') {
-    if (args[0].next) {
-      return floatNext(args[0]);
-    }
-    const min = args[0].min !== undefined ? args[0].min : 0;
-    const max = args[0].max !== undefined ? args[0].max : 1;
-    return (
-      floatInternal()
-        .map((v) => min + v * (max - min))
-        // For v = 0.9999999403953552, min: 0, max: 5e-324
-        // we have `min + v * (max - min)` equal to `max`
-        .filter((g) => g !== max || g === min)
-    );
-  } else {
-    const a = args[0];
-    const b = args[1];
-    if (a === undefined) return floatInternal();
-    if (b === undefined)
-      return (
-        floatInternal()
-          .map((v) => v * a)
-          // For v = 0.9999999403953552, a: 5e-324
-          // we have `v * a` equal to `a`
-          .filter((g) => g !== a || g === 0)
-      );
-    return (
-      floatInternal()
-        .map((v) => a + v * (b - a))
-        // For v = 0.9999999403953552, a: 0, b: 5e-324
-        // we have `a + v * (b - a)` equal to `b`
-        .filter((g) => g !== b || g === a)
-    );
-  }
-}
-
-/** @internal */ const doubleFactor = Math.pow(2, 27);
-/** @internal */ const doubleDivisor = Math.pow(2, -53);
-
-/** @internal */
-const doubleInternal = (): Arbitrary<number> => {
-  // uniformaly in the range 0 (inc.), 1 (exc.)
-  return tuple(next(26), next(27)).map((v) => (v[0] * doubleFactor + v[1]) * doubleDivisor);
-};
-
-/**
- * Constraints to be applied on {@link double}
- * @remarks Since 2.6.0
- * @public
- */
-export type DoubleConstraints =
-  | {
-      /**
-       * Enable new version of fc.double
-       * @remarks Since 2.8.0
-       */
-      next?: false;
-      /**
-       * Lower bound for the generated doubles (included)
-       * @remarks Since 2.6.0
-       */
-      min?: number;
-      /**
-       * Upper bound for the generated doubles (excluded)
-       * @remarks Since 2.6.0
-       */
-      max?: number;
-    }
-  | ({
-      /**
-       * Enable new version of fc.double
-       * @remarks Since 2.8.0
-       */
-      next: true;
-    } & DoubleNextConstraints);
-
-/**
- * For floating point numbers between 0.0 (included) and 1.0 (excluded) - accuracy of `1 / 2**53`
- * @remarks Since 0.0.6
- * @public
- */
-function double(): Arbitrary<number>;
-/**
- * For floating point numbers between 0.0 (included) and max (excluded) - accuracy of `max / 2**53`
- *
- * @param max - Upper bound of the generated floating point
- *
- * @deprecated
- * Superceded by `fc.double({max})` - see {@link https://github.com/dubzzz/fast-check/issues/992 | #992}.
- * Ease the migration with {@link https://github.com/dubzzz/fast-check/tree/main/codemods/unify-signatures | our codemod script}.
- *
- * @remarks Since 1.0.0
- * @public
- */
-function double(max: number): Arbitrary<number>;
-/**
- * For floating point numbers between min (included) and max (excluded) - accuracy of `(max - min) / 2**53`
- *
- * @param min - Lower bound of the generated floating point
- * @param max - Upper bound of the generated floating point
- *
- * @remarks You may prefer to use `fc.double({min, max})` instead.
- * @remarks Since 1.0.0
- * @public
- */
-function double(min: number, max: number): Arbitrary<number>;
-/**
- * For floating point numbers in range defined by constraints - accuracy of `(max - min) / 2**53`
- *
- * @param constraints - Constraints to apply when building instances
- *
- * @remarks Since 2.6.0
- * @public
- */
-function double(constraints: DoubleConstraints): Arbitrary<number>;
-function double(...args: [] | [number] | [number, number] | [DoubleConstraints]): Arbitrary<number> {
-  if (typeof args[0] === 'object') {
-    if (args[0].next) {
-      return doubleNext(args[0]);
-    }
-    const min = args[0].min !== undefined ? args[0].min : 0;
-    const max = args[0].max !== undefined ? args[0].max : 1;
-    return (
-      doubleInternal()
-        .map((v) => min + v * (max - min))
-        // For v = 0.9999999999999999, min: 0, max: 5e-324
-        // we have `min + v * (max - min)` equal to `max`
-        .filter((g) => g !== max || g === min)
-    );
-  } else {
-    const a = args[0];
-    const b = args[1];
-    if (a === undefined) return doubleInternal();
-    if (b === undefined)
-      return (
-        doubleInternal()
-          .map((v) => v * a)
-          // For v = 0.9999999999999999, a: 5e-324
-          // we have `v * a` equal to `a`
-          .filter((g) => g !== a || g === 0)
-      );
-    return (
-      doubleInternal()
-        .map((v) => a + v * (b - a))
-        // For v = 0.9999999999999999, a: 0, b: 5e-324
-        // we have `a + v * (b - a)` equal to `b`
-        .filter((g) => g !== b || g === a)
-    );
-  }
-}
-
-export { float, double };
diff --git a/src/fast-check-default.ts b/src/fast-check-default.ts
index 3d48da48..18541aeb 100644
--- a/src/fast-check-default.ts
+++ b/src/fast-check-default.ts
@@ -44,7 +44,8 @@ import { Arbitrary } from './check/arbitrary/definition/Arbitrary';
 import { Shrinkable } from './check/arbitrary/definition/Shrinkable';
 import { dictionary } from './arbitrary/dictionary';
 import { emailAddress } from './arbitrary/emailAddress';
-import { double, float, DoubleConstraints, FloatConstraints } from './check/arbitrary/FloatingPointArbitrary';
+import { double, DoubleConstraints } from './arbitrary/double';
+import { float, FloatConstraints } from './arbitrary/float';
 import { frequency, WeightedArbitrary, FrequencyValue, FrequencyContraints } from './arbitrary/frequency';
 import { compareBooleanFunc } from './arbitrary/compareBooleanFunc';
 import { compareFunc } from './arbitrary/compareFunc';
@@ -144,7 +145,7 @@ import { ArbitraryWithContextualShrink } from './check/arbitrary/definition/Arbi
 import { CommandsContraints } from './check/model/commands/CommandsContraints';
 import { PreconditionFailure } from './check/precondition/PreconditionFailure';
 import { RandomType } from './check/runner/configuration/RandomType';
-import { FloatNextConstraints } from './check/arbitrary/FloatNextArbitrary';
+import { FloatNextConstraints } from './arbitrary/_next/floatNext';
 import { int8Array, IntArrayConstraints } from './arbitrary/int8Array';
 import { int16Array } from './arbitrary/int16Array';
 import { int32Array } from './arbitrary/int32Array';
@@ -155,7 +156,7 @@ import { uint32Array } from './arbitrary/uint32Array';
 import { float32Array, Float32ArrayConstraints } from './arbitrary/float32Array';
 import { float64Array, Float64ArrayConstraints } from './arbitrary/float64Array';
 import { sparseArray, SparseArrayConstraints } from './arbitrary/sparseArray';
-import { DoubleNextConstraints } from './check/arbitrary/DoubleNextArbitrary';
+import { DoubleNextConstraints } from './arbitrary/_next/doubleNext';
 import { NextArbitrary } from './check/arbitrary/definition/NextArbitrary';
 import { NextValue } from './check/arbitrary/definition/NextValue';
 import { convertFromNext, convertFromNextWithShrunkOnce, convertToNext } from './check/arbitrary/definition/Converters';


Test patch:
diff --git a/test/unit/check/arbitrary/helpers/ArrayInt64.spec.ts b/test/unit/arbitrary/_internals/helpers/ArrayInt64.spec.ts
similarity index 65%
rename from test/unit/check/arbitrary/helpers/ArrayInt64.spec.ts
rename to test/unit/arbitrary/_internals/helpers/ArrayInt64.spec.ts
index e0159cc8..decb2bd0 100644
--- a/test/unit/check/arbitrary/helpers/ArrayInt64.spec.ts
+++ b/test/unit/arbitrary/_internals/helpers/ArrayInt64.spec.ts
@@ -9,7 +9,7 @@ import {
   logLike64,
   negative64,
   substract64,
-} from '../../../../../src/check/arbitrary/helpers/ArrayInt64';
+} from '../../../../../src/arbitrary/_internals/helpers/ArrayInt64';
 
 function toArrayInt64(b: bigint, withNegativeZero: boolean): ArrayInt64 {
   const posB = b < BigInt(0) ? -b : b;
@@ -48,13 +48,23 @@ describe('ArrayInt64', () => {
   const MaxArrayIntValue = (BigInt(1) << BigInt(64)) - BigInt(1);
 
   describe('isEqual64', () => {
-    it('Should consider identical values as equal', () =>
+    it('should consider identical values as equal', () => {
       fc.assert(
         fc.property(fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }), (a) => {
-          expect(isEqual64(toArrayInt64(a, false), toArrayInt64(a, false))).toBe(true);
+          // Arrange
+          const a64 = toArrayInt64(a, false);
+          const a64Cloned = toArrayInt64(a, false);
+
+          // Act
+          const out = isEqual64(a64, a64Cloned);
+
+          // Assert
+          expect(out).toBe(true);
         })
-      ));
-    it('Should consider two different values as not equal', () =>
+      );
+    });
+
+    it('should consider two different values as not equal', () => {
       fc.assert(
         fc.property(
           fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }),
@@ -62,12 +72,22 @@ describe('ArrayInt64', () => {
           fc.boolean(),
           fc.boolean(),
           (a, b, na, nb) => {
+            // Arrange
             fc.pre(a !== b);
-            expect(isEqual64(toArrayInt64(a, na), toArrayInt64(b, nb))).toBe(false);
+            const a64 = toArrayInt64(a, na);
+            const b64 = toArrayInt64(b, nb);
+
+            // Act
+            const out = isEqual64(a64, b64);
+
+            // Assert
+            expect(out).toBe(false);
           }
         )
-      ));
-    it('Should consider zero and -zero to be equal', () => {
+      );
+    });
+
+    it('should consider zero and -zero to be equal', () => {
       expect(isEqual64({ sign: -1, data: [0, 0] }, { sign: -1, data: [0, 0] })).toBe(true);
       expect(isEqual64({ sign: 1, data: [0, 0] }, { sign: -1, data: [0, 0] })).toBe(true);
       expect(isEqual64({ sign: -1, data: [0, 0] }, { sign: 1, data: [0, 0] })).toBe(true);
@@ -76,7 +96,7 @@ describe('ArrayInt64', () => {
   });
 
   describe('isStrictlySmaller64', () => {
-    it('Should properly compare two ArrayInt64 (including negative zeros)', () =>
+    it('should properly compare two ArrayInt64 (including negative zeros)', () => {
       fc.assert(
         fc.property(
           fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }),
@@ -84,11 +104,21 @@ describe('ArrayInt64', () => {
           fc.boolean(),
           fc.boolean(),
           (a, b, na, nb) => {
-            expect(isStrictlySmaller64(toArrayInt64(a, na), toArrayInt64(b, nb))).toBe(a < b);
+            // Arrange
+            const a64 = toArrayInt64(a, na);
+            const b64 = toArrayInt64(b, nb);
+
+            // Act
+            const out = isStrictlySmaller64(a64, b64);
+
+            // Assert
+            expect(out).toBe(a < b);
           }
         )
-      ));
-    it('Should consider zero and -zero as equal values (never strictly smaller that the other)', () => {
+      );
+    });
+
+    it('should consider zero and -zero as equal values (never strictly smaller that the other)', () => {
       expect(isStrictlySmaller64({ sign: -1, data: [0, 0] }, { sign: -1, data: [0, 0] })).toBe(false);
       expect(isStrictlySmaller64({ sign: 1, data: [0, 0] }, { sign: -1, data: [0, 0] })).toBe(false);
       expect(isStrictlySmaller64({ sign: -1, data: [0, 0] }, { sign: 1, data: [0, 0] })).toBe(false);
@@ -97,7 +127,7 @@ describe('ArrayInt64', () => {
   });
 
   describe('substract64', () => {
-    it('Should properly substract two ArrayInt64 (including negative zeros)', () =>
+    it('should properly substract two ArrayInt64 (including negative zeros)', () => {
       fc.assert(
         fc.property(
           fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }),
@@ -105,45 +135,72 @@ describe('ArrayInt64', () => {
           fc.boolean(),
           fc.boolean(),
           (a, b, na, nb) => {
+            // Arrange
             const expectedResult = a - b;
             fc.pre(expectedResult >= -MaxArrayIntValue);
             fc.pre(expectedResult <= MaxArrayIntValue);
-            const result64 = substract64(toArrayInt64(a, na), toArrayInt64(b, nb));
+            const a64 = toArrayInt64(a, na);
+            const b64 = toArrayInt64(b, nb);
+
+            // Act
+            const result64 = substract64(a64, b64);
+
+            // Assert
             expectValidArrayInt(result64);
             expectValidZeroIfAny(result64);
             expect(toBigInt(result64)).toBe(expectedResult);
           }
         )
-      ));
-    it('Should equal to first term if second one is zero', () =>
+      );
+    });
+
+    it('should equal to first term if second one is zero', () => {
       fc.assert(
         fc.property(
           fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }),
           fc.boolean(),
           fc.boolean(),
           (a, na, nb) => {
-            const result64 = substract64(toArrayInt64(a, na), toArrayInt64(BigInt(0), nb));
+            // Arrange
+            const a64 = toArrayInt64(a, na);
+            const b64 = toArrayInt64(BigInt(0), nb);
+
+            // Act
+            const result64 = substract64(a64, b64);
+
+            // Assert
             expectValidArrayInt(result64);
             expectValidZeroIfAny(result64);
             expect(result64).toEqual(toArrayInt64(a, false)); // toArrayInt64(a, false): sign must be + for 0
           }
         )
-      ));
-    it('Should equal to minus second term if first one is zero', () =>
+      );
+    });
+
+    it('should equal to minus second term if first one is zero', () => {
       fc.assert(
         fc.property(
           fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }),
           fc.boolean(),
           fc.boolean(),
           (a, na, nb) => {
-            const result64 = substract64(toArrayInt64(BigInt(0), nb), toArrayInt64(a, na));
+            // Arrange
+            const z64 = toArrayInt64(BigInt(0), nb);
+            const a64 = toArrayInt64(a, na);
+
+            // Act
+            const result64 = substract64(z64, a64);
+
+            // Assert
             expectValidArrayInt(result64);
             expectValidZeroIfAny(result64);
             expect(result64).toEqual(toArrayInt64(-a, false)); // toArrayInt64(-a, false): sign must be + for 0
           }
         )
-      ));
-    it('Should equal to zero when substracting zeros', () => {
+      );
+    });
+
+    it('should equal to zero when substracting zeros', () => {
       const negZero: ArrayInt64 = { sign: -1, data: [0, 0] };
       const posZero: ArrayInt64 = { sign: 1, data: [0, 0] };
       expect(substract64(negZero, negZero)).toEqual(posZero);
@@ -154,19 +211,26 @@ describe('ArrayInt64', () => {
   });
 
   describe('negative64', () => {
-    it('Should properly negate an ArrayInt64 (including negative zeros)', () =>
+    it('should properly negate an ArrayInt64 (including negative zeros)', () => {
       fc.assert(
         fc.property(fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }), fc.boolean(), (a, na) => {
+          // Arrange
           const expectedResult = -a;
-          const result64 = negative64(toArrayInt64(a, na));
+          const a64 = toArrayInt64(a, na);
+
+          // Act
+          const result64 = negative64(a64);
+
+          // Assert
           expectValidArrayInt(result64);
           expect(toBigInt(result64)).toBe(expectedResult);
         })
-      ));
+      );
+    });
   });
 
   describe('add64', () => {
-    it('Should properly add two ArrayInt64 (including negative zeros)', () =>
+    it('should properly add two ArrayInt64 (including negative zeros)', () => {
       fc.assert(
         fc.property(
           fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }),
@@ -174,45 +238,72 @@ describe('ArrayInt64', () => {
           fc.boolean(),
           fc.boolean(),
           (a, b, na, nb) => {
+            // Arrange
             const expectedResult = a + b;
             fc.pre(expectedResult >= -MaxArrayIntValue);
             fc.pre(expectedResult <= MaxArrayIntValue);
-            const result64 = add64(toArrayInt64(a, na), toArrayInt64(b, nb));
+            const a64 = toArrayInt64(a, na);
+            const b64 = toArrayInt64(b, nb);
+
+            // Act
+            const result64 = add64(a64, b64);
+
+            // Assert
             expectValidArrayInt(result64);
             expectValidZeroIfAny(result64);
             expect(toBigInt(result64)).toBe(expectedResult);
           }
         )
-      ));
-    it('Should equal to first term if second one is zero', () =>
+      );
+    });
+
+    it('should equal to first term if second one is zero', () => {
       fc.assert(
         fc.property(
           fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }),
           fc.boolean(),
           fc.boolean(),
           (a, na, nb) => {
-            const result64 = add64(toArrayInt64(a, na), toArrayInt64(BigInt(0), nb));
+            // Arrange
+            const a64 = toArrayInt64(a, na);
+            const z64 = toArrayInt64(BigInt(0), nb);
+
+            // Act
+            const result64 = add64(a64, z64);
+
+            // Assert
             expectValidArrayInt(result64);
             expectValidZeroIfAny(result64);
             expect(result64).toEqual(toArrayInt64(a, false)); // toArrayInt64(a, false): sign must be + for 0
           }
         )
-      ));
-    it('Should equal to second term if first one is zero', () =>
+      );
+    });
+
+    it('should equal to second term if first one is zero', () => {
       fc.assert(
         fc.property(
           fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }),
           fc.boolean(),
           fc.boolean(),
           (a, na, nb) => {
-            const result64 = add64(toArrayInt64(BigInt(0), nb), toArrayInt64(a, na));
+            // Arrange
+            const z64 = toArrayInt64(BigInt(0), nb);
+            const a64 = toArrayInt64(a, na);
+
+            // Act
+            const result64 = add64(z64, a64);
+
+            // Assert
             expectValidArrayInt(result64);
             expectValidZeroIfAny(result64);
             expect(result64).toEqual(toArrayInt64(a, false)); // toArrayInt64(a, false): sign must be + for 0
           }
         )
-      ));
-    it('Should equal to zero when adding zeros together', () => {
+      );
+    });
+
+    it('should equal to zero when adding zeros together', () => {
       const negZero: ArrayInt64 = { sign: -1, data: [0, 0] };
       const posZero: ArrayInt64 = { sign: 1, data: [0, 0] };
       expect(add64(negZero, negZero)).toEqual(posZero);
@@ -223,36 +314,57 @@ describe('ArrayInt64', () => {
   });
 
   describe('halve64', () => {
-    it('Should properly halve an ArrayInt64 (including negative zeros)', () =>
+    it('should properly halve an ArrayInt64 (including negative zeros)', () => {
       fc.assert(
         fc.property(fc.bigInt({ min: -MaxArrayIntValue, max: MaxArrayIntValue }), fc.boolean(), (a, na) => {
+          // Arrange
           const expectedResult = a / BigInt(2);
-          const result64 = halve64(toArrayInt64(a, na));
+          const a64 = toArrayInt64(a, na);
+
+          // Act
+          const result64 = halve64(a64);
+
+          // Assert
           expectValidArrayInt(result64);
           expect(toBigInt(result64)).toBe(expectedResult);
         })
-      ));
+      );
+    });
   });
 
   describe('logLike64', () => {
-    it('Should properly log2 an ArrayInt64', () =>
+    it('should properly log2 an ArrayInt64', () => {
       fc.assert(
         fc.property(fc.bigInt({ min: BigInt(1), max: MaxArrayIntValue }), (a) => {
+          // Arrange
           const expectedResult = Math.floor(Math.log(Number(a)) / Math.log(2));
-          const result64 = logLike64(toArrayInt64(a, false)); // no negative zero: a > 0
+          const a64 = toArrayInt64(a, false); // no negative zero: a > 0
+
+          // Act
+          const result64 = logLike64(a64);
+
+          // Assert
           expectValidArrayInt(result64);
           expect(toBigInt(result64)).toBe(BigInt(expectedResult));
         })
-      ));
+      );
+    });
 
-    it('Should properly log2 a negative ArrayInt64', () =>
+    it('should properly log2 a negative ArrayInt64', () => {
       fc.assert(
         fc.property(fc.bigInt({ min: BigInt(1), max: MaxArrayIntValue }), (a) => {
+          // Arrange
           const expectedResult = -Math.floor(Math.log(Number(a)) / Math.log(2));
-          const result64 = logLike64(toArrayInt64(-a, false)); // no negative zero: a > 0
+          const a64 = toArrayInt64(-a, false); // no negative zero: a > 0
+
+          // Act
+          const result64 = logLike64(a64);
+
+          // Assert
           expectValidArrayInt(result64);
           expect(toBigInt(result64)).toBe(BigInt(expectedResult));
         })
-      ));
+      );
+    });
   });
 });
diff --git a/test/unit/arbitrary/_internals/helpers/DoubleHelpers.spec.ts b/test/unit/arbitrary/_internals/helpers/DoubleHelpers.spec.ts
new file mode 100644
index 00000000..7e341461
--- /dev/null
+++ b/test/unit/arbitrary/_internals/helpers/DoubleHelpers.spec.ts
@@ -0,0 +1,230 @@
+import * as fc from '../../../../../lib/fast-check';
+
+import { float64raw, isStrictlySmaller } from '../../../check/arbitrary/generic/FloatingPointHelpers';
+import {
+  decomposeDouble,
+  doubleToIndex,
+  indexToDouble,
+} from '../../../../../src/arbitrary/_internals/helpers/DoubleHelpers';
+
+type Index = ReturnType<typeof doubleToIndex>;
+const toIndex = (raw: bigint | string): Index => {
+  const b = typeof raw === 'string' ? BigInt(raw) : raw;
+  const pb = b < BigInt(0) ? -b : b;
+  return { sign: b < BigInt(0) ? -1 : 1, data: [Number(pb >> BigInt(32)), Number(pb & BigInt(0xffffffff))] };
+};
+const toBigInt = (index: Index): bigint => {
+  return BigInt(index.sign) * ((BigInt(index.data[0]) << BigInt(32)) + BigInt(index.data[1]));
+};
+
+describe('decomposeDouble', () => {
+  it('should properly decompose basic values', () => {
+    expect(decomposeDouble(0)).toEqual({ exponent: -1022, significand: 0 });
+    expect(decomposeDouble(1)).toEqual({ exponent: 0, significand: 1 });
+    expect(decomposeDouble(128)).toEqual({ exponent: 7, significand: 1 });
+    expect(decomposeDouble(201)).toEqual({ exponent: 7, significand: 1.5703125 });
+  });
+
+  it('should properly decompose negative values', () => {
+    expect(decomposeDouble(-0)).toEqual({ exponent: -1022, significand: -0 });
+    expect(decomposeDouble(-1)).toEqual({ exponent: 0, significand: -1 });
+  });
+
+  it('should properly decompose extreme values', () => {
+    expect(decomposeDouble(Number.MAX_VALUE)).toEqual({ exponent: 1023, significand: 2 - Number.EPSILON });
+    expect(decomposeDouble(Number.MIN_VALUE)).toEqual({ exponent: -1022, significand: Number.EPSILON });
+    expect(decomposeDouble(Number.EPSILON)).toEqual({ exponent: -52, significand: 1 });
+    expect(decomposeDouble(1 + Number.EPSILON)).toEqual({ exponent: 0, significand: 1 + Number.EPSILON });
+  });
+
+  it('should decompose a 64-bit float into its equivalent (significand, exponent)', () => {
+    fc.assert(
+      fc.property(float64raw(), (f64) => {
+        // Arrange
+        fc.pre(!Number.isNaN(f64));
+
+        // Act
+        const { exponent, significand } = decomposeDouble(f64);
+
+        // Assert
+        expect(significand * 2 ** exponent).toBe(f64);
+      })
+    );
+  });
+});
+
+describe('doubleToIndex', () => {
+  it('should always produce well-formed indexes', () => {
+    fc.assert(
+      fc.property(float64raw(), (d) => {
+        // Arrange
+        fc.pre(!Number.isNaN(d));
+
+        // Act
+        const index = doubleToIndex(d);
+
+        // Assert
+        expect(index.data[0]).toBeGreaterThanOrEqual(0);
+        expect(index.data[0]).toBeLessThanOrEqual(0xffffffff);
+        expect(Number.isInteger(index.data[0])).toBe(true);
+        expect(index.data[1]).toBeGreaterThanOrEqual(0);
+        expect(index.data[1]).toBeLessThanOrEqual(0xffffffff);
+        expect(Number.isInteger(index.data[1])).toBe(true);
+      })
+    );
+  });
+
+  if (typeof BigInt === 'undefined') {
+    it('no test', () => {
+      expect(true).toBe(true);
+    });
+    return;
+  } // Following tests require BigInt to be launched
+
+  it('should properly compute indexes', () => {
+    expect(doubleToIndex(0)).toEqual(toIndex('0'));
+    expect(doubleToIndex(Number.MIN_VALUE)).toEqual(toIndex('1'));
+    expect(doubleToIndex(2 * Number.MIN_VALUE)).toEqual(toIndex('2'));
+    expect(doubleToIndex(3 * Number.MIN_VALUE)).toEqual(toIndex('3'));
+    // Last double with minimal exponent, ie -1022
+    // index(last with min exponent) = 2**53 - 1
+    expect(doubleToIndex(2 ** -1022 * (2 - Number.EPSILON))).toEqual(toIndex('9007199254740991'));
+    // First double without minimal exponent, ie -1022
+    // index(first without min exponent) = index(last with min exponent) + 1
+    expect(doubleToIndex(2 ** -1021)).toEqual(toIndex('9007199254740992'));
+    // Number.EPSILON === 1. * 2**-52 --> m = 1, e = -52
+    // index(Number.EPSILON) = 2**53 + (-52 - (-1022) -1) * 2**52
+    expect(doubleToIndex(Number.EPSILON)).toEqual(toIndex('4372995238176751616'));
+    // index(1 - Number.EPSILON / 2) = index(1) - 1
+    expect(doubleToIndex(1 - Number.EPSILON / 2)).toEqual(toIndex('4607182418800017407'));
+    // 1 === 1. * 2**0 --> m = 1, e = 0
+    // index(1) = 2**53 + (0 - (-1022) -1) * 2**52
+    expect(doubleToIndex(1)).toEqual(toIndex('4607182418800017408'));
+    // index(1 + Number.EPSILON) = index(1) + 1
+    expect(doubleToIndex(1 + Number.EPSILON)).toEqual(toIndex('4607182418800017409'));
+    // index(2 - Number.EPSILON) = index(2) - 1 = index(1 + (2 ** 52 - 1) * Number.EPSILON)
+    expect(doubleToIndex(2 - Number.EPSILON)).toEqual(toIndex('4611686018427387903'));
+    // 1 === 1. * 2**1 --> m = 1, e = 1
+    // index(2) = index(1) + 2**52
+    expect(doubleToIndex(2)).toEqual(toIndex('4611686018427387904'));
+    // Number.MAX_VALUE === (1 + (2**52-1)/2**52) * 2**1023 --> m = 1 + (2**52-1)/2**52, e = 1023
+    // index(Number.MAX_VALUE) = index(next(Number.MAX_VALUE)) -1 = 2**53 + (1024 - (-1022) -1) * 2**52 -1
+    expect(doubleToIndex(Number.MAX_VALUE)).toEqual(toIndex('9218868437227405311'));
+  });
+
+  it('should properly compute negative indexes', () => {
+    expect(doubleToIndex(-0)).toEqual(toIndex('-1'));
+    expect(doubleToIndex(-Number.MIN_VALUE)).toEqual(toIndex('-2'));
+    expect(doubleToIndex(-Number.MAX_VALUE)).toEqual(toIndex('-9218868437227405312'));
+  });
+
+  it('should properly compute indexes for infinity', () => {
+    expect(doubleToIndex(Number.NEGATIVE_INFINITY)).toEqual(
+      toIndex(toBigInt(doubleToIndex(-Number.MAX_VALUE)) - BigInt(1))
+    );
+    expect(doubleToIndex(Number.POSITIVE_INFINITY)).toEqual(
+      toIndex(toBigInt(doubleToIndex(Number.MAX_VALUE)) + BigInt(1))
+    );
+  });
+
+  it('should be able to infer index for negative double from the positive one', () => {
+    fc.assert(
+      fc.property(float64raw(), (d) => {
+        // Arrange
+        fc.pre(!Number.isNaN(d));
+        const posD = d > 0 || 1 / d > 0 ? d : -d;
+
+        // Act
+        const bigIntIndexPos = toBigInt(doubleToIndex(posD));
+        const bigIntIndexNeg = toBigInt(doubleToIndex(-posD));
+
+        // Assert
+        expect(bigIntIndexNeg).toEqual(-bigIntIndexPos - BigInt(1));
+      })
+    );
+  });
+
+  it('should return index +1 for the successor of a given double', () => {
+    fc.assert(
+      fc.property(
+        fc.integer({ min: -1022, max: +1023 }),
+        fc.integer({ min: 0, max: 2 ** 53 - 1 }),
+        (exponent, rescaledSignificand) => {
+          // Arrange
+          fc.pre(exponent === -1022 || rescaledSignificand >= 2 ** 52); // valid
+          fc.pre(exponent !== 1023 || rescaledSignificand !== 2 ** 53 - 1); // not max
+          const current = rescaledSignificand * Number.EPSILON * 2 ** exponent;
+          const next = (rescaledSignificand + 1) * Number.EPSILON * 2 ** exponent;
+
+          // Act
+          const bigIntIndexCurrent = toBigInt(doubleToIndex(current));
+          const bigIntIndexNext = toBigInt(doubleToIndex(next));
+
+          // Assert
+          expect(bigIntIndexNext).toEqual(bigIntIndexCurrent + BigInt(1));
+        }
+      )
+    );
+  });
+
+  it('should preserve ordering between two doubles', () => {
+    fc.assert(
+      fc.property(float64raw(), float64raw(), (fa64, fb64) => {
+        // Arrange
+        fc.pre(!Number.isNaN(fa64) && !Number.isNaN(fb64));
+
+        // Act / Assert
+        if (isStrictlySmaller(fa64, fb64)) {
+          expect(toBigInt(doubleToIndex(fa64))).toBeLessThan(toBigInt(doubleToIndex(fb64)));
+        } else {
+          expect(toBigInt(doubleToIndex(fa64))).toBeGreaterThanOrEqual(toBigInt(doubleToIndex(fb64)));
+        }
+      })
+    );
+  });
+});
+
+describe('indexToDouble', () => {
+  it('Should reverse doubleToIndex', () =>
+    fc.assert(
+      fc.property(float64raw(), (f64) => {
+        fc.pre(!Number.isNaN(f64));
+        expect(indexToDouble(doubleToIndex(f64))).toBe(f64);
+      })
+    ));
+
+  if (typeof BigInt === 'undefined') {
+    it('no test', () => {
+      expect(true).toBe(true);
+    });
+    return;
+  } // Following tests require BigInt to be launched
+
+  it('should properly find doubles corresponding to well-known values', () => {
+    expect(indexToDouble(toIndex('-9218868437227405313'))).toBe(Number.NEGATIVE_INFINITY);
+    expect(indexToDouble(toIndex('-9218868437227405312'))).toBe(-Number.MAX_VALUE);
+    expect(indexToDouble(toIndex('-1'))).toBe(-0);
+    expect(indexToDouble(toIndex('0'))).toBe(0);
+    expect(indexToDouble(toIndex('4372995238176751616'))).toBe(Number.EPSILON);
+    expect(indexToDouble(toIndex('9218868437227405311'))).toBe(Number.MAX_VALUE);
+    expect(indexToDouble(toIndex('9218868437227405312'))).toBe(Number.POSITIVE_INFINITY);
+  });
+
+  it('should be reversed by doubleToIndex', () => {
+    fc.assert(
+      fc.property(
+        fc.bigInt({ min: BigInt('-9218868437227405313'), max: BigInt('9218868437227405312') }),
+        (bigIntIndex) => {
+          // The test below checks that indexToDouble(doubleToIndex) is identity
+          // It does not confirm that doubleToIndex(indexToDouble)) is identity
+
+          // Arrange
+          const index = toIndex(bigIntIndex);
+
+          // Act / Assert
+          expect(doubleToIndex(indexToDouble(index))).toEqual(index);
+        }
+      )
+    );
+  });
+});
diff --git a/test/unit/arbitrary/_internals/helpers/FloatHelpers.spec.ts b/test/unit/arbitrary/_internals/helpers/FloatHelpers.spec.ts
new file mode 100644
index 00000000..8f79d5f3
--- /dev/null
+++ b/test/unit/arbitrary/_internals/helpers/FloatHelpers.spec.ts
@@ -0,0 +1,183 @@
+import * as fc from '../../../../../lib/fast-check';
+
+import {
+  float32raw,
+  isNotNaN32bits,
+  isStrictlySmaller,
+  isFiniteNotNaN32bits,
+} from '../../../check/arbitrary/generic/FloatingPointHelpers';
+import {
+  decomposeFloat,
+  EPSILON_32,
+  floatToIndex,
+  indexToFloat,
+  MAX_VALUE_32,
+  MIN_VALUE_32,
+} from '../../../../../src/arbitrary/_internals/helpers/FloatHelpers';
+
+describe('decomposeFloat', () => {
+  it('should properly decompose basic values', () => {
+    expect(decomposeFloat(0)).toEqual({ exponent: -126, significand: 0 });
+    expect(decomposeFloat(1)).toEqual({ exponent: 0, significand: 1 });
+    expect(decomposeFloat(128)).toEqual({ exponent: 7, significand: 1 });
+    expect(decomposeFloat(201)).toEqual({ exponent: 7, significand: 1.5703125 });
+  });
+
+  it('should properly decompose negative values', () => {
+    expect(decomposeFloat(-0)).toEqual({ exponent: -126, significand: -0 });
+    expect(decomposeFloat(-1)).toEqual({ exponent: 0, significand: -1 });
+  });
+
+  it('should properly decompose extreme values', () => {
+    expect(decomposeFloat(MAX_VALUE_32)).toEqual({ exponent: 127, significand: 1 + (2 ** 23 - 1) / 2 ** 23 });
+    expect(decomposeFloat(MIN_VALUE_32)).toEqual({ exponent: -126, significand: 2 ** -23 });
+    expect(decomposeFloat(EPSILON_32)).toEqual({ exponent: -23, significand: 1 });
+    expect(decomposeFloat(1 + EPSILON_32)).toEqual({ exponent: 0, significand: 1 + 2 ** -23 });
+  });
+
+  it('should decompose a 32-bit float into its equivalent (significand, exponent)', () => {
+    fc.assert(
+      fc.property(float32raw(), (f32) => {
+        // Arrange
+        fc.pre(isFiniteNotNaN32bits(f32));
+
+        // Act
+        const { exponent, significand } = decomposeFloat(f32);
+
+        // Assert
+        expect(significand * 2 ** exponent).toBe(f32);
+      })
+    );
+  });
+});
+
+describe('floatToIndex', () => {
+  it('should properly compute indexes', () => {
+    expect(floatToIndex(0)).toBe(0);
+    expect(floatToIndex(MIN_VALUE_32)).toBe(1);
+    expect(floatToIndex(2 * MIN_VALUE_32)).toBe(2);
+    expect(floatToIndex(3 * MIN_VALUE_32)).toBe(3);
+    // EPSILON_32 === 1. * 2**-23 --> m = 1, e = -23
+    // index(EPSILON_32) = 2**24 + (-23 - (-126) -1) * 2**23
+    expect(floatToIndex(EPSILON_32)).toBe(872415232);
+    // index(1 - EPSILON_32 / 2) = index(1) - 1
+    expect(floatToIndex(1 - EPSILON_32 / 2)).toEqual(1065353215);
+    // 1 === 1. * 2**0 --> m = 1, e = 0
+    // index(1) = 2**24 + (0 - (-126) -1) * 2**23
+    expect(floatToIndex(1)).toEqual(1065353216);
+    // index(1 + EPSILON_32) = index(1) + 1
+    expect(floatToIndex(1 + EPSILON_32)).toEqual(1065353217);
+    // index(2 - EPSILON_32) = index(2) - 1 = index(1 + (2 ** 23 - 1) * EPSILON_32)
+    expect(floatToIndex(2 - EPSILON_32)).toEqual(1073741823);
+    // 1 === 1. * 2**1 --> m = 1, e = 1
+    // index(2) = index(1) + 2**23
+    expect(floatToIndex(2)).toEqual(1073741824);
+    expect(floatToIndex(MAX_VALUE_32)).toBe(2139095039);
+  });
+
+  it('should properly compute negative indexes', () => {
+    expect(floatToIndex(-0)).toEqual(-1);
+    expect(floatToIndex(-MIN_VALUE_32)).toBe(-2);
+    expect(floatToIndex(-MAX_VALUE_32)).toBe(-2139095040);
+  });
+
+  it('should properly compute indexes for infinity', () => {
+    expect(floatToIndex(Number.NEGATIVE_INFINITY)).toBe(floatToIndex(-MAX_VALUE_32) - 1);
+    expect(floatToIndex(Number.POSITIVE_INFINITY)).toBe(floatToIndex(MAX_VALUE_32) + 1);
+  });
+
+  it('should be able to infer index for negative float from the positive one', () => {
+    fc.assert(
+      fc.property(float32raw(), (f) => {
+        // Arrange
+        fc.pre(isNotNaN32bits(f));
+        const posD = f > 0 || 1 / f > 0 ? f : -f;
+
+        // Act
+        const indexPos = floatToIndex(posD);
+        const indexNeg = floatToIndex(-posD);
+
+        // Assert
+        expect(indexNeg).toEqual(-indexPos - 1);
+      })
+    );
+  });
+
+  it('should return index +1 for the successor of a given float', () => {
+    fc.assert(
+      fc.property(
+        fc.integer({ min: -126, max: +127 }),
+        fc.integer({ min: 0, max: 2 ** 24 - 1 }),
+        (exponent, rescaledSignificand) => {
+          // Arrange
+          fc.pre(exponent === -126 || rescaledSignificand >= 2 ** 23); // valid
+          fc.pre(exponent !== 127 || rescaledSignificand !== 2 ** 24 - 1); // not max
+          const current = rescaledSignificand * EPSILON_32 * 2 ** exponent;
+          const next = (rescaledSignificand + 1) * EPSILON_32 * 2 ** exponent;
+
+          // Act / Assert
+          expect(floatToIndex(next)).toEqual(floatToIndex(current) + 1);
+        }
+      )
+    );
+  });
+
+  it('should preserve ordering between two floats', () => {
+    fc.assert(
+      fc.property(float32raw(), float32raw(), (fa32, fb32) => {
+        // Arrange
+        fc.pre(isNotNaN32bits(fa32) && isNotNaN32bits(fb32));
+
+        // Act / Assert
+        if (isStrictlySmaller(fa32, fb32)) expect(floatToIndex(fa32)).toBeLessThan(floatToIndex(fb32));
+        else expect(floatToIndex(fa32)).toBeGreaterThanOrEqual(floatToIndex(fb32));
+      })
+    );
+  });
+});
+
+describe('indexToFloat', () => {
+  it('should properly find floats corresponding to well-known values', () => {
+    expect(indexToFloat(-2139095041)).toBe(Number.NEGATIVE_INFINITY);
+    expect(indexToFloat(-2139095040)).toBe(-MAX_VALUE_32);
+    expect(indexToFloat(-1)).toBe(-0);
+    expect(indexToFloat(0)).toBe(0);
+    expect(indexToFloat(872415232)).toBe(EPSILON_32);
+    expect(indexToFloat(2139095039)).toBe(MAX_VALUE_32);
+    expect(indexToFloat(2139095040)).toBe(Number.POSITIVE_INFINITY);
+  });
+
+  it('should only produce 32-bit floating point numbers (excluding NaN)', () => {
+    fc.assert(
+      fc.property(fc.integer(-2139095041, 2139095040), (index) => {
+        // Arrange / Act
+        const f = indexToFloat(index);
+
+        // Assert
+        expect(f).toBe(new Float32Array([f])[0]);
+      })
+    );
+  });
+
+  it('should reverse floatToIndex', () => {
+    fc.assert(
+      fc.property(float32raw(), (f32) => {
+        // Arrange
+        fc.pre(isNotNaN32bits(f32));
+
+        // Act / Assert
+        expect(indexToFloat(floatToIndex(f32))).toBe(f32);
+      })
+    );
+  });
+
+  it('should be reversed by floatToIndex', () => {
+    fc.assert(
+      fc.property(fc.integer(-2139095041, 2139095040), (index) => {
+        // The test below checks that indexToFloat(floatToIndex) is identity
+        // It does not confirm that floatToIndex(indexToFloat)) is identity
+        expect(floatToIndex(indexToFloat(index))).toBe(index);
+      })
+    );
+  });
+});
diff --git a/test/unit/arbitrary/_internals/mappers/ArrayToMap.spec.ts b/test/unit/arbitrary/_internals/mappers/ArrayToMap.spec.ts
new file mode 100644
index 00000000..72b233ed
--- /dev/null
+++ b/test/unit/arbitrary/_internals/mappers/ArrayToMap.spec.ts
@@ -0,0 +1,47 @@
+import { arrayToMapUnmapper } from '../../../../../src/arbitrary/_internals/mappers/ArrayToMap';
+
+describe('arrayToMapUnmapper', () => {
+  it('should be able to unmap a Map with primitives', () => {
+    // Arrange
+    const expectedArray: [unknown, unknown][] = [
+      ['a', 'A'],
+      ['b', 'B'],
+    ];
+    const source = new Map(expectedArray);
+
+    // Act
+    const out = arrayToMapUnmapper(source);
+
+    // Assert
+    expect(out).toEqual(expectedArray);
+  });
+
+  it('should be able to unmap a Map with object as keys and values', () => {
+    // Arrange
+    const k1 = {};
+    const v1 = {};
+    const k2 = {};
+    const v2 = {};
+    const source = new Map([
+      [k1, v1],
+      [k2, v2],
+    ]);
+
+    // Act
+    const out = arrayToMapUnmapper(source);
+
+    // Assert
+    expect(out[0][0]).toBe(k1);
+    expect(out[0][1]).toBe(v1);
+    expect(out[1][0]).toBe(k2);
+    expect(out[1][1]).toBe(v2);
+  });
+
+  it('should reject unmap of raw object', () => {
+    // Arrange
+    const source = {};
+
+    // Act / Assert
+    expect(() => arrayToMapUnmapper(source)).toThrowError();
+  });
+});
diff --git a/test/unit/arbitrary/_internals/mappers/ArrayToSet.spec.ts b/test/unit/arbitrary/_internals/mappers/ArrayToSet.spec.ts
new file mode 100644
index 00000000..ba752599
--- /dev/null
+++ b/test/unit/arbitrary/_internals/mappers/ArrayToSet.spec.ts
@@ -0,0 +1,37 @@
+import { arrayToSetUnmapper } from '../../../../../src/arbitrary/_internals/mappers/ArrayToSet';
+
+describe('arrayToSetUnmapper', () => {
+  it('should be able to unmap a Set with primitives', () => {
+    // Arrange
+    const expectedArray: unknown[] = ['a', 'b'];
+    const source = new Set(expectedArray);
+
+    // Act
+    const out = arrayToSetUnmapper(source);
+
+    // Assert
+    expect(out).toEqual(expectedArray);
+  });
+
+  it('should be able to unmap a Set with object as keys and values', () => {
+    // Arrange
+    const v1 = {};
+    const v2 = {};
+    const source = new Set([v1, v2]);
+
+    // Act
+    const out = arrayToSetUnmapper(source);
+
+    // Assert
+    expect(out[0]).toBe(v1);
+    expect(out[1]).toBe(v2);
+  });
+
+  it('should reject unmap of raw object', () => {
+    // Arrange
+    const source = {};
+
+    // Act / Assert
+    expect(() => arrayToSetUnmapper(source)).toThrowError();
+  });
+});
diff --git a/test/unit/arbitrary/_internals/mappers/ObjectToPrototypeLess.spec.ts b/test/unit/arbitrary/_internals/mappers/ObjectToPrototypeLess.spec.ts
new file mode 100644
index 00000000..f46a2867
--- /dev/null
+++ b/test/unit/arbitrary/_internals/mappers/ObjectToPrototypeLess.spec.ts
@@ -0,0 +1,69 @@
+import {
+  objectToPrototypeLessMapper,
+  objectToPrototypeLessUnmapper,
+} from '../../../../../src/arbitrary/_internals/mappers/ObjectToPrototypeLess';
+
+describe('objectToPrototypeLessMapper', () => {
+  it('should be able to map empty instance with Object prototype to empty without any prototype', () => {
+    // Arrange
+    const source = {};
+    expect(source).toBeInstanceOf(Object);
+
+    // Act
+    const out = objectToPrototypeLessMapper(source);
+
+    // Assert
+    expect(out).toEqual(Object.create(null));
+    expect(out).not.toBeInstanceOf(Object);
+  });
+
+  it('should be able to map non-empty instance with Object prototype to non-empty without any prototype', () => {
+    // Arrange
+    const source = { a: 1, b: 2 };
+    expect(source).toBeInstanceOf(Object);
+
+    // Act
+    const out = objectToPrototypeLessMapper(source);
+
+    // Assert
+    expect(out).toEqual(Object.assign(Object.create(null), { a: 1, b: 2 }));
+    expect(out).not.toBeInstanceOf(Object);
+  });
+});
+
+describe('objectToPrototypeLessUnmapper', () => {
+  it('should be able to unmap empty instance without any prototype', () => {
+    // Arrange
+    const source = Object.create(null);
+    expect(source).not.toBeInstanceOf(Object);
+
+    // Act
+    const out = objectToPrototypeLessUnmapper(source);
+
+    // Assert
+    expect(out).toEqual({});
+    expect(out).toBeInstanceOf(Object);
+  });
+
+  it('should be able to unmap non-empty instance without any prototype', () => {
+    // Arrange
+    const source = Object.assign(Object.create(null), { a: 1, b: 2 });
+    expect(source).not.toBeInstanceOf(Object);
+
+    // Act
+    const out = objectToPrototypeLessUnmapper(source);
+
+    // Assert
+    expect(out).toEqual({ a: 1, b: 2 });
+    expect(out).toBeInstanceOf(Object);
+  });
+
+  it('should reject unmap of raw object', () => {
+    // Arrange
+    const source = {};
+    expect(source).toBeInstanceOf(Object);
+
+    // Act / Assert
+    expect(() => objectToPrototypeLessUnmapper(source)).toThrowError();
+  });
+});
diff --git a/test/unit/arbitrary/_internals/mappers/UnboxedToBoxed.spec.ts b/test/unit/arbitrary/_internals/mappers/UnboxedToBoxed.spec.ts
new file mode 100644
index 00000000..2c902dc0
--- /dev/null
+++ b/test/unit/arbitrary/_internals/mappers/UnboxedToBoxed.spec.ts
@@ -0,0 +1,44 @@
+import fc from '../../../../../lib/fast-check';
+import {
+  unboxedToBoxedMapper,
+  unboxedToBoxedUnmapper,
+} from '../../../../../src/arbitrary/_internals/mappers/UnboxedToBoxed';
+
+describe('charsToStringUnmapper', () => {
+  it.each`
+    source                | expected
+    ${new Boolean(false)} | ${false}
+    ${new Boolean(true)}  | ${true}
+    ${new Number(0)}      | ${0}
+    ${new Number(10)}     | ${10}
+    ${new String('')}     | ${''}
+    ${new String('a')}    | ${'a'}
+  `('should be able to unbox boxed values like $expected', ({ source, expected }) => {
+    // Arrange / Act / Assert
+    expect(unboxedToBoxedUnmapper(source)).toEqual(expected);
+  });
+
+  it.each`
+    source
+    ${true}
+    ${10}
+    ${'a'}
+    ${[]}
+    ${{}}
+    ${Object.create(null)}
+  `('should keep already unboxed values as-is like $source', ({ source }) => {
+    // Arrange / Act / Assert
+    expect(unboxedToBoxedUnmapper(source)).toEqual(source);
+  });
+
+  it('should be able to box and unbox any non-boxed value', () =>
+    fc.assert(
+      fc.property(fc.array(fc.anything({ withBoxedValues: false })), (data) => {
+        // Arrange
+        const source = unboxedToBoxedMapper(data);
+
+        // Act / Assert
+        expect(unboxedToBoxedUnmapper(source)).toBe(data);
+      })
+    ));
+});
diff --git a/test/unit/arbitrary/_next/doubleNext.spec.ts b/test/unit/arbitrary/_next/doubleNext.spec.ts
new file mode 100644
index 00000000..bcd8104c
--- /dev/null
+++ b/test/unit/arbitrary/_next/doubleNext.spec.ts
@@ -0,0 +1,329 @@
+import * as fc from '../../../../lib/fast-check';
+
+import { doubleNext, DoubleNextConstraints } from '../../../../src/arbitrary/_next/doubleNext';
+import {
+  add64,
+  ArrayInt64,
+  isEqual64,
+  substract64,
+  Unit64,
+} from '../../../../src/arbitrary/_internals/helpers/ArrayInt64';
+import {
+  defaultDoubleRecordConstraints,
+  doubleNextConstraints,
+  float64raw,
+  isStrictlySmaller,
+} from '../../check/arbitrary/generic/FloatingPointHelpers';
+import { doubleToIndex, indexToDouble } from '../../../../src/arbitrary/_internals/helpers/DoubleHelpers';
+
+import { fakeNextArbitrary, fakeNextArbitraryStaticValue } from '../../check/arbitrary/generic/NextArbitraryHelpers';
+import { convertFromNextWithShrunkOnce, convertToNext } from '../../../../src/check/arbitrary/definition/Converters';
+import { fakeRandom } from '../../check/arbitrary/generic/RandomHelpers';
+
+import {
+  assertProduceCorrectValues,
+  assertShrinkProducesStrictlySmallerValue,
+  assertProduceSameValueGivenSameSeed,
+} from '../../check/arbitrary/generic/NextArbitraryAssertions';
+
+import * as ArrayInt64ArbitraryMock from '../../../../src/arbitrary/_internals/ArrayInt64Arbitrary';
+
+function beforeEachHook() {
+  jest.resetModules();
+  jest.restoreAllMocks();
+}
+beforeEach(beforeEachHook);
+fc.configureGlobal({
+  ...fc.readConfigureGlobal(),
+  beforeEach: beforeEachHook,
+});
+
+describe('doubleNext', () => {
+  it('should accept any valid range of floating point numbers (including infinity)', () => {
+    fc.assert(
+      fc.property(doubleNextConstraints(), (ct) => {
+        // Arrange
+        spyArrayInt64();
+
+        // Act
+        const arb = doubleNext(ct);
+
+        // Assert
+        expect(arb).toBeDefined();
+      })
+    );
+  });
+
+  it('should accept any constraits defining min (not-NaN) equal to max', () => {
+    fc.assert(
+      fc.property(
+        float64raw(),
+        fc.record({ noDefaultInfinity: fc.boolean(), noNaN: fc.boolean() }, { withDeletedKeys: true }),
+        (f, otherCt) => {
+          // Arrange
+          fc.pre(!Number.isNaN(f));
+          spyArrayInt64();
+
+          // Act
+          const arb = doubleNext({ ...otherCt, min: f, max: f });
+
+          // Assert
+          expect(arb).toBeDefined();
+        }
+      )
+    );
+  });
+
+  it('should reject NaN if specified for min', () => {
+    // Arrange
+    const arrayInt64 = spyArrayInt64();
+
+    // Act / Assert
+    expect(() => doubleNext({ min: Number.NaN })).toThrowError();
+    expect(arrayInt64).not.toHaveBeenCalled();
+  });
+
+  it('should reject NaN if specified for max', () => {
+    // Arrange
+    const arrayInt64 = spyArrayInt64();
+
+    // Act / Assert
+    expect(() => doubleNext({ max: Number.NaN })).toThrowError();
+    expect(arrayInt64).not.toHaveBeenCalled();
+  });
+
+  it('should reject if specified min is strictly greater than max', () => {
+    fc.assert(
+      fc.property(float64raw(), float64raw(), (da, db) => {
+        // Arrange
+        fc.pre(!Number.isNaN(da));
+        fc.pre(!Number.isNaN(db));
+        fc.pre(!Object.is(da, db)); // Object.is can distinguish -0 from 0, while !== cannot
+        const arrayInt64 = spyArrayInt64();
+        const min = isStrictlySmaller(da, db) ? db : da;
+        const max = isStrictlySmaller(da, db) ? da : db;
+
+        // Act / Assert
+        expect(() => doubleNext({ min, max })).toThrowError();
+        expect(arrayInt64).not.toHaveBeenCalled();
+      })
+    );
+  });
+
+  it('should reject impossible noDefaultInfinity-based ranges', () => {
+    // Arrange
+    const arrayInt64 = spyArrayInt64();
+
+    // Act / Assert
+    expect(() => doubleNext({ min: Number.POSITIVE_INFINITY, noDefaultInfinity: true })).toThrowError();
+    expect(() => doubleNext({ max: Number.NEGATIVE_INFINITY, noDefaultInfinity: true })).toThrowError();
+    expect(arrayInt64).not.toHaveBeenCalled();
+  });
+
+  if (typeof BigInt !== 'undefined') {
+    it('should properly convert integer value for index between min and max into its associated float value', () => {
+      fc.assert(
+        fc.property(fc.option(doubleNextConstraints(), { nil: undefined }), fc.bigUintN(64), (ct, mod) => {
+          // Arrange
+          const { instance: mrng } = fakeRandom();
+          const { min, max } = minMaxForConstraints(ct || {});
+          const minIndex = doubleToIndex(min);
+          const maxIndex = doubleToIndex(max);
+          const arbitraryGeneratedIndex = toIndex(
+            (mod % (toBigInt(maxIndex) - toBigInt(minIndex) + BigInt(1))) + toBigInt(minIndex)
+          );
+          spyArrayInt64WithValue(() => arbitraryGeneratedIndex);
+
+          // Act
+          const arb = doubleNext(ct);
+          const { value_: f } = arb.generate(mrng);
+
+          // Assert
+          expect(f).toBe(indexToDouble(arbitraryGeneratedIndex));
+        })
+      );
+    });
+  }
+
+  describe('with NaN', () => {
+    const withNaNRecordConstraints = { ...defaultDoubleRecordConstraints, noNaN: fc.constant(false) };
+
+    it('should ask for a range with one extra value (far from zero)', () => {
+      fc.assert(
+        fc.property(doubleNextConstraints(withNaNRecordConstraints), (ct) => {
+          // Arrange
+          const { max } = minMaxForConstraints(ct);
+          const arrayInt64 = spyArrayInt64();
+
+          // Act
+          doubleNext({ ...ct, noNaN: true });
+          doubleNext(ct);
+
+          // Assert
+          expect(arrayInt64).toHaveBeenCalledTimes(2);
+          const constraintsNoNaN = arrayInt64.mock.calls[0];
+          const constraintsWithNaN = arrayInt64.mock.calls[1];
+          if (max > 0) {
+            // max > 0  --> NaN will be added as the greatest value
+            expect(constraintsWithNaN[0]).toEqual(constraintsNoNaN[0]);
+            expect(constraintsWithNaN[1]).toEqual(add64(constraintsNoNaN[1], Unit64));
+          } else {
+            // max <= 0 --> NaN will be added as the smallest value
+            expect(constraintsWithNaN[0]).toEqual(substract64(constraintsNoNaN[0], Unit64));
+            expect(constraintsWithNaN[1]).toEqual(constraintsNoNaN[1]);
+          }
+        })
+      );
+    });
+
+    it('should properly convert the extra value to NaN', () => {
+      fc.assert(
+        fc.property(doubleNextConstraints(withNaNRecordConstraints), (ct) => {
+          // Arrange
+          // Setup mocks for integer
+          const { instance: mrng } = fakeRandom();
+          const arbitraryGenerated = { value: { sign: 1, data: [Number.NaN, Number.NaN] } as ArrayInt64 };
+          const arrayInt64 = spyArrayInt64WithValue(() => arbitraryGenerated.value);
+          // Call float next to find out the value required for NaN
+          doubleNext({ ...ct, noNaN: true });
+          const arb = doubleNext(ct);
+          // Extract NaN "index"
+          const [minNonNaN] = arrayInt64.mock.calls[0];
+          const [minNaN, maxNaN] = arrayInt64.mock.calls[1];
+          const indexForNaN = !isEqual64(minNonNaN, minNaN) ? minNaN : maxNaN;
+          if (indexForNaN === undefined) throw new Error('No value available for NaN');
+          arbitraryGenerated.value = indexForNaN;
+
+          // Act
+          const { value_: f } = arb.generate(mrng);
+
+          // Assert
+          expect(f).toBe(Number.NaN);
+        })
+      );
+    });
+  });
+
+  describe('without NaN', () => {
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    const { noNaN, ...noNaNRecordConstraints } = defaultDoubleRecordConstraints;
+
+    it('should ask integers between the indexes corresponding to min and max', () => {
+      fc.assert(
+        fc.property(doubleNextConstraints(noNaNRecordConstraints), (ctDraft) => {
+          // Arrange
+          const ct = { ...ctDraft, noNaN: true };
+          const arrayInt64 = spyArrayInt64();
+          const { min, max } = minMaxForConstraints(ct);
+          const minIndex = doubleToIndex(min);
+          const maxIndex = doubleToIndex(max);
+
+          // Act
+          doubleNext(ct);
+
+          // Assert
+          expect(arrayInt64).toHaveBeenCalledTimes(1);
+          expect(arrayInt64).toHaveBeenCalledWith(minIndex, maxIndex);
+        })
+      );
+    });
+  });
+});
+
+describe('doubleNext (integration)', () => {
+  type Extra = DoubleNextConstraints | undefined;
+  const extraParameters: fc.Arbitrary<Extra> = fc.option(doubleNextConstraints(), { nil: undefined });
+
+  const isCorrect = (v: number, extra: Extra) => {
+    expect(typeof v).toBe('number'); // should always produce numbers
+
+    if (extra === undefined) {
+      return; // no other constraints
+    }
+    if (extra.noNaN) {
+      expect(v).not.toBe(Number.NaN); // should not produce NaN if explicitely asked not too
+    }
+    if (extra.min !== undefined && !Number.isNaN(v)) {
+      expect(v).toBeGreaterThanOrEqual(extra.min); // should always be greater than min when specified
+    }
+    if (extra.max !== undefined && !Number.isNaN(v)) {
+      expect(v).toBeLessThanOrEqual(extra.max); // should always be smaller than max when specified
+    }
+    if (extra.noDefaultInfinity) {
+      if (extra.min === undefined) {
+        expect(v).not.toBe(Number.NEGATIVE_INFINITY); // should not produce -infinity when noInfinity and min unset
+      }
+      if (extra.max === undefined) {
+        expect(v).not.toBe(Number.POSITIVE_INFINITY); // should not produce +infinity when noInfinity and max unset
+      }
+    }
+  };
+
+  const isStrictlySmaller = (fa: number, fb: number) =>
+    Math.abs(fa) < Math.abs(fb) || //              Case 1: abs(a) < abs(b)
+    (Object.is(fa, +0) && Object.is(fb, -0)) || // Case 2: +0 < -0  --> we shrink from -0 to +0
+    (!Number.isNaN(fa) && Number.isNaN(fb)); //    Case 3: notNaN < NaN, NaN is one of the extreme values
+
+  const doubleNextBuilder = (extra: Extra) => convertToNext(doubleNext(extra));
+
+  it('should produce the same values given the same seed', () => {
+    assertProduceSameValueGivenSameSeed(doubleNextBuilder, { extraParameters });
+  });
+
+  it('should only produce correct values', () => {
+    assertProduceCorrectValues(doubleNextBuilder, isCorrect, { extraParameters });
+  });
+
+  // Not Implemented Yet!
+  //it('should produce values seen as shrinkable without any context', () => {
+  //  assertProduceValuesShrinkableWithoutContext(doubleNextBuilder, { extraParameters });
+  //});
+
+  // Not Implemented Yet!
+  //it('should be able to shrink to the same values without initial context', () => {
+  //  assertShrinkProducesSameValueWithoutInitialContext(doubleNextBuilder, { extraParameters });
+  //});
+
+  it('should preserve strictly smaller ordering in shrink', () => {
+    assertShrinkProducesStrictlySmallerValue(doubleNextBuilder, isStrictlySmaller, { extraParameters });
+  });
+});
+
+// Helpers
+
+type Index = ReturnType<typeof doubleToIndex>;
+
+function toIndex(raw: bigint | string): Index {
+  const b = typeof raw === 'string' ? BigInt(raw) : raw;
+  const pb = b < BigInt(0) ? -b : b;
+  return { sign: b < BigInt(0) ? -1 : 1, data: [Number(pb >> BigInt(32)), Number(pb & BigInt(0xffffffff))] };
+}
+
+function toBigInt(index: Index): bigint {
+  return BigInt(index.sign) * ((BigInt(index.data[0]) << BigInt(32)) + BigInt(index.data[1]));
+}
+
+function minMaxForConstraints(ct: DoubleNextConstraints) {
+  const noDefaultInfinity = ct.noDefaultInfinity;
+  const {
+    min = noDefaultInfinity ? -Number.MAX_VALUE : Number.NEGATIVE_INFINITY,
+    max = noDefaultInfinity ? Number.MAX_VALUE : Number.POSITIVE_INFINITY,
+  } = ct;
+  return { min, max };
+}
+
+function spyArrayInt64() {
+  const { instance, map } = fakeNextArbitrary<ArrayInt64>();
+  const { instance: mappedInstance } = fakeNextArbitrary();
+  const arrayInt64 = jest.spyOn(ArrayInt64ArbitraryMock, 'arrayInt64');
+  arrayInt64.mockImplementation(() => convertFromNextWithShrunkOnce(instance, undefined));
+  map.mockReturnValue(mappedInstance);
+  return arrayInt64;
+}
+
+function spyArrayInt64WithValue(value: () => ArrayInt64) {
+  const { instance } = fakeNextArbitraryStaticValue<ArrayInt64>(value);
+  const integer = jest.spyOn(ArrayInt64ArbitraryMock, 'arrayInt64');
+  integer.mockImplementation(() => convertFromNextWithShrunkOnce(instance, undefined));
+  return integer;
+}
diff --git a/test/unit/arbitrary/_next/floatNext.spec.ts b/test/unit/arbitrary/_next/floatNext.spec.ts
new file mode 100644
index 00000000..1e2e3a47
--- /dev/null
+++ b/test/unit/arbitrary/_next/floatNext.spec.ts
@@ -0,0 +1,318 @@
+import * as fc from '../../../../lib/fast-check';
+
+import { floatNext, FloatNextConstraints } from '../../../../src/arbitrary/_next/floatNext';
+import {
+  floatNextConstraints,
+  float32raw,
+  isNotNaN32bits,
+  float64raw,
+  isStrictlySmaller,
+  defaultFloatRecordConstraints,
+  is32bits,
+} from '../../check/arbitrary/generic/FloatingPointHelpers';
+import { floatToIndex, indexToFloat, MAX_VALUE_32 } from '../../../../src/arbitrary/_internals/helpers/FloatHelpers';
+import { convertFromNextWithShrunkOnce, convertToNext } from '../../../../src/check/arbitrary/definition/Converters';
+
+import { fakeNextArbitrary, fakeNextArbitraryStaticValue } from '../../check/arbitrary/generic/NextArbitraryHelpers';
+import { fakeRandom } from '../../check/arbitrary/generic/RandomHelpers';
+
+import {
+  assertProduceCorrectValues,
+  assertShrinkProducesStrictlySmallerValue,
+  assertProduceSameValueGivenSameSeed,
+} from '../../check/arbitrary/generic/NextArbitraryAssertions';
+
+import * as IntegerMock from '../../../../src/arbitrary/integer';
+
+function beforeEachHook() {
+  jest.resetModules();
+  jest.restoreAllMocks();
+}
+beforeEach(beforeEachHook);
+fc.configureGlobal({
+  ...fc.readConfigureGlobal(),
+  beforeEach: beforeEachHook,
+});
+
+function minMaxForConstraints(ct: FloatNextConstraints) {
+  const noDefaultInfinity = ct.noDefaultInfinity;
+  const {
+    min = noDefaultInfinity ? -MAX_VALUE_32 : Number.NEGATIVE_INFINITY,
+    max = noDefaultInfinity ? MAX_VALUE_32 : Number.POSITIVE_INFINITY,
+  } = ct;
+  return { min, max };
+}
+
+describe('floatNext', () => {
+  it('should accept any valid range of 32-bit floating point numbers (including infinity)', () => {
+    fc.assert(
+      fc.property(floatNextConstraints(), (ct) => {
+        // Arrange
+        spyInteger();
+
+        // Act
+        const arb = floatNext(ct);
+
+        // Assert
+        expect(arb).toBeDefined();
+      })
+    );
+  });
+
+  it('should accept any constraits defining min (32-bit float not-NaN) equal to max', () => {
+    fc.assert(
+      fc.property(
+        float32raw(),
+        fc.record({ noDefaultInfinity: fc.boolean(), noNaN: fc.boolean() }, { withDeletedKeys: true }),
+        (f, otherCt) => {
+          // Arrange
+          fc.pre(isNotNaN32bits(f));
+          spyInteger();
+
+          // Act
+          const arb = floatNext({ ...otherCt, min: f, max: f });
+
+          // Assert
+          expect(arb).toBeDefined();
+        }
+      )
+    );
+  });
+
+  it('should reject non-32-bit or NaN floating point numbers if specified for min', () => {
+    fc.assert(
+      fc.property(float64raw(), (f64) => {
+        // Arrange
+        fc.pre(!isNotNaN32bits(f64));
+        const integer = spyInteger();
+
+        // Act / Assert
+        expect(() => floatNext({ min: f64 })).toThrowError();
+        expect(integer).not.toHaveBeenCalled();
+      })
+    );
+  });
+
+  it('should reject non-32-bit or NaN floating point numbers if specified for max', () => {
+    fc.assert(
+      fc.property(float64raw(), (f64) => {
+        // Arrange
+        fc.pre(!isNotNaN32bits(f64));
+        const integer = spyInteger();
+
+        // Act / Assert
+        expect(() => floatNext({ max: f64 })).toThrowError();
+        expect(integer).not.toHaveBeenCalled();
+      })
+    );
+  });
+
+  it('should reject if specified min is strictly greater than max', () => {
+    fc.assert(
+      fc.property(float32raw(), float32raw(), (fa32, fb32) => {
+        // Arrange
+        fc.pre(isNotNaN32bits(fa32));
+        fc.pre(isNotNaN32bits(fb32));
+        fc.pre(!Object.is(fa32, fb32)); // Object.is can distinguish -0 from 0, while !== cannot
+        const integer = spyInteger();
+        const min = isStrictlySmaller(fa32, fb32) ? fb32 : fa32;
+        const max = isStrictlySmaller(fa32, fb32) ? fa32 : fb32;
+
+        // Act / Assert
+        expect(() => floatNext({ min, max })).toThrowError();
+        expect(integer).not.toHaveBeenCalled();
+      })
+    );
+  });
+
+  it('should reject impossible noDefaultInfinity-based ranges', () => {
+    // Arrange
+    const integer = spyInteger();
+
+    // Act / Assert
+    expect(() => floatNext({ min: Number.POSITIVE_INFINITY, noDefaultInfinity: true })).toThrowError();
+    expect(() => floatNext({ max: Number.NEGATIVE_INFINITY, noDefaultInfinity: true })).toThrowError();
+    expect(integer).not.toHaveBeenCalled();
+  });
+
+  it('should properly convert integer value for index between min and max into its associated float value', () =>
+    fc.assert(
+      fc.property(fc.option(floatNextConstraints(), { nil: undefined }), fc.maxSafeNat(), (ct, mod) => {
+        // Arrange
+        const { instance: mrng } = fakeRandom();
+        const { min, max } = minMaxForConstraints(ct || {});
+        const minIndex = floatToIndex(min);
+        const maxIndex = floatToIndex(max);
+        const arbitraryGeneratedIndex = (mod % (maxIndex - minIndex + 1)) + minIndex;
+        spyIntegerWithValue(() => arbitraryGeneratedIndex);
+
+        // Act
+        const arb = floatNext(ct);
+        const { value_: f } = arb.generate(mrng);
+
+        // Assert
+        expect(f).toBe(indexToFloat(arbitraryGeneratedIndex));
+      })
+    ));
+
+  describe('with NaN', () => {
+    const withNaNRecordConstraints = { ...defaultFloatRecordConstraints, noNaN: fc.constant(false) };
+
+    it('should ask for a range with one extra value (far from zero)', () => {
+      fc.assert(
+        fc.property(floatNextConstraints(withNaNRecordConstraints), (ct) => {
+          // Arrange
+          const { max } = minMaxForConstraints(ct);
+          const integer = spyInteger();
+
+          // Act
+          floatNext({ ...ct, noNaN: true });
+          floatNext(ct);
+
+          // Assert
+          expect(integer).toHaveBeenCalledTimes(2);
+          const integerConstraintsNoNaN = integer.mock.calls[0][0];
+          const integerConstraintsWithNaN = integer.mock.calls[1][0];
+          if (max > 0) {
+            // max > 0  --> NaN will be added as the greatest value
+            expect(integerConstraintsWithNaN.min).toBe(integerConstraintsNoNaN.min);
+            expect(integerConstraintsWithNaN.max).toBe(integerConstraintsNoNaN.max! + 1);
+          } else {
+            // max <= 0 --> NaN will be added as the smallest value
+            expect(integerConstraintsWithNaN.min).toBe(integerConstraintsNoNaN.min! - 1);
+            expect(integerConstraintsWithNaN.max).toBe(integerConstraintsNoNaN.max);
+          }
+        })
+      );
+    });
+
+    it('should properly convert the extra value to NaN', () =>
+      fc.assert(
+        fc.property(floatNextConstraints(withNaNRecordConstraints), (ct) => {
+          // Arrange
+          // Setup mocks for integer
+          const { instance: mrng } = fakeRandom();
+          const arbitraryGenerated = { value: Number.NaN };
+          const integer = spyIntegerWithValue(() => arbitraryGenerated.value);
+          // Call float next to find out the value required for NaN
+          floatNext({ ...ct, noNaN: true });
+          const arb = floatNext(ct);
+          // Extract NaN "index"
+          const { min: minNonNaN } = integer.mock.calls[0][0];
+          const { min: minNaN, max: maxNaN } = integer.mock.calls[1][0];
+          const indexForNaN = minNonNaN !== minNaN ? minNaN : maxNaN;
+          if (indexForNaN === undefined) throw new Error('No value available for NaN');
+          arbitraryGenerated.value = indexForNaN;
+
+          // Act
+          const { value_: f } = arb.generate(mrng);
+
+          // Assert
+          expect(f).toBe(Number.NaN);
+        })
+      ));
+  });
+
+  describe('without NaN', () => {
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    const { noNaN, ...noNaNRecordConstraints } = defaultFloatRecordConstraints;
+
+    it('should ask integers between the indexes corresponding to min and max', () => {
+      fc.assert(
+        fc.property(floatNextConstraints(noNaNRecordConstraints), (ctDraft) => {
+          // Arrange
+          const ct = { ...ctDraft, noNaN: true };
+          const integer = spyInteger();
+          const { min, max } = minMaxForConstraints(ct);
+          const minIndex = floatToIndex(min);
+          const maxIndex = floatToIndex(max);
+
+          // Act
+          floatNext(ct);
+
+          // Assert
+          expect(integer).toHaveBeenCalledTimes(1);
+          expect(integer).toHaveBeenCalledWith({ min: minIndex, max: maxIndex });
+        })
+      );
+    });
+  });
+});
+
+describe('floatNext (integration)', () => {
+  type Extra = FloatNextConstraints | undefined;
+  const extraParameters: fc.Arbitrary<Extra> = fc.option(floatNextConstraints(), { nil: undefined });
+
+  const isCorrect = (v: number, extra: Extra) => {
+    expect(typeof v).toBe('number'); // should always produce numbers
+    expect(is32bits(v)).toBe(true); // should always produce 32-bit floats
+
+    if (extra === undefined) {
+      return; // no other constraints
+    }
+    if (extra.noNaN) {
+      expect(v).not.toBe(Number.NaN); // should not produce NaN if explicitely asked not too
+    }
+    if (extra.min !== undefined && !Number.isNaN(v)) {
+      expect(v).toBeGreaterThanOrEqual(extra.min); // should always be greater than min when specified
+    }
+    if (extra.max !== undefined && !Number.isNaN(v)) {
+      expect(v).toBeLessThanOrEqual(extra.max); // should always be smaller than max when specified
+    }
+    if (extra.noDefaultInfinity) {
+      if (extra.min === undefined) {
+        expect(v).not.toBe(Number.NEGATIVE_INFINITY); // should not produce -infinity when noInfinity and min unset
+      }
+      if (extra.max === undefined) {
+        expect(v).not.toBe(Number.POSITIVE_INFINITY); // should not produce +infinity when noInfinity and max unset
+      }
+    }
+  };
+
+  const isStrictlySmaller = (fa: number, fb: number) =>
+    Math.abs(fa) < Math.abs(fb) || //              Case 1: abs(a) < abs(b)
+    (Object.is(fa, +0) && Object.is(fb, -0)) || // Case 2: +0 < -0  --> we shrink from -0 to +0
+    (!Number.isNaN(fa) && Number.isNaN(fb)); //    Case 3: notNaN < NaN, NaN is one of the extreme values
+
+  const floatNextBuilder = (extra: Extra) => convertToNext(floatNext(extra));
+
+  it('should produce the same values given the same seed', () => {
+    assertProduceSameValueGivenSameSeed(floatNextBuilder, { extraParameters });
+  });
+
+  it('should only produce correct values', () => {
+    assertProduceCorrectValues(floatNextBuilder, isCorrect, { extraParameters });
+  });
+
+  // Not Implemented Yet!
+  //it('should produce values seen as shrinkable without any context', () => {
+  //  assertProduceValuesShrinkableWithoutContext(floatNextBuilder, { extraParameters });
+  //});
+
+  // Not Implemented Yet!
+  //it('should be able to shrink to the same values without initial context', () => {
+  //  assertShrinkProducesSameValueWithoutInitialContext(floatNextBuilder, { extraParameters });
+  //});
+
+  it('should preserve strictly smaller ordering in shrink', () => {
+    assertShrinkProducesStrictlySmallerValue(floatNextBuilder, isStrictlySmaller, { extraParameters });
+  });
+});
+
+// Helpers
+
+function spyInteger() {
+  const { instance, map } = fakeNextArbitrary<number>();
+  const { instance: mappedInstance } = fakeNextArbitrary();
+  const integer = jest.spyOn(IntegerMock, 'integer');
+  integer.mockImplementation(() => convertFromNextWithShrunkOnce(instance, undefined));
+  map.mockReturnValue(mappedInstance);
+  return integer;
+}
+
+function spyIntegerWithValue(value: () => number) {
+  const { instance } = fakeNextArbitraryStaticValue<number>(value);
+  const integer = jest.spyOn(IntegerMock, 'integer');
+  integer.mockImplementation(() => convertFromNextWithShrunkOnce(instance, undefined));
+  return integer;
+}
diff --git a/test/unit/check/arbitrary/DoubleNextArbitrary.itest.spec.ts b/test/unit/check/arbitrary/DoubleNextArbitrary.itest.spec.ts
deleted file mode 100644
index 6dbafeb0..00000000
--- a/test/unit/check/arbitrary/DoubleNextArbitrary.itest.spec.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import * as fc from '../../../../lib/fast-check';
-
-import { doubleNext, DoubleNextConstraints } from '../../../../src/check/arbitrary/DoubleNextArbitrary';
-import { doubleNextConstraints } from './generic/FloatingPointHelpers';
-import * as genericHelper from './generic/GenericArbitraryHelper';
-
-describe('DoubleNextArbitrary', () => {
-  describe('doubleNext', () => {
-    describe('Is valid arbitrary?', () => {
-      genericHelper.isValidArbitrary((ct?: DoubleNextConstraints) => doubleNext(ct), {
-        isStrictlySmallerValue: (fa, fb) =>
-          Math.abs(fa) < Math.abs(fb) || //              Case 1: abs(a) < abs(b)
-          (Object.is(fa, +0) && Object.is(fb, -0)) || // Case 2: +0 < -0  --> we shrink from -0 to +0
-          (!Number.isNaN(fa) && Number.isNaN(fb)), //    Case 3: notNaN < NaN, NaN is one of the extreme values
-        isValidValue: (g: number, ct?: DoubleNextConstraints) => {
-          if (typeof g !== 'number') return false; // should always produce numbers
-          if (Number.isNaN(g)) {
-            if (ct !== undefined && ct.noNaN) return false; // should not produce NaN if explicitely asked not too
-            return true;
-          }
-          if (ct !== undefined && ct.min !== undefined && g < ct.min) return false; // should always be greater than min when specified
-          if (ct !== undefined && ct.max !== undefined && g > ct.max) return false; // should always be smaller than max when specified
-          if (ct !== undefined && ct.noDefaultInfinity) {
-            if (ct.min === undefined && g === Number.NEGATIVE_INFINITY) return false; // should not produce -infinity when noInfity and min unset
-            if (ct.max === undefined && g === Number.POSITIVE_INFINITY) return false; // should not produce +infinity when noInfity and max unset
-          }
-          return true;
-        },
-        seedGenerator: fc.option(doubleNextConstraints(), { nil: undefined }),
-      });
-    });
-  });
-});
diff --git a/test/unit/check/arbitrary/DoubleNextArbitrary.utest.spec.ts b/test/unit/check/arbitrary/DoubleNextArbitrary.utest.spec.ts
deleted file mode 100644
index 25a16be3..00000000
--- a/test/unit/check/arbitrary/DoubleNextArbitrary.utest.spec.ts
+++ /dev/null
@@ -1,388 +0,0 @@
-import * as fc from '../../../../lib/fast-check';
-
-import {
-  decomposeDouble,
-  doubleNext,
-  DoubleNextConstraints,
-  doubleToIndex,
-  indexToDouble,
-} from '../../../../src/check/arbitrary/DoubleNextArbitrary';
-
-import { mocked } from 'ts-jest/utils';
-import { arbitraryFor } from './generic/ArbitraryBuilder';
-import * as stubRng from '../../stubs/generators';
-
-import * as ArrayInt64ArbitraryMock from '../../../../src/check/arbitrary/helpers/ArrayInt64Arbitrary';
-import { add64, ArrayInt64, isEqual64, substract64, Unit64 } from '../../../../src/check/arbitrary/helpers/ArrayInt64';
-import {
-  defaultDoubleRecordConstraints,
-  doubleNextConstraints,
-  float64raw,
-  isStrictlySmaller,
-} from './generic/FloatingPointHelpers';
-jest.mock('../../../../src/check/arbitrary/helpers/ArrayInt64Arbitrary');
-
-type Index = ReturnType<typeof doubleToIndex>;
-const toIndex = (raw: bigint | string): Index => {
-  const b = typeof raw === 'string' ? BigInt(raw) : raw;
-  const pb = b < BigInt(0) ? -b : b;
-  return { sign: b < BigInt(0) ? -1 : 1, data: [Number(pb >> BigInt(32)), Number(pb & BigInt(0xffffffff))] };
-};
-const toBigInt = (index: Index): bigint => {
-  return BigInt(index.sign) * ((BigInt(index.data[0]) << BigInt(32)) + BigInt(index.data[1]));
-};
-
-const mrng = () => stubRng.mutable.nocall();
-
-function minMaxForConstraints(ct: DoubleNextConstraints) {
-  const noDefaultInfinity = ct.noDefaultInfinity;
-  const {
-    min = noDefaultInfinity ? -Number.MAX_VALUE : Number.NEGATIVE_INFINITY,
-    max = noDefaultInfinity ? Number.MAX_VALUE : Number.POSITIVE_INFINITY,
-  } = ct;
-  return { min, max };
-}
-
-function mockNoOpArrayInt64Arb(opts: { single?: boolean } = {}) {
-  // Mocking integer: not expecting any call there
-  const { arrayInt64 } = mocked(ArrayInt64ArbitraryMock);
-  if (opts.single) arrayInt64.mockImplementationOnce(() => arbitraryFor([]));
-  else arrayInt64.mockImplementation(() => arbitraryFor([]));
-  return arrayInt64;
-}
-
-beforeEach(() => {
-  jest.clearAllMocks();
-  jest.resetAllMocks();
-});
-const previousGlobal = fc.readConfigureGlobal();
-fc.configureGlobal({
-  ...previousGlobal,
-  beforeEach: () => {
-    jest.clearAllMocks();
-    jest.resetAllMocks();
-  },
-});
-
-describe('DoubleNextArbitrary', () => {
-  describe('doubleNext', () => {
-    it('Should accept any valid range of floating point numbers (including infinity)', () =>
-      fc.assert(
-        fc.property(doubleNextConstraints(), (ct) => {
-          mockNoOpArrayInt64Arb();
-          expect(doubleNext(ct)).toBeDefined();
-        })
-      ));
-    it('Should accept any constraits defining min (not-NaN) equal to max', () =>
-      fc.assert(
-        fc.property(
-          float64raw(),
-          fc.record({ noDefaultInfinity: fc.boolean(), noNaN: fc.boolean() }, { withDeletedKeys: true }),
-          (f, otherCt) => {
-            fc.pre(!Number.isNaN(f));
-            mockNoOpArrayInt64Arb();
-            expect(doubleNext({ ...otherCt, min: f, max: f })).toBeDefined();
-          }
-        )
-      ));
-    it('Should reject NaN if specified for min', () => {
-      mockNoOpArrayInt64Arb();
-      expect(() => doubleNext({ min: Number.NaN })).toThrowError();
-    });
-    it('Should reject NaN if specified for max', () => {
-      mockNoOpArrayInt64Arb();
-      expect(() => doubleNext({ max: Number.NaN })).toThrowError();
-    });
-    it('Should reject if specified min is strictly greater than max', () =>
-      fc.assert(
-        fc.property(float64raw(), float64raw(), (da, db) => {
-          fc.pre(!Number.isNaN(da));
-          fc.pre(!Number.isNaN(db));
-          fc.pre(!Object.is(da, db)); // Object.is can distinguish -0 from 0, while !== cannot
-          mockNoOpArrayInt64Arb();
-          const min = isStrictlySmaller(da, db) ? db : da;
-          const max = isStrictlySmaller(da, db) ? da : db;
-          expect(() => doubleNext({ min, max })).toThrowError();
-        })
-      ));
-    it('Should reject impossible noDefaultInfinity-based ranges', () => {
-      mockNoOpArrayInt64Arb();
-      expect(() => doubleNext({ min: Number.POSITIVE_INFINITY, noDefaultInfinity: true })).toThrowError();
-      expect(() => doubleNext({ max: Number.NEGATIVE_INFINITY, noDefaultInfinity: true })).toThrowError();
-    });
-
-    if (typeof BigInt !== 'undefined') {
-      it('Should properly convert integer value for index between min and max into its associated float value', () =>
-        fc.assert(
-          fc.property(fc.option(doubleNextConstraints(), { nil: undefined }), fc.bigUintN(64), (ct, mod) => {
-            // Arrange
-            const { arrayInt64 } = mocked(ArrayInt64ArbitraryMock);
-            const { min, max } = minMaxForConstraints(ct || {});
-            const minIndex = doubleToIndex(min);
-            const maxIndex = doubleToIndex(max);
-            const arbitraryGeneratedIndex = toIndex(
-              (mod % (toBigInt(maxIndex) - toBigInt(minIndex) + BigInt(1))) + toBigInt(minIndex)
-            );
-            arrayInt64.mockImplementationOnce(() => arbitraryFor([{ value: arbitraryGeneratedIndex }]));
-
-            // Act
-            const arb = doubleNext(ct);
-            const { value_: f } = arb.generate(mrng());
-
-            // Assert
-            expect(f).toBe(indexToDouble(arbitraryGeneratedIndex));
-          })
-        ));
-    }
-
-    describe('with NaN', () => {
-      const withNaNRecordConstraints = { ...defaultDoubleRecordConstraints, noNaN: fc.constant(false) };
-
-      it('Should ask for a range with one extra value (far from zero)', () =>
-        fc.assert(
-          fc.property(doubleNextConstraints(withNaNRecordConstraints), (ct) => {
-            // Arrange
-            const { max } = minMaxForConstraints(ct);
-            const arrayInt64 = mockNoOpArrayInt64Arb();
-
-            // Act
-            doubleNext({ ...ct, noNaN: true });
-            doubleNext(ct);
-
-            // Assert
-            expect(arrayInt64).toHaveBeenCalledTimes(2);
-            const constraintsNoNaN = arrayInt64.mock.calls[0];
-            const constraintsWithNaN = arrayInt64.mock.calls[1];
-            if (max > 0) {
-              // max > 0  --> NaN will be added as the greatest value
-              expect(constraintsWithNaN[0]).toEqual(constraintsNoNaN[0]);
-              expect(constraintsWithNaN[1]).toEqual(add64(constraintsNoNaN[1], Unit64));
-            } else {
-              // max <= 0 --> NaN will be added as the smallest value
-              expect(constraintsWithNaN[0]).toEqual(substract64(constraintsNoNaN[0], Unit64));
-              expect(constraintsWithNaN[1]).toEqual(constraintsNoNaN[1]);
-            }
-          })
-        ));
-      it('Should properly convert the extra value to NaN', () =>
-        fc.assert(
-          fc.property(doubleNextConstraints(withNaNRecordConstraints), (ct) => {
-            // Arrange
-            // Setup mocks for integer
-            const arbitraryGenerated = { value: { sign: 1, data: [Number.NaN, Number.NaN] } as ArrayInt64 };
-            const arrayInt64 = mockNoOpArrayInt64Arb({ single: true });
-            arrayInt64.mockImplementationOnce(() => arbitraryFor([arbitraryGenerated]));
-            // Call float next to find out the value required for NaN
-            doubleNext({ ...ct, noNaN: true });
-            const arb = doubleNext(ct);
-            // Extract NaN "index"
-            const [minNonNaN] = arrayInt64.mock.calls[0];
-            const [minNaN, maxNaN] = arrayInt64.mock.calls[1];
-            const indexForNaN = !isEqual64(minNonNaN, minNaN) ? minNaN : maxNaN;
-            if (indexForNaN === undefined) throw new Error('No value available for NaN');
-            arbitraryGenerated.value = indexForNaN;
-
-            // Act
-            const { value_: f } = arb.generate(mrng());
-
-            // Assert
-            expect(f).toBe(Number.NaN);
-          })
-        ));
-    });
-
-    describe('without NaN', () => {
-      // eslint-disable-next-line @typescript-eslint/no-unused-vars
-      const { noNaN, ...noNaNRecordConstraints } = defaultDoubleRecordConstraints;
-
-      it('Should ask integers between the indexes corresponding to min and max', () =>
-        fc.assert(
-          fc.property(doubleNextConstraints(noNaNRecordConstraints), (ctDraft) => {
-            // Arrange
-            const ct = { ...ctDraft, noNaN: true };
-            const arrayInt64 = mockNoOpArrayInt64Arb();
-            const { min, max } = minMaxForConstraints(ct);
-            const minIndex = doubleToIndex(min);
-            const maxIndex = doubleToIndex(max);
-
-            // Act
-            doubleNext(ct);
-
-            // Assert
-            expect(arrayInt64).toHaveBeenCalledTimes(1);
-            expect(arrayInt64).toHaveBeenCalledWith(minIndex, maxIndex);
-          })
-        ));
-    });
-  });
-
-  describe('decomposeDouble (@internal)', () => {
-    it('Should properly decompose basic values', () => {
-      expect(decomposeDouble(0)).toEqual({ exponent: -1022, significand: 0 });
-      expect(decomposeDouble(1)).toEqual({ exponent: 0, significand: 1 });
-      expect(decomposeDouble(128)).toEqual({ exponent: 7, significand: 1 });
-      expect(decomposeDouble(201)).toEqual({ exponent: 7, significand: 1.5703125 });
-    });
-    it('Should properly decompose negative values', () => {
-      expect(decomposeDouble(-0)).toEqual({ exponent: -1022, significand: -0 });
-      expect(decomposeDouble(-1)).toEqual({ exponent: 0, significand: -1 });
-    });
-    it('Should properly decompose extreme values', () => {
-      expect(decomposeDouble(Number.MAX_VALUE)).toEqual({ exponent: 1023, significand: 2 - Number.EPSILON });
-      expect(decomposeDouble(Number.MIN_VALUE)).toEqual({ exponent: -1022, significand: Number.EPSILON });
-      expect(decomposeDouble(Number.EPSILON)).toEqual({ exponent: -52, significand: 1 });
-      expect(decomposeDouble(1 + Number.EPSILON)).toEqual({ exponent: 0, significand: 1 + Number.EPSILON });
-    });
-    it('Should decompose a 64-bit float into its equivalent (significand, exponent)', () =>
-      fc.assert(
-        fc.property(float64raw(), (f64) => {
-          fc.pre(!Number.isNaN(f64));
-          const { exponent, significand } = decomposeDouble(f64);
-          expect(significand * 2 ** exponent).toBe(f64);
-        })
-      ));
-  });
-
-  describe('doubleToIndex (@internal)', () => {
-    it('Should always produce well-formed indexes', () =>
-      fc.assert(
-        fc.property(float64raw(), (d) => {
-          fc.pre(!Number.isNaN(d));
-          const index = doubleToIndex(d);
-          expect(index.data[0]).toBeGreaterThanOrEqual(0);
-          expect(index.data[0]).toBeLessThanOrEqual(0xffffffff);
-          expect(Number.isInteger(index.data[0])).toBe(true);
-          expect(index.data[1]).toBeGreaterThanOrEqual(0);
-          expect(index.data[1]).toBeLessThanOrEqual(0xffffffff);
-          expect(Number.isInteger(index.data[1])).toBe(true);
-        })
-      ));
-
-    if (typeof BigInt === 'undefined') {
-      it('no test', () => {
-        expect(true).toBe(true);
-      });
-      return;
-    } // Following tests require BigInt to be launched
-
-    it('Should properly compute indexes', () => {
-      expect(doubleToIndex(0)).toEqual(toIndex('0'));
-      expect(doubleToIndex(Number.MIN_VALUE)).toEqual(toIndex('1'));
-      expect(doubleToIndex(2 * Number.MIN_VALUE)).toEqual(toIndex('2'));
-      expect(doubleToIndex(3 * Number.MIN_VALUE)).toEqual(toIndex('3'));
-      // Last double with minimal exponent, ie -1022
-      // index(last with min exponent) = 2**53 - 1
-      expect(doubleToIndex(2 ** -1022 * (2 - Number.EPSILON))).toEqual(toIndex('9007199254740991'));
-      // First double without minimal exponent, ie -1022
-      // index(first without min exponent) = index(last with min exponent) + 1
-      expect(doubleToIndex(2 ** -1021)).toEqual(toIndex('9007199254740992'));
-      // Number.EPSILON === 1. * 2**-52 --> m = 1, e = -52
-      // index(Number.EPSILON) = 2**53 + (-52 - (-1022) -1) * 2**52
-      expect(doubleToIndex(Number.EPSILON)).toEqual(toIndex('4372995238176751616'));
-      // index(1 - Number.EPSILON / 2) = index(1) - 1
-      expect(doubleToIndex(1 - Number.EPSILON / 2)).toEqual(toIndex('4607182418800017407'));
-      // 1 === 1. * 2**0 --> m = 1, e = 0
-      // index(1) = 2**53 + (0 - (-1022) -1) * 2**52
-      expect(doubleToIndex(1)).toEqual(toIndex('4607182418800017408'));
-      // index(1 + Number.EPSILON) = index(1) + 1
-      expect(doubleToIndex(1 + Number.EPSILON)).toEqual(toIndex('4607182418800017409'));
-      // index(2 - Number.EPSILON) = index(2) - 1 = index(1 + (2 ** 52 - 1) * Number.EPSILON)
-      expect(doubleToIndex(2 - Number.EPSILON)).toEqual(toIndex('4611686018427387903'));
-      // 1 === 1. * 2**1 --> m = 1, e = 1
-      // index(2) = index(1) + 2**52
-      expect(doubleToIndex(2)).toEqual(toIndex('4611686018427387904'));
-      // Number.MAX_VALUE === (1 + (2**52-1)/2**52) * 2**1023 --> m = 1 + (2**52-1)/2**52, e = 1023
-      // index(Number.MAX_VALUE) = index(next(Number.MAX_VALUE)) -1 = 2**53 + (1024 - (-1022) -1) * 2**52 -1
-      expect(doubleToIndex(Number.MAX_VALUE)).toEqual(toIndex('9218868437227405311'));
-    });
-    it('Should properly compute negative indexes', () => {
-      expect(doubleToIndex(-0)).toEqual(toIndex('-1'));
-      expect(doubleToIndex(-Number.MIN_VALUE)).toEqual(toIndex('-2'));
-      expect(doubleToIndex(-Number.MAX_VALUE)).toEqual(toIndex('-9218868437227405312'));
-    });
-    it('Should properly compute indexes for infinity', () => {
-      expect(doubleToIndex(Number.NEGATIVE_INFINITY)).toEqual(
-        toIndex(toBigInt(doubleToIndex(-Number.MAX_VALUE)) - BigInt(1))
-      );
-      expect(doubleToIndex(Number.POSITIVE_INFINITY)).toEqual(
-        toIndex(toBigInt(doubleToIndex(Number.MAX_VALUE)) + BigInt(1))
-      );
-    });
-    it('Should be able to infer index for negative double from the positive one', () =>
-      fc.assert(
-        fc.property(float64raw(), (d) => {
-          fc.pre(!Number.isNaN(d));
-          const posD = d > 0 || 1 / d > 0 ? d : -d;
-          const bigIntIndexPos = toBigInt(doubleToIndex(posD));
-          const bigIntIndexNeg = toBigInt(doubleToIndex(-posD));
-          expect(bigIntIndexNeg).toEqual(-bigIntIndexPos - BigInt(1));
-        })
-      ));
-    it('Should return index +1 for the successor of a given double', () =>
-      fc.assert(
-        fc.property(
-          fc.integer({ min: -1022, max: +1023 }),
-          fc.integer({ min: 0, max: 2 ** 53 - 1 }),
-          (exponent, rescaledSignificand) => {
-            fc.pre(exponent === -1022 || rescaledSignificand >= 2 ** 52); // valid
-            fc.pre(exponent !== 1023 || rescaledSignificand !== 2 ** 53 - 1); // not max
-            const current = rescaledSignificand * Number.EPSILON * 2 ** exponent;
-            const next = (rescaledSignificand + 1) * Number.EPSILON * 2 ** exponent;
-            const bigIntIndexCurrent = toBigInt(doubleToIndex(current));
-            const bigIntIndexNext = toBigInt(doubleToIndex(next));
-            expect(bigIntIndexNext).toEqual(bigIntIndexCurrent + BigInt(1));
-          }
-        )
-      ));
-    it('Should preserve ordering between two doubles', () =>
-      fc.assert(
-        fc.property(float64raw(), float64raw(), (fa64, fb64) => {
-          fc.pre(!Number.isNaN(fa64) && !Number.isNaN(fb64));
-          if (isStrictlySmaller(fa64, fb64)) {
-            expect(toBigInt(doubleToIndex(fa64))).toBeLessThan(toBigInt(doubleToIndex(fb64)));
-          } else {
-            expect(toBigInt(doubleToIndex(fa64))).toBeGreaterThanOrEqual(toBigInt(doubleToIndex(fb64)));
-          }
-        })
-      ));
-  });
-
-  describe('indexToDouble (@internal)', () => {
-    it('Should reverse doubleToIndex', () =>
-      fc.assert(
-        fc.property(float64raw(), (f64) => {
-          fc.pre(!Number.isNaN(f64));
-          expect(indexToDouble(doubleToIndex(f64))).toBe(f64);
-        })
-      ));
-
-    if (typeof BigInt === 'undefined') {
-      it('no test', () => {
-        expect(true).toBe(true);
-      });
-      return;
-    } // Following tests require BigInt to be launched
-
-    it('Should properly find doubles corresponding to well-known values', () => {
-      expect(indexToDouble(toIndex('-9218868437227405313'))).toBe(Number.NEGATIVE_INFINITY);
-      expect(indexToDouble(toIndex('-9218868437227405312'))).toBe(-Number.MAX_VALUE);
-      expect(indexToDouble(toIndex('-1'))).toBe(-0);
-      expect(indexToDouble(toIndex('0'))).toBe(0);
-      expect(indexToDouble(toIndex('4372995238176751616'))).toBe(Number.EPSILON);
-      expect(indexToDouble(toIndex('9218868437227405311'))).toBe(Number.MAX_VALUE);
-      expect(indexToDouble(toIndex('9218868437227405312'))).toBe(Number.POSITIVE_INFINITY);
-    });
-    it('Should be reversed by doubleToIndex', () =>
-      fc.assert(
-        fc.property(
-          fc.bigInt({ min: BigInt('-9218868437227405313'), max: BigInt('9218868437227405312') }),
-          (bigIntIndex) => {
-            // The test below checks that indexToDouble(doubleToIndex) is identity
-            // It does not confirm that doubleToIndex(indexToDouble)) is identity
-            const index = toIndex(bigIntIndex);
-            expect(doubleToIndex(indexToDouble(index))).toEqual(index);
-          }
-        )
-      ));
-  });
-});
diff --git a/test/unit/check/arbitrary/FloatNextArbitrary.itest.spec.ts b/test/unit/check/arbitrary/FloatNextArbitrary.itest.spec.ts
deleted file mode 100644
index 065ccdc7..00000000
--- a/test/unit/check/arbitrary/FloatNextArbitrary.itest.spec.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import * as fc from '../../../../lib/fast-check';
-
-import { floatNext, FloatNextConstraints } from '../../../../src/check/arbitrary/FloatNextArbitrary';
-import { floatNextConstraints, is32bits } from './generic/FloatingPointHelpers';
-import * as genericHelper from './generic/GenericArbitraryHelper';
-
-describe('FloatNextArbitrary', () => {
-  describe('floatNext', () => {
-    describe('Is valid arbitrary?', () => {
-      genericHelper.isValidArbitrary((ct?: FloatNextConstraints) => floatNext(ct), {
-        isStrictlySmallerValue: (fa, fb) =>
-          Math.abs(fa) < Math.abs(fb) || //              Case 1: abs(a) < abs(b)
-          (Object.is(fa, +0) && Object.is(fb, -0)) || // Case 2: +0 < -0  --> we shrink from -0 to +0
-          (!Number.isNaN(fa) && Number.isNaN(fb)), //    Case 3: notNaN < NaN, NaN is one of the extreme values
-        isValidValue: (g: number, ct?: FloatNextConstraints) => {
-          if (typeof g !== 'number') return false; // should always produce numbers
-          if (!is32bits(g)) return false; // should always produce 32-bit floats
-          if (Number.isNaN(g)) {
-            if (ct !== undefined && ct.noNaN) return false; // should not produce NaN if explicitely asked not too
-            return true;
-          }
-          if (ct !== undefined && ct.min !== undefined && g < ct.min) return false; // should always be greater than min when specified
-          if (ct !== undefined && ct.max !== undefined && g > ct.max) return false; // should always be smaller than max when specified
-          if (ct !== undefined && ct.noDefaultInfinity) {
-            if (ct.min === undefined && g === Number.NEGATIVE_INFINITY) return false; // should not produce -infinity when noInfity and min unset
-            if (ct.max === undefined && g === Number.POSITIVE_INFINITY) return false; // should not produce +infinity when noInfity and max unset
-          }
-          return true;
-        },
-        seedGenerator: fc.option(floatNextConstraints(), { nil: undefined }),
-      });
-    });
-  });
-});
diff --git a/test/unit/check/arbitrary/FloatNextArbitrary.utest.spec.ts b/test/unit/check/arbitrary/FloatNextArbitrary.utest.spec.ts
deleted file mode 100644
index a8b72d7f..00000000
--- a/test/unit/check/arbitrary/FloatNextArbitrary.utest.spec.ts
+++ /dev/null
@@ -1,343 +0,0 @@
-import * as fc from '../../../../lib/fast-check';
-
-import {
-  decomposeFloat,
-  floatToIndex,
-  indexToFloat,
-  EPSILON_32,
-  MAX_VALUE_32,
-  MIN_VALUE_32,
-  floatNext,
-  FloatNextConstraints,
-} from '../../../../src/check/arbitrary/FloatNextArbitrary';
-import {
-  floatNextConstraints,
-  float32raw,
-  isNotNaN32bits,
-  float64raw,
-  isStrictlySmaller,
-  isFiniteNotNaN32bits,
-  defaultFloatRecordConstraints,
-} from './generic/FloatingPointHelpers';
-
-import { mocked } from 'ts-jest/utils';
-import { arbitraryFor } from './generic/ArbitraryBuilder';
-import * as stubRng from '../../stubs/generators';
-
-import * as IntegerMock from '../../../../src/arbitrary/integer';
-jest.mock('../../../../src/arbitrary/integer');
-
-const mrng = () => stubRng.mutable.nocall();
-
-function minMaxForConstraints(ct: FloatNextConstraints) {
-  const noDefaultInfinity = ct.noDefaultInfinity;
-  const {
-    min = noDefaultInfinity ? -MAX_VALUE_32 : Number.NEGATIVE_INFINITY,
-    max = noDefaultInfinity ? MAX_VALUE_32 : Number.POSITIVE_INFINITY,
-  } = ct;
-  return { min, max };
-}
-
-function mockNoOpIntegerArb(opts: { single?: boolean } = {}) {
-  // Mocking integer: not expecting any call there
-  const { integer } = mocked(IntegerMock);
-  if (opts.single) integer.mockImplementationOnce(() => arbitraryFor([]));
-  else integer.mockImplementation(() => arbitraryFor([]));
-  return integer;
-}
-
-beforeEach(() => {
-  jest.clearAllMocks();
-  jest.resetAllMocks();
-});
-const previousGlobal = fc.readConfigureGlobal();
-fc.configureGlobal({
-  ...previousGlobal,
-  beforeEach: () => {
-    jest.clearAllMocks();
-    jest.resetAllMocks();
-  },
-});
-
-describe('FloatNextArbitrary', () => {
-  describe('floatNext', () => {
-    it('Should accept any valid range of 32-bit floating point numbers (including infinity)', () =>
-      fc.assert(
-        fc.property(floatNextConstraints(), (ct) => {
-          mockNoOpIntegerArb();
-          expect(floatNext(ct)).toBeDefined();
-        })
-      ));
-    it('Should accept any constraits defining min (32-bit float not-NaN) equal to max', () =>
-      fc.assert(
-        fc.property(
-          float32raw(),
-          fc.record({ noDefaultInfinity: fc.boolean(), noNaN: fc.boolean() }, { withDeletedKeys: true }),
-          (f, otherCt) => {
-            fc.pre(isNotNaN32bits(f));
-            mockNoOpIntegerArb();
-            expect(floatNext({ ...otherCt, min: f, max: f })).toBeDefined();
-          }
-        )
-      ));
-    it('Should reject non-32-bit or NaN floating point numbers if specified for min', () =>
-      fc.assert(
-        fc.property(float64raw(), (f64) => {
-          fc.pre(!isNotNaN32bits(f64));
-          mockNoOpIntegerArb();
-          expect(() => floatNext({ min: f64 })).toThrowError();
-        })
-      ));
-    it('Should reject non-32-bit or NaN floating point numbers if specified for max', () =>
-      fc.assert(
-        fc.property(float64raw(), (f64) => {
-          fc.pre(!isNotNaN32bits(f64));
-          mockNoOpIntegerArb();
-          expect(() => floatNext({ max: f64 })).toThrowError();
-        })
-      ));
-    it('Should reject if specified min is strictly greater than max', () =>
-      fc.assert(
-        fc.property(float32raw(), float32raw(), (fa32, fb32) => {
-          fc.pre(isNotNaN32bits(fa32));
-          fc.pre(isNotNaN32bits(fb32));
-          fc.pre(!Object.is(fa32, fb32)); // Object.is can distinguish -0 from 0, while !== cannot
-          mockNoOpIntegerArb();
-          const min = isStrictlySmaller(fa32, fb32) ? fb32 : fa32;
-          const max = isStrictlySmaller(fa32, fb32) ? fa32 : fb32;
-          expect(() => floatNext({ min, max })).toThrowError();
-        })
-      ));
-    it('Should reject impossible noDefaultInfinity-based ranges', () => {
-      mockNoOpIntegerArb();
-      expect(() => floatNext({ min: Number.POSITIVE_INFINITY, noDefaultInfinity: true })).toThrowError();
-      expect(() => floatNext({ max: Number.NEGATIVE_INFINITY, noDefaultInfinity: true })).toThrowError();
-    });
-    it('Should properly convert integer value for index between min and max into its associated float value', () =>
-      fc.assert(
-        fc.property(fc.option(floatNextConstraints(), { nil: undefined }), fc.maxSafeNat(), (ct, mod) => {
-          // Arrange
-          const { integer } = mocked(IntegerMock);
-          const { min, max } = minMaxForConstraints(ct || {});
-          const minIndex = floatToIndex(min);
-          const maxIndex = floatToIndex(max);
-          const arbitraryGeneratedIndex = (mod % (maxIndex - minIndex + 1)) + minIndex;
-          integer.mockImplementationOnce(() => arbitraryFor([{ value: arbitraryGeneratedIndex }]));
-
-          // Act
-          const arb = floatNext(ct);
-          const { value_: f } = arb.generate(mrng());
-
-          // Assert
-          expect(f).toBe(indexToFloat(arbitraryGeneratedIndex));
-        })
-      ));
-
-    describe('with NaN', () => {
-      const withNaNRecordConstraints = { ...defaultFloatRecordConstraints, noNaN: fc.constant(false) };
-
-      it('Should ask for a range with one extra value (far from zero)', () =>
-        fc.assert(
-          fc.property(floatNextConstraints(withNaNRecordConstraints), (ct) => {
-            // Arrange
-            const { max } = minMaxForConstraints(ct);
-            const integer = mockNoOpIntegerArb();
-
-            // Act
-            floatNext({ ...ct, noNaN: true });
-            floatNext(ct);
-
-            // Assert
-            expect(integer).toHaveBeenCalledTimes(2);
-            const integerConstraintsNoNaN = integer.mock.calls[0][0];
-            const integerConstraintsWithNaN = integer.mock.calls[1][0];
-            if (max > 0) {
-              // max > 0  --> NaN will be added as the greatest value
-              expect(integerConstraintsWithNaN.min).toBe(integerConstraintsNoNaN.min);
-              expect(integerConstraintsWithNaN.max).toBe(integerConstraintsNoNaN.max! + 1);
-            } else {
-              // max <= 0 --> NaN will be added as the smallest value
-              expect(integerConstraintsWithNaN.min).toBe(integerConstraintsNoNaN.min! - 1);
-              expect(integerConstraintsWithNaN.max).toBe(integerConstraintsNoNaN.max);
-            }
-          })
-        ));
-      it('Should properly convert the extra value to NaN', () =>
-        fc.assert(
-          fc.property(floatNextConstraints(withNaNRecordConstraints), (ct) => {
-            // Arrange
-            // Setup mocks for integer
-            const arbitraryGenerated = { value: Number.NaN };
-            const integer = mockNoOpIntegerArb({ single: true });
-            integer.mockImplementationOnce(() => arbitraryFor([arbitraryGenerated]));
-            // Call float next to find out the value required for NaN
-            floatNext({ ...ct, noNaN: true });
-            const arb = floatNext(ct);
-            // Extract NaN "index"
-            const { min: minNonNaN } = integer.mock.calls[0][0];
-            const { min: minNaN, max: maxNaN } = integer.mock.calls[1][0];
-            const indexForNaN = minNonNaN !== minNaN ? minNaN : maxNaN;
-            if (indexForNaN === undefined) throw new Error('No value available for NaN');
-            arbitraryGenerated.value = indexForNaN;
-
-            // Act
-            const { value_: f } = arb.generate(mrng());
-
-            // Assert
-            expect(f).toBe(Number.NaN);
-          })
-        ));
-    });
-
-    describe('without NaN', () => {
-      // eslint-disable-next-line @typescript-eslint/no-unused-vars
-      const { noNaN, ...noNaNRecordConstraints } = defaultFloatRecordConstraints;
-
-      it('Should ask integers between the indexes corresponding to min and max', () =>
-        fc.assert(
-          fc.property(floatNextConstraints(noNaNRecordConstraints), (ctDraft) => {
-            // Arrange
-            const ct = { ...ctDraft, noNaN: true };
-            const integer = mockNoOpIntegerArb();
-            const { min, max } = minMaxForConstraints(ct);
-            const minIndex = floatToIndex(min);
-            const maxIndex = floatToIndex(max);
-
-            // Act
-            floatNext(ct);
-
-            // Assert
-            expect(integer).toHaveBeenCalledTimes(1);
-            expect(integer).toHaveBeenCalledWith({ min: minIndex, max: maxIndex });
-          })
-        ));
-    });
-  });
-
-  describe('decomposeFloat (@internal)', () => {
-    it('Should properly decompose basic values', () => {
-      expect(decomposeFloat(0)).toEqual({ exponent: -126, significand: 0 });
-      expect(decomposeFloat(1)).toEqual({ exponent: 0, significand: 1 });
-      expect(decomposeFloat(128)).toEqual({ exponent: 7, significand: 1 });
-      expect(decomposeFloat(201)).toEqual({ exponent: 7, significand: 1.5703125 });
-    });
-    it('Should properly decompose negative values', () => {
-      expect(decomposeFloat(-0)).toEqual({ exponent: -126, significand: -0 });
-      expect(decomposeFloat(-1)).toEqual({ exponent: 0, significand: -1 });
-    });
-    it('Should properly decompose extreme values', () => {
-      expect(decomposeFloat(MAX_VALUE_32)).toEqual({ exponent: 127, significand: 1 + (2 ** 23 - 1) / 2 ** 23 });
-      expect(decomposeFloat(MIN_VALUE_32)).toEqual({ exponent: -126, significand: 2 ** -23 });
-      expect(decomposeFloat(EPSILON_32)).toEqual({ exponent: -23, significand: 1 });
-      expect(decomposeFloat(1 + EPSILON_32)).toEqual({ exponent: 0, significand: 1 + 2 ** -23 });
-    });
-    it('Should decompose a 32-bit float into its equivalent (significand, exponent)', () =>
-      fc.assert(
-        fc.property(float32raw(), (f32) => {
-          fc.pre(isFiniteNotNaN32bits(f32));
-          const { exponent, significand } = decomposeFloat(f32);
-          expect(significand * 2 ** exponent).toBe(f32);
-        })
-      ));
-  });
-
-  describe('floatToIndex (@internal)', () => {
-    it('Should properly compute indexes', () => {
-      expect(floatToIndex(0)).toBe(0);
-      expect(floatToIndex(MIN_VALUE_32)).toBe(1);
-      expect(floatToIndex(2 * MIN_VALUE_32)).toBe(2);
-      expect(floatToIndex(3 * MIN_VALUE_32)).toBe(3);
-      // EPSILON_32 === 1. * 2**-23 --> m = 1, e = -23
-      // index(EPSILON_32) = 2**24 + (-23 - (-126) -1) * 2**23
-      expect(floatToIndex(EPSILON_32)).toBe(872415232);
-      // index(1 - EPSILON_32 / 2) = index(1) - 1
-      expect(floatToIndex(1 - EPSILON_32 / 2)).toEqual(1065353215);
-      // 1 === 1. * 2**0 --> m = 1, e = 0
-      // index(1) = 2**24 + (0 - (-126) -1) * 2**23
-      expect(floatToIndex(1)).toEqual(1065353216);
-      // index(1 + EPSILON_32) = index(1) + 1
-      expect(floatToIndex(1 + EPSILON_32)).toEqual(1065353217);
-      // index(2 - EPSILON_32) = index(2) - 1 = index(1 + (2 ** 23 - 1) * EPSILON_32)
-      expect(floatToIndex(2 - EPSILON_32)).toEqual(1073741823);
-      // 1 === 1. * 2**1 --> m = 1, e = 1
-      // index(2) = index(1) + 2**23
-      expect(floatToIndex(2)).toEqual(1073741824);
-      expect(floatToIndex(MAX_VALUE_32)).toBe(2139095039);
-    });
-    it('Should properly compute negative indexes', () => {
-      expect(floatToIndex(-0)).toEqual(-1);
-      expect(floatToIndex(-MIN_VALUE_32)).toBe(-2);
-      expect(floatToIndex(-MAX_VALUE_32)).toBe(-2139095040);
-    });
-    it('Should properly compute indexes for infinity', () => {
-      expect(floatToIndex(Number.NEGATIVE_INFINITY)).toBe(floatToIndex(-MAX_VALUE_32) - 1);
-      expect(floatToIndex(Number.POSITIVE_INFINITY)).toBe(floatToIndex(MAX_VALUE_32) + 1);
-    });
-    it('Should be able to infer index for negative float from the positive one', () =>
-      fc.assert(
-        fc.property(float32raw(), (f) => {
-          fc.pre(isNotNaN32bits(f));
-          const posD = f > 0 || 1 / f > 0 ? f : -f;
-          const indexPos = floatToIndex(posD);
-          const indexNeg = floatToIndex(-posD);
-          expect(indexNeg).toEqual(-indexPos - 1);
-        })
-      ));
-    it('Should return index +1 for the successor of a given float', () =>
-      fc.assert(
-        fc.property(
-          fc.integer({ min: -126, max: +127 }),
-          fc.integer({ min: 0, max: 2 ** 24 - 1 }),
-          (exponent, rescaledSignificand) => {
-            fc.pre(exponent === -126 || rescaledSignificand >= 2 ** 23); // valid
-            fc.pre(exponent !== 127 || rescaledSignificand !== 2 ** 24 - 1); // not max
-            const current = rescaledSignificand * EPSILON_32 * 2 ** exponent;
-            const next = (rescaledSignificand + 1) * EPSILON_32 * 2 ** exponent;
-            expect(floatToIndex(next)).toEqual(floatToIndex(current) + 1);
-          }
-        )
-      ));
-    it('Should preserve ordering between two floats', () =>
-      fc.assert(
-        fc.property(float32raw(), float32raw(), (fa32, fb32) => {
-          fc.pre(isNotNaN32bits(fa32) && isNotNaN32bits(fb32));
-          if (isStrictlySmaller(fa32, fb32)) expect(floatToIndex(fa32)).toBeLessThan(floatToIndex(fb32));
-          else expect(floatToIndex(fa32)).toBeGreaterThanOrEqual(floatToIndex(fb32));
-        })
-      ));
-  });
-
-  describe('indexToFloat (@internal)', () => {
-    it('Should properly find floats corresponding to well-known values', () => {
-      expect(indexToFloat(-2139095041)).toBe(Number.NEGATIVE_INFINITY);
-      expect(indexToFloat(-2139095040)).toBe(-MAX_VALUE_32);
-      expect(indexToFloat(-1)).toBe(-0);
-      expect(indexToFloat(0)).toBe(0);
-      expect(indexToFloat(872415232)).toBe(EPSILON_32);
-      expect(indexToFloat(2139095039)).toBe(MAX_VALUE_32);
-      expect(indexToFloat(2139095040)).toBe(Number.POSITIVE_INFINITY);
-    });
-    it('Should only produce 32-bit floating point numbers (excluding NaN)', () =>
-      fc.assert(
-        fc.property(fc.integer(-2139095041, 2139095040), (index) => {
-          const f = indexToFloat(index);
-          expect(f).toBe(new Float32Array([f])[0]);
-        })
-      ));
-    it('Should reverse floatToIndex', () =>
-      fc.assert(
-        fc.property(float32raw(), (f32) => {
-          fc.pre(isNotNaN32bits(f32));
-          expect(indexToFloat(floatToIndex(f32))).toBe(f32);
-        })
-      ));
-    it('Should be reversed by floatToIndex', () =>
-      fc.assert(
-        fc.property(fc.integer(-2139095041, 2139095040), (index) => {
-          // The test below checks that indexToFloat(floatToIndex) is identity
-          // It does not confirm that floatToIndex(indexToFloat)) is identity
-          expect(floatToIndex(indexToFloat(index))).toBe(index);
-        })
-      ));
-  });
-});
diff --git a/test/unit/check/arbitrary/FloatingPointArbitrary.spec.ts b/test/unit/check/arbitrary/FloatingPointArbitrary.spec.ts
index e06700ff..55ba731a 100644
--- a/test/unit/check/arbitrary/FloatingPointArbitrary.spec.ts
+++ b/test/unit/check/arbitrary/FloatingPointArbitrary.spec.ts
@@ -2,7 +2,8 @@ import * as fc from '../../../../lib/fast-check';
 
 import { Arbitrary } from '../../../../src/check/arbitrary/definition/Arbitrary';
 import { Random } from '../../../../src/random/generator/Random';
-import { float, double } from '../../../../src/check/arbitrary/FloatingPointArbitrary';
+import { double } from '../../../../src/arbitrary/double';
+import { float } from '../../../../src/arbitrary/float';
 
 import * as genericHelper from './generic/GenericArbitraryHelper';
 
diff --git a/test/unit/check/arbitrary/generic/FloatingPointHelpers.ts b/test/unit/check/arbitrary/generic/FloatingPointHelpers.ts
index e4fb76ce..f4623e9e 100644
--- a/test/unit/check/arbitrary/generic/FloatingPointHelpers.ts
+++ b/test/unit/check/arbitrary/generic/FloatingPointHelpers.ts
@@ -1,6 +1,6 @@
 import * as fc from '../../../../../lib/fast-check';
-import { DoubleNextConstraints } from '../../../../../src/check/arbitrary/DoubleNextArbitrary';
-import { FloatNextConstraints } from '../../../../../src/check/arbitrary/FloatNextArbitrary';
+import { DoubleNextConstraints } from '../../../../../src/arbitrary/_next/doubleNext';
+import { FloatNextConstraints } from '../../../../../src/arbitrary/_next/floatNext';
 
 export function float32raw(): fc.Arbitrary<number> {
   return fc.integer().map((n32) => new Float32Array(new Int32Array([n32]).buffer)[0]);
diff --git a/test/unit/check/arbitrary/helpers/ArrayInt64Arbitrary.itest.spec.ts b/test/unit/check/arbitrary/helpers/ArrayInt64Arbitrary.itest.spec.ts
index 08263559..08e4472b 100644
--- a/test/unit/check/arbitrary/helpers/ArrayInt64Arbitrary.itest.spec.ts
+++ b/test/unit/check/arbitrary/helpers/ArrayInt64Arbitrary.itest.spec.ts
@@ -1,7 +1,7 @@
 import * as fc from '../../../../../lib/fast-check';
 
-import { ArrayInt64 } from '../../../../../src/check/arbitrary/helpers/ArrayInt64';
-import { arrayInt64 } from '../../../../../src/check/arbitrary/helpers/ArrayInt64Arbitrary';
+import { ArrayInt64 } from '../../../../../src/arbitrary/_internals/helpers/ArrayInt64';
+import { arrayInt64 } from '../../../../../src/arbitrary/_internals/ArrayInt64Arbitrary';
 import * as genericHelper from '../generic/GenericArbitraryHelper';
 
 function toArrayInt64(b: bigint): ArrayInt64 {
diff --git a/test/unit/check/arbitrary/helpers/ArrayInt64Arbitrary.utest.spec.ts b/test/unit/check/arbitrary/helpers/ArrayInt64Arbitrary.utest.spec.ts
index ccdafc94..ceb9cee9 100644
--- a/test/unit/check/arbitrary/helpers/ArrayInt64Arbitrary.utest.spec.ts
+++ b/test/unit/check/arbitrary/helpers/ArrayInt64Arbitrary.utest.spec.ts
@@ -1,7 +1,7 @@
 import * as fc from '../../../../../lib/fast-check';
 
-import { arrayInt64 as arrayInt64Old } from '../../../../../src/check/arbitrary/helpers/ArrayInt64Arbitrary';
-import { ArrayInt64 } from '../../../../../src/check/arbitrary/helpers/ArrayInt64';
+import { arrayInt64 as arrayInt64Old } from '../../../../../src/arbitrary/_internals/ArrayInt64Arbitrary';
+import { ArrayInt64 } from '../../../../../src/arbitrary/_internals/helpers/ArrayInt64';
 import { NextArbitrary } from '../../../../../src/check/arbitrary/definition/NextArbitrary';
 import { convertToNext } from '../../../../../src/check/arbitrary/definition/Converters';