-module(metamon@generator@shrink). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/metamon/generator/shrink.gleam"). -export([int_toward/2, list_drops/1]). -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( " Generic shrink helpers used by `metamon/generator` to build\n" " `Tree(a)`-shaped values for primitive types. Each helper returns a\n" " list of \"smaller\" candidates that the runner will explore in order.\n" ). -file("src/metamon/generator/shrink.gleam", 27). -spec compute_halves(integer()) -> list(integer()). compute_halves(N) -> case N =:= 0 of true -> []; false -> [N | compute_halves(N div 2)] end. -file("src/metamon/generator/shrink.gleam", 38). -spec reverse_into(list(EFD), list(EFD)) -> list(EFD). reverse_into(Items, Acc) -> case Items of [] -> Acc; [First | Rest] -> reverse_into(Rest, [First | Acc]) end. -file("src/metamon/generator/shrink.gleam", 34). -spec reverse(list(EFA)) -> list(EFA). reverse(Items) -> reverse_into(Items, []). -file("src/metamon/generator/shrink.gleam", 49). -spec dedupe_loop(list(integer()), list(integer()), list(integer())) -> list(integer()). dedupe_loop(Items, Seen, Acc) -> case Items of [] -> reverse(Acc); [First | Rest] -> case gleam@list:contains(Seen, First) of true -> dedupe_loop(Rest, Seen, Acc); false -> dedupe_loop(Rest, [First | Seen], [First | Acc]) end end. -file("src/metamon/generator/shrink.gleam", 45). -spec dedupe_keeping_first(list(integer())) -> list(integer()). dedupe_keeping_first(Items) -> dedupe_loop(Items, [], []). -file("src/metamon/generator/shrink.gleam", 15). ?DOC( " Halve `value` toward `origin`. Returns the empty list when no\n" " further progress is possible (e.g. value already equals origin).\n" "\n" " The classic QuickCheck-style shrink: try the origin first, then\n" " progressively closer-to-`value` candidates. For example,\n" " `int_toward(0, 64)` yields `[0, 32, 48, 56, 60, 62, 63]`. The\n" " runner walks this list in order, so the smallest counter-example\n" " is found in a single step when `origin` itself triggers the bug.\n" ). -spec int_toward(integer(), integer()) -> list(integer()). int_toward(Origin, Value) -> case Value =:= Origin of true -> []; false -> Diff = Value - Origin, _pipe = compute_halves(Diff), _pipe@1 = gleam@list:map(_pipe, fun(H) -> Value - H end), dedupe_keeping_first(_pipe@1) end. -file("src/metamon/generator/shrink.gleam", 102). -spec drop_window(list(EGD), integer(), integer(), list(EGD)) -> list(EGD). drop_window(Items, Drop_start, Drop_count, Acc) -> case {Items, Drop_start, Drop_count} of {[], _, _} -> reverse(Acc); {[_ | Rest], 0, Dc} when Dc > 0 -> drop_window(Rest, 0, Dc - 1, Acc); {[First | Rest@1], 0, _} -> drop_window(Rest@1, 0, 0, [First | Acc]); {[First@1 | Rest@2], Ds, _} -> drop_window(Rest@2, Ds - 1, Drop_count, [First@1 | Acc]) end. -file("src/metamon/generator/shrink.gleam", 86). -spec drop_windows(list(EFX), integer(), integer(), list(list(EFX))) -> list(list(EFX)). drop_windows(Items, Window, Remaining_starts, Acc) -> case Remaining_starts < 0 of true -> Acc; false -> Drop_start = Remaining_starts, Candidate = drop_window(Items, Drop_start, Window, []), drop_windows( Items, Window, Remaining_starts - Window, [Candidate | Acc] ) end. -file("src/metamon/generator/shrink.gleam", 71). -spec drop_at_each_size(list(EFR), integer(), integer(), list(list(EFR))) -> list(list(EFR)). drop_at_each_size(Items, Total, Drop_size, Acc) -> case Drop_size =< 0 of true -> Acc; false -> New_acc = drop_windows(Items, Drop_size, Total - Drop_size, Acc), drop_at_each_size(Items, Total, Drop_size div 2, New_acc) end. -file("src/metamon/generator/shrink.gleam", 65). ?DOC( " Shrink a list by trying progressively smaller drops:\n" " drop everything, then drop half, then drop a quarter, etc.\n" "\n" " Returns candidate sub-lists in order from \"most aggressive\" to\n" " \"least aggressive\" so the runner finds short failing examples first.\n" ). -spec list_drops(list(EFN)) -> list(list(EFN)). list_drops(Items) -> Length = erlang:length(Items), _pipe = drop_at_each_size(Items, Length, Length, []), reverse(_pipe).