I need your help to see if the following binary search code is correct. I did my best to cover the corner cases. I wonder if I missed anything.
The code as it is with tests (you can play with it online here):
function binarySearch<a, r>(
hay: a[],
needle: a,
compare:(one: a, another: a, oneGreaterThanAnother: () => r, oneLessThanAnother: () => r, oneEqualToAnother: () => r) => r,
haveStaightHit: (hay: a[], index: number) => r,
haveExpectedToBeAt: (hay: a[], index: number) => r
) {
if (hay.length > 0) {
return binarySearchUnsafe(hay, needle, 0, hay.length - 1, compare, haveStaightHit, haveExpectedToBeAt);
} else {
return haveExpectedToBeAt(hay, 0);
}
}
function binarySearchUnsafe<a, r>(
hay: a[],
needle: a,
from: number,
to: number,
compare:(one: a, another: a, oneGreaterThanAnother: () => r, oneLessThanAnother: () => r, oneEqualToAnother: () => r) => r,
haveStaightHit: (hay: a[], index: number) => r,
haveExpectedToBeAt: (hay: a[], index: number) => r
): r {
if (from < to) {
var at = from + (~~((to - from) / 2));
return compare(
needle,
hay[at],
function needleIsOnTheRight() {
return binarySearchUnsafe(hay, needle, at + 1, to, compare, haveStaightHit, haveExpectedToBeAt);
},
function needleIsOnTheLeft() {
return binarySearchUnsafe(hay, needle, from, at, compare, haveStaightHit, haveExpectedToBeAt);
},
function needleIsFound() {
return haveStaightHit(hay, at);
}
);
} else if (from > to) {
throw new Error('From index ' + from + ' is bigger than the to index ' + to + '.');
} else {
var at = from /* === to */;
return compare(
needle,
hay[at],
function needleIsOnTheRight() {
return haveExpectedToBeAt(hay, at + 1);
},
function needleIsOnTheLeft() {
return haveExpectedToBeAt(hay, at);
},
function needleIsFound() {
return haveStaightHit(hay, at)
}
);
}
}
function areEqual(actual: any, expected: any) {
if (actual !== expected) throw new Error('Expected: ' + expected + ', Actual: ' + actual);
}
function compareStrings<r>(one: string, another: string, oneGreaterThanAnother: () => r, oneLessThanAnother: () => r, oneEqualToAnother: () => r) : r {
if (one > another) {
return oneGreaterThanAnother();
} else if (one < another) {
return oneLessThanAnother();
} else {
return oneEqualToAnother();
}
}
function failOver(message: string) : any {
return function() {
throw new Error(message);
}
}
try {
binarySearch([], 'x', compareStrings, failOver('Direct hit is not expected.'), (hay, index) => areEqual(index, 0));
binarySearch(['b', 'd', 'f'], 'b', compareStrings, (hay, index) => areEqual(index, 0), failOver('Direct hit is expected.'));
binarySearch(['b', 'd', 'f'], 'd', compareStrings, (hay, index) => areEqual(index, 1), failOver('Direct hit is expected.'));
binarySearch(['b', 'd', 'f'], 'f', compareStrings, (hay, index) => areEqual(index, 2), failOver('Direct hit is expected.'));
binarySearch(['b', 'd', 'f'], 'g', compareStrings, failOver('Direct hit is not expected.'), (hay, index) => areEqual(index, 3));
binarySearch(['b', 'd', 'f'], 'a', compareStrings, failOver('Direct hit is not expected.'), (hay, index) => areEqual(index, 0));
binarySearch(['b', 'd', 'f'], 'c', compareStrings, failOver('Direct hit is not expected.'), (hay, index) => areEqual(index, 1));
alert('You are all good!');
} catch (e) {
alert(e.message);
}