15 Jul 2013, 03:42
Dave_gnome_head_isolated_pragsmall

Dave Thomas (342 posts)

  • A basic Caesar cypher consists of shifting the letters is a message by a fixed offset. For an offset of 1, for example, a will become b, b will become c, and z will become a. If the offset is 13, we have the ROT13 algorithm.

    Lists and binaries can both be string-like. Write a Caesar protocol that applies to both. It would include two functions: encrypt(string, shift) and rot13(string).

20 Aug 2014, 05:20
Red-panda-icon_pragsmall

Taian Su (5 posts)

My solution:

defprotocol Caesar do
  def encrypt(string, shift)

  def rot13(string)
end

defimpl Caesar, for: [BitString, List] do
  @letter_length 26
  @upper_case_start 65
  @upper_case_end 90
  @lower_case_start 97
  @lower_case_end 122

  defp is_upper_case?(i) do
    i >= @upper_case_start && i <= @upper_case_end
  end

  defp is_lower_case?(i) do
    i >= @lower_case_start && i <= @lower_case_end
  end

  def encrypt(string, shift) do
    string
    |> to_char_list
    |> Enum.map(fn i -> shift_char(i, shift) end)
    |> List.to_string
  end

  defp shift_char(i, shift) do
    cond do
      is_upper_case?(i) ->
        rotate( i - @upper_case_start, shift) + @upper_case_start
      is_lower_case?(i) ->
        rotate(i - @lower_case_start, shift) + @lower_case_start
      true ->
        i
    end
  end

  defp rotate(i, shift) do
    rem(i + shift, @letter_length)
  end

  def rot13(string) do
    encrypt(string, 13)
  end
end
25 Sep 2014, 07:19
Patrick_pragsmall

Patrick Oscity (13 posts)

My Solution:

defmodule Caesar.Shared do
  def rot13(string) do
    Caesar.encrypt(string, 13)
  end
end

defprotocol Caesar do
  def encrypt(string, shift)
  defdelegate rot13(string), to: Caesar.Shared
end

defimpl Caesar, for: BitString do
  def encrypt(string, shift) do
    to_char_list(string)
    |> Caesar.encrypt(shift)
    |> to_string
  end
end

defimpl Caesar, for: List do
  def encrypt(string, shift) do
    Enum.map(string, &rotate(&1, shift))
  end

  @upper 65..90
  @lower 97..122

  def rotate(char, shift) when char in @upper do
    do_rotate(char, shift, @upper)
  end

  def rotate(char, shift) when char in @lower do
    do_rotate(char, shift, @lower)
  end

  def rotate(char, _shift), do: char
  
  defp do_rotate(char, shift, range) do
    char
    |> normalize(range)
    |> shift(shift, range)
    |> denormalize(range)
  end

  defp shift(char, shift, min..max) do
    rem(char + shift, max-min+1)
  end

  defp normalize(char, min.._max) do
    char - min
  end

  defp denormalize(char, min.._max) do
    char + min
  end
end

IO.puts Caesar.encrypt 'single quoted a-z and A-Z', 0
IO.puts Caesar.encrypt 'single quoted a-z and A-Z', 1
IO.puts Caesar.rot13 'single quoted a-z and A-Z'
IO.puts Caesar.rot13 "double quoted hello"
IO.puts Caesar.rot13 "Let's keep non-ASCII characters unencrypted to be VIM compatible, shall we?"
IO.puts Caesar.rot13 Caesar.rot13 "I am a SYMMETRIC cipher"
# single quoted a-z and A-Z
# tjohmf rvpufe b-a boe B-A
# fvatyr dhbgrq n-m naq N-M
# qbhoyr dhbgrq uryyb
# Yrg'f xrrc aba-NFPVV punenpgref harapelcgrq gb or IVZ pbzcngvoyr, funyy jr?
# I am a SYMMETRIC cipher
21 Dec 2014, 14:07
Generic-user-small

Tim Rozmajzl (3 posts)

Here’s my attempt. BTW, I tried to use the when char in @upper guard clause a la Patrick, but received an error about not being able to invoke remote function Module.get_attribute/3 inside of a match.

defprotocol Cypher do
  def encrypt(string, shift)
  def rot13(string)
end

defimpl Cypher, for: List do

  def encrypt(list, shift) do
      list |> (Enum.map &encrypt_char(&1, shift))
  end

  def rot13(list), do: encrypt(list, 13)

  def encrypt_char(char, shift) when char in ?a..?z do
        ?a + rem(char + shift - ?a, 26)
  end
  def encrypt_char(char, shift) when char in ?A..?Z do
        ?A + rem(char + shift - ?A, 26)
  end
  def encrypt_char(char = ?\s, shift), do: char
  
end

defimpl Cypher, for: BitString do
  def encrypt(string, shift) do
    Cypher.List.encrypt(String.to_char_list(string), shift)
      |> List.to_string
  end

  def rot13(string), do: encrypt(string, 13)
end
24 Jan 2015, 22:45
Generic-user-small

Pierre Sugar (56 posts)

defprotocol Caesar do
  def cypher(string, offset)
  def rot13(string)
end

defimpl Caesar, for: [BitString] do
  def cypher(string, offset), do: string 
                                  |> to_char_list 
                                  |> CaesarHelper.rotate(offset)

  def rot13(string), do: cypher(string, 13)
end

defimpl Caesar, for: [List] do
  def cypher(list, offset), do: list 
                                |> CaesarHelper.rotate(offset)

  def rot13(list), do: cypher(list, 13)
end

defmodule CaesarHelper do
  def rotate(values, offset) do
    values |> Enum.map(fn(x) -> adjust_to_char(x+rem(offset, 90)) end)
  end

  def adjust_to_char(value) when value > ?\z, do: rem(value, ?z) + ?\s
  def adjust_to_char(value) when value < ?\s, do: ?z - rem(?\s - value, 90)
  def adjust_to_char(value),                  do: value
end

IO.puts Caesar.cypher([69,108,105,120,105,114], 3)                  # => Hol!Lu
IO.puts Caesar.cypher([72,111,108,33,108,117], -3)                  # => Elixir
IO.puts Caesar.cypher("Elixir rocks",1004)                          # => Szw,w&.&#qy'
IO.puts Caesar.cypher(Caesar.cypher("Elixir rocks", 1004), -1004)   # => Elixir rocks
IO.puts Caesar.rot13("Elixir rocks")                                # => Ryv+v%-%"px& 
IO.puts Caesar.cypher(Caesar.rot13("Elixir rocks"), -13)            # => Elixir rocks
  You must be logged in to comment