-module(gens). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -define(FILEPATH, "src/gens.gleam"). -export([get/1, gen/2, combine/2, from_list/1, list_repeat/1, from_lazy_list/1, while/1, forever/1, infinite/2, merge/3, chain/1, monad/0, from_stream/1, to_stream/1]). -export_type([generator/2, generator_m/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("================== Generator ==================\n"). -type generator(HXT, HXU) :: {generator, HXU, fun((HXU) -> gleam@option:option({HXT, HXU}))}. -type generator_m(HXV) :: any() | {gleam_phantom, HXV}. -file("src/gens.gleam", 24). ?DOC( " Returns the next element of a generator and the updated gen\n" " ```gleam\n" " let counter =\n" " Generator(state: 0, next: fn(c) { Some(#(c, c + 1)) })\n" " case get(counter).0 {\n" " None -> echo \"no more numbers\"\n" " Some(x) -> echo x // -> 0\n" " }\n" " ```\n" ). -spec get(generator(HXW, HXX)) -> {gleam@option:option(HXW), generator(HXW, HXX)}. get(G) -> case (erlang:element(3, G))(erlang:element(2, G)) of none -> {none, G}; {some, {X, S}} -> {{some, X}, {generator, S, erlang:element(3, G)}} end. -file("src/gens.gleam", 32). ?DOC(" Tail recursive function for gen\n"). -spec gen_acc(generator(HYD, HYE), integer(), list(HYD)) -> {list(HYD), generator(HYD, HYE)}. gen_acc(G, N, Ls) -> case N > 0 of false -> {lists:reverse(Ls), G}; true -> case get(G) of {none, _} -> {lists:reverse(Ls), G}; {{some, X}, G2} -> gen_acc(G2, N - 1, [X | Ls]) end end. -file("src/gens.gleam", 54). ?DOC( " Generates at most n elements and returns the updated gen\n" " ```gleam\n" " let counter =\n" " Generator(state: 0, next: fn(c) { Some(#(c, c + 1)) })\n" " let #(nums, _) = gen(counter, 5)\n" " echo nums // -> [0, 1, 2, 3, 4]\n" " ```\n" ). -spec gen(generator(HYL, HYM), integer()) -> {list(HYL), generator(HYL, HYM)}. gen(G, N) -> gen_acc(G, N, []). -file("src/gens.gleam", 70). ?DOC( " Combines two generators into one, advancing them separately\n" " ```gleam\n" " let two_powers =\n" " Generator(state: 1, next: fn(p) { Some(#(p, p * 2)) })\n" " let bellow_three =\n" " Generator(state: 0, next: fn(n) { Some(#(n < 3, n + 1)) })\n" "\n" " let z = combine(two_powers, bellow_three)\n" " let #(res, _) = gen(z, 5)\n" " echo res\n" " // -> [#(1, True), #(2, True), #(4, True), #(8, False), #(16, False)]\n" " ```\n" ). -spec combine(generator(HYS, HYT), generator(HYW, HYX)) -> generator({HYS, HYW}, {HYT, HYX}). combine(G1, G2) -> {generator, {erlang:element(2, G1), erlang:element(2, G2)}, fun(State) -> {State1, State2} = State, Res1 = (erlang:element(3, G1))(State1), Res2 = (erlang:element(3, G2))(State2), case {Res1, Res2} of {{some, {X, State3}}, {some, {Y, State4}}} -> {some, {{X, Y}, {State3, State4}}}; {_, _} -> none end end}. -file("src/gens.gleam", 108). ?DOC( " Generates the lists elements\n" " ```gleam\n" " let gen_fruit = from_list([\"apple\", \"banana\", \"orange\"])\n" " let #(fruit1, gen_fruit2) = get(gen_fruit)\n" " echo fruit1\n" " // -> Some(\"apple\")\n" " ```\n" " ```gleam\n" " let #(fruit2, gen_fruit3) = get(gen_fruit2)\n" " echo fruit2\n" " // -> Some(\"banana\")\n" " ```\n" " ```gleam\n" " let #(fruit3, gen_fruit4) = get(gen_fruit3)\n" " echo fruit3\n" " // -> Some(\"orange\")\n" " ```\n" " ```gleam\n" " let #(fruit4, _) = get(gen_fruit4)\n" " echo fruit4\n" " // -> None\n" " ```\n" ). -spec from_list(list(HZC)) -> generator(HZC, list(HZC)). from_list(L) -> {generator, L, fun(Ls) -> case Ls of [] -> none; [X | Rest] -> {some, {X, Rest}} end end}. -file("src/gens.gleam", 124). ?DOC( " Generates the lists elements on repeat\n" " ```gleam\n" " let gen_fruit = list_repeat([\"apple\", \"banana\", \"orange\"])\n" " let #(fruits, _) = gen(gen_fruit, 5)\n" " echo fruits\n" " // -> [\"apple\", \"banana\", \"orange\", \"apple\", \"banana\"]\n" " ```\n" ). -spec list_repeat(list(HZH)) -> generator(HZH, list(HZH)). list_repeat(L) -> {generator, L, fun(Ls) -> case Ls of [] -> none; [X | Rest] -> case Rest of [] -> {some, {X, L}}; _ -> {some, {X, Rest}} end end end}. -file("src/gens.gleam", 145). ?DOC( " Conversion from **LazyList** to **Generator** \n" " ```gleam\n" " let infinite_list = lazy.new() |> lazy.drop(3) |> lazy.map(fn(x) { x * 10 })\n" " let ten_gen = from_lazy_list(infinite_list)\n" " let #(res, _) = gen(ten_gen, 10)\n" " echo res\n" " // -> [30, 40, 50, 60, 70, 80, 90, 100, 110, 120]\n" " ```\n" ). -spec from_lazy_list(gens@lazy:lazy_list(HZM)) -> generator(HZM, gens@lazy:lazy_list(HZM)). from_lazy_list(L) -> {generator, L, fun(Ls) -> case gens@lazy:take(Ls, 1) of [] -> none; [X | _] -> {some, {X, begin _pipe = Ls, gens@lazy:drop(_pipe, 1) end}} end end}. -file("src/gens.gleam", 173). ?DOC( " Generates a list of all the elements. \\\n" " If the generator does not have a reachable end condition, then this function does not end!!!\n" " ```gleam\n" " let gen_ten =\n" " Generator(5, fn(x) {\n" " case x < 10 {\n" " True -> Some(#(x, x + 2))\n" " False -> None\n" " }\n" " })\n" " echo while(gen_ten)\n" " // -> [5, 7, 9]\n" " ```\n" " This function is the in verse of `from_list`\n" " ```gleam\n" " let gen_li = from_list([\"A\", \"B\", \"C\"])\n" " echo while(gen_li)\n" " // -> [\"A\", \"B\", \"C\"]\n" " ```\n" ). -spec while(generator(HZR, any())) -> list(HZR). while(G) -> case get(G) of {none, _} -> []; {{some, X}, G2} -> [X | while(G2)] end. -file("src/gens.gleam", 204). ?DOC( " Conversion from **Generator** to **LazyList** \\\n" " Since each element in the lazy list needs to be generated separately, this function can be very slow!!! (O(n^2))\n" " ```gleam\n" " let gen_nat = Generator(1, fn(c) { Some(#(c, c + 1)) })\n" " let lazy_nat = forever(gen_nat)\n" " echo lazy.take(lazy_nat, 5)\n" " // -> [Some(1), Some(2), Some(3), Some(4), Some(5)]\n" " ```\n" " This function is the inverse of `from_lazy_list`\n" " ```gleam\n" " let lazy_odds =\n" " lazy.new()\n" " |> lazy.filter(int.is_odd)\n" " |> lazy.map(int.to_string)\n" " let gen_odds = from_lazy_list(lazy_odds)\n" " let lazy_odds_2 = forever(gen_odds) |> lazy.map(option.lazy_unwrap(_, fn() { panic }))\n" " \n" " echo lazy.take(lazy_odds, 5)\n" " // -> [\"1\", \"3\", \"5\", \"7\", \"9\"]\n" " echo gen(gen_odds, 5) |> pair.first\n" " // -> [\"1\", \"3\", \"5\", \"7\", \"9\"]\n" " echo lazy.take(lazy_odds_2, 5)\n" " // -> [\"1\", \"3\", \"5\", \"7\", \"9\"]\n" " ```\n" ). -spec forever(generator(HZW, any())) -> gens@lazy:lazy_list(gleam@option:option(HZW)). forever(G) -> _pipe = gens@lazy:new(), _pipe@4 = gens@lazy:map(_pipe, fun(N) -> _pipe@1 = gen(G, N + 1), _pipe@2 = gleam@pair:first(_pipe@1), _pipe@3 = gleam@list:drop(_pipe@2, N - 1), gleam@list:last(_pipe@3) end), gens@lazy:map(_pipe@4, fun(Res) -> case Res of {ok, X} -> {some, X}; {error, _} -> none end end). -file("src/gens.gleam", 223). ?DOC( " Creates a generator with `no end condition`\n" " ```gleam\n" " let gen_nat = infinite(1, fn(x) { #(x, x + 1) })\n" " echo gen(gen_nat, 5).0\n" " // -> [1, 2, 3, 4, 5]\n" " ```\n" ). -spec infinite(IAC, fun((IAC) -> {IAD, IAC})) -> generator(IAD, IAC). infinite(State, Next) -> {generator, State, fun(X) -> {some, Next(X)} end}. -file("src/gens.gleam", 237). ?DOC( " **Merges** two `sorted` generators into one\n" " ```gleam\n" " let counter1 = Generator(0, fn(c) { Some(#(c, c + 1)) })\n" " let counter2 = Generator(0, fn(c) { Some(#(c, c + 2)) })\n" " let merged = merge(counter1, counter2, int.compare)\n" " merged \n" " |> gen(8) \n" " |> echo\n" " // -> #([0, 0, 1, 2, 2, 3, 4, 4], Generator(#(5, 6), fn() { ... }))\n" " ```\n" ). -spec merge( generator(IAG, IAH), generator(IAG, IAK), fun((IAG, IAG) -> gleam@order:order()) ) -> generator(IAG, {IAH, IAK}). merge(G1, G2, Comp) -> {generator, {erlang:element(2, G1), erlang:element(2, G2)}, fun(S) -> {State1, State2} = S, case {(erlang:element(3, G1))(State1), (erlang:element(3, G2))(State2)} of {{some, {X1, St1}}, {some, {X2, St2}}} -> case Comp(X1, X2) of gt -> {some, {X2, {State1, St2}}}; _ -> {some, {X1, {St1, State2}}} end; {{some, {X1@1, St1@1}}, none} -> {some, {X1@1, {St1@1, State2}}}; {none, {some, {X2@1, St2@1}}} -> {some, {X2@1, {State1, St2@1}}}; {none, none} -> none end end}. -file("src/gens.gleam", 261). -spec chain_tail_rec( list(IAP), list(fun((IAP) -> gleam@option:option({IAR, IAP}))), {gleam@option:option(IAR), list(IAP)} ) -> {gleam@option:option(IAR), list(IAP)}. chain_tail_rec(States, Nexts, Acc) -> case Acc of {{some, X}, State_list} -> case States of [St | Rest_states] -> chain_tail_rec( Rest_states, [], {{some, X}, [St | State_list]} ); [] -> {{some, X}, lists:reverse(State_list)} end; {none, State_list@1} -> case {States, Nexts} of {[St@1 | Rest_states@1], [Nx | Rest_nexts]} -> case Nx(St@1) of {some, {X@1, New_st}} -> chain_tail_rec( Rest_states@1, [], {{some, X@1}, [New_st | State_list@1]} ); none -> chain_tail_rec( Rest_states@1, Rest_nexts, {none, [St@1 | State_list@1]} ) end; {_, _} -> {none, lists:reverse(State_list@1)} end end. -file("src/gens.gleam", 312). ?DOC( " **Chains** a list of generators\n" " ```gleam\n" " let gen_three =\n" " Generator(1, fn(x) {\n" " case x <= 3 {\n" " True -> Some(#(x, x + 1))\n" " False -> None\n" " }\n" " })\n" " let gen_nat = infinite(1, fn(x) { #(x, x + 1) })\n" " // Once the first generator ends, the second one begins\n" " let gen_chain = chain([gen_three, gen_nat])\n" " gen_chain\n" " |> gen(8)\n" " |> pair.first\n" " // -> [1, 2, 3, 1, 2, 3, 4, 5]\n" " ```\n" ). -spec chain(list(generator(IAY, IAZ))) -> generator(IAY, list(IAZ)). chain(Generators) -> {generator, begin _pipe = Generators, gleam@list:map(_pipe, fun(G) -> erlang:element(2, G) end) end, fun(Gen_states) -> Gen_nexts = begin _pipe@1 = Generators, gleam@list:map(_pipe@1, fun(G@1) -> erlang:element(3, G@1) end) end, case chain_tail_rec(Gen_states, Gen_nexts, {none, []}) of {none, _} -> none; {{some, X}, States} -> {some, {X, States}} end end}. -file("src/gens.gleam", 339). ?DOC( " `Monad` instance for `Generator` type\n" " ```gleam\n" " let plus_one = infinite(1, fn(x) { #(x, x + 1) })\n" " let plus_two = infinite(1, fn(x) { #(x, x + 2) })\n" " let g = {\n" " use x <- monad().bind(plus_one)\n" " echo x // -> 1, 4, 7, 10, 13..\n" " use y <- monad().map(plus_two)\n" " echo y // -> 2, 5, 8, 9, 12..\n" " x + y\n" " }\n" " g |> gen(5) |> pair.first |> echo\n" " // -> [3, 9, 15, 21, 27]\n" " ```\n" ). -spec monad() -> cat@monad:monad(generator_m(IBG), IBI, IBJ, generator(IBI, IBG), generator(IBJ, IBG)). monad() -> {monad, fun(_) -> erlang:error(#{gleam_error => panic, message => <<"`panic` expression evaluated."/utf8>>, file => <>, module => <<"gens"/utf8>>, function => <<"monad"/utf8>>, line => 341}) end, fun(G, F) -> {generator, erlang:element(2, G), fun(State) -> case (erlang:element(3, G))(State) of none -> none; {some, {X, New_state}} -> (erlang:element(3, F(X)))(New_state) end end} end, fun(G@1, F@1) -> {generator, erlang:element(2, G@1), fun(State@1) -> case (erlang:element(3, G@1))(State@1) of none -> none; {some, {X@1, New_state@1}} -> {some, {F@1(X@1), New_state@1}} end end} end}. -file("src/gens.gleam", 394). ?DOC( " Conversion from **Stream** to **Generator** \\\n" " Helper Stream\n" " ```gleam\n" " pub fn dummy() -> Stream(Nil) {\n" " Stream(head: fn() { Nil }, tail: dummy)\n" " }\n" " ```\n" " Fibonacci Stream\n" " ```gleam\n" " let fibo_s =\n" " dummy()\n" " |> stream.scan(#(1, 1), fn(_, int_pair) {\n" " case int_pair {\n" " #(x, y) -> #(y, x + y)\n" " }\n" " })\n" " |> stream.map(fn(int_pair) { int_pair.1 })\n" " \n" " fibo_s\n" " |> stream.take(5)\n" " |> echo\n" " // -> [1, 2, 3, 5, 8]\n" " ```\n" " Fibonacci Generator\n" " ```gleam\n" " let fibo_g = from_stream(fibo_s)\n" " \n" " fibo_g\n" " |> gen(5)\n" " |> pair.first\n" " |> echo\n" " // -> [1, 2, 3, 5, 8]\n" " ```\n" ). -spec from_stream(gens@stream:stream(IBT)) -> generator(IBT, gens@stream:stream(IBT)). from_stream(Stream) -> {generator, Stream, fun(S) -> {some, {(erlang:element(2, S))(), (erlang:element(3, S))()}} end}. -file("src/gens.gleam", 424). ?DOC( " Conversion from **Generator** to **Stream** \\\n" " Fibonacci Generator\n" " ```gleam\n" " let fibo_g =\n" " Generator(state: #(1, 1), next: fn(int_pair) {\n" " case int_pair {\n" " #(x, y) -> Some(#(y, #(y, x + y)))\n" " }\n" " })\n" "\n" " fibo_g\n" " |> gen(5)\n" " |> pair.first\n" " |> echo\n" " // -> [1, 2, 3, 5, 8]\n" " ```\n" " Fibonacci Stream\n" " ```gleam\n" " let fibo_s = to_stream(fibo_g)\n" "\n" " fibo_s\n" " |> stream.map(option.unwrap(_, -1))\n" " |> stream.take(5)\n" " |> echo\n" " // -> [1, 2, 3, 5, 8]\n" " ```\n" ). -spec to_stream(generator(IBY, any())) -> gens@stream:stream(gleam@option:option(IBY)). to_stream(Generator) -> {stream, fun() -> case get(Generator) of {X, _} -> X end end, fun() -> case get(Generator) of {_, Next_gen} -> to_stream(Next_gen) end end}.