# 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:

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

• We only have to map Ё and ё to Х̈ and х̈, right? And not also the reverse from X/x to E/e? – Kevin Cruijssen Oct 10 '19 at 7:41
• @Kevin Cruijseen Since Х̈ doesn't exist in Russian, it falls in don't care situation. – Dannyu NDos Oct 10 '19 at 8:03
• Are all non-Cyrillic characters in the input guaranteed to be standard ASCII? (32-126) – Arnauld Oct 10 '19 at 8:51
• Your 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 Oct 10 '19 at 15:25
• That's far too soon to be accepting a solution. – Shaggy Oct 10 '19 at 20:46

# 05AB1E, 164039 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.

• Undealt with Ё. – Dannyu NDos Oct 10 '19 at 7:33
• @DannyuNDos Fixed (at the cost of 24 bytes.. >.>) – Kevin Cruijssen Oct 10 '19 at 8:28
• The 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 Oct 10 '19 at 14:34
• @Grimy Hmm, sounds like something to report as a bug. And thanks for the -1. Nice approach with the XOR 16 – Kevin Cruijssen Oct 10 '19 at 14:47
• And the second part can be •2.w2γ•3äçDl‚vy«: for -6. – Grimmy Oct 10 '19 at 14:55

# 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
]

• 110 with some bitwise magic – Grimmy Oct 10 '19 at 15:36
• Easy fix, still 110 – Grimmy Oct 10 '19 at 16:20
• @Grimy Would you like to post it as a separate answer? – Arnauld Oct 10 '19 at 16:22
• No, I'm fine, thanks. – Grimmy Oct 10 '19 at 16:24

# 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.

• seems works without ё case 22 bytes – Nahuel Fouilleul Oct 10 '19 at 10:42
• @NahuelFouilleul That's not a U+0451 that you've got there... – Neil Oct 10 '19 at 10:46
• ok it was composed character, my bad, this is why it was working – Nahuel Fouilleul Oct 10 '19 at 10:57
• seems number of bytes not up to date isn't 27 – Nahuel Fouilleul Oct 10 '19 at 11:10
• @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 Oct 10 '19 at 22:13

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

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


Try it online!

• Does it work in Perl 6? – Dannyu NDos Oct 10 '19 at 10:44
• I don't know perl 6, 40 bytes ->52 bytes because there was an issue with ё instead of ё in input – Nahuel Fouilleul Oct 10 '19 at 11:01

# 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!

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

}
}
)


# Charcoal, 53 bytes

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


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

ＦＳ


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.