Russian Caesar cipher

11

The objective

Given a Russian text, encrypt it with Caesar cipher with key 16.

The basic Cyrillic alphabets

The basic Cyrillic alphabets are: (U+0410 – U+042F)

АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ

By the Caesar cipher, they are mapped to:

РСТУФХЦЧШЩЪЫЬЭЮЯАБВГДЕЖЗИЙКЛМНОП

The small letters (U+0430 – U+044F) are also mapped likewise.

Note the absence of Ё.

Rules

  1. Ё (U+0401) and ё (U+0451) are not considered basic, and are mapped to Х̈ (U+0425 U+0308) and х̈ (U+0445 U+0308), respectively.

  2. Any other characters are preserved.

Example

The following sentence:

В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!

Is encrypted to:

Т зрйре оур цшы сл жшвагб? Фр, эю дрымиштлщ нъчхьяыпа!

Dannyu NDos

Posted 2019-10-10T06:57:39.030

Reputation: 2 087

We only have to map Ё and ё to Х̈ and х̈, right? And not also the reverse from X/x to E/e? – Kevin Cruijssen – 2019-10-10T07:41:09.170

@Kevin Cruijseen Since Х̈ doesn't exist in Russian, it falls in don't care situation. – Dannyu NDos – 2019-10-10T08:03:21.037

Are all non-Cyrillic characters in the input guaranteed to be standard ASCII? (32-126) – Arnauld – 2019-10-10T08:51:33.880

@Arnauld You can assume so. – Dannyu NDos – 2019-10-10T09:19:15.210

8Your answer to @KevinCruijssen should probably be included as a short comment in the challenge to make this perfectly clear. (My initial version was converting x/X + diaeresis to E/e + diaeresis.) – Arnauld – 2019-10-10T15:25:49.393

5That's far too soon to be accepting a solution. – Shaggy – 2019-10-10T20:46:34.257

test case for Ё/ё: 'Ёжик живёт под ёлочкой' -> 'Х̈цшъ цштх̈в яюф х̈ыюзъющ' – mazzy – 2019-10-11T09:42:31.553

Answers

5

05AB1E, 16 40 39 33 bytes

63ÝD16^‚Ž4K+ç`‡•2.w2γ•3äçDl‚vy`«:

Would be just the first 15 bytes without the edge case of mapping Ёё to Х̈х̈.

-7 bytes thanks to @Grimy.

Try it online.

Explanation:

63Ý              # Push a list in the range [0,63]
   D             # Duplicate it
    16^          # Bitwise-XOR each value with 16: [16..31, 0..15, 48..63, 33..47]
        ‚        # Pair it with the initial [0,63] list we duplicated
         Ž4K     # Push compressed integer 1040
            +    # Add it to each integer in the inner lists
             `   # Push both lists separated to the stack again
              ‡  # Transliterate the characters in the first list to the second list
                 # in the (implicit) input-string
•2.w2γ•          # Push compressed integer 10251061776
       3ä        # Spit it into three parts: [1025,1061,776]
         ç       # Convert each to a character: ["Ё","Х","̈"]
Dl               # Create a lowercase copy: ["ё","х","̈"]
  ‚              # Pair them together: [["Ё","Х","̈"],["ё","х","̈"]]
   v             # Loop over both these lists `y`:
    y`           #  Push the characters of the current list separated to the stack
      «          #  Append the top two together: Х̈/х̈
       :         #  And replace the Ё/ё with Х̈/х̈ in the (modified) input-string
                 # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why Ž4K is 1040 and •2.w2γ• is 10251061776.

Kevin Cruijssen

Posted 2019-10-10T06:57:39.030

Reputation: 67 575

Undealt with Ё. – Dannyu NDos – 2019-10-10T07:33:18.477

@DannyuNDos Fixed (at the cost of 24 bytes.. >.>) – Kevin Cruijssen – 2019-10-10T08:28:43.390

2The first part can be 63ÝD16^‚Ž4K+ç`‡ for -1. It's a shame doesn't work on cyrillic letters (even though u and l do), that would save a lot. – Grimmy – 2019-10-10T14:34:55.227

2@Grimy Hmm, sounds like something to report as a bug. And thanks for the -1. Nice approach with the XOR 16 – Kevin Cruijssen – 2019-10-10T14:47:03.753

2And the second part can be •2.w2γ•3äçDl‚vy`«: for -6. – Grimmy – 2019-10-10T14:55:40.480

@Grimy Thanks again. :) After I added the 0776 to my compressed integer I thought about using something with 766 at the end with or as well, but couldn't really get it to work.. – Kevin Cruijssen – 2019-10-10T15:44:29.773

6

JavaScript (ES6),  148 ... 110  108 bytes

Saved 12 bytes thanks to @Grimy!

This function applies some maths to the code points.

s=>s.replace(/./g,s=>String.fromCharCode(...(n=s.charCodeAt()-304)%80-1?[(n^16*(n>0))+304]:[1060+n%48,776]))

Try it online!

How?

For each character in the input string, we define \$n\$ as its code point minus \$304\$.

 characters    | code points  | n
