protected void PrepareLimPar() { coParLowLim = GetUniquePar <ParFormula>(DefPar.Common.LowLim); coParUpLim = GetUniquePar <ParFormula>(DefPar.Common.UpLim); coParThres = GetUniquePar <ParFormula>(DefPar.Common.Threshold); ParCateg coParLimpriority = GetUniquePar <ParCateg>(DefPar.Common.Limpriority); // the following tries to optimise by getting the values here, if they are read-time-available (e.g. 600#y, GetSystemYear, ...) // and by checking for implausible values (e.g. upLim = 10, lowLim = 100) if already possible if (coParLowLim != null) { if (coParLowLim.IsGlobal(true)) { lowLim = coParLowLim.GetGlobalValue(); coParLowLim = null; } else { doRunTimeLimCheck = true; } } if (coParUpLim != null) { if (coParUpLim.IsGlobal(true)) { upLim = coParUpLim.GetGlobalValue(); coParUpLim = null; } else { doRunTimeLimCheck = true; } } bool checkThres = coParThres != null; if (coParThres != null) { if (coParThres.IsGlobal(true)) { thres = coParThres.GetGlobalValue(); coParThres = null; } else { doRunTimeThresCheck = true; } } if (coParLimpriority != null) { limPri = coParLimpriority.GetCateg(); } if (!doRunTimeLimCheck) // both limits available (might be double.MinValue and/or double.MaxValue, but does no harm here) { CheckLimits(ref lowLim, ref upLim); if (checkThres && !doRunTimeThresCheck) // threshold and both limits available { if (!CheckThreshold(thres, lowLim, upLim)) { thres = lowLim; } } } }
/// <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}" }); } }
protected override double DoFunWork(HH hh, List <Person> tu) { // first make a run-time copy of all bands (for thread-safety) List <Band> rtBands = new List <Band>(bands.Count); bands.ForEach((b) => rtBands.Add(b.Clone() as Band)); double safeBaseThreshold = baseThreshold; double safeQuotient = quotient; // do the rouding and check the threshold & quotient double baseVal = basePar.GetValue(hh, tu); if (!double.IsNaN(roundBase)) { baseVal = Math.Round(baseVal / roundBase) * roundBase; } if (baseThresholdPar != null && !baseThresholdPar.IsGlobal(true)) { safeBaseThreshold = baseThresholdPar.GetValue(hh, tu); } if (quotientPar != null && !quotientPar.IsGlobal(true)) { safeQuotient = quotientPar.GetValue(hh, tu); } if (baseVal < safeBaseThreshold) { return(0); // if base is under the threshold, return 0 } // calculate and check the limits - we only need to calculate the upLim of each Band at runtime, except for Band 1 if (doRunTimeLimCheck) { if (rtBands[0].pLowLim != null) { rtBands[0].lowLim = rtBands[0].pLowLim.GetValue(hh, tu); } foreach (Band band in rtBands) { if (band.pUpLim != null) { band.upLim = band.pUpLim.GetValue(hh, tu); } } CheckGroupLimits(rtBands, description.funID, hh); } // then check if we need to calculate any rates/amounts if (doRunTimeRateCheck) { foreach (Band band in rtBands) { if (band.pRate != null) { band.rate = band.pRate.GetValue(hh, tu); } else if (band.pAmount != null) { band.amount = band.pAmount.GetValue(hh, tu); } } } baseVal = baseVal / safeQuotient; double tax = 0; if (simpleProg) // apply the max rate/amount to all income { if (rtBands.Count > 0 && baseVal > rtBands.Last().upLim) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = true, runTimeErrorId = description.funID, message = $"{description.Get()}: '{DefPar.SchedCalc.Base}' exceeds '{DefPar.SchedCalc.Band_UpLim}' of highest band ({baseVal}>{rtBands.Last().upLim}) - '{(double.IsNaN(rtBands.Last().rate) ? DefPar.SchedCalc.Band_Amount : DefPar.SchedCalc.Band_Rate)}' of highest band is applied" }); } // get the highest band Band hb = null; foreach (Band b in rtBands) { if (b.lowLim <= baseVal) { hb = b; } if (b.upLim > baseVal) { break; } } if (hb == null) { tax = 0; } else if (!double.IsNaN(hb.rate)) { tax = baseVal * hb.rate; } else { tax = hb.amount; } } else // apply each rate/amount to the equivalent band { double prevLim = Math.Max(rtBands[0].lowLim, 0); // start taxing at the lowest limit of the first band foreach (Band b in rtBands) { if (prevLim > baseVal) { break; } if (!double.IsNaN(b.rate)) { tax += (Math.Max(Math.Min(baseVal, b.upLim) - prevLim, 0)) * b.rate; } else { tax += b.amount; } prevLim = b.upLim; } } tax = tax * safeQuotient; return(tax); }
protected override void PrepareNonCommonPar() { if (GetParGroups(DefPar.SchedCalc.Band_XXX).Count < 1) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: SchedCalc must have at least one band" }); return; } foreach (KeyValuePair <int, List <ParBase> > bandParList in GetParGroups(DefPar.SchedCalc.Band_XXX)) { Band band = new Band(); // Prepare the Band Limits band.band = bandParList.Key; band.pLowLim = GetUniqueGroupPar <ParFormula>(DefPar.SchedCalc.Band_LowLim, bandParList.Value); if (band.pLowLim != null) // if there is a lowLim { if (band.pLowLim.IsGlobal(true)) { band.lowLim = band.pLowLim.GetGlobalValue(); band.pLowLim = null; } else { doRunTimeLimCheck = true; } } band.pUpLim = GetUniqueGroupPar <ParFormula>(DefPar.SchedCalc.Band_UpLim, bandParList.Value); if (band.pUpLim != null) // if there is a lowLim { if (band.pUpLim.IsGlobal(true)) { band.upLim = band.pUpLim.GetGlobalValue(); band.pUpLim = null; } else { doRunTimeLimCheck = true; } } // Prepare the Band Rate/Amount band.pRate = GetUniqueGroupPar <ParFormula>(DefPar.SchedCalc.Band_Rate, bandParList.Value); band.pAmount = GetUniqueGroupPar <ParFormula>(DefPar.SchedCalc.Band_Amount, bandParList.Value); if (band.pRate != null && band.pAmount != null) // Rate and Amount cannot co-exist { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: Rate and Amount cannot co-exist in group {band.band}" }); } if (band.pRate == null && band.pAmount == null) // Rate and Amount cannot both be missing { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: Rate and Amount cannot both be missing in group {band.band}" }); } if (band.pRate != null) { if (band.pRate.IsGlobal(true)) { band.rate = band.pRate.GetGlobalValue(); band.pRate = null; } else { doRunTimeRateCheck = true; } } if (band.pAmount != null) { if (band.pAmount.IsGlobal(true)) { band.amount = band.pAmount.GetGlobalValue(); band.pAmount = null; } else { doRunTimeRateCheck = true; } } bands.Add(band); } // check that all required limits exist for (int i = 1; i < bands.Count; i++) { // if both limits are missing then produce an error if (bands[i - 1].upLim == double.MaxValue && bands[i - 1].pUpLim == null && bands[i].lowLim == double.MinValue && bands[i].pLowLim == null) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: Band {bands[i - 1].band}: insufficient definition of upper limit. Use parameters 'band_uplim' or 'band_lowlim'(+1)." }); } // if both limits are there, then produce an error else if ((bands[i - 1].upLim != double.MaxValue || bands[i - 1].pUpLim != null) && (bands[i].lowLim != double.MinValue || bands[i].pLowLim != null)) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: Double definition of upper limit as 'Band_UpLim {bands[i - 1].band}' and 'Band_LowLim {bands[i].band}'." }); } // else, make sure that the upper limit always exist (copy from Group+1 lower limit if required) else if (bands[i - 1].upLim == double.MaxValue && bands[i - 1].pUpLim == null) { if (bands[i].pLowLim == null) { bands[i - 1].upLim = bands[i].lowLim; // copy the litteral limit if it exists } else { bands[i - 1].pUpLim = bands[i].pLowLim; // else copy the parameter to be run at run-time } } } // if possible, check that limits are also in order if (!doRunTimeLimCheck) { CheckGroupLimits(bands); } // prepare the other parameters basePar = GetUniquePar <ParFormula>(DefPar.SchedCalc.Base); if (basePar == null) { infoStore.communicator.ReportError(new Communicator.ErrorInfo() { isWarning = false, message = $"{description.Get()}: Missing required parameter Base" }); } ParBool doAverageRatesPar = GetUniquePar <ParBool>(DefPar.SchedCalc.Do_Average_Rates); doAverageRates = doAverageRatesPar != null && doAverageRatesPar.GetBoolValue(); ParBool simpleProgPar = GetUniquePar <ParBool>(DefPar.SchedCalc.Simple_Prog); simpleProg = simpleProgPar != null && simpleProgPar.GetBoolValue(); quotientPar = GetUniquePar <ParFormula>(DefPar.SchedCalc.Quotient); if (quotientPar != null) { if (quotientPar.IsGlobal(true)) { quotient = quotientPar.GetGlobalValue(); quotientPar = null; } } baseThresholdPar = GetUniquePar <ParFormula>(DefPar.SchedCalc.BaseThreshold); if (baseThresholdPar != null) { if (baseThresholdPar.IsGlobal(true)) { baseThreshold = baseThresholdPar.GetGlobalValue(); baseThresholdPar = null; } } ParNumber roundBasePar = GetUniquePar <ParNumber>(DefPar.SchedCalc.Round_Base); if (roundBasePar != null) { roundBase = roundBasePar.GetValue(); } }
/// <summary> /// does condition refer to variables which are equal for each person, this is necessary for checking RunCond /// strong = true: condition may only contain global queries and numbers, i.e. no global variables (which may change at run-time) /// i.e. strong means is-read-time-available /// </summary> internal bool IsGlobal(bool strong = false) { return(parFormula.IsGlobal(strong)); }