exercise: AllYourBase version: 2 plan: 23 imports: '&convert-base' tests: |- for @($c-data) -> $case { if $case ~~ Array:D { test } else { given $case { when 'empty list' { test [] } when /base|digit/ { throws-like {call-convert-base}, Exception, $_ } when /zero/ { when 'leading zeros' { test [4,2] } default { test [0] } } flunk "$_; not tested" if %*ENV; # To ensure that no canonical-data cases are missed. } } sub test (Array:D $expected = $case) { is-deeply call-convert-base, $expected, $case } sub call-convert-base { convert-base(|$case) } } unit: module example: |- class X::AllYourBase::InvalidBase is Exception { has $.payload; method message {"$!payload is not a valid base."} } class X::AllYourBase::InvalidDigit is Exception { has %.payload; method message {"%!payload is not a valid digit for base %!payload."} } sub convert-base (Int:D $input-base, @input-digits, Int:D $output-base --> Array:D) is export { for $input-base, $output-base { X::AllYourBase::InvalidBase.new(payload => $_).throw if $_ < 2; } from-decimal($output-base, (to-decimal $input-base, @input-digits)); } sub to-decimal ($input-base, @input-digits) { return [].Slip if !@input-digits; my $elems = @input-digits.elems; for @input-digits { if $_ == 0 { $elems-- } else { last } } my $dec = 0; loop (my $i = 0; $i < $elems; $i++) { if @input-digits.reverse[$i] < 0 || @input-digits.reverse[$i] >= $input-base { X::AllYourBase::InvalidDigit.new( :payload(base => $input-base, digit => @input-digits.reverse[$i]) ).throw; } $dec += @input-digits.reverse[$i] * $input-base ** $i; } return $dec; } sub from-decimal ($output-base, $dec) { my @output-digits; my $num = $dec; while $num >= $output-base { unshift @output-digits, $num % $output-base; $num div= $output-base; } unshift @output-digits, $num; return @output-digits; }