-module(twister).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src\\twister.gleam").
-export([from_list/1, blank/0, add/2, set_modulo/2, run/2, run_default/3, run_generate/3, compose/2]).
-export_type([permutation/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(
" The main module of `twister`, containing basically all of the relevant API.\n"
" \n"
" ## Introduction\n"
" To use this library, you first need to create a [`Permutation`](twister.html#Permutation),\n"
" using one of the provided methods:\n"
" 1. Create it from a `List` of indexes directly, using the\n"
" [`from_list`](twister.html#from_list) function\n"
" 2. Create it using a builder pattern by first initializing it with\n"
" [`blank`](twister.html#blank), then adding indexes to the end one by one with\n"
" [`add`](twister.html#add).\n"
" \n"
" ## Examples\n"
" \n"
" ### Creating a Permutation\n"
" \n"
" > *Note* : Inside of the `Permutation` data type, the list of indices is stored in\n"
" reverse order. This is because when running one, the order is also reversed.\n"
" So, instead of needlessly reversing the internal List, it's just stored back to front.\n"
" \n"
" Using a List of indices\n"
" ```gleam\n"
" // Create a `Permutation` from a `List` of indices\n"
" twister.from_list([1, 4, 2, 0, 3, 5])\n"
" // -> Permutation(False, Some(5), [5, 3, 0, 2, 4, 1])\n"
" // This is the important part ^^^^^^^^^^^^^^^^\n"
" ```\n"
" \n"
" Using the builder\n"
" ```gleam\n"
" // Create a `Permutation` using a builder\n"
" twister.blank()\n"
" |> twister.add(1)\n"
" |> twister.add(4)\n"
" |> twister.add(2)\n"
" |> twister.add(0)\n"
" |> twister.add(3)\n"
" |> twister.add(5)\n"
" // -> Permutation(False, Some(5), [5, 3, 0, 2, 4, 1])\n"
" // Same as before, just more annoying. But sometimes useful\n"
" ```\n"
" \n"
" ### Using a Permutation\n"
" \n"
" Using [`twister.run`](twister.html#run) returns a `Result(List(a), Nil)`;\n"
" - `Ok(List(a))` if all of the indices are smaller than the length of the input `List`\n"
" - `Error(Nil)` if some of the indices were outside the bounds of the input `List`\n"
" ```gleam\n"
" let perm = twister.from_list([1, 4, 2, 0, 3, 5])\n"
" // Call it with run to get a `Result(List(a), Nil)`.\n"
" assert twister.run(perm, [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"])\n"
" == Ok([\"b\", \"e\", \"c\", \"a\", \"d\", \"f\"])\n"
" ```\n"
" \n"
" Otherwise, you can use [`twister.run_default`](twister.html#run_default) or\n"
" [`twister.run_generate`](twister.html#run_generate) to replace missing elements if\n"
" indices are outside of the bounds of the input `List`.\n"
" ```gleam\n"
" let perm = twister.from_list([1, 4, 2, 0, 3, 5])\n"
" \n"
" assert twister.run_generate(perm, [\"a\", \"b\", \"c\", \"d\", \"e\"], int.to_string)\n"
" == [\"b\", \"e\", \"c\", \"a\", \"d\", \"5\"]\n"
" // Index outside the bounds ^^^ replaced with generated value\n"
" assert twister.run_default(perm, [\"a\", \"b\", \"c\", \"d\", \"e\"], \"\")\n"
" == [\"b\", \"e\", \"c\", \"a\", \"d\", \"\"]\n"
" // Index outside the bounds ^^ replaced with default value\n"
" ```\n"
" \n"
).
-type permutation() :: {permutation,
boolean(),
gleam@option:option(integer()),
list(integer())}.
-file("src\\twister.gleam", 104).
?DOC(
" Create a new `Permutation` from a given list of indexes.\n"
" \n"
" When later running a permutation *on* some `List`, that `List` must have a length more than\n"
" or equal to the largest index in the list of indexes within, or it will return `Error(Nil)`.\n"
).
-spec from_list(list(integer())) -> permutation().
from_list(L) ->
{permutation, false, twister@util:largest(L), lists:reverse(L)}.
-file("src\\twister.gleam", 118).
?DOC(
" Create a new, blank `Permutation`.\n"
" \n"
" This function is meant to be used as the starting point for constructing a `Permutation`\n"
" using the builder pattern, by using the [`add`](twister.html#add) function to add a new\n"
" index at the end of the list of indexes.\n"
).
-spec blank() -> permutation().
blank() ->
{permutation, false, none, []}.
-file("src\\twister.gleam", 135).
?DOC(
" Add an index to the end of a `Permutation`.\n"
" \n"
" This function is meant to be used after creating a blank `Permutation` using the\n"
" [`blank`](twister.html#blank) function, but there's nothing stopping you from using it to\n"
" add elements to a `Permutation` created using the [`from_list`](twister.html#from_list)\n"
" function.\n"
" \n"
" When later running a permutation *on* some `List`, that `List` must have a length more\n"
" than or equal to the largest index in the list of indexes within, or it will return\n"
" `Error(Nil)`.\n"
).
-spec add(permutation(), integer()) -> permutation().
add(Perm, I) ->
{permutation, Modulo, Old_max, Output} = Perm,
New_max = case Old_max of
{some, Prev_max} ->
gleam@int:max(Prev_max, I);
none ->
I
end,
{permutation, Modulo, {some, New_max}, [I | Output]}.
-file("src\\twister.gleam", 144).
-spec set_modulo(permutation(), boolean()) -> permutation().
set_modulo(Perm, To) ->
{permutation, To, erlang:element(3, Perm), erlang:element(4, Perm)}.
-file("src\\twister.gleam", 295).
-spec map_modulo(list(integer()), integer()) -> list(integer()).
map_modulo(Indexes, Modulo) ->
_pipe = Indexes,
gleam@list:map(_pipe, fun(I) -> _pipe@1 = gleam@int:modulo(I, Modulo),
gleam@result:unwrap(_pipe@1, 0) end).
-file("src\\twister.gleam", 175).
-spec run_loop(list(DMC), list(integer()), list(DMC)) -> {ok, list(DMC)} |
{error, nil}.
run_loop(Over, Indexes, Acc) ->
case Indexes of
[] ->
{ok, Acc};
[Index | Rest] ->
case twister@util:at(Over, Index) of
{ok, El} ->
run_loop(Over, Rest, [El | Acc]);
{error, nil} ->
{error, nil}
end
end.
-file("src\\twister.gleam", 163).
?DOC(
" Run a created `Permutation` on some `List`.\n"
" \n"
" This function returns a `Result` because there's no guarantee that the provided\n"
" `List` contains enough elements to build the permuted output.\n"
" \n"
" For example, if I have a `Permutation` like `[0, 2, 5]` and I pass in `[\"a\", \"b\"]`,\n"
" there simply aren't enough elements in the `List` to find the element at index 5,\n"
" or even 2. So this function returns `Error(Nil)`.\n"
" \n"
" If you wish to guarantee that a `Permutation` always returns a `List` the exact same\n"
" length as the `List` of specified indexes, use the [`run_default`](twister.html#run_default)\n"
" function, and choose a default value to return when the index is out of bounds.\n"
).
-spec run(permutation(), list(DLX)) -> {ok, list(DLX)} | {error, nil}.
run(Perm, L) ->
case {Perm, erlang:length(L)} of
{{permutation, _, _, []}, _} ->
{ok, []};
{{permutation, _, none, _}, _} ->
{error, nil};
{{permutation, false, {some, Max}, Non_empty}, Len} when Len >= Max ->
run_loop(L, Non_empty, []);
{{permutation, true, {some, _}, Non_empty@1}, Len@1} when Len@1 > 0 ->
run_loop(
L,
begin
_pipe = Non_empty@1,
map_modulo(_pipe, erlang:length(L))
end,
[]
);
{{permutation, _, {some, _}, _}, _} ->
{error, nil}
end.
-file("src\\twister.gleam", 243).
-spec run_generate_loop(
list(DMP),
list(integer()),
fun((integer()) -> DMP),
list(DMP)
) -> list(DMP).
run_generate_loop(Over, Indexes, Fun, Acc) ->
case Indexes of
[] ->
Acc;
[Index | Rest] ->
run_generate_loop(
Over,
Rest,
Fun,
[case twister@util:at(Over, Index) of
{ok, El} ->
El;
{error, nil} ->
Fun(Index)
end | Acc]
)
end.
-file("src\\twister.gleam", 200).
?DOC(
" Run a created `Permutation` on some `List`.\n"
" \n"
" This function always returns a `List(a)` by simply putting the provided `default`\n"
" argument in the returned `List` whenever the requested index is out of bounds.\n"
" \n"
" ## Note\n"
" If while creating the `Permutation`, the [`set_modulo`](twister.html#set_modulo)\n"
" function was used, this function will behave identically to [`run`](twister.html#run),\n"
" as all of the indexes will definitely be within the bounds of the provided `List`.\n"
).
-spec run_default(permutation(), list(DMJ), DMJ) -> list(DMJ).
run_default(Perm, L, Default) ->
case Perm of
{permutation, _, _, []} ->
[];
{permutation, false, _, Non_empty} ->
run_generate_loop(L, Non_empty, fun(_) -> Default end, []);
{permutation, true, _, Non_empty@1} ->
run_generate_loop(
L,
begin
_pipe = Non_empty@1,
map_modulo(_pipe, erlang:length(L))
end,
fun(_) -> Default end,
[]
)
end.
-file("src\\twister.gleam", 230).
?DOC(
" Run a created `Permutation` on some `List`.\n"
" \n"
" This function always returns a `List(a)` by simply generating a value in the returned\n"
" `List` whenever the requested index is out of bounds, with the index in question\n"
" as the input argument.\n"
" \n"
" ## Note\n"
" If while creating the `Permutation`, the [`set_modulo`](twister.html#set_modulo)\n"
" function was used, this function will behave identically to [`run`](twister.html#run),\n"
" as all of the indexes will definitely be within the bounds of the provided `List`.\n"
).
-spec run_generate(permutation(), list(DMM), fun((integer()) -> DMM)) -> list(DMM).
run_generate(Perm, L, Fun) ->
case Perm of
{permutation, _, _, []} ->
[];
{permutation, false, _, Non_empty} ->
run_generate_loop(L, Non_empty, Fun, []);
{permutation, true, _, Non_empty@1} ->
run_generate_loop(
L,
begin
_pipe = Non_empty@1,
map_modulo(_pipe, erlang:length(L))
end,
Fun,
[]
)
end.
-file("src\\twister.gleam", 272).
?DOC(
" Transform two `Permutation`s into a third, by executing the first one, then the second one.\n"
" \n"
" Importantly, the first permutation must have an internal length of `n + 1`, where `n` is\n"
" the largest index of the second, and its' length will be that of the second one.\n"
" \n"
" Lastly, the resulting `Permutation` will essentially be a new one, not inheriting the properties\n"
" of the input ones (specifically the modulo from [`set_modulo`](twister.html#set_modulo)).\n"
).
-spec compose(permutation(), permutation()) -> {ok, permutation()} |
{error, nil}.
compose(First, Second) ->
_pipe = run(Second, erlang:element(4, First)),
gleam@result:map(_pipe, fun(Indexes) -> from_list(Indexes) end).