> 1
> Input
>> 1…2
>> L!
>> L+1
>> L∣2
>> L⋅R
>> 2%L
>> Each 4 3
>> Each 5 9
>> Each 6 10
>> Each 7 11 3
> {0}
>> 12∖13
>> Each 8 14
>> L≠1
>> Each 16 15
>> Each 7 17 15
>> 18∖13
>> [19]
>> 2’
>> 21⋅20
>> Output 22
Try it online!
This returns an empty list for non-Pillai primes, and a non-empty list otherwise.
How it works
Whispers was designed for manipulation on real/complex numbers, with a little bit of array commands added for good measure, hence the repeated use of Each
to iterate over the generated lists.
A bit of background on Whispers:
Whispers is slightly different in it's execution path to most other languages. Rather than work through each line linearly, only branching at conditionals, Whispers begins on the last line in the file beginning with >
(the rules are slightly more complicated than that, but that's all we need to know for now), and the meanings of numbers differ, depending on whether the line starts with >
or >>
.
If the line starts with >
, such as > 1
or > Input
, this is a constant line - it returns the same value each time. Here, numbers represent their numerical form, so the first line will always return 1 when called.
If the line starts with >>
however, numbers are treated as references to other lines, sort of like function calls, if you will. For example, in the line >> 1…2
, this does not perform the …
command on the integers 1 and 2, but rather on the values returned from lines 1 and 2. In this case, those values are the integer 1 and whatever integer we're passed as input.
For this example, let's consider the input of 23. Keep in mind that, due to Whispers' preprocessing, the second line (> Input
) is converted to > 23
.
Our first command is on line 3: >> 1…2
. …
is dyadic range, in this case from 1 to 23, yielding {1, 2, ... 22, 23}. Next, we skip down to lines 9 through 12:
>> Each 4 3
>> Each 5 9
>> Each 6 10
>> Each 7 11 3
Here we have 4 consectutive Each
statements, each of which iterate over the previous result, essentially mapping the 4 commands over the array on line 3: the range. The first three statements are simple maps, with lines 4, 5 and 6:
>> L!
>> L+1
>> L∣2
These three commands, over an integer n, yields (n!+1)∣x, where ! denotes factorial, ∣ denotes divisbility and x is the input. Finally, line 12 has a dyadic map structure.
A dyadic map structure takes three integers: the target, the left and the right, each indexes to other lines. Here, we zip the left and the right to produce a list of pairs, then reduce each pair by the dyadic command (the target). Here, if the input is 23, the lists are {1, 2, ... 22, 23} and {0, 0, ... 1, 0} and the command is
>> L⋅R
which multiplies the left argument by the right. This produces an array of integers, with 0 at the indexes of integers whose factorials incremented aren't divisible by the inputs, and the original index where they are. We'll call this array A. Next, we remove the 0s from A by taking the set difference between {0} and A:
> {0}
>> 12∖13
With our example input, this produces the set {14, 18, 22}. Next we take the remainder of the input being divided by each value in the set, and check if that remainder is not equal to 1:
>> 2%L
>> Each 8 14
>> L≠1
>> Each 16 15
Again, we have a list of either 0 or 1s and need to remove the 0s and replace the 1s with the original values. Here we repeat the code we saw above, but with >> 18∖13
rather than 12
. Finally, we cast this resulting set to a list for a final check. Unfortunately, our code must also reject composite numbers which achieve all these criteria, such as 437. So we add our final check, multiplying our final list by the primality of the input. Due to how Python multiplication works on lists, 0 replaces it with an empty list, and 1 has no effect. So we calculate the primality of the input, multiply that by the list of ms for the input and ouput the final result:
>> 2’
>> 21⋅20
>> Output 22
[(23, 14), (23, 18), (59, 15), (59, 40), (59, 43), (83, 13), (83, 36), (83, 69), (109, 86), (139, 16), (593, 274)]
. I've also added them to the challenge. \$\endgroup\$