diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ca5d6d4d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +/obj/Debug +/bin/Debug +/.vs diff --git a/App.config b/App.config new file mode 100644 index 00000000..8fc05512 --- /dev/null +++ b/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/ChangeCalculatorFactory.cs b/ChangeCalculatorFactory.cs new file mode 100644 index 00000000..236278fb --- /dev/null +++ b/ChangeCalculatorFactory.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SWCashRegister +{ + public static class ChangeCalculatorFactory + { + private static RandomChangeCalculator randomChangeCalculator = new RandomChangeCalculator(); + private static MinimumChangeCalculator minimumChangeCalculator = new MinimumChangeCalculator(); + + public static IChangeCalculator GetChangeCalculator(int changeAmount) + { + if (changeAmount % 3 == 0) + { + return randomChangeCalculator; + } + else + { + return minimumChangeCalculator; + } + } + } +} diff --git a/Currency.cs b/Currency.cs new file mode 100644 index 00000000..9c24297a --- /dev/null +++ b/Currency.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SWCashRegister +{ + public class Currency + { + public static Currency Hundred { get; } = new Currency("hundred", "hundreds", 10000); + public static Currency Fifty { get; } = new Currency("fifty", "fifties", 5000); + public static Currency Twenty { get; } = new Currency("twenty", "twenties", 2000); + public static Currency Ten { get; } = new Currency("ten", "tens", 1000); + public static Currency Five { get; } = new Currency("five", "fives", 500); + public static Currency Dollar { get; } = new Currency("dollar", "dollars", 100); + public static Currency Quarter { get; } = new Currency("quarter", "quarters", 25); + public static Currency Dime { get; } = new Currency("dime", "dimes", 10); + public static Currency Nickel { get; } = new Currency("nickel", "nickels", 5); + public static Currency Penny { get; } = new Currency("penny", "pennies", 1); + + public static IEnumerable List() + { + return new[] { Hundred, Fifty, Twenty, Ten, Five, Dollar, Quarter, Dime, Nickel, Penny }; + } + + private Currency(string name, string plural, int value) + { + Name = name; + Plural = plural; + Value = value; + } + + public string Name { get; private set; } + public string Plural { get; private set; } + public int Value { get; private set; } + } +} diff --git a/Files/input.txt b/Files/input.txt new file mode 100644 index 00000000..4dbe6515 --- /dev/null +++ b/Files/input.txt @@ -0,0 +1,10 @@ +2.12,3.00 +1.97,2.00 +3.33,5.00 +5.00,3.33 +3.33,5.00 +1.97, 2.00 +34, 50 +3.330,5.000 +32.45,42.4 +this,that \ No newline at end of file diff --git a/IChangeCalculator.cs b/IChangeCalculator.cs new file mode 100644 index 00000000..a277f736 --- /dev/null +++ b/IChangeCalculator.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SWCashRegister +{ + public interface IChangeCalculator + { + IList<(Currency, int)> GetChange(int changeAmount); + } +} diff --git a/MinimumChangeCalculator.cs b/MinimumChangeCalculator.cs new file mode 100644 index 00000000..376c3233 --- /dev/null +++ b/MinimumChangeCalculator.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SWCashRegister +{ + public class MinimumChangeCalculator : IChangeCalculator + { + public IList<(Currency, int)> GetChange(int changeAmount) + { + List<(Currency Currency, int Quantity)> change = new List<(Currency Currency, int Quantity)>(); + + foreach (var currency in Currency.List()) + { + if (changeAmount >= currency.Value) + { + int quantity = changeAmount / currency.Value; + change.Add((Currency: currency, Quantity: quantity)); + changeAmount -= currency.Value * quantity; + } + } + + return change; + } + } +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 00000000..f8450884 --- /dev/null +++ b/Program.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text.RegularExpressions; + +namespace SWCashRegister +{ + class Program + { + static void Main(string[] args) + { + string line; + + if (!System.IO.File.Exists("input.txt")) + { + Console.WriteLine("Could not find file input.txt."); + return; + } + + try + { + using (var input = new StreamReader("input.txt")) + using (var output = new StreamWriter("output.txt")) + { + while ((line = input.ReadLine()) != null) + { + if (!ValidateLine(line)) + { + Console.WriteLine($"Found invalid value: {line}"); + continue; + } + + var values = line.Split(','); + int price = (int)(Convert.ToDecimal(values[0]) * 100); + int amountPaid = (int)(Convert.ToDecimal(values[1]) * 100); + + if (amountPaid < price) + { + Console.WriteLine($"Insufficient funds: {line}"); + continue; + } + + int changeAmount = amountPaid - price; + + IChangeCalculator calculator = ChangeCalculatorFactory.GetChangeCalculator(price); + output.WriteLine(PrintChange(calculator.GetChange(changeAmount))); + } + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + Console.WriteLine("Press any key to quit."); + Console.ReadKey(); + } + } + + /// + /// Returns true if the line contains two decimal values, separated by a comma + /// + /// + /// True if line is correctly formatted + public static bool ValidateLine(string line) + { + var regex = new Regex(@"^\d+\.\d{2}\,\d+\.\d{2}$"); + return regex.IsMatch(line) ; + } + + /// + /// Gets a text description of change quantities. + /// + /// A list mapping a currency to a quantity + /// Description of change. + public static string PrintChange(IList<(Currency Currency, int Quantity)> change) + { + List changeText = new List(); + + var sortedChange = change + .GroupBy(x => x.Currency) + .Select(g => new { + Currency = g.Key, + Quantity = g.Sum(x => x.Quantity) + }) + .OrderByDescending(x => x.Currency.Value); + + foreach (var denomination in sortedChange) + { + changeText.Add($"{denomination.Quantity} {(denomination.Quantity == 1 ? denomination.Currency.Name : denomination.Currency.Plural)}"); + } + + return String.Join(",", changeText); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..7043532a --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SWCashRegister")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SWCashRegister")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("83c9c135-36e1-4d63-bb89-04367da7da25")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RandomChangeCalculator.cs b/RandomChangeCalculator.cs new file mode 100644 index 00000000..ac012101 --- /dev/null +++ b/RandomChangeCalculator.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SWCashRegister +{ + public class RandomChangeCalculator : IChangeCalculator + { + public IList<(Currency, int)> GetChange(int changeAmount) + { + List<(Currency Currency, int Quantity)> change = new List<(Currency Currency, int Quantity)>(); + Random random = new Random(); + + while (changeAmount > 0) + { + List availableCurrencies = Currency.List().Where(x => x.Value <= changeAmount).ToList(); + int index = random.Next(0, availableCurrencies.Count()); + Currency currency = availableCurrencies[index]; + change.Add((Currency: currency, Quantity: 1)); + changeAmount -= currency.Value; + } + + return change; + } + } +} diff --git a/SWCashRegister.csproj b/SWCashRegister.csproj new file mode 100644 index 00000000..c11690a7 --- /dev/null +++ b/SWCashRegister.csproj @@ -0,0 +1,62 @@ + + + + + Debug + AnyCPU + {83C9C135-36E1-4D63-BB89-04367DA7DA25} + Exe + SWCashRegister + SWCashRegister + v4.7.1 + 512 + true + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SWCashRegister.sln b/SWCashRegister.sln new file mode 100644 index 00000000..a8d46f3d --- /dev/null +++ b/SWCashRegister.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SWCashRegister", "SWCashRegister.csproj", "{83C9C135-36E1-4D63-BB89-04367DA7DA25}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {83C9C135-36E1-4D63-BB89-04367DA7DA25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83C9C135-36E1-4D63-BB89-04367DA7DA25}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83C9C135-36E1-4D63-BB89-04367DA7DA25}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83C9C135-36E1-4D63-BB89-04367DA7DA25}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {09B5569E-E268-4199-994A-FE759C731E19} + EndGlobalSection +EndGlobal