-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🚧 progress: Import existing sources from @aureooms/js-itertools.
And make tests pass.
- Loading branch information
1 parent
3e4e69d
commit 46fca74
Showing
9 changed files
with
10,396 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/** | ||
* Algorithm used by {@link product} to compute the product of one or more | ||
* iterables from pools of symbols. | ||
* | ||
* @example | ||
* // Ax Ay Bx By Cx Cy Dx Dy | ||
* _product(['xy', 'ABCD'], 0 , 2) ; | ||
* | ||
* @example | ||
* // 000 001 010 011 100 101 110 111 | ||
* _product([[0,1],[0,1],[0,1]], 0 , 3) ; | ||
* | ||
* @param {any[][]} pools - The pools of symbols in reverse order. | ||
* @param {number} i - Index of the pool to draw the next symbol from | ||
* @param {number} n - Number of pools in total. | ||
* @returns {IterableIterator} | ||
*/ | ||
export default function* _product(pools, i, n) { | ||
if (i === n) { | ||
yield []; | ||
return; | ||
} | ||
|
||
const iterable = pools[i]; | ||
|
||
for (const buffer of _product(pools, i + 1, n)) { | ||
for (const item of iterable) { | ||
buffer.push(item); | ||
|
||
yield buffer; | ||
|
||
buffer.pop(item); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import {deque} from '@aureooms/js-collections-deque'; | ||
|
||
import {iter, _next, count} from '@aureooms/js-itertools'; | ||
|
||
/** | ||
* Computes the product of two iterables in a way that allows for one or both | ||
* of them to be infinite (in constrast with {@link product}). | ||
* Although the output iterator may be infinite, it is guaranteed that each | ||
* value of the product is located at some finite position in the output | ||
* iterator. | ||
* | ||
* If one of the two inputs has finite size N, it is guaranteed that memory | ||
* usage never exceeds O(N) values. If both inputs have infinite size, then | ||
* memory usage grows proportionately to the square root of the number of | ||
* output pairs. | ||
* | ||
* @example | ||
* // returns [ [ 0 , 0 ] , [ 0 , 1 ] , [ 1 , 0 ] , [ 1 , 1 ] ] | ||
* list( diagonal( range( 2 ) , range( 2 ) ) ) ; | ||
* | ||
* @param {Iterable} A - The first iterable. | ||
* @param {Iterable} B - The second iterable. | ||
* @returns {IterableIterator} | ||
* | ||
*/ | ||
export default function* diagonal(A, B) { | ||
const itA = iter(A); | ||
const itB = iter(B); | ||
const _A = deque(); | ||
const _B = deque(); | ||
|
||
for (const n of count()) { | ||
let _a = _next(itA); | ||
let _b = _next(itB); | ||
if (!_a.done) { | ||
// eslint-disable-next-line no-negated-condition | ||
if (!_b.done) { | ||
_A.append(_a.value); | ||
_B.append(_b.value); | ||
for (let i = 0; i <= n; ++i) yield [_A.get(i), _B.get(n - i)]; | ||
} else { | ||
if (_B.length === 0) return; | ||
do { | ||
_A.append(_a.value); | ||
_A.popleft(); | ||
for (let i = 0; i < n; ++i) yield [_A.get(i), _B.get(n - i - 1)]; | ||
_a = _next(itA); | ||
} while (!_a.done); | ||
|
||
break; | ||
} | ||
// eslint-disable-next-line no-negated-condition | ||
} else if (!_b.done) { | ||
if (_A.length === 0) return; | ||
do { | ||
_B.append(_b.value); | ||
_B.popleft(); | ||
for (let i = 0; i < n; ++i) yield [_A.get(i), _B.get(n - i - 1)]; | ||
_b = _next(itB); | ||
} while (!_b.done); | ||
|
||
break; | ||
} else { | ||
break; | ||
} | ||
} | ||
|
||
// Yield lower triangle | ||
|
||
const n = _A.length; | ||
|
||
for (let i = 1; i < n; ++i) { | ||
for (let j = i; j < n; ++j) { | ||
yield [_A.get(j), _B.get(n - j + i - 1)]; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
const answer = 42; | ||
export default answer; | ||
export {default as _product} from './_product.js'; | ||
export {default as diagonal} from './diagonal.js'; | ||
export {default as product} from './product.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {list, ncycle, map, reversed} from '@aureooms/js-itertools'; | ||
|
||
import _product from './_product.js'; | ||
|
||
/** | ||
* Computes the product of the iterables given as first parameter. The second | ||
* parameter is an integer that tells how many times the list of iterables | ||
* given as input should be concatenated to itself before computing the | ||
* product. This second parameter is optional and its default value is | ||
* <code>1</code>. | ||
* | ||
* @example | ||
* // Ax Ay Bx By Cx Cy Dx Dy | ||
* product(['ABCD', 'xy']) ; | ||
* | ||
* @example | ||
* // 000 001 010 011 100 101 110 111 | ||
* product([range(2)], 3) ; | ||
* | ||
* @param {Iterable} iterables - The input iterables. | ||
* @param {Number} repeat - The number of times to cycle through the input iterables. | ||
* @return {Iterator} | ||
*/ | ||
|
||
export default function product(iterables, repeat = 1) { | ||
const pools = list(ncycle(reversed(map(list, iterables)), repeat)); | ||
|
||
return map(list, _product(pools, 0, pools.length)); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
import test from 'ava'; | ||
|
||
import {lexicographical, increasing} from '@aureooms/js-compare'; | ||
|
||
import { | ||
list, | ||
diagonal, | ||
range, | ||
count, | ||
take, | ||
sorted, | ||
} from '@aureooms/js-itertools'; | ||
|
||
import {product} from '../../src/index.js'; | ||
|
||
test('diagonal docstring example', (t) => { | ||
const result = list(diagonal(range(2), range(2))); | ||
const expected = [ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[1, 1], | ||
]; | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal finite square', (t) => { | ||
const result = list(diagonal(range(3), range(3))); | ||
const expected = [ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[0, 2], | ||
[1, 1], | ||
[2, 0], | ||
[1, 2], | ||
[2, 1], | ||
[2, 2], | ||
]; | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal finite rectangle (vertical)', (t) => { | ||
const result = list(diagonal(range(4), range(3))); | ||
const expected = [ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[0, 2], | ||
[1, 1], | ||
[2, 0], | ||
[1, 2], | ||
[2, 1], | ||
[3, 0], | ||
[2, 2], | ||
[3, 1], | ||
[3, 2], | ||
]; | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal finite rectangle (horizontal)', (t) => { | ||
const result = list(diagonal(range(3), range(4))); | ||
const expected = [ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[0, 2], | ||
[1, 1], | ||
[2, 0], | ||
[0, 3], | ||
[1, 2], | ||
[2, 1], | ||
[1, 3], | ||
[2, 2], | ||
[2, 3], | ||
]; | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal large but finite', (t) => { | ||
const N = 10; | ||
const M = 10000; | ||
const result = list(take(diagonal(range(M), range(M)), N)); | ||
const expected = [ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[0, 2], | ||
[1, 1], | ||
[2, 0], | ||
[0, 3], | ||
[1, 2], | ||
[2, 1], | ||
[3, 0], | ||
]; | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal infinite', (t) => { | ||
const N = 10; | ||
const result = list(take(diagonal(count(), count()), N)); | ||
const expected = [ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[0, 2], | ||
[1, 1], | ||
[2, 0], | ||
[0, 3], | ||
[1, 2], | ||
[2, 1], | ||
[3, 0], | ||
]; | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal empty x empty', (t) => { | ||
const result = list(diagonal(range(0), range(0))); | ||
const expected = []; | ||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal empty x finite', (t) => { | ||
const result = list(diagonal(range(0), range(100))); | ||
const expected = []; | ||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal finite x empty', (t) => { | ||
const result = list(diagonal(range(100), range(0))); | ||
const expected = []; | ||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal empty x infinite', (t) => { | ||
const result = list(diagonal(range(0), count())); | ||
const expected = []; | ||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal infinite x empty', (t) => { | ||
const result = list(diagonal(count(), range(0))); | ||
const expected = []; | ||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal finite x infinite', (t) => { | ||
const N = 13; | ||
const result = list(take(diagonal(range(3), count()), N)); | ||
const expected = [ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[0, 2], | ||
[1, 1], | ||
[2, 0], | ||
[0, 3], | ||
[1, 2], | ||
[2, 1], | ||
[0, 4], | ||
[1, 3], | ||
[2, 2], | ||
[0, 5], | ||
]; | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal infinite x finite', (t) => { | ||
const N = 13; | ||
const result = list(take(diagonal(count(), range(3)), N)); | ||
const expected = [ | ||
[0, 0], | ||
[0, 1], | ||
[1, 0], | ||
[0, 2], | ||
[1, 1], | ||
[2, 0], | ||
[1, 2], | ||
[2, 1], | ||
[3, 0], | ||
[2, 2], | ||
[3, 1], | ||
[4, 0], | ||
[3, 2], | ||
]; | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
const compare = lexicographical(increasing); | ||
const set = (x) => sorted(compare, x); | ||
|
||
test('diagonal compared to product (vertical)', (t) => { | ||
const M = 100; | ||
const N = 25; | ||
|
||
const expected = list(product([range(M), range(N)])); | ||
const result = set(diagonal(range(M), range(N))); | ||
|
||
t.deepEqual(expected, result); | ||
}); | ||
|
||
test('diagonal compared to product (horizontal)', (t) => { | ||
const M = 25; | ||
const N = 100; | ||
|
||
const expected = list(product([range(M), range(N)])); | ||
const result = set(diagonal(range(M), range(N))); | ||
|
||
t.deepEqual(expected, result); | ||
}); |
Oops, something went wrong.