blog | oilshell.org

Four Functions and Enum Labelings

2016-12-26

This post shows four solutions to an instance of the function-directed enum labeling problem. Today I'll show the code, and tomorrow I'll explain how they were discovered.

It's important to understand that these four functions are not equivalent: each one requires a different enum labeling. But they have the same structure, which is what we care about.

For brevity, the first three are shown in Python without the enum labeling, which looks like a random assignment of integers. The last one is shown in C++ with the enum labeling and proof that it works.

Notes:

Solution 1

The idea here is that id & (-id - 23) is close to what we want, and then we fix it up with some ad hoc conditionals. The naive version would have around 21 tests; this has 6.

def LookupKind1(id):
  kind = id & (-id - 23)
  if kind:      return kind
  if id <= 1:   return 20
  if id <= 9:   return 44
  if id <= 40:  return 48
  if id <= 72:  return 50
  if id <= 105: return 4
  return 2

Solution 2

The expression (id & 230) & (id + 13) has 3 operations instead of 2, but it only requires one conditional fix.

def LookupKind2(id):
  return (id & 230) & (id + 13) if id else 32

Solution 3

I used Veedrac's method to find this solution with 5 instructions and no branches. In the last post I talked about branch misprediction a bit.

def LookupKind3(id):
  return (-id + 12) & (id ^ 6) & (id + 14)

Solution 4

This solution has only 4 instructions, and still no branches.

def LookupKind4(id):
  return 175 & id & ((id ^ 173) + 11)

Here it is in strongly-typed C++:

Kind LookupKind(Id id) {
  int i = static_cast<int>(id);
  int k = 175 & i & ((i ^ 173) + 11);
  return static_cast<Kind>(k);
}

The file ik7.cc has the full enum labeling and an exhaustive test of all 221 lookups. Run it like this:

$ ./run.sh ik7
+ mkdir -p _tmp
+ c++ -std=c++11 -O3 -o _tmp/ik7 ik7.cc
+ _tmp/ik7
PASSED  // This is printed after 221 tests pass

Tomorrow I'll explain how these solutions were discovered. You can also take a peek at the reddit thread, but I'll explain it a bit more concisely and present the "metaprogramming" code.