The two main things I had to fix were header dependencies and missing files. The first is easily fixed, you just need to parse headers as well. The second isn’t too hard to fix either, depending on how you intend on finding files.

Finding headers relies on two parameters passed to the script. The first one is a series of include paths (that you’d have anyway in a Makefile) and the second the source files (that you’d also have in a Makefile). Typically, your Makefile will have lines such as:

SOURCES= \ $(wildcard sources/*.cpp) INCLUDES=-I. -I./includes/ -I./plugins -I/usr/include/boost/

and you invoke the script:

depend: @./make-depend.sh "$(INCLUDES)" "$(SOURCES)" > .depend

Then, you can grep your way into the files:

headers=$( grep -e '^\ *#\ *include' $f \ | sed 's/.*\(<\|"\)\(.*\)\(>\|"\).*/\1\2\3/' \ | tr '\n' ' ' \ | sed -e 's/[ ]*$//' \ )

This new version finds both <includes> and “includes”, and keeps the delimiter. I will use that later on to check whether or not the file is missing.

To check if an include exists in your project, I check all the directories pointed by the includes list, and if the script doesn’t find the files, it will check if it’s included within quotes or within <>. If it is quoted, then it *must* be local, and the script should have found it: it outputs an error about that file not being found. If it is enclosed in <> the script considers that if it includes a point (as in <thingie.hpp>) it’s part of the project and must be found, otherwise it assume it’s some part of the standard library and doesn’t complain.

function exists { local where=$1 local dir=$2 local name=$3 local found=false dequoted_name=$(echo $name | sed 's/\(<\|>\|"\)//g') for w in ${where[@]} do local f=$w/$dir/$dequoted_name if [ -f "$f" ] then echo -n $(joli $f)' ' found=true fi done # file not found? if [ $found = false ] then # check if it might be in the STL # by assuming that if there's a dot in # the name, it's user-defined (and if # it's in quotation, check anyway) # if [[ "$name" =~ .*'.'.* || \ "$name" =~ \".*\" ]] then echo not found: $name 1>&2 else :; # should do better checking fi fi }

*

*

There are still a number of things to fix. First, the script doesn’t deal with random spaces, for example `< spaces >`, but that shouldn’t be a problem.

*

*

Click to deconflapulate:

#!/usr/bin/env bash function joli { # sed: the first symbol after s is the separator # replaces multiple consecutives ///// by / # then removes initial ./ at the begining echo $1 | sed -e 's,/[/]*,/,g' | sed -e 's,^\./,,' } function exists { local where=$1 local dir=$2 local name=$3 local found=false dequoted_name=$(echo $name | sed 's/\(<\|>\|"\)//g') for w in ${where[@]} do local f=$w/$dir/$dequoted_name if [ -f "$f" ] then echo -n $(joli $f)' ' found=true fi done # file not found? if [ $found = false ] then # check if it might be in the STL # by assuming that if there's a dot in # the name, it's user-defined (and if # it's in quotation, check anyway) # if [[ "$name" =~ .*'.'.* || \ "$name" =~ \".*\" ]] then echo not found: $name 1>&2 else :; # should do better checking fi fi } ################################3 includes=$(echo $1 | sed s/-I//g) files=$2 all_headers=( ) for f in ${files[@]} do # grep: finds lines that begin by #include # sed: extracts between < > or " " but keeps delimiters # tr: replaces \n by space # sed: removes trailing spaces echo -n ${f%.*}.o": "$f" " headers=$( grep -e '^\ *#\ *include' $f \ | sed 's/.*\(<\|"\)\(.*\)\(>\|"\).*/\1\2\3/' \ | tr '\n' ' ' \ | sed -e 's/[ ]*$//' \ ) z=$( for h in ${headers[@]} do #echo "$h" 1>&2 d=$(dirname $h | sed -e 's/^\.//' ) b=$(basename $h) exists "${includes[@]}" "$d" "$b" done ) echo $z # | sed -e 's/\ /\ \\\n/g' echo all_headers+=( $z ) done headers=$(echo ${all_headers[@]} | tr ' ' '\n' | sort -u) for h in ${headers[@]} do echo $h":" echo done exit 0]]>

Well, that’s not bad, but for now, I must reconsider the time I invested, and will continue to invest in this blog. Posting once a week, even with the summer hiatuses, is becoming a strenuous pace, especially that I also have—many—other things to do. So I was tempted to just end it, and say, well, after ten years, it’s much better than your average two-post blog, let’s call it quits. But the thing is, I enjoy writing about the things I do, the math I work out, and other ideas. But I will likely stop for this month, then be back with once-a-month posting.

‘Till then, it’s summer. Let’s enjoy it.

]]>

