Home Manual Reference Source

src/rabinKarp.js

import assert from 'assert';
import {startsWith} from '@string-searching/brute-force';

/**
 * Note that q should be random and about m (that is, log m bits) for best
 * performance.
 * Make sure d * (q-1) <= Number.MAX_SAFE_INTEGER
 * and q + (q-1) <= Number.MAX_SAFE_INTEGER. Otherwise use bigints.
 *
 * @param {Function} code Returns a value between 0 and d-1.
 * @param {number} d |∑|
 * @param {number} q is the prime number to use to lessen spurious hits
 * @param {number} [one=1] The value for one. Given 1n if using bigints.
 * @return {Function}
 */
const rabinKarp = (code, d, q, one = 1) => {
	assert(typeof d !== 'number' || q - 1 <= Number.MAX_SAFE_INTEGER / d);
	assert(typeof q !== 'number' || q - 1 / 2 <= Number.MAX_SAFE_INTEGER / 2);
	/**
	 * FindAll.
	 *
	 * @param {string} s
	 * @param {number} si
	 * @param {number} sj
	 * @param {string} p
	 * @param {number} pi
	 * @param {number} pj
	 */
	const findAll = function* (s, si, sj, p, pi, pj) {
		const n = sj - si;
		const m = pj - pi;

		if (n < m) return;

		let sh = code(s[si]) % q;
		let ph = code(p[pi]) % q;
		let of = one;

		for (let i = 1; i < m; ++i) {
			sh *= d;
			sh %= q;
			sh += code(s[si + i]) % q;
			sh %= q;
			ph *= d;
			ph %= q;
			ph += code(p[pi + i]) % q;
			ph %= q;
			of *= d;
			of %= q;
		}

		const j = sj - m;
		let i = si;
		for (; i < j; ++i) {
			if (sh === ph && startsWith(p, pi, pj, s, i)) yield i;

			sh -= ((code(s[i]) % q) * of) % q;
			sh += q;
			sh %= q;
			sh *= d;
			sh %= q;
			sh += code(s[i + m]) % q;
			sh %= q;
		}

		if (sh === ph && startsWith(p, pi, pj, s, i)) yield i;
	};

	return findAll;
};

export default rabinKarp;