Real-world programs often use libraries like ICU to support Unicode.

The puzzle is to create the smallest possible function that can determine whether a given Unicode character is lowercase, uppercase, or neither (a commonly used Unicode character property).

You may indicate the property in any way (whatever is shortest for your language); just explain what the output means in your answer.

Of course, using a language feature or a function from the standard library that already provides this distinction is forbidden.

This could potentially have some practical use, since some embedded programs have to choose between including a bulky library and not supporting Unicode.

  • 3
    \$\begingroup\$ Character classification changes (albeit in a stable manner) with each release of Unicode. Are you asking for a function that is hard coded to a particular version of Unicode? If so, then this is nothing more than an exercise in data compression, and one where I could choose the release to my advantage. As specified, the problem isn't actually practical, as your classification options are not a useful subset of the Unicode classifications, even for simple text processing. (There are many letters that are neither lowercase or uppercase, for example.) \$\endgroup\$ Commented Jul 14, 2011 at 1:55
  • 1
    \$\begingroup\$ Also, where can the database of character classifications be found? \$\endgroup\$
    – Joey Adams
    Commented Jul 14, 2011 at 6:31
  • 2
    \$\begingroup\$ @Joey (and his upvoter): unicode.org/Public/UNIDATA/UnicodeData.txt – the usual place, basically. \$\endgroup\$
    – Joey
    Commented Jul 14, 2011 at 8:01
  • 1
    \$\begingroup\$ MtnViewMark: Character classes are final for already-encoded characters, so nothing changes there, except characters are added. Also, how many uper- and lowercase characters have been added recently, especially in the BMP? I currently can only think of uppercase sharp s and the math alphabets (which are in the SMP, anyway). Agreed that it's only data compression, but for reasonably current versions of Unicode the number of upper- and lowercase characters is fairly stable. \$\endgroup\$
    – Joey
    Commented Jul 14, 2011 at 8:37
  • 1
    \$\begingroup\$ Voting to close as unclear. Challenges should be self-contained, so the list or qualifications for each output should be made clear. If linked as an external file, the file should be a file that will not change over time. \$\endgroup\$
    – mbomb007
    Commented Apr 16, 2018 at 21:00

4 Answers 4


C & C++, 1922 1216 bytes/chars (BMP only 1451 1005 bytes/chars)

This is the "shortest" solution that came to mind that is both valid C and C++ and that supports all of Unicode version 6.1, not just the BMP:

int s[]={-74,-68,31054,-50,-49,-48,-47,-46,-44,-43,-39,-38,-37,-34,-33,-32,-30,-29,-28,-27,-26,-25,-24,-23,-22,-17,-16,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,19,20,27,28,29,53195,31,34,36,42,49,50,52,54,58,59,62,64,66,73,81,98,102,103,140,194,263,275,290,1066,1222,1230,2685,2732,2890,3379,6009,21254,22391,30996,53209};
int x(int a,char*p){int c=0,i=0,b=-1;while(c>0||s[*p-32]){if(!c)c=s[*p-32]<0?-s[*p++-32]:1,i=s[*p++-32];b+=i,--c;if(a==b)return 1;}return 0;}

