# You're on a 8 day streak!

Duolingo, the language learning app, has a lot of things going for it, but there is one major issue that drives me crazy. It tells me how many days in a row I've used the app with a message like You're on a 7 day streak! Setting aside hyphenation and whether the number should be spelled out, this works fine for most numbers, but is indisputably wrong when it says You're on a 8 day streak! I'm not using it to learn English but this is still unfortunate behavior for a language app.

You're going to help out the Duolingo team by writing a complete program or function that figures out whether a given number should be preceded by a or an. A number is preceded by a if its pronunciation in spoken English begins with a consonant or semivowel sound, and preceded by an if its pronunciation begins with a vowel sound. Thus the only numbers preceded by an are those whose pronunciation begins with eight, eleven, eighteen, or eighty.

Presumably the Duolingo dev team left this bug in because they ran out of space for more source code in the app, so you need to make this code as short as possible in the hopes they can squeeze it in.

Your code must take an integer from 0 to 2,147,483,647 and output a or an. A trailing newline is optional. For the purposes of this challenge, 1863 is read as one thousand eight hundred and sixty-three, not eighteen hundred and sixty-three.

Test cases:

0 → a
8 → an
11 → an
18 → an
84 → an
110 → a
843 → an
1111 → a
1863 → a
8192 → an
11000 → an
18000 → an
110000 → a
180000 → a
1141592 → a
1897932 → a
11234567 → an
18675309 → an

• Is this endorsed by Duolingo? If not, you should get them to pay us for improving the language on a language learning site. Dec 17 '15 at 7:38
• Is 1100 (an) eleven hundred or (a) one thousand and one hundred? Dec 17 '15 at 8:04
• Bilbo would disagree with some of your test cases. :) Dec 17 '15 at 9:26
• @Zaibis: "one" here is pronounced like "wun", which has a consonant sound. Hence, "a one thousand and one hundred day streak". Dec 17 '15 at 10:26
• They probably left this bug because they thought no one would reach a 8 day streak. Dec 17 '15 at 11:36

## Python 2, 60 bytes

lambda n:'a'+'n'[:n[0]=='8'orn[:2]in len(n)%3/2*'118']


An anonymous function. Adds an n if either:

• The first digit is 8
• The first two digits are 11 or 18, and the length is 2 modulo 3.
• I know this is a super old question, but I think  n>='8'  saves three bytes.
– Lynn
May 24 '18 at 18:01
• @Lynn Won't that mess up nines though?
– xnor
May 24 '18 at 18:05
• Oh, of course! I was fooled by the test suite :)
– Lynn
May 24 '18 at 18:48

# Pyth, 23 bytes

<>n\8hz}hjsz^T3,hT18"an


This selects how many letters to slice off the end of "an" by checking whether the first letter is not an 8 and that the first digit of the number when considered in base 1000 is neither 11 nor 18. The resulting boolean is the number of characters to slice of the end.

• Very creative. Also, terrifying. Dec 20 '15 at 12:50

# GNU Sed, 32

Score includes +1 for -E option to sed.

s/^8.*|^1[18](...)*$/an/ t ca :  Try it online. • Remove groups of 3 digits from the end of each number until there is only 1 to 3 digits left • Match any number starting with 8 or exactly 11 or 18 and change to an • Change all other numbers to a Thanks to @MartinBüttner for his retina approach that saved 10 bytes. # Shell + bsd-games, 30 number -l|sed '/^e/{can q};ca'  Input read from STDIN. number converts a decimal string into words. It is then a simple matter to decide whether or not the result begins with e. • +1 for using bsd-games, I didn't actually think they'd ever be useful :) Dec 17 '15 at 15:38 • @ASCIIThenANSI yep, bsd-games are useful here and there :) Dec 17 '15 at 17:28 ## Retina, 27 bytes This isn't very different from DigitalTrauma's Retina answer, but they insisted I post this myself. ^8.*|^1[18](...)*$
an
\d+
a


Try it online.

The first regex replaces all relevant numbers with an, and the second replaces all remaining numbers with a. This works for the same bytes:

