Skip to content

Commit 8057d4d

Browse files
🚧 progress: First draft.
1 parent e6a20a5 commit 8057d4d

File tree

8 files changed

+10197
-27
lines changed

8 files changed

+10197
-27
lines changed

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
String searching failure function for JavaScript.
55
See [docs](https://string-data-structure.github.io/failure-function/index.html).
66

7-
> :building_construction: Caveat emptor! This is work in progress. Code may be
8-
> working. Documentation may be present. Coherence may be. Maybe.
9-
10-
> :warning: Depending on your environment, the code may require
11-
> `regeneratorRuntime` to be defined, for instance by importing
12-
> [regenerator-runtime/runtime](https://www.npmjs.com/package/regenerator-runtime).
7+
```js
8+
import {build} from '@string-data-structure/failure-function';
9+
const s = 'abracadabra';
10+
const t = new Int32Array(s.length + 1);
11+
build(s, 0, s.length, t, 0);
12+
```
1313

1414
[![License](https://img.shields.io/github/license/string-data-structure/failure-function.svg)](https://raw.githubusercontent.com/string-data-structure/failure-function/main/LICENSE)
1515
[![Version](https://img.shields.io/npm/v/@string-data-structure/failure-function.svg)](https://www.npmjs.org/package/@string-data-structure/failure-function)

doc/manual/usage.md

+3-14
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,8 @@
11
# Usage
22

3-
> :warning: Depending on your environment, the code may require
4-
> `regeneratorRuntime` to be defined, for instance by importing
5-
> [regenerator-runtime/runtime](https://www.npmjs.com/package/regenerator-runtime).
6-
7-
First, require the polyfill at the entry point of your application
8-
```js
9-
require( 'regenerator-runtime/runtime' ) ;
10-
// or
11-
import 'regenerator-runtime/runtime.js' ;
12-
```
13-
14-
Then, import the library where needed
3+
Import the library where needed
154
```js
16-
const failureFunction = require( '@string-data-structure/failure-function' ) ;
5+
const {build} = require( '@string-data-structure/failure-function' ) ;
176
// or
18-
import * as failureFunction from '@string-data-structure/failure-function' ;
7+
import {build} from '@string-data-structure/failure-function' ;
198
```

src/build.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Computes the failure function for input string.
3+
*
4+
* This is the "next[j]" table found in
5+
* "Fast pattern matching in strings" by Knuth, Morris, and Pratt,
6+
* although here indices are 0-based hence all indices and inputs are one less
7+
* than in that paper.
8+
*
9+
* NOTE The main loop is somewhat unrolled for faster execution. This was not
10+
* benchmarked.
11+
*
12+
* @param {ArrayLike} p
13+
* @param {number} pi
14+
* @param {number} pj
15+
* @param {number[]} t
16+
* @param {number} ti
17+
*/
18+
const build = (p, pi, pj, t, ti) => {
19+
t[ti] = -1;
20+
if (pi === pj) return;
21+
const p0 = pi;
22+
if (++pi === pj) {
23+
t[++ti] = 0;
24+
return;
25+
}
26+
27+
const t0 = ti;
28+
t[++ti] = p[pi] === p[p0] ? -1 : 0;
29+
let m = 0;
30+
--pj;
31+
while (pi < pj) {
32+
while (p[pi] !== p[p0 + m]) {
33+
m = t[t0 + m];
34+
if (m === -1) break;
35+
}
36+
37+
++m;
38+
t[++ti] = p[++pi] === p[p0 + m] ? t[t0 + m] : m;
39+
}
40+
41+
while (p[pj] !== p[p0 + m]) {
42+
m = t[t0 + m];
43+
if (m === -1) break;
44+
}
45+
46+
++m;
47+
t[++ti] = m;
48+
};
49+
50+
export default build;

src/index.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
const answer = 42;
2-
export default answer;
1+
export {default as build} from './build.js';

test/src/_fixtures.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {build} from '../../src/index.js';
2+
3+
const _table = (p, pi, pj) => {
4+
// eslint-disable-next-line unicorn/no-new-array
5+
const next = new Array(pj - pi + 1);
6+
build(p, pi, pj, next, 0);
7+
return next;
8+
};
9+
10+
export const table = (input) => _table(input, 0, input.length);

test/src/api.js

-5
This file was deleted.

test/src/next.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import test from 'ava';
2+
3+
import {table} from './_fixtures.js';
4+
5+
const isNext = (t, table, input) => {
6+
if (input === '') {
7+
t.deepEqual(table, [-1]);
8+
return;
9+
}
10+
11+
// TODO this test is not complete
12+
13+
t.is(table[0], -1);
14+
t.not(table[input.length], -1);
15+
for (let j = 1; j <= input.length; ++j) {
16+
const i = table[j];
17+
// TODO test when i === -1
18+
if (i !== -1) {
19+
// Prefix and suffix match
20+
t.true(input.slice(0, i) === input.slice(j - i, j));
21+
// Next character does not match
22+
t.true(j === input.length || input[i] !== input[j]);
23+
// A longer proper prefix/suffix pair does not match
24+
// TODO More possibilities need to be checked
25+
t.true(
26+
i + 1 === j ||
27+
input.slice(0, i + 1) !== input.slice(j - (i + 1), j) ||
28+
i + 2 === j ||
29+
j === input.length ||
30+
input[i + 1] === input[j],
31+
);
32+
}
33+
}
34+
};
35+
36+
const macro = (t, input, expected) => {
37+
const next = table(input);
38+
t.deepEqual(next, expected);
39+
isNext(t, next, input);
40+
};
41+
42+
macro.title = (title, input, expected) =>
43+
title ?? `next(${input}) is ${JSON.stringify(expected)}`;
44+
45+
const auto = (t, input) => {
46+
const next = table(input);
47+
isNext(t, next, input);
48+
};
49+
50+
auto.title = (title, input) => title ?? `isNext(next(${input}))`;
51+
52+
test(macro, '', [-1]);
53+
test(macro, 'z', [-1, 0]);
54+
test(macro, 'abcd', [-1, 0, 0, 0, 0]);
55+
test(macro, 'aaaa', [-1, -1, -1, -1, 3]);
56+
test(macro, 'axax', [-1, 0, -1, 0, 2]);
57+
test(macro, 'axxa', [-1, 0, 0, -1, 1]);
58+
test(macro, 'aaaab', [-1, -1, -1, -1, 3, 0]);
59+
test(macro, 'abracadabra', [-1, 0, 0, -1, 1, -1, 1, -1, 0, 0, -1, 4]);
60+
test(
61+
macro,
62+
'abaababaabaababaababa',
63+
[-1, 0, -1, 1, 0, -1, 3, -1, 1, 0, -1, 6, 0, -1, 3, -1, 1, 0, -1, 11, -1, 8],
64+
);
65+
66+
test(auto, 'eifoiwhfeldkasjflkdshfldshflkkdadkkkkkkkkkkkkasjfdljfdleifo');
67+
test(auto, 'aaaaaaaaaaaabbbbbbbbbbaaaaaaaaabbbbbbbb');

0 commit comments

Comments
 (0)