with Raku

This is my response to The Weekly Challenge #269.

You are given an array of positive integers,

Write a script to find out if it is possible to select two or more elements of the given array such that the bitwise OR of the selected elements has at lest one trailing zero in its binary representation.

Example 1:

`@ints`

.
Write a script to find out if it is possible to select two or more elements of the given array such that the bitwise OR of the selected elements has at lest one trailing zero in its binary representation.

Example 1:

Input: @ints = (1, 2, 3, 4, 5)
Output: true
Say, we pick 2 and 4, their bitwise OR is 6. The binary representation
of 6 is 110. Return true since we have one trailing zero.

Example 2:
Input: @ints = (2, 3, 8, 16)
Output: true
Say, we pick 2 and 8, their bitwise OR is 10. The binary representation
of 10 is 1010. Return true since we have one trailing zero.

Example 3:
Input: @ints = (1, 2, 5, 7, 9)
Output: false

File: bitwise-or

#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 1 # [1]
&& all(@ints) ~~ UInt && all(@ints) > 0, # [1a]
:v(:$verbose));
for @ints.combinations(2..*) -> @combination # [2]
{
my $or = [+|] @combination; # [3]
my $binary = $or.fmt('%b'); # [4]
my $trailing = so ($binary ~~ /.0$/); # [5]
say ": Combination: { @combination.join(",") } -> or: $or -> \
binary: $binary | trailing 0: $trailing" if $verbose;
if $trailing # [6]
{
say True; # [6a]
exit; # [6b]
}
}
say False; # [7]

[1] At least one element, all of which must be of the `UInt`

(Unsigned
Integer) type [1a] and they must also be greater than zero [1b].

[2] Iterate over all possible combinations of the input,
with at least two elements, with `combinations`

.

See docs.raku.org/routine/combinations for more information about `combinations`

.

[3] Use the Reduction Metaoperator
`[]`

in combination with the Binary OR operator `+|`

to reduce
the canditate values, of which there are two or more, to the binary or'ed
result.

See
docs.raku.org/language/operators#Reduction_metaoperators for more
information about the Reduction Metaoperator `[]`

.

[4] Get the binary representation with `fmt`

.

See docs.raku.org/routine/fmt for more information about `fmt`

.

[5] Do we have a trailing zero? Note the period, as we insist on
a binary digit *before* that zero. It has to trail behind something,
right? We get a match object on success, or `Nil`

on failure. They are
not really suitable for printing, so we apply the Boolean coercer `so`

to coerce the result to a Boolean value; True on a successful match and
False if not.

See docs.raku.org/routine/so for more information about `so`

.

[6] Do we have a match? If so, say so [6a] and exit [6b].

[7] We have tried all the combinations without finding a match. We have failed. Say so.

Running it:

$ ./bitwise-or 1 2 3 4 5
True
$ ./bitwise-or 2 3 8 16
True
$ ./bitwise-or 1 2 5 7 9
False

Looking good.

With verbose mode:

$ ./bitwise-or -v 1 2 3 4 5
: Combination: 1,2 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 1,3 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 1,4 -> or: 5 -> binary: 101 | trailing 0: False
: Combination: 1,5 -> or: 5 -> binary: 101 | trailing 0: False
: Combination: 2,3 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 2,4 -> or: 6 -> binary: 110 | trailing 0: True
True
$ ./bitwise-or -v 2 3 8 16
: Combination: 2,3 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 2,8 -> or: 10 -> binary: 1010 | trailing 0: True
True
$ ./bitwise-or -v 1 2 5 7 9
: Combination: 1,2 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 1,5 -> or: 5 -> binary: 101 | trailing 0: False
: Combination: 1,7 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,9 -> or: 9 -> binary: 1001 | trailing 0: False
: Combination: 2,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 2,7 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 2,9 -> or: 11 -> binary: 1011 | trailing 0: False
: Combination: 5,7 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 5,9 -> or: 13 -> binary: 1101 | trailing 0: False
: Combination: 7,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 1,2,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,2,7 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,2,9 -> or: 11 -> binary: 1011 | trailing 0: False
: Combination: 1,5,7 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,5,9 -> or: 13 -> binary: 1101 | trailing 0: False
: Combination: 1,7,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 2,5,7 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 2,5,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 2,7,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 5,7,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 1,2,5,7 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,2,5,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 1,2,7,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 1,5,7,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 2,5,7,9 -> or: 15 -> binary: 1111 | trailing 0: False
: Combination: 1,2,5,7,9 -> or: 15 -> binary: 1111 | trailing 0: False
False

What about detecting if there are more matches?

