using System; namespace Amphora.UnitTests { public class SinCosHelpersTests : UnitTestSharp.TestFixture { public class QuarterTurnedTests : UnitTestSharp.TestFixture { public void QuarterZero_KeepsTheSineAndCosine() { double sine; double cosine; SinCosHelpers.QuarterTurned(0, 0.25, 0.75, out sine, out cosine); CheckEqual(0.25, sine); CheckEqual(0.75, cosine); } public void QuarterOne_TakesTheCosineAsSineAndMinusTheSineAsCosine() { double sine; double cosine; SinCosHelpers.QuarterTurned(1, 0.25, 0.75, out sine, out cosine); CheckEqual(0.75, sine); CheckEqual(-0.25, cosine); } public void QuarterTwo_NegatesBoth() { double sine; double cosine; SinCosHelpers.QuarterTurned(2, 0.25, 0.75, out sine, out cosine); CheckEqual(-0.25, sine); CheckEqual(-0.75, cosine); } public void QuarterThree_TakesMinusTheCosineAsSineAndTheSineAsCosine() { double sine; double cosine; SinCosHelpers.QuarterTurned(3, 0.25, 0.75, out sine, out cosine); CheckEqual(-0.75, sine); CheckEqual(0.25, cosine); } public void ZeroSine_GivesAPlusZeroWhereverTheResultIsZero() { double sine; double cosine; SinCosHelpers.QuarterTurned(0, 0.0, 1.0, out sine, out cosine); CheckEqual(0L, BitConverter.DoubleToInt64Bits(sine)); SinCosHelpers.QuarterTurned(1, 0.0, 1.0, out sine, out cosine); CheckEqual(0L, BitConverter.DoubleToInt64Bits(cosine)); SinCosHelpers.QuarterTurned(2, 0.0, 1.0, out sine, out cosine); CheckEqual(0L, BitConverter.DoubleToInt64Bits(sine)); SinCosHelpers.QuarterTurned(3, 0.0, 1.0, out sine, out cosine); CheckEqual(0L, BitConverter.DoubleToInt64Bits(cosine)); } } public class TinySinCosTests : UnitTestSharp.TestFixture { public void Zero_KeepsItsSignInTheSine() { double sine; double cosine; SinCosHelpers.TinySinCos(0.0, out sine, out cosine); CheckEqual(0L, BitConverter.DoubleToInt64Bits(sine)); CheckEqual(1.0, cosine); SinCosHelpers.TinySinCos(-0.0, out sine, out cosine); CheckEqual(long.MinValue, BitConverter.DoubleToInt64Bits(sine)); CheckEqual(1.0, cosine); } public void NegativeAngle_GivesTheNegatedSine() { double sine; double cosine; double negatedSine; SinCosHelpers.TinySinCos(1e-200, out sine, out cosine); SinCosHelpers.TinySinCos(-1e-200, out negatedSine, out cosine); CheckEqual(-sine, negatedSine); CheckEqual(1.0, cosine); } public void AnglesWhoseSineIsSubnormal_GetTheNearestDoubleNotADoubleRounding() { for (int i = 0; i < PortableMathTests.SubnormalSineTurns.Length; i++) { double sine; double cosine; SinCosHelpers.TinySinCos(PortableMathTests.SubnormalSineTurns[i], out sine, out cosine); CheckEqual(PortableMathTests.SubnormalSines[i], sine); SinCosHelpers.TinySinCos(-PortableMathTests.SubnormalSineTurns[i], out sine, out cosine); CheckEqual(-PortableMathTests.SubnormalSines[i], sine); } } public void SmallestSubnormal_GivesTheNearestSubnormalSine() { double sine; double cosine; SinCosHelpers.TinySinCos(BitConverter.Int64BitsToDouble(1L), out sine, out cosine); CheckEqual(BitConverter.Int64BitsToDouble(6L), sine); CheckEqual(1.0, cosine); } } public class TryRoundTests : UnitTestSharp.TestFixture { public void LowPartWellInsideHalfAStep_RoundsToTheHighPart() { double rounded; CheckTrue(SinCosHelpers.TryRound(1.0, 1e-20, 1e-30, out rounded)); CheckEqual(1.0, rounded); } public void LowPartWithinTheSlackOfHalfAStep_CannotBeDecided() { double rounded; CheckFalse(SinCosHelpers.TryRound(1.0, 1.1102230246251565e-16, 1e-16, out rounded)); } public void LowPartPastHalfAStepBeyondTheSlack_RoundsToTheNextDouble() { double rounded; CheckTrue(SinCosHelpers.TryRound(1.0, 1.5e-16, 1e-20, out rounded)); CheckEqual(1.0000000000000002, rounded); } public void LowPartOfSeveralSteps_RoundsTheWholeSum() { double rounded; CheckTrue(SinCosHelpers.TryRound(1.0, 4.440892098500626e-16, 1e-20, out rounded)); CheckEqual(1.0000000000000004, rounded); } public void NegativeHighPart_RoundsAwayFromZeroForANegativeLowPart() { double rounded; CheckTrue(SinCosHelpers.TryRound(-1.0, -1.5e-16, 1e-20, out rounded)); CheckEqual(-1.0000000000000002, rounded); } } public class SettleHardTests : UnitTestSharp.TestFixture { public void Zero_IsZeroAndOne() { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(0.0, false, false, ref sine, ref cosine); CheckEqual(0.0, sine); CheckEqual(1.0, cosine); } public void AnEighthOfATurnEitherWay_IsRootTwoOverTwoWithTheSineSigned() { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(0.125, false, false, ref sine, ref cosine); CheckEqual(0.7071067811865476, sine); CheckEqual(0.7071067811865476, cosine); SinCosHelpers.SettleHard(-0.125, false, false, ref sine, ref cosine); CheckEqual(-0.7071067811865476, sine); CheckEqual(0.7071067811865476, cosine); } public void AngleBetweenTableEntries_IsTheNearestDouble() { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(0.1, false, false, ref sine, ref cosine); CheckEqual(0.5877852522924731, sine); CheckEqual(0.8090169943749475, cosine); } public void OnlyTheUndecidedSine_IsChanged() { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(0.1, false, true, ref sine, ref cosine); CheckEqual(0.5877852522924731, sine); CheckEqual(5.0, cosine); } public void OnlyTheUndecidedCosine_IsChanged() { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(0.1, true, false, ref sine, ref cosine); CheckEqual(5.0, sine); CheckEqual(0.8090169943749475, cosine); } public void AnglesTheTableSumsCannotDecide_AreTheNearestDoubles() { for (int i = 0; i < PortableMathTests.HardSinCosTurns.Length; i++) { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(PortableMathTests.HardSinCosTurns[i], false, false, ref sine, ref cosine); CheckEqual(PortableMathTests.HardSinOfTurns[i], sine); CheckEqual(PortableMathTests.HardCosOfTurns[i], cosine); } } public void AnglesTheRefinedSumsCannotDecide_AreTheNearestDoubles() { for (int i = 0; i < PortableMathTests.DeepSinCosTurns.Length; i++) { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(PortableMathTests.DeepSinCosTurns[i], false, false, ref sine, ref cosine); CheckEqual(PortableMathTests.DeepSinOfTurns[i], sine); CheckEqual(PortableMathTests.DeepCosOfTurns[i], cosine); } } public void AnglesTheCarefulSumsCannotDecide_AreTheNearestDoubles() { for (int i = 0; i < PortableMathTests.DeepestSinCosTurns.Length; i++) { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(PortableMathTests.DeepestSinCosTurns[i], false, false, ref sine, ref cosine); CheckEqual(PortableMathTests.DeepestSinOfTurns[i], sine); CheckEqual(PortableMathTests.DeepestCosOfTurns[i], cosine); } } public void AngleOnlyTheSineOfWhichNeedsTheFinestSums_LeavesTheCosineAlone() { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(PortableMathTests.DeepestSinCosTurns[0], false, true, ref sine, ref cosine); CheckEqual(PortableMathTests.DeepestSinOfTurns[0], sine); CheckEqual(5.0, cosine); } public void AngleOnlyTheCosineOfWhichNeedsTheFinestSums_LeavesTheSineAlone() { double sine = 5.0; double cosine = 5.0; SinCosHelpers.SettleHard(PortableMathTests.DeepestSinCosTurns[3], true, false, ref sine, ref cosine); CheckEqual(5.0, sine); CheckEqual(PortableMathTests.DeepestCosOfTurns[3], cosine); } } public class FinishLargeTests : UnitTestSharp.TestFixture { public void TableAngle_GivesTheTableEntry() { double sine; double cosine; double turns = 4398046511104.0 + 1.0 / 1024.0; SinCosHelpers.FinishLarge(turns, turns, out sine, out cosine); CheckEqual(0.0061358846491544753, sine); CheckEqual(0.99998117528260111, cosine); } public void NegativeTableAngle_NegatesTheSine() { double sine; double cosine; double turns = 4398046511104.0 + 1.0 / 1024.0; SinCosHelpers.FinishLarge(-turns, turns, out sine, out cosine); CheckEqual(-0.0061358846491544753, sine); CheckEqual(0.99998117528260111, cosine); } public void HalfTurnsPast2To51_AreReducedBeforeTheTable() { double sine; double cosine; double turns = 2251799813685248.0 + 0.5; SinCosHelpers.FinishLarge(turns, turns, out sine, out cosine); CheckEqual(0.0, sine); CheckEqual(-1.0, cosine); } public void WholeTurnsOnly_AreZeroAndOne() { double sine; double cosine; SinCosHelpers.FinishLarge(1e300, 1e300, out sine, out cosine); CheckEqual(0.0, sine); CheckEqual(1.0, cosine); } public void Infinity_IsNotANumber() { double sine; double cosine; SinCosHelpers.FinishLarge(double.PositiveInfinity, double.PositiveInfinity, out sine, out cosine); CheckTrue(double.IsNaN(sine) && double.IsNaN(cosine)); } public void NotANumber_IsNotANumber() { double sine; double cosine; SinCosHelpers.FinishLarge(double.NaN, double.NaN, out sine, out cosine); CheckTrue(double.IsNaN(sine) && double.IsNaN(cosine)); } } } }