-module(gap). -compile(no_auto_import). -export([to_styled/1, compare_strings/2, compare_lists/2]). -export_type([score/1]). -type score(FFC) :: {score, integer(), gleam@option:option(FFC)}. -spec to_styled(gap@comparison:comparison(any())) -> gap@styled_comparison:styled_comparison(). to_styled(Comparison) -> _pipe = Comparison, _pipe@1 = gap@styling:from_comparison(_pipe), _pipe@2 = gap@styling:highlight( _pipe@1, fun gap@styling:first_highlight_default/1, fun gap@styling:second_highlight_default/1, fun gap@styling:no_highlight/1 ), gap@styling:to_styled_comparison(_pipe@2). -spec compare_strings(binary(), binary()) -> gap@comparison:comparison(binary()). compare_strings(First, Second) -> Comparison = compare_lists( gleam@string:to_graphemes(First), gleam@string:to_graphemes(Second) ), case Comparison of {list_comparison, First@1, Second@1} -> {string_comparison, First@1, Second@1}; {string_comparison, First@2, Second@2} -> {string_comparison, First@2, Second@2} end. -spec compare_lists(list(FFM), list(FFM)) -> gap@comparison:comparison(FFM). compare_lists(First_sequence, Second_sequence) -> Diff_map@2 = begin _pipe = Second_sequence, gleam@list:index_fold( _pipe, gleam@map:new(), fun(Diff_map, Item_second, Index_second) -> _pipe@1 = First_sequence, gleam@list:index_fold( _pipe@1, Diff_map, fun(Diff_map@1, Item_first, Index_first) -> build_diff_map( Item_first, Index_first, Item_second, Index_second, Diff_map@1 ) end ) end ) end, Tracking = begin _pipe@2 = back_track( Diff_map@2, gleam@list:length(First_sequence) - 1, gleam@list:length(Second_sequence) - 1, [] ), gleam@map:from_list(_pipe@2) end, First_segments = collect_matches( Tracking, First_sequence, fun(Key) -> {First, _} = Key, First end ), Second_segments = collect_matches( Tracking, Second_sequence, fun(Key@1) -> {_, Second} = Key@1, Second end ), {list_comparison, First_segments, Second_segments}. -spec collect_matches( gleam@map:map_({integer(), integer()}, any()), list(FFR), fun(({integer(), integer()}) -> integer()) ) -> list(gap@comparison:match(list(FFR))). collect_matches(Tracking, Str, Extract_fun) -> Matching_indexes = begin _pipe = gleam@map:keys(Tracking), _pipe@1 = gleam@list:map(_pipe, Extract_fun), gleam@set:from_list(_pipe@1) end, Matches = begin _pipe@2 = Str, gleam@list:index_map( _pipe@2, fun(Index, Item) -> case gleam@set:contains(Matching_indexes, Index) of true -> {match, Item}; false -> {no_match, Item} end end ) end, _pipe@3 = Matches, _pipe@4 = gleam@list:chunk(_pipe@3, fun(Match) -> case Match of {match, _} -> true; {no_match, _} -> false end end), gleam@list:map(_pipe@4, fun(Match_list) -> case Match_list of [{match, _} | _] -> {match, gleam@list:filter_map( Match_list, fun(Match@1) -> case Match@1 of {match, Item@1} -> {ok, Item@1}; {no_match, _} -> {error, nil} end end )}; [{no_match, _} | _] -> {no_match, gleam@list:filter_map( Match_list, fun(Match@2) -> case Match@2 of {no_match, Item@2} -> {ok, Item@2}; {match, _} -> {error, nil} end end )} end end). -spec back_track( gleam@map:map_({integer(), integer()}, score(FFV)), integer(), integer(), list({{integer(), integer()}, FFV}) ) -> list({{integer(), integer()}, FFV}). back_track(Diff_map, First_index, Second_index, Stack) -> case (First_index =:= 0) orelse (Second_index =:= 0) of true -> This_score = begin _pipe = gleam@map:get(Diff_map, {First_index, Second_index}), gleam@result:unwrap(_pipe, {score, 0, none}) end, case This_score of {score, _, {some, Item}} -> [{{First_index, Second_index}, Item} | Stack]; _ -> case {First_index, Second_index} of {0, A} when A > 0 -> back_track( Diff_map, First_index, Second_index - 1, Stack ); {A@1, 0} when A@1 > 0 -> back_track( Diff_map, First_index - 1, Second_index, Stack ); {0, 0} -> Stack; {_, _} -> back_track( Diff_map, First_index - 1, Second_index, Stack ) end end; false -> This_score@1 = begin _pipe@1 = gleam@map:get(Diff_map, {First_index, Second_index}), gleam@result:unwrap(_pipe@1, {score, 0, none}) end, case This_score@1 of {score, _, {some, Item@1}} -> back_track( Diff_map, First_index - 1, Second_index - 1, [{{First_index, Second_index}, Item@1} | Stack] ); {score, _, none} -> Up = begin _pipe@2 = gleam@map:get( Diff_map, {First_index, Second_index - 1} ), gleam@result:unwrap(_pipe@2, {score, 0, none}) end, Back = begin _pipe@3 = gleam@map:get( Diff_map, {First_index - 1, Second_index} ), gleam@result:unwrap(_pipe@3, {score, 0, none}) end, case gleam@int:compare( erlang:element(2, Up), erlang:element(2, Back) ) of gt -> back_track( Diff_map, First_index, Second_index - 1, Stack ); lt -> back_track( Diff_map, First_index - 1, Second_index, Stack ); eq -> case {First_index, Second_index} of {0, A@2} when A@2 > 0 -> back_track( Diff_map, First_index, Second_index - 1, Stack ); {A@3, 0} when A@3 > 0 -> back_track( Diff_map, First_index - 1, Second_index, Stack ); {0, 0} -> Stack; {_, _} -> back_track( Diff_map, First_index - 1, Second_index, Stack ) end end end end. -spec build_diff_map( FGB, integer(), FGB, integer(), gleam@map:map_({integer(), integer()}, score(FGB)) ) -> gleam@map:map_({integer(), integer()}, score(FGB)). build_diff_map(First_item, First_index, Second_item, Second_index, Diff_map) -> Prev_score = begin _pipe = gleam@map:get(Diff_map, {First_index - 1, Second_index - 1}), gleam@result:unwrap(_pipe, {score, 0, none}) end, Derived_score_up = begin _pipe@1 = Diff_map, _pipe@2 = gleam@map:get(_pipe@1, {First_index, Second_index - 1}), gleam@result:unwrap(_pipe@2, {score, 0, none}) end, Derived_score_back = begin _pipe@3 = Diff_map, _pipe@4 = gleam@map:get(_pipe@3, {First_index - 1, Second_index}), gleam@result:unwrap(_pipe@4, {score, 0, none}) end, Derived_score = gleam@int:max( erlang:element(2, Derived_score_up), erlang:element(2, Derived_score_back) ), This_score = case First_item =:= Second_item of true -> {score, erlang:element(2, Prev_score) + 1, {some, First_item}}; false -> {score, Derived_score, none} end, _pipe@5 = Diff_map, gleam@map:insert(_pipe@5, {First_index, Second_index}, This_score).