-module(gens). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -define(FILEPATH, "src/gens.gleam"). -export([new/0, gen/2, map/2, filter/2, drop/2, next/1, zip/2, list_zip/2]). -export_type([generator/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. -opaque generator(DTS) :: {generator, integer(), fun((integer()) -> DTS), fun((integer()) -> boolean())}. -file("src/gens.gleam", 13). ?DOC( " Default generator for the list of `natural numbers` [0..]\n" " ```gleam\n" " new() |> gen(5)\n" " // -> [0, 1, 2, 3, 4]\n" " ```\n" ). -spec new() -> generator(integer()). new() -> {generator, 0, fun(X) -> X end, fun(_) -> true end}. -file("src/gens.gleam", 18). ?DOC(" `Tail recursive` function for **gen**\n"). -spec gen_acc( integer(), integer(), integer(), fun((integer()) -> DTU), fun((integer()) -> boolean()), list(DTU) ) -> list(DTU). gen_acc(Index, Step, Total, Fmap, Filt, Acc) -> case Step < Total of false -> _pipe = Acc, lists:reverse(_pipe); true -> case Filt(Index) of false -> gen_acc(Index + 1, Step, Total, Fmap, Filt, Acc); true -> case Step >= 0 of false -> gen_acc(Index + 1, Step + 1, Total, Fmap, Filt, Acc); true -> gen_acc( Index + 1, Step + 1, Total, Fmap, Filt, [Fmap(Index) | Acc] ) end end end. -file("src/gens.gleam", 49). ?DOC( " **Generates** a `finite list` from a generator and a length\n" " ```gleam\n" " gen(new(), 5)\n" " // -> [0, 1, 2, 3, 4]\n" " ```\n" ). -spec gen(generator(DTX), integer()) -> list(DTX). gen(Ga, N) -> case Ga of {generator, Index, Amap, Afilt} -> gen_acc(Index, 0, N, Amap, Afilt, []) end. -file("src/gens.gleam", 63). ?DOC( " **Maps** each element of the generated list\n" " ```gleam\n" " new()\n" " |> map(fn(x) { x + 3 })\n" " |> map(int.to_string)\n" " |> gen(5)\n" " // -> [\"3\", \"4\", \"5\", \"6\", \"7\"]\n" " ```\n" ). -spec map(generator(DUA), fun((DUA) -> DUC)) -> generator(DUC). map(Ga, F) -> case Ga of {generator, Index, Amap, Afilt} -> {generator, Index, fun(N) -> F(Amap(N)) end, Afilt} end. -file("src/gens.gleam", 78). ?DOC( " **Filters** elements from the generated list\n" " ```gleam\n" " new()\n" " |> filter(fn(x) { x % 2 == 0 })\n" " |> filter(fn(x) { x != 4 })\n" " |> gen(5)\n" " // -> [0, 2, 6, 8, 10]\n" " ```\n" ). -spec filter(generator(DUE), fun((DUE) -> boolean())) -> generator(DUE). filter(Ga, F) -> case Ga of {generator, Index, Amap, Afilt} -> {generator, Index, Amap, fun(N) -> Afilt(N) andalso F(Amap(N)) end} end. -file("src/gens.gleam", 86). -spec advance(integer(), integer(), fun((integer()) -> boolean())) -> integer(). advance(Index, Steps, Filt) -> case Steps >= 0 of false -> Index - 1; true -> case Filt(Index) of false -> advance(Index + 1, Steps, Filt); true -> advance(Index + 1, Steps - 1, Filt) end end. -file("src/gens.gleam", 110). ?DOC( " **Drops** the first n generated elements\n" " ```gleam\n" " new() // [0, 1, 2, 3, 4..]\n" " |> drop(4) // [4, 5, 6, 7..]\n" " |> filter(int.is_even) // [4, 6, 8..]\n" " |> gen(5)\n" " // -> [4, 6, 8, 10, 12]\n" " new() // [0, 1, 2, 3, 4..]\n" " |> filter(int.is_even) // [0, 2, 4, 6, 8..]\n" " |> drop(4) // [8, 10, 12..]\n" " |> gen(5)\n" " // -> [8, 10, 12, 14, 16]\n" " ```\n" ). -spec drop(generator(DUH), integer()) -> generator(DUH). drop(Ga, Steps) -> case Steps >= 0 of false -> Ga; true -> case Ga of {generator, Index, Amap, Afilt} -> {generator, advance(Index, Steps, Afilt), Amap, Afilt} end end. -file("src/gens.gleam", 122). -spec next_index( integer(), fun((integer()) -> DUK), fun((integer()) -> boolean()) ) -> {DUK, integer()}. next_index(Index, Fmap, Filt) -> case Filt(Index) of false -> next_index(Index + 1, Fmap, Filt); true -> {Fmap(Index), Index} end. -file("src/gens.gleam", 140). ?DOC( " **Yields** one element and advances the generator\n" " ```gleam\n" " let #(x, g) = new() |> next\n" " // -> #(0, Generator(Int))\n" " g |> gen(3)\n" " // -> [1, 2, 3]\n" " ```\n" ). -spec next(generator(DUL)) -> {DUL, generator(DUL)}. next(Ga) -> case Ga of {generator, Index, Amap, Afilt} -> {Element, New_index} = next_index(Index, Amap, Afilt), {Element, {generator, New_index + 1, Amap, Afilt}} end. -file("src/gens.gleam", 160). ?DOC( " **Combines** two generators into one \\\n" " - The resulting index is the maximum of the two gens \n" " - The filters get combined\n" " - For separate indexes, do `list.zip(gen(g1, n), gen(g2, n))`\n" " ```gleam\n" " let g1 = new() |> map(fn(x) { x + 2 })\n" " let g2 = new() |> filter(int.is_even)\n" " zip(g1, g2)\n" " |> gen(3)\n" " // -> [#(2, 0), #(4, 2), #(6, 4)]\n" " ```\n" ). -spec zip(generator(DUO), generator(DUQ)) -> generator({DUO, DUQ}). zip(Ga, Gb) -> {generator, Aindex, Amap, Afilt} = Ga, {generator, Bindex, Bmap, Bfilt} = Gb, {generator, gleam@int:max(Aindex, Bindex), fun(N) -> {Amap(N), Bmap(N)} end, fun(N@1) -> Afilt(N@1) andalso Bfilt(N@1) end}. -file("src/gens.gleam", 174). ?DOC( " **Zips** a list with an infinite list\n" " ```gleam\n" " [\"a\", \"b\", \"c\"] \n" " |> list_zip(new())\n" " // -> [#(\"a\", 0), #(\"b\", 1), #(\"c\", 2)]\n" " ```\n" ). -spec list_zip(list(DUT), generator(DUV)) -> list({DUT, DUV}). list_zip(La, Gb) -> gleam@list:zip(La, gen(Gb, erlang:length(La))).