_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.
Related. – Martin Ender – 2016-04-25T06:39:00.273
3
Someone should write a program in DNA# that is also a Watson-Crick palindrome. :D (might not be possible)
– mbomb007 – 2016-04-25T16:49:43.930Or, if you like, "a word is a Watson–Crick palindrome if it has order 2 in the free group on 2 generators" (or on n generators!). – wchargin – 2016-04-26T03:19:02.433
(I guess technically that's "order at most 2.") – wchargin – 2016-04-26T03:19:17.400
Do the true and false values need to be "consistent"? I.e., only one true output and only one false output. (As opposed to e.g. returning the input string for true, and
nil
for false.) – Mitch Schwartz – 2016-04-26T11:10:40.083Extending it to truthy and falsey values might lead to answers that return
[1, 2, 3]
as true and[]
as false but would lead to many different possible outputs. It would be better to be consistent, always returningtrue
when true andnil
when false, or some other arrangement. – miles – 2016-04-26T11:17:59.537Is the name "Watson--Crick palindrome" a thing? It should really be "Watson--Crick--Franklin palindrome" :P
– Andras Deak – 2016-04-27T13:20:21.520Possible Handy Hint: Odd length strings cannot be W-C Palindromes. The middle character will always transpose to something different. – Obsidian Phoenix – 2016-04-27T15:13:03.203
1@AndrasDeak According to Watsons book, Franklin was apparently mostly a thorn in their side. She repeatedly refused to hand over x-rays showing the helix (as I recall), because she refused to believe it. Its worth a read if you are interested in the discovery at any rate. – Obsidian Phoenix – 2016-04-27T15:19:00.507
@ObsidianPhoenix funny, I've even read the book (to be fair, at the age of 12), and didn't remember this aspect, only that without the diffraction data they might not have figured out the structure. I guess I do have to re-read it, thanks:) – Andras Deak – 2016-04-27T15:30:18.600
@AndrasDeak I was the same, at school and not since. My recollection could be skewed, but I seem to recall that was the case. I enjoyed it, should probably read it again some day. – Obsidian Phoenix – 2016-04-27T15:32:04.220
@mbomb007 On it. – Khuldraeseth na'Barya – 2017-12-12T19:10:49.103