Написать кодировщик VIC шифра


18

VIC шифр является одним из самых сложных и карандашных бумажных шифров когда - либо придуманные. Используемый в 1950-х годах советским шпионом Рейно Хайяненом под кодовым названием «ВИКТОР», его основным принципом является безопасность через запутывание; много запутывания.

Ваша задача - написать программу или функцию, которая будет принимать сообщение и кодировать его с помощью шифра VIC. Я также разместил здесь проблему с шифровальным декодером VIC . Если какая-либо из следующих инструкций неясна, не стесняйтесь спрашивать о них в комментариях. Инструкции адаптированы с этого сайта .

Кодирование шифра VIC

подготовка

Вам понадобится пять входов:

  • текстовое сообщение
  • короткое ключевое слово или фраза, содержащая наиболее распространенные буквы на вашем языке
  • ключевая фраза, такая как цитата или строка из песни (не менее 20 символов)
  • дата (или другое число, которое состоит из шести цифр или более)
  • номер личного агента

На практике эти последние четыре должны быть предварительно согласованы отправителем и получателем, включая вопрос о том, используется ли номер агента отправителя или получателя в кодировании.

Мой пример сообщения будет: We are discovered. Take what you can. Burn everything else. Move to Safehouse Foxtrot 3.

Мы будем кодировать на английском языке (хотя вы можете использовать любой язык и алфавит), и наиболее распространенные буквы в английском алфавите A, E, I, N, O, R, S, T. Я буду использовать ключевое слово SENATORI.

Моя ключевая фраза - цитата Ричарда Фейнмана: «Первый принцип заключается в том, что вы не должны обманывать себя - и вы - самый легкий человек, чтобы обмануть».

В качестве даты я буду использовать 31 июля 2016 года (в формате 3172016), то есть день, когда я написал это описание.

Личный номер, который я выбрал для себя 9.

Краткое изложение шагов

  1. Получите промежуточные ключи для использования в следующих шагах.
  2. Построить и применить раскидистую шахматную доску.
  3. Построить и применить первую таблицу транспозиции.
  4. Построить и применить вторую (нарушенную) таблицу транспозиции.
  5. Завершите сообщение, вставив группу индикаторов сообщений.

Submechanisms

Еще две вещи, которые нужно объяснить, прежде чем мы углубимся в суть вопроса: процессы сложения цепочек и секвенирования.

Цепное сложение, также известное как отставший генератор Фибоначчи, работает, беря последовательность начальных цифр, добавляя первые две цифры без переноса (затем сложите их вместе mod 10) и добавляя результат в конец. Например:

79081
7 + 9 = 6

790816
9 + 0 = 9

7908169
0 + 8 = 8

79081698
8 + 1 = 9

790816989
1 + 6 = 7

7908169897
... and so on

Последовательность - это, по сути, последовательность букв или цифр и их маркировка в алфавитном / числовом порядке. Дубликаты помечены слева направо. Например:

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

Я использую здесь нулевое индексирование, но индексирование, как вам нравится.

1. Промежуточные ключи

Разделите первые 20 букв ключевой фразы на две группы по 10 и секвенируйте каждую в отдельности, которую мы будем называть S1и S2.

    THEFIRSTPR
S1: 8201357946

    INCIPLEIST
S2: 2603751489

Выберите случайный 5-значный идентификатор сообщения M(это может быть один из входных данных, если вы предпочитаете):

M = 47921

Вычтите без заимствования (вычтите mod 10) первые пять цифр контрольной даты 3172016из M:

M      47921
date - 31720
     = 16201

Цепочка добавить результат, пока у вас есть десять цифр:

1620178218

Добавьте эти цифры к S1, без переноса или mod 10, чтобы получить G:

     1620178218
S1 + 8201357946
G  = 9821425154

Выше S2напишите последовательность 0123456789. Найдите каждую цифру Gв последовательности 0123456789 и замените ее цифрой непосредственно под ней в S2. Результат есть T.

   0123456789
S2 2603751489

G  9821425154
T  9806705657

Используйте цепочку, чтобы расширить Tдо 60 цифр.

9806705657

becomes

980670565778637511245490262369939288595822106344304316978734

Эти последние 50 цифр в пяти рядах по десять цифр в каждом образуют Uблок.

T  9806705657
U  7863751124
   5490262369
   9392885958
   2210634430
   4316978734

Последние две неравные цифры Uблока по отдельности добавляются к личному номеру агента, чтобы задать ширину двух транспозиций, pи q.

9 + 3 = 12 (p, первая ширина транспонирования) 9 + 4 = 13 (q, вторая ширина транспонирования)

Упорядочить Tи использовать эту последовательность для копирования столбцов Uблока сверху вниз в новый ряд цифр V.

