2024-02-29 14:06:33 +00:00
|
|
|
# A little math utility for common operations. Don't expect any high-level
|
|
|
|
# mathematical operations nor godly optimizations expected from a typical math
|
|
|
|
# library, it's just basic high school type of shit in all aspects.
|
2024-03-02 10:10:21 +00:00
|
|
|
{ pkgs, lib, self }:
|
2024-02-29 14:06:33 +00:00
|
|
|
|
|
|
|
rec {
|
2024-05-30 06:42:29 +00:00
|
|
|
# We have the rounding functions here anyways so we may as well include the
|
|
|
|
# rest of the decimal place changing functions here for consistency.
|
|
|
|
inherit (builtins) floor ceil;
|
|
|
|
|
2024-06-03 08:14:06 +00:00
|
|
|
constants = {
|
|
|
|
pi = 3.141592653589793238462643383279502884197;
|
|
|
|
e = 2.7182818284590452353602874713527;
|
|
|
|
ln10 = 2.302585092994046;
|
|
|
|
ln2 = 0.6931471805599453;
|
|
|
|
|
2024-07-07 12:14:41 +00:00
|
|
|
# The precision target for our functions that need them.
|
|
|
|
epsilon = pow 0.1 13;
|
2024-06-03 08:14:06 +00:00
|
|
|
};
|
2024-05-29 14:56:33 +00:00
|
|
|
|
2024-02-29 14:06:33 +00:00
|
|
|
/* Returns the absolute value of the given number.
|
|
|
|
|
|
|
|
Type: abs :: Int -> Int
|
|
|
|
|
|
|
|
Example:
|
|
|
|
abs -4
|
|
|
|
=> 4
|
|
|
|
|
|
|
|
abs (1 / 5)
|
|
|
|
=> 0.2
|
|
|
|
*/
|
|
|
|
abs = number:
|
|
|
|
if number < 0 then -(number) else number;
|
|
|
|
|
2024-07-07 12:14:41 +00:00
|
|
|
/* Given a Nix number, force it to be a floating value.
|
|
|
|
|
|
|
|
Type: toFloat :: Number -> Float
|
|
|
|
|
|
|
|
Example:
|
|
|
|
toFloat 5
|
|
|
|
=> 5.0
|
|
|
|
|
|
|
|
toFloat 59.0
|
|
|
|
=> 59.0
|
|
|
|
*/
|
|
|
|
toFloat = x:
|
|
|
|
1.0 * x;
|
|
|
|
|
2024-02-29 14:06:33 +00:00
|
|
|
/* Exponentiates the given base with the exponent.
|
|
|
|
|
|
|
|
Type: pow :: Int -> Int -> Int
|
|
|
|
|
|
|
|
Example:
|
|
|
|
pow 2 3
|
|
|
|
=> 8
|
|
|
|
|
|
|
|
pow 6 4
|
|
|
|
=> 1296
|
|
|
|
*/
|
|
|
|
pow = base: exponent:
|
2024-06-15 11:54:57 +00:00
|
|
|
# Just to be a contrarian, I'll just make this as a tail recursive function
|
|
|
|
# instead lol.
|
2024-02-29 14:06:33 +00:00
|
|
|
let
|
|
|
|
absValue = abs exponent;
|
2024-06-15 11:54:57 +00:00
|
|
|
iter = product: counter: maxCount:
|
|
|
|
if counter > maxCount
|
2024-02-29 14:06:33 +00:00
|
|
|
then product
|
2024-06-15 11:54:57 +00:00
|
|
|
else iter (product * base) (counter + 1) maxCount;
|
2024-02-29 14:06:33 +00:00
|
|
|
value = iter 1 1 absValue;
|
|
|
|
in
|
|
|
|
if exponent < 0 then (1 / value) else value;
|
|
|
|
|
2024-06-03 08:14:06 +00:00
|
|
|
/* Given a number as x, return e^x.
|
|
|
|
|
|
|
|
Type: exp :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
exp 0
|
|
|
|
=> 1
|
|
|
|
|
|
|
|
exp 1
|
|
|
|
=> 2.7182818284590452353602874713527
|
|
|
|
|
|
|
|
exp -1
|
|
|
|
=> 0.36787944117144233
|
|
|
|
*/
|
|
|
|
exp = x:
|
|
|
|
pow constants.e x;
|
|
|
|
|
2024-06-01 05:21:30 +00:00
|
|
|
/* Given a number, find its square root. This method is implemented using
|
|
|
|
Newton's method.
|
|
|
|
|
|
|
|
Type: sqrt :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
sqrt 4
|
|
|
|
=> 2
|
|
|
|
|
|
|
|
sqrt 169
|
|
|
|
=> 13
|
|
|
|
|
|
|
|
sqrt 12
|
|
|
|
=> 3.464101615
|
|
|
|
*/
|
|
|
|
sqrt = number:
|
|
|
|
assert lib.assertMsg (number >= 0)
|
|
|
|
"bahaghariLib.math.sqrt: Only positive numbers are allowed";
|
|
|
|
let
|
|
|
|
# Changing this value can change the result drastically. A value of
|
|
|
|
# 10^-13 for tolerance seems to be the most balanced so far since we are
|
|
|
|
# dealing with floats and should be enough for most cases.
|
2024-06-03 08:14:06 +00:00
|
|
|
tolerance = constants.epsilon;
|
2024-06-01 05:21:30 +00:00
|
|
|
|
|
|
|
iter = value:
|
|
|
|
let
|
|
|
|
root = 0.5 * (value + (number / value));
|
|
|
|
in
|
|
|
|
if (abs (root - value) > tolerance) then
|
|
|
|
iter root
|
|
|
|
else
|
|
|
|
value;
|
|
|
|
in
|
|
|
|
iter number;
|
|
|
|
|
2024-05-29 14:56:33 +00:00
|
|
|
/* Implements the factorial function with the given value.
|
|
|
|
|
|
|
|
Type: factorial :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
factorial 3
|
|
|
|
=> 6
|
|
|
|
|
|
|
|
factorial 10
|
|
|
|
=> 3628800
|
|
|
|
*/
|
|
|
|
factorial = x:
|
|
|
|
assert lib.assertMsg (x >= 0)
|
2024-06-11 08:39:09 +00:00
|
|
|
"bahaghariLib.math.factorial: Given value is not a positive integer";
|
2024-05-29 14:56:33 +00:00
|
|
|
product (lib.range 1 x);
|
|
|
|
|
2024-03-02 04:54:59 +00:00
|
|
|
/* Returns a boolean whether the given number is within the given (inclusive) range.
|
|
|
|
|
|
|
|
Type: isWithinRange :: Number -> Number -> Number -> Bool
|
|
|
|
|
|
|
|
Example:
|
|
|
|
isWithinRange 30 50 6
|
|
|
|
=> false
|
|
|
|
|
|
|
|
isWithinRange 0 100 75
|
|
|
|
=> true
|
|
|
|
*/
|
|
|
|
isWithinRange = min: max: number:
|
2024-03-02 10:10:21 +00:00
|
|
|
(lib.max number min) <= (lib.min number max);
|
2024-03-02 04:54:59 +00:00
|
|
|
|
2024-06-15 07:12:05 +00:00
|
|
|
/* Returns a boolean whether the given number is within the given (exclusive) range.
|
|
|
|
|
|
|
|
Type: isWithinRange :: Number -> Number -> Number -> Bool
|
|
|
|
|
|
|
|
Example:
|
|
|
|
isWithinRange 30 50 6
|
|
|
|
=> false
|
|
|
|
|
|
|
|
isWithinRange 0 100 75
|
|
|
|
=> true
|
|
|
|
*/
|
|
|
|
isWithinRange' = min: max: number:
|
|
|
|
(lib.max number min) < (lib.min number max);
|
|
|
|
|
2024-02-29 14:06:33 +00:00
|
|
|
/* Given a number, make it grow by given amount of percentage.
|
|
|
|
A value of 100 should make the number doubled.
|
|
|
|
|
|
|
|
Type: grow :: Number -> Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
grow 4 50.0
|
|
|
|
=> 2
|
|
|
|
|
|
|
|
grow 55.5 100
|
|
|
|
=> 111
|
|
|
|
*/
|
2024-03-02 04:54:59 +00:00
|
|
|
grow = value: number:
|
2024-02-29 14:06:33 +00:00
|
|
|
number + (percentage number value);
|
|
|
|
|
2024-03-02 04:54:59 +00:00
|
|
|
/* Similar to `grow` but only limits to be within the given (inclusive)
|
|
|
|
range.
|
2024-02-29 14:06:33 +00:00
|
|
|
|
2024-03-02 04:54:59 +00:00
|
|
|
Type: grow' :: Number -> Number -> Number -> Number
|
2024-02-29 14:06:33 +00:00
|
|
|
|
|
|
|
Example:
|
2024-03-02 04:54:59 +00:00
|
|
|
grow' 0 255 12 100
|
|
|
|
=> 24
|
2024-02-29 14:06:33 +00:00
|
|
|
|
2024-03-02 04:54:59 +00:00
|
|
|
grow' 1 10 5 (-200)
|
|
|
|
=> 1
|
2024-02-29 14:06:33 +00:00
|
|
|
*/
|
2024-03-02 04:54:59 +00:00
|
|
|
grow' = min: max: value: number:
|
2024-06-15 11:54:57 +00:00
|
|
|
self.trivial.clamp min max (grow number value);
|
2024-02-29 14:06:33 +00:00
|
|
|
|
|
|
|
/* Given a number, return its value by the given percentage.
|
|
|
|
|
|
|
|
Type: percentage :: Number -> Number -> Number
|
|
|
|
|
|
|
|
Example:
|
2024-03-02 09:18:15 +00:00
|
|
|
percentage 100.0 4
|
2024-02-29 14:06:33 +00:00
|
|
|
=> 4
|
|
|
|
|
2024-03-02 09:18:15 +00:00
|
|
|
percentage 200.0 5
|
2024-02-29 14:06:33 +00:00
|
|
|
=> 10
|
2024-03-02 09:18:15 +00:00
|
|
|
|
|
|
|
percentage 55.4 58
|
|
|
|
=> 32.132
|
|
|
|
|
|
|
|
percentage 0 24654
|
|
|
|
=> 0
|
2024-02-29 14:06:33 +00:00
|
|
|
*/
|
2024-03-02 09:18:15 +00:00
|
|
|
percentage = value: number:
|
|
|
|
if value == 0
|
|
|
|
then 0
|
|
|
|
else number / (100.0 / value);
|
2024-02-29 14:06:33 +00:00
|
|
|
|
2024-05-29 14:56:33 +00:00
|
|
|
/* Given a number, round up (or down) its number to the nearest ones place.
|
2024-02-29 14:06:33 +00:00
|
|
|
|
|
|
|
Type: round :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
round 3.5
|
|
|
|
=> 4
|
|
|
|
|
|
|
|
round 2.3
|
|
|
|
=> 2
|
|
|
|
|
|
|
|
round 2.7
|
|
|
|
=> 3
|
|
|
|
*/
|
2024-06-15 11:54:57 +00:00
|
|
|
round = round' 0;
|
2024-05-29 14:56:33 +00:00
|
|
|
|
|
|
|
/* Given a tens place (10 ^ n) and a number, round the nearest integer to its
|
|
|
|
given place.
|
|
|
|
|
|
|
|
Type: round' :: Number -> Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
# Round the number to the nearest ones.
|
|
|
|
round' 0 5.65
|
|
|
|
=> 6
|
|
|
|
|
|
|
|
# Round the number to the nearest tens.
|
|
|
|
round' 1 5.65
|
|
|
|
=> 10
|
|
|
|
|
|
|
|
# Round the number to the nearest hundreds.
|
|
|
|
round' 2 5.65
|
|
|
|
=> 0
|
|
|
|
|
|
|
|
# Round the number to the nearest tenth.
|
|
|
|
round' (-1) 5.65
|
|
|
|
=> 5.7
|
|
|
|
*/
|
|
|
|
round' = tens: number:
|
2024-02-29 14:06:33 +00:00
|
|
|
let
|
2024-05-29 14:56:33 +00:00
|
|
|
nearest = pow 10.0 tens;
|
|
|
|
difference = number / nearest;
|
2024-02-29 14:06:33 +00:00
|
|
|
in
|
2024-05-30 06:42:29 +00:00
|
|
|
floor (difference + 0.5) * nearest;
|
2024-05-29 14:56:33 +00:00
|
|
|
|
2024-06-15 11:46:34 +00:00
|
|
|
/* Given a base and a modulus, returns the value of a modulo operation.
|
|
|
|
|
|
|
|
Type: mod :: Number -> Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
mod 5 4
|
|
|
|
=> 1
|
|
|
|
|
|
|
|
mod 1245 4.5
|
|
|
|
=> 3
|
|
|
|
|
|
|
|
mod 19 (-12)
|
|
|
|
=> -5
|
|
|
|
*/
|
|
|
|
mod = base: modulus:
|
|
|
|
remainder ((remainder base modulus) + modulus) modulus;
|
|
|
|
|
2024-06-15 05:38:42 +00:00
|
|
|
/* Similar to the nixpkgs' `trivial.mod` but retain the decimal values. This
|
2024-06-15 11:45:33 +00:00
|
|
|
is just an approximation from ECMAScript's implementation of the remainder
|
|
|
|
operator.
|
2024-06-15 05:38:42 +00:00
|
|
|
|
2024-06-15 08:23:30 +00:00
|
|
|
Type: remainder :: Number -> Number -> Number
|
2024-06-15 05:38:42 +00:00
|
|
|
|
|
|
|
Example:
|
2024-06-15 08:23:30 +00:00
|
|
|
remainder 4.25 2
|
2024-06-15 05:38:42 +00:00
|
|
|
=> 0.25
|
|
|
|
|
2024-06-15 08:23:30 +00:00
|
|
|
remainder 1.5 2
|
2024-06-15 05:38:42 +00:00
|
|
|
=> 1.5
|
|
|
|
|
2024-06-15 08:23:30 +00:00
|
|
|
remainder 65 5
|
2024-06-15 05:38:42 +00:00
|
|
|
=> 0
|
|
|
|
|
2024-06-15 08:23:30 +00:00
|
|
|
remainder (-54) 4
|
2024-06-15 05:38:42 +00:00
|
|
|
=> -2
|
|
|
|
|
2024-06-15 08:23:30 +00:00
|
|
|
remainder (-54) (-4)
|
2024-06-15 05:38:42 +00:00
|
|
|
=> -2
|
|
|
|
*/
|
2024-06-15 11:45:33 +00:00
|
|
|
remainder = dividend: divisor:
|
2024-06-15 05:38:42 +00:00
|
|
|
let
|
2024-06-15 11:45:33 +00:00
|
|
|
quotient = dividend / divisor;
|
2024-06-15 05:38:42 +00:00
|
|
|
in
|
2024-06-15 11:45:33 +00:00
|
|
|
dividend - ((floor quotient) * divisor);
|
2024-06-15 05:38:42 +00:00
|
|
|
|
2024-05-29 14:56:33 +00:00
|
|
|
/* Adds all of the given items on the list starting from a sum of zero.
|
|
|
|
|
|
|
|
Type: summate :: List[Number] -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
summate [ 1 2 3 4 ]
|
|
|
|
=> 10
|
|
|
|
*/
|
|
|
|
summate = builtins.foldl' builtins.add 0;
|
|
|
|
|
|
|
|
/* Multiply all of the given items on the list starting from a product of 1.
|
|
|
|
|
|
|
|
Type: product :: List[Number] -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
product [ 1 2 3 4 ]
|
|
|
|
=> 24
|
|
|
|
*/
|
|
|
|
product = builtins.foldl' builtins.mul 1;
|
2024-06-27 13:08:09 +00:00
|
|
|
|
2024-07-07 12:14:41 +00:00
|
|
|
# The following trigonometric functions is pretty much sourced from the following link.
|
|
|
|
# https://lantian.pub/en/article/modify-computer/nix-trigonometric-math-library-from-zero.lantian/
|
|
|
|
|
|
|
|
/* Given a number in radians, return the value applied with a sine function.
|
|
|
|
|
|
|
|
Type: sin :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
sin 10
|
|
|
|
=> -0.5440211108893698
|
|
|
|
|
|
|
|
sin (constants.pi / 2)
|
|
|
|
=> 1
|
|
|
|
*/
|
|
|
|
sin = x: let
|
|
|
|
x' = mod (toFloat x) (2 * constants.pi);
|
|
|
|
step = i: (pow (-1) (i - 1)) * product (lib.genList (j: x' / (j + 1)) (i * 2 - 1));
|
|
|
|
iter = value: counter: let
|
|
|
|
value' = step counter;
|
|
|
|
in
|
|
|
|
if (abs value') < constants.epsilon
|
|
|
|
then value
|
|
|
|
else iter (value' + value) (counter + 1);
|
|
|
|
in
|
|
|
|
if x < 0
|
|
|
|
then -(sin (-x))
|
|
|
|
else iter 0 1;
|
|
|
|
|
|
|
|
/* Given a number in radians, apply the cosine function.
|
|
|
|
|
|
|
|
Type: cos :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
cos 10
|
|
|
|
=> -0.8390715290764524
|
|
|
|
|
|
|
|
cos 0
|
|
|
|
=> 1
|
|
|
|
*/
|
|
|
|
cos = x: sin (0.5 * constants.pi - x);
|
|
|
|
|
|
|
|
/* Given a number in radians, apply the tan trigonometric function.
|
|
|
|
|
|
|
|
Type: tan :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
tan 0
|
|
|
|
=> 0
|
|
|
|
|
|
|
|
tan 10
|
|
|
|
=> 0.6483608274590866
|
|
|
|
*/
|
|
|
|
tan = x: (sin x) / (cos x);
|
|
|
|
|
2024-06-27 13:08:09 +00:00
|
|
|
/* Given a number in radians, convert it to degrees.
|
|
|
|
|
|
|
|
Type: radiansToDegrees :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
radiansToDegrees bahaghariLib.math.constants.pi
|
|
|
|
=> 180
|
|
|
|
|
|
|
|
radiansToDegrees 180
|
|
|
|
=> 10313.240312355
|
|
|
|
*/
|
|
|
|
radiansToDegrees = x:
|
|
|
|
x * 180.0 / constants.pi;
|
|
|
|
|
|
|
|
/* Given a number in degrees unit, convert it to radians.
|
|
|
|
|
|
|
|
Type: degreesToRadians :: Number -> Number
|
|
|
|
|
|
|
|
Example:
|
|
|
|
degreesToRadians 180
|
|
|
|
=> 3.141592653589793238462643383279502884197
|
|
|
|
|
|
|
|
degreesToRadians 360
|
|
|
|
=> 6.283185307
|
|
|
|
|
|
|
|
degreesToRadians 95
|
|
|
|
=> 1.658062789
|
|
|
|
*/
|
|
|
|
degreesToRadians = x:
|
|
|
|
x * constants.pi / 180.0;
|
2024-02-29 14:06:33 +00:00
|
|
|
}
|