/// <summary>
        ///     Provides the value of the function
        /// </summary>
        /// <param name="context"></param>
        /// <param name="actuals"></param>
        /// <param name="explain"></param>
        /// <returns>The value for the function application</returns>
        public virtual IValue Evaluate(InterpretationContext context, Dictionary<Actual, IValue> actuals,
            ExplanationPart explain)
        {
            IValue retVal = null;

            bool useCache = EFSSystem.CacheFunctions;
            ExplainedValue explainedValue = null;

            // Only use the cached value when the EFSSystem indicates that caches should be used
            // This condition has been added to handle the fact that the user changes the status
            // of EFSSystem.CacheFunctions within a test session
            if (useCache)
            {
                if (_cachedResult == null)
                {
                    _cachedResult = new CurryCache(this);
                }

                explainedValue = _cachedResult.GetValue(actuals);
            }

            if (explainedValue == null)
            {
                int token = context.LocalScope.PushContext();
                AssignParameters(context, actuals);
                if (Cases.Count > 0)
                {
                    bool preConditionSatisfied = false;

                    // Statically defined function
                    foreach (Case aCase in Cases)
                    {
                        // Evaluate the function
                        ExplanationPart subExplanation = ExplanationPart.CreateSubExplanation(explain, aCase);
                        preConditionSatisfied = aCase.EvaluatePreConditions(context, subExplanation);
                        ExplanationPart.SetNamable(subExplanation,
                            preConditionSatisfied ? EFSSystem.BoolType.True : EFSSystem.BoolType.False);
                        if (preConditionSatisfied)
                        {
                            retVal = aCase.Expression.GetValue(context, subExplanation);
                            break;
                        }
                    }

                    if (!preConditionSatisfied)
                    {
                        ExplanationPart.CreateSubExplanation(explain, "Partial function called outside its domain");
                        AddError("Partial function called outside its domain");
                    }
                }
                else if (Surface != null && FormalParameters.Count == 2)
                {
                    double x = 0.0;
                    double y = 0.0;
                    Parameter formal1 = (Parameter) FormalParameters[0];
                    Parameter formal2 = (Parameter) FormalParameters[1];
                    foreach (KeyValuePair<Actual, IValue> pair in actuals)
                    {
                        if (pair.Key.Parameter == formal1)
                        {
                            x = GetDoubleValue(pair.Value);
                        }
                        if (pair.Key.Parameter == formal2)
                        {
                            y = GetDoubleValue(pair.Value);
                        }
                    }
                    retVal = new DoubleValue(EFSSystem.DoubleType, Surface.Val(x, y));
                }
                else if (Graph != null && FormalParameters.Count < 2)
                {
                    if (FormalParameters.Count == 0)
                    {
                        retVal = new DoubleValue(EFSSystem.DoubleType, Graph.Evaluate(0));
                    }
                    else if (FormalParameters.Count == 1)
                    {
                        double x = 0.0;
                        Parameter formal = (Parameter) FormalParameters[0];
                        foreach (KeyValuePair<Actual, IValue> pair in actuals)
                        {
                            if (pair.Key.Parameter == formal)
                            {
                                x = GetDoubleValue(pair.Value);
                            }
                        }
                        retVal = new DoubleValue(EFSSystem.DoubleType, Graph.Evaluate(x));
                    }
                }
                context.LocalScope.PopContext(token);

                ExplanationPart.SetNamable(explain, retVal);
                if (useCache)
                {
                    _cachedResult.SetValue(actuals, retVal, explain);
                }
            }
            else
            {
                retVal = explainedValue.Value;
                ExplanationPart subExplain = ExplanationPart.CreateSubExplanation(explain, "Cached result = ");
                ExplanationPart.SetNamable(subExplain, retVal);

                // Reuse the explanation of the return value computation
                // Topmost entry is the function call, useless, so don't provide it
                if (explainedValue.Explanation != null)
                {
                    foreach (ExplanationPart part in explainedValue.Explanation.SubExplanations)
                    {
                        ExplanationPart.AddSubExplanation(subExplain, part);
                    }
                }
            }

            return retVal;
        }
        /// <summary>
        ///     Clears the caches for this function
        /// </summary>
        public override void ClearCache()
        {
            base.ClearCache();

            _cachedResult = null;
            Graph = null;
            Surface = null;
        }