From e3122bfd24f93c8a7b7bc3d7692c6a67a02a6dbd Mon Sep 17 00:00:00 2001 From: Gabriel Arazas Date: Sun, 7 Jul 2024 20:14:41 +0800 Subject: [PATCH] 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). --- subprojects/bahaghari/LICENSE | 4 ++ subprojects/bahaghari/lib/math.nix | 72 ++++++++++++++++++- subprojects/bahaghari/tests/lib/math.nix | 91 +++++++++++++++++++++++- 3 files changed, 164 insertions(+), 3 deletions(-) diff --git a/subprojects/bahaghari/LICENSE b/subprojects/bahaghari/LICENSE index ce20b53f..ef817257 100644 --- a/subprojects/bahaghari/LICENSE +++ b/subprojects/bahaghari/LICENSE @@ -1,5 +1,9 @@ Copyright (c) 2024 Gabriel Arazas +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 of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights diff --git a/subprojects/bahaghari/lib/math.nix b/subprojects/bahaghari/lib/math.nix index 8be21661..2277a0ae 100644 --- a/subprojects/bahaghari/lib/math.nix +++ b/subprojects/bahaghari/lib/math.nix @@ -14,8 +14,8 @@ rec { ln10 = 2.302585092994046; ln2 = 0.6931471805599453; - # The minimum precision for our functions that need them. - epsilon = pow 10 (-13); + # The precision target for our functions that need them. + epsilon = pow 0.1 13; }; /* Returns the absolute value of the given number. @@ -32,6 +32,20 @@ rec { abs = 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. Type: pow :: Int -> Int -> Int @@ -314,6 +328,60 @@ rec { */ 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. Type: radiansToDegrees :: Number -> Number diff --git a/subprojects/bahaghari/tests/lib/math.nix b/subprojects/bahaghari/tests/lib/math.nix index 76571525..cb712385 100644 --- a/subprojects/bahaghari/tests/lib/math.nix +++ b/subprojects/bahaghari/tests/lib/math.nix @@ -208,7 +208,7 @@ lib.runTests { }; testMathSqrt = { - expr = self.math.sqrt 4; + expr = round' (self.math.sqrt 4); expected = 2; }; @@ -362,4 +362,93 @@ lib.runTests { expr = self.math.round' (-3) (self.math.radiansToDegrees 4.5); 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); + }; }