Skip to content

Commit 1062c0b

Browse files
🚧 progress: First draft.
1 parent f3c5182 commit 1062c0b

File tree

12 files changed

+9804
-9
lines changed

12 files changed

+9804
-9
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,26 @@ See [docs](https://string-data-structure.github.io/fibonacci-string/index.html).
1111
> `regeneratorRuntime` to be defined, for instance by importing
1212
> [regenerator-runtime/runtime](https://www.npmjs.com/package/regenerator-runtime).
1313
14+
```js
15+
import {build} from '@string-data-structure/fibonacci-string';
16+
17+
const f = build({iadd: (x, y) => x+y, zero: () => 1, one: () => 1});
18+
19+
f(0); // 1
20+
f(1); // 0
21+
f(2); // 1
22+
f(3); // 1
23+
f(4); // 0
24+
f(5); // 1
25+
f(6); // 0
26+
f(7); // 1
27+
f(8); // 1
28+
f(9); // 0
29+
30+
const fn = build({iadd: (x, y) => x+y, zero: () => 1n, one: () => 1n});
31+
f(4802349082340928340983n); // 0
32+
```
33+
1434
[![License](https://img.shields.io/github/license/string-data-structure/fibonacci-string.svg)](https://raw.githubusercontent.com/string-data-structure/fibonacci-string/main/LICENSE)
1535
[![Version](https://img.shields.io/npm/v/@string-data-structure/fibonacci-string.svg)](https://www.npmjs.org/package/@string-data-structure/fibonacci-string)
1636
[![Tests](https://img.shields.io/github/workflow/status/string-data-structure/fibonacci-string/ci:test?event=push&label=tests)](https://github.com/string-data-structure/fibonacci-string/actions/workflows/ci:test.yml?query=branch:main)

doc/scripts/header.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ domReady(() => {
1717
header.insertBefore(projectname, header.firstChild);
1818

1919
const testlink = document.querySelector('header > a[data-ice="testLink"]');
20-
testlink.href = 'https://app.codecov.io/gh/string-data-structure/fibonacci-string';
20+
testlink.href =
21+
'https://app.codecov.io/gh/string-data-structure/fibonacci-string';
2122
testlink.target = '_BLANK';
2223

2324
const searchBox = document.querySelector('.search-box');

package.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,20 @@
6464
"release": "np --message ':hatching_chick: release: Bumping to v%s.'",
6565
"test": "ava"
6666
},
67-
"dependencies": {},
67+
"dependencies": {
68+
"@functional-abstraction/predicate": "^3.0.2",
69+
"@iterable-iterator/slice": "^0.0.1"
70+
},
6871
"devDependencies": {
6972
"@babel/core": "7.14.3",
7073
"@babel/preset-env": "7.14.2",
7174
"@babel/register": "7.13.16",
7275
"@commitlint/cli": "12.1.4",
76+
"@iterable-iterator/list": "^0.0.2",
77+
"@iterable-iterator/map": "^0.1.0",
78+
"@iterable-iterator/range": "^1.0.0",
79+
"@iterable-iterator/select": "^0.0.1",
80+
"@iterable-iterator/zip": "^0.0.2",
7381
"@js-library/commitlint-config": "0.0.4",
7482
"ava": "3.15.0",
7583
"babel-plugin-transform-remove-console": "6.9.4",
@@ -202,6 +210,7 @@
202210
"unicorn"
203211
],
204212
"rules": {
213+
"camelcase": "off",
205214
"unicorn/filename-case": [
206215
"error",
207216
{

src/build.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {takewhile} from '@iterable-iterator/slice';
2+
import {le} from '@functional-abstraction/predicate';
3+
4+
import gen from './gen.js';
5+
6+
/**
7+
* Build.
8+
*
9+
* @param {Object}
10+
* @param {number|bigint} n
11+
* @return {number[]|bigint[]}
12+
*/
13+
const build = ({zero, one, iadd}, n) => {
14+
const g = gen(iadd, zero(), one());
15+
return Array.from(takewhile(le(n), g));
16+
};
17+
18+
export default build;

src/gen.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Gen.
3+
*
4+
* @param {Function} iadd
5+
* @param {number|bigint} a
6+
* @param {number|bigint} b
7+
* @return {IterableIterator<number>|IterableIterator<bigint>}
8+
*/
9+
const gen = function* (iadd, a, b) {
10+
yield a;
11+
yield b;
12+
while (true) {
13+
yield (a = iadd(b, a));
14+
yield (b = iadd(a, b));
15+
}
16+
};
17+
18+
export default gen;

src/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
const answer = 42;
2-
export default answer;
1+
export {default as build} from './build.js';
2+
export {default as gen} from './gen.js';
3+
export {default as query} from './query.js';

src/query.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import assert from 'node:assert';
2+
import build from './build.js';
3+
4+
/**
5+
* Query ϕ[i].
6+
*
7+
* @param {Object} type
8+
* @param {number[]|bigint[]} array
9+
* @param {number|bigint} i
10+
* @return {number|bigint}
11+
*/
12+
const query = (type, array, i) => {
13+
assert(typeof i === 'bigint' || (Number.isInteger(i) && i >= 0));
14+
assert(typeof i !== 'bigint' || i >= 0n);
15+
assert(array || type);
16+
const F = array ?? build(type, i);
17+
let n_1 = F.length - 1;
18+
const zero = n_1 === -1 ? i : F[0] - F[0];
19+
assert(array || i === zero || i >= F[n_1]);
20+
// eslint-disable-next-line no-constant-condition
21+
while (true) {
22+
assert(i >= zero);
23+
assert(array || n_1 + 1 === F.length);
24+
if (n_1 <= 0) return i;
25+
assert(n_1 + 1 >= 2);
26+
assert(i < F[n_1] + F[n_1 - 1]); // Equivalent to i < F[n] = F[n_1 + 1]
27+
if (i >= F[n_1]) {
28+
i -= F[n_1];
29+
--n_1;
30+
if (F !== array) F.pop();
31+
}
32+
33+
if (F !== array) F.pop();
34+
--n_1;
35+
}
36+
};
37+
38+
export default query;

test/src/_fixtures.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import assert from 'node:assert';
2+
3+
import {nth} from '@iterable-iterator/select';
4+
import {list} from '@iterable-iterator/list';
5+
import {map} from '@iterable-iterator/map';
6+
import {range} from '@iterable-iterator/range';
7+
import {take} from '@iterable-iterator/slice';
8+
9+
import {query, gen} from '../../src/index.js';
10+
11+
// eslint-disable-next-line no-return-assign
12+
export const iadd = (a, b) => (a += b);
13+
export const zero = () => 1;
14+
export const one = () => 2;
15+
export const type = {zero, one, iadd};
16+
17+
export const zeron = () => 1n;
18+
export const onen = () => 2n;
19+
export const typen = {zero: zeron, one: onen, iadd};
20+
21+
export const repr = (x) => {
22+
const s = typeof x === 'bigint' ? `${x}n` : JSON.stringify(x);
23+
if (s.length <= 40) return s;
24+
return s.slice(0, 19) + '..' + s.slice(-19);
25+
};
26+
27+
const concat = (a, b) => a.concat(b);
28+
export const string = (x, y) => (n) => nth(gen(concat, y, x), n - 1);
29+
30+
export const gs = function* ({a, b}, n) {
31+
if (n === 0) return;
32+
if (n === 1) {
33+
for (let i = 0; i < b.length; ++i) {
34+
yield a.length + i;
35+
}
36+
37+
return;
38+
}
39+
40+
const F = Array.from(take(gen(iadd, a.length, a.length + b.length), n - 1));
41+
const Fn = F.pop();
42+
for (const i of range(Fn)) yield query(undefined, F, i);
43+
};
44+
45+
export const makeSymbol = ({a, b}) => {
46+
const m = a.length;
47+
const n = b.length;
48+
const number = (i) => {
49+
assert(i >= 0);
50+
assert(i <= m + n);
51+
return i < m ? a[i] : b[i - m];
52+
};
53+
54+
const mn = BigInt(m);
55+
const nn = BigInt(n);
56+
const bigint = (_in) => {
57+
assert(_in >= 0n);
58+
assert(_in <= mn + nn);
59+
const i = Number(_in);
60+
return i < m ? a[i] : b[i - m];
61+
};
62+
63+
return (x) => (typeof x === 'bigint' ? bigint(x) : number(x));
64+
};
65+
66+
export const fs = (alphabet, n) =>
67+
list(map(makeSymbol(alphabet), gs(alphabet, n))).join('');

test/src/api.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

test/src/query.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import test from 'ava';
2+
3+
import {enumerate} from '@iterable-iterator/zip';
4+
5+
import {query} from '../../src/index.js';
6+
7+
import {makeSymbol, type, typen, repr} from './_fixtures.js';
8+
9+
const symbol = makeSymbol({a: 'a', b: 'b'});
10+
11+
const macro = (t, type, i, expected) => {
12+
t.is(symbol(query(type, undefined, i)), expected);
13+
};
14+
15+
macro.title = (title, _, i, expected) =>
16+
title ?? `query(${repr(i)}) is ${repr(expected)}`;
17+
18+
const phi10 = 'abaababaabaababaababaabaababaabaababaababaabaababaababa';
19+
const input = phi10;
20+
21+
for (const [i, x] of enumerate(input)) {
22+
test(macro, type, i, x);
23+
test(macro, typen, BigInt(i), x);
24+
}

test/src/str.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import test from 'ava';
2+
3+
import {fs, string, repr} from './_fixtures.js';
4+
5+
const macro = (t, alphabet, n, expected) => {
6+
t.is(fs(alphabet, n), expected);
7+
};
8+
9+
macro.title = (title, alphabet, n, expected) =>
10+
title ?? `[${repr(alphabet)}] fs(${n}) is ${repr(expected)}`;
11+
12+
const expected = ({a, b}, n) => string(a, b)(n);
13+
14+
const auto = (t, alphabet, n) => {
15+
return macro(t, alphabet, n, expected(alphabet, n));
16+
};
17+
18+
auto.title = (title, alphabet, n) =>
19+
macro.title(title, alphabet, n, expected(alphabet, n));
20+
21+
const ab = {a: 'a', b: 'b'};
22+
23+
const alphabets = [
24+
ab,
25+
{a: 'x', b: 'y'},
26+
{a: 'ii', b: 'oo'},
27+
{a: 'abra', b: 'cadabra'},
28+
];
29+
30+
test(macro, ab, 0, '');
31+
test(macro, ab, 1, 'b');
32+
test(macro, ab, 2, 'a');
33+
test(macro, ab, 3, 'ab');
34+
test(macro, ab, 4, 'aba');
35+
test(macro, ab, 5, 'abaab');
36+
test(macro, ab, 6, 'abaababa');
37+
test(macro, ab, 7, 'abaababaabaab');
38+
test(macro, ab, 8, 'abaababaabaababaababa');
39+
test(macro, ab, 9, 'abaababaabaababaababaabaababaabaab');
40+
test(macro, ab, 10, 'abaababaabaababaababaabaababaabaababaababaabaababaababa');
41+
42+
for (const alphabet of alphabets) {
43+
test(auto, alphabet, 15);
44+
test(auto, alphabet, 20);
45+
}

0 commit comments

Comments
 (0)