diff --git a/.gitignore b/.gitignore index fb1a0d8..fe51822 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ # User files *.user +.idea \ No newline at end of file diff --git a/Izzy.Web.Tests/Izzy.Web.Tests.csproj b/Izzy.Web.Tests/Izzy.Web.Tests.csproj index 48b5d85..78dc050 100644 --- a/Izzy.Web.Tests/Izzy.Web.Tests.csproj +++ b/Izzy.Web.Tests/Izzy.Web.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.2 @@ -7,13 +7,16 @@ - - - + + + + + + - - + + diff --git a/Izzy.Web.Tests/Matchers/HasTransfer.cs b/Izzy.Web.Tests/Matchers/HasTransfer.cs new file mode 100644 index 0000000..3470474 --- /dev/null +++ b/Izzy.Web.Tests/Matchers/HasTransfer.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Linq; +using Izzy.Web.Model; +using Newtonsoft.Json; +using NHamcrest; +using Xunit; + +namespace Izzy.Web.Tests.Matchers +{ + public class HasTransfer : IMatcher> + { + private Transfer _expected; + + public HasTransfer(Transfer expected) + { + _expected = expected; + } + + public void DescribeTo(IDescription description) + { + description.AppendText("From DescribeTo"); + } + + public bool Matches(List actual) + { + return actual + .Any( + t => + t.From == _expected.From + && t.To == _expected.To + && t.Roubles == _expected.Roubles + ); + } + + public void DescribeMismatch(List item, IDescription description) + { + description.AppendText("From DescribeMismatch").AppendText(JsonConvert.SerializeObject(item)); + } + } +} \ No newline at end of file diff --git a/Izzy.Web.Tests/ReceiptTests.cs b/Izzy.Web.Tests/ReceiptTests.cs deleted file mode 100644 index bc97871..0000000 --- a/Izzy.Web.Tests/ReceiptTests.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using Xunit; -using Izzy.Web.Model; - -namespace Izzy.Web.Tests -{ - public class ReceiptTests - { - [Fact] - public void Test1() - { - // Arrange - var receipt = new Receipt( - new List{ - new Person("Alice", 600), - new Person("Bob", 1100), - new Person("Carol", 0) - } - ); - - // Act - var transfers = receipt.Transfers(); - - // Assert - var first = transfers[0]; - Assert.Equal("Bob", first.From); - Assert.Equal("Alice", first.To); - Assert.Equal(33.33m, first.Roubles); - var second = transfers[1]; - Assert.Equal("Carol", second.From); - Assert.Equal("Bob", second.To); - Assert.Equal(566.67m, second.Roubles); - } - } -} diff --git a/Izzy.Web.Tests/Receipts/OneMoreThanMiddle/ThreePersons.cs b/Izzy.Web.Tests/Receipts/OneMoreThanMiddle/ThreePersons.cs new file mode 100644 index 0000000..3804b00 --- /dev/null +++ b/Izzy.Web.Tests/Receipts/OneMoreThanMiddle/ThreePersons.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Izzy.Web.Model; +using Izzy.Web.Tests.Matchers; +using NHamcrest; +using Xunit; + +namespace Izzy.Web.Tests.Receipts.OneMoreThanMiddle +{ + public class ThreePersons + { + [Fact] + public void Test() + { + // Act + var receipt = new Receipt( + new List{ + new Person("Alice", 100), + new Person("Bob", 30), + new Person("Carol", 20) + } + ); + + var transfers = receipt.Transfers(); + + // Assert + NHamcrest.XUnit.Assert.That(transfers, Is.OfLength(2)); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Bob", "Alice", 20m))); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Carol", "Alice", 30m))); + } + } +} \ No newline at end of file diff --git a/Izzy.Web.Tests/Receipts/OneMoreThanMiddle/TwoPersons.cs b/Izzy.Web.Tests/Receipts/OneMoreThanMiddle/TwoPersons.cs new file mode 100644 index 0000000..3563770 --- /dev/null +++ b/Izzy.Web.Tests/Receipts/OneMoreThanMiddle/TwoPersons.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Izzy.Web.Model; +using Izzy.Web.Tests.Matchers; +using NHamcrest; +using Xunit; + +namespace Izzy.Web.Tests.Receipts.OneMoreThanMiddle +{ + public class TwoPersons + { + [Fact] + public void Test1() + { + // Act + var receipt = new Receipt( + new List{ + new Person("Alice", 100), + new Person("Bob", 0) + } + ); + + var transfers = receipt.Transfers(); + + // Assert + NHamcrest.XUnit.Assert.That(transfers, Is.OfLength(1)); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Bob", "Alice", 50m))); + } + } +} \ No newline at end of file diff --git a/Izzy.Web.Tests/Receipts/TwoMoreThanMiddle/FivePersons.cs b/Izzy.Web.Tests/Receipts/TwoMoreThanMiddle/FivePersons.cs new file mode 100644 index 0000000..2dcec34 --- /dev/null +++ b/Izzy.Web.Tests/Receipts/TwoMoreThanMiddle/FivePersons.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Izzy.Web.Model; +using Izzy.Web.Tests.Matchers; +using NHamcrest; +using Xunit; + +namespace Izzy.Web.Tests.Receipts.TwoMoreThanMiddle +{ + public class FivePerson + { + [Fact] + public void TwoMoreThanMiddle_Calculate_ValidTransfers() + { + // Act + var receipt = new Receipt( + new List{ + new Person("Alice", 6800), + new Person("Bob", 2900), + new Person("Carol", 1500), + new Person("Dave", 6400), + new Person("Eve", 100) + } + ); + + var transfers = receipt.Transfers(); + + // Assert + NHamcrest.XUnit.Assert.That(transfers, Is.OfLength(4)); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Bob", "Dave", 640m))); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Eve", "Dave", 180m))); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Eve", "Alice", 3260m))); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Carol", "Dave", 2040m))); + } + } +} \ No newline at end of file diff --git a/Izzy.Web.Tests/Receipts/TwoMoreThanMiddle/ThreePersons.cs b/Izzy.Web.Tests/Receipts/TwoMoreThanMiddle/ThreePersons.cs new file mode 100644 index 0000000..b929eda --- /dev/null +++ b/Izzy.Web.Tests/Receipts/TwoMoreThanMiddle/ThreePersons.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using Izzy.Web.Model; +using Izzy.Web.Tests.Matchers; +using NHamcrest; +using Xunit; + + +namespace Izzy.Web.Tests.Receipts.TwoMoreThanMiddle +{ + public class TwoMoreThanMiddleTests + { + [Fact] + public void TwoMoreThanMiddle_Calculate_ValidTransfers() + { + // Act + var receipt = new Receipt( + new List{ + new Person("Alice", 600), + new Person("Bob", 1100), + new Person("Carol", 0) + } + ); + + var transfers = receipt.Transfers(); + + // Assert + NHamcrest.XUnit.Assert.That(transfers, Is.OfLength(2)); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Carol", "Alice", 33.33m))); + NHamcrest.XUnit.Assert.That(transfers, new HasTransfer(new Transfer("Carol", "Bob", 533.33m))); + } + } +} diff --git a/Izzy.Web/Model/Receipt.cs b/Izzy.Web/Model/Receipt.cs index e8c793d..ce149af 100644 --- a/Izzy.Web/Model/Receipt.cs +++ b/Izzy.Web/Model/Receipt.cs @@ -17,22 +17,49 @@ public List Transfers() { var transfers = new List(); var total = this._persons.Sum(p => p.Roubles); - var roublesPerOne = Decimal.Round(total / this._persons.Count(), 2); - var personsTop = this._persons - .OrderByDescending(p => p.Roubles); - var spender = personsTop.First(); - var personsExceptSpender = personsTop.Skip(1); - foreach (var person in personsExceptSpender) + var middle = Decimal.Round(total / this._persons.Count(), 2); + var personsTop = this._persons.OrderByDescending(p => p.Roubles).ToList(); + var moreThanMiddle = personsTop.Where(p => p.Roubles >= middle).ToList(); + var lessThanMiddle = personsTop.Except(moreThanMiddle).ToList(); + for (int i = 0; i < moreThanMiddle.Count; i++) { - transfers.Add( - new DifferenceTransfer( - person, - spender, - roublesPerOne - person.Roubles - ) - ); + var spender = moreThanMiddle[i]; + for (int j = lessThanMiddle.Count - 1; j >= 0; j--) + { + if (spender.Roubles - middle != 0) + { + var debtor = lessThanMiddle[j]; + var debtorDiff = middle - debtor.Roubles; + var spenderDiff = spender.Roubles - middle; + if (debtorDiff > spenderDiff) + { + transfers.Add(new Transfer(debtor, spender, spenderDiff)); + debtor.Roubles += spenderDiff; + spender.Roubles -= spenderDiff; + } + else + { + transfers.Add(new Transfer(debtor, spender, debtorDiff)); + debtor.Roubles += debtorDiff; + spender.Roubles -= debtorDiff; + } + } + } } + return transfers; } + + private Decimal diff(Person debtor, Person spender, Decimal middle) + { + if (debtor.Roubles > spender.Roubles - middle) + { + return spender.Roubles - middle; + } + else + { + return debtor.Roubles; + } + } } } \ No newline at end of file