Home Manual Reference Source

src/recurse.js

import assert from 'assert';

import {ValueError} from '@failure-abstraction/error';

import twoWayAlloc from './twoWayAlloc.js';
import twoWayScan from './twoWayScan.js';

import RecurseDeeper from './RecurseDeeper.js';
import StackEntry from './StackEntry.js';

/**
 * Recurse.
 *
 * @param {number} MAX
 * @param {Function} eq
 * @param {number} li
 * @param {number} lj
 * @param {number} ri
 * @param {number} rj
 * @return {IterableIterator}
 */
export default function recurse(MAX, eq, li, lj, ri, rj) {
	assert(MAX >= 0);
	assert(lj - li + rj - ri > MAX);
	if (li === lj || ri === rj) {
		assert(lj - li > MAX || rj - ri > MAX);
		throw new ValueError();
	}

	assert(li < lj);
	assert(ri < rj);
	assert(!eq(li, ri));
	assert(!eq(lj - 1, rj - 1));

	const {V, centerF, centerB} = twoWayAlloc(MAX, li, lj, ri, rj);

	const split = twoWayScan(MAX, V, centerF, centerB, eq, li, lj, ri, rj);

	const k = split.k;
	const xBegin = split.xBegin;
	const xEnd = split.xEnd;
	const distanceLeft = split.distanceLeft;
	const distance = split.distance;
	const distanceRight = (distance - distanceLeft) | 0;

	console.debug({k, xBegin, xEnd, distance});

	if (distance === -1) throw new ValueError();

	assert(distance > 0);
	const maxDistance =
		(((lj - li) | 0) + ((((rj - ri) | 0) - ((xEnd - xBegin) << 1)) | 0)) | 0;
	if (distance === maxDistance) {
		// Early exit when there is no match in the recursive calls
		assert(xBegin < xEnd);
		return [
			[li, xBegin, ri, (xBegin - k) | 0],
			[xEnd, lj, (xEnd - k) | 0, rj],
		][Symbol.iterator]();
	}

	assert(distance < maxDistance);
	return new RecurseDeeper(
		V,
		[
			new StackEntry(distanceRight, xEnd, lj, (xEnd - k) | 0, rj),
			new StackEntry(distanceLeft, li, xBegin, ri, (xBegin - k) | 0),
		],
		eq,
	);
}