Current section
Files
Jump to
Current section
Files
src/metamon@generator@shrink.erl
-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).