The Rosenberg-Strong function [1,2] maps the pairs (i,j) in the following way:

If we look at it closely, we see that numbers aren’t placed on diagonals, but in “shells” (technically, gnomons). The original idea was to use this as a storage allocation method, so that each new “shell” was somehow minimal (I am not convinced how this is meant to make sense in block-based storage).

How do we construct the function? Well, first thing is to figure how to generate the shells:

That’s easily found: the shell number is simply max(i,j)! We then notice a pattern linking the number of the shell and its first number:

Now to compute the numbers in the gnomon, let’s assume i are rows and j columns. If i is equal to the row, we add j; if j is equal to the column, we add i, plus the middle of the interval. This is j+max(i,j)-i. The inverse is almost simpler than the function itself.

To find the inverse, we notice that the shell containing some number is . Then is the base of the shell; is the “rest” the position of on the gnomon; if (the gnomon with base has numbers on it), it's on the horizontal section and the pair is , otherwise it's on the vertical part, and the pair is $(2b-r,b)$.

The Mathematica code is:

s[i_, j_] := Max[i, j]^2 + j + (Max[i, j] - i) is[n_] := Module[{b = Floor[Sqrt[n]], r}, r = n - b^2; If[r < b, {b, r}, {2 b - r, b} ] ]

*

* *

The scan order imposed by the Rosenberg-Strong function is:

We see that at each gnomon’s end, we jump across the map to the other side. What we could want, is rather that it wraps always to a close neighbor.

Fortunately, getting a boustrophedonic variant isn’t hard at all! If the shell is even, we wind it one way; if it’s odd, we wind it the other:

We, indeed, get:

The Mathematica code is:

bs[i_, j_] := If[EvenQ[Max[i, j]^2], s[i, j], s[j, i]] ibs[n_] := Module[{t = is[n]}, If[EvenQ[Max[t]^2], t, Reverse[t]] ]

*

* *

What if we’re interested in more than two numbers? We could, of course, devise some complicated function for 3, or more; but we can use the following relations to accomplish just that with function :

,

,

,

etc.

[1] Arnold L. Rosenberg — *Allocating Storage for Extendible Arrays* — J. ACM, vol 21, no 4 (1974), p. 652–670

[2] Arnold L. Rosenberg, H. R. Strong — *Addressing Arrays by Shells* — IBM Technical Disclosure Bulletin, vol 14, no 10 (1972), p. 3026–3028

Cantor’s function associates pairs (i,j) with a single number:

…but that’s not the only way of doing this. A much more fun—and spatially coherent—is the boustrophedonic pairing function.

Boustrophedonic (litt. “ox-turning”, as an image of how oxen plow fields, revering course at the end of a line) will associate pairs in a spatially coherent manner:

Numerically,

Of course, there’s the fun in finding the function, but there are also applications. For example, one could use this order to scan an (arbitrarily large) matrix before/for quantization.

*

* *

Let’s define:

.

with its rounded-down inverse

.

Now, the hard part is managing the reversals. Diagonal direction depends on the diagonal’s number. If we pair and , then this combination will lie on diagonal . If the diagonal number is even, it winds north-east, if it’s odd, it winds south-west. It it winds south-west, we count backwards—relative to Cantor’s function—from the *next* diagonal.

.

The function’s inverse must also deal with diagonal reversal:

.

where

,

.

*

* *

The Mathematica code is quite simple:

