Repository: marpple/FxTS
Pull request #332 (2025-06-20 06:20:51)

feat: add shuffle function with Fisher-Yates algorithm
## Summary
- Add core shuffle function in `src/shuffle.ts` with Fisher-Yates algorithm
- Add lazy evaluation version in `src/Lazy/shuffle.ts` with iterator support
- Add shuffle method to fx() method chaining (FxIterable/FxAsyncIterable)
- Support for arrays, strings, sync/async iterables, and concurrent processing
- Maintain full TypeScript type inference and FxTS conventions

## Features
- **Fisher-Yates algorithm**: Ensures uniform random distribution
- **Multiple formats**: Works with arrays, strings, and iterables
- **Sync/Async support**: Full compatibility with Promise and AsyncIterable
- **Lazy evaluation**: Memory-efficient iterator-based processing
- **Method chaining**: Integrates seamlessly with fx() chains
- **Concurrent processing**: Supports FxTS concurrent operations
- **Type safety**: Complete TypeScript type inference

## Test Coverage
- Core function tests (`test/shuffle.spec.ts`)
- Lazy evaluation tests (`test/Lazy/shuffle.spec.ts`) 
- Method chaining tests (`test/fx-shuffle.spec.ts`)
- Type checking tests (`type-check/shuffle.test.ts`, `type-check/Lazy/shuffle.test.ts`)
- Statistical randomness validation
- Edge cases (empty arrays, single elements)
- Concurrent processing scenarios

## Usage Examples
```typescript
// Basic usage
shuffle([1, 2, 3, 4, 5]); // [3, 1, 5, 2, 4]
shuffle("hello"); // ["o", "e", "l", "h", "l"]

// Method chaining
fx([1, 2, 3, 4, 5])
  .filter(n => n % 2 === 0)
  .shuffle()
  .take(2)
  .toArray(); // [4, 2]

// Async support
await shuffle(toAsync([1, 2, 3])); // [2, 3, 1]
```

## Implementation Details
- Follows existing FxTS patterns and conventions
- Uses Fisher-Yates algorithm for optimal randomness
- Supports currying for functional composition
- Compatible with all FxTS type utilities
- No breaking changes to existing API

## Checklist
- [x] Implementation follows FxTS coding standards
- [x] Comprehensive test coverage (26+ test cases)
- [x] TypeScript type safety verified
- [x] JSDoc documentation provided
- [x] Supports all FxTS patterns (sync/async/lazy/chaining)
- [x] No breaking changes to existing API
- [x] Fisher-Yates algorithm for optimal randomness

ppeeou: @noughts  Thanks for your interest in `fxts`. 
The shuffle function seems useful.

It seems like it should be included in either Lazy or Util, but which one would be more useful?

If it's included in Lazy, it should have some benefit when delayed.
hg-pyun: I think it makes sense for it to go into util.
ppeeou: @noughts  Please move it to the Util function and I think it will be fine if you just modify the lint 😄

Patch:
diff --git a/src/Lazy/fx.ts b/src/Lazy/fx.ts
index 5ba55994..93b43986 100644
--- a/src/Lazy/fx.ts
+++ b/src/Lazy/fx.ts
@@ -25,6 +25,7 @@ import flatMap from "./flatMap";
 import map from "./map";
 import peek from "./peek";
 import reject from "./reject";
+import shuffle from "./shuffle";
 import slice from "./slice";
 import take from "./take";
 import takeUntilInclusive from "./takeUntilInclusive";
@@ -194,6 +195,15 @@ class FxAsyncIterable<A> {
       : new FxAsyncIterable(slice(start, end, this.asyncIterable));
   }
 
