From 4e5dfd7134dd6a314c58580ecd1ea898f9ddd205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20BECHER?= Date: Sun, 19 Oct 2025 14:41:58 +0200 Subject: [PATCH] add cartesian product --- src/async/impl.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++ src/async/types.ts | 1 + src/sync/impl.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++++ src/sync/types.ts | 1 + 4 files changed, 98 insertions(+) diff --git a/src/async/impl.ts b/src/async/impl.ts index ee85c95..965d078 100644 --- a/src/async/impl.ts +++ b/src/async/impl.ts @@ -664,6 +664,10 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im return new ZippedAsyncSequence(this, wrap(sequence)); } + cartesianProduct(sequence: MaybeAsyncIterable): AsyncSequence<[TElement, TOther]> { + return new CartesianProductAsyncSequence(this, wrap(sequence)); + } + indexed(): AsyncSequence<[number, TElement]> { return new IndexedAsyncSequence(this); } @@ -1022,6 +1026,10 @@ export class DelegatedAsyncSequence extends AsyncSequenceMarker implem return this.#sequence.zip(wrap(sequence)); } + cartesianProduct(sequence: MaybeAsyncIterable): AsyncSequence<[TElement, TOther]> { + return this.#sequence.cartesianProduct(wrap(sequence)); + } + indexed(): AsyncSequence<[number, TElement]> { return this.#sequence.indexed(); } @@ -1821,6 +1829,46 @@ class ZippedAsyncSequence extends BaseAsyncSequence<[T, U]> { } } +export class CartesianProductAsyncSequence extends BaseAsyncSequence<[T, U]> { + readonly #first: AsyncSequence; + readonly #second: AsyncSequence; + + constructor(first: AsyncSequence, second: AsyncSequence) { + super(); + + this.#first = first; + this.#second = second; + } + + override async nonEnumeratedCount() { + const n1 = await this.#first.nonEnumeratedCount(); + + if (n1 < 0) { + return -1; + } + + const n2 = await this.#second.nonEnumeratedCount(); + + if (n2 < 0) { + return -1; + } + + return n1 * n2; + } + + override async maxCount() { + return await this.#first.maxCount() * await this.#second.maxCount(); + } + + override async *iterator() { + for await (const firstObj of this.#first) { + for await (const secondObj of this.#second) { + yield [firstObj, secondObj] as [T, U]; + } + } + } +} + class UnionAsyncSequence extends BaseAsyncSequence { readonly #first: AsyncSequence; readonly #second: AsyncSequence; diff --git a/src/async/types.ts b/src/async/types.ts index 767ffd9..f1b194a 100644 --- a/src/async/types.ts +++ b/src/async/types.ts @@ -108,6 +108,7 @@ export interface AsyncSequence extends AsyncIterable { forEach(action: MaybeAsyncAction): Promise; zip(sequence: MaybeAsyncIterable): AsyncSequence<[TElement, TOther]>; + cartesianProduct(sequence: MaybeAsyncIterable): AsyncSequence<[TElement, TOther]>; indexed(): AsyncSequence<[number, TElement]>; diff --git a/src/sync/impl.ts b/src/sync/impl.ts index e706638..2b1e3b0 100644 --- a/src/sync/impl.ts +++ b/src/sync/impl.ts @@ -673,6 +673,10 @@ export abstract class BaseSequence extends SequenceMarker implements S return new ZippedSequence(this, wrap(sequence)); } + cartesianProduct(sequence: Iterable): Sequence<[TElement, TOther]> { + return new CartesianProductSequence(this, wrap(sequence)); + } + indexed(): Sequence<[number, TElement]> { return new IndexedSequence(this); } @@ -1039,6 +1043,10 @@ export class DelegatedSequence extends SequenceMarker implements Seque return this.#sequence.zip(sequence); } + cartesianProduct(sequence: Iterable) { + return this.#sequence.cartesianProduct(sequence); + } + indexed() { return this.#sequence.indexed(); } @@ -2094,6 +2102,46 @@ export class ZippedSequence extends BaseSequence<[T, U]> { } } +export class CartesianProductSequence extends BaseSequence<[T, U]> { + readonly #first: Sequence; + readonly #second: Sequence; + + constructor(first: Sequence, second: Sequence) { + super(); + + this.#first = first; + this.#second = second; + } + + override nonEnumeratedCount() { + const n1 = this.#first.nonEnumeratedCount(); + + if (n1 < 0) { + return -1; + } + + const n2 = this.#second.nonEnumeratedCount(); + + if (n2 < 0) { + return -1; + } + + return n1 * n2; + } + + override maxCount() { + return this.#first.maxCount() * this.#second.maxCount(); + } + + override *iterator() { + for (const firstObj of this.#first) { + for (const secondObj of this.#second) { + yield [firstObj, secondObj] as [T, U]; + } + } + } +} + class UnionSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; diff --git a/src/sync/types.ts b/src/sync/types.ts index b27bb0b..fc2cb02 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -111,6 +111,7 @@ export interface Sequence extends Iterable { forEach(action: Action): void; zip(sequence: Iterable): Sequence<[TElement, TOther]>; + cartesianProduct(sequence: Iterable): Sequence<[TElement, TOther]>; indexed(): Sequence<[number, TElement]>;