^8.*|^1[18](...)*$n ^\d* a  • +1 this is almost the same level of abuse of regex as primality testing :) – cat Dec 17 '15 at 12:07 • And the good thing is, Duolingo is actually written in Retina so it should be a breeze to integrate this. Or wait, what language was it? Dec 18 '15 at 18:22 • @ceasedtoturncounterclockwis I'm being told it's actually written in Hexagony, but they wrote a Retina-to-Hexagony transpiler, so this shouldn't be an issue. Dec 18 '15 at 18:24 # C++, 101 This is my challenge, so this isn't meant to be a competitive answer. Just wanted to see how short I could get it in C++. String operations are just too verbose so this is done with math. I feel like there must be a way to get that condition smaller but I can't quite figure it out. const char*f(int i){int n=0,d=0;for(;i;(!(d++%3)&(i==18|i==11))|i==8?n=1:0,i/=10);return n?"an":"a";}  ## Mathematica, 53 bytes If[#~IntegerName~"Words"~StringStartsQ~"e","an","a"]&  A solution using string processing would actually end up being longer. ## PostScript, 119 113 characters 10 string cvs dup 0 get 56 eq exch dup length 3 mod 2 eq{0 2 getinterval dup(11)eq exch(18)eq or or}{pop}ifelse  With test code: /An { 10 string cvs dup 0 get 56 eq exch dup length 3 mod 2 eq{0 2 getinterval dup(11)eq exch(18)eq or or}{pop}ifelse } def /ShouldBeFalse [ 0 110 1111 1863 110000 180000 1141592 1897932 ] def /ShouldBeTrue [ 8 11 18 84 843 8192 11000 18000 11234567 18675309 ] def () = (ShouldBeFalse) = ShouldBeFalse {An =} forall () = (ShouldBeTrue) = ShouldBeTrue {An =} forall  ## JavaScript (ES6) 706146 38 bytes n=>/^8|^1[18](...)*$/.test(n)?'an':'a'


Community wiki because the current solution is so different than my original. Thanks everyone!

• that makes sense. Thanks for explaining. Dec 18 '15 at 2:54
• @Pavlo Very nice, I forgot about single expressions after discovering the eval trick! I knew there had to be a better regular expression as well, but I couldn't figure anything out that was shorter. Thank you! Dec 19 '15 at 20:43
• @Pavlo Sweet, updated again! Learning lots, thank you very much :) Dec 20 '15 at 2:56
• URGH! Forgot to shave 2 bytes! Here is the final one: n=>/^8|^(?=1[18])..(\d{3})*$/.test(n)?'an':'a' (es6fiddle.net/iiehl1ex). It is 46 bytes long. Dec 20 '15 at 12:06 • @ScottKaye The code is very simple: It checks if it starts with 8, if it starts with 1[18] and if the length of numbers is 2 * (3n). Basically, it is your entire code, but within a regular expression. Dec 20 '15 at 15:13 ## Seriously, 43 40 bytes 9⌐9τk,;;$l3@\3*╤@\(íub)$#p'8=)XkΣ'n*'a+  The strategy here is to only look at the 1, 2, or 3 most significant digits, by integer-dividing the input by the largest value 10^(3n) that is less than the input. Try it online Explanation: 9⌐9τk,;;$l3@\3*╤@\(íub)$#p'8=)XkΣ'n*'a+ 9⌐9τk push [11, 18] ,;; push 3 copies of input (n)$l                               get length of n as string (effectively floor(log(n,10)))
3@\3*╤                         get largest 10^(3n) less than the length
@\                       get most significant digits of n (x)
(í                     bring list from back, push the index of x in the list or -1 if not in list
ub)                  increment by 1, convert to boolean, shove to bottom
$#p push first digit from n (as string) '8= push 1 if "8" else 0 )X shove to bottom of stack, discard remaining digits kΣ'n* push sum of stack, push a string containing that many "n"s 'a+ push "a", concatenate  # Retina, 34 Direct translation of my sed answer: +(.)...$
$1 ^8.*|^1[18]$
an
\d+
a


