-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat: add method SinFunction #568
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| using System; | ||
| using Algorithms.Numeric; | ||
| using NUnit.Framework; | ||
|
|
||
| namespace Algorithms.Tests.Numeric; | ||
|
|
||
| /// <summary> | ||
| /// Tests for the Sin class, which implements sine calculation via Maclaurin series. | ||
| /// </summary> | ||
| public static class SinTests | ||
| { | ||
| // A tolerance level for comparing the series approximation with the built-in Math.Sin. | ||
| private const double Tolerance = 1e-6; | ||
|
|
||
| /// <summary> | ||
| /// Tests the sine calculation for various common trigonometric angles | ||
| /// using the default number of terms (now 20). | ||
| /// </summary> | ||
| /// <param name="x">The angle in radians.</param> | ||
| [TestCase(0.0)] | ||
| [TestCase(Math.PI / 6)] // sin(30 deg) = 0.5 | ||
| [TestCase(Math.PI / 2)] // sin(90 deg) = 1.0 | ||
| [TestCase(Math.PI)] // sin(180 deg) = 0.0 | ||
| [TestCase(3 * Math.PI / 2)] // sin(270 deg) = -1.0 | ||
| [TestCase(2 * Math.PI)] // sin(360 deg) = 0.0 | ||
| [TestCase(1.5)] // Arbitrary value | ||
| public static void GetsSineValueWithDefaultTerms(double x) | ||
| { | ||
| // Arrange | ||
| var expected = Math.Sin(x); | ||
|
|
||
| // Act | ||
| var result = Sin.Calculate(x); | ||
|
|
||
| // Assert | ||
| // Check if the result is close to the expected value within the defined tolerance. | ||
| Assert.That(result, Is.EqualTo(expected).Within(Tolerance)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Tests if the angle reduction logic works correctly by passing a large angle. | ||
| /// The angle (20*PI + PI/2) should be equivalent to PI/2, where sin(x) = 1. | ||
| /// </summary> | ||
| [Test] | ||
| public static void GetsSineValueWithLargeAngleReduction() | ||
| { | ||
| // Arrange: Angle significantly larger than 2*PI | ||
| double largeAngle = 20 * Math.PI + (Math.PI / 2); // Should reduce to PI/2 | ||
|
|
||
| // Act | ||
| var result = Sin.Calculate(largeAngle); | ||
|
|
||
| // Assert | ||
| // Expected value is 1.0 (sin(PI/2)) | ||
| Assert.That(result, Is.EqualTo(1.0).Within(Tolerance)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Tests the sine calculation for negative angles. | ||
| /// </summary> | ||
| /// <param name="x">The negative angle in radians.</param> | ||
| [TestCase(-Math.PI / 2)] // sin(-90 deg) = -1.0 | ||
| [TestCase(-Math.PI / 4)] // sin(-45 deg) = -0.707... | ||
| public static void GetsSineValueForNegativeAngle(double x) | ||
| { | ||
| // Arrange | ||
| var expected = Math.Sin(x); | ||
|
|
||
| // Act | ||
| var result = Sin.Calculate(x); | ||
|
|
||
| // Assert | ||
| Assert.That(result, Is.EqualTo(expected).Within(Tolerance)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Tests the precision when only a small number of terms is used (e.g., 1 term). | ||
| /// sin(x) approx x for small x. | ||
| /// </summary> | ||
| [Test] | ||
| public static void GetsSineValueWithMinimalTerms() | ||
| { | ||
| // Arrange | ||
| double x = 0.1; // A small angle where the first term dominates | ||
| int terms = 1; // Only the first term (x) is calculated | ||
|
|
||
| // Act | ||
| var result = Sin.Calculate(x, terms); | ||
|
|
||
| // Assert | ||
| // Result should be approximately Math.Sin(0.1). | ||
| // The error of the one-term approximation (x) is roughly x^3/3! (approx 1.66e-4 for x=0.1), | ||
| // so we use a looser tolerance (2e-4) to ensure the test passes reliably. | ||
| Assert.That(result, Is.EqualTo(Math.Sin(x)).Within(2e-4)); // Tolerance increased to 2e-4 | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Tests that calculating the sine with a non-positive number of terms throws an ArgumentException. | ||
| /// </summary> | ||
| /// <param name="numTerms">The invalid number of terms.</param> | ||
| [TestCase(0)] | ||
| [TestCase(-1)] | ||
| [TestCase(-10)] | ||
| public static void ThrowsExceptionForInvalidTerms(int numTerms) | ||
| { | ||
| // Arrange | ||
| double x = 1.0; | ||
|
|
||
| // Act | ||
| void Act() => Sin.Calculate(x, numTerms); | ||
|
|
||
| // Assert | ||
| _ = Assert.Throws<ArgumentException>(Act); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,66 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using System.Numerics; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace Algorithms.Numeric; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Calculates the sine function (sin(x)) using the Maclaurin series expansion (Taylor series centered at 0). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ... | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static class Sin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Calculates the sine of an angle x using a finite number of terms from the Maclaurin series. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Note: For high precision, use Math.Sin(). This method is for demonstrating the series expansion. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// <param name="x">The angle in radians.</param> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// <param name="terms">The number of terms (odd powers) to include in the series. Default is 20 for required precision.</param> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// <returns>The approximate value of sin(x).</returns> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static double Calculate(double x, int terms = 20) // Increased default from 15 to 20 for higher precision | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (terms <= 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new ArgumentException("The number of terms must be a positive integer."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Best practice for series convergence: reduce the angle to the range [-2*PI, 2*PI]. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The series converges for all x, but convergence is faster near 0. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| x %= 2 * Math.PI; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
siriak marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double result = 0.0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double xSquared = x * x; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // currentPower: Stores x^k | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double currentPower = x; // Starts at x^1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // currentFactorial: Stores k! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double currentFactorial = 1.0; // Starts at 1! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int sign = 1; // Starts positive | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // k is the exponent/factorial index (1, 3, 5, 7, ...) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (int i = 0, k = 1; i < terms; i++, k += 2) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (k > 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Iteratively update the components for the next term: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Current exponent k is (k-2) + 2. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // x^k = x^(k-2) * x^2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentPower *= xSquared; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // k! = (k-2)! * (k-1) * k | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Example: 5! = 3! * 4 * 5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentFactorial *= (k - 1) * k; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
siriak marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Calculate the term: (sign) * x^k / k! | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| double term = currentPower / currentFactorial; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result += sign * term; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sign *= -1; // Alternate the sign for the next term | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
+66
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace Algorithms.Numeric; | |
| /// <summary> | |
| /// Calculates the sine function (sin(x)) using the Maclaurin series expansion (Taylor series centered at 0). | |
| /// sin(x) = x - x^3/3! + x^5/5! - x^7/7! + ... | |
| /// </summary> | |
| public static class Sin | |
| { | |
| /// <summary> | |
| /// Calculates the sine of an angle x using a finite number of terms from the Maclaurin series. | |
| /// Note: For high precision, use Math.Sin(). This method is for demonstrating the series expansion. | |
| /// </summary> | |
| /// <param name="x">The angle in radians.</param> | |
| /// <param name="terms">The number of terms (odd powers) to include in the series. Default is 20 for required precision.</param> | |
| /// <returns>The approximate value of sin(x).</returns> | |
| public static double Calculate(double x, int terms = 20) // Increased default from 15 to 20 for higher precision | |
| { | |
| if (terms <= 0) | |
| { | |
| throw new ArgumentException("The number of terms must be a positive integer."); | |
| } | |
| // Best practice for series convergence: reduce the angle to the range [-2*PI, 2*PI]. | |
| // The series converges for all x, but convergence is faster near 0. | |
| x %= 2 * Math.PI; | |
| double result = 0.0; | |
| double xSquared = x * x; | |
| // currentPower: Stores x^k | |
| double currentPower = x; // Starts at x^1 | |
| // currentFactorial: Stores k! | |
| double currentFactorial = 1.0; // Starts at 1! | |
| int sign = 1; // Starts positive | |
| // k is the exponent/factorial index (1, 3, 5, 7, ...) | |
| for (int i = 0, k = 1; i < terms; i++, k += 2) | |
| { | |
| if (k > 1) | |
| { | |
| // Iteratively update the components for the next term: | |
| // Current exponent k is (k-2) + 2. | |
| // x^k = x^(k-2) * x^2 | |
| currentPower *= xSquared; | |
| // k! = (k-2)! * (k-1) * k | |
| // Example: 5! = 3! * 4 * 5 | |
| currentFactorial *= (k - 1) * k; | |
| } | |
| // Calculate the term: (sign) * x^k / k! | |
| double term = currentPower / currentFactorial; | |
| result += sign * term; | |
| sign *= -1; // Alternate the sign for the next term | |
| } | |
| return result; | |
| } | |
| } | |
| // Removed duplicate Sin class. Use Maclaurin.Sin() in Algorithms/Numeric/Series/Maclaurin.cs for sine calculation via Maclaurin series. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
System.Numericsnamespace is imported but never used in this file. Remove this unused import to keep the code clean.