How to get the codepoint of a variable?

Hello Elixir community,

in order to implement a cipher for a code challenge I have to add the integer representation of each key in the message and the key among other things. The integer representation: A becomes 1, B becomes 2 etc. We can ignore the offset in ASCII for now.

msg: ABC = 1,2,3
key: DEF = 4,5,6

=> 1+4, 2+5, 3+6
=> 5,7,9

I tried to create a function that adds the codepoints of two chars a and b:

def combine(a, b), do: ?a + ?b

The problem is that the codepoints of the letters a and b are added, not the parameter’s codepoints. Hence I’m always adding 97 + 98, regardless which chars I passed to combine.

Is there an alternative way to get the code point of a parameter/variable?

Hmm, why not just:

def combine(a, b), do: a + b

Or what are you trying to accomplish?

For note, ?x is just the ASCII value of x, the actual letter, it has nothing to do with any binding that may be named that way as well.

Hello @OvermindDL1,

thank you for your reply.

I’m trying to implement the Solitaire cipher. For this I have a message and a key of the same length which only contain uppercased letters of the latin alphabet.

In the next step I have to add the numerical representations (e.g. A being 1 in the alphabet or 65 in ASCII) of each letter in the message and the key. The result is the encrypted message.

I wrote a zipWith helper function (borrowed from Haskell) to add the letters of the key and message:

zipWith(msg, key, &combiner/2)

As you already saw in the implementation of the combiner method it takes two letters and adds their numerical representation.

The point where I’m struggling is that I can’t get the ASCII code for the letters in order to add them.

@digitalcraftsman: How about using charlist?

You should know that:

'abc' == [97, 98, 99]

So you will have:

msg = 'abc'
key = 'def`

Then you can use &Enum.with_index/1.
Finally you can use &Enum.map/1 on one of them.
Example:

defmodule Example do
  def sample(msg, key) do
    msg_with_index = Enum.with_index(msg)
    key_with_index = Enum.with_index(key)
    IO.puts "message codepoints: " <> inspect(msg, charlists: :as_lists)
    IO.puts "key codepoints: " <> inspect(key, charlists: :as_lists)
    Enum.map(msg_with_index, &combine(&1, key_with_index))
  end

  defp combine({msg, index}, key_with_index) do
    {key, _} = Enum.find(key_with_index, fn {_, index2} -> index == index2 end)
    IO.puts "#{msg} + #{key} = #{msg + key}"
    msg + key
  end
end

Finally run them like:

msg = 'abc'
key = 'def'
Example.sample(msg, key)

or:

msg = [1, 2, 3]
key = [4, 5, 6]
Example.sample(msg, key)

or:

msg_string = "abc"
key_string = "def"
msg = String.to_charlist(msg_string)
key = String.to_charlist(key_string)
Example.sample(msg, key)
2 Likes

Hello @Eiji,

thank you for sharing your ideas and posting the detailed code examples :thumbsup:

At the beginning I might have thought to complex about the problem with the goal to reduce the number of list operations. However, with your appraoch and some inspiration from the submitted solutions of RubyQuiz #1 I came up with the following:

Feel free to suggest improvements.

defmodule SolitaireCipher do
  def encrypt(msg, key) do
    process(msg, key, fn(m, k) -> 64 + mod(m + k - 128) end)
  end

  def decrypt(msg, key) do
    process(msg, key, fn(m, k) -> 64 + mod(m - k) end)
  end

  defp process(msg, key, routine) do
    msg
    |> prepare
    |> Enum.map(fn(msg_p) -> combine(prepare(key), msg_p, routine) end)
    |> List.to_string
  end

  defp prepare(input) do
    input
    |> String.upcase
    |> String.replace(~r/[^A-Z]/, "")
    |> String.to_charlist
    |> Enum.with_index
  end

  defp combine(key_with_index, {m, m_index}, routine) do
    {k, _} = Enum.find(key_with_index, fn({_, k_index}) -> m_index == k_index end)
    routine.(m, k)
  end

  defp mod(char) when char > 26, do: char-26
  defp mod(char) when char <  1, do: char+26
  defp mod(char), do: char
end

And if someone wants to try the example from the quiz:

msg = "Code in Ruby, live longer!"
key = "DWJXHYRFDGTMSHPUURXJ"

SolitaireCipher.encrypt(msg, key) 
|> SolitaireCipher.decrypt(key)

@digitalcraftsman: if it works as expected (decrypted message is not same as original) then it’s ok
I’m not expert of speed optimisations and cryptography, so if there is something to suggest then I see only indention and documentation + type validation.
Please use 2 spaces for indention (at least when sharing code in forum - simple replace) - I think that comparing this my code looks better :smiley:
Example:

defmodule SolitaireCipher do
  @moduledoc """
  Solitare Cipher
  ( ... )

  Usage:

      iex> msg = "Code in Ruby, live longer!"
      "Code in Ruby, live longer!"
      iex> key = "DWJXHYRFDGTMSHPUURXJ"
      "DWJXHYRFDGTMSHPUURXJ"
      iex> msg |> SolitaireCipher.encrypt(key) |> SolitaireCipher.decrypt(key)
      "CODEINRUBYLIVELONGER"
  """

  @spec encrypt(String.t, String.t) :: String.t
  @doc """
  Encrypting message.

  This function requires two parameters:
  `msg` - message to encrypt
  `key` - key for encryption and decryption process
  """
  def encrypt(msg, key) when is_bitstring(msg) and is_bitstring(key) do
    # ...
  end

  # and similarly for decrypt method
  # ...

end

Well, the message that the types in is sanitized before the encryption starts. This process removes everything that isn’t a letter of the latin alphabet and uppercases the remaining letters. Hence you actually encrypt “CODEINRUBYLIVELONGER” according to the exercise.

This was just a fun exercise so that I’ve a reason to get more familiar with the standard library. The cryptographic aspect can be omitted. I was more talking about reducing operations on the strings and char lists.

Please use 2 spaces for indention (at least when sharing code in forum - simple replace) - I think that comparing this my code looks better

I’ll fix the indentation in the code example above. Regarding documentation I’ll look into this as well. Currently, I’m half way through Elixir in Action and there still a ton of stuff left to learn.

Thanks again for your time.

1 Like