# Write a VIC cipher encoder

The VIC cipher is one of the most complicated pencil and paper ciphers ever devised. Used in the 1950s by Soviet spy Reino Häyhänen, codenamed "VICTOR", its main principle is security through obfuscation; a lot of obfuscation.

Your task is to write a program or function that will take a message and encode it using the VIC cipher. I have also posted a VIC cipher decoder challenge here. If any of the following instructions are unclear, don't hesitate to ask about them in the comments. The instructions are adapted from this site.

# Encoding the VIC cipher

Preparation

You will need five inputs:

• the plaintext message
• a short keyword or phrase containing the most common letters in your language
• a key phrase, such as a quote or a line from a song (at least 20 characters)
• a date (or another number that is six digits or more)
• a personal agent number

In practice, these last four should be agreed upon beforehand by the sender and the recipient, including whether the agent number of the sender or the recipient is used in encoding.

My example message will be: We are discovered. Take what you can. Burn everything else. Move to Safehouse Foxtrot 3.

We'll be encoding in English (though you may use whatever language and alphabet you prefer) and the most common letters in the English alphabet are A, E, I, N, O, R, S, T. I'll use the keyword SENATORI.

My key phrase is a quote by Richard Feynman: "The first principle is that you must not fool yourself — and you are the easiest person to fool."

As a date, I'll use July 31, 2016 (in the format 3172016), which is the day it was when I wrote this description.

The personal number I have chosen for myself is 9.

Summary of steps

1. Derive the intermediate keys for use in the following steps.
2. Construct and apply the straddling checkerboard.
3. Construct and apply the first transposition table.
4. Construct and apply the second (disrupted) transposition table.
5. Finalize the message by inserting the message indicator group.

Submechanisms

Two more things to explain before we get into the meat of the matter: the processes of chain addition and sequentializing.

Chain addition, also known as a lagged Fibonacci generator, works by taking a starting digit sequence, adding the first two digits without carrying (add them together then mod 10) and appending the result to the end. For example:

79081
7 + 9 = 6

790816
9 + 0 = 9

7908169
0 + 8 = 8

79081698
8 + 1 = 9

790816989
1 + 6 = 7

7908169897
... and so on


Sequentializing is essentially taking a sequence of letters or digits and labeling them by their alphabetical/numerical order. Duplicates are labelled left to right. For example:

E X A M P L E
0           # A
1   0       2   # Es
1   0     3 2   # L
1   0 4   3 2   # M
1   0 4 5 3 2   # P
1 6 0 4 5 3 2   # X

3  3  0  5  8  4  2  0  4  7  5  4  8  1
0              1                     # 0s
0              1                 2   # 1
0           3  1                 2   # 2
4  5  0           3  1                 2   # 3s
4  5  0        6  3  1  7        8     2   # 4s
4  5  0  9     6  3  1  7    10  8     2   # 5s
4  5  0  9     6  3  1  7 11 10  8     2   # 7
4  5  0  9 12  6  3  1  7 11 10  8 13  2   # 8s


I use zero-indexing here, but index however you like.

1. Intermediate Keys

Split the first 20 letters of the key phrase into two groups of 10 and sequentialize each individually, which we will call S1 and S2.

    THEFIRSTPR
S1: 8201357946

INCIPLEIST
S2: 2603751489


Choose a random 5-digit message identifier, M (this can be one of the inputs if you prefer):

M = 47921


Subtract, without borrowing (subtract mod 10), the first five digits of the key date 3172016 from M:

M      47921
date - 31720
= 16201


Chain add the result until you have ten digits:

1620178218


Add these digits to S1, without carrying or mod 10, to obtain G:

     1620178218
S1 + 8201357946
G  = 9821425154


Above S2, write the sequence 0123456789. Locate each digit of G in the sequence 0123456789 and replace it with the digit directly below it in S2. The result is T.

   0123456789
S2 2603751489

G  9821425154
T  9806705657


Use chain addition to expand T to 60 digits.

9806705657

becomes

980670565778637511245490262369939288595822106344304316978734


These last 50 digits, in five rows of ten digits each, form the U block.

T  9806705657
U  7863751124
5490262369
9392885958
2210634430
4316978734


The last two non-equal digits of the U block are individually added to the agent's personal number to give the widths of the two transpositions, p and q.

9 + 3 = 12 (p, first transposition width) 9 + 4 = 13 (q, second transposition width)

Sequentialize T and use this sequence to copy off the columns of the U block, from top to bottom, into a new row of digits, V.

T     9806705657
seqT  9804612537

U     7863751124
5490262369
9392885958
2210634430
4316978734

V     69911 56837 12548 26533 30206 13947 72869 49804 84323 75924


Sequentialize the first p digits to get the key for the first transposition K1, and the following q digits for the key for the second K2.

First 12  6  9  9  1  1  5  6  8  3  7  1  2
K1        6 10 11  0  1  5  7  9  4  8  2  3

Next 13   5  4  8  2  6  5  3  3  3  0  2  0  6
K2        8  7 12  2 10  9  4  5  6  0  3  1 11


Finally, sequentialize the final row of the U block to get C, the column headers for the straddling checkerboard:

U5  4316978734
C   3105968724


First, I will give my example checkerboard then explain the principles in creating it in that way:

  3 1 0 5 9 6 8 7 2 4
S E N A T O R I
2 B D G J L P U W Y .
4 C F H K M Q V X Z #


The first line of letters is our short keyword SENATORI. Your keyword can be any string without duplicates, but since it defines the top row of your checkerboard, choose wisely. Above the keyword is C, and the other rows are the rest of your alphabet in whatever order you choose. In my case, I filled the checkerboard with the rest of the Latin alphabet, a punctuation mark . and a mark for demarcating numbers #. Essentially, the checkerboard is a fancy substitution cipher. For example, "E" will be substituted with 1, and "W" will be substituted with 27.

Once we have encoded our plaintext message with this checkerboard, but first, we need to make the beginning of our message less obvious by splitting it at a random position, and making it all uppercase. To denote the other original beginning, we use two full stops ..

We are discovered. Take what you can. Burn everything else. Move to Safehouse Foxtrot 3.


