From ffa3bc85ebc8ff2a9d70d272f4c65b2342316d6b Mon Sep 17 00:00:00 2001 From: Julio Date: Thu, 24 Sep 2020 19:34:55 +0200 Subject: add juliodcs week79 perl and raku solutions --- challenge-079/juliodcs/perl/ch-1.pl | 62 ++++++++++++++++++++++++ challenge-079/juliodcs/perl/ch-2.pl | 75 +++++++++++++++++++++++++++++ challenge-079/juliodcs/raku/ch-1.raku | 38 +++++++++++++++ challenge-079/juliodcs/raku/ch-2.raku | 88 +++++++++++++++++++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 challenge-079/juliodcs/perl/ch-1.pl create mode 100644 challenge-079/juliodcs/perl/ch-2.pl create mode 100644 challenge-079/juliodcs/raku/ch-1.raku create mode 100644 challenge-079/juliodcs/raku/ch-2.raku (limited to 'challenge-079') diff --git a/challenge-079/juliodcs/perl/ch-1.pl b/challenge-079/juliodcs/perl/ch-1.pl new file mode 100644 index 0000000000..45140137ec --- /dev/null +++ b/challenge-079/juliodcs/perl/ch-1.pl @@ -0,0 +1,62 @@ +#!perl + +# This code is way longer than my initial approach: +# +# $total += count_ones to_binary $number-- while $number > 0; +# say $total % 1000000007; +# +# But at the same time it should be much faster +# since the cost is logarithmic +# +# Try me with big numbers! + +use strict; +use warnings; +use feature 'say'; +use experimental 'signatures'; + +use constant MODULE => 1000000007; +use constant INTEGER_LAST => 2**63 - 1; + +use constant W_ACCURATE => 'Warning: intermediate result > Integer\'last. Result *may* not be accurate!'; +use constant E_ACCURATE => 'Error: Number cannot be > Integer\'last'; +use constant E_NO_NUMBER => 'You need to submit a number'; + +sub length_bin($number) { + length sprintf '%b', $number; +} + +# Given a number, it calculates the flips of the most-significant-bit number +# e.g., ms-flips of 13 (1101) returns the number of flips for number 8 (1000) +sub score($number) { + return 1 if $number == 1; + 1 + ( length_bin($number) - 1 ) * 2**( length_bin($number) - 2 ); +} + +# Remove most significat bit and return number +sub next_number($number) { + $number - 2**( length_bin($number) - 1 ); +} + +sub calculate ( $number, $total = 0 ) { + if ( $number == 0 ) { + say {*STDERR} W_ACCURATE if $total > INTEGER_LAST; + return $total % MODULE; + } + + # All bits besides the first need extra flips + # extra flips are equal to the number itself + my $extra = $total == 0 ? 0 : $number; + + # Use tail call optimization + @_ = ( next_number($number), $total + score($number) + $extra ); + goto &calculate; +} + +my $number = shift // q(); + +die E_NO_NUMBER if $number !~ m{^\d+$}sxm; + +die E_ACCURATE if $number > INTEGER_LAST; + +say 'Result: ' . calculate($number); diff --git a/challenge-079/juliodcs/perl/ch-2.pl b/challenge-079/juliodcs/perl/ch-2.pl new file mode 100644 index 0000000000..453cb48010 --- /dev/null +++ b/challenge-079/juliodcs/perl/ch-2.pl @@ -0,0 +1,75 @@ +use strict; +use warnings; +use experimental 'signatures'; +use feature qw(say state); +use List::Util qw(max min reduce); + +use constant E_MUST_BE_POSITIVE => 'ERROR: All numbers must be > 0'; +use constant E_NO_DATA => 'ERROR: You need to input some numbers'; +use constant E_NUMERIC => 'ERROR: Only number allowed'; +use constant W_UNALIGNED_FOOTER => "WARNING: footer numbers may not be aligned\n"; + +sub calculate_water($histogram) { + + # Counting water with regex! Yay! + # https://regex101.com/r/Ph88Wi/1/ + my @matches = $histogram =~ m{ + (?: + [#] [ ] \K [ ] + | \G [ ] \K [ ] + ) + (?= [ ]*+ [#] ) # An ending block must exist + }sxmg; + scalar @matches; +} + +sub max_value { + state $max_value = max @ARGV; +} + +sub pad($str) { + sprintf '%' . length( max_value() ) . 's', $str; +} + +sub data_item ( $line, $x_val ) { + $line <= $ARGV[$x_val] ? q{ #} : q{ }; +} + +sub data_line($line) { + reduce { $a . data_item( $line, $b ) } + ( q{}, 0 .. @ARGV - 1 ); +} + +sub histogram_lines { + reduce { $a . sprintf "%s%s\n", pad($b), data_line($b) } + ( q{}, reverse 1 .. max_value() ); +} + +sub separator { + ( pad(q(-)) . q( -) x @ARGV ) . "\n"; +} + +sub footer { + sprintf pad(q( )) . "[%s]\n", join(q( ), @ARGV) +} + +## MAIN ## + +die E_NO_DATA if @ARGV == 0; +die E_NUMERIC if @ARGV != grep { m/^\d+$/ } @ARGV; +die E_MUST_BE_POSITIVE if ( min @ARGV ) < 1; + +my $histogram = histogram_lines(); + +say "\n"; + +use constant MAX_SINGLE_DIGIT => 9; +if (max_value() > MAX_SINGLE_DIGIT) { + say {*STDERR} W_UNALIGNED_FOOTER; +} + +print $histogram; +print separator(); +print footer(); + +say "\nTotal water: " . calculate_water($histogram); diff --git a/challenge-079/juliodcs/raku/ch-1.raku b/challenge-079/juliodcs/raku/ch-1.raku new file mode 100644 index 0000000000..1d120bc689 --- /dev/null +++ b/challenge-079/juliodcs/raku/ch-1.raku @@ -0,0 +1,38 @@ +# This code is way longer than my initial approach: +# +# $total += count_ones to_binary $number-- while $number > 0; +# say $total % 1000000007; +# +# But at the same time it should be much faster +# since the cost is logarithmic +# +# Try me with big numbers! + +constant \MODULE = 1000000007; + +# Given a number, it calculates the flips of the most-significant-bit number +# e.g., ms-flips of 13 (1101) returns the number of flips for number 8 (1000) +sub ms-flips(UInt:D \number) returns UInt:D { + number == 1 ?? 1 !! 1 + number.msb * 2 ** (number.msb - 1) +} + +# Remove most significat bit and return number +sub next-number(UInt:D \number) returns UInt:D { + number - 2 ** number.msb +} + +sub calculate(UInt:D \number, UInt:D \accumulator = 0) returns UInt:D { + return accumulator % MODULE if number == 0; + + # All bits besides the first need extra flips + # extra flips are equal to the number itself + my \extra-flips = accumulator == 0 ?? 0 !! number; + my \next-accumulator = accumulator + ms-flips(number) + extra-flips; + + calculate next-number(number), next-accumulator +} + +sub MAIN(UInt:D \number) returns Nil { + say 'Result: ' ~ calculate number; + return; +} diff --git a/challenge-079/juliodcs/raku/ch-2.raku b/challenge-079/juliodcs/raku/ch-2.raku new file mode 100644 index 0000000000..26097a7438 --- /dev/null +++ b/challenge-079/juliodcs/raku/ch-2.raku @@ -0,0 +1,88 @@ +class WaterHistogram { + subset Natural of UInt where * >= 1; + + constant \W_UNALIGNED_FOOTER = "WARNING: footer numbers may not be aligned\n"; + + has Natural @!x-values; + has Str $!lines; + + method !x-values is rw { @!x-values } + method !lines is rw { $!lines } + + method new(*@list) { + my $self = WaterHistogram.bless; + $self!init: @list; + $self + } + + method print-it returns Nil { + constant \MAX_SINGLE_DIGIT = 9; + if self!max-value > MAX_SINGLE_DIGIT { + $*ERR.say: W_UNALIGNED_FOOTER; + } + + print self!lines; + print self!separator; + print self!footer; + + return; + } + + method count-water returns UInt:D { + # Counting water with Raku regex! Yay! + elems self!lines ~~ m:g/「 」 / + } + + method !separator returns Str:D { + self!pad(「-」) ~ 「 -」 x self!x-values ~ "\n"; + } + + method !footer returns Str:D { + self!pad(「 」) ~ sprintf "[%s]\n", self!x-values.join: 「 」 + } + + method !init(*@list) returns Nil { + self!x-values = @list.map: *.Int; + self!lines = self!prepare-lines; + return; + } + + method !prepare-lines returns Str:D { + sub add-lines(Str:D $acc, Natural:D $line) { + $acc ~ self!pad(~$line) ~ self!data-line($line) ~ "\n" + } + + [[&add-lines]] 「」, self!max-value ... 1; + } + + method !pad(Str:D $str) returns Str:D { + sprintf "%{self!max-value.chars}s", $str; + } + + method !data-line(Natural:D $line) returns Str:D { + sub add-data-item(Str:D $acc, UInt:D $point-index) { + $acc ~ self!data-item( $line, $point-index ) + } + + [[&add-data-item]] 「」, |^self!x-values.elems + } + + method !data-item($line, $x-index) { + $line <= self!x-values[$x-index] ?? 「 #」 !! 「 」 + } + + method !max-value returns Natural:D { + state Natural $max = self!x-values.max; + $max + } +} + +sub MAIN(*@args) { + say "\n"; + + my $water-histogram = WaterHistogram.new: @args; + + $water-histogram.print-it; + + say "\nTotal water: " ~ $water-histogram.count-water; +} -- cgit From ca7bab766928cad6fd56416a884553f4a0778480 Mon Sep 17 00:00:00 2001 From: Julio Date: Thu, 24 Sep 2020 19:47:19 +0200 Subject: add juliodcs week79 perl and raku solutions --- challenge-079/juliodcs/perl/ch-1.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'challenge-079') diff --git a/challenge-079/juliodcs/perl/ch-1.pl b/challenge-079/juliodcs/perl/ch-1.pl index 45140137ec..5409ee8c5a 100644 --- a/challenge-079/juliodcs/perl/ch-1.pl +++ b/challenge-079/juliodcs/perl/ch-1.pl @@ -28,7 +28,7 @@ sub length_bin($number) { # Given a number, it calculates the flips of the most-significant-bit number # e.g., ms-flips of 13 (1101) returns the number of flips for number 8 (1000) -sub score($number) { +sub ms_flips($number) { return 1 if $number == 1; 1 + ( length_bin($number) - 1 ) * 2**( length_bin($number) - 2 ); } @@ -49,7 +49,7 @@ sub calculate ( $number, $total = 0 ) { my $extra = $total == 0 ? 0 : $number; # Use tail call optimization - @_ = ( next_number($number), $total + score($number) + $extra ); + @_ = ( next_number($number), $total + ms_flips($number) + $extra ); goto &calculate; } -- cgit