bahaghari/lib: add trigonometric functions to math subset

Code taken from
https://lantian.pub/en/article/modify-computer/nix-trigonometric-math-library-from-zero.lantian/
with proper attributions (hopefully).
This commit is contained in:
Gabriel Arazas 2024-07-07 20:14:41 +08:00
parent 4d32203d4d
commit e3122bfd24
No known key found for this signature in database
GPG Key ID: 62104B43D00AA360
3 changed files with 164 additions and 3 deletions

View File

@ -1,5 +1,9 @@
Copyright (c) 2024 Gabriel Arazas <foodogsquared@foodogsquared.one> Copyright (c) 2024 Gabriel Arazas <foodogsquared@foodogsquared.one>
Program uses the following files/implementations from other authors;
see their respective license headers for more details:
lib/math.nix, specifically the sin, cos, tan, and arctan functions: Copyright (c) 2023 Yuhui Xu
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights

View File

@ -14,8 +14,8 @@ rec {
ln10 = 2.302585092994046; ln10 = 2.302585092994046;
ln2 = 0.6931471805599453; ln2 = 0.6931471805599453;
# The minimum precision for our functions that need them. # The precision target for our functions that need them.
epsilon = pow 10 (-13); epsilon = pow 0.1 13;
}; };
/* Returns the absolute value of the given number. /* Returns the absolute value of the given number.
@ -32,6 +32,20 @@ rec {
abs = number: abs = number:
if number < 0 then -(number) else number; if number < 0 then -(number) else number;
/* 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;
/* Exponentiates the given base with the exponent. /* Exponentiates the given base with the exponent.
Type: pow :: Int -> Int -> Int Type: pow :: Int -> Int -> Int
@ -314,6 +328,60 @@ rec {
*/ */
product = builtins.foldl' builtins.mul 1; product = builtins.foldl' builtins.mul 1;
# 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);
/* Given a number in radians, convert it to degrees. /* Given a number in radians, convert it to degrees.
Type: radiansToDegrees :: Number -> Number Type: radiansToDegrees :: Number -> Number

View File

@ -208,7 +208,7 @@ lib.runTests {
}; };
testMathSqrt = { testMathSqrt = {
expr = self.math.sqrt 4; expr = round' (self.math.sqrt 4);
expected = 2; expected = 2;
}; };
@ -362,4 +362,93 @@ lib.runTests {
expr = self.math.round' (-3) (self.math.radiansToDegrees 4.5); expr = self.math.round' (-3) (self.math.radiansToDegrees 4.5);
expected = 257.831; expected = 257.831;
}; };
# At this point, most of the things are just adjusting to the quirks of those
# accursed floating-values.
testMathSine = {
expr = round' (self.math.sin 10);
expected = round' (-0.5440211108893698);
};
testMathSine2 = {
expr = self.math.sin 0;
expected = 0;
};
testMathSine3 = let
round' = self.math.round' (-5);
in {
expr = round' (self.math.sin (self.math.constants.pi / 2));
expected = round' 1;
};
testMathSine4 = {
expr = round' (self.math.sin 360);
expected = round' 0.9589157234143065;
};
testMathSine5 = {
expr = round' (self.math.sin 152);
expected = round' 0.933320523748862;
};
testMathSine6 = {
expr = round' (self.math.sin (-152));
expected = round' (-0.933320523748862);
};
testMathCosine = {
expr = round' (self.math.cos 10);
expected = round' (-0.8390715290764524);
};
testMathCosine2 = {
expr = round' (self.math.cos 0);
expected = 1;
};
testMathCosine3 = {
expr = round' (self.math.cos self.math.constants.pi);
expected = -1;
};
testMathCosine4 = {
expr = round' (self.math.cos (self.math.constants.pi * 2));
expected = 1;
};
testMathCosine5 = {
expr = round' (self.math.cos 1);
expected = round' 0.5403023058681398;
};
testMathCosine6 = {
expr = round' (self.math.cos 152);
expected = round' 0.35904428689111606;
};
testMathTangent = {
expr = round' (self.math.tan 10);
expected = round' 0.6483608274590866;
};
testMathTangent2 = {
expr = round' (self.math.tan 0);
expected = 0;
};
testMathTangent3 = {
expr = round' (self.math.tan (self.math.constants.pi / 4));
expected = round' (0.99999999999999999);
};
testMathTangent4 = {
expr = round' (self.math.tan 152);
expected = round' 2.5994579438382797;
};
testMathTangent5 = {
expr = round' (self.math.tan (-152));
expected = round' (-2.5994579438382797);
};
} }