Proposal: Kernel.mod/2 (Modulo function)

644 views
Skip to first unread message

eksperimental

unread,
Oct 25, 2015, 5:44:24 AM10/25/15
to elixir-l...@googlegroups.com
Some time ago I had to build a function to determine the GCD (greatest
common denominator), and I was suprised to find out that neither Elixir
nor Erlang, had a modulo function (the closest is rem and it's
So i decided to create one myself.

I think it will be good to have it in Kernel, since I think in the same
line as rem/2, and it's pretty common function in math that is missing.

there are two fashions of modulo, one that will return a negative
integer for `mod(number, modulus)` if number is negative (Java does
this),
an another fashion that will always return positive.
the latter one is the approach I choose.
But if needed I could created mod/3 taking :negative as last argument.

the code has been tested in every possible case, please let me know if
something has been left behind

Code:
https://github.com/eksperimental/experimental_elixir/blob/master/lib/kernel_modulo.ex
Tests:
https://github.com/eksperimental/experimental_elixir/blob/master/test/kernel_modulo_test.exs

Peter Hamilton

unread,
Oct 25, 2015, 11:58:24 AM10/25/15
to elixir-l...@googlegroups.com

Your explanation was cut off. How is rem insufficient?


--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/20151025164416.1a2492aa.eksperimental%40autistici.org.
For more options, visit https://groups.google.com/d/optout.

Theron Boerner

unread,
Oct 25, 2015, 12:07:07 PM10/25/15
to elixir-l...@googlegroups.com
Peter,

The difference is as such:
 
There is a difference between modulus and remainder. For example:
-21 mod 4 is 3 because -21 + 4 x 6 is 3.
But -21 divided by 4 gives -5 with a remainder of -1.
For positive values, there is no difference.


Booker Bense

unread,
Oct 25, 2015, 2:30:54 PM10/25/15
to elixir-lang-core
Mathematically there is no difference between 3 and  -1 in the modular arithmetic. 

(i.e. adding 3 to a number mod 4 is the same as subtracting  1 mod 4 ) 
 
The only difference between rem and mod is the user's expectation of the result. 

You can write a perfectly fine GCD algorithm using rem. Any math you do shouldn't 
care, the problem comes when you take the math and apply it else where. 

I'd guess the reason mod is always positive in most languages relates to the 
problem is where want to cycle through an array in 
languages that do not allow negative array values. 

Is there any place in Elixir where you'd module arithmetic to get an "array index" that
doesn't understand that -1 is counting back from the end of the "array"? 

Regardless, your implementation seems overly complex to me, all you need is 

case rem(a,b) do 
         value when value > 0 -> value
         value  -> value + b 
end 

It might make more sense to implement it as a macro. 

- Booker C. Bense

Booker Bense

unread,
Oct 25, 2015, 3:15:53 PM10/25/15
to elixir-lang-core
Whoops, got the signs wrong that should be 

case rem(a,b) do 
         value when value < 0 -> value + b 
         value  -> value
end 



On Sunday, October 25, 2015 at 11:30:54 AM UTC-7, Booker Bense wrote:
Mathematically there is no difference between 3 and  -1 in the modular arithmetic. 

(i.e. adding 3 to a number mod 4 is the same as subtracting  1 mod 4 ) 
 
The only difference between rem and mod is the user's expectation of the result. 

You can write a perfectly fine GCD algorithm using rem. Any math you do shouldn't 
care, the problem comes when you take the math and apply it else where. 

I'd guess the reason mod is always positive in most languages relates to the 
problem is where want to cycle through an array in 
languages that do not allow negative array values. 

Is there any place in Elixir where you'd module arithmetic to get an "array index" that
doesn't understand that -1 is counting back from the end of the "array"? 

Regardless, your implementation seems overly complex to me, all you need is 



Booker Bense

unread,
Oct 25, 2015, 4:29:03 PM10/25/15
to elixir-lang-core
Hmm, my simple case statement doesn't work when the modulus is negative. 

If you define modulus to mean always return the integer representation of the modulus group element 
that is between the modulus and zero, then when the modulus is negative, you should return a negative
number,  So that becomes, 

case rem(a,b) do 
   value when value > 0 and b < 0 -> value + b
   value when value < 0 and b > 0 -> value + b
   value -> value
end 

Or you could argue that a negative value in the modulus makes no sense as it's number of elements in 
the group of integers over addition and you can't have a negative set size.  

This is what Ruby does 

x.modulus(y)  means x-y*(x/y).floor

But that extends the % operator to work on real numbers, rather than just integers. 
However, :erlang.rem does not support that.

I think it's fairly important that we are going to create something that people
would grab rather than just use Kernel.rem, that it should not be slower. 

- Booker C. Bense

eksperimental

unread,
Oct 25, 2015, 4:47:09 PM10/25/15
to elixir-l...@googlegroups.com
Thank you for you feedback Peter and Taken
sorry the message was cut off,

Peter: the difference is that "mod/2" will always return positive
(forget that part of the email saying i could create a version of mod
that returns negative, I wrote the function long time ago
and forgot about the details).

Some languages implement only the remainder function, some others both,
naming them "%", "rem", "mod", or "div"
https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation


Brooker,
you are absolutetly right.
the reason why is looks overly complex is because I translated it from
the Mathematical definition... I forgot about the similarities with
rem/2,
so thank you for that.
One edge case that your function implementation doesn't cover is when
dealing with two negative numbers,

I have updated my code based on your suggestions,
it's implemented as a normalize_mod private function.
let me know what you think,
https://github.com/eksperimental/experimental_elixir/blob/master/lib/kernel_modulo.ex#L42-L53

It passes all the test, so we should be good.
> > <https://groups.google.com/d/msgid/elixir-lang-core/CAOMhEnzJtk9C6ySSpUiSqcKT9DrscG11%3DoaXaMz3MUN89egMWw%40mail.gmail.com?utm_medium=email&utm_source=footer>
> > .

Robert Virding

unread,
Oct 26, 2015, 6:03:08 PM10/26/15
to elixir-lang-core, eksper...@autistici.org
The reason why 'rem' is called 'rem' because that is what the '%' did in C, at least the C's we were working with. We felt it was better to get the name right. :-)

Robert

Booker Bense

unread,
Oct 26, 2015, 6:26:04 PM10/26/15
to elixir-lang-core
I knew all this sounded familiar... I vaguely remember writing this code a lot

index = foo % array_length
if ( index < 0 ) { index = index + array_length} 

- Booker C. Bense 

On Mon, Oct 26, 2015 at 3:03 PM, Robert Virding <rvir...@gmail.com> wrote:
The reason why 'rem' is called 'rem' because that is what the '%' did in C, at least the C's we were working with. We felt it was better to get the name right. :-)

Robert

--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/XGDA_EPgPRY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/cc004c65-6300-4c4b-822d-f36beb4398aa%40googlegroups.com.

Alexei Sholik

unread,
Oct 27, 2015, 5:02:06 AM10/27/15
to elixir-lang-core
The table in this Wikipedia article shows dozens of modulo/remainder operations in various languages, but only a handful of them return a result that is always positive. https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation

What makes you say that it's a common function, eksperimental?

eksperimental

unread,
Oct 27, 2015, 11:29:42 AM10/27/15
to elixir-l...@googlegroups.com
You are right Alexir,
What we should do is do as most of the functions that support two
functions (like rem and mod),
is one should have the sign of the Dividend (rem in this case)
and mod should have the sign of the Divisor.

Eric Meadows-Jönsson

unread,
Oct 27, 2015, 6:25:37 PM10/27/15
to elixir-l...@googlegroups.com
I think it's wrong to add another global modulo function (and one that doesn't work in guards). If we add a module for math functions it would fit in there.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/20151027222935.55a49cd6.eksperimental%40autistici.org.