+  /**
+   * Returns AsyncIterable of the given elements in random order using Fisher-Yates algorithm.
+   *
+   * see {@link https://fxts.dev/docs/shuffle | shuffle}
+   */
+  shuffle(): FxAsyncIterable<A> {
+    return new FxAsyncIterable(shuffle(this.asyncIterable));
+  }
+
   /**
    *
    * `chain` allows you to use functions that are not provided in method chaining.
@@ -537,6 +547,15 @@ export class FxIterable<A> {
       : new FxIterable(slice(start, end, this.iterable));
   }
 
+  /**
+   * Returns Iterable of the given elements in random order using Fisher-Yates algorithm.
+   *
+   * see {@link https://fxts.dev/docs/shuffle | shuffle}
+   */
+  shuffle(): FxIterable<A> {
+    return new FxIterable(shuffle(this.iterable));
+  }
+
   /**
    *
    * `chain` allows you to use functions that are not provided in method chaining.
diff --git a/src/Lazy/index.ts b/src/Lazy/index.ts
index a1789260..e119eac4 100644
--- a/src/Lazy/index.ts
+++ b/src/Lazy/index.ts
@@ -30,6 +30,7 @@ import reject from "./reject";
 import repeat from "./repeat";
 import reverse from "./reverse";
 import scan from "./scan";
+import shuffle from "./shuffle";
 import slice from "./slice";
 import split from "./split";
 import take from "./take";
@@ -80,6 +81,7 @@ export {
   repeat,
   reverse,
   scan,
+  shuffle,
   slice,
   split,
   take,
diff --git a/src/Lazy/shuffle.ts b/src/Lazy/shuffle.ts
new file mode 100644
index 00000000..00bf9ccd
--- /dev/null
+++ b/src/Lazy/shuffle.ts
@@ -0,0 +1,110 @@
+import { isAsyncIterable, isIterable } from "../_internal/utils";
+import isArray from "../isArray";
+import isString from "../isString";
+import toArray from "../toArray";
+import type ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
+import concurrent, { isConcurrent } from "./concurrent";
+
+function* sync<T>(iterable: Iterable<T>) {
+  const arr =
+    isArray(iterable) || isString(iterable) ? iterable : toArray(iterable);
+
+  // Fisher-Yates shuffle
+  const result = [...arr];
+  for (let i = result.length - 1; i > 0; i--) {
+    const j = Math.floor(Math.random() * (i + 1));
+    [result[i], result[j]] = [result[j], result[i]];
+  }
+
+  for (const item of result) {
+    yield item;
+  }
+}
+
+async function* asyncSequential<T>(iterable: AsyncIterable<T>) {
+  const arr = await toArray(iterable);
+
+  // Fisher-Yates shuffle
+  const result = [...arr];
+  for (let i = result.length - 1; i > 0; i--) {
+    const j = Math.floor(Math.random() * (i + 1));
+    [result[i], result[j]] = [result[j], result[i]];
+  }
+
+  for (const item of result) {
+    yield item;
+  }
+}
+
+function async<T>(iterable: AsyncIterable<T>): AsyncIterableIterator<T> {
+  let iterator: AsyncIterator<T>;
+  return {
+    [Symbol.asyncIterator]() {
+      return this;
+    },
+
+    async next(_concurrent: any) {
+      if (iterator === undefined) {
+        iterator = isConcurrent(_concurrent)
+          ? asyncSequential(concurrent(_concurrent.length, iterable))
+          : asyncSequential(iterable);
+      }
+
+      return iterator.next(_concurrent);
+    },
+  };
+}
+
+/**
+ * Returns Iterable/AsyncIterable of the given elements in random order using Fisher-Yates algorithm.
+ * Note: Evaluates all Iterable and returns a lazy-shuffled iterator.
+ *
+ * @example
+ * ```ts
+ * const iter1 = shuffle([1, 2, 3]);
+ * iter1.next(); // {value: 2, done: false} (random)
+ * iter1.next(); // {value: 1, done: false} (random)
+ * iter1.next(); // {value: 3, done: false} (random)
+ * iter1.next(); // {value: undefined, done: true}
+ *
+ * const iter2 = shuffle("abc");
+ * iter2.next(); // {value: "c", done: false} (random)
+ * iter2.next(); // {value: "a", done: false} (random)
+ * iter2.next(); // {value: "b", done: false} (random)
+ * iter2.next(); // {value: undefined, done: true}
+ *
+ * // with pipe
+ * pipe(
+ *  [1, 2, 3, 4, 5],
+ *  shuffle,
+ *  toArray,
+ * ); // [3, 1, 5, 2, 4] (random order)
+ *
+ * pipe(
+ *  "abcde",
+ *  shuffle,
+ *  toArray,
+ * ); // ["c", "a", "e", "b", "d"] (random order)
+ * ```
+ *
+ * see {@link https://fxts.dev/docs/pipe | pipe} {@link https://fxts.dev/docs/toArray | toArray}
+ */
+function shuffle<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  iterable: T,
+): ReturnIterableIteratorType<T>;
+
+function shuffle<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  iterable: T,
+) {
+  if (isIterable(iterable)) {
+    return sync(iterable);
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return async(iterable);
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default shuffle;
diff --git a/src/index.ts b/src/index.ts
index ff0496aa..fafb3fe0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -53,6 +53,7 @@ import prop from "./prop";
 import props from "./props";
 import reduce from "./reduce";
 import reduceLazy from "./reduceLazy";
+import shuffle from "./shuffle";
 import size from "./size";
 import some from "./some";
 import sort from "./sort";
@@ -126,6 +127,7 @@ export {
   props,
   reduce,
   reduceLazy,
+  shuffle,
   size,
   unless,
   some,
diff --git a/src/shuffle.ts b/src/shuffle.ts
new file mode 100644
index 00000000..1e4a10a8
--- /dev/null
+++ b/src/shuffle.ts
@@ -0,0 +1,82 @@
+import { isAsyncIterable, isIterable } from "./_internal/utils";
+import isArray from "./isArray";
+import pipe1 from "./pipe1";
+import toArray from "./toArray";
+import type IterableInfer from "./types/IterableInfer";
+import type ReturnValueType from "./types/ReturnValueType";
+
+/**
+ * Returns a new array with the elements of the input array shuffled randomly using Fisher-Yates algorithm.
+ *
+ * @example
+ * ```ts
+ * shuffle([1, 2, 3, 4, 5]); // [3, 1, 5, 2, 4] (random order)
+ * shuffle("hello"); // ["o", "e", "l", "h", "l"] (random order)
+ * ```
+ */
+// prettier-ignore
+function shuffle(
+    iterable: readonly []
+): any[];
+
+// prettier-ignore
+function shuffle<T>(
+  iterable: Iterable<T>
+): T[];
+
+function shuffle<T>(iterable: AsyncIterable<T>): Promise<T[]>;
+
+function shuffle<T extends Iterable<unknown> | AsyncIterable<unknown>>(): (
+  iterable: T,
+) => ReturnValueType<T, IterableInfer<T>[]>;
+
+function shuffle<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  iterable?: T,
+):
+  | IterableInfer<T>[]
+  | Promise<IterableInfer<T>[]>
+  | ((iterable: T) => ReturnValueType<T, IterableInfer<T>[]>) {
+  if (iterable === undefined) {
+    return (iterable: T) => {
+      return shuffle(iterable as any) as ReturnValueType<T, IterableInfer<T>[]>;
+    };
+  }
+
+  if (isArray(iterable)) {
+    const result = [...(iterable as any[])];
+    for (let i = result.length - 1; i > 0; i--) {
+      const j = Math.floor(Math.random() * (i + 1));
+      [result[i], result[j]] = [result[j], result[i]];
+    }
+    return result;
+  }
+
+  if (isIterable(iterable)) {
+    return pipe1(toArray(iterable as Iterable<IterableInfer<T>>), (arr) => {
+      const result = [...arr];
+      for (let i = result.length - 1; i > 0; i--) {
+        const j = Math.floor(Math.random() * (i + 1));
+        [result[i], result[j]] = [result[j], result[i]];
+      }
+      return result;
+    });
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return pipe1(
+      toArray(iterable as AsyncIterable<IterableInfer<T>>),
+      (arr) => {
+        const result = [...arr];
+        for (let i = result.length - 1; i > 0; i--) {
+          const j = Math.floor(Math.random() * (i + 1));
+          [result[i], result[j]] = [result[j], result[i]];
+        }
+        return result;
+      },
+    );
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default shuffle;


Test patch:
diff --git a/test/Lazy/shuffle.spec.ts b/test/Lazy/shuffle.spec.ts
new file mode 100644
index 00000000..edd4db86
--- /dev/null
+++ b/test/Lazy/shuffle.spec.ts
@@ -0,0 +1,129 @@
+import { delay, filter, map, pipe, toArray, toAsync } from "../../src";
+import shuffle from "../../src/Lazy/shuffle";
+import concurrent, { Concurrent } from "../../src/Lazy/concurrent";
+import { generatorMock } from "../utils";
+
+describe("shuffle", function () {
+  describe("sync", function () {
+    it.each([
+      [[1, 2, 3, 4], 4],
+      ["abcd", 4],
+    ])(
+      "should return the given elements %s in shuffled order with length %s",
+      function (input, expectedLength) {
+        const result = toArray(shuffle(input));
+        expect(result).toHaveLength(expectedLength);
+
+        // Check all elements are present
+        if (Array.isArray(input)) {
+          expect(result.sort()).toEqual(input.sort());
+        } else {
+          expect(result.sort()).toEqual([...input].sort());
+        }
+      },
+    );
+
+    it("should handle empty array", function () {
+      const result = toArray(shuffle([]));
+      expect(result).toEqual([]);
+    });
+
+    it("should handle single element", function () {
+      const result = toArray(shuffle([42]));
+      expect(result).toEqual([42]);
+    });
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res = pipe([1, 2, 3, 4, 5, 6], shuffle, toArray);
+      expect(res).toHaveLength(6);
+      expect(res.sort()).toEqual([1, 2, 3, 4, 5, 6]);
+    });
+
+    it("should produce different results on multiple calls (statistical test)", function () {
+      const input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+      const results = [];
+
+      // Generate multiple shuffles
+      for (let i = 0; i < 50; i++) {
+        results.push(toArray(shuffle(input)));
+      }
+
+      // Check that not all results are identical (very unlikely with proper shuffling)
+      const firstResult = JSON.stringify(results[0]);
+      const allIdentical = results.every(
+        (result) => JSON.stringify(result) === firstResult,
+      );
+      expect(allIdentical).toBe(false);
+    });
+  });
+
+  describe("async", function () {
+    it.each([
+      [[1, 2, 3, 4], 4],
+      ["abcd", 4],
+    ])(
+      "should return the given elements %s in shuffled order with length %s",
+      async function (input, expectedLength) {
+        const result = await toArray(shuffle(toAsync(input as any)));
+        expect(result).toHaveLength(expectedLength);
+
+        // Check all elements are present
+        if (Array.isArray(input)) {
+          expect(result.sort()).toEqual(input.sort());
+        } else {
+          expect(result.sort()).toEqual([...input].sort());
+        }
+      },
+    );
+
+    it("should handle empty async array", async function () {
+      const result = await toArray(shuffle(toAsync([])));
+      expect(result).toEqual([]);
+    });
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res = await pipe([1, 2, 3, 4, 5, 6], toAsync, shuffle, toArray);
+      expect(res).toHaveLength(6);
+      expect(res.sort()).toEqual([1, 2, 3, 4, 5, 6]);
+    });
+
+    it("should be shuffled elements concurrently", async function () {
+      const res = await pipe(
+        toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
+        map((a) => delay(50, a)),
+        filter((a) => a % 2 === 0),
+        shuffle,
+        concurrent(3),
+        toArray,
+      );
+      expect(res).toHaveLength(5);
+      expect(res.sort((a, b) => a - b)).toEqual([2, 4, 6, 8, 10]);
+    }, 450);
+
+    it("should be passed concurrent object when job works concurrently", async function () {
+      const mock = generatorMock();
+      const iter = shuffle(mock);
+      const concurrent = Concurrent.of(2) as any;
+
+      await iter.next(concurrent);
+      expect((mock as any).getConcurrent()).toEqual(concurrent);
+    });
+
+    it("should produce different results on multiple calls (statistical test)", async function () {
+      const input = [1, 2, 3, 4, 5, 6, 7, 8];
+      const results = [];
+
+      // Generate multiple shuffles
+      for (let i = 0; i < 20; i++) {
+        results.push(await toArray(shuffle(toAsync(input))));
+      }
+
+      // Check that not all results are identical (very unlikely with proper shuffling)
+      const firstResult = JSON.stringify(results[0]);
+      const allIdentical = results.every(
+        (result) => JSON.stringify(result) === firstResult,
+      );
+      expect(allIdentical).toBe(false);
+    });
+  });
+});
diff --git a/test/fx-shuffle.spec.ts b/test/fx-shuffle.spec.ts
new file mode 100644
index 00000000..d8876e56
--- /dev/null
+++ b/test/fx-shuffle.spec.ts
@@ -0,0 +1,35 @@
+import { fx, toAsync } from "../src";
+
+describe("fx shuffle method", function () {
+  it("should work with fx method chaining for sync", function () {
+    const input = [1, 2, 3, 4, 5];
+    const result = fx(input).shuffle().toArray();
+
+    expect(result).toHaveLength(input.length);
+    expect(result.sort()).toEqual(input.sort());
+  });
+
+  it("should work with fx method chaining for async", async function () {
+    const input = [1, 2, 3, 4, 5];
+    const result = await fx(toAsync(input)).shuffle().toArray();
+
+    expect(result).toHaveLength(input.length);
+    expect(result.sort()).toEqual(input.sort());
+  });
+
+  it("should produce different results", function () {
+    const input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    const results = [];
+
+    for (let i = 0; i < 20; i++) {
+      results.push(fx(input).shuffle().toArray());
+    }
+
+    // Check that not all results are identical
+    const firstResult = JSON.stringify(results[0]);
+    const allIdentical = results.every(
+      (result) => JSON.stringify(result) === firstResult,
+    );
+    expect(allIdentical).toBe(false);
+  });
+});
diff --git a/test/shuffle.spec.ts b/test/shuffle.spec.ts
new file mode 100644
index 00000000..59bbb0c5
--- /dev/null
+++ b/test/shuffle.spec.ts
@@ -0,0 +1,103 @@
+import { filter, pipe, shuffle, toAsync } from "../src";
+
+describe("shuffle", function () {
+  describe("sync", function () {
+    it("should shuffle array elements", function () {
+      const input = [1, 2, 3, 4, 5];
+      const result = shuffle(input);
+
+      // Check all elements are present
+      expect(result).toHaveLength(input.length);
+      expect(result.sort()).toEqual(input.sort());
+
+      // Check it's a new array
+      expect(result).not.toBe(input);
+    });
+
+    it("should shuffle string elements", function () {
+      const input = "hello";
+      const result = shuffle(input);
+
+      // Check all elements are present
+      expect(result).toHaveLength(input.length);
+      expect(result.sort()).toEqual([...input].sort());
+    });
+
+    it("should handle empty array", function () {
+      const result = shuffle([]);
+      expect(result).toEqual([]);
+    });
+
+    it("should handle single element", function () {
+      const result = shuffle([42]);
+      expect(result).toEqual([42]);
+    });
+
+    it("should be able to be used in the pipeline", function () {
+      const input = [1, 2, 3, 4, 5, 6];
+      const filtered = pipe(
+        input,
+        filter((a) => a % 2 === 0),
+      );
+      const res = shuffle(filtered);
+
+      // Check filtered elements are all present
+      expect(res.sort()).toEqual([2, 4, 6]);
+    });
+
+    it("should produce different results on multiple calls (statistical test)", function () {
+      const input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+      const results = [];
+
+      // Generate multiple shuffles
+      for (let i = 0; i < 100; i++) {
+        results.push(shuffle(input));
+      }
+
+      // Check that not all results are identical (very unlikely with proper shuffling)
+      const firstResult = JSON.stringify(results[0]);
+      const allIdentical = results.every(
+        (result) => JSON.stringify(result) === firstResult,
+      );
+      expect(allIdentical).toBe(false);
+    });
+  });
+
+  describe("async", function () {
+    it("should shuffle async array elements", async function () {
+      const input = [1, 2, 3, 4, 5];
+      const result = await shuffle(toAsync(input));
+
+      // Check all elements are present
+      expect(result).toHaveLength(input.length);
+      expect(result.sort()).toEqual(input.sort());
+    });
+
+    it("should shuffle async string elements", async function () {
+      const input = "hello";
+      const result = await shuffle(toAsync(input));
+
+      // Check all elements are present
+      expect(result).toHaveLength(input.length);
+      expect(result.sort()).toEqual([...input].sort());
+    });
+
+    it("should handle empty async array", async function () {
+      const result = await shuffle(toAsync([]));
+      expect(result).toEqual([]);
+    });
+
+    it("should be able to be used in the pipeline", async function () {
+      const input = [1, 2, 3, 4, 5, 6];
+      const filtered = pipe(
+        input,
+        toAsync,
+        filter((a) => a % 2 === 0),
+      );
+      const res = await shuffle(filtered);
+
+      // Check filtered elements are all present
+      expect(res.sort()).toEqual([2, 4, 6]);
+    });
+  });
+});
diff --git a/type-check/Lazy/shuffle.test.ts b/type-check/Lazy/shuffle.test.ts
new file mode 100644
index 00000000..02bce8c0
--- /dev/null
+++ b/type-check/Lazy/shuffle.test.ts
@@ -0,0 +1,33 @@
+import { pipe, toArray, toAsync } from "../../src";
+import shuffle from "../../src/Lazy/shuffle";
+import * as Test from "../../src/types/Test";
+
+const { checks, check } = Test;
+
+// Test basic shuffle
+const res1 = shuffle([1, 2, 3, 4, 5]);
+const res2 = shuffle("hello");
+const res3 = shuffle(toAsync([1, 2, 3, 4, 5]));
+const res4 = shuffle(toAsync("hello"));
+
+checks([
+  check<typeof res1, IterableIterator<number>, Test.Pass>(),
+  check<typeof res2, IterableIterator<string>, Test.Pass>(),
+  check<typeof res3, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res4, AsyncIterableIterator<string>, Test.Pass>(),
+]);
+
+// Test in pipe with toArray
+const res5 = pipe([1, 2, 3, 4, 5], shuffle, toArray);
+const res6 = pipe("hello", shuffle, toArray);
+const res7 = pipe(toAsync([1, 2, 3, 4, 5]), shuffle, toArray);
+
+checks([
+  check<typeof res5, number[], Test.Pass>(),
+  check<typeof res6, string[], Test.Pass>(),
+  check<typeof res7, Promise<number[]>, Test.Pass>(),
+]);
+
+// Test empty array
+const res8 = shuffle([]);
+checks([check<typeof res8, IterableIterator<never>, Test.Pass>()]);
diff --git a/type-check/shuffle.test.ts b/type-check/shuffle.test.ts
new file mode 100644
index 00000000..a7de9874
--- /dev/null
+++ b/type-check/shuffle.test.ts
@@ -0,0 +1,31 @@
+import { pipe, shuffle, toAsync } from "../src";
+import * as Test from "../src/types/Test";
+
+const { checks, check } = Test;
+
+const res1 = shuffle([1, 2, 3, 4, 5]);
+const res2 = shuffle("hello");
+const res3 = shuffle(toAsync([1, 2, 3, 4, 5]));
+const res4 = shuffle(toAsync("hello"));
+
+checks([
+  check<typeof res1, number[], Test.Pass>(),
+  check<typeof res2, string[], Test.Pass>(),
+  check<typeof res3, Promise<number[]>, Test.Pass>(),
+  check<typeof res4, Promise<string[]>, Test.Pass>(),
+]);
+
+// Test curried version
+const shuffleCurried = shuffle();
+const res5 = shuffleCurried([1, 2, 3]);
+
+checks([check<typeof res5, number[], Test.Pass>()]);
+
+// Test in pipe
+const res8 = pipe([1, 2, 3, 4, 5], shuffle);
+
+checks([check<typeof res8, number[], Test.Pass>()]);
+
+// Test empty array
+const res11 = shuffle([]);
+checks([check<typeof res11, any[], Test.Pass>()]);