private static Result <bool> RearrangeOnce(EqnCalc eqCalc, int argIndexToPreserve, ContentManager cm)
        {
            FunctionCalc lhsFunCalc  = eqCalc.Inputs[0] as FunctionCalc;
            Function     lhsFunction = lhsFunCalc?.Function;

            if (lhsFunction == null)
            {
                return(Result <bool> .Bad($"Unexpected error: lhs is not a funcalc"));
            }

            SingleResult argToPreserve = lhsFunCalc.Inputs[argIndexToPreserve];

            // Create placeholders that the function rearrangements will treat as variables
            IList <string> argPlaceholders = new List <string>();
            IDictionary <string, SingleResult> placeFillers = new Dictionary <string, SingleResult>();

            for (int i = 0; i < lhsFunCalc.Inputs.Count; i++)
            {
                string placeholder = $"v{i}";
                argPlaceholders.Add(placeholder);
                if (i != argIndexToPreserve)
                {
                    placeFillers.Add(placeholder, lhsFunCalc.Inputs[i]);
                }
            }
            string resPlaceHolder = "res";

            placeFillers.Add(resPlaceHolder, eqCalc.Inputs[1]);

            // Ask the function definition for a rearrangement that isolates the required variable
            Result <string> r_rearrangement = lhsFunction.GetRearrangement(argPlaceholders, resPlaceHolder, argIndexToPreserve);

            if (r_rearrangement.IsNotGood() || string.IsNullOrEmpty(r_rearrangement.Value))
            {
                return(Result <bool> .Bad($"Rearrangement failed: {r_rearrangement.Message}"));
            }

            // Parse the rearrangement expression
            JongErrWarn  errW   = null;
            FunctionCalc newRhs = cm.CreateFunctionCalcFromExpression(r_rearrangement.Value, "", null, out errW);

            if (newRhs == null)
            {
                return(Result <bool> .Bad($"Rearrangement cannot be used: {errW?.ErrWarnString}"));
            }

            // Subsitute for the placeholders
            Result <bool> r_ok = SubsituteForPlaceholders(newRhs, placeFillers);

            if (r_ok.IsNotGood())
            {
                return(Result <bool> .Bad($"Error substituting for placeholders: {r_ok.Message}"));
            }

            // Update the eqCalc with the rearrangement
            eqCalc.Inputs[1] = newRhs;
            eqCalc.Inputs[0] = argToPreserve;

            return(Result <bool> .Good(true));
        }
        /// <summary>
        /// Recursively calc param types for all sub-levels and this level.
        /// Guard against a mistake in the tree that causes infinite recursion.
        /// </summary>
        /// <returns></returns>
        private Result <Dimensions> CalcDimensions_Recursive(IList <SingleResult> callStack)
        {
            try
            {
                IList <Dimensions> argDimensions = new List <Dimensions>();
                IList <double?>    argConstants  = new List <double?>();

                foreach (SingleResult input in Inputs)
                {
                    double?constVal = null;

                    if (input is SingleValue)
                    {
                        argDimensions.Add(input.CalcQuantity?.GetUOM()?.Dimensions);

                        if ((input is Literal) || (input is Constant))
                        {
                            constVal = input.CalcQuantity.Value;
                        }
                    }
                    else if (input is FunctionCalc)
                    {
                        FunctionCalc subFunctionCalc = (input as FunctionCalc);

                        if (callStack.IndexOf(input) >= 0)
                        {
                            return(Result <Dimensions> .Bad("Infinite recursion in Expression Tree"));
                        }

                        callStack.Add(input);
                        Result <Dimensions> subDims = subFunctionCalc.CalcDimensions_Recursive(callStack);
                        callStack.Remove(input);

                        if (subDims.IsBad())
                        {
                            return(Result <Dimensions> .Bad(subDims.Message));
                        }

                        argDimensions.Add(subDims.Value);
                    }
                    else
                    {
                        return(Result <Dimensions> .Bad(ErrorUtils.UnspecifiedErrorMsg("CalcDimensions_Recursive", "")));
                    }

                    argConstants.Add(constVal);
                }

                Result <Dimensions> resDims = Function.CalcDimensions(argDimensions, argConstants);

                return(resDims);
            }
            catch (Exception ex)
            {
                return(Result <Dimensions> .Bad(ErrorUtils.UnspecifiedErrorMsg("CalcDimensions_Recursive", ex)));
            }
        }
        /// <returns>true if any errors are found at this level or below</returns>
        /// Guard against a mistake in the tree that causes infinite recursion.
        private static bool GetErrors2(FunctionCalc fnCalc, ref IList <FunCalcError> lowestErrors, ref IList <FunCalcError> allErrors, string NumberFormat, IList <SingleResult> callStack)
        {
            bool         bRet         = false;
            FunCalcError funCalcError = null;

            // ----------------------
            if (fnCalc.GetCalcStatus() == CalcStatus.Bad)
            {
                funCalcError = new FunCalcError(fnCalc.CalcQuantity?.Message, fnCalc);
                allErrors.Add(funCalcError);
                bRet = true;
            }

            // ----------------------
            bool bFoundLower = false;

            foreach (SingleResult input in fnCalc.Inputs)
            {
                bool bFoundMore = false;
                if (input is FunctionCalc)
                {
                    if (callStack.IndexOf(input) >= 0)
                    {
                        throw new UnspecifiedException("Infinite recursion in Expression Tree");
                    }
                    callStack.Add(input);
                    bFoundMore = GetErrors2(((FunctionCalc)input), ref lowestErrors, ref allErrors, NumberFormat, callStack);
                    callStack.Remove(input);
                }
                else
                {
                    bFoundMore = GetErrors3(input, ref lowestErrors, ref allErrors, NumberFormat);
                }

                if (bFoundMore)
                {
                    bFoundLower = true;
                }
            }

            // ----------------------
            if (bFoundLower)
            {
                bRet = true;
            }
            else if (funCalcError != null)
            {
                lowestErrors.Add(funCalcError);
            }

            return(bRet);
        }