T     9806705657
seqT  9804612537

U     7863751124
      5490262369
      9392885958
      2210634430
      4316978734

V     69911 56837 12548 26533 30206 13947 72869 49804 84323 75924

Упорядочить первые pцифры, чтобы получить ключ для первой транспонирования K1, и следующие qцифры для ключа для второй 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

Наконец, секвенируйте последнюю строку Uблока, чтобы получить Cзаголовки столбцов для трансграничной шахматной доски:

U5  4316978734
C   3105968724

2. Шатаясь шахматная доска

Сначала я приведу пример шахматной доски, а затем объясню принципы ее создания следующим образом:

  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 #

Первая строка букв - это наше короткое ключевое слово SENATORI. Ваше ключевое слово может быть любой строкой без дубликатов, но, поскольку оно определяет верхний ряд вашей шахматной доски, выбирайте мудро. Над ключевым словом находится C, а остальные строки - остальная часть вашего алфавита в любом порядке, который вы выберете. В моем случае я заполнил шахматную доску остальным латинским алфавитом, знаком пунктуации .и знаком для разграничения чисел #. По сути, шахматная доска является причудливым шифром замещения. Например, «E» будет замещен 1, а «W» будет заменен 27.

После того, как мы закодировали наше текстовое сообщение с помощью этой шахматной доски, но сначала нам нужно сделать начало нашего сообщения менее очевидным, разбив его на случайную позицию и сделав все заглавными. Чтобы обозначить другое оригинальное начало, мы используем две точки остановки..

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

становится

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

Мы кодируем с помощью шахматной доски, давая нам:

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

Если длина сообщения не делится на 5, мы добавляем несколько нулевых символов для дополнения сообщения. Наше сообщение длиной 109 цифр, поэтому я добавлю один ноль: «4».

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

Примечание. Поскольку мое примерное сообщение не содержит цифр, я скажу здесь, что вы можете указать, скажем, как #3#, что закодировано как 44344здесь.

3. Первая транспозиция

Создайте таблицу транспонирования, написав K1(из раздела «Промежуточные ключи»), а затем закодированное сообщение из предыдущего шага в строках одинаковой длины под ключом:

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

Взяв пронумерованные столбцы в порядке их номеров, мы получим:

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

4. Вторая транспозиция

Первая транспозиция была относительно простой. Этот, однако, является нарушенным транспонированием. Схема разрушения определяется шириной таблицы и ключа. В нашем примере у нас есть 110 цифр и 13 столбцов, что означает, что у нас будет 8 полных строк и 6 остатков. Мы начинаем заполнять первый ряд, но останавливаемся в столбце 0 и продолжаем следующим образом:

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

Затем мы заполняем последние несколько пробелов оставшимися цифрами.

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

Теперь мы зачитываем столбцы точно так же, как мы это делали при первом транспонировании.

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

И разбить все на 5-значные группы:

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

5. Завершите сообщение

Последний шаг - вставить наш случайный идентификатор 47921сообщения в само сообщение. Последняя цифра контрольной даты 6указывает расстояние, которое группа должна быть от конца.

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

Примечания к этой проблеме

  • Вам предоставляется как минимум пять входных данных: сообщение, ключевое слово буквы, ключевая фраза, дата и личный номер. Вы можете включить два дополнительных ввода: идентификатор случайного сообщения и нули, необходимые для заполнения сообщения, или ваша функция может генерировать некоторые случайные числа самостоятельно.
  • Можно предположить, что все входные данные верны, с правильным количеством цифр и букв (5-значный идентификатор сообщения, не менее 20 цифр для ключевой фразы и т. Д.). Вы можете предположить, что в ваших строках (сообщение и ключевые слова) уже удалены все знаки препинания и пробелы, кроме тех, которые вы разрешаете в своей версии, и что числа уже обозначены знаками чисел.
  • В первом ключевом слове не должно быть повторяющихся букв, и в вашем коде вы можете предполагать, что в нем никогда не бывает повторяющихся букв.
  • Язык, который вы используете для кодирования, не имеет значения, поскольку язык уже существует, алфавит уже существует, и вы указываете, какой язык вы используете в своем ответе.
  • Какой бы алфавит вы ни использовали для своей шахматной доски, вы можете добавлять или удалять символы, чтобы дополнить шахматную доску. Укажите, для чего вы используете эти символы (например, знаки препинания, отдельный символ «начало сообщения», символы для общих слов). Вы можете полностью отказаться от знака числа и прописать числа или включить каждую цифру в шахматную доску, используя слот, где знак числа был для чего-то другого. Пожалуйста, укажите, какую шахматную доску вы использовали в своем ответе.
  • Выходные данные должны быть либо строкой пятизначных групп, разделенных пробелами, списком пятизначных целых чисел или чем-то подобным.
  • Я использовал нулевую индексацию и 0123456789в моем примере. Вы можете использовать 1-индексирование и / 1234567890или другую систему в своем ответе, если вы укажете, что вы использовали.

