awochna

Elixir Math Tips

02 April 2017

The following post was inspired by the tricks I learned about performing math operations in Elixir while working through the Elixir Exercisms. I feel like I learned a lot about the nuances of the language while working through the exercises and I highly recommend it!

The big, key features of Elixir that set it apart among modern languages are its scalability, fault tolerance, and the actor model of proccesses. You probably aren’t using Elixir in particular for its math capabilities, but that doesn’t mean you won’t be doing math operations in Elixir. Below is a list (in no particular order) of some of the little things I learned about performing math in Elixir.

Don’t always divide with /, use div/2

This is especially true when you want an integer as the result. By default, dividing with / results in a float:

iex> 4 / 2
2.0

From here, to get to a integer, you would have to use trunc/1, which cuts off any decimals, or round/1 which rounds to the nearest integer:

iex> 5 / 2
2.5
iex> trunc(5 / 2)
2
iex> round(5 / 2)
3

If you want just the integer (probably because you’ll get the remainder using rem/2), you can just use div/2:

iex> div(5, 2)
2
iex> rem(5, 2)
1

Personally, while working through the exercisms, I found that I end up using div/2 more than diving with /.

Don’t forget you have access to the Erlang stdlib’s :math module

This has been a personal difficulty of mine when transitioning my thinking to Elixir. I always seem to forget that I have access to the whole of the Erlang standard library, which includes a :math module. Need to find the square root of something or solve a logarithmic expression? Erlang has a :math.sqrt/1 and :math.log/1.

It’s also a good idea to probably expand on this and just try to keep this in mind while working in Elixir.

You probably don’t want to use :math.pow/2 for large numbers

One exception I found is that when raising a base number to an exponent, you probably don’t want to use the :math.pow/2 function. This has something to do with how the :math library handles large numbers, and is something that I didn’t dive into to figure out.

iex> :math.pow(42, 909)
** (ArithmeticError) bad argument in arithmetic expression
    (stdlib) :math.pow(42, 909)

Instead, you can easily define your own using two function heads:

def pow(base, 1), do: base
def pow(base, exp), do: base * pow(base, exp - 1)

This will give you exactly what you want, a really big number.

Erlang’s :crypto module contains mod_pow/3

This one comes from the Diffie-Hellmen exercise. To generate a public key or a shared secret, you need to get the modulus of the result of raising a very large base to a very large exponent. You can write this out in Elixir and it will happily perform the operation for you (using the previously defined pow/2):

def generate_public_key(mod, base, exp), do: rem(pow(base, exp), mod)

However, this will choke in calculating a sufficiently large numbers and be catastrophically slow. Since what we’re working with here is a cryptographic algorithm, we should keep Erlang’s :crypto module in mind. It contains a mod_pow/3 function that can do this for us, and really fast. The one catch here is that it returns a binary that you have to decode into an unsigned integer. The bonus here, is that it actually is returning an integer for you, no round/1 or trunc/1 necessary!

def generate_public_key(mod, base, exp) do
  :binary.decode_unsigned(:crypto.mod_pow(base, exp, mod))
end

I haven’t delved into why it’s so fast exactly, but I assumed it has something to do about it not being worried about intermediate integer representation.

Conclusion

I think the best advice I have to give, both for math and Elixir development in general, is just don’t forget about all of the extra goodies you get from Erlang!

Have an interesting or cool math trick of your own? Comment below or tell me about it on twitter!

Related Posts