\[CapitalDelta][x_] := 1/2 x (x + 1) i\[CapitalDelta][x_] := Floor[Sqrt[2 x + 1/4] - 1/2] b[i_, j_] := If[EvenQ[i + j], \[CapitalDelta][i + j] + j, \[CapitalDelta][i + j + 1] - j - 1] iB[n_] := Module[{d = i\[CapitalDelta][n], r}, r = n - \[CapitalDelta][d]; If[EvenQ[d], {d - r, r}, {r, d - r} ] ]]]>

In the last few weeks (of posts, because in real time, I worked on that problem over a week-end) we’ve looked at how to generate well distributed, maximally different colors. The methods were to use well-distributed sequences or lattices to ensure that the colors are equidistant. What if we used physical analogies to push the colors around so that they are maximally apart?

That’s exactly what I tried, with mixed results. So the idea was to start with randomly distributed colors within the color cube, then have them to move around so that they maximize distance. The obvious analogy is a gas, where each molecule push against the others to fill the volume (assuming no gravity). The repulsion force (vector) between two molecule and is

.

where and are n-dimensional positions/vectors.

The magic constant must be finely tune to account for scale, repulsion force, and other tweaks. The total motion of a molecule is therefore the sum of the repulsion with its neighbors:

.

then we modify using , where is another magic constant that vaguely corresponds to viscosity (but numerically it serves to ensure molecules move relatively slowly).

Finally, all positions are constrained to a bounding box.

*

* *

Launching the code on twenty points, we see that the colors push against each other somewhat as expected:

The colors distribute kind of evenly (unfortunately, the very pale colors, like white, do not show up well in the animation), but the solutions are sensitive to the initial conditions, the number of iterations, and the magic constants and . With a small number of colors, it works however well: at 8 colors, each finds its own corner of the cube.

But with a real gas filling a real container, the container itself exerts repulsion, so corners and surfaces aren’t “attractive”, and the molecules distribute themselves more evenly. Here, I haven’t implemented container repulsion, so colors just flee from the center of the cube!

*

* *

It’s a very complicated way of distributing colors against the edges of the cube, and we have much better ways of doing this.

]]>

Dispersing points on a square grid—the most intuitive choice—doesn’t distribute them equidistantly. Immediate neighbors are at distance 1, but the other, diagonal neighbors, are at a distance of √2. However, if we use a hexagonal lattice:

Each point is at a distance 1 of all its immediate neighbors. The grid is also not complicated to create: its basis is the equilateral triangle. Its basis vectors are

,

.

The combinations , with varying and will cover the entire lattice.

However, we’re interested only to the points of the lattice that will correspond to colors; say RGB-representable colors. The colorspace might be HSV, but only colors that are mappable to RGB (and our device’s gamut). Remember HSV: the hue is given by the angle made between a point and some reference axis. Each point of the lattice can have its angle measured against some reference vector, say (1,0), that would be red. Distance to origin is saturation and height is brightness. Let’s conveniently say that in 2D, brightness is distance to the origin (up to a radius of 1) and that saturation is always maximal.

What varies now is the scale of the grid: if we vary and coarsely, we have few colors, if we vary them finely, we have more.

*

* *

The number of colors generated by this scheme cannot be arbitrary: colors are placed on the lattice within a radius 1 of the origin, and varying the density of (and in the same way) will allow a limited number of colors to be generated: 1, 7, 19, 37, 61, 91, 127, …

These are centered hexagonal numbers, and the general formula to generate the sequence is

.

*

* *

To create the animation, I used the following Mathematica Code:

(* To make it simpler with vectors *) ATan[z_] := If[z == {0, 0}, 0, ArcTan[z[[1]], z[[2]]] ] Palette[d_] := Graphics[ { {White, Circle[{0, 0}, 1 + 1/(2 d)]}, { PointSize[1/(3 d + 1/2)], Table[ If[Norm[u x + y v] <= 1, { Hue[ATan[u x + y v]/(2 \[Pi]), 1, Sqrt[ Norm[u x + y v]]], Point[x u + y v] }] , {x, -2, 2, 1/d}, {y, -2, 2, 1/d}] } }, ImageSize -> 500 ]

One thing you may notice is that I do not use the norm, but its square root. Why? This gives better contrast—it’s a heuristic observation.

*

* *

If we use two dimensions for hue and saturation, we’re left with a third for value, or brightness:

