# Write a pluralization function for Russian

In English, nouns can take on two different forms depending on whether they are singular (one) or plural (anything else). For example, we would say "1 dog" but "2 dogs", "0 dogs", "57 dogs" and so forth.

In Russian, there are three categories. Instead of "1 dog, 2 dogs, 5 dogs", in Russian it would be "1 собака, 2 собаки, 5 собак".

The categories are divided according to the following logic:

• "Singular": used for 1 and any number ending in 1, except for numbers ending in 11.
• Examples: 1 собака, 21 собака, 101 собака
• "Few": used for 2, 3, and 4, and any number ending in 2, 3, or 4 except for numbers ending in 12, 13, and 14.
• Examples: 2 собаки, 3 собаки, 4 собаки, 32 собаки, 43 собаки, 104 собаки
• "Many": anything that is not considered "Singular" or "Few".
• Examples: 0 собак, 5 собак, 11 собак, 13 собак, 25 собак, 111 собак, 114 собак

## The challenge

Given an integer input in the range [0, 1000], return 1 if it belongs to the "singular" category, 2 if it belongs to the "few" category, and 5 if it belongs to the "many" category.

Your program may be a function or it can use STDIN. You may print to STDOUT or return a value from the function

This is a code golf challenge, so the solution with the fewest number of bytes wins.

• @Phoenix Not in Russian. May 31 '17 at 15:58
• Why 1, 2, and 5 in particular? Also, why can't I use exit codes? May 31 '17 at 16:13
• @Phoenix That sounds so completely wrong to me - broken Russian - I've always used the form in the question and I find it correct, and apparently it is May 31 '17 at 20:10
• @CalculatorFeline If you start counting from 1, you get singular at 1, few first occurs at 2, many first show up at 5. Makes perfect sense :-) Jun 1 '17 at 6:48
• Counting in Russian is extremely hard. It's perhaps worth noting that the final digit determines the case. 1=Nominative singular 2,3,4=Genitive singular, 5-0 Genitive plural. This changes with the case of the phrase, and as there are 6 cases, there are 24 forms of 'one' (which is masculine), 24 forms of 'two' (which is feminine) and so on. It is said of the professor of Russian in my local university would be unlikely to be able to translate "with 2345 dogs", because 'with' demands the instrumental case (a hard one). Jun 1 '17 at 12:18

# Python 2, 36 bytes

lambda n:'5521'[n%~9/-3>>n/10%~9/-9]


Try it online!

Same length arithmetically:

lambda n:5/(n%~9/-3>>n/10%~9/-9or 1)


Let's first look at simpler code that doesn't account for teens.

lambda n:'5521'[n%~9/-3]


Here, we want a mapping of the one's digit to an output that works like

[5, 1, 2, 2, 2, 5, 5, 5, 5, 5][n%10]


But, rather than take n modulo 10 (%10), we can do n%-10, which maps to the intervals [-9..0] to give remainders:

> [n%~9 for n in range(10)]
[0, -9, -8, -7, -6, -5, -4, -3, -2, -1]


This is promising because the first two entries 0 and -9 are far apart, and they need to be sent to different outputs. Also, -10 can be shortened to ~9.

From here, floor-dividing by /-3 gives chunks of 3 with the right starting spot

> [n%~9/-3 for n in range(10)]
[0, 3, 2, 2, 2, 1, 1, 1, 0, 0]


To get the desired output, we now just need to map 0->5, 1->5, 2->2, 1->1, which we do with the string selection '5521'[_].

Now, we also need numbers ending in 11 to 15 to always give 5. We first do this by detecting whether then tens digit is 1. Taking n/10 to remove the last digit, we then apply %~9 as before to get the outcomes

[0, -9, -8, -7, -6, -5, -4, -3, -2, -1]


for respective final digits. The digit of 1 that we want to detect is mapped to the extremal value -9. Floor-dividing by -9 turns it to 1 and everything else to 0.

> [k%~9/-9 for k in range(10)]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]


Finally, we make this indicator being 1 always give output 5. This is done by bit-shifting the result of n%~9/-3 right by the indicator. The result of 0,1,2,3 always bit-shifts right to 0 or 1, which gives an output of 5 as desired.

• Please explain. May 31 '17 at 17:40

# Python 2, 45 bytes

lambda n,s='5122255555':(s+'5'*10+s*8)[n%100]


