@a-s8h/liblevenshtein
Version:
Various utilities regarding Levenshtein transducers.
141 lines (135 loc) • 4.55 kB
text/coffeescript
fs = require 'fs'
{levenshtein: {distance}} = require '../../src/levenshtein/distance'
lorem_ipsum = do ->
path = "#{__dirname}/../../shared/resources/lorem-ipsum-terms.txt"
lorem_ipsum = fs.readFileSync(path, 'utf8').split('\n')
if lorem_ipsum[lorem_ipsum.length - 1] is ''
lorem_ipsum.pop() #-> drop the empty string
lorem_ipsum
axioms =
equal_self_similarity: (test) ->
d = @distance
i = 0
while i < lorem_ipsum.length
a = lorem_ipsum[i]
x = d(a,a)
j = 0
while j < lorem_ipsum.length
b = lorem_ipsum[j]
y = d(b,b)
if x isnt y
test.ok false, "Expected d(#{a},#{a}) = d(#{b},#{b})"
j += 1
i += 1
test.done()
minimality: (test) ->
d = @distance
i = 0
while i < lorem_ipsum.length
a = lorem_ipsum[i]
x = d(a,a)
j = 0
while j < i
b = lorem_ipsum[j]
y = d(a,b)
z = d(b,a)
unless y > x
test.ok false, "Expected d(#{a},#{b}) > d(#{a},#{a})"
unless z > x
test.ok false, "Expected d(#{b},#{a}) > d(#{a},#{a})"
j += 1
j = i + 1
while j < lorem_ipsum.length
b = lorem_ipsum[j]
y = d(a,b)
z = d(b,a)
unless y > x
test.ok false, "Expected d(#{a},#{b}) > d(#{a},#{a})"
unless z > x
test.ok false, "Expected d(#{b},#{a}) > d(#{a},#{a})"
j += 1
i += 1
test.done()
symmetry: (test) ->
d = @distance
i = 0
while i < lorem_ipsum.length
a = lorem_ipsum[i]
j = 0
while j < lorem_ipsum.length
b = lorem_ipsum[j]
x = d(a,b)
y = d(b,a)
if x isnt y
test.ok false, "Expected d(#{a},#{b}) = d(#{b},#{a})"
j += 1
i += 1
test.done()
triangle_inequality: (test) ->
d = @distance
i = 0
while i < lorem_ipsum.length
a = lorem_ipsum[i]
j = 0
while j < lorem_ipsum.length
b = lorem_ipsum[j]
x = d(a,b)
k = 0
while k < lorem_ipsum.length
c = lorem_ipsum[k]
y = d(a,c)
z = d(b,c)
unless x + y >= z
test.ok false, "Expected d(#{a},#{b}) + d(#{a},#{c} >= d(#{b},#{c}))"
unless x + z >= y
test.ok false, "Expected d(#{a},#{b}) + d(#{b},#{c} >= d(#{a},#{c}))"
unless y + z >= x
test.ok false, "Expected d(#{a},#{c}) + d(#{b},#{c} >= d(#{a},#{b}))"
k += 1
j += 1
i += 1
test.done()
operations = (algorithm, transposition=2, merge=2, split=2) ->
tests =
setUp: (callback) ->
@distance = distance(algorithm)
callback()
'The distance should conform to Equal Self-Similarity': axioms.equal_self_similarity
'The distance should conform to Minimality': axioms.minimality
'The distance should conform to Symmetry': axioms.symmetry
'The distance should conform to the Triangle Inequality': axioms.triangle_inequality
'The distance between a term and itself should be 0': (test) ->
test.strictEqual @distance('foo', 'foo'), 0
test.done()
'An insertion should incur a penalty of 1 unit': (test) ->
test.strictEqual @distance('foo', 'food'), 1
test.strictEqual @distance('foo', 'fodo'), 1
test.strictEqual @distance('foo', 'fdoo'), 1
test.strictEqual @distance('foo', 'dfoo'), 1
test.done()
'A deletion should incur a penalty of 1 unit': (test) ->
test.strictEqual @distance('foo', 'oo'), 1
test.strictEqual @distance('foo', 'fo'), 1
test.done()
'A substitution should incur a penalty of 1 unit': (test) ->
test.strictEqual @distance('foo', 'boo'), 1
test.strictEqual @distance('foo', 'fbo'), 1
test.strictEqual @distance('foo', 'fob'), 1
test.done()
tests["A transposition should incur a penalty of #{transposition} unit(s)"] =
(test) ->
test.strictEqual @distance('foo', 'ofo'), transposition
test.done()
tests["A merge should incur a penalty of #{merge} unit(s)"] =
(test) ->
test.strictEqual @distance('clog', 'dog'), merge
test.done()
tests["A split should incur a penalty of #{split} unit(s)"] =
(test) ->
test.strictEqual @distance('dog', 'clog'), split
test.done()
tests
module.exports =
'Levenshtein Distance': operations('standard')
'Levenshtein Distance Extended with Transposition': operations('transposition', 1)
'Levenshtein Distance Extended with Merge and Split': operations('merge_and_split', 2, 1, 1)