The code is pretty much the same, except that we now have another dimension. The lattice is extended to 3D in a way that ensures the points remain equidistant not only in the hue/saturation plane but also between planes. That’s more or less the “cannonball stack” arrangement:

We now need three basis vectors:

,

,

.

and cycling through inside a cylinder (unit radius and height 2, centered at 0), with varying density produces the animation above. The Mathematica code is:

ATan2[u_] := ATan[u[[1 ;; 2]]] Palette3D[d_] := Graphics3D[ { { PointSize[1/(3 d + 2)], Table[ If[(Norm[(x u3 + y v3 + z w3) {1, 1, 0}] <= 1) && (Norm[z w3] <= 1), { Hue[ATan2[x u3 + y v3 + z w3]/(2 \[Pi]), Norm[(x u3 + y v3 + z w3) {1, 1, 0}], ((x u3 + y v3 + z w3)[[3]] + 1) 1/2 1/Sqrt[2/3] ], Point[x u3 + y v3 + z w3] } ] , {x, -2, 2, 1/d}, {y, -2, 2, 1/d}, {z, -1, 1, 1/d}] } }, Lighting -> None, RotationAction -> "Clip", SphericalRegion -> True, Boxed -> False, ImageSize -> 500 ]

*

* *

Either of the above methods are truly useful when the number of colors remains modest. We’re not very good at distinguishing colors. Maybe only a few tens of colors displayed simultaneously will appear different enough to be useful—we’re much, much better at differentiating brightness. Combining brightness and color maybe a bit better than using only color or only brightness.

]]>Somehow, we need a maximally distributed set of points in RGB space (but not necessarily RGB). We might have just what we need for this! We’ve discussed Halton sequences before. They’re a simple way of progressively and uniformly distribute points over an interval. The sequence starts by the ends of the interval then progressively fills the gaps. It generates the sequence 0, 1, 0.5, 0.25, 0.75, 0.125, 0.625, 0.375, 0.875, …

We can use this mechanism to generate colors, where each of the components R, G and B are generated using Halton sequences and combined. We should first generate all combinations with 0 and 1, then all combinations with 0, 0.5, and 1, then all with 0, 0.25, 0.5, 0.75, and 1, and so on until we arrive to the desired number of distinct colors (we will make sure to generate colors only once).

Transposed to 24-bits color, the sequence becomes 0, 255, 127, 63, 191, 31, 95, 159, 223, … The first few colors are given here:

0,0,0 | |

0,0,255 | |

0,255,0 | |

0,255,255 | |

255,0,0 | |

255,0,255 | |

255,255,0 | |

255,255,255 | |

0,0,127 | |

0,127,0 | |

0,127,127 | |

0,127,255 | |

0,255,127 | |

127,0,0 | |

127,0,127 | |

127,0,255 | |

127,127,0 | |

127,127,127 | |

127,127,255 | |

127,255,0 | |

127,255,127 | |

127,255,255 | |

255,0,127 | |

255,127,0 | |

255,127,127 | |

255,127,255 | |

255,255,127 | |

0,0,63 | |

0,0,191 | |

0,63,0 | |

0,63,63 | |

0,63,127 | |

0,63,191 | |

0,63,255 | |

0,127,63 | |

0,127,191 | |

0,191,0 | |

0,191,63 | |

0,191,127 | |

0,191,191 | |

0,191,255 | |

0,255,63 | |

0,255,191 |

Now let’s see how we generate this. The Halton sequence generator is explained in this previous post, but I’ve refined the reversal a bit to make sure it works properly on signed values.

//////////////////////////////////////// template <typename T> T halton_reverse(typename std::make_unsigned<T>::type x) { T t=0; while (x) { t<<=1; t|=(x&1); x>>=1; } return t; }

C++11’s type_traits contain a lot of interesting stuff. `make_unsigned` gives you the same-sized unsigned version of the integral type given as argument.

