Exemple #1
0
        /// <summary>
        /// Process the uploaded file contents and return a list of records with completed calculations
        /// </summary>
        /// <param name="fileContents"></param>
        /// <returns></returns>
        public IEnumerable <CashRegisterRecord> ProcessFile(string fileContents)
        {
            // Parse the file contents to get a list of records
            IEnumerable <CashRegisterRecord> records = ParseFileContents(fileContents);

            // Calculate change and denominations for each record
            foreach (CashRegisterRecord record in records)
            {
                try
                {
                    record.Change = CalculateChange(record.Owed, record.Paid);
                    // Client's "twist"
                    // If amount owed is divisible by 3, use random algorithm to determine bills and coins
                    BillsAndCoinsAlgorithm algorithm = BillsAndCoinsAlgorithm.Greedy;
                    if ((record.Owed * 100) % 3 == 0)
                    {
                        algorithm = BillsAndCoinsAlgorithm.Random;
                    }
                    record.ChangeText = GetBillsAndCoins(record.Change, algorithm);
                }
                catch (Exception ex)
                {
                    // Label this record as having an error
                    // Continue processing the rest of the records
                    record.Error = true;
                }
            }

            return(records);
        }
Exemple #2
0
        /// <summary>
        /// Determine how many of each currency denomination is needed to make the provided amount
        /// </summary>
        /// <param name="amount"></param>
        protected virtual string GetBillsAndCoins(decimal amount, BillsAndCoinsAlgorithm algorithm = BillsAndCoinsAlgorithm.Greedy)
        {
            try
            {
                // Get all denominations (ordered by value descending)
                Denomination[] denominations = GetDenominations();

                // String with bills and change written out
                StringBuilder sb = new StringBuilder();

                if (amount < 0)
                {
                    // Data isn't right...
                    sb.Append("Customer still owes $" + (0 - amount));
                }
                else if (amount == 0)
                {
                    // There is nothing to calculate
                    sb.Append("No change");
                }
                else
                {
                    // Starting amount, when a matching bill or coin is found, their value will be subtracted from this amount
                    decimal amountLeft = amount;

                    switch (algorithm)
                    {
                    case BillsAndCoinsAlgorithm.Greedy:
                        // Use greedy algorithm to determine the minimum amount of bills and change
                        // If I recall correctly, in this case, greedy algorithm will always produce the best result

                        // Start with the highest denomination
                        foreach (Denomination denomination in denominations)
                        {
                            decimal value = denomination.Value;
                            string  name  = denomination.Name;

                            // Calculate how many times this denomination fits in amount left
                            int count = Convert.ToInt32(Math.Truncate(amountLeft / value));
                            if (count > 0)
                            {
                                // Append the amount to the result string
                                sb.Append((sb.Length > 1 ? ", " : "") + count + " " + (count > 1 ? name.ToPlural() : name));

                                // Substract the amount from the running amount left
                                amountLeft -= count * value;
                            }
                        }
                        break;

                    case BillsAndCoinsAlgorithm.Random:
                        // Random algorithm
                        // To make sure that bills and coins add up correctly and implementation is efficient and easy,
                        // follow the same pattern as greedy algorithm, but randomly choose the number of bills / coins at each step.
                        // At the end, get all the available pennies (or lowest denomination)
                        // This will produce a correct result, but will add a degree of randomess

                        Random rng = new Random();

                        // Start with the highest denomination
                        for (int i = 0; i < denominations.Length; i++)
                        {
                            decimal value = denominations[i].Value;
                            string  name  = denominations[i].Name;

                            // Calculate how many times this denomination fits in amount left
                            int count = Convert.ToInt32(Math.Truncate(amountLeft / value));

                            // Randomize if this is not the lowest denomination (penny)
                            if (i < denominations.Length - 1)
                            {
                                count = rng.Next(count);
                            }

                            if (count > 0)
                            {
                                // Append the amount to the result string
                                sb.Append((sb.Length > 1 ? ", " : "") + count + " " + (count > 1 ? name.ToPlural() : name));

                                // Substract the amount from the running amount left
                                amountLeft -= count * value;
                            }
                        }
                        break;

                    default:
                        throw new Exception("Unknown algorithm to determine bills and coins.");
                    }

                    // Note any amount that we were unable to process
                    // This could only happen if user entered values under 1 cent (or similar situation)
                    if (amountLeft > 0)
                    {
                        sb.Append((sb.Length > 1 ? " " : "") + "($" + amountLeft + " that cannot be given in change)");
                    }
                }

                return(sb.ToString());
            }
            catch (Exception ex)
            {
                throw new Exception("Unable to determine bills and change for " + amount + ".", ex);
            }
        }