simple_cipher
This commit is contained in:
84
elixir/simple-cipher/lib/simple_cipher.ex
Normal file
84
elixir/simple-cipher/lib/simple_cipher.ex
Normal file
@@ -0,0 +1,84 @@
|
||||
defmodule SimpleCipher do
|
||||
@chars ?a..?z
|
||||
|
||||
defguardp is_char(char) when char in @chars
|
||||
defguardp is_sign(sign) when sign in [-1, 1]
|
||||
|
||||
defp cipher(text, key) do
|
||||
length = String.length(text)
|
||||
times = div(length, String.length(key)) + 1
|
||||
|
||||
key
|
||||
|> Kernel.to_charlist()
|
||||
|> Enum.map(&(&1 - ?a))
|
||||
|> List.duplicate(times)
|
||||
|> List.flatten()
|
||||
|> Enum.take(length)
|
||||
end
|
||||
|
||||
defp rotate(text, key, sign) when is_binary(text) and is_sign(sign) do
|
||||
cipher = cipher(text, key)
|
||||
|
||||
text
|
||||
|> Kernel.to_charlist()
|
||||
|> Enum.with_index(fn element, index -> rotate(element, Enum.at(cipher, index), sign) end)
|
||||
|> Kernel.to_string()
|
||||
end
|
||||
|
||||
defp rotate(char, shift, sign) when is_sign(sign) and is_char(char) and is_char(char + sign * shift), do: char + sign * shift
|
||||
defp rotate(char, shift, sign) when is_sign(sign) and is_char(char), do: char + sign * shift - sign * 26
|
||||
|
||||
@doc """
|
||||
Given a `plaintext` and `key`, encode each character of the `plaintext` by
|
||||
shifting it by the corresponding letter in the alphabet shifted by the number
|
||||
of letters represented by the `key` character, repeating the `key` if it is
|
||||
shorter than the `plaintext`.
|
||||
|
||||
For example, for the letter 'd', the alphabet is rotated to become:
|
||||
|
||||
defghijklmnopqrstuvwxyzabc
|
||||
|
||||
You would encode the `plaintext` by taking the current letter and mapping it
|
||||
to the letter in the same position in this rotated alphabet.
|
||||
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
defghijklmnopqrstuvwxyzabc
|
||||
|
||||
"a" becomes "d", "t" becomes "w", etc...
|
||||
|
||||
Each letter in the `plaintext` will be encoded with the alphabet of the `key`
|
||||
character in the same position. If the `key` is shorter than the `plaintext`,
|
||||
repeat the `key`.
|
||||
|
||||
Example:
|
||||
|
||||
plaintext = "testing"
|
||||
key = "abc"
|
||||
|
||||
The key should repeat to become the same length as the text, becoming
|
||||
"abcabca". If the key is longer than the text, only use as many letters of it
|
||||
as are necessary.
|
||||
"""
|
||||
def encode(plaintext, key), do: rotate(plaintext, key, 1)
|
||||
|
||||
@doc """
|
||||
Given a `ciphertext` and `key`, decode each character of the `ciphertext` by
|
||||
finding the corresponding letter in the alphabet shifted by the number of
|
||||
letters represented by the `key` character, repeating the `key` if it is
|
||||
shorter than the `ciphertext`.
|
||||
|
||||
The same rules for key length and shifted alphabets apply as in `encode/2`,
|
||||
but you will go the opposite way, so "d" becomes "a", "w" becomes "t",
|
||||
etc..., depending on how much you shift the alphabet.
|
||||
"""
|
||||
def decode(ciphertext, key), do: rotate(ciphertext, key, -1)
|
||||
|
||||
@doc """
|
||||
Generate a random key of a given length. It should contain lowercase letters only.
|
||||
"""
|
||||
def generate_key(length) do
|
||||
1..length
|
||||
|> Enum.map(fn _i -> Enum.random(@chars) end)
|
||||
|> to_string()
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user