src/gliff@internal@patience.erl

-module(gliff@internal@patience).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gliff/internal/patience.gleam").
-export([diff/2]).
-export_type([match/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(false).

-type match() :: {match, integer(), integer(), binary()}.

-file("src/gliff/internal/patience.gleam", 48).
?DOC(false).
-spec count_occurrences(list(binary()), gleam@dict:dict(binary(), integer())) -> gleam@dict:dict(binary(), integer()).
count_occurrences(Lines, Acc) ->
    case Lines of
        [] ->
            Acc;

        [Line | Rest] ->
            Count = case gleam_stdlib:map_get(Acc, Line) of
                {ok, N} ->
                    N + 1;

                {error, _} ->
                    1
            end,
            count_occurrences(Rest, gleam@dict:insert(Acc, Line, Count))
    end.

-file("src/gliff/internal/patience.gleam", 64).
?DOC(false).
-spec index_lines(
    list(binary()),
    integer(),
    gleam@dict:dict(binary(), integer())
) -> gleam@dict:dict(binary(), integer()).
index_lines(Lines, Idx, Acc) ->
    case Lines of
        [] ->
            Acc;

        [Line | Rest] ->
            index_lines(Rest, Idx + 1, gleam@dict:insert(Acc, Line, Idx))
    end.

-file("src/gliff/internal/patience.gleam", 33).
?DOC(false).
-spec find_unique(list(binary())) -> gleam@dict:dict(binary(), integer()).
find_unique(Lines) ->
    Counts = count_occurrences(Lines, maps:new()),
    Indexed = index_lines(Lines, 0, maps:new()),
    gleam@dict:fold(
        Counts,
        maps:new(),
        fun(Acc, Key, Count) -> case Count =:= 1 of
                true ->
                    case gleam_stdlib:map_get(Indexed, Key) of
                        {ok, Idx} ->
                            gleam@dict:insert(Acc, Key, Idx);

                        {error, _} ->
                            Acc
                    end;

                false ->
                    Acc
            end end
    ).

-file("src/gliff/internal/patience.gleam", 113).
?DOC(false).
-spec insert_into_tails(
    list({integer(), list(match())}),
    match(),
    list({integer(), list(match())})
) -> list({integer(), list(match())}).
insert_into_tails(Tails, M, Acc) ->
    case Tails of
        [] ->
            lists:reverse([{erlang:element(3, M), [M]} | Acc]);

        [{Tail_val, Seq} | Rest] ->
            case erlang:element(3, M) =< Tail_val of
                true ->
                    New_seq = case Acc of
                        [] ->
                            [M];

                        [{_, Prev_seq} | _] ->
                            [M | Prev_seq]
                    end,
                    lists:append(
                        lists:reverse([{erlang:element(3, M), New_seq} | Acc]),
                        Rest
                    );

                false ->
                    insert_into_tails(Rest, M, [{Tail_val, Seq} | Acc])
            end
    end.

-file("src/gliff/internal/patience.gleam", 96).
?DOC(false).
-spec lis_loop(list(match()), list({integer(), list(match())})) -> list(match()).
lis_loop(Remaining, Tails) ->
    case Remaining of
        [] ->
            case gleam@list:last(Tails) of
                {ok, {_, Seq}} ->
                    lists:reverse(Seq);

                {error, _} ->
                    []
            end;

        [M | Rest] ->
            New_tails = insert_into_tails(Tails, M, []),
            lis_loop(Rest, New_tails)
    end.

-file("src/gliff/internal/patience.gleam", 88).
?DOC(false).
-spec longest_increasing_subsequence(list(match())) -> list(match()).
longest_increasing_subsequence(Matches) ->
    case Matches of
        [] ->
            [];

        [Only] ->
            [Only];

        _ ->
            lis_loop(Matches, [])
    end.

-file("src/gliff/internal/patience.gleam", 168).
?DOC(false).
-spec slice(list(EXU), integer(), integer()) -> list(EXU).
slice(Items, From, To) ->
    _pipe = Items,
    _pipe@1 = gleam@list:drop(_pipe, From),
    gleam@list:take(_pipe@1, To - From).

-file("src/gliff/internal/patience.gleam", 174).
?DOC(false).
-spec prepend_reversed(list(EXX), list(EXX)) -> list(EXX).
prepend_reversed(Items, Onto) ->
    case Items of
        [] ->
            Onto;

        [H | T] ->
            prepend_reversed(T, [H | Onto])
    end.

-file("src/gliff/internal/patience.gleam", 135).
?DOC(false).
-spec diff_between_anchors(
    list(binary()),
    list(binary()),
    list(match()),
    integer(),
    integer(),
    list(gliff@types:raw_edit())
) -> list(gliff@types:raw_edit()).
diff_between_anchors(Old, New, Anchors, Old_pos, New_pos, Acc) ->
    case Anchors of
        [] ->
            Old_rest = gleam@list:drop(Old, Old_pos),
            New_rest = gleam@list:drop(New, New_pos),
            Tail_edits = gliff@internal@myers:diff(Old_rest, New_rest),
            prepend_reversed(Tail_edits, Acc);

        [{match, Old_idx, New_idx, Value} | Rest_anchors] ->
            Old_before = slice(Old, Old_pos, Old_idx),
            New_before = slice(New, New_pos, New_idx),
            Gap_edits = gliff@internal@myers:diff(Old_before, New_before),
            Acc1 = prepend_reversed(Gap_edits, Acc),
            Acc2 = [{raw_equal, Value} | Acc1],
            diff_between_anchors(
                Old,
                New,
                Rest_anchors,
                Old_idx + 1,
                New_idx + 1,
                Acc2
            )
    end.

-file("src/gliff/internal/patience.gleam", 181).
?DOC(false).
-spec compare_int(integer(), integer()) -> gleam@order:order().
compare_int(A, B) ->
    case A < B of
        true ->
            lt;

        false ->
            case A =:= B of
                true ->
                    eq;

                false ->
                    gt
            end
    end.

-file("src/gliff/internal/patience.gleam", 75).
?DOC(false).
-spec find_matches(
    gleam@dict:dict(binary(), integer()),
    gleam@dict:dict(binary(), integer())
) -> list(match()).
find_matches(Old_unique, New_unique) ->
    _pipe = gleam@dict:fold(
        Old_unique,
        [],
        fun(Acc, Line, Old_idx) ->
            case gleam_stdlib:map_get(New_unique, Line) of
                {ok, New_idx} ->
                    [{match, Old_idx, New_idx, Line} | Acc];

                {error, _} ->
                    Acc
            end
        end
    ),
    gleam@list:sort(
        _pipe,
        fun(A, B) -> compare_int(erlang:element(2, A), erlang:element(2, B)) end
    ).

-file("src/gliff/internal/patience.gleam", 7).
?DOC(false).
-spec diff(list(binary()), list(binary())) -> list(gliff@types:raw_edit()).
diff(Old, New) ->
    case {Old, New} of
        {[], []} ->
            [];

        {[], _} ->
            gleam@list:map(New, fun(Field@0) -> {raw_insert, Field@0} end);

        {_, []} ->
            gleam@list:map(Old, fun(Field@0) -> {raw_delete, Field@0} end);

        {_, _} ->
            Old_unique = find_unique(Old),
            New_unique = find_unique(New),
            Matches = find_matches(Old_unique, New_unique),
            case Matches of
                [] ->
                    gliff@internal@myers:diff(Old, New);

                _ ->
                    Lis = longest_increasing_subsequence(Matches),
                    _pipe = diff_between_anchors(Old, New, Lis, 0, 0, []),
                    lists:reverse(_pipe)
            end
    end.