# Perl Weekly Challenge 200.

My solutions (task 1 and task 2 ) to the The Weekly Challenge - 200.

``````Submitted by: Mohammad S Anwar
You are given an array of integers.

Write a script to find out all Arithmetic Slices for the given array of integers.

An integer array is called arithmetic if it has at least 3 elements and the
differences between any three consecutive elements are the same.

Example 1
Input: @array = (1,2,3,4)
Output: (1,2,3), (2,3,4), (1,2,3,4)
Example 2
Input: @array = (2)
Output: () as no slice found.
``````

I’m not sure about the meaning of slices. I’ll assume it means consecutive array fragments. Thus a slice is defined by its first and last index. I solve the task by producing all possible slices of three or more elements and filtering trough the number of uniq values in the array of differences. This fits a two liner:

``````perl -MList::Util=uniq -E '
@l=@ARGV; say "[@\$_]", for grep {@m=@\$_; my @d; \$d[\$_]=\$m[\$_+1]-\$m[\$_] for 0..@m-2; uniq(@d)==1}
map {\$s=\$_; map {[@l[\$s..\$_]]}\$s+2..@l-1}(0..@l-3)
' 1 2 3 4
``````

Results:

``````[1 2 3]
[1 2 3 4]
[2 3 4]

perl -MList::Util=uniq -E '
@l=@ARGV; say "[@\$_]", for grep {@m=@\$_; my @d; \$d[\$_]=\$m[\$_+1]-\$m[\$_] for 0..@m-2; uniq(@d)==1}
map {\$s=\$_; map {[@l[\$s..\$_]]}\$s+2..@l-1}(0..@l-3)
' 2
``````

Results: (none)

An example with different strides:

``````perl -MList::Util=uniq -E '
@l=@ARGV; say "[@\$_]", for grep {@m=@\$_; my @d; \$d[\$_]=\$m[\$_+1]-\$m[\$_] for 0..@m-2; uniq(@d)==1}
map {\$s=\$_; map {[@l[\$s..\$_]]}\$s+2..@l-1}(0..@l-3)
' 1 1 2 3 4 6 8
``````

Results:

``````[1 2 3]
[1 2 3 4]
[2 3 4]
[4 6 8]
``````

The full code is almost identical

`````` 1  # Perl weekly challenge 200
2  # Task 1:  Arithmetic Slices
3  #
5  use v5.36;
6  use List::Util qw(uniq);
7  say(<<~"FIN"), exit unless @ARGV;
8      Usage: \$0 N1 [N2...]
9      to find all arithmetic slices of the array N1 N2...
10      FIN
11  my @in=@ARGV;
12  say "The arithmetic slices of [ ", join " ", @in, "] are\n[";
13  my @results=
14      grep {
15  	my @m=@\$_;
16  	my @diff;
17  	\$diff[\$_]=\$m[\$_+1]-\$m[\$_] for 0..@m-2;
18  	uniq(@diff)==1
19      }
20      map {
21  	my \$start=\$_;
22  	map {[@in[\$start..\$_]]}\$start+2..@in-1
23      } 0..@in-3;
24  say "    [@\$_]" for @results;
25  say "]";
``````

Example:

``````./ch-1.pl  1 2 3 4
./ch-1.pl  2
./ch-1.pl  1 2 3 4 6 8
``````

Results:

``````The arithmetic slices of [ 1 2 3 4 ] are
[
[1 2 3]
[1 2 3 4]
[2 3 4]
]
The arithmetic slices of [ 2 ] are
[
]
The arithmetic slices of [ 1 2 3 4 6 8 ] are
[
[1 2 3]
[1 2 3 4]
[2 3 4]
[4 6 8]
]
``````

# Task 2: Seven Segment 200

``````Submitted by: Ryan J Thompson
A seven segment display is an electronic component, usually used to display digits.
The segments are labeled 'a' through 'g' as shown:

a
-------
|     |
f|  g  | b
-------
|     |
e|     | c
-------
d

The encoding of each digit can thus be represented compactly as a truth table:

my @truth = qw<abcdef bc abdeg abcdg bcfg acdfg acdefg abc abcdefg abcfg>;
For example, \$truth = ‘bc’. The digit 1 would have segments ‘b’ and ‘c’ enabled.