Beispiel #4
0
        public static void AddEquationToSectionNode(ContentManager contentManager, SectionNode sectionNode, EquationDetails ed)
        {
            string description = "";

            if (!string.IsNullOrEmpty(ed.Name))
            {
                description += ((string.IsNullOrEmpty(description) ? "" : " ") + ed.Name);
            }
            if (!string.IsNullOrEmpty(ed.Reference))
            {
                description += ((string.IsNullOrEmpty(description) ? "" : " ") + ed.Reference);
            }
            if (!string.IsNullOrEmpty(ed.Description))
            {
                description += ((string.IsNullOrEmpty(description) ? "" : " ") + ed.Description);
            }

            // ----------------------
            JongErrWarn  errW    = null;
            FunctionCalc funCalc = contentManager.CreateFunctionCalcFromExpression(ed.EquationAsText, description, ed.VariableInfos, out errW);

            // ----------------------
            if (funCalc != null)
            {
                // ------------- Record the test values if there are any
                if ((ed.TestValues != null) && (ed.TestValues.Count > 0))
                {
                    funCalc.TestValues = new Dictionary </*variable name*/ string, double>();

                    for (int i = 0; i < ed.VariableInfos.Count; i++)
                    {
                        VarInfo vi = ed.VariableInfos[i];
                        funCalc.TestValues.Add(vi.Name, ed.TestValues[i]);
                    }
                }
                funCalc.ExpectedDimensions = new Dictionary </*variable name*/ string, Dimensions>();
                if (ed.VariableInfos != null)
                {
                    foreach (VarInfo vi in ed.VariableInfos)
                    {
                        funCalc.ExpectedDimensions.Add(vi.Name, vi.ParamType?.Dimensions);
                    }
                }

                // -------------
                sectionNode.EqnCalcs.Add((EqnCalc)funCalc);
            }
        }
        /// <summary>
        /// Recursively calculate all sub-levels and this level.
        /// Determine all the results and information possible.
        /// Guard against a mistake in the tree that causes infinite recursion.
        /// </summary>
        /// <returns></returns>
        private void Calculate_Recursive(IList <SingleResult> callStack)
        {
            foreach (SingleResult input in Inputs)
            {
                if (input is FunctionCalc)
                {
                    FunctionCalc subFunctionCalc = (input as FunctionCalc);

                    if (callStack.IndexOf(input) >= 0)
                    {
                        throw new UnspecifiedException("Infinite recursion in Expression Tree");
                    }
                    callStack.Add(input);
                    subFunctionCalc.Calculate_Recursive(callStack);
                    callStack.Remove(input);
                }
            }

            Calculate_Here();
        }
        /// Guard against a mistake in the tree that causes infinite recursion.
        private static void GetCalculationTreeText2(FunctionCalc fnCalc, ref string calculationTree, string sCurrentIndent, string sIndentStep, string NumberFormat, IList <SingleResult> callStack)
        {
            if (fnCalc.Function is FnEquals)
            {
                calculationTree += string.Format("{0}{1} ({2}) {3}\n",
                                                 sCurrentIndent, fnCalc.Function.Name, fnCalc.Function.AsciiSymbol,
                                                 (fnCalc.CalcQuantity.Message.Length == 0) ? "" : (" // " + fnCalc.CalcQuantity.Message));
            }
            else
            {
                calculationTree += string.Format("{0}{1} ({2}) = {3}{4}\n",
                                                 sCurrentIndent, fnCalc.Function.Name, fnCalc.Function.AsciiSymbol,
                                                 ((fnCalc.GetCalcStatus() == CalcStatus.Good) ?
                                                  string.Format("{0:" + NumberFormat + "}", fnCalc.CalcQuantity.Value) :
                                                  "!"),
                                                 (fnCalc.CalcQuantity.Message.Length == 0) ? "" : (" // " + fnCalc.CalcQuantity.Message));
            }

            // ----------------------
            foreach (SingleResult input in fnCalc.Inputs)
            {
                if (input is FunctionCalc)
                {
                    if (callStack.IndexOf(input) >= 0)
                    {
                        throw new UnspecifiedException("Infinite recursion in Expression Tree");
                    }
                    callStack.Add(input);
                    GetCalculationTreeText2(((FunctionCalc)input), ref calculationTree, (sCurrentIndent + sIndentStep), sIndentStep, NumberFormat, callStack);
                    callStack.Remove(input);
                }
                else
                {
                    FunctionCalc.GetCalculationTreeText3(input, ref calculationTree, (sCurrentIndent + sIndentStep), NumberFormat);
                }
            }
        }
        // ------------------------

        private static string FunctionCalcAsText(FunctionCalc expr, bool bSkipOpBrackets, Function lastFn, IList <SingleResult> callStack)
        {
            const string pName = "FunctionCalcAsText";
            string       sText = ""; // TODO: Perhaps replace this with StringBuilder?

            bool bThisSkipOpBrackets = bSkipOpBrackets;
            bool bNextSkipOpBrackets = false;

            Function fn = expr.Function;

            if (fn is FnEquals)
            {
                bNextSkipOpBrackets = true;
            }

            if (fn.AsciiLayout == FuncLayout.FuncLayout)
            {
                sText += fn.AsciiSymbol;

                sText += "(";

                AddExpressionInputs(ref sText, expr.Inputs, callStack);

                sText += ")";
            }
            else if (fn.AsciiLayout == FuncLayout.Op_Term)
            {
                bThisSkipOpBrackets = true;

                if (!bThisSkipOpBrackets)
                {
                    sText += "(";
                }

                sText += fn.AsciiSymbol;

                sText += ExpressionAsText(expr.Inputs[0], bSkipOpBrackets: bNextSkipOpBrackets, lastFn: fn, callStack: callStack);

                if (!bThisSkipOpBrackets)
                {
                    sText += ")";
                }
            }
            else if ((fn.AsciiLayout == FuncLayout.Term_Op_Term) || (fn.AsciiLayout == FuncLayout.Term_Superscript_Term))
            {
                if (fn == lastFn)        // is Functions.FnMultiply)
                {
                    bThisSkipOpBrackets = true;
                }
                if (!bThisSkipOpBrackets)
                {
                    sText += "(";
                }

                sText += ExpressionAsText(expr.Inputs[0], bSkipOpBrackets: bNextSkipOpBrackets, lastFn: fn, callStack: callStack);

                sText += fn.AsciiSymbol;

                sText += ExpressionAsText(expr.Inputs[1], bSkipOpBrackets: bNextSkipOpBrackets, lastFn: fn, callStack: callStack);

                if (!bThisSkipOpBrackets)
                {
                    sText += ")";
                }
            }
            else if (fn.AsciiLayout == FuncLayout.Term_OverOp_Term)
            {
                // The test below is commented out because although it is likely correct, it is not necessary...
                //if (!(fn is Functions.FnDivide))
                //{
                //    throw new UnspecifiedException($"Error in {pName}");        // What else could it be? What do we show instead of a line?
                //}

                if (!bThisSkipOpBrackets)
                {
                    sText += "(";
                }

                bNextSkipOpBrackets = true;     //Explicity added below

                bool bNumeratorBrackets   = ((expr.Inputs[0] is SingleValue)?false:true);
                bool bDenominatorBrackets = ((expr.Inputs[1] is SingleValue) ? false : true);

                if (bNumeratorBrackets)
                {
                    sText += "(";
                }

                sText += ExpressionAsText(expr.Inputs[0], bSkipOpBrackets: bNextSkipOpBrackets, lastFn: fn, callStack: callStack);

                if (bNumeratorBrackets)
                {
                    sText += ")";
                }

                sText += fn.AsciiSymbol;

                if (bDenominatorBrackets)
                {
                    sText += "(";
                }

                sText += ExpressionAsText(expr.Inputs[1], bSkipOpBrackets: bNextSkipOpBrackets, lastFn: fn, callStack: callStack);

                if (bDenominatorBrackets)
                {
                    sText += ")";
                }

                if (!bThisSkipOpBrackets)
                {
                    sText += ")";
                }
            }
            //else if (fn.AsciiLayout == FuncLayout.Term_Superscript_Term)
            //{
            //    sText += ExpressionAsText(expr.Inputs[0], bSkipOpBrackets: bNextSkipOpBrackets, lastFn: fn, callStack: callStack);

            //    if (expr.Inputs[1] is SingleValue)
            //    {
            //        sText += ((SingleValue)expr.Inputs[1]).Text;
            //    }
            //    else
            //    {
            //        // TODO: Equations
            //    }
            //}

            return(sText);
        }