becomes

HING ELSE. MOVE TO SAFEHOUSE FOXTROT#3#.. WE ARE
DISCOVERED. TAKE WHAT YOU CAN. BURN EVERYT


We encode with the checkerboard, giving us:

407020 1293124 496481 96 354114062831 416479869443442424 271 581
2173436481812124 95451 274059 22628 435024 232880 14818229


If the length of the message isn't divisible by 5, we add some null characters to pad out the message. Our message is 109 digits long, so I will add one null: "4".

40702 01293 12449 64819 63541 14062 83141 64798 69443 44242 42715
81217 34364 81812 12495 45127 40592 26284 35024 23288 01481 82294


Note: Since my example message does not contain numbers, I'll say here that you might designate, say, as #3#, which is encoded as 44344 here.

3. First Transposition

Create the transposition table by writing K1 (from the Intermediate Keys section) followed by the encoded message from the previous step, in rows of the same length, below the key:

K1   6 10 11  0  1  5  7  9  4  8  2  3

4  0  7  0  2  0  1  2  9  3  1  2
4  4  9  6  4  8  1  9  6  3  5  4
1  1  4  0  6  2  8  3  1  4  1  6
4  7  9  8  6  9  4  4  3  4  4  2
4  2  4  2  7  1  5  8  1  2  1  7
3  4  3  6  4  8  1  8  1  2  1  2
4  9  5  4  5  1  2  7  4  0  5  9
2  2  6  2  8  4  3  5  0  2  4  2
3  2  8  8  0  1  4  8  1  8  2  2
9  4


Taking the numbered columns in order of their numbers we get:

060826428  246674580  151411542  246272922  961311401  082918141
4414434239 118451234  334422028  293488758  0417249224 794943568


4. Second Transposition

The first transposition was relatively simple. This one, however, is a disrupted transposition. The disruption pattern is determined by the width of the table and the key. In our example, we have 110 digits and 13 columns, meaning we'll have 8 full rows and 6 leftovers. We start filling in the first row, but stop at column 0, and continue as follows:

K2   8  7 12  2 10  9  4  5  6  0  3  1 11

0  6  0  8  2  6  4  2  8              stop at 0
2  4  6  6  7  4  5  8  0  1           continue in a triangle pattern
5  1  4  1  1  5  4  2  2  4  6
2  7  2  9  2  2  9  6  1  3  1  1
4  0  1  0  8  2  9  1  8  1  4  1  4  until the end
4  1  4  4  3  4  2  3  9  1  1        restart and stop at 1
8  4  5  1  2  3  4  3  3  4  4  2
2  0  2  8  2  9  3  4  8  8  7  5  8
0  4  1                                restart and stop at 2


Then we fill the last few spaces with the remaining digits.

K2   8  7 12  2 10  9  4  5  6  0  3  1 11

0  6  0  8  2  6  4  2  8  7  2  4  9
2  4  6  6  7  4  5  8  0  1  2  2  4
5  1  4  1  1  5  4  2  2  4  6  7  9
2  7  2  9  2  2  9  6  1  3  1  1  4
4  0  1  0  8  2  9  1  8  1  4  1  4
4  1  4  4  3  4  2  3  9  1  1  9  4
8  4  5  1  2  3  4  3  3  4  4  2  3
2  0  2  8  2  9  3  4  8  8  7  5  8
0  4  1  5  6  8


Now, we read off the columns in the exactly the same way we did for in the first transposition.

71431148  42711925  861904185 22614147  45499243  28261334  80218938
641701404 025244820 645224398 271283226 94944438  064214521


And split everything up into 5-digit groups:

71431 14842 71192 58619 04185 22614 14745 49924 32826 13348 02189
38641 70140 40252 44820 64522 43982 71283 22694 94443 80642 14521


5. Finalize the Message

The final step is to insert our random message identifier, 47921, into the message itself. The final digit of the key date 6 indicates the distance the group should be from the end.

71431 14842 71192 58619 04185 22614 14745 49924 32826 13348 02189 38641
70140 40252 44820 64522 43982 47921 71283 22694 94443 80642 14521


Notes for this challenge

• You are given a minimum of five inputs: the message, the letter keyword, the key phrase, the date, and a personal number. You may include two additional inputs: the random message identifier and the nulls needed to pad out the message, or your function may generate some random numbers on its own.
• You may assume all inputs are valid, with the correct number of digits and letters (5-digit message identifier, at least 20 digits for the key phrase, and so on). You may assume that your strings (the message and keywords) have already had all punctuation and spaces removed except for those that you allow in your version, and that numbers are already demarcated with number signs.
• The first keyword should not have duplicate letters in it, and in your code, you may assume that it never has duplicate letters.
• The language you use to encode in does not matter, as long as the language is preexisting, the alphabet is preexisting, and you specify which language you use in your answer.
• Whichever alphabet you employ for your straddling checkerboard, you may add or remove symbols to pad the checkerboard out. Specify what you use those symbols for (for example, punctuation, a separate "message begin" symbol, symbols for common words). You may forego the number sign entirely and spell out the numbers or include each digit in the checkerboard, using the slot where the number sign was for something else. Please specify which checkerboard you used in your answer.
• The output should be either a string of space-separated five-digit groups, a list of five-digit integers, or something similar.
• I used zero-indexing and 0123456789 in my example. You may use 1-indexing and 1234567890, or some other system in your answer, as long as you specify what you used.

Here is an example implementation on Ideone.

This is a long post and I wrote most of it by hand, so if there are any confusing parts in this post or errors in my counting and transposing, please let me know. Good luck and good golfing!

• adding the first two digits without adding Do you mean carrying? – isaacg Aug 8 '16 at 6:09
• @isaacg Yes, I did. Edited. – Sherlock9 Aug 8 '16 at 6:57
• Could you clarify on what you mean by without borrowing and without carrying? Do you mean add and subtract mod 10, i.e. (6+7) mod 10 = 3 and (6-8) mod 10 = 8? – R. Kap Aug 9 '16 at 4:21
• @R.Kap Yeah, let me go clarify that. – Sherlock9 Aug 9 '16 at 4:54
• Do we have to demarcate numbers? – R. Kap Aug 11 '16 at 3:04