Try it online!

• o_O i'm blown away... I had 56 bytes :\ May 31 '17 at 16:34
• Also, isn't (s+'5'*10+s*9) 110 chars? May 31 '17 at 16:40
• @CalculatorFeline hmm, time to get a coffee @.@
– Rod
May 31 '17 at 16:43
• @Rod or a covfefe? May 31 '17 at 20:25

# Perl 5, 26 bytes

25 bytes of code + -p flag.

$_=/1.$|[5-90]$/?5:2-/1$/


Try it online!

For one more byte, there is $_=/(?<!1)[1-4]$/?2-/1$/:5. Explanations: (on the 27 bytes version; the 26 is quite symmetrical) Both "singular" and "few" end with "not a 1 followed by a digit from 1 to 4" (tested with (?<!1)[1-4]$/). In those case, the result is 2, minus 1 if the number ends with 1 (2-/1$/). Otherwise, the result if 5. • tfw Perl beats 05AB1E by a fair amount. May 31 '17 at 16:20 # JavaScript (ES6), 53494840393837 36 bytes n=>/[05-9]$|1.$/.test(n)?5:1+(n%5>1)  ## Try it f= n=>/[05-9]$|1.$/.test(n)?5:1+(n%5>1) oninput=_=>o.innerText=f(+i.value);o.innerText=f(i.value=0) <input id=i type=number><pre id=o> • 1[1-4] can be 1. and /1$/.test(s) could be +s%10==1. Never forget unary +! May 31 '17 at 16:43
• Thanks, @CalculatorFeline - well-spotted on the first one :) May 31 '17 at 16:54
• I don't think you need the unary + at all, s%10 should convert s to a number. May 31 '17 at 16:55
• Yup, just realised that too, @ETHproductions. May 31 '17 at 16:56
• n%10 -> n%5 saves a byte Jun 1 '17 at 11:48

# Jelly,  19  18 bytes

DµṖṚi1’ȧṪị“M;ọ6’D¤


Try it online! or see the three groups from 0 to 1000 inclusive in this test suite.

### How?

DµṖṚi1’ȧṪị“M;ọ6’D¤ - Main link: non-negative number, n  e.g. 301      311      313
D                  - cast to decimal list                [3,0,1]  [3,1,1]  [1,3,3]
µ                 - monadic chain separation, call that d
Ṗ                - pop d                               [3,0]      [3,1]    [1,3]
Ṛ               - reverse                             [0,3]      [1,3]    [3,1]
1             - literal 1
i              - first index of (0 if not found)      0          1        2
’            - decrement                           -1          0        1
Ṫ          - tail d                               1          1        3
ȧ           - logical and                          1          0        3
“M;ọ6’   -   base 250 literal = 1222555555
D  -   cast to decimal list [1,2,2,2,5,5,5,5,5,5]
ị         - index into (1-based and modular)     1          5        2

• Explanation please. May 31 '17 at 16:47
• @CalculatorFeline still working on golfing... May 31 '17 at 16:47
• @CalculatorFeline well I can't find better; explanation added. May 31 '17 at 17:34
• In what character encoding those 18 characters can be represented by 18 bytes? Jun 1 '17 at 8:59
• @exebook Jelly uses its own code page Jun 1 '17 at 10:26

# 05AB1E, 38 19 bytes

Uses the index-trick from Rod's python answer

•1rê¢°•©5T×®9×JIт%è


Try it online!

Explanation

•1rê¢°•              # push the number 5122255555
©             # store a copy in register
5T×          # push 5 repeated 10 times
®         # retrieve the first number from register
9×       # repeat it 9 times
J      # join everything to string
Iт%   # push input mod 100
è  # use this to index into the string of digits

• You're losing to Perl, I think something is wrong here. May 31 '17 at 16:21
• @Phoenix: Yeah. Either this challenge is well suited to regex or I'm doing something terribly wrong :) To be fair, Perl is often pretty golfy. May 31 '17 at 16:27
• @Enigma ... and Perl golfers are often very good, right? ;-)
May 31 '17 at 16:32
• @Dada: Very true! May 31 '17 at 16:33
• Explanation please. May 31 '17 at 16:47

# PHP>=7.1, 44 Bytes

<?=$argn[-2]!=1&($m=($argn+9)%10)<4?2-!$m:5;


Online Version