Вот пример реализации на Ideone .

Это длинный пост, и я написал большую часть его вручную, поэтому, если в этом посте есть какие-то запутанные части или ошибки в подсчете и транспонировании, пожалуйста, дайте мне знать. Удачи и хорошего гольфа!


1
adding the first two digits without addingТы имеешь в виду ношение?
Исаак

@isaacg Да, я сделал. Ред.
Sherlock9

Не могли бы вы уточнить, что вы подразумеваете под without borrowingи without carrying? Вы имеете в виду сложение и вычитание мода 10, т.е. (6+7) mod 10 = 3и (6-8) mod 10 = 8?
Р. Кап

@ R.Kap Да, позволь мне уточнить это.
Sherlock9

У нас есть разграничить числа?
Р. Кап

Ответы:


10

Питон 3 , 1423 1348 1324 1316 1300 1286 1250 1249 1209 1206 1204 байта

Это, безусловно, самый длинный гольф, который я когда-либо делал, и единственный гольф, где меня всерьез беспокоило, когда у меня заканчиваются односимвольные переменные имена. Предложения по игре в гольф приветствуются. Попробуйте онлайн!

Я кодирую заглавными буквами латинского алфавита с дополнительными символами .и #, используя 0-индексирование и 0123456789при преобразовании gв t. Моя шахматная доска имеет формат, подобный следующему примеру:

  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

Изменить: -63 байта благодаря предложению TuukkaX сократить некоторые из часто используемых функций с однобуквенными переменными. -12 байт от a, g, tболее компактных.

Изменить: -24 байта от создания путем удаления имен переменных для промежуточных ключей, которые используются только один раз, а именно a, g, s, S, k, K.

Изменить: -74 байта от консолидации H(), T() and C().

Редактировать: -1 байт, спасибо Нику А за предложение перейти ord(s[i])+ord(s[i+1])на sum(map(ord,s[i:i+2])). -2 байта от изменения 2+=[a] вызовов на +=a,. -13 байтов от изменения G()найденного индекса минимума s. -2 байта от изменения y=(y+1)%vдо y=-~y%v. -15 байт от назначения k.index()до K. -4 байт от назначения 10до W. -5 байтов от присвоения 1-I(d[-1])к Xвнутренней V. -3 байта от переписывания C()основного цикла. -2 байта от реорганизации 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=[0]*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=[0]*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 = [0]*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 = [0]*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))
    u = chain_add(t,60)[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:])

2
Python 3 допускает использование символов Юникода в качестве переменных.
Пол

Изменение ord(seq[i])+ord(seq[i+1])к sum(map(ord,seq[i:i+2]))сохраняет 1 символ я верю.

3

С 2880 2769 2766 2762 2743 2741 2739 2699 2458 байт

#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[2]={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[3];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;}}}

Боже мой. Это самая длинная программа я когда - либо имел в гольф. Это также первый случай, когда у меня фактически закончились односимвольные имена переменных для глобальной области видимости, поэтому мне пришлось перейти к использованию двухсимвольных (тот факт, что я, очевидно, не могу переопределить переменные, не помогает). Поэтому советы по гольфу очень ценятся.

Ungolfed

Компилируется без каких-либо предупреждений, в отличие от версии для гольфа. Незначительные изменения, внесенные в версию для гольфа, не будут отражены в этой версии без гольфа.

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

char*Chain_Add(char*String,int End,int Start){
  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));
  return Chain_Add(C,End,Start+1);
}

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;
}

char*Straddling_Checkerboard(char*C,char*Key,char*Message,char null){
  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[26]='.';
  Checkerboard[27]='#';
  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[2]={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[3];
    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);
  }
  char*G=Chain_Add(M,5,0);
  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*Z=Chain_Add(T,50,0);
  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);
  char*Transposed_2=Disrupted_Transpose(Transpose(Straddling_Checkerboard(C,Phrase,Message,null),K1),K2);
  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;
    }
  }
}

