Skip to content

Commit

Permalink
Sync All your base (#694)
Browse files Browse the repository at this point in the history
  • Loading branch information
mk-mxp authored Apr 28, 2024
1 parent 9e6a0b1 commit d2a5961
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 89 deletions.
34 changes: 15 additions & 19 deletions exercises/practice/all-your-base/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
# Instructions

Convert a number, represented as a sequence of digits in one base, to any other base.
Convert a sequence of digits in one base, representing a number, into a sequence of digits in another base, representing the same number.

Implement general base conversion. Given a number in base **a**,
represented as a sequence of digits, convert it to base **b**.
~~~~exercism/note
Try to implement the conversion yourself.
Do not use something else to perform the conversion for you.
~~~~

## Note
## About [Positional Notation][positional-notation]

- 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**.

## About [Positional Notation](https://en.wikipedia.org/wiki/Positional_notation)
The number 42, _in base 10_, means:

In positional notation, a number in base **b** can be understood as a linear
combination of powers of **b**.
`(4 × 10¹) + (2 × 10⁰)`

The number 42, *in base 10*, means:
The number 101010, _in base 2_, means:

(4 * 10^1) + (2 * 10^0)
`(1 × 2⁵) + (0 × 2⁴) + (1 × 2³) + (0 × 2²) + (1 × 2¹) + (0 × 2⁰)`

The number 101010, *in base 2*, means:
The number 1120, _in base 3_, means:

(1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (0 * 2^2) + (1 * 2^1) + (0 * 2^0)
`(1 × 3³) + (1 × 3²) + (2 × 3¹) + (0 × 3⁰)`

The number 1120, *in base 3*, means:
_Yes. Those three numbers above are exactly the same. Congratulations!_

(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!*
[positional-notation]: https://en.wikipedia.org/wiki/Positional_notation
8 changes: 8 additions & 0 deletions exercises/practice/all-your-base/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Introduction

You've just been hired as professor of mathematics.
Your first week went well, but something is off in your second week.
The problem is that every answer given by your students is wrong!
Luckily, your math skills have allowed you to identify the problem: the student answers _are_ correct, but they're all in base 2 (binary)!
Amazingly, it turns out that each week, the students use a different base.
To help you quickly verify the student answers, you'll be building a tool to translate between bases.
34 changes: 8 additions & 26 deletions exercises/practice/all-your-base/.meta/example.php
Original file line number Diff line number Diff line change
@@ -1,41 +1,23 @@
<?php

/*
* By adding type hints and enabling strict type checking, code can become
* easier to read, self-documenting and reduce the number of potential bugs.
* By default, type declarations are non-strict, which means they will attempt
* to change the original type to match the type specified by the
* type-declaration.
*
* In other words, if you pass a string to a function requiring a float,
* it will attempt to convert the string value to a float.
*
* To enable strict mode, a single declare directive must be placed at the top
* of the file.
* This means that the strictness of typing is configured on a per-file basis.
* This directive not only affects the type declarations of parameters, but also
* a function's return type.
*
* For more info review the Concept on strict type checking in the PHP track
* <link>.
*
* To disable strict typing, comment out the directive below.
*/

declare(strict_types=1);

function rebase(int $fromBase, array $digits, int $toBase)
function rebase(int $fromBase, array $digits, int $toBase): array
{
if (empty($digits) || $digits[0] == 0 || $fromBase <= 1 || $toBase <= 1) {
return null;
if ($fromBase <= 1) {
throw new InvalidArgumentException('input base must be >= 2');
}

if ($toBase <= 1) {
throw new InvalidArgumentException('output base must be >= 2');
}

$decTotal = 0;
$ordered = array_reverse($digits);
for ($i = 0; $i < count($digits); $i++) {
$decTotal += $ordered[$i] * pow($fromBase, $i);
if ($ordered[$i] >= $fromBase || $ordered[$i] < 0) {
return null;
throw new InvalidArgumentException('all digits must satisfy 0 <= d < input base');
}
}

Expand Down
1 change: 1 addition & 0 deletions exercises/practice/all-your-base/.meta/tests.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,4 @@ description = "output base is negative"

[0e6c895d-8a5d-4868-a345-309d094cfe8d]
description = "both bases are negative"
comment = "We cannot know which condition comes first. So no message is enforced."
2 changes: 1 addition & 1 deletion exercises/practice/all-your-base/AllYourBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

declare(strict_types=1);

function rebase(int $number, array $sequence, int $base)
function rebase(int $fromBase, array $digits, int $toBase): array
{
throw new \BadFunctionCallException("Implement the rebase function");
}
170 changes: 127 additions & 43 deletions exercises/practice/all-your-base/AllYourBaseTest.php
Original file line number Diff line number Diff line change
@@ -1,142 +1,226 @@
<?php

/*
* By adding type hints and enabling strict type checking, code can become
* easier to read, self-documenting and reduce the number of potential bugs.
* By default, type declarations are non-strict, which means they will attempt
* to change the original type to match the type specified by the
* type-declaration.
*
* In other words, if you pass a string to a function requiring a float,
* it will attempt to convert the string value to a float.
*
* To enable strict mode, a single declare directive must be placed at the top
* of the file.
* This means that the strictness of typing is configured on a per-file basis.
* This directive not only affects the type declarations of parameters, but also
* a function's return type.
*
* For more info review the Concept on strict type checking in the PHP track
* <link>.
*
* To disable strict typing, comment out the directive below.
*/

declare(strict_types=1);

/**
* These tests are adapted from the canonical data in the
* `problem-specifications` repository.
*/
class AllYourBaseTest extends PHPUnit\Framework\TestCase
{
public static function setUpBeforeClass(): void
{
require_once 'AllYourBase.php';
}

/**
* uuid 5ce422f9-7a4b-4f44-ad29-49c67cb32d2c
* @testdox Single bit one to decimal
*/
public function testSingleBitOneToDecimal(): void
{
$this->assertEquals([1], rebase(2, [1], 10));
}

/**
* uuid 0cc3fea8-bb79-46ac-a2ab-5a2c93051033
* @testdox Binary to single decimal
*/
public function testBinaryToSingleDecimal(): void
{
$this->assertEquals([5], rebase(2, [1, 0, 1], 10));
}

/**
* uuid f12db0f9-0d3d-42c2-b3ba-e38cb375a2b8
* @testdox Single decimal to binary
*/
public function testSingleDecimalToBinary(): void
{
$this->assertEquals([1, 0, 1], rebase(10, [5], 2));
}

/**
* uuid 2c45cf54-6da3-4748-9733-5a3c765d925b
* @testdox Binary to multiple decimal
*/
public function testBinaryToMultipleDecimal(): void
{
$this->assertEquals([4, 2], rebase(2, [1, 0, 1, 0, 1, 0], 10));
}

/**
* uuid 65ddb8b4-8899-4fcc-8618-181b2cf0002d
* @testdox Decimal to binary
*/
public function testDecimalToBinary(): void
{
$this->assertEquals([1, 0, 1, 0, 1, 0], rebase(10, [4, 2], 2));
}

/**
* uuid 8d418419-02a7-4824-8b7a-352d33c6987e
* @testdox Trinary to hexadecimal
*/
public function testTrinaryToHexadecimal(): void
{
$this->assertEquals([2, 10], rebase(3, [1, 1, 2, 0], 16));
}

/**
* uuid d3901c80-8190-41b9-bd86-38d988efa956
* @testdox Hexadecimal to trinary
*/
public function testHexadecimalToTrinary(): void
{
$this->assertEquals([1, 1, 2, 0], rebase(16, [2, 10], 3));
}

/**
* uuid 5d42f85e-21ad-41bd-b9be-a3e8e4258bbf
* @testdox 15-bit integer
*/
public function test15BitIntegers(): void
{
$this->assertEquals([6, 10, 45], rebase(97, [3, 46, 60], 73));
}

public function testEmptyListReturnsNull(): void
/**
* uuid d68788f7-66dd-43f8-a543-f15b6d233f83
* @testdox Empty list
*/
public function testEmptyList(): void
{
$this->assertEquals(null, rebase(10, [], 10));
$this->assertEquals([0], rebase(2, [], 10));
}

public function testSingleZeroReturnsNull(): void
/**
* uuid 5e27e8da-5862-4c5f-b2a9-26c0382b6be7
* @testdox Single zero
*/
public function testSingleZero(): void
{
$this->assertEquals(null, rebase(10, [0], 2));
$this->assertEquals([0], rebase(10, [0], 2));
}

public function testMultipleZerosReturnsNull(): void
/**
* uuid 2e1c2573-77e4-4b9c-8517-6c56c5bcfdf2
* @testdox Multiple zeros
*/
public function testMultipleZeros(): void
{
$this->assertEquals(null, rebase(10, [0, 0, 0], 2));
$this->assertEquals([0], rebase(10, [0, 0, 0], 2));
}

public function testLeadingZerosReturnsNull(): void
/**
* uuid 3530cd9f-8d6d-43f5-bc6e-b30b1db9629b
* @testdox Leading zeros
*/
public function testLeadingZeros(): void
{
$this->assertEquals(null, rebase(10, [0, 6, 0], 2));
$this->assertEquals([4, 2], rebase(7, [0, 6, 0], 10));
}

/**
* uuid a6b476a1-1901-4f2a-92c4-4d91917ae023
* @testdox Input base is one
*/
public function testFirstBaseIsOne(): void
{
$this->assertEquals(null, rebase(1, [6, 0], 2));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('input base must be >= 2');

rebase(1, [0], 10);
}

/**
* uuid e21a693a-7a69-450b-b393-27415c26a016
* @testdox Input base is zero
*/
public function testFirstBaseIsZero(): void
{
$this->assertEquals(null, rebase(0, [6, 0], 2));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('input base must be >= 2');

rebase(0, [], 10);
}

/**
* uuid 54a23be5-d99e-41cc-88e0-a650ffe5fcc2
* @testdox Input base is negative
*/
public function testFirstBaseIsNegative(): void
{
$this->assertEquals(null, rebase(-1, [6, 0], 2));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('input base must be >= 2');

rebase(-2, [1], 10);
}

/**
* uuid 9eccf60c-dcc9-407b-95d8-c37b8be56bb6
* @testdox Negative digit
*/
public function testNegativeDigit(): void
{
$this->assertEquals(null, rebase(10, [1, -1, 0], 2));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('all digits must satisfy 0 <= d < input base');

rebase(2, [1, -1, 1, 0, 1, 0], 10);
}

/**
* uuid 232fa4a5-e761-4939-ba0c-ed046cd0676a
* @testdox Invalid positive digit
*/
public function testInvalidPositiveDigit(): void
{
$this->assertEquals(null, rebase(2, [1, 2, 0], 10));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('all digits must satisfy 0 <= d < input base');

rebase(2, [1, 2, 1, 0, 1, 0], 10);
}

/**
* uuid 14238f95-45da-41dc-95ce-18f860b30ad3
* @testdox Output base is one
*/
public function testSecondBaseIsOne(): void
{
$this->assertEquals(null, rebase(2, [1, 1, 0], 1));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('output base must be >= 2');

rebase(2, [1, 0, 1, 0, 1, 0], 1);
}

/**
* uuid 73dac367-da5c-4a37-95fe-c87fad0a4047
* @testdox Output base is zero
*/
public function testSecondBaseIsZero(): void
{
$this->assertEquals(null, rebase(2, [1, 1, 0], 0));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('output base must be >= 2');

rebase(10, [7], 0);
}

/**
* uuid 13f81f42-ff53-4e24-89d9-37603a48ebd9
* @testdox Output base is negative
*/
public function testSecondBaseIsNegative(): void
{
$this->assertEquals(null, rebase(2, [1, 1, 0], -1));
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('output base must be >= 2');

rebase(2, [1], -7);
}

/**
* uuid 0e6c895d-8a5d-4868-a345-309d094cfe8d
* @testdox Both bases are negative
*/
public function testBothBasesIsNegative(): void
{
$this->assertEquals(null, rebase(-3, [1, 1, 0], -1));
$this->expectException(InvalidArgumentException::class);

rebase(-2, [1], -7);
}
}

0 comments on commit d2a5961

Please sign in to comment.