How to translate this piece of Golang code to Elixir?

I’m fresh new to Elixir. I’m trying to port a golang library to elixir. But I don’t know how to deal with the the loop part. As a functional programming language. Elixir provides no loop.

func (r *ISAAC) isaac() {
	r.cc = r.cc + 1    /* cc just gets incremented once per 256 results */
	r.bb = r.bb + r.cc /* then combined with bb */

	for i := 0; i < 256; i++ {
		x := r.mm[i]
		switch i % 4 {
		case 0:
			r.aa = r.aa ^ (r.aa << 13)
		case 1:
			r.aa = r.aa ^ (r.aa >> 6)
		case 2:
			r.aa = r.aa ^ (r.aa << 2)
		case 3:
			r.aa = r.aa ^ (r.aa >> 16)
		}
		r.aa = r.mm[(i+128)%256] + r.aa
		y := r.mm[(x>>2)%256] + r.aa + r.bb
		r.mm[i] = y
		r.bb = r.mm[(y>>10)%256] + x
		r.randrsl[i] = r.bb
	}

	/* Note that bits 2..9 are chosen from x but 10..17 are chosen
	   from y.  The only important thing here is that 2..9 and 10..17
	   don't overlap.  2..9 and 10..17 were then chosen for speed in
	   the optimized version (rand.c) */
	/* See http://burtleburtle.net/bob/rand/isaac.html
	   for further explanations and analysis. */
}
1 Like

Its hard to say without knowing what ISAAC looks like internally…

But loops are generally replaced by recursion or higher order functions in Enum.


In general I tend to avoid mechanical translations and rewrite from scratch.

There are a lot of concepts in go that you can’t use in elixir. Some do not have even a direct equivalen, like the massive amount of mutation you do in your code.

1 Like

Isaac is a cryptographically secure random number generator. (https://github.com/gtank/isaac/blob/master/isaac.go) This is the golang verison of the CSPRNG. The original version is in C. (http://burtleburtle.net/bob/c/readable.c)

1 Like

If its crypto, don’t touch it. First rule of crypto is to not do it yourself but to use libraries that have been there for a while and are battleproven.

Just implement some bindings to the C or GO versions via NIFs or C-Ports, or use some package from hex.pm if available, or take a look at the erlang :crypto module.

2 Likes

Thank you. I’ll give it a try.

1 Like

To answer your original question:

1 Like

A very slow, probably buggy mechanical translation:

def isaac(r) do
  import Bitwise 

  r = %{r | cc: r.cc + 1}
  r = %{r | bb: r.bb + r.cc}
  r = 
    Enum.reduce(0..255, r, fn i, r -> 
      x = Enum.at(r.mm, i)
      r = 
        case rem(i, 4) do
          0 -> %{r | aa: r.aa ^^^ (r.aa <<< 13)}
          1 -> %{r | aa: r.aa ^^^ (r.aa >>> 6)}
          2 -> %{r | aa: r.aa ^^^ (r.aa <<< 2)}
          3 -> %{r | aa: r.aa ^^^ (r.aa >>> 16)}
        end
      r = %{r | aa: Enum.at(r.mm, rem(i + 128, 256)) + r.aa}
      y = Enum.at(r.mm, rem(x >> 2, 256)) + r.aa + r.bb
      r = %{r | mm: List.replace_at(r.mm, i, y)}
      r = %{r | bb: Enum.at(r.mm, rem(y >>> 10, 256)) + x}
      %{r | randrsl: List.replace_at(r.randrsl, i, r.bb)}
    end)
2 Likes

I highlighted the reason why one doesn’t DIY crypto but uses battle proven libraries and wrappers.

1 Like

Yes of course, but porting code can be a great learning exercise.
TIL about Bitwise, which I’ve never had a reason to use yet :smiley:

4 Likes

Pointing to recursion, comprehensions, and higher-order functions only help so much. They tell us what the desired result will look like, or should look like, but not how to get there :confused:

@AlecHsueh Once you know what you’re doing, whether that’s by reading that particular Go fragment or not, then you can give it a go in Elixir. It’s hard to get rid of the for and the mutation with a line-by-line translation but understanding it helps to get to something that’s more Elixir-like.

There’s a collection of some kind underlying most fragments that have a C-like for. A brief glance at the above suggests that it involves byte manipulation or binary data. An Elixir Binary, along with a binary comprehension, and a bunch of meaningful functions might be an idiomatic choice here :slight_smile:

I hope that helps: it’s a great exercise.

3 Likes