diff options
Diffstat (limited to 'aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam')
-rw-r--r-- | aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam b/aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam new file mode 100644 index 0000000..5d62419 --- /dev/null +++ b/aoc2023/build/packages/gleam_stdlib/src/gleam/float.gleam @@ -0,0 +1,546 @@ +//// Functions for working with floats. +//// +//// ## Division by zero +//// +//// Gleam runs on the Erlang virtual machine, which does not follow the IEEE +//// 754 standard for floating point arithmetic and does not have an `Infinity` +//// value. In Erlang division by zero results in a crash, however Gleam does +//// not have partial functions and operators in core so instead division by zero +//// returns zero, a behaviour taken from Pony, Coq, and Lean. +//// +//// This may seem unexpected at first, but it is no less mathematically valid +//// than crashing or returning a special value. Division by zero is undefined +//// in mathematics. + +import gleam/order.{type Order} + +/// Attempts to parse a string as a `Float`, returning `Error(Nil)` if it was +/// not possible. +/// +/// ## Examples +/// +/// ```gleam +/// > parse("2.3") +/// Ok(2.3) +/// ``` +/// +/// ```gleam +/// > parse("ABC") +/// Error(Nil) +/// ``` +/// +pub fn parse(string: String) -> Result(Float, Nil) { + do_parse(string) +} + +@external(erlang, "gleam_stdlib", "parse_float") +@external(javascript, "../gleam_stdlib.mjs", "parse_float") +fn do_parse(a: String) -> Result(Float, Nil) + +/// Returns the string representation of the provided `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(2.3) +/// "2.3" +/// ``` +/// +pub fn to_string(x: Float) -> String { + do_to_string(x) +} + +@external(erlang, "gleam_stdlib", "float_to_string") +@external(javascript, "../gleam_stdlib.mjs", "float_to_string") +fn do_to_string(a: Float) -> String + +/// Restricts a `Float` between a lower and upper bound. +/// +/// ## Examples +/// +/// ```gleam +/// > clamp(1.2, min: 1.4, max: 1.6) +/// 1.4 +/// ``` +/// +pub fn clamp(x: Float, min min_bound: Float, max max_bound: Float) -> Float { + x + |> min(max_bound) + |> max(min_bound) +} + +/// Compares two `Float`s, returning an `Order`: +/// `Lt` for lower than, `Eq` for equals, or `Gt` for greater than. +/// +/// ## Examples +/// +/// ```gleam +/// > compare(2.0, 2.3) +/// Lt +/// ``` +/// +/// To handle +/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems) +/// you may use [`loosely_compare`](#loosely_compare) instead. +/// +pub fn compare(a: Float, with b: Float) -> Order { + case a == b { + True -> order.Eq + False -> + case a <. b { + True -> order.Lt + False -> order.Gt + } + } +} + +/// Compares two `Float`s within a tolerance, returning an `Order`: +/// `Lt` for lower than, `Eq` for equals, or `Gt` for greater than. +/// +/// This function allows Float comparison while handling +/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems). +/// +/// Notice: For `Float`s the tolerance won't be exact: +/// `5.3 - 5.0` is not exactly `0.3`. +/// +/// ## Examples +/// +/// ```gleam +/// > loosely_compare(5.0, with: 5.3, tolerating: 0.5) +/// Eq +/// ``` +/// +/// If you want to check only for equality you may use +/// [`loosely_equals`](#loosely_equals) instead. +/// +pub fn loosely_compare( + a: Float, + with b: Float, + tolerating tolerance: Float, +) -> Order { + let difference = absolute_value(a -. b) + case difference <=. tolerance { + True -> order.Eq + False -> compare(a, b) + } +} + +/// Checks for equality of two `Float`s within a tolerance, +/// returning an `Bool`. +/// +/// This function allows Float comparison while handling +/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems). +/// +/// Notice: For `Float`s the tolerance won't be exact: +/// `5.3 - 5.0` is not exactly `0.3`. +/// +/// ## Examples +/// +/// ```gleam +/// > loosely_equals(5.0, with: 5.3, tolerating: 0.5) +/// True +/// ``` +/// +/// ```gleam +/// > loosely_equals(5.0, with: 5.1, tolerating: 0.1) +/// False +/// ``` +/// +pub fn loosely_equals( + a: Float, + with b: Float, + tolerating tolerance: Float, +) -> Bool { + let difference = absolute_value(a -. b) + difference <=. tolerance +} + +/// Compares two `Float`s, returning the smaller of the two. +/// +/// ## Examples +/// +/// ```gleam +/// > min(2.0, 2.3) +/// 2.0 +/// ``` +/// +pub fn min(a: Float, b: Float) -> Float { + case a <. b { + True -> a + False -> b + } +} + +/// Compares two `Float`s, returning the larger of the two. +/// +/// ## Examples +/// +/// ```gleam +/// > max(2.0, 2.3) +/// 2.3 +/// ``` +/// +pub fn max(a: Float, b: Float) -> Float { + case a >. b { + True -> a + False -> b + } +} + +/// Rounds the value to the next highest whole number as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// > ceiling(2.3) +/// 3.0 +/// ``` +/// +pub fn ceiling(x: Float) -> Float { + do_ceiling(x) +} + +@external(erlang, "math", "ceil") +@external(javascript, "../gleam_stdlib.mjs", "ceiling") +fn do_ceiling(a: Float) -> Float + +/// Rounds the value to the next lowest whole number as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// > floor(2.3) +/// 2.0 +/// ``` +/// +pub fn floor(x: Float) -> Float { + do_floor(x) +} + +@external(erlang, "math", "floor") +@external(javascript, "../gleam_stdlib.mjs", "floor") +fn do_floor(a: Float) -> Float + +/// Rounds the value to the nearest whole number as an `Int`. +/// +/// ## Examples +/// +/// ```gleam +/// > round(2.3) +/// 2 +/// ``` +/// +/// ```gleam +/// > round(2.5) +/// 3 +/// ``` +/// +pub fn round(x: Float) -> Int { + do_round(x) +} + +@target(erlang) +@external(erlang, "erlang", "round") +fn do_round(a: Float) -> Int + +@target(javascript) +fn do_round(x: Float) -> Int { + case x >=. 0.0 { + True -> js_round(x) + _ -> 0 - js_round(negate(x)) + } +} + +@target(javascript) +@external(javascript, "../gleam_stdlib.mjs", "round") +fn js_round(a: Float) -> Int + +/// Returns the value as an `Int`, truncating all decimal digits. +/// +/// ## Examples +/// +/// ```gleam +/// > truncate(2.4343434847383438) +/// 2 +/// ``` +/// +pub fn truncate(x: Float) -> Int { + do_truncate(x) +} + +@external(erlang, "erlang", "trunc") +@external(javascript, "../gleam_stdlib.mjs", "truncate") +fn do_truncate(a: Float) -> Int + +/// Returns the absolute value of the input as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// > absolute_value(-12.5) +/// 12.5 +/// ``` +/// +/// ```gleam +/// > absolute_value(10.2) +/// 10.2 +/// ``` +/// +pub fn absolute_value(x: Float) -> Float { + case x >=. 0.0 { + True -> x + _ -> 0.0 -. x + } +} + +/// Returns the results of the base being raised to the power of the +/// exponent, as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// > power(2.0, -1.0) +/// Ok(0.5) +/// ``` +/// +/// ```gleam +/// > power(2.0, 2.0) +/// Ok(4.0) +/// ``` +/// +/// ```gleam +/// > power(8.0, 1.5) +/// Ok(22.627416997969522) +/// ``` +/// +/// ```gleam +/// > 4.0 |> power(of: 2.0) +/// Ok(16.0) +/// ``` +/// +/// ```gleam +/// > power(-1.0, 0.5) +/// Error(Nil) +/// ``` +/// +pub fn power(base: Float, of exponent: Float) -> Result(Float, Nil) { + let fractional: Bool = ceiling(exponent) -. exponent >. 0.0 + // In the following check: + // 1. If the base is negative and the exponent is fractional then + // return an error as it will otherwise be an imaginary number + // 2. If the base is 0 and the exponent is negative then the expression + // is equivalent to the exponent divided by 0 and an error should be + // returned + case base <. 0.0 && fractional || base == 0.0 && exponent <. 0.0 { + True -> Error(Nil) + False -> Ok(do_power(base, exponent)) + } +} + +@external(erlang, "math", "pow") +@external(javascript, "../gleam_stdlib.mjs", "power") +fn do_power(a: Float, b: Float) -> Float + +/// Returns the square root of the input as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// > square_root(4.0) +/// Ok(2.0) +/// ``` +/// +/// ```gleam +/// > square_root(-16.0) +/// Error(Nil) +/// ``` +/// +pub fn square_root(x: Float) -> Result(Float, Nil) { + power(x, 0.5) +} + +/// Returns the negative of the value provided. +/// +/// ## Examples +/// +/// ```gleam +/// > negate(1.0) +/// -1.0 +/// ``` +/// +pub fn negate(x: Float) -> Float { + -1.0 *. x +} + +/// Sums a list of `Float`s. +/// +/// ## Example +/// +/// ```gleam +/// > sum([1.0, 2.2, 3.3]) +/// 6.5 +/// ``` +/// +pub fn sum(numbers: List(Float)) -> Float { + numbers + |> do_sum(0.0) +} + +fn do_sum(numbers: List(Float), initial: Float) -> Float { + case numbers { + [] -> initial + [x, ..rest] -> do_sum(rest, x +. initial) + } +} + +/// Multiplies a list of `Float`s and returns the product. +/// +/// ## Example +/// +/// ```gleam +/// > product([2.5, 3.2, 4.2]) +/// 33.6 +/// ``` +/// +pub fn product(numbers: List(Float)) -> Float { + case numbers { + [] -> 1.0 + _ -> do_product(numbers, 1.0) + } +} + +fn do_product(numbers: List(Float), initial: Float) -> Float { + case numbers { + [] -> initial + [x, ..rest] -> do_product(rest, x *. initial) + } +} + +/// Generates a random float between the given minimum and maximum values. +/// +/// +/// ## Examples +/// +/// ```gleam +/// > random(1.0, 5.0) +/// 2.646355926896028 +/// ``` +/// +pub fn random(min: Float, max: Float) -> Float { + do_random_uniform() *. { max -. min } +. min +} + +/// Returns a random float uniformly distributed in the value range +/// 0.0 =< X < 1.0 and updates the state in the process dictionary. +/// See: <https://www.erlang.org/doc/man/rand.html#uniform-0> +/// +@external(erlang, "rand", "uniform") +@external(javascript, "../gleam_stdlib.mjs", "random_uniform") +fn do_random_uniform() -> Float + +/// Returns division of the inputs as a `Result`. +/// +/// ## Examples +/// +/// ```gleam +/// > divide(0.0, 1.0) +/// Ok(1.0) +/// ``` +/// +/// ```gleam +/// > divide(1.0, 0.0) +/// Error(Nil) +/// ``` +/// +pub fn divide(a: Float, by b: Float) -> Result(Float, Nil) { + case b { + 0.0 -> Error(Nil) + b -> Ok(a /. b) + } +} + +/// Adds two floats together. +/// +/// It's the function equivalent of the `+.` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// > add(1.0, 2.0) +/// 3.0 +/// ``` +/// +/// ```gleam +/// > import gleam/list +/// > list.fold([1.0, 2.0, 3.0], 0.0, add) +/// 6.0 +/// ``` +/// +/// ```gleam +/// > 3.0 |> add(2.0) +/// 5.0 +/// ``` +/// +pub fn add(a: Float, b: Float) -> Float { + a +. b +} + +/// Multiplies two floats together. +/// +/// It's the function equivalent of the `*.` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// > multiply(2.0, 4.0) +/// 8.0 +/// ``` +/// +/// ```gleam +/// import gleam/list +/// > list.fold([2.0, 3.0, 4.0], 1.0, multiply) +/// 24.0 +/// ``` +/// +/// ```gleam +/// > 3.0 |> multiply(2.0) +/// 6.0 +/// ``` +/// +pub fn multiply(a: Float, b: Float) -> Float { + a *. b +} + +/// Subtracts one float from another. +/// +/// It's the function equivalent of the `-.` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// > subtract(3.0, 1.0) +/// 2.0 +/// ``` +/// +/// ```gleam +/// > import gleam/list +/// > list.fold([1.0, 2.0, 3.0], 10.0, subtract) +/// 4.0 +/// ``` +/// +/// ```gleam +/// > 3.0 |> subtract(_, 2.0) +/// 1.0 +/// ``` +/// +/// ```gleam +/// > 3.0 |> subtract(2.0, _) +/// -1.0 +/// ``` +/// +pub fn subtract(a: Float, b: Float) -> Float { + a -. b +} |