# Python 3, 1423134813241316130012861250124912091206 1204 bytes

This is definitely the longest golf I've ever done, and the only golf where I was seriously worried about running out of one-character variable names. Golfing suggestions welcome. Try it online!

I'm encoding with the uppercase Latin alphabet with additional characters . and #, using 0-indexing and 0123456789 when converting g to t. My checkerboard is in a format similar to the following example:

  2 9 7 4 5 8 3 1 0 6    # C
S E N A T O R I        # keyword
0 B D G J L P U W Y .    # remaining alphabet arranged in columns
6 C F H K M Q V X Z #    # . and # at the end


Edit: -63 bytes thanks to TuukkaX's suggestion to shorten some of the frequently used functions with one-letter variables. -12 bytes from making a, g, t more compact.

Edit: -24 bytes from making by removing variable names for intermediate keys that are only used once, namely a, g, s, S, k, K.

Edit: -74 bytes from consolidating H(), T() and C().

Edit: -1 byte thanks to Nick A for their suggestion of changing ord(s[i])+ord(s[i+1]) to sum(map(ord,s[i:i+2])). -2 bytes from changing 2 +=[a] calls to +=a,. -13 bytes from changing how G() found the index of the minimum of s. -2 bytes from changing y=(y+1)%v to y=-~y%v. -15 bytes from assigning k.index() to K. -4 bytes from assigning 10 to W. -5 bytes from assigning 1-I(d[-1]) to X inside V. -3 bytes from rewriting C()'s main loop. -2 bytes from reorganizing T().