• 3 bytes shorter: <?=$argn[-2]!=1&($m=($argn+9)%10)<4?2-!$m:5;. Jun 1 '17 at 6:07

# MCxxxx Assembly, 123 Bytes

e:slx x0
mov x0 acc
dst 2 0
tlt acc 11
-tgt acc 14
-jmp v
+dgt 0
teq acc 1
+mov 1 x1
+jmp e
tlt acc 5
+mov 2 x1
v:-mov 5 x1


### Note:

TiO doesn't support this language, which is used in the Zachtronics game Shenzhen I/O, so there's no link to test this.

## Explanation:

This is a function that takes input through XBus port x0, and outputs through port x1. It's too long to execute on an MC4000, but fits nicely into the memory of an MC6000. XBus ports, for those unfamiliar, allow the transmission of discrete packets of digital data.

One piece of information that may be helpful in reading this: in MCxxxx assembly, test instructions set a flag that indicates which branch should be taken. Lines that begin with + are only executed if the most recent test returned true, and lines that begin with - are only executed if the test was false.

Line by line:

e:slx x0    # Label this line e, then sleep until input is available on XBus port x0
mov x0 acc  # Move the input into register acc
dst 2 0     # Set the leftmost digit of the input to 0
tlt acc 11  # Test if the value in acc is less than 11
-tgt acc 14 # If it's not, check if it's greater than 14
-jmp v      # If it's not, jump to the line labeled v (the last line)
+dgt 0      # If either of the previous tests returned true,
#     set acc to the value of acc's rightmost digit
teq acc 1   # Test if acc equals 1
+mov 1 x1   # If it does, return 1
tlt acc 5   # Test if acc is less than 5
+mov 2 x1   # If it is, return 2
v:-mov 5 x1 # If the previous test is false, return 5


A note about scoring: MCxxxx assembly doesn't have functions per se, but this is as close to a function as you can get - it's a program that fits in a single execution node, takes input through one port and outputs through another. As a result, I've scored this like a function (i.e. without counting the bytes necessary to make a valid MCxxxx emulator file).

# Retina, 25 21 bytes

1.$5 Td512225 !.$


Try it online! -4 bytes thanks to Neil.

• Save 4 bytes by using !.$ to extract the last digit. Try it online! – Neil Jun 1 '17 at 8:47 • I knew there was a better way! Jun 1 '17 at 15:22 # Haskell, 62 58 bytes f n|s<-"5122255555"=(s++('5'<$[0..9])++cycle s)!!mod n 100


Try it online!

## Explanation

This builds the following string:

5122255555555555555551222555555122255555512225555551222555555122255555512225555551222555555122255555...

Which is a table where cell n contains the answer for the nth number. The table is only correct for the first 100 elements, hence the mod.

• Can you explain what is going on here? You could surely shorten it using f n|s<-"5122255555"=(s++('5'<\$[0..9])++cycle s)!!mod n 100 Jun 1 '17 at 12:53
• I didn't know that was possible! Jun 1 '17 at 13:00
• There are a lot more tips and tricks in codegolf.stackexchange.com/questions/19255/… really worth reading=) Jun 1 '17 at 13:02

Scala, 110 bytes

n=>Stream.iterate("512225555555555555555")(_=>"1222555555").flatMap(_.toCharArray).map(_.toInt).take(n-1).head


# Turtlèd, 35 bytes

!--.(1#0#)+.@3(1@1)(2@2)(3@2)(4@2),


Try it online!

This function requires that the input starts with a >, which I guess is ok because python2 uses input semi regularly, and that needs quotes.

Explanation:

!             input the number as a string, complete with the >
--.          wrap around to the end of the string, and then move one back. if this
is a single digit, we end up on the >,
otherwise we end up on the second to last digit. write the digit/>

(1#0#)    if it is 1, set the string to 0. this way it will always write 3 at the end.

+.       write the last digit (or 0 if the second last digit was 1)
@3      set the character variable to 3. this means if what was written was not
in (1, 2, 3, 4), then it will write 3 at the end
(1@1)    if the character written was a 1, set the character to be written
at the end to 1
(2@2)(3@2)(4@2)
if it is any of 2,3,4, set the character to be written at the end to 2
,    write the character that was set

• Does > serve a purpose in Turtled or is it an arbitrary character you've added to the input? Jun 2 '17 at 8:10