_8
,%
;
"}{{+_5
"= %_!
= """{
;"{" )!
Terminates with a division-by-zero error (error message on STDERR).
Try it online!
The layout feels really inefficient but I'm just not seeing a way to golf it right now.
Explanation
This solution is based on Dennis's arithmetic trick: take all character codes modulo 8
, add a pair from both ends and make sure it's divisible by 5
.
Labyrinth primer:
- Labyrinth has two stacks of arbitrary-precision integers, main and aux(iliary), which are initially filled with an (implicit) infinite amount of zeros.
- The source code resembles a maze, where the instruction pointer (IP) follows corridors when it can (even around corners). The code starts at the first valid character in reading order, i.e. in the top left corner in this case. When the IP comes to any form of junction (i.e. several adjacent cells in addition to the one it came from), it will pick a direction based on the top of the main stack. The basic rules are: turn left when negative, keep going ahead when zero, turn right when positive. And when one of these is not possible because there's a wall, then the IP will take the opposite direction. The IP also turns around when hitting dead ends.
- Digits are processed by multiplying the top of the main stack by 10 and then adding the digit.
The code starts with a small 2x2, clockwise loop, which reads all input modulo 8:
_ Push a 0.
8 Turn into 8.
% Modulo. The last three commands do nothing on the first iteration
and will take the last character code modulo 8 on further iterations.
, Read a character from STDIN or -1 at EOF. At EOF we will leave loop.
Now ;
discards the -1
. We enter another clockwise loop which moves the top of the main stack (i.e. the last character) down to the bottom:
" No-op, does nothing.
} Move top of the stack over to aux. If it was at the bottom of the stack
this will expose a zero underneath and we leave the loop.
= Swap top of main with top of aux. The effect of the last two commands
together is to move the second-to-top stack element from main to aux.
" No-op.
Now there's a short linear bit:
{{ Pull two characters from aux to main, i.e. the first and last (remaining)
characters of the input (mod 8).
+ Add them.
_5 Push 5.
% Modulo.
The IP is now at a junction which acts as a branch to test divisibility by 5. If the result of the modulo is non-zero, we know that the input is not a Watson-Crick palindrome and we turn east:
_ Push 0.
! Print it. The IP hits a dead end and turns around.
_ Push 0.
% Try to take modulo, but division by zero fails and the program terminates.
Otherwise, we need to keep checking the rest of the input, so the IP keeps going south. The {
pulls over the bottom of the remaining input. If we've exhausted the input, then this will be a 0
(from the bottom of aux), and the IP continues moving south:
) Increment 0 to 1.
! Print it. The IP hits a dead end and turns around.
) Increment 0 to 1.
{ Pull a zero over from aux, IP keeps moving north.
% Try to take modulo, but division by zero fails and the program terminates.
Otherwise, there are more characters in the string to be checked. The IP turns west and moves into the next (clockwise) 2x2 loop which consists largely of no-ops:
" No-op.
" No-op.
{ Pull one value over from aux. If it's the bottom of aux, this will be
zero and the IP will leave the loop eastward.
" No-op.
After this loop, we've got the input on the main stack again, except for its first and last character and with a zero on top. The ;
discards the 0
and then =
swaps the tops of the stacks, but this is just to cancel the first =
in the loop, because we're now entering the loop in a different location. Rinse and repeat.