Please note that "shortest" in this case does not mean that the solution is in any way efficient; it simply means that it is the shortest source code solution. It does not require any Unicode support in the standard library, and in fact does not use the standard library at all. (Which I now understand is what code golf means ... I'm a newbie around here, what can I say.)

To build the lower and upper case data tables I worked as follows:

  1. Downloaded UnicodeData.txt.
  2. Extracted a list of all lines that included the text ";Ll;" or ";Lu;" to indicate a lower case letter or upper case letter respectively.
  3. Built two vectors of code points using the data from step 2.
  4. Converted each absolute code point value to a relative value, the difference between the previous code point and this code point. Because it is convenient for the difference to always be a positive integer (not zero or negative), I use -1 as the value of the first previous code point. In this way zero will not appear anywhere in the sequence.
  5. Used a form of run length encoding to compress runs of identical values into a pair of values. If the next value appears two or more times consecutively, replace the sequence with the negative of the length of the sequence and the value. Otherwise the value only appears once, so just use it as is.
  6. Terminated the compressed vector with zero to mark the end of the sequence.
  7. I noticed there were 96 unique values in the run length encoded vectors, so I built an array of the unique integers and used the index into that array for the lower and upper case vectors.

The above process compressed the vector of lower case code points from 1751 unique values to 350 mostly small non-unique values. In like fashion, the upper case code point vector went from 1441 values to 331 values.

Next I wrote the unique value vector out as a comma separated list of integers suitable for including in source code. I assumed int was 32 bits to avoid using long so that I could save an additional four characters / bytes of source code. Then I wrote out the lower and upper case vectors as strings, where each character in the string has a code 32 greater than its index into the unique integer array. This saves about half the characters necessary to encode the lower & upper case array through omission of comma characters. Three of the characters used have to be escaped (\", \, and \177). They are assigned to unique integers that only appear once in the data so as to minimize the size of the string literals.

What I wound up with was a global array of integers, two global arrays of characters, and a function. An array named s for the unique signed integers, an array named u for the character based upper case code point indexes, and another array named l for the character based lower case code point indexes. The function x takes code point a and pointer to an array of characters p and returns 1 if the code point is in the decompressed / normalized array of code points or 0 otherwise.

If you want to determine if code point cp is lower case, you call "x(cp, l)". If you want to determine if it is upper case, you call "x(cp, u)".

Since the point to this exercise is to make the smallest code to accomplish the task, I never save the decompressed / normalized data. I decompress it piecemeal as I need it every time I call it.

It would be possible to terminate the while loop early if the code point I'm checking has already been passed by during decompression. That would add extra code so I didn't bother, deciding I'd rather pay the speed penalty.

I admit the code is ugly. I did not write it this way originally. Here is the C++ version that I derived the compact C version from:

const int Ii[] = {-74,-68,31054,-50,-49,-48,-47,-46,-44,-43,-39,-38,-37,-34,-33,-32,-30,-29,-28,-27,-26,-25,-24,-23,-22,-17,-16,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,19,20,27,28,29,53195,31,34,36,42,49,50,52,54,58,59,62,64,66,73,81,98,102,103,140,194,263,275,290,1066,1222,1230,2685,2732,2890,3379,6009,21254,22391,30996,53209};

bool isXx(int cp, const char* Xx)
    int count = 0, step = 0, code = -1;

    while ((count > 0) || (Ii[*Xx-32] != 0))
        if (count == 0)
            count = (Ii[*Xx-32] < 0) ? -(Ii[*(Xx++)-32]) : 1;
            step = Ii[*(Xx++)-32];

        code += step;

        if (cp < code)

        if (cp == code)
            return true;

    return false;

inline bool isLl(int cp)
    return isXx(cp, Ll);

inline bool isLu(int cp)
    return isXx(cp, Lu);

In the end, the compact version takes 1922 1216 bytes (including CR+LF end of line sequences). The original version only takes 2271 1578 bytes.

To put this in context of the other solutions that only support the BMP, the equivalent compact version only takes 1451 1005 bytes (or 1800 1367 bytes for the original version).

  • \$\begingroup\$ I'm accepting this one even though it's a bit longer, because it supports astral-plane characters, does something clever, and explains how it was created. Also, it's probably possible to shorten the arrays by encoding them into character strings in some way. \$\endgroup\$ Commented Sep 16, 2012 at 21:54
  • \$\begingroup\$ Thanks. I considered encoding them in some other way, but the obvious solutions would have involved more code to decode, and I intuited that the overhead wouldn't make enough of a difference (though I didn't try so I could be wrong). \$\endgroup\$ Commented Sep 16, 2012 at 22:05
  • \$\begingroup\$ @Mechanicalsnail So after a few hours of not thinking about it, I came up with a new encoding using character strings that involves little over head and shrinks the size of the compact solution from 1922 bytes/chars to 1216 bytes/chars. So two things: 1, you were correct, and 2, since I'm a relative newbie to the codegolf.stackexchange site, what's the best way to share it? Should I replace the original above, or append it to the end of this accepted answer, or post yet another answer? \$\endgroup\$ Commented Sep 17, 2012 at 0:50
  • \$\begingroup\$ I would probably replace the existing answer. Also keep an old version when it contains something interesting or if they help understand how the solution works; if the new version is better in all respects then just delete the old one. I would add a second answer if my new solution is completely different (e.g. works by a different method, is written in a different language, etc.). \$\endgroup\$ Commented Sep 17, 2012 at 7:17

Python 3, 1369 chars (not bytes)

def f(x):
 if x in a:
  return 'lowercase'
  return 'uppercase'
 return 'neither'

Almost as small as the other answer (uses the same approach but using literals instead of numbers) but no effort at all.

Your browser may not render the chars properly and it is UTF-8

Edit: Making uppercase characters and adding those which didn't correspond to any lowercase saved a ton of chars

  • \$\begingroup\$ Technically that's reusing embedded Unicode information, though, thus approximately on par with directly querying character information. \$\endgroup\$
    – Joey
    Commented Jul 14, 2011 at 11:10
  • \$\begingroup\$ @Joye on many languages 'a' == 97. So what's the difference of comparing a char to a number or to another char? \$\endgroup\$
    – JBernardo
    Commented Jul 14, 2011 at 11:28
  • 1
    \$\begingroup\$ I meant the upper() part which is relying on Python knowing how to uppercase arbitrary Unicode characters, thereby not embedding the mapping itself. \$\endgroup\$
    – Joey
    Commented Jul 14, 2011 at 12:31

PowerShell, 2101

filter x{if(97..122+170,181,186+223..246+248..255+311..312+(0..26|%{$_*2+257})+


  • Returns 1 for lower-case, 2 for upper-case and 0 for neither.
  • Works only for BMP code points (as the char type in .NET represents a single UTF-16 code unit).

This uses a few distinct ways of compressing the code points for the two sets:

  • adjacent code points are conflated in a range (97..122)
  • code points that are two apart are conflated in a small pipeline (0..22|%{$_*2+331})
  • Ranges with only one missing value are done via 7522..7578-ne7544, effectively removing that single element. This may not be done with all such rages by now.

I did the first two steps automatically, the third one and minor optimizations (0..1|%{2*$_+11370} is useless) were done by hand.

Some tests:

PS> [char]'a'|x
PS> [char]'A'|x
PS> [char]'1'|x
PS> [char]'α'|x
PS> [char]'Д'|x

Python 2: 811 characters (after minification)

def D(s):
    k = 0
    for i in map(ord, s.decode('base64').decode('utf8')):
        for j in range(i // 2):
            yield k
            k ^= i % 2
        k ^= 1 - i % 2
f = lambda x: U[x] and 'upper' or L[x] and 'lower' or 'neither'

This supports the base multi-lingual plane only (as for the other answers). You could make it a bit shorter by omitting the encoding/decoding steps (and just putting Unicode strings into the program source), but then I think you really ought to count the length in bytes rather than characters.

  • 2
    \$\begingroup\$ Why do you only count non-whitespace characters? \$\endgroup\$
    – Joey
    Commented Jul 16, 2011 at 16:27
  • 1
    \$\begingroup\$ Because removal of whitespace is essentially a mechanical operation. In most cases it just makes the code harder to read, without adding anything of value. When analyzing the code, of course the first thing you would do is to restore the whitespace to make it readable. So I prefer to take this step for granted, in order to focus on the more interesting and creative aspects of the challenge. \$\endgroup\$
    – user2186
    Commented Jul 16, 2011 at 16:54
  • \$\begingroup\$ So there is no necessary whitespace in there. Plenty of languages have that and then not counting characters you need for the program to work is a bit cheating. \$\endgroup\$
    – Joey
    Commented Jul 16, 2011 at 16:59
  • \$\begingroup\$ Would it be better if I gave the length after minification instead, like this? \$\endgroup\$
    – user2186
    Commented Jul 16, 2011 at 17:12
  • \$\begingroup\$ That's better, I guess. Usually rules like "Don't count whitespace" are given in the task specification here and when they're absent everyone should count the same. \$\endgroup\$
    – Joey
    Commented Jul 16, 2011 at 17:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.