🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of Selticq

Selticq's solution

to All Your Base in the Delphi Pascal Track

Published at Feb 07 2021 · 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.

About Positional Notation

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.

Loading Exercises into Delphi

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]
    procedure trinary_to_hexadecimal;

    [Test]
    [Ignore]
    procedure hexadecimal_to_trinary;

    [Test]
    [Ignore]
    procedure integer_15_bit;

    [Test]
    [Ignore]
    procedure empty_list;

    [Test]
    [Ignore]
    procedure single_zero;

    [Test]
    [Ignore]
    procedure multiple_zeros;

    [Test]
    [Ignore]
    procedure leading_zeros;

    [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;

procedure TAllYourBaseTest.hexadecimal_to_trinary;
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;

procedure TAllYourBaseTest.leading_zeros;
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;

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

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

interface

type TBase = class
  public
    class function Rebase(BaseIn: Integer; Values: TArray<Integer>; BaseOut: Integer): TArray<Integer>;
  private
    class function GetHighestPower(Base, Number: Integer): Integer;
    class function GetNumber(Base: Integer; Values: TArray<Integer>): Integer;
    class procedure TestArguments(BaseIn, BaseOut: Integer; Values: TArray<Integer>);
end;

implementation

uses System.Math, System.SysUtils;

class function TBase.GetHighestPower(Base, Number: Integer): Integer;
begin
  Result := 0;
  while Power(Base, Result) < Number do
    Inc(Result);
  if (Power(Base, Result) > Number) then
    Dec(Result);
end;

class function TBase.GetNumber(Base: Integer; Values: TArray<Integer>): Integer;
begin
  Result := 0;
  for var I := Low(Values) to High(Values) do
    Inc(Result, Values[I] * Round(Power(Base, High(Values) - I)));
end;

class function TBase.Rebase(BaseIn: Integer; Values: TArray<Integer>; BaseOut: Integer): TArray<Integer>;
var
  Number, HighestPower: Integer;
begin
  TestArguments(BaseIn, BaseOut, Values);
  Number := GetNumber(BaseIn, Values);
  if Number = 0 then
    begin
        Result := [0];
        Exit;
    end;
  HighestPower := GetHighestPower(BaseOut, Number);
  for var I := HighestPower downto 0 do
    begin
      SetLength(Result, Length(Result) + 1);
      Result[High(Result)] := 0;
      while Number >= Power(BaseOut, I) do
        begin
          Inc(Result[High(Result)]);
          Dec(Number, Round(Power(BaseOut, I)));
        end;
    end;
end;

class procedure TBase.TestArguments(BaseIn, BaseOut: Integer; Values: TArray<Integer>);
begin
  if (BaseIn < 2) or (BaseOut < 2) then
    raise EArgumentOutOfRangeException.Create('input base must be >= 2');
  for var I := Low(Values) to High(Values) do
    if (Values[I] < 0) or (Values[I] >= BaseIn) then
      raise EArgumentOutOfRangeException.Create('all digits must satisfy 0 <= d < input base');
end;

end.

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?