Example #1
0
        static void Main(string[] args)
        {
            Action<Equation> AssertIsTrue = (eq) =>
            {
                if (!eq) Console.WriteLine(eq.ToString());
            };

            Func<MathObject, MathObject> sin = obj => Trig.Sin(obj);
            Func<MathObject, MathObject> cos = obj => Trig.Cos(obj);
            Func<MathObject, MathObject> tan = obj => new Tan(obj).Simplify();

            Func<MathObject, MathObject> asin = obj => new Asin(obj).Simplify();
            Func<MathObject, MathObject> atan = obj => new Atan(obj).Simplify();

            {
                var a = new Symbol("a");
                var b = new Symbol("b");
                var c = new Symbol("c");
                var d = new Symbol("d");

                var x = new Symbol("x");
                var y = new Symbol("y");
                var z = new Symbol("z");

                Func<int, Integer> Int = (n) => new Integer(n);

                {
                    DoubleFloat.tolerance = 0.000000001;

                    Assert(new DoubleFloat(1.2).Equals(new DoubleFloat(1.2)), "new DoubleFloat(1.2).Equals(new DoubleFloat(1.2))");

                    Assert(new DoubleFloat(1.20000001).Equals(new DoubleFloat(1.20000002)) == false, "new DoubleFloat(1.20000001).Equals(new DoubleFloat(1.20000002)) == false");

                    Assert(new DoubleFloat(1.2000000000001).Equals(new DoubleFloat(1.200000000002)), "new DoubleFloat(1.2000000000001).Equals(new DoubleFloat(1.200000000002))");

                    Assert(new DoubleFloat(1.2).Equals(new DoubleFloat(1.23)) == false, "new DoubleFloat(1.2).Equals(new DoubleFloat(1.23)) == false");

                    DoubleFloat.tolerance = null;
                }

                #region Simplify

                AssertIsTrue(x + x == 2 * x);

                AssertIsTrue(x + x == 2 * x);

                AssertIsTrue(x + x + x == 3 * x);

                AssertIsTrue(5 + x + 2 == 7 + x);

                AssertIsTrue(3 + x + 5 + x == 8 + 2 * x);

                AssertIsTrue(4 * x + 3 * x == 7 * x);

                AssertIsTrue(x + y + z + x + y + z == 2 * x + 2 * y + 2 * z);

                AssertIsTrue(10 - x == 10 + x * -1);

                AssertIsTrue(x * y / 3 == Int(1) / 3 * x * y);

                AssertIsTrue(x / y == x * (y ^ -1));

                AssertIsTrue(x / 3 == x * (Int(1) / 3));

                AssertIsTrue(6 * x * y / 3 == 2 * x * y);

                AssertIsTrue((((x ^ Int(1) / 2) ^ Int(1) / 2) ^ 8) == (x ^ 2));

                AssertIsTrue(((((x * y) ^ (Int(1) / 2)) * (z ^ 2)) ^ 2) == (x * y * (z ^ 4)));

                AssertIsTrue(x / x == 1);

                AssertIsTrue(x / y * y / x == 1);

                AssertIsTrue((x ^ 2) * (x ^ 3) == (x ^ 5));

                AssertIsTrue(x + y + x + z + 5 + z == 5 + 2 * x + y + 2 * z);

                AssertIsTrue(((Int(1) / 2) * x + (Int(3) / 4) * x) == Int(5) / 4 * x);

                AssertIsTrue(1.2 * x + 3 * x == 4.2 * x);

                AssertIsTrue(3 * x + 1.2 * x == 4.2 * x);

                AssertIsTrue(1.2 * x * 3 * y == 3.5999999999999996 * x * y);

                AssertIsTrue(3 * x * 1.2 * y == 3.5999999999999996 * x * y);

                AssertIsTrue(3.4 * x * 1.2 * y == 4.08 * x * y);

                AssertIsTrue((a == b) == (a == b));

                #endregion

                #region Power.Simplify

                AssertIsTrue((0 ^ x) == 0);
                AssertIsTrue((1 ^ x) == 1);
                AssertIsTrue((x ^ 0) == 1);
                AssertIsTrue((x ^ 1) == x);

                #endregion

                // Product.Simplify

                AssertIsTrue(x * 0 == 0);

                // Difference

                AssertIsTrue(-x == -1 * x);

                AssertIsTrue(x - y == x + -1 * y);

                #region Substitute

                AssertIsTrue(Int(10).Substitute(Int(10), 20) == 20);
                AssertIsTrue(Int(10).Substitute(Int(15), 20) == 10);

                AssertIsTrue(new DoubleFloat(1.0).Substitute(new DoubleFloat(1.0), 2.0) == 2.0);
                AssertIsTrue(new DoubleFloat(1.0).Substitute(new DoubleFloat(1.5), 2.0) == 1.0);

                AssertIsTrue((Int(1) / 2).Substitute(Int(1) / 2, Int(3) / 4) == Int(3) / 4);
                AssertIsTrue((Int(1) / 2).Substitute(Int(1) / 3, Int(3) / 4) == Int(1) / 2);

                AssertIsTrue(x.Substitute(x, y) == y);
                AssertIsTrue(x.Substitute(y, y) == x);

                AssertIsTrue((x ^ y).Substitute(x, 10) == (10 ^ y));
                AssertIsTrue((x ^ y).Substitute(y, 10) == (x ^ 10));

                AssertIsTrue((x ^ y).Substitute(x ^ y, 10) == 10);

                AssertIsTrue((x * y * z).Substitute(x, y) == ((y ^ 2) * z));
                AssertIsTrue((x * y * z).Substitute(x * y * z, x) == x);

                AssertIsTrue((x + y + z).Substitute(x, y) == ((y * 2) + z));
                AssertIsTrue((x + y + z).Substitute(x + y + z, x) == x);

                AssertIsTrue(
                    ((((x * y) ^ (Int(1) / 2)) * (z ^ 2)) ^ 2)
                        .Substitute(x, 10)
                        .Substitute(y, 20)
                        .Substitute(z, 3)
                        == 16200
                        );

                #region Equation.Substitute

                AssertIsTrue((x == y).Substitute(y, z) == (x == z));

                AssertIsTrue((x != y).Substitute(y, z) == (x != z));

                (x == 0).Substitute(x, 0).AssertEqTo(true);

                (x == 0).Substitute(x, 1).AssertEqTo(false);

                (x != 0).Substitute(x, 0).AssertEqTo(false);

                (x != 0).Substitute(x, 1).AssertEqTo(true);

                #endregion

                #endregion

                AssertIsTrue(sin(new DoubleFloat(3.14159 / 2)) == 0.99999999999911982);

                AssertIsTrue(sin(x + y) + sin(x + y) == 2 * sin(x + y));

                AssertIsTrue(sin(x + x) == sin(2 * x));

                AssertIsTrue(sin(x + x).Substitute(x, 1) == sin(Int(2)));

                AssertIsTrue(sin(x + x).Substitute(x, 1.0) == 0.90929742682568171);

                AssertIsTrue(sin(2 * x).Substitute(x, y) == sin(2 * y));

                // Product.RecursiveSimplify

                AssertIsTrue(1 * x == x);

                AssertIsTrue(x * 1 == x);

                AssertIsTrue(x != y);

                AssertIsTrue(x != 10);

                // ==(double a, MathObject b)

                AssertIsTrue(1.0 == new DoubleFloat(3.0) - 2.0);

                AssertIsTrue((a == b) != (a != b));

                #region Equation.ToString

                Assert((x == y).ToString() == "x == y", "x == y");

                Assert((x != y).ToString() == "x != y", "x != y");

                #endregion

                #region Function.ToString

                Assert(new And().ToString() == "And()", "And()");

                #endregion

                #region Equation.Simplify

                (new Integer(0) == new Integer(0)).Simplify().AssertEqTo(true);

                (new Integer(0) == new Integer(1)).Simplify().AssertEqTo(false);

                (new Integer(0) != new Integer(1)).Simplify().AssertEqTo(true);

                (new Integer(0) != new Integer(0)).Simplify().AssertEqTo(false);

                #endregion

                #region And

                new And().Simplify().AssertEqTo(true);

                new And(10).Simplify().AssertEqTo(10);

                new And(true).Simplify().AssertEqTo(true);

                new And(false).Simplify().AssertEqTo(false);

                new And(10, 20, 30).Simplify().AssertEqTo(new And(10, 20, 30));

                new And(10, false, 20).Simplify().AssertEqTo(false);

                new And(10, true, 20).Simplify().AssertEqTo(new And(10, 20));

                new And(10, new And(20, 30), 40)
                    .Simplify()
                    .AssertEqTo(new And(10, 20, 30, 40));

                #endregion

                #region Or

                new Or(10).Simplify().AssertEqTo(10);

                new Or(true).Simplify().AssertEqTo(true);

                new Or(false).Simplify().AssertEqTo(false);

                new Or(10, 20, false).Simplify().AssertEqTo(new Or(10, 20));

                new Or(false, false).Simplify().AssertEqTo(false);

                new Or(10, true, 20, false).Simplify().AssertEqTo(true);

                new Or(10, false, 20).Simplify().AssertEqTo(new Or(10, 20));

                new Or(10, new Or(20, 30), 40)
                    .Simplify()
                    .AssertEqTo(new Or(10, 20, 30, 40));

                #endregion

                #region Function.Map

                new And(1, 2, 3, 4, 5, 6).Map(elt => elt * 2)
                    .AssertEqTo(new And(2, 4, 6, 8, 10, 12));

                new And(1, 2, 3, 4, 5, 6).Map(elt => (elt is Integer) && (elt as Integer).val % 2 == 0 ? elt : false)
                    .AssertEqTo(false);

                new Or(1, 2, 3).Map(elt => elt * 2)
                    .AssertEqTo(new Or(2, 4, 6));

                new Or(1, 2, 3, 4, 5, 6).Map(elt => (elt is Integer) && (elt as Integer).val % 2 == 0 ? elt : false)
                    .AssertEqTo(new Or(2, 4, 6));

                #endregion Function.Map

                #region Sum

                Assert((x + y).Equals(x * y) == false, "(x + y).Equals(x * y)");

                #endregion

                #region Has

                Assert(a.Has(elt => elt == a), "a.Has(elt => elt == a)");

                Assert(a.Has(elt => elt == b) == false, "a.Has(elt => elt == b) == false");

                Assert((a == b).Has(elt => elt == a), "Has - 3");

                Assert((a == b).Has(elt => elt == c) == false, "Has - 4");

                Assert(((a + b) ^ c).Has(elt => elt == a + b), "Has - 5");

                Assert(((a + b) ^ c).Has(elt => (elt is Power) && (elt as Power).exp == c), "Has - 6");

                Assert((x * (a + b + c)).Has(elt => (elt is Sum) && (elt as Sum).Has(b)), "Has - 7");

                Assert((x * (a + b + c)).Has(elt => (elt is Sum) && (elt as Sum).elts.Any(obj => obj == b)), "Has - 8");

                Assert((x * (a + b + c)).Has(elt => (elt is Product) && (elt as Product).elts.Any(obj => obj == b)) == false, "Has - 9");

                #endregion

                #region FreeOf

                Assert((a + b).FreeOf(b) == false, "(a + b).FreeOf(b)");
                Assert((a + b).FreeOf(c) == true, "(a + b).FreeOf(c)");
                Assert(((a + b) * c).FreeOf(a + b) == false, "((a + b) * c).FreeOf(a + b)");
                Assert((sin(x) + 2 * x).FreeOf(sin(x)) == false, "(sin(x) + 2 * x).FreeOf(sin(x))");
                Assert(((a + b + c) * d).FreeOf(a + b) == true, "((a + b + c) * d).FreeOf(a + b)");
                Assert(((y + 2 * x - y) / x).FreeOf(x) == true, "((y + 2 * x - y) / x).FreeOf(x)");
                Assert(((x * y) ^ 2).FreeOf(x * y) == true, "((x * y) ^ 2).FreeOf(x * y)");

                #endregion

                #region Denominator
                {
                    ((new Integer(2) / 3) * ((x * (x + 1)) / (x + 2)) * (y ^ z))
                        .Denominator()
                        .AssertEqTo(3 * (x + 2));
                }
                #endregion

                #region LogicalExpand

                new And(new Or(a, b), c)
                    .LogicalExpand()
                    .AssertEqTo(
                        new Or(
                            new And(a, c),
                            new And(b, c)));

                new And(a, new Or(b, c))
                    .LogicalExpand()
                    .AssertEqTo(new Or(new And(a, b), new And(a, c)));

                new And(a, new Or(b, c), d)
                    .LogicalExpand()
                    .AssertEqTo(
                        new Or(
                            new And(a, b, d),
                            new And(a, c, d)));

                new And(new Or(a == b, b == c), x == y)
                    .LogicalExpand()
                    .AssertEqTo(
                        new Or(
                            new And(a == b, x == y),
                            new And(b == c, x == y)));

                new And(
                    new Or(a == b, b == c),
                    new Or(c == d, d == a),
                    x == y)
                    .LogicalExpand()
                    .AssertEqTo(
                        new Or(
                            new And(a == b, c == d, x == y),
                            new And(a == b, d == a, x == y),
                            new And(b == c, c == d, x == y),
                            new And(b == c, d == a, x == y)));

                #endregion

                #region SimplifyEquation

                (2 * x == 0)
                    .SimplifyEquation()
                    .AssertEqTo(x == 0);

                (2 * x != 0)
                    .SimplifyEquation()
                    .AssertEqTo(x != 0);

                ((x ^ 2) == 0)
                    .SimplifyEquation()
                    .AssertEqTo(x == 0);

                #endregion

                #region SimplifyLogical

                new And(a, b, c, a)
                    .SimplifyLogical()
                    .AssertEqTo(new And(a, b, c));

                #endregion SimplifyLogical

                #region DegreeGpe

                {
                    var w = new Symbol("w");

                    Assert(
                        ((3 * w * x ^ 2) * (y ^ 3) * (z ^ 4)).DegreeGpe(new List<MathObject>() { x, z }) == 6,
                        "((3 * w * x ^ 2) * (y ^ 3) * (z ^ 4)).DegreeGpe(new List<MathObject>() { x, z })");

                    Assert(
                        ((a * x ^ 2) + b * x + c).DegreeGpe(new List<MathObject>() { x }) == 2,
                        "((a * x ^ 2) + b * x + c).DegreeGpe(new List<MathObject>() { x })");

                    Assert(
                        (a * (sin(x) ^ 2) + b * sin(x) + c).DegreeGpe(new List<MathObject>() { sin(x) }) == 2,
                        "(a * (sin(x) ^ 2) + b * sin(x) + c).DegreeGpe(new List<MathObject>() { sin(x) })");

                    Assert(
                        (2 * (x ^ 2) * y * (z ^ 3) + w * x * (z ^ 6)).DegreeGpe(new List<MathObject>() { x, z }) == 7,
                        "(2 * (x ^ 2) * y * (z ^ 3) + w * x * (z ^ 6)).DegreeGpe(new List<MathObject>() { x, z })");
                }

                #endregion

                #region CoefficientGpe

                AssertIsTrue((a * (x ^ 2) + b * x + c).CoefficientGpe(x, 2) == a);

                AssertIsTrue((3 * x * (y ^ 2) + 5 * (x ^ 2) * y + 7 * x + 9).CoefficientGpe(x, 1) == 3 * (y ^ 2) + 7);

                AssertIsTrue((3 * x * (y ^ 2) + 5 * (x ^ 2) * y + 7 * x + 9).CoefficientGpe(x, 3) == 0);

                Assert(
                    (3 * sin(x) * (x ^ 2) + 2 * x + 4).CoefficientGpe(x, 2) == null,
                    "(3 * sin(x) * (x ^ 2) + 2 * x + 4).CoefficientGpe(x, 2) == null");

                #endregion

                #region AlgebraicExpand

                AssertIsTrue(
                    ((x + 2) * (x + 3) * (x + 4)).AlgebraicExpand()
                    ==
                    24 + 26 * x + 9 * (x ^ 2) + (x ^ 3));

                AssertIsTrue(
                    ((x + y + z) ^ 3).AlgebraicExpand()
                    ==
                    (x ^ 3) + (y ^ 3) + (z ^ 3) +
                    3 * (x ^ 2) * y +
                    3 * (y ^ 2) * x +
                    3 * (x ^ 2) * z +
                    3 * (y ^ 2) * z +
                    3 * (z ^ 2) * x +
                    3 * (z ^ 2) * y +
                    6 * x * y * z);

                AssertIsTrue(
                    (((x + 1) ^ 2) + ((y + 1) ^ 2)).AlgebraicExpand()
                    ==
                    2 + 2 * x + (x ^ 2) + 2 * y + (y ^ 2));

                AssertIsTrue(
                    ((((x + 2) ^ 2) + 3) ^ 2).AlgebraicExpand()
                    ==
                    49 + 56 * x + 30 * (x ^ 2) + 8 * (x ^ 3) + (x ^ 4));

                AssertIsTrue(
                    sin(x * (y + z)).AlgebraicExpand()
                    ==
                    sin(x * y + x * z));

                AssertIsTrue(
                    (a * (b + c) == x * (y + z)).AlgebraicExpand()
                    ==
                    (a * b + a * c == x * y + x * z));

                #endregion

                #region IsolateVariable

                (x + y + z == 0).IsolateVariable(a).AssertEqTo(x + y + z == 0);

                // (x * a + x * b == 0).IsolateVariable(x).Disp();

                (x * (a + b) - x * a - x * b + x == c)
                    .IsolateVariable(x)
                    .AssertEqTo(x == c);

                new And(x == y, a == b)
                    .IsolateVariable(b)
                    .AssertEqTo(new And(x == y, b == a));

                new Or(new And(y == x, z == x), new And(b == x, c == x))
                    .IsolateVariable(x)
                    .AssertEqTo(new Or(new And(x == y, x == z), new And(x == b, x == c)));

                Assert((0 == x - y).IsolateVariableEq(x).Equals(x == y), "(0 == x - y).IsolateVariable(x).Equals(x == y)");

                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                (a * (x ^ 2) + b * x + c == 0)
                    .IsolateVariable(x)
                    .AssertEqTo(

                        new Or(

                            new And(
                                x == (-b + sqrt((b ^ 2) + -4 * a * c)) / (2 * a),
                                a != 0
                            ),

                            new And(
                                x == (-b - sqrt((b ^ 2) + -4 * a * c)) / (2 * a),
                                a != 0
                            ),

                            new And(x == -c / b, a == 0, b != 0),

                            new And(a == 0, b == 0, c == 0)
                        )
                    );

                (a * (x ^ 2) + c == 0)
                    .IsolateVariable(x)
                    .AssertEqTo(

                        new Or(
                            new And(
                                x == sqrt(-4 * a * c) / (2 * a),
                                a != 0
                            ),

                            new And(
                                x == -sqrt(-4 * a * c) / (2 * a),
                                a != 0
                            ),

                            new And(a == 0, c == 0)
                        )
                    );

                // a x^2 + b x + c == 0
                // a x^2 + c == - b x
                // (a x^2 + c) / x == - b

                ((a * (x ^ 2) + c) / x == -b)
                    .IsolateVariable(x)
                    .AssertEqTo(

                    new Or(

                            new And(
                                x == (-b + sqrt((b ^ 2) + -4 * a * c)) / (2 * a),
                                a != 0
                            ),

                            new And(
                                x == (-b - sqrt((b ^ 2) + -4 * a * c)) / (2 * a),
                                a != 0
                            ),

                            new And(x == -c / b, a == 0, b != 0),

                            new And(a == 0, b == 0, c == 0)
                        )
                );

                (sqrt(x + y) == z).IsolateVariable(x).AssertEqTo(x == (z ^ 2) - y);

                (a * b + a == c)
                    .IsolateVariable(a)
                    .AssertEqTo(a == c / (b + 1));

                (a * b + a * c == d)
                    .IsolateVariable(a)
                    .AssertEqTo(a == d / (b + c));

                (1 / sqrt(x) == y)
                    .IsolateVariable(x)
                    .AssertEqTo(x == (y ^ -2));

                (y == sqrt(x) / x)
                    .IsolateVariable(x)
                    .AssertEqTo(x == (y ^ -2));

                (-sqrt(x) + z * x == y)
                    .IsolateVariable(x)
                    .AssertEqTo(-sqrt(x) + z * x == y);

                (sqrt(a + x) - z * x == -y)
                    .IsolateVariable(x)
                    .AssertEqTo(sqrt(a + x) - z * x == -y);

                (sqrt(2 + x) * sqrt(3 + x) == y)
                    .IsolateVariable(x)
                    .AssertEqTo(sqrt(2 + x) * sqrt(3 + x) == y);

                #endregion

                #region EliminateVariable

                new And((x ^ 3) == (y ^ 5), z == x)
                .EliminateVariable(x)
                .AssertEqTo((z ^ 3) == (y ^ 5));

                new And((x ^ 3) == (y ^ 5), z == (x ^ 7))
                .EliminateVariable(x)
                .AssertEqTo(new And((x ^ 3) == (y ^ 5), z == (x ^ 7)));

                #endregion

            }

            #region EliminateVariable

            #region
            {

                var x = new Symbol("x");
                var y = new Symbol("y");
                var z = new Symbol("z");

                var eqs = new And()
                {
                    args =
                    {
                        (x ^ 2) - 4 == 0,
                        y + x == 0,
                        x + z == 10
                    }
                }
                .Simplify();

                var half = new Integer(1) / 2;

                Func<MathObject, MathObject> sqrt = obj => obj ^ half;

                ((x ^ 2) - 4 == 0)
                    .IsolateVariableEq(x)
                    .AssertEqTo(new Or(x == half * sqrt(16), x == -half * sqrt(16)));

                eqs.EliminateVariable(x)
                    .AssertEqTo(
                        new Or(
                            new And(
                                half * sqrt(16) + y == 0,
                                half * sqrt(16) + z == 10
                            ),
                            new And(
                                -half * sqrt(16) + y == 0,
                                -half * sqrt(16) + z == 10
                            )
                        )
                    );

            }
            #endregion

            #region
            {
                var a = new Symbol("a");
                var x = new Symbol("x");
                var y = new Symbol("y");
                var z = new Symbol("z");

                new Or(
                    new And(x == y, x == z, x == a),
                    new And(x == -y, x == z, x == a)
                    )
                    .EliminateVariable(x)
                    .AssertEqTo(
                        new Or(
                            new And(y == z, y == a),
                            new And(-y == z, -y == a)
                        )
                    )
                    .EliminateVariable(y)
                    .AssertEqTo(new Or(z == a, z == a));
            }
            #endregion

            {
                var x = new Symbol("x");
                var y = new Symbol("y");
                var z = new Symbol("z");

                new And(y != z, y == x, y == 10)
                    .EliminateVariable(y)
                    .AssertEqTo(new And(x != z, x == 10));
            }

            #region PSE Example 2.6

            {
                var sAC = new Symbol("sAC");
                var sAB = new Symbol("sAB");

                var vA = new Symbol("vA");
                var vB = new Symbol("vB");
                var vC = new Symbol("vC");

                var a = new Symbol("a");

                var tAC = new Symbol("tAC");
                var tAB = new Symbol("tAB");

                var eqs = new And(tAB == tAC / 2);

                eqs.args.AddRange(Kinematic(sAC, vA, vC, a, tAC));
                eqs.args.AddRange(Kinematic(sAB, vA, vB, a, tAB));

                var vals = new List<Equation>() { vA == 10, vC == 30, tAC == 10 };

                eqs
                    .EliminateVariables(tAB, sAC, vB, sAB)
                    .IsolateVariable(a)
                    .AssertEqTo(a == (vC - vA) / tAC)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(a == 2);

                eqs
                    .EliminateVariables(vB, a, tAB, sAC)
                    .AssertEqTo(sAB == tAC / 4 * (2 * vA + (vC - vA) / 2))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(sAB == 75);
            }

            #endregion

            #region PSE Example 2.7
            {
                // s =
                // u = 63
                // v =  0
                // a =
                // t =  2

                var s = new Symbol("s");
                var u = new Symbol("u");
                var v = new Symbol("v");
                var a = new Symbol("a");
                var t = new Symbol("t");

                var eqs = new And();

                eqs.args.AddRange(Kinematic(s, u, v, a, t));

                var vals = new List<Equation>() { u == 63, v == 0, t == 2.0 };

                eqs
                    .EliminateVariable(s)
                    .AssertEqTo(v == a * t + u)
                    .IsolateVariable(a)
                    .AssertEqTo(a == (v - u) / t)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(a == -31.5);

                eqs
                    .EliminateVariable(a)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(s == 63.0);
            }
            #endregion

            #region PSE Example 2.8
            {
                // car
                //
                // s1 =
                // u1 = 45
                // v1 = 45
                // a1 =  0
                // t1 =

                // officer
                //
                // s2 =
                // u2 =  0
                // v2 =
                // a2 =  3
                // t2

                var s1 = new Symbol("s1");
                var u1 = new Symbol("u1");
                var v1 = new Symbol("v1");
                var a1 = new Symbol("a1");
                var t1 = new Symbol("t1");

                var s2 = new Symbol("s2");
                var u2 = new Symbol("u2");
                var v2 = new Symbol("v2");
                var a2 = new Symbol("a2");
                var t2 = new Symbol("t2");

                var eqs = new And(
                    u1 == v1,
                    s1 == s2,
                    t2 == t1 - 1);

                eqs.args.AddRange(Kinematic(s1, u1, v1, a1, t1));
                eqs.args.AddRange(Kinematic(s2, u2, v2, a2, t2));

                var vals = new List<Equation>()
                {
                    v1 == 45.0,
                    u2 == 0,
                    a2 == 3
                };

                eqs
                    .EliminateVariables(s2, t1, a1, s1, v2, u1)
                    .IsolateVariable(t2)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(new Or(t2 == -0.96871942267131317, t2 == 30.968719422671313));
            }
            #endregion

            #region PSE Example 2.12

            {
                var yA = new Symbol("yA");
                var yB = new Symbol("yB");
                var yC = new Symbol("yC");
                var yD = new Symbol("yD");

                var tA = new Symbol("tA");
                var tB = new Symbol("tB");
                var tC = new Symbol("tC");
                var tD = new Symbol("tD");

                var vA = new Symbol("vA");
                var vB = new Symbol("vB");
                var vC = new Symbol("vC");
                var vD = new Symbol("vD");

                var a = new Symbol("a");

                var eqs = new And();

                eqs.args.AddRange(Kinematic(yA, yB, vA, vB, a, tA, tB));
                eqs.args.AddRange(Kinematic(yB, yC, vB, vC, a, tB, tC));
                eqs.args.AddRange(Kinematic(yC, yD, vC, vD, a, tC, tD));

                var vals = new List<Equation>()
                {
                    yA == 50,
                    yC == 50,
                    vA == 20,
                    vB == 0,
                    a == -9.8,
                    tA == 0,
                    tD == 5
                };

                // velocity and position at t = 5.00 s

                DoubleFloat.tolerance = 0.000000001;

                eqs
                    .EliminateVariables(tB, tC, vC, yB, yD)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(new Or(vD == -29.000000000000004, vD == -29.000000000000007));

                eqs
                    .EliminateVariables(tB, tC, vC, yB, vD)
                    .IsolateVariable(yD)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(new Or(yD == 27.499999999, yD == 27.499999999));

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE Example 4.3

            {
                var xA = new Symbol("xA");
                var xB = new Symbol("xB");
                var xC = new Symbol("xC");

                var yA = new Symbol("yA");
                var yB = new Symbol("yB");
                var yC = new Symbol("yC");

                var vxA = new Symbol("vxA");
                var vxB = new Symbol("vxB");
                var vxC = new Symbol("vxC");

                var vyA = new Symbol("vyA");
                var vyB = new Symbol("vyB");
                var vyC = new Symbol("vyC");

                var tAB = new Symbol("tAB");
                var tAC = new Symbol("tAC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");

                var eqs = new And(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    tAC == 2 * tAB,

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    vxC == vxA + ax * tAB,
                    vyC == vyA + ay * tAB,

                    xC == xA + vxA * tAC + ax * (tAC ^ 2) / 2,
                    yC == yA + vyA * tAC + ay * (tAC ^ 2) / 2

                    );

                var zeros = new List<Equation>() { xA == 0, yA == 0, ax == 0, vyB == 0 };

                var vals = new List<Equation>() { thA == (20).ToRadians(), vA == 11.0, ay == -9.8, Trig.Pi == Math.PI };

                eqs
                    .EliminateVariables(xB, yC, vxB, vxC, vyC, yB, tAC, vxA, vyA, tAB)
                    .SubstituteEqLs(zeros)
                    .AssertEqTo(xC == -2 * cos(thA) * sin(thA) * (vA ^ 2) / ay)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(xC == 7.9364592624562507);

                eqs
                    .EliminateVariables(xB, yC, vxB, vxC, vyC, xC, vxA, tAC, vyA, tAB)
                    .SubstituteEqLs(zeros)
                    .AssertEqTo(yB == -(sin(thA) ^ 2) * (vA ^ 2) / (2 * ay))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(yB == 0.72215873425009314);
            }

            #endregion

            #region PSE 5E Example 4.5

            {
                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var xB = new Symbol("xB");
                var xC = new Symbol("xC");

                var yA = new Symbol("yA");
                var yB = new Symbol("yB");
                var yC = new Symbol("yC");

                var vxA = new Symbol("vxA");
                var vxB = new Symbol("vxB");
                var vxC = new Symbol("vxC");

                var vyA = new Symbol("vyA");
                var vyB = new Symbol("vyB");
                var vyC = new Symbol("vyC");

                var tAB = new Symbol("tAB");
                var tAC = new Symbol("tAC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");

                var vC = new Symbol("vC");

                var eqs = new And(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    // tAC == 2 * tAB,

                    // vxB == vxA + ax * tAB,
                    // vyB == vyA + ay * tAB,

                    // xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    // yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    vxC == vxA + ax * tAC,
                    vyC == vyA + ay * tAC,

                    // xC == xA + vxA * tAC + ax * (tAC ^ 2) / 2,
                    yC == yA + vyA * tAC + ay * (tAC ^ 2) / 2,

                    vC == sqrt((vxC ^ 2) + (vyC ^ 2)),

                    ay != 0
                );

                var zeros = new List<Equation>() { ax == 0, yC == 0 };
                var vals = new List<Equation>() { yA == 45, vA == 20, thA == (30).ToRadians(), ay == -9.8, Trig.Pi == Math.PI };

                DoubleFloat.tolerance = 0.00001;

                eqs
                    .EliminateVariables(vC, vxA, vxC, vyC, vyA)
                    .IsolateVariable(tAC)
                    .LogicalExpand().SimplifyEquation().SimplifyLogical()
                    .CheckVariable(ay)
                    .AssertEqTo(
                        new Or(
                            new And(
                                tAC == -(sin(thA) * vA + sqrt((sin(thA) ^ 2) * (vA ^ 2) + 2 * ay * (yC - yA))) / ay,
                                ay != 0),
                            new And(
                                tAC == -(sin(thA) * vA - sqrt((sin(thA) ^ 2) * (vA ^ 2) + 2 * ay * (yC - yA))) / ay,
                                ay != 0)))
                    .SubstituteEqLs(zeros)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(new Or(tAC == 4.2180489012229376, tAC == -2.1772325746923267));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxC, vxA, vyA, vyC, tAC)
                    .SimplifyEquation().SimplifyLogical()
                    .CheckVariable(ay)
                    .AssertEqTo(
                        new Or(
                            new And(
                                ay != 0,
                                vC == sqrt((cos(thA) ^ 2) * (vA ^ 2) + ((sin(thA) * vA - (sin(thA) * vA + sqrt((sin(thA) ^ 2) * (vA ^ 2) + -2 * ay * yA))) ^ 2))),
                            new And(
                                ay != 0,
                                vC == sqrt((cos(thA) ^ 2) * (vA ^ 2) + ((sin(thA) * vA - (sin(thA) * vA - sqrt((sin(thA) ^ 2) * (vA ^ 2) + -2 * ay * yA))) ^ 2)))))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(new Or(vC == 35.805027579936315, vC == 35.805027579936322));

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E Example 4.6

            {
                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var xB = new Symbol("xB");

                var yA = new Symbol("yA");
                var yB = new Symbol("yB");

                var vxA = new Symbol("vxA");
                var vxB = new Symbol("vxB");

                var vyA = new Symbol("vyA");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var eqs = new And(

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    vxA != 0,

                    ay != 0
                );

                var vals = new List<Equation>() { xA == 0, yA == 100, vxA == 40, vyA == 0, yB == 0, ax == 0, ay == -9.8, Trig.Pi == Math.PI };

                var zeros = vals.Where(eq => eq.b == 0).ToList();

                DoubleFloat.tolerance = 0.00001;

                eqs
                    .EliminateVariables(vxB, vyB, tAB)
                    .IsolateVariable(xB)
                    .LogicalExpand().SimplifyEquation()
                    .CheckVariable(ay)
                    .CheckVariable(vxA).SimplifyLogical()
                    .SubstituteEq(ax == 0)
                    .AssertEqTo(
                        new Or(
                            new And(
                                vxA != 0,
                                xB == -1 * (ay ^ -1) * (vxA ^ 2) * (-1 * (-1 * (vxA ^ -1) * vyA + ay * (vxA ^ -2) * xA) + sqrt(((-1 * (vxA ^ -1) * vyA + ay * (vxA ^ -2) * xA) ^ 2) + 2 * ay * (vxA ^ -2) * ((vxA ^ -1) * vyA * xA - ay / 2 * (vxA ^ -2) * (xA ^ 2) + -1 * yA + yB))),
                                ay * (vxA ^ -2) != 0,
                                ay != 0),
                            new And(
                                vxA != 0,
                                xB == -1 * (ay ^ -1) * (vxA ^ 2) * (-1 * (-1 * (vxA ^ -1) * vyA + ay * (vxA ^ -2) * xA) + -1 * sqrt(((-1 * (vxA ^ -1) * vyA + ay * (vxA ^ -2) * xA) ^ 2) + 2 * ay * (vxA ^ -2) * ((vxA ^ -1) * vyA * xA - ay / 2 * (vxA ^ -2) * (xA ^ 2) + -1 * yA + yB))),
                                ay * (vxA ^ -2) != 0,
                                ay != 0)))
                    .SubstituteEqLs(zeros)
                    .AssertEqTo(
                        new Or(
                            new And(
                                vxA != 0,
                                xB == -1 / ay * (vxA ^ 2) * sqrt(-2 * ay * (vxA ^ -2) * yA),
                                ay / (vxA ^ 2) != 0,
                                ay != 0),
                            new And(
                                vxA != 0,
                                xB == 1 / ay * (vxA ^ 2) * sqrt(-2 * ay * (vxA ^ -2) * yA),
                                ay / (vxA ^ 2) != 0,
                                ay != 0)))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(new Or(xB == 180.70158058105022, xB == -180.70158058105022));

                eqs
                    .EliminateVariables(vxB, xB, tAB)
                    .IsolateVariable(vyB)
                    .LogicalExpand().SimplifyEquation()
                    .CheckVariable(ay)
                    .AssertEqTo(
                        new Or(
                            new And(
                                vyB == -1 * ay * sqrt(2 * (ay ^ -1) * ((ay ^ -1) / 2 * (vyA ^ 2) + -1 * yA + yB)),
                    // (ay ^ -1) != 0,
                                vxA != 0,
                                ay != 0),
                            new And(
                                vyB == ay * sqrt(2 * (ay ^ -1) * ((ay ^ -1) / 2 * (vyA ^ 2) + -1 * yA + yB)),
                    // (ay ^ -1) != 0,
                                vxA != 0,
                                ay != 0)))
                    .SubstituteEqLs(zeros)
                    .AssertEqTo(
                        new Or(
                          new And(
                              vyB == -ay * sqrt(-2 / ay * yA),
                    // 1 / ay != 0,
                              vxA != 0,
                              ay != 0),
                          new And(
                              vyB == ay * sqrt(-2 / ay * yA),
                    // 1 / ay != 0,
                              vxA != 0,
                              ay != 0)))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(new Or(vyB == 44.271887242357309, vyB == -44.271887242357309));

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E Example 4.7

            {
                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var th = new Symbol("th");
                var d = new Symbol("d");

                var eqs = new And(

                    cos(th) == (xB - xA) / d,
                    sin(th) == (yA - yB) / d,

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    yB != 0,

                    ay != 0
                );

                var vals = new List<Equation>() { xA == 0, yA == 0, vxA == 25, vyA == 0, ax == 0, ay == -9.8, th == (35).ToRadians(), Trig.Pi == Math.PI };

                var zeros = vals.Where(eq => eq.b == 0).ToList();

                DoubleFloat.tolerance = 0.00001;

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, vyB, d, yB, tAB)
                    .IsolateVariable(xB)
                    .LogicalExpand()
                    .CheckVariable(ay)
                    .SimplifyEquation()
                    .AssertEqTo(
                        new Or(
                            new And(
                                xB == -(sin(th) / cos(th) + sqrt((cos(th) ^ -2) * (sin(th) ^ 2))) * (vxA ^ 2) / ay,
                                ay / (vxA ^ 2) != 0,
                                sin(th) / cos(th) * xB != 0,
                                ay != 0),
                            new And(
                                xB == -(sin(th) / cos(th) - sqrt((cos(th) ^ -2) * (sin(th) ^ 2))) * (vxA ^ 2) / ay,
                                ay / (vxA ^ 2) != 0,
                                sin(th) / cos(th) * xB != 0,
                                ay != 0)))
                    .SubstituteEqLs(vals)
                    .SimplifyEquation()
                    .AssertEqTo(
                        new Or(
                            new And(
                                xB == 89.312185996136435,
                                xB != 0),
                            new And(
                                xB == 7.0805039835788038E-15,
                                xB != 0)));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, vyB, d, xB, tAB)
                    .IsolateVariable(yB)
                    .LogicalExpand()
                    .CheckVariable(yB)
                    .AssertEqTo(
                        new And(
                            yB == 2 * (sin(th) ^ 2) * (vxA ^ 2) / ay / (cos(th) ^ 2),
                            -ay * (cos(th) ^ 2) / (sin(th) ^ 2) / (vxA ^ 2) / 2 != 0,
                            yB != 0,
                            ay != 0))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(
                        new And(
                            yB == -62.537065888482395,
                            yB != 0));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, vyB, d, xB, yB)
                    .IsolateVariable(tAB)
                    .LogicalExpand().CheckVariable(ay).SimplifyEquation().SimplifyLogical()
                    .AssertEqTo(
                        new Or(
                            new And(
                                tAB == -(sin(th) * vxA / cos(th) + sqrt((sin(th) ^ 2) * (vxA ^ 2) / (cos(th) ^ 2))) / ay,
                                ay != 0,
                                sin(th) * tAB * vxA / cos(th) != 0),
                            new And(
                                tAB == -(sin(th) * vxA / cos(th) - sqrt((sin(th) ^ 2) * (vxA ^ 2) / (cos(th) ^ 2))) / ay,
                                ay != 0,
                                sin(th) * tAB * vxA / cos(th) != 0)))
                    .SubstituteEqLs(vals)
                    .CheckVariable(tAB).SimplifyEquation()
                    .AssertEqTo(
                        new And(
                            tAB == 3.5724874398454571,
                            tAB != 0));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, d, tAB, xB, yB)
                    .IsolateVariable(vyB)
                    .LogicalExpand()
                    .CheckVariable(ay)
                    .SimplifyEquation()
                    .CheckVariable(ay)
                    .AssertEqTo(
                        new Or(
                            new And(
                                vyB == -ay * (sin(th) * vxA / (ay * cos(th)) + sqrt((sin(th) ^ 2) * (vxA ^ 2) / ((ay ^ 2) * (cos(th) ^ 2)))),
                                sin(th) * vxA * vyB / (ay * cos(th)) != 0,
                                ay != 0),
                            new And(
                                vyB == -ay * (sin(th) * vxA / (ay * cos(th)) - sqrt((sin(th) ^ 2) * (vxA ^ 2) / ((ay ^ 2) * (cos(th) ^ 2)))),
                                sin(th) * vxA * vyB / (ay * cos(th)) != 0,
                                ay != 0)))
                    .SubstituteEqLs(vals)
                    .CheckVariable(vyB)
                    .SimplifyEquation()
                    .CheckVariable(vyB)
                    .AssertEqTo(
                        new And(
                            vyB == -35.010376910485483,
                            vyB != 0));

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E P4.9

            {
                // In a local bar, a customer slides an empty beer mug
                // down the counter for a refill. The bartender is momentarily
                // distracted and does not see the mug, which slides
                // off the counter and strikes the floor 1.40 m from the
                // base of the counter. If the height of the counter is
                // 0.860 m, (a) with what velocity did the mug leave the
                // counter and (b) what was the direction of the mug’s
                // velocity just before it hit the floor?

                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var thB = new Symbol("thB");
                var vB = new Symbol("vB");

                var eqs = new And(

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    tan(thB) == vyB / vxB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    xB != 0
                );

                var vals = new List<Equation>() { xA == 0, yA == 0.86, /* vxA */ vyA == 0, xB == 1.4, yB == 0, /* vxB vyB vB thB */ /* tAB */ ax == 0, ay == -9.8 };

                var zeros = vals.Where(eq => eq.b == 0).ToList();

                DoubleFloat.tolerance = 0.00001;

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(thB, vxB, vyB, tAB)
                    .IsolateVariable(vxA)
                    .LogicalExpand()
                    .AssertEqTo(
                        new Or(
                            new And(
                                vxA == ay * (xB ^ 2) / yA / 4 * sqrt(-8 / ay * (xB ^ -2) * yA),
                                2 / ay * (xB ^ -2) * yA != 0,
                                xB != 0),
                            new And(
                                vxA == -ay * (xB ^ 2) / yA / 4 * sqrt(-8 / ay * (xB ^ -2) * yA),
                                2 / ay * (xB ^ -2) * yA != 0,
                                xB != 0)))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(new Or(vxA == -3.3417722634053204, vxA == 3.3417722634053204));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, vyB, tAB, vxA)
                    .LogicalExpand()
                    .CheckVariable(xB)
                    .SimplifyLogical()
                    .IsolateVariable(thB)
                    .AssertEqTo(
                        new And(
                            -tan(thB) / ay != 0,
                            thB == new Atan(-2 * yA / xB),
                            xB != 0))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(
                        new And(
                            0.1020408163265306 * tan(thB) != 0,
                            thB == -0.88760488150470185));

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region SumDifferenceFormulaFunc

            // sin(u) cos(v) - cos(u) sin(v) -> sin(u - v)

            Func<MathObject, MathObject> SumDifferenceFormulaFunc = elt =>
            {
                if (elt is Sum)
                {
                    var items = new List<MathObject>();

                    foreach (var item in (elt as Sum).elts)
                    {
                        if (
                            item is Product &&
                            (item as Product).elts[0] == -1 &&
                            (item as Product).elts[1] is Cos &&
                            (item as Product).elts[2] is Sin
                            )
                        {
                            var u_ = ((item as Product).elts[1] as Cos).args[0];
                            var v_ = ((item as Product).elts[2] as Sin).args[0];

                            Func<MathObject, bool> match = obj =>
                                obj is Product &&
                                (obj as Product).elts[0] is Cos &&
                                (obj as Product).elts[1] is Sin &&

                                ((obj as Product).elts[1] as Sin).args[0] == u_ &&
                                ((obj as Product).elts[0] as Cos).args[0] == v_;

                            if (items.Any(obj => match(obj)))
                            {
                                items = items.Where(obj => match(obj) == false).ToList();

                                items.Add(sin(u_ - v_));
                            }
                            else items.Add(item);
                        }
                        else items.Add(item);
                    }

                    return new Sum() { elts = items }.Simplify();
                }

                return elt;
            };

            #endregion

            {
                var u = new Symbol("u");
                var v = new Symbol("v");

                (sin(u) * cos(v) - cos(u) * sin(v))
                    .DeepSelect(SumDifferenceFormulaFunc)
                    .AssertEqTo(sin(u - v));
            }

            #region DoubleAngleFormulaFunc

            // sin(u) cos(u) -> sin(2 u) / 2

            Func<MathObject, MathObject> DoubleAngleFormulaFunc =
                    elt =>
                    {
                        if (elt is Product)
                        {
                            var items = new List<MathObject>();

                            foreach (var item in (elt as Product).elts)
                            {
                                if (item is Sin)
                                {
                                    var sym = (item as Sin).args.First();

                                    if (items.Any(obj => (obj is Cos) && (obj as Cos).args.First() == sym))
                                    {
                                        items = items.Where(obj => ((obj is Cos) && (obj as Cos).args.First() == sym) == false).ToList();

                                        items.Add(sin(2 * sym) / 2);
                                    }
                                    else items.Add(item);
                                }

                                else if (item is Cos)
                                {
                                    var sym = (item as Cos).args.First();

                                    if (items.Any(obj => (obj is Sin) && (obj as Sin).args.First() == sym))
                                    {
                                        items = items.Where(obj => ((obj is Sin) && (obj as Sin).args.First() == sym) == false).ToList();

                                        items.Add(sin(2 * sym) / 2);
                                    }
                                    else items.Add(item);
                                }

                                else items.Add(item);

                            }
                            return new Product() { elts = items }.Simplify();
                        }
                        return elt;
                    };

            #endregion

            #region PSE 5E P4.11

            {
                // One strategy in a snowball fight is to throw a first snowball
                // at a high angle over level ground. While your opponent is watching
                // the first one, you throw a second one at a low angle and timed
                // to arrive at your opponent before or at the same time as the first one.

                // Assume both snowballs are thrown with a speed of 25.0 m/s.

                // The first one is thrown at an angle of 70.0° with respect to the horizontal.

                // (a) At what angle should the second (lowangle)
                // snowball be thrown if it is to land at the same
                // point as the first?

                // (b) How many seconds later should the second snowball
                // be thrown if it is to land at the same time as the first?

                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");

                var thA = new Symbol("thA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");
                var tAC = new Symbol("tAC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var eqs = new And(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2
                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>() { xA == 0, yA == 0, /* vxA vyA */ vA == 25.0, /* thA == 70.0, */ /* xB == 20.497, */ /* yB */ /* vxB */ vyB == 0, /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    {
                        // thA = ... || thA = ...

                        var expr = eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(yB, vxA, vyA, vxB, tAB)
                            .DeepSelect(DoubleAngleFormulaFunc)
                            .IsolateVariable(thA);

                        // th_delta = ...

                        var th1 = ((expr as Or).args[0] as Equation).b;
                        var th2 = ((expr as Or).args[1] as Equation).b;

                        var th_delta = new Symbol("th_delta");

                        eqs
                            .Add(th_delta == (th1 - th2).AlgebraicExpand())
                            .SubstituteEqLs(zeros)

                            .EliminateVariables(yB, vxA, vyA, vxB, tAB)

                            .DeepSelect(DoubleAngleFormulaFunc)
                            .EliminateVariable(xB)

                            .AssertEqTo(th_delta == asin(sin(2 * thA)) - Pi / 2)

                            .SubstituteEq(thA == (70).ToRadians())
                            .SubstituteEq(Pi == Math.PI)

                            .AssertEqTo(th_delta == -0.87266462599716454)
                            ;
                    }

                    {
                        // tAB = ...

                        var tAB_eq = eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(yB, vxA, vyA, vxB, xB)
                            .IsolateVariable(tAB);

                        new And(
                            new Or(thA == (20).ToRadians(), thA == (70).ToRadians()),
                            tAB_eq,
                            tAC == tAB * 2)
                            .LogicalExpand()
                            .EliminateVariables(thA, tAB)

                            .AssertEqTo(new Or(
                                tAC == -2 * sin(Pi / 9) * vA / ay,
                                tAC == -2 * sin(7 * Pi / 18) * vA / ay))

                            .SubstituteEqLs(vals)
                            .AssertEqTo(
                                new Or(
                                    tAC == 1.7450007312534115,
                                    tAC == 4.794350106050552));
                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E P4.13

            {
                // An artillery shell is fired with an initial velocity of
                // 300 m/s at 55.0° above the horizontal. It explodes on a
                // mountainside 42.0 s after firing. What are the x and y
                // coordinates of the shell where it explodes, relative to its
                // firing point?

                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var eqs = new And(
                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2
                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>() { xA == 0, yA == 0, /* vxA vyA */ vA == 300.0, thA == (55).ToRadians(), /* xB yB vxB vyB */ tAB == 42, ax == 0, ay == -9.8, Pi == Math.PI };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariable(vxA)
                            .EliminateVariable(vyA)

                            .AssertEqTo(
                                new And(
                                    vxB == cos(thA) * vA,
                                    vyB == ay * tAB + sin(thA) * vA,
                                    xB == cos(thA) * tAB * vA,
                                    yB == ay * (tAB ^ 2) / 2 + sin(thA) * tAB * vA))

                            .SubstituteEqLs(vals)

                            .AssertEqTo(
                                new And(
                                    vxB == 172.07293090531385,
                                    vyB == -165.85438671330249,
                                    xB == 7227.0630980231817,
                                    yB == 1677.7157580412968))

                            ;
                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E P4.15

            {
                // A projectile is fired in such a way that its horizontal
                // range is equal to three times its maximum height.
                //
                // What is the angle of projection?
                // Give your answer to three significant figures.

                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var xC = new Symbol("xC");
                var yC = new Symbol("yC");

                var vxC = new Symbol("vxC");
                var vyC = new Symbol("vyC");

                var tAB = new Symbol("tAB");
                var tBC = new Symbol("tBC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var eqs = new And(

                    xC - xA == 3 * yB,

                    tAB == tBC,

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    vxC == vxB + ax * tBC,
                    vyC == vyB + ay * tBC,

                    xC == xB + vxB * tBC + ax * (tBC ^ 2) / 2,
                    yC == yB + vyB * tBC + ay * (tBC ^ 2) / 2

                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        xA == 0, yA == 0, /* vxA vyA vA thA */ /* xB yB vxB */ vyB == 0, /* tAB tBC */
                        /* xC */ yC == 0,

                        ax == 0, ay == -9.8, Pi == Math.PI
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    eqs
                        .SubstituteEqLs(zeros)
                        .EliminateVariables(xC, tAB, vxA, vyA, vxB, xB, yB, vxC, vyC, tBC)
                        .IsolateVariable(thA)
                        .AssertEqTo(thA == new Atan(new Integer(4) / 3));
                }

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E P4.17

            {
                // A cannon with a muzzle speed of 1000 m/s is used to
                // start an avalanche on a mountain slope. The target is
                // 2000 m from the cannon horizontally and 800 m above
                // the cannon.
                //
                // At what angle, above the horizontal, should the cannon be fired?

                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var xC = new Symbol("xC");
                var yC = new Symbol("yC");

                var vxC = new Symbol("vxC");
                var vyC = new Symbol("vyC");

                var tAB = new Symbol("tAB");
                var tBC = new Symbol("tBC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var phi = new Symbol("phi");

                var eqs = new And(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2
                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        xA ==    0, yA ==   0, /* vxA vyA */ vA == 1000, /* thA */
                        xB == 2000, yB == 800.0, /* vxB vyB */
                        /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(vxA, vyA, vxB, vyB, tAB)

                            .MultiplyBothSidesBy(cos(thA) ^ 2).AlgebraicExpand()
                            .Substitute(cos(thA) ^ 2, (1 + cos(2 * thA)) / 2)
                            .DeepSelect(DoubleAngleFormulaFunc).AlgebraicExpand()
                            .AddToBothSides(-sin(2 * thA) * xB / 2)
                            .AddToBothSides(-yB / 2)
                            .MultiplyBothSidesBy(2 / xB).AlgebraicExpand()

                            // yB / xB = tan(phi)
                            // yB / xB = sin(phi) / cos(phi)

                            // phi = atan(yB / xB)

                            .Substitute(cos(2 * thA) * yB / xB, cos(2 * thA) * sin(phi) / cos(phi))
                            .MultiplyBothSidesBy(cos(phi)).AlgebraicExpand()
                            .DeepSelect(SumDifferenceFormulaFunc)
                            .IsolateVariable(thA)

                            .Substitute(phi, new Atan(yB / xB).Simplify())

                            .AssertEqTo(
                                new Or(
                                    thA == -(asin(ay * cos(atan(yB / xB)) * (vA ^ -2) * xB + -1 * cos(atan(yB / xB)) * yB / xB) - atan(yB / xB)) / 2,
                                    thA == -(-asin(ay * cos(atan(yB / xB)) * (vA ^ -2) * xB - cos(atan(yB / xB)) * yB / xB) - atan(yB / xB) + Pi) / 2))

                            .SubstituteEqLs(vals)

                            .AssertEqTo(
                                new Or(
                                    thA == 0.39034573609628065,
                                    thA == -1.5806356857788124))
                            ;
                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion

            #endregion

            #region PSE 5E Example 4.3
            {
                var thA = new Symbol("thA"); // angle at point A
                var vA = new Symbol("vA"); // velocity at point A

                var g = new Symbol("g"); // magnitude of gravity

                var _g = new Point(0, -g); // gravity vector

                var objA =
                    new Obj()
                    {
                        position = new Point(0, 0),
                        velocity = Point.FromAngle(thA, vA),
                        acceleration = _g,
                        time = new Integer(0)
                    };

                var objB =
                    new Obj()
                    {
                        velocity = new Point(objA.velocity.x, 0),
                        acceleration = _g
                    };

                var timeB = Calc.Time(objA, objB);
                var timeC = timeB * 2;

                objB = objA.AtTime(timeB);
                var objC = objA.AtTime(timeC);

                //Console.WriteLine("How far does he dump in the horizontal direction?");

                AssertIsTrue(objC.position.x == 2 * Trig.Cos(thA) * Trig.Sin(thA) * (vA ^ 2) / g);

                //Console.WriteLine("What is the maximum height reached?");

                AssertIsTrue(objB.position.y == (Trig.Sin(thA) ^ 2) * (vA ^ 2) / 2 / g);

                // Console.WriteLine("Distance jumped: ");

                var deg = 3.14159 / 180.0;

                AssertIsTrue(
                    objC.position.x
                    // .Substitute(thA, Trig.ToRadians(20))
                    .Substitute(thA, 20 * deg)
                    .Substitute(g, 9.8)
                    .Substitute(Trig.Pi, 3.14159)
                    .Substitute(vA, 11)
                    ==
                    7.9364536850196412);

                //Console.WriteLine("Maximum height reached: ");

                AssertIsTrue(
                    objB.position.y
                    .Substitute(g, 9.8)
                    .Substitute(thA, Trig.ToRadians(20))
                    .Substitute(Trig.Pi, 3.14159)
                    .Substitute(vA, 11)
                    ==
                    0.72215756424454336);
            }
            #endregion

            #region PSE 5E EXAMPLE 4.5
            {
                // A stone is thrown from the top of a building upward at an
                // angle of 30.0° to the horizontal and with an initial speed of
                // 20.0 m/s, as shown in Figure 4.12. If the height of the building
                // is 45.0 m, (a) how long is it before the stone hits the ground?
                // (b) What is the speed of the stone just before it strikes the
                // ground?

                var thA = new Symbol("thA"); // angle at point A
                var vA = new Symbol("vA"); // velocity at point A

                var g = new Symbol("g"); // magnitude of gravity

                var _g = new Point(0, -g); // gravity vector

                var objA = new Obj()
                {
                    position = new Point(0, 0),
                    velocity = Point.FromAngle(thA, vA),
                    acceleration = _g,
                    time = new Integer(0)
                };

                var objB = new Obj()
                {
                    velocity = new Point(objA.velocity.x, 0),
                    acceleration = _g,
                };

                var timeB = Calc.Time(objA, objB);

                objB = objA.AtTime(timeB);

                var timeC = timeB * 2;

                var objC = objA.AtTime(timeC);

                var yD = new Symbol("yD");

                var objD = new Obj()
                {
                    position = new Point(null, yD),
                    velocity = new Point(objA.velocity.x, null),
                    acceleration = _g
                };

                var timeAD = Calc.Time(objA, objD, 1);

                objD = objA.AtTime(timeAD);

                // "How long is it before the stone hits the ground?".Disp();

                // "Symbolic answer:".Disp();

                AssertIsTrue(
                    timeAD
                    ==
                    -1 * (g ^ -1) * (-1 * Trig.Sin(thA) * vA + -1 * (((Trig.Sin(thA) ^ 2) * (vA ^ 2) + -2 * g * yD) ^ (new Integer(1) / 2))));

                // "Numeric answer:".Disp();

                AssertEqual(
                    (DoubleFloat)
                    timeAD
                        .Substitute(g, 9.8)
                        .Substitute(thA, (30).ToRadians())
                        .Substitute(Trig.Pi, 3.14159)
                        .Substitute(vA, 20)
                        .Substitute(yD, -45),
                    new DoubleFloat(4.21804787012706),
                    0.0001);

                // "What is the speed of the stone just before it strikes the ground?".Disp();

                // "Symbolic answer:".Disp();

                AssertIsTrue(
                    objD.velocity.Norm()
                    ==
                    (((Trig.Cos(thA) ^ 2) * (vA ^ 2) + (Trig.Sin(thA) ^ 2) * (vA ^ 2) + -2 * g * yD) ^ (new Integer(1) / 2)));

                // "Numeric answer:".Disp();

                AssertEqual(
                    (DoubleFloat)
                    objD.velocity.Norm()
                        .Substitute(g, 9.8)
                        .Substitute(thA, (30).ToRadians())
                        .Substitute(Trig.Pi, 3.14159)
                        .Substitute(vA, 20)
                        .Substitute(yD, -45),
                    new DoubleFloat(35.805027579936315),
                    0.1);
            }
            #endregion

            #region PSE 5E EXAMPLE 4.6
            {
                // An Alaskan rescue plane drops a package of emergency rations
                // to a stranded party of explorers, as shown in Figure
                // 4.13. If the plane is traveling horizontally at 40.0 m/s and is
                // 100 m above the ground, where does the package strike the
                // ground relative to the point at which it was released?

                var xA = new Symbol("xA");      // position.x at point A

                var yA = new Symbol("yA");      // position.y at point A

                var thA = new Symbol("thA");    // angle at point A

                var vA = new Symbol("vA");      // velocity at point A

                var g = new Symbol("g");        // magnitude of gravity

                var _g = new Point(0, -g);      // gravity vector

                var objA = new Obj()            // obj at the initial position
                {
                    position = new Point(xA, yA),
                    velocity = Point.FromAngle(thA, vA),
                    acceleration = _g,
                    time = 0
                };

                var objB = new Obj()            // obj at the final position
                {
                    position = new Point(null, 0),
                    velocity = new Point(objA.velocity.x, null),
                    acceleration = _g
                };

                var timeB = Calc.Time(objA, objB, 1);

                objB = objA.AtTime(timeB);

                //"Where does the package strike the ground relative to the point at which it was released?".Disp(); "".Disp();

                //"symbolic:".Disp();

                //objB.position.x.Disp(); "".Disp();

                AssertIsTrue(
                    objB.position.x
                    ==
                    xA - cos(thA) / g * vA * (-sin(thA) * vA - (((sin(thA) ^ 2) * (vA ^ 2) + 2 * g * yA) ^ new Integer(1) / 2)));

                //"numeric:".Disp();

                //objB.position.x
                //    .Substitute(xA, 0)
                //    .Substitute(yA, 100)
                //    .Substitute(vA, 40)
                //    .Substitute(thA, 0.0)
                //    .Substitute(g, 9.8)
                //    .Disp();

                AssertEqual(
                    objB.position.x
                        .Substitute(xA, 0)
                        .Substitute(yA, 100)
                        .Substitute(vA, 40)
                        .Substitute(thA, 0.0)
                        .Substitute(g, 9.8),
                    180.70158058105025);

                //"".Disp();

                //("What are the horizontal and vertical components " +
                // "of the velocity of the package just before it hits the ground?").Disp(); "".Disp();

                //"symbolic velocity.x:".Disp();

                //objB.velocity.x.Disp(); "".Disp();

                AssertIsTrue(objB.velocity.x == cos(thA) * vA);

                //"symbolic velocity.y:".Disp();

                //objB.velocity.y.Disp(); "".Disp();

                AssertIsTrue(
                    objB.velocity.y
                    ==
                    -1 * (((sin(thA) ^ 2) * (vA ^ 2) + 2 * g * yA) ^ (new Integer(1) / 2)));

                //"numeric velocity.x:".Disp();

                //objB.velocity.x
                //    .Substitute(xA, 0)
                //    .Substitute(yA, 100)
                //    .Substitute(vA, 40)
                //    .Substitute(thA, 0.0)
                //    .Substitute(g, 9.8)
                //    .Disp(); "".Disp();

                AssertEqual(
                    objB.velocity.x
                        .Substitute(xA, 0)
                        .Substitute(yA, 100)
                        .Substitute(vA, 40)
                        .Substitute(thA, 0.0)
                        .Substitute(g, 9.8),
                    40);

                //"numeric velocity.y:".Disp();

                //objB.velocity.y
                //    .Substitute(xA, 0)
                //    .Substitute(yA, 100)
                //    .Substitute(vA, 40)
                //    .Substitute(thA, 0.0)
                //    .Substitute(g, 9.8)
                //    .Disp(); "".Disp();

                AssertEqual(
                    objB.velocity.y
                        .Substitute(xA, 0)
                        .Substitute(yA, 100)
                        .Substitute(vA, 40)
                        .Substitute(thA, 0.0)
                        .Substitute(g, 9.8),
                    -44.271887242357316);
            }
            #endregion

            #region PSE 5E EXAMPLE 4.7

            {
                // A ski jumper leaves the ski track moving in the horizontal
                // direction with a speed of 25.0 m/s, as shown in Figure 4.14.
                // The landing incline below him falls off with a slope of 35.0°.
                // Where does he land on the incline?

                var thA = new Symbol("thA");    // angle at point A

                var vA = new Symbol("vA");      // velocity at point A

                var g = new Symbol("g");        // magnitude of gravity

                var _g = new Point(0, -g);      // gravity vector

                var th = new Symbol("th");      // angle of incline

                var objA = new Obj()
                {
                    position = new Point(0, 0),
                    velocity = Point.FromAngle(thA, vA),
                    acceleration = _g,
                    time = 0
                };

                Func<MathObject, MathObject> numeric = obj =>
                    obj
                        .Substitute(vA, 25)
                        .Substitute(thA, 0.0)
                        .Substitute(th, (-35).ToRadians())
                        .Substitute(Trig.Pi, 3.14159)
                        .Substitute(g, 9.8);

                var intersection = objA.ProjectileInclineIntersection(th);

                Action nl = () => "".Disp();

                // "Where does he land on the incline?".Disp(); nl();

                // "x position (symbolic):".Disp();

                // intersection.x.Disp(); nl();

                AssertIsTrue(
                    intersection.x
                    ==
                    -2 * (cos(th) ^ -1) * (cos(thA) ^ 2) * (g ^ -1) * (sin(th) + -1 * cos(th) * (cos(thA) ^ -1) * sin(thA)) * (vA ^ 2));

                //"y position (symbolic):".Disp();

                //intersection.y.Disp(); nl();

                AssertIsTrue(
                    intersection.y
                    ==
                    -2 * (cos(th) ^ -2) * (cos(thA) ^ 2) / g * sin(th) * (sin(th) + -1 * cos(th) * (cos(thA) ^ -1) * sin(thA)) * (vA ^ 2));

                //"x position (numeric):".Disp();

                //numeric(intersection.x).Disp(); nl();

                AssertEqual(numeric(intersection.x), 89.3120879153208);

                //"y position (numeric):".Disp();

                //numeric(intersection.y).Disp(); nl();

                AssertEqual(numeric(intersection.y), -62.536928534704884);

                var objB = new Obj()
                {
                    position = intersection,
                    acceleration = _g
                };

                //"Determine how long the jumper is airborne".Disp(); nl();

                //"symbolic:".Disp();

                var timeB = Calc.Time(objA, objB, 1);

                // timeB.Disp(); nl();

                Func<MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                AssertIsTrue(
                    timeB
                    ==
                    -1 / g *
                    (-sin(thA) * vA -
                        sqrt(
                            (sin(thA) ^ 2) * (vA ^ 2) + 4 * (cos(th) ^ -2) * (cos(thA) ^ 2) * sin(th) *
                            (sin(th) - cos(th) / cos(thA) * sin(thA)) *
                            (vA ^ 2))));

                //"numeric:".Disp();

                //numeric(timeB).Disp(); nl();

                AssertEqual(numeric(timeB), 3.5724835166128317);

                objB = objA.AtTime(timeB);

                //"Determine his vertical component of velocity just before he lands".Disp();
                //nl();

                //"symbolic:".Disp();

                //objB.velocity.y.Disp(); nl();

                AssertIsTrue(
                    objB.velocity.y
                    ==
                    -sqrt(
                        (sin(thA) ^ 2) * (vA ^ 2)
                        +
                        4 * (cos(th) ^ -2) * (cos(thA) ^ 2) * sin(th) *
                        (sin(th) - cos(th) * (cos(thA) ^ -1) * sin(thA)) *
                        (vA ^ 2)));

                //"numeric:".Disp();

                //numeric(objB.velocity.y).Disp();

                AssertEqual(
                    numeric(objB.velocity.y),
                    -35.010338462805755);
            }

            #endregion

            #region PSE 5E PROBLEM 4.11

            {
                // One strategy in a snowball fight is to throw a first snowball at a
                // high angle over level ground. While your opponent is watching the
                // first one, you throw a second one at a low angle and timed to arrive
                // at your opponent before or at the same time as the first one. Assume
                // both snowballs are thrown with a speed of 25.0 m/s. The first one is
                // thrown at an angle of 70.0° with respect to the horizontal.
                //
                // (a) At what angle should the second (low-angle) snowball be thrown
                // if it is to land at the same point as the first?
                //
                // (b) How many seconds later should the second snowball be thrown if it
                // is to land at the same time as the first?

                var xA = new Symbol("xA");      // position.x at point A
                var yA = new Symbol("yA");      // position.y at point A
                var th1A = new Symbol("th1A");  // angle of snowball 1 at point A
                var vA = new Symbol("vA");      // velocity at point A

                var g = new Symbol("g");        // magnitude of gravity
                var _g = new Point(0, -g);      // gravity vector

                //Func<MathObject, MathObject> numeric = obj =>
                //    obj
                //        .Substitute(xA, 0)
                //        .Substitute(xB, 1.4)
                //        .Substitute(yA, 0.86)
                //        .Substitute(g, 9.8)
                //        .Substitute(Trig.Pi, 3.14159);

                var obj1A = new Obj()           // snowball 1 at initial point
                {
                    position = new Point(xA, yA),
                    velocity = Point.FromAngle(th1A, vA),
                    acceleration = _g,
                    time = 0
                };

                var obj1B = new Obj()            // snowball 1 at final point
                {
                    position = new Point(null, 0),
                    velocity = new Point(obj1A.velocity.x, null),
                    acceleration = _g
                };

                var time1B = Calc.Time(obj1A, obj1B, 1);

                obj1B = obj1A.AtTime(time1B);

                var obj2A = new Obj()           // snowball 2 at initial point
                {
                    position = obj1A.position,
                    speed = vA,
                    acceleration = _g
                };

                var obj2B = new Obj()           // snowball 2 at final point
                {
                    position = obj1B.position,
                    acceleration = _g
                };

                //Calc.InitialAngle(obj2A, obj2B, 1, 0)
                //    .Substitute(yA, 0)
                //    .Substitute(th1A, (70).ToRadians())
                //    .Substitute(vA, 25)
                //    .Substitute(Trig.Pi, 3.14159)
                //    .Substitute(g, 9.8)
                //    .ToDegrees()
                //    .Substitute(Trig.Pi, 3.14159)
                //    .Disp();

                var th2 = Calc.InitialAngle(obj2A, obj2B, 0, 0);

                //("At what angle should the second (low-angle) snowball " +
                //"be thrown if it is to land at the same point as the first?").Disp();

                //"".Disp();

                //"symbolic:".Disp();

                //th2.Disp(); "".Disp();

                //"numeric:".Disp();

                AssertEqual(
                    th2
                        .ToDegrees()
                        .Substitute(yA, 0)
                        .Substitute(th1A, (70).ToRadians())
                        .Substitute(vA, 25)
                        .Substitute(g, 9.8)
                        .Substitute(Trig.Pi, Math.PI),
                    20.000000000000007);

                //"".Disp();

                obj2A.velocity = Point.FromAngle(th2, vA);

                var time2B = Calc.Time(obj2A, obj2B, 1);

                //("How many seconds later should the second snowball be thrown if it " +
                //"is to land at the same time as the first?").Disp();

                //"".Disp();

                //"symbolic:".Disp();

                //(time1B - time2B).Disp(); "".Disp();

                //"numeric:".Disp();

                //(time1B - time2B)
                //    .Substitute(yA, 0)
                //    .Substitute(th1A, (70).ToRadians())
                //    .Substitute(vA, 25)
                //    .Substitute(Trig.Pi, 3.14159)
                //    .Substitute(g, 9.8)
                //    .Disp();

                AssertEqual(
                    (time1B - time2B)
                        .Substitute(yA, 0)
                        .Substitute(th1A, (70).ToRadians())
                        .Substitute(vA, 25)
                        .Substitute(Trig.Pi, 3.14159)
                        .Substitute(g, 9.8),
                    3.0493426265020469);

                //Console.ReadLine();
            }

            #endregion

            #region PSE 5E PROBLEM 4.17

            {
                // A cannon with a muzzle speed of 1 000 m/s is used to
                // start an avalanche on a mountain slope. The target is
                // 2 000 m from the cannon horizontally and 800 m above
                // the cannon. At what angle, above the horizontal, should
                // the cannon be fired?

                var xA = new Symbol("xA");      // position.x at point A
                var yA = new Symbol("yA");      // position.y at point A
                var thA = new Symbol("thA");  // angle of snowball 1 at point A
                var vA = new Symbol("vA");      // velocity at point A

                var xB = new Symbol("xB");      // position.x at point A
                var yB = new Symbol("yB");      // position.y at point A

                var g = new Symbol("g");        // magnitude of gravity
                var _g = new Point(0, -g);      // gravity vector

                var objA = new Obj()
                {
                    position = new Point(xA, yA),
                    speed = vA,
                    acceleration = _g,
                    time = 0
                };

                var objB = new Obj()
                {
                    position = new Point(xB, yB),
                    acceleration = _g
                };

                //"At what angle, above the horizontal, should the cannon be fired?".Disp();

                AssertEqual(
                    Calc.InitialAngle(objA, objB)
                        .ToDegrees()
                        .Substitute(xA, 0)
                        .Substitute(yA, 0)
                        .Substitute(xB, 2000.0)
                        .Substitute(yB, 800)
                        .Substitute(vA, 1000)
                        .Substitute(g, 9.8)
                        .Substitute(Trig.Pi, Math.PI),
                    22.365163229244317);

                //Calc.InitialAngle(objA, objB)
                //    .ToDegrees()
                //    .Substitute(xA, 0)
                //    .Substitute(yA, 0)
                //    .Substitute(xB, 2000.0)
                //    .Substitute(yB, 800)
                //    .Substitute(vA, 1000)
                //    .Substitute(g, 9.8)
                //    .Substitute(Trig.Pi, Math.PI)
                //    .Disp();
            }

            #endregion

            #region PSE 5E PROBLEM 4.24
            {
                // A bag of cement of weight 325 N hangs from three
                // wires as shown in Figure P5.24. Two of the wires make
                // angles th1 = 60.0° and th2 = 25.0° with the horizontal. If
                // the system is in equilibrium, find the tensions
                // T1, T2, and T3 in the wires.

                var F1 = new Symbol("F1");
                var F2 = new Symbol("F2");
                var F3 = new Symbol("F3");

                var th1 = new Symbol("th1");
                var th2 = new Symbol("th2");
                var th3 = new Symbol("th3");

                var _F1 = new Point() { angle = th1 };
                var _F2 = new Point() { angle = th2 };
                var _F3 = new Point() { magnitude = F3, angle = th3 };

                var m = new Symbol("m");

                var obj = new Obj();

                obj.acceleration.x = 0;
                obj.acceleration.y = 0;

                obj.mass = m;

                obj.forces.Add(_F1);
                obj.forces.Add(_F2);
                obj.forces.Add(_F3);

                //"F1 magnitude, symbolic:".Disp(); "".Disp();

                //obj.ForceMagnitude(_F1).Disp(); "".Disp();

                //"F1 magnitude, numeric:".Disp(); "".Disp();

                //obj.ForceMagnitude(_F1)
                //    .Substitute(F3, 325)
                //    .Substitute(th1, (180 - 60).ToRadians())
                //    .Substitute(th2, (25).ToRadians())
                //    .Substitute(th3, (270).ToRadians())
                //    .Substitute(Trig.Pi, Math.PI)
                //    .Disp();

                AssertEqual(
                    obj.ForceMagnitude(_F1)
                        .Substitute(F3, 325)
                        .Substitute(th1, (180 - 60).ToRadians())
                        .Substitute(th2, (25).ToRadians())
                        .Substitute(th3, (270).ToRadians())
                        .Substitute(Trig.Pi, Math.PI),
                    295.67516405290525);

                // "".Disp();

                //"F3 magnitude, numeric:".Disp(); "".Disp();

                //obj.ForceMagnitude(_F2)
                //    .Substitute(F3, 325)
                //    .Substitute(th1, (180 - 60).ToRadians())
                //    .Substitute(th2, (25).ToRadians())
                //    .Substitute(th3, (270).ToRadians())
                //    .Substitute(Trig.Pi, Math.PI)
                //    .Disp();

                AssertEqual(
                    obj.ForceMagnitude(_F2)
                        .Substitute(F3, 325)
                        .Substitute(th1, (180 - 60).ToRadians())
                        .Substitute(th2, (25).ToRadians())
                        .Substitute(th3, (270).ToRadians())
                        .Substitute(Trig.Pi, Math.PI),
                    163.12072360079395);
            }
            #endregion

            #region PSE 5E PROBLEM 5.26
            {
                // You are a judge in a children’s kite-flying contest, and
                // two children will win prizes for the kites that pull most
                // strongly and least strongly on their strings. To measure
                // string tensions, you borrow a weight hanger, some slotted
                // weights, and a protractor from your physics teacher
                // and use the following protocol, illustrated in Figure
                // P5.26: Wait for a child to get her kite well-controlled,
                // hook the hanger onto the kite string about 30 cm from
                // her hand, pile on weights until that section of string is
                // horizontal, record the mass required, and record the
                // angle between the horizontal and the string running up
                // to the kite. (a) Explain how this method works. As you
                // construct your explanation, imagine that the children’s
                // parents ask you about your method, that they might
                // make false assumptions about your ability without concrete
                // evidence, and that your explanation is an opportunity to
                // give them confidence in your evaluation tech-nique.
                // (b) Find the string tension if the mass required
                // to make the string horizontal is 132 g and the angle of
                // the kite string is 46.3°.

                var F1 = new Symbol("F1");
                var F2 = new Symbol("F2");
                var F3 = new Symbol("F3");

                var th1 = new Symbol("th1");
                var th2 = new Symbol("th2");
                var th3 = new Symbol("th3");

                var _F1 = new Point() { angle = th1 };
                var _F2 = new Point() { angle = th2 };
                var _F3 = new Point() { magnitude = F3, angle = th3 };

                var m = new Symbol("m");

                var obj = new Obj();

                obj.acceleration.x = 0;
                obj.acceleration.y = 0;

                obj.mass = m;

                obj.forces.Add(_F1);
                obj.forces.Add(_F2);
                obj.forces.Add(_F3);

                //"Tension in line to kite:".Disp(); "".Disp();

                //obj.ForceMagnitude(_F2)
                //    .Substitute(th1, (180).ToRadians())
                //    .Substitute(th2, (46.3 * Math.PI / 180))
                //    .Substitute(th3, (270).ToRadians())
                //    .Substitute(F3, 0.132 * 9.8)
                //    .Substitute(Trig.Pi, Math.PI)
                //    .Disp();

                AssertEqual(
                    obj.ForceMagnitude(_F2)
                        .Substitute(th1, (180).ToRadians())
                        .Substitute(th2, (46.3 * Math.PI / 180))
                        .Substitute(th3, (270).ToRadians())
                        .Substitute(F3, 0.132 * 9.8)
                        .Substitute(Trig.Pi, Math.PI),
                    1.7892929261294661);

                //Console.ReadLine();
            }
            #endregion

            #region PSE 5E PROBLEM 5.28
            {
                // A fire helicopter carries a 620-kg bucket of water at the
                // end of a cable 20.0 m long. As the aircraft flies back
                // from a fire at a constant speed of 40.0 m/s, the cable
                // makes an angle of 40.0° with respect to the vertical.
                // (a) Determine the force of air resistance on the bucket.
                // (b) After filling the bucket with sea water, the pilot re-
                // turns to the fire at the same speed with the bucket now
                // making an angle of 7.00° with the vertical. What is the
                // mass of the water in the bucket?

                var F1 = new Symbol("F1"); // force of air resistance
                var F2 = new Symbol("F2"); // force of cable
                var F3 = new Symbol("F3"); // force of gravity

                var th1 = new Symbol("th1");
                var th2 = new Symbol("th2");
                var th3 = new Symbol("th3");

                var _F1 = new Point() { angle = th1 };
                var _F2 = new Point() { angle = th2 };
                var _F3 = new Point() { magnitude = F3, angle = th3 };

                var m = new Symbol("m");

                var obj = new Obj();

                obj.acceleration.x = 0;
                obj.acceleration.y = 0;

                obj.mass = m;

                obj.forces.Add(_F1);
                obj.forces.Add(_F2);
                obj.forces.Add(_F3);

                //"Force of air resistance on the bucket:".Disp(); "".Disp();

                var FAir =
                    obj.ForceMagnitude(_F1)
                        .Substitute(F3, 620 * 9.8)
                        .Substitute(th1, (180).ToRadians())
                        .Substitute(th2, (90 - 40).ToRadians())
                        .Substitute(th3, (270).ToRadians())
                        .Substitute(Trig.Pi, Math.PI);

                AssertEqual(
                    obj.ForceMagnitude(_F1)
                        .Substitute(F3, 620 * 9.8)
                        .Substitute(th1, (180).ToRadians())
                        .Substitute(th2, (90 - 40).ToRadians())
                        .Substitute(th3, (270).ToRadians())
                        .Substitute(Trig.Pi, Math.PI),
                    5098.3693590331513);

                //"".Disp();

                _F1.magnitude = FAir;

                _F3.magnitude = null;

                var FBucketWithWater =
                    obj.ForceMagnitude(_F3)
                        .Substitute(th1, (180).ToRadians())
                        .Substitute(th2, (90 - 7).ToRadians())
                        .Substitute(th3, (270).ToRadians())
                        .Substitute(Trig.Pi, Math.PI);

                //"What is the mass of the water in the bucket?".Disp(); "".Disp();

                //(FBucketWithWater / 9.8 - 620).Disp();

                AssertEqual(
                    (FBucketWithWater / 9.8 - 620),
                    3617.0292120139611);
            }
            #endregion

            #region PSE 5E PROBLEM 5.30
            {
                // A simple accelerometer is constructed by suspending a
                // mass m from a string of length L that is tied to the top
                // of a cart. As the cart is accelerated the string-mass
                // system makes a constant angle th with the vertical.
                // (a) Assuming that the string mass is negligible compared
                // with m, derive an expression for the cart’s acceleration
                // in terms of and show that it is independent of
                // the mass mand the length L.
                // (b) Determine the acceleration of the cart when th = 23.0°.

                var F1 = new Symbol("F1"); // force of string
                var F2 = new Symbol("F2"); // force of gravity

                var th1 = new Symbol("th1");
                var th2 = new Symbol("th2"); ;

                var _F1 = new Point() { angle = th1 };
                var _F2 = new Point() { angle = th2, magnitude = F2 };

                var m = new Symbol("m");

                var g = new Symbol("g");

                var obj = new Obj() { mass = m };

                obj.acceleration.y = 0;

                obj.forces.Add(_F1);
                obj.forces.Add(_F2);

                _F1.magnitude = obj.ForceMagnitude(_F1);

                //("Derive an expression for the cart’s acceleration in terms " +
                //"of and show that it is independent of the mass mand the length L:").Disp();

                //"".Disp();

                //obj.AccelerationX()
                //    .Substitute(F2, m * g)
                //    .Substitute(Trig.Cos(th2), 0)
                //    .Substitute(Trig.Sin(th2), -1)
                //    .Disp();

                //"".Disp();

                //"Determine the acceleration of the cart when th = 23.0°".Disp(); "".Disp();

                //obj.AccelerationX()
                //    .Substitute(F2, m * g)
                //    .Substitute(Trig.Cos(th2), 0)
                //    .Substitute(Trig.Sin(th2), -1)
                //    .Substitute(th1, (90 - 23).ToRadians())
                //    .Substitute(Trig.Pi, Math.PI)
                //    .Substitute(g, 9.8)
                //    .Disp();

                AssertEqual(
                    obj.AccelerationX()
                        .Substitute(F2, m * g)
                        .Substitute(Trig.Cos(th2), 0)
                        .Substitute(Trig.Sin(th2), -1)
                        .Substitute(th1, (90 - 23).ToRadians())
                        .Substitute(Trig.Pi, Math.PI)
                        .Substitute(g, 9.8),
                    4.1598531988541261);
            }
            #endregion

            #region PSE 5E PROBLEM 5.31
            {
                // Two people pull as hard as they can on ropes attached
                // to a boat that has a mass of 200 kg. If they pull in the
                // same direction, the boat has an acceleration of
                // 1.52 m/s^2 to the right. If they pull in opposite directions,
                // the boat has an acceleration of 0.518 m/s^2
                // to the left. What is the force exerted by each person on the
                // boat? (Disregard any other forces on the boat.)

                // Trig.Cos(new DoubleFloat(Math.PI)).Disp();

                var m = new Symbol("m");

                var aAx = new Symbol("aAx");
                var aBx = new Symbol("aBx");

                var objA = new Obj() { mass = m };

                objA.acceleration.x = aAx;

                var _F1A = new Point() { angle = 0 };
                var _F2A = new Point() { angle = 0 };

                objA.forces.Add(_F1A);
                objA.forces.Add(_F2A);

                var objB = new Obj() { mass = m };

                objB.acceleration.x = aBx;

                var _F1B = new Point() { angle = 0 };
                var _F2B = new Point() { angle = new DoubleFloat(Math.PI) };

                objB.forces.Add(_F1B);
                objB.forces.Add(_F2B);

                //"force 1 magnitude (symbolic):".Disp(); "".Disp();

                //Calc.ForceMagnitude(objA, objB, _F1A, _F1B)
                //    .Substitute(Trig.Cos(0), 1)
                //    .Disp();

                //"force 1 magnitude (numeric):".Disp(); "".Disp();

                //Calc.ForceMagnitude(objA, objB, _F1A, _F1B)
                //    .Substitute(Trig.Cos(0), 1)
                //    .Substitute(m, 200)
                //    .Substitute(aAx, 1.52)
                //    .Substitute(aBx, -0.518)
                //    .Disp();

                AssertEqual(
                    Calc.ForceMagnitude(objA, objB, _F1A, _F1B)
                        .Substitute(Trig.Cos(0), 1)
                        .Substitute(m, 200)
                        .Substitute(aAx, 1.52)
                        .Substitute(aBx, -0.518),
                    100.19999999999999);

                //"".Disp();

                //"force 2 magnitude (symbolic):".Disp(); "".Disp();

                //Calc.ForceMagnitude(objA, objB, _F2A, _F2B)
                //    .Substitute(Trig.Cos(0), 1)
                //    .Disp();

                //"force 2 magnitude (numeric):".Disp(); "".Disp();

                //Calc.ForceMagnitude(objA, objB, _F2A, _F2B)
                //    .Substitute(Trig.Cos(0), 1)
                //    .Substitute(m, 200)
                //    .Substitute(aAx, 1.52)
                //    .Substitute(aBx, -0.518)
                //    .Disp();

                //"".Disp();

                AssertEqual(
                    Calc.ForceMagnitude(objA, objB, _F2A, _F2B)
                        .Substitute(Trig.Cos(0), 1)
                        .Substitute(m, 200)
                        .Substitute(aAx, 1.52)
                        .Substitute(aBx, -0.518),
                    203.8);

            }
            #endregion

            Console.WriteLine("Testing complete");

            Console.ReadLine();
        }
Example #2
0
        static void Main(string[] args)
        {
            Console.OutputEncoding = Encoding.UTF8;

            Action<Equation> AssertIsTrue = (eq) =>
            {
                if (!eq) Console.WriteLine(eq.ToString());
            };

            Action<Equation> AssertIsFalse = (eq) =>
            {
                if (eq) Console.WriteLine(eq.ToString());
            };

            {
                var a = new Symbol("a");
                var b = new Symbol("b");
                var c = new Symbol("c");
                var d = new Symbol("d");

                var x = new Symbol("x");
                var y = new Symbol("y");
                var z = new Symbol("z");

                Func<int, Integer> Int = (n) => new Integer(n);

                {
                    DoubleFloat.tolerance = 0.000000001;

                    Assert(new DoubleFloat(1.2).Equals(new DoubleFloat(1.2)), "new DoubleFloat(1.2).Equals(new DoubleFloat(1.2))");

                    Assert(new DoubleFloat(1.20000001).Equals(new DoubleFloat(1.20000002)) == false, "new DoubleFloat(1.20000001).Equals(new DoubleFloat(1.20000002)) == false");

                    Assert(new DoubleFloat(1.2000000000001).Equals(new DoubleFloat(1.200000000002)), "new DoubleFloat(1.2000000000001).Equals(new DoubleFloat(1.200000000002))");

                    Assert(new DoubleFloat(1.2).Equals(new DoubleFloat(1.23)) == false, "new DoubleFloat(1.2).Equals(new DoubleFloat(1.23)) == false");

                    DoubleFloat.tolerance = null;
                }

                #region Const

                AssertIsTrue((2 * x * y).Const() == 2);

                AssertIsTrue((x * y / 2).Const() == new Integer(1) / 2);

                AssertIsTrue((0.1 * x * y).Const() == 0.1);

                AssertIsTrue((x * y).Const() == 1);

                #endregion
            
                #region Simplify

                AssertIsTrue(x + x == 2 * x);

                AssertIsTrue(x + x == 2 * x);

                AssertIsTrue(x + x + x == 3 * x);

                AssertIsTrue(5 + x + 2 == 7 + x);

                AssertIsTrue(3 + x + 5 + x == 8 + 2 * x);

                AssertIsTrue(4 * x + 3 * x == 7 * x);

                AssertIsTrue(x + y + z + x + y + z == 2 * x + 2 * y + 2 * z);

                AssertIsTrue(10 - x == 10 + x * -1);

                AssertIsTrue(x * y / 3 == Int(1) / 3 * x * y);

                AssertIsTrue(x / y == x * (y ^ -1));

                AssertIsTrue(x / 3 == x * (Int(1) / 3));

                AssertIsTrue(6 * x * y / 3 == 2 * x * y);

                AssertIsTrue((((x ^ Int(1) / 2) ^ Int(1) / 2) ^ 8) == (x ^ 2));

                AssertIsTrue(((((x * y) ^ (Int(1) / 2)) * (z ^ 2)) ^ 2) == (x * y * (z ^ 4)));

                AssertIsTrue(x / x == 1);

                AssertIsTrue(x / y * y / x == 1);

                AssertIsTrue((x ^ 2) * (x ^ 3) == (x ^ 5));

                AssertIsTrue(x + y + x + z + 5 + z == 5 + 2 * x + y + 2 * z);

                AssertIsTrue(((Int(1) / 2) * x + (Int(3) / 4) * x) == Int(5) / 4 * x);

                AssertIsTrue(1.2 * x + 3 * x == 4.2 * x);

                AssertIsTrue(3 * x + 1.2 * x == 4.2 * x);

                AssertIsTrue(1.2 * x * 3 * y == 3.5999999999999996 * x * y);

                AssertIsTrue(3 * x * 1.2 * y == 3.5999999999999996 * x * y);

                AssertIsTrue(3.4 * x * 1.2 * y == 4.08 * x * y);

                AssertIsTrue((a == b) == (a == b));

                #endregion

                #region Power.Simplify

                AssertIsTrue((0 ^ x) == 0);
                AssertIsTrue((1 ^ x) == 1);
                AssertIsTrue((x ^ 0) == 1);
                AssertIsTrue((x ^ 1) == x);

                #endregion

                // Product.Simplify

                AssertIsTrue(x * 0 == 0);

                // Difference

                AssertIsTrue(-x == -1 * x);

                AssertIsTrue(x - y == x + -1 * y);

                #region Substitute

                AssertIsTrue(Int(10).Substitute(Int(10), 20) == 20);
                AssertIsTrue(Int(10).Substitute(Int(15), 20) == 10);

                AssertIsTrue(new DoubleFloat(1.0).Substitute(new DoubleFloat(1.0), 2.0) == 2.0);
                AssertIsTrue(new DoubleFloat(1.0).Substitute(new DoubleFloat(1.5), 2.0) == 1.0);

                AssertIsTrue((Int(1) / 2).Substitute(Int(1) / 2, Int(3) / 4) == Int(3) / 4);
                AssertIsTrue((Int(1) / 2).Substitute(Int(1) / 3, Int(3) / 4) == Int(1) / 2);

                AssertIsTrue(x.Substitute(x, y) == y);
                AssertIsTrue(x.Substitute(y, y) == x);

                AssertIsTrue((x ^ y).Substitute(x, 10) == (10 ^ y));
                AssertIsTrue((x ^ y).Substitute(y, 10) == (x ^ 10));

                AssertIsTrue((x ^ y).Substitute(x ^ y, 10) == 10);

                AssertIsTrue((x * y * z).Substitute(x, y) == ((y ^ 2) * z));
                AssertIsTrue((x * y * z).Substitute(x * y * z, x) == x);

                AssertIsTrue((x + y + z).Substitute(x, y) == ((y * 2) + z));
                AssertIsTrue((x + y + z).Substitute(x + y + z, x) == x);

                AssertIsTrue(
                    ((((x * y) ^ (Int(1) / 2)) * (z ^ 2)) ^ 2)
                        .Substitute(x, 10)
                        .Substitute(y, 20)
                        .Substitute(z, 3)
                        == 16200
                        );

                #region Equation.Substitute

                AssertIsTrue((x == y).Substitute(y, z) == (x == z));

                AssertIsTrue((x != y).Substitute(y, z) == (x != z));

                (x == 0).Substitute(x, 0).AssertEqTo(true);

                (x == 0).Substitute(x, 1).AssertEqTo(false);

                (x != 0).Substitute(x, 0).AssertEqTo(false);

                (x != 0).Substitute(x, 1).AssertEqTo(true);

                #endregion

                #endregion

                AssertIsTrue(sin(new DoubleFloat(3.14159 / 2)) == 0.99999999999911982);

                AssertIsTrue(sin(x + y) + sin(x + y) == 2 * sin(x + y));

                AssertIsTrue(sin(x + x) == sin(2 * x));

                AssertIsTrue(sin(x + x).Substitute(x, 1) == sin(Int(2)));

                AssertIsTrue(sin(x + x).Substitute(x, 1.0) == 0.90929742682568171);

                AssertIsTrue(sin(2 * x).Substitute(x, y) == sin(2 * y));

                // Product.RecursiveSimplify

                AssertIsTrue(1 * x == x);

                AssertIsTrue(x * 1 == x);

                AssertIsTrue(x != y);

                AssertIsTrue(x != 10);

                // ==(double a, MathObject b)

                AssertIsTrue(1.0 == new DoubleFloat(3.0) - 2.0);

                AssertIsTrue((a == b) != (a != b));
                
                (sqrt(a * b) * (sqrt(a * b) / a) / c)
                    .AssertEqTo(b / c);


                Action<MathObject, string> AssertToStringMatch = 
                    (MathObject obj, string str) => Assert(obj.ToString() == str, $"({str}).ToString()");
                
                MathObject.ToStringForm = MathObject.ToStringForms.Full;
                                
                AssertToStringMatch(x + y + z, "x + y + z");
                                
                AssertToStringMatch(x + y * z, "x + y * z");
                                                
                AssertToStringMatch((x + y) * z, "(x + y) * z");
                
                Assert((sin(x) * cos(y)).ToString() == "cos(y) * sin(x)", "(sin(x) * cos(y)).ToString()");
                                
                AssertToStringMatch(and(x, y, z), "and(x, y, z)");
                                
                AssertToStringMatch(x ^ y, "x ^ y");
                                
                AssertToStringMatch((x * y) ^ (x + z), "(x * y) ^ (x + z)");


                
                Assert((x - y).ToString() == "x + -1 * y", "(x - y).ToString()");

                Assert((x - y - z).ToString() == "x + -1 * y + -1 * z", "(x - y - z).ToString()");

                Assert((x / y).ToString() == "x * y ^ -1", "(x / y).ToString()");
                
                Assert((x - (y - z)).ToString() == "x + -1 * (y + -1 * z)", "(x - (y - z)).ToString()");


                MathObject.ToStringForm = MathObject.ToStringForms.Standard;
                
                Assert((x + y).ToString() == "x + y", "(x + y).ToString()");

                Assert((x - y).ToString() == "x - y", "(x - y).ToString()");
                    
                Assert((x - y - z).ToString() == "x - y - z", "(x - y - z).ToString()");
                    
                Assert((-x - y - z).ToString() == "-x - y - z", "(x - y - z).ToString()");
                    
                Assert((2 * x - 3 * y - 4 * z).ToString() == "2 * x - 3 * y - 4 * z", "(2 * x - 3 * y - 4 * z).ToString()");
                    
                Assert((x - (y - z)).ToString() == "x - (y - z)", "(x - (y - z)).ToString()");
                
                Assert((x - y + z).ToString() == "x - y + z", "(x - y + z).ToString()");
                    
                Assert((-x).ToString() == "-x", "(-x).ToString()");
                    
                Assert((x / y).ToString() == "x / y", "(x / y).ToString()");
                    
                Assert((x / (y + z)).ToString() == "x / (y + z)", "(x / (y + z)).ToString()");
                                        
                Assert(((x + y) / (x + z)).ToString() == "(x + y) / (x + z)", "((x + y) / (x + z)).ToString()");
                                        
                Assert((-x * y).ToString() == "-x * y", "(-x * y).ToString()");

                Assert((x * -y).ToString() == "-x * y", "(x * -y).ToString()");


                Assert(sin(x / y).ToString() == "sin(x / y)", "sin(x / y).ToString()");
                                                
                Assert(
                    (x == -sqrt(2 * y * (-z * a + y * (b ^ 2) / 2 - c * y * d + c * y * z * sin(x))) / y).ToString() ==
                    "x == -sqrt(2 * y * ((b ^ 2) * y / 2 - c * d * y - a * z + c * sin(x) * y * z)) / y",
                    "(x == -sqrt(2 * y * (-z * a + y * (b ^ 2) / 2 - c * y * d + c * y * z * sin(x))) / y).ToString()");
                
                Assert((x * (y ^ z)).ToString() == "x * (y ^ z)", "(x * (y ^ z)).ToString()");

                Assert((x + (y ^ z)).ToString() == "x + (y ^ z)", "((x + (y ^ z)).ToString()");
                
                Assert(sqrt(x).ToString() == "sqrt(x)", "sqrt(x).ToString()");
                
                Assert(sqrt(x).FullForm().ToString() == "x ^ 1/2", "sqrt(x).FullForm()");
                
                Assert((x ^ (new Integer(1) / 3)).ToString() == "x ^ 1/3", "(x ^ (new Integer(1) / 3)).ToString()");

                Assert(and(and(x, y), and(x, z)).SimplifyLogical().ToString() == "and(x, y, z)", 
                    "and(and(x, y), and(x, z)).SimplifyLogical().ToString()");

                AssertToStringMatch(x == sqrt(2 * (y * z - cos(a) * y * z)), "x == sqrt(2 * (y * z - cos(a) * y * z))");
                                
                AssertToStringMatch(
                     a == (-c * cos(d) - b * c * sin(d) + x * y + b * x * z) / (-y - z),
                    "a == (-c * cos(d) - b * c * sin(d) + x * y + b * x * z) / (-y - z)");
                
                AssertToStringMatch(
                     x == -(sin(y) / cos(y) + sqrt((sin(y) ^ 2) / (cos(y) ^ 2))) * (z ^ 2) / a,
                    "x == -(sin(y) / cos(y) + sqrt((sin(y) ^ 2) / (cos(y) ^ 2))) * (z ^ 2) / a");
                                
                AssertToStringMatch(x * sqrt(y), "x * sqrt(y)");
                                
                AssertToStringMatch(x / sqrt(y), "x / sqrt(y)");

                AssertToStringMatch(sqrt(y) / x, "sqrt(y) / x");
                
                AssertToStringMatch((x ^ 2) / (y ^ 3), "(x ^ 2) / (y ^ 3)");
                                                               
                AssertToStringMatch(
                     x == y * sqrt(-8 * a / (y * (z ^ 2))) * (z ^ 2) / (4 * a),
                    "x == y * sqrt(-8 * a / (y * (z ^ 2))) * (z ^ 2) / (4 * a)");
                
                AssertToStringMatch(-(-1 + x), "-(-1 + x)");
                
                #region Equation.ToString

                Assert((x == y).ToString() == "x == y", "x == y");

                Assert((x != y).ToString() == "x != y", "x != y");

                #endregion

                #region Function.ToString

                Assert(new And().ToString() == "and()", "and()");
               
                #endregion

                #region Equation.Simplify

                (new Integer(0) == new Integer(0)).Simplify().AssertEqTo(true);

                (new Integer(0) == new Integer(1)).Simplify().AssertEqTo(false);

                (new Integer(0) != new Integer(1)).Simplify().AssertEqTo(true);

                (new Integer(0) != new Integer(0)).Simplify().AssertEqTo(false);

                #endregion

                #region And

                and().AssertEqTo(true);

                and(10).AssertEqTo(10);

                and(true).AssertEqTo(true);

                and(false).AssertEqTo(false);

                and(10, 20, 30).AssertEqTo(and(10, 20, 30));

                and(10, false, 20).AssertEqTo(false);

                and(10, true, 20).AssertEqTo(and(10, 20));

                and(10, and(20, 30), 40)
                    
                    .AssertEqTo(and(10, 20, 30, 40));

                #endregion

                #region Or

                or(10).AssertEqTo(10);

                or(true).AssertEqTo(true);

                or(false).AssertEqTo(false);

                or(10, 20, false).AssertEqTo(or(10, 20));

                or(false, false).AssertEqTo(false);

                or(10, true, 20, false).AssertEqTo(true);

                or(10, false, 20).AssertEqTo(or(10, 20));

                or(10, or(20, 30), 40)
                    
                    .AssertEqTo(or(10, 20, 30, 40));

                #endregion

                #region Function.Map

                new And(1, 2, 3, 4, 5, 6).Map(elt => elt * 2)
                    .AssertEqTo(and(2, 4, 6, 8, 10, 12));

                new And(1, 2, 3, 4, 5, 6).Map(elt => (elt is Integer) && (elt as Integer).val % 2 == 0 ? elt : false)
                    .AssertEqTo(false);

                new Or(1, 2, 3).Map(elt => elt * 2)
                    .AssertEqTo(or(2, 4, 6));

                new Or(1, 2, 3, 4, 5, 6).Map(elt => (elt is Integer) && (elt as Integer).val % 2 == 0 ? elt : false)
                    .AssertEqTo(or(2, 4, 6));

                #endregion Function.Map

                #region Sum

                Assert((x + y).Equals(x * y) == false, "(x + y).Equals(x * y)");

                #endregion
                
                {
                    (x < y).Substitute(x, 10).Substitute(y, 20).AssertEqTo(true);

                    (x > y).Substitute(x, 10).Substitute(y, 20).AssertEqTo(false);
                }

                var Pi = new Symbol("Pi");

                var half = new Integer(1) / 2;

                #region Sin



                {
                    sin(0).AssertEqTo(0);

                    sin(Pi).AssertEqTo(0);

                    sin(-10).AssertEqTo(-sin(10));

                    sin(-x).AssertEqTo(-sin(x));

                    sin(-5 * x).AssertEqTo(-sin(5 * x));

                    // sin(k/n pi) for n = 1 2 3 4 6

                    sin(-2 * Pi).AssertEqTo(0);
                    sin(-1 * Pi).AssertEqTo(0);
                    sin( 2 * Pi).AssertEqTo(0);
                    sin( 3 * Pi).AssertEqTo(0);

                    sin(-7 * Pi / 2).AssertEqTo(1);
                    sin(-5 * Pi / 2).AssertEqTo(-1);
                    sin(-3 * Pi / 2).AssertEqTo(1);
                    sin(-1 * Pi / 2).AssertEqTo(-1);
                    sin( 1 * Pi / 2).AssertEqTo(1);
                    sin( 3 * Pi / 2).AssertEqTo(-1);
                    sin( 5 * Pi / 2).AssertEqTo(1);
                    sin( 7 * Pi / 2).AssertEqTo(-1);
                    
                    sin(-4 * Pi / 3).AssertEqTo( sqrt(3)/2);
                    sin(-2 * Pi / 3).AssertEqTo(-sqrt(3)/2);
                    sin(-1 * Pi / 3).AssertEqTo(-sqrt(3)/2);
                    sin( 1 * Pi / 3).AssertEqTo( sqrt(3)/2);
                    sin( 2 * Pi / 3).AssertEqTo( sqrt(3)/2);
                    sin( 4 * Pi / 3).AssertEqTo(-sqrt(3)/2);
                    sin( 5 * Pi / 3).AssertEqTo(-sqrt(3)/2);
                    sin( 7 * Pi / 3).AssertEqTo( sqrt(3)/2);

                    sin(-3 * Pi / 4).AssertEqTo(-1/sqrt(2));
                    sin(-1 * Pi / 4).AssertEqTo(-1/sqrt(2));
                    sin( 1 * Pi / 4).AssertEqTo( 1/sqrt(2));
                    sin( 3 * Pi / 4).AssertEqTo( 1/sqrt(2));
                    sin( 5 * Pi / 4).AssertEqTo(-1/sqrt(2));
                    sin( 7 * Pi / 4).AssertEqTo(-1/sqrt(2));
                    sin( 9 * Pi / 4).AssertEqTo( 1/sqrt(2));
                    sin(11 * Pi / 4).AssertEqTo( 1/sqrt(2));

                    // var half = new Integer(1) / 2;

                    sin(-5 * Pi / 6).AssertEqTo(-half);
                    sin(-1 * Pi / 6).AssertEqTo(-half);
                    sin( 1 * Pi / 6).AssertEqTo( half);
                    sin( 5 * Pi / 6).AssertEqTo( half);
                    sin( 7 * Pi / 6).AssertEqTo(-half);
                    sin(11 * Pi / 6).AssertEqTo(-half);
                    sin(13 * Pi / 6).AssertEqTo( half);
                    sin(17 * Pi / 6).AssertEqTo( half);

                    // sin(a/b pi) where a/b > 1/2 (i.e. not in first quadrant)

                    sin(15 * Pi / 7).AssertEqTo( sin(1 * Pi / 7));
                    sin( 8 * Pi / 7).AssertEqTo(-sin(1 * Pi / 7));
                    sin( 4 * Pi / 7).AssertEqTo( sin(3 * Pi / 7));

                    // sin( a + b + ... + n * pi ) where abs(n) >= 2

                    sin(x - 3 * Pi).AssertEqTo(sin(x + Pi));
                    sin(x - 2 * Pi).AssertEqTo(sin(x));
                    sin(x + 2 * Pi).AssertEqTo(sin(x));
                    sin(x + 3 * Pi).AssertEqTo(sin(x + Pi));
                    sin(x + 7 * Pi / 2).AssertEqTo(sin(x + 3 * Pi / 2));

                    // sin( a + b + ... + n/2 * pi )

                    sin(x - 3 * Pi / 2).AssertEqTo( cos(x));
                    sin(x - 1 * Pi / 2).AssertEqTo(-cos(x));
                    sin(x + 1 * Pi / 2).AssertEqTo( cos(x));
                    sin(x + 3 * Pi / 2).AssertEqTo(-cos(x));
                }


                {
                    sin(Pi + x).AssertEqTo(-sin(x));
                    
                    sin(Pi + x + y).AssertEqTo(-sin(x + y));
                }

                {
                    // var Pi = new Symbol("Pi");

                    cos(Pi + x).AssertEqTo(-cos(x));

                    cos(Pi + x + y).AssertEqTo(-cos(x + y));
                }


                #endregion

                #region Cos

                {
                    // var Pi = new Symbol("Pi");

                    cos(0).AssertEqTo(1);

                    cos(Pi).AssertEqTo(-1);

                    cos(-10).AssertEqTo(cos(10));

                    cos(-10 * x).AssertEqTo(cos(10 * x));

                    cos(3 * Pi).AssertEqTo(-1);

                    cos(2 * Pi * 3 / 4).AssertEqTo(0);

                    // cos( a + b + ... + n * pi ) where abs(n) >= 2

                    cos(x - 3 * Pi).AssertEqTo(cos(x + Pi));
                    cos(x + 3 * Pi).AssertEqTo(cos(x + Pi));

                    cos(x - 2 * Pi).AssertEqTo(cos(x));
                    cos(x + 2 * Pi).AssertEqTo(cos(x));

                    cos(x + Pi * 7 / 2).AssertEqTo(cos(x + Pi * 3 / 2));

                    // cos( a + b + ... + n/2 * pi )

                    cos(x - Pi * 3 / 2).AssertEqTo(-sin(x));
                    cos(x - Pi * 1 / 2).AssertEqTo(sin(x));
                    cos(x + Pi * 1 / 2).AssertEqTo(-sin(x));
                    cos(x + Pi * 3 / 2).AssertEqTo(sin(x));
                }


                #endregion
                
                #region Has

                Assert(a.Has(elt => elt == a), "a.Has(elt => elt == a)");

                Assert(a.Has(elt => elt == b) == false, "a.Has(elt => elt == b) == false");

                Assert((a == b).Has(elt => elt == a), "Has - 3");

                Assert((a == b).Has(elt => elt == c) == false, "Has - 4");

                Assert(((a + b) ^ c).Has(elt => elt == a + b), "Has - 5");

                Assert(((a + b) ^ c).Has(elt => (elt is Power) && (elt as Power).exp == c), "Has - 6");

                Assert((x * (a + b + c)).Has(elt => (elt is Sum) && (elt as Sum).Has(b)), "Has - 7");

                Assert((x * (a + b + c)).Has(elt => (elt is Sum) && (elt as Sum).elts.Any(obj => obj == b)), "Has - 8");

                Assert((x * (a + b + c)).Has(elt => (elt is Product) && (elt as Product).elts.Any(obj => obj == b)) == false, "Has - 9");

                #endregion

                #region FreeOf

                Assert((a + b).FreeOf(b) == false, "(a + b).FreeOf(b)");
                Assert((a + b).FreeOf(c) == true, "(a + b).FreeOf(c)");
                Assert(((a + b) * c).FreeOf(a + b) == false, "((a + b) * c).FreeOf(a + b)");
                Assert((sin(x) + 2 * x).FreeOf(sin(x)) == false, "(sin(x) + 2 * x).FreeOf(sin(x))");
                Assert(((a + b + c) * d).FreeOf(a + b) == true, "((a + b + c) * d).FreeOf(a + b)");
                Assert(((y + 2 * x - y) / x).FreeOf(x) == true, "((y + 2 * x - y) / x).FreeOf(x)");
                Assert(((x * y) ^ 2).FreeOf(x * y) == true, "((x * y) ^ 2).FreeOf(x * y)");

                #endregion

                // Symbolism.Numerator.Extensions.Numerator(x ^ -2).Disp();

                #region Numerator

                AssertIsTrue((x ^ -1).Numerator() == 1);

                AssertIsTrue((x ^ -half).Numerator() == 1);
                

                #endregion

                #region Denominator
                {


                    ((new Integer(2) / 3) * ((x * (x + 1)) / (x + 2)) * (y ^ z))
                        .Denominator()
                        .AssertEqTo(3 * (x + 2));
                }
                #endregion
                
                #region LogicalExpand

                and(or(a, b), c)
                    .LogicalExpand()
                    .AssertEqTo(
                        or(
                            and(a, c),
                            and(b, c)));

                and(a, or(b, c))
                    .LogicalExpand()
                    .AssertEqTo(or(and(a, b), and(a, c)));

                and(a, or(b, c), d)
                    .LogicalExpand()
                    .AssertEqTo(
                        or(
                            and(a, b, d),
                            and(a, c, d)));

                and(or(a == b, b == c), x == y)
                    .LogicalExpand()
                    .AssertEqTo(
                        or(
                            and(a == b, x == y),
                            and(b == c, x == y)));

                and(
                    or(a == b, b == c),
                    or(c == d, d == a),
                    x == y)
                    .LogicalExpand()
                    .AssertEqTo(
                        or(
                            and(a == b, c == d, x == y),
                            and(a == b, d == a, x == y),
                            and(b == c, c == d, x == y),
                            and(b == c, d == a, x == y)));

                #endregion

                #region SimplifyEquation

                (2 * x == 0)
                    .SimplifyEquation()
                    .AssertEqTo(x == 0);

                (2 * x != 0)
                    .SimplifyEquation()
                    .AssertEqTo(x != 0);

                ((x ^ 2) == 0)
                    .SimplifyEquation()
                    .AssertEqTo(x == 0);

                #endregion

                #region SimplifyLogical

                and(a, b, c, a)
                    .SimplifyLogical()
                    .AssertEqTo(and(a, b, c));

                #endregion SimplifyLogical

                #region DegreeGpe

                {
                    var w = new Symbol("w");

                    Assert(
                        ((3 * w * x ^ 2) * (y ^ 3) * (z ^ 4)).DegreeGpe(new List<MathObject>() { x, z }) == 6,
                        "((3 * w * x ^ 2) * (y ^ 3) * (z ^ 4)).DegreeGpe(new List<MathObject>() { x, z })");

                    Assert(
                        ((a * x ^ 2) + b * x + c).DegreeGpe(new List<MathObject>() { x }) == 2,
                        "((a * x ^ 2) + b * x + c).DegreeGpe(new List<MathObject>() { x })");

                    Assert(
                        (a * (sin(x) ^ 2) + b * sin(x) + c).DegreeGpe(new List<MathObject>() { sin(x) }) == 2,
                        "(a * (sin(x) ^ 2) + b * sin(x) + c).DegreeGpe(new List<MathObject>() { sin(x) })");

                    Assert(
                        (2 * (x ^ 2) * y * (z ^ 3) + w * x * (z ^ 6)).DegreeGpe(new List<MathObject>() { x, z }) == 7,
                        "(2 * (x ^ 2) * y * (z ^ 3) + w * x * (z ^ 6)).DegreeGpe(new List<MathObject>() { x, z })");
                }

                #endregion

                #region CoefficientGpe

                AssertIsTrue((a * (x ^ 2) + b * x + c).CoefficientGpe(x, 2) == a);

                AssertIsTrue((3 * x * (y ^ 2) + 5 * (x ^ 2) * y + 7 * x + 9).CoefficientGpe(x, 1) == 3 * (y ^ 2) + 7);

                AssertIsTrue((3 * x * (y ^ 2) + 5 * (x ^ 2) * y + 7 * x + 9).CoefficientGpe(x, 3) == 0);

                Assert(
                    (3 * sin(x) * (x ^ 2) + 2 * x + 4).CoefficientGpe(x, 2) == null,
                    "(3 * sin(x) * (x ^ 2) + 2 * x + 4).CoefficientGpe(x, 2) == null");

                #endregion

                #region AlgebraicExpand

                AssertIsTrue(
                    ((x + 2) * (x + 3) * (x + 4)).AlgebraicExpand()
                    ==
                    24 + 26 * x + 9 * (x ^ 2) + (x ^ 3));

                AssertIsTrue(
                    ((x + y + z) ^ 3).AlgebraicExpand()
                    ==
                    (x ^ 3) + (y ^ 3) + (z ^ 3) +
                    3 * (x ^ 2) * y +
                    3 * (y ^ 2) * x +
                    3 * (x ^ 2) * z +
                    3 * (y ^ 2) * z +
                    3 * (z ^ 2) * x +
                    3 * (z ^ 2) * y +
                    6 * x * y * z);

                AssertIsTrue(
                    (((x + 1) ^ 2) + ((y + 1) ^ 2)).AlgebraicExpand()
                    ==
                    2 + 2 * x + (x ^ 2) + 2 * y + (y ^ 2));

                AssertIsTrue(
                    ((((x + 2) ^ 2) + 3) ^ 2).AlgebraicExpand()
                    ==
                    49 + 56 * x + 30 * (x ^ 2) + 8 * (x ^ 3) + (x ^ 4));

                AssertIsTrue(
                    sin(x * (y + z)).AlgebraicExpand()
                    ==
                    sin(x * y + x * z));


                AssertIsTrue(
                    (a * (b + c) == x * (y + z)).AlgebraicExpand()
                    ==
                    (a * b + a * c == x * y + x * z));

                (5 * x * (500 / (x ^ 2) * (sqrt(3.0) / 4) + 1) + 2 * (x ^ 2) + (sqrt(3.0) / 2) * (x ^ 2))
                    .AlgebraicExpand()
                    .AssertEqTo(1082.5317547305483 / x + 5 * x + 2.8660254037844384 * (x ^ 2));

                #endregion

                #region IsolateVariable

                (x + y + z == 0).IsolateVariable(a).AssertEqTo(x + y + z == 0);

                // (x * a + x * b == 0).IsolateVariable(x).Disp();

                (x * (a + b) - x * a - x * b + x == c)
                    .IsolateVariable(x)
                    .AssertEqTo(x == c);

                and(x == y, a == b)
                    .IsolateVariable(b)
                    .AssertEqTo(and(x == y, b == a));

                or(and(y == x, z == x), and(b == x, c == x))
                    .IsolateVariable(x)
                    .AssertEqTo(or(and(x == y, x == z), and(x == b, x == c)));

                Assert((0 == x - y).IsolateVariableEq(x).Equals(x == y), "(0 == x - y).IsolateVariable(x).Equals(x == y)");

                

                (a * (x ^ 2) + b * x + c == 0)
                    .IsolateVariable(x)
                    .AssertEqTo(

                        or(

                            and(
                                x == (-b + sqrt((b ^ 2) + -4 * a * c)) / (2 * a),
                                a != 0
                            ),

                            and(
                                x == (-b - sqrt((b ^ 2) + -4 * a * c)) / (2 * a),
                                a != 0
                            ),

                            and(x == -c / b, a == 0, b != 0),

                            and(a == 0, b == 0, c == 0)
                        )
                    );

                (a * (x ^ 2) + c == 0)
                    .IsolateVariable(x)
                    .AssertEqTo(

                        or(
                            and(
                                x == sqrt(-4 * a * c) / (2 * a),
                                a != 0
                            ),

                            and(
                                x == -sqrt(-4 * a * c) / (2 * a),
                                a != 0
                            ),

                            and(a == 0, c == 0)
                        )
                    );

                // a x^2 + b x + c == 0
                // a x^2 + c == - b x
                // (a x^2 + c) / x == - b

                ((a * (x ^ 2) + c) / x == -b)
                    .IsolateVariable(x)
                    .AssertEqTo(

                    or(

                            and(
                                x == (-b + sqrt((b ^ 2) + -4 * a * c)) / (2 * a),
                                a != 0
                            ),

                            and(
                                x == (-b - sqrt((b ^ 2) + -4 * a * c)) / (2 * a),
                                a != 0
                            ),

                            and(x == -c / b, a == 0, b != 0),

                            and(a == 0, b == 0, c == 0)
                        )
                );

                (sqrt(x + y) == z).IsolateVariable(x).AssertEqTo(x == (z ^ 2) - y);

                (a * b + a == c)
                    .IsolateVariable(a)
                    .AssertEqTo(a == c / (b + 1));

                (a * b + a * c == d)
                    .IsolateVariable(a)
                    .AssertEqTo(a == d / (b + c));

                (1 / sqrt(x) == y)
                    .IsolateVariable(x)
                    .AssertEqTo(x == (y ^ -2));

                (y == sqrt(x) / x)
                    .IsolateVariable(x)
                    .AssertEqTo(x == (y ^ -2));

                (-sqrt(x) + z * x == y)
                    .IsolateVariable(x)
                    .AssertEqTo(-sqrt(x) + z * x == y);

                (sqrt(a + x) - z * x == -y)
                    .IsolateVariable(x)
                    .AssertEqTo(sqrt(a + x) - z * x == -y);

                (sqrt(2 + x) * sqrt(3 + x) == y)
                    .IsolateVariable(x)
                    .AssertEqTo(sqrt(2 + x) * sqrt(3 + x) == y);
                
                ((x + 1) / (x + 2) == 3)
                    .IsolateVariable(x)
                    .AssertEqTo(x == -new Integer(5) / 2);

                ((1 + 2 * x) / (3 * x - 4) == 5)
                    .IsolateVariable(x)
                    .AssertEqTo(x == new Integer(21) / 13);

                #endregion

                #region EliminateVariable

                and((x ^ 3) == (y ^ 5), z == x)
                .EliminateVariable(x)
                .AssertEqTo((z ^ 3) == (y ^ 5));

                and((x ^ 3) == (y ^ 5), z == (x ^ 7))
                .EliminateVariable(x)
                .AssertEqTo(and((x ^ 3) == (y ^ 5), z == (x ^ 7)));

                #endregion

                and(x + y == z, x / y == 0, x != 0).CheckVariable(x).AssertEqTo(false);
            }

            #region EliminateVariable
            
            {
                var x = new Symbol("x");
                var y = new Symbol("y");
                var z = new Symbol("z");
                                
                var eqs = and(
                    (x ^ 2) - 4 == 0,
                    y + x == 0,
                    x + z == 10
                );
                
                var half = new Integer(1) / 2;
                                
                ((x ^ 2) - 4 == 0)
                    .IsolateVariableEq(x)
                    .AssertEqTo(or(x == half * sqrt(16), x == -half * sqrt(16)));

                eqs.EliminateVariable(x)
                    .AssertEqTo(
                        or(
                            and(
                                half * sqrt(16) + y == 0,
                                half * sqrt(16) + z == 10
                            ),
                            and(
                                -half * sqrt(16) + y == 0,
                                -half * sqrt(16) + z == 10
                            )
                        )
                    );
            }
            
            {
                var a = new Symbol("a");
                var x = new Symbol("x");
                var y = new Symbol("y");
                var z = new Symbol("z");

                or(
                    and(x == y, x == z, x == a),
                    and(x == -y, x == z, x == a))
                    .EliminateVariable(x)
                    .AssertEqTo(
                        or(
                            and(y == z, y == a),
                            and(-y == z, -y == a)
                        )
                    )
                    .EliminateVariable(y)
                    .AssertEqTo(or(z == a, z == a));
            }
            
            {
                var x = new Symbol("x");
                var y = new Symbol("y");
                var z = new Symbol("z");

                and(y != z, y == x, y == 10)
                    .EliminateVariable(y)
                    .AssertEqTo(and(x != z, x == 10));
            }

            #endregion

            #region PSE Example 2.6

            {
                var sAC = new Symbol("sAC");
                var sAB = new Symbol("sAB");

                var vA = new Symbol("vA");
                var vB = new Symbol("vB");
                var vC = new Symbol("vC");

                var a = new Symbol("a");

                var tAC = new Symbol("tAC");
                var tAB = new Symbol("tAB");
                
                var eqs = and(
                    tAB == tAC / 2,
                    Kinematic(sAC, vA, vC, a, tAC),
                    Kinematic(sAB, vA, vB, a, tAB)
                    );
                
                var vals = new List<Equation>() { vA == 10, vC == 30, tAC == 10 };

                eqs
                    .EliminateVariables(tAB, sAC, vB, sAB)
                    .IsolateVariable(a)
                    .AssertEqTo(a == (vC - vA) / tAC)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(a == 2);

                eqs
                    .EliminateVariables(vB, a, tAB, sAC)
                    .AssertEqTo(sAB == tAC / 4 * (2 * vA + (vC - vA) / 2))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(sAB == 75);
            }

            #endregion

            #region PSE Example 2.7
            {
                // s = 
                // u = 63
                // v =  0
                // a =
                // t =  2

                var s = new Symbol("s");
                var u = new Symbol("u");
                var v = new Symbol("v");
                var a = new Symbol("a");
                var t = new Symbol("t");
                
                var eqs = Kinematic(s, u, v, a, t);
                
                var vals = new List<Equation>() { u == 63, v == 0, t == 2.0 };

                eqs
                    .EliminateVariable(s)
                    .AssertEqTo(v == a * t + u)
                    .IsolateVariable(a)
                    .AssertEqTo(a == (v - u) / t)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(a == -31.5);

                eqs
                    .EliminateVariable(a)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(s == 63.0);
            }
            #endregion

            #region PSE Example 2.8
            {
                // car
                //
                // s1 =  
                // u1 = 45
                // v1 = 45
                // a1 =  0
                // t1 = 

                // officer
                //
                // s2 =  
                // u2 =  0
                // v2 = 
                // a2 =  3
                // t2

                var s1 = new Symbol("s1");
                var u1 = new Symbol("u1");
                var v1 = new Symbol("v1");
                var a1 = new Symbol("a1");
                var t1 = new Symbol("t1");

                var s2 = new Symbol("s2");
                var u2 = new Symbol("u2");
                var v2 = new Symbol("v2");
                var a2 = new Symbol("a2");
                var t2 = new Symbol("t2");
                                                
                var eqs = and(
                    u1 == v1,
                    s1 == s2,
                    t2 == t1 - 1,
                    Kinematic(s1, u1, v1, a1, t1),
                    Kinematic(s2, u2, v2, a2, t2));
                
                var vals = new List<Equation>() 
                {
                    v1 == 45.0,
                    u2 == 0,
                    a2 == 3
                };

                eqs
                    .EliminateVariables(s2, t1, a1, s1, v2, u1)
                    .IsolateVariable(t2)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(or(t2 == -0.96871942267131317, t2 == 30.968719422671313));
            }
            #endregion

            #region PSE Example 2.12

            {
                var yA = new Symbol("yA");
                var yB = new Symbol("yB");
                var yC = new Symbol("yC");
                var yD = new Symbol("yD");

                var tA = new Symbol("tA");
                var tB = new Symbol("tB");
                var tC = new Symbol("tC");
                var tD = new Symbol("tD");

                var vA = new Symbol("vA");
                var vB = new Symbol("vB");
                var vC = new Symbol("vC");
                var vD = new Symbol("vD");

                var a = new Symbol("a");
                                                
                var eqs = and(
                    Kinematic(yA, yB, vA, vB, a, tA, tB),
                    Kinematic(yB, yC, vB, vC, a, tB, tC),
                    Kinematic(yC, yD, vC, vD, a, tC, tD));
                
                var vals = new List<Equation>()
                {
                    yA == 50,
                    yC == 50,
                    vA == 20,
                    vB == 0,
                    a == -9.8,
                    tA == 0,
                    tD == 5
                };

                // velocity and position at t = 5.00 s

                DoubleFloat.tolerance = 0.000000001;

                eqs
                    .EliminateVariables(tB, tC, vC, yB, yD)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(or(vD == -29.000000000000004, vD == -29.000000000000007));

                eqs
                    .EliminateVariables(tB, tC, vC, yB, vD)
                    .IsolateVariable(yD)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(or(yD == 27.499999999, yD == 27.499999999));

                DoubleFloat.tolerance = null;
            }

            #endregion
            
            #region PSE Example 4.3
            {
                // A long-jumper leaves the ground at an angle of 20.0° above
                // the horizontal and at a speed of 11.0 m/s.

                // (a) How far does he jump in the horizontal direction?
                // (Assume his motion is equivalent to that of a particle.)

                // (b) What is the maximum height reached?

                var xA = new Symbol("xA");
                var xB = new Symbol("xB");
                var xC = new Symbol("xC");

                var yA = new Symbol("yA");
                var yB = new Symbol("yB");
                var yC = new Symbol("yC");

                var vxA = new Symbol("vxA");
                var vxB = new Symbol("vxB");
                var vxC = new Symbol("vxC");

                var vyA = new Symbol("vyA");
                var vyB = new Symbol("vyB");
                var vyC = new Symbol("vyC");

                var tAB = new Symbol("tAB");
                var tAC = new Symbol("tAC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    tAC == 2 * tAB,


                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,


                    vxC == vxA + ax * tAB,
                    vyC == vyA + ay * tAB,

                    xC == xA + vxA * tAC + ax * (tAC ^ 2) / 2,
                    yC == yA + vyA * tAC + ay * (tAC ^ 2) / 2

                    );

                var zeros = new List<Equation>() { xA == 0, yA == 0, ax == 0, vyB == 0 };

                var vals = new List<Equation>() { thA == (20).ToRadians(), vA == 11.0, ay == -9.8, Pi == Math.PI };

                eqs
                    .EliminateVariables(xB, yC, vxB, vxC, vyC, yB, tAC, vxA, vyA, tAB)
                    .SubstituteEqLs(zeros)
                    .AssertEqTo(xC == -2 * cos(thA) * sin(thA) * (vA ^ 2) / ay)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(xC == 7.9364592624562507);

                eqs
                    .EliminateVariables(xB, yC, vxB, vxC, vyC, xC, vxA, tAC, vyA, tAB)
                    .SubstituteEqLs(zeros)
                    .AssertEqTo(yB == -(sin(thA) ^ 2) * (vA ^ 2) / (2 * ay))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(yB == 0.72215873425009314);
            }

            #endregion
            
            #region PSE Example 4.3 KinematicObjectABC
            {
                // A long-jumper leaves the ground at an angle of 20.0° above
                // the horizontal and at a speed of 11.0 m/s.

                // (a) How far does he jump in the horizontal direction?
                // (Assume his motion is equivalent to that of a particle.)

                // (b) What is the maximum height reached?

                var obj = new KinematicObjectABC("obj");

                var yB = new Symbol("yB");
                var xC = new Symbol("xC");
                var ay = new Symbol("ay");
                var thA = new Symbol("thA");
                var vA = new Symbol("vA");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    obj.TrigEquationsA(),

                    obj.tAC == 2 * obj.tAB,

                    obj.EquationsAB(),
                    obj.EquationsAC()
                    
                    );

                var vals = new List<Equation>()
                {
                    obj.xA == 0,
                    obj.yA == 0,

                    obj.vA == vA,
                    obj.thA == thA,

                    obj.yB == yB,
                    obj.vyB == 0,

                    obj.xC == xC,

                    obj.ax == 0,
                    obj.ay == ay
                };

                var numerical_vals = new List<Equation>()
                {
                    thA == (20).ToRadians(),
                    vA == 11,
                    ay == -9.8,
                    Pi == Math.PI
                    
                };

                // xC
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(
                        obj.vxA, obj.vyA, obj.vyC, obj.vxC, obj.vxB,
                        obj.xB, yB, obj.yC,
                        obj.tAC, obj.tAB
                    )
                    
                    .AssertEqTo(xC == -2 * cos(thA) * sin(thA) * (vA ^ 2) / ay)

                    .SubstituteEqLs(numerical_vals)

                    .AssertEqTo(xC == 7.9364592624562507);

                // yB
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(
                        obj.tAB, obj.tAC,
                        obj.vxA, obj.vxB, obj.vxC, obj.vyC, obj.vyA,
                        obj.xB, xC, obj.yC
                    )
                    
                    .AssertEqTo(yB == -(sin(thA) ^ 2) * (vA ^ 2) / (2 * ay))

                    .SubstituteEqLs(numerical_vals)

                    .AssertEqTo(yB == 0.72215873425009314);

            }
            #endregion
            
            #region PSE 5E Example 4.5

            {
                var xA = new Symbol("xA");
                var xB = new Symbol("xB");
                var xC = new Symbol("xC");

                var yA = new Symbol("yA");
                var yB = new Symbol("yB");
                var yC = new Symbol("yC");

                var vxA = new Symbol("vxA");
                var vxB = new Symbol("vxB");
                var vxC = new Symbol("vxC");

                var vyA = new Symbol("vyA");
                var vyB = new Symbol("vyB");
                var vyC = new Symbol("vyC");

                var tAB = new Symbol("tAB");
                var tAC = new Symbol("tAC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");

                var vC = new Symbol("vC");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    // tAC == 2 * tAB,

                    // vxB == vxA + ax * tAB,
                    // vyB == vyA + ay * tAB,

                    // xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    // yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    vxC == vxA + ax * tAC,
                    vyC == vyA + ay * tAC,

                    // xC == xA + vxA * tAC + ax * (tAC ^ 2) / 2,
                    yC == yA + vyA * tAC + ay * (tAC ^ 2) / 2,

                    vC == sqrt((vxC ^ 2) + (vyC ^ 2)),

                    ay != 0
                );

                var zeros = new List<Equation>() { ax == 0, yC == 0 };
                var vals = new List<Equation>() { yA == 45, vA == 20, thA == (30).ToRadians(), ay == -9.8, Pi == Math.PI };

                DoubleFloat.tolerance = 0.00001;

                eqs
                    .EliminateVariables(vC, vxA, vxC, vyC, vyA)
                    .IsolateVariable(tAC)
                    .LogicalExpand().SimplifyEquation().SimplifyLogical()
                    .CheckVariable(ay)
                    .AssertEqTo(
                        or(
                            and(
                                tAC == -(sin(thA) * vA + sqrt((sin(thA) ^ 2) * (vA ^ 2) + 2 * ay * (yC - yA))) / ay,
                                ay != 0),
                            and(
                                tAC == -(sin(thA) * vA - sqrt((sin(thA) ^ 2) * (vA ^ 2) + 2 * ay * (yC - yA))) / ay,
                                ay != 0)))
                    .SubstituteEqLs(zeros)
                    .SubstituteEqLs(vals)
                    .AssertEqTo(or(tAC == 4.2180489012229376, tAC == -2.1772325746923267));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxC, vxA, vyA, vyC, tAC)
                    .SimplifyEquation().SimplifyLogical()
                    .CheckVariable(ay)
                    .AssertEqTo(
                        or(
                            and(
                                ay != 0,
                                vC == sqrt((cos(thA) ^ 2) * (vA ^ 2) + ((sin(thA) * vA - (sin(thA) * vA + sqrt((sin(thA) ^ 2) * (vA ^ 2) + -2 * ay * yA))) ^ 2))),
                            and(
                                ay != 0,
                                vC == sqrt((cos(thA) ^ 2) * (vA ^ 2) + ((sin(thA) * vA - (sin(thA) * vA - sqrt((sin(thA) ^ 2) * (vA ^ 2) + -2 * ay * yA))) ^ 2)))))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(or(vC == 35.805027579936315, vC == 35.805027579936322));

                DoubleFloat.tolerance = null;
            }

            #endregion
            
            #region PSE 5E Example 4.6
            {
                // An Alaskan rescue plane drops a package of emergency rations to 
                // a stranded party of explorers, as shown in Figure 4.13.
                // If the plane is traveling horizontally at 40.0 m/s and is
                // 100 m above the ground, where does the package strike the
                // ground relative to the point at which it was released?

                // What are the horizontal and vertical components
                // of the velocity of the package just before it hits the ground?
                
                var xA = new Symbol("xA");
                var xB = new Symbol("xB");

                var yA = new Symbol("yA");
                var yB = new Symbol("yB");

                var vxA = new Symbol("vxA");
                var vxB = new Symbol("vxB");

                var vyA = new Symbol("vyA");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    vxA != 0,

                    ay != 0
                );

                var vals = new List<Equation>() { xA == 0, yA == 100, vxA == 40, vyA == 0, yB == 0, ax == 0, ay == -9.8, Pi == Math.PI };

                var zeros = vals.Where(eq => eq.b == 0).ToList();

                DoubleFloat.tolerance = 0.00001;

                eqs
                    .EliminateVariables(vxB, vyB, tAB)
                    .IsolateVariable(xB)
                    .LogicalExpand().SimplifyEquation()
                    .CheckVariable(ay)
                    .CheckVariable(vxA).SimplifyLogical()
                    .SubstituteEq(ax == 0)        
                    .AssertEqTo(
                        or(
                            and(
                                vxA != 0,
                                xB == -(vxA ^ 2) * (-(- vyA / vxA + ay / (vxA ^ 2) * xA) + sqrt(((- vyA / vxA + ay * xA / (vxA ^ 2)) ^ 2) + 2 * ay * (vyA * xA / vxA - ay / 2 / (vxA ^ 2) * (xA ^ 2) - yA + yB) / (vxA ^ 2))) / ay,
                                ay / (vxA ^ 2) != 0,
                                ay != 0),
                            and(
                                vxA != 0,
                                xB == -(vxA ^ 2) * (-(-vyA / vxA + ay / (vxA ^ 2) * xA) - sqrt(((-vyA / vxA + ay * xA / (vxA ^ 2)) ^ 2) + 2 * ay * (vyA * xA / vxA - ay / 2 / (vxA ^ 2) * (xA ^ 2) - yA + yB) / (vxA ^ 2))) / ay,
                                ay / (vxA ^ 2) != 0,
                                ay != 0)))
                    .SubstituteEqLs(zeros)
                    .AssertEqTo(
                        or(
                            and(
                                vxA != 0,
                                xB == -1 / ay * (vxA ^ 2) * sqrt(-2 * ay * (vxA ^ -2) * yA),
                                ay / (vxA ^ 2) != 0,
                                ay != 0),
                            and(
                                vxA != 0,
                                xB == 1 / ay * (vxA ^ 2) * sqrt(-2 * ay * (vxA ^ -2) * yA),
                                ay / (vxA ^ 2) != 0,
                                ay != 0)))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(or(xB == 180.70158058105022, xB == -180.70158058105022));

                eqs
                    .EliminateVariables(vxB, xB, tAB)
                    .IsolateVariable(vyB)
                    .LogicalExpand().SimplifyEquation()
                    .CheckVariable(ay)
                    .AssertEqTo(
                        or(
                            and(
                                vyB == -1 * ay * sqrt(2 * (ay ^ -1) * ((ay ^ -1) / 2 * (vyA ^ 2) + -1 * yA + yB)),
                                vxA != 0,
                                ay != 0),
                            and(
                                vyB == ay * sqrt(2 * (ay ^ -1) * ((ay ^ -1) / 2 * (vyA ^ 2) + -1 * yA + yB)),
                                vxA != 0,
                                ay != 0)))
                    .SubstituteEqLs(zeros)
                    .AssertEqTo(
                        or(
                          and(
                              vyB == -ay * sqrt(-2 / ay * yA),
                              vxA != 0,
                              ay != 0),
                          and(
                              vyB == ay * sqrt(-2 / ay * yA),
                              vxA != 0,
                              ay != 0)))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(or(vyB == 44.271887242357309, vyB == -44.271887242357309));

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E Example 4.7

            {
                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var th = new Symbol("th");
                var d = new Symbol("d");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    cos(th) == (xB - xA) / d,
                    sin(th) == (yA - yB) / d,

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    yB != 0,

                    ay != 0
                );

                var vals = new List<Equation>() { xA == 0, yA == 0, vxA == 25, vyA == 0, ax == 0, ay == -9.8, th == (35).ToRadians(), Pi == Math.PI };

                var zeros = vals.Where(eq => eq.b == 0).ToList();

                DoubleFloat.tolerance = 0.00001;

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, vyB, d, yB, tAB)
                    .IsolateVariable(xB)
                    .LogicalExpand()
                    .CheckVariable(ay)
                    .SimplifyEquation()
                    .AssertEqTo(
                        or(
                            and(
                                xB == -(sin(th) / cos(th) + sqrt((cos(th) ^ -2) * (sin(th) ^ 2))) * (vxA ^ 2) / ay,
                                ay / (vxA ^ 2) != 0,
                                sin(th) / cos(th) * xB != 0,
                                ay != 0),
                            and(
                                xB == -(sin(th) / cos(th) - sqrt((cos(th) ^ -2) * (sin(th) ^ 2))) * (vxA ^ 2) / ay,
                                ay / (vxA ^ 2) != 0,
                                sin(th) / cos(th) * xB != 0,
                                ay != 0)))
                    .SubstituteEqLs(vals)
                    .SimplifyEquation()
                    .AssertEqTo(
                        or(
                            and(
                                xB == 89.312185996136435,
                                xB != 0),
                            and(
                                xB == 7.0805039835788038E-15,
                                xB != 0)));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, vyB, d, xB, tAB)
                    .IsolateVariable(yB)
                    .LogicalExpand()
                    .CheckVariable(yB)
                    .AssertEqTo(
                        and(
                            yB == 2 * (sin(th) ^ 2) * (vxA ^ 2) / ay / (cos(th) ^ 2),
                            -ay * (cos(th) ^ 2) / (sin(th) ^ 2) / (vxA ^ 2) / 2 != 0,
                            yB != 0,
                            ay != 0))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(
                        and(
                            yB == -62.537065888482395,
                            yB != 0));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, vyB, d, xB, yB)
                    .IsolateVariable(tAB)
                    .LogicalExpand().CheckVariable(ay).SimplifyEquation().SimplifyLogical()
                    .AssertEqTo(
                        or(
                            and(
                                tAB == -(sin(th) * vxA / cos(th) + sqrt((sin(th) ^ 2) * (vxA ^ 2) / (cos(th) ^ 2))) / ay,
                                ay != 0,
                                sin(th) * tAB * vxA / cos(th) != 0),
                            and(
                                tAB == -(sin(th) * vxA / cos(th) - sqrt((sin(th) ^ 2) * (vxA ^ 2) / (cos(th) ^ 2))) / ay,
                                ay != 0,
                                sin(th) * tAB * vxA / cos(th) != 0)))
                    .SubstituteEqLs(vals)
                    .CheckVariable(tAB).SimplifyEquation()
                    .AssertEqTo(
                        and(
                            tAB == 3.5724874398454571,
                            tAB != 0));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, d, tAB, xB, yB)
                    .IsolateVariable(vyB)
                    .LogicalExpand()
                    .CheckVariable(ay)
                    .SimplifyEquation()
                    .CheckVariable(ay)
                    .AssertEqTo(
                        or(
                            and(
                                vyB == -ay * (sin(th) * vxA / (ay * cos(th)) + sqrt((sin(th) ^ 2) * (vxA ^ 2) / ((ay ^ 2) * (cos(th) ^ 2)))),
                                sin(th) * vxA * vyB / (ay * cos(th)) != 0,
                                ay != 0),
                            and(
                                vyB == -ay * (sin(th) * vxA / (ay * cos(th)) - sqrt((sin(th) ^ 2) * (vxA ^ 2) / ((ay ^ 2) * (cos(th) ^ 2)))),
                                sin(th) * vxA * vyB / (ay * cos(th)) != 0,
                                ay != 0)))
                    .SubstituteEqLs(vals)
                    .CheckVariable(vyB)
                    .SimplifyEquation()
                    .CheckVariable(vyB)
                    .AssertEqTo(
                        and(
                            vyB == -35.010376910485483,
                            vyB != 0));

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E P4.9

            {
                // In a local bar, a customer slides an empty beer mug
                // down the counter for a refill. The bartender is momentarily 
                // distracted and does not see the mug, which slides
                // off the counter and strikes the floor 1.40 m from the
                // base of the counter. If the height of the counter is 
                // 0.860 m, (a) with what velocity did the mug leave the
                // counter and (b) what was the direction of the mug’s 
                // velocity just before it hit the floor?
                
                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var thB = new Symbol("thB");
                var vB = new Symbol("vB");

                var eqs = and(

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    tan(thB) == vyB / vxB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    xB != 0
                );

                var vals = new List<Equation>() { xA == 0, yA == 0.86, /* vxA */ vyA == 0, xB == 1.4, yB == 0, /* vxB vyB vB thB */ /* tAB */ ax == 0, ay == -9.8 };

                var zeros = vals.Where(eq => eq.b == 0).ToList();

                DoubleFloat.tolerance = 0.00001;

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(thB, vxB, vyB, tAB)
                    .IsolateVariable(vxA)
                    .LogicalExpand()
                    .AssertEqTo(
                        or(
                            and(
                                vxA == ay * (xB ^ 2) / yA / 4 * sqrt(-8 / ay * (xB ^ -2) * yA),
                                2 / ay * (xB ^ -2) * yA != 0,
                                xB != 0),
                            and(
                                vxA == -ay * (xB ^ 2) / yA / 4 * sqrt(-8 / ay * (xB ^ -2) * yA),
                                2 / ay * (xB ^ -2) * yA != 0,
                                xB != 0)))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(or(vxA == -3.3417722634053204, vxA == 3.3417722634053204));

                eqs
                    .SubstituteEqLs(zeros)
                    .EliminateVariables(vxB, vyB, tAB, vxA)
                    .LogicalExpand()
                    .CheckVariable(xB)
                    .SimplifyLogical()
                    .IsolateVariable(thB)
                    .AssertEqTo(
                        and(
                            -tan(thB) / ay != 0,
                            thB == new Atan(-2 * yA / xB),
                            xB != 0))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(
                        and(
                            0.1020408163265306 * tan(thB) != 0,
                            thB == -0.88760488150470185));

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region SumDifferenceFormulaFunc

            // sin(u) cos(v) - cos(u) sin(v) -> sin(u - v)

            Func<MathObject, MathObject> SumDifferenceFormulaFunc = elt =>
            {
                if (elt is Sum)
                {
                    var items = new List<MathObject>();

                    foreach (var item in (elt as Sum).elts)
                    {
                        if (
                            item is Product &&
                            (item as Product).elts[0] == -1 &&
                            (item as Product).elts[1] is Cos &&
                            (item as Product).elts[2] is Sin
                            )
                        {
                            var u_ = ((item as Product).elts[1] as Cos).args[0];
                            var v_ = ((item as Product).elts[2] as Sin).args[0];

                            Func<MathObject, bool> match = obj =>
                                obj is Product &&
                                (obj as Product).elts[0] is Cos &&
                                (obj as Product).elts[1] is Sin &&

                                ((obj as Product).elts[1] as Sin).args[0] == u_ &&
                                ((obj as Product).elts[0] as Cos).args[0] == v_;

                            if (items.Any(obj => match(obj)))
                            {
                                items = items.Where(obj => match(obj) == false).ToList();

                                items.Add(sin(u_ - v_));
                            }
                            else items.Add(item);
                        }
                        else items.Add(item);
                    }

                    return new Sum() { elts = items }.Simplify();
                }

                return elt;
            };

            {
                var u = new Symbol("u");
                var v = new Symbol("v");

                (sin(u) * cos(v) - cos(u) * sin(v))
                    .DeepSelect(SumDifferenceFormulaFunc)
                    .AssertEqTo(sin(u - v));
            }

            #endregion
            
            #region SumDifferenceFormulaFunc 

            // sin(u) cos(v) + cos(u) sin(v) -> sin(u + v)

            Func<MathObject, MathObject> SumDifferenceFormulaAFunc = elt =>
            {
                if (elt is Sum)
                {
                    var items = new List<MathObject>();

                    foreach (var item in (elt as Sum).elts)
                    {
                        if (
                            item is Product &&
                            (item as Product).elts[0] is Cos &&
                            (item as Product).elts[1] is Sin
                            )
                        {
                            var u_ = ((item as Product).elts[0] as Cos).args[0];
                            var v_ = ((item as Product).elts[1] as Sin).args[0];

                            Func<MathObject, bool> match = obj =>
                                obj is Product &&
                                (obj as Product).elts[0] is Cos &&
                                (obj as Product).elts[1] is Sin &&

                                ((obj as Product).elts[1] as Sin).args[0] == u_ &&
                                ((obj as Product).elts[0] as Cos).args[0] == v_;

                            if (items.Any(obj => match(obj)))
                            {
                                items = items.Where(obj => match(obj) == false).ToList();

                                items.Add(sin(u_ + v_));
                            }
                            else items.Add(item);
                        }
                        else items.Add(item);
                    }

                    return new Sum() { elts = items }.Simplify();
                }

                return elt;
            };

            {
                var u = new Symbol("u");
                var v = new Symbol("v");

                (sin(u) * cos(v) + cos(u) * sin(v))
                    .DeepSelect(SumDifferenceFormulaAFunc)
                    .AssertEqTo(sin(u + v));
            }

            #endregion
            
            #region DoubleAngleFormulaFunc

            // sin(u) cos(u) -> sin(2 u) / 2

            Func<MathObject, MathObject> DoubleAngleFormulaFunc =
                    elt =>
                    {
                        if (elt is Product)
                        {
                            var items = new List<MathObject>();

                            foreach (var item in (elt as Product).elts)
                            {
                                if (item is Sin)
                                {
                                    var sym = (item as Sin).args.First();

                                    if (items.Any(obj => (obj is Cos) && (obj as Cos).args.First() == sym))
                                    {
                                        items = items.Where(obj => ((obj is Cos) && (obj as Cos).args.First() == sym) == false).ToList();

                                        items.Add(sin(2 * sym) / 2);
                                    }
                                    else items.Add(item);
                                }

                                else if (item is Cos)
                                {
                                    var sym = (item as Cos).args.First();

                                    if (items.Any(obj => (obj is Sin) && (obj as Sin).args.First() == sym))
                                    {
                                        items = items.Where(obj => ((obj is Sin) && (obj as Sin).args.First() == sym) == false).ToList();

                                        items.Add(sin(2 * sym) / 2);
                                    }
                                    else items.Add(item);
                                }

                                else items.Add(item);

                            }
                            return new Product() { elts = items }.Simplify();
                        }
                        return elt;
                    };

            #endregion

            #region SinCosToTanFunc


            // sin(x) / cos(x) -> tan(x)

            Func<MathObject, MathObject> SinCosToTanFunc = elt =>
            {
                if (elt is Product)
                {
                    if ((elt as Product).elts.Any(obj1 =>
                            obj1 is Sin &&
                            (elt as Product).elts.Any(obj2 => obj2 == 1 / cos((obj1 as Sin).args[0]))))
                    {
                        var sin_ = (elt as Product).elts.First(obj1 =>
                            obj1 is Sin &&
                            (elt as Product).elts.Any(obj2 => obj2 == 1 / cos((obj1 as Sin).args[0])));

                        var arg = (sin_ as Sin).args[0];

                        return elt * cos(arg) / sin(arg) * tan(arg);
                    }

                    return elt;
                }

                return elt;
            };

            #endregion

            {
                var x = new Symbol("x");
                var y = new Symbol("y");

                (sin(x) / cos(x)).DeepSelect(SinCosToTanFunc)

                    .AssertEqTo(tan(x));

                (y * sin(x) / cos(x)).DeepSelect(SinCosToTanFunc)

                    .AssertEqTo(tan(x) * y);

                (sin(x) * sin(y) / cos(x) / cos(y))
                    .DeepSelect(SinCosToTanFunc)
                    .DeepSelect(SinCosToTanFunc)

                    .AssertEqTo(tan(x) * tan(y));
            }
            
            #region PSE 5E P4.11

            {
                // One strategy in a snowball fight is to throw a first snowball
                // at a high angle over level ground. While your opponent is watching
                // the first one, you throw a second one at a low angle and timed
                // to arrive at your opponent before or at the same time as the first one.

                // Assume both snowballs are thrown with a speed of 25.0 m/s.

                // The first one is thrown at an angle of 70.0° with respect to the horizontal. 

                // (a) At what angle should the second (lowangle) 
                // snowball be thrown if it is to land at the same
                // point as the first?

                // (b) How many seconds later should the second snowball 
                // be thrown if it is to land at the same time as the first?
                
                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");

                var thA = new Symbol("thA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");
                var tAC = new Symbol("tAC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var eqs = new And(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2
                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>() { xA == 0, yA == 0, /* vxA vyA */ vA == 25.0, /* thA == 70.0, */ /* xB == 20.497, */ /* yB */ /* vxB */ vyB == 0, /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    {
                        // thA = ... || thA = ...

                        var expr = eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(yB, vxA, vyA, vxB, tAB)
                            .DeepSelect(DoubleAngleFormulaFunc)
                            .IsolateVariable(thA);

                        // th_delta = ...

                        var th1 = ((expr as Or).args[0] as Equation).b;
                        var th2 = ((expr as Or).args[1] as Equation).b;

                        var th_delta = new Symbol("th_delta");

                        eqs
                            .Add(th_delta == (th1 - th2).AlgebraicExpand())
                            .SubstituteEqLs(zeros)

                            .EliminateVariables(yB, vxA, vyA, vxB, tAB)

                            .DeepSelect(DoubleAngleFormulaFunc)
                            .EliminateVariable(xB)

                            .AssertEqTo(th_delta == asin(sin(2 * thA)) - Pi / 2)

                            .SubstituteEq(thA == (70).ToRadians())
                            .SubstituteEq(Pi == Math.PI)

                            .AssertEqTo(th_delta == -0.87266462599716454)
                            ;
                    }

                    {
                        // tAB = ...

                        var tAB_eq = eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(yB, vxA, vyA, vxB, xB)
                            .IsolateVariable(tAB);

                        and(
                            or(thA == (20).ToRadians(), thA == (70).ToRadians()),
                            tAB_eq,
                            tAC == tAB * 2)
                            .LogicalExpand()
                            .EliminateVariables(thA, tAB)

                            .AssertEqTo(or(
                                tAC == -2 * sin(Pi / 9) * vA / ay,
                                tAC == -2 * sin(7 * Pi / 18) * vA / ay))

                            .SubstituteEqLs(vals)
                            .AssertEqTo(
                                or(
                                    tAC == 1.7450007312534115,
                                    tAC == 4.794350106050552));
                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion


            #region PSE 5E P4.13

            {
                // An artillery shell is fired with an initial velocity of 
                // 300 m/s at 55.0° above the horizontal. It explodes on a
                // mountainside 42.0 s after firing. What are the x and y
                // coordinates of the shell where it explodes, relative to its
                // firing point?
                
                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");

                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var eqs = and(
                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2
                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>() { xA == 0, yA == 0, /* vxA vyA */ vA == 300.0, thA == (55).ToRadians(), /* xB yB vxB vyB */ tAB == 42, ax == 0, ay == -9.8, Pi == Math.PI };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariable(vxA)
                            .EliminateVariable(vyA)

                            .AssertEqTo(
                                and(
                                    vxB == cos(thA) * vA,
                                    vyB == ay * tAB + sin(thA) * vA,
                                    xB == cos(thA) * tAB * vA,
                                    yB == ay * (tAB ^ 2) / 2 + sin(thA) * tAB * vA))

                            .SubstituteEqLs(vals)

                            .AssertEqTo(
                                and(
                                    vxB == 172.07293090531385,
                                    vyB == -165.85438671330249,
                                    xB == 7227.0630980231817,
                                    yB == 1677.7157580412968))

                            ;
                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E P4.15

            {
                // A projectile is fired in such a way that its horizontal
                // range is equal to three times its maximum height.
                //
                // What is the angle of projection? 
                // Give your answer to three significant figures.
                
                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");


                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");


                var xC = new Symbol("xC");
                var yC = new Symbol("yC");

                var vxC = new Symbol("vxC");
                var vyC = new Symbol("vyC");


                var tAB = new Symbol("tAB");
                var tBC = new Symbol("tBC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    xC - xA == 3 * yB,

                    tAB == tBC,


                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,


                    vxC == vxB + ax * tBC,
                    vyC == vyB + ay * tBC,

                    xC == xB + vxB * tBC + ax * (tBC ^ 2) / 2,
                    yC == yB + vyB * tBC + ay * (tBC ^ 2) / 2

                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>() 
                    { 
                        xA == 0, yA == 0, /* vxA vyA vA thA */ /* xB yB vxB */ vyB == 0, /* tAB tBC */ 
                        /* xC */ yC == 0,

                        ax == 0, ay == -9.8, Pi == Math.PI 
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    eqs
                        .SubstituteEqLs(zeros)
                        .EliminateVariables(xC, tAB, vxA, vyA, vxB, xB, yB, vxC, vyC, tBC)
                        .IsolateVariable(thA)
                        .AssertEqTo(thA == new Atan(new Integer(4) / 3));
                }

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E P4.17

            {
                // A cannon with a muzzle speed of 1000 m/s is used to
                // start an avalanche on a mountain slope. The target is 
                // 2000 m from the cannon horizontally and 800 m above
                // the cannon.
                //
                // At what angle, above the horizontal, should the cannon be fired?

                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");


                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");


                var xC = new Symbol("xC");
                var yC = new Symbol("yC");

                var vxC = new Symbol("vxC");
                var vyC = new Symbol("vyC");


                var tAB = new Symbol("tAB");
                var tBC = new Symbol("tBC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var phi = new Symbol("phi");

                var eqs = and(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2
                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>() 
                    { 
                        xA ==    0, yA ==   0, /* vxA vyA */ vA == 1000, /* thA */ 
                        xB == 2000, yB == 800.0, /* vxB vyB */ 
                        /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI 
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(vxA, vyA, vxB, vyB, tAB)

                            .MultiplyBothSidesBy(cos(thA) ^ 2).AlgebraicExpand()
                            .Substitute(cos(thA) ^ 2, (1 + cos(2 * thA)) / 2)
                            .DeepSelect(DoubleAngleFormulaFunc).AlgebraicExpand()
                            .AddToBothSides(-sin(2 * thA) * xB / 2)
                            .AddToBothSides(-yB / 2)
                            .MultiplyBothSidesBy(2 / xB).AlgebraicExpand()

                            // yB / xB = tan(phi)
                            // yB / xB = sin(phi) / cos(phi)

                            // phi = atan(yB / xB)

                            .Substitute(cos(2 * thA) * yB / xB, cos(2 * thA) * sin(phi) / cos(phi))
                            .MultiplyBothSidesBy(cos(phi)).AlgebraicExpand()
                            .DeepSelect(SumDifferenceFormulaFunc)
                            .IsolateVariable(thA)

                            .Substitute(phi, new Atan(yB / xB).Simplify())

                            .AssertEqTo(
                                or(
                                    thA == -(asin(ay * cos(atan(yB / xB)) * (vA ^ -2) * xB + -1 * cos(atan(yB / xB)) * yB / xB) - atan(yB / xB)) / 2,
                                    thA == -(-asin(ay * cos(atan(yB / xB)) * (vA ^ -2) * xB - cos(atan(yB / xB)) * yB / xB) - atan(yB / xB) + Pi) / 2))

                            .SubstituteEqLs(vals)

                            .AssertEqTo(
                                or(
                                    thA == 0.39034573609628065,
                                    thA == -1.5806356857788124))
                            ;
                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion
            
            #region PSE 5E P4.19

            {
                // A placekicker must kick a football from a point 36.0 m
                // (about 40 yards) from the goal, and half the crowd
                // hopes the ball will clear the crossbar, which is 3.05 m
                // high. When kicked, the ball leaves the ground with a
                // speed of 20.0 m/s at an angle of 53.0° to the horizontal.
                //
                // (a) By how much does the ball clear or fall short of
                //     clearing the crossbar ?
                //
                // (b) Does the ball approach the crossbar while still 
                //     rising or while falling ?
                
                Func <MathObject, MathObject> sqrt = obj => obj ^ (new Integer(1) / 2);

                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");


                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");
                
                var tAB = new Symbol("tAB");
                
                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var cleared_by = new Symbol("cleared_by");

                var goal_height = new Symbol("goal_height");
                
                var eqs = and(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,

                    cleared_by == yB - goal_height
                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        xA == 0, yA == 0, /* vxA vyA */ vA == 20, thA == (53).ToRadians(),
                        xB == 36, /* yB */ /* vxB vyB */ 
                        /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI,

                        goal_height == 3.05
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(vxA, vyA, vxB, vyB, tAB, yB)

                            .AssertEqTo(
                                cleared_by == -goal_height + sin(thA) / cos(thA) * xB + ay / 2 * (cos(thA) ^ -2) * (vA ^ -2) * (xB ^ 2)
                                )

                            .SubstituteEqLs(vals)

                            .AssertEqTo(cleared_by == 0.88921618776713007);
                    }

                    {
                        eqs
                            .SubstituteEqLs(zeros)

                            .EliminateVariables(cleared_by, vxA, vyA, vxB, tAB, yB)
                            .IsolateVariable(vyB)

                            .AssertEqTo(vyB == sin(thA) * vA + ay / cos(thA) / vA * xB)
                            
                            .SubstituteEqLs(vals)

                            .AssertEqTo(vyB == -13.338621888454744);
                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion
            
            #region PSE 5E P4.21

            {
                // A firefighter a distance d from a burning building directs 
                // a stream of water from a fire hose at angle θi above
                // the horizontal as in Figure P4.20.If the initial speed of
                // the stream is vi, at what height h does the water strike
                // the building?
                
                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");


                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");

                var tAB = new Symbol("tAB");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var d = new Symbol("d");
                var thi = new Symbol("thi");
                var vi = new Symbol("vi");
                var h = new Symbol("h");
                
                var eqs = and(

                    vxA == vA * cos(thA),
                    vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2
                    
                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        xA == 0, yA == 0, /* vxA vyA */ vA == vi, thA == thi,
                        xB == d, yB == h, /* vxB vyB */ 
                        /* tAB */ ax == 0, ay == -9.8, Pi == Math.PI
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();
                    
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(vxA, vyA, vxB, vyB, tAB)
                            
                            .SubstituteEqLs(vals.Where(eq => eq.b is Symbol).ToList())

                            .AssertEqTo(

                                h == d * sin(thi) / cos(thi) + ay * (d ^ 2) / (cos(thi) ^ 2) / (vi ^ 2) / 2
                                
                                );
                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion
            
            #region PSE 5E P4.23

            {
                // A basketball star covers 2.80 m horizontally in a jump to
                // dunk the ball. His motion through space can be modeled as 
                // that of a particle at a point called his center of mass. 
                // His center of mass is at elevation 1.02 m when he leaves 
                // the floor. It reaches a maximum height of 1.85 m above 
                // the floor and is at elevation 0.900 m when he touches down
                // again.

                // Determine:

                // (a) his time of flight (his “hang time”)

                // (b) his horizontal and (c) vertical velocity components at the instant of takeoff

                // (d) his takeoff angle. 

                // (e) For comparison, determine the hang time of a
                // whitetail deer making a jump with center-of-mass elevations
                // y_i = 1.20 m
                // y_max = 2.50 m
                // y_f = 0.700 m
                
                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");


                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");


                var tAB = new Symbol("tAB");


                var xC = new Symbol("xC");
                var yC = new Symbol("yC");

                var vxC = new Symbol("vxC");
                var vyC = new Symbol("vyC");


                var tBC = new Symbol("tBC");

                var tAC = new Symbol("tAC");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    //vxA == vA * cos(thA),
                    //vyA == vA * sin(thA),

                    vxB == vxA + ax * tAB,
                    vyB == vyA + ay * tAB,

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,
                    yB == yA + vyA * tAB + ay * (tAB ^ 2) / 2,


                    vxC == vxB + ax * tBC,
                    vyC == vyB + ay * tBC,

                    xC == xB + vxB * tBC + ax * (tBC ^ 2) / 2,
                    yC == yB + vyB * tBC + ay * (tBC ^ 2) / 2,

                    tAC == tAB + tBC,

                    // vyA / vxA == tan(thA),

                    tan(thA) == vyA / vxA,

                    ay != 0

                );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        xA == 0,    yA == 1.02, /* vxA vyA vA thA */
                        /* xB */    yB == 1.85, /* vxB            */ vyB == 0,
                        xC == 2.80, yC == 0.9,  /* vxC vyC        */

                        /* tAB tBC */ ax == 0, ay == -9.8, Pi == Math.PI
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    {
                        eqs
                            .SubstituteEqLs(zeros)

                            .EliminateVariables(thA, vxB, xB, vxC, vyC, vxA, vyA, tAB)

                            .CheckVariable(ay).SimplifyEquation().SimplifyLogical()

                            .EliminateVariable(tBC)

                            .LogicalExpand().SimplifyEquation().CheckVariable(ay).SimplifyLogical()

                            .AssertEqTo(

                                or(
                                    and(ay != 0, tAC == (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + -1 * (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))),
                                    and(ay != 0, tAC == (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))),
                                    and(ay != 0, tAC == -1 * (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + -1 * (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))),
                                    and(ay != 0, tAC == -1 * (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC)))))

                            .SubstituteEqLs(vals)

                            .AssertEqTo(

                                or(
                                    tAC == 0.028747849043843032,
                                    tAC == -0.85188272280886768,
                                    tAC == 0.85188272280886768,
                                    tAC == -0.028747849043843032));
                    }

                    {
                        eqs
                            .SubstituteEqLs(zeros)

                            .EliminateVariables(thA, vxB, vxC, xB)

                            .IsolateVariable(vxA)

                            .EliminateVariables(tAC, vyC, tAB, vyA)

                            .SimplifyEquation().CheckVariable(ay)

                            .EliminateVariable(tBC)

                            .LogicalExpand().SimplifyEquation().CheckVariable(ay).SimplifyLogical()

                            .AssertEqTo(

                                or(
                                    and(ay != 0, vxA == xC * ((-1 * sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)) + -1 * (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))) ^ -1)),
                                    and(ay != 0, vxA == xC * ((-1 * sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)) + (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))) ^ -1)),
                                    and(ay != 0, vxA == xC * ((sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)) + -1 * (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))) ^ -1)),
                                    and(ay != 0, vxA == xC * ((sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)) + (ay ^ -1) * sqrt(2 * ay * (-1 * yB + yC))) ^ -1))))

                            .SubstituteEqLs(vals)

                            .AssertEqTo(

                                or(
                                    vxA == 97.398591307814215,
                                    vxA == -3.286837407346058,
                                    vxA == 3.286837407346058,
                                    vxA == -97.398591307814215));
                    }

                    {
                        eqs
                            .SubstituteEqLs(zeros)

                            .EliminateVariables(thA, vxA, vxC, vyC, vxB, xB, tAB, tAC, tBC)

                            .SimplifyEquation().CheckVariable(ay).SimplifyLogical()

                            .IsolateVariable(vyA)

                            .LogicalExpand().SimplifyEquation().CheckVariable(ay)

                            .AssertEqTo(
                                or(
                                    and(ay != 0, vyA == ay * sqrt(-2 * (ay ^ -1) * (-1 * yA + yB))),
                                    and(ay != 0, vyA == -1 * ay * sqrt(-2 * (ay ^ -1) * (-1 * yA + yB)))))

                            .SubstituteEqLs(vals)

                            .AssertEqTo(
                                or(
                                    vyA == -4.0333608814486217,
                                    vyA == 4.0333608814486217));
                    }

                    {
                        eqs
                            .SubstituteEqLs(zeros)

                            .EliminateVariables(vxA, vyA, vxB, xB, vxC, tBC, tAB, vyC, tAC)

                            .LogicalExpand()
                            .SimplifyEquation()
                            .SimplifyLogical()
                            .CheckVariable(ay)

                            .AssertEqTo(

                                or(
                                    and(ay != 0, tan(thA) == -1 * (xC ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) * ((ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + -1 * sqrt(2 * (ay ^ -1) * (-1 * yB + yC)))),
                                    and(ay != 0, tan(thA) == -1 * (xC ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) * ((ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + sqrt(2 * (ay ^ -1) * (-1 * yB + yC)))),
                                    and(ay != 0, tan(thA) == (xC ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) * (-1 * (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + -1 * sqrt(2 * (ay ^ -1) * (-1 * yB + yC)))),
                                    and(ay != 0, tan(thA) == (xC ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) * (-1 * (ay ^ -1) * sqrt(-2 * ay * (-1 * yA + yB)) + sqrt(2 * (ay ^ -1) * (-1 * yB + yC))))

                                    ))

                            .IsolateVariable(thA)

                            .SubstituteEqLs(vals)

                            .AssertEqTo(
                                or(
                                    thA == 0.88702813023277882,
                                    thA == -0.041387227947930878,
                                    thA == -0.041387227947930878,
                                    thA == 0.88702813023277882));

                    }
                }

                DoubleFloat.tolerance = null;
            }

            #endregion

            #region PSE 5E E5.1

            {
                // A hockey puck having a mass of 0.30 kg slides on the horizontal, 
                // frictionless surface of an ice rink. Two forces act on
                // the puck, as shown in Figure 5.5.The force F1 has a magnitude 
                // of 5.0 N, and the force F2 has a magnitude of 8.0 N.

                // Determine both the magnitude and the direction of the puck’s acceleration.

                // Determine the components of a third force that,
                // when applied to the puck, causes it to have zero acceleration.
                
                var F = new Symbol("F");
                var th = new Symbol("th");

                var Fx = new Symbol("Fx");
                var Fy = new Symbol("Fy");


                var F1 = new Symbol("F1");
                var th1 = new Symbol("th1");

                var F1x = new Symbol("F1x");
                var F1y = new Symbol("F1y");
                

                var F2 = new Symbol("F2");
                var th2 = new Symbol("th2");

                var F2x = new Symbol("F2x");
                var F2y = new Symbol("F2y");


                var F3 = new Symbol("F3");
                var th3 = new Symbol("th3");

                var F3x = new Symbol("F3x");
                var F3y = new Symbol("F3y");


                var a = new Symbol("a");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var m = new Symbol("m");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    Fx == F * cos(th),
                    Fy == F * sin(th),

                    Fx == ax * m,
                    Fy == ay * m,

                    Fx == F1x + F2x + F3x,
                    Fy == F1y + F2y + F3y,

                    F1x == F1 * cos(th1), F1y == F1 * sin(th1),

                    F2x == F2 * cos(th2), F2y == F2 * sin(th2),

                    F3x == F3 * cos(th3), F3y == F3 * sin(th3),

                    a == sqrt((ax ^ 2) + (ay ^ 2))

                    );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {

                        m == 0.3,

                        F1 == 5.0, th1 == (-20).ToRadians(),
                        F2 == 8.0, th2 == (60).ToRadians(),

                        F3 == 0,

                        Pi == Math.PI
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // a 
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(ax, ay, Fx, Fy, F, F1x, F1y, F2x, F2y, F3x, F3y)
                            .DeepSelect(SinCosToTanFunc)
                            .EliminateVariable(th)
                            
                            .AssertEqTo(

                                a ==
                                    sqrt(
                                        ((cos(th1) * F1 + cos(th2) * F2) ^ 2) * (m ^ -2) +
                                        (cos(atan(((cos(th1) * F1 + cos(th2) * F2) ^ -1) * (F1 * sin(th1) + F2 * sin(th2)))) ^ -2) *
                                        ((cos(th1) * F1 + cos(th2) * F2) ^ 2) *
                                        (m ^ -2) *
                                        (sin(atan(((cos(th1) * F1 + cos(th2) * F2) ^ -1) * (F1 * sin(th1) + F2 * sin(th2)))) ^ 2))

                            )

                            .SubstituteEqLs(vals)

                            .Substitute(3, 3.0)

                            //.DispLong()

                            .AssertEqTo(a == 33.811874017759315);
                    }

                    // th
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(a, F, Fx, Fy, ax, ay, F1x, F1y, F2x, F2y, F3x, F3y)
                            .DeepSelect(SinCosToTanFunc)
                            .IsolateVariable(th)

                            .AssertEqTo(

                                th == atan((F1 * sin(th1) + F2 * sin(th2)) / (cos(th1) * F1 + cos(th2) * F2))

                                )

                            .SubstituteEqLs(vals)

                            .Substitute(3, 3.0)

                            .AssertEqTo(th == 0.54033704850428876);
                    }
                }

                {
                    var vals = new List<Equation>()
                    {

                        m == 0.3,

                        F1 == 5.0, th1 == (-20).ToRadians(),
                        F2 == 8.0, th2 == (60).ToRadians(),

                        ax == 0, ay == 0,

                        Pi == Math.PI
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();
                    
                    // F3x
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(F3, th3, F3y, F1x, F2x, Fx, F, Fy, F1y, F2y, a)
                            .IsolateVariable(F3x)
                            
                            .AssertEqTo(F3x == -1 * cos(th1) * F1 + -1 * cos(th2) * F2)

                            .SubstituteEqLs(vals)
                            
                            .AssertEqTo(F3x == -8.6984631039295444);
                    }


                    // F3y
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(F3, th3, F3x, F1x, F2x, Fx, F, Fy, F1y, F2y, a)
                            .IsolateVariable(F3y)

                            .AssertEqTo(F3y == -1 * F1 * sin(th1) + -1 * F2 * sin(th2))
                            
                            .SubstituteEqLs(vals)

                            // .DispLong()

                            .Substitute(3, 3.0)

                            .AssertEqTo(F3y == -5.2181025136471657);
                    }   
                }
            }

            #endregion
            
            #region PSE 5E E5.4

            {
                // A traffic light weighing 125 N hangs from a cable tied to two
                // other cables fastened to a support. The upper cables make
                // angles of 37.0° and 53.0° with the horizontal. Find the tension
                // in the three cables.
                
                var F = new Symbol("F");    // total force magnitude
                var th = new Symbol("th");  // total force direction

                var Fx = new Symbol("Fx");  // total force x-component
                var Fy = new Symbol("Fy");  // total force y-component


                var F1 = new Symbol("F1");
                var th1 = new Symbol("th1");

                var F1x = new Symbol("F1x");
                var F1y = new Symbol("F1y");


                var F2 = new Symbol("F2");
                var th2 = new Symbol("th2");

                var F2x = new Symbol("F2x");
                var F2y = new Symbol("F2y");


                var F3 = new Symbol("F3");
                var th3 = new Symbol("th3");

                var F3x = new Symbol("F3x");
                var F3y = new Symbol("F3y");


                var a = new Symbol("a");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var m = new Symbol("m");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    Fx == F * cos(th),
                    Fy == F * sin(th),

                    Fx == ax * m,
                    Fy == ay * m,

                    Fx == F1x + F2x + F3x,
                    Fy == F1y + F2y + F3y,

                    F1x == F1 * cos(th1), F1y == F1 * sin(th1),
                    F2x == F2 * cos(th2), F2y == F2 * sin(th2),
                    F3x == F3 * cos(th3), F3y == F3 * sin(th3),

                    a == sqrt((ax ^ 2) + (ay ^ 2))

                    );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {

                        // m 

                        /* F1 */    th1 == (180 - 37).ToRadians(),  // F1x F1y
                        /* F2 */    th2 == (53).ToRadians(),        // F2x F2y
                        F3 == 125,  th3 == (270).ToRadians(),       // F3x F3y
                        
                        ax == 0,    ay == 0,

                        Pi == Math.PI
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // F1
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(Fx, Fy, F, F1x, F1y, F2x, F2y, F2, F3x, F3y, a)
                            .IsolateVariable(F1)
                            
                            .AssertEqTo(F1 == (F3 * sin(th3) - cos(th3) * F3 * sin(th2) / cos(th2)) / (cos(th1) * sin(th2) / cos(th2) - sin(th1)))

                            .SubstituteEqLs(vals)

                            .AssertEqTo(F1 == 75.226877894006023);
                    }

                    // F2
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(Fx, Fy, F, F1x, F1y, F2x, F2y, F1, F3x, F3y, a)
                            .IsolateVariable(F2)

                            .AssertEqTo(F2 == (cos(th3) * F3 * sin(th1) / cos(th1) - F3 * sin(th3)) / (sin(th2) - cos(th2) * sin(th1) / cos(th1)))
                            
                            .SubstituteEqLs(vals)

                            .AssertEqTo(F2 == 99.829438755911582);
                    }

                }
            }

            #endregion
            
            #region PSE 5E E5.6

            {
                // A crate of mass m is placed on a frictionless inclined plane of
                // angle θ. (a) Determine the acceleration of the crate after it is
                // released.

                // (b) Suppose the crate is released from rest at the top of
                // the incline, and the distance from the front edge of the crate
                // to the bottom is d. How long does it take the front edge to
                // reach the bottom, and what is its speed just as it gets there?
                
                var F = new Symbol("F");    // total force magnitude
                var th = new Symbol("th");  // total force direction

                var Fx = new Symbol("Fx");  // total force x-component
                var Fy = new Symbol("Fy");  // total force y-component


                var F1 = new Symbol("F1");
                var th1 = new Symbol("th1");

                var F1x = new Symbol("F1x");
                var F1y = new Symbol("F1y");


                var F2 = new Symbol("F2");
                var th2 = new Symbol("th2");

                var F2x = new Symbol("F2x");
                var F2y = new Symbol("F2y");


                //var F3 = new Symbol("F3");
                //var th3 = new Symbol("th3");

                //var F3x = new Symbol("F3x");
                //var F3y = new Symbol("F3y");


                var a = new Symbol("a");

                var ax = new Symbol("ax");
                var ay = new Symbol("ay");

                var m = new Symbol("m");

                var n = new Symbol("n");
                var g = new Symbol("g");

                var incline = new Symbol("incline");

                var Pi = new Symbol("Pi");
                
                var xA = new Symbol("xA");
                var yA = new Symbol("yA");

                var vxA = new Symbol("vxA");
                var vyA = new Symbol("vyA");

                var vA = new Symbol("vA");
                var thA = new Symbol("thA");


                var xB = new Symbol("xB");
                var yB = new Symbol("yB");

                var vxB = new Symbol("vxB");
                var vyB = new Symbol("vyB");


                var tAB = new Symbol("tAB");
                
                var d = new Symbol("d");
                
                var eqs = and(

                    Fx == F * cos(th),
                    Fy == F * sin(th),

                    Fx == ax * m,
                    Fy == ay * m,

                    Fx == F1x + F2x, //+ F3x,
                    Fy == F1y + F2y, //+ F3y,

                    F1x == F1 * cos(th1), F1y == F1 * sin(th1),
                    F2x == F2 * cos(th2), F2y == F2 * sin(th2),
                    //F3x == F3 * cos(th3), F3y == F3 * sin(th3),

                    a == sqrt((ax ^ 2) + (ay ^ 2)),

                    xB == xA + vxA * tAB + ax * (tAB ^ 2) / 2,

                    vxB == vxA + ax * tAB,

                    d != 0

                    );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {

                        // m 
                        
                        F1 == n,        th1 == 90 * Pi / 180,            // F1x F1y
                        F2 == m * g,    th2 == 270 * Pi / 180 + incline, // F2x F2y
                        //F3 == 125,    th3 == (270).ToRadians(),        // F3x F3y
                        
                        /* ax */  ay == 0,

                        // Pi == Math.PI

                        xA == 0, xB == d, vxA == 0
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // ax
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(a, F)
                            .DeepSelect(SinCosToTanFunc)
                            .EliminateVariables(th, Fx, F1x, F2x, Fy, F1y, F2y, vxB, xB)
                            .SubstituteEqLs(vals)
                            .EliminateVariable(n)
                            .IsolateVariable(ax)

                            .AssertEqTo(
                                and(
                                    ax == g * sin(incline),
                                    d != 0));
                    }

                    // tAB
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(a, F)
                            .DeepSelect(SinCosToTanFunc)
                            .EliminateVariables(th, Fx, F1x, F2x, Fy, F1y, F2y, ax, vxB)
                            .SubstituteEqLs(vals)
                            .EliminateVariable(n)
                            .IsolateVariable(tAB).LogicalExpand().CheckVariable(d)
                            
                            .AssertEqTo(
                                or(
                                    and(
                                        tAB == - sqrt(2 * d * g * sin(incline)) / sin(incline) / g,
                                        - g * sin(incline) / 2 != 0,
                                        d != 0),
                                    and(
                                        tAB == sqrt(2 * d * g * sin(incline)) / sin(incline) / g,
                                        -g * sin(incline) / 2 != 0,
                                        d != 0))
                            );
                    }

                    // vxB
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .EliminateVariables(a, F)
                            .DeepSelect(SinCosToTanFunc)
                            .EliminateVariables(th, Fx, F1x, F2x, Fy, F1y, F2y, ax, tAB)
                            .SubstituteEqLs(vals)
                            .CheckVariable(d)
                            .EliminateVariable(n)

                            .AssertEqTo(
                                or(
                                    and(
                                        -g * sin(incline) / 2 != 0,
                                        vxB == -sqrt(2 * d * g * sin(incline)),
                                        d != 0
                                    ),
                                    and(
                                        -g * sin(incline) / 2 != 0,
                                        vxB == sqrt(2 * d * g * sin(incline)),
                                        d != 0))
                            );
                    }
                }
            }

            #endregion
            
            #region PSE 5E E5.9

            {
                // When two objects of unequal mass are hung vertically over a
                // frictionless pulley of negligible mass, as shown in Figure
                // 5.15a, the arrangement is called an Atwood machine. The device 
                // is sometimes used in the laboratory to measure the freefall
                // acceleration.
                //
                // Determine the magnitude of the acceleration of the two 
                // objects and the tension in the lightweight cord.
                
                var F_m1 = new Symbol("F_m1");      // total force on mass 1
                var F_m2 = new Symbol("F_m2");      // total force on mass 2

                var F1_m1 = new Symbol("F1_m1");    // force 1 on mass 1
                var F2_m1 = new Symbol("F2_m1");    // force 2 on mass 1

                var F1_m2 = new Symbol("F1_m2");    // force 1 on mass 2
                var F2_m2 = new Symbol("F2_m2");    // force 2 on mass 2

                var m1 = new Symbol("m1");
                var m2 = new Symbol("m2");

                var a = new Symbol("a");

                var T = new Symbol("T");

                var g = new Symbol("g");


                var eqs = and(

                    F_m1 == F1_m1 - F2_m1,
                    F_m2 == F2_m2 - F1_m2,

                    F_m1 == m1 * a,
                    F_m2 == m2 * a,

                    F1_m1 == T,
                    F2_m1 == m1 * g,

                    F1_m2 == T,
                    F2_m2 == m2 * g
                    
                    );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        m1 == 2.0, m2 == 4.0, g == 9.8
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // a
                    {
                        eqs
                            .EliminateVariables(F_m1, F_m2, F2_m1, F2_m2, F1_m1, F1_m2, T)
                            .IsolateVariable(a)
                            
                            .AssertEqTo(
                                a == (-1 * g * m1 + g * m2) / (m1 + m2)
                            )

                            .SubstituteEqLs(vals)
                            
                            .AssertEqTo(a == 3.2666666666666666);
                    }

                    // T
                    {
                        eqs
                            .EliminateVariables(F_m1, F_m2, F2_m1, F2_m2, F1_m1, F1_m2, a)
                            .IsolateVariable(T)
                            
                            .AssertEqTo(
                                T == 2 * g * m2 / (1 + m2 / m1)
                            )

                            .SubstituteEqLs(vals)
                            
                            .AssertEqTo(
                                T == 26.133333333333333
                            );
                    }
                    
                }
            }

            #endregion
            
            #region PSE 5E E5.10 - Acceleration of Two Objects Connected by a Cord

            {
                // A ball of mass m1 and a block of mass m2 are attached by a
                // lightweight cord that passes over a frictionless pulley of 
                // negligible mass, as shown in Figure 5.16a. The block lies 
                // on a frictionless incline of angle th. Find the magnitude 
                // of the acceleration of the two objects and the tension in the cord.

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

                var F1_m1 = new Symbol("F1_m1");        // force 1 on mass 1
                var F2_m1 = new Symbol("F2_m1");        // force 2 on mass 1
                var F3_m1 = new Symbol("F3_m1");        // force 3 on mass 1

                var th1_m1 = new Symbol("th1_m1");      // direction of force 1 on mass 1
                var th2_m1 = new Symbol("th2_m1");      // direction of force 2 on mass 1
                var th3_m1 = new Symbol("th3_m1");      // direction of force 3 on mass 1

                var F1x_m1 = new Symbol("F1x_m1");      // x-component of force 1 on mass 1
                var F2x_m1 = new Symbol("F2x_m1");      // x-component of force 2 on mass 1
                var F3x_m1 = new Symbol("F3x_m1");      // x-component of force 3 on mass 1

                var F1y_m1 = new Symbol("F1y_m1");      // y-component of force 1 on mass 1
                var F2y_m1 = new Symbol("F2y_m1");      // y-component of force 2 on mass 1
                var F3y_m1 = new Symbol("F3y_m1");      // y-component of force 3 on mass 1

                var Fx_m1 = new Symbol("Fx_m1");        // x-component of total force on mass 1
                var Fy_m1 = new Symbol("Fy_m1");        // y-component of total force on mass 1

                var ax_m1 = new Symbol("ax_m1");        // x-component of acceleration of mass 1
                var ay_m1 = new Symbol("ay_m1");        // y-component of acceleration of mass 1

                var m1 = new Symbol("m1");

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

                var F1_m2 = new Symbol("F1_m2");        // force 1 on mass 2
                var F2_m2 = new Symbol("F2_m2");        // force 2 on mass 2
                var F3_m2 = new Symbol("F3_m2");        // force 3 on mass 2

                var th1_m2 = new Symbol("th1_m2");      // direction of force 1 on mass 2
                var th2_m2 = new Symbol("th2_m2");      // direction of force 2 on mass 2
                var th3_m2 = new Symbol("th3_m2");      // direction of force 3 on mass 2

                var F1x_m2 = new Symbol("F1x_m2");      // x-component of force 1 on mass 2
                var F2x_m2 = new Symbol("F2x_m2");      // x-component of force 2 on mass 2
                var F3x_m2 = new Symbol("F3x_m2");      // x-component of force 3 on mass 2

                var F1y_m2 = new Symbol("F1y_m2");      // y-component of force 1 on mass 2
                var F2y_m2 = new Symbol("F2y_m2");      // y-component of force 2 on mass 2
                var F3y_m2 = new Symbol("F3y_m2");      // y-component of force 3 on mass 2

                var Fx_m2 = new Symbol("Fx_m2");        // x-component of total force on mass 2
                var Fy_m2 = new Symbol("Fy_m2");        // y-component of total force on mass 2

                var ax_m2 = new Symbol("ax_m2");        // x-component of acceleration of mass 2
                var ay_m2 = new Symbol("ay_m2");        // y-component of acceleration of mass 2

                var m2 = new Symbol("m2");

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

                var incline = new Symbol("incline");

                var T = new Symbol("T");                // tension in cable

                var g = new Symbol("g");                // gravity

                var n = new Symbol("n");                // normal force on block

                var a = new Symbol("a");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    ax_m2 == ay_m1,                     // the block moves right as the ball moves up

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

                    F1x_m1 == F1_m1 * cos(th1_m1),
                    F2x_m1 == F2_m1 * cos(th2_m1),
                    F3x_m1 == F3_m1 * cos(th3_m1),

                    F1y_m1 == F1_m1 * sin(th1_m1),
                    F2y_m1 == F2_m1 * sin(th2_m1),
                    F3y_m1 == F3_m1 * sin(th3_m1),

                    Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1,
                    Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1,

                    Fx_m1 == m1 * ax_m1,
                    Fy_m1 == m1 * ay_m1,

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

                    F1x_m2 == F1_m2 * cos(th1_m2),
                    F2x_m2 == F2_m2 * cos(th2_m2),
                    F3x_m2 == F3_m2 * cos(th3_m2),

                    F1y_m2 == F1_m2 * sin(th1_m2),
                    F2y_m2 == F2_m2 * sin(th2_m2),
                    F3y_m2 == F3_m2 * sin(th3_m2),

                    Fx_m2 == F1x_m2 + F2x_m2 + F3x_m2,
                    Fy_m2 == F1y_m2 + F2y_m2 + F3y_m2,

                    Fx_m2 == m2 * ax_m2,
                    Fy_m2 == m2 * ay_m2,

                    ////////////////////////////////////////////////////////////////////////////////
                    
                    a == ax_m2
                    
                    );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        ax_m1 == 0,                         // ball  moves vertically
                        ay_m2 == 0,                         // block moves horizontally

                        F1_m1 == T,
                        F2_m1 == m1 * g,
                        F3_m1 == 0,

                        th1_m1 == 90 * Pi / 180,            // force 1 is straight up
                        th2_m1 == 270 * Pi / 180,           // force 2 is straight down

                        F1_m2 == n,
                        F2_m2 == T,
                        F3_m2 == m2 * g,

                        th1_m2 == 90 * Pi / 180,            // force 1 is straight up
                        th2_m2 == 180 * Pi / 180,           // force 2 is straight down
                        th3_m2 == 270 * Pi / 180 + incline  // force 3 direction

                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();
                    
                    // a
                    {
                        eqs
                            .SubstituteEqLs(vals)

                            .EliminateVariables(
                                F1x_m1, F2x_m1, F3x_m1,
                                F1y_m1, F2y_m1, F3y_m1,
                                
                                Fx_m1, Fy_m1,

                                F1x_m2, F2x_m2, F3x_m2,
                                F1y_m2, F2y_m2, F3y_m2,

                                Fx_m2, Fy_m2,                                
                                
                                ax_m2, n, T, ay_m1
                            )
                            
                            .AssertEqTo(
                            
                                a == (g * m1 - g * m2 * sin(incline)) / (-m1 - m2)

                            )

                            .SubstituteEq(m1 == 10.0)
                            .SubstituteEq(m2 == 5.0)
                            .SubstituteEq(incline == 45 * Math.PI / 180)
                            .SubstituteEq(g == 9.8)

                            .AssertEqTo(a == -4.2234511814572784);
                    }
                    
                    // T
                    {
                        eqs
                            .SubstituteEqLs(vals)

                            .EliminateVariables(
                                F1x_m1, F2x_m1, F3x_m1,
                                F1y_m1, F2y_m1, F3y_m1,

                                Fx_m1, Fy_m1,

                                F1x_m2, F2x_m2, F3x_m2,
                                F1y_m2, F2y_m2, F3y_m2,

                                Fx_m2, Fy_m2,

                                ax_m2, n, a, ay_m1
                            )
                            
                            .IsolateVariable(T)
                            .RationalizeExpression()
                            
                            .AssertEqTo(

                                T == m1 * (-g * m2 - g * m2 * sin(incline)) / (-m1 - m2)

                            );
                    }
                }
            }

            #endregion

            #region PSE 5E E5.10 - Acceleration of Two Objects Connected by a Cord - Obj3

            {
                // A ball of mass m1 and a block of mass m2 are attached by a
                // lightweight cord that passes over a frictionless pulley of 
                // negligible mass, as shown in: 
                //
                //      http://i.imgur.com/XMHM6On.png
                //
                // The block lies on a frictionless incline of angle th.
                //
                // Find the magnitude of the acceleration of the two objects
                // and the tension in the cord.

                var bal = new Obj2("bal");
                var blk = new Obj3("blk");

                var th = new Symbol("th");

                var T = new Symbol("T");                // tension in cable
                var g = new Symbol("g");                // gravity
                var n = new Symbol("n");                // normal force on block
                var a = new Symbol("a");

                var m1 = new Symbol("m1");
                var m2 = new Symbol("m2");

                var Pi = new Symbol("Pi");

                var eqs = and(

                    blk.ax == bal.ay,                   // the block moves right as the ball moves up

                    a == blk.ax,

                    bal.Equations(),
                    blk.Equations()

                    );

                DoubleFloat.tolerance = 0.00001;

                var vals = new List<Equation>
                {
                    bal.ax == 0,

                    bal.m == m1,

                    bal.F1 == T,            bal.th1 == (90).ToRadians(),                // force 1 is straight up
                    bal.F2 == m1 * g,       bal.th2 == (270).ToRadians(),               // force 2 is straight down

                    blk.ay == 0,

                    blk.m == m2,

                    blk.F1 == n,            blk.th1 == (90).ToRadians(),                // force 1 is straight up
                    blk.F2 == T,            blk.th2 == (180).ToRadians(),               // force 2 is straight down
                    blk.F3 == m2 * g,       blk.th3 == (270).ToRadians() + th           // force 3 direction
                };

                // a
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        bal.ΣFx, bal.F1x, bal.F2x,
                        bal.ΣFy, bal.F1y, bal.F2y,

                        blk.ΣFx, blk.F1x, blk.F2x, blk.F3x,
                        blk.ΣFy, blk.F1y, blk.F2y, blk.F3y,

                        blk.ax, bal.ay,

                        T, n
                    )

                    .IsolateVariable(a)

                    .AssertEqTo(

                        a == (g * m1 - g * m2 * sin(th)) / (-m1 - m2)

                    );

                // T
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        bal.ΣFx, bal.F1x, bal.F2x,
                        bal.ΣFy, bal.F1y, bal.F2y,

                        blk.ΣFx, blk.F1x, blk.F2x, blk.F3x,
                        blk.ΣFy, blk.F1y, blk.F2y, blk.F3y,

                        blk.ax, bal.ay,

                        a, n
                    )

                .IsolateVariable(T)

                .RationalizeExpression()

                .AssertEqTo(

                    T == m1 * (-g * m2 - g * m2 * sin(th)) / (-m1 - m2)

                );
            }

            #endregion

            #region PSE 5E E5.12 - Experimental Determination of μs and μk

            {
                // The following is a simple method of measuring coefficients of
                // friction: Suppose a block is placed on a rough surface
                // inclined relative to the horizontal, as shown in Figure 5.19. 
                // The incline angle is increased until the block starts to move. 
                // Let us show that by measuring the critical angle θ_c at which this
                // slipping just occurs, we can obtain μs.

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

                var F1_m1 = new Symbol("F1_m1");        // force 1 on mass 1
                var F2_m1 = new Symbol("F2_m1");        // force 2 on mass 1
                var F3_m1 = new Symbol("F3_m1");        // force 3 on mass 1

                var th1_m1 = new Symbol("th1_m1");      // direction of force 1 on mass 1
                var th2_m1 = new Symbol("th2_m1");      // direction of force 2 on mass 1
                var th3_m1 = new Symbol("th3_m1");      // direction of force 3 on mass 1

                var F1x_m1 = new Symbol("F1x_m1");      // x-component of force 1 on mass 1
                var F2x_m1 = new Symbol("F2x_m1");      // x-component of force 2 on mass 1
                var F3x_m1 = new Symbol("F3x_m1");      // x-component of force 3 on mass 1

                var F1y_m1 = new Symbol("F1y_m1");      // y-component of force 1 on mass 1
                var F2y_m1 = new Symbol("F2y_m1");      // y-component of force 2 on mass 1
                var F3y_m1 = new Symbol("F3y_m1");      // y-component of force 3 on mass 1

                var Fx_m1 = new Symbol("Fx_m1");        // x-component of total force on mass 1
                var Fy_m1 = new Symbol("Fy_m1");        // y-component of total force on mass 1

                var ax_m1 = new Symbol("ax_m1");        // x-component of acceleration of mass 1
                var ay_m1 = new Symbol("ay_m1");        // y-component of acceleration of mass 1

                var m1 = new Symbol("m1");

                ////////////////////////////////////////////////////////////////////////////////
                
                var incline = new Symbol("incline");
                
                var f_s = new Symbol("f_s");            // force due to static friction
                
                var g = new Symbol("g");                // gravity

                var n = new Symbol("n");                // normal force on block

                var a = new Symbol("a");

                var Pi = new Symbol("Pi");

                var mu_s = new Symbol("mu_s");          // coefficient of static friction

                var eqs = and(
                    
                    F1x_m1 == F1_m1 * cos(th1_m1),
                    F2x_m1 == F2_m1 * cos(th2_m1),
                    F3x_m1 == F3_m1 * cos(th3_m1),

                    F1y_m1 == F1_m1 * sin(th1_m1),
                    F2y_m1 == F2_m1 * sin(th2_m1),
                    F3y_m1 == F3_m1 * sin(th3_m1),

                    Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1,
                    Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1,

                    Fx_m1 == m1 * ax_m1,
                    Fy_m1 == m1 * ay_m1,

                    f_s == mu_s * n
                    
                    );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        ax_m1 == 0,                         
                        ay_m1 == 0,                         
                        
                        F1_m1 == n,                     
                        F2_m1 == f_s,
                        F3_m1 == m1 * g,

                        th1_m1 == 90 * Pi / 180,            // force 1 is straight up
                        th2_m1 == 180 * Pi / 180,           // force 2 is straight down
                        th3_m1 == 270 * Pi / 180 + incline  // force 3 direction 
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // mu_s
                    {
                        eqs
                            .SubstituteEqLs(vals)

                            .EliminateVariables(
                                F1x_m1, F2x_m1, F3x_m1,
                                F1y_m1, F2y_m1, F3y_m1,
                                Fx_m1, Fy_m1,
                                f_s, n
                            )
                            .IsolateVariable(mu_s)

                            .DeepSelect(SinCosToTanFunc)

                            .AssertEqTo(mu_s == tan(incline));
                    }
                }
            }

            #endregion
            
            #region PSE 5E E5.13 - The Sliding Hockey Puck

            {
                // A hockey puck on a frozen pond is given an initial speed of
                // 20.0  m/s. If the puck always remains on the ice and slides
                // 115 m before coming to rest, determine the coefficient of
                // kinetic friction between the puck and ice.
                
                ////////////////////////////////////////////////////////////////////////////////

                var s = new Symbol("s");                // displacement
                var u = new Symbol("u");                // initial velocity
                var v = new Symbol("v");                // final velocity
                var a = new Symbol("a");                // acceleration
                var t = new Symbol("t");                // time elapsed
                
                var F1_m1 = new Symbol("F1_m1");        // force 1 on mass 1
                var F2_m1 = new Symbol("F2_m1");        // force 2 on mass 1
                var F3_m1 = new Symbol("F3_m1");        // force 3 on mass 1

                var th1_m1 = new Symbol("th1_m1");      // direction of force 1 on mass 1
                var th2_m1 = new Symbol("th2_m1");      // direction of force 2 on mass 1
                var th3_m1 = new Symbol("th3_m1");      // direction of force 3 on mass 1

                var F1x_m1 = new Symbol("F1x_m1");      // x-component of force 1 on mass 1
                var F2x_m1 = new Symbol("F2x_m1");      // x-component of force 2 on mass 1
                var F3x_m1 = new Symbol("F3x_m1");      // x-component of force 3 on mass 1

                var F1y_m1 = new Symbol("F1y_m1");      // y-component of force 1 on mass 1
                var F2y_m1 = new Symbol("F2y_m1");      // y-component of force 2 on mass 1
                var F3y_m1 = new Symbol("F3y_m1");      // y-component of force 3 on mass 1

                var Fx_m1 = new Symbol("Fx_m1");        // x-component of total force on mass 1
                var Fy_m1 = new Symbol("Fy_m1");        // y-component of total force on mass 1

                var ax_m1 = new Symbol("ax_m1");        // x-component of acceleration of mass 1
                var ay_m1 = new Symbol("ay_m1");        // y-component of acceleration of mass 1

                var m1 = new Symbol("m1");

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

                // var incline = new Symbol("incline");
                
                var f_s = new Symbol("f_s");            // force due to static friction

                var f_k = new Symbol("f_k");            // force due to kinetic friction

                var g = new Symbol("g");                // gravity

                var n = new Symbol("n");                // normal force on block

                // var a = new Symbol("a");

                var Pi = new Symbol("Pi");

                var mu_s = new Symbol("mu_s");          // coefficient of static friction

                var mu_k = new Symbol("mu_k");          // coefficient of kinetic friction
                
                var eqs = and(

                    a == ax_m1,

                    v == u + a * t,
                    s == (u + v) * t / 2,
                    
                    F1x_m1 == F1_m1 * cos(th1_m1),
                    F2x_m1 == F2_m1 * cos(th2_m1),
                    F3x_m1 == F3_m1 * cos(th3_m1),

                    F1y_m1 == F1_m1 * sin(th1_m1),
                    F2y_m1 == F2_m1 * sin(th2_m1),
                    F3y_m1 == F3_m1 * sin(th3_m1),

                    Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1,
                    Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1,

                    Fx_m1 == m1 * ax_m1,
                    Fy_m1 == m1 * ay_m1,

                    f_s == mu_s * n,
                    f_k == mu_k * n
                    
                    );
                
                DoubleFloat.tolerance = 0.00001;

                {
                    var symbolic_vals = new List<Equation>()
                    {
                        F1_m1 == n,
                        F2_m1 == f_k,
                        F3_m1 == m1 * g,

                        th1_m1 == 90 * Pi / 180,            // force 1 is straight up
                        th2_m1 == 180 * Pi / 180,           // force 2 is left
                        th3_m1 == 270 * Pi / 180            // force 3 is straight down
                    };

                    var vals = new List<Equation>()
                    {
                        //ax_m1 == 0,
                        ay_m1 == 0,

                        s == 115,
                        u == 20,
                        v == 0,

                        g == 9.8
                    };
                    
                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // mu_k
                    {
                        eqs
                            .SubstituteEqLs(zeros)
                            .SubstituteEqLs(symbolic_vals)

                            .EliminateVariables(
                                t,
                                F1x_m1, F2x_m1, F3x_m1,
                                F1y_m1, F2y_m1, F3y_m1,

                                Fx_m1, Fy_m1,

                                f_s, f_k,

                                n,

                                ax_m1, a

                                )

                            .IsolateVariable(mu_k)
                            
                            .AssertEqTo(    mu_k == (u ^ 2) / s / g / 2    )

                            .SubstituteEqLs(vals)
                            
                            .AssertEqTo(    mu_k == 0.17746228926353147    );
                    }
                }
            }

            #endregion
            
            #region PSE 5E E5.14 - Acceleration of Two Connected Objects When Friction Is Present

            {
                // A block of mass m1 on a rough, horizontal surface is connected
                // to a ball of mass m2 by a lightweight cord over a lightweight,
                // frictionless pulley, as shown:
                //
                // http://i.imgur.com/0fHOmGJ.png
                //
                // A force of magnitude F at an angle th with the horizontal is
                // applied to the block as shown. The coefficient of kinetic
                // friction between the block and surface is mu_k.
                // 
                // Determine the magnitude of the acceleration of the two objects.
                
                ////////////////////////////////////////////////////////////////////////////////

                var F1_m1 = new Symbol("F1_m1");        // force 1 on mass 1
                var F2_m1 = new Symbol("F2_m1");        // force 2 on mass 1
                var F3_m1 = new Symbol("F3_m1");        // force 3 on mass 1
                var F4_m1 = new Symbol("F4_m1");        // force 4 on mass 1
                var F5_m1 = new Symbol("F5_m1");        // force 5 on mass 1

                var th1_m1 = new Symbol("th1_m1");      // direction of force 1 on mass 1
                var th2_m1 = new Symbol("th2_m1");      // direction of force 2 on mass 1
                var th3_m1 = new Symbol("th3_m1");      // direction of force 3 on mass 1
                var th4_m1 = new Symbol("th4_m1");      // direction of force 4 on mass 1
                var th5_m1 = new Symbol("th5_m1");      // direction of force 5 on mass 1

                var F1x_m1 = new Symbol("F1x_m1");      // x-component of force 1 on mass 1
                var F2x_m1 = new Symbol("F2x_m1");      // x-component of force 2 on mass 1
                var F3x_m1 = new Symbol("F3x_m1");      // x-component of force 3 on mass 1
                var F4x_m1 = new Symbol("F4x_m1");      // x-component of force 4 on mass 1
                var F5x_m1 = new Symbol("F5x_m1");      // x-component of force 5 on mass 1

                var F1y_m1 = new Symbol("F1y_m1");      // y-component of force 1 on mass 1
                var F2y_m1 = new Symbol("F2y_m1");      // y-component of force 2 on mass 1
                var F3y_m1 = new Symbol("F3y_m1");      // y-component of force 3 on mass 1
                var F4y_m1 = new Symbol("F4y_m1");      // y-component of force 4 on mass 1
                var F5y_m1 = new Symbol("F5y_m1");      // y-component of force 5 on mass 1

                var Fx_m1 = new Symbol("Fx_m1");        // x-component of total force on mass 1
                var Fy_m1 = new Symbol("Fy_m1");        // y-component of total force on mass 1

                var ax_m1 = new Symbol("ax_m1");        // x-component of acceleration of mass 1
                var ay_m1 = new Symbol("ay_m1");        // y-component of acceleration of mass 1

                var m1 = new Symbol("m1");

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

                var F1_m2 = new Symbol("F1_m2");        // force 1 on mass 2
                var F2_m2 = new Symbol("F2_m2");        // force 2 on mass 2
                
                var th1_m2 = new Symbol("th1_m2");      // direction of force 1 on mass 2
                var th2_m2 = new Symbol("th2_m2");      // direction of force 2 on mass 2
                
                var F1x_m2 = new Symbol("F1x_m2");      // x-component of force 1 on mass 2
                var F2x_m2 = new Symbol("F2x_m2");      // x-component of force 2 on mass 2
                
                var F1y_m2 = new Symbol("F1y_m2");      // y-component of force 1 on mass 2
                var F2y_m2 = new Symbol("F2y_m2");      // y-component of force 2 on mass 2
                
                var Fx_m2 = new Symbol("Fx_m2");        // x-component of total force on mass 2
                var Fy_m2 = new Symbol("Fy_m2");        // y-component of total force on mass 2

                var ax_m2 = new Symbol("ax_m2");        // x-component of acceleration of mass 2
                var ay_m2 = new Symbol("ay_m2");        // y-component of acceleration of mass 2

                var m2 = new Symbol("m2");

                ////////////////////////////////////////////////////////////////////////////////
                
                var F = new Symbol("F");                // force applied at angle on block
                var th = new Symbol("th");              // angle of force applied on block
                var T = new Symbol("T");                // tension in cable
                var g = new Symbol("g");                // gravity
                var n = new Symbol("n");                // normal force on block

                var a = new Symbol("a");

                var Pi = new Symbol("Pi");
                                
                var f_k = new Symbol("f_k");            // force due to kinetic friction
                
                var mu_k = new Symbol("mu_k");          // coefficient of kinetic friction

                var eqs = and(
                    
                    ax_m1 == ay_m2,                     // the block moves right as the ball moves up

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

                    F1x_m1 == F1_m1 * cos(th1_m1),
                    F2x_m1 == F2_m1 * cos(th2_m1),
                    F3x_m1 == F3_m1 * cos(th3_m1),
                    F4x_m1 == F4_m1 * cos(th4_m1),
                    F5x_m1 == F5_m1 * cos(th5_m1),

                    F1y_m1 == F1_m1 * sin(th1_m1),
                    F2y_m1 == F2_m1 * sin(th2_m1),
                    F3y_m1 == F3_m1 * sin(th3_m1),
                    F4y_m1 == F4_m1 * sin(th4_m1),
                    F5y_m1 == F5_m1 * sin(th5_m1),

                    Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1 + F4x_m1 + F5x_m1,
                    Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1 + F4y_m1 + F5y_m1,

                    Fx_m1 == m1 * ax_m1,
                    Fy_m1 == m1 * ay_m1,

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

                    F1x_m2 == F1_m2 * cos(th1_m2),
                    F2x_m2 == F2_m2 * cos(th2_m2),

                    F1y_m2 == F1_m2 * sin(th1_m2),
                    F2y_m2 == F2_m2 * sin(th2_m2),

                    Fx_m2 == F1x_m2 + F2x_m2,
                    Fy_m2 == F1y_m2 + F2y_m2,

                    Fx_m2 == m2 * ax_m2,
                    Fy_m2 == m2 * ay_m2,

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

                    f_k == mu_k * n,

                    a == ax_m1

                    );
                
                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        ay_m1 == 0,                                     // block moves horizontally
                        ax_m2 == 0,                                     // ball moves vertically
                        
                        F1_m1 == F,         th1_m1 == th,               // force applied at angle
                        F2_m1 == n,         th2_m1 == 90 * Pi / 180,    // normal force is straight up
                        F3_m1 == T,         th3_m1 == 180 * Pi / 180,   // force due to cord is left
                        F4_m1 == f_k,       th4_m1 == 180 * Pi / 180,   // force due to friction is left
                        F5_m1 == m1 * g,    th5_m1 == 270 * Pi / 180,   // force due to gravity is down
                        
                        F1_m2 == T,         th1_m2 == 90 * Pi / 180,    // force due to cord is up
                        F2_m2 == m2 * g,    th2_m2 == 270 * Pi / 180    // force due to gravity is down                                
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // a
                    {
                        eqs
                            .SubstituteEqLs(vals)

                            .EliminateVariables(
                                ax_m1,

                                Fx_m1, Fy_m1,
                                Fx_m2, Fy_m2,

                                F1x_m1, F2x_m1, F3x_m1, F4x_m1, F5x_m1,
                                F1y_m1, F2y_m1, F3y_m1, F4y_m1, F5y_m1,

                                F1x_m2, F2x_m2,
                                F1y_m2, F2y_m2,

                                T, f_k, n,

                                ay_m2
                            )
                            
                            .AssertEqTo(

                                a == (g * m2 + g * m1 * mu_k - F * mu_k * sin(th) - cos(th) * F) / (-m1 - m2)

                            );
                    }
                }
            }

            #endregion
                                    
            #region PSE 5E E5.14 - Acceleration of Two Connected Objects When Friction Is Present - Obj5

            {
                // A block of mass m1 on a rough, horizontal surface is connected
                // to a ball of mass m2 by a lightweight cord over a lightweight,
                // frictionless pulley, as shown:
                //
                // http://i.imgur.com/0fHOmGJ.png
                //
                // A force of magnitude F at an angle th with the horizontal is
                // applied to the block as shown. The coefficient of kinetic
                // friction between the block and surface is mu_k.
                // 
                // Determine the magnitude of the acceleration of the two objects.

                var blk = new Obj5("blk");
                var bal = new Obj3("bal");
                
                var F = new Symbol("F");                // force applied at angle on block
                var th = new Symbol("th");              // angle of force applied on block
                var T = new Symbol("T");                // tension in cable
                var g = new Symbol("g");                // gravity
                var n = new Symbol("n");                // normal force on block

                var a = new Symbol("a");

                var Pi = new Symbol("Pi");

                var f_k = new Symbol("f_k");            // force due to kinetic friction

                var mu_k = new Symbol("mu_k");          // coefficient of kinetic friction

                var m1 = new Symbol("m1");
                var m2 = new Symbol("m2");

                var eqs = and(

                    blk.ax == bal.ay,                   // the block moves right as the ball moves up

                    blk.Equations(),
                    bal.Equations(),

                    f_k == mu_k * n,

                    a == blk.ax

                    );
                
                var vals = new List<Equation>()
                {
                    blk.ay == 0,                                        // block moves horizontally
                        
                    blk.F1 == F,            blk.th1 == th,              // block moves horizontally
                    blk.F2 == n,            blk.th2 == 90 * Pi / 180,   // normal force is straight up
                    blk.F3 == T,            blk.th3 == 180 * Pi / 180,  // force due to cord is left
                    blk.F4 == f_k,          blk.th4 == 180 * Pi / 180,  // force due to friction is left
                    blk.F5 == blk.m * g,    blk.th5 == 270 * Pi / 180,  // force due to gravity is down

                    bal.ax == 0,                                        // ball moves vertically

                    bal.F1 == T,            bal.th1 == 90 * Pi / 180,   // force due to cord is up
                    bal.F2 == bal.m * g,    bal.th2 == 270 * Pi / 180,  // force due to gravity is down
                    bal.F3 == 0,

                    blk.m == m1,
                    bal.m == m2
                };

                // a
                
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        blk.ax,

                        blk.ΣFx, blk.ΣFy,
                        bal.ΣFx, bal.ΣFy,

                        blk.F1x, blk.F2x, blk.F3x, blk.F4x, blk.F5x,
                        blk.F1y, blk.F2y, blk.F3y, blk.F4y, blk.F5y,

                        bal.F1x, bal.F2x, bal.F3x,
                        bal.F1y, bal.F2y, bal.F3y,

                        T, f_k, n,

                        bal.ay
                    )

                    .AssertEqTo(

                        a == (g * m2 + g * m1 * mu_k - F * mu_k * sin(th) - cos(th) * F) / (-m1 - m2)

                    );
                
            }

            #endregion
            
            #region PSE 5E P5.25

            {
                // A bag of cement of weight F_g hangs from three wires as
                // shown in http://i.imgur.com/f5JpLjY.png
                //  
                // Two of the wires make angles th1 and th2 with the horizontal.
                // If the system is in equilibrium, show that the tension in the
                // left -hand wire is:
                //
                //          T1 == F_g cos(th2) / sin(th1 + th2)
                
                ////////////////////////////////////////////////////////////////////////////////

                var F1_m1 = new Symbol("F1_m1");        // force 1 on mass 1
                var F2_m1 = new Symbol("F2_m1");        // force 2 on mass 1
                var F3_m1 = new Symbol("F3_m1");        // force 3 on mass 1

                var th1_m1 = new Symbol("th1_m1");      // direction of force 1 on mass 1
                var th2_m1 = new Symbol("th2_m1");      // direction of force 2 on mass 1
                var th3_m1 = new Symbol("th3_m1");      // direction of force 3 on mass 1

                var F1x_m1 = new Symbol("F1x_m1");      // x-component of force 1 on mass 1
                var F2x_m1 = new Symbol("F2x_m1");      // x-component of force 2 on mass 1
                var F3x_m1 = new Symbol("F3x_m1");      // x-component of force 3 on mass 1

                var F1y_m1 = new Symbol("F1y_m1");      // y-component of force 1 on mass 1
                var F2y_m1 = new Symbol("F2y_m1");      // y-component of force 2 on mass 1
                var F3y_m1 = new Symbol("F3y_m1");      // y-component of force 3 on mass 1

                var Fx_m1 = new Symbol("Fx_m1");        // x-component of total force on mass 1
                var Fy_m1 = new Symbol("Fy_m1");        // y-component of total force on mass 1

                var ax_m1 = new Symbol("ax_m1");        // x-component of acceleration of mass 1
                var ay_m1 = new Symbol("ay_m1");        // y-component of acceleration of mass 1

                var m1 = new Symbol("m1");

                ////////////////////////////////////////////////////////////////////////////////
                
                var g = new Symbol("g");                // gravity
                
                var a = new Symbol("a");

                var Pi = new Symbol("Pi");

                var T1 = new Symbol("T1");
                var T2 = new Symbol("T2");
                var T3 = new Symbol("T3");

                var th1 = new Symbol("th1");
                var th2 = new Symbol("th2");
                
                var eqs = and(
                    
                    F1x_m1 == F1_m1 * cos(th1_m1),
                    F2x_m1 == F2_m1 * cos(th2_m1),
                    F3x_m1 == F3_m1 * cos(th3_m1),

                    F1y_m1 == F1_m1 * sin(th1_m1),
                    F2y_m1 == F2_m1 * sin(th2_m1),
                    F3y_m1 == F3_m1 * sin(th3_m1),

                    Fx_m1 == F1x_m1 + F2x_m1 + F3x_m1,
                    Fy_m1 == F1y_m1 + F2y_m1 + F3y_m1,

                    Fx_m1 == m1 * ax_m1,
                    Fy_m1 == m1 * ay_m1
                    
                    );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        ax_m1 == 0,
                        ay_m1 == 0,
                        
                        F1_m1 == T2,
                        F2_m1 == T1,
                        F3_m1 == m1 * g,

                        th1_m1 == th2,                              
                        th2_m1 == 180 * Pi / 180 - th1,             
                        th3_m1 == 270 * Pi / 180
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // T1
                    {
                        eqs
                            .SubstituteEqLs(vals)

                            .EliminateVariables(
                            
                                F1x_m1, F2x_m1, F3x_m1,
                                F1y_m1, F2y_m1, F3y_m1,

                                Fx_m1, Fy_m1,

                                T2
                            )

                            .IsolateVariable(T1)
                            
                            .RationalizeExpression()

                            .DeepSelect(SumDifferenceFormulaAFunc)

                            .AssertEqTo(
                                T1 == cos(th2) * g * m1 / sin(th1 + th2)
                            );
                    }
                }
            }

            #endregion
            
            #region PSE 5E P5.25 Obj

            {
                // A bag of cement of weight F_g hangs from three wires as
                // shown in http://i.imgur.com/f5JpLjY.png
                //  
                // Two of the wires make angles th1 and th2 with the horizontal.
                // If the system is in equilibrium, show that the tension in the
                // left -hand wire is:
                //
                //          T1 == F_g cos(th2) / sin(th1 + th2)
                
                var bag = new Obj3("bag");
                
                var T1 = new Symbol("T1");
                var T2 = new Symbol("T2");
                var T3 = new Symbol("T3");

                var F_g = new Symbol("F_g");

                var th1 = new Symbol("th1");
                var th2 = new Symbol("th2");

                var eqs = bag.Equations();

                var vals = new List<Equation>()
                {
                    bag.ax == 0,
                    bag.ay == 0,

                    bag.F1 == T1,       bag.th1 == (180).ToRadians() - th1,
                    bag.F2 == T2,       bag.th2 == th2,
                    bag.F3 == F_g,      bag.th3 == (270).ToRadians()
                };

                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        bag.ΣFx, bag.F1x, bag.F2x, bag.F3x,
                        bag.ΣFy, bag.F1y, bag.F2y, bag.F3y,

                        T2
                    
                    )

                    .IsolateVariable(T1)

                    .RationalizeExpression()

                    .DeepSelect(SumDifferenceFormulaAFunc)

                    .AssertEqTo(    T1 == cos(th2) * F_g / sin(th1 + th2)   );
                
            }

            #endregion
            
            #region PSE 5E P5.31

            {
                // Two people pull as hard as they can on ropes attached
                // to a boat that has a mass of 200 kg. If they pull in the
                // same direction, the boat has an acceleration of
                // 1.52 m/s^2 to the right. If they pull in opposite directions,
                // the boat has an acceleration of 0.518 m/s^2 to the
                // left.
                // 
                // What is the force exerted by each person on the
                // boat? (Disregard any other forces on the boat.)

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

                var F1_m1 = new Symbol("F1_m1");        // force 1 on mass 1
                var F2_m1 = new Symbol("F2_m1");        // force 2 on mass 1
                
                var th1_m1 = new Symbol("th1_m1");      // direction of force 1 on mass 1
                var th2_m1 = new Symbol("th2_m1");      // direction of force 2 on mass 1
                
                var F1x_m1 = new Symbol("F1x_m1");      // x-component of force 1 on mass 1
                var F2x_m1 = new Symbol("F2x_m1");      // x-component of force 2 on mass 1
                
                var F1y_m1 = new Symbol("F1y_m1");      // y-component of force 1 on mass 1
                var F2y_m1 = new Symbol("F2y_m1");      // y-component of force 2 on mass 1
                
                var Fx_m1 = new Symbol("Fx_m1");        // x-component of total force on mass 1
                var Fy_m1 = new Symbol("Fy_m1");        // y-component of total force on mass 1

                var ax_m1 = new Symbol("ax_m1");        // x-component of acceleration of mass 1
                var ay_m1 = new Symbol("ay_m1");        // y-component of acceleration of mass 1

                var m1 = new Symbol("m1");

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

                var F1_m2 = new Symbol("F1_m2");        // force 1 on mass 2
                var F2_m2 = new Symbol("F2_m2");        // force 2 on mass 2

                var th1_m2 = new Symbol("th1_m2");      // direction of force 1 on mass 2
                var th2_m2 = new Symbol("th2_m2");      // direction of force 2 on mass 2

                var F1x_m2 = new Symbol("F1x_m2");      // x-component of force 1 on mass 2
                var F2x_m2 = new Symbol("F2x_m2");      // x-component of force 2 on mass 2

                var F1y_m2 = new Symbol("F1y_m2");      // y-component of force 1 on mass 2
                var F2y_m2 = new Symbol("F2y_m2");      // y-component of force 2 on mass 2

                var Fx_m2 = new Symbol("Fx_m2");        // x-component of total force on mass 2
                var Fy_m2 = new Symbol("Fy_m2");        // y-component of total force on mass 2

                var ax_m2 = new Symbol("ax_m2");        // x-component of acceleration of mass 2
                var ay_m2 = new Symbol("ay_m2");        // y-component of acceleration of mass 2

                var m2 = new Symbol("m2");

                ////////////////////////////////////////////////////////////////////////////////
                
                var Pi = new Symbol("Pi");
                
                var T1 = new Symbol("T1");
                var T2 = new Symbol("T2");

                var eqs = and(

                    m1 == m2,
                                        
                    F1x_m1 == F1_m1 * cos(th1_m1),
                    F2x_m1 == F2_m1 * cos(th2_m1),
                    
                    F1y_m1 == F1_m1 * sin(th1_m1),
                    F2y_m1 == F2_m1 * sin(th2_m1),
                    
                    Fx_m1 == F1x_m1 + F2x_m1,
                    Fy_m1 == F1y_m1 + F2y_m1,

                    Fx_m1 == m1 * ax_m1,
                    Fy_m1 == m1 * ay_m1,


                    F1x_m2 == F1_m2 * cos(th1_m2),
                    F2x_m2 == F2_m2 * cos(th2_m2),

                    F1y_m2 == F1_m2 * sin(th1_m2),
                    F2y_m2 == F2_m2 * sin(th2_m2),

                    Fx_m2 == F1x_m2 + F2x_m2,
                    Fy_m2 == F1y_m2 + F2y_m2,

                    Fx_m2 == m2 * ax_m2,
                    Fy_m2 == m2 * ay_m2
                    );

                DoubleFloat.tolerance = 0.00001;

                {
                    var vals = new List<Equation>()
                    {
                        ay_m1 == 0,

                        F1_m1 == T1,    th1_m1 == 0,
                        F2_m1 == T2,    th2_m1 == 0,

                        ay_m2 == 0,

                        F1_m2 == T1,    th1_m2 == 180 * Pi / 180,
                        F2_m2 == T2,    th2_m2 == 0
                    };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    var numerical_vals = new List<Equation>()
                    {
                        m1 == 200,

                        ax_m1 == 1.52,
                        ax_m2 == -0.518
                    };
                    
                    // T1
                    {
                        eqs
                            .SubstituteEqLs(vals)

                            .EliminateVariables(
                            
                                m2,

                                F1x_m1, F2x_m1,
                                F1y_m1, F2y_m1,

                                F1x_m2, F2x_m2,
                                F1y_m2, F2y_m2,

                                Fx_m1, Fy_m1,
                                
                                Fx_m2, Fy_m2,

                                T2
                                
                                )

                            .IsolateVariable(T1)

                            .AssertEqTo(
                            
                                T1 == -(ax_m2 * m1 - ax_m1 * m1) / 2
                                
                            )
                            
                            .SubstituteEqLs(numerical_vals)

                            .AssertEqTo(T1 == 203.8);
                    }

                    // T2
                    {
                        eqs
                            .SubstituteEqLs(vals)

                            .EliminateVariables(

                                m2,

                                F1x_m1, F2x_m1,
                                F1y_m1, F2y_m1,

                                F1x_m2, F2x_m2,
                                F1y_m2, F2y_m2,

                                Fx_m1, Fy_m1,

                                Fx_m2, Fy_m2,

                                T1

                                )

                            .IsolateVariable(T2)

                            .AssertEqTo(
                            
                                T2 == (ax_m1 * m1 + ax_m2 * m1) / 2
                                
                            )
                            
                            .SubstituteEqLs(numerical_vals)

                            .AssertEqTo(T2 == 100.19999999999999);
                    }
                }
            }

            #endregion

            #region PSE 5E P5.31 Obj

            {
                // Two people pull as hard as they can on ropes attached
                // to a boat that has a mass of 200 kg. If they pull in the
                // same direction, the boat has an acceleration of
                // 1.52 m/s^2 to the right. If they pull in opposite directions,
                // the boat has an acceleration of 0.518 m/s^2 to the
                // left.
                // 
                // What is the force exerted by each person on the
                // boat? (Disregard any other forces on the boat.)

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

                var b1 = new Obj2("b1");            // boat in scenario 1 (same direction)
                var b2 = new Obj2("b2");            // boat in scenario 2 (opposite directions)

                var m = new Symbol("m");

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

                var Pi = new Symbol("Pi");

                var T1 = new Symbol("T1");
                var T2 = new Symbol("T2");

                var eqs = and(

                    b1.Equations(),
                    b2.Equations()

                    );

                DoubleFloat.tolerance = 0.00001;

                var vals = new List<Equation>()
                {
                    b1.m == m,

                    b1.ay == 0,

                    b1.F1 == T1, b1.th1 == 0,
                    b1.F2 == T2, b1.th2 == 0,

                    b2.m == m,

                    b2.ay == 0,

                    b2.F1 == T1, b2.th1 == (180).ToRadians(),
                    b2.F2 == T2, b2.th2 == 0

                };

                var zeros = vals.Where(eq => eq.b == 0).ToList();

                var numerical_vals = new List<Equation>()
                {
                    m == 200,

                    b1.ax == 1.52,
                    b2.ax == -0.518
                };

                // T1
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        b1.ΣFx, b1.F1x, b1.F2x,
                        b1.ΣFy, b1.F1y, b1.F2y,

                        b2.ΣFx, b2.F1x, b2.F2x,
                        b2.ΣFy, b2.F1y, b2.F2y,

                        T2
                    )

                    .IsolateVariable(T1)

                    .AssertEqTo(

                        T1 == -(b2.ax * m - b1.ax * m) / 2

                    )

                    .SubstituteEqLs(numerical_vals)

                    .AssertEqTo(T1 == 203.8);

                // T2
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        b1.ΣFx, b1.F1x, b1.F2x,
                        b1.ΣFy, b1.F1y, b1.F2y,

                        b2.ΣFx, b2.F1x, b2.F2x,
                        b2.ΣFy, b2.F1y, b2.F2y,

                        T1
                    )

                    .IsolateVariable(T2)

                    .AssertEqTo(

                        T2 == (b1.ax * m + b2.ax * m) / 2

                    )

                    .SubstituteEqLs(numerical_vals)

                    .AssertEqTo(T2 == 100.19999999999999);

            }

            #endregion
            
            #region PSE 5E P5.55 

            {
                // An inventive child named Pat wants to reach an apple
                // in a tree without climbing the tree. Sitting in a chair
                // connected to a rope that passes over a frictionless pulley
                // Pat pulls on the loose end of the rope with such a force
                // that the spring scale reads 250 N. Pat’s weight is 320 N,
                // and the chair weighs 160 N.
                //
                // http://i.imgur.com/wwlypzB.png
                //
                // (a) Draw free - body diagrams for Pat and the chair considered as
                // separate systems, and draw another diagram for Pat and
                // the chair considered as one system.
                //
                // (b) Show that the acceleration of the system is upward and
                // find its magnitude.
                //
                // (c) Find the force Pat exerts on the chair.

                var b = new Obj3("b");          // boy
                var c = new Obj3("c");          // chair
                var s = new Obj3("s");          // system

                var T = new Symbol("T");        // rope tension
                var n = new Symbol("n");        // normal force

                var Fg_b = new Symbol("Fg_b");  // force due to gravity of the boy
                var Fg_c = new Symbol("Fg_c");  // force due to gravity of the chair
                var Fg_s = new Symbol("Fg_s");  // force due to gravity of the system

                var a = new Symbol("a");        // acceleration

                var Pi = new Symbol("Pi");
                var g = new Symbol("g");

                var eqs = and(

                    Fg_b == b.m * g,
                    Fg_c == c.m * g,
                    Fg_s == s.m * g,

                    Fg_s == Fg_c + Fg_b,

                    s.Equations(),
                    c.Equations()

                    );

                var vals = new List<Equation>()
                {
                    //b.ax == 0,
                    c.ax == 0,
                    s.ax == 0,

                    //b.F1 == T,          b.th1 == 90 * Pi / 180,
                    //b.F2 == n,          b.th2 == 90 * Pi / 180,
                    //b.F3 == b.m * g,    b.th3 == 270 * Pi / 180,

                    c.F1 == T,          c.th1 == 90 * Pi / 180,
                    c.F2 == n,          c.th2 == 270 * Pi / 180,
                    c.F3 == Fg_c,       c.th3 == 270 * Pi / 180,

                    s.F1 == T,          s.th1 == 90 * Pi / 180,
                    s.F2 == T,          s.th2 == 90 * Pi / 180,
                    s.F3 == Fg_s,       s.th3 == 270 * Pi / 180,

                    //b.ay == a,
                    c.ay == a,
                    s.ay == a
                };

                var numerical_vals = new List<Equation>()
                {
                    T == 250.0,
                    Fg_b == 320,
                    Fg_c == 160,
                    g == 9.8
                };

                DoubleFloat.tolerance = 0.00001;

                // a
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        s.ΣFx, s.F1x, s.F2x, s.F3x,
                        s.ΣFy, s.F1y, s.F2y, s.F3y,

                        c.ΣFx, c.F1x, c.F2x, c.F3x,
                        c.ΣFy, c.F1y, c.F2y, c.F3y,

                        n,

                        s.m,

                        Fg_s,

                        b.m, c.m

                    )

                    .IsolateVariable(a)

                    .AssertEqTo(

                        a == -g * (Fg_b + Fg_c - 2 * T) / (Fg_b + Fg_c)

                    )

                    .SubstituteEqLs(numerical_vals)

                    .AssertEqTo(a == 0.40833333333333333);

                // n
                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        s.ΣFx, s.F1x, s.F2x, s.F3x,
                        s.ΣFy, s.F1y, s.F2y, s.F3y,

                        c.ΣFx, c.F1x, c.F2x, c.F3x,
                        c.ΣFy, c.F1y, c.F2y, c.F3y,

                        c.m, s.m,

                        Fg_s,

                        b.m,

                        a

                    )

                    .IsolateVariable(n)

                    .AssertEqTo(

                        n == -1 * (Fg_c - T - Fg_c * (Fg_b + Fg_c - 2 * T) / (Fg_b + Fg_c))

                    )

                    .SubstituteEqLs(numerical_vals);

                DoubleFloat.tolerance = null;

            }

            #endregion
            
            #region PSE 5E P5.59

            {
                // A mass M is held in place by an applied force F and a
                // pulley system: 
                //
                //                 http://i.imgur.com/TPAHTlW.png
                //
                // The pulleys are massless and frictionless. Find 
                //                     
                // (a) the tension in each section of rope, T1, T2, T3, T4, and T5
                //                     
                // (b) the magnitude of F. 
                //                     
                // (Hint: Draw a free - body diagram for each pulley.)

                var pul1_F = new Symbol("pul1_F");      // magnitude of total force on pully 1
                var pul1_m = new Symbol("pul1_m");      // mass of pully 1
                var pul1_a = new Symbol("pul1_a");      // acceleration of pully 1

                var pul2_F = new Symbol("pul2_F");      // magnitude of total force on pully 2
                var pul2_m = new Symbol("pul2_m");      // mass of pully 2
                var pul2_a = new Symbol("pul2_a");      // acceleration of pully 2
                
                var T1 = new Symbol("T1");
                var T2 = new Symbol("T2");
                var T3 = new Symbol("T3");
                var T4 = new Symbol("T4");
                var T5 = new Symbol("T5");

                var F = new Symbol("F");

                var M = new Symbol("M");

                var g = new Symbol("g");

                var eqs = and(

                     T1 == F,
                     T2 == T3,
                     T1 == T3,
                     T5 == M * g,
                     
                     pul1_a == 0,
                     pul1_m == 0,

                     pul1_F == T4 - T1 - T2 - T3,
                     pul1_F == pul1_m * pul1_a,

                     pul2_m == 0,

                     pul2_F == T2 + T3 - T5,
                     pul2_F == pul2_m * pul2_a

                    );

                DoubleFloat.tolerance = 0.00001;

                // T1
                {
                    eqs
                        .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T2, T3, T4, T5, F)
                        .IsolateVariable(T1)
                        .AssertEqTo(T1 == g * M / 2);
                }
                
                // T2
                {
                    eqs
                        .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T3, T4, T5, F)
                        .IsolateVariable(T2)
                        .AssertEqTo(T2 == g * M / 2);
                }

                // T3
                {
                    eqs
                        .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T2, T4, T5, F)
                        .IsolateVariable(T3)
                        .AssertEqTo(T3 == g * M / 2);
                }

                // T4
                {
                    eqs
                        .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T2, T3, T5, F)
                        .IsolateVariable(T4)
                        .AssertEqTo(T4 == g * M * 3 / 2);
                }

                // T5
                {
                    eqs
                        .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T2, T3, T4, F)
                        .AssertEqTo(T5 == g * M);
                }

                // F
                {
                    eqs
                        .EliminateVariables(pul1_F, pul2_F, pul1_m, pul2_m, pul1_a, T1, T2, T3, T4, T5)
                        .IsolateVariable(F)
                        .AssertEqTo(F == g * M / 2);
                }
            }

            #endregion
            
            #region PSE 5E P5.69
            {
                // What horizontal force must be applied to the cart shown:

                // http://i.imgur.com/fpkzsYI.png

                // so that the blocks remain stationary relative to the cart?
                // Assume all surfaces, wheels, and pulley are frictionless.
                // (Hint:Note that the force exerted by the string accelerates m1.)

                var blk1 = new Obj3("blk1");
                var blk2 = new Obj3("blk2");

                var sys = new Obj3("sys");
                
                var m1 = new Symbol("m1");
                var m2 = new Symbol("m2");
                
                var T = new Symbol("T");
                var F = new Symbol("F");
                var M = new Symbol("M");
                var g = new Symbol("g");
                var a = new Symbol("a");
                
                var eqs = and(

                    blk1.Equations(),
                    blk2.Equations(),

                    sys.Equations()
                    
                    );

                var vals = new List<Equation>()
                {
                    blk1.ax == a,
                    blk1.ay == 0,

                    blk1.m == m1,

                    blk1.F1 == T,   blk1.th1 == 0,

                    blk1.th2 == (90).ToRadians(),
                    blk1.th3 == (270).ToRadians(),


                    blk2.ax == a,
                    blk2.ay == 0,

                    blk2.m == m2,

                    blk2.th1 == 0,

                    blk2.F2 == T,       blk2.th2 == (90).ToRadians(),
                    blk2.F3 == m2 * g,  blk2.th3 == (270).ToRadians(),


                    sys.ax == a,
                    sys.ay == 0,

                    sys.m == M + m1 + m2,

                    sys.F1 == F,        sys.th1 == 0,

                    sys.th2 == (90).ToRadians(),
                    sys.th3 == (270).ToRadians()

                };

                eqs
                    .SubstituteEqLs(vals)

                    .EliminateVariables(

                        blk1.ΣFx, blk1.F1x, blk1.F2x, blk1.F3x,
                        blk1.ΣFy, blk1.F1y, blk1.F2y, blk1.F3y,

                        blk1.F2,

                        blk2.ΣFx, blk2.F1x, blk2.F2x, blk2.F3x,
                        blk2.ΣFy, blk2.F1y, blk2.F2y, blk2.F3y,

                        blk2.F1,

                        sys.ΣFx, sys.F1x, sys.F2x, sys.F3x,
                        sys.ΣFy, sys.F1y, sys.F2y, sys.F3y,

                        sys.F2,

                        T, a

                    )

                    .AssertEqTo(   F == g * m2 / m1 * (M + m1 + m2)   );
            }
            #endregion
                        
            #region PSE 5E E7.7
            {
                // A  6.0-kg block initially at rest is pulled to the right along a
                // horizontal, frictionless surface by a constant horizontal force
                // of 12 N. Find the speed of the block after it has moved 3.0 m.
                
                var W = new Symbol("W");
                var F = new Symbol("F");
                var d = new Symbol("d");

                var Kf = new Symbol("Kf");
                var Ki = new Symbol("Ki");

                var m = new Symbol("m");

                var vf = new Symbol("vf");
                var vi = new Symbol("vi");

                var eqs = and(

                    W == F * d,

                    W == Kf - Ki,

                    Kf == m * (vf ^ 2) / 2,
                    Ki == m * (vi ^ 2) / 2,

                    m != 0

                    );

                var vals = new List<Equation>() { m == 6.0, vi == 0, F == 12, d == 3 };

                // vf
                eqs
                    .EliminateVariables(Kf, Ki, W)
                    .IsolateVariable(vf)
                    .LogicalExpand().CheckVariable(m).SimplifyEquation().SimplifyLogical()

                    .AssertEqTo(

                        or(
                            and(
                                vf == sqrt(-2 * m * (-d * F - m * (vi ^ 2) / 2)) / m,
                                m != 0),
                            and(
                                vf == -sqrt(-2 * m * (-d * F - m * (vi ^ 2) / 2)) / m,
                                m != 0)))
                                
                    .SubstituteEq(vi == 0)
                                        
                    .AssertEqTo(

                        or(
                            and(
                                vf == sqrt(2 * d * F * m) / m,
                                m != 0),
                            and(
                                vf == -sqrt(2 * d * F * m) / m,
                                m != 0)))
                    
                    .SubstituteEqLs(vals)

                    .AssertEqTo(
                        or(
                            vf == 3.4641016151377544,
                            vf == -3.4641016151377544));

            }
            #endregion
                        
            #region PSE 5E E7.8
            {
                // Find the final speed of the block described in Example 7.7 if
                // the surface is not frictionless but instead has a coefficient of
                // kinetic friction of 0.15.

                var W = new Symbol("W");
                var F = new Symbol("F");
                var d = new Symbol("d");
                var n = new Symbol("n");

                var g = new Symbol("g");

                var Kf = new Symbol("Kf");
                var Ki = new Symbol("Ki");

                var m = new Symbol("m");

                var vf = new Symbol("vf");
                var vi = new Symbol("vi");

                var fk = new Symbol("fk");
                                
                var μk = new Symbol("μk");

                var eqs = and(

                    Kf == m * (vf ^ 2) / 2,
                    Ki == m * (vi ^ 2) / 2,

                    W == F * d,

                    n == m * g,

                    fk == n * μk,

                    W - fk * d == Kf - Ki,

                    m != 0

                    );

                var vals = new List<Equation>()
                {
                    vi == 0,
                    F == 12.0,
                    d == 3.0,

                    m == 6.0,
                    
                    μk == 0.15,

                    g == 9.8,
                };

                // vf
                eqs
                    .EliminateVariables(Kf, Ki, W, n, fk)
                    .IsolateVariable(vf)
                    .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)
                    .SubstituteEq(vi == 0)
                    .AssertEqTo(
                        or(
                            and(
                                vf == -sqrt(2 * m * (d * F - d * g * m * μk)) / m,
                                m != 0),
                            and(
                                vf == sqrt(2 * m * (d * F - d * g * m * μk)) / m,
                                m != 0)))

                    .SubstituteEqLs(vals)

                    .AssertEqTo(or(vf == -1.7832554500127007, vf == 1.7832554500127007));

            }
            #endregion
            
            #region PSE 5E E7.11
            {
                // A block of mass 1.6 kg is attached to a horizontal spring that
                // has a force constant of 1.0 x 10^3 N/m, as shown in Figure
                // 7.10. The spring is compressed 2.0 cm and is then released
                // from  rest.
                
                // (a) Calculate the  speed of  the block  as it  passes
                // through the equilibrium position x = 0 if the surface is frictionless.

                // (b) Calculate the speed of the block as it passes through
                // the equilibrium position if a constant frictional force of 4.0 N
                // retards its motion from the moment it is released.

                var ΣW = new Symbol("ΣW");
                                
                var Kf = new Symbol("Kf");
                var Ki = new Symbol("Ki");

                var m = new Symbol("m");
                var d = new Symbol("d");
                var k = new Symbol("k");

                var vf = new Symbol("vf");
                var vi = new Symbol("vi");

                var fk = new Symbol("fk");
                
                var W_s = new Symbol("W_s");
                var W_f = new Symbol("W_f");
                
                var x_max = new Symbol("x_max");

                var eqs = and(

                    W_s == k * (x_max ^ 2) / 2,

                    Kf == m * (vf ^ 2) / 2,
                    Ki == m * (vi ^ 2) / 2,
                    
                    W_f == -fk * d,

                    ΣW == Kf - Ki,

                    ΣW == W_s + W_f,

                    m != 0

                    );

                // vf
                {
                    var vals = new List<Equation>() { m == 1.6, vi == 0, fk == 0, k == 1000, x_max == -0.02 };

                    eqs
                        .EliminateVariables(ΣW, Kf, Ki, W_f, W_s)
                        .IsolateVariable(vf)
                        .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)
                        
                        .AssertEqTo(
                            or(
                                and(
                                    vf == sqrt(-2 * m * (d * fk - m * (vi ^ 2) / 2 - k * (x_max ^ 2) / 2)) / m,
                                    m != 0),
                                and(
                                    vf == -sqrt(-2 * m * (d * fk - m * (vi ^ 2) / 2 - k * (x_max ^ 2) / 2)) / m,
                                    m != 0)))
                                              
                        .SubstituteEqLs(vals)
                        
                        .AssertEqTo(or(vf == 0.5, vf == -0.5));
                }

                // vf
                {
                    var vals = new List<Equation>() { m == 1.6, vi == 0, fk == 4, k == 1000, x_max == -0.02, d == 0.02 };

                    eqs
                        .EliminateVariables(ΣW, Kf, Ki, W_f, W_s)
                        .IsolateVariable(vf)
                        .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)
                        
                        .SubstituteEqLs(vals)
                        
                        .AssertEqTo(or(vf == 0.3872983346207417, vf == -0.3872983346207417));
                }
                
            }
            #endregion
                        
            #region PSE 6E P7.3
            {
                // Batman, whose mass is 80.0kg, is dangling on the free end
                // of a 12.0m rope, the other end of which is fixed to a tree
                // limb above. He is able to get the rope in motion as only
                // Batman knows how, eventually getting it to swing enough
                // that he can reach a ledge when the rope makes a 60.0°
                // angle with the vertical. How much work was done by the
                // gravitational force on Batman in this maneuver?

                var m = new Symbol("m");
                var a = new Symbol("a");

                var W = new Symbol("W");
                var F = new Symbol("F");
                var d = new Symbol("d");

                var yA = new Symbol("yA");
                var yB = new Symbol("yB");

                var th = new Symbol("th");

                var len = new Symbol("len");
                
                var eqs = and(

                    yA == -len,

                    yB == -len * cos(th),

                    d == yB - yA,

                    F == m * a,

                    W == F * d
                              
                    );

                var vals = new List<Equation>()
                { m == 80, len == 12, th == (60).ToRadians(), a == -9.8 };

                eqs
                    .EliminateVariables(F, d, yA, yB)

                    .AssertEqTo(W == a * (len - cos(th) * len) * m)

                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(W == -4704.0);
            }
            #endregion
                        
            #region PSE 5E P7.23
            {
                // If it takes 4.00J of work to stretch a Hooke’s-law spring
                // 10.0cm from its unstressed length, determine the extra
                // work required to stretch it an additional 10.0cm.

                var WsAB = new Symbol("WsAB");
                
                var WsA = new Symbol("WsA");
                var WsB = new Symbol("WsB");

                var k = new Symbol("k");

                var xA = new Symbol("xA");
                var xB = new Symbol("xB");
                                
                var eqs = and(
                    
                    WsA == k * (xA ^ 2) / 2,
                    WsB == k * (xB ^ 2) / 2,

                    WsAB == WsB - WsA
                                        
                    );

                var vals = new List<Equation>() { xA == 0.1, xB == 0.2, WsA == 4 };

                eqs

                    .EliminateVariables(WsB, k)

                    .AssertEqTo(
                    
                        WsAB == WsA * (xB ^ 2) / (xA ^ 2) - WsA     
                        
                        )
                        
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(WsAB == 12.0);
                
            }
            #endregion
            
            #region PSE 5E 7.33
            {
                // A 40.0-kg box initially at rest is pushed 5.00m along a
                // rough, horizontal floor with a constant applied horizontal
                // force of 130 N. If the coefficient of friction between
                // the box and the floor is 0.300, find

                // (a) the work done by the applied force
                // (b) the energy loss due to friction
                // (c) the work done by the normal force
                // (d) the work done by gravity
                // (e) the change in kinetic energy of the box
                // (f) the final speed of the box

                var ΣW = new Symbol("ΣW");

                var Kf = new Symbol("Kf");
                var Ki = new Symbol("Ki");

                var F = new Symbol("F");

                var m = new Symbol("m");
                var d = new Symbol("d");

                var n = new Symbol("n");
                var g = new Symbol("g");

                var vf = new Symbol("vf");
                var vi = new Symbol("vi");

                var fk = new Symbol("fk");

                var W_F = new Symbol("W_F"); 
                var W_f = new Symbol("W_f");

                var μk = new Symbol("μk");

                var eqs = and(

                    n == m * g,

                    fk == μk * n,

                    Kf == m * (vf ^ 2) / 2,
                    Ki == m * (vi ^ 2) / 2,
                    
                    W_F == F * d,

                    W_f == -fk * d,

                    ΣW == Kf - Ki,
                                        
                    ΣW == W_F + W_f,

                    m != 0

                    );

                var vals = new List<Equation>()
                { m == 40, vi == 0, d == 5, F == 130, μk == 0.3, g == 9.8 };
                
                // W_F, W_f
                eqs
                    .EliminateVariables(fk, n, Kf, Ki, ΣW, vf)
                    .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)
                    
                    .AssertEqTo(
                        and(
                            m != 0,
                            W_F == d * F,
                            W_f == -d * g * m * μk))
                            
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(and(W_F == 650, W_f == -588.0));

                // ΣW
                eqs
                    .EliminateVariables(W_F, W_f, fk, n, Ki, Kf)

                    .AssertEqTo(
                        and(
                            ΣW == m * (vf ^ 2) / 2 - m * (vi ^ 2) / 2,
                            ΣW == d * F - d * g * m * μk,
                            m != 0))
                                                
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(and(ΣW == 20 * (vf ^ 2), ΣW == 62.0));
                
                // vf
                eqs
                    .EliminateVariables(Kf, Ki, ΣW, W_F, W_f, fk, n)
                    .IsolateVariable(vf)
                    .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)

                    .AssertEqTo(
                        or(
                            and(
                                vf == sqrt(-2 * m * (-d * F - m * (vi ^ 2) / 2 + d * g * m * μk)) / m,
                                m != 0),
                            and(
                                vf == -sqrt(-2 * m * (-d * F - m * (vi ^ 2) / 2 + d * g * m * μk)) / m,
                                m != 0)))
                                
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(or(vf == 1.7606816861659009, vf == -1.7606816861659009));
            }
            #endregion
            
            #region PSE 5E P7.35
            {
                // A crate of mass 10.0kg is pulled up a rough incline with
                // an initial speed of 1.50 m/s.The pulling force is 100 N
                // parallel to the incline, which makes an angle of 20.0°
                // with the horizontal. The coefficient of kinetic friction is
                // 0.400, and the crate is pulled 5.00 m.

                // (a) How much work is done by gravity?
                // (b) How much energy is lost because of friction?
                // (c) How much work is done by the 100-N force?
                // (d) What is the change in kinetic energy of the crate?
                // (e) What is the speed of the crate after it has been pulled 5.00 m?

                var ΣW = new Symbol("ΣW");

                var Kf = new Symbol("Kf");
                var Ki = new Symbol("Ki");

                var F = new Symbol("F");

                var m = new Symbol("m");
                var d = new Symbol("d");

                var n = new Symbol("n");
                var g = new Symbol("g");

                var vf = new Symbol("vf");
                var vi = new Symbol("vi");

                var fk = new Symbol("fk");

                var W_F = new Symbol("W_F");
                var W_f = new Symbol("W_f");
                var W_g = new Symbol("W_g");

                var μk = new Symbol("μk");
                                
                var th = new Symbol("th");

                var F_g = new Symbol("F_g");

                var Pi = new Symbol("Pi");
                
                var eqs = and(

                    F_g == m * g,

                    n == F_g * cos(th),

                    fk == μk * n,

                    Kf == m * (vf ^ 2) / 2,
                    Ki == m * (vi ^ 2) / 2,

                    W_F == F * d,

                    W_f == -fk * d,
                                                                                
                    W_g == - F_g * sin(th) * d,

                    ΣW == Kf - Ki,

                    ΣW == W_F + W_f + W_g,

                    m != 0
                    
                    );

                var vals = new List<Equation>()
                {
                    m == 10.0, g == 9.8, d == 5.0, th == (20).ToRadians(), μk == 0.4, F == 100.0,
                    vi == 1.5, Pi == Math.PI
                };

                // W_g, W_f, W_F
                eqs
                    .EliminateVariables(F_g, fk, n)

                    .AssertEqTo(
                        and(
                            Kf == m * (vf ^ 2) / 2,
                            Ki == m * (vi ^ 2) / 2,
                            W_F == d * F,
                            W_f == -cos(th) * d * g * m * μk,
                            W_g == -d * g * m * sin(th),
                            ΣW == Kf - Ki,
                            ΣW == W_f + W_F + W_g,
                            m != 0
                        ))
                    
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(
                        and(
                            Kf == 5.0 * (vf ^ 2),
                            Ki == 11.25,
                            W_F == 500.0,
                            W_f == -184.17975367403804,
                            W_g == -167.58987022957766,
                            ΣW == Kf - Ki,
                            ΣW == W_f + W_F + W_g
                        ));
                
                // ΣW
                eqs
                    .EliminateVariables(F_g, fk, n, W_F, W_f, W_g)
                    
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(
                        and(
                            Kf == 5.0 * (vf ^ 2),
                            Ki == 11.25,
                            ΣW == Kf - Ki,
                            ΣW == 148.23037609638431
                        ));

                // vf
                eqs
                    .EliminateVariables(F_g, fk, n, W_F, W_f, W_g, ΣW, Kf, Ki)

                    .IsolateVariable(vf)

                    .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)
                                                                                 
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(or(vf == 5.6476610396939435, vf == -5.6476610396939435));

            }
            #endregion
            
            #region PSE 5E P7.39
            {
                // A bullet with a mass of 5.00 g and a speed of 600 m/s
                // penetrates a tree to a depth of 4.00 cm.

                // (a) Use work and energy considerations to find the average
                // frictional force that stops the bullet.

                // (b) Assuming that the frictional force is constant,
                // determine how much time elapsed between the moment
                // the bullet entered the tree and the moment it stopped.

                var ΣW = new Symbol("ΣW");

                var Kf = new Symbol("Kf");
                var Ki = new Symbol("Ki");
                
                var m = new Symbol("m");
                var d = new Symbol("d");
                
                var vf = new Symbol("vf");
                var vi = new Symbol("vi");

                var fk = new Symbol("fk");
                                
                var W_f = new Symbol("W_f");

                var t = new Symbol("t");

                var eqs = and(
                    
                    Kf == m * (vf ^ 2) / 2,
                    Ki == m * (vi ^ 2) / 2,

                    W_f == -fk * d,

                    ΣW == Kf - Ki,

                    ΣW == W_f
                    
                    );

                var vals = new List<Equation>() { m == 0.005, vi == 600.0, vf == 0.0, d == 0.04 };

                // fk
                eqs
                    .EliminateVariables(W_f, ΣW, Ki, Kf)
                    .IsolateVariable(fk)
                    .AssertEqTo(
                    
                        fk == (m * (vi ^ 2) / 2 - m * (vf ^ 2) / 2) / d
                        
                        )
                    
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(fk == 22500.0);
                
                // t
                (d == (vi + vf) * t / 2)
                    .IsolateVariable(t)
                    .AssertEqTo(t == 2 * d / (vf + vi))
                    .SubstituteEqLs(vals)
                    .AssertEqTo(t == 1.3333333333333334e-4);
                
            }
            #endregion
            
            #region PSE 5E P7.41
            {
                // A 2.00-kg block is attached to a spring of force constant
                // 500 N/m, as shown in Figure 7.10. The block is pulled
                // 5.00 cm to the right of equilibrium and is then released
                // from rest. Find the speed of the block as it passes
                // through equilibrium if

                // (a) the horizontal surface is frictionless

                // (b) the coefficient of friction between the block and the surface is 0.350.

                var ΣW = new Symbol("ΣW");

                var Kf = new Symbol("Kf");
                var Ki = new Symbol("Ki");
                                
                var m = new Symbol("m");
                var d = new Symbol("d");

                var n = new Symbol("n");
                var g = new Symbol("g");
                var k = new Symbol("k");

                var vf = new Symbol("vf");
                var vi = new Symbol("vi");

                var fk = new Symbol("fk");
                                
                var W_f = new Symbol("W_f");
                var W_s = new Symbol("W_s");

                var μk = new Symbol("μk");
                                
                var xi = new Symbol("xi");
                var xf = new Symbol("xf");
                
                var eqs = and(
                    
                    n == m * g,

                    fk == μk * n,

                    Kf == m * (vf ^ 2) / 2,
                    Ki == m * (vi ^ 2) / 2,

                    W_f == -fk * d,
                                                            
                    W_s == k * (xi ^ 2) / 2 - k * (xf ^ 2) / 2,                    

                    ΣW == Kf - Ki,

                    ΣW == W_f + W_s,

                    m != 0
                    
                    );

                var vals = new List<Equation>()
                { m == 2.0, k == 500, xi == 0.05, xf == 0.0, vi == 0, d == 0.05, g == 9.8 };

                eqs
                    .EliminateVariables(Kf, Ki, ΣW, W_f, W_s, n, fk)
                    .IsolateVariable(vf)
                    .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)

                    .AssertEqTo(
                        or(
                            and(
                                vf == sqrt(-2 * m * (-m * (vi ^ 2) / 2 + k * (xf ^ 2) / 2 - k * (xi ^ 2) / 2 + d * g * m * μk)) / m,
                                m != 0
                            ),
                            and(
                                vf == -sqrt(-2 * m * (-m * (vi ^ 2) / 2 + k * (xf ^ 2) / 2 - k * (xi ^ 2) / 2 + d * g * m * μk)) / m,
                                m != 0)))

                    .SubstituteEqLs(vals).SubstituteEq(μk == 0)

                    .AssertEqTo(or(vf == 0.79056941504209488, vf == -0.79056941504209488));

                eqs
                    .EliminateVariables(Kf, Ki, ΣW, W_f, W_s, n, fk)
                    .IsolateVariable(vf)
                    .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)
                    .SubstituteEqLs(vals).SubstituteEq(μk == 0.35)
                    .AssertEqTo(or(vf == 0.53103672189407025, vf == -0.53103672189407025));
            }
            #endregion
            
            #region PSE 5E 7.55
            {
                // A baseball outfielder throws a 0.150-kg baseball at a
                // speed of 40.0 m/s and an initial angle of 30.0°. What is
                // the kinetic energy of the baseball at the highest point of
                // the trajectory?

                var vx = new Symbol("vx");
                var vi = new Symbol("vi");
                var th = new Symbol("th");

                var m = new Symbol("m");
                var K = new Symbol("K");

                var vals = new List<Equation>() { m == 0.15, vi == 40.0, th == (30).ToRadians() };

                var eqs = and(

                    vx == vi * cos(th),
                    
                    K == m * (vx ^ 2) / 2
                    
                    );

                eqs
                    .EliminateVariables(vx)
                    
                    .AssertEqTo(K == (cos(th) ^ 2) * m * (vi ^ 2) / 2)
                    
                    .SubstituteEqLs(vals)

                    .AssertEqTo(K == 90.0);
            }
            #endregion

            #region PSE 5E E8.2
            {
                // A ball  of mass m is dropped from a height h above the
                // ground, as shown in Figure 8.6.

                // (a) Neglecting air resistance, determine the speed of
                // the ball when it is at a height ya bove the ground.

                // (b) Determine the speed of the ball at y if at the instant of
                // release it already has an initial speed vi at the initial altitude h.
                
                var m = new Symbol("m");

                var yi = new Symbol("yi");
                var yf = new Symbol("yf");

                var vi = new Symbol("vi");
                var vf = new Symbol("vf");

                var Ki = new Symbol("Ki");
                var Kf = new Symbol("Kf");

                var Ugi = new Symbol("Ugi");
                var Ugf = new Symbol("Ugf");

                var ΣUi = new Symbol("ΣUi");
                var ΣUf = new Symbol("ΣUf");

                var Ei = new Symbol("Ei");
                var Ef = new Symbol("Ef");

                var g = new Symbol("g");

                var h = new Symbol("h");
                var y = new Symbol("y");

                var eqs = and(
                    Ki == m * (vi^2) / 2,
                    Kf == m * (vf^2) / 2,

                    Ugi == m * g * yi,
                    Ugf == m * g * yf,

                    ΣUi == Ugi,
                    ΣUf == Ugf,

                    Ei == Ki + ΣUi,
                    Ef == Kf + ΣUf,

                    Ei == Ef
                );

                var vals = new List<Equation>() { yi == h, yf == y };

                // vf, vi == 0
                eqs
                    .EliminateVariables(Ugi, Ugf, ΣUi, ΣUf, Ki, Kf, Ei, Ef)
                    .MultiplyBothSidesBy(1 / m)
                    .AlgebraicExpand()
                    .IsolateVariable(vf)
                    .SubstituteEqLs(vals)
                    .SubstituteEq(vi == 0)

                    .AssertEqTo(
                        or(
                            vf == -sqrt(2 * (g * h - g * y)),
                            vf == sqrt(2 * (g * h - g * y))
                        ));

                // vf
                eqs
                    .EliminateVariables(Ugi, Ugf, ΣUi, ΣUf, Ki, Kf, Ei, Ef)
                    .MultiplyBothSidesBy(1 / m)
                    .AlgebraicExpand()
                    .IsolateVariable(vf)
                    .SubstituteEqLs(vals)
                    
                    .AssertEqTo(
                        or(
                            vf == -sqrt(2 * (g * h + (vi ^ 2) / 2 - g * y)),
                            vf == sqrt(2 * (g * h + (vi ^ 2) / 2 - g * y))
                        ));
                
            }
            #endregion
                                    
            #region PSE 5E E8.3
            {
                // A pendulum consists of a sphere of mass mattached to a light
                // cord of length L, as shown in Figure 8.7. The sphere is released
                // from rest when the cord makes an angle thA with the vertical,
                // and the pivot at P is frictionless.

                // (a) Find the speed of the sphere when it is at the lowest point B.

                // (b) What is the tension T_B in the cord at B?

                // (c) A pendulum of length 2.00 m and mass 0.500 kg
                // is released from rest when the cord makes an angle of 30.0°
                // with the vertical. Find the speed of the sphere and the tension
                // in the cord when the sphere is at its lowest point.
                
                var m = new Symbol("m");

                var yi = new Symbol("yi");
                var yf = new Symbol("yf");

                var vi = new Symbol("vi");
                var vf = new Symbol("vf");

                var Ki = new Symbol("Ki");
                var Kf = new Symbol("Kf");

                var Ugi = new Symbol("Ugi");
                var Ugf = new Symbol("Ugf");

                var ΣUi = new Symbol("ΣUi");
                var ΣUf = new Symbol("ΣUf");

                var Ei = new Symbol("Ei");
                var Ef = new Symbol("Ef");

                var g = new Symbol("g");

                var L = new Symbol("L");

                var thA = new Symbol("thA");

                var ar_f = new Symbol("ar_f");

                var r = new Symbol("r");

                var ΣFr = new Symbol("ΣFr");

                var T_f = new Symbol("T_f");

                var vf_sq = new Symbol("vf_sq");

                var eqs = and(
                    
                    Ki == m * (vi^2) / 2,
                    Kf == m * (vf^2) / 2,
                
                    Ugi == m * g * yi,
                    Ugf == m * g * yf,
                
                    ΣUi == Ugi,
                    ΣUf == Ugf,

                    Ei == Ki + ΣUi,
                    Ef == Kf + ΣUf,

                    Ei == Ef,

                    ar_f == (vf ^ 2) / r,

                    ΣFr == T_f - m * g,

                    ΣFr == m * ar_f

                    );

                var vals = new List<Equation>()
                {
                    yi == -L * cos(thA),
                    yf == -L,
                    vi == 0,

                    r == L
                };

                var numerical_vals = new List<Equation>() { L == 2.0, m == 0.5, thA == (30).ToRadians(), g == 9.8 };

                // vf
                eqs
                    .SubstituteEqLs(vals)
                    .EliminateVariables(ar_f, ΣFr, T_f, Ki, Kf, Ugi, Ugf, ΣUi, ΣUf, Ei, Ef)
                    .MultiplyBothSidesBy(1 / m)
                    .AlgebraicExpand()
                    .IsolateVariable(vf)

                    .AssertEqTo(

                        or(
                            vf == -sqrt(2 * (g * L - cos(thA) * g * L)),
                            vf == sqrt(2 * (g * L - cos(thA) * g * L))
                        )

                    )
                    
                    .SubstituteEqLs(numerical_vals).Substitute(3, 3.0)

                    .AssertEqTo(
                        or(
                            vf == -2.2916815161906787,
                            vf == 2.2916815161906787
                        )
                    );

                // T_f
                eqs
                    .SubstituteEqLs(vals)
                    .Substitute(vf ^ 2, vf_sq)
                    .EliminateVariables(Ki, Kf, Ugi, Ugf, ΣUi, ΣUf, Ei, Ef, ar_f, ΣFr, vf_sq)
                    .MultiplyBothSidesBy(1 / m)
                    .AlgebraicExpand()
                    .IsolateVariable(T_f)

                    .AssertEqTo(
                        
                        T_f == (3 * g - 2 * cos(thA) * g) * m
                        
                    );
                
            }
            #endregion
                        
            #region PSE 5E E8.4
            {
                // A 3.00-kg crate slides down a ramp. The ramp is 1.00 m in
                // length and inclined at an angle of 30.0°, as shown in Figure
                // 8.8. The crate starts from rest at the top, experiences a
                // constant frictional force of magnitude 5.00 N, and continues to
                // move a short distance on the flat floor after it leaves the
                // ramp. Use energy methods to determine the speed of the
                // crate at the bottom of the ramp.

                var m = new Symbol("m");

                var yi = new Symbol("yi");
                var yf = new Symbol("yf");

                var vi = new Symbol("vi");
                var vf = new Symbol("vf");

                var Ki = new Symbol("Ki");
                var Kf = new Symbol("Kf");

                var Ugi = new Symbol("Ugi");
                var Ugf = new Symbol("Ugf");

                var ΣUi = new Symbol("ΣUi");
                var ΣUf = new Symbol("ΣUf");

                var Ei = new Symbol("Ei");
                var Ef = new Symbol("Ef");

                var fk = new Symbol("fk");

                var W_f = new Symbol("W_f");

                var ΔE = new Symbol("ΔE");

                var g = new Symbol("g");

                var d = new Symbol("d");

                var θ = new Symbol("θ");

                var eqs = and(

                    yi == d * sin(θ),

                    Ki == m * (vi ^ 2) / 2,
                    Kf == m * (vf ^ 2) / 2,

                    Ugi == m * g * yi,
                    Ugf == m * g * yf,

                    ΣUi == Ugi,
                    ΣUf == Ugf,

                    W_f == -fk * d,

                    ΔE == W_f,

                    Ei == Ki + ΣUi,
                    Ef == Kf + ΣUf,

                    Ei + ΔE == Ef,

                    m != 0

                    );

                var vals = new List<Equation>()
                { m == 3.0, d == 1.0, θ == (30).ToRadians(), fk == 5.0, vi == 0.0, g == 9.8, yf == 0.0 };
                    
                eqs
                    .EliminateVariables(Ei, Ef, ΔE, Ki, Kf, ΣUi, ΣUf, W_f, Ugi, Ugf, yi)
                    .IsolateVariable(vf)
                    .LogicalExpand().SimplifyEquation().SimplifyLogical().CheckVariable(m)

                    .AssertEqTo(
                        or(
                            and(
                                vf == -sqrt(2 * m * (-d * fk + m * (vi ^ 2) / 2 - g * m * yf + g * m * d * sin(θ))) / m,
                                m != 0
                            ),
                            and(
                                vf == sqrt(2 * m * (-d * fk + m * (vi ^ 2) / 2 - g * m * yf + g * m * d * sin(θ))) / m,
                                m != 0
                            )))
                            
                    .SubstituteEqLs(vals)

                    .AssertEqTo(or(vf == -2.54296414970142, vf == 2.54296414970142));
            }
            #endregion

            #region PSE 5E Example 8.5
            {
                // A child of mass mrides on an irregularly curved slide of
                // height as shown in  Figure 8.9.The child starts
                // from rest at the top.

                // (a) Determine his speed at the bottom, assuming no friction is present.

                // (b) If a force of kinetic friction acts on the child, how
                // much mechanical energy does the system lose? Assume that
                // vf = 3.0 m/s and m = 20.0 kg.

                var m = new Symbol("m");

                var yi = new Symbol("yi");
                var yf = new Symbol("yf");

                var vi = new Symbol("vi");
                var vf = new Symbol("vf");

                var Ki = new Symbol("Ki");
                var Kf = new Symbol("Kf");

                var Ugi = new Symbol("Ugi");
                var Ugf = new Symbol("Ugf");

                var ΣUi = new Symbol("ΣUi");
                var ΣUf = new Symbol("ΣUf");

                var Ei = new Symbol("Ei");
                var Ef = new Symbol("Ef");

                var fk = new Symbol("fk");

                var W_f = new Symbol("W_f");

                var ΔE = new Symbol("ΔE");

                var g = new Symbol("g");

                var d = new Symbol("d");

                var eqs = and(

                    Ki == m * (vi ^ 2) / 2,
                    Kf == m * (vf ^ 2) / 2,

                    Ugi == m * g * yi,
                    Ugf == m * g * yf,

                    ΣUi == Ugi,
                    ΣUf == Ugf,

                    W_f == -fk * d,

                    ΔE == W_f,

                    Ei == Ki + ΣUi,
                    Ef == Kf + ΣUf,

                    Ei + ΔE == Ef);

                {
                    var vals = new List<Equation>()
                    { yi == 2.0, yf == 0, vi == 0, fk == 0, g == 9.8 };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // vf
                    eqs
                        .SubstituteEqLs(zeros)
                        .EliminateVariables(Ei, Ef, ΔE, Ki, Kf, ΣUi, ΣUf, W_f, Ugi, Ugf)
                        .MultiplyBothSidesBy(1 / m)
                        .IsolateVariable(vf)

                        .AssertEqTo(
                            or(
                                vf == -sqrt(2 * g * yi),
                                vf == sqrt(2 * g * yi)))

                        .SubstituteEqLs(vals)

                        .AssertEqTo(
                            or(
                                vf == -6.2609903369994111,
                                vf == 6.2609903369994111));
                }

                {
                    var vals = new List<Equation>()
                    { m == 20.0, yi == 2.0, yf == 0, vi == 0, vf == 3.0, g == 9.8 };

                    var zeros = vals.Where(eq => eq.b == 0).ToList();

                    // ΔE
                    eqs
                        .SubstituteEqLs(zeros)
                        .EliminateVariables(fk, Ei, Ef, Ki, Kf, ΣUi, ΣUf, Ugi, Ugf, W_f)
                        .IsolateVariable(ΔE)
                        
                        .AssertEqTo(ΔE == m * (vf ^ 2) / 2 - g * m * yi)
                        
                        .SubstituteEqLs(vals)
                        
                        .AssertEqTo(ΔE == -302.0);
                }
            }
            #endregion
            
            Console.WriteLine("Testing complete");
            
            Console.ReadLine();
        }
