diff options
| author | Mohammad S Anwar <Mohammad.Anwar@yahoo.com> | 2021-07-10 11:02:04 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-07-10 11:02:04 +0100 |
| commit | bfc689abbf4ed0096af240edc786450a8db470e1 (patch) | |
| tree | 7d388d451e04dc90d864a989c1e8268f57ff8508 /challenge-120 | |
| parent | 0d24c6dde3a0932f997d12bd7709b15897d77499 (diff) | |
| parent | 7f6babdb3116e9ce14c223fa928fe527a0eea28b (diff) | |
| download | perlweeklychallenge-club-bfc689abbf4ed0096af240edc786450a8db470e1.tar.gz perlweeklychallenge-club-bfc689abbf4ed0096af240edc786450a8db470e1.tar.bz2 perlweeklychallenge-club-bfc689abbf4ed0096af240edc786450a8db470e1.zip | |
Merge pull request #4469 from PerlMonk-Athanasius/branch-for-challenge-120
Perl & Raku solutions to Tasks 1 & 2 of the Perl Weekly Challenge #120
Diffstat (limited to 'challenge-120')
| -rw-r--r-- | challenge-120/athanasius/perl/ch-1.pl | 138 | ||||
| -rw-r--r-- | challenge-120/athanasius/perl/ch-2.pl | 224 | ||||
| -rw-r--r-- | challenge-120/athanasius/raku/ch-1.raku | 114 | ||||
| -rw-r--r-- | challenge-120/athanasius/raku/ch-2.raku | 219 |
4 files changed, 695 insertions, 0 deletions
diff --git a/challenge-120/athanasius/perl/ch-1.pl b/challenge-120/athanasius/perl/ch-1.pl new file mode 100644 index 0000000000..2caba57fcf --- /dev/null +++ b/challenge-120/athanasius/perl/ch-1.pl @@ -0,0 +1,138 @@ +#!perl + +############################################################################### +=comment + +Perl Weekly Challenge 120 +========================= + +TASK #1 +------- +*Swap Odd/Even bits* + +Submitted by: Mohammad S Anwar + +You are given a positive integer $N less than or equal to 255. + +Write a script to swap the odd positioned bit with even positioned bit and +print the decimal equivalent of the new binary representation. + +Example + + Input: $N = 101 + Output: 154 + + Binary representation of the given number is 01 10 01 01. + The new binary representation after the odd/even swap is 10 01 10 10. + The decimal equivalent of 10011010 is 154. + + Input: $N = 18 + Output: 33 + + Binary representation of the given number is 00 01 00 10. + The new binary representation after the odd/even swap is 00 10 00 01. + The decimal equivalent of 100001 is 33. + +=cut +############################################################################### + +#--------------------------------------# +# Copyright © 2021 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=comment + +Usage +----- +If the constant $SHOW_BITS is set to a true value, the output will include +binary representations of $N and its odd/even-swapped counterpart. For example: + + Input: $N = 101 (01 10 01 01) + Output: 154 (10 01 10 10) + +Otherwise, only the decimal values will be shown: + + Input: $N = 101 + Output: 154 + +Algorithm +--------- +1. Convert decimal $N to its 8-bit binary representation using sprintf '%08b' +2. Extract the odd/even binary pairs with a regular expression +3. Form $S_bin, the binary representation of the solution, by concatenating the + nibbles in reverse order +4. Find the decimal equivalent of $S_bin by using oct with an argument formed + by prefixing '0b' to $S_bin (this indicates to the built-in oct function + that its argument is a binary number) +5. Output the solution (with suitable vertical alignment for ease of viewing) + +=cut +#============================================================================== + +use strict; +use warnings; +use Const::Fast; +use Regexp::Common qw( number ); + +const my $SHOW_BITS => 1; +const my $USAGE => +"Usage: + perl $0 <N> + + <N> A positive integer not greater than 255\n"; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + $| = 1; + print "\nChallenge 120, Task #1: Swap Odd/Even bits (Perl)\n\n"; +} + +#============================================================================== +MAIN: +#============================================================================== +{ + my $N_dec = parse_command_line(); # Decimal + my $N_bin = sprintf '%08b', $N_dec; # Binary + my @pairs = $N_bin =~ / ( \d{2} ) /gx; # Odd/even pairs + my @swaps = map { scalar reverse } @pairs; + my $S_bin = join '', @swaps; # ("S" for "swap") + my $S_dec = oct "0b$S_bin"; # Decimal + my $len_N = length $N_dec; + my $len_S = length $S_dec; + my $width = $len_N >= $len_S ? $len_N : $len_S; + + printf "Input: \$N = %*d%s\nOutput: %*d%s\n", + $width, $N_dec, $SHOW_BITS ? ' (' . join(' ', @pairs) . ')' : '', + $width, $S_dec, $SHOW_BITS ? ' (' . join(' ', @swaps) . ')' : ''; +} + +#------------------------------------------------------------------------------ +sub parse_command_line +#------------------------------------------------------------------------------ +{ + my $args = scalar @ARGV; + $args == 1 or error( "Expected 1 command line argument, found $args" ); + + my $N = $ARGV[ 0 ]; + $N =~ / ^ $RE{num}{int} $ /x + or error( qq["$N" is not a valid integer] ); + $N += 0; # Normalize: e.g., 010 --> 10 + $N >= 0 or error( "$N is less than 0" ); + $N <= 255 or error( "$N is greater than 255" ); + + return $N; +} + +#------------------------------------------------------------------------------ +sub error +#------------------------------------------------------------------------------ +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +############################################################################### diff --git a/challenge-120/athanasius/perl/ch-2.pl b/challenge-120/athanasius/perl/ch-2.pl new file mode 100644 index 0000000000..0524ca0ea8 --- /dev/null +++ b/challenge-120/athanasius/perl/ch-2.pl @@ -0,0 +1,224 @@ +#!perl + +############################################################################### +=comment + +Perl Weekly Challenge 120 +========================= + +TASK #2 +------- +*Clock Angle* + +Submitted by: Mohammad S Anwar + +You are given time $T in the format hh:mm. + +Write a script to find the smaller angle formed by the hands of an analog clock +at a given time. + +HINT: A analog clock is divided up into 12 sectors. One sector represents 30 +degree (360/12 = 30). + +Example + + Input: $T = '03:10' + Output: 35 degree + + The distance between the 2 and the 3 on the clock is 30 degree. + For the 10 minutes i.e. 1/6 of an hour that have passed. + The hour hand has also moved 1/6 of the distance between the 3 and the 4, + which adds 5 degree (1/6 of 30). + The total measure of the angle is 35 degree. + + Input: $T = '04:00' + Output: 120 degree + +=cut +############################################################################### + +#--------------------------------------# +# Copyright © 2021 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=comment + +Note on Output +-------------- +Should fractions of a degree be output as decimals or as minutes and seconds? +The issue is simplified by the observation that the smallest fraction is half a +degree. (See below: ma_deg = (hours * 30) + (mins / 60) * 30 + = (hours * 30) + (mins / 2) +and since hours and mins are both positive integers, ma_deg ends in 0.5° when +the value of mins is odd.) Therefore, whole-number solutions are output as +degrees only; solutions ending in 0.5° are output as both --.5° and --° 30' 0". + +Algorithm +--------- +Extract hour and mins from T using a regular expression + +Let "the axis" be the vertical line passing through points 12 and 6 on the + clockface + +Let ha_deg be the angle, in degrees, between the axis and the hour hand of the + clock, moving clockwise from the axis to the hour hand +Calculation: + ha_deg = mins * 60° + +Let ma_deg be the angle, in degrees, between the axis and the minute hand of + the clock, moving clockwise from the axis to the minute hand +Calculation: + ma_deg = (hours * 30°) + (mins / 60) * 30° + +Let hm_deg be the smaller of the two angles, in degrees, between the hour and + minutes hands of the clock at the specified time +Calculation: + 1. hm_deg = |ha_deg - ma_deg| + 2. IF hm_deg > 180° THEN hm_deg = 360° - hm_deg + +Interface +--------- +This script was developed and tested under Windows 8.1 (64-bit), with output to +a Command Prompt screen. For proper display of the degree symbol (°), the +Windows active code page is set to 1252 (Latin 1). This is implemented in a +BEGIN block. The original active code page is restored on script completion in +an END block. + +=cut +#============================================================================== + +use strict; +use warnings; +use utf8; +use Capture::Tiny qw( capture ); +use Const::Fast; +use Regexp::Common qw( number ); + +use constant CODE_PAGE => 1252; + +const my $DEGREES_PER_CIRCLE => 360; +const my $HOURS_PER_HALF_DAY => 12; +const my $MINUTES_PER_HOUR => 60; +const my $DEGREES_PER_HOUR => $DEGREES_PER_CIRCLE / $HOURS_PER_HALF_DAY; +const my $DEGREES_PER_MINUTE => $DEGREES_PER_CIRCLE / $MINUTES_PER_HOUR; +const my $EPSILON => 1e-12; +const my $USAGE => +"Usage: + perl $0 <T> + + <T> A time in 12-hour \"hh:mm\" format\n"; + +#============================================================================== +MAIN: +#============================================================================== +{ + my ($hour, $mins) = parse_command_line(); + + printf "Input: \$T = %02d:%02d\n", $hour, $mins; + + my $ma_deg = $DEGREES_PER_MINUTE * $mins; + my $ha_deg = $DEGREES_PER_HOUR * ($hour + ($mins / $MINUTES_PER_HOUR)); + + my $hm_deg = abs( $ha_deg - $ma_deg ); + $hm_deg = $DEGREES_PER_CIRCLE - $hm_deg + if $hm_deg > ($DEGREES_PER_CIRCLE / 2); + + my $diff = $hm_deg - int $hm_deg; + + if ($diff < $EPSILON) # There is no fractional part + { + print "Output: $hm_deg°\n"; + } + elsif (($diff - 0.5) < $EPSILON) # Fractional part is 0.5° (i.e., 30') + { + printf qq[Output: %.1f° (= %d° 30' 0")\n], $hm_deg, $hm_deg; + } + else + { + die "\$diff = $diff, stopped"; # Impossible case + } +} + +#------------------------------------------------------------------------------ +sub parse_command_line +#------------------------------------------------------------------------------ +{ + my $args = scalar @ARGV; + $args == 1 + or error( "Expected 1 command line argument, found $args" ); + + my ($hour, $mins) = $ARGV[ 0 ] =~ / ^ (\d\d) \: (\d\d) $ /x + or error( 'Invalid time format' ); + + # Validate hour + + $hour =~ / ^ $RE{num}{int} $ /x + or error( qq["$hour" is not a valid integer] ); + + 0 < $hour <= 12 + or error( sprintf qq[Hour "%02d" is outside range 1 to 12], $hour ); + + # Validate minutes + + $mins =~ / ^ $RE{num}{int} $ /x + or error( qq["$mins" is not a valid integer] ); + + 0 <= $mins < 60 + or error( qq[Minutes "$mins" is outside range 0 to 59] ); + + return ($hour, $mins); +} + +#------------------------------------------------------------------------------ +sub error +#------------------------------------------------------------------------------ +{ + my ($message) = @_; + + die "ERROR: $message\n$USAGE"; +} + +#============================================================================== +# Initialize and finalize +#============================================================================== +{ + my $code_page; # Active code page + + #-------------------------------------------------------------------------- + BEGIN + #-------------------------------------------------------------------------- + { + $| = 1; + + # Facilitate correct display of the degree symbol (°) in the Windows + # Command Prompt window + + if ($^O =~ /MSWin/i) + { + my ($stdout, $stderr, $exit) = capture { system 'chcp' }; + warn $stderr if $stderr || $exit; + + ($code_page) = $stdout =~ /^Active code page: (\d+)\s$/; + + (undef, $stderr, $exit) = capture { system 'chcp', CODE_PAGE }; + warn $stderr if $stderr || $exit; + } + + print "\nChallenge 120, Task #2: Clock Angle (Perl)\n\n"; + } + + #-------------------------------------------------------------------------- + END + #-------------------------------------------------------------------------- + { + if ($^O =~ /MSWin/i) # Restore the original active code page + { + my (undef, $stderr, $exit) = capture { system 'chcp', $code_page }; + + warn $stderr if $stderr || $exit; + } + } +} + +############################################################################### diff --git a/challenge-120/athanasius/raku/ch-1.raku b/challenge-120/athanasius/raku/ch-1.raku new file mode 100644 index 0000000000..0fba506727 --- /dev/null +++ b/challenge-120/athanasius/raku/ch-1.raku @@ -0,0 +1,114 @@ +use v6d; + +############################################################################### +=begin comment + +Perl Weekly Challenge 120 +========================= + +TASK #1 +------- +*Swap Odd/Even bits* + +Submitted by: Mohammad S Anwar + +You are given a positive integer $N less than or equal to 255. + +Write a script to swap the odd positioned bit with even positioned bit and +print the decimal equivalent of the new binary representation. + +Example + + Input: $N = 101 + Output: 154 + + Binary representation of the given number is 01 10 01 01. + The new binary representation after the odd/even swap is 10 01 10 10. + The decimal equivalent of 10011010 is 154. + + Input: $N = 18 + Output: 33 + + Binary representation of the given number is 00 01 00 10. + The new binary representation after the odd/even swap is 00 10 00 01. + The decimal equivalent of 100001 is 33. + +=end comment +############################################################################### + +#--------------------------------------# +# Copyright © 2021 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=begin comment + +Usage +----- +If the constant $SHOW-BITS is set to True, the output will include binary +representations of $N and its odd/even-swapped counterpart. For example: + + Input: $N = 101 (01 10 01 01) + Output: 154 (10 01 10 10) + +Otherwise, only the decimal values will be shown: + + Input: $N = 101 + Output: 154 + +Algorithm +--------- +1. Convert decimal $N to its 8-bit binary representation using sprintf '%08b' +2. Extract the odd/even binary pairs with a regular expression +3. Form $S-bin, the binary representation of the solution, by concatenating the + nibbles in reverse order +4. Find the decimal equivalent of $S-bin using radix notation +5. Output the solution (with suitable vertical alignment for ease of viewing) + +=end comment +#============================================================================== + +my Bool constant $SHOW-BITS = True; + +#------------------------------------------------------------------------------ +BEGIN +#------------------------------------------------------------------------------ +{ + "\nChallenge 120, Task #1: Swap Odd/Even bits (Raku)\n".put; +} + +#============================================================================== +sub MAIN +( + UInt:D $N where { $N <= 255 } #= A positive integer not greater than 255 +) +#============================================================================== +{ + my UInt $N-dec = $N + 0; # Decimal (normalized) + my Str $N-bin = '%08b'.sprintf: $N-dec; # Binary + $N-bin ~~ m:g/ (\d ** 2) /; # Extract the 4 odd/ + my Str @pairs = $/.map: { .Str }; # even binary pairs + my Str @swaps = @pairs.map: { .flip }; # Swap each pair + my Str $S-bin = @swaps.join; # ("S" for "swap") + my UInt $S-dec = :2( $S-bin ); # Decimal + my UInt $len-N = $N-dec.chars; + my UInt $len-S = $S-dec.chars; + my UInt $width = $len-N >= $len-S ?? $len-N !! $len-S; + + "Input: \$N = %*d%s\nOutput: %*d%s\n".printf: + $width, $N-dec, $SHOW-BITS ?? ' (' ~ @pairs.join( ' ' ) ~ ')' !! '', + $width, $S-dec, $SHOW-BITS ?? ' (' ~ @swaps.join( ' ' ) ~ ')' !! ''; +} + +#------------------------------------------------------------------------------ +sub USAGE() +#------------------------------------------------------------------------------ +{ + my Str $usage = $*USAGE; + + $usage ~~ s/ ($*PROGRAM-NAME) /raku $0/; + + $usage.put; +} + +############################################################################## diff --git a/challenge-120/athanasius/raku/ch-2.raku b/challenge-120/athanasius/raku/ch-2.raku new file mode 100644 index 0000000000..b0e5649e43 --- /dev/null +++ b/challenge-120/athanasius/raku/ch-2.raku @@ -0,0 +1,219 @@ +use v6d; + +############################################################################### +=begin comment + +Perl Weekly Challenge 120 +========================= + +TASK #2 +------- +*Clock Angle* + +Submitted by: Mohammad S Anwar + +You are given time $T in the format hh:mm. + +Write a script to find the smaller angle formed by the hands of an analog clock +at a given time. + +HINT: A analog clock is divided up into 12 sectors. One sector represents 30 +degree (360/12 = 30). + +Example + + Input: $T = '03:10' + Output: 35 degree + + The distance between the 2 and the 3 on the clock is 30 degree. + For the 10 minutes i.e. 1/6 of an hour that have passed. + The hour hand has also moved 1/6 of the distance between the 3 and the 4, + which adds 5 degree (1/6 of 30). + The total measure of the angle is 35 degree. + + Input: $T = '04:00' + Output: 120 degree + +=end comment +############################################################################### + +#--------------------------------------# +# Copyright © 2021 PerlMonk Athanasius # +#--------------------------------------# + +#============================================================================== +=begin comment + +Note on Output +-------------- +Should fractions of a degree be output as decimals or as minutes and seconds? +The issue is simplified by the observation that the smallest fraction is half a +degree. (See below: ma-deg = (hours * 30) + (mins / 60) * 30 + = (hours * 30) + (mins / 2) +and since hours and mins are both positive integers, ma-deg ends in 0.5° when +the value of mins is odd.) Therefore, whole-number solutions are output as +degrees only; solutions ending in 0.5° are output as both --.5° and --° 30' 0". + +Algorithm +--------- +Extract hour and mins from T using a regular expression + +Let "the axis" be the vertical line passing through points 12 and 6 on the + clockface + +Let ha-deg be the angle, in degrees, between the axis and the hour hand of the + clock, moving clockwise from the axis to the hour hand +Calculation: + ha-deg = mins * 60° + +Let ma-deg be the angle, in degrees, between the axis and the minute hand of + the clock, moving clockwise from the axis to the minute hand +Calculation: + ma-deg = (hours * 30°) + (mins / 60) * 30° + +Let hm-deg be the smaller of the two angles, in degrees, between the hour and + minutes hands of the clock at the specified time +Calculation: + 1. hm-deg = |ha-deg - ma-deg| + 2. IF hm-deg > 180° THEN hm-deg = 360° - hm-deg + +Interface +--------- +This script was developed and tested under Windows 8.1 (64-bit), with output to +a Command Prompt screen. For proper display of the degree symbol (°), the Win- +dows active code page is set to 65001 (Unicode, UTF-8). This is implemented in +a BEGIN block. The original active code page is restored on script completion +in an END block. + +Note that the code page 65001 implementation for the Command Prompt is buggy. +The addition of carriage return characters to the final output statements is a +kludge to work around a bug which duplicates the final two characters of +output. + +=end comment +#============================================================================== + +my Int constant DEGS-PER-CIRCLE = 360; +my Int constant HRS-PER-HALF-DAY = 12; +my Int constant MINUTES-PER-HOUR = 60; +my Int constant DEGS-PER-HOUR = (DEGS-PER-CIRCLE / HRS-PER-HALF-DAY).Int; +my Int constant DEGS-PER-MINUTE = (DEGS-PER-CIRCLE / MINUTES-PER-HOUR).Int; +my Int constant CODE-PAGE = 65001; +my Real constant EPSILON = 1e-12; + +#============================================================================== +sub MAIN +( + Str:D $T where / ^ \d\d \: \d\d $ / #= A time in 12-hour "hh:mm" format +) +#============================================================================== +{ + my UInt ($hour, $mins) = parse-time( $T ); + + "Input: \$T = %02d:%02d\n".printf: $hour, $mins; + + my UInt $ma-deg = DEGS-PER-MINUTE * $mins; + my Real $ha-deg = DEGS-PER-HOUR * ($hour + $mins / MINUTES-PER-HOUR); + my Real $hm-deg = ($ha-deg - $ma-deg).abs; + $hm-deg = DEGS-PER-CIRCLE - $hm-deg + if $hm-deg > (DEGS-PER-CIRCLE / 2); + my Real $diff = $hm-deg - $hm-deg.floor; + + if $diff < EPSILON # There is no fractional part + { + "Output: $hm-deg°\n\r\r".print; + } + elsif ($diff - 0.5) < EPSILON # Fractional part is 0.5° (i.e., 30') + { + qq[Output: %.1f° (= %d° 30' 0")\n\r\r].printf: $hm-deg, $hm-deg; + } + else + { + die "\$diff = $diff"; # Impossible case + } +} + +#------------------------------------------------------------------------------ +sub parse-time( Str:D $T --> List:D[ UInt:D, UInt:D ] ) +#------------------------------------------------------------------------------ +{ + my UInt ($hour, $mins) = $T.split( ':' ).map: { .Int }; + + 0 < $hour <= 12 + or error( qq[Hour "%02d" is outside range 1 to 12].sprintf: $hour ); + + 0 <= $mins < 60 + or error( qq[Minutes "$mins" is outside range 0 to 59] ); + + return [ $hour, $mins ]; +} + +#------------------------------------------------------------------------------ +sub error( Str:D $message ) +#------------------------------------------------------------------------------ +{ + "ERROR: $message".put; + + USAGE(); + + exit; +} + +#------------------------------------------------------------------------------ +sub USAGE() +#------------------------------------------------------------------------------ +{ + my Str $usage = $*USAGE; + + $usage ~~ s:g/ ($*PROGRAM-NAME) /raku $0/; + $usage.put; +} + +#============================================================================== +# Initialize and finalize +#============================================================================== +{ + my Int $code-page; + + #-------------------------------------------------------------------------- + BEGIN + #-------------------------------------------------------------------------- + { + # Facilitate correct display of the degree symbol (°) in the Windows + # Command Prompt window + + if VM.osname ~~ m:i/ MSWin / + { + my Proc $proc = run 'chcp', :out, :err; + my Str $error = $proc.err.slurp: :close; + my Str $stdout = $proc.out.slurp: :close; + + note $error if $error; + + $stdout ~~ / ^ Active \s code \s page \: \s (\d+) \s $ /; + $code-page = $0.Int; + + $proc = run 'chcp', CODE-PAGE, :out, :err; + $error = $proc.err.slurp: :close; + + note $error if $error; + } + + "\nChallenge 120, Task #2: Clock Angle (Raku)\n".put; + } + + #-------------------------------------------------------------------------- + END + #-------------------------------------------------------------------------- + { + if VM.osname ~~ m:i/ MSWin / # Restore the original active code page + { + my Proc $proc = run 'chcp', $code-page, :out, :err; + my Str $error = $proc.err.slurp: :close; + + note $error if $error; + } + } +} + +############################################################################## |