File: bitwise-or-all#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 1
&& all(@ints) ~~ UInt && all(@ints) > 0,
:a(:$all) = False, # [1]
:v(:$verbose) = $all);
my $found = False; # [2]
for @ints.combinations(2..*) -> @combination
{
my $or = [+|] @combination;
my $binary = $or.fmt('%b');
my $trailing = so ($binary ~~ /.0$/);
say ": Combination: { @combination.join(",") } -> or: $or -> \
binary: $binary | trailing 0: $trailing" if $verbose;
if $trailing
{
$found = True; # [3]
last unless $all;
}
}
say $found; # [4]

[1] Use «-a» to show all the combinations. This does not make sense without verbose mode, so it enables that by default.

[2] Not found initially.

[3] We have found it. Terminate the loop (with `last`

)
unless we have used «-a».

See docs.raku.org/routine/last for more information about `last`

.

[4] Print the result.

Running it, with manual highlighting of the True lines:

$ ./bitwise-or-all -a 1 2 3 4 5
: Combination: 1,2 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 1,3 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 1,4 -> or: 5 -> binary: 101 | trailing 0: False
: Combination: 1,5 -> or: 5 -> binary: 101 | trailing 0: False
: Combination: 2,3 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 2,4 -> or: 6 -> binary: 110 | trailing 0: True
: Combination: 2,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 3,4 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 3,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 4,5 -> or: 5 -> binary: 101 | trailing 0: False
: Combination: 1,2,3 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 1,2,4 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,2,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,3,4 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,3,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,4,5 -> or: 5 -> binary: 101 | trailing 0: False
: Combination: 2,3,4 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 2,3,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 2,4,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 3,4,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,2,3,4 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,2,3,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,2,4,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,3,4,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 2,3,4,5 -> or: 7 -> binary: 111 | trailing 0: False
: Combination: 1,2,3,4,5 -> or: 7 -> binary: 111 | trailing 0: False
True
$ ./bitwise-or-all -a 2 3 8 16
: Combination: 2,3 -> or: 3 -> binary: 11 | trailing 0: False
: Combination: 2,8 -> or: 10 -> binary: 1010 | trailing 0: True
: Combination: 2,16 -> or: 18 -> binary: 10010 | trailing 0: True
: Combination: 3,8 -> or: 11 -> binary: 1011 | trailing 0: False
: Combination: 3,16 -> or: 19 -> binary: 10011 | trailing 0: False
: Combination: 8,16 -> or: 24 -> binary: 11000 | trailing 0: True
: Combination: 2,3,8 -> or: 11 -> binary: 1011 | trailing 0: False
: Combination: 2,3,16 -> or: 19 -> binary: 10011 | trailing 0: False
: Combination: 2,8,16 -> or: 26 -> binary: 11010 | trailing 0: True
: Combination: 3,8,16 -> or: 27 -> binary: 11011 | trailing 0: False
: Combination: 2,3,8,16 -> or: 27 -> binary: 11011 | trailing 0: False
True

The third example does not have any True lines, so is not shown.

You are given an array of distinct integers,

Write a script to distribute the elements as described below:

If the last element of the array

When done distribution, return the concatenated arrays.

Example 1:

File: distribute-elements
`@ints`

.
Write a script to distribute the elements as described below:

- Put the 1st element of the given array to a new array @arr1.
- Put the 2nd element of the given array to a new array @arr2.

`@arr1`

and `@arr2`

,
then follow the rule below:
If the last element of the array

`@arr1`

is greater than the last
element of the array `@arr2`

then add the first element of the given
array to `@arr1`

otherwise to the array `@arr2`

.
When done distribution, return the concatenated arrays.

`@arr1`

and
`@arr2`

.
Example 1:

Input: @ints = (2, 1, 3, 4, 5)
Output: (2, 3, 4, 5, 1)
1st operation:
Add 1 to @arr1 = (2)
2nd operation:
Add 2 to @arr2 = (1)
3rd operation:
Now the last element of @arr1 is greater than the last element
of @arr2, add 3 to @arr1 = (2, 3).
4th operation:
Again the last element of @arr1 is greate than the last element
of @arr2, add 4 to @arr1 = (2, 3, 4)
5th operation:
Finally, the last element of @arr1 is again greater than the last
element of @arr2, add 5 to @arr1 = (2, 3, 4, 5)
Mow we have two arrays:
@arr1 = (2, 3, 4, 5)
@arr2 = (1)
Concatenate the two arrays and return the final array: (2, 3, 4, 5, 1).

Example 2:
Input: @ints = (3, 2, 4)
Output: (3, 4, 2)
1st operation:
Add 1 to @arr1 = (3)
2nd operation:
Add 2 to @arr2 = (2)
3rd operation:
Now the last element of @arr1 is greater than the last element
of @arr2, add 4 to @arr1 = (3, 4).
Mow we have two arrays:
@arr1 = (3, 4)
@arr2 = (2)
Concatenate the two arrays and return the final array: (3, 4, 2).

