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

dgeiger's solution

to All Your Base in the Delphi Pascal Track

Published at Sep 04 2020 · 0 comments
Instructions
Test suite
Solution

Convert a number, represented as a sequence of digits in one base, to any other base.

Implement general base conversion. Given a number in base a, represented as a sequence of digits, convert it to base b.

Note

• Try to implement the conversion yourself. Do not use something else to perform the conversion for you.

In positional notation, a number in base b can be understood as a linear combination of powers of b.

The number 42, in base 10, means:

(4 * 10^1) + (2 * 10^0)

The number 101010, in base 2, means:

(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)

The number 1120, in base 3, means:

(1 * 3^3) + (1 * 3^2) + (2 * 3^1) + (0 * 3^0)

I think you got the idea!

Yes. Those three numbers above are exactly the same. Congratulations!

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.

Submitting Incomplete Solutions

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

uAllYourBaseTests.pas

unit uAllYourBaseTests;

interface
uses
DUnitX.TestFramework, System.SysUtils;

const
CanonicalVersion = '2.3.0.1';

type
[TestFixture]
TAllYourBaseTest = class(TObject)
private
procedure CompareArrays(Array1, Array2: TArray<integer>);
public
[Test]
//    [Ignore('Comment the "[Ignore]" statement to run the test')]
procedure single_bit_one_to_decimal;

[Test]
[Ignore]
procedure binary_to_single_decimal;

[Test]
[Ignore]
procedure single_decimal_to_binary;

[Test]
[Ignore]
procedure binary_to_multiple_decimal;

[Test]
[Ignore]
procedure decimal_to_binary;

[Test]
[Ignore]

[Test]
[Ignore]

[Test]
[Ignore]
procedure integer_15_bit;

[Test]
[Ignore]
procedure empty_list;

[Test]
[Ignore]
procedure single_zero;

[Test]
[Ignore]
procedure multiple_zeros;

[Test]
[Ignore]

[Test]
[Ignore]
procedure input_base_is_one;

[Test]
[Ignore]
procedure input_base_is_zero;

[Test]
[Ignore]
procedure input_base_is_negative;

[Test]
[Ignore]
procedure negative_digit;

[Test]
[Ignore]
procedure invalid_positive_digit;

[Test]
[Ignore]
procedure output_base_is_one;

[Test]
[Ignore]
procedure output_base_is_zero;

[Test]
[Ignore]
procedure output_base_is_negative;

[Test]
[Ignore]
procedure both_bases_are_negative;
end;

implementation
uses uAllYourBase;

procedure TAllYourBaseTest.binary_to_multiple_decimal;
begin
CompareArrays([4, 2], TBase.Rebase(2, [1, 0, 1, 0, 1, 0], 10));
end;

procedure TAllYourBaseTest.binary_to_single_decimal;
begin
CompareArrays([5], TBase.Rebase(2, [1, 0, 1], 10));
end;

procedure TAllYourBaseTest.both_bases_are_negative;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(-2, [1], -7);
end, EArgumentOutOfRangeException, 'input base must be >= 2');
end;

procedure TAllYourBaseTest.CompareArrays(Array1, Array2: TArray<integer>);
var
i: integer;
begin
Assert.AreEqual(Length(Array1), Length(Array2), ' - Array lengths must be equal');
for i := Low(Array1) to High(Array1) do
Assert.AreEqual(Array1[i], Array2[i], format('Expecting element %d to = %d, Actual = %d',
[i, Array1[i], Array2[i]]));
end;

procedure TAllYourBaseTest.decimal_to_binary;
begin
CompareArrays([1, 0, 1, 0, 1, 0], TBase.Rebase(10, [4, 2], 2));
end;

procedure TAllYourBaseTest.empty_list;
begin
CompareArrays([0], TBase.Rebase(2, [], 10));
end;

begin
CompareArrays([1, 1, 2, 0], TBase.Rebase(16, [2, 10], 3));
end;

procedure TAllYourBaseTest.input_base_is_negative;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(-2, [1], 10);
end, EArgumentOutOfRangeException, 'input base must be >= 2');
end;

procedure TAllYourBaseTest.input_base_is_one;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(1, [0], 10);
end, EArgumentOutOfRangeException, 'input base must be >= 2');
end;

procedure TAllYourBaseTest.input_base_is_zero;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(0, [0], 10);
end, EArgumentOutOfRangeException, 'input base must be >= 2');
end;

procedure TAllYourBaseTest.integer_15_bit;
begin
CompareArrays([6, 10, 45], TBase.Rebase(97, [3, 46, 60], 73));
end;