---------------+--------------+---------------
 А to Я        | 1040 to 1071 | 736 to 767
 а to я        | 1072 to 1103 | 768 to 799
 Ё             | 1025         | 721
 ё             | 1105         | 801
 ASCII         | 0 to 126     | -304 to -178

Then we apply the following logic:

// neither 'Ё' nor 'ё'?
n % 80 - 1 ?
  // output a single code point
  [
    // invert the case if this is a Cyrillic character
    (n ^ 16 * (n > 0))
    // and restore the original offset
    + 304
  ]
:
  // output two code points
  [
    // the first one is either 1061 (Х) or 1093 (х)
    1060 + n % 48,
    // the 2nd one is the combining diaeresis
    776
  ]

Arnauld

Posted 2019-10-10T06:57:39.030

Reputation: 111 334

110 with some bitwise magic – Grimmy – 2019-10-10T15:36:49.590

1Easy fix, still 110 – Grimmy – 2019-10-10T16:20:07.990

@Grimy Would you like to post it as a separate answer? – Arnauld – 2019-10-10T16:22:37.077

No, I'm fine, thanks. – Grimmy – 2019-10-10T16:24:42.777

5

Retina 0.8.2, 41 bytes

T`А-Па-пя-рЯ-Р`Ro
Ё
Х̈
ё
х̈

Try it online! Just a transliteration and a fixup of the two special cases. The characters to be transliterated are listed in mirror order so that Ro can be used to specify the reverse order for the transliteration.

Neil

Posted 2019-10-10T06:57:39.030

Reputation: 95 035

seems works without ё case 22 bytes

– Nahuel Fouilleul – 2019-10-10T10:42:40.747

@NahuelFouilleul That's not a U+0451 that you've got there... – Neil – 2019-10-10T10:46:10.210

ok it was composed character, my bad, this is why it was working – Nahuel Fouilleul – 2019-10-10T10:57:08.953

seems number of bytes not up to date isn't 27 – Nahuel Fouilleul – 2019-10-10T11:10:23.847

@NahuelFouilleul Unfortunately TIO always assumes I'm using ISO-8859-1 (most Retina programs do) but here I'm using UTF-8 so I have to count it in UTF-8 bytes. – Neil – 2019-10-10T22:13:14.583

3

Red, 236 166 bytes

func[s][rejoin collect[foreach c s[keep
case[c =#"Ё"["Х̈ "]c =#"ё"["х̈"]c < #"А"[c]any[c > #"Џ"c < #"ѐ"][(pick[#"А"#"а"]c < #"а")+ to 1 c - 1024 % 32]]]]]

Try it online!

Galen Ivanov

Posted 2019-10-10T06:57:39.030

Reputation: 13 815

3

Perl 5 (-pC -Mutf8 -MUnicode::Normalize=NFD), 37 bytes (27 chars)

$_=NFD$_;y;А-я;Р-ЯА-Пр-яа-п

Try it online!

Nahuel Fouilleul

Posted 2019-10-10T06:57:39.030

Reputation: 5 582

Does it work in Perl 6? – Dannyu NDos – 2019-10-10T10:44:51.423

I don't know perl 6, 40 bytes ->52 bytes because there was an issue with ё instead of ё in input – Nahuel Fouilleul – 2019-10-10T11:01:18.307

1

PHP (7.4), 199 chars, 333 bytes

<?=strtr($argn,array_combine(($s=str_split)(АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяёЁ,2),[...$s(РСТУФХЦЧШЩЪЫЬЭЮЯАБВГДЕЖЗИЙКЛМНОПрстуфхцчшщъыьэюяабвгдежзийклмноп,2),х̈,Х̈ ]));

Try it online!

Just a string replacement.

Night2

Posted 2019-10-10T06:57:39.030

Reputation: 5 484

1

PowerShell, 103 102 101 bytes

-join$(switch -c($args){Ё{'Х̈'}ё{'х̈'}default{[char](2*(($_-band16)-8)*($_-in1040..1103)+$_)}})

Try it online!

This script takes a splatted string. Unrolled:

-join$(
    switch -CaseSensitive ($args){
        Ё{'Х̈'}
        ё{'х̈'}
        default {
            $offset  = 2*(($_ -band 16)-8) # -band is 'Bitwise AND'
            $offset *= ($_ -in 1040..1103) # is basic Cyrillic alphabet?
            [char]($offset+$_)

        }
    }
)

mazzy

Posted 2019-10-10T06:57:39.030

Reputation: 4 832

1

Charcoal, 53 bytes

FS¿⁼Ё↥ι⁺§хХ⁼Ёϊ¿№…А¦ѐι℅⁺℅ι⎇‹↥ιР¹⁶±¹⁶ι

Try it online! Link is to verbose version of code. Explanation:

FS

Loop over the characters in the input.

¿⁼Ё↥ι

If the uppercase is equal to Ё...

⁺§хХ⁼Ёϊ

... then print the appropriate х or Х with its combining character...

¿№…А¦ѐι

... otherwise if it's a Cyrillic letter...

℅⁺℅ι⎇‹↥ιР¹⁶±¹⁶

... do some maths on its ordinal to create the character to output...

ι

... otherwise output the input character.

Neil

Posted 2019-10-10T06:57:39.030

Reputation: 95 035