diff options
Diffstat (limited to 'aoc2023/build/packages/gap/src')
-rw-r--r-- | aoc2023/build/packages/gap/src/gap.app.src | 13 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap.erl | 538 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap.gleam | 438 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap/comparison.gleam | 22 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap/myers.gleam | 122 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap/styled_comparison.gleam | 4 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap/styling.gleam | 233 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap@comparison.erl | 15 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap@myers.erl | 156 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap@styled_comparison.erl | 8 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap@styling.erl | 202 | ||||
-rw-r--r-- | aoc2023/build/packages/gap/src/gap_ffi.mjs | 431 |
12 files changed, 0 insertions, 2182 deletions
diff --git a/aoc2023/build/packages/gap/src/gap.app.src b/aoc2023/build/packages/gap/src/gap.app.src deleted file mode 100644 index 1abc7df..0000000 --- a/aoc2023/build/packages/gap/src/gap.app.src +++ /dev/null @@ -1,13 +0,0 @@ -{application, gap, [ - {vsn, "1.0.1"}, - {applications, [gleam_community_ansi, - gleam_stdlib, - gleeunit]}, - {description, "A Gleam library for comparing strings/lists and producing a textual (styled) representation of the differences."}, - {modules, [gap, - gap@comparison, - gap@myers, - gap@styled_comparison, - gap@styling]}, - {registered, []} -]}. diff --git a/aoc2023/build/packages/gap/src/gap.erl b/aoc2023/build/packages/gap/src/gap.erl deleted file mode 100644 index 827e5ce..0000000 --- a/aoc2023/build/packages/gap/src/gap.erl +++ /dev/null @@ -1,538 +0,0 @@ --module(gap). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([to_styled/1, compare_strings_with_algorithm/3, compare_lists_with_algorithm/3, myers/2, compare_lists/2, compare_strings/2, lcs/2]). --export_type([score/1]). - --type score(GAM) :: {score, integer(), gleam@option:option(GAM)}. - --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_with_algorithm( - binary(), - binary(), - fun((list(binary()), list(binary())) -> gap@comparison:comparison(binary())) -) -> gap@comparison:comparison(binary()). -compare_strings_with_algorithm(First, Second, Algorithm) -> - Comparison = Algorithm( - 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_with_algorithm( - list(GBB), - list(GBB), - fun((list(GBB), list(GBB)) -> gap@comparison:comparison(GBB)) -) -> gap@comparison:comparison(GBB). -compare_lists_with_algorithm(First_sequence, Second_sequence, Algorithm) -> - Algorithm(First_sequence, Second_sequence). - --spec myers(list(GBG), list(GBG)) -> gap@comparison:comparison(GBG). -myers(First_sequence, Second_sequence) -> - Edits = gap@myers:difference(First_sequence, Second_sequence), - _pipe = Edits, - _pipe@1 = gleam@list:reverse(_pipe), - gleam@list:fold( - _pipe@1, - {list_comparison, [], []}, - fun(Comparison, Edit) -> case Comparison of - {list_comparison, First, Second} -> - case Edit of - {eq, Segment} -> - {list_comparison, - [{match, Segment} | First], - [{match, Segment} | Second]}; - - {ins, Segment@1} -> - {list_comparison, - First, - [{no_match, Segment@1} | Second]}; - - {del, Segment@2} -> - {list_comparison, - [{no_match, Segment@2} | First], - Second} - end; - - {string_comparison, _, _} -> - Comparison - end end - ). - --spec compare_lists(list(GAX), list(GAX)) -> gap@comparison:comparison(GAX). -compare_lists(First_sequence, Second_sequence) -> - myers(First_sequence, Second_sequence). - --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 prepend_and_merge( - list(gap@comparison:match(list(GBO))), - gap@comparison:match(list(GBO)) -) -> list(gap@comparison:match(list(GBO))). -prepend_and_merge(Matches, Match) -> - case {Matches, Match} of - {[], _} -> - [Match]; - - {[{match, First_match} | Rest], {match, _}} -> - [{match, - begin - _pipe = erlang:element(2, Match), - gleam@list:append(_pipe, First_match) - end} | - Rest]; - - {[{no_match, First_match@1} | Rest@1], {no_match, _}} -> - [{no_match, - begin - _pipe@1 = erlang:element(2, Match), - gleam@list:append(_pipe@1, First_match@1) - end} | - Rest@1]; - - {Matches@1, Match@1} -> - [Match@1 | Matches@1] - end. - --spec append_and_merge( - list(gap@comparison:match(list(GBX))), - gap@comparison:match(list(GBX)) -) -> list(gap@comparison:match(list(GBX))). -append_and_merge(Matches, Match) -> - _pipe@3 = case {begin - _pipe = Matches, - gleam@list:reverse(_pipe) - end, - Match} of - {[], _} -> - [Match]; - - {[{match, First_match} | Rest], {match, _}} -> - [{match, - begin - _pipe@1 = First_match, - gleam@list:append(_pipe@1, erlang:element(2, Match)) - end} | - Rest]; - - {[{no_match, First_match@1} | Rest@1], {no_match, _}} -> - [{no_match, - begin - _pipe@2 = First_match@1, - gleam@list:append(_pipe@2, erlang:element(2, Match)) - end} | - Rest@1]; - - {Matches@1, Match@1} -> - [Match@1 | Matches@1] - end, - gleam@list:reverse(_pipe@3). - --spec collect_matches( - gleam@map:map_(GFY, any()), - list(GCH), - fun((GFY) -> integer()) -) -> list(gap@comparison:match(list(GCH))). -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(GCL)), - integer(), - integer(), - list({{integer(), integer()}, GCL}) -) -> list({{integer(), integer()}, GCL}). -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( - GCR, - integer(), - GCR, - integer(), - gleam@map:map_({integer(), integer()}, score(GCR)) -) -> gleam@map:map_({integer(), integer()}, score(GCR)). -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). - --spec lcs(list(GBK), list(GBK)) -> gap@comparison:comparison(GBK). -lcs(First_sequence, Second_sequence) -> - Leading_matches = begin - _pipe = gleam@list:zip(First_sequence, Second_sequence), - _pipe@1 = gleam@list:take_while( - _pipe, - fun(Pair) -> erlang:element(1, Pair) =:= erlang:element(2, Pair) end - ), - gleam@list:map(_pipe@1, fun gleam@pair:first/1) - end, - Num_leading_matches = gleam@list:length(Leading_matches), - Trailing_matches = begin - _pipe@2 = gleam@list:zip( - gleam@list:reverse(First_sequence), - gleam@list:reverse(Second_sequence) - ), - _pipe@3 = gleam@list:take_while( - _pipe@2, - fun(Pair@1) -> - erlang:element(1, Pair@1) =:= erlang:element(2, Pair@1) - end - ), - _pipe@4 = gleam@list:map(_pipe@3, fun gleam@pair:first/1), - gleam@list:reverse(_pipe@4) - end, - Num_trailing_matches = gleam@list:length(Trailing_matches), - First_sequence_to_diff = begin - _pipe@5 = First_sequence, - _pipe@6 = gleam@list:drop(_pipe@5, Num_leading_matches), - gleam@list:take( - _pipe@6, - (gleam@list:length(First_sequence) - Num_leading_matches) - Num_trailing_matches - ) - end, - Second_sequence_to_diff = begin - _pipe@7 = Second_sequence, - _pipe@8 = gleam@list:drop(_pipe@7, Num_leading_matches), - gleam@list:take( - _pipe@8, - (gleam@list:length(Second_sequence) - Num_leading_matches) - Num_trailing_matches - ) - end, - Diff_map@2 = begin - _pipe@9 = Second_sequence_to_diff, - gleam@list:index_fold( - _pipe@9, - gleam@map:new(), - fun(Diff_map, Item_second, Index_second) -> - _pipe@10 = First_sequence_to_diff, - gleam@list:index_fold( - _pipe@10, - 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, - {First_segments@1, Second_segments@1} = case {First_sequence_to_diff, - Second_sequence_to_diff} of - {[], []} -> - {[], []}; - - {First_matching, []} -> - {[{no_match, First_matching}], []}; - - {[], Second_matching} -> - {[], [{no_match, Second_matching}]}; - - {First_sequence_to_diff@1, Second_sequence_to_diff@1} -> - Tracking = begin - _pipe@11 = back_track( - Diff_map@2, - gleam@list:length(First_sequence_to_diff@1) - 1, - gleam@list:length(Second_sequence_to_diff@1) - 1, - [] - ), - gleam@map:from_list(_pipe@11) - end, - First_segments = collect_matches( - Tracking, - First_sequence_to_diff@1, - fun(Key) -> - {First, _} = Key, - First - end - ), - Second_segments = collect_matches( - Tracking, - Second_sequence_to_diff@1, - fun(Key@1) -> - {_, Second} = Key@1, - Second - end - ), - {First_segments, Second_segments} - end, - {First_segments_with_leading_trailing, - Second_segments_with_leading_trailing} = case {Leading_matches, - Trailing_matches} of - {[], []} -> - {First_segments@1, Second_segments@1}; - - {[], Trailing_matches@1} -> - {begin - _pipe@12 = First_segments@1, - append_and_merge(_pipe@12, {match, Trailing_matches@1}) - end, - begin - _pipe@13 = Second_segments@1, - append_and_merge(_pipe@13, {match, Trailing_matches@1}) - end}; - - {Leading_matches@1, []} -> - {begin - _pipe@14 = First_segments@1, - prepend_and_merge(_pipe@14, {match, Leading_matches@1}) - end, - begin - _pipe@15 = Second_segments@1, - prepend_and_merge(_pipe@15, {match, Leading_matches@1}) - end}; - - {Leading_matches@2, Trailing_matches@2} -> - {begin - _pipe@16 = First_segments@1, - _pipe@17 = prepend_and_merge( - _pipe@16, - {match, Leading_matches@2} - ), - append_and_merge(_pipe@17, {match, Trailing_matches@2}) - end, - begin - _pipe@18 = Second_segments@1, - _pipe@19 = prepend_and_merge( - _pipe@18, - {match, Leading_matches@2} - ), - append_and_merge(_pipe@19, {match, Trailing_matches@2}) - end} - end, - {list_comparison, - First_segments_with_leading_trailing, - Second_segments_with_leading_trailing}. diff --git a/aoc2023/build/packages/gap/src/gap.gleam b/aoc2023/build/packages/gap/src/gap.gleam deleted file mode 100644 index 7eb0e7f..0000000 --- a/aoc2023/build/packages/gap/src/gap.gleam +++ /dev/null @@ -1,438 +0,0 @@ -import gleam/string -import gleam/list -import gleam/pair -import gleam/map.{type Map} -import gleam/result -import gleam/option.{type Option, None, Some} -import gleam/int -import gleam/order.{Eq, Gt, Lt} -import gleam/set -import gap/comparison.{ - type Comparison, type Match, type Segments, ListComparison, Match, NoMatch, - StringComparison, -} -import gap/styled_comparison.{type StyledComparison} -import gap/styling.{ - first_highlight_default, from_comparison, highlight, no_highlight, - second_highlight_default, to_styled_comparison, -} -import gap/myers.{type Edit, Del, Eq as MyerEq, Ins} - -type MatchedItem(a) = - #(#(Int, Int), a) - -type Score(a) { - Score(value: Int, item: Option(a)) -} - -type DiffMap(a) = - Map(#(Int, Int), Score(a)) - -/// Creates a `StyledComparison` from `Comparison` using default values for -/// highting and serialization. -/// -/// ## Example -/// -/// ```gleam -/// > compare_strings("abc", "abe") |> to_styled() -/// ``` -/// This will return a `StyledComparison(first, second)` where "c" in `first` is green -/// and "e" in `second` is red. -pub fn to_styled(comparison: Comparison(a)) -> StyledComparison { - comparison - |> from_comparison() - |> highlight(first_highlight_default, second_highlight_default, no_highlight) - |> to_styled_comparison() -} - -/// Compare two string and return a `StringComparison` which will be styled as string -/// when passed to `to_styled` -/// -/// Will use the default `myers` algorithm -pub fn compare_strings(first: String, second: String) -> Comparison(String) { - let comparison = - compare_lists(string.to_graphemes(first), string.to_graphemes(second)) - case comparison { - ListComparison(first, second) -> StringComparison(first, second) - StringComparison(first, second) -> StringComparison(first, second) - } -} - -/// Compare two string and return a `StringComparison` which will be styled as string -/// when passed to `to_styled` -/// -/// Algorithm can be used to select either `myers` or the legacy `lcs` algorithm -pub fn compare_strings_with_algorithm( - first: String, - second: String, - algorithm, -) -> Comparison(String) { - let comparison = - algorithm(string.to_graphemes(first), string.to_graphemes(second)) - case comparison { - ListComparison(first, second) -> StringComparison(first, second) - StringComparison(first, second) -> StringComparison(first, second) - } -} - -/// Compare two lists and return a `ListComparison` which will be styled as list -/// when passed to `to_styled` -/// -/// Will use the default `myers` algorithm -pub fn compare_lists( - first_sequence: List(a), - second_sequence: List(a), -) -> Comparison(a) { - myers(first_sequence, second_sequence) -} - -/// Compare two lists and return a `ListComparison` which will be styled as list -/// when passed to `to_styled` -/// -/// Algorithm can be used to select either `myers` or the legacy `lcs` algorithm -pub fn compare_lists_with_algorithm( - first_sequence: List(a), - second_sequence: List(a), - algorithm, -) -> Comparison(a) { - algorithm(first_sequence, second_sequence) -} - -/// An adapter for the the `myers` algorithm. -/// Intended to be use as an argument to `compare_strings_with_algorithm` or -/// `compare_lists_with_algorithm` -pub fn myers(first_sequence: List(a), second_sequence: List(a)) -> Comparison(a) { - let edits = myers.difference(first_sequence, second_sequence) - edits - |> list.reverse() - |> list.fold( - ListComparison([], []), - fn(comparison: Comparison(a), edit: Edit(a)) { - case comparison { - ListComparison(first, second) -> { - case edit { - MyerEq(segment) -> - ListComparison( - [Match(segment), ..first], - [Match(segment), ..second], - ) - Ins(segment) -> ListComparison(first, [NoMatch(segment), ..second]) - Del(segment) -> ListComparison([NoMatch(segment), ..first], second) - } - } - StringComparison(..) -> comparison - } - }, - ) -} - -/// An adapter for the the `lcs` (longest common subsequence) algorithm. -/// Intended to be use as an argument to `compare_strings_with_algorithm` or -/// `compare_lists_with_algorithm` -pub fn lcs(first_sequence: List(a), second_sequence: List(a)) -> Comparison(a) { - let leading_matches = - list.zip(first_sequence, second_sequence) - |> list.take_while(fn(pair) { pair.0 == pair.1 }) - |> list.map(pair.first) - let num_leading_matches = list.length(leading_matches) - let trailing_matches = - list.zip(list.reverse(first_sequence), list.reverse(second_sequence)) - |> list.take_while(fn(pair) { pair.0 == pair.1 }) - |> list.map(pair.first) - |> list.reverse() - let num_trailing_matches = list.length(trailing_matches) - let first_sequence_to_diff = - first_sequence - |> list.drop(num_leading_matches) - |> list.take( - list.length(first_sequence) - num_leading_matches - num_trailing_matches, - ) - let second_sequence_to_diff = - second_sequence - |> list.drop(num_leading_matches) - |> list.take( - list.length(second_sequence) - num_leading_matches - num_trailing_matches, - ) - - let diff_map = - second_sequence_to_diff - |> list.index_fold( - map.new(), - fn(diff_map, item_second, index_second) { - first_sequence_to_diff - |> list.index_fold( - diff_map, - fn(diff_map, item_first, index_first) { - build_diff_map( - item_first, - index_first, - item_second, - index_second, - diff_map, - ) - }, - ) - }, - ) - let #(first_segments, second_segments) = case - first_sequence_to_diff, - second_sequence_to_diff - { - [], [] -> #([], []) - first_matching, [] -> #([NoMatch(first_matching)], []) - [], second_matching -> #([], [NoMatch(second_matching)]) - first_sequence_to_diff, second_sequence_to_diff -> { - let tracking = - back_track( - diff_map, - list.length(first_sequence_to_diff) - 1, - list.length(second_sequence_to_diff) - 1, - [], - ) - |> map.from_list() - - let first_segments = - collect_matches( - tracking, - first_sequence_to_diff, - fn(key) { - let #(first, _) = key - first - }, - ) - let second_segments = - collect_matches( - tracking, - second_sequence_to_diff, - fn(key) { - let #(_, second) = key - second - }, - ) - #(first_segments, second_segments) - } - } - - let #( - first_segments_with_leading_trailing, - second_segments_with_leading_trailing, - ) = case leading_matches, trailing_matches { - [], [] -> #(first_segments, second_segments) - [], trailing_matches -> #( - first_segments - |> append_and_merge(Match(trailing_matches)), - second_segments - |> append_and_merge(Match(trailing_matches)), - ) - leading_matches, [] -> #( - first_segments - |> prepend_and_merge(Match(leading_matches)), - second_segments - |> prepend_and_merge(Match(leading_matches)), - ) - leading_matches, trailing_matches -> #( - first_segments - |> prepend_and_merge(Match(leading_matches)) - |> append_and_merge(Match(trailing_matches)), - second_segments - |> prepend_and_merge(Match(leading_matches)) - |> append_and_merge(Match(trailing_matches)), - ) - } - - ListComparison( - first_segments_with_leading_trailing, - second_segments_with_leading_trailing, - ) -} - -fn prepend_and_merge( - matches: List(Match(List(a))), - match: Match(List(a)), -) -> List(Match(List(a))) { - case matches, match { - [], _ -> [match] - [Match(first_match), ..rest], Match(_) -> [ - Match( - match.item - |> list.append(first_match), - ), - ..rest - ] - [NoMatch(first_match), ..rest], NoMatch(_) -> [ - NoMatch( - match.item - |> list.append(first_match), - ), - ..rest - ] - matches, match -> [match, ..matches] - } -} - -fn append_and_merge( - matches: List(Match(List(a))), - match: Match(List(a)), -) -> List(Match(List(a))) { - case - matches - |> list.reverse(), - match - { - [], _ -> [match] - [Match(first_match), ..rest], Match(_) -> [ - Match( - first_match - |> list.append(match.item), - ), - ..rest - ] - [NoMatch(first_match), ..rest], NoMatch(_) -> [ - NoMatch( - first_match - |> list.append(match.item), - ), - ..rest - ] - matches, match -> [match, ..matches] - } - |> list.reverse() -} - -fn collect_matches(tracking, str: List(a), extract_fun) -> Segments(a) { - let matching_indexes = - map.keys(tracking) - |> list.map(extract_fun) - |> set.from_list() - - let matches = - str - |> list.index_map(fn(index, item) { - case set.contains(matching_indexes, index) { - True -> Match(item) - False -> NoMatch(item) - } - }) - - matches - |> list.chunk(fn(match) { - case match { - Match(_) -> True - NoMatch(_) -> False - } - }) - |> list.map(fn(match_list) { - case match_list { - [Match(_), ..] -> - Match(list.filter_map( - match_list, - fn(match) { - case match { - Match(item) -> Ok(item) - NoMatch(_) -> Error(Nil) - } - }, - )) - [NoMatch(_), ..] -> - NoMatch(list.filter_map( - match_list, - fn(match) { - case match { - NoMatch(item) -> Ok(item) - Match(_) -> Error(Nil) - } - }, - )) - } - }) -} - -fn back_track( - diff_map: DiffMap(a), - first_index: Int, - second_index: Int, - stack: List(MatchedItem(a)), -) -> List(MatchedItem(a)) { - case first_index == 0 || second_index == 0 { - True -> { - let this_score = - map.get(diff_map, #(first_index, second_index)) - |> result.unwrap(Score(0, None)) - case this_score { - Score(_, Some(item)) -> [#(#(first_index, second_index), item), ..stack] - _ -> - case first_index, second_index { - 0, a if a > 0 -> - back_track(diff_map, first_index, second_index - 1, stack) - a, 0 if a > 0 -> - back_track(diff_map, first_index - 1, second_index, stack) - 0, 0 -> stack - _, _ -> back_track(diff_map, first_index - 1, second_index, stack) - } - } - } - False -> { - let this_score = - map.get(diff_map, #(first_index, second_index)) - |> result.unwrap(Score(0, None)) - case this_score { - Score(_, Some(item)) -> - back_track( - diff_map, - first_index - 1, - second_index - 1, - [#(#(first_index, second_index), item), ..stack], - ) - Score(_, None) -> { - let up = - map.get(diff_map, #(first_index, second_index - 1)) - |> result.unwrap(Score(0, None)) - let back = - map.get(diff_map, #(first_index - 1, second_index)) - |> result.unwrap(Score(0, None)) - case int.compare(up.value, back.value) { - 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 { - 0, a if a > 0 -> - back_track(diff_map, first_index, second_index - 1, stack) - a, 0 if a > 0 -> - back_track(diff_map, first_index - 1, second_index, stack) - 0, 0 -> stack - _, _ -> - back_track(diff_map, first_index - 1, second_index, stack) - } - } - } - } - } - } -} - -fn build_diff_map( - first_item: a, - first_index: Int, - second_item: a, - second_index: Int, - diff_map: DiffMap(a), -) -> DiffMap(a) { - let prev_score = - map.get(diff_map, #(first_index - 1, second_index - 1)) - |> result.unwrap(Score(0, None)) - let derived_score_up = - diff_map - |> map.get(#(first_index, second_index - 1)) - |> result.unwrap(Score(0, None)) - let derived_score_back = - diff_map - |> map.get(#(first_index - 1, second_index)) - |> result.unwrap(Score(0, None)) - let derived_score = int.max(derived_score_up.value, derived_score_back.value) - let this_score = case first_item == second_item { - True -> Score(prev_score.value + 1, Some(first_item)) - False -> Score(derived_score, None) - } - diff_map - |> map.insert(#(first_index, second_index), this_score) -} diff --git a/aoc2023/build/packages/gap/src/gap/comparison.gleam b/aoc2023/build/packages/gap/src/gap/comparison.gleam deleted file mode 100644 index da30c29..0000000 --- a/aoc2023/build/packages/gap/src/gap/comparison.gleam +++ /dev/null @@ -1,22 +0,0 @@ -/// Comparison of two strings or lists -/// -/// The comparison consists of two lists of matched segments. The segments represent -/// a sequence of succeeding matches or non-matches (up until the next match/non-match) -/// -/// For lists the elements in the segment will be same as the elements in the list, and -/// for strings the elements will be the graphemes of the string -pub type Comparison(a) { - ListComparison(first: Segments(a), second: Segments(a)) - StringComparison(first: Segments(String), second: Segments(String)) -} - -/// Indicating that the item has a matching (`Match`) or no matching (`NoMatch`) item in the -/// other string/list -pub type Match(a) { - Match(item: a) - NoMatch(item: a) -} - -/// List of segments of succeeding matches / non-matches -pub type Segments(a) = - List(Match(List(a))) diff --git a/aoc2023/build/packages/gap/src/gap/myers.gleam b/aoc2023/build/packages/gap/src/gap/myers.gleam deleted file mode 100644 index f0b8ddc..0000000 --- a/aoc2023/build/packages/gap/src/gap/myers.gleam +++ /dev/null @@ -1,122 +0,0 @@ -import gleam/list - -pub type Edit(a) { - Eq(List(a)) - Del(List(a)) - Ins(List(a)) -} - -type Path(a) { - Path(x: Int, y: Int, list1: List(a), list2: List(a), edits: List(Edit(a))) -} - -type Status(a) { - Done(edits: List(Edit(a))) - Next(paths: List(Path(a))) - Cont(path: Path(a)) -} - -/// The algorithm is outlined in the -/// "An O(ND) Difference Algorithm and Its Variations" paper by E. Myers. -/// -/// Adapted from the implementation of "myers_difference" in Elixirs List module -pub fn difference(list1: List(a), list2: List(a)) -> List(Edit(a)) { - let path = Path(0, 0, list1, list2, []) - find_script(0, list.length(list1) + list.length(list2), [path]) -} - -fn find_script(envelope: Int, max: Int, paths: List(Path(a))) { - case envelope > max { - True -> [] - False -> { - case each_diagonal(-envelope, envelope, paths, []) { - Done(edits) -> compact_reverse(edits, []) - Next(paths) -> find_script(envelope + 1, max, paths) - _ -> panic as "Didn't expect a Cont here" - } - } - } -} - -fn compact_reverse(edits: List(Edit(a)), acc: List(Edit(a))) -> List(Edit(a)) { - case edits, acc { - [], acc -> acc - [Eq(elem), ..rest], [Eq(result), ..acc_rest] -> - compact_reverse(rest, [Eq(list.flatten([elem, result])), ..acc_rest]) - [Del(elem), ..rest], [Del(result), ..acc_rest] -> - compact_reverse(rest, [Del(list.flatten([elem, result])), ..acc_rest]) - [Ins(elem), ..rest], [Ins(result), ..acc_rest] -> - compact_reverse(rest, [Ins(list.flatten([elem, result])), ..acc_rest]) - [Eq(elem), ..rest], acc -> compact_reverse(rest, [Eq(elem), ..acc]) - [Del(elem), ..rest], acc -> compact_reverse(rest, [Del(elem), ..acc]) - [Ins(elem), ..rest], acc -> compact_reverse(rest, [Ins(elem), ..acc]) - } -} - -fn each_diagonal( - diag: Int, - limit: Int, - paths: List(Path(a)), - next_paths: List(Path(a)), -) -> Status(a) { - case diag > limit { - True -> Next(list.reverse(next_paths)) - False -> { - let #(path, rest) = proceed_path(diag, limit, paths) - case follow_snake(path) { - Cont(path) -> each_diagonal(diag + 2, limit, rest, [path, ..next_paths]) - other -> other - } - } - } -} - -fn proceed_path( - diag: Int, - limit: Int, - paths: List(Path(a)), -) -> #(Path(a), List(Path(a))) { - let neg_limit = -limit - case diag, limit, paths { - 0, 0, [path] -> #(path, []) - diag, _limit, [path, ..] as paths if diag == neg_limit -> #( - move_down(path), - paths, - ) - diag, limit, [path, ..] as paths if diag == limit -> #( - move_right(path), - paths, - ) - _diag, _limit, [path1, path2, ..rest] -> { - case path1.y > path2.y { - True -> #(move_right(path1), [path2, ..rest]) - False -> #(move_down(path2), [path2, ..rest]) - } - } - } -} - -fn move_right(path: Path(a)) -> Path(a) { - case path { - Path(x, y, list1, [elem, ..rest], edits) -> - Path(x + 1, y, list1, rest, [Ins([elem]), ..edits]) - Path(x, y, list1, [], edits) -> Path(x + 1, y, list1, [], edits) - } -} - -fn move_down(path: Path(a)) -> Path(a) { - case path { - Path(x, y, [elem, ..rest], list2, edits) -> - Path(x, y + 1, rest, list2, [Del([elem]), ..edits]) - Path(x, y, [], list2, edits) -> Path(x, y + 1, [], list2, edits) - } -} - -fn follow_snake(path: Path(a)) -> Status(a) { - case path { - Path(x, y, [elem1, ..rest1], [elem2, ..rest2], edits) if elem1 == elem2 -> - follow_snake(Path(x + 1, y + 1, rest1, rest2, [Eq([elem1]), ..edits])) - Path(_x, _y, [], [], edits) -> Done(edits) - _ -> Cont(path) - } -} diff --git a/aoc2023/build/packages/gap/src/gap/styled_comparison.gleam b/aoc2023/build/packages/gap/src/gap/styled_comparison.gleam deleted file mode 100644 index 7103c2e..0000000 --- a/aoc2023/build/packages/gap/src/gap/styled_comparison.gleam +++ /dev/null @@ -1,4 +0,0 @@ -/// A comparison where the parts have been styled (serialized and highlighted) -pub type StyledComparison { - StyledComparison(first: String, second: String) -} diff --git a/aoc2023/build/packages/gap/src/gap/styling.gleam b/aoc2023/build/packages/gap/src/gap/styling.gleam deleted file mode 100644 index 623ae8a..0000000 --- a/aoc2023/build/packages/gap/src/gap/styling.gleam +++ /dev/null @@ -1,233 +0,0 @@ -import gleam/option.{type Option, None, Some} -import gleam/list -import gleam/string -import gleam_community/ansi -import gap/comparison.{ - type Comparison, type Segments, ListComparison, Match, NoMatch, - StringComparison, -} -import gap/styled_comparison.{type StyledComparison, StyledComparison} - -/// The `Highlighter`takes a string representation of the item that was not matching -/// and should return a string representation that can be used to visually indicate that -/// it is a non-matching item. -/// -/// The default implementation of the highlighters uses the -/// [gleam_community/ansi](https://hexdocs.pm/gleam_community_ansi/index.html) library -/// to set a different color for the item, but any type if indication can be used as long -/// as it returns a valid string -pub type Highlighter = - fn(String) -> String - -/// `Part` is used to indicate to a custom serializer if it should produce a serialization -/// based on a segment with items or the final string that contains already serialized segments -pub type Part(a) { - /// `acc` the already serialized part of the result, `part` is the current segment that should be serialized and appended and `highlighter` is the `Highlighter` that can be used to indicate non-matching items - Part(acc: String, part: List(a), highlight: Highlighter) - /// `all` is a string representing all serialized segments. This can be useful if some string should be prepended/appended to the final result - All(all: String) -} - -/// A `Serializer`can be used to create string representation of the comparison results -/// -/// See [serialize](#serialize) for adding custom serializers and [mk_generic_serializer](#mk_generic_serializer) -pub type Serializer(a) = - fn(Part(a)) -> String - -/// Highlighters to use for indicating matches / non-matches -/// -/// `first` is used to highlight non-matches in the first string/list -/// `second` is used to highlight non-matches in the second string/list -/// `matching` is used to highlight matches in the both strings/lists -pub type Highlighters { - Highlighters(first: Highlighter, second: Highlighter, matching: Highlighter) -} - -/// Styling of a `Comparison` -/// -/// See [from_comparison](#from_comparison) -pub opaque type Styling(a) { - Styling( - comparison: Comparison(a), - serializer: Option(Serializer(a)), - highlight: Option(Highlighters), - ) -} - -/// Create a new `Styling` from a `Comparison` -/// -/// The `Styling` can be customized by adding highlighters and a serializer -/// See [highlight](#highlight) and [serialize](#serialize) -pub fn from_comparison(comparison: Comparison(a)) -> Styling(a) { - Styling(comparison, None, None) -} - -/// Add highlighters to the `Styling` -/// -/// The highlighters are used to mark the matching/non-matching items in the -/// first/second list/string -pub fn highlight( - styling: Styling(a), - first: Highlighter, - second: Highlighter, - matching: Highlighter, -) -> Styling(a) { - Styling(..styling, highlight: Some(Highlighters(first, second, matching))) -} - -/// Add a serializer to the `Styling` -/// -/// The serializer is used to create string representation of the items in the segments of the `Comparison` -/// See [Part](#part) for details -/// -/// > **NOTE:** `StringComparison` will always use the default string serializer (concatenating the graphemes). -/// > If there is a need for custom serialization of `StringComparison` convert the string to a list of -/// > graphemes and treat it as a `ListComparison` -pub fn serialize(styling: Styling(a), serializer: Serializer(a)) -> Styling(a) { - Styling(..styling, serializer: Some(serializer)) -} - -/// Creates a styled comparison using either custom highlighters/serializer if they where added or default -/// highlighters and/or serializer -pub fn to_styled_comparison(styling: Styling(a)) -> StyledComparison { - let highlight = - styling.highlight - |> option.unwrap(Highlighters( - first_highlight_default, - second_highlight_default, - no_highlight, - )) - case styling.comparison { - StringComparison(first, second) -> - to_strings( - first, - second, - // NOTE: Using string serializer here because otherwise we need to have a specific string serializer on the styling - string_serializer, - highlight.first, - highlight.second, - highlight.matching, - ) - ListComparison(first, second) -> - to_strings( - first, - second, - option.unwrap(styling.serializer, generic_serializer), - highlight.first, - highlight.second, - highlight.matching, - ) - } -} - -/// Default highlighter for the first string/list in the comparison -pub fn first_highlight_default(string: String) -> String { - case string { - " " -> - string - |> ansi.underline() - |> ansi.bold() - |> ansi.green() - - _ -> - string - |> ansi.green() - |> ansi.bold() - } -} - -/// Default highlighter for the second string/list in the comparison -pub fn second_highlight_default(string: String) -> String { - case string { - " " -> - string - |> ansi.underline() - |> ansi.bold() - |> ansi.red() - - _ -> - string - |> ansi.red() - |> ansi.bold() - } -} - -/// Default highlighter used for matching items -pub fn no_highlight(string: String) -> String { - string -} - -fn string_serializer(part: Part(String)) -> String { - case part { - Part(acc, sequence, highlight) -> - acc <> { - sequence - |> list.map(highlight) - |> string.join("") - } - All(string) -> string - } -} - -fn generic_serializer(part: Part(a)) -> String { - mk_generic_serializer(", ", fn(all) { "[" <> all <> "]" })(part) -} - -/// Creates a generic serializer that uses `separator` between all items and calls -/// `around` for possibility to prepend/append strings to the final result -pub fn mk_generic_serializer(separator: String, around: fn(String) -> String) { - fn(part) { - case part { - Part(acc, sequence, highlight) -> { - let segment_separator = case acc { - "" -> "" - _ -> separator - } - acc <> segment_separator <> { - sequence - |> list.map(string.inspect) - |> list.map(highlight) - |> string.join(separator) - } - } - All(string) -> around(string) - } - } -} - -fn to_strings( - first: Segments(a), - second: Segments(a), - serializer: Serializer(a), - first_highlight: Highlighter, - second_highlight: Highlighter, - no_highlight: Highlighter, -) -> StyledComparison { - let first_styled = - first - |> list.fold( - "", - fn(str, match) { - case match { - Match(item) -> serializer(Part(str, item, no_highlight)) - NoMatch(item) -> serializer(Part(str, item, first_highlight)) - } - }, - ) - let second_styled = - second - |> list.fold( - "", - fn(str, match) { - case match { - Match(item) -> serializer(Part(str, item, no_highlight)) - NoMatch(item) -> serializer(Part(str, item, second_highlight)) - } - }, - ) - - StyledComparison( - serializer(All(first_styled)), - serializer(All(second_styled)), - ) -} diff --git a/aoc2023/build/packages/gap/src/gap@comparison.erl b/aoc2023/build/packages/gap/src/gap@comparison.erl deleted file mode 100644 index 36bd1c3..0000000 --- a/aoc2023/build/packages/gap/src/gap@comparison.erl +++ /dev/null @@ -1,15 +0,0 @@ --module(gap@comparison). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export_type([comparison/1, match/1]). - --type comparison(FQT) :: {list_comparison, - list(match(list(FQT))), - list(match(list(FQT)))} | - {string_comparison, - list(match(list(binary()))), - list(match(list(binary())))}. - --type match(FQU) :: {match, FQU} | {no_match, FQU}. - - diff --git a/aoc2023/build/packages/gap/src/gap@myers.erl b/aoc2023/build/packages/gap/src/gap@myers.erl deleted file mode 100644 index 6a8ad35..0000000 --- a/aoc2023/build/packages/gap/src/gap@myers.erl +++ /dev/null @@ -1,156 +0,0 @@ --module(gap@myers). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([difference/2]). --export_type([edit/1, path/1, status/1]). - --type edit(FUV) :: {eq, list(FUV)} | {del, list(FUV)} | {ins, list(FUV)}. - --type path(FUW) :: {path, - integer(), - integer(), - list(FUW), - list(FUW), - list(edit(FUW))}. - --type status(FUX) :: {done, list(edit(FUX))} | - {next, list(path(FUX))} | - {cont, path(FUX)}. - --spec compact_reverse(list(edit(FVH)), list(edit(FVH))) -> list(edit(FVH)). -compact_reverse(Edits, Acc) -> - case {Edits, Acc} of - {[], Acc@1} -> - Acc@1; - - {[{eq, Elem} | Rest], [{eq, Result} | Acc_rest]} -> - compact_reverse( - Rest, - [{eq, gleam@list:flatten([Elem, Result])} | Acc_rest] - ); - - {[{del, Elem@1} | Rest@1], [{del, Result@1} | Acc_rest@1]} -> - compact_reverse( - Rest@1, - [{del, gleam@list:flatten([Elem@1, Result@1])} | Acc_rest@1] - ); - - {[{ins, Elem@2} | Rest@2], [{ins, Result@2} | Acc_rest@2]} -> - compact_reverse( - Rest@2, - [{ins, gleam@list:flatten([Elem@2, Result@2])} | Acc_rest@2] - ); - - {[{eq, Elem@3} | Rest@3], Acc@2} -> - compact_reverse(Rest@3, [{eq, Elem@3} | Acc@2]); - - {[{del, Elem@4} | Rest@4], Acc@3} -> - compact_reverse(Rest@4, [{del, Elem@4} | Acc@3]); - - {[{ins, Elem@5} | Rest@5], Acc@4} -> - compact_reverse(Rest@5, [{ins, Elem@5} | Acc@4]) - end. - --spec move_right(path(FWA)) -> path(FWA). -move_right(Path) -> - case Path of - {path, X, Y, List1, [Elem | Rest], Edits} -> - {path, X + 1, Y, List1, Rest, [{ins, [Elem]} | Edits]}; - - {path, X@1, Y@1, List1@1, [], Edits@1} -> - {path, X@1 + 1, Y@1, List1@1, [], Edits@1} - end. - --spec move_down(path(FWD)) -> path(FWD). -move_down(Path) -> - case Path of - {path, X, Y, [Elem | Rest], List2, Edits} -> - {path, X, Y + 1, Rest, List2, [{del, [Elem]} | Edits]}; - - {path, X@1, Y@1, [], List2@1, Edits@1} -> - {path, X@1, Y@1 + 1, [], List2@1, Edits@1} - end. - --spec proceed_path(integer(), integer(), list(path(FVU))) -> {path(FVU), - list(path(FVU))}. -proceed_path(Diag, Limit, Paths) -> - Neg_limit = - Limit, - case {Diag, Limit, Paths} of - {0, 0, [Path]} -> - {Path, []}; - - {Diag@1, _, [Path@1 | _] = Paths@1} when Diag@1 =:= Neg_limit -> - {move_down(Path@1), Paths@1}; - - {Diag@2, Limit@1, [Path@2 | _] = Paths@2} when Diag@2 =:= Limit@1 -> - {move_right(Path@2), Paths@2}; - - {_, _, [Path1, Path2 | Rest]} -> - case erlang:element(3, Path1) > erlang:element(3, Path2) of - true -> - {move_right(Path1), [Path2 | Rest]}; - - false -> - {move_down(Path2), [Path2 | Rest]} - end - end. - --spec follow_snake(path(FWG)) -> status(FWG). -follow_snake(Path) -> - case Path of - {path, X, Y, [Elem1 | Rest1], [Elem2 | Rest2], Edits} when Elem1 =:= Elem2 -> - follow_snake( - {path, X + 1, Y + 1, Rest1, Rest2, [{eq, [Elem1]} | Edits]} - ); - - {path, _, _, [], [], Edits@1} -> - {done, Edits@1}; - - _ -> - {cont, Path} - end. - --spec each_diagonal(integer(), integer(), list(path(FVO)), list(path(FVO))) -> status(FVO). -each_diagonal(Diag, Limit, Paths, Next_paths) -> - case Diag > Limit of - true -> - {next, gleam@list:reverse(Next_paths)}; - - false -> - {Path, Rest} = proceed_path(Diag, Limit, Paths), - case follow_snake(Path) of - {cont, Path@1} -> - each_diagonal(Diag + 2, Limit, Rest, [Path@1 | Next_paths]); - - Other -> - Other - end - end. - --spec find_script(integer(), integer(), list(path(FVD))) -> list(edit(FVD)). -find_script(Envelope, Max, Paths) -> - case Envelope > Max of - true -> - []; - - false -> - case each_diagonal(- Envelope, Envelope, Paths, []) of - {done, Edits} -> - compact_reverse(Edits, []); - - {next, Paths@1} -> - find_script(Envelope + 1, Max, Paths@1); - - _ -> - erlang:error(#{gleam_error => panic, - message => <<"Didn't expect a Cont here"/utf8>>, - module => <<"gap/myers"/utf8>>, - function => <<"find_script"/utf8>>, - line => 35}) - end - end. - --spec difference(list(FUY), list(FUY)) -> list(edit(FUY)). -difference(List1, List2) -> - Path = {path, 0, 0, List1, List2, []}, - find_script(0, gleam@list:length(List1) + gleam@list:length(List2), [Path]). diff --git a/aoc2023/build/packages/gap/src/gap@styled_comparison.erl b/aoc2023/build/packages/gap/src/gap@styled_comparison.erl deleted file mode 100644 index 14cc390..0000000 --- a/aoc2023/build/packages/gap/src/gap@styled_comparison.erl +++ /dev/null @@ -1,8 +0,0 @@ --module(gap@styled_comparison). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export_type([styled_comparison/0]). - --type styled_comparison() :: {styled_comparison, binary(), binary()}. - - diff --git a/aoc2023/build/packages/gap/src/gap@styling.erl b/aoc2023/build/packages/gap/src/gap@styling.erl deleted file mode 100644 index ba226c3..0000000 --- a/aoc2023/build/packages/gap/src/gap@styling.erl +++ /dev/null @@ -1,202 +0,0 @@ --module(gap@styling). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([from_comparison/1, highlight/4, serialize/2, first_highlight_default/1, second_highlight_default/1, no_highlight/1, mk_generic_serializer/2, to_styled_comparison/1]). --export_type([part/1, highlighters/0, styling/1]). - --type part(FRE) :: {part, binary(), list(FRE), fun((binary()) -> binary())} | - {all, binary()}. - --type highlighters() :: {highlighters, - fun((binary()) -> binary()), - fun((binary()) -> binary()), - fun((binary()) -> binary())}. - --opaque styling(FRF) :: {styling, - gap@comparison:comparison(FRF), - gleam@option:option(fun((part(FRF)) -> binary())), - gleam@option:option(highlighters())}. - --spec from_comparison(gap@comparison:comparison(FRI)) -> styling(FRI). -from_comparison(Comparison) -> - {styling, Comparison, none, none}. - --spec highlight( - styling(FRL), - fun((binary()) -> binary()), - fun((binary()) -> binary()), - fun((binary()) -> binary()) -) -> styling(FRL). -highlight(Styling, First, Second, Matching) -> - erlang:setelement( - 4, - Styling, - {some, {highlighters, First, Second, Matching}} - ). - --spec serialize(styling(FRO), fun((part(FRO)) -> binary())) -> styling(FRO). -serialize(Styling, Serializer) -> - erlang:setelement(3, Styling, {some, Serializer}). - --spec first_highlight_default(binary()) -> binary(). -first_highlight_default(String) -> - case String of - <<" "/utf8>> -> - _pipe = String, - _pipe@1 = gleam_community@ansi:underline(_pipe), - _pipe@2 = gleam_community@ansi:bold(_pipe@1), - gleam_community@ansi:green(_pipe@2); - - _ -> - _pipe@3 = String, - _pipe@4 = gleam_community@ansi:green(_pipe@3), - gleam_community@ansi:bold(_pipe@4) - end. - --spec second_highlight_default(binary()) -> binary(). -second_highlight_default(String) -> - case String of - <<" "/utf8>> -> - _pipe = String, - _pipe@1 = gleam_community@ansi:underline(_pipe), - _pipe@2 = gleam_community@ansi:bold(_pipe@1), - gleam_community@ansi:red(_pipe@2); - - _ -> - _pipe@3 = String, - _pipe@4 = gleam_community@ansi:red(_pipe@3), - gleam_community@ansi:bold(_pipe@4) - end. - --spec no_highlight(binary()) -> binary(). -no_highlight(String) -> - String. - --spec string_serializer(part(binary())) -> binary(). -string_serializer(Part) -> - case Part of - {part, Acc, Sequence, Highlight} -> - <<Acc/binary, - (begin - _pipe = Sequence, - _pipe@1 = gleam@list:map(_pipe, Highlight), - gleam@string:join(_pipe@1, <<""/utf8>>) - end)/binary>>; - - {all, String} -> - String - end. - --spec mk_generic_serializer(binary(), fun((binary()) -> binary())) -> fun((part(any())) -> binary()). -mk_generic_serializer(Separator, Around) -> - fun(Part) -> case Part of - {part, Acc, Sequence, Highlight} -> - Segment_separator = case Acc of - <<""/utf8>> -> - <<""/utf8>>; - - _ -> - Separator - end, - <<<<Acc/binary, Segment_separator/binary>>/binary, - (begin - _pipe = Sequence, - _pipe@1 = gleam@list:map( - _pipe, - fun gleam@string:inspect/1 - ), - _pipe@2 = gleam@list:map(_pipe@1, Highlight), - gleam@string:join(_pipe@2, Separator) - end)/binary>>; - - {all, String} -> - Around(String) - end end. - --spec generic_serializer(part(any())) -> binary(). -generic_serializer(Part) -> - (mk_generic_serializer( - <<", "/utf8>>, - fun(All) -> <<<<"["/utf8, All/binary>>/binary, "]"/utf8>> end - ))(Part). - --spec to_strings( - list(gap@comparison:match(list(FRY))), - list(gap@comparison:match(list(FRY))), - fun((part(FRY)) -> binary()), - fun((binary()) -> binary()), - fun((binary()) -> binary()), - fun((binary()) -> binary()) -) -> gap@styled_comparison:styled_comparison(). -to_strings( - First, - Second, - Serializer, - First_highlight, - Second_highlight, - No_highlight -) -> - First_styled = begin - _pipe = First, - gleam@list:fold(_pipe, <<""/utf8>>, fun(Str, Match) -> case Match of - {match, Item} -> - Serializer({part, Str, Item, No_highlight}); - - {no_match, Item@1} -> - Serializer({part, Str, Item@1, First_highlight}) - end end) - end, - Second_styled = begin - _pipe@1 = Second, - gleam@list:fold( - _pipe@1, - <<""/utf8>>, - fun(Str@1, Match@1) -> case Match@1 of - {match, Item@2} -> - Serializer({part, Str@1, Item@2, No_highlight}); - - {no_match, Item@3} -> - Serializer({part, Str@1, Item@3, Second_highlight}) - end end - ) - end, - {styled_comparison, - Serializer({all, First_styled}), - Serializer({all, Second_styled})}. - --spec to_styled_comparison(styling(any())) -> gap@styled_comparison:styled_comparison(). -to_styled_comparison(Styling) -> - Highlight = begin - _pipe = erlang:element(4, Styling), - gleam@option:unwrap( - _pipe, - {highlighters, - fun first_highlight_default/1, - fun second_highlight_default/1, - fun no_highlight/1} - ) - end, - case erlang:element(2, Styling) of - {string_comparison, First, Second} -> - to_strings( - First, - Second, - fun string_serializer/1, - erlang:element(2, Highlight), - erlang:element(3, Highlight), - erlang:element(4, Highlight) - ); - - {list_comparison, First@1, Second@1} -> - to_strings( - First@1, - Second@1, - gleam@option:unwrap( - erlang:element(3, Styling), - fun generic_serializer/1 - ), - erlang:element(2, Highlight), - erlang:element(3, Highlight), - erlang:element(4, Highlight) - ) - end. diff --git a/aoc2023/build/packages/gap/src/gap_ffi.mjs b/aoc2023/build/packages/gap/src/gap_ffi.mjs deleted file mode 100644 index 235c80b..0000000 --- a/aoc2023/build/packages/gap/src/gap_ffi.mjs +++ /dev/null @@ -1,431 +0,0 @@ -import { - Error, - List, - Ok, - inspect, - toList, - makeError, - isEqual, -} from "./gleam.mjs"; -import * as $option from "../gleam_stdlib/gleam/option.mjs"; - -const HASHCODE_CACHE = new WeakMap(); -const Nil = undefined; - -class MutableMap { - static #hashcode_cache = new WeakMap(); - - static hash(value) { - let existing = this.#hashcode_cache.get(value); - if (existing) { - return existing; - } else if (value instanceof Object) { - let hashcode = inspect(value); - HASHCODE_CACHE.set(value, hashcode); - return hashcode; - } else { - return value.toString(); - } - } - - constructor() { - this.entries = new globalThis.Map(); - } - - get size() { - return this.entries.size; - } - - inspect() { - let entries = [...this.entries.values()] - .map((pair) => inspect(pair)) - .join(", "); - return `map.from_list([${entries}])`; - } - - toList() { - return List.fromArray([...this.entries.values()]); - } - - insert(k, v) { - this.entries.set(MutableMap.hash(k), [k, v]); - return this; - } - - delete(k) { - this.entries.delete(MutableMap.hash(k)); - return this; - } - - get(key) { - let code = MutableMap.hash(key); - if (this.entries.has(code)) { - return new Ok(this.entries.get(code)[1]); - } else { - return new Error(Nil); - } - } -} - -export function new_mutable_map() { - return new MutableMap(); -} - -export function mutable_map_size(map) { - return map.size; -} - -export function mutable_map_to_list(map) { - return map.toList(); -} - -export function mutable_map_remove(k, map) { - return map.delete(k); -} - -export function mutable_map_get(map, key) { - return map.get(key); -} - -export function mutable_map_insert(key, value, map) { - return map.insert(key, value); -} - -// From map.mjs - -export function size(map) { - return mutable_map_size(map); -} - -export function to_list(map) { - return mutable_map_to_list(map); -} - -export function new$() { - return new_mutable_map(); -} - -export function get(from, get) { - return mutable_map_get(from, get); -} - -function do_has_key(key, map) { - return !isEqual(get(map, key), new Error(undefined)); -} - -export function has_key(map, key) { - return do_has_key(key, map); -} - -export function insert(map, key, value) { - return mutable_map_insert(key, value, map); -} - -function insert_pair(map, pair) { - return insert(map, pair[0], pair[1]); -} - -export function update(map, key, fun) { - let _pipe = map; - let _pipe$1 = get(_pipe, key); - let _pipe$2 = $option.from_result(_pipe$1); - let _pipe$3 = fun(_pipe$2); - return ((_capture) => { - return insert(map, key, _capture); - })(_pipe$3); -} - -export function delete$(map, key) { - return mutable_map_remove(key, map); -} - -function fold_list_of_pair(loop$list, loop$initial) { - while (true) { - let list = loop$list; - let initial = loop$initial; - if (list.hasLength(0)) { - return initial; - } else if (list.atLeastLength(1)) { - let x = list.head; - let rest = list.tail; - loop$list = rest; - loop$initial = insert(initial, x[0], x[1]); - } else { - throw makeError( - "case_no_match", - "gleam/map", - 98, - "fold_list_of_pair", - "No case clause matched", - { values: [list] } - ); - } - } -} - -function do_from_list(list) { - return fold_list_of_pair(list, new$()); -} - -export function from_list(list) { - return do_from_list(list); -} - -function do_fold(loop$list, loop$initial, loop$fun) { - while (true) { - let list = loop$list; - let initial = loop$initial; - let fun = loop$fun; - if (list.hasLength(0)) { - return initial; - } else if (list.atLeastLength(1)) { - let k = list.head[0]; - let v = list.head[1]; - let tail = list.tail; - loop$list = tail; - loop$initial = fun(initial, k, v); - loop$fun = fun; - } else { - throw makeError( - "case_no_match", - "gleam/map", - 558, - "do_fold", - "No case clause matched", - { values: [list] } - ); - } - } -} - -export function fold(map, initial, fun) { - let _pipe = map; - let _pipe$1 = to_list(_pipe); - return do_fold(_pipe$1, initial, fun); -} - -function do_map_values(f, map) { - let f$1 = (map, k, v) => { - return insert(map, k, f(k, v)); - }; - let _pipe = map; - return fold(_pipe, new$(), f$1); -} - -export function map_values(map, fun) { - return do_map_values(fun, map); -} - -function do_filter(f, map) { - let insert$1 = (map, k, v) => { - let $ = f(k, v); - if ($) { - return insert(map, k, v); - } else { - return map; - } - }; - let _pipe = map; - return fold(_pipe, new$(), insert$1); -} - -export function filter(map, property) { - return do_filter(property, map); -} - -function do_keys_acc(loop$list, loop$acc) { - while (true) { - let list = loop$list; - let acc = loop$acc; - if (list.hasLength(0)) { - return reverse_and_concat(acc, toList([])); - } else if (list.atLeastLength(1)) { - let x = list.head; - let xs = list.tail; - loop$list = xs; - loop$acc = toList([x[0]], acc); - } else { - throw makeError( - "case_no_match", - "gleam/map", - 276, - "do_keys_acc", - "No case clause matched", - { values: [list] } - ); - } - } -} - -function do_keys(map) { - let list_of_pairs = (() => { - let _pipe = map; - return to_list(_pipe); - })(); - return do_keys_acc(list_of_pairs, toList([])); -} - -export function keys(map) { - return do_keys(map); -} - -function reverse_and_concat(loop$remaining, loop$accumulator) { - while (true) { - let remaining = loop$remaining; - let accumulator = loop$accumulator; - if (remaining.hasLength(0)) { - return accumulator; - } else if (remaining.atLeastLength(1)) { - let item = remaining.head; - let rest = remaining.tail; - loop$remaining = rest; - loop$accumulator = toList([item], accumulator); - } else { - throw makeError( - "case_no_match", - "gleam/map", - 269, - "reverse_and_concat", - "No case clause matched", - { values: [remaining] } - ); - } - } -} - -function do_values_acc(loop$list, loop$acc) { - while (true) { - let list = loop$list; - let acc = loop$acc; - if (list.hasLength(0)) { - return reverse_and_concat(acc, toList([])); - } else if (list.atLeastLength(1)) { - let x = list.head; - let xs = list.tail; - loop$list = xs; - loop$acc = toList([x[1]], acc); - } else { - throw makeError( - "case_no_match", - "gleam/map", - 314, - "do_values_acc", - "No case clause matched", - { values: [list] } - ); - } - } -} - -function do_values(map) { - let list_of_pairs = (() => { - let _pipe = map; - return to_list(_pipe); - })(); - return do_values_acc(list_of_pairs, toList([])); -} - -export function values(map) { - return do_values(map); -} - -function insert_taken(loop$map, loop$desired_keys, loop$acc) { - while (true) { - let map = loop$map; - let desired_keys = loop$desired_keys; - let acc = loop$acc; - let insert$1 = (taken, key) => { - let $ = get(map, key); - if ($.isOk()) { - let value = $[0]; - return insert(taken, key, value); - } else { - return taken; - } - }; - if (desired_keys.hasLength(0)) { - return acc; - } else if (desired_keys.atLeastLength(1)) { - let x = desired_keys.head; - let xs = desired_keys.tail; - loop$map = map; - loop$desired_keys = xs; - loop$acc = insert$1(acc, x); - } else { - throw makeError( - "case_no_match", - "gleam/map", - 411, - "insert_taken", - "No case clause matched", - { values: [desired_keys] } - ); - } - } -} - -function do_take(desired_keys, map) { - return insert_taken(map, desired_keys, new$()); -} - -export function take(map, desired_keys) { - return do_take(desired_keys, map); -} - -function fold_inserts(loop$new_entries, loop$map) { - while (true) { - let new_entries = loop$new_entries; - let map = loop$map; - if (new_entries.hasLength(0)) { - return map; - } else if (new_entries.atLeastLength(1)) { - let x = new_entries.head; - let xs = new_entries.tail; - loop$new_entries = xs; - loop$map = insert_pair(map, x); - } else { - throw makeError( - "case_no_match", - "gleam/map", - 451, - "fold_inserts", - "No case clause matched", - { values: [new_entries] } - ); - } - } -} - -function do_merge(map, new_entries) { - let _pipe = new_entries; - let _pipe$1 = to_list(_pipe); - return fold_inserts(_pipe$1, map); -} - -export function merge(map, new_entries) { - return do_merge(map, new_entries); -} - -export function drop(loop$map, loop$disallowed_keys) { - while (true) { - let map = loop$map; - let disallowed_keys = loop$disallowed_keys; - if (disallowed_keys.hasLength(0)) { - return map; - } else if (disallowed_keys.atLeastLength(1)) { - let x = disallowed_keys.head; - let xs = disallowed_keys.tail; - loop$map = delete$(map, x); - loop$disallowed_keys = xs; - } else { - throw makeError( - "case_no_match", - "gleam/map", - 514, - "drop", - "No case clause matched", - { values: [disallowed_keys] } - ); - } - } -} |