using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using UnitTestSharp;

namespace Azimuth.UnitTests
{
    public class ScalarTests : TestFixture
    {
        public void CSharpTest_EqualsBetweenDifferentNumericTypes()
        {
            int value1 = 10;
            double value2 = 10;

            CheckFalse(value1.Equals(value2));
            Check(value2.Equals(value1));
        }

        public class EqualsTests : TestFixture
        {
            public void EqualsMethod_EqualScalar()
            {
                Scalar value1 = 10;
                Scalar value2 = 10;

                Check(value1.Equals(value2));
                Check(value2.Equals(value1));
            }

            public void EqualsMethod_EqualScalarPrimitive()
            {
                Scalar value1 = 10;
                double value2 = 10;

                Check(value1.Equals(value2));
                Check(value2.Equals(value1));
            }

            public void EqualsMethod_NotEqualScalar()
            {
                Scalar value1 = 10;
                Scalar value2 = 11;

                CheckFalse(value1.Equals(value2));
                CheckFalse(value2.Equals(value1));
            }

            public void EqualsMethod_NotEqualScalarPrimitive()
            {
                Scalar value1 = 10;
                double value2 = 11;

                CheckFalse(value1.Equals(value2));
                CheckFalse(value2.Equals(value1));
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void EqualsOperator_EqualScalar()
            {
                Scalar value1 = 10;
                Scalar value2 = 10;

                Check(value1 == value2);
                Check(value2 == value1);
            }

            public void EqualsOperator_EqualScalarPrimitive()
            {
                Scalar value1 = 10;
                double value2 = 10;

                Check(value1 == value2);
                Check(value2 == value1);
            }

            public void EqualsOperator_NotEqualScalar()
            {
                Scalar value1 = 10;
                Scalar value2 = 11;

                CheckFalse(value1 == value2);
                CheckFalse(value2 == value1);
            }

            public void EqualsOperator_NotEqualScalarPrimitive()
            {
                Scalar value1 = 10;
                double value2 = 11;

                CheckFalse(value1 == value2);
                CheckFalse(value2 == value1);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void NotEqualsOperator_EqualScalar()
            {
                Scalar value1 = 10;
                Scalar value2 = 10;

                CheckFalse(value1 != value2);
                CheckFalse(value2 != value1);
            }

            public void NotEqualsOperator_EqualScalarPrimitive()
            {
                Scalar value1 = 10;
                double value2 = 10;

                CheckFalse(value1 != value2);
                CheckFalse(value2 != value1);
            }

            public void NotEqualsOperator_NotEqualScalar()
            {
                Scalar value1 = 10;
                Scalar value2 = 11;

                Check(value1 != value2);
                Check(value2 != value1);
            }

            public void NotEqualsOperator_NotEqualScalarPrimitive()
            {
                Scalar value1 = 10;
                double value2 = 11;

                Check(value1 != value2);
                Check(value2 != value1);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void EqualsObjectMethod_Null()
            {
                Scalar value = 10;

                CheckFalse(value.Equals(null));
            }

            public void EqualsObjectMethod_Scalar()
            {
                Scalar value1 = 10;
                object value2 = (Scalar)10;

                Check(value1.Equals(value2));
                Check(value2.Equals(value1));
            }

            public void EqualsObjectMethod_double()
            {
                Scalar value1 = 10;
                object value2 = (double)10;

                Check(value1.Equals(value2));
                //Check(value2.Equals(value1));
            }

            public void EqualsObjectMethod_bool()
            {
                Scalar value1 = 1000;
                object value2 = true;

                CheckFalse(value1.Equals(value2));
                CheckFalse(value2.Equals(value1));
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void CanHandleUnitTestSharpCheckEqual_Scalar()
            {
                Scalar value1 = 10;
                Scalar value2 = 10;

                CheckEqual(value1, value2);
                CheckEqual(value2, value1);
            }

            public void CanHandleUnitTestSharpCheckEqual_ScalarPrimitive()
            {
                Scalar value1 = 10;
                double value2 = 10;

                CheckEqual(value1, value2);
                CheckEqual(value2, value1);
            }

            public void CanHandleUnitTestSharpCheckEqual_Object()
            {
                Scalar value1 = 10;
                object value2 = (double)10;

                CheckEqual(value1, value2);
                CheckEqual(value2, value1);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void FuzzyEqualityTest()
            {
                Scalar a = 1.0;
                Scalar b = 1.0 + 1e-11;

                CheckEqual(a, b);
                CheckEqual(b, a);
            }

            public void FuzzyEquality_OperatorTest()
            {
                Scalar a = 1.0;
                Scalar b = 1.0 + 1e-11;

                Check(a == b);
                Check(b == a);
            }

            public void FuzzyEquality_ObjEqualsTest()
            {
                Scalar a = 1.0;
                Scalar b = 1.0 + 1e-11;

                Check(a.Equals((object)b));
                Check(b.Equals((object)a));
            }

            public void FuzzyEquality_ScalarEqualsTest()
            {
                Scalar a = 1.0;
                Scalar b = 1.0 + 1e-11;

                Check(a.Equals(b));
                Check(b.Equals(a));
            }

            public void FuzzyEquality_StaticMethod()
            {
                Scalar a = 1.0;
                Scalar b = 1.0 + 1e-11;

                Check(Scalar.FuzzyEquality(a, b));
                Check(Scalar.FuzzyEquality(b, a));
                Check(b.Equals(a));
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void Gotcha_00()
            {
                Scalar v1 = 0;
                Scalar v2 = 0;

                CheckEqual(v1, v2);
                CheckEqual(v2, v1);
            }

            public void Gotcha_InfInf()
            {
                Scalar v1 = Scalar.PositiveInfinity;
                Scalar v2 = Scalar.PositiveInfinity;

                CheckEqual(v1, v2);
                CheckEqual(v2, v1);
            }

            public void Gotcha_NegInfNegInf()
            {
                Scalar v1 = Scalar.NegativeInfinity;
                Scalar v2 = Scalar.NegativeInfinity;

                CheckEqual(v1, v2);
                CheckEqual(v2, v1);
            }

            public void Gotcha_ZeroInf()
            {
                Scalar v1 = 0;
                Scalar v2 = Scalar.PositiveInfinity;

                CheckNotEqual(v1, v2);
                CheckNotEqual(v2, v1);
            }

            public void Gotcha_NaNNan()
            {
                Scalar v1 = Scalar.NaN;
                Scalar v2 = Scalar.NaN;

                CheckNotEqual(v1, v2);
                CheckNotEqual(v2, v1);
            }
        }

        public class CtorTests : TestFixture
        {
            public void ScalarConstructorTest_Double()
            {
                double value = (9.0/8.0);
                Scalar target = new Scalar(value);
                CheckEqual(value, target);
            }

            public void ScalarConstructorTest_Float()
            {
                float value = (9.0f / 8.0f);
                Scalar target = new Scalar(value);
                CheckEqual(value, target);
            }
        }

		public class IVectorSpaceTests : TestFixture
		{
			public void AssignmentAdd()
			{
				IVectorSpace<Scalar> a = (Scalar)10;
				Scalar b = 5;

                a.AddAssignment(b);

				CheckEqual(15, a);
				CheckEqual(5, b);
			}

			public void AssignmentSubtract()
			{
                IVectorSpace<Scalar> a = (Scalar)10;
				Scalar b = 5;

                a.SubtractAssignment(b);

				CheckEqual(5, a);
				CheckEqual(5, b);
			}

			public void AssignmentScale()
			{
                IVectorSpace<Scalar> a = (Scalar)10;
				Scalar b = 5;

                a.ScaleAssignment(b);

				CheckEqual(50, a);
				CheckEqual(5, b);
			}

            public void AssignmentScaleThenAdd()
            {
                IVectorSpace<Scalar> a = (Scalar)10;
                Scalar b = 5;
                Scalar scale = 10;

                a.ScaleThenAddAssignment(scale, b);

                CheckEqual(60, a);
                CheckEqual(5, b);
            }

            public void InnerProduct()
            {
                Scalar a = 10;
                Scalar b = 20;

                CheckEqual(200, a.InnerProduct(b));
                CheckEqual(10, a);
                CheckEqual(20, b);
            }

            public void Dimension()
            {
                IVectorSpace<Scalar> v1 = new Scalar(4);
                CheckEqual(1, v1.Dimension);
            }
            
            public void GetDimension()
            {
                IVectorSpace<Scalar> a = new Scalar(21);
                CheckEqual(21, a[0]);
            }

            public void GetDimension_OutOfBounds()
            {
                IVectorSpace<Scalar> a = new Scalar(21);
                CheckThrow(typeof(System.Exception));
                Scalar dummy = a[1];
                CheckFalse(dummy == 0); //dummy
            }

            public void MinMax()
            {
                Scalar a = 13;
                Scalar b = 1;
                Scalar c, d;
                a.MinMax(b, out c, out d);
                CheckEqual(1, c);
                CheckEqual(13, d);
            }

            public void IsOrigin_False()
            {
                IVectorSpace<Scalar> a = (Scalar)10;

                CheckFalse(a.IsOrigin());
            }

            public void IsOrigin_True()
            {
                IVectorSpace<Scalar> a = (Scalar)0;

                CheckTrue(a.IsOrigin());
            }

            public void Clone()
            {
                Scalar a = 10;

                Scalar b = ((IVectorSpace<Scalar>)a).Clone();
                a++;

                CheckEqual(10, b);
                CheckEqual(11, a);
            }
		}

        public class ConversionsTests : TestFixture
        {
            public void ExplicitFloat()
            {
                float valueF1 = (9.0f / 8.0f);
                Scalar valueS = valueF1;
                float valueF2 = (float)valueS;

                CheckEqual(valueF1, valueF2);
            }

            public void ExplicitDouble()
            {
                double valueF1 = (9.0 / 8.0);
                Scalar valueS = valueF1;
                double valueF2 = (double)valueS;

                CheckEqual(valueF1, valueF2);
            }

            public void ImplicitScalarPrimitive()
            {                
                Scalar valueS = 10;

                //NOTE: This won't compile if you switch to using floats by defining AZIMUTH_USE_FLOAT
                double valueD = valueS;

                CheckEqual(valueD, valueS);
            }
        }

        public class OperatorTests : TestFixture
        {
            public void LessThan_True()
            {
                Scalar v1 = 1;
                Scalar v2 = 2;

                Check(v1 < v2);
            }

            public void LessThan_False_Equal()
            {
                Scalar v1 = 1;
                Scalar v2 = 1;

                CheckFalse(v1 < v2);
            }

            public void LessThan_False_Greater()
            {
                Scalar v1 = 2;
                Scalar v2 = 1;

                CheckFalse(v1 < v2);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void GreaterThan_True()
            {
                Scalar v1 = 2;
                Scalar v2 = 1;

                Check(v1 > v2);
            }

            public void GreaterThan_False_Equal()
            {
                Scalar v1 = 1;
                Scalar v2 = 1;

                CheckFalse(v1 > v2);
            }

            public void GreaterThan_False_Less()
            {
                Scalar v1 = 1;
                Scalar v2 = 2;

                CheckFalse(v1 > v2);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void GreaterThanEqual_True_GreaterThan()
            {
                Scalar v1 = 2;
                Scalar v2 = 1;

                Check(v1 >= v2);
            }

            public void GreaterThanEqual_True_Equal()
            {
                Scalar v1 = 1;
                Scalar v2 = 1;

                Check(v1 >= v2);
            }

            public void GreaterThanEqual_True_FuzzyEqual()
            {
                Scalar v1 = 1;
                Scalar v2 = 1 + 1e-11;

                Check(v1 >= v2);
            }

            public void GreaterThanEqual_False_Less()
            {
                Scalar v1 = 1;
                Scalar v2 = 2;

                CheckFalse(v1 >= v2);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void LessThanEqual_True_LessThan()
            {
                Scalar v1 = 1;
                Scalar v2 = 2;

                Check(v1 <= v2);
            }

            public void LessThanEqual_True_Equal()
            {
                Scalar v1 = 1;
                Scalar v2 = 1;

                Check(v1 <= v2);
            }

            public void LessThanEqual_True_FuzzyEqual()
            {
                Scalar v1 = 1;
                Scalar v2 = 1 - 1e-11;

                Check(v1 <= v2);
            }

            public void LessThanEqual_False_Greater()
            {
                Scalar v1 = 2;
                Scalar v2 = 1;

                CheckFalse(v1 <= v2);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void Addition()
            {
                Scalar v1 = 17;
                Scalar v2 = 23;

                CheckEqual(40, v1 + v2);
                CheckEqual(40, v2 + v1);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void Subtraction()
            {
                Scalar v1 = 17;
                Scalar v2 = 23;

                CheckEqual(6, v2 - v1);
                CheckEqual(-6, v1 - v2);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void Negation()
            {
                Scalar v1 = 17;

                CheckEqual(-17, -v1);
                CheckEqual(17, -(-v1));
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void Multiplication()
            {
                Scalar v1 = 17;
                Scalar v2 = 23;

                CheckEqual(391, v1 * v2);
                CheckEqual(391, v2 * v1);
            }

            //public void Multiplication_Inf0()
            //{
            //    Scalar v1 = Scalar.PositiveInfinity;
            //    Scalar v2 = 0;

            //    CheckEqual(0, v1 * v2);
            //    CheckEqual(0, v2 * v1);
            //}

            //public void Multiplication_NegInf0()
            //{
            //    Scalar v1 = Scalar.NegativeInfinity;
            //    Scalar v2 = 0;

            //    CheckEqual(0, v1 * v2);
            //    CheckEqual(0, v2 * v1);
            //}

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////

            public void Division()
            {
                Scalar v1 = 9.0 / 8.0;
                Scalar v2 = 18.0 / 8.0;

                CheckEqual(0.5, v1 / v2);
                CheckEqual(2, v2 / v1);
            }

            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
        }

        public class ToStringTests : TestFixture
        {
            // Note: we have to force the culture for these tests so that the unit tests run the same for
            // other developers.

            public class American : TestFixture
            {
                CultureInfo culture;

                public override void FixtureSetup()
                {
                    culture = CultureInfo.CreateSpecificCulture("en-US");
                }

                public void Basic()
                {
                    CheckEqual("1.125", ((Scalar)9.0 / 8.0).ToString(culture));
                }

                public void Thousands()
                {
                    CheckEqual("1001.125", (1000 + (Scalar)9.0 / 8.0).ToString(culture));
                }

                public void TooManyDigits()
                {
                    CheckEqual("0.3333333333", ((Scalar)(1.0 / 3.0)).ToString(culture));
                }

                public void GenericFormat()
                {
                    CheckEqual("0.3333333333", ((Scalar)(1.0 / 3.0)).ToString("G", culture));
                }

                public void Rounding()
                {
                    CheckEqual("0.6666666667", ((Scalar)(2.0 / 3.0)).ToString(culture));
                }

                public void HugeNumber()
                {
                    Scalar value = 1.0 / Scalar.FuzzyEqualityEpsilon + 0.1;

                    CheckEqual("10000000000", value.ToString(culture));
                }

                public void DropsTrailingZeros()
                {
                    Scalar value = 1.0;

                    CheckEqual("1", value.ToString(culture));
                }

                public void CustomFormater()
                {
                    CheckEqual("MOM: 0.3", ((Scalar)(1.0 / 3.0)).ToString("MOM: 0.#", culture));
                }
            }

            public class European : TestFixture
            {
                CultureInfo culture;

                public override void FixtureSetup()
                {
                    culture = CultureInfo.CreateSpecificCulture("de-DE");
                }

                public void Basic()
                {
                    CheckEqual("1,125", ((Scalar)9.0 / 8.0).ToString(culture));
                }

                public void Thousands()
                {
                    CheckEqual("1001,125", (1000 + (Scalar)9.0 / 8.0).ToString(culture));
                }

                public void TooManyDigits()
                {
                    CheckEqual("0,3333333333", ((Scalar)(1.0 / 3.0)).ToString(culture));
                }

                public void GenericFormat()
                {
                    CheckEqual("0,3333333333", ((Scalar)(1.0 / 3.0)).ToString("G", culture));
                }

                public void Rounding()
                {
                    CheckEqual("0,6666666667", ((Scalar)(2.0 / 3.0)).ToString(culture));
                }

                public void HugeNumber()
                {
                    Scalar value = 1.0 / Scalar.FuzzyEqualityEpsilon + 0.1;

                    CheckEqual("10000000000", value.ToString(culture));
                }

                public void DropsTrailingZeros()
                {
                    Scalar value = 1.0;

                    CheckEqual("1", value.ToString(culture));
                }

                public void CustomFormater()
                {
                    CheckEqual("MOM: 0,3", ((Scalar)(1.0 / 3.0)).ToString("MOM: 0.#", culture));
                }
            }
        }

        public class SignTests : TestFixture
        {
            public void Zero()
            {
                Scalar a = 0;

                CheckEqual(1.0, a.Sign);
            }

            public void Positive()
            {
                Scalar a = 1;

                CheckEqual(1.0, a.Sign);
            }

            public void Negative()
            {
                Scalar a = -1;

                CheckEqual(-1.0, a.Sign);
            }

            public void PositiveInfinity()
            {
                Scalar a = Scalar.PositiveInfinity;

                CheckEqual(1.0, a.Sign);
            }

            public void NegativeInfinity()
            {
                Scalar a = Scalar.NegativeInfinity;

                CheckEqual(-1.0, a.Sign);
            }

            public void NaN()
            {
                Scalar a = Scalar.NaN;

                CheckEqual(1.0, a.Sign);
            }
        }

        public class FLOPCounting : TestFixture
        {
#if AZIMUTH_COUNT_FLOPS
            public override void  TestSetup()
            {
                Scalar.ResetFlopCount();
            }

            public void CheckIfAzimuthAndTestsAreConsistent()
            {
                CheckTrue(Scalar.IsCountingFlops);
            }

            public void CheckZeroCount()
            {
                CheckEqual(0, Scalar.FLOPs);
            }

            public void Add()
            {
                Scalar a = 0;

                CheckEqual(0, Scalar.FLOPs);

                a += 20;

                CheckEqual(1, Scalar.FLOPs);
            }

            public void Sub()
            {
                Scalar a = 0;

                CheckEqual(0, Scalar.FLOPs);

                a -= 20;

                CheckEqual(1, Scalar.FLOPs);
            }

            public void Mul()
            {
                Scalar a = 10;

                CheckEqual(0, Scalar.FLOPs);

                a *= 20;

                CheckEqual(1, Scalar.FLOPs);
            }

            public void Div()
            {
                Scalar a = 10;

                CheckEqual(0, Scalar.FLOPs);

                a /= 2;

                CheckEqual(1, Scalar.FLOPs);
            }

            public void Equality()
            {
                Scalar a = 0;

                CheckEqual(0, Scalar.FLOPs);

                bool value = (a == 20);

                CheckEqual(1, Scalar.FLOPs);
            }

            public void Inequality()
            {
                Scalar a = 0;

                CheckEqual(0, Scalar.FLOPs);

                bool value = (a != 20);

                CheckEqual(1, Scalar.FLOPs);
            }

            public void LessThan()
            {
                Scalar a = 0;

                CheckEqual(0, Scalar.FLOPs);

                bool value = a < 20;

                CheckEqual(1, Scalar.FLOPs);
            }

            public void GreatherThan()
            {
                Scalar a = 0;

                CheckEqual(0, Scalar.FLOPs);

                bool value = a > 20;

                CheckEqual(1, Scalar.FLOPs);
            }
#else
            public void CheckIfAzimuthAndTestsAreConsistent()
            {
                // You need to make sure that the Unit Tests are compiled 
                // with AZIMUTH_COUNT_FLOPS if Azimuth proper is.
                CheckFalse(Scalar.IsCountingFlops);
            }

            public void CountIsZero()
            {
                CheckEqual(0, Scalar.FLOPs);
            }

            public void CountDoesNotIncrment()
            {
                Scalar a = 0;
                CheckEqual(0, Scalar.FLOPs);

                a += 20;
                CheckEqual(0, Scalar.FLOPs);
            }
#endif
        }
    }
}