예제 #1
0
        // used by HasMinValInTU and HasMaxValInTU
        protected void PrepareMinMaxPar(FunBase fun, List <string> footnoteNumbers)
        {
            minMaxVal = fun.GetFootnotePar <ParVarIL>(DefQuery.Par.Val, footnoteNumbers);
            if (minMaxVal == null)
            {
                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                {
                    isWarning = false, message = $"{description.Get()}: compulsory query-parameter {DefQuery.Par.Val} missing"
                });
            }

            ParBool parUnique = fun.GetFootnotePar <ParBool>(DefQuery.Par.Unique, footnoteNumbers);

            if (parUnique != null)
            {
                minMaxUnique = parUnique.GetBoolValue();
            }

            ParBool parAd = fun.GetFootnotePar <ParBool>(DefQuery.Par.Adults_Only, footnoteNumbers);

            if (parAd != null)
            {
                minMaxAdultsOnly = parAd.GetBoolValue();
            }
        }
예제 #2
0
        // note that at this stage parameters are available (via GetxxxPar-functions of FunBase)
        // but not yet prepared, i.e. only xmlValue is available
        internal override void ProvideIndexInfo()
        {
            ParBase parName = GetUniquePar <ParBase>(DefPar.DefIl.Name); if (parName == null)
            {
                return;
            }

            Dictionary <string, double> content = new Dictionary <string, double>();

            foreach (var part in GetPlaceholderPar <ParBase>())
            {
                string varILName = part.Key;
                content.Add(varILName, GetAddMod(part.Value)); // note that vars/ILs are 'registered' (i.e.a.o. checked) in ParIL.CheckAndPrepare (i.e. on 1st use)
            }

            ParBool parWarn      = GetUniquePar <ParBool>(DefPar.DefIl.Warn_If_NonMonetary);
            bool    warnIfNonMon = parWarn == null?DefinitionAdmin.GetParDefault <bool>(DefFun.DefIl, DefPar.DefIl.Warn_If_NonMonetary) :
                                       ParBool.IsTrue(parWarn.xmlValue); // note: GetParBoolValueOrDefault cannot be used as parameter WarnIfNonMon is not yet checked

            infoStore.operandAdmin.RegisterIL(parName.xmlValue, DefFun.DefIl, description, content, warnIfNonMon);
        }