Write a program that accepts any decimal number and draws that number as a horizontal
sequence of ASCII seven segment displays, similar to the following:

-------  -------  -------
|  |     |  |     |
|  |     |  |     |
-------
|        |     |  |     |
|        |     |  |     |
-------  -------  -------
To qualify as a seven segment display, each segment must be drawn (or not drawn)

The number "200" was of course chosen to celebrate our 200th week!
``````

For simplicity I use numbers 0-6 instead of the letters a-g for the truth table. I make a binary mask to determine which positions in the 7×7=49 matrix of ascii characters should be turned on for each segment:

``````1111111000000000000000000000000000000000000000000
0000000000000100000010000000000000000000000000000
0000000000000000000000000000000000100000010000000
0000000000000000000000000000000000000000001111111
0000000000000000000000000000100000010000000000000
0000000100000010000000000000000000000000000000000
0000000000000000000001111111000000000000000000000
``````

which correspond in decimal to

``````558551906910208, 34628173824, 16512, 127, 1056768, 2216203124736, 266338304
``````

For each digit, I `or` the corresponding bits and map each to “ “, “-“ or “|” according to its value (0 or 1) and position (multiple of 3 or not). The code is the following:

`````` 1  # Perl weekly challenge 200
2  # Task 2:  Seven Segment 200
3  #
5  use v5.36;
6  say(<<~"FIN"), exit unless @ARGV;
7      Usage: \$0 N1 [N2...]
8      to print N1, N2... as 7 segments.
9      FIN
10  my @truth=qw(012345 12 01346 01236 1256 02356 023456 012 0123456 01256); #0-6 instead of a-g
11  # Binary masks for each segment
12  my @masks=(558551906910208, 34628173824, 16512, 127, 1056768, 2216203124736, 266338304);
13  for(@ARGV){ # for each number
14      my @lines=('')x7;
15      for(split '', \$_){ # for each digit
16          my \$code=0;
17          \$code|=\$masks[\$_] for split "", \$truth[\$_];
18          my @bin=split "", sprintf "%049b", \$code; # As binary array
19          my @bits=map {my \$x=\$_; [map{!\$_?" ":\$x%3?"|":"-"} @bin[7*\$_..7*\$_+6]]} (0..6);
20          \$lines[\$_].="  ". join "", @{\$bits[\$_]} for (0..6);
21      }
22      say \$_ for @lines;
23  }
``````

Example:

``````./ch-2.pl 200 9876543210
``````

Results:

``````-------  -------  -------
|  |     |  |     |
|  |     |  |     |
-------
|        |     |  |     |
|        |     |  |     |
-------  -------  -------
-------  -------  -------  -------  -------           -------  -------           -------
|     |  |     |        |  |        |        |     |        |        |        |  |     |
|     |  |     |        |  |        |        |     |        |        |        |  |     |
-------  -------           -------  -------  -------  -------  -------
|  |     |        |  |     |        |        |        |  |              |  |     |
|  |     |        |  |     |        |        |        |  |              |  |     |
-------           -------  -------           -------  -------           -------
``````

I can compress the code to an ugly three and a half liner:

``````perl -E '
@t=qw(012345 12 01346 01236 1256 02356 023456 012 0123456 01256);@s=(558551906910208,34628173824,
16512,127,1056768,2216203124736,266338304);@l=("")x7; for(split "", shift){my \$s; \$s|=\$s[\$_] for
split "",\$t[\$_]; @b=split "", sprintf "%049b", \$s; for my \$i(0..6){\$l[\$i].= "  ".join "",
map{!\$_?" ":\$i%3?"|":"-"} @b[7*\$i..7*\$i+6]}}say \$_ for @l;
' 9876543210
``````

Results:

``````-------  -------  -------  -------  -------           -------  -------           -------
|     |  |     |        |  |        |        |     |        |        |        |  |     |
|     |  |     |        |  |        |        |     |        |        |        |  |     |
-------  -------           -------  -------  -------  -------  -------
|  |     |        |  |     |        |        |        |  |              |  |     |
|  |     |        |  |     |        |        |        |  |              |  |     |
-------           -------  -------           -------  -------           -------
``````

If I use a bit map instead of a truth table the code can be further reduced slightly.

