# Trifid Cipher (without keyword)

## Introduction:

I have loads of different ciphers stored in a document I once compiled as a kid, I picked a few of the ones I thought were best suitable for challenges (not too trivial, and not too hard) and transformed them into challenges. Most of them are still in the sandbox, and I'm not sure yet whether I'll post all of them, or only a few. Here is the second one (the Computer Cipher was the first one I posted).

For the Trifid Cipher (without using a keyword) the alphabet (and an additional wildcard) is divided into three 3 by 3 tables:

table 1:     table 2:     table 3:
|1 2 3       |1 2 3       |1 2 3
-+-----      -+-----      -+-----
1|a b c      1|j k l      1|s t u
2|d e f      2|m n o      2|v w x
3|g h i      3|p q r      3|y z


A text we want to encipher is first character by character encoded into table-row-column numbers. For example, the text this is a trifid cipher becomes:

        t h i s   i s   a   t r i f i d   c i p h e r
table:  3 1 1 3 3 1 3 3 1 3 3 2 1 1 1 1 3 1 1 2 1 1 2
row:    1 3 3 1 3 3 1 3 1 3 1 3 3 2 3 2 3 1 3 3 3 2 3
column: 2 2 3 1 3 3 1 3 1 3 2 3 3 3 3 1 3 3 3 1 2 2 3


We then put everything after one another row by row in the table above in groups of three:

311 331 331 332 111 131 121 121 331 331 313 133 232 313 332 322 313 313 132 333 313 331 223


And those are transformed back to characters using the same tables:

s   y   y   z   a   g   d   d   y   y   u   i   q   u   z   w   u   u   h       u   y   o


One thing to note, the input-length should be coprime to 3. So if the length is a multiple of 3, we append one or two trailing spaces to make the input-length not a multiple 3 anymore.

## Challenge:

Given a string sentence_to_encipher, encipher it as described above.