예제 #3
0
        /// <summary>
        /// try to identify parameters and, if valid, generate the respective ParXXX and put into respective lists (see above)
        /// </summary>
        private void ClassifyParameters(Dictionary <string, ExeXml.Par> xmlParList, DefinitionAdmin.Fun funDef)
        {
            int dummyGroupNumber = -9999; // this is for placeholder-parameters, see below

            foreach (var xmlPar in xmlParList)
            {
                Description parDescription = new Description(description, xmlPar.Value, xmlPar.Key);
                string      xmlParName     = xmlPar.Value.Name;

                // first check if it is an 'ordinary' parameter ...
                DefinitionAdmin.Par parDef = funDef.GetParDef(xmlParName);
                if (parDef != null)
                {
                    string officialParName = funDef.GetOfficalParName(xmlParName); // is (in contrast to xmlParName) case-sensitive and
                                                                                   // corresponds to constants as defined in Common-library
                                                                                   // generate parameter ...
                    ParBase par = GeneratePar(xmlPar, parDef, parDescription);
                    // ... and put it into the list it belongs too ...
                    if (parDef.isFootnote) // ... footnote-list
                    {
                        if (GetGroupNumber(xmlPar.Value, parDescription, out int dummy))
                        {
                            if (!footnotePar.ContainsKey(xmlPar.Value.Group))
                            {
                                footnotePar.Add(xmlPar.Value.Group, new Dictionary <string, ParBase>());
                            }
                            if (!footnotePar[xmlPar.Value.Group].ContainsKey(officialParName))
                            {
                                footnotePar[xmlPar.Value.Group].Add(officialParName, par);
                            }
                            else
                            {
                                ReportDoubleDef(parDescription);
                            }
                        }
                    }
                    else if (parDef.maxCount == 1) // ... unique-parameter-list
                    {
                        if (!uniquePar.ContainsKey(officialParName))
                        {
                            uniquePar.Add(officialParName, par);
                            if (infoStore.runConfig.warnAboutUselessGroups && !string.IsNullOrEmpty(xmlPar.Value.Group))
                            {
                                ReportUselessGroup(parDescription);
                            }
                        }
                        else
                        {
                            ReportDoubleDef(parDescription);
                        }
                    }
                    else // ... non-unique-parameter-list
                    {
                        if (!nonUniquePar.ContainsKey(officialParName))
                        {
                            nonUniquePar.Add(officialParName, new List <ParBase>());
                        }
                        nonUniquePar[officialParName].Add(par);
                        if (infoStore.runConfig.warnAboutUselessGroups && !string.IsNullOrEmpty(xmlPar.Value.Group))
                        {
                            ReportUselessGroup(parDescription);
                        }
                    }
                    continue;
                }
                // ... if not successuful, check if it is a group parameter ...
                parDef = funDef.GetGroupParDef(xmlParName, out string groupName);
                if (parDef != null)
                {
                    if (GetGroupNumber(xmlPar.Value, parDescription, out int groupNo))
                    {
                        // generate parameter ...
                        ParBase par = GeneratePar(xmlPar, parDef, parDescription);
                        // ... and put it into group-parameter-list
                        if (!groupPar.ContainsKey(groupName))
                        {
                            groupPar.Add(groupName, new SortedList <int, List <ParBase> >());
                        }
                        if (!groupPar[groupName].ContainsKey(groupNo))
                        {
                            groupPar[groupName].Add(groupNo, new List <ParBase>());
                        }
                        if (parDef.maxCount > 1 || GetUniqueGroupPar <ParBase>(xmlPar.Value.Name, groupPar[groupName][groupNo]) == null)
                        {
                            groupPar[groupName][groupNo].Add(par);
                        }
                        else
                        {
                            ReportDoubleDef(parDescription);
                        }
                    }
                    continue;
                }
                // ... if still not successful, it could still be placehoder parameter ...
                if (funDef.AllowsForPlaceholders())
                {
                    parDef = funDef.GetPlaceholderDef(out string phGroupName);
                    ParBase par = GeneratePar(xmlPar, parDef, parDescription, true);
                    if (phGroupName == null) // non-group placeholder, as e.g. in SetDefault, DefIL
                    {
                        if (!placeholderPar.TryAdd(xmlParName, par))
                        {
                            infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                            {
                                isWarning = true, message = $" {parDescription.Get()}: {xmlParName} double definition (is ignored)"
                            });
                        }
                    }
                    else // group placeholder, as e.g. in DefVar (PH, IsGlobal, IsMonetary), Uprate (PH, FactorCond)
                    {
                        GetGroupNumber(xmlPar.Value, parDescription, // note that this returns a dummy-group if the user didn't indicate one
                                       out int groupNo, true); // as placeholders only actually need a group if they are "further specified" (e.g. uprating-factors: factor_condition)
                        if (!groupPar.ContainsKey(phGroupName))
                        {
                            groupPar.Add(phGroupName, new SortedList <int, List <ParBase> >());
                        }
                        if (!groupPar[phGroupName].ContainsKey(groupNo))
                        {
                            groupPar[phGroupName].Add(groupNo, new List <ParBase>());
                        }
                        groupPar[phGroupName][groupNo].Add(par);
                    }
                    continue;
                }
                // ... now we give up
                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                {
                    isWarning = true, message = $" {parDescription.Get()}: unknown parameter"
                });
            }

            if (infoStore.runConfig.warnAboutUselessGroups)
            {
                foreach (var groupType in groupPar)
                {
                    if (groupType.Key.ToLower() == DefPar.SchedCalc.Band_XXX.ToLower())
                    {
                        continue;
                    }
                    foreach (var group in groupType.Value)
                    {
                        if (group.Key >= 0 && group.Value.Count == 1)
                        {
                            ReportUselessGroup(group.Value[0].description);
                        }
                    }
                }
            }

            // internal function for generating parameters
            ParBase GeneratePar(KeyValuePair <string, ExeXml.Par> xmlPar, DefinitionAdmin.Par parDef,
                                Description parDescription, bool isPlaceholder = false)
            {
                ParBase par;

                switch (parDef.valueType)
                {
                case DefPar.PAR_TYPE.FORMULA: par = new ParFormula(infoStore); break;

                case DefPar.PAR_TYPE.CONDITION: par = new ParCond(infoStore); break;

                case DefPar.PAR_TYPE.BOOLEAN: par = new ParBool(infoStore); break;

                case DefPar.PAR_TYPE.NUMBER: par = new ParNumber(infoStore); break;

                case DefPar.PAR_TYPE.TEXT: par = new ParBase(infoStore); break;

                case DefPar.PAR_TYPE.VAR: par = new ParVar(infoStore); break;

                case DefPar.PAR_TYPE.OUTVAR: par = new ParOutVar(infoStore); break;

                case DefPar.PAR_TYPE.IL: par = new ParIL(infoStore); break;

                case DefPar.PAR_TYPE.TU: par = new ParTU(infoStore); break;

                case DefPar.PAR_TYPE.VARorIL: par = new ParVarIL(infoStore); break;

                case DefPar.PAR_TYPE.CATEG: par = new ParCateg(infoStore, parDef.categValues); break;

                case DefPar.PAR_TYPE.PLACEHOLDER: par = new ParBase(infoStore); break;

                default: throw new Exception($"Not yet properly implemented parameter value type {parDef.valueType}");
                }
                par.description = parDescription;
                par.xmlValue    = xmlPar.Value.val;
                par.description.isPlaceholder = isPlaceholder;
                return(par);
            }

            bool GetGroupNumber(ExeXml.Par p, Description parDescription, out int groupNumber, bool allowDummy = false)
            {
                groupNumber = dummyGroupNumber++;  // placeholders (e.g.uprating factors) only actually need a group
                if (string.IsNullOrEmpty(p.Group)) // if they are "further specified" (e.g.factor_condition)
                {
                    if (allowDummy)
                    {
                        return(true);
                    }
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false, message = $" {parDescription.Get()}: missing group"
                    });
                    return(false);
                }
                if (int.TryParse(p.Group, out groupNumber))
                {
                    return(true);
                }
                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                {
                    isWarning = false, message = $" {parDescription.Get()}: invalid group {p.Group} (must be an integer number)"
                });
                return(false);
            }

            void ReportDoubleDef(Description parDescription)
            {
                infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                {
                    isWarning = false, message = $" {parDescription.Get()}: defined more than once"
                });
            }

            void ReportUselessGroup(Description parDescription)
            {
                if (parDescription.GetFunName() != DefFun.Store)    // add a special exception for Store, as it has its own reporting
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = true, message = $" {parDescription.Get()}: group has no use"
                    });
                }
            }
        }
