changed README.md
 
@@ -1,18 +1,24 @@
1
1
# Math
2
2
3
+ [![hex.pm version](https://img.shields.io/hexpm/v/math.svg)](https://hex.pm/packages/math)
4
+ [![Build Status](https://travis-ci.org/folz/math.svg?branch=master)](https://travis-ci.org/folz/math)
5
+
3
6
The Math module adds many useful functions that extend Elixir's standard library.
4
7
5
8
- General Functions
6
- - `a <~> b` Comparison of floats, to check if they are _nearly_ equal.
9
+ - `a <~> b` Comparison of floats, to check if they are effectively equal (if their absolute difference is less than `@epsilon`).
7
10
- `Math.pow(x, n)` Arithmetic exponentiation. Works both with integer powers and floats.
8
11
- `Math.sqrt(x)` The square root of *x*.
9
12
- `Math.nth_root(x, n)` The n-th root of *x*.
10
13
- `Math.isqrt(x)` The integer square root of *x*.
11
14
- `Math.gcd(a, b)` The greatest common divisor of *a* and *b*.
15
+ - `Math.egcd(a, b)` Calculates integers *gcd*, *s*, and *t* in `as + bt = gcd(a,b)`. See also `Math.gcd(a, b)`
12
16
- `Math.lcm(a, b)` The least common multiple of *a* and *b*.
13
17
- `Math.factorial(n)` The *n*-th factorial number.
14
18
- `Math.k_permutations(n, k)` The number of distinct ways to create groups of size *k* from *n* distinct elements.
15
19
- `Math.k_combinations(n, k)` The number of distinct ways to create groups of size *k* from *n* distinct elements where order does not matter.
20
+ - `Math.mod_inv(a, m)` An integer *b* that fufills `ab = 1 (mod m)`. Returns an ok/error tuple.
21
+ - `Math.mod_inv!(a, m)` See `Math.mod_inv(a, m)`, but returns the value or raises an error.
16
22
17
23
18
24
- Logarithms
 
@@ -51,25 +57,30 @@ The Math module adds many useful functions that extend Elixir's standard library
51
57
52
58
Math is [available in Hex](https://hex.pm/packages/math). The package can be installed by:
53
59
54
- 1. Add math to your list of dependencies in `mix.exs`:
60
+ 1. Add `math` to your list of dependencies in `mix.exs`:
55
61
56
- def deps do
57
- [
58
- {:math, "~> 0.3.1"}
59
- ]
60
- end
62
+ ```ex
63
+ def deps do
64
+ [
65
+ {:math, "~> 0.4.0"}
66
+ ]
67
+ end
68
+ ```
61
69
62
- 2. Require or import the Math library anywhere in your code you'd like:
70
+ 2. Require or import the Math library anywhere in your code you'd like:
63
71
64
- require Math
72
+ ```ex
73
+ require Math
65
74
66
- or
75
+ # or
67
76
68
- import Math
77
+ import Math
78
+ ```
69
79
70
- (Importing allows usage of the `<~>` operator)
80
+ (Importing allows usage of the `<~>` operator)
71
81
72
82
## Changelog
83
+ - 0.5.0 Adds `Math.mod_inv`, `Math.mod_inv!` and `Math.egcd`
73
84
- 0.4.0 Adds `Math.Enum.mode/1`.
74
85
- 0.3.1 Updates formatting to hide warnings in newer versions of Elixir.
75
86
- 0.3.0 Fixed incorrect median for lists with even number of items. Updated tests.
changed hex_metadata.config
 
@@ -10,4 +10,4 @@
10
10
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/folz/math">>}]}.
11
11
{<<"name">>,<<"math">>}.
12
12
{<<"requirements">>,[]}.
13
- {<<"version">>,<<"0.4.0">>}.
13
+ {<<"version">>,<<"0.5.0">>}.
changed lib/math.ex
 
@@ -9,7 +9,7 @@ defmodule Math do
9
9
@epsilon 1.0e-15
10
10
11
11
# Theoretical limit is 1.80e308, but Erlang errors at that value, so the practical limit is slightly below that one.
12
- @max_value 1.79769313486231580793e308
12
+ @max_value 1.79_769_313_486_231_580_793e308
13
13
14
14
@type x :: number
15
15
@type y :: number
 
@@ -192,11 +192,43 @@ defmodule Math do
192
192
6
193
193
"""
194
194
@spec gcd(integer, integer) :: non_neg_integer
195
- def gcd(a, 0), do: abs(a)
196
195
197
- def gcd(0, b), do: abs(b)
198
- def gcd(a, b) when a < 0 or b < 0, do: gcd(abs(a), abs(b))
199
- def gcd(a, b), do: gcd(b, rem(a, b))
196
+ def gcd(a, b) when is_integer(a) and is_integer(b), do: egcd(a, b) |> elem(0)
197
+
198
+ @doc """
199
+ Calculates integers `gcd`, `s`, and `t` for `as + bt = gcd(a, b)`
200
+
201
+ Also see `Math.gcd/2`.
202
+
203
+ Returns a tuple: `{gcd, s, t}`
204
+
205
+ ## Examples
206
+
207
+ iex> Math.egcd(2, 4)
208
+ {2, 1, 0}
209
+ iex> Math.egcd(2, 3)
210
+ {1, -1, 1}
211
+ iex> Math.egcd(12, 8)
212
+ {4, 1, -1}
213
+ iex> Math.egcd(54, 24)
214
+ {6, 1, -2}
215
+ iex> Math.egcd(-54, 24)
216
+ {6, 1, -2}
217
+ """
218
+ @spec egcd(integer, integer) :: non_neg_integer
219
+ def egcd(a, b) when is_integer(a) and is_integer(b), do: _egcd(abs(a), abs(b), 0, 1, 1, 0)
220
+
221
+ defp _egcd(0, b, s, t, _u, _v), do: {b, s, t}
222
+
223
+ defp _egcd(a, b, s, t, u, v) do
224
+ q = div(b, a)
225
+ r = rem(b, a)
226
+
227
+ m = s - u * q
228
+ n = t - v * q
229
+
230
+ _egcd(r, a, u, v, m, n)
231
+ end
200
232
201
233
@doc """
202
234
Calculates the Least Common Multiple of two numbers.
 
@@ -472,4 +504,63 @@ defmodule Math do
472
504
"""
473
505
@spec atanh(x) :: float
474
506
defdelegate atanh(x), to: :math
507
+
508
+ @doc """
509
+ Computes the modular multiplicatibe inverse of `a` under modulo `m`
510
+
511
+ In other words, given integers `a` and `m` calculate a value `b` for `ab = 1 (mod m)`
512
+
513
+ Returns an `{:ok, b}` tuple or `{:error, :not_coprime}` when `b` cannot be calculated for the inputs.
514
+
515
+ ## Examples
516
+
517
+ iex> Math.mod_inv 3, 11
518
+ {:ok, 4}
519
+ iex> Math.mod_inv 10, 17
520
+ {:ok, 12}
521
+ iex> Math.mod_inv 123, 455
522
+ {:ok, 37}
523
+ iex> Math.mod_inv 123, 456
524
+ {:error, :not_coprime}
525
+
526
+ """
527
+ @spec mod_inv(a :: integer, m :: integer) :: {:ok, integer} | {:error, :not_coprime}
528
+ def mod_inv(a, m)
529
+
530
+ def mod_inv(a, m) when is_float(a) or is_float(m),
531
+ do: raise(ArgumentError, "Inputs cannot be of type float")
532
+
533
+ def mod_inv(a, m) when is_integer(a) and is_integer(m) do
534
+ case egcd(a, m) do
535
+ {1, s, _t} -> {:ok, rem(s + m, m)}
536
+ _ -> {:error, :not_coprime}
537
+ end
538
+ end
539
+
540
+ @doc """
541
+ Computes the modular multiplicatibe inverse of `a` under modulo `m`
542
+
543
+ Similar to `mod_in/2`, but returns only the value or raises an error.
544
+
545
+ ## Examples
546
+
547
+ iex> Math.mod_inv! 3, 11
548
+ 4
549
+ iex> Math.mod_inv! 10, 17
550
+ 12
551
+ iex> Math.mod_inv! 123, 455
552
+ 37
553
+ iex> Math.mod_inv! 123, 456
554
+ ** (ArithmeticError) Inputs are not coprime!
555
+
556
+ """
557
+ @spec mod_inv!(a :: integer, m :: integer) :: integer
558
+ def mod_inv!(a, m)
559
+
560
+ def mod_inv!(a, m) do
561
+ case mod_inv(a, m) do
562
+ {:ok, b} -> b
563
+ _ -> raise ArithmeticError, "Inputs are not coprime!"
564
+ end
565
+ end
475
566
end
changed mix.exs
 
@@ -6,7 +6,7 @@ defmodule Math.Mixfile do
6
6
def project do
7
7
[
8
8
app: :math,
9
- version: "0.4.0",
9
+ version: "0.5.0",
10
10
elixir: "~> 1.2",
11
11
description: description(),
12
12
package: package(),