Example 3:
Input: @ints = (5, 4, 3 ,8)
Output: (5, 3, 4, 8)
1st operation:
Add 1 to @arr1 = (5)
2nd operation:
Add 2 to @arr2 = (4)
3rd operation:
Now the last element of @arr1 is greater than the last element
of @arr2, add 3 to @arr1 = (5, 3).
4th operation:
Again the last element of @arr2 is greate than the last element
of @arr1, add 8 to @arr2 = (4, 8)
Mow we have two arrays:
@arr1 = (5, 3)
@arr2 = (4, 8)
Concatenate the two arrays and return the final array: (5, 3, 4, 8).

#! /usr/bin/env raku
unit sub MAIN (*@ints where @ints.elems > 1 # [1]
&& @ints.unique.elems == @ints.elems # [1a]
&& all(@ints) ~~ Int, # [1b]
:v(:$verbose));
my @arr1 = @ints.shift.Int; # [2]
my @arr2 = @ints.shift.Int; # [3]
if $verbose
{
say ": 1st operation. Add { @arr1[*-1] } to @arr1 = ({ @arr1.join(", ") })";
say ": 2nd operation. Add { @arr2[*-1] } to @arr2 = ({ @arr2.join(", ") })";
}
my $counter = 3; # [4]
while @ints.elems # [5]
{
my $int = @ints.shift.Int; # [5]
my $last1 = @arr1.tail; # [6]
my $last2 = @arr2[*-1]; # [6a]
if $last1 > $last2 # [7]
{
@arr1.push: $int; # [7a]
say ": {$counter++}rd operation. Last of @ints1 > last of @ints2: \
Add $int to @arr1 = ({ @arr1.join(", ") })" if $verbose;
}
else # [7b]
{
@arr2.push: $int; # [7b]
say ": {$counter++}rd operation. Last of @ints2 <= last of @ints2: \
Add $int to @arr2 = ({ @arr2.join(", ") })" if $verbose;
}
}
if $verbose
{
say ": @arr1 = ({ @arr1.join(", ") })";
say ": @arr2 = ({ @arr2.join(", ") })";
}
my @result = (@arr1, @arr2).flat; # [8]
say "({ @result.join(", ") })"; # [9]

[1] At least one element, without duplicates (by comparing the number of elements after removing any duplicates) [1a] and they must all be integers [1b].

[2] Set up the first array with the first value, coerced to integer to get rid of the pesky IntStr type we got courtesy of the command line.

[3] Ditto for the second array.

[4] For verbose mode.

[5] As long as there are more values to parse, get the first one, coerced to an integer [5a].

[6] We can use the `tail`

method to get the last value in
the array, or `[*-1]`

to access the value with an index from the end.

See docs.raku.org/routine/tail for more information about `tail`

.

[7] Push the value to the correct arary.

[8] Add the two arrays together, with a final `flat`

to get
a single array (instead of an array with two elements, each of which itself
is an array).

See docs.raku.org/routine/flat for more information about `flat`

.

[9] Pretty print the result.

Running it:

$ ./distribute-elements 2 1 3 4 5
(2, 3, 4, 5, 1)
$ ./distribute-elements 3 2 4
(3, 4, 2)
$ ./distribute-elements 5 4 3 8
(5, 3, 4, 8)

Looking good.

With verbose mode:

$ ./distribute-elements -v 2 1 3 4 5
: 1st operation. Add 2 to @arr1 = (2)
: 2nd operation. Add 1 to @arr2 = (1)
: 3rd operation. Last of @ints1 > last of @ints2: Add 3 to @arr1 = (2, 3)
: 4rd operation. Last of @ints1 > last of @ints2: Add 4 to @arr1 = (2, 3, 4)
: 5rd operation. Last of @ints1 > last of @ints2: Add 5 to @arr1 = (2, 3, 4, 5)
: @arr1 = (2, 3, 4, 5)
: @arr2 = (1)
(2, 3, 4, 5, 1)
$ ./distribute-elements -v 3 2 4
: 1st operation. Add 3 to @arr1 = (3)
: 2nd operation. Add 2 to @arr2 = (2)
: 3rd operation. Last of @ints1 > last of @ints2: Add 4 to @arr1 = (3, 4)
: @arr1 = (3, 4)
: @arr2 = (2)
(3, 4, 2)
$ ./distribute-elements -v 5 4 3 8
: 1st operation. Add 5 to @arr1 = (5)
: 2nd operation. Add 4 to @arr2 = (4)
: 3rd operation. Last of @ints1 > last of @ints2: Add 3 to @arr1 = (5, 3)
: 4rd operation. Last of @ints2 <= last of @ints2: Add 8 to @arr2 = (4, 8)
: @arr1 = (5, 3)
: @arr2 = (4, 8)
(5, 3, 4, 8)

And that's it.