Skip to content

Commit

Permalink
Make use of LCS
Browse files Browse the repository at this point in the history
  • Loading branch information
hawk committed Mar 17, 2017
1 parent 096c04d commit ee95a0c
Show file tree
Hide file tree
Showing 14 changed files with 462 additions and 406 deletions.
11 changes: 8 additions & 3 deletions src/lux_case.erl
Expand Up @@ -457,18 +457,23 @@ print_fail(OldI0, NewI, File, Results,
{Actual, Rest};
<<"success pattern matched ", _/binary>> ->
{Actual, Rest};
_ when is_atom(Actual) ->
_ when is_atom(Actual), is_binary(Rest) ->
{atom_to_list(Actual), Rest};
_ when is_atom(Actual), is_atom(Rest) ->
{atom_to_list(Actual), list_to_binary(atom_to_list(Rest))};
_ when is_binary(Actual) ->
{<<"error">>, Actual}
end,
Diff = lux_utils:shrink_diff(Expected, NewRest),
FailBin =
?l2b(
[
io_lib:format("expected\n\t~s\n",
[simple_to_string(Expected)]),
io_lib:format("actual ~s\n\t~s",
[NewActual, simple_to_string(NewRest)])
io_lib:format("actual ~s\n\t~s\n",
[NewActual, simple_to_string(NewRest)]),
io_lib:format("diff\n\t~s",
[simple_to_string(Diff)])
]),
case OldI0#istate.progress of
silent ->
Expand Down
2 changes: 1 addition & 1 deletion src/lux_debug.erl
Expand Up @@ -999,7 +999,7 @@ tail_format("compact", Format, Data) ->
io:format(Format, Data);
tail_format("verbose", Format, Data) ->
Str = lists:flatten(io_lib:format(Format, Data)),
io:format(lux_utils:dequote(Str)).
io:format(lux_log:dequote(Str)).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Expand Down
159 changes: 102 additions & 57 deletions src/lux_diff.erl
Expand Up @@ -12,7 +12,7 @@
-module(lux_diff).

-export([
compare/2, compare/3,
compare/2, compare2/3,
split_diff/1,
apply_verbose_diff/2, apply_compact_diff/2,
test/0, test2/2
Expand All @@ -30,17 +30,13 @@
%% both lists. The other elements can be merged in afterwards. This
%% greatly speeds up the case when only a few elements are the same.

-spec(compare([elem()],[elem()]) -> compact_diff()).
compare(A, B) ->
compare(A, B, default_match()).

-spec(compare(A :: elem_list(),B :: elem_list(), match_fun()) ->
compact_diff()).
compare(A, A, _Fun) ->
-spec(compare(A::elem_list(), B::elem_list()) -> compact_diff()).
compare(A, A) ->
[A];
compare(A, B, Fun) ->
compare(A, B) ->
ASame = A -- (A -- B),
BSame = B -- (B -- A),
Fun = default_match(),
CompactDiff = compare2(ASame, BSame, Fun),
merge_unique(A, B, CompactDiff, []).

Expand Down Expand Up @@ -80,16 +76,18 @@ grab_until([X|Xs], Y, Acc, Add) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Calcuate shortest edit list to go from A to B

-spec(compare2(A :: elem_list(),B :: elem_list(), match_fun()) ->
-spec(compare2(A::elem_list(), B::elem_list(), Fun::match_fun()) ->
compact_diff()).
compare2(A, B, Fun) ->
DataA = list_to_tuple(A),
DataB = list_to_tuple(B),
DownVector = ets:new(down, [private]),
UpVector = ets:new(up, [private]),
try
Diff = lcs(DataA, 0, size(DataA), DataB, 0, size(DataB),
DownVector, UpVector, Fun, []),
Diff = lcs(DataA, 0, size(DataA),
DataB, 0, size(DataB),
DownVector, UpVector,
Fun, []),
merge_cleanup(Diff,[])
after
ets:delete(DownVector),
Expand Down Expand Up @@ -144,7 +142,9 @@ merge_cleanup_keep([Keep|Rest], Acc, KeepAcc) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Longest Common Subsequence

lcs(DataA, LowerA0, UpperA0, DataB, LowerB0, UpperB0, DownVector, UpVector,
lcs(DataA, LowerA0, UpperA0,
DataB, LowerB0, UpperB0,
DownVector, UpVector,
Fun, Acc0) ->
%% Skip equal lines at start
{LowerA, LowerB, StartKeep} =
Expand Down Expand Up @@ -197,12 +197,18 @@ lcs(DataA, LowerA0, UpperA0, DataB, LowerB0, UpperB0, DownVector, UpVector,
LowerB =:= UpperB ->
acc_add(StartKeep++EndKeep, Acc0);
true ->
{SX, SY} = sms(DataA, LowerA, UpperA, DataB, LowerB, UpperB,
DownVector, UpVector, Fun),
Acc1 = lcs(DataA, LowerA, SX, DataB, LowerB, SY, DownVector,
UpVector, Fun, acc_add(StartKeep,Acc0)),
Acc2 = lcs(DataA, SX, UpperA, DataB, SY, UpperB, DownVector,
UpVector, Fun, Acc1),
{SX, SY} = sms(DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector,
Fun),
Acc1 = lcs(DataA, LowerA, SX,
DataB, LowerB, SY,
DownVector, UpVector,
Fun, acc_add(StartKeep,Acc0)),
Acc2 = lcs(DataA, SX, UpperA,
DataB, SY, UpperB,
DownVector, UpVector,
Fun, Acc1),
acc_add(EndKeep, Acc2)
end.

Expand All @@ -216,7 +222,10 @@ acc_add(E, Acc) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Find Shortest Middle Sname

sms(DataA, LowerA, UpperA, DataB, LowerB, UpperB, DownVector, UpVector, Fun) ->
sms(DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector,
Fun) ->
DownK = LowerA - LowerB,
UpK = UpperA - UpperB,
%%
Expand All @@ -228,37 +237,59 @@ sms(DataA, LowerA, UpperA, DataB, LowerB, UpperB, DownVector, UpVector, Fun) ->
vset(DownVector, DownK+1, LowerA),
vset(UpVector, UpK-1, UpperA),
%%
sms_d(0, MaxD, DataA, LowerA, UpperA, DataB, LowerB, UpperB, DownVector,
UpVector, DownK, UpK, Delta, OddDelta, Fun).
sms_d(0, MaxD,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun).

%% D loop
sms_d(D, MaxD, _DataA, _LowerA, _UpperA, _DataB, _LowerB, _UpperB, _DownVector,
_UpVector, _DownK, _UpK, _Delta, _OddDelta, _Fun) when D > MaxD ->
sms_d(D, MaxD,
_DataA, _LowerA, _UpperA,
_DataB, _LowerB, _UpperB,
_DownVector,_UpVector,_DownK,_UpK,
_Delta, _OddDelta, _Fun) when D > MaxD ->
throw(error);
sms_d(D, MaxD, DataA, LowerA, UpperA, DataB, LowerB, UpperB, DownVector,
UpVector, DownK, UpK, Delta, OddDelta, Fun) ->
case sms_d_k_f(DownK-D, D, DataA, LowerA, UpperA, DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK, Delta, OddDelta, Fun) of
sms_d(D, MaxD,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun) ->
case sms_d_k_f(DownK-D, D,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun) of
not_found ->
case sms_d_k_r(UpK-D, D, DataA, LowerA, UpperA, DataB, LowerB,
UpperB, DownVector, UpVector, DownK, UpK,
case sms_d_k_r(UpK-D, D,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun) of
not_found ->
sms_d(D+1, MaxD, DataA, LowerA, UpperA, DataB, LowerB,
UpperB, DownVector, UpVector, DownK, UpK, Delta,
OddDelta, Fun);
sms_d(D+1, MaxD,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun);
Point -> Point
end;
Point -> Point
end.

%% Forward snake
sms_d_k_f(K, D, _DataA, _LowerA, _UpperA, _DataB, _LowerB, _UpperB,
_DownVector, _UpVector, DownK, _UpK, _Delta, _OddDelta, _Fun)
sms_d_k_f(K, D,
_DataA, _LowerA, _UpperA,
_DataB, _LowerB, _UpperB,
_DownVector, _UpVector, DownK, _UpK,
_Delta, _OddDelta, _Fun)
when K > (DownK+D) ->
not_found;
sms_d_k_f(K, D, DataA, LowerA, UpperA, DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK, Delta, OddDelta, Fun) ->
sms_d_k_f(K, D,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun) ->
if
K =:= (DownK - D) ->
X = vget(DownVector, K+1); %% Down
Expand Down Expand Up @@ -301,23 +332,33 @@ sms_d_k_f(K, D, DataA, LowerA, UpperA, DataB, LowerB, UpperB,
UpVK =< DownVK ->
{DownVK, DownVK-K};
true ->
sms_d_k_f(K+2, D, DataA, LowerA, UpperA, DataB,
LowerB, UpperB, DownVector, UpVector,
DownK, UpK, Delta, OddDelta, Fun)
sms_d_k_f(K+2, D,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun)
end;
true ->
sms_d_k_f(K+2, D, DataA, LowerA, UpperA, DataB,
LowerB, UpperB, DownVector, UpVector,
DownK, UpK, Delta, OddDelta, Fun)
sms_d_k_f(K+2, D,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun)
end.

%% Backward snake
sms_d_k_r(K, D, _DataA, _LowerA, _UpperA, _DataB, _LowerB, _UpperB,
_DownVector, _UpVector, _DownK, UpK, _Delta, _OddDelta, _Fun)
sms_d_k_r(K, D,
_DataA, _LowerA, _UpperA,
_DataB, _LowerB, _UpperB,
_DownVector, _UpVector, _DownK, UpK,
_Delta, _OddDelta, _Fun)
when K > (UpK+D) ->
not_found;
sms_d_k_r(K, D, DataA, LowerA, UpperA, DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK, Delta, OddDelta, Fun) ->
sms_d_k_r(K, D,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun) ->
if
K =:= (UpK+D) ->
X = vget(UpVector, K-1); %% Up
Expand Down Expand Up @@ -360,14 +401,18 @@ sms_d_k_r(K, D, DataA, LowerA, UpperA, DataB, LowerB, UpperB,
UpVK =< DownVK ->
{DownVK, DownVK-K};
true ->
sms_d_k_r(K+2, D, DataA, LowerA, UpperA, DataB, LowerB,
UpperB, DownVector, UpVector, DownK, UpK, Delta,
OddDelta, Fun)
sms_d_k_r(K+2, D,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun)
end;
true ->
sms_d_k_r(K+2, D, DataA, LowerA, UpperA, DataB, LowerB,
UpperB, DownVector, UpVector, DownK, UpK, Delta,
OddDelta, Fun)
sms_d_k_r(K+2, D,
DataA, LowerA, UpperA,
DataB, LowerB, UpperB,
DownVector, UpVector, DownK, UpK,
Delta, OddDelta, Fun)
end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Expand All @@ -391,7 +436,7 @@ split_diff([[H|T]|Rest], Acc) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Apply diff to list

-spec(apply_verbose_diff(A :: elem_list(), Diff :: verbose_diff()) ->
-spec(apply_verbose_diff(A::elem_list(), Diff::verbose_diff()) ->
elem_list()).
apply_verbose_diff(A, Diff) ->
apply_verbose_diff(A, Diff, []).
Expand All @@ -405,7 +450,7 @@ apply_verbose_diff([A|As], [{'-',A}|Rest], Acc) ->
apply_verbose_diff([A|As], [{'=',A}|Rest], Acc) ->
apply_verbose_diff(As, Rest, [A|Acc]).

-spec(apply_compact_diff(A :: elem_list(), Diff :: compact_diff()) ->
-spec(apply_compact_diff(A::elem_list(), Diff::compact_diff()) ->
elem_list()).
apply_compact_diff(A, Diff) ->
apply_compact_diff(A, Diff, []).
Expand Down Expand Up @@ -509,9 +554,9 @@ test(N, Max, Var) ->
test2(A, B) ->
Fun = default_match(),
erlang:garbage_collect(),
{Time2,CompactDiff1} = timer:tc(fun() -> compare(A,B,Fun) end),
{Time2,CompactDiff1} = timer:tc(fun() -> compare(A ,B) end),
erlang:garbage_collect(),
{Time1,CompactDiff2} = timer:tc(fun() -> compare2(A,B,Fun) end),
{Time1,CompactDiff2} = timer:tc(fun() -> compare2(A, B, Fun) end),
try
[] = validate_diff(CompactDiff1, []),
[] = validate_diff(CompactDiff2, []),
Expand Down

0 comments on commit ee95a0c

Please sign in to comment.