Try it online.

One byte saved thanks to @Timwi.

# Perl 6,  31  30 bytes

{'a'~'n'x?/^8|^1<[18]>[...]*$/} # 31 bytes  {<a an>[?/^8|^1<[18]>[...]*$/]} # 31 bytes

{<a an>[?/^8|^[11|18][...]*$/]} # 31 bytes  {'a'~'n'x?/^8|^1[1|8][...]*$/} # 30 bytes

{<a an>[?/^8|^1[1|8][...]*$/]} # 30 bytes  ( Perl 6 uses [ ] in regexes for non-capturing ( ), and uses <[ ]> for character sets ) Usage: # store it in a lexical code variable for ease of use my &code = {...} my @a = <0 110 1111 1863 110000 180000 1141592 1897932>; my @an = <8 11 18 843 8192 11000 18000 11234567 18675309>; say @a.map: &code; say @an.map: &code;  (a a a a a a a a) (an an an an an an an an an)  ## PostScript, 109 bytes (a)exch 10 string cvs dup[exch length 3 mod 2 eq{(11)(18)}if(8)]{anchorsearch{pop pop(an)exch}if}forall pop =  The code verifies if the number starts with certain prefixes. The prefix 8 is always checked (eight, eighty-something, eight-hundreds-and), but 11 and 18 (eleven and eighteen) are checked only when the number of digits is a multiple of 3 plus 2. We start with a tentative result of a and when a prefix is found the result gets replaced with an. anchorsearch is used to avoid extracting a prefix from the string. Even if a match is found we continue verifying the rest of the prefixes – why waste 5 bytes for the  exit? –, but because the original string gets replaced with a we are sure not to get any false positives. To return the a-or-an result on the operand stack instead of printing it, remove the trailing  = (resulting length: 107 bytes). Test code: /DO { ... the code above ... } def (Should be "a"s:) = {0 110 1111 1863 110000 180000 1141592 1897932} { DO } forall (Should be "an"s:) = {8 11 18 84 843 8192 11000 18000 11234567 18675309} { DO } forall flush  ## PostScript (with binary tokens), 63 bytes (a)’>10’¥’1’8[’>’b3’j2’={(11)(18)}if(8)]{’${’u’u(an)’>}if}’I’u=