procedure TAllYourBaseTest.invalid_positive_digit;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(2, [1, 2, 1, 0, 1, 0], 10);
end, EArgumentOutOfRangeException, 'all digits must satisfy 0 <= d < input base');
end;

begin
CompareArrays([4, 2], TBase.Rebase(7, [0, 6, 0], 10));
end;

procedure TAllYourBaseTest.multiple_zeros;
begin
CompareArrays([0], TBase.Rebase(10, [0, 0, 0], 2));
end;

procedure TAllYourBaseTest.negative_digit;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(2, [1, -1, 1, 0, 1, 0], 10);
end, EArgumentOutOfRangeException, 'all digits must satisfy 0 <= d < input base');
end;

procedure TAllYourBaseTest.output_base_is_negative;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(2, [1], -7);
end, EArgumentOutOfRangeException, 'output base must be >= 2');
end;

procedure TAllYourBaseTest.output_base_is_one;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(2, [1, 0, 1, 0, 1, 0], 1);
end, EArgumentOutOfRangeException, 'output base must be >= 2');
end;

procedure TAllYourBaseTest.output_base_is_zero;
begin
Assert.WillRaise(procedure
begin
TBase.Rebase(10, [7], 0);
end, EArgumentOutOfRangeException, 'output base must be >= 2');
end;

procedure TAllYourBaseTest.single_bit_one_to_decimal;
begin
CompareArrays([1], TBase.Rebase(2, [1], 10));
end;

procedure TAllYourBaseTest.single_decimal_to_binary;
begin
CompareArrays([1, 0, 1], TBase.Rebase(10, [5], 2));
end;

procedure TAllYourBaseTest.single_zero;
begin
CompareArrays([0], TBase.Rebase(10, [0], 2));
end;

begin
CompareArrays([2, 10], TBase.Rebase(3, [1, 1, 2, 0], 16));
end;

initialization
TDUnitX.RegisterTestFixture(TAllYourBaseTest);
end.
unit uAllYourBase;

interface

uses
System.Generics.Collections, System.SysUtils;

type
TBase = class
private
class var FValue: Integer;
class var FInBase: Integer;
class var FDigits: TArray<Integer>;
class var FOutBase: Integer;

class procedure CalculateValue;
class function GetValue: TArray<Integer>;

public
class function Rebase(InBase: Integer; Digits: TArray<Integer>; OutBase: Integer): TArray<Integer>;
end;

implementation

{ TBase }

class procedure TBase.CalculateValue;
var
Index: Integer;
Digit: Integer;
begin
FValue := 0;

// We need to scan the digit array and process each digit
for Index := 0 to Length(FDigits) - 1 do
begin
// Get the current digit
Digit := FDigits[Index];

// Is the digit valid for this base?
if not (Digit in [0 .. FInBase - 1]) then
// No, throw an exception
raise EArgumentOutOfRangeException.Create('all digits must satisfy 0 <= d < input base');

// We need to multiply the value by the base each time we process the
// next digit ...
FValue := FValue * FInBase;

// and then add the digit to the value.
FValue := FValue + Digit;
end;
end;

class function TBase.GetValue: TArray<Integer>;
var
OldValue: Integer;
NewValue: Integer;
Digits: TList<Integer>;
Index: Integer;
begin
// Create a list for the digits in the new base
Digits := TList<Integer>.Create;

// Get the decimal value we calculated earlier
OldValue := FValue;

NewValue := 0;

repeat
// Get the low-order digit in the new base
NewValue := OldValue mod FOutBase;

// Add the digit to the list

// Divide by the new base to get ready for the next digit
OldValue := OldValue div FOutBase;

// We do this until we have calculated all of the digits
until OldValue = 0;

// Since we added the digits starting with the low-order one, we
// need to reverse the list before putting the digits in the Result array
Digits.Reverse;

// Set the size of the Result array
SetLength(Result, Digits.Count);

// For each digit, put it into the proper place in the Result array
for Index := 0 to Digits.Count - 1 do
Result[Index] := Digits.Items[Index];
end;

class function TBase.Rebase(InBase: Integer; Digits: TArray<Integer>;
OutBase: Integer): TArray<Integer>;
begin
// We need to make sure the base of the input digits is valid
if InBase < 2 then
// It isn't so throw an exception
raise EArgumentOutOfRangeException.Create('input base must be >= 2');

// We need to make sure the base of the output digits is valid
if OutBase < 2 then
// It isn't so throw an exception
raise EArgumentOutOfRangeException.Create('output base must be >= 2');

// Save the passed values into our object
FInBase := InBase;
FDigits := Digits;
FOutBase := OutBase;

// Convert the digits to a decimal value
CalculateValue;

// Return the decimal value converted to the new base
Result := GetValue;
end;

end.