/// <summary>
 /// Gets the base dollar value of the given factor
 /// </summary>
 /// <param name="tbl"></param>
 /// <returns></returns>
 internal static double GetFactorBaseValue(FactorTables tbl)
 {
     var xmlDoc = tbl == FactorTables.CreditCardDebt || tbl == FactorTables.HomeDebt ||
                 tbl == FactorTables.VehicleDebt
        ? TreeData.UsPersonalDebt
        : TreeData.UsPersonalWealth;
     var tblName = Enum.GetName(typeof(FactorTables), tbl);
     var tblXPath = $"//table[@name='{tblName}']";
     var tblNode = xmlDoc.SelectSingleNode(tblXPath) as XmlElement;
     if (string.IsNullOrWhiteSpace(tblNode?.Attributes?["value"]?.Value))
         return 0.0D;
     double dblOut;
     if (!double.TryParse(tblNode.Attributes["value"].Value, out dblOut))
         return 0.0D;
     return dblOut;
 }
 /// <summary>
 /// Get a random value for the given <see cref="factor"/> table.
 /// </summary>
 /// <param name="factor"></param>
 /// <param name="factorMultiplier"></param>
 /// <param name="stdDevAsPercent"></param>
 /// <param name="assignedBase">
 /// Optional value to directly assign a factor table's base value, defaults to 
 /// the value from <see cref="GetFactorBaseValue"/>
 /// </param>
 /// <returns></returns>
 protected internal double GetRandomFactorValue(FactorTables factor, double factorMultiplier,
     double stdDevAsPercent, double? assignedBase = null)
 {
     var baseValue = assignedBase.GetValueOrDefault(GetFactorBaseValue(factor));
     baseValue = Math.Round(baseValue + baseValue*factorMultiplier, 2);
     var randValue = Math.Round(
         Etx.RandomValueInNormalDist(Math.Round(baseValue, 0), Math.Round(baseValue*stdDevAsPercent, 0)), 2);
     return randValue;
 }
        /// <summary>
        /// Gets the sum of the factors based on the criteria.
        /// </summary>
        /// <param name="tbl"></param>
        /// <param name="edu"></param>
        /// <param name="race"></param>
        /// <param name="region"></param>
        /// <param name="age"></param>
        /// <param name="gender"></param>
        /// <param name="maritialStatus"></param>
        /// <returns></returns>
        internal static double GetFactor(FactorTables tbl, OccidentalEdu edu, NorthAmericanRace race,
            AmericanRegion region, int age, Gender gender, MaritialStatus maritialStatus)
        {
            var xmlDoc = tbl == FactorTables.CreditCardDebt || tbl == FactorTables.HomeDebt ||
                         tbl == FactorTables.VehicleDebt
                ? TreeData.UsPersonalDebt
                : TreeData.UsPersonalWealth;
            var tblName = Enum.GetName(typeof(FactorTables), tbl);
            var eduName = GetXmlEduName(edu);
            var raceName = Enum.GetName(typeof(NorthAmericanRace), race);
            var regionName = Enum.GetName(typeof(AmericanRegion), region);
            var genderName = Enum.GetName(typeof(Gender), gender);

            var tblXPath = $"//table[@name='{tblName}']";

            var sum = 0.0D;
            var hash = new Dictionary<string, string>
            {
                {"Edu", eduName},
                {"Race", raceName},
                {"Region", regionName},
            };

            foreach (var factor in hash.Keys)
            {
                var xmlElem = xmlDoc.SelectSingleNode($"{tblXPath}/factor[@name='{factor}']/add[@name='{hash[factor]}']") as XmlElement;
                var factorVal = xmlElem?.Attributes["value"]?.Value;
                if (string.IsNullOrWhiteSpace(factorVal))
                    continue;
                double dblOut;
                if (double.TryParse(factorVal, out dblOut))
                    sum += dblOut;
            }

            var ageNode = maritialStatus == MaritialStatus.Remarried || maritialStatus == MaritialStatus.Remarried
                ? xmlDoc.SelectSingleNode($"{tblXPath}/factor[@name='Age']/factor[@name='Married']")
                : xmlDoc.SelectSingleNode($"{tblXPath}/factor[@name='Age']/factor[@name='{genderName}']");
            if (ageNode == null)
                return sum;
            foreach (var anode in ageNode.ChildNodes)
            {
                var ageElem = anode as XmlElement;
                if (ageElem == null)
                    continue;
                var minAge = ageElem.Attributes["min"]?.Value;
                var maxAge = ageElem.Attributes["max"]?.Value;
                if (string.IsNullOrWhiteSpace(minAge) || string.IsNullOrWhiteSpace(maxAge))
                    continue;
                int min, max;
                if (!int.TryParse(minAge, out min) || !int.TryParse(maxAge, out max))
                    continue;
                var isInRange = age >= min && age <= max;
                if (!isInRange || string.IsNullOrWhiteSpace(ageElem.Attributes["value"]?.Value))
                    continue;
                var factorVal = ageElem.Attributes["value"].Value;
                double dblOut;
                if (double.TryParse(factorVal, out dblOut))
                    sum += dblOut;
            }
            return sum;
        }