The ’ are bytes with the value 146 (decimal), ¥ is a 165 and $ is a 3. All others are printable 7-bit ASCII characters. This is the same as my PostScript [pure ASCII] version, but uses binary tokens where this helps reduce the total length. I post it separately for 3 reasons: • In the general case, an implementation that minimizes the ASCII code is not necessarily the same as the one minimizing the binary version. Some longer piece of ASCII PostScript code could compress better than another and its corresponding binary version be shorter. • Binary code is not suitable everywhere, so a pure ASCII answer may be preferred even if longer. • It wouldn’t be fair to compare the length of a pure ASCII PostScript answer with one using binary encodings. # Pyth, 29 31 ?:_ec_z3"(^18$|^11$|^8)"0"an"\a  Reverses the string, splits it into sections of three, reverses it again, then chooses the appropriate ending. • This is wrong on the input 111 - it gives an Dec 17 '15 at 5:41 • You're right. Fixed. Dec 17 '15 at 22:00 # Python 3, 110939176747065 64 bytes Here is a long one, but a simple one. Edit: Corrected with thanks to isaacg. Saved some whitespace after the comparisons. Many bytes saved thanks to Timwi, Mego, benpop and Alissa. n=input();print("a"+"n"*(len(n)%3>1and n[:2]in"118"or"8"==n[0]))  or for the same number of bytes. n=input();print("a"+"n"[:len(n)%3>1and n[:2]in"118"or"8"==n[0]])  Ungolfed: def a(): n=input() if "8"==n[:1]: a = "n" elif len(n)%3 == 2 and (n[:2] in ["11", "18"]): a = "n" else: a = "" return "a"+a  • This is incorrect on the input 843, "eight hundred fourty-three", which should be an. Dec 17 '15 at 5:51 • @isaacg Not only are you correct, but this simplifies my code immensely. Thanks! It turns out I was only looking at eight, eight thousand, eight million, while ignoring cases like eighty and eight hundred. Dec 17 '15 at 5:54 • Why (-~len(n)%3)<1 instead of len(n)%3==2? Dec 17 '15 at 5:59 • Could (n[:2]=="11"or n[:2]=="18") be shortened to "118".contains(n[:2])? Dec 17 '15 at 6:00 • Or even n[:2]in"118"? Dec 17 '15 at 6:03 # Java 10, 102 bytes n->{var N=n+"";return(n>9&&"118".contains(N.substring(0,2))&N.length()%3>1)|N.charAt(0)==56?"an":"a";}  Try it online. Explanation: n->{ // Method with integer parameter and String return-type var N=n+""; // Input integer as String return(n>9&& // If the input has at least two digits, "118".contains(N.substring(0,2)) // and the first two digits are "11" or "18", &N.length()%3>1) // and the length modulo-3 is 2 |N.charAt(0)==56? // Or if the first digit is an '8': "an" // Return "an" : // Else: "a";} // Return "a"  # Japt, 28 27 bytes 'a+'npUì v ¥8ª[B18]d¥UìA³ v  Try it online! ### Unpacked & How it works 'a+'npUì v ==8||[B18]d==UìAp3 v 'a+'np "a" + "n".repeat(...) Uì v ==8 First digit in decimal == 8 || or... [B18]d [11,18].some(...) ==UìAp3 v == First digit in base 10**3  • You can replace 1e3 with A³ May 24 '18 at 0:45 # GNU sed -r + BSD number, 34 bytes s/(e?).*/number &/e s//a\1/ y/e/n/  First we convert to English number. Then delete everything except a possible initial e, and prefix with a. Then convert the e (if present) to n. The only golfing trick is to match the optional e in the first substitution, so we can reuse the pattern in the following line. ## Demo for i in 0 8 11 18 84 110 843 1111 1863 8192 \ 11000 18000 110000 180000 1141592 1897932 11234567 18675309 do printf "%'10d → %s\n"$i $(./66841.sed <<<$i)
done

         0 → a
8 → an
11 → an
18 → an
84 → an
110 → a
843 → an
1,111 → a
1,863 → a
8,192 → an
11,000 → an
18,000 → an
110,000 → a
180,000 → a
1,141,592 → a
1,897,932 → a
11,234,567 → an
18,675,309 → an


# Perl 5-p, 26 bytes

$_=a.n x/^8|^1[18](...)*$/


Try it online!

# 05AB1E, 26 21 bytes

₄внŽ4$2ôQàI8Å?+>„ans∍  Explanation: ₄в # Convert the (implicit) input-integer to base-1000 as list н # Pop and push the first integer in this list # i.e. 11234567 → [11,234,567] → 11 # i.e. 110000 → [110,0] → 110 # i.e. 8192 → [8,192] → 8 Ž4$      # Push compressed integer 1118
2ô    # Split it into parts of size 2: [11,18]
Q   # Check for each if it's equal to the integer
à  # Pop and push the maximum to check if either is truthy
#  11 → [1,0] → 1
#  110 → [0,0] → 0
#  8 → [0,0] → 0
I           # Push the input-integer again
8Å?        # Check if it starts with an 8
#  11234567 → 0
#  110000 → 0
#  8192 → 1
+           # Add the two checks together
>          # Increase it by 1
#  1+0 → 1 → 2
#  0+0 → 0 → 1
#  0+1 → 1 → 2
„an       # Push string "an"
s∍     # And shorten it to a length equal to the integer
#  2 → "an"
#  1 → "a"
#  2 → "an"
# (after which the result is output implicitly)


