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

Greycoat21's solution

to All Your Base in the Delphi Pascal Track

Published at Oct 23 2019 · 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
  TDigit  = Integer;
  TNumber = TArray<Integer>;

  TBase = class
  private
    class procedure ValidateArguments(
      SrcBase: Integer;
      Number:  TNumber;
      DstBase: Integer);
  public
    class function Rebase(
      SrcBase: Integer;
      Number:  TNumber;
      DstBase: Integer): TNumber;
  end;

implementation

uses
  SysUtils, Math;

{ TBase }

//------------------------------------------------------------------------------
class function TBase.Rebase(
  SrcBase: Integer;
  Number:  TNumber;
  DstBase: Integer): TNumber;
var
  Digit: TDigit;
  Value, Multiplicand, i: Integer;
begin
  ValidateArguments(SrcBase, Number, DstBase);

  // Convert from SrcBase to base 10
  Value := 0;
  for i := Low(Number) to High(Number) do begin
    Multiplicand := Trunc(Power(SrcBase, i));
    Digit := Number[High(Number) - i];
    Value := Value + (Digit * Multiplicand);
  end;

  // Convert from base 10 to DstBase
  Result := [];
  repeat
    Digit  := Value mod DstBase;
    Result := [Digit] + Result;
    Value  := Value div DstBase;
  until Value = 0;
end;
//------------------------------------------------------------------------------
class procedure TBase.ValidateArguments(

  SrcBase: Integer;

  Number: TNumber;

  DstBase: Integer);

  procedure ValidateBase(Base: Integer; BaseCode: string);
  begin
    if Base < 2 then
      raise EArgumentOutOfRangeException.Create(
        BaseCode + ' base must be >= 2');
  end;

  procedure ValidateNumber(SrcBase: Integer; Number:  TNumber);

    procedure ValidateDigit(SrcBase: Integer; Digit: TDigit);
    begin
      if (0 > Digit) or (Digit >= SrcBase) then
        raise EArgumentOutOfRangeException.Create(
          'all digits must satisfy 0 <= d < input base');
    end;

  var
    Digit: TDigit;
  begin
    for Digit in Number do
      ValidateDigit(SrcBase, Digit);
  end;

const
  sSrc = 'input';
  sDst = 'output';
begin
  ValidateBase(SrcBase, sSrc);
  ValidateBase(DstBase, sDst);
  ValidateNumber(SrcBase, Number);
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?