changed
README.md
|
@@ -45,6 +45,7 @@ The Math module adds many useful functions that extend Elixir's standard library
|
45
45
|
- `Math.Enum.product(collection)` The result of multiplying all elements in the passed collection.
|
46
46
|
- `Math.Enum.mean(collection)` the mean of the numbers in the collection.
|
47
47
|
- `Math.Enum.median(collection)` the median of the numbers in the collection.
|
48
|
+ - `Math.Enum.mode(collection)` the mode of the numbers in the collection.
|
48
49
|
|
49
50
|
## Installation
|
50
51
|
|
|
@@ -54,7 +55,7 @@ Math is [available in Hex](https://hex.pm/packages/math). The package can be ins
|
54
55
|
|
55
56
|
def deps do
|
56
57
|
[
|
57
|
- {:math, "~> 0.3.0"}
|
58
|
+ {:math, "~> 0.3.1"}
|
58
59
|
]
|
59
60
|
end
|
60
61
|
|
|
@@ -69,6 +70,8 @@ Math is [available in Hex](https://hex.pm/packages/math). The package can be ins
|
69
70
|
(Importing allows usage of the `<~>` operator)
|
70
71
|
|
71
72
|
## Changelog
|
73
|
+ - 0.4.0 Adds `Math.Enum.mode/1`.
|
74
|
+ - 0.3.1 Updates formatting to hide warnings in newer versions of Elixir.
|
72
75
|
- 0.3.0 Fixed incorrect median for lists with even number of items. Updated tests.
|
73
76
|
- 0.2.0 Added `factorial/1`, `nth_sqrt/2`, `k_permutations/2`, `k_combinations/2`, `gcd/2`, `lcm/2` and `Math.Enum` functions. Improved documentation.
|
74
77
|
- 0.1.0 Added integer variant of `pow/1`, `isqrt/2`, `deg2rad/1`, `rad2deg/1`. Improved documentation.
|
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.3.1">>}.
|
13
|
+ {<<"version">>,<<"0.4.0">>}.
|
changed
lib/math.ex
|
@@ -14,7 +14,6 @@ defmodule Math do
|
14
14
|
@type x :: number
|
15
15
|
@type y :: number
|
16
16
|
|
17
|
-
|
18
17
|
@doc """
|
19
18
|
The mathematical constant *π* (pi).
|
20
19
|
|
|
@@ -24,8 +23,7 @@ defmodule Math do
|
24
23
|
@spec pi :: float
|
25
24
|
defdelegate pi, to: :math
|
26
25
|
|
27
|
- @rad_in_deg (180/:math.pi)
|
28
|
-
|
26
|
+ @rad_in_deg 180 / :math.pi()
|
29
27
|
|
30
28
|
@doc """
|
31
29
|
The mathematical constant *τ* (tau).
|
|
@@ -59,18 +57,20 @@ defmodule Math do
|
59
57
|
"""
|
60
58
|
@spec number <~> number :: boolean
|
61
59
|
def x <~> y do
|
62
|
- absX = abs(x)
|
63
|
- absY = abs(y)
|
60
|
+ abs_x = abs(x)
|
61
|
+ abs_y = abs(y)
|
64
62
|
diff = abs(x - y)
|
65
63
|
|
66
64
|
# Hacky comparison for floats that are nearly equal.
|
67
65
|
cond do
|
68
66
|
x == y ->
|
69
67
|
true
|
68
|
+
|
70
69
|
x == 0 or y == 0 ->
|
71
70
|
diff < @epsilon
|
71
|
+
|
72
72
|
true ->
|
73
|
- diff / min((absX + absY), @max_value) < @epsilon
|
73
|
+ diff / min(abs_x + abs_y, @max_value) < @epsilon
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
|
@@ -116,9 +116,9 @@ defmodule Math do
|
116
116
|
defp _pow(x, n, y \\ 1)
|
117
117
|
defp _pow(_x, 0, y), do: y
|
118
118
|
defp _pow(x, 1, y), do: x * y
|
119
|
- defp _pow(x, n, y) when (n < 0), do: _pow(1 / x, -n, y)
|
119
|
+ defp _pow(x, n, y) when n < 0, do: _pow(1 / x, -n, y)
|
120
120
|
defp _pow(x, n, y) when rem(n, 2) == 0, do: _pow(x * x, div(n, 2), y)
|
121
|
- defp _pow(x, n, y), do: _pow(x * x, div((n - 1), 2), x * y)
|
121
|
+ defp _pow(x, n, y), do: _pow(x * x, div(n - 1, 2), x * y)
|
122
122
|
|
123
123
|
@doc """
|
124
124
|
Calculates the non-negative square root of *x*.
|
|
@@ -159,9 +159,9 @@ defmodule Math do
|
159
159
|
@spec isqrt(integer) :: integer
|
160
160
|
def isqrt(x)
|
161
161
|
|
162
|
- def isqrt(x) when x < 0, do: raise ArithmeticError
|
162
|
+ def isqrt(x) when x < 0, do: raise(ArithmeticError)
|
163
163
|
|
164
|
- def isqrt(x), do: _isqrt(x, 1, div((1 + x), 2))
|
164
|
+ def isqrt(x), do: _isqrt(x, 1, div(1 + x, 2))
|
165
165
|
|
166
166
|
defp _isqrt(x, m, n) when abs(m - n) <= 1 and n * n <= x, do: n
|
167
167
|
defp _isqrt(_x, m, n) when abs(m - n) <= 1, do: n - 1
|
|
@@ -170,7 +170,6 @@ defmodule Math do
|
170
170
|
_isqrt(x, n, div(n + div(x, n), 2))
|
171
171
|
end
|
172
172
|
|
173
|
-
|
174
173
|
#
|
175
174
|
@doc """
|
176
175
|
Calculates the Greatest Common divisor of two numbers.
|
|
@@ -197,7 +196,7 @@ defmodule Math do
|
197
196
|
|
198
197
|
def gcd(0, b), do: abs(b)
|
199
198
|
def gcd(a, b) when a < 0 or b < 0, do: gcd(abs(a), abs(b))
|
200
|
- def gcd(a, b), do: gcd(b, rem(a,b))
|
199
|
+ def gcd(a, b), do: gcd(b, rem(a, b))
|
201
200
|
|
202
201
|
@doc """
|
203
202
|
Calculates the Least Common Multiple of two numbers.
|
|
@@ -219,11 +218,11 @@ defmodule Math do
|
219
218
|
def lcm(a, b)
|
220
219
|
|
221
220
|
def lcm(0, 0), do: 0
|
221
|
+
|
222
222
|
def lcm(a, b) do
|
223
223
|
abs(Kernel.div(a * b, gcd(a, b)))
|
224
224
|
end
|
225
225
|
|
226
|
-
|
227
226
|
@precompute_factorials_up_to 1000
|
228
227
|
@doc """
|
229
228
|
Calculates the factorial of *n*: 1 * 2 * 3 * ... * *n*
|
|
@@ -244,12 +243,14 @@ defmodule Math do
|
244
243
|
|
245
244
|
def factorial(0), do: 1
|
246
245
|
|
247
|
- for {n, fact} <- (1..@precompute_factorials_up_to |> Enum.scan( {0, 1}, fn n, {_prev_n, prev_fact} -> {n, n * prev_fact} end)) do
|
246
|
+ for {n, fact} <-
|
247
|
+ 1..@precompute_factorials_up_to
|
248
|
+ |> Enum.scan({0, 1}, fn n, {_prev_n, prev_fact} -> {n, n * prev_fact} end) do
|
248
249
|
def factorial(unquote(n)), do: unquote(fact)
|
249
250
|
end
|
250
251
|
|
251
252
|
def factorial(n) when n >= 0 do
|
252
|
- n * factorial(n-1)
|
253
|
+ n * factorial(n - 1)
|
253
254
|
end
|
254
255
|
|
255
256
|
@doc """
|
|
@@ -278,7 +279,6 @@ defmodule Math do
|
278
279
|
div(factorial(n), factorial(n - k))
|
279
280
|
end
|
280
281
|
|
281
|
-
|
282
282
|
@doc """
|
283
283
|
Calculates the k-combinations of *n*.
|
284
284
|
|
|
@@ -299,7 +299,6 @@ defmodule Math do
|
299
299
|
div(factorial(n), factorial(k) * factorial(n - k))
|
300
300
|
end
|
301
301
|
|
302
|
-
|
303
302
|
# Logarithms and exponentiation
|
304
303
|
|
305
304
|
@doc """
|
|
@@ -338,6 +337,7 @@ defmodule Math do
|
338
337
|
"""
|
339
338
|
@spec log(x, number) :: float
|
340
339
|
def log(x, x), do: 1.0
|
340
|
+
|
341
341
|
def log(x, b) do
|
342
342
|
:math.log(x) / :math.log(b)
|
343
343
|
end
|
|
@@ -472,5 +472,4 @@ defmodule Math do
|
472
472
|
"""
|
473
473
|
@spec atanh(x) :: float
|
474
474
|
defdelegate atanh(x), to: :math
|
475
|
-
|
476
475
|
end
|
changed
lib/math/enum.ex
|
@@ -3,6 +3,7 @@ defmodule Math.Enum do
|
3
3
|
Math.Enum defines Math-functions that work on any collection extending the Enumerable protocol.
|
4
4
|
This means Maps, Lists, Sets, etc., and any custom collection types as well.
|
5
5
|
"""
|
6
|
+ require Integer
|
6
7
|
|
7
8
|
@doc """
|
8
9
|
Calculates the product, obtained by multiplying all elements in *collection* with eachother.
|
|
@@ -20,7 +21,7 @@ defmodule Math.Enum do
|
20
21
|
|
21
22
|
# General implementation for any enumerable.
|
22
23
|
def product(collection) do
|
23
|
- Enum.reduce(collection, &(&1 * &2))
|
24
|
+ Enum.reduce(collection, &*/2)
|
24
25
|
end
|
25
26
|
|
26
27
|
@doc """
|
|
@@ -42,11 +43,12 @@ defmodule Math.Enum do
|
42
43
|
iex> Math.Enum.mean []
|
43
44
|
nil
|
44
45
|
"""
|
45
|
- @spec mean(Enum.t) :: number
|
46
|
+ @spec mean(Enum.t()) :: number
|
46
47
|
def mean(collection)
|
47
48
|
|
48
49
|
def mean(collection) do
|
49
50
|
count = Enum.count(collection)
|
51
|
+
|
50
52
|
case count do
|
51
53
|
0 -> nil
|
52
54
|
_ -> Enum.sum(collection) / count
|
|
@@ -76,23 +78,53 @@ defmodule Math.Enum do
|
76
78
|
iex> Math.Enum.median []
|
77
79
|
nil
|
78
80
|
"""
|
79
|
- @spec median(Enum.t) :: number | nil
|
81
|
+ @spec median(Enum.t()) :: number | nil
|
80
82
|
def median(collection)
|
81
83
|
|
82
84
|
def median(collection) do
|
83
85
|
count = Enum.count(collection)
|
86
|
+ mid_point = div(count, 2)
|
87
|
+
|
84
88
|
cond do
|
85
|
- count == 0 -> nil
|
86
|
- rem(count, 2) == 1 -> # Middle element exists
|
87
|
- Enum.sort(collection) |> Enum.at(div(count, 2))
|
89
|
+ count == 0 ->
|
90
|
+ nil
|
91
|
+
|
92
|
+ # Middle element exists
|
93
|
+ Integer.is_odd(count) ->
|
94
|
+ collection
|
95
|
+ |> Enum.sort()
|
96
|
+ |> Enum.fetch!(mid_point)
|
97
|
+
|
88
98
|
true ->
|
89
|
- # Take two middle-most elements.
|
90
|
- sorted_collection = Enum.sort(collection)
|
91
|
- [
|
92
|
- Enum.at(sorted_collection, div(count, 2)),
|
93
|
- Enum.at(sorted_collection, div(count, 2) - 1)
|
94
|
- ]
|
95
|
- |> Math.Enum.mean
|
99
|
+ collection
|
100
|
+ |> Enum.sort()
|
101
|
+ |> Enum.slice((mid_point - 1)..mid_point)
|
102
|
+ |> Math.Enum.mean()
|
96
103
|
end
|
97
104
|
end
|
105
|
+
|
106
|
+ @doc """
|
107
|
+ Calculates the mode of a given collection of numbers.
|
108
|
+
|
109
|
+ Always returns a list. An empty input results in an empty list. Supports bimodal/multimodal collections by returning a list with multiple values.
|
110
|
+
|
111
|
+ ## Examples
|
112
|
+
|
113
|
+ iex> Math.Enum.mode [1, 2, 3, 4, 1]
|
114
|
+ [1]
|
115
|
+ iex> Math.Enum.mode [1, 2, 3, 2, 3]
|
116
|
+ [2, 3]
|
117
|
+ iex> Math.Enum.mode []
|
118
|
+ []
|
119
|
+ """
|
120
|
+ @spec mode(Enum.t()) :: Enum.t()
|
121
|
+ def mode(collection)
|
122
|
+
|
123
|
+ def mode(collection) do
|
124
|
+ collection
|
125
|
+ |> Enum.reduce(%{}, fn k, acc -> Map.update(acc, k, 0, &(&1 + 1)) end)
|
126
|
+ |> Enum.group_by(&elem(&1, 1), &elem(&1, 0))
|
127
|
+ |> Enum.max_by(&elem(&1, 0), fn -> {nil, []} end)
|
128
|
+ |> elem(1)
|
129
|
+ end
|
98
130
|
end
|
changed
mix.exs
|
@@ -1,22 +1,30 @@
|
1
1
|
defmodule Math.Mixfile do
|
2
2
|
use Mix.Project
|
3
3
|
|
4
|
+ @source_url "https://github.com/folz/math"
|
5
|
+
|
4
6
|
def project do
|
5
|
- [app: :math,
|
6
|
- version: "0.3.1",
|
7
|
- elixir: "~> 1.2",
|
8
|
- description: description(),
|
9
|
- package: package(),
|
10
|
- deps: deps(),
|
11
|
- build_embedded: Mix.env == :prod,
|
12
|
- start_permanent: Mix.env == :prod]
|
7
|
+ [
|
8
|
+ app: :math,
|
9
|
+ version: "0.4.0",
|
10
|
+ elixir: "~> 1.2",
|
11
|
+ description: description(),
|
12
|
+ package: package(),
|
13
|
+ deps: deps(),
|
14
|
+ source_url: @source_url,
|
15
|
+ build_embedded: Mix.env() == :prod,
|
16
|
+ start_permanent: Mix.env() == :prod,
|
17
|
+ docs: docs()
|
18
|
+ ]
|
13
19
|
end
|
14
20
|
|
15
21
|
# Configuration for the OTP application
|
16
22
|
#
|
17
23
|
# Type "mix help compile.app" for more information
|
18
24
|
def application do
|
19
|
- [applications: [:logger]]
|
25
|
+ [
|
26
|
+ extra_applications: [:logger]
|
27
|
+ ]
|
20
28
|
end
|
21
29
|
|
22
30
|
defp description do
|
|
@@ -27,15 +35,26 @@ defmodule Math.Mixfile do
|
27
35
|
|
28
36
|
defp package do
|
29
37
|
[
|
38
|
+ name: :math,
|
39
|
+ files: ["lib", "mix.exs", "README*", "LICENSE"],
|
30
40
|
maintainers: ["Rodney Folz", "Wiebe-Marten Wijnja/Qqwy"],
|
31
41
|
licenses: ["Apache-2.0"],
|
32
|
- links: %{"GitHub": "https://github.com/folz/math"}
|
42
|
+ links: %{GitHub: @source_url}
|
33
43
|
]
|
34
44
|
end
|
35
45
|
|
36
46
|
defp deps do
|
37
47
|
[
|
38
|
- {:ex_doc, ">= 0.11.4", only: [:dev]}
|
48
|
+ {:credo, "~> 1.1.0", only: [:dev, :test], runtime: false},
|
49
|
+ {:ex_doc, ">= 0.11.4", only: [:docs]},
|
50
|
+ {:inch_ex, ">= 0.0.0", only: [:docs]},
|
51
|
+ ]
|
52
|
+ end
|
53
|
+
|
54
|
+ defp docs do
|
55
|
+ [
|
56
|
+ main: "readme",
|
57
|
+ extras: ["README.md"]
|
39
58
|
]
|
40
59
|
end
|
41
60
|
end
|