See this 05AB1E tip of mine (section How to compress large integers?) to understand why Ž4$ is 1118. ## TeaScript, 35 bytes [18,11,8,80]I(+xh(x.n%3¶3©?'an':'a'  Try it here. ### Explanation  xh(x.n%3¶3 get the relevant digits from the input xh compiles to x.head which returns the first n chars of x (implicit input) ¶ ('\xb6') compiles to || + cast the result to an integer since .includes does a strict comparison © ('\xa9') compiles to )) [18,11,8,80] array of the special cases I( I( is an alias for .includes( which returns true if the array contains the argument ?'an':'a' finally, return 'an' if the array contains the number, 'a' otherwise  ## Python 2.7, 66 s=input() print['a','an'][s[:1]=='8'or s[:2]in len(s)%3/2*'118']  Obviously not as short as the lambda one. # QuadS, 32 bytes 'a',⍵ ^8.*|^1[18](...)*$
⊃⍵L/'n'


Try it online!

# Stax, 25 bytes

â-x▬♪°∞▄'δL|÷æ╪║>₧4¢ÿ·7åR


Run and debug it

Unpacked, ungolfed, and commented, it looks like this.

Vk|Eh       get the first "digit" after converting to base 1000
AJ|Eh       get the first "digit" after converting to base 100
c20>9*^/    if the result is greater than 20, divide it by 10 again
"AMj"!#     is the result one of [8, 11, 18]?
^           increment by 1
.an(        keep that many characters of the string "an"


Run this one

# Whitespace, 243 bytes

[S S S T    T   S S S S T   N
_Push_97_a][T   N
S S _Print_as_character][S S S T    N
_Push_1][S N
S _Duplicate_1][S N
S _Duplicate_1][T   N
T   T   _Read_STDIN_as_integer][T   T   T   _Retrieve_input][N
S S S T N
_Create_Label_LOOP][S N
T   _Swap_top_two][S S S T  N
_Push_1][T  S S S _Add][S N
T   _Swap_top_two][S N
S _Duplicate][S S S T   T   S S T   S S N
_Push_100][T    S S T   _Subtract][N
T   T   T   N
_If_negative_jump_to_Label_TWO_DIGITS][S S S T  S ST    S N
_Push_10][T S T S _Integer_division][N
S N
S T N
_Jump_to_Label_LOOP][N
S S T   N
_Create_Label_TWO_DIGITS][S N
S _Duplicate][S S S T   S S S N
_Push_8][T  S S T   _Subtract][N
T   S S S N
_If_zero_jump_to_Label_PRINT_n][S N
S _Duplicate][S S S T   S T T   N
_Push_11][T S S T   _Subtract][N
T   S S N
_If_0_jump_to_Label_2_MOD_3][S N
S _Duplicate][S S S T   S S T   S N
_Push_18][T S S T   _Subtract][N
T   S S N
_If_0_jump_to_Label_2_MOD_3][S S S T    S ST    S N
_Push_10][T S T S _Integer_division][S N
S _Duplicate][N
T   S N
_If_0_jump_to_Label_EXIT][N
S N
T   N
_Jump_to_Label_TWO_DIGITS][N
S S S N
_Create_Label_2_MOD_3][S N
T   _Swap_top_two][S S S T  T   N
_Push_3][T  S T T   _Modulo][S S S T    S M
_Push_2][T  S S T   _Subtract][N
T   T   N
_If_negative_jump_to_Label_EXIT][N
S S S S N
_Create_Label_PRINT_n][S S S T  T   S T T   T   S N
_Push_110_n][T  N
S S _Print_as_character][N
S S N
_Create_Label_EXIT]


Letters S (space), T (tab), and N (new-line) added as highlighting only.
[..._some_action] added as explanation only.

Try it online (with raw spaces, tabs and new-lines only).
Program stops with an error: No exit found.

Explanation in pseudo-code:

Print "a"
Integer input = STDIN as integer
Integer counter = 1
Start LOOP:
counter = counter + 1
If(input < 100)
input = input integer-divided by 10
Go to next iteration of LOOP

function TWO_DIGITS:
If(input == 8)
If(input == 11 or input == 18)
input = input integer-divided by 10
If(input == 0)
Exit program
Recursive call to TWO_DIGITS

function 2_MOD_3:
If(counter modulo-3 != 2)
Exit program

function PRINT_n:
Print "n"
Exit program


# C++, 80 79 bytes

[](int i){for(;i>999;i/=1e3);return i-11&&i-18&&i/100-8&&i/10-8&&i-8?"a":"an";}


It turned out 4 bytes shorter to explicitly test against 8xx and 8x than to have another /=10 loop, like this:

[](int i){for(;i>999;i/=1e3);for(i==11|i==18?i=8:0;i>9;i/=10);return i-8?"a":"an";}


## Demo

#include <locale>
#include <cstdio>
int main(int argc, char**argv)
{
auto const f =
[](int i){for(;i>999;i/=1e3);return i-11&&i-18&&i/100-8&&i/10-8&&i-8?"a":"an";}
;

std::locale::global(std::locale{""});
for (int i = 1;  i < argc;  ++i) {
auto const n = std::stoi(argv[i]);
printf("%'10d → %s\n", n, f(n));
}
}

         0 → a
8 → an
11 → an
18 → an
84 → an
110 → a
843 → an
1,111 → a
1,863 → a
8,192 → an
11,000 → an
18,000 → an
110,000 → a
180,000 → a
1,141,592 → a
1,897,932 → a
11,234,567 → an
18,675,309 → an

• I don't know C++ too well, but can i/=1000 be i/=1e3, and can all && become &? May 25 '18 at 8:53
• It indeed seems to work: Try it online. May 25 '18 at 9:02
• @Kevin - I did at one point have 1e3 there; I changed it during debugging and forgot to change it back. The && can't all be &, because subtraction yields integers and not booleans - e.g. 19-11 is 8, and 19-18 is 1; see that 8 && 1 is true, but 8 & 1 is false. We could use & but we'd need to change - to != and also add parentheses. May 25 '18 at 10:01
• Ah of course.. & indeed doesn't work here, my bad. Btw, why don't you add a TIO-link to your answer as well? May 25 '18 at 11:36

# Perl, 7155 49 bytes

$_=<>;$_=/^8/||/^1[18]/&&length%3==1?'an':'a';say


I knew the ternary operator would help one day...

Let me break this down.

• $_=<> accepts a number as input. • The big $_=... block will set the value of $_ after it's used. • ...?...:... is the ternary operator. If the condition (first argument) is true, it returns the second argument. Otherwise, it returns the third. • /^8/||(/^1[18]/&&length%3==2) checks to see if the number starts with 8 or begins with 11 or 18 (1[18] accepts either) and has a length mod 3 of 2. • If that's true, $_ is set to an. Otherwise, it's set to a.
• It then prints out the contents of $_ (either a or an) with say. Changes • Saved 16 bytes thanks to msh210. • Saved 6 bytes by removing parens and using defaults. • $_=<>;$_=(/^8/)||/^1[18]/&&length($_)%3==1?'an':'a';say saves a few bytes. (Though the number to compare to it depends on what your newline character is, but that doesn't change the byte count.) Dec 17 '15 at 18:58
• @msh210 It looks like that's only 55 bytes meaning it saves 16 bytes. I'll add that in. Thanks! Dec 17 '15 at 19:14
• You're welcome. Oh, and you can drop the first parens (I assume. I haven't tested). I'd think you could also change length($_) to length (or at least drop the parens) but that's not working for me for some reason. Dec 17 '15 at 19:20 • @msh210 Yep, you can drop the parens and ($_) to get $_=<>;$_=/^8/||/^1[18]/&&length%3==1?'an':'a';say, which is only 49 bytes. Dec 17 '15 at 19:23
• You can save some bytes by using -p instead of $_=<> and say, y///c instead of length, and dropping the quotes around a and an: perl -pe'$_=/^8/||/^1[18]/&&y///c%3==2?an:a' (34 bytes + 1 for -p). Note that input cannot end with a newline: echo -n 11 | perl -pe'...'. This also fixes a bug: length%3==2 is parsed as length(%3)==2, not as length(\$_)%3==2`, so it always returns false. Dec 17 '15 at 21:01