For more options, visit https://groups.google.com/d/optout.


--
Eric Meadows-Jönsson

eksperimental

unread,
Nov 1, 2015, 7:32:15 AM11/1/15
to elixir-l...@googlegroups.com
Thanks Booker for all the comments
Sadly it cannot work in guards,
the final code is
https://github.com/eksperimental/experimental_elixir/blob/master/lib/kernel_modulo.ex

@doc """
Modulo operation.

Returns the remainder after division of `number` by `modulus`.
The sign of the result will always be the same sign as the `modulus`.
"""
@spec mod(integer, integer) :: non_neg_integer
def mod(number, modulus) when is_integer(number) and
is_integer(modulus) do
case rem(number, modulus) do
remainder when remainder > 0 and modulus < 0
or remainder < 0 and modulus > 0 ->
remainder + modulus
remainder ->
remainder
end
end

And thanks Robert, Alexei and Eric for useful comments.

On Tue, 27 Oct 2015 23:25:34 +0100
Eric Meadows-Jönsson <eric.meado...@gmail.com> wrote:

> I think it's wrong to add another global modulo function (and one that
> doesn't work in guards). If we add a module for math functions it
> would fit in there.
>
> On Tuesday, 27 October 2015, eksperimental
> <eksper...@autistici.org> wrote:
>
> > You are right Alexir,
> > What we should do is do as most of the functions that support two
> > functions (like rem and mod),
> > is one should have the sign of the Dividend (rem in this case)
> > and mod should have the sign of the Divisor.
> >
> > On Tue, 27 Oct 2015 11:01:45 +0200
> > Alexei Sholik <alcos...@gmail.com <javascript:;>> wrote:
> >
> > > The table in this Wikipedia article shows dozens of
> > > modulo/remainder operations in various languages, but only a
> > > handful of them return a result that is always positive.
> > >
> > https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation
> > >
> > > What makes you say that it's a common function, eksperimental?
> > >
> > > On Sun, Oct 25, 2015 at 10:47 PM, eksperimental
> > > <eksper...@autistici.org <javascript:;>
> > > > > >> <javascript:;>. To
> > view this
> > > > > >> discussion on the web visit
> > > > > >>
> > > >
> > https://groups.google.com/d/msgid/elixir-lang-core/20151025164416.1a2492aa.eksperimental%40autistici.org
> > > > > >> .
> > > > > >> For more options, visit https://groups.google.com/d/optout.
> > > > > >>
> > > > > > --
> > > > > > You received this message because you are subscribed to the
> > > > > > Google Groups "elixir-lang-core" group.
> > > > > > To unsubscribe from this group and stop receiving emails
> > > > > > from it, send an email to
> > > > > > elixir-lang-co...@googlegroups.com
> > > > > > <javascript:;>. To
> > view this
> > > > > > discussion on the web visit
> > > > > >
> > > >
> > https://groups.google.com/d/msgid/elixir-lang-core/CAOMhEnzJtk9C6ySSpUiSqcKT9DrscG11%3DoaXaMz3MUN89egMWw%40mail.gmail.com
> > > > > > <
> > > >
> > https://groups.google.com/d/msgid/elixir-lang-core/CAOMhEnzJtk9C6ySSpUiSqcKT9DrscG11%3DoaXaMz3MUN89egMWw%40mail.gmail.com?utm_medium=email&utm_source=footer
> > > > >
> > > > > > .
> > > > > >
> > > > > > For more options, visit https://groups.google.com/d/optout.
> > > > > >
> > > > >
> > > >
> > > > --
> > > > You received this message because you are subscribed to the
> > > > Google Groups "elixir-lang-core" group.
> > > > To unsubscribe from this group and stop receiving emails from
> > > > it, send an email to
> > > > elixir-lang-co...@googlegroups.com
> > <javascript:;>.
> > > > To view this discussion on the web visit
> > > >
> > https://groups.google.com/d/msgid/elixir-lang-core/20151026034702.22385929.eksperimental%40autistici.org
> > > > .
> > > > For more options, visit https://groups.google.com/d/optout.
> > > >
> > >
> >
> > --
> > You received this message because you are subscribed to the Google
> > Groups "elixir-lang-core" group.
> > To unsubscribe from this group and stop receiving emails from it,
> > send an email to elixir-lang-co...@googlegroups.com
> > <javascript:;>. To view this discussion on the web visit
> > https://groups.google.com/d/msgid/elixir-lang-core/20151027222935.55a49cd6.eksperimental%40autistici.org
Reply all
Reply to author
Forward
0 new messages