/// <summary>
        /// Calculates the area between, in the case of this overload, two curves.
        /// </summary>
        /// <param name="calc">The <see cref="Calculator"/> to calculate the Y-values with.</param>
        /// <param name="top">The curve that defines the top of the integral.</param>
        /// <param name="bottom">The curve that defines the bottom of the integral.</param>
        /// <param name="leftBound">The left bound of the integral.</param>
        /// <param name="rightBound">The right bound of the integral.</param>
        /// <returns>
        /// The area between the curves <paramref name="top"/> and <paramref name="bottom"/>.
        /// </returns>
        public static double CalculateIntegral(Calculator calc, StandardExpression top, StandardExpression bottom, double leftBound, double rightBound)
        {
            Func<Calculator, double> distanceMethod;

            // Optimize for anything that might be a constant here
            if (top.Expression.IsConstant && bottom.Expression.IsConstant)
            {
                // The integral between two constants is just a rectangle
                double topConstant = calc.Evaluate(top.Expression);
                double bottomConstant = calc.Evaluate(bottom.Expression);

                // Area = Width * Height
                return (rightBound - leftBound) * (topConstant - bottomConstant);
            }

            if (top.Expression.IsConstant)
            {
                double topConstant = calc.Evaluate(top.Expression);
                distanceMethod = c => topConstant - c.Evaluate(bottom.Expression);
            }
            else if (bottom.Expression.IsConstant)
            {
                double bottomConstant = calc.Evaluate(bottom.Expression);
                distanceMethod = c => c.Evaluate(top.Expression) - bottomConstant;
            }
            else
            {
                distanceMethod = c => c.Evaluate(top.Expression) - c.Evaluate(bottom.Expression);
            }

            return CalculateIntegral(calc, distanceMethod, leftBound, rightBound);
        }
        public static double CalculateDerivative(Calculator calc, StandardExpression expr, double x, out double y)
        {
            calc.GraphingArgumentValue = x;
            double y1 = calc.Evaluate(expr.Expression);

            calc.GraphingArgumentValue = x + DeltaX;
            double y2 = calc.Evaluate(expr.Expression);

            y = y1;

            // lim h -> 0 (f(x + h) - f(x)) / h = slope of the derivative
            return (y2 - y1) / DeltaX;
        }
        public static double CalculateDerivative(Calculator calc, ParametricExpression expr, double t, out PointD result)
        {
            calc.GraphingArgumentValue = t;
            double x1 = calc.Evaluate(expr.XExpression);
            double y1 = calc.Evaluate(expr.YExpression);

            calc.GraphingArgumentValue = t + DeltaX;
            double x2 = calc.Evaluate(expr.XExpression);
            double y2 = calc.Evaluate(expr.YExpression);

            double fPrime1 = (x2 - x1) / DeltaX;
            double fPrime2 = (y2 - y1) / DeltaX;

            result = new PointD(x1, y1);

            return fPrime2 / fPrime1;
        }
        private static PointD[] CalculateLine(Calculator calc, CalculatedLine line, double end, ref double pos)
        {
            if (pos > end)
            {
                throw new ArgumentException("The current position is greater than the end.");
            }

            int i = 0;
            var filled = new PointD[(int)Math.Round(Math.Abs(end - pos) / line.Increment)];
            while (end - pos > 1E-10 || pos < end)
            {
                calc.GraphingArgumentValue = pos;
                filled[i++] = new PointD(pos * line.XScale,
                                         calc.Evaluate(((StandardExpression)line.Expression).Expression) * line.YScale);

                pos += line.Increment;
            }

            return filled;
        }
        private static PointD RefineSignificant(Calculator calc, CalculatedLine line, RoughSignificant roughSig, Mode mode)
        {
            double lo = roughSig.Left.X / line.XScale;
            double hi = roughSig.Right.X / line.XScale;
            double mid = (lo + hi) / 2;
            double lastMid = double.NaN;

            var stdExpr = (StandardExpression)line.Expression;
            Func<double, int> derivComparer = mode == Mode.Maximum ? _maxDerivComparer : _minDerivComparer;

            double y;
            while (lastMid != mid)
            {
                double deriv = DerivativeCalculator.CalculateDerivative(calc, stdExpr, mid, out y);
                int result = derivComparer(deriv);
                if (result == -1)
                {
                    lo = mid;
                }
                else if (result == 1)
                {
                    hi = mid;
                }
                else
                {
                    // Unlikely, but possible
                    break;
                }

                lastMid = mid;
                mid = (lo + hi) / 2;
            }

            mid = Math.Round(mid, DerivativeCalculator.DeltaXPrecision);

            calc.GraphingArgumentValue = mid;
            y = calc.Evaluate(stdExpr.Expression);

            return new PointD(mid, y);
        }
        private static PointD RefineIntersection(Calculator calc, StandardExpression expr1, StandardExpression expr2, IList<PointD> intersectionData)
        {
            // Whether or not expr1 starts out above expr2
            bool oneOnTop = intersectionData[0].Y > intersectionData[2].Y;

            double lo = intersectionData[0].X;
            double hi = intersectionData[1].X;
            double mid = (lo + hi) / 2;
            double lastMid;

            do
            {
                calc.GraphingArgumentValue = mid;

                double y1 = calc.Evaluate(expr1.Expression);
                double y2 = calc.Evaluate(expr2.Expression);

                // Unlikely, but possible
                if (y1 == y2)
                {
                    break;
                }

                // Check to see if the current "on top" status (whether expr1
                // is above expr2) is consistent with the initial "on top" status.
                if ((y1 > y2) == oneOnTop)
                {
                    lo = mid;
                }
                else
                {
                    hi = mid;
                }

                lastMid = mid;
                mid = (lo + hi) / 2;
            }
            while (mid != lastMid);

            // Recalculate the Y value against both expressions and return the average
            calc.GraphingArgumentValue = mid;
            return new PointD(mid, (calc.Evaluate(expr1.Expression) + calc.Evaluate(expr2.Expression)) / 2).CorrectForGdiLimit();
        }
        private static PointD RefineZero(Calculator calc, StandardExpression expr, double lowerBound, double upperBound, Sign initialSign)
        {
            double lo = lowerBound;
            double hi = upperBound;
            double mid = (lo + hi) / 2;
            double lastMid;
            double y;

            int i = 0;
            do
            {
                calc.GraphingArgumentValue = mid;
                y = calc.Evaluate(expr.Expression);

                Sign currentSign = y < 0 ? Sign.Negative : Sign.Positive;

                if (currentSign == initialSign)
                {
                    lo = mid;
                }
                else
                {
                    hi = mid;
                }

                lastMid = mid;
                mid = (lo + hi) / 2;

                // The "i < 100" below is only necessary when running in release mode, without a debugger attached.
                // Otherwise, it goes into an infinite loop, even though mid and lastMid actually do equal eachother.
                // I have no idea why that is (though its probably a race condition of some kind, despite the fact
                // that I'm not doing any multithreading anywhere around here).
                i++;
            }
            while (mid != lastMid && i < 100);

            return new PointD(mid, y);
        }
        private static void PushExpressionValue(ILGenerator il, CompiledExpression left, List<CompiledExpression> expressions, Calculator calc)
        {
            if (left.Arguments.Count > 0 || left.GraphingArgumentPresent)
            {
                // Push the Calculator
                il.Emit(OpCodes.Ldarg_1);

                // Evaluate and push the left side of the equality
                il.Emit(OpCodes.Ldarg_0); // Push the expression array

                int index;
                if ((index = expressions.IndexOf(left)) != -1)
                {
                    il.Emit(OpCodes.Ldelem, index);
                }
                else
                {
                    il.Emit(OpCodes.Ldc_I4, expressions.Count);
                    il.Emit(OpCodes.Ldelem, typeof(CompiledExpression)); // Push the left expression
                    expressions.Add(left);
                }

                il.Emit(OpCodes.Call, _evaluateInfo); // Call Calculator.Evaluate(), pushing the result
            }
            else
            {
                // Push the static result of the left side's evaluation
                il.Emit(OpCodes.Ldc_R8, calc.Evaluate(left));
            }
        }
        public static ConstructedMatrix Calculate(Calculator calc, CompiledExpression expr, Window3D window, float step)
        {
            float lowZ = float.MaxValue, highZ = float.MinValue;
            List<Vector3> results = new List<Vector3>();
            List<int> startPoints = new List<int>();

            int i = 0;
            for (float x = window.MinimumX; x < window.MaximumX; x += step)
            {
                startPoints.Add(i);
                calc.SetArgument("x", x);

                bool outsideGraph = true;
                Vector3? last = null;
                for (float y = window.MinimumY; y < window.MaximumY; y += step)
                {
                    calc.SetArgument("y", y);

                    float z = (float)calc.Evaluate(expr);

                    Vector3 result = new Vector3(x, y, z);
                    results.Add(result);

                    bool includeResult = false, includeLast = false;
                    if (z > window.MaximumZ || z < window.MinimumZ)
                    {
                        // Outside the window
                        if (!outsideGraph)
                        {
                            // First point outside the window
                            includeResult = true;
                            outsideGraph = true;
                        }
                    }
                    else
                    {
                        // Inside the window
                        if (outsideGraph)
                        {
                            // First point inside the window, include the last vertex as well
                            includeLast = true;
                        }

                        includeResult = true;
                        outsideGraph = false;
                    }

                    if (includeResult)
                    {
                        if (z < lowZ)
                        {
                            lowZ = includeLast && last.HasValue ? Math.Min(z, last.Value.Z) : z;
                        }

                        if (z > highZ)
                        {
                            highZ = includeLast && last.HasValue ? Math.Max(z, last.Value.Z) : z;
                        }
                    }

                    last = result;
                    i++;
                }
            }

            List<int> indices = new List<int>();
            for (int x = 0; x < startPoints.Count - 1; x++)
            {
                int currentX = startPoints[x];
                int nextX = startPoints[x + 1];

                for (int y = 0; y < startPoints[x + 1] - startPoints[x] - 1; y++)
                {
                    indices.AddRange(new[] { currentX + y, nextX + y, nextX + y + 1, currentX + y, nextX + y + 1, currentX + y + 1 });
                }
            }

            int[] colors = new int[results.Count];

            float absLowZ = Math.Abs(lowZ);
            float mulFactor = 255F / (absLowZ + Math.Abs(highZ));

            for (int j = 0; j < results.Count; j++)
            {
                float z = results[j].Z;

                int normalized = Math.Max(0, Math.Min(255, (int)Math.Round((z + absLowZ) * mulFactor)));
                colors[j] = Color.FromArgb(normalized, normalized, normalized).ToArgb();
            }

            return new ConstructedMatrix(results.ToArray(), indices.ToArray(), colors.ToArray());
        }