I=int;L=list;E=len;R=range;B=str;J=''.join;W=10
def H(s,e):
for i in R(e-E(s)):s+=chr(48+sum(map(ord,s[i:i+2]))%32%W)
return s
def Q(s):
r=*E(s);s=L(s)
for z in R(E(s)):b=s.index(min(s));r[b]=z;s[b]="~"
return r
def T(x,k,d=0):
u=E(x);v=E(k);g=R(v);K=k.index;n=u//v+1;w=[];e=r=y=0;i=K(y);c=[]
if d:
while r<n:
if r>n-1:i=min(i,(u%v or v))
w+=L(x[e:e+i]),;e+=i;i+=1;r+=1
if i>v:y=-~y%v;i=K(y)
r=y=0;i=v-K(y)
while r<n:
w[r]+=L(x[e:e+i]);e+=i;i-=1;r+=1
if i<1:y+=1;i+=v-K(y);r+=1
w[-1]+=['']*(v-E(w[-1]))
for j in g:c+=J(z[j]for z in w),
else:c=[x[i::v]for i in g]
s=*v
for f in g:s[k[f]]=c[f]
return J(s)
def C(m,s,w,n):
t={".":s[-2:],"#":s[-1]*2};j=z=0
for x in R(26):
v=chr(x+65)
if v in w:t[v]=s[w.index(v)]
else:t[v]=s[z-2]+s[j];j+=z;z=-~z%2
r=J(i.isdigit()and i or t[i]for i in m)
return r+n[:-E(r)%5]
def V(m,w,P,d,A,M,n):X=1-I(d[-1]);t=J(B(Q(P[W:20])[I(J(B((I(H(J(B((I(M[i])-I(d[i]))%W)for i in R(5)),W)[i])+I(Q(P[:W])[i]))%W)for i in R(W))[i])])for i in R(W));u=H(t,60)[W:];p=A+I(u[-2]);v=T(u,Q(t));z=T(T(C(m,J(B(i)for i in Q(u[40:])),w,n),Q(v[:p])),Q(v[p:p+A+I(u[-1])]),1);e=[z[5*i:5*-~i]for i in R(-(-E(z)//5))];return' '.join(e[:X]+[M]+e[X:])


Ungolfing:

def chain_add(seq, end):
for i in range(end - len(seq)):
seq += chr(48+sum(map(ord,seq[i:i+2]))%32%10)
return seq

def sequent(seq):
res = *len(seq)
seq = list(seq)
for z in range(len(seq)):
b = seq.index(min(seq))
res[b] = z
seq[b] = "~"
return res

def transpose(text, keys, disrupt=False):
if disrupt:
num_rows = len(text) // len(keys) + 1
len_last = len(text) % len(keys)
if len_last == 0:
len_last = len(keys)
d_rows = []
text_index = 0
current_row = 0
stop_key = 0
stop_index = keys.index(stop_key)
while current_row < num_rows:
if current_row > num_rows-1:
stop_index = min(stop_index, len_last)
d_rows += [list(text[text_index:text_index+stop_index])]
text_index += stop_index
stop_index += 1
if stop_index>len(keys):
stop_key = (stop_key+1) % len(keys)
stop_index = keys.index(stop_key)
current_row += 1
current_row = 0
stop_key = 0
stop_len = len(keys) - keys.index(stop_key)
while current_row < num_rows:
d_rows[current_row] += list(text[text_index:text_index+stop_len])
text_index += stop_len
stop_len -= 1
if stop_len < 1:
stop_key += 1
stop_len = len(keys) - keys.index(stop_key)
current_row += 1
current_row += 1
d_rows[-1] += ['']*(len(keys)-len(d_rows[-1]))
columns = []
for j in range(len(keys)):
columns += [''.join(i[j]for i in d_rows)]
else:
columns = ['']*len(keys)
for t in range(len(text)):
columns[t%len(keys)] += text[t]
res = *len(keys)
for index in range(len(keys)):
res[keys[index]] = columns[index]
return''.join(res)

def checkerboard(message, seq, word, null):
trans = {".":seq[-2:], "#":seq[-1]*2};res='';j=z=0
for x in range(26):
v = chr(x + 65)
if v in word:
trans[v] = seq[word.index(v)]
else:
trans[v] = seq[z-2] + seq[j]
j += z
z = (z+1) % 2
for i in message:
if i.isdigit():
res += i
else:
res += trans[i]
return res + null[:-len(res)%5]

def vic_cipher(message, keyword, phrase, date, agent, m_id, null):
s1 = sequent(phrase[:10])
s2 = sequent(phrase[10:20])
a = ''.join(str((int(m_id[i])-int(date[i]))%10) for i in range(5))
g = ''.join(str((int(a[i])+int(s1[i]))%10) for i in range(10))
t = ''.join(str(s2[int(g[i])]) for i in range(10))
p = agent+int(u[-2])
q = agent+int(u[-1])
seqT = sequent(t)
v = transpose(u,seqT)
k1 = sequent(v[:p])
k2 = sequent(v[p:p+q])
c = ''.join(str(i)for i in sequent(u[40:]))
x = checkerboard(message,c,keyword,null)
y = transpose(x,k1)
z = transpose(y,k2,1)
e = [z[5*i:5*(i+1)] for i in range(-(-len(z)//5))]
X = 1-int(date[-1])
return ' '.join(e[:X] + [m_id] + e[X:])

• Python 3 allows unicode characters as variables, FYI. – Paul Aug 10 '16 at 2:50
• Changing ord(seq[i])+ord(seq[i+1]) to sum(map(ord,seq[i:i+2])) saves 1 character I believe. – user52452 Jan 9 '17 at 17:01

# C, 28802769276627622743274127392699 2458 bytes

#include<stdio.h>
#define m(x)malloc(x)
#define Y(x)strlen(x)
typedef int i;typedef char*c;c _(c A,i B,i D){if(D>=B){return A;}c C=m(Y(A)+2);sprintf(C,"%s%c",A,48+(A[D]+A[D+1]-96)%10);return _(C,B,D+1);}c l(c A){i J=Y(A);c P=m(J+2);for(i m=0;m<J;m++){P[m]=32;}for(i v=0;v<J;v++){char G;i R;for(i u=0;u<J;u++){R=u<1|A[u]<G?u:R;G=u<1|A[u]<G?A[u]:G;}P[R]=48+v;c V=m(J);for(i t=0;t<J;t++){V[t]=t!=R?A[t]:97;}A=V;}return P;}c S(c C,c N,c I,char U){srand(time(NULL));i M=Y(I);i O=Y(N);i R=rand()%M;c Q=m(M+1);for(i u=R;u<M;u++){Q[u-R]=I[u];}Q[M-R]=46;for(i H=0;H<R;H++){Q[H+M-R+1]=I[H];}c g=m(28);c V=m(28);strcat(V,C);sprintf(g,"%s%s",N,"BCDFGHJKLMPQUVWXYZ.#");i B=Y(N);for(i q=B;q<10;q++){for(i x=0;x<10;x++){char J={C[q],C[x]};V[B]=48+atoi(J);B++;}}c w=m(M*2+4);for(i J=0;J<=M;J++){i K=0;for(i X=0;X<28;X++){if(Q[J]==g[X]){char F;sprintf(F,"%d",V[X]-48);strcat(w,F);K=1;}}if(K<1){w[Y(w)]=Q[J];}}i f=Y(w);if(f%5>0){c P=m(5-f%5);for(i E=0;E<5-f%5;E++){P[E]=U;}strcat(w,P);}return w;}c a(c I,c U){i M=Y(I),K=Y(U);c T=m(M);i F=0;for(i b=0;b<K;b++){for(i y=0;y<K;y++){if(U[y]==48+b){for(i u=y;u<M;u+=K){T[F]=I[u];F++;}}}}return T;}c da(c I,c K){i e=Y(I),k=Y(K);c T=m(e);for(i y=0;y<e;y++){T[y]=32;}i F,P;F=P=0;for(i u=0;u<k;u++){for(i v=0;v<k;v++){T[F]=I[P];P++;F++;if(K[v+1]-48==u){for(i C=1;C<k-v;C++){F+=k-v-C;for(i E=0;E<=v+C;E++){if(F<e&P<e){T[F]=I[P];}F++;P++;}}break;}}if(F>e){break;}}i U=0;for(i g=0;g<e;g++){U=T[g]-48<10&-1<T[g]-48?U+1:U;}for(i j=U;j<e;j++){for(i x=0;x<e;x++){if(T[x]==32){T[x]=I[j];break;}}}return a(T,K);}En(c n,c m,c k,i d,c v,c s,char u){c S1,S2;S1=m(10);S2=m(10);for(i i=0;i<20;i++){if(i<10){S1[i]=k[i];}else{S2[i-10]=k[i];}}S1=l(S1);S2=l(S2);c M=m(5);for(i i=4;i>-1;i--){M[i]=48+(s[i]-v[i])%10;}c G=_(M,5,0);for(i y=0;y<10;y++){G[y]=48+(S1[y]+G[y]-96)%10;}c N="0123456789";c T=m(10);for(i q=0;q<10;q++){for(i t=0;t<10;t++){if(N[t]==G[q]){T[q]=S2[t];}}}c Z=_(T,50,0);c U=m(50);for(i h=0;h<50;h++){U[h]=Z[h+10];}i p,q;for(i b=49;b>10;b++){if(U[b]!=U[b-1]){q=d+U[b]-48;p=d+U[b-1]-48;break;}}c V=m(50);i Ct=0;for(i j=0;j<10;j++){for(i o=0;o<10;o++){if(l(T)[o]==48+j){for(i x=o;x<o+41;x+=10){V[Ct]=U[x];Ct+=1;}}}}c K1=m(p);c K2=m(q);for(i D=0;D<p+q;D++){if(D<p){K1[D]=V[D];}else{K2[D-p]=V[D];}}K1=l(K1);K2=l(K2);c C=m(10);for(i b=40;b<50;b++){C[b-40]=U[b];}C=l(C);c t=da(a(S(C,m,n,u),K1),K2);i O=0;for(i B=0;B<Y(t)/5+1;B++){if(B==Y(t)/5-v[Y(v)-1]+49){printf("%s ",s);}else{for(i J=O;J<O+5;J++){printf("%c",t[J]);}printf(" ");O+=5;}}}


My gosh. This is the longest program I have ever had to golf. This is also the first on which I actually ran out of single-character variable names for the global scope and so had to move on to using a few 2 character ones (the fact that I apparently cannot redeclare variables doesn't help). Golfing tips are therefore very much appreciated.

## Ungolfed

Compiles without any warnings, unlike the golfed version. Minor changes made to the golfed version will not be reflected in this ungolfed version.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

if(Start>=End){return String;}
char*C=malloc(strlen(String)+2);
sprintf(C,"%s%c",String,'0'+(((String[Start]-'0')+(String[Start+1]-'0'))%10));
}

char*Sequent(char*String){
int J=strlen(String);
char*P=malloc(J+2);
for(int m=0;m<J;m++){
P[m]=' ';
}
for(int v=0;v<J;v++){
char G;
int R;
for(int u=0;u<J;u++){
R=(u<1||String[u]<G)?u:R;
G=(u<1||String[u]<G)?String[u]:G;
}
P[R]='0'+v;
char*V=malloc(J);
for(int t=0;t<J;t++){
if(t!=R){
V[t]=String[t];
}
else{
V[t]='a';
}
}
String=V;
}
return P;
}

srand(time(NULL));
int Msg_Len=strlen(Message);
int Key_Len=strlen(Key);
int R=rand()%Msg_Len;
char*Q=malloc(Msg_Len+1);
for(int u=R;u<Msg_Len;u++){
Q[u-R]=Message[u];
}
Q[Msg_Len-R]='.';
for(int H=0;H<R;H++){
Q[H+Msg_Len-R+1]=Message[H];
}
char*Alphabet=malloc(26);
for(int W=0;W<26;W++){
Alphabet[W]='A'+W;
}
int q=0;
char*e=malloc(Key_Len);
for(int z=0;z<Key_Len;z++){
if(strchr(e,Key[z])!=NULL){
q++;
}
else{
e[z-q]=Key[z];
}
}
int r=0;
for(int h=0;h<26;h++){
if(strchr(e,Alphabet[h-r])!=NULL){
for(int X=h-r;X<26;X++){
Alphabet[X]=Alphabet[X+1];
}
r++;
}
}
char*Checkerboard=malloc(28);
for(int i=0;i<26;i++){
if(i<strlen(e)){
Checkerboard[i]=e[i];
}
else{
Checkerboard[i]=Alphabet[i-strlen(e)];
}
}
Checkerboard='.';
Checkerboard='#';
char*Values=malloc(28);
strcat(Values,C);
int B=strlen(e);
for(int q=B;q<10;q++){
for(int x=0;x<10;x++){
char J={C[q],C[x]};
Values[B]='0'+atoi(J);
B++;
}
}
char*Encoded=malloc(Msg_Len*2+4);
for(int J=0;J<=Msg_Len;J++){
int K=0;
for(int X=0;X<28;X++){
if(Q[J]==Checkerboard[X]){
char F;
sprintf(F,"%d",Values[X]-'0');
strcat(Encoded,F);
//printf("F = %s while Q[J] = %c and Checkerboard[X] = %c and Encoded = %s\n",F,Q[J],Checkerboard[X],Encoded);
K=1;
}
}
if(K<1){
Encoded[strlen(Encoded)]=Q[J];
}
}
int Encded_Len=strlen(Encoded);
if(Encded_Len%5>0){
char*P=malloc(5-Encded_Len%5);
for(int E=0;E<5-Encded_Len%5;E++){
P[E]=null;
}
strcat(Encoded,P);
}
return Encoded;
}

char*Transpose(char*Message,char*K1){
int Msg_Len=strlen(Message),K1_Len=strlen(K1);
char*T=malloc(Msg_Len);
int F=0;
for(int i=0;i<K1_Len;i++){
for(int y=0;y<K1_Len;y++){
if(K1[y]=='0'+i){
for(int u=y;u<Msg_Len;u+=K1_Len){
T[F]=Message[u];
F++;
}
}
}
}
return T;
}

char*Disrupted_Transpose(char*Message,char*K2){
int Msg_Len=strlen(Message),K2_Len=strlen(K2);
char*T=malloc(Msg_Len);
for(int y=0;y<Msg_Len;y++){
T[y]=' ';
}
int F=0;
int P=0;
for(int u=0;u<K2_Len;u++){
for(int v=0;v<K2_Len;v++){
T[F]=Message[P];
P++;F++;
if(K2[v+1]-'0'==u){
for(int C=1;C<K2_Len-v;C++){
F+=K2_Len-v-C;
for(int E=0;E<=v+C;E++){
if(F<Msg_Len&P<Msg_Len){
T[F]=Message[P];
}
F++;P++;
}
}
break;
}
}
if(F>Msg_Len){
break;
}
}
int U=0;
for(int g=0;g<Msg_Len;g++){
U=(T[g]-'0'<10&-1<T[g]-'0')?U+1:U;
}
for(int j=U;j<Msg_Len;j++){
for(int x=0;x<Msg_Len;x++){
if(T[x]==' '){
T[x]=Message[j];
break;
}
}
}
return Transpose(T,K2);
}

void VIC_Encoder(char*Message,char*Phrase,char*Key,int a_id,char*date,char*m_id,char null){
char*S1=malloc(10);
char*S2=malloc(10);
for(int i=0;i<20;i++){
if(i<10){
S1[i]=Key[i];
}
else{
S2[i-10]=Key[i];
}
}
S1=Sequent(S1);
S2=Sequent(S2);
char*M=malloc(5);
for(int i=4;i>-1;i--){
M[i]='0'+(((m_id[i]-'0')-(date[i]-'0'))%10);
}
for(int y=0;y<10;y++){
G[y]='0'+(((S1[y]-'0')+(G[y]-'0'))%10);
}
char*N="0123456789";
char*T=malloc(10);
for(int q=0;q<10;q++){
for(int t=0;t<10;t++){
if(N[t]==G[q]){
T[q]=S2[t];
}
}
}
char*U=malloc(50);
for(int h=0;h<50;h++){
U[h]=Z[h+10];
}
int p,q;
for(int b=49;b>10;b++){
if(U[b]!=U[b-1]){
q=a_id+(U[b]-'0');
p=a_id+(U[b-1]-'0');
break;
}
}
char*seqT=Sequent(T);
char*V=malloc(50);
int Count=0;
for(int j=0;j<10;j++){
for(int o=0;o<10;o++){
if(seqT[o]=='0'+j){
for(int x=o;x<o+41;x+=10){
V[Count]=U[x];
Count+=1;
}
}
}
}
char*K1=malloc(p);
char*K2=malloc(q);
for(int D=0;D<p+q;D++){
if(D<p){
K1[D]=V[D];
}
else{
K2[D-p]=V[D];
}
}
K1=Sequent(K1);
K2=Sequent(K2);
char*C=malloc(10);
for(int b=40;b<50;b++){
C[b-40]=U[b];
}
C=Sequent(C);
int O=0;
for(int B=0;B<strlen(Transposed_2)/5+1;B++){
if(B==strlen(Transposed_2)/5-date[strlen(date)-1]+'1'){
printf("%s ",m_id);
}
else{
for(int J=O;J<O+5;J++){
printf("%c",Transposed_2[J]);
}
printf(" ");
O+=5;
}
}
}


## Notes

• This uses a checkerboard similar to the following to encode the message:

  3 4 5 6 2 3 4 5 6 7
S E N A T O R I
6 B C D F G H J K L M
7 P Q U V W X Y Z . #

• This assumes that all applicable strings are given in uppercase. The message should also have all punctuation except periods removed and all numbers demarcated by #s, while the key phrase should have all punctuation removed.

• The resulting encoded message is output to STDOUT as a string of space-separated five-digit groups.

• The input message should be in English.

• I would have combined a few of the functions I used, but then I would have to resort to using more two-letter variable names, making the final program longer than it would be with a few more functions.

• This currently does not assume that the keyword (at least in English) is always going to contain the same set of letters, and so makes up for that by removing duplicates, manipulating the checkerboard, etc. This capability is apparently not required by OP, so I am currently golfing off the extra, unnecessary bytes that is taking up. Updated for golfed version.

# JavaScript (ES6), 946938 953 bytes

V=(c,d,f,g,j,k,m)=>{S=a=>a.split,J=a=>a.join,A=(a,b)=>{for(i=0;a[L="length"]<b;a+=(a[i++]- -a[i])%h);return a},Q=b=>(a=S(b).sort(),S(b).map(b=>a[i=a[X="indexOf"](b)]=i)),u=A(t=J(S(A(J(S(k).map((a,b)=>Math.abs(a-g[b]))),h=10)).map((a,b)=>Q(f[C="slice"](h,20))[(Q(f[C](0,h))[b]- -a)%h])),60)[C](h),T=((a,b,c)=>{if(r=Array(l=b[L]).fill(""),c){for(e=a[L]/l,i=0,w=[],u=R=b[X](x=0);i<e;)w[i++]=a[P](0,R++),u?u=0:R>l||(R=b[X](u=++x));for(i=0;i<e;)w[i]=J(w[i].concat(a[P](0,l-w[i++][L])));a=J(w)}for(i in a)r[+b[i%l]]+=a[i];return r}),v=J(T(u,Q(t))),q=J(Q(u[C](-h))),t="ABCDEFGHIJKLMNOPQRSTUVWXYZ#".match(new RegExp("[^"+d+"]","g")),t[P="splice"](9,0,"."),M=[];for(i in t)M[t[i]]=q[8^i/h]+(M[d[i]]=q[i%h]);for(n=c[L],b=J((c[C](n-=new Date%n)+"."+c[C](0,n)).split(/ |/).map(a=>M[a]||a)),b+=m.repeat(5-b[L]%5),i=f=q=49;f==q;)f=+u[i-1]+j,q=+u[i++]+j;return t=J(T(S(J(T(b,Q(v[C](0,f))))),Q(v.substr(f,q)),1)).match(/.{5}/g),g=-g[C](-1),g++&&t[P](g||t[L],0,k),t}


I saw over the weekend there was no JS entry for this yet, so here's my (last minute) attempt. Implementing and golfing this was as crazy as it was fun!

### Demo Snippet

V=(c,d,f,g,j,k,m)=>{S=a=>a.split,J=a=>a.join,A=(a,b)=>{for(i=0;a[L="length"]<b;a+=(a[i++]- -a[i])%h);return a},Q=b=>(a=S(b).sort(),S(b).map(b=>a[i=a[X="indexOf"](b)]=i)),u=A(t=J(S(A(J(S(k).map((a,b)=>Math.abs(a-g[b]))),h=10)).map((a,b)=>Q(f[C="slice"](h,20))[(Q(f[C](0,h))[b]- -a)%h])),60)[C](h),T=((a,b,c)=>{if(r=Array(l=b[L]).fill(""),c){for(e=a[L]/l,i=0,w=[],u=R=b[X](x=0);i<e;)w[i++]=a[P](0,R++),u?u=0:R>l||(R=b[X](u=++x));for(i=0;i<e;)w[i]=J(w[i].concat(a[P](0,l-w[i++][L])));a=J(w)}for(i in a)r[+b[i%l]]+=a[i];return r}),v=J(T(u,Q(t))),q=J(Q(u[C](-h))),t="ABCDEFGHIJKLMNOPQRSTUVWXYZ#".match(new RegExp("[^"+d+"]","g")),t[P="splice"](9,0,"."),M=[];for(i in t)M[t[i]]=q[8^i/h]+(M[d[i]]=q[i%h]);for(n=c[L],b=J((c[C](n-=new Date%n)+"."+c[C](0,n)).split(/ |/).map(a=>M[a]||a)),b+=m.repeat(5-b[L]%5),i=f=q=49;f==q;)f=+u[i-1]+j,q=+u[i++]+j;return t=J(T(S(J(T(b,Q(v[C](0,f))))),Q(v.substr(f,q)),1)).match(/.{5}/g),g=-g[C](-1),g++&&t[P](g||t[L],0,k),t}
<div id="demo">
<h2>Test</h1>
<p><strong>Message: </strong><input id="message" type="text" value="WE ARE DISCOVERED. TAKE WHAT YOU CAN. BURN EVERYTHING ELSE. MOVE TO SAFEHOUSE FOXTROT #3#."/></p>
<p><strong>Keyword: </strong><input id="keyword" type="text" value="SENATORI"/></p>
<p><strong>Phrase: </strong><input id="phrase" type="text" value="THEFIRSTPRINCIPLEISTHATYOUMUSTNOTFOOLYOURSELFANDYOUARETHEEASIESTPERSONTOFOOL"/></p>
<p><strong>Date: </strong><input id="date" type="text" value="3172016"/></p>
<p><strong>Agent Id: </strong><input id="agent" type="text" value="9"/></p>
<p><strong>Message Id: </strong><input id="mid" type="text" value="47921"/></p>
<p><strong>Null: </strong><input id="null" type="text" value="4"/></p>
</div>
document.getElementById('message').value,
document.getElementById('keyword').value,
document.getElementById('phrase').value,
document.getElementById('date').value,
document.getElementById('agent').value,
document.getElementById('mid').value,
document.getElementById('null').value).join(', '))"/>

### Edit: -8 bytes

Realized there were extra parenthesis around function S,J,A,Q

### Edit: +15 bytes

Updated the logic for how the message id is placed in the final message (now 1-indexed and 0 does not include it in the output).

## Ungolfed

chainAdd = (s,l)=>{for(i=0;s.length<l;s+=(s[i++]- -s[i])%10);return s;}

sequentialize = (s)=> {
a=s.split('').sort();
return s.split('').map(c=>(i=a.indexOf(c),a[i]='',i));
}

transpose = (s,k,disruptive)=>{
var result=Array(k.length).fill('')
if(disruptive){
rows=[]
k_index=0;
rowLength=k.indexOf(k_index);
triangling=!rowLength;

expectedRows = s.length/k.length
for(row=0;row<expectedRows;row++){
rows[row]=s.splice(0,rowLength++)
if(triangling){
if(rowLength>k.length){
triangling=false;
rowLength=k.indexOf(++k_index)
}
}
else{
triangling=true;
}
}

for(row=0;row<expectedRows;row++){
rows[row]= rows[row].concat(s.splice(0,k.length-rows[row].length)).join('')
}
s=rows.join('')
}
for(i in s)
result[+k[i%k.length]]+=s[i];
return result;
}

checkerboard =(message,seq, keyword, nulls)=>{
t='ABCDEFGHIJKLMNOPQRSTUVWXYZ#'.match(new RegExp('[^'+keyword+']','g'));
t.splice(9,0,'.')

map=[]
for(i in t)
map[t[i]]=(seq[8^(i/10)])+(map[keyword[i]]=seq[i%10])

r = new Date%message.length;
rotateMessage=message.substr(message.length-r)+'.'+message.substr(0,message.length-r)

result =rotateMessage.split(/ |/).map(x=>map[x]||x).join('');
result+=nulls.repeat(5-result.length%5)

return result;
}

vic = (message, keyword, phrase, date, agent, m_id, nulls)=>{
s1=sequentialize(phrase.substr(0,10))//.join('')
s2=sequentialize(phrase.substr(10,10))//.join('')

r = m_id.split('').map((x,i)=>Math.abs(x-date[i])).join('')

t = g.map(i=>s2[+i]).join('');

var p,q;
for(i=49;p==q;i++){
p=agent + +u[i-1];
q=agent + +u[i];
}
seqT = sequentialize(t);
v=transpose(u,seqT).join('');

k1 = sequentialize(v.substr(0,p));
k2 = sequentialize(v.substr(p,q));
c  = sequentialize(u.substr(-10)).join('')

CB =checkerboard(message,c, keyword, nulls);
t1=transpose(CB,k1).join('')
t2=transpose(t1.split(''),k2,1).join('').match(/.{5}/g);
(d=-date.substr(-1))&&t2.splice((d+1)||t2.length,0,m_id);
return t2;
}


## Notes

• This uses a checkerboard similar to the following to encode the message:

  3 1 0 5 9 6 8 7 2 4
S E N A T O R I
2 B C D F G H J K L .
4 M P Q U V W X Y Z #

• All strings are given in uppercase. The message is alphanumeric Latin (plus . and #) and should have all punctuation (except periods) removed. All numbers should already be marked with #s. The key phrase should have all punctuation/spaces removed.

• The resulting message is returned as an array of 5 digit strings.

## Enhancements

• I feel like there's a way to abuse the "All languages" to save some bytes. If I had more time I'd refigure this to assume the language was something like Hawaiian which has only 12 letters.

• Any golfing suggestions are always welcome.

• Could you please add a snippet so I can verify that this works? If it does, then I can award you the bounty. – R. Kap Jan 12 '17 at 2:13
• @R.Kap Sure, I've added a demo snippet – SLuck49 Jan 12 '17 at 3:19
• Hmm...in the demo, the message identifier seems to be 7 away from the end instead of 6. Also, in your ungolfed version, the same Id seems to be 6 away from the beginning instead of the end. – R. Kap Jan 12 '17 at 3:21
• @R.Kap Yeah there was a bug when I first posted it (must not have fixed it in the ungolfed). As for the golfed, I assumed that was 0-indexed because otherwise if 1 means the very end where would you say the message identifier should go on a 0? I can change it I just need to know. – SLuck49 Jan 12 '17 at 4:28
• I would say that on a 0 the message identifier should be omitted from the output. – R. Kap Jan 12 '17 at 5:00

## Clojure, 1197 1212 bytes

Ough, I'm exhausted.

Update: Added the required random splitting location of the message, ungolfed version uses the same location as the given example so that algorithm can be easily verified.

(defn enc[I K E D Y M](let[P split-at A concat Z zipmap R partition W mapcat % count X repeat O vector / map x(fn[C](apply / O C))G range t 10 r(G t)m(fn[i](mod i t))F(fn[[n & N]](/ last(iterate(fn[[[b & N]a]][(A N[(m(+ a b))])b])[N n])))Q(fn[S](for[i(G(% S))a[(nth S i)]](apply +(%(filter #{a}(take i S)))(for[b S :when(pos?(compare a b))]1))))[S J](/ Q(P t(take 20 E)))T(/(Z r J)(/ m(/ + S(F(/ - M D)))))U(take 50(drop t(F T)))l(last U)p(+ Y(last(remove #{l}U)))V(W(Z(Q T)(x(R t U)))r)[k j](/ Q(P p(take(+ p Y l)V)))B(into(Z(/ char(G 48 58))(G))(/(fn[i c][c(+(*(quot i 10)20)(nth(Q(reverse(take t(reverse U))))(m i)))])(G)(A(str K",,")(remove(set K)(/ char(A(G 65 91)".#"))))))?(% k)T(vec(filter some?(W(Z k(x(R ?(A(flatten(R 5(A(W str(/ B(let[[b e](P(rand-int(count I))I)](apply str(A e".. "b)))))(X 4(B\,)))))(X(dec ?)nil)))))(G ?))))w (% j)NR(+(quot(% T)w)1)L(flatten(for[k r](for[i(G(.indexOf j k)(inc w))](G i))))C(for[[r c](/ O(rest(reductions + -1(/(fn[i](get{0 1}i 0))L)))L):when(< r NR)][r c])H(R 5(filter some?(W(Z j(x(R w (A(vals(into(sorted-map)(/ O(A C(for[i(G NR)j(G w)c[[i j]]:when(not((set C)c))]c))T)))(X(dec w)nil)))))(G w))))](/(fn[p](apply str p))(let[[b e](P(-(% H)(D 6)-1)H)](A b[M]e)))))


Sample inputs and test case:

(def mymsg (clojure.string/upper-case "We are discovered. Take what you can. Burn everything else. Move to Safehouse Foxtrot#3#"))
(def mykey "SENATORI")
(def mypharase (clojure.string/upper-case (apply str (remove #{\space} "The first principle is that you must not fool yourself — and you are the easiest person to fool."))))
(def mydate [3 1 7 2 0 1 6])
(def mynum 9)
(def M [4 7 9 2 1])

;("61231" "12824" "71192" "58609" "92185" "48612" "14927" "22944" "34046" "13348" "04159" "38645" "70546" "20254" "22026" "64584" "21904" "47921" "90253" "42694" "42221" "56644" "14541")
(enc mymsg mykey mypharase mydate mynum M)


Ungolfed:

(defn enc[mymsg mykey mypharase mydate mynum M]
(let[t       10
r       (range t)
m       (fn[i](mod i t))
lagfib  (fn[[n & N]](map last(iterate(fn[[[b & N]a]][(concat N[(m(+ a b))])b])[N n])))
toseq   (fn[S](for[i(range(count S))a[(nth S i)]](apply +(count(filter #{a}(take i S)))(for[b S :when(pos?(compare a b))]1))))
[S1 S2] (map toseq(split-at t(take 20 mypharase)))
M2      (take t(lagfib(map - M mydate)))
G       (map m(map + S1 M2))
Gmap    (zipmap r S2)
T       (map Gmap G)
U       (take 50(drop t(lagfib T)))
L2      (last U)
L1      (last(remove #{L2}U))
p       (+ mynum L1)
q       (+ mynum L2)
seqT    (toseq T)
V       (mapcat(zipmap seqT(apply map vector(partition t U)))r)
[K1 K2] (map toseq(split-at p(take(+ p q)V)))
C       (toseq(reverse(take t(reverse U))))
B       (into(zipmap(map char(range 48 58))(range))(map(fn[i c][c(+(*(quot i 10)20)(nth C(m i)))])(range)(concat(str mykey",,")(remove(set mykey)(map char(concat(range 65 91)".#"))))))
;B       (into(zipmap(map char(range 48 58))(range))(map(fn[i c][c(+(nth C(quot i 3))(*(mod i 3)20))])(range)(flatten(apply map vector(partition 10(apply concat mykey",,"(apply map vector (partition 2(remove(set mykey)(map char(concat(range 65 91)".#")))))))))))
N1      (count K1)
mymsg   (flatten(partition 5(concat(mapcat str(map B(let[[b e](split-at 49 mymsg)](apply str(concat e".. "b)))))(repeat 4(B\,)))))
T1      (vec(filter some?(mapcat(zipmap K1(apply map vector(partition N1(concat mymsg(repeat(dec N1)nil)))))(range N1))))
N2      (count K2)
NR      (+(quot(count T1)N2)1)
cols    (flatten(for[k r](for[i(range(.indexOf K2 k)(+(count K2)1))](range i))))
rows    (rest(reductions + -1(map(fn[i](get{0 1}i 0))cols)))
coords  (for[[r c](map vector rows cols):when(< r NR)][r c])
coords  (concat coords(for[i(range NR)j(range N2)c[[i j]]:when(not((set coords)c))]c))
T2      (partition 5(filter some?(mapcat(zipmap K2(apply map vector(partition N2(concat(vals(into(sorted-map)(map vector coords T1)))(repeat(dec N2)nil)))))(range N2))))]
(map(fn[p](apply str p))(let[[b e](split-at(-(count T2)(mydate 6)-1)T2)](concat b[M]e)))))


It has an alternative implementation on the checkerboard B which is the same as on the task definition. But the submission uses an other one in which unused alphabets first fill the 2nd row and then the 3rd instead of filling column-by-column.

• Considered writing a Clojure solution, but my head exploded while I was reading the question. How long did that take to write? – Carcigenicate Jan 11 '17 at 0:16
• Maybe 3 hours while watching Youtube on the side. This started off quite easy but I was about to give up when I had to implement the second "disrupted transposition". Now that coords is generated twice, first generating the triangular shape and then filling any coordinates which were missing. Also "padding to length of multiply of N" might have more elegant solution than concatenating N - 1 elements and partitioning to lengths of N. – NikoNyrh Jan 11 '17 at 0:23
• Oh damn I forgot to change the hard-coded split point at (split-at 49 mymsg), 49 should be something like (rand-int(count mymsg)) so correct answer would be a bit over 1200 bytes. zzz – NikoNyrh Jan 11 '17 at 1:12
• Damn. Probably still less than the c answer though. – Carcigenicate Jan 11 '17 at 1:20