Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync All your base #694

Merged
merged 6 commits into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
mk-mxp marked this conversation as resolved.
Show resolved Hide resolved
* @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);
}
}