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); }
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); }