/// <summary>
        /// Initializes a new instance of the Function class.
        /// </summary>
        /// <param name="identifier">The name of the function. The identifier is the following part of the
        /// function: "_f_(x[,...])."</param>
        /// <param name="expression">The expression this function should represent.</param>
        /// <param name="arguments">The explicit arguments that should be specified whenever this function
        /// is called. The order of the items in this parameter is the order in which the arguments should
        /// be specified when calling <see cref="Evaluate"/>.</param>
        public Function(string identifier, string expression, IList<string> arguments)
        {
            Id = _nextId++;
            Identifier = identifier;

            // Only optimize the graphing argument if there isn't an explicit argument
            // with the same identifier
            _expr = ExpressionCompiler.CompileInfix(expression,
                                                    !arguments.Any(arg => arg == Calculator.GraphingArgument));

            if (arguments.Count == 0)
            {
                ExplicitArguments = new ReadOnlyCollection<string>(new List<string>());
                return;
            }

            // The local indicies of the explicit arguments in the expression's arguments
            _explicitArgIndices = new int[arguments.Count];

            for (int i = 0; i < _explicitArgIndices.Length; i++)
            {
                bool found = false;

                foreach (Argument arg in Expression.Arguments.Where(arg => arg.Identifier == arguments[i]))
                {
                    _explicitArgIndices[i] = arg.Index;

                    found = true;
                    break;
                }

                if (!found)
                {
                    // The argument is not used in the expression
                    _explicitArgIndices[i] = -1;
                }
            }

            ExplicitArguments = arguments.ToList().AsReadOnly();
        }
        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));
            }
        }
        /// <summary>
        /// Evaluates the specified expression using this instance's arguments and functions.
        /// </summary>
        /// <param name="expr">The expression to evaluate.</param>
        /// <returns>A value that is the result of the evaluation of the specified expression.</returns>
        public double Evaluate(CompiledExpression expr)
        {
            var args = new double[expr.ArgumentCount];

            for (int i = 0; i < args.Length; i++)
            {
                Argument arg = expr.Arguments[i];

                args[i] = GetArgument(arg.Identifier);
            }

            return expr.Evaluate(args, this);
        }
        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());
        }