Perl Weekly Challenge 120.

My solutions (task 1 and task 2) to the The 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.

I can get the even and odd bits by applying the & operator with masks that have only even or odd bits on: 10101010=>170 is an 8 bit binary number with all odd bits set (counting right to left from 0) while 01010101=>85 has all even bits set. Then I can shift the odd bits left and the even bits right and combine them with | to get the result. The program is a one liner:

perl -E 'say "Input: $_, Output: ",\
      ((($_&170)>>1) |(($_&85)<<1)) for @ARGV' 101 18

Results:

Input: 101, Output: 154
Input: 18, Output: 33

The full version:

# Perl weekly challenge 120
# Task 1: Swap odd/even bits
#
# See https://wlmb.github.io/2021/07/07/PWC120/#task-1-swap-oddeven-bits
use strict;
use warnings;
use v5.12;
use List::Util qw(all);
my $odd=170; # 10101010
my $even=85; # 01010101

say "Input: $_, Output: ",
     0<=$_<=255?((($_&$odd)>>1) |(($_&$even)<<1)):"Invalid"
     for @ARGV;

Example:

./ch-1.pl 101 18 300

Results:

Input: 101, Output: 154
Input: 18, Output: 33
Input: 300, Output: Invalid

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

The angle moved by the minutes hand is proportional to the minutes. The angle moved by the hours hand is proportional to the hour+the fraction of hour corresponding to the minutes. So the solution to the problem is simply obtained using modular arithmetic. If the angle is too large, I substract it from a full turn. This allows a one liner solution.

perl -E 'for(@ARGV){$_=~m/^(\d?\d):(\d\d)$/ or next; $a=(30*($1+$2/60)-$2*6)%360;\
      say "Input: $_, Output: ", $a>180?360-$a:$a;}' 03:10 04:00

Results:

Input: 03:10, Output; 35
Input: 04:00, Output; 120

The full version

# Perl weekly challenge 120
# Task 2: Clock angle
#
# See https://wlmb.github.io/2021/07/07/PWC120/#task-2-clock-angle
use strict;
use warnings;
use v5.12;

for(@ARGV){
    say "Wrong format: $_", next
         unless $_=~m/^(\d?\d):(\d\d)$/
                && 0<=$1<=24 && 0<=$1<60; # <24?
    my $angle=(30*($1+$2/60)-$2*6)%360;
    $angle=360-$angle if $angle>180;
    say "Input: $_, Output: $angle";
}

Examples:

./ch-2.pl 03:10 04:00

Results:

Input: 03:10, Output: 35
Input: 04:00, Output: 120

Other examples:

./ch-2.pl 15:10 16:00 16:40

Results:

Input: 15:10, Output: 35
Input: 16:00, Output: 120
Input: 16:40, Output: 100
Written on July 7, 2021