Avatar of 4d47
0
0
Genius
0
0

4d47's solution

to All Your Base in the Perl 6 Track

0
0
Genius
0
0
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!

Resources

Remember to check out the Perl 6 documentation and resources pages for information, tips, and examples if you get stuck.

Running the tests

There is a test suite and module included with the exercise. The test suite (a file with the extension .t) will attempt to run routines from the module (a file with the extension .pm6). Add/modify routines in the module so that the tests will pass! You can view the test data by executing the command perl6 --doc *.t (* being the name of the test suite), and run the test suite for the exercise by executing the command prove6 . in the exercise directory.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

all-your-base.t

#!/usr/bin/env perl6
use v6;
use Test;
use JSON::Fast;
use lib $?FILE.IO.dirname;
use AllYourBase;
plan 21;

my $c-data = from-json $=pod.pop.contents;
for $c-data<cases>.values -> $case {
  sub call-convert-base {
    convert-base(
      bases  => %(<from to> Z=> .<input><inputBase outputBase>),
      digits => .<input><digits>,
    ) given $case;
  }

  given $case {
    if .<expected><error> {
      throws-like {call-convert-base}, Exception, .<description>;
    }
    else {
      cmp-ok call-convert-base, ‘~~’, |.<expected description>;
    }
  }
}

=head2 Canonical Data
=begin code
{
  "exercise": "all-your-base",
  "version": "2.3.0",
  "comments": [
    "This canonical data makes the following choices:",
    "1. Zero is always represented in outputs as [0] instead of [].",
    "2. In no other instances are leading zeroes present in any outputs.",
    "3. Leading zeroes are accepted in inputs.",
    "4. An empty sequence of input digits is considered zero, rather than an error.",
    "",
    "Tracks that wish to make different decisions for these choices may translate appropriately.",
    "",
    "All your numeric-base are belong to [2..]. :)"
  ],
  "cases": [
    {
      "description": "single bit one to decimal",
      "property": "rebase",
      "input": {
        "inputBase": 2,
        "digits": [1],
        "outputBase": 10
      },
      "expected": [1]
    },
    {
      "description": "binary to single decimal",
      "property": "rebase",
      "input": {
        "inputBase": 2,
        "digits": [1, 0, 1],
        "outputBase": 10
      },
      "expected": [5]
    },
    {
      "description": "single decimal to binary",
      "property": "rebase",
      "input": {
        "inputBase": 10,
        "digits": [5],
        "outputBase": 2
      },
      "expected": [1, 0, 1]
    },
    {
      "description": "binary to multiple decimal",
      "property": "rebase",
      "input": {
        "inputBase": 2,
        "digits": [1, 0, 1, 0, 1, 0],
        "outputBase": 10
      },
      "expected": [4, 2]
    },
    {
      "description": "decimal to binary",
      "property": "rebase",
      "input": {
        "inputBase": 10,
        "digits": [4, 2],
        "outputBase": 2
      },
      "expected": [1, 0, 1, 0, 1, 0]
    },
    {
      "description": "trinary to hexadecimal",
      "property": "rebase",
      "input": {
        "inputBase": 3,
        "digits": [1, 1, 2, 0],
        "outputBase": 16
      },
      "expected": [2, 10]
    },
    {
      "description": "hexadecimal to trinary",
      "property": "rebase",
      "input": {
        "inputBase": 16,
        "digits": [2, 10],
        "outputBase": 3
      },
      "expected": [1, 1, 2, 0]
    },
    {
      "description": "15-bit integer",
      "property": "rebase",
      "input": {
        "inputBase": 97,
        "digits": [3, 46, 60],
        "outputBase": 73
      },
      "expected": [6, 10, 45]
    },
    {
      "description": "empty list",
      "property": "rebase",
      "input": {
        "inputBase": 2,
        "digits": [],
        "outputBase": 10
      },
      "expected": [0]
    },
    {
      "description": "single zero",
      "property": "rebase",
      "input": {
        "inputBase": 10,
        "digits": [0],
        "outputBase": 2
      },
      "expected": [0]
    },
    {
      "description": "multiple zeros",
      "property": "rebase",
      "input": {
        "inputBase": 10,
        "digits": [0, 0, 0],
        "outputBase": 2
      },
      "expected": [0]
    },
    {
      "description": "leading zeros",
      "property": "rebase",
      "input": {
        "inputBase": 7,
        "digits": [0, 6, 0],
        "outputBase": 10
      },
      "expected": [4, 2]
    },
    {
      "description": "input base is one",
      "property": "rebase",
      "input": {
        "inputBase": 1,
        "digits": [0],
        "outputBase": 10
      },
      "expected": {"error": "input base must be >= 2"}
    },
    {
      "description": "input base is zero",
      "property": "rebase",
      "input": {
        "inputBase": 0,
        "digits": [],
        "outputBase": 10
      },
      "expected": {"error": "input base must be >= 2"}
    },
    {
      "description": "input base is negative",
      "property": "rebase",
      "input": {
        "inputBase": -2,
        "digits": [1],
        "outputBase": 10
      },
      "expected": {"error": "input base must be >= 2"}
    },
    {
      "description": "negative digit",
      "property": "rebase",
      "input": {
        "inputBase": 2,
        "digits": [1, -1, 1, 0, 1, 0],
        "outputBase": 10
      },
      "expected": {"error": "all digits must satisfy 0 <= d < input base"}
    },
    {
      "description": "invalid positive digit",
      "property": "rebase",
      "input": {
        "inputBase": 2,
        "digits": [1, 2, 1, 0, 1, 0],
        "outputBase": 10
      },
      "expected": {"error": "all digits must satisfy 0 <= d < input base"}
    },
    {
      "description": "output base is one",
      "property": "rebase",
      "input": {
        "inputBase": 2,
        "digits": [1, 0, 1, 0, 1, 0],
        "outputBase": 1
      },
      "expected": {"error": "output base must be >= 2"}
    },
    {
      "description": "output base is zero",
      "property": "rebase",
      "input": {
        "inputBase": 10,
        "digits": [7],
        "outputBase": 0
      },
      "expected": {"error": "output base must be >= 2"}
    },
    {
      "description": "output base is negative",
      "property": "rebase",
      "input": {
        "inputBase": 2,
        "digits": [1],
        "outputBase": -7
      },
      "expected": {"error": "output base must be >= 2"}
    },
    {
      "description": "both bases are negative",
      "property": "rebase",
      "input": {
        "inputBase": -2,
        "digits": [1],
        "outputBase": -7
      },
      "expected": {"error": "input base must be >= 2"}
    }
  ]
}
=end code
unit module AllYourBase:ver<2>;

sub convert-base(Int:D $input-base, List $digits, Int:D $output-base --> List) is export {
    die if $input-base < 2;
    return $digits if not $digits;
    to-digits(from-digits($digits, $input-base), $output-base)
}

sub from-digits(List $digits, Int $base --> Int) {
    die if not $digits;
    die if $digits.grep( * < 0 );
    die if $digits.grep( * >= $base );
    ($digits.values Z $digits.keys.reverse).flat.map({ $^a * $base ** $^b }).sum
}

sub to-digits(Int $n is copy, Int $base --> List) {
    my $digits = [];
    while $n > 0 { 
        $digits.unshift: $n % $base;
        $n div= $base;
    }
    $digits || [0]
}

What can you learn from this solution?

A huge amount can be learnt 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 I could read more about to develop my understanding?