Then, we need to generate the sequence progressively: 0, 255, then 0, 127, 255, then 0, 63, 127, 191, 255, etc… To do this, we need to look at how the number of bits in the sequence and the product with 255 combine. Let’s recall that the sequence, in binary, is 0, 1, 0.1, 0.01, 0.11, 0.001, … To stretch values on 0 to 255, we need to understand that 0*255=0, 1*255=255, 0.1*255=1*127 (with truncation), 0.01*255=1*63, 0.11*255=3*64, 0.001*255=1*31, etc.

int steps=256; std::set<int> s{0}; for (int bits=0;bits<nb_bits;bits++,steps/=2) { std::cout << "bits=" << bits << "\tsteps=" << steps; int next_limit=(1<<bits); for (int v=prev_limit;v<=next_limit;v++) s.insert(std::max(0,halton_reverse<int>(v)*steps-1)); ...

Once new values are added to the set, we only need to combine them to generate all RGB combinations:

for (int r : s) for (int g : s) for (int b : s) if (seen.find({r,g,b})==seen.end()) { all_colors.push_back({r,g,b}); seen.insert({r,g,b}); }

*

* *

Click to inflatulize:

#include <type_traits> #include <cstdint> #include <set> #include <list> #include <iostream> #include <iomanip> //////////////////////////////////////// using rgb = struct __RGB__ { int r,g,b; bool operator<(const __RGB__ & other) const { return (r<other.r) || (r==other.r && g<other.g) || (r==other.r && g==other.g && b<other.b); } }; //////////////////////////////////////// template <typename T> T halton_reverse(typename std::make_unsigned<T>::type x) { T t=0; while (x) { t<<=1; t|=(x&1); x>>=1; } return t; } //////////////////////////////////////// int main() { const int nb_bits=5; int steps=256; int prev_limit=0; std::list<rgb> all_colors; // generation order std::set<rgb> seen; // to check if already seen std::set<int> s;// for sorting purposes (to make it pretty) s.insert(0); for (int bits=0;bits<nb_bits;bits++,steps/=2) { std::cout << "bits=" << bits << "\tsteps=" << steps; int next_limit=(1<<bits); for (int v=prev_limit;v<=next_limit;v++) s.insert(std::max(0,halton_reverse<int>(v)*steps-1)); for (int v : s) std::cout << '\t' << v; std::cout << std::endl; std::cout << '\t'; for (int r : s) for (int g : s) for (int b : s) if (seen.find({r,g,b})==seen.end()) { std::cout << '{' << r << ',' << g << ',' << b << "} "; all_colors.push_back({r,g,b}); seen.insert({r,g,b}); } std::cout << std::endl; prev_limit=next_limit; } // show (html) std::cout << "<table align=\"center\" border=\"1px\">" << std::endl; for (rgb c: all_colors) std::cout << "<tr>" << "<td>" << c.r << ',' << c.g << ',' << c.b << "</td>" << "<td width=\"100\" style=\"background-color:#" << std::hex << std::setfill('0') << std::setw(2) << c.r << std::setw(2) << c.g << std::setw(2) << c.b << std::setfill(' ') << std::dec << "\"></td>" << "</tr>" << std::endl; std::cout << "</table>"; // show (c/c++) int n=0; std::cout << "// " << all_colors.size() << std::endl; std::cout << "{"; for (rgb c: all_colors) { std::cout << '{' << std::setw(3) << c.r << ',' << std::setw(3) << c.g << ',' << std::setw(3) << c.b << "},"; n++; if (n%4==0) std::cout << std::endl; if (n==257) break; } std::cout << '}' << std::endl; return 0; }]]>

The basic idea behind these colorspaces is the good ol’ color wheel, where primary colors are placed on a triangle and secondary colors between; complementary colors are to be found opposite on the circle. That’s intuitive enough, but we still have to make it into a workable colorspace.

We—computer scientists at least—think of color primarily as RGB (possibly with different gammas, like sRGB) and only occasionally in terms of other colorspaces. So let’s built the intuition on how HSV from RGB.

We know we want at least one dimension to be associated to luminance. The great diagonal of a RGB cube is just that: it passes from black to white, with all intermediary shades of gray. We also want the other dimensions to correspond to the Yellow-Blue and Red-Green differences, or to other concepts such as hue and saturation. Let’s see what happens when we make the RGB cube stand on its tail.

The RGB cube standing up with the great diagonal as principal axis casts an interesting “shadow”: an hexagon. Moreover, if we assign the colors to this hexagon with the corresponding points on the cube, we get a “color wheel”—not quite round, but that’s not that important for now.

Every point in the cube can be projected onto the hexagon:

In transparency, the parallelogram generated by a particular RGB color (here, the color (0.5,0.7,0.3), with normalized components). The arrows shows there the color is projected on the hexagon. Interestingly, the projection of the color (as a linear combination of , , and , the basis of the RGB color space onto the hexagon is the same as the color expressed as the projection of the basis vectors (because , if is the projection, the basis, and the color). In other wods, we can project the axes of the cube onto the hexagon and use those projection to compute where the color lands:

The vectors , , and are the *projected* RGB axes. Let’s call the coordinates in the hexagonal plane and . Then

projects a color onto the hexagonal plane.

Let’s come back to the above figure. In the small circle, we have the projected color (0.5,0.7,0.3). It does match its surrounding rather well. However, that’s a bit of cheating, and a also a bit misleading. Indeed, the plane is only two dimensional, and we’re missing a dimension: a lot of colors will map to the same point. The missing third dimension is brightness.

The hue is simply the angle formed by the point, the origin, and some conventional point—say pure saturated red ().

The saturation is the distance from the center. At the center, we have the grays, therefore no saturation. Saturation is maximal on the edges of the hexagon.

Brightness is perpendicular to the hexagonal plane. It may, or may not be normalized. Some use the usual weightings of 0.299, 0.587, and 0.114 on RGB to get the brightness, but we might as well just use the transform implied by rotation of the cube on its tail—more on this a bit below.

*

* *

Now, if we add the third axis, we get an hexagonal prism:

In this model, it seems that we’re using too much of that colorspace for the colors we can distinguish. Indeed, it’s impossible to have a color that is very saturated and very dark at the same time. So the bottom end should be reduced to a point. So let’s do that. We get:

But ultimately, we want a color wheel-like system. We can stretch that hexagon into a circle:

*

* *

Now, how to we map a RGB color to that lovely funnel space?

- The hue is given by the angle of the vector:
,

possibly normalized to (0,1) (as Mathematica expects), or , …

- Saturation is typically computed as instead of vector norm of .
- Brightness is given as .

The standard implementation of RGB to HSV (and back) is a bit tricky. First, we must figure out into which of the six regions of the hexagon the projection lands, find the corresponding tangent, saturation, and brightness.

RGBHSV[{r_, g_, b_}] := Module[{h, s, v, c, M = Max[r, g, b], m = Min[r, g, b]}, c = M - m; h = If[c == 0, 0, Which[ M == r, Mod[(g - b)/c, 6], M == g, (b - r)/c + 2, M == b, (r - g)/c + 4, True, 0 ] ]; s = If[M != 0, (M - m)/M, 0]; v = M; Return [{ h, s, v}] ] HSVRGB[{h_, s_, v_}] := Module[{i, f, p, q, t, th}, th = h;(* Mod[Round[h],360]/60; *) i = Floor[th]; f = th - i; p = v (1 - s); q = v (1 - s f); t = v (1 - s (1 - f)); Which[ i == 0, {v, t, p}, i == 1, {q, v, p}, i == 2, {p, v, t}, i == 3, {p, q, v}, i == 4, {t, p, v}, i == 5, {v, p, q} ] ]

*

* *

We can still refine HSV a bit, or maybe make it a bit more intuitive. As we remarked, a color can’t be very saturated and very dark at the same time. Likewise, it’s hard to have a very bright and very saturated color—it will be perceived as white. HSL takes advantage of this and use a double cone (technically a bicone):

*

* *

The above procedures aren’t that complex, but one might rightly think that they are too slow for fast colorspace translation. Imagine coding video in HSV: you would need to compute `RGBHSV` for each pixel. Certainly we could do better computationally.

Well, the very first transformation, the one that set the RGB cube on its tail, is pretty much what we need: the great diagonal onto one of the axes, and the two others perpendicular to it. Let’s derive the transformation matrix.

First, consider the rotations around the red and green axes:

and

.

To bring the cube on its tail, we must align its great diagonal onto the first axis of the new colorspace. We rotate along the green axis to bring the diagonal in the Red/Blue plane, then along the Green axis to bring it to the red axis. In other words:

.

I came up with (the essentially trivial) colorspace sometimes in the 90s, but not as a replacement for HSV, merely to get a length-preserving color transform with one axis being brightness. The fact that is a rotation has the interesting side-effect that it is orthonormal and the inverse is merely its transpose: .

]]>Typically, the function is merely a series of exclusive ors, which basically computes the parity of the selected bits. This will help us create an efficient implementation—maybe using the compiler intrinsics.

