USING: accessors arrays combinators combinators.short-circuit kernel make math math.order sequences sequences.private typed ; IN: lcs [ drop 0 ] with map ; : levenshtein-initialize ( |str1| |str2| -- matrix ) [ ] bi@ [ [ + ] curry map ] with map ; :: run-lcs ( old new init step -- matrix ) old length 1 + new length 1 + init call :> matrix old length [| i | new length [| j | i j matrix old new step loop-step ] each ] each matrix ; inline PRIVATE> : levenshtein ( old new -- n ) [ levenshtein-initialize ] [ levenshtein-step ] run-lcs last last ; TUPLE: retain item ; TUPLE: delete item ; TUPLE: insert item ; > 1 - ] [ old>> ] bi nth-unsafe ; : new-nth ( state -- elt ) [ j>> 1 - ] [ new>> ] bi nth-unsafe ; : top-beats-side? ( state -- ? ) [ [ i>> ] [ j>> 1 - ] [ table>> ] tri nth-unsafe nth-unsafe ] [ [ i>> 1 - ] [ j>> ] [ table>> ] tri nth-unsafe nth-unsafe ] bi > ; : retained? ( state -- ? ) { [ i>> 0 > ] [ j>> 0 > ] [ [ old-nth ] [ new-nth ] bi = ] } 1&& ; : do-retain ( state -- state ) dup old-nth retain boa , [ 1 - ] change-i [ 1 - ] change-j ; : inserted? ( state -- ? ) { [ j>> 0 > ] [ { [ i>> zero? ] [ top-beats-side? ] } 1|| ] } 1&& ; : do-insert ( state -- state ) dup new-nth insert boa , [ 1 - ] change-j ; : deleted? ( state -- ? ) { [ i>> 0 > ] [ { [ j>> zero? ] [ top-beats-side? not ] } 1|| ] } 1&& ; : do-delete ( state -- state ) dup old-nth delete boa , [ 1 - ] change-i ; : (trace-diff) ( state -- ) { { [ dup retained? ] [ do-retain (trace-diff) ] } { [ dup inserted? ] [ do-insert (trace-diff) ] } { [ dup deleted? ] [ do-delete (trace-diff) ] } [ drop ] ! i=j=0 } cond ; : trace-diff ( old new table -- diff ) [ ] [ first length 1 - ] [ length 1 - ] tri trace-state boa [ (trace-diff) ] { } make reverse! ; PRIVATE> : lcs-diff ( old new -- diff ) 2dup [ lcs-initialize ] [ lcs-step ] run-lcs trace-diff ; : lcs ( seq1 seq2 -- lcs ) [ lcs-diff [ retain? ] filter ] keep [ item>> ] swap map-as ;