defmodule Bottom do @moduledoc """ This is an Elixir implementation of [bottom](https://github.com/kaylynn234/bottom). Bottom encodes UTF-8 text into a sequence comprised of bottom emoji (`πŸ«‚βœ¨πŸ₯Ίβ€οΈ`, with , sprinkled in for good measure) followed by `πŸ‘‰πŸ‘ˆ`. It can encode any valid UTF-8 - being a bottom transcends language, after all - and decode back into UTF-8. """ @character_values [ {200, "πŸ«‚"}, {50, "πŸ’–"}, {10, "✨"}, {5, "πŸ₯Ί"}, {1, ","}, {0, "❀️"} ] @sep "πŸ‘‰πŸ‘ˆ" @doc ~S""" Encodes UTF-8 text into bottom emoji. ## Examples iex> Bottom.encode("Test") {:ok, "πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ"} iex> Bottom.encode("Hello world!") {:ok, "πŸ’–βœ¨βœ¨,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,πŸ‘‰πŸ‘ˆβœ¨βœ¨βœ¨,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ‘‰πŸ‘ˆβœ¨βœ¨βœ¨,,,πŸ‘‰πŸ‘ˆ"} iex> Bottom.encode("γŒγ‚“γ°γ‚Œ") {:ok, "πŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ’–βœ¨βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆπŸ«‚βœ¨βœ¨πŸ₯Ί,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨βœ¨βœ¨βœ¨πŸ‘‰πŸ‘ˆ"} """ def encode(text) do encoded = text |> :binary.bin_to_list() |> IO.inspect() |> Enum.map(&encode_byte/1) |> Enum.map(&Enum.join/1) |> Enum.map(fn x -> x <> @sep end) |> Enum.join() {:ok, encoded} end @doc ~S""" Decodes a sequence of bottom emoji into UTF-8 text. Returns `{:error, :invalid}` if text is not a valid sequence of emojis. ## Examples iex> Bottom.decode("πŸ’–βœ¨βœ¨βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯ΊπŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,πŸ‘‰πŸ‘ˆ") {:ok, "Test"} iex> Bottom.decode("πŸ’–βœ¨βœ¨,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,πŸ‘‰πŸ‘ˆβœ¨βœ¨βœ¨,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨πŸ₯Ί,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–βœ¨,,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ₯Ί,,,πŸ‘‰πŸ‘ˆπŸ’–πŸ’–πŸ‘‰πŸ‘ˆβœ¨βœ¨βœ¨,,,πŸ‘‰πŸ‘ˆ") {:ok, "Hello world!"} iex> Bottom.decode("πŸ˜€πŸ˜ƒπŸ˜„πŸ˜πŸ˜†πŸ˜…πŸ˜‚") {:error, :invalid} """ def decode(text) do decoded = text |> String.trim_trailing(@sep) |> String.split(@sep) |> Enum.map(&decode_byte/1) |> to_string() {:ok, decoded} rescue _ -> {:error, :invalid} end defp encode_byte(value) do case value do 0 -> {_, emoji} = Enum.find(@character_values, fn {num, _} -> num == 0 end) [emoji] value -> _encode_byte(value) end end defp _encode_byte(value) do case value do 0 -> [] value -> {max, emoji} = Enum.find(@character_values, fn {num, _} -> value >= num end) [emoji | _encode_byte(value - max)] end end defp decode_byte(text) do text |> String.codepoints() |> Enum.map(&Enum.find(@character_values, fn {_, emoji} -> emoji == &1 end)) |> Enum.map(fn {num, _} -> num end) |> Enum.sum() end end