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. – Arc676 Dec 17 '15 at 7:38
• Is 1100 (an) eleven hundred or (a) one thousand and one hundred? – user3819867 Dec 17 '15 at 8:04
• Bilbo would disagree with some of your test cases. :) – Martin Ender 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". – El'endia Starman Dec 17 '15 at 10:26
• They probably left this bug because they thought no one would reach a 8 day streak. – PNDA Dec 17 '15 at 11:36

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. – Hellreaver Dec 20 '15 at 12:50

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

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 :) – ASCIIThenANSI Dec 17 '15 at 15:38 • @ASCIIThenANSI yep, bsd-games are useful here and there :) – Digital Trauma 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? – ceased to turn counterclockwis 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. – Martin Ender 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. – Daniel F 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! – Scott Dec 19 '15 at 20:43
• @Pavlo Sweet, updated again! Learning lots, thank you very much :) – Scott 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. – Ismael Miguel 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. – Ismael Miguel 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. 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. – isaacg 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. – Sherlock9 Dec 17 '15 at 5:54 • Why (-~len(n)%3)<1 instead of len(n)%3==2? – Timwi Dec 17 '15 at 5:59 • Could (n[:2]=="11"or n[:2]=="18") be shortened to "118".contains(n[:2])? – Timwi Dec 17 '15 at 6:00 • Or even n[:2]in"118"? – Timwi 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³ – Oliver 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


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

05AB1E, 26 bytes

g3%ô¬D11Qs18Q+I1£8Q+>„ans∍


Can probably be golfed a bit more, but its working.

Explanation:

g3%                  # Length of the input, modulo-3
#  11234567 → 8 → 2
#  110000 → 6 → 0
ô                 # Split the input into chunks of that size
#  11234567 and 2 → ['11', '23', '45', '67']
#  110000 and 0 → ['110000']
¬                # Take the Head (first element)
#  ['11', '23', '45', '67'] → '11'
#  ['110000'] → '110000'
D11Q            # Does it equal 11?
#  '11' and 11 → 1
#  '110000' and 11 → 0
s18Q            # Or does it equal 18?
#  '11' and 18 → 0
#  '110000' and 18 → 0
+           # Add them together (if it was either 11 or 18, this becomes 1)
#  1 and 0 → 1
#  0 and 0 → 0
I1£                  # Get the first character of the input
#  11234567 → '1'
#  110000 → '1'
8Q                # Does it equal 8?
#  '1' and 8 → 0
#  1 and 0 → 1
#  0 and 0 → 0
>         # Increase it by 1
#  1 → 2
#  0 → 1
„ans∍    # Push "an", and shorten it to a size equal to the result above
#  "an" and 2 → "an"
#  "an" and 1 → "a"


'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) Jump to function TWO_DIGITS input = input integer-divided by 10 Go to next iteration of LOOP function TWO_DIGITS: If(input == 8) Jump to function PRINT_n If(input == 11 or input == 18) Jump to function 2_MOD_3 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 Jump to function PRINT_n 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 &? – Kevin Cruijssen May 25 '18 at 8:53 • It indeed seems to work: Try it online. – Kevin Cruijssen 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. – Toby Speight 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? – Kevin Cruijssen May 25 '18 at 11:36 Perl 5-p, 26 bytes $_=a.n x/^8|^1[18](...)*$/  Try it online! 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.) – msh210 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! – ASCIIThenANSI 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. – msh210 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. – ASCIIThenANSI 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. – ThisSuitIsBlackNot Dec 17 '15 at 21:01

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` – isaacg Dec 17 '15 at 5:41
• You're right. Fixed. – Moose Dec 17 '15 at 22:00