ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# eferim's solution

## to Book Store in the Delphi Pascal Track

Published at Sep 12 2018 · 0 comments
Instructions
Test suite
Solution

#### Note:

This exercise has changed since this solution was written.

To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts on multiple book purchases.

One copy of any of the five books costs \$8.

If, however, you buy two different books, you get a 5% discount on those two books.

If you buy 3 different books, you get a 10% discount.

If you buy 4 different books, you get a 20% discount.

If you buy all 5, you get a 25% discount.

Note: that if you buy four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs \$8.

Your mission is to write a piece of code to calculate the price of any conceivable shopping basket (containing only books of the same series), giving as big a discount as possible.

For example, how much does this basket of books cost?

• 2 copies of the first book
• 2 copies of the second book
• 2 copies of the third book
• 1 copy of the fourth book
• 1 copy of the fifth book

One way of grouping these 8 books is:

• 1 group of 5 --> 25% discount (1st,2nd,3rd,4th,5th)
• +1 group of 3 --> 10% discount (1st,2nd,3rd)

This would give a total of:

• 5 books at a 25% discount
• +3 books at a 10% discount

Resulting in:

• 5 x (8 - 2.00) == 5 x 6.00 == \$30.00
• +3 x (8 - 0.80) == 3 x 7.20 == \$21.60

For a total of \$51.60

However, a different way to group these 8 books is:

• 1 group of 4 books --> 20% discount (1st,2nd,3rd,4th)
• +1 group of 4 books --> 20% discount (1st,2nd,3rd,5th)

This would give a total of:

• 4 books at a 20% discount
• +4 books at a 20% discount

Resulting in:

• 4 x (8 - 1.60) == 4 x 6.40 == \$25.60
• +4 x (8 - 1.60) == 4 x 6.40 == \$25.60

For a total of \$51.20

And \$51.20 is the price with the biggest discount.

## Testing

In order to run the tests for this track, you will need to install DUnitX. Please see the installation instructions for more information.

If Delphi is properly installed, and `*.dpr` file types have been associated with Delphi, then double clicking the supplied `*.dpr` file will start Delphi and load the exercise/project. `control + F9` is the keyboard shortcut to compile the project or pressing `F9` will compile and run the project.

Alternatively you may opt to start Delphi and load your project via. the `File` drop down menu.

### When Questions Come Up

We monitor the Pascal-Delphi support room on gitter.im to help you with any questions that might arise.

### Submitting Exercises

Note that, when trying to submit an exercise, make sure the exercise file you're submitting is in the `exercism/delphi/<exerciseName>` directory.

For example, if you're submitting `ubob.pas` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/delphi/bob/ubob.pas`.

## Source

Inspired by the harry potter kata from Cyber-Dojo. http://cyber-dojo.org

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you may receive assistance from a mentor.

### uBookStoreTests.pas

``````unit uBookStoreTests;

interface
uses
DUnitX.TestFramework;

const
CanonicalVersion = '1.3.0';

type
[TestFixture]
hpTests = class(TObject)
public
[Test]
//    [Ignore('Comment the "[Ignore]" statement to run the test')]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]
procedure Four_groups_of_four_are_cheaper_than_two_groups_each_of_five_and_three;
end;

implementation
uses System.SysUtils, uBookStore;

Expected: integer;
begin
Expected := 800;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 1600;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 0;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 1520;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 2160;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

procedure hpTests.Four_groups_of_four_are_cheaper_than_two_groups_each_of_five_and_three;
Expected: integer;
begin
Expected := 10240;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 2560;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 3000;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 5120;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 5560;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 6000;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 6800;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

Expected: integer;
begin
Expected := 7520;

assert.AreEqual(Expected, fBasket.Total, format('Total should be %d cents',[Expected]));
end;

initialization
TDUnitX.RegisterTestFixture(hpTests);
end.``````
``````unit uBookStore;

interface

type
function Total : integer;
end;

implementation

uses System.Generics.Collections;

type
private
FColRangs : TList<integer>;
FDiscounts : TDictionary<integer, single>;
function CombValue(ACom : TList<integer>) : integer;
public
destructor Destroy; override;
function Total : integer;
end;

begin
end;

var
FQtys : TDictionary<integer, integer>;
BSets : TList<integer>;

function FindQty(ABasket: TArray<integer>) : TDictionary<integer, integer>;
var B : integer;
begin
Result :=  TDictionary<integer, integer>.Create;
if not Result.ContainsKey(B) then
else
Result.Items[B] := Result.Items[B] + 1;
end;

function QtyToSets(AQtys : TDictionary<integer, integer>) : TList<integer>;
var BSet: TPair<integer, integer>;
begin
Result := TList<integer>.Create;
for BSet in AQtys do
Result.Sort;
end;

function RangOfSets(ASets : TList<integer>) : TList<integer>;
var i, R, Tmp: Integer;
begin
Result := TList<integer>.Create;
if ASets.Count > 0 then
for i := 0 to ASets.Last - 1 do
begin
Tmp := 0;
for R in ASets do
if R - i > 0 then
inc(Tmp);
end;
end;

begin
// Discounts
FDiscounts := TDictionary<integer, single>.Create;

// Find qty of each book

// Group to sets
BSets := QtyToSets(FQtys);
FQtys.DisposeOf;

// Make rang of sets
FColRangs := RangOfSets(BSets);
BSets.DisposeOf;
end;

begin
FDiscounts.DisposeOf;
FColRangs.DisposeOf;
inherited;
end;

var LVal : integer;

procedure Recombine;
begin
if FColRangs.First - FColRangs.Last > 1 then
begin
FColRangs[0] := FColRangs.First - 1;
FColRangs[FColRangs.Count - 1] := FColRangs.Last + 1;
FColRangs.Sort;
FColRangs.Reverse;
end;
end;

begin
if FColRangs.Count = 0 then
exit(0);
Result := CombValue(FColRangs);

while FColRangs.First - FColRangs.Last > 1 do
begin
Recombine;
LVal := CombValue(FColRangs);
if LVal < Result then
Result := LVal;
end;

end;

function TBasket.CombValue(ACom : TList<integer>) : integer;
var
S: integer;
begin
Result := 0;
for S in Acom do
Result := Result + round(S * 800 * FDiscounts[S]);
end;

end.``````