예제 #4
0
        protected override void PrepareNonCommonPar()
        {
            if (!IsRunCondMet()) // note that this works for info that is clearly available at read-time, e.g. {IsUsedDatabase} or {1}
            {
                return;          // but simply returns true for global variables, which may (in theory) change over run-time
            }
            // to handle the latter IsRunCond is called again in the Run-function (see below)
            // consequently for e.g. {0} the variables are not even generated
            // while for {$Const}, with $Const=0 at the point of request, the variables exist but are set to 0
            // this also reflects the behaviour of the old executable

            ParBase parSysYear = GetUniquePar <ParBase>(isConst ? DefPar.DefConst.Const_SystemYear : DefPar.DefVar.Var_SystemYear);

            if (parSysYear != null && !EM_Helpers.DoesValueMatchPattern(parSysYear.xmlValue, infoStore.country.sys.year))
            {
                return;
            }

            ParBase parDataSet = GetUniquePar <ParBase>(isConst ? DefPar.DefConst.Const_Dataset : DefPar.DefVar.Var_Dataset);

            if (parDataSet != null && !infoStore.IsUsedDatabase(parDataSet.xmlValue))
            {
                return;
            }

            foreach (var g in GetParGroups(isConst ? DefPar.DefConst.THE_ONLY_GROUP : DefPar.DefVar.THE_ONLY_GROUP))
            {
                List <ParBase> group = g.Value;

                // global variables (DefConst) allow for parameter Condition (but there is no reason why it wouldn't work for DefVar as well, thus no check)
                ParCond cond = GetUniqueGroupPar <ParCond>(DefPar.DefConst.Condition, group);

                List <ParFormula> initFormulas = GetPlaceholderGroupPar <ParFormula>(group);

                if (initFormulas.Count == 0) // there can be single Monetary parameters if definition is n/a for this system
                {
                    continue;
                }
                if (cond == null && initFormulas.Count > 1)
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false,
                        message   = $"{initFormulas[0].description.Get()}: multiple definition, group {g.Key} is already in use"
                    });
                    continue;
                }

                foreach (ParFormula initFormula in initFormulas)
                {
                    string varName = initFormula.description.GetParName();
                    if (!EM_Helpers.IsValidName(varName, out string illegal))
                    {
                        infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                        {
                            isWarning = false,
                            message   = $"{initFormula.description.Get()}: name contains illegal character(s) '{illegal}'"
                        });
                        continue;
                    }

                    if (isConst && cond == null && !initFormula.IsGlobal())
                    {
                        infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                        {
                            isWarning = true,
                            message   = $"{initFormula.description.Get()}: trying to initialise global variable with a non-global amount"
                        });
                    }

                    bool isMonetary = false;
                    if (!isConst) // variables defined with DefConst cannot be monetary (more precisely, should not be added over TUs)
                    {
                        // if monetary-parameter is defined, use it, otherwise monetary=true (more precisely, get the default value from lib-def)
                        ParBool parMonetary = GetUniqueGroupPar <ParBool>(DefPar.DefVar.Var_Monetary, group);
                        isMonetary = parMonetary != null?parMonetary.GetBoolValue()
                                         : DefinitionAdmin.GetParDefault <bool>(funName: DefFun.DefVar, parName: DefPar.DefVar.Var_Monetary);
                    }

                    if (!infoStore.operandAdmin.Exists(varName)) // double definition is not illegal (maybe questionable, but handled like this in old exe)
                    {
                        infoStore.operandAdmin.RegisterVar(name: varName, creatorFun: DefFun.DefVar, description: initFormula.description,
                                                           isMonetary: isMonetary,
                                                           isGlobal: isConst && cond == null,
                                                           isWriteable: true,     // DefVar defined variables can be used as output-var (this is a bit lax with DefConst)
                                                           setInitialised: true); // DefVar defined variables always have an initial value -
                                                                                  // if user sets it n/a it does not exist, otherwise she needs to assign a value
                                                                                  // actual initialisation is done in the Run function
                        if (!infoStore.operandAdmin.Exists(varName))
                        {
                            continue;                                          // registration failed
                        }
                    }
                    else if (infoStore.operandAdmin.GetCreatorFun(varName) != DefFun.DefVar && infoStore.operandAdmin.GetCreatorFun(varName) != DefFun.DefConst)
                    {
                        infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                        {
                            isWarning = false,
                            message   = $"{description.Get()} {varName}: {DefFun.DefVar}/{DefFun.DefConst} can only overwrite variables created by (another) {DefFun.DefVar}/{DefFun.DefConst}"
                        });
                        continue;
                    }

                    // variable cannot yet be initialised, as at this stage the hh-data does not exist (no formula interpretation possible yet)
                    // note on still setting the variable initialised at this stage, in context with the "not-initialised" warning:
                    // parameters are prepared in the spine-order, thus parameters which are prepared before would get a "not known"
                    // and parameters prepared afterwards are "legal" users
                    if (!varDefs.ContainsKey(varName))
                    {
                        varDefs.Add(varName, new List <VarDef>());                               // see definition of varDefs for reason for Dictionary
                    }
                    varDefs[varName].Add(new VarDef()
                    {
                        varSpec = new VarSpec()
                        {
                            name = varName
                        }, initFormula = initFormula, condition = cond, groupNo = g.Key
                    });
                }
            }

            // a variable can only be defined more than once within one function, if it is conditioned:
            // only one definition can exist without a condition (which will be used as the default fallback value if conditions also exist)
            foreach (List <VarDef> condConstDef in varDefs.Values)
            {
                if (condConstDef.Count > 1 && (from c in condConstDef where c.condition == null select c).Count() > 1)
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false,
                        message   = $"{description.Get()}: multiple definitions of {condConstDef[0].varSpec.name} without {DefPar.DefConst.Condition} found"
                    });
                }
            }
        }
        protected override void PrepareNonCommonPar()
        {
            ParCateg parType = GetUniquePar <ParCateg>(DefPar.DefTu.Type);

            if (parType != null)
            {
                tuType = parType.GetCateg();
            }

            if (tuType != DefPar.Value.TUTYPE_IND)
            {
                // get the INCOME used to define the head
                headDefInc = GetUniquePar <ParVarIL>(DefPar.DefTu.HeadDefInc);
                if (headDefInc == null) // if no income defined, use ils_origy as default
                {
                    headDefInc             = new ParVarIL(infoStore);
                    headDefInc.xmlValue    = infoStore.country.sys.headDefInc == string.Empty ? DefVarName.ILSORIGY : infoStore.country.sys.headDefInc;
                    headDefInc.description = description;
                    headDefInc.CheckAndPrepare(this);
                }

                // get all the conditions and set the defaults if they did not exist before
                if (extHeadCond == null)
                {
                    extHeadCond = new ParCond(infoStore)
                    {
                        xmlValue = DefPar.DefTu.DEFAULT_CONDITION.EXTHEAD, description = description
                    };
                    extHeadCond.CheckAndPrepare(this);
                }
            }

            if (partnerCond == null)
            {
                partnerCond = new ParCond(infoStore)
                {
                    xmlValue = DefPar.DefTu.DEFAULT_CONDITION.PARTNER, description = description
                };
                partnerCond.CheckAndPrepare(this);
            }

            if (depChildCond == null)
            {
                depChildCond = new ParCond(infoStore)
                {
                    xmlValue = "{0}", description = description
                };
                depChildCond.CheckAndPrepare(this);
            }

            if (ownChildCond == null)
            {
                ownChildCond = new ParCond(infoStore)
                {
                    xmlValue = DefPar.DefTu.DEFAULT_CONDITION.OWNCHILD, description = description
                };
                ownChildCond.CheckAndPrepare(this);
            }

            if (ownDepChildCond == null)
            {
                ownDepChildCond = new ParCond(infoStore)
                {
                    xmlValue = DefPar.DefTu.DEFAULT_CONDITION.OWNDEPCHILD, description = description
                };
                ownDepChildCond.CheckAndPrepare(this);
            }

            if (looseDepChildCond == null)
            {
                looseDepChildCond = new ParCond(infoStore)
                {
                    xmlValue = DefPar.DefTu.DEFAULT_CONDITION.LOOSEDEPCHILD, description = description
                };
                looseDepChildCond.CheckAndPrepare(this);
            }

            if (depParentCond == null)
            {
                depParentCond = new ParCond(infoStore)
                {
                    xmlValue = DefPar.DefTu.DEFAULT_CONDITION.DEPPARENT, description = description
                };
                depParentCond.CheckAndPrepare(this);
            }

            if (depRelativeCond == null)
            {
                depRelativeCond = new ParCond(infoStore)
                {
                    xmlValue = DefPar.DefTu.DEFAULT_CONDITION.DEPRELATIVE, description = description
                };
                depRelativeCond.CheckAndPrepare(this);
            }

            if (loneParentCond == null)
            {
                loneParentCond = new ParCond(infoStore)
                {
                    xmlValue = DefPar.DefTu.DEFAULT_CONDITION.LONEPARENT, description = description
                };
                loneParentCond.CheckAndPrepare(this);
            }

            // get all the booleans and set the values
            ParBool noChildIfHeadPar = GetUniquePar <ParBool>(DefPar.DefTu.NoChildIfHead);

            noChildIfHead = noChildIfHeadPar != null && noChildIfHeadPar.GetBoolValue();
            ParBool noChildIfPartnerPar = GetUniquePar <ParBool>(DefPar.DefTu.NoChildIfPartner);

            noChildIfPartner = noChildIfPartnerPar != null && noChildIfPartnerPar.GetBoolValue();
            ParBool assignDepChOfDependentsPar = GetUniquePar <ParBool>(DefPar.DefTu.AssignDepChOfDependents);

            assignDepChOfDependents = assignDepChOfDependentsPar != null && assignDepChOfDependentsPar.GetBoolValue();
            ParBool assignPartnerOfDependentsPar = GetUniquePar <ParBool>(DefPar.DefTu.AssignPartnerOfDependents);

            assignPartnerOfDependents = assignPartnerOfDependentsPar != null && assignPartnerOfDependentsPar.GetBoolValue();
            ParBool stopIfNoHeadFoundPar = GetUniquePar <ParBool>(DefPar.DefTu.StopIfNoHeadFound);

            stopIfNoHeadFound = stopIfNoHeadFoundPar != null && stopIfNoHeadFoundPar.GetBoolValue();
            ParCateg multiplePartnersPar = GetUniquePar <ParCateg>(DefPar.DefTu.MultiplePartners);

            multiplePartners = multiplePartnersPar == null ? string.Empty : multiplePartnersPar.GetCateg();
        }
        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();
            }
        }
        protected override void PrepareNonCommonPar()
        {
            ParVar fv = GetUniquePar <ParVar>(DefPar.AddHHMembers.FlagVar);

            if (fv != null)
            {
                flagVar = new VarSpec()
                {
                    name = fv.name
                }
            }
            ;

            foreach (var g in GetParGroups(DefPar.AddHHMembers.GROUP_MAIN))
            {
                var            group = g.Value; int groupNo = g.Key;
                AddInstruction addInstruction = new AddInstruction();

                // first find out whether we are adding children or partners or other persons ...
                ParCateg parWho = GetUniqueGroupPar <ParCateg>(DefPar.AddHHMembers.Add_Who, group); if (parWho == null)
                {
                    continue;                                                                                                    // compulsory parameter, error already issued
                }
                addInstruction.addWho = parWho.GetCateg();

                // ... depending on that, check the add-condition
                ParCond parParent = GetUniqueGroupPar <ParCond>(DefPar.AddHHMembers.ParentCond, group);
                ParCond parPartner = GetUniqueGroupPar <ParCond>(DefPar.AddHHMembers.PartnerCond, group);
                ParCond parOther = GetUniqueGroupPar <ParCond>(DefPar.AddHHMembers.HHCond, group);
                string  missing = string.Empty; string tooMuch = string.Empty;
                switch (addInstruction.addWho)
                {
                case DefPar.Value.ADDHHMEMBERS_CHILD:
                    if (parParent != null)
                    {
                        addInstruction.cond = parParent;
                    }
                    else
                    {
                        missing = DefPar.AddHHMembers.ParentCond;
                    }
                    if (parPartner != null)
                    {
                        tooMuch = DefPar.AddHHMembers.PartnerCond + " ";
                    }
                    if (parOther != null)
                    {
                        tooMuch = DefPar.AddHHMembers.HHCond;
                    }
                    ParBool parIsPP = GetUniqueGroupPar <ParBool>(DefPar.AddHHMembers.IsPartnerParent, group);
                    if (parIsPP != null)
                    {
                        addInstruction.isPartnerParent = parIsPP.GetBoolValue();
                    }
                    break;

                case DefPar.Value.ADDHHMEMBERS_PARTNER:
                    if (parParent != null)
                    {
                        tooMuch = DefPar.AddHHMembers.ParentCond + " ";
                    }
                    if (parPartner != null)
                    {
                        addInstruction.cond = parPartner;
                    }
                    else
                    {
                        missing = DefPar.AddHHMembers.PartnerCond;
                    }
                    if (parOther != null)
                    {
                        tooMuch = DefPar.AddHHMembers.HHCond;
                    }
                    break;

                case DefPar.Value.ADDHHMEMBERS_OTHER:
                    if (parParent != null)
                    {
                        tooMuch = DefPar.AddHHMembers.ParentCond + " ";
                    }
                    if (parPartner != null)
                    {
                        tooMuch = DefPar.AddHHMembers.PartnerCond;
                    }
                    if (parOther != null)
                    {
                        addInstruction.cond = parOther;
                    }
                    else
                    {
                        missing = DefPar.AddHHMembers.HHCond;
                    }
                    break;

                default: continue;     // error is caught by general error handling
                }
                if (missing != string.Empty)
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = false,
                        message   = $"{description.Get()} (group {groupNo}): {DefPar.AddHHMembers.Add_Who}={addInstruction.addWho} requires parameter {missing}"
                    });
                }
                if (tooMuch != string.Empty)
                {
                    infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                    {
                        isWarning = true,
                        message   = $"{description.Get()} (group {groupNo}): {DefPar.AddHHMembers.Add_Who}={addInstruction.addWho} does not require {tooMuch}"
                    });
                }

                // here we are only gathering the variables that are to be set, but do not evaluate whether they exist
                // note: this function does not "register" variables, i.e. if a variable is not used anywhere else, it cannot be set
                foreach (ParFormula parVarDefinition in GetPlaceholderGroupPar <ParFormula>(group))
                {
                    string varName = parVarDefinition.description.GetParName();
                    if (addInstruction.prepVarDefinitions.ContainsKey(varName))
                    {
                        infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                        {
                            isWarning = false,
                            message   = $"{parVarDefinition.description.Get()}: double definition of variable {varName}"
                        });
                        continue;
                    }
                    if (varName == DefVarName.IDHH || varName == DefVarName.IDPERSON || varName == DefVarName.DWT || varName == DefVarName.DCT)
                    {
                        infoStore.communicator.ReportError(new Communicator.ErrorInfo()
                        {
                            isWarning = false,
                            message   = $"{parVarDefinition.description.Get()}: variable {varName} is system-set and cannot be changed"
                        });
                        continue;
                    }
                    addInstruction.prepVarDefinitions.Add(varName, parVarDefinition);
                }
                addInstructions.Add(addInstruction);
            }
        }