diff --git a/src/gleam_community/maths/float_list.gleam b/src/gleam_community/maths/float_list.gleam index 860774b..455a40c 100644 --- a/src/gleam_community/maths/float_list.gleam +++ b/src/gleam_community/maths/float_list.gleam @@ -26,14 +26,14 @@ //// * **Distances, sums, and products** //// * [`sum`](#sum) //// * [`product`](#product) +//// * [`cumulative_sum`](#cumulative_sum) +//// * [`cumulative_product`](#cumulative_product) //// * [`norm`](#norm) //// * [`minkowski_distance`](#minkowski_distance) //// * [`euclidean_distance`](#euclidean_distance) //// * [`manhatten_distance`](#manhatten_distance) -//// * [`cumulative_sum`](#cumulative_sum) -//// * [`cumulative_product`](#cumulative_product) //// * **Ranges and intervals** -//// * [`arrange`](#arrange) +//// * [`arange`](#arange) //// * [`linear_space`](#linear_space) //// * [`logarithmic_space`](#logarithmic_space) //// * [`geometric_space`](#geometric_space) @@ -252,7 +252,7 @@ pub fn euclidean_distance( /// /// /// -/// Calculcate the Euclidean distance between two lists (representing vectors): +/// Calculcate the Manhatten distance between two lists (representing vectors): /// /// \\[ /// \sum_{i=1}^n \left|x_i - x_j \right| @@ -271,11 +271,11 @@ pub fn euclidean_distance( /// assert Ok(tol) = floatx.power(-10.0, -6.0) /// /// // Empty lists returns 0.0 -/// float_list.manhatten_distance([], [], 1.0) +/// float_list.manhatten_distance([], []) /// |> should.equal(Ok(0.0)) /// /// // Differing lengths returns error -/// float_list.manhatten_distance([], [1.0], 1.0) +/// float_list.manhatten_distance([], [1.0]) /// |> should.be_error() /// /// assert Ok(result) = float_list.manhatten_distance([0.0, 0.0], [1.0, 2.0]) @@ -516,17 +516,17 @@ pub fn geometric_space( /// import gleam_community/maths/float_list /// /// pub fn example () { -/// float_list.arrange(1.0, 5.0, 1.0) +/// float_list.arange(1.0, 5.0, 1.0) /// |> should.equal([1.0, 2.0, 3.0, 4.0]) /// /// // No points returned since /// // start smaller than stop and positive step -/// float_list.arrange(5.0, 1.0, 1.0) +/// float_list.arange(5.0, 1.0, 1.0) /// |> should.equal([]) /// /// // Points returned since /// // start smaller than stop but negative step -/// float_list.arrange(5.0, 1.0, -1.0) +/// float_list.arange(5.0, 1.0, -1.0) /// |> should.equal([5.0, 4.0, 3.0, 2.0]) /// } /// @@ -537,7 +537,7 @@ pub fn geometric_space( /// /// /// -pub fn arrange(start: Float, stop: Float, step: Float) -> List(Float) { +pub fn arange(start: Float, stop: Float, step: Float) -> List(Float) { case start >=. stop && step >. 0.0 || start <=. stop && step <. 0.0 { True -> [] False -> { @@ -556,10 +556,6 @@ pub fn arrange(start: Float, stop: Float, step: Float) -> List(Float) { } } -// fn do_arrange(start: Float, step: Float, direction: Float) -> Float { -// case -// } - ///
/// /// Spot a typo? Open an issue! @@ -629,15 +625,15 @@ pub fn sum(arr: List(Float)) -> Float { /// import gleam_community/maths/float_list /// /// pub fn example () { -/// // An empty list returns an error +/// // An empty list returns 0.0 /// [] /// |> float_list.sum() -/// |> should.equal(0.) +/// |> should.equal(0.0) /// /// // Valid input returns a result -/// [1., 2., 3.] +/// [1.0, 2.0, 3.0] /// |> float_list.product() -/// |> should.equal(6.) +/// |> should.equal(6.0) /// } /// /// @@ -652,7 +648,7 @@ pub fn product(arr: List(Float)) -> Float { [] -> 0.0 _ -> arr - |> list.fold(0.0, fn(acc: Float, a: Float) -> Float { a *. acc }) + |> list.fold(1.0, fn(acc: Float, a: Float) -> Float { a *. acc }) } } diff --git a/src/gleam_community/maths/int.gleam b/src/gleam_community/maths/int.gleam index 8426686..a2ba8dd 100644 --- a/src/gleam_community/maths/int.gleam +++ b/src/gleam_community/maths/int.gleam @@ -30,12 +30,13 @@ //// * [`flip_sign`](#flipsign) //// * **Misc. mathematical functions** //// * [`minimum`](#min) -//// * [`maxximum`](#max) +//// * [`maximum`](#max) //// * [`minmax`](#minmax) //// * **Division functions** //// * [`gcd`](#gcd) //// * [`lcm`](#lcm) //// * [`divisors`](#divisors) +//// * [`proper_divisors`](#proper_divisors) //// * **Combinatorial functions** //// * [`combination`](#combination) //// * [`factorial`](#factorial) @@ -532,7 +533,7 @@ pub fn is_power(x: Int, y: Int) -> Bool { /// ///
/// -/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is a perfect number. A number is perfect if it is equal to the sum of its proper positive divisors. +/// A function that tests whether a given integer value $$n \in \mathbb{Z}$$ is a perfect number. A number is perfect if it is equal to the sum of its proper positive divisors. /// ///
/// Details @@ -564,8 +565,17 @@ pub fn is_power(x: Int, y: Int) -> Bool { /// /// /// -pub fn is_perfect(x: Int, y: Int) -> Bool { - todo +pub fn is_perfect(n: Int) -> Bool { + do_sum(proper_divisors(n)) == n +} + +fn do_sum(arr: List(Int)) -> Int { + case arr { + [] -> 0 + _ -> + arr + |> list.fold(0, fn(acc: Int, a: Int) -> Int { a + acc }) + } } ///
@@ -574,7 +584,46 @@ pub fn is_perfect(x: Int, y: Int) -> Bool { /// ///
/// -/// The function returns all the positive divisors of an integer. +/// The function returns all the positive divisors of an integer, excluding the number iteself. +/// +///
+/// Example: +/// +/// import gleeunit/should +/// import gleam_community/maths/int as intx +/// +/// pub fn example() { +/// intx.proper_divisors(4) +/// |> should.equal([1, 2]) +/// +/// intx.proper_divisors(6) +/// |> should.equal([1, 2, 3]) +/// +/// intx.proper_divisors(13) +/// |> should.equal([1]) +/// +/// } +///
+/// +///
+/// +/// Back to top ↑ +/// +///
+/// +pub fn proper_divisors(n: Int) -> List(Int) { + let divisors: List(Int) = find_divisors(n) + divisors + |> list.take(list.length(divisors) - 1) +} + +///
+/// +/// Spot a typo? Open an issue! +/// +///
+/// +/// The function returns all the positive divisors of an integer, including the number iteself. /// ///
/// Example: @@ -588,6 +637,9 @@ pub fn is_perfect(x: Int, y: Int) -> Bool { /// /// intx.divisors(6) /// |> should.equal([1, 2, 3, 6]) +/// +/// intx.proper_divisors(13) +/// |> should.equal([1, 13]) /// } ///
/// @@ -597,8 +649,26 @@ pub fn is_perfect(x: Int, y: Int) -> Bool { /// /// /// -pub fn divisors(x: Int) -> List(Int) { - todo +pub fn divisors(n: Int) -> List(Int) { + find_divisors(n) +} + +pub fn find_divisors(n: Int) -> List(Int) { + let nabs: Float = float.absolute_value(to_float(n)) + assert Ok(sqrt_result) = floatx.square_root(nabs) + let max: Int = floatx.to_int(sqrt_result) + 1 + list.range(2, max) + |> list.fold( + [1, n], + fn(acc: List(Int), i: Int) -> List(Int) { + case n % i == 0 { + True -> [i, n / i, ..acc] + False -> acc + } + }, + ) + |> list.unique() + |> list.sort(int.compare) } ///
diff --git a/src/gleam_community/maths/int_list.gleam b/src/gleam_community/maths/int_list.gleam index c4439af..a1701d3 100644 --- a/src/gleam_community/maths/int_list.gleam +++ b/src/gleam_community/maths/int_list.gleam @@ -26,9 +26,9 @@ //// * **Distances, sums and products** //// * [`sum`](#sum) //// * [`product`](#product) -//// * [`norm`](#norm) //// * [`cumulative_sum`](#cumulative_sum) //// * [`cumulative_product`](#cumulative_product) +//// * [`manhatten_distance`](#manhatten_distance) //// * **Misc. mathematical functions** //// * [`maximum`](#maximum) //// * [`minimum`](#minimum) @@ -42,6 +42,164 @@ import gleam/float import gleam/pair import gleam_community/maths/int as intx +///
+/// +/// Spot a typo? Open an issue! +/// +///
+/// +/// Calculcate the Manhatten distance between two lists (representing vectors): +/// +/// \\[ +/// \sum_{i=1}^n \left|x_i - x_j \right| +/// \\] +/// +/// In the formula, $$n$$ is the length of the two lists and $$x_i, y_i$$ are the values in the respective input lists indexed by $$i, j$$. +/// +///
+/// Example: +/// +/// import gleeunit/should +/// import gleam_community/maths/float as floatx +/// import gleam_community/maths/float_list +/// +/// pub fn example () { +/// // Empty lists returns 0 +/// float_list.manhatten_distance([], []) +/// |> should.equal(Ok(0.0)) +/// +/// // Differing lengths returns error +/// float_list.manhatten_distance([], [1]) +/// |> should.be_error() +/// +/// assert Ok(result) = int_list.manhatten_distance([0, 0], [1, 2]) +/// result +/// |> should.equal(3) +/// } +///
+/// +///
+/// +/// Back to top ↑ +/// +///
+/// +pub fn manhatten_distance( + xarr: List(Int), + yarr: List(Int), +) -> Result(Int, String) { + let xlen: Int = list.length(xarr) + let ylen: Int = list.length(yarr) + case xlen == ylen { + False -> + "Invalid input argument: length(xarr) != length(yarr). Valid input is when length(xarr) == length(yarr)." + |> Error + True -> + list.zip(xarr, yarr) + |> list.map(fn(tuple: #(Int, Int)) -> Int { + int.absolute_value(pair.first(tuple) - pair.second(tuple)) + }) + |> sum() + |> Ok + } +} + +///
+/// +/// Spot a typo? Open an issue! +/// +///
+/// +/// Calculcate the sum of the elements in a list: +/// +/// \\[ +/// \sum_{i=1}^n x_i +/// \\] +/// +/// In the formula, $$n$$ is the length of the list and $$x_i$$ is the value in the input list indexed by $$i$$. +/// +///
+/// Example: +/// +/// import gleeunit/should +/// import gleam_community/maths/float_list +/// +/// pub fn example () { +/// // An empty list returns 0 +/// [] +/// |> float_list.sum() +/// |> should.equal(0) +/// +/// // Valid input returns a result +/// [1, 2, 3] +/// |> float_list.sum() +/// |> should.equal(6) +/// } +///
+/// +///
+/// +/// Back to top ↑ +/// +///
+/// +pub fn sum(arr: List(Int)) -> Int { + case arr { + [] -> 0 + _ -> + arr + |> list.fold(0, fn(acc: Int, a: Int) -> Int { a + acc }) + } +} + +///
+/// +/// Spot a typo? Open an issue! +/// +///
+/// +/// Calculcate the product of the elements in a list: +/// +/// \\[ +/// \prod_{i=1}^n x_i +/// \\] +/// +/// In the formula, $$n$$ is the length of the list and $$x_i$$ is the value in the input list indexed by $$i$$. +/// +///
+/// Example: +/// +/// import gleeunit/should +/// import gleam_community/maths/float_list +/// +/// pub fn example () { +/// // An empty list returns an error +/// [] +/// |> float_list.sum() +/// |> should.equal(0.) +/// +/// // Valid input returns a result +/// [1., 2., 3.] +/// |> float_list.product() +/// |> should.equal(6.) +/// } +///
+/// +///
+/// +/// Back to top ↑ +/// +///
+/// +pub fn product(arr: List(Int)) -> Int { + case arr { + [] -> 0 + _ -> + arr + |> list.fold(1, fn(acc: Int, a: Int) -> Int { a * acc }) + } +} + ///
/// /// Spot a typo? Open an issue! diff --git a/test/gleam/gleam_community_maths_float_list_test.gleam b/test/gleam/gleam_community_maths_float_list_test.gleam index 60c0401..6421afd 100644 --- a/test/gleam/gleam_community_maths_float_list_test.gleam +++ b/test/gleam/gleam_community_maths_float_list_test.gleam @@ -88,7 +88,7 @@ pub fn float_list_minkowski_test() { float_list.minkowski_distance([], [], 1.0) |> should.equal(Ok(0.0)) - // Differing lenghths returns error + // Differing lengths returns error float_list.minkowski_distance([], [1.0], 1.0) |> should.be_error() @@ -141,7 +141,7 @@ pub fn float_list_euclidean_test() { float_list.euclidean_distance([], []) |> should.equal(Ok(0.0)) - // Differing lenghths returns error + // Differing lengths returns error float_list.euclidean_distance([], [1.0]) |> should.be_error() @@ -159,7 +159,7 @@ pub fn float_list_manhatten_test() { float_list.manhatten_distance([], []) |> should.equal(Ok(0.0)) - // Differing lenghths returns error + // Differing lengths returns error float_list.manhatten_distance([], [1.0]) |> should.be_error() @@ -353,35 +353,35 @@ pub fn float_list_geometric_space_test() { |> should.be_error() } -pub fn float_list_arrange_test() { +pub fn float_list_arange_test() { // Positive start, stop, step - float_list.arrange(1.0, 5.0, 1.0) + float_list.arange(1.0, 5.0, 1.0) |> should.equal([1.0, 2.0, 3.0, 4.0]) - float_list.arrange(1.0, 5.0, 0.5) + float_list.arange(1.0, 5.0, 0.5) |> should.equal([1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]) - float_list.arrange(1.0, 2.0, 0.25) + float_list.arange(1.0, 2.0, 0.25) |> should.equal([1.0, 1.25, 1.5, 1.75]) // Reverse (switch start/stop largest/smallest value) - float_list.arrange(5.0, 1.0, 1.0) + float_list.arange(5.0, 1.0, 1.0) |> should.equal([]) // Reverse negative step - float_list.arrange(5.0, 1.0, -1.0) + float_list.arange(5.0, 1.0, -1.0) |> should.equal([5.0, 4.0, 3.0, 2.0]) // Positive start, negative stop, step - float_list.arrange(5.0, -1.0, -1.0) + float_list.arange(5.0, -1.0, -1.0) |> should.equal([5.0, 4.0, 3.0, 2.0, 1.0, 0.0]) // Negative start, stop, step - float_list.arrange(-5.0, -1.0, -1.0) + float_list.arange(-5.0, -1.0, -1.0) |> should.equal([]) // Negative start, stop, positive step - float_list.arrange(-5.0, -1.0, 1.0) + float_list.arange(-5.0, -1.0, 1.0) |> should.equal([-5.0, -4.0, -3.0, -2.0]) } @@ -445,6 +445,22 @@ pub fn float_list_extrema_test() { |> should.equal(Ok(#(1.0, 4.0))) } +pub fn float_list_sum_test() { + // An empty list returns 0 + [] + |> float_list.sum() + |> should.equal(0.0) + + // Valid input returns a result + [1.0, 2.0, 3.0] + |> float_list.sum() + |> should.equal(6.0) + + [-2.0, 4.0, 6.0] + |> float_list.sum() + |> should.equal(8.0) +} + pub fn float_list_cumulative_sum_test() { // An empty lists returns an empty list [] @@ -455,6 +471,26 @@ pub fn float_list_cumulative_sum_test() { [1.0, 2.0, 3.0] |> float_list.cumulative_sum() |> should.equal([1.0, 3.0, 6.0]) + + [-2.0, 4.0, 6.0] + |> float_list.cumulative_sum() + |> should.equal([-2.0, 2.0, 8.0]) +} + +pub fn float_list_product_test() { + // An empty list returns 0 + [] + |> float_list.product() + |> should.equal(0.0) + + // Valid input returns a result + [1.0, 2.0, 3.0] + |> float_list.product() + |> should.equal(6.0) + + [-2.0, 4.0, 6.0] + |> float_list.product() + |> should.equal(-48.0) } pub fn float_list_cumulative_product_test() { @@ -467,4 +503,8 @@ pub fn float_list_cumulative_product_test() { [1.0, 2.0, 3.0] |> float_list.cumumlative_product() |> should.equal([1.0, 2.0, 6.0]) + + [-2.0, 4.0, 6.0] + |> float_list.cumumlative_product() + |> should.equal([-2.0, -8.0, -48.0]) } diff --git a/test/gleam/gleam_community_maths_int_list_test.gleam b/test/gleam/gleam_community_maths_int_list_test.gleam index 1be0e4e..a0ec5d6 100644 --- a/test/gleam/gleam_community_maths_int_list_test.gleam +++ b/test/gleam/gleam_community_maths_int_list_test.gleam @@ -69,6 +69,22 @@ pub fn int_list_extrema_test() { |> should.equal(Ok(#(1, 4))) } +pub fn int_list_sum_test() { + // An empty list returns 0 + [] + |> int_list.sum() + |> should.equal(0) + + // Valid input returns a result + [1, 2, 3] + |> int_list.sum() + |> should.equal(6) + + [-2, 4, 6] + |> int_list.sum() + |> should.equal(8) +} + pub fn int_list_cumulative_sum_test() { // An empty lists returns an empty list [] @@ -79,6 +95,26 @@ pub fn int_list_cumulative_sum_test() { [1, 2, 3] |> int_list.cumulative_sum() |> should.equal([1, 3, 6]) + + [-2, 4, 6] + |> int_list.cumulative_sum() + |> should.equal([-2, 2, 8]) +} + +pub fn int_list_product_test() { + // An empty list returns 0 + [] + |> int_list.product() + |> should.equal(0) + + // Valid input returns a result + [1, 2, 3] + |> int_list.product() + |> should.equal(6) + + [-2, 4, 6] + |> int_list.product() + |> should.equal(-48) } pub fn int_list_cumulative_product_test() { @@ -91,4 +127,22 @@ pub fn int_list_cumulative_product_test() { [1, 2, 3] |> int_list.cumumlative_product() |> should.equal([1, 2, 6]) + + [-2, 4, 6] + |> int_list.cumumlative_product() + |> should.equal([-2, -8, -48]) +} + +pub fn int_list_manhatten_test() { + // Empty lists returns 0 + int_list.manhatten_distance([], []) + |> should.equal(Ok(0)) + + // Differing lengths returns error + int_list.manhatten_distance([], [1]) + |> should.be_error() + + assert Ok(result) = int_list.manhatten_distance([0, 0], [1, 2]) + result + |> should.equal(3) } diff --git a/test/gleam/gleam_community_maths_int_test.gleam b/test/gleam/gleam_community_maths_int_test.gleam index 26d790a..af4809f 100644 --- a/test/gleam/gleam_community_maths_int_test.gleam +++ b/test/gleam/gleam_community_maths_int_test.gleam @@ -277,3 +277,51 @@ pub fn int_to_float_test() { intx.to_float(1) |> should.equal(1.0) } + +pub fn int_proper_divisors_test() { + intx.proper_divisors(2) + |> should.equal([1]) + + intx.proper_divisors(6) + |> io.debug() + |> should.equal([1, 2, 3]) + intx.proper_divisors(13) + |> should.equal([1]) + + intx.proper_divisors(18) + |> should.equal([1, 2, 3, 6, 9]) +} + +pub fn int_divisors_test() { + intx.divisors(2) + |> should.equal([1, 2]) + + intx.divisors(6) + |> should.equal([1, 2, 3, 6]) + + intx.divisors(13) + |> should.equal([1, 13]) + + intx.divisors(18) + |> should.equal([1, 2, 3, 6, 9, 18]) +} + +pub fn int_is_perfect_test() { + intx.is_perfect(6) + |> should.equal(True) + + intx.is_perfect(28) + |> should.equal(True) + + intx.is_perfect(496) + |> should.equal(True) + + intx.is_perfect(1) + |> should.equal(False) + + intx.is_perfect(3) + |> should.equal(False) + + intx.is_perfect(13) + |> should.equal(False) +}