Let’s first convince ourselves that a series of xors are equivalent to computing the parity. If you xor a series of bits together, the result will be one only if there’s an odd number of bits that are 1s, and zero if the number is even. That’s exactly how parity is defined. More precisely, that’s how odd parity is computed (even parity gives 1 when the number of bits set to 1 is even).

Lucky for us, GCC has an intrinsic function `__builtin_parity` that computes odd parity. To get even parity, we xor the odd parity with 1—this will allow 0 as a state (otherwise it’d be a fixed point, which is now 0xff…f).

Extracting a certain number of bits from the register (the state of the pseudo-random number generator) seems to be a complicated operation until we realize that and-ing the state with a mask with 1 bits in the interesting position will extract the bits just fine for the computation of parity!

Shifting shouldn’t be a mysterious step.

The complete generator therefore fits into one line of code, or almost:

#include <time.h> #include <cstdint> #include <iostream> class lsr { private: uint32_t mask; uint32_t selector; uint32_t state; public: uint32_t operator()() { uint32_t t=state; state=((state<<1) | (__builtin_parity(state & selector)^1)) & mask; return t; } lsr( int bits, uint32_t s, uint32_t seed) : mask((1<<bits)-1), // actually 32-bit safe! selector(s), state(seed & mask) {} }; int main() { lsr a(10,0x204,0); // or time(0)); for (int i=0;i<1024;i++) std::cout << '\t' << a(); return 0; }