``````perl -E '
@d=(63,6,91,79,102,109,125,7,127,103);@s=(558551906910208,34628173824,16512,127,1056768,2216203124736,
266338304);my @l=("")x7;for(split "", shift){\$x=\$d[\$_];my \$s;\$x&(1<<\$_)&&(\$s|=\$s[\$_])for 0..6;
@b=split "", sprintf "%049b", \$s;for my \$i(0..6){\$l[\$i].= "  ". join "", map{!\$_?" ":\$i%3?"|":"-"}
@b[7*\$i..7*\$i+6]}}say \$_ for @l;
' 9876543210
``````

Results:

``````-------  -------  -------  -------  -------           -------  -------           -------
|     |  |     |        |  |        |        |     |        |        |        |  |     |
|     |  |     |        |  |        |        |     |        |        |        |  |     |
-------  -------           -------  -------  -------  -------  -------
|  |     |        |  |     |        |        |        |  |              |  |     |
|  |     |        |  |     |        |        |        |  |              |  |     |
-------           -------  -------           -------  -------           -------
``````

Actually, it is not necessary to build the binary codes for each digit repeatedly. I can do that once:

``````perl -E '
@d=(63,6,91,79,102,109,125,7,127,103);@s=(558551906910208,34628173824,16512,127,1056768,2216203124736,
266338304);my @l=("")x7;for(0..9){\$x=\$d[\$_];my \$s;\$x&(1<<\$_)&&(\$s|=\$s[\$_])for 0..6;
say sprintf "%049b %d", \$s, \$s;}
'

1111111100000110000010000000100000110000011111111  560802739282175
0000000000000100000010000000000000100000010000000  34628190336
1111111000000100000011111111100000010000001111111  558586802479231
1111111000000100000011111111000000100000011111111  558586801438975
0000000100000110000011111111000000100000010000000  2251097653376
1111111100000010000001111111000000100000011111111  560768376389887
1111111100000010000001111111100000110000011111111  560768377446655
1111111000000100000010000000000000100000010000000  558586535100544
1111111100000110000011111111100000110000011111111  560803005620479
1111111100000110000011111111000000100000010000000  560803004563584
``````

Then, I can incorporate the result in a simpler program:

``````perl -E '
@d=(560802739282175,34628190336,558586802479231,558586801438975,2251097653376,560768376389887,
560768377446655,558586535100544,560803005620479,560803004563584); my @l=("")x7;for(split "", shift)
{@b=split "", sprintf "%049b", \$d[\$_];for my \$i(0..6){\$l[\$i].= "  ". join "", map{!\$_?" ":\$i%3?"|":"-"}
@b[7*\$i..7*\$i+6]}}say \$_ for @l;
' 9876543210
``````

Results:

``````-------  -------  -------  -------  -------           -------  -------           -------
|     |  |     |        |  |        |        |     |        |        |        |  |     |
|     |  |     |        |  |        |        |     |        |        |        |  |     |
-------  -------           -------  -------  -------  -------  -------
|  |     |        |  |     |        |        |        |  |              |  |     |
|  |     |        |  |     |        |        |        |  |              |  |     |
-------           -------  -------           -------  -------           -------
``````

Nevertheless, it is still too long for a one-liner.

``````perl -E 'say \$_ for (0b111_101_000_101_111,
0b000_001_000_001_000,
0b111_001_111_100_111,
0b111_001_111_001_111,
0b000_101_111_001_000,
0b111_100_111_001_111,
0b000_100_111_101_111,
0b111_001_000_001_000,
0b111_101_111_101_111,
0b111_101_111_001_000)'
``````

I can shorten it to a reasonable 2+liner using a smaller 3x5 matrix instead of a 7x7 one. Shorter, but still doesn’t fit a tweet.

``````perl -E '@d=(31279,520,29671,29647,3016,31183,2543,29192,31727,31688);for(@ARGV){my @l=("")x5;
for(split ""){@b=split "", sprintf "%015b", \$d[\$_];for my \$i(0..5){\$l[\$i].= "  ". join "",
map{!\$_?" ":\$i%2?"|":"-"} @b[3*\$i..3*\$i+2]}}say \$_ for @l};' 200 9876543210
``````
Written on January 16, 2023