2025 Update:
- Wikipedia now states: The BIGMIN problem has first been stated and its solution shown in Tropf and Herzog.[10] For the history after the puplication see [11].
- What is below seems to be a variant of BIGMIN, with the advantage of running in log bit length. The original BIGMIN is proportional to the bit length.
Many years ago, while doing my thesis, I played a bit with z-order / Morton coding (see https://en.wikipedia.org/wiki/Z-order_curve). Therefore I share here a little snippet of code from ~1995, that I happened to fall upon while cleaning up an old laptop. I have not noted that the equivalent logic has already been shared, therefore I took an hour to write it up in Python (Jupyter notebook style uploaded here). I would be curious to know if it can be found elsewhere.
First a bit of historical context. My first job (before my PhD) had me writing a Design Rule Checker (DRC) for the world's first analog CAD tool, developed at the CSEM https://www.csem.ch/Home (see ILAC https://ieeexplore.ieee.org/document/18603). Among many things, a DRC does boolean operation on layers of integrated circuit masks, and when these operations produce unexpected results, you know that your CAD tool has made a mistake. Focusing on this blog, a DRC must efficiently "join" geometric datasets, and the question is then how should we structure the data of these geometries for optimal performance. At the CSEM I was using K-d trees (https://en.wikipedia.org/wiki/K-d_tree), yet one of the data structures that came out during this period was the Skip list (https://en.wikipedia.org/wiki/Skip_list), and because the skip list is only a one dimensional structure, I thought it would be fun to use z-ordering, to join 2 or more dimensions structures with skip lists.
The difficulty is that keys in the skip list space only have one dimension, and it is not good enough to compare these keys to do high performance geometry operations. We need a way to take these one dimensional keys and optimally project them into the different dimensions of the geometry space. This is what the function "next_above" does below. You give it a boundary of z-space defined by its maximum z-order value, and as well a direction and a reference value, and it will tell you the next z-order value at the edge of the z-space that is aligned with the reference value along the given direction.
This is a 2d z-order example, but the concept works in any dimension.
We need some basic reference values:
log_bit_count = 5
bit_count = (1<<log_bit_count)
mask = 0xFFFFFFFFFFFFFFFF
mask0 = 0x5555555555555555
mask1 = mask0>>1 # not really but avoids python issues
This function finds the next constrained z-order value past a z-order region. Copyright 2020 James Litsios.
In [3]:
def next_above(x, m, z):
a = (x&~m)|(z&m)
b = x^a# different bits
v = b
for kp in range(0, log_bit_count+1):
k = 1<<kp
v |= (v>>k)
w = ((v+1)>>1)&x
if v==0:
w = 1 # case where already on border
nx = (((x|m)+w)&~m)&~v
y = nx | (z&m)
return y
From the web, some utility code to demonstrate things:
In [4]:
def to_morton2(x, y):
# taken from https://stackoverflow.com/questions/30539347/2d-morton-code-encode-decode-64bits
x = (x | (x << 16)) & 0x0000FFFF0000FFFF
x = (x | (x << 8)) & 0x00FF00FF00FF00FF
x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0F
x = (x | (x << 2)) & 0x3333333333333333
x = (x | (x << 1)) & 0x5555555555555555
x = (y | (y << 16)) & 0x0000FFFF0000FFFF
x = (y | (y << 8)) & 0x00FF00FF00FF00FF
x = (y | (y << 4)) & 0x0F0F0F0F0F0F0F0F
x = (y | (y << 2)) & 0x3333333333333333
x = (y | (y << 1)) & 0x5555555555555555
return x | (y << 1)
def from_morton1(x):
x = x & 0x5555555555555555
x = (x | (x >> 1))& 0x3333333333333333
x = (x | (x >> 2))& 0x0F0F0F0F0F0F0F0F
x = (x | (x >> 4))& 0x00FF00FF00FF00FF
x = (x | (x >> 8))& 0x0000FFFF0000FFFF
x = (x | (x >> 16)) & 0x00000000FFFFFFFF
return x
def from_morton2(d):
x = from_morton1(d)
y = from_morton1(d >> 1)
return x, y
In [5]:
import numpy as np
Choose a size for the example:
In [6]:
size = 64
Let's color a grid with two regions of lower ranges of z-ordered values:
In [7]:
z0, z1, z2 = (0, size*size//10, 2*size*size//3)
grid1 = np.ones((size+1,size+1,3))
for z in range(z0, z1):
x, y = from_morton2(z)
grid1[x,y,:] = (0,0.5,0)
for z in range(z1, z2):
x, y = from_morton2(z)
grid1[x,y,:] = (0.2,0.7,0.2)
x, y = from_morton2(z1)
grid1[x,y,:] = (1.0,0,0)
x, y = from_morton2(z2)
grid1[x,y,:] = (1.0,0,0)
What we have done is to mark in green the x,y values corresponding to all z-order values up to z2, which correspond to the coordinate (0, 63). For clarity, there are two zones, and the last value (0, 63) is shown in red, as is the coordinate (21, 10), that corresponds to z1.
In [8]:
from_morton2(z1)
Out[8]:
(21, 10)
In [9]:
from_morton2(z2)
Out[9]:
(0, 63)
In [10]:
import matplotlib.pyplot as plt
plt.figure(figsize = (5,5))
plt.imshow(grid1)
plt.show()
![](data:image/png;base64,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)
Now we choose twenty "lookup keys" to demonstrate what "next_above" does:
In [11]:
keys = np.random.randint(0,size,(20,))
In [12]:
keys
Out[12]:
array([34, 49, 10, 12, 45, 61, 10, 59, 16, 31, 37, 23, 12, 36, 27, 28, 26,
First we find the next z-order value outside the two z-order region that aligns "horizontally" with the given keys:
In [13]:
grid2 = grid1.copy()
for key in keys:
grid2[key, :, :] *= 0.9
grid2[key, 0, :] = (0.7,0.7,0.7)
zabove1 = next_above(z1, mask0, to_morton2(key, 0))
xa1, ya1 = from_morton2(zabove1)
grid2[xa1,ya1,:] = (1,0,1)
zabove2 = next_above(z2, mask0, to_morton2(key, 0))
xa2, ya2 = from_morton2(zabove2)
grid2[xa2,ya2,:] = (1,0,1)
In [14]:
plt.figure(figsize = (5,5))
plt.imshow(grid2)
plt.show()
![](data:image/png;base64,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)
Then, for demonstration purpose, we find the next z-order value outside the two z-order region that aligns "vertically" with the given keys:
In [15]:
grid3 = grid1.copy()
for key in keys:
grid3[:, key, :] *= 0.9
grid3[0, key, :] = (0.7,0.7,0.7)
zabove1 = next_above(z1, mask1, to_morton2(0, key))
xa1, ya1 = from_morton2(zabove1)
grid3[xa1,ya1,:] = (1,0,1)
zabove2 = next_above(z2, mask1, to_morton2(0, key))
xa2, ya2 = from_morton2(zabove2)
grid3[xa2,ya2,:]= (1,0,1)
In [16]:
plt.figure(figsize = (5,5))
plt.imshow(grid3)
plt.show()
![](data:image/png;base64,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)
The original ~1995 Prolog version of the algorithm:
xor X Y = (X or Y) and not (X and Y);
shiftDown V = shiftDown2 1 V;
shiftDown2 32 V = V;
shiftDown2 K V = shiftDown2 (2*K) (V or (shr V K));
nextAbove X M Z = Y
where
A = (X and not M) or (Z and M),
B = X xor A,
V = shiftDown B,
Y = nextAbove2 X M Z V (ifelse (V=0) 1 ((shr (V+1) 1) and X));
nextAbove2 X M Z V W = Y
where
NX = (((X or M) + W) and not M) and not V,
Y = NX or (Z and M);
C++ version from ~2003:
int
NextAbove(
int x,
int m,
int z){
int a = (x&~m)|(z&m);
int b = x^a; // different bits
int v= b;
{
for(int k=1; k<32; k<<=1){ //smeared bits down
v |= (v>>k);
}
}
int w = ((v+1)>>1)&x; // keep top bit if it is in x
if(v==0){
w = 1; // case where is alreay on "border"
}
int nx = (((x|m)+w)&~m)&~v;
int y = nx | (z&m);
return y;
}