internal override void CheckAndPrepare(FunBase fun, List <string> footnoteNumbers) { foreach (string dVar in new List <string>() { DefVarName.DDI, DefVarName.DDILV }) { if (infoStore.operandAdmin.Exists(dVar)) { continue; } if (dVar == DefVarName.DDI) { autoReg = true; } infoStore.operandAdmin.CheckRegistration(name: dVar, isOutVar: false, warnIfNotInit: true, description: description); infoStore.operandAdmin.SetProvidedBySetDefault(dVar); } }
private string constName = null; // only relevant if value is a constant, e.g. #_amount = $Const_A, Factor_Value = $Const_B internal override void CheckAndPrepare(FunBase fun) { double periodFactor = HandlePeriodFactor(out string xmlCleaned); // if TryParse works: a simple number ... if (double.TryParse(EM_Helpers.AdaptDecimalSign(xmlCleaned), out value)) { value *= periodFactor; return; } // ... if not: possibly a constant if (infoStore.operandAdmin.Exists(xmlValue) && infoStore.operandAdmin.GetIsGlobal(xmlValue)) { constName = xmlValue; // note: not necessary to us xmlCleaned, as a constant cannot have a period } else { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: {xmlValue} is not a valid number" }); } }
internal override void CheckAndPrepare(FunBase fun) { if (infoStore.indexTUs.ContainsKey(xmlValue)) { name = xmlValue; // in contrast to other functions FunDefTU's CheckAndPrepare must be called on "first usage" instead of once it is defined // otherwise one would get "not-yet-calculated"-waringings for variables, which are not // available at the point of TU-definition, but are available at the point of first usage if (!infoStore.indexTUs[xmlValue].isPrepared) { infoStore.indexTUs[xmlValue].CheckAndPrepare(); } infoStore.indexTUs[xmlValue].isPrepared = true; // to avoid that CheckAndPrepare is called for further usages } else { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: unknown tax unit {xmlValue}" }); } }
// currently only used by IsNtoMchild, but still make it general as future queries may make use of it protected void PrepareNMPar(FunBase fun, List <string> footnoteNumbers) { parN = fun.GetFootnotePar <ParNumber>(DefQuery.Par.N, footnoteNumbers); if (parN == null) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: compulsory query-parameter {DefQuery.Par.N} missing" }); } parM = fun.GetFootnotePar <ParNumber>(DefQuery.Par.M, footnoteNumbers); if (parM == null) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: compulsory query-parameter {DefQuery.Par.M} missing" }); } // note: getting the numbers and not just the parameters at this stage may be more efficient, but would make using // constants more difficult (actually happens) and a future change to ParFormula (for more flexibility) impossible }
internal override void CheckAndPrepare(FunBase fun, List <string> footnoteNumbers) { }
internal override void CheckAndPrepare(FunBase fun, List <string> footnoteNumbers) { PrepareIncomeInfoPar(fun, footnoteNumbers); }
internal void PrepareSpine(ref bool runSequential) { string BEFORE = "BEFORE", AFTER = "AFTER", IN = "IN", spineState = BEFORE; FunBase causeForSequential = null, prevFun = null; if (!runSequential) { foreach (FunBase fun in infoStore.spine.Values) { switch (DefinitionAdmin.GetFunDefinition(fun.description.GetFunName()).runMode) { case DefFun.RUN_MODE.NOT_APPLICABLE: continue; case DefFun.RUN_MODE.IN_SPINE: if (spineState == AFTER) { runSequential = true; causeForSequential = prevFun; } else { spineState = IN; runInSpine.Add(fun); } break; case DefFun.RUN_MODE.OUTSIDE_SPINE: if (spineState == IN) { spineState = AFTER; } if (spineState == AFTER) { runAfterSpine.Add(fun as FunOutOfSpineBase); } else { runBeforeSpine.Add(fun as FunOutOfSpineBase); } break; } if (runSequential) { break; } prevFun = fun; } } if (runSequential) { runBeforeSpine.Clear(); runAfterSpine.Clear(); runInSpine.Clear(); foreach (FunBase fun in infoStore.spine.Values) { if (DefinitionAdmin.GetFunDefinition(fun.description.GetFunName()).runMode != DefFun.RUN_MODE.NOT_APPLICABLE) { runInSpine.Add(fun); } } string cause = causeForSequential == null ? "Forcing sequential" : $"{causeForSequential.description.Get()}: usage inside spine"; infoStore.communicator.ReportProgress(new Communicator.ProgressInfo() { message = cause + " causes (slightly) slower run" }); } // organise loop-handling (if there is/are any) foreach (FunBase fun in infoStore.spine.Values) { if (fun is FunLoop) { FunLoop funLoop = fun as FunLoop; if (!funLoop.GetRange(runInSpine) || IsLoopIntersection(funLoop)) { continue; } // put loops into Dictionaries that allow LoopyGetNextFun to recognise that it needs to "talk" to the loops (when it hits start or end) // if there are several loops with the same last function (nested loops) they are sorted reversly // by their first function, in order to "ask" the inner loop first, if there should be a jump back if (!loopEnds.ContainsKey(funLoop.indexLastFun)) { loopEnds.Add(funLoop.indexLastFun, new SortedList <double, FunLoop>()); } double iSort = funLoop.indexFirstFun; // if loops have the same start and end, make the first declared the outer while (loopEnds[funLoop.indexLastFun].ContainsKey(iSort * (-1))) { iSort += 0.001; } loopEnds[funLoop.indexLastFun].Add(iSort * (-1), funLoop); // similar holds for loops with the same start, they need to be sorted reversly by their last function // in order to ask the outer loop first, if there should be a jump over if (!loopStarts.ContainsKey(funLoop.indexFirstFun)) { loopStarts.Add(funLoop.indexFirstFun, new SortedList <double, FunLoop>()); } iSort = funLoop.indexLastFun; // see above, wrt to loops with same start and end while (loopStarts[funLoop.indexFirstFun].ContainsKey(iSort * (-1))) { iSort -= 0.001; } loopStarts[funLoop.indexFirstFun].Add(iSort * (-1), funLoop); } } }
internal override void CheckAndPrepare(FunBase fun, List <string> footnoteNumbers) { isParent.CheckAndPrepare(fun, footnoteNumbers); isWithPartner.CheckAndPrepare(fun, footnoteNumbers); }
/// <summary> check and prepare the query's parameters </summary> internal abstract void CheckAndPrepare(FunBase fun, List <string> footnoteNumbers); // usually that's one footnote number (e.g. IsDepChild#2 -> 2)
private bool RunSequential(SpineAdmin spineManager) { int curFunIndex = 0; Dictionary <FunBase, List <FunUnitLoop> > unitLoopRanges = spineManager.GetUnitLoopRangesForSequentialRun(); while (true) // loop over functions { FunBase fun = spineManager.GetNextFun(ref curFunIndex); if (fun == null) { break; } DefFun.RUN_MODE runMode = // find out if the function expects to be run within HH-loop or not DefinitionAdmin.GetFunDefinition(fun.description.GetFunName()).runMode; if (runMode != DefFun.RUN_MODE.IN_SPINE) { (fun as FunOutOfSpineBase).Run(); // concerns functions usually running pre- or post-spine (DefOutput, Totals, etc.) } else { // concerns functions usually running in spine (BenCalc, Elig, etc.) FunInSpineBase spineFun = fun as FunInSpineBase; if (infoStore.runConfig.forceSequentialRun) { // run in a single thread foreach (HH hh in infoStore.hhAdmin.hhs) { foreach (List <Person> tu in hh.GetTUs(spineFun.GetTUName())) { SpineFunRun(spineFun, hh, tu); } } } else { // run in multiple threads Parallel.ForEach <HH>(infoStore.hhAdmin.hhs, hh => { foreach (List <Person> tu in hh.GetTUs(spineFun.GetTUName())) { SpineFunRun(spineFun, hh, tu); } }); } } if (!infoStore.communicator.ReportProgress( new Communicator.ProgressInfo() { message = $"Done with function {fun.description.Get(true)}" })) { return(false); } } return(true); void SpineFunRun(FunInSpineBase spineFun, HH hh, List <Person> tu) { bool run = false; if (unitLoopRanges.ContainsKey(spineFun)) // there are UnitLoops in the spine { // note that a function may be enclosed by more than one UnitLoop (see NRR add-on) foreach (FunUnitLoop ul in unitLoopRanges[spineFun]) // if any of the UnitLoops allows running - run { if (!ul.IsFinished(hh)) { run = true; } } } else { run = true; // the usual case - no UnitLoops } if (run) { spineFun.Run(hh, tu); } } }
internal override void CheckAndPrepare(FunBase fun) { ExtractStandardFootnotes(xmlValue, out string queryName, fun); // it is not impossible that #_Low/UpLim are applied on queries // and #_Level may make good sense DefinitionAdmin.GetQueryDefinition(queryName, out DefinitionAdmin.Query queryDef, out string queryMainName); switch (queryMainName) // a query may have aliases, therefore GetQueryDefinition returns the "main"-name { case DefQuery.GetSystemYear: query = new QueryGetSystemYear(infoStore); break; case DefQuery.GetDataIncomeYear: query = new QueryGetDataIncomeYear(infoStore); break; case DefQuery.GetExchangeRate: query = new QueryGetExchangeRate(infoStore); break; case DefQuery.GetPartnerIncome: query = new QueryGetPartnerIncome(infoStore); break; case DefQuery.GetMotherIncome: query = new QueryGetMotherIncome(infoStore); break; case DefQuery.GetFatherIncome: query = new QueryGetFatherIncome(infoStore); break; case DefQuery.GetCoupleIncome: query = new QueryGetCoupleIncome(infoStore); break; case DefQuery.GetParentsIncome: query = new QueryGetParentsIncome(infoStore); break; case DefQuery.GetOwnChildrenIncome: query = new QueryGetOwnChildrenIncome(infoStore); break; case DefQuery.HasMaxValInTu: query = new QueryHasMaxValInTu(infoStore); break; case DefQuery.HasMinValInTu: query = new QueryHasMinValInTu(infoStore); break; case DefQuery.IsHeadOfTu: query = new QueryIsHead(infoStore); break; case DefQuery.IsMarried: query = new QueryIsMarried(infoStore); break; case DefQuery.IsCohabiting: query = new QueryIsCohabiting(infoStore); break; case DefQuery.IsWithPartner: query = new QueryIsWithPartner(infoStore); break; case DefQuery.IsUsedDatabase: query = new QueryIsUsedDatabase(infoStore); break; case DefQuery.IsNtoMchild: query = new QueryIsNtoMChild(infoStore); break; case DefQuery.IsParent: query = new QueryIsParent(infoStore); break; case DefQuery.IsParentOfDepChild: query = new QueryIsParentOfDepChild(infoStore); break; case DefQuery.IsInEducation: query = new QueryIsInEducation(infoStore); break; case DefQuery.IsDisabled: query = new QueryIsDisabled(infoStore); break; case DefQuery.IsPartner: query = new QueryIsPartner(infoStore); break; case DefQuery.IsDepChild: query = new QueryIsDepChild(infoStore); break; case DefQuery.IsOwnChild: query = new QueryIsOwnChild(infoStore); break; case DefQuery.IsOwnDepChild: query = new QueryIsOwnDepChild(infoStore); break; case DefQuery.IsLooseDepChild: query = new QueryIsLooseDepChild(infoStore); break; case DefQuery.IsDepParent: query = new QueryIsDepParent(infoStore); break; case DefQuery.IsDepRelative: query = new QueryIsDepRelative(infoStore); break; case DefQuery.IsLoneParentOfDepChild: query = new QueryIsLoneParentOfDepChild(infoStore); break; case DefQuery.IsCivilServant: query = new QueryIsCivilServant(infoStore); break; case DefQuery.IsBlueColl: query = new QueryIsBlueColl(infoStore); break; case DefQuery.IsOutputCurrencyEuro: query = new QueryIsOutputCurrencyEuro(infoStore); break; case DefQuery.IsParamCurrencyEuro: query = new QueryIsParamCurrencyEuro(infoStore); break; case DefQuery.IsLoneParent: query = new QueryIsLoneParent(infoStore); break; case DefQuery.nChildrenOfCouple: query = new QueryNChildrenOfCouple(infoStore); break; case DefQuery.nDepChildrenOfCouple: query = new QueryNDepChildrenOfCouple(infoStore); break; case DefQuery.nAdultsInTu: query = new QueryNAdultsInTu(infoStore); break; case DefQuery.nDepChildrenInTu: query = new QueryNDepChildrenInTu(infoStore); break; case DefQuery.nLooseDepChildrenInTu: query = new QueryNLooseDepChildrenInTu(infoStore); break; case DefQuery.nDepParentsInTu: query = new QueryNDepParentsInTu(infoStore); break; case DefQuery.nDepRelativesInTu: query = new QueryNDepRelativesInTu(infoStore); break; case DefQuery.nDepParentsAndRelativesInTu: query = new QueryNDepParentsAndRelativesInTu(infoStore); break; case DefQuery.nPersInUnit: query = new QueryNPersInUnit(infoStore); break; case DefQuery.rand: query = new QueryRand(infoStore); break; default: // the query must be defined in DefQuery, otherwise there wouldn't be a ParQuery-parameter, but it seems to be not implemented) infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: usage of not implemented query {queryName}" }); return; } isGlobal = queryDef.isGlobal; query.description = new Description(description, queryName); query.CheckAndPrepare(fun, GetFootnoteNumbers(xmlValue, out string dummy)); }
/// <summary> /// checks if the standard footnote-parameters, i.e. #_LowLim, #_UpLim, #_Level, are applied on the parameter /// if so, stores them in lowLim, upLim and alternativeTU (see above) /// also provides the "cleaned" value (e.g. yem#1 -> yem) /// note that the footnote-parameters are assessed via the (mother)function (parameter fun) /// also note that the only other (not here handled) footnote-parameters are 'amount', which is handled by ParFormula /// and query parameters which will not issue a "missing" (see below) but let the query check if it can use the footnote /// </summary> protected void ExtractStandardFootnotes(string value, out string cleanedValue, FunBase fun) { List <string> numbers = GetFootnoteNumbers(value, out cleanedValue); // it's unlikely, but yem#1#3 is possible (see GetFootnoteNumbers) if (numbers.Count == 0) { return; // no footnotes } // limits (see remark in _FunInSpineBase_Lim wrt not using a common implementation for limits) pLowLim = fun.GetFootnotePar <ParFormula>(DefPar.Footnote.LowLim, numbers); if (pLowLim != null) { if (pLowLim.IsGlobal(true)) { lowLim = pLowLim.GetGlobalValue(); pLowLim = null; } else { doRunTimeLimCheck = true; } } pUpLim = fun.GetFootnotePar <ParFormula>(DefPar.Footnote.UpLim, numbers); if (pUpLim != null) { if (pUpLim.IsGlobal(true)) { upLim = pUpLim.GetGlobalValue(); pUpLim = null; } else { doRunTimeLimCheck = true; } } ParCateg pLimpriority = fun.GetFootnotePar <ParCateg>(DefPar.Footnote.LimPriority, numbers); if (pLimpriority != null) { limPri = pLimpriority.GetCateg(); } if (!doRunTimeLimCheck) // both limits available (might be double.MinValue and/or double.MaxValue, but does no harm here) { CheckLimits(ref lowLim, ref upLim); } // alternative TU ParBase parAltTU = fun.GetFootnotePar <ParBase>(DefPar.Footnote.Level, numbers); if (parAltTU != null) { alternativeTU = parAltTU.xmlValue; } // if none of the standard footnotes is defined in the function #x points to nowhere ... if (GetType() == typeof(ParQuery)) // ... except for queries, which may have own footnotes { return; // (this is slightly negligent, as it accepts e.g. IsMarried#1 without respective footnote-parameter) } if (pLowLim == null && lowLim == double.MinValue && pUpLim == null && upLim == double.MaxValue && parAltTU == null) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = true, message = $"{description.Get()}: missing footnote parameter for {value}" }); } }
internal string alternativeTU = null; // #_Level internal virtual void CheckAndPrepare(FunBase fun) { }
internal override void CheckAndPrepare(FunBase fun) { if (FunIlArithOp.HandlesCheckAndPrepare(this)) { return; // IlArithOp has a special handling for the Formula parameter (see FunIlArithOp.cs) } if (xmlValue.Contains("}#")) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = true, runTimeErrorId = description.funID, message = $"{description.Get()}: Footnotes should not be placed outside curly brackets! Please make sure that all footnotes are directly applied to a single operand." }); } // for optimisation reasons handle #Amount and periods (#y, #w, ...) before parsing the formula: // alternatively one could allow for PAR_TYPE.NUMBER in the switch below, which would create a ParNumber that takes care // but then e.g. 1200#y*3 would, for each HH, ask for parNumber.GetValue()*3, while with this "preparsing" we just have 300 string preParsedFormula = PreParse(xmlValue, fun); // parse the main formula try { parsedExpression = calc.CompileExpression(preParsedFormula); } catch (Exception e) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: {e.Message}" }); return; } foreach (string op in calc.Variables.Keys) // parse the formula ... { ParBase_FormulaComponent opPar = null; // ... and prepare the operands GetFootnoteNumbers(op, out string opName); // temporarily remove potential footnotes to get the pure operand (e.g. yem#1->yem) // handle "partner:yem" or "head:yem" if (opName.Contains(":")) { // this is not allowed outside DefTU! if (description.fun.Name != DefFun.DefTu) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: invalid use of target outside of a DefTU '{opName}'." }); } opName = opName.Substring(opName.IndexOf(":") + 1); } if (infoStore.operandAdmin.Exists(opName)) { switch (infoStore.operandAdmin.GetParType(opName)) { //case DefPar.PAR_TYPE.NUMBER: opPar = new ParNumber(infoStore); break; // see PreParse above case DefPar.PAR_TYPE.VAR: opPar = new ParVar(infoStore); break; case DefPar.PAR_TYPE.IL: opPar = new ParIL(infoStore); break; case DefPar.PAR_TYPE.QUERY: opPar = new ParQuery(infoStore); break; } } else { opPar = new ParVar(infoStore); // if not yet in the operand-list, the only (not faulty) option is a data-variable } opPar.description = description; opPar.xmlValue = op; opPar.CheckAndPrepare(fun); operands.Add(op, opPar); } }