mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-18 22:37:43 +00:00
533 lines
21 KiB
JavaScript
533 lines
21 KiB
JavaScript
var assert = require('chai').assert;
|
|
const LMLayerWorker = require('../../../../web/lm-worker/build/intermediate.js');
|
|
var ClassicalDistanceCalculation = LMLayerWorker.correction.ClassicalDistanceCalculation;
|
|
|
|
function prettyPrintMatrix(matrix) {
|
|
for(let r = 0; r < matrix.length; r++) {
|
|
console.log(JSON.stringify(matrix[r], function(key, value) {
|
|
if(value == Number.MAX_VALUE) {
|
|
return "MAX";
|
|
} else if(value === undefined) {
|
|
return -1;
|
|
} else {
|
|
return value;
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
function compute(input, match, mode, bandSize) {
|
|
let buffer = new ClassicalDistanceCalculation();
|
|
|
|
/* SUPPORTED MODES:
|
|
* "InputThenMatch" // adds all input chars, then all match chars.
|
|
* "MatchThenInput" // adds all match chars, then all input chars.
|
|
*/
|
|
|
|
// TEMP: once diagonal expansion is implemented, do this LATER, AFTER adding the chars.
|
|
bandSize = bandSize || 1;
|
|
buffer.diagonalWidth = bandSize;
|
|
|
|
switch(mode || "InputThenMatch") {
|
|
case "InputThenMatch":
|
|
for(let i = 0; i < input.length; i++) {
|
|
buffer = buffer.addInputChar({
|
|
key: input.charAt(i)
|
|
});
|
|
}
|
|
|
|
for(let j = 0; j < match.length; j++) {
|
|
buffer = buffer.addMatchChar({
|
|
key: match.charAt(j)
|
|
});
|
|
}
|
|
break;
|
|
case "MatchThenInput":
|
|
for(let j = 0; j < match.length; j++) {
|
|
buffer = buffer.addMatchChar({
|
|
key: match.charAt(j)
|
|
});
|
|
}
|
|
|
|
for(let i = 0; i < input.length; i++) {
|
|
buffer = buffer.addInputChar({
|
|
key: input.charAt(i)
|
|
});
|
|
}
|
|
break;
|
|
default:
|
|
throw "Invalid test mode specified!"
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
describe('Classical Damerau-Levenshtein edit-distance calculation', function() {
|
|
describe('unweighted', function() {
|
|
it("'abc' -> 'abc' = 0", function() {
|
|
assert.equal(compute("abc", "abc", "InputThenMatch").getFinalCost(), 0);
|
|
assert.equal(compute("abc", "abc", "MatchThenInput").getFinalCost(), 0);
|
|
});
|
|
|
|
it("'abc' -> 'cab' = 2", function() {
|
|
assert.equal(compute("abc", "cab", "InputThenMatch").getFinalCost(), 2);
|
|
assert.equal(compute("abc", "cab", "MatchThenInput").getFinalCost(), 2);
|
|
});
|
|
|
|
it("'abc' -> 'cdb' = 3", function() {
|
|
assert.equal(compute("abc", "cdb", "InputThenMatch").getFinalCost(), 3);
|
|
assert.equal(compute("abc", "cdb", "MatchThenInput").getFinalCost(), 3);
|
|
});
|
|
|
|
it("'cab' -> 'bac' = 2", function() {
|
|
assert.equal(compute("cab", "bac", "InputThenMatch").getFinalCost(), 2);
|
|
assert.equal(compute("cab", "bac", "MatchThenInput").getFinalCost(), 2);
|
|
});
|
|
|
|
it("'cab' -> 'bdc' = 2", function() {
|
|
assert.equal(compute("cab", "bdc", "InputThenMatch").getFinalCost(), 3);
|
|
assert.equal(compute("cab", "bdc", "MatchThenInput").getFinalCost(), 3);
|
|
});
|
|
|
|
it("'access' -> 'assess' = 2", function() {
|
|
assert.equal(compute("access", "assess", "InputThenMatch").getFinalCost(), 2);
|
|
assert.equal(compute("access", "assess", "MatchThenInput").getFinalCost(), 2);
|
|
})
|
|
|
|
it("'foo' -> 'foo' = 0", function() {
|
|
assert.equal(compute("foo", "foo", "InputThenMatch").getFinalCost(), 0);
|
|
assert.equal(compute("foo", "foo", "MatchThenInput").getFinalCost(), 0);
|
|
})
|
|
|
|
it("'help' -> 'yelp' = 1", function() {
|
|
assert.equal(compute("help", "yelp", "InputThenMatch").getFinalCost(), 1);
|
|
assert.equal(compute("help", "yelp", "MatchThenInput").getFinalCost(), 1);
|
|
})
|
|
|
|
it("'teh' -> 'the' = 1", function() {
|
|
assert.equal(compute("teh", "the", "InputThenMatch").getFinalCost(), 1);
|
|
assert.equal(compute("teh", "the", "MatchThenInput").getFinalCost(), 1);
|
|
});
|
|
|
|
// 'aa' considered to be inserted within the transposition.
|
|
it("'teaah' -> 'the' = 3", function() {
|
|
assert.equal(compute("teaah", "the", "InputThenMatch").getFinalCost(), 3); // Note: requires diagonal of width 2, not 1.
|
|
assert.equal(compute("teaah", "the", "MatchThenInput").getFinalCost(), 3);
|
|
});
|
|
|
|
// d & b transposed, then 'c' inserted.
|
|
it("'adb' -> 'abcd' = 2", function() {
|
|
assert.equal(compute("adb", "abcd", "InputThenMatch").getFinalCost(), 2);
|
|
assert.equal(compute("adb", "abcd", "MatchThenInput").getFinalCost(), 2);
|
|
});
|
|
|
|
it("'the' -> '' = 3", function() {
|
|
// Oh yeah, gotta do the null-string match case.
|
|
assert.equal(compute("the", "", "InputThenMatch").getFinalCost(), 3);
|
|
assert.equal(compute("the", "", "MatchThenInput").getFinalCost(), 3);
|
|
});
|
|
|
|
it("'' -> 'the' = 3", function() {
|
|
// Oh yeah, gotta do the null-string match case.
|
|
assert.equal(compute("", "the", "InputThenMatch").getFinalCost(), 3);
|
|
assert.equal(compute("", "the", "MatchThenInput").getFinalCost(), 3);
|
|
});
|
|
|
|
it("'accomodate' -> 'accommodate' = 1", function() {
|
|
assert.equal(compute("accomodate", "accommodate", "InputThenMatch").getFinalCost(), 1);
|
|
assert.equal(compute("accomodate", "accommodate", "MatchThenInput").getFinalCost(), 1);
|
|
});
|
|
|
|
it("'belitttle' -> 'belittle' = 1", function() {
|
|
assert.equal(compute("belitttle", "belittle", "InputThenMatch").getFinalCost(), 1);
|
|
assert.equal(compute("belitttle", "belittle", "MatchThenInput").getFinalCost(), 1);
|
|
});
|
|
|
|
it("'harras' -> 'harass' = 2", function() {
|
|
assert.equal(compute("harras", "harass", "InputThenMatch").getFinalCost(), 2);
|
|
assert.equal(compute("harras", "harass", "MatchThenInput").getFinalCost(), 2);
|
|
});
|
|
|
|
it("'hiegth' -> 'height' = 2", function() {
|
|
assert.equal(compute("hiegth", "height", "InputThenMatch").getFinalCost(), 2);
|
|
assert.equal(compute("hiegth", "height", "MatchThenInput").getFinalCost(), 2);
|
|
});
|
|
|
|
it("'jellyifhs' -> 'jellyfish' = 2", function() {
|
|
assert.equal(compute("jellyifhs", "jellyfish", "InputThenMatch").getFinalCost(), 2);
|
|
assert.equal(compute("jellyifhs", "jellyfish", "MatchThenInput").getFinalCost(), 2);
|
|
});
|
|
|
|
it("'aadddres' -> 'address' = 3", function() {
|
|
// If diagonal set to '1', cost is reported as 4.
|
|
assert.equal(compute("aadddres", "address", "InputThenMatch").getFinalCost(), 3); // Error - is returning 5, not 4 (which would be correct for current implementation state)
|
|
assert.equal(compute("aadddres", "address", "MatchThenInput").getFinalCost(), 3);
|
|
});
|
|
|
|
describe("Intermediate cost tests", function() {
|
|
it("'abc' -> 'cab' (width 1) = 2", function() {
|
|
// Technically a heuristic here, but it gets the right value b/c all changes are on the diagonal.
|
|
assert.equal(compute("abc", "cab", "InputThenMatch").getHeuristicFinalCost(), 2);
|
|
assert.equal(compute("abc", "cab", "MatchThenInput").getHeuristicFinalCost(), 2);
|
|
});
|
|
|
|
it("'aadddres' -> 'address' (width 1) = 4", function() {
|
|
// If diagonal set to '1', cost is reported as 4.
|
|
assert.equal(compute("aadddres", "address", "InputThenMatch").getHeuristicFinalCost(), 4);
|
|
assert.equal(compute("aadddres", "address", "MatchThenInput").getHeuristicFinalCost(), 4);
|
|
});
|
|
|
|
it("'aadddres' -> 'address' (width 2) = 3", function() {
|
|
// If diagonal set to '1', cost is reported as 4.
|
|
assert.equal(compute("aadddres", "address", "InputThenMatch", 2).getHeuristicFinalCost(), 3);
|
|
assert.equal(compute("aadddres", "address", "MatchThenInput", 2).getHeuristicFinalCost(), 3);
|
|
});
|
|
|
|
it("'jellyifhs' -> 'jellyfish' (width 1) = 2", function() {
|
|
// Technically a heuristic here, but it gets the right value b/c all changes are on the diagonal.
|
|
assert.equal(compute("jellyifhs", "jellyfish", "InputThenMatch").getHeuristicFinalCost(), 2);
|
|
assert.equal(compute("jellyifhs", "jellyfish", "MatchThenInput").getHeuristicFinalCost(), 2);
|
|
});
|
|
|
|
// Two transpositions: abc -> ca, ig <- ghi. Also, one deletion: 'd'.
|
|
it("'abcdefig' -> 'caefghi' (width 1) = 7", function() {
|
|
let buffer = compute("abcdefig", "caefghi", "InputThenMatch");
|
|
// This test case was constructed with the tranposition parts outside of the center diagonal.
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
});
|
|
|
|
// Two transpositions: abc -> ca, ig <- ghi. Also, one deletion: 'd'.
|
|
it("'abcdefig' -> 'caefghi' (width 2) = 5", function() {
|
|
let buffer = compute("abcdefig", "caefghi", "InputThenMatch", 2);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 5);
|
|
});
|
|
|
|
// Two transpositions: abc -> ca, ig <- ghi. Also, one deletion: 'd'.
|
|
it("'abcdefigj' -> 'caefghij' (width 1) = 7", function() {
|
|
let buffer = compute("abcdefigj", "caefghij", "InputThenMatch", 1);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
});
|
|
|
|
// Two transpositions: abc -> ca, ig <- ghi. Also, one deletion: 'd'.
|
|
it("'abcdefigj' -> 'caefghij' (width 2) = 5", function() {
|
|
let buffer = compute("abcdefigj", "caefghij", "InputThenMatch", 2);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 5);
|
|
});
|
|
|
|
// Two transpositions: abcd -> da, zx <- xyz and one deletion ('g')
|
|
// The intermediate 'ef[g]hi' ensures that the two transpositions are kept separate.
|
|
it("'abcdefghizx' -> 'daefhixyz' (width 2) = 8", function() {
|
|
let buffer = compute("abcdefghizx", "daefhixyz", "InputThenMatch", 2);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 8);
|
|
});
|
|
|
|
// Two transpositions: abcd -> da, zx <- xyz and one deletion ('g')
|
|
// The intermediate 'ef[g]hi' ensures that the two transpositions are kept separate.
|
|
it("'abcdefghizx' -> 'daefhixyz' (width 3) = 6", function() {
|
|
let buffer = compute("abcdefghizx", "daefhixyz", "InputThenMatch", 3);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 6);
|
|
});
|
|
|
|
// Two transpositions: abcd -> da, zw <- wxyz and two deletions ('gh')
|
|
// The intermediate deletions help to ensure that the two transpositions are kept separate.
|
|
it("'abcdefijzw' -> 'daefghijwxyz' (width 2) = 8", function() {
|
|
let buffer = compute("abcdefijzw", "daefghijwxyz", "InputThenMatch", 2);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 8);
|
|
});
|
|
|
|
// One transposition: da <- abcd, one insertion ('g'), and two deletions ('yz').
|
|
it("'daefhiwxyz' -> 'abcdefghiyz' (width 1) = 9", function() {
|
|
let buffer = compute("daefhiwxyz", "abcdefghiyz", "InputThenMatch", 1);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 9);
|
|
});
|
|
|
|
// One transposition: da <- abcd, one insertion ('g'), and two deletions ('yz').
|
|
it("'daefhiwxyz' -> 'abcdefghiyz' (width 2) = 7", function() {
|
|
let buffer = compute("daefhiwxyz", "abcdefghiyz", "InputThenMatch", 2);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
});
|
|
|
|
// One transposition: da <- abcd, one insertion ('g'), and two deletions ('yz').
|
|
it("'daefhiwxyz' -> 'abcdefghiyz' (width 3) = 6", function() {
|
|
let buffer = compute("daefhiwxyz", "abcdefghiyz", "InputThenMatch", 3);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 6);
|
|
});
|
|
|
|
// Two transpositions: da <- abcd, wxyz -> zw and one deletion ('g')
|
|
it("'daefghiwxyz' -> 'abcdefhizw' (width 1) = 9", function() {
|
|
let buffer = compute("daefghiwxyz", "abcdefhizw", "InputThenMatch", 1);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 9);
|
|
});
|
|
|
|
// Two transpositions: da <- abcd, wxyz -> zw and one deletion ('g')
|
|
it("'daefghiwxyz' -> 'abcdefhizw' (width 2) = 7", function() {
|
|
buffer = compute("daefghiwxyz", "abcdefhizw", "InputThenMatch", 2);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
});
|
|
});
|
|
|
|
describe("Diagonal extension tests", function() {
|
|
it("'aadddres' -> 'address' (width 1->2) = 3", function() {
|
|
// If diagonal set to '1', cost is reported as 4.
|
|
let buffer = compute("aadddres", "address", "InputThenMatch", 1);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 4);
|
|
|
|
// 1 -> 2
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 3);
|
|
});
|
|
|
|
// Two transpositions: abc -> ca, ig <- ghi. Also, one deletion: 'd'.
|
|
it("'abcdefig' -> 'caefghi' (width 1->2) = 5", function() {
|
|
let buffer = compute("abcdefig", "caefghi", "InputThenMatch", 1);
|
|
// This test case was constructed with the tranposition parts outside of the center diagonal.
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
|
|
// 1 -> 2
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 5);
|
|
});
|
|
|
|
// Two transpositions: abc -> ca, ig <- ghi. Also, one deletion: 'd'.
|
|
it("'abcdefigj' -> 'caefghij' (width 1->2) = 5", function() {
|
|
let buffer = compute("abcdefigj", "caefghij", "InputThenMatch", 1);
|
|
// This test case was constructed with the tranposition parts outside of the center diagonal.
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
|
|
// 1 -> 2
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 5);
|
|
});
|
|
|
|
// Two transpositions: abcd -> da, zx <- xyz and one deletion ('g')
|
|
// The intermediate 'ef[g]hi' ensures that the two transpositions are kept separate.
|
|
it("'abcdefghizx' -> 'daefhixyz' (width 1->2) = 8", function() {
|
|
let buffer = compute("abcdefghizx", "daefhixyz", "InputThenMatch", 1);
|
|
assert.equal(buffer.getHeuristicFinalCost(), Number.MAX_VALUE);
|
|
|
|
// 1 -> 2
|
|
// Will become too small during expansion if not properly checking
|
|
// transpositions for the diagonal's left cell.
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 8);
|
|
});
|
|
|
|
// Two transpositions: abcd -> da, zx <- xyz and one deletion ('g')
|
|
// The intermediate 'ef[g]hi' ensures that the two transpositions are kept separate.
|
|
it("'abcdefghizx' -> 'daefhixyz' (width 2->3) = 6", function() {
|
|
let buffer = compute("abcdefghizx", "daefhixyz", "InputThenMatch", 2);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 8);
|
|
|
|
// 2 -> 3
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 6);
|
|
});
|
|
|
|
// Two transpositions: abcd -> da, zw <- wxyz and two deletions ('gh')
|
|
// The intermediate deletions help to ensure that the two transpositions are kept separate.
|
|
it("'abcdefijzw' -> 'daefghijwxyz' (width 1->2) = 8", function() {
|
|
// Relies on a propagated insertion.
|
|
let buffer = compute("abcdefijzw", "daefghijwxyz", "InputThenMatch", 1);
|
|
assert.equal(buffer.getHeuristicFinalCost(), Number.MAX_VALUE);
|
|
|
|
// 1 -> 2
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 8);
|
|
});
|
|
|
|
// Two transpositions: da <- abcd, wxyz -> zw and one deletion ('g')
|
|
it("'daefghiwxyz' -> 'abcdefhizw' (width 1->2) = 7", function() {
|
|
// Relies on a propagated transposition and multiple propagated substitutions.
|
|
// Inspection of the diagonal-expansion update shows it involves a LOT of propagations!
|
|
let buffer = compute("daefghiwxyz", "abcdefhizw", "InputThenMatch", 1);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 9);
|
|
|
|
// 1 -> 2
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
});
|
|
|
|
// One transposition: da <- abcd, one insertion ('g'), and two deletions ('yz').
|
|
it("'daefhiwxyz' -> 'abcdefghiyz' (width 1->2) = 6", function() {
|
|
let buffer = compute("daefhiwxyz", "abcdefghiyz", "InputThenMatch", 1);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 9);
|
|
|
|
// 1 -> 2
|
|
// Will become too small during expansion if not properly checking
|
|
// transpositions for the diagonal's right cell.
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
});
|
|
|
|
// One transposition: da <- abcd, one insertion ('g'), and two deletions ('yz').
|
|
it("'daefhiwxyz' -> 'abcdefghiyz' (width 2->3) = 6", function() {
|
|
let buffer = compute("daefhiwxyz", "abcdefghiyz", "InputThenMatch", 2);
|
|
assert.equal(buffer.getHeuristicFinalCost(), 7);
|
|
|
|
// 2 -> 3
|
|
// relies on a propagated deletion!
|
|
buffer = buffer.increaseMaxDistance();
|
|
assert.equal(buffer.getHeuristicFinalCost(), 6);
|
|
});
|
|
});
|
|
|
|
|
|
describe("Bounded final cost tests", function() {
|
|
it("'adddress' -> 'address' has final cost within 4", function() {
|
|
let buffer = compute("aadddres", "address", "InputThenMatch");
|
|
assert.isTrue(buffer.hasFinalCostWithin(4));
|
|
})
|
|
|
|
it("'adddress' -> 'address' has final cost within 3", function() {
|
|
let buffer = compute("aadddres", "address", "InputThenMatch");
|
|
assert.isTrue(buffer.hasFinalCostWithin(3));
|
|
})
|
|
|
|
it("'adddress' -> 'address' does not have final cost within 2", function() {
|
|
let buffer = compute("aadddres", "address", "InputThenMatch");
|
|
assert.isFalse(buffer.hasFinalCostWithin(2));
|
|
})
|
|
});
|
|
});
|
|
|
|
describe("subset operations", function() {
|
|
it("properly truncates tracked input & match sequences", function() {
|
|
let buffer = compute("tear", "there", "InputThenMatch");
|
|
assert.equal(buffer.getFinalCost(), 3);
|
|
|
|
let trimmedBuffer = buffer.getSubset(3, 3);
|
|
assert.equal(trimmedBuffer.inputSequence.length, 3);
|
|
assert.equal(trimmedBuffer.matchSequence.length, 3);
|
|
|
|
let expectedInput = ['t', 'e', 'a'];
|
|
for(let i=0; i < expectedInput.length; i++) {
|
|
assert.equal(trimmedBuffer.inputSequence[i].key, expectedInput[i]);
|
|
}
|
|
|
|
let expectedMatch = ['t', 'h', 'e'];
|
|
for(let i=0; i < expectedMatch.length; i++) {
|
|
assert.equal(trimmedBuffer.matchSequence[i].key, expectedMatch[i]);
|
|
}
|
|
});
|
|
|
|
it("['tear' => 'there'] trimmed to ['tea' => 'the']", function() {
|
|
let buffer = compute("tear", "there", "InputThenMatch");
|
|
assert.equal(buffer.getFinalCost(), 3);
|
|
|
|
let trimmedBuffer = buffer.getSubset(3, 3);
|
|
assert.equal(trimmedBuffer.getFinalCost(), 2);
|
|
});
|
|
|
|
it("['tear' => 'there'] trimmed to ['te' => 'the']", function() {
|
|
let buffer = compute("tear", "there", "InputThenMatch");
|
|
assert.equal(buffer.getFinalCost(), 3);
|
|
|
|
let trimmedBuffer = buffer.getSubset(2, 3);
|
|
assert.equal(trimmedBuffer.getFinalCost(), 1);
|
|
});
|
|
|
|
it("['tear' => 'there'] trimmed to ['tear' => '']", function() {
|
|
let buffer = compute("tear", "there", "InputThenMatch");
|
|
assert.equal(buffer.getFinalCost(), 3);
|
|
|
|
let trimmedBuffer = buffer.getSubset(4, 0);
|
|
assert.equal(trimmedBuffer.getFinalCost(), 4);
|
|
});
|
|
|
|
it("['tear' => 'there'] trimmed to ['' => 'there']", function() {
|
|
let buffer = compute("tear", "there", "InputThenMatch");
|
|
assert.equal(buffer.getFinalCost(), 3);
|
|
|
|
let trimmedBuffer = buffer.getSubset(0, 5);
|
|
assert.equal(trimmedBuffer.getFinalCost(), 5);
|
|
});
|
|
|
|
it("['jellyifhs' -> 'jellyfish'] trimmed to ['jelly' => 'jelly']", function() {
|
|
let buffer = compute("jellyifhs", "jellyfish");
|
|
|
|
let trimmedBuffer = buffer.getSubset(5, 5);
|
|
assert.equal(trimmedBuffer.getFinalCost(), 0)
|
|
});
|
|
|
|
it("['jellyifhs' -> 'jellyfish'] trimmed to ['jellyifh' => 'jellyfis']", function() {
|
|
let buffer = compute("jellyifhs", "jellyfish");
|
|
|
|
let trimmedBuffer = buffer.getSubset(8, 8);
|
|
assert.equal(trimmedBuffer.getFinalCost(), 2)
|
|
});
|
|
});
|
|
|
|
describe("edit path construction", function() {
|
|
let op = {
|
|
s: 'substitute',
|
|
m: 'match',
|
|
i: 'insert',
|
|
d: 'delete',
|
|
ts: 'transpose-start',
|
|
te: 'transpose-end',
|
|
ti: 'transpose-insert',
|
|
td: 'transpose-delete'
|
|
}
|
|
|
|
it("'accomodate' -> 'accommodate'", function() {
|
|
let buffer = compute("accomodate", "accommodate", "InputThenMatch");
|
|
|
|
let editSequence = [
|
|
// a c c o m
|
|
op.m, op.m, op.m, op.m, op.m,
|
|
// +m o d a t e
|
|
op.i, op.m, op.m, op.m, op.m, op.m
|
|
];
|
|
|
|
assert.deepEqual(buffer.editPath(), editSequence);
|
|
});
|
|
|
|
it("'harras' -> 'harass'", function() {
|
|
let buffer = compute("harras", "harass", "InputThenMatch");
|
|
|
|
let editSequence = [
|
|
// h a r -r a s +s
|
|
op.m, op.m, op.m, op.d, op.m, op.m, op.i
|
|
];
|
|
|
|
assert.deepEqual(buffer.editPath(), editSequence);
|
|
});
|
|
|
|
it("'access' -> 'assess'", function() {
|
|
let buffer = compute("access", "assess", "InputThenMatch");
|
|
|
|
let editSequence = [
|
|
// a c/s c/s e s s
|
|
op.m, op.s, op.s, op.m, op.m, op.m
|
|
];
|
|
|
|
assert.deepEqual(buffer.editPath(), editSequence);
|
|
});
|
|
|
|
it("'ifhs' -> 'fish'", function() {
|
|
let buffer = compute("ifhs", "fish");
|
|
|
|
let editSequence = [
|
|
op.ts, op.te, op.ts, op.te
|
|
];
|
|
|
|
assert.deepEqual(buffer.editPath(), editSequence);
|
|
});
|
|
|
|
// Two transpositions: abc -> ca, ig <- ghi. Also, one deletion: 'd'.
|
|
it("'abczdefig' -> 'cazefghi'", function() {
|
|
let buffer = compute("abczdefig", "cazefghi", "InputThenMatch", 2);
|
|
|
|
let editSequence = [
|
|
// --- abc -> ca ----
|
|
// a/c b/_ c/a z -d e f
|
|
op.ts, op.td, op.te, op.m, op.d, op.m, op.m,
|
|
// --- ig -> ghi ----
|
|
// i/g _/h g/i
|
|
op.ts, op.ti, op.te
|
|
];
|
|
|
|
assert.deepEqual(buffer.editPath(), editSequence);
|
|
});
|
|
})
|
|
});
|