diff --git a/lib/orangeade/generator/atom.ex b/lib/orangeade/generator/atom.ex new file mode 100644 index 0000000..a711264 --- /dev/null +++ b/lib/orangeade/generator/atom.ex @@ -0,0 +1,63 @@ +defmodule Orangeade.Generator.Atom do + @moduledoc """ + Provides a function for creating a stream of ascii atoms. + """ + + alias Orangeade.Generator.PrintableASCIIAlphabetCharacter.Minuscule + alias Orangeade.Generator.BoundNatural + + @doc """ + Creates a stream of ascii atoms of max default length 6. + + ## Examples + + iex> Caffeine.Stream.take( + ...> Orangeade.Generator.Atom.stream(), + ...> 10) + [:seg, :icrifx, :v, :voya, :kxv, :enoxal, :new, :xidtdo, :ibmwc, :pemdfx] + + """ + @spec stream() :: Caffeine.Stream.t() + def stream do + stream(max_word_length: 6) + end + + @doc """ + Given a max word length creates a stream of ascii atoms. + + ## Examples + + iex> Caffeine.Stream.take( + ...> Orangeade.Generator.Atom.stream(max_word_length: 10), + ...> 10) + [:oyavicrif, :oxalkxvv, :wen, :done, :mwcxidt, :mdfxib, :yuepe, :yvgcbweioh, + :qdyvvut, :hs] + + """ + @spec stream(max_word_length: non_neg_integer) :: Caffeine.Stream.t() + def stream(max_word_length: l) do + alphabet = Minuscule.stream() + lengths = BoundNatural.stream(limit: l) + stream(alphabet, lengths) + end + + defp stream(alphabet, lengths) do + {word, atail} = split(alphabet, Caffeine.Stream.head(lengths)) + rest = fn -> + stream(atail, Caffeine.Stream.tail(lengths)) end + Caffeine.Stream.construct(word, rest) + end + + defp split(alphabet, length) do + split([], length + 1, alphabet) + end + + defp split(word, 0, alphabet) do + {List.to_atom(word), alphabet} + end + + defp split(word, n, alphabet) do + split([Caffeine.Stream.head(alphabet)|word], n - 1, Caffeine.Stream.tail(alphabet)) + end + +end diff --git a/lib/orangeade/generator/printable_ascii_alphabet_character.ex b/lib/orangeade/generator/printable_ascii_alphabet_character.ex new file mode 100644 index 0000000..b786d4b --- /dev/null +++ b/lib/orangeade/generator/printable_ascii_alphabet_character.ex @@ -0,0 +1,118 @@ +defmodule Orangeade.Generator.PrintableASCIIAlphabetCharacter do + @moduledoc """ + Provides a function for creating a stream of ASCII alphabet characters. + """ + alias Caffeine.Stream + alias Orangeade.Generator.BoundNatural + alias Orangeade.Generator.PrintableASCIIAlphabetCharacter.Majuscule + alias Orangeade.Generator.PrintableASCIIAlphabetCharacter.Minuscule + + defmodule Majuscule do + @doc """ + Creates a stream of ASCII alphabet lowercase characters + + # Example + + iex> Caffeine.Stream.take( + ...> Orangeade.Generator.PrintableASCIIAlphabetCharacter.Majuscule.stream(), + ...> 50) + 'XONEWENODTDIXCWMBIXFDMEPEUYHOIEWBCGVYTUVVYDQSHEMLB' + + """ + @spec stream() :: Caffeine.Stream.t() + def stream do + Caffeine.Stream.map(BoundNatural.stream(limit: length()), &to_majuscule/1) + end + + defp to_majuscule(n) do + n + lower_bound() + end + + defp length do + upper_bound() - lower_bound() + end + + def lower_bound do + ?A + end + + defp upper_bound do + ?Z + end + end + + defmodule Minuscule do + @doc """ + Creates a stream of ASCII alphabet lowercase characters + + # Example + iex> Caffeine.Stream.take( + ...> Orangeade.Generator.PrintableASCIIAlphabetCharacter.Minuscule.stream(), + ...> 50) + 'onewenodtdixcwmbixfdmepeuyhoiewbcgvytuvvydqshemlbn' + + + """ + @spec stream() :: Caffeine.Stream.t() + def stream do + Caffeine.Stream.map(BoundNatural.stream(limit: length()), &to_minuscule/1) + end + + defp to_minuscule(n) do + n + lower_bound() + end + + def length do + upper_bound() - lower_bound() + end + + def lower_bound do + ?a + end + + defp upper_bound do + ?z + end + end + + @doc """ + Creates a stream of ASCII alphabet characters + + # Example + iex> Caffeine.Stream.take( + ...> Orangeade.Generator.PrintableASCIIAlphabetCharacter.stream(), + ...> 50) + 'NeWeNoDtDiXcWmBiXfDmEpEuYhOiEwBcGvYtUvVyDqShEmLbNc' + + + """ + + @spec stream() :: Caffeine.Stream.t() + def stream do + base = BoundNatural.stream(limit: length()) + filter = BoundNatural.stream(limit: 2) + stream(base, filter) + end + + defp stream(base, filter) do + head = to_alphabet_character(Stream.head(base), Stream.head(filter)) + rest = fn -> stream(Stream.tail(base),Stream.tail(filter)) end + Caffeine.Stream.construct(head, rest) + end + + defp to_alphabet_character(base, filter) do + case filter do + 1 -> + base + Majuscule.lower_bound() + 0 -> + base + Minuscule.lower_bound() + end + end + + defp length do + Minuscule.length() + end + + + +end diff --git a/test/orangeade/generator/atom_test.exs b/test/orangeade/generator/atom_test.exs new file mode 100644 index 0000000..6339321 --- /dev/null +++ b/test/orangeade/generator/atom_test.exs @@ -0,0 +1,21 @@ +defmodule Orangeade.Generator.AtomTest do + use ExUnit.Case + + test "Check if first 10 generated elements are atoms" do + list_of_elements = + Orangeade.Generator.Atom.stream() + |> Caffeine.Stream.take(10) + + assert Enum.all?(list_of_elements, &is_atom/1) + end + + test "Check if atoms have default max word length not greater than 6" do + lengths_of_elements = + Orangeade.Generator.Atom.stream() + |> Caffeine.Stream.take(10) + |> Enum.map(&Atom.to_charlist/1) + |> Enum.map(&length/1) + + refute Enum.any?(lengths_of_elements,fn e -> e > 6 end) + end +end diff --git a/test/orangeade/generator/printable_ascii_alphabet_character_test.exs b/test/orangeade/generator/printable_ascii_alphabet_character_test.exs new file mode 100644 index 0000000..b535eb9 --- /dev/null +++ b/test/orangeade/generator/printable_ascii_alphabet_character_test.exs @@ -0,0 +1,29 @@ +defmodule Orangeade.Generator.PrintableASCIIAlphabetCharacterTest do + use ExUnit.Case + + test "Stream contains only alphabet characters" do + list = Orangeade.Generator.PrintableASCIIAlphabetCharacter.stream() + |> Caffeine.Stream.take(1_000) + + refute Enum.any?(list, fn e -> + e < ?A or (e > ?Z and e < ?a) or e > ?z end) + end + + test "Majuscule stream contains only uppercase characters" do + {min, max} = Orangeade.Generator.PrintableASCIIAlphabetCharacter.Majuscule.stream() + |> Caffeine.Stream.take(1_000) + |> Enum.min_max + + assert (?A <= min) and (max <= ?Z) + end + + + test "Minuscule stream contains only lowercase characters" do + {min, max} = Orangeade.Generator.PrintableASCIIAlphabetCharacter.Minuscule.stream() + |> Caffeine.Stream.take(1_000) + |> Enum.min_max + + assert (?a <= min) and (max <= ?z) + end + +end diff --git a/test/orangeade/generator/term_test.exs b/test/orangeade/generator/term_test.exs index fc8649c..3f43c1c 100644 --- a/test/orangeade/generator/term_test.exs +++ b/test/orangeade/generator/term_test.exs @@ -5,7 +5,7 @@ defmodule Orangeade.Generator.TermTest do test "Check if there are different data types in first 100 generated elements" do list_of_elements = Term.stream() - |> Caffeine.Stream.take(100) + |> Caffeine.Stream.take(200) list_of_conditions = [ Enum.any?(list_of_elements, &is_negative/1),