You only have to encipher given the sentence_to_encipher, so no need to create a deciphering program/function as well. I might make a part 2 challenge for the deciphering in the future however (although I have the feeling it's to trivial/similar to the enciphering process).

## Challenge rules:

• You can assume the sentence_to_encipher will only contain letters and spaces.
• You can use either full lowercase or full uppercase (please state which one you've used in your answer).
• You can choose to append either one or two trailing spaces when the input-length is 3 to make it not a multiple of 3 anymore.
• I/O is flexible. Both input and output can be a string, list/array/stream of characters, etc.

## General rules:

• This is , so shortest answer in bytes wins.
Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
• Standard rules apply for your answer with default I/O rules, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters and return-type, full programs. Your call.
• Default Loopholes are forbidden.
• If possible, please add a link with a test for your code (i.e. TIO).
• Also, adding an explanation for your answer is highly recommended.

## Test cases:

Input:            "this is a trifid cipher"
Output:           "syyzagddyyuiquzwuuh uyo"

Input:            "test"
Output:           "utbk"

Input:            "output"
Possible outputs: "rrvgivx" (one space) or "rrzcc lr" (two spaces)

Input:            "trifidcipher"
Possible output:  "vabbuxlzz utr" (one space) or "vabbyzv rx ie " (two spaces)

• I read that as "without keyboard". That was going to be a delightful challenge! – Cort Ammon Dec 11 '18 at 19:36
• Why should the input's length be coprime to 3? I don't see how that matters here. – Nic Hartley Dec 11 '18 at 19:53
• The coprime requirement isn't necessary to make the cipher work, but it does make it very slightly more secure by ensuring the groups of three digits will not line up with the row divisions of the initial digit list. – Sparr Dec 11 '18 at 22:38
• @NicHartley You're indeed right that even if the input is divisible by 3 you can still transpose the table. I initially didn't had that rule in the Sandbox, but someone said to me the Wikipedia for Trifid shows it should add one or two spaces to make the enciphering slightly more secure. So I added it as part of the challenge, and to make it more in sync with the Wikipedia page (apart from the Keyword that I removed). – Kevin Cruijssen Dec 12 '18 at 8:05
• @KevinCruijssen I... really don't see how that'd make it more secure (if anything, forcing the input to be not of a certain length would make it less secure, as you could guess that the last character is encoding a space) but I do agree that keeping the cipher description here in line with Wikipedia is a good idea. Thanks for explaining! – Nic Hartley Dec 12 '18 at 22:58

# Jelly, 2926 25 bytes

⁶Øa;3ṗ¤,©Ṛy;⁶$L3ḍƊ¡ZFs3®y  Try it online! ### How it works ⁶Øa;3ṗ¤,©Ṛy;⁶$L3ḍƊ¡ZFs3®y  Main link. Argument: s (string)

⁶                          Set the return value to space.
Øa;                       Append it to "a...z".
3ṗ¤                    Yield the third Cartesian power of [1, 2, 3].
,©                  Pair the results and store the pair in the register.
The register now holds
[[[1, 1, 1], ..., [3, 3, 3]], ['a', ... ,'z', ' '].
Ṛ                 Reverse the outer array.
;⁶$Append a space to s... L3ḍƊ¡ if the length is divisible by 3. y Transliterate according to the mapping to the left. ZFs3 Zip/transpose, flatten, split into chunks of length 3. ®y Transliterate according to the mapping in the register.  • I never know the rules about side-effect outputs... but the arrangement ;L3ḍƊ¡⁶µ⁶Øa;3ṗ¤,ðṚyZFs3⁸y can do away with the µ if it's acceptable for 24. – Jonathan Allan Dec 11 '18 at 20:39 • We have a weak consensus (+6/-1) that this is allowed, so thanks! – Dennis Dec 11 '18 at 21:22 • The output appears to be incorrect. I don't think the appended space "sticks". – Dennis Dec 11 '18 at 21:29 # Charcoal, 39 bytes ≔Ｅ⁺θ× ¬﹪Ｌθ³⌕βιθ⭆⪪Ｅ⁺÷θ⁹⁺÷θ³θ﹪ι³¦³§⁺β ↨³ι  Try it online! Link is to verbose version of code. Explanation: ≔ Assign θ Input string ⁺ Concatenated with Literal space × Repeated θ Input string Ｌ Length ﹪ Modulo ³ Literal 3 ¬ Logical not Ｅ Mapped over characters ι Current character ⌕ Position found in β Lowercase alphabet θ To variable θ List of positions ÷ Vectorised integer divide by ⁹ Literal 9 ⁺ Concatenated with θ List of positions ÷ Vectorised integer divide by ³ Literal 3 ⁺ Concatenated with θ List of positions Ｅ Map over values ι Current value ﹪ Modulo ³ Literal 3 ⪪ Split into ³ Groups of 3 ⭆ Map over groups and join β Lowercase alphabet ⁺ Concatenated with Literal space § Cyclically indexed by ι Current group ↨ Converted from ³ Base 3 Implicitly print  # Python 2, 180176174165 163 bytes lambda s:''.join(chr(32+(33+a*9+3*b+c)%59)for a,b,c in zip(*[iter(sum(zip(*[(c/9,c/3%3,c%3)for c in map(o,s+' '[len(s)%3:])]),()))]*3)) o=lambda c:(ord(c)%32-1)%27  Try it online! Input can be upper or lower. Output is uppercase # Pyth, 34 33 bytes m@+G;id3csCmtj+27x+G;d3+W!%lz3zd3  Full program. Input is expected as lowercase, output is a character array. Try it online here, or verify all test cases at once here. m@+G;id3csCmtj+27x+G;d3+W!%lz3zd3 Implicit: z=input(), d=" ", G=lowercase alphabet lz Length of z % 3 The above, mod 3 W! If the above != 3... + zd ... append a space to z m Map the elements of the above, as d, using: +G; Append a space to the lowercase alphabet x d Find the 0-based index of d in the above +27 Add 27 to the above j 3 Convert to base 3 t Discard first element (undoes the +27, ensures result is 3 digits long) C Transpose the result of the map s Flatten c 3 Split into chunks of length 3 m Map the elements of the above, as d, using: id3 Convert to decimal from base 3 @+G; Index the above number into the alphabet + space Implicit print  Alternative 34 byte solution: sm@+G;id3csCm.[03jx+G;d3+W!%lz3zd3 - rather than +27 and tail, uses .[03 to pad with 0 to length 3. Can be 33 if the leading s is dropped. Edit: saved a byte by dropping leading s as character arrays are valid output # Ruby, 153145138 131 bytes ->a{a<<" "if a.size%3<1;a.map{|c|[(b=(c.ord%32-1)%27)/9,b%9/3,b%3]}.transpose.join.scan(/.{3}/).map{|x|((x.to_i(3)+65)%91+32).chr}}  Try it online! A quick and naive approach, works with lowercase text. Inputs and outputs arrays of characters. # Java (JDK), 192 bytes s->{String T="",R=T,C=T,r=T;for(int c:s){c-=c<33?6:97;T+=c/9;R+=c%9/3;C+=c%3;}for(var S:(s.length%3<1?T+2+R+2+C+2:T+R+C).split("(?<=\\G...)"))r+=(char)((Byte.valueOf(S,3)+65)%91+32);return r;}  Try it online! Very naive approach. Takes a lowercase char[] as input but outputs a String. ## Explanations s->{ // char[]-accepting lambda String T="", // declare variables Table as an empty string, R=T, // Row as an empty string, C=T, // Column as an empty string, r=T; // result as an empty string. for(int c:s){ // for each character c-=c<33?6:97; // map each letter to a number from 0 to 25, space to 26. T+=c/9; // append the table-value to Table R+=c%9/3; // append the row-value to Row C+=c%3; // append the column-value to Column } // for(var S: // For each token of... (s.length%3<1?T+2+R+2+C+2:T+R+C) // a single string out of table, row and column and take the space into account if the length is not coprime to 3... .split("(?<=\\G...)")) // split every 3 characters r+=(char)((Byte.valueOf(S,3)+65)%91+32); // Parses each 3-characters token into a number, using base 3, // and make it a letter or a space return r; // return the result }  ## Credits • Two small golfs: Integer.valueOf to Byte.valueOf and R+=c<26?(char)(c+97):' '; to R+=(char)(c<26?c+97:32); – Kevin Cruijssen Dec 11 '18 at 12:59 • 202 bytes – Kevin Cruijssen Dec 11 '18 at 13:49 # R, 145 bytes function(s,K=array(c(97:122,32),rep(3,3)))intToUtf8(K[matrix(arrayInd(match(c(utf8ToInt(s),32[!nchar(s)%%3]),K),dim(K))[,3:1],,3,byrow=T)[,3:1]])  Try it online! I/O as strings; adds one space. The strange repetition of [,3:1] is because R's natural array indexing is somewhat different. • Dang, you beat me by over 200 bytes. I'm always impressed by your coding, @Giuseppe. I shouldn't even bother trying sometimes – Sumner18 Dec 11 '18 at 21:52 • @Sumner18 well thanks! I usually try to let a day or two pass before answering challenges since I know there are plenty of other R golfers here now, but I couldn't resist this one since I saw it in the Sandbox. You're always welcome to bounce ideas for golfing off us in the R golfing chatroom. :-) – Giuseppe Dec 11 '18 at 22:00 • @Sumner18 also there's no shame in trying and coming up short, my first submission here was horrible and I've only gotten better! Please continue to post, I think it's always good to get feedback so you can improve :-) – Giuseppe Dec 11 '18 at 22:13 # APL+WIN, 102 bytes ⎕av[n[c⍳(⊂[2]((⍴t),3)⍴,⍉⊃(c←⊂[2]c,(,⍉9 3⍴c←9/⍳3),[1.1]27⍴⍳3)[(⎕av[n←(97+⍳26),33])⍳t←t,(3|⍴t←⎕)↓' '])]]  Explanation: t←t,(3|⍴t←⎕)↓' ' Prompts for input and applies coprime condition (⎕av[n←(97+⍳26),33]⍳ Indices of characters in APL atomic vector c←⊂[2]c,(,⍉9 3⍴c←9/⍳3),[1.1]27⍴⍳3) Create a matrix of table, row column for 27 characters ⊂[2]((⍴t),3)⍴,⍉⊃ Extract columns of c corresponding to input and re-order c⍳ Identify Column indices of re-ordered columns ⎕av[.....] Use indices back in atomic vector to give enciphered text  Example of screen shot of test case: ⎕av[n[c⍳(⊂[2]((⍴t),3)⍴,⍉⊃(c←⊂[2]c,(,⍉9 3⍴c←9/⍳3),[1.1]27⍴⍳3)[(⎕av[n←(97+⍳26),33])⍳t←t,(3|⍴t←⎕)↓' '])]] ⎕: 'output' rrvgivx  • Would you mind adding a screenshot of (one or multiple of) the test cases? I know the WIN APL version isn't available on TIO, but I'd still like to see some kind of verification, since I barely know how to interpret APL code by just reading it, let alone verifying it without running. :) – Kevin Cruijssen Dec 11 '18 at 14:05 • Not quite sure how to do that but this is what it would look like. I will add something to entry above – Graham Dec 11 '18 at 15:03 • Normally I can use Dyalog Classic in TIO but in this case its atomic vector is in a different order so the indexing will not work. – Graham Dec 11 '18 at 15:13 SAS, 305 bytes A hearty 'oof' for this SAS monstrosity. There's a lot of random string formatting that I thought I could avoid going into this; I'm sure there are better ways of doing some of this. data;input n:&$99.;n=tranwrd(trim(n)," ","{");if mod(length(n),3)=0then n=cats(n,'{');f=n;l=length(n);array a(999);do i=1to l;v=rank(substr(n,i,1))-97;a{i}=int(v/9);a{i+l}=mod(int(v/3),3);a{i+l*2}=mod(v,3);end;f='';do i=1to l*3by 3;f=cats(f,byte(a{i}*9+a{i+1}*3+a{i+2}+97));end;f=tranwrd(f,"{"," ");cards;


Input is entered on newlines after the cards statement, like so:

data;input n:&$99.;n=tranwrd(trim(n)," ","{");if mod(length(n),3)=0then n=cats(n,'{');f=n;l=length(n);array a(999);do i=1to l;v=rank(substr(n,i,1))-97;a{i}=int(v/9);a{i+l}=mod(int(v/3),3);a{i+l*2}=mod(v,3);end;f='';do i=1to l*3by 3;f=cats(f,byte(a{i}*9+a{i+1}*3+a{i+2}+97));end;f=tranwrd(f,"{"," ");cards; this is a trifid cipher test output trifidcipher  Outputs a dataset containing the output in the variable f, along with a bunch of helper variables/array values. Ungolfed/explanation: data; input n : &$99.; /* Read a line of input, maximum 99 characters */

n=tranwrd(trim(n)," ","{"); /* Replace spaces with '{' (this is the ASCII character following 'z', so it makes it easy to do byte conversions, and lets us not have to deal with spaces, which SAS does not like) */
if mod(length(n),3)=0then n=cats(n,'{'); /* If length of n is not coprime with 3, add an extra "space" to the end */

f=n; /* Set output = input, so that the string will have the same length */
l=length(n);    /* Get the length of the input */
array a(999);   /* Array of values to store intermediate results */

do i = 1 to l; /* For each character in the input... */
v = rank(substr(n,i,1))-97; /* Get the value of the current character, from 0-26 */

a{i}=int(v/9);          /* Get the table of the current character and store at appropriate index, from 0-2  */
a{i+l}=mod(int(v/3),3); /* Get the row of the current character, from 0-2 */
a{i+l*2}=mod(v,3);      /* Get the column of the current character, from 0-2  */
end;

f='';

do i = 1 to l*3 by 3; /* For each character in the output... */
f=cats(f,byte(a{i}*9+a{i+1}*3+a{i+2}+97)); /* Convert values back from base 3 to base 10, and convert back into ASCII value */
end;

f = tranwrd(f,"{"," "); /* Replaces our "spaces" with actual spaces for final output */

/* Test cases */
cards;
this is a trifid cipher
test
output
trifidcipher


# JavaScript (Node.js),  146 141 139  136 bytes

I/O is in lowercase.

s=>'931'.replace(/./g,d=>Buffer(s.length%3?s:s+0).map(c=>(o=(c>48?c-16:26)/d%3+o*3%27|0,++i)%3?0:(o+97)%123||32),i=o=0).split\0.join


Try it online!

### Commented

s =>                       // s = input string
'931'.replace(/./g, d => // for each digit d = 9, 3 and 1:
Buffer(                //   create a buffer from:
s.length % 3 ?       //     if the length of s is coprime with 3:
s                  //       the original input string
:                    //     else:
s + 0              //       the input string + an extra '0'
)                      //
.map(c =>              //   for each ASCII code c from this Buffer:
( o =                //     update o:
( c > 48 ?         //       if c is neither a space nor the extra '0':
c - 16         //         yield c - 16 (which gives 81 .. 106)
:                //       else:
26             //         this is the 26th character (space)
) / d % 3 +        //       divide by d and apply modulo 3
o * 3 % 27 | 0,    //       add o * 3, apply modulo 27, coerce to integer
++i                //       increment i
) % 3 ?              //     if i mod 3 is not equal to 0:
0                  //       yield 0 (NUL character)
:                    //     else:
(o + 97) % 123     //       convert o to the ASCII code of the output letter
|| 32              //       or force 32 (space) for the 26th character
),                     //   end of map()
i = o = 0              //   start with i = o = 0
).split\0.join       // end of replace(); remove the NUL characters

• I think you've explained it once before, but how does (o=...,++i)%3 work in JS again? Is (o,i) a tuple or something, and both inner integers are converted to their modulo-3? As a Java developer, it still confuses me a bit to see (a,b)%c. Nice answer though! I like how you convert every third digit, and then remove the first two null-bytes. +1 from me. – Kevin Cruijssen Dec 11 '18 at 14:24
• @KevinCruijssen Quoting MDN: "The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand." So, the modulo is only applied to ++i. – Arnauld Dec 11 '18 at 14:31

# 05AB1E, 25 bytes

g3Öð×«SAð«3L3ã©‡ø˜3ô®Að«‡


Since no one posted a 05AB1E answer yet, I figured I'd post my own solution. I see now it's very similar to @Dennis♦' Jelly answer, even though I came up with it independently before I posted the challenge.

Input as string, output as a list of characters. Adds one space if the length is divisible by 3.

Explanation:

g3Ö         # Check if the length of the (implicit) input is divisible by 3
# (results in 1 for truthy or 0 for falsey)
#  i.e. "out" → 1
#  i.e. "test" → 0
ð×       # Repeat a space that many times
#  i.e. 1 → " "
#  i.e. 0 → ""
«      # And append it to the (implicit) input
#  i.e. "out" and " " → "out "
#  i.e. "test" and "" → "test"
S     # Then make the string a list of characters
#  i.e. "out " → ["o","u","t"," "]
#  i.e. "test" → ["t","e","s","t"]
A           # Push the lowercase alphabet
ð«         # Appended with a space ("abcdefghijklmnopqrstuvwxyz ")
3L       # Push list [1,2,3]
3ã     # Cartesian repeated 3 times: [[1,1,1],[1,1,2],...,[3,3,2],[3,3,3]]
©    # Save that list of triplets in the registry (without popping)
‡   # Transliterate, mapping the letters or space to the triplet at the same index
#  i.e. ["o","u","t"," "] → [[2,2,3],[3,1,3],[3,1,2],[3,3,3]]
#  i.e. ["t","e","s","t"] → [[3,1,2],[1,2,2],[3,1,1],[3,1,2]]
ø           # Zip, swapping all rows/columns
#  i.e. [[2,2,3],[3,1,3],[3,1,2],[3,3,3]] → [[2,3,3,3],[2,1,1,3],[3,3,2,3]]
#  i.e. [[3,1,2],[1,2,2],[3,1,1],[3,1,2]] → [[3,1,3,3],[1,2,1,1],[2,2,1,2]]
˜          # Flatten the list
#  i.e. [[2,3,3,3],[2,1,1,3],[3,3,2,3]] → [2,3,3,3,2,1,1,3,3,3,2,3]
#  i.e. [[3,1,3,3],[1,2,1,1],[2,2,1,2]] → [3,1,3,3,1,2,1,1,2,2,1,2]
3ô        # Split it into parts of size 3
#  i.e. [2,3,3,3,2,1,1,3,3,3,2,3] → [[2,3,3],[3,2,1],[1,3,3],[3,2,3]]
#  i.e. [3,1,3,3,1,2,1,1,2,2,1,2] → [[3,1,3],[3,1,2],[1,1,2],[2,1,2]]
®           # Push the triplets from the registry again
Að«        # Push the lowercase alphabet appended with a space again
‡       # Transliterate again, mapping the triplets back to letters (or a space)
# (and output the result implicitly)
#  i.e. [[2,3,3],[3,2,1],[1,3,3],[3,2,3]] → ["r","v","i","x"]
#  i.e. [[3,1,3],[3,1,2],[1,1,2],[2,1,2]] → ["u","t","b","k"]


# Japt, 42 bytes

;Êv3 ?UpS:U
m!bS=iC)®+27 ì3 ÅÃÕc ò3 £SgXì3


Try it online!

The core of this answer comes from a deleted answer by Shaggy, but he never came back to handle inputs of length divisible by 3 so this is a fixed version.

Explanation:

;                                 #Set C to the string "abcdefghijklmnopqrstuvwxyz"

Ê                                #Get the length of the input
v3 ?                            #If it is divisible by 3:
UpS                         # Add a space
:U                       #Otherwise don't add a space
#Store the result in U

S=iC)                          #Set S to C plus a space
m                                 #For each character in U:
!bS                              # Get the position of that character in S
®        Ã                #For each resulting index:
ì3                   # Convert to base 3
+27    Å                 # Including leading 0s up to 3 places
Õ               #Transpose rows and columns
c              #Flatten
ò3           #Cut into segments of length 3
£         #For each segment:
Xì3    # Read it as a base 3 number
Sg       # Get the letter from S with that index


# C# (Visual C# Interactive Compiler), 178 bytes

s=>{int[]a={9,3,1},b=(s.Length%3>0?s:s+0).Select(c=>c<97?26:c-97).ToArray();s="";for(int i=0,k,l=b.Length;i<l*3;s+=(char)(k>25?32:97+k))k=a.Sum(p=>b[i%l]/a[i++/l]%3*p);return s;}


Try it online!

Less golfed... It's still confusing :)

// s is an input string
s=>{
// powers of 3
int[]a={9,3,1},
// ensure the length of s is coprime to 3
// and convert to numbers from 0-26
b=(s.Length%3>0?s:s+0).Select(c=>c<97?26:c-97).ToArray();
// reset s to collect result
s="";
// main loop
for(
// i is the main index variable
// k is the value of the encoded character
// l is the length
int i=0,k,l=b.Length;
// i continues until it is 3x the length of the string
i<l*3;
// convert k to a character and append
s+=(char)(k>25?32:97+k)
)
// compute the trifid
// (this is the confusing part :)
k=a.Sum(p=>b[i%l]/a[i++/l]%3*p);
// return the result
return s;
}