Example #3
0
        public static MathObject QuadraticEquation(MathObject a, MathObject b, MathObject c, int solution=0)
        {
            if (a == new Integer(0) || a == new DoubleFloat(0.0))
                throw new Exception("a is zero. Equation is not quadratic.");

            var discriminant = b * b - 4 * a * c;

            var half = new Integer(1) / 2;

            if (solution == 0)
                return (-b + (discriminant ^ half)) / (2 * a);

            if (solution == 1)
                return (-b - (discriminant ^ half)) / (2 * a);

            throw new Exception();
        }
Example #4
0
        public static MathObject Time(Obj a, Obj b, int solution = 0)
        {
            if (a.velocity.x != null &&
                b.velocity.x != null &&
                a.acceleration.x != null &&
                a.acceleration.x != new DoubleFloat(0.0) &&
                a.acceleration.x != new Integer(0))
                return (b.velocity.x - a.velocity.x) / a.acceleration.x;

            if (a.velocity.y != null &&
                b.velocity.y != null &&
                a.acceleration.y != null &&
                a.acceleration.y != new DoubleFloat(0.0) &&
                a.acceleration.y != new Integer(0))
                return (b.velocity.y - a.velocity.y) / a.acceleration.y;

            // yf = yi + vyi * t + 1/2 * ay * t^2
            // 0 = 1/2 * ay * t^2 + vyi * t + yi - yf
            // apply quadratic equation to find t

            if (a.position.y != null &&
                b.position.y != null &&
                a.velocity.y != null &&
                a.acceleration.y != null &&
                a.acceleration.y != new Integer(0) &&
                a.acceleration.y != new DoubleFloat(0.0))
            {
                var half = new Integer(1) / 2;

                return
                    Misc.QuadraticEquation(
                        half * a.acceleration.y,
                        a.velocity.y,
                        a.position.y - b.position.y,
                        solution);
            }

            throw new Exception();
        }
Example #5
0
        public static Point InitialVelocity(Obj a, Obj b)
        {
            if (a.time != null && b.time != null)
            {
                var dt = b.time - a.time;

                if (Misc.NotNull(
                    a.position.x,
                    a.position.y,
                    b.position.x,
                    b.position.y,
                    a.acceleration.x,
                    a.acceleration.y))
                {
                    var half = new Integer(1) / 2;

                    return
                        (b.position - a.position - half * a.acceleration * (dt ^ 2))
                        /
                        dt;
                }
            }

            throw new Exception();
        }
Example #6
0
 public Fraction(Integer a, Integer b)
 {
     numerator = a; denominator = b;
 }