This lovely piece of code generates

0 1 3 7 14 28 56 113 227 455 910 797 571 118 236 472 945 866 708 393 787 550 77 154 309 618 212 424 849 674 324 648 272 545 66 133 266 533 43 87 174 348 696 368 737 450 901 779 534 45 90 181 362 725 427 855 687 351 702 381 762 500 1000 976 928 832 640 256 513 2 5 10 21 42 85 170 341 682 340 680 336

*

* *

The above minimal implementation uses a mysterious value: the selector. The interesting question is now “how do we get a good selector?” The answer is not entirely trivial. You want a selector that will allow the generator to visit every state (except the fixed point, which will be 0xff…f in even parity) exactly once.

Brute-forcing the solutions isn’t that much interesting, but that’s something you can do rather easily. For each registers sizes 2 to 32, the first selectors I found were:

Bits | Selector |

2 | 3 |

3 | 5 |

4 | 9 |

5 | 12 |

6 | 21 |

7 | 41 |

8 | 8e |

9 | 108 |

10 | 204 |

11 | 402 |

12 | 829 |

13 | 100d |

14 | 2015 |

15 | 4001 |

16 | 8016 |

17 | 10004 |

18 | 20013 |

19 | 40013 |

20 | 80004 |

21 | 100002 |

22 | 200001 |

23 | 400010 |

24 | 80000d |

25 | 1000004 |

26 | 2000023 |

27 | 4000013 |

28 | 8000004 |

29 | 10000002 |

30 | 20000029 |

31 | 40000004 |

32 | 80000057 |

Are those good selectors? Yes, but linear feedback shift registers are limited in very strange ways. For example, if your initial seed is 1, you may see the first values generated be 0, 1, 3, 7, 14, 28, 56, 113, … that’s doesn’t look very random. First, there are no measure of dispersion. The values above merely guaranty that each state (except 0xff..ff) is visited exactly once before starting to repeat over again. I do not know how to measure dispersion meaningfully. Maximizing the difference between consecutive values? Between consecutive residues modulo some number?

To be continued, maybe.

]]>