defmodule RotationalCipher do @lower ?a..?z @upper ?A..?Z defguard is_lower(char) when char in @lower defguard is_upper(char) when char in @upper defguard is_letter(char) when char in @lower or char in @upper defp rotate_char(char, shift) when is_lower(char) and is_lower(char + shift), do: char + shift defp rotate_char(char, shift) when is_upper(char) and is_upper(char + shift), do: char + shift defp rotate_char(char, shift) when is_letter(char), do: char + shift - 26 defp rotate_char(char, _shift) when not is_letter(char), do: char @doc """ Given a plaintext and amount to shift by, return a rotated string. Example: iex> RotationalCipher.rotate("Attack at dawn", 13) "Nggnpx ng qnja" """ @spec rotate(text :: String.t(), shift :: integer) :: String.t() def rotate(text, shift) do text |> to_char_list() |> Enum.map(&rotate_char(&1, shift)) |> to_string() end end