Примечания

  • Для кодирования сообщения используется шахматная доска, подобная следующей:

      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 . #
    
  • Это предполагает, что все применимые строки приведены в верхнем регистре. В сообщении также должны быть удалены все знаки препинания, кроме точек и всех чисел #, обозначенных s, в то время как ключевая фраза должна содержать все знаки пунктуации удалены.

  • Полученное закодированное сообщение выводится в STDOUT в виде строки разделенных пробелами пятизначных групп.

  • Входное сообщение должно быть на английском языке.

  • Я бы объединил несколько функций, которые использовал, но тогда мне пришлось бы прибегнуть к использованию двухбуквенных имен переменных, что сделало бы окончательную программу длиннее, чем было бы, с несколькими дополнительными функциями.

  • В настоящее время это не предполагает, что ключевое слово (по крайней мере, на английском языке) всегда будет содержать один и тот же набор букв, и поэтому восполняет это, удаляя дубликаты, манипулируя шахматной доской и т. Д. Эта возможность, очевидно, не требуется OP, таким образом, я в настоящее время играю в гольф от лишних, ненужных байтов, которые занимают. Обновлено для игры в гольф.


2

JavaScript (ES6), 946 938 953 байта

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}

В выходные я видел, что для этого еще не было записи в JS, так что вот моя (последняя минута) попытка. Внедрение и игра в гольф это было так же безумно, как было весело!

Демо-фрагмент

Изменить: -8 байт

Понял, что были дополнительные скобки вокруг функции S,J,A,Q

Изменить: +15 байт

Обновлена ​​логика message idразмещения символа в последнем сообщении (теперь 1 индексируется, а 0 не включает его в вывод).

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('')
    g = chainAdd(r,10).split('').map((x,i)=>(s1[i]- -x)%10);

    t = g.map(i=>s2[+i]).join('');
    u=chainAdd(t,60).substr(10)

    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;
}

Примечания

  • Для кодирования сообщения используется шахматная доска, подобная следующей:

      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 #
    
  • Все строки указаны в верхнем регистре. Сообщение имеет буквенно-цифровую латиницу (плюс .и #), и все знаки препинания (кроме точек) должны быть удалены. Все числа уже должны быть отмечены с #. В ключевой фразе должны быть удалены все знаки препинания / пробелы.

  • Полученное сообщение возвращается в виде массива из 5-значных строк.

Улучшения

  • Я чувствую, что есть способ злоупотребить "Все языки", чтобы сохранить некоторые байты. Если бы у меня было больше времени, я бы изменил это так, чтобы предположить, что этот язык похож на гавайский, в котором всего 12 букв.

  • Любые предложения по гольфу всегда приветствуются.


Не могли бы вы добавить фрагмент, чтобы я мог убедиться, что это работает? Если это произойдет, то я могу наградить вас за вознаграждение.
Р. Кап

@ R.Kap Конечно, я добавил демонстрационный фрагмент
SLuck49

Хм ... в демо, message identifierкажется, что 7далеко от конца, а не 6. Кроме того, в вашей версии без гольфа то же самое, Idкажется, 6далеко от начала, а не до конца.
Р. Кап

@ R.Kap Да, была ошибка, когда я впервые опубликовал ее (не должно быть исправлено в ungolfed). Что касается игры в гольф, я предположил, что индекс был 0, потому что в противном случае, если 1означает самый конец, куда, по вашему мнению, message identifierдолжен идти 0? Я могу изменить это, мне просто нужно знать.
SLuck49

Я бы сказал , что на следует исключить из вывода. 0message identifier
Р. Кап

1

Clojure, 1197 1212 байт

О, я измотан.

Обновление: добавлено требуемое место случайного разбиения сообщения, версия без заглавных букв использует то же место, что и в приведенном примере, так что алгоритм может быть легко проверен.

(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)))))

Образцы входных данных и контрольный пример:

(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)))))

У него есть альтернативная реализация на шахматной доске, Bтакая же, как в определении задачи. Но в представлении используется другой, в котором неиспользуемые алфавиты сначала заполняют 2-ю строку, а затем 3-ю вместо столбцов за столбцом.


Рассматривал написание решения Clojure, но моя голова взорвалась, когда я читал вопрос. Сколько времени это заняло, чтобы написать?
Carcigenicate

Может быть, 3 часа во время просмотра Youtube на стороне. Это началось довольно легко, но я собирался сдаться, когда мне пришлось осуществить вторую «нарушенную транспозицию». Теперь это coordsгенерируется дважды, сначала генерируя треугольную форму, а затем заполняя любые отсутствующие координаты. Кроме того, «дополнение к длине умножения N» может иметь более элегантное решение, чем объединение N - 1 элементов и разбиение на длины N.
NikoNyrh

Черт, я забыл изменить жестко запрограммированную точку разделения на (split-at 49 mymsg)49, что-то вроде этого, (rand-int(count mymsg))поэтому правильный ответ будет чуть более 1200 байтов. zzz
NikoNyrh

Черт. Вероятно, все еще меньше, чем ответ c, хотя.
Carcigenicate
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.