Perl Weekly Challenge 121: Invert Bit

These are some answers to the Week 121 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due on July 18, 2021 at 24:00. This blog post offers some solutions to this challenge, please don’t read on if you intend to complete the challenge on your own.

You are given integers 0 <= $m <= 255 and 1 <= $n <= 8.

Write a script to invert $n bit from the end of the binary representation of $m and print the decimal representation of the new binary number.

Example:

Input: $m = 12, $n = 3
Output: 8

Binary representation of $m = 00001100
Invert 3rd bit from the end = 00001000
Decimal equivalent of 00001000 = 8

Input $m = 18, $n = 4
Output: 26

Binary representation of $m = 00010010
Invert 4th bit from the end = 00011010
Decimal equivalent of 00011010 = 26

Invert Bit in Raku

We use the fmt method to convert the input numeral into a binary string. We could also use the base method, but the fmt method makes it possible to also specify an output format on 8 digits in one step (with leading 0’s when needed). Then we use the substr to obtain the value of the $nth bit from the right (using the *-$n subscript for that), and we use the substr-rw to modify the relevant bit. Finally, we use the parse-base to convert back the result into its numeric equivalent.

use v6;

sub invert-bit (UInt $m where * <=255, UInt $n where 1 <= * <= 8) {
    my $bin = $m.fmt("%08b");
    # say $bin;
    my $bit = $bin.substr(*-$n, 1);
    $bin.substr-rw(*-$n, 1) = $bit == 0 ?? 1 !! 0;
    # say $bin;
    return $bin.parse-base(2);
}
for 12, 3, 
    18, 4, 
    249, 1 {
    say "$^a $^b => ", invert-bit $^a, $^b;
}

This program displays the following output:

$ raku ./invert-bit.raku
12 3 => 8
18 4 => 26
249 1 => 248

Invert Bit in Perl

The Perl program is essentially a port to Perl of the Raku program above. Since Perl doesn’t have a binary string to numeral conversion, we re-use the bin2dec subroutine implemented in a previous challenge. And we use sprintf to perform decimal to binary representation conversion.

use strict;
use warnings;
use feature qw/say/;

sub bin2dec {
    my $bin = shift;
    my $sum = 0;
    for my $i (split //, $bin) {
        $sum = $sum * 2 + $i;
    }
    return $sum;
}

sub invert_bit {
    my ($m, $n) = @_;
    my $bin = sprintf "%08b", $m;
    # say $bin;
    my $bit = substr $bin, -$n, 1;
    substr $bin, -$n, 1, $bit == 0 ? 1 : 0;
    # say $bin;
    return bin2dec $bin;
}
for my $pair ( [12, 3], [18, 4], [249, 1] ) {
    say "@$pair => ", invert_bit @$pair;
}

This program displays the following output:

$ perl ./invert-bit.pl
12 3 => 8
18 4 => 26
249 1 => 248

Wrapping up

Because of my volunteer activity involvement in a Covid-19 vaccination center this weekend, I won’t have time this week to work on task 2. The traveling salesman problem has been studied in detail over decades and is not particularly difficult to understand and implement, but it does require quite a bit of coding effort for which I don’t have any free time this week.

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on July 25, 2021. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.