Home Manual Reference Source

src/macro.js

import {copy} from '@array-like/copy';
import {arrayValue, entropy} from './fixtures.js';
import {increasing, decreasing} from './defaults.js';

const macro = (
	t,
	{search: _search, array, length, delta: _delta, min, max, seed, pos, found},
) => {
	const {randint} = entropy(seed);
	const {fn: search} = unpackFunction(_search);
	const {fn: delta} = unpackFunction(_delta);

	// SETUP REF ARRAY
	const _max = arrayValue(array, max + 1);
	const _min = arrayValue(array, min - 1);
	// eslint-disable-next-line new-cap
	const ref = new array(length);
	for (let j = 0; j < length; ++j)
		ref[j] = arrayValue(array, randint(min, max + 1));
	Array.prototype.sort.call(ref, delta);

	// SETUP TEST ARRAY
	// eslint-disable-next-line new-cap
	const a = new array(length);
	copy(ref, 0, length, a, 0);

	// TEST SEARCH
	if (length > 0) {
		// CHECK > OUTER BOUND
		let v = delta(-1, 0) < 0 ? _max : _min;
		let r = search(delta, a, 0, length, v);
		t.false(found(r), `not found ${v}`);
		t.is(pos(r), length, `where === ${length}`);

		// CHECK BODY
		for (let i = 0; i < length; ++i) {
			r = search(delta, a, 0, length, a[i]);
			t.true(found(r), `found  a[${i}]`);
			t.deepEqual(a[pos(r)], a[i], `val  === ${a[i]}`);
		}

		// CHECK INNER MISS
		for (let i = 1; i < length; ++i) {
			const values = new Set([
				arrayValue(array, Number(a[i - 1]) + (delta(-1, 0) < 0 ? 1 : -1)),
				arrayValue(array, (Number(a[i - 1]) + Number(a[i])) / 2),
				arrayValue(array, Number(a[i]) + (delta(-1, 0) < 0 ? -1 : 1)),
			]);
			for (const v of values) {
				if (delta(v, a[i - 1]) <= 0) continue;
				if (delta(v, a[i]) >= 0) continue;
				const r = search(delta, a, 0, length, v);
				t.false(found(r), `not found ${v}`);
				t.deepEqual(pos(r), i, `pos === ${i}`);
			}
		}

		// CHECK < OUTER BOUND
		v = delta(-1, 0) > 0 ? _max : _min;
		r = search(delta, a, 0, length, v);
		t.false(found(r), `not found ${v}`);
		t.is(pos(r), 0, 'where === 0');
	} else {
		const r = search(delta, a, 0, length, _min);
		t.false(found(r), `not found ${_min}`);
		t.is(pos(r), 0, 'where === 0');
	}

	// CHECK NOT MODIFIED
	t.deepEqual(a, ref, 'not modified check');
};

const deltaName = ({name, fn}) => {
	switch (fn) {
		case increasing:
			return 'increasing';
		case decreasing:
			return 'decreasing';
		default:
			return name || '<unknown delta function>';
	}
};

const unpackFunction = (object) => {
	if (typeof object === 'function') {
		return {
			name: object.name,
			fn: object,
		};
	}

	return object;
};

macro.title = (title, {search, array, length, delta, seed, min, max}) => {
	if (title !== undefined) return title;
	const {name: searchName} = unpackFunction(search);
	const deltaUnpacked = unpackFunction(delta);
	return `${searchName || '<unknown search function>'} (new ${
		array.name
	}(${length}){${min},${max}}${JSON.stringify(seed)}, ${deltaName(
		deltaUnpacked,
	)})`;
};

export default macro;