diff --git a/.gitignore b/.gitignore index 1bc915c..ccae525 100644 --- a/.gitignore +++ b/.gitignore @@ -152,5 +152,8 @@ Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ +# NuGet +.nuget/nuget.exe + # Mac desktop service store files .DS_Store diff --git a/DistObje/App.config b/DistObje/App.config index e2bfdb6..8520422 100644 --- a/DistObje/App.config +++ b/DistObje/App.config @@ -4,6 +4,12 @@
+ + + + + + diff --git a/DistObje/DistObje.csproj b/DistObje/DistObje.csproj index 9b2d353..386b90a 100644 --- a/DistObje/DistObje.csproj +++ b/DistObje/DistObje.csproj @@ -39,6 +39,7 @@ ..\packages\EntityFramework.6.0.2\lib\net45\EntityFramework.SqlServer.dll + @@ -99,6 +100,11 @@ + + + PreserveNewest + + diff --git a/DistObje/Program.cs b/DistObje/Program.cs index 7e15863..52b071f 100644 --- a/DistObje/Program.cs +++ b/DistObje/Program.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.Data.Entity; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml.Linq; namespace DistObje { @@ -16,7 +18,12 @@ public class DbContext : System.Data.Entity.DbContext protected override void OnModelCreating(DbModelBuilder modelBuilder) { - modelBuilder.Entity().Property(x => x.Porcentaje).HasPrecision(18, 4); + byte precision, scale; + if (!byte.TryParse(ConfigurationManager.AppSettings["PorcentajePrecision"], out precision)) + throw new ConfigurationErrorsException("La clave 'PorcentajePrecision' no está definida o es inválida en App.config."); + if (!byte.TryParse(ConfigurationManager.AppSettings["PorcentajeScale"], out scale)) + throw new ConfigurationErrorsException("La clave 'PorcentajeScale' no está definida o es inválida en App.config."); + modelBuilder.Entity().Property(x => x.Porcentaje).HasPrecision(precision, scale); } } @@ -29,47 +36,47 @@ public class ObjDistribuir public Decimal Porcentaje { get; set; } } - private static readonly List _objetivos = new List + private static List LoadObjetivos() + { + string configFile = ConfigurationManager.AppSettings["ObjetivosConfigFile"]; + if (string.IsNullOrEmpty(configFile)) + throw new ConfigurationErrorsException("La clave 'ObjetivosConfigFile' no está definida en App.config."); + if (!System.IO.File.Exists(configFile)) + throw new System.IO.FileNotFoundException("No se encontró el archivo de configuración de objetivos.", configFile); + + var doc = XDocument.Load(configFile); + var result = new List(); + foreach (var e in doc.Root.Elements("Objetivo")) { - new ObjDistribuir() {Nombre = "Persona 0", Monto = 37777}, - - new ObjDistribuir() {Nombre = "Persona 2", Monto = 26671}, - new ObjDistribuir() {Nombre = "Persona 3", Monto = 1776}, - new ObjDistribuir() {Nombre = "Persona 4", Monto = 2750}, - new ObjDistribuir() {Nombre = "Persona 5", Monto = 1913}, - new ObjDistribuir() {Nombre = "Persona 6", Monto = 1700}, - new ObjDistribuir() {Nombre = "Persona 7", Monto = 1100}, - new ObjDistribuir() {Nombre = "Persona 8", Monto = 5754}, - new ObjDistribuir() {Nombre = "Persona 9", Monto = 1502}, - new ObjDistribuir() {Nombre = "Persona 10", Monto = 1287}, - new ObjDistribuir() {Nombre = "Persona 11", Monto = 2131}, - new ObjDistribuir() {Nombre = "Persona 12", Monto = 107}, - new ObjDistribuir() {Nombre = "Persona 13", Monto = 1704}, - new ObjDistribuir() {Nombre = "Persona 14", Monto = 320}, - new ObjDistribuir() {Nombre = "Persona 15", Monto = 562}, - new ObjDistribuir() {Nombre = "Persona 16", Monto = 225}, - new ObjDistribuir() {Nombre = "Persona 17", Monto = 524}, - new ObjDistribuir() {Nombre = "Persona 18", Monto = 567}, - new ObjDistribuir() {Nombre = "Persona 19", Monto = 1053}, - new ObjDistribuir() {Nombre = "Persona 20", Monto = 32}, - new ObjDistribuir() {Nombre = "Persona 21", Monto = 614}, - new ObjDistribuir() {Nombre = "Persona 22", Monto = 1430}, - new ObjDistribuir() {Nombre = "Persona 23", Monto = 563}, - new ObjDistribuir() {Nombre = "Persona 24", Monto = 20484}, - new ObjDistribuir() {Nombre = "Persona 25", Monto = 202}, - new ObjDistribuir() {Nombre = "Persona 26", Monto = 290}, - }; - + var nombreAttr = e.Attribute("Nombre"); + var montoAttr = e.Attribute("Monto"); + if (nombreAttr == null) + throw new ConfigurationErrorsException("Un elemento Objetivo no tiene el atributo 'Nombre'."); + if (montoAttr == null) + throw new ConfigurationErrorsException("Un elemento Objetivo no tiene el atributo 'Monto'."); + result.Add(new ObjDistribuir + { + Nombre = nombreAttr.Value, + Monto = decimal.Parse(montoAttr.Value, CultureInfo.InvariantCulture) + }); + } + return result; + } private static void Main(string[] args) { - var totalElementos = _objetivos.Sum(a => a.Monto); + int roundingDigits; + if (!int.TryParse(ConfigurationManager.AppSettings["RoundingDigits"], out roundingDigits)) + throw new ConfigurationErrorsException("La clave 'RoundingDigits' no está definida o es inválida en App.config."); + var objetivos = LoadObjetivos(); + + var totalElementos = objetivos.Sum(a => a.Monto); - foreach (var item in _objetivos) + foreach (var item in objetivos) { item.Porcentaje = Math.Round( - item.Monto/(totalElementos > 0 ? totalElementos : item.Monto), 4); + item.Monto/(totalElementos > 0 ? totalElementos : item.Monto), roundingDigits); } @@ -78,12 +85,12 @@ private static void Main(string[] args) p.SaveChanges(); if (!p.Objetivos.Any()) { - p.Objetivos.AddRange(_objetivos); + p.Objetivos.AddRange(objetivos); p.SaveChanges(); } Console.WriteLine("From Memory"); Console.WriteLine("Total Elementos {0}", totalElementos); - Console.WriteLine("Total Porcentaje {0}", _objetivos.Sum(a => a.Porcentaje)); + Console.WriteLine("Total Porcentaje {0}", objetivos.Sum(a => a.Porcentaje)); Console.WriteLine("From Db"); Console.WriteLine("Total Elementos {0}", p.Objetivos.Sum(a => a.Monto)); @@ -312,6 +319,237 @@ public override String ToString() + private static decimal RoundToSignificantDigits(decimal m, int digits) + { + // nothing to do if the value is zero + if (m == 0.0M) return m; + var d = Convert.ToDouble(m); + double scale = Math.Pow(10, Math.Floor(Math.Log10(d)) + 1); + return Convert.ToDecimal(scale * Math.Round(d / scale, digits)); + } + + + } +} + + + + /// + /// A class to allow the conversion of doubles to string representations of + /// their exact decimal values. The implementation aims for readability over + /// efficiency. + /// + public class DoubleConverter + { + /// + /// Converts the given double to a string representation of its + /// exact decimal value. + /// + /// The double to convert. + /// A string representation of the double's exact decimal value. + public static string ToExactString(double d) + { + if (double.IsPositiveInfinity(d)) + return "+Infinity"; + if (double.IsNegativeInfinity(d)) + return "-Infinity"; + if (double.IsNaN(d)) + return "NaN"; + + // Translate the double into sign, exponent and mantissa. + long bits = BitConverter.DoubleToInt64Bits(d); + // Note that the shift is sign-extended, hence the test against -1 not 1 + bool negative = (bits < 0); + int exponent = (int)((bits >> 52) & 0x7ffL); + long mantissa = bits & 0xfffffffffffffL; + + // Subnormal numbers; exponent is effectively one higher, + // but there's no extra normalisation bit in the mantissa + if (exponent == 0) + { + exponent++; + } + // Normal numbers; leave exponent as it is but add extra + // bit to the front of the mantissa + else + { + mantissa = mantissa | (1L << 52); + } + + // Bias the exponent. It's actually biased by 1023, but we're + // treating the mantissa as m.0 rather than 0.m, so we need + // to subtract another 52 from it. + exponent -= 1075; + + if (mantissa == 0) + { + return "0"; + } + + /* Normalize */ + while ((mantissa & 1) == 0) + { /* i.e., Mantissa is even */ + mantissa >>= 1; + exponent++; + } + + /// Construct a new decimal expansion with the mantissa + ArbitraryDecimal ad = new ArbitraryDecimal(mantissa); + + // If the exponent is less than 0, we need to repeatedly + // divide by 2 - which is the equivalent of multiplying + // by 5 and dividing by 10. + if (exponent < 0) + { + for (int i = 0; i < -exponent; i++) + ad.MultiplyBy(5); + ad.Shift(-exponent); + } + // Otherwise, we need to repeatedly multiply by 2 + else + { + for (int i = 0; i < exponent; i++) + ad.MultiplyBy(2); + } + + // Finally, return the string with an appropriate sign + if (negative) + return "-" + ad.ToString(); + else + return ad.ToString(); + } + + /// Private class used for manipulating + class ArbitraryDecimal + { + /// Digits in the decimal expansion, one byte per digit + byte[] digits; + /// + /// How many digits are *after* the decimal point + /// + int decimalPoint = 0; + + /// + /// Constructs an arbitrary decimal expansion from the given long. + /// The long must not be negative. + /// + internal ArbitraryDecimal(long x) + { + string tmp = x.ToString(CultureInfo.InvariantCulture); + digits = new byte[tmp.Length]; + for (int i = 0; i < tmp.Length; i++) + digits[i] = (byte)(tmp[i] - '0'); + Normalize(); + } + + /// + /// Multiplies the current expansion by the given amount, which should + /// only be 2 or 5. + /// + internal void MultiplyBy(int amount) + { + byte[] result = new byte[digits.Length + 1]; + for (int i = digits.Length - 1; i >= 0; i--) + { + int resultDigit = digits[i] * amount + result[i + 1]; + result[i] = (byte)(resultDigit / 10); + result[i + 1] = (byte)(resultDigit % 10); + } + if (result[0] != 0) + { + digits = result; + } + else + { + Array.Copy(result, 1, digits, 0, digits.Length); + } + Normalize(); + } + + /// + /// Shifts the decimal point; a negative value makes + /// the decimal expansion bigger (as fewer digits come after the + /// decimal place) and a positive value makes the decimal + /// expansion smaller. + /// + internal void Shift(int amount) + { + decimalPoint += amount; + } + + /// + /// Removes leading/trailing zeroes from the expansion. + /// + internal void Normalize() + { + int first; + for (first = 0; first < digits.Length; first++) + if (digits[first] != 0) + break; + int last; + for (last = digits.Length - 1; last >= 0; last--) + if (digits[last] != 0) + break; + + if (first == 0 && last == digits.Length - 1) + return; + + byte[] tmp = new byte[last - first + 1]; + for (int i = 0; i < tmp.Length; i++) + tmp[i] = digits[i + first]; + + decimalPoint -= digits.Length - (last + 1); + digits = tmp; + } + + /// + /// Converts the value to a proper decimal string representation. + /// + public override String ToString() + { + char[] digitString = new char[digits.Length]; + for (int i = 0; i < digits.Length; i++) + digitString[i] = (char)(digits[i] + '0'); + + // Simplest case - nothing after the decimal point, + // and last real digit is non-zero, eg value=35 + if (decimalPoint == 0) + { + return new string(digitString); + } + + // Fairly simple case - nothing after the decimal + // point, but some 0s to add, eg value=350 + if (decimalPoint < 0) + { + return new string(digitString) + + new string('0', -decimalPoint); + } + + // Nothing before the decimal point, eg 0.035 + if (decimalPoint >= digitString.Length) + { + return "0." + + new string('0', (decimalPoint - digitString.Length)) + + new string(digitString); + } + + // Most complicated case - part of the string comes + // before the decimal point, part comes after it, + // eg 3.5 + return new string(digitString, 0, + digitString.Length - decimalPoint) + + "." + + new string(digitString, + digitString.Length - decimalPoint, + decimalPoint); + } + } + } + + + + private static decimal RoundToSignificantDigits(decimal m, int digits) { // nothing to do if the value is zero diff --git a/DistObje/objetivos.xml b/DistObje/objetivos.xml new file mode 100644 index 0000000..8c2f352 --- /dev/null +++ b/DistObje/objetivos.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +