Cursed fire or #define
black magic

Have you ever wondered whether it is possible to write fully functional code using only the #define
directive in C?
It's well-known that the C++ constexpr
and templates are Turing complete, developers even write ray tracers [1, 2] that do all evaluations at compile time (instead of runtime).
What about the C preprocessor? As it turns out, the question is a bit more complex than one might think.
Let us try to figure it out. There won't be any ray tracer, but you'll see quite a bit of cursed code.
First of all, why do I raise the question? If you find old-school computer graphics boring, you can skip the next section.
The most mundane fire, non-cursed yet
In fact, recently I promised to write a simple, yet quite rich compiler for the wend language that I just invented over the weekend.
Although it's easy to code, it's harder to describe.
A good description needs vibrant examples.
I'm allergic to code examples like calculating Fibonacci numbers.
For crying out loud! Since wend is pretty primitive, I need examples that are simple yet still impressive.
Suddenly, remembered the old demoscene!
Let us say, I want a bonfire animation.

It's not all that difficult.
I can't run graphics mode, but my terminal supports the \033[
escape sequence, so a single print
instruction is enough to draw the fire!
By the way, I have heard that the Windows terminal also supports the ANSI escape sequences, but I haven't checked it myself.
The technology being sorted out, all that's left to do is write the code.
Since I am only half-crazy, I'll write it first in C, and then translate it (manually) into the wend language.
Indeed, my compiler is good, but C offers a greater variety of tools.
In addition to that, my compiler isn't bug-free, and I'm too lazy to chase the issues out.
Of course, it happened to me to come across the GCC bugs, but they're rare and almost extinct.
Let us see how to create such a fire animation, and then we'll get back to the preprocessor and the black magic.
This is what the starting point looks like:
The starting point
| #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#define WIDTH 80
#define HEIGHT 25
#define FPS 30
const char* palette[256] = {
#define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m "
ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36),
ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16),
ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4),
ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0),
ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0),
ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0),
ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0),
ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0),
ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0),
ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0),
ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0),
ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0),
ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0),
ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0),
ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0),
ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0),
#define W ANSIRGB(252,252,252)
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W
#undef W
#undef ANSIRGB
};
static uint8_t fire[WIDTH * HEIGHT];
int main() {
printf("\033[2J"); // clear screen
for (;;) {
printf("\033[H"); // home
// rendering body
{
fire[rand()%(WIDTH*HEIGHT)] = 255;
}
for (int j = 0; j<HEIGHT; j++) { // show the buffer
for (int i = 0; i<WIDTH; i++)
printf(palette[fire[i+j*WIDTH]]);
printf("\033[49m\n");
}
usleep(1000000/FPS);
}
return 0;
}
|
First, I define the dimensions of my terminal (80x25), then define the 256-color palette array and the fire
buffer that actually contains the picture of the (future) bonfire.
Then, in the infinite for(;;)
loop, I render the buffer, where I fill randomly selected pixels with white color.
We obtain quite an expected result:

The white pixels will be sparks of flame.
The sparks cool down pretty quickly while heating the surroundings.
It's easy to simulate: at each new frame, we just blur the picture from the previous frame.
All the code changes will occur inside the block marked as // rendering body
, and each time I'll highlight the lines that change.
You can find the final code in my compiler repository.
The simplest way to blur a picture is to compute the average value of each pixel in relation to its neighboring pixels.
Virtually all implementations I come across require creating a copy of the entire buffer.
Check it on Wikipedia for an example.
However, such filters are separable in coordinates, so the blur can be obtained
by two separate motion blur filters: one horizontal and the other one vertical.
Let us start with the vertical one (lines 60-62):
Vertical motion blur
| #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#define WIDTH 80
#define HEIGHT 25
#define FPS 30
const char* palette[256] = {
#define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m "
ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36),
ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16),
ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4),
ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0),
ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0),
ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0),
ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0),
ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0),
ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0),
ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0),
ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0),
ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0),
ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0),
ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0),
ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0),
ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0),
#define W ANSIRGB(252,252,252)
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W
#undef W
#undef ANSIRGB
};
static uint8_t fire[WIDTH * HEIGHT];
void line_blur(int offset, int step, int nsteps) {
uint8_t circ[3] = {0, fire[offset], fire[offset+step]};
uint8_t beg = 1;
for (int i=0; i<nsteps; i++) {
fire[offset] = (circ[0]+circ[1]+circ[2])/3;
circ[(beg+++2)%3] = i+2<nsteps ? fire[offset+2*step] : 0;
offset += step;
}
}
int main() {
printf("\033[2J"); // clear screen
for (;;) {
printf("\033[H"); // home
// rendering body
{
// box blur: first vertical motion blur then horizontal motion blur
for (int i = 0; i<WIDTH; i++)
line_blur(i, WIDTH, HEIGHT);
fire[rand()%(WIDTH*HEIGHT)] = 255;
}
for (int j = 0; j<HEIGHT; j++) { // show the buffer
for (int i = 0; i<WIDTH; i++)
printf(palette[fire[i+j*WIDTH]]);
printf("\033[49m\n");
}
usleep(1000000/FPS);
}
return 0;
}
|
A three-element ring buffer is all we need: no more copies of the screen buffer.
The code gives the following animation (I slowed down the video a bit to make it clearer):

Let us add the horizontal motion blur (lines 63-64), thus making it a box blur:
Box blur
| #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#define WIDTH 80
#define HEIGHT 25
#define FPS 30
const char* palette[256] = {
#define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m "
ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36),
ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16),
ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4),
ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0),
ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0),
ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0),
ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0),
ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0),
ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0),
ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0),
ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0),
ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0),
ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0),
ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0),
ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0),
ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0),
#define W ANSIRGB(252,252,252)
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W
#undef W
#undef ANSIRGB
};
static uint8_t fire[WIDTH * HEIGHT];
void line_blur(int offset, int step, int nsteps) {
uint8_t circ[3] = {0, fire[offset], fire[offset+step]};
uint8_t beg = 1;
for (int i=0; i<nsteps; i++) {
fire[offset] = (circ[0]+circ[1]+circ[2])/3;
circ[(beg+++2)%3] = i+2<nsteps ? fire[offset+2*step] : 0;
offset += step;
}
}
int main() {
printf("\033[2J"); // clear screen
for (;;) {
printf("\033[H"); // home
// rendering body
{
// box blur: first vertical motion blur then horizontal motion blur
for (int i = 0; i<WIDTH; i++)
line_blur(i, WIDTH, HEIGHT);
for (int j = 0; j<HEIGHT; j++)
line_blur(j*WIDTH, 1, WIDTH);
fire[rand()%(WIDTH*HEIGHT)] = 255;
}
for (int j = 0; j<HEIGHT; j++) { // show the buffer
for (int i = 0; i<WIDTH; i++)
printf(palette[fire[i+j*WIDTH]]);
printf("\033[49m\n");
}
usleep(1000000/FPS);
}
return 0;
}
|

The heat from a single pixel quickly spreads out quickly, so it becomes nearly invisible in my palette.
On the first iteration, the white pixel is surrounded by eight black pixels; on the second, all nine pixels have a value of 255/9 = 28, and so on:
iteration 1 iteration 2 iteration 3
0 0 0 0 0 3 6 9 6 3
0 0 0 0 28 28 28 0 6 12 18 12 6
0 255 0 0 28 28 28 0 9 18 28 18 9
0 0 0 0 28 28 28 0 6 12 18 12 6
0 0 0 0 0 3 6 9 6 3
Here, I scattered sparks all over the screen, but in reality, the heat is coming directly from the fire.
Let us fix the code a bit to allow fire pixels to be generated on the bottom line only (lines 66-70):
Fire bed
| #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#define WIDTH 80
#define HEIGHT 25
#define FPS 30
const char* palette[256] = {
#define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m "
ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36),
ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16),
ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4),
ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0),
ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0),
ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0),
ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0),
ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0),
ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0),
ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0),
ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0),
ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0),
ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0),
ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0),
ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0),
ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0),
#define W ANSIRGB(252,252,252)
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W
#undef W
#undef ANSIRGB
};
static uint8_t fire[WIDTH * HEIGHT];
void line_blur(int offset, int step, int nsteps) {
uint8_t circ[3] = {0, fire[offset], fire[offset+step]};
uint8_t beg = 1;
for (int i=0; i<nsteps; i++) {
fire[offset] = (circ[0]+circ[1]+circ[2])/3;
circ[(beg+++2)%3] = i+2<nsteps ? fire[offset+2*step] : 0;
offset += step;
}
}
int main() {
printf("\033[2J"); // clear screen
for (;;) {
printf("\033[H"); // home
// rendering body
{
// box blur: first vertical motion blur then horizontal motion blur
for (int i = 0; i<WIDTH; i++)
line_blur(i, WIDTH, HEIGHT);
for (int j = 0; j<HEIGHT; j++)
line_blur(j*WIDTH, 1, WIDTH);
for (int i = 0; i<WIDTH; i++) { // add heat to the bed
int idx = i+(HEIGHT-1)*WIDTH;
if (!(rand()%32))
fire[idx] = 128+rand()%128; // sparks
}
}
for (int j = 0; j<HEIGHT; j++) { // show the buffer
for (int i = 0; i<WIDTH; i++)
printf(palette[fire[i+j*WIDTH]]);
printf("\033[49m\n");
}
usleep(1000000/FPS);
}
return 0;
}
|

The air does not randomly ignite anymore, even if the animation becomes less interesting.
In fact, we forgot about convection!
Let us just scroll the previous frame up one line at each step (lines 60-63):
Convection
| #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#define WIDTH 80
#define HEIGHT 25
#define FPS 30
const char* palette[256] = {
#define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m "
ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36),
ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16),
ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4),
ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0),
ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0),
ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0),
ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0),
ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0),
ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0),
ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0),
ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0),
ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0),
ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0),
ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0),
ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0),
ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0),
#define W ANSIRGB(252,252,252)
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W
#undef W
#undef ANSIRGB
};
static uint8_t fire[WIDTH * HEIGHT];
void line_blur(int offset, int step, int nsteps) {
uint8_t circ[3] = {0, fire[offset], fire[offset+step]};
uint8_t beg = 1;
for (int i=0; i<nsteps; i++) {
fire[offset] = (circ[0]+circ[1]+circ[2])/3;
circ[(beg+++2)%3] = i+2<nsteps ? fire[offset+2*step] : 0;
offset += step;
}
}
int main() {
printf("\033[2J"); // clear screen
for (;;) {
printf("\033[H"); // home
// rendering body
{
for (int j = 1; j<HEIGHT; j++) // scroll up
for (int i = 0; i<WIDTH; i++)
fire[i+(j-1)*WIDTH] = fire[i+j*WIDTH] ;
// box blur: first vertical motion blur then horizontal motion blur
for (int i = 0; i<WIDTH; i++)
line_blur(i, WIDTH, HEIGHT);
for (int j = 0; j<HEIGHT; j++)
line_blur(j*WIDTH, 1, WIDTH);
for (int i = 0; i<WIDTH; i++) { // add heat to the bed
int idx = i+(HEIGHT-1)*WIDTH;
if (!(rand()%32))
fire[idx] = 128+rand()%128; // sparks
}
}
for (int j = 0; j<HEIGHT; j++) { // show the buffer
for (int i = 0; i<WIDTH; i++)
printf(palette[fire[i+j*WIDTH]]);
printf("\033[49m\n");
}
usleep(1000000/FPS);
}
return 0;
}
|

That looks much more like it!
But our campfire should have embers: sparks usually don't come out of nowhere.
Let us paint the ember bed a permanent color (and thus add heat) to the bottom line of the picture (lines 74-75):
Ember bed
| #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#define WIDTH 80
#define HEIGHT 25
#define FPS 30
const char* palette[256] = {
#define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m "
ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36),
ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16),
ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4),
ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0),
ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0),
ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0),
ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0),
ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0),
ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0),
ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0),
ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0),
ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0),
ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0),
ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0),
ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0),
ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0),
#define W ANSIRGB(252,252,252)
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W
#undef W
#undef ANSIRGB
};
static uint8_t fire[WIDTH * HEIGHT];
void line_blur(int offset, int step, int nsteps) {
uint8_t circ[3] = {0, fire[offset], fire[offset+step]};
uint8_t beg = 1;
for (int i=0; i<nsteps; i++) {
fire[offset] = (circ[0]+circ[1]+circ[2])/3;
circ[(beg+++2)%3] = i+2<nsteps ? fire[offset+2*step] : 0;
offset += step;
}
}
int main() {
printf("\033[2J"); // clear screen
for (;;) {
printf("\033[H"); // home
// rendering body
{
for (int j = 1; j<HEIGHT; j++) // scroll up
for (int i = 0; i<WIDTH; i++)
fire[i+(j-1)*WIDTH] = fire[i+j*WIDTH] ;
// box blur: first vertical motion blur then horizontal motion blur
for (int i = 0; i<WIDTH; i++)
line_blur(i, WIDTH, HEIGHT);
for (int j = 0; j<HEIGHT; j++)
line_blur(j*WIDTH, 1, WIDTH);
for (int i = 0; i<WIDTH; i++) { // add heat to the bed
int idx = i+(HEIGHT-1)*WIDTH;
if (!(rand()%32))
fire[idx] = 128+rand()%128; // sparks
else
fire[idx] = fire[idx]<16 ? 16 : fire[idx]; // ember bed
}
}
for (int j = 0; j<HEIGHT; j++) { // show the buffer
for (int i = 0; i<WIDTH; i++)
printf(palette[fire[i+j*WIDTH]]);
printf("\033[49m\n");
}
usleep(1000000/FPS);
}
return 0;
}
|

That's almost it, but we have too much heat, don't you think?
Let us add a cooling effect as a final touch (lines 70-72):
Cooling
| #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#define WIDTH 80
#define HEIGHT 25
#define FPS 30
const char* palette[256] = {
#define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m "
ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36),
ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16),
ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4),
ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0),
ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0),
ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0),
ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0),
ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0),
ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0),
ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0),
ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0),
ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0),
ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0),
ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0),
ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0),
ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0),
#define W ANSIRGB(252,252,252)
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W
#undef W
#undef ANSIRGB
};
static uint8_t fire[WIDTH * HEIGHT];
void line_blur(int offset, int step, int nsteps) {
uint8_t circ[3] = {0, fire[offset], fire[offset+step]};
uint8_t beg = 1;
for (int i=0; i<nsteps; i++) {
fire[offset] = (circ[0]+circ[1]+circ[2])/3;
circ[(beg+++2)%3] = i+2<nsteps ? fire[offset+2*step] : 0;
offset += step;
}
}
int main() {
printf("\033[2J"); // clear screen
for (;;) {
printf("\033[H"); // home
// rendering body
{
for (int j = 1; j<HEIGHT; j++) // scroll up
for (int i = 0; i<WIDTH; i++)
fire[i+(j-1)*WIDTH] = fire[i+j*WIDTH] ;
// box blur: first vertical motion blur then horizontal motion blur
for (int i = 0; i<WIDTH; i++)
line_blur(i, WIDTH, HEIGHT);
for (int j = 0; j<HEIGHT; j++)
line_blur(j*WIDTH, 1, WIDTH);
for (int i = 0; i< WIDTH*HEIGHT; i++) // cooling
if (rand()%2 && fire[i]>0)
fire[i]--;
for (int i = 0; i<WIDTH; i++) { // add heat to the bed
int idx = i+(HEIGHT-1)*WIDTH;
if (!(rand()%32))
fire[idx] = 128+rand()%128; // sparks
else
fire[idx] = fire[idx]<16 ? 16 : fire[idx]; // ember bed
}
}
for (int j = 0; j<HEIGHT; j++) { // show the buffer
for (int i = 0; i<WIDTH; i++)
printf(palette[fire[i+j*WIDTH]]);
printf("\033[49m\n");
}
usleep(1000000/FPS);
}
return 0;
}
|

Well, that's it, I am happy with the result.
The mundane, non-cursed flame is lit.
I guess we're ready to start translating the code to wend, right?
Get ready a machina just around the corner, deus ex parked it for me.
Deus ex machina
The attentive reader might have noticed that in this demo, I have used the fire[]
buffer to render the animation,
but my wend language doesn't have arrays!
It is impossible to compute colors for each pixel independently, because the heat dissipation (and convection, too) requires knowledge of the state of neighboring pixels.
No worries, wend has functions.
Let us imagine that I need an 8-element array.
We can emulate it using eight different variables and two functions, the getter/setter pair:
uint8_t fire0, fire1, fire2, fire3, fire4, fire5, fire6, fire7;
uint8_t get_fire(int i) {
if (i==0) return fire0;
if (i==1) return fire1;
if (i==2) return fire2;
if (i==3) return fire3;
if (i==4) return fire4;
if (i==5) return fire5;
if (i==6) return fire6;
if (i==7) return fire7;
}
void set_fire(int i, uint8_t v) {
[...]
}
I omit the setter function because it's structurally similar to the getter.
The code seems trivial, but I have just made a linked list instead of an array.
To get to the 2'000th element, I'll have to do 2'000 comparisons.
It does not really matter, but I dislike it.
We can use a dichotomy to convert linear complexity to logarithmic.
uint8_t get_fire(int i) {
if (i<4) {
if (i<2) {
if (i<1) {
return fire0;
} else {
return fire1;
}
} else {
if (i<3) {
return fire2;
} else {
return fire3;
}
}
} else {
if (i<6) {
if (i<5) {
return fire4;
} else {
return fire5;
}
} else {
if (i<7) {
return fire6;
} else {
return fire7;
}
}
}
}
That's better.
This very code pushes me to write the article.
My terminal is 80x25, so I need 2'000 memory cells.
2'048 is very close to 2'000, and it's an exact power of two.
So, I get a minimal overhead and don't have to puzzle over about boundary conditions — I can just write a fully-balanced binary search tree.
I could have taken any programming language and generated the right text line.
However, I don't know why, but I decided to write a simple #define
switch in the source code of fire.
This #define
might switch between a base array and an emulated array: it would have been an easy way to make sure I didn't mess up anywhere.
I have asked myself: can this function be created using the C preprocessor?
To do that, I'd have to write a recursive #define
.
Is it possible to do this, and if so, how? Of course, I started googling the question.
And I accidentally stumbled upon a curious thread on cplusplus.com, I have even screenshoted it.
Screenshot

Someone asked the same question I did. In response, he was told three times that the #define
recursion was impossible. Mwahaha!
Keep in mind that I'm not the smartest person in the room.
I just found the right URL on Stack Overflow and only tried to sum up what I have learned.
How C preprocessor works, or why macros aren't functions
Let us dig into how the C preprocessor works.
In the above code (bonfire), we have already encountered the #define WIDTH 80
.
This is quite a standard case to define constants.
By the way, don't do this in C++, constexpr
is a better option!
Define macros have many unpleasant moments, which can be removed with constexpr
.
When the lexer encounters the WIDTH
token, it replaces it with 80 before running the compiler.
Macros can also look function-like.
For example, the famous #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
macro.
Note: don't do it like that! In the third decade of the 21st century, I don't see any reason to continue using code from the 70s.
The "execution," or rather the expansion of macro commands, is purely textual.
The preprocessor doesn't understand the C language.
So, if you give it MIN(habracadabra, circ][(beg+++))
, it'll happily convert it to (((habracadabra) < (circ][(beg+++))) ? (habracadabra) : (circ][(beg+++)
! Check it yourself with gcc -E source.c
.
When we want to use a function-like macro command, and we see the similar syntax, most programmers assume that it behaves like a function too.
It is tempting to think that first we evaluate the arguments and then pass them to the body of the parent macro command.
Nope.
The preprocessor isn't the C language, and macros don't behave that way, and the MIN(habracadabra, circ][(beg+++))
example is proof of that.
Let me list macro expansion rules in the order in which they're executed:
- casting to string (there's no the
#
operator in the article);
- substituting arguments for parameter names (without expanding the tokens);
- token concatenation (there are numerous
##
operators in the article);
- expanding parameter tokens;
- rescanning and expanding the result.
These are dark times, or the time has come for the black magic
Let us check the simplest example of tail recursion (in C):
void recursion(int d) {
printf("%d ", d);
if (d!=0) recursion(d - 1);
}
If we call recursion(3)
, we'll see 3 2 1 0
in the terminal.
We need to learn how to do something like that using macros only.
OK, let us go through the ingredients one by one, starting with the simplest and working our way up.
We need to know:
- how to decrement;
- how to do conditional branching;
- how to test a numeric value for zero;
- how to make a recursive call.
Decrement
Let us start with the first item, how to decrement.
The preprocessor is pretty dumb, it just does find-and-replace in the source code.
This may be annoying, but it also allows you to manipulate parts of expressions if you want to, so, it makes a certain sense.
The preprocessor doesn't know anything about arithmetics.
It just makes text substitutions, making things a bit tricky.
Let us make the first try:
ssloy@home:~$ gcc -P -E - <<<'
#define DEC(n) n - 1
DEC(3)
'
3 - 1
For better readability, I am running GCC directly from the command line, so you can see both the source code and the preprocessor output side-by-side.
So, the DEC(3)
macro command doesn't expand into the desired constant, 2
, but rather into the 3-1
expression.
It is not a problem if we are creative enough. Here comes the token concatenation:
ssloy@home:~$ gcc -P -E - <<<'
#define DEC(n) DEC_##n
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
DEC(3)
DEC(DEC(3))
'
2
DEC_DEC(3)
Upon expansion of DEC(3)
, the DEC_
and 3
tokens are merged, and a new DEC_3
token is generated, and this one expands to 2
.
Just what we needed!
There is one problem, though: the trick doesn't work with DEC(DEC(3))
.
Why is that? Let us check the rules of macro expansion.
Concatenation occurs prior to parameters expansion, so the code actually doesn't do what it looks like at first glance:
we merge the DEC_
token with the non-expanded text of the DEC(3)
parameter, and that's where it stops working.
No biggies, it's not too hard to help here: we can defer the concatenation by pushing it one level deeper:
ssloy@home:~$ gcc -P -E - <<<'
#define CONCAT(a,b) a##b
#define DEC(n) CONCAT(DEC_,n)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
DEC(3)
DEC(DEC(3))
DEC(DEC(DEC(3)))
'
2
1
0
I declare CONCAT
, the macro command to merge two tokens, and all problems disappear: decrement operates fine using numeric constants, not expressions.
Note: I can't decrement, for example, 4 in this code.
It's fair to ask whether it is reasonable to define a macro command for each numeric value.
Short answer: forget about reason when programming only with lexer without a parser!
The detailed answer: in this case, the decrement is based on the depth of recursion.
It seldom goes over a dozen or two levels.
Conditional branching
So, we have learned the most important thing: now we can create new tokens by merging, and we can use these tokens as names for other macro commands!
In such a case, branching is a piece of cake.
Let us take a look at the following code:
ssloy@home:~$ gcc -P -E - <<<'
#define CONCAT(a,b) a##b
#define IF_ELSE(b) CONCAT(IF_,b)
#define IF_0(i) ELSE_0
#define IF_1(i) i ELSE_1
#define ELSE_0(e) e
#define ELSE_1(e)
IF_ELSE(1)(then body)(else body)
IF_ELSE(0)(then body)(else body)
'
then body
else body
IF_ELSE
is a macro command that takes only 0 or 1 as an argument and generates either the IF_0
token or the IF_1
token using trivial concatenation.
IF_0
is a command that generates the ELSE_0
token, and discarding its own arguments along the way.
ELSE_0
is just an identity map.
Let us follow the whole chain of expanding IF_ELSE(0)(then body)(else body)
:
IF_ELSE(0)(then body)(else body)
IF_0(then body)(else body)
ELSE_0(else body)
(else body)
The expansion with the 1 as argument is pretty similar.
Check for zero
Now you, seasoned metaprogrammers, won't be scared of a simple null check :)
ssloy@home:~$ gcc -P -E - <<<'
#define SECOND(a, b, ...) b
#define TEST(...) SECOND(__VA_ARGS__, 0)
#define ISZERO(n) TEST(ISZERO_ ## n)
#define ISZERO_0 TRASH, 1
ISZERO(0)
ISZERO(3)
'
1
0
Let us get into it.
When we expand ISZERO(n)
, the first thing we do (again, look at the expansion rule order) is concatenation.
In this example, I expand ISZERO(0)
and ISZERO(3)
macros.
Let us follow the expansion path for ISZERO(3)
first.
ISZERO_3
is expanded to TEST(ISZERO_3)
, which, in its turn, is expanded to SECOND(ISZERO_3, 0)
, and it terminates in 0
constant,
since SECOND(a, b, ...)
is a variadic macro returning its second parameter.
Now let us follow the expansion path for ISZERO(0)
.
It turns out that it is expanded to TEST(ISZERO_0)
, where ISZERO_0
is the name of an already existing macro command!
Then it expands to a list of SECOND(TRASH,1,0)
, and finally to 1
.
This is the key idea: to get something with a comma to the arguments of SECOND
, we should pass zero to the ISZERO
command.
Recursion
If you have defeated the zero test, it means that only the sky is the limit for you.
Let us make the last step.
It's very important to know that all substitutions in macros happen during the lexing phase PRIOR to parsing.
The general idea is that it's up to the parser to handle recursions (hello, Turing completeness in templates).
The creators of the C lexer made significant efforts to prevent recursive calls.
This is necessary to avoid infinite loops during macro expansion.
Let us take a look at the following piece of code:
ssloy@home:~$ gcc -P -E - <<<'
#define FOO F BAR
#define BAR B FOO
FOO
BAR
'
F B FOO
B F BAR
It works like this: the preprocessor knows which macros it expands.
If it detects one of the macros again at the expanding stage, it paints it blue and leaves it as it is.
Let us suppose we want to expand the FOO
token.
The preprocessor enters the "expanding FOO
" state, processes the F
token.
However, when it expands the BAR
macro, it encounters the FOO
token again and immediately paints it blue, thus banning further expansion.
It's pretty much the same story when we expand the BAR
macro, and, therefore, instead of infinite recursion we obtain just one iteration.
Now let us add few parentheses:
ssloy@home:~$ gcc -P -E - <<<'
#define FOO() F BAR
#define BAR() B FOO
FOO()()()()()()()()()
'
F B F B F B F B F BAR
Interesting and curious.
What happened? Something pretty interesting happened: we expanded the FOO
macro command to a command with parameters.
Let us go through two levels of recursion:
FOO()()()()()()()()()
F BAR()()()()()()()()
F B FOO()()()()()()() <- here FOO is not painted blue!
When we encounter FOO
for the second time, the lexer does not recognize it because the lexer generated the FOO
token without parameters.
That is the end of the FOO
handling.
Then the lexer rescans the string, detects the brackets, and calls FOO
for the second time.
And then a third time.
A fourth time.
I am feeling lucky!
Let me remind you that a direct call to FOO()
inside BAR
doesn't work.
We should stop the context of an expanding macro command before we encounter the same macro command token with parameters.
And, as it turns out, it's not a hard thing to do.
First, let us add an empty macro command, EMPTY()
:
ssloy@home:~$ gcc -P -E - <<<'
#define EMPTY()
#define FOO() F BAR EMPTY() ()
#define BAR() B FOO EMPTY() ()
FOO()
#define EVAL(x) x
EVAL(FOO())
EVAL(EVAL(FOO()))
EVAL(EVAL(EVAL(FOO())))
EVAL(EVAL(EVAL(EVAL(FOO()))))
'
F BAR ()
F B FOO ()
F B F BAR ()
F B F B FOO ()
F B F B F BAR ()
Now, upon expansion of FOO()
, the lexer creates the F
token and the BAR
token that don't match the name of the BAR()
macro command.
Then the lexer handles the empty token and leaves the brackets ()
as is.
That's it, the FOO()
context is finished, and nothing has been painted blue.
However, FOO()
expands to a rather boring F BAR ()
, just like before.
What do we get? Look further down the code.
I define the identity macro command #define EVAL(x) x
, and the EVAL(FOO())
recursion becomes much more interesting.
Let us follow the whole chain (refresh your memory of the expansion rules, especially the last one):
EVAL(FOO()) <- start EVAL context
FOO() <- at this stage, we start expansion of the EVAL parameters
F BAR () <- the single EVAL parameter is expanded
F B FOO () <- RESCAN after EVAL parameters have been expanded!</code></pre>
Well, that's about it.
After wrapping it up in two EVAL
s, we're done here.
In general, we need about as many EVAL
wrappers as there are levels of recursion.
This can be provided with just a few lines of code.
Let us put it all together.
Here is the reference C program that we want to mimick with #define
recursion:
ssloy@home:~$ gcc -xc - <<<'
#include <stdio.h>
void foo(int d) {
printf("%d ", d);
if (d!=0) foo(d - 1);
}
int main() {
foo(3);
return 0;
}
' && ./a.out
3 2 1 0 ssloy@home:~$
No problem at all, we can do that! Here are just copy-pasted code snippets we saw before.
We have to be careful with another level of delay on line 28.
The BAR
token is generated inside the branching instruction, so we need to wait for one more iteration of EVAL
to ensure that BAR
isn't painted blue.
| ssloy@home:~$ gcc -xc - <<<'
#include <stdio.h>
#define CONCAT(a,b) a##b
#define DEC(n) CONCAT(DEC_,n)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define IF_ELSE(b) CONCAT(IF_,b)
#define IF_0(i) ELSE_0
#define IF_1(i) i ELSE_1
#define ELSE_0(e) e
#define ELSE_1(e)
#define SECOND(a, b, ...) b
#define TEST(...) SECOND(__VA_ARGS__, 0)
#define ISZERO(n) TEST(ISZERO_ ## n)
#define ISZERO_0 TRASH, 1
#define EMPTY()
#define FOO(d) \
printf("%d ", d); \
IF_ELSE(ISZERO(d)) \
( ) \
( BAR EMPTY EMPTY() () (DEC(d)) )
#define BAR(d) FOO EMPTY() (d)
#define EVAL(x) EVAL1(EVAL1(EVAL1(x)))
#define EVAL1(x) EVAL2(EVAL2(EVAL2(x)))
#define EVAL2(x) EVAL3(EVAL3(EVAL3(x)))
#define EVAL3(x) x
int main() {
EVAL(FOO(3))
return 0;
}
' && ./a.out
3 2 1 0 ssloy@home:~$
|
Cursed fire
Here is the complete source file for the cursed fire.
Cursed fire
| #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#define WIDTH 80
#define HEIGHT 25
#define FPS 30
const char* palette[256] = {
#define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m "
ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36),
ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16),
ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4),
ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0),
ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0),
ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0),
ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0),
ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0),
ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0),
ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0),
ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0),
ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0),
ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0),
ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0),
ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0),
ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0),
#define W ANSIRGB(252,252,252)
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W
#undef W
#undef ANSIRGB
};
#define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__
#define CONCAT(a,b) a##b
#define IF_ELSE(b) CONCAT(IF_,b)
#define IF_0(...) ELSE_0
#define IF_1(...) __VA_ARGS__ ELSE_1
#define ELSE_0(...) __VA_ARGS__
#define ELSE_1(...)
#define SECOND(a, b, ...) b
#define TEST(...) SECOND(__VA_ARGS__, 0)
#define ISZERO(n) TEST(ISZERO_ ## n)
#define ISZERO_0 TRASH, 1
#define DEC(n) CONCAT(DEC_, n)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define DEC_6 5
#define DEC_7 6
#define DEC_8 7
#define DEC_9 8
#define DEC_10 9
#define DEC_11 10
#define EMPTY()
#define DELAY(id) id EMPTY()
#define DELAY2(...) __VA_ARGS__ DELAY(EMPTY)()
#define DEPTH 11
#define FIRE(d,id) \
IF_ELSE(ISZERO(d)) \
( uint8_t id; ) \
( \
DELAY2(FIRE_)( DEC(d), id##0 ) \
DELAY2(FIRE_)( DEC(d), id##1 ) \
)
#define FIRE_(...) DELAY(FIRE)(__VA_ARGS__)
EVAL(FIRE(DEPTH,fire))
uint8_t get_fire(int i) {
#define GETTER(d,m,id) \
IF_ELSE(ISZERO(d)) \
( return id; ) \
( \
if (i<(m)) \
DELAY2(GETTER_)(DEC(d), ((m)-(1<<DEC(DEC(d)))), id##0) \
else \
DELAY2(GETTER_)(DEC(d), ((m)+(1<<DEC(DEC(d)))), id##1) \
)
#define GETTER_(...) DELAY(GETTER) (__VA_ARGS__)
EVAL(GETTER(DEPTH,1<<(DEPTH-1),fire));
}
void set_fire(int i, uint8_t v) {
#define SETTER(d,m,id) \
IF_ELSE(ISZERO(d)) \
( id = v; ) \
( \
if (i<(m)) \
DELAY2(SETTER_)(DEC(d), ((m)-(1<<DEC(DEC(d)))), id##0) \
else \
DELAY2(SETTER_)(DEC(d), ((m)+(1<<DEC(DEC(d)))), id##1) \
)
#define SETTER_(...) DELAY(SETTER)(__VA_ARGS__)
EVAL(SETTER(DEPTH,1<<(DEPTH-1),fire));
}
void line_blur(int offset, int step, int nsteps) {
uint8_t circ[3] = {0, get_fire(offset), get_fire(offset+step)};
uint8_t beg = 1;
for (int i=0; i<nsteps; i++) {
set_fire(offset, (circ[0]+circ[1]+circ[2])/3);
circ[(beg+++2)%3] = i+2<nsteps ? get_fire(offset+2*step) : 0;
offset += step;
}
}
int main() {
printf("\033[2J"); // clear screen
for (;;) {
printf("\033[H"); // home
for (int j = 1; j<HEIGHT; j++) // scroll up
for (int i = 0; i<WIDTH; i++)
set_fire(i+(j-1)*WIDTH, get_fire(i+j*WIDTH));
// box blur: first horizontal motion blur then vertical motion blur
for (int j = 0; j<HEIGHT; j++)
line_blur(j*WIDTH, 1, WIDTH);
for (int i = 0; i<WIDTH; i++)
line_blur(i, WIDTH, HEIGHT);
for (int i = 0; i< WIDTH*HEIGHT; i++) // cool
if (rand()%2 && get_fire(i)>0)
set_fire(i, get_fire(i)-1);
for (int i = 0; i<WIDTH; i++) { // add heat to the bed
int idx = i+(HEIGHT-1)*WIDTH;
if (!(rand()%32))
set_fire(idx, 128+rand()%128); // sparks
else
set_fire(idx, get_fire(idx)<16 ? 16 : get_fire(idx)); // ember bed
}
for (int j = 0; j<HEIGHT; j++) { // show the buffer
for (int i = 0; i<WIDTH; i++)
printf(palette[get_fire(i+j*WIDTH)]);
printf("\033[49m\n");
}
usleep(1000000/FPS);
}
return 0;
}
|
As I promised, the entire frame buffer is stored in separate variables; there are no arrays!
If you look at the lines 80-88, for example (highlighted in the code), those generate the variables.
By running gcc -E
on the source file, this portion generates the 2048 variables just as expected:
Preprocessed variables
| uint8_t fire00000000000; uint8_t fire00000000001; uint8_t fire00000000010; uint8_t fire00000000011; uint8_t fire00000000100; uint8_t fire00000000101; uint8_t fire00000000110; uint8_t fire00000000111; uint8_t fire00000001000; uint8_t fire00000001001; uint8_t fire00000001010; uint8_t fire00000001011; uint8_t fire00000001100; uint8_t fire00000001101; uint8_t fire00000001110; uint8_t fire00000001111; uint8_t fire00000010000; uint8_t fire00000010001; uint8_t fire00000010010; uint8_t fire00000010011; uint8_t fire00000010100; uint8_t fire00000010101; uint8_t fire00000010110; uint8_t fire00000010111; uint8_t fire00000011000; uint8_t fire00000011001; uint8_t fire00000011010; uint8_t fire00000011011; uint8_t fire00000011100; uint8_t fire00000011101; uint8_t fire00000011110; uint8_t fire00000011111; uint8_t fire00000100000; uint8_t fire00000100001; uint8_t fire00000100010; uint8_t fire00000100011; uint8_t fire00000100100; uint8_t fire00000100101; uint8_t fire00000100110; uint8_t fire00000100111; uint8_t fire00000101000; uint8_t fire00000101001; uint8_t fire00000101010; uint8_t fire00000101011; uint8_t fire00000101100; uint8_t fire00000101101; uint8_t fire00000101110; uint8_t fire00000101111; uint8_t fire00000110000; uint8_t fire00000110001; uint8_t fire00000110010; uint8_t fire00000110011; uint8_t fire00000110100; uint8_t fire00000110101; uint8_t fire00000110110; uint8_t fire00000110111; uint8_t fire00000111000; uint8_t fire00000111001; uint8_t fire00000111010; uint8_t fire00000111011; uint8_t fire00000111100; uint8_t fire00000111101; uint8_t fire00000111110; uint8_t fire00000111111; uint8_t fire00001000000; uint8_t fire00001000001; uint8_t fire00001000010; uint8_t fire00001000011; uint8_t fire00001000100; uint8_t fire00001000101; uint8_t fire00001000110; uint8_t fire00001000111; uint8_t fire00001001000; uint8_t fire00001001001; uint8_t fire00001001010; uint8_t fire00001001011; uint8_t fire00001001100; uint8_t fire00001001101; uint8_t fire00001001110; uint8_t fire00001001111; uint8_t fire00001010000; uint8_t fire00001010001; uint8_t fire00001010010; uint8_t fire00001010011; uint8_t fire00001010100; uint8_t fire00001010101; uint8_t fire00001010110; uint8_t fire00001010111; uint8_t fire00001011000; uint8_t fire00001011001; uint8_t fire00001011010; uint8_t fire00001011011; uint8_t fire00001011100; uint8_t fire00001011101; uint8_t fire00001011110; uint8_t fire00001011111; uint8_t fire00001100000; uint8_t fire00001100001; uint8_t fire00001100010; uint8_t fire00001100011; uint8_t fire00001100100; uint8_t fire00001100101; uint8_t fire00001100110; uint8_t fire00001100111; uint8_t fire00001101000; uint8_t fire00001101001; uint8_t fire00001101010; uint8_t fire00001101011; uint8_t fire00001101100; uint8_t fire00001101101; uint8_t fire00001101110; uint8_t fire00001101111; uint8_t fire00001110000; uint8_t fire00001110001; uint8_t fire00001110010; uint8_t fire00001110011; uint8_t fire00001110100; uint8_t fire00001110101; uint8_t fire00001110110; uint8_t fire00001110111; uint8_t fire00001111000; uint8_t fire00001111001; uint8_t fire00001111010; uint8_t fire00001111011; uint8_t fire00001111100; uint8_t fire00001111101; uint8_t fire00001111110; uint8_t fire00001111111; uint8_t fire00010000000; uint8_t fire00010000001; uint8_t fire00010000010; uint8_t fire00010000011; uint8_t fire00010000100; uint8_t fire00010000101; uint8_t fire00010000110; uint8_t fire00010000111; uint8_t fire00010001000; uint8_t fire00010001001; uint8_t fire00010001010; uint8_t fire00010001011; uint8_t fire00010001100; uint8_t fire00010001101; uint8_t fire00010001110; uint8_t fire00010001111; uint8_t fire00010010000; uint8_t fire00010010001; uint8_t fire00010010010; uint8_t fire00010010011; uint8_t fire00010010100; uint8_t fire00010010101; uint8_t fire00010010110; uint8_t fire00010010111; uint8_t fire00010011000; uint8_t fire00010011001; uint8_t fire00010011010; uint8_t fire00010011011; uint8_t fire00010011100; uint8_t fire00010011101; uint8_t fire00010011110; uint8_t fire00010011111; uint8_t fire00010100000; uint8_t fire00010100001; uint8_t fire00010100010; uint8_t fire00010100011; uint8_t fire00010100100; uint8_t fire00010100101; uint8_t fire00010100110; uint8_t fire00010100111; uint8_t fire00010101000; uint8_t fire00010101001; uint8_t fire00010101010; uint8_t fire00010101011; uint8_t fire00010101100; uint8_t fire00010101101; uint8_t fire00010101110; uint8_t fire00010101111; uint8_t fire00010110000; uint8_t fire00010110001; uint8_t fire00010110010; uint8_t fire00010110011; uint8_t fire00010110100; uint8_t fire00010110101; uint8_t fire00010110110; uint8_t fire00010110111; uint8_t fire00010111000; uint8_t fire00010111001; uint8_t fire00010111010; uint8_t fire00010111011; uint8_t fire00010111100; uint8_t fire00010111101; uint8_t fire00010111110; uint8_t fire00010111111; uint8_t fire00011000000; uint8_t fire00011000001; uint8_t fire00011000010; uint8_t fire00011000011; uint8_t fire00011000100; uint8_t fire00011000101; uint8_t fire00011000110; uint8_t fire00011000111; uint8_t fire00011001000; uint8_t fire00011001001; uint8_t fire00011001010; uint8_t fire00011001011; uint8_t fire00011001100; uint8_t fire00011001101; uint8_t fire00011001110; uint8_t fire00011001111; uint8_t fire00011010000; uint8_t fire00011010001; uint8_t fire00011010010; uint8_t fire00011010011; uint8_t fire00011010100; uint8_t fire00011010101; uint8_t fire00011010110; uint8_t fire00011010111; uint8_t fire00011011000; uint8_t fire00011011001; uint8_t fire00011011010; uint8_t fire00011011011; uint8_t fire00011011100; uint8_t fire00011011101; uint8_t fire00011011110; uint8_t fire00011011111; uint8_t fire00011100000; uint8_t fire00011100001; uint8_t fire00011100010; uint8_t fire00011100011; uint8_t fire00011100100; uint8_t fire00011100101; uint8_t fire00011100110; uint8_t fire00011100111; uint8_t fire00011101000; uint8_t fire00011101001; uint8_t fire00011101010; uint8_t fire00011101011; uint8_t fire00011101100; uint8_t fire00011101101; uint8_t fire00011101110; uint8_t fire00011101111; uint8_t fire00011110000; uint8_t fire00011110001; uint8_t fire00011110010; uint8_t fire00011110011; uint8_t fire00011110100; uint8_t fire00011110101; uint8_t fire00011110110; uint8_t fire00011110111; uint8_t fire00011111000; uint8_t fire00011111001; uint8_t fire00011111010; uint8_t fire00011111011; uint8_t fire00011111100; uint8_t fire00011111101; uint8_t fire00011111110; uint8_t fire00011111111; uint8_t fire00100000000; uint8_t fire00100000001; uint8_t fire00100000010; uint8_t fire00100000011; uint8_t fire00100000100; uint8_t fire00100000101; uint8_t fire00100000110; uint8_t fire00100000111; uint8_t fire00100001000; uint8_t fire00100001001; uint8_t fire00100001010; uint8_t fire00100001011; uint8_t fire00100001100; uint8_t fire00100001101; uint8_t fire00100001110; uint8_t fire00100001111; uint8_t fire00100010000; uint8_t fire00100010001; uint8_t fire00100010010; uint8_t fire00100010011; uint8_t fire00100010100; uint8_t fire00100010101; uint8_t fire00100010110; uint8_t fire00100010111; uint8_t fire00100011000; uint8_t fire00100011001; uint8_t fire00100011010; uint8_t fire00100011011; uint8_t fire00100011100; uint8_t fire00100011101; uint8_t fire00100011110; uint8_t fire00100011111; uint8_t fire00100100000; uint8_t fire00100100001; uint8_t fire00100100010; uint8_t fire00100100011; uint8_t fire00100100100; uint8_t fire00100100101; uint8_t fire00100100110; uint8_t fire00100100111; uint8_t fire00100101000; uint8_t fire00100101001; uint8_t fire00100101010; uint8_t fire00100101011; uint8_t fire00100101100; uint8_t fire00100101101; uint8_t fire00100101110; uint8_t fire00100101111; uint8_t fire00100110000; uint8_t fire00100110001; uint8_t fire00100110010; uint8_t fire00100110011; uint8_t fire00100110100; uint8_t fire00100110101; uint8_t fire00100110110; uint8_t fire00100110111; uint8_t fire00100111000; uint8_t fire00100111001; uint8_t fire00100111010; uint8_t fire00100111011; uint8_t fire00100111100; uint8_t fire00100111101; uint8_t fire00100111110; uint8_t fire00100111111; uint8_t fire00101000000; uint8_t fire00101000001; uint8_t fire00101000010; uint8_t fire00101000011; uint8_t fire00101000100; uint8_t fire00101000101; uint8_t fire00101000110; uint8_t fire00101000111; uint8_t fire00101001000; uint8_t fire00101001001; uint8_t fire00101001010; uint8_t fire00101001011; uint8_t fire00101001100; uint8_t fire00101001101; uint8_t fire00101001110; uint8_t fire00101001111; uint8_t fire00101010000; uint8_t fire00101010001; uint8_t fire00101010010; uint8_t fire00101010011; uint8_t fire00101010100; uint8_t fire00101010101; uint8_t fire00101010110; uint8_t fire00101010111; uint8_t fire00101011000; uint8_t fire00101011001; uint8_t fire00101011010; uint8_t fire00101011011; uint8_t fire00101011100; uint8_t fire00101011101; uint8_t fire00101011110; uint8_t fire00101011111; uint8_t fire00101100000; uint8_t fire00101100001; uint8_t fire00101100010; uint8_t fire00101100011; uint8_t fire00101100100; uint8_t fire00101100101; uint8_t fire00101100110; uint8_t fire00101100111; uint8_t fire00101101000; uint8_t fire00101101001; uint8_t fire00101101010; uint8_t fire00101101011; uint8_t fire00101101100; uint8_t fire00101101101; uint8_t fire00101101110; uint8_t fire00101101111; uint8_t fire00101110000; uint8_t fire00101110001; uint8_t fire00101110010; uint8_t fire00101110011; uint8_t fire00101110100; uint8_t fire00101110101; uint8_t fire00101110110; uint8_t fire00101110111; uint8_t fire00101111000; uint8_t fire00101111001; uint8_t fire00101111010; uint8_t fire00101111011; uint8_t fire00101111100; uint8_t fire00101111101; uint8_t fire00101111110; uint8_t fire00101111111; uint8_t fire00110000000; uint8_t fire00110000001; uint8_t fire00110000010; uint8_t fire00110000011; uint8_t fire00110000100; uint8_t fire00110000101; uint8_t fire00110000110; uint8_t fire00110000111; uint8_t fire00110001000; uint8_t fire00110001001; uint8_t fire00110001010; uint8_t fire00110001011; uint8_t fire00110001100; uint8_t fire00110001101; uint8_t fire00110001110; uint8_t fire00110001111; uint8_t fire00110010000; uint8_t fire00110010001; uint8_t fire00110010010; uint8_t fire00110010011; uint8_t fire00110010100; uint8_t fire00110010101; uint8_t fire00110010110; uint8_t fire00110010111; uint8_t fire00110011000; uint8_t fire00110011001; uint8_t fire00110011010; uint8_t fire00110011011; uint8_t fire00110011100; uint8_t fire00110011101; uint8_t fire00110011110; uint8_t fire00110011111; uint8_t fire00110100000; uint8_t fire00110100001; uint8_t fire00110100010; uint8_t fire00110100011; uint8_t fire00110100100; uint8_t fire00110100101; uint8_t fire00110100110; uint8_t fire00110100111; uint8_t fire00110101000; uint8_t fire00110101001; uint8_t fire00110101010; uint8_t fire00110101011; uint8_t fire00110101100; uint8_t fire00110101101; uint8_t fire00110101110; uint8_t fire00110101111; uint8_t fire00110110000; uint8_t fire00110110001; uint8_t fire00110110010; uint8_t fire00110110011; uint8_t fire00110110100; uint8_t fire00110110101; uint8_t fire00110110110; uint8_t fire00110110111; uint8_t fire00110111000; uint8_t fire00110111001; uint8_t fire00110111010; uint8_t fire00110111011; uint8_t fire00110111100; uint8_t fire00110111101; uint8_t fire00110111110; uint8_t fire00110111111; uint8_t fire00111000000; uint8_t fire00111000001; uint8_t fire00111000010; uint8_t fire00111000011; uint8_t fire00111000100; uint8_t fire00111000101; uint8_t fire00111000110; uint8_t fire00111000111; uint8_t fire00111001000; uint8_t fire00111001001; uint8_t fire00111001010; uint8_t fire00111001011; uint8_t fire00111001100; uint8_t fire00111001101; uint8_t fire00111001110; uint8_t fire00111001111; uint8_t fire00111010000; uint8_t fire00111010001; uint8_t fire00111010010; uint8_t fire00111010011; uint8_t fire00111010100; uint8_t fire00111010101; uint8_t fire00111010110; uint8_t fire00111010111; uint8_t fire00111011000; uint8_t fire00111011001; uint8_t fire00111011010; uint8_t fire00111011011; uint8_t fire00111011100; uint8_t fire00111011101; uint8_t fire00111011110; uint8_t fire00111011111; uint8_t fire00111100000; uint8_t fire00111100001; uint8_t fire00111100010; uint8_t fire00111100011; uint8_t fire00111100100; uint8_t fire00111100101; uint8_t fire00111100110; uint8_t fire00111100111; uint8_t fire00111101000; uint8_t fire00111101001; uint8_t fire00111101010; uint8_t fire00111101011; uint8_t fire00111101100; uint8_t fire00111101101; uint8_t fire00111101110; uint8_t fire00111101111; uint8_t fire00111110000; uint8_t fire00111110001; uint8_t fire00111110010; uint8_t fire00111110011; uint8_t fire00111110100; uint8_t fire00111110101; uint8_t fire00111110110; uint8_t fire00111110111; uint8_t fire00111111000; uint8_t fire00111111001; uint8_t fire00111111010; uint8_t fire00111111011; uint8_t fire00111111100; uint8_t fire00111111101; uint8_t fire00111111110; uint8_t fire00111111111; uint8_t fire01000000000; uint8_t fire01000000001; uint8_t fire01000000010; uint8_t fire01000000011; uint8_t fire01000000100; uint8_t fire01000000101; uint8_t fire01000000110; uint8_t fire01000000111; uint8_t fire01000001000; uint8_t fire01000001001; uint8_t fire01000001010; uint8_t fire01000001011; uint8_t fire01000001100; uint8_t fire01000001101; uint8_t fire01000001110; uint8_t fire01000001111; uint8_t fire01000010000; uint8_t fire01000010001; uint8_t fire01000010010; uint8_t fire01000010011; uint8_t fire01000010100; uint8_t fire01000010101; uint8_t fire01000010110; uint8_t fire01000010111; uint8_t fire01000011000; uint8_t fire01000011001; uint8_t fire01000011010; uint8_t fire01000011011; uint8_t fire01000011100; uint8_t fire01000011101; uint8_t fire01000011110; uint8_t fire01000011111; uint8_t fire01000100000; uint8_t fire01000100001; uint8_t fire01000100010; uint8_t fire01000100011; uint8_t fire01000100100; uint8_t fire01000100101; uint8_t fire01000100110; uint8_t fire01000100111; uint8_t fire01000101000; uint8_t fire01000101001; uint8_t fire01000101010; uint8_t fire01000101011; uint8_t fire01000101100; uint8_t fire01000101101; uint8_t fire01000101110; uint8_t fire01000101111; uint8_t fire01000110000; uint8_t fire01000110001; uint8_t fire01000110010; uint8_t fire01000110011; uint8_t fire01000110100; uint8_t fire01000110101; uint8_t fire01000110110; uint8_t fire01000110111; uint8_t fire01000111000; uint8_t fire01000111001; uint8_t fire01000111010; uint8_t fire01000111011; uint8_t fire01000111100; uint8_t fire01000111101; uint8_t fire01000111110; uint8_t fire01000111111; uint8_t fire01001000000; uint8_t fire01001000001; uint8_t fire01001000010; uint8_t fire01001000011; uint8_t fire01001000100; uint8_t fire01001000101; uint8_t fire01001000110; uint8_t fire01001000111; uint8_t fire01001001000; uint8_t fire01001001001; uint8_t fire01001001010; uint8_t fire01001001011; uint8_t fire01001001100; uint8_t fire01001001101; uint8_t fire01001001110; uint8_t fire01001001111; uint8_t fire01001010000; uint8_t fire01001010001; uint8_t fire01001010010; uint8_t fire01001010011; uint8_t fire01001010100; uint8_t fire01001010101; uint8_t fire01001010110; uint8_t fire01001010111; uint8_t fire01001011000; uint8_t fire01001011001; uint8_t fire01001011010; uint8_t fire01001011011; uint8_t fire01001011100; uint8_t fire01001011101; uint8_t fire01001011110; uint8_t fire01001011111; uint8_t fire01001100000; uint8_t fire01001100001; uint8_t fire01001100010; uint8_t fire01001100011; uint8_t fire01001100100; uint8_t fire01001100101; uint8_t fire01001100110; uint8_t fire01001100111; uint8_t fire01001101000; uint8_t fire01001101001; uint8_t fire01001101010; uint8_t fire01001101011; uint8_t fire01001101100; uint8_t fire01001101101; uint8_t fire01001101110; uint8_t fire01001101111; uint8_t fire01001110000; uint8_t fire01001110001; uint8_t fire01001110010; uint8_t fire01001110011; uint8_t fire01001110100; uint8_t fire01001110101; uint8_t fire01001110110; uint8_t fire01001110111; uint8_t fire01001111000; uint8_t fire01001111001; uint8_t fire01001111010; uint8_t fire01001111011; uint8_t fire01001111100; uint8_t fire01001111101; uint8_t fire01001111110; uint8_t fire01001111111; uint8_t fire01010000000; uint8_t fire01010000001; uint8_t fire01010000010; uint8_t fire01010000011; uint8_t fire01010000100; uint8_t fire01010000101; uint8_t fire01010000110; uint8_t fire01010000111; uint8_t fire01010001000; uint8_t fire01010001001; uint8_t fire01010001010; uint8_t fire01010001011; uint8_t fire01010001100; uint8_t fire01010001101; uint8_t fire01010001110; uint8_t fire01010001111; uint8_t fire01010010000; uint8_t fire01010010001; uint8_t fire01010010010; uint8_t fire01010010011; uint8_t fire01010010100; uint8_t fire01010010101; uint8_t fire01010010110; uint8_t fire01010010111; uint8_t fire01010011000; uint8_t fire01010011001; uint8_t fire01010011010; uint8_t fire01010011011; uint8_t fire01010011100; uint8_t fire01010011101; uint8_t fire01010011110; uint8_t fire01010011111; uint8_t fire01010100000; uint8_t fire01010100001; uint8_t fire01010100010; uint8_t fire01010100011; uint8_t fire01010100100; uint8_t fire01010100101; uint8_t fire01010100110; uint8_t fire01010100111; uint8_t fire01010101000; uint8_t fire01010101001; uint8_t fire01010101010; uint8_t fire01010101011; uint8_t fire01010101100; uint8_t fire01010101101; uint8_t fire01010101110; uint8_t fire01010101111; uint8_t fire01010110000; uint8_t fire01010110001; uint8_t fire01010110010; uint8_t fire01010110011; uint8_t fire01010110100; uint8_t fire01010110101; uint8_t fire01010110110; uint8_t fire01010110111; uint8_t fire01010111000; uint8_t fire01010111001; uint8_t fire01010111010; uint8_t fire01010111011; uint8_t fire01010111100; uint8_t fire01010111101; uint8_t fire01010111110; uint8_t fire01010111111; uint8_t fire01011000000; uint8_t fire01011000001; uint8_t fire01011000010; uint8_t fire01011000011; uint8_t fire01011000100; uint8_t fire01011000101; uint8_t fire01011000110; uint8_t fire01011000111; uint8_t fire01011001000; uint8_t fire01011001001; uint8_t fire01011001010; uint8_t fire01011001011; uint8_t fire01011001100; uint8_t fire01011001101; uint8_t fire01011001110; uint8_t fire01011001111; uint8_t fire01011010000; uint8_t fire01011010001; uint8_t fire01011010010; uint8_t fire01011010011; uint8_t fire01011010100; uint8_t fire01011010101; uint8_t fire01011010110; uint8_t fire01011010111; uint8_t fire01011011000; uint8_t fire01011011001; uint8_t fire01011011010; uint8_t fire01011011011; uint8_t fire01011011100; uint8_t fire01011011101; uint8_t fire01011011110; uint8_t fire01011011111; uint8_t fire01011100000; uint8_t fire01011100001; uint8_t fire01011100010; uint8_t fire01011100011; uint8_t fire01011100100; uint8_t fire01011100101; uint8_t fire01011100110; uint8_t fire01011100111; uint8_t fire01011101000; uint8_t fire01011101001; uint8_t fire01011101010; uint8_t fire01011101011; uint8_t fire01011101100; uint8_t fire01011101101; uint8_t fire01011101110; uint8_t fire01011101111; uint8_t fire01011110000; uint8_t fire01011110001; uint8_t fire01011110010; uint8_t fire01011110011; uint8_t fire01011110100; uint8_t fire01011110101; uint8_t fire01011110110; uint8_t fire01011110111; uint8_t fire01011111000; uint8_t fire01011111001; uint8_t fire01011111010; uint8_t fire01011111011; uint8_t fire01011111100; uint8_t fire01011111101; uint8_t fire01011111110; uint8_t fire01011111111; uint8_t fire01100000000; uint8_t fire01100000001; uint8_t fire01100000010; uint8_t fire01100000011; uint8_t fire01100000100; uint8_t fire01100000101; uint8_t fire01100000110; uint8_t fire01100000111; uint8_t fire01100001000; uint8_t fire01100001001; uint8_t fire01100001010; uint8_t fire01100001011; uint8_t fire01100001100; uint8_t fire01100001101; uint8_t fire01100001110; uint8_t fire01100001111; uint8_t fire01100010000; uint8_t fire01100010001; uint8_t fire01100010010; uint8_t fire01100010011; uint8_t fire01100010100; uint8_t fire01100010101; uint8_t fire01100010110; uint8_t fire01100010111; uint8_t fire01100011000; uint8_t fire01100011001; uint8_t fire01100011010; uint8_t fire01100011011; uint8_t fire01100011100; uint8_t fire01100011101; uint8_t fire01100011110; uint8_t fire01100011111; uint8_t fire01100100000; uint8_t fire01100100001; uint8_t fire01100100010; uint8_t fire01100100011; uint8_t fire01100100100; uint8_t fire01100100101; uint8_t fire01100100110; uint8_t fire01100100111; uint8_t fire01100101000; uint8_t fire01100101001; uint8_t fire01100101010; uint8_t fire01100101011; uint8_t fire01100101100; uint8_t fire01100101101; uint8_t fire01100101110; uint8_t fire01100101111; uint8_t fire01100110000; uint8_t fire01100110001; uint8_t fire01100110010; uint8_t fire01100110011; uint8_t fire01100110100; uint8_t fire01100110101; uint8_t fire01100110110; uint8_t fire01100110111; uint8_t fire01100111000; uint8_t fire01100111001; uint8_t fire01100111010; uint8_t fire01100111011; uint8_t fire01100111100; uint8_t fire01100111101; uint8_t fire01100111110; uint8_t fire01100111111; uint8_t fire01101000000; uint8_t fire01101000001; uint8_t fire01101000010; uint8_t fire01101000011; uint8_t fire01101000100; uint8_t fire01101000101; uint8_t fire01101000110; uint8_t fire01101000111; uint8_t fire01101001000; uint8_t fire01101001001; uint8_t fire01101001010; uint8_t fire01101001011; uint8_t fire01101001100; uint8_t fire01101001101; uint8_t fire01101001110; uint8_t fire01101001111; uint8_t fire01101010000; uint8_t fire01101010001; uint8_t fire01101010010; uint8_t fire01101010011; uint8_t fire01101010100; uint8_t fire01101010101; uint8_t fire01101010110; uint8_t fire01101010111; uint8_t fire01101011000; uint8_t fire01101011001; uint8_t fire01101011010; uint8_t fire01101011011; uint8_t fire01101011100; uint8_t fire01101011101; uint8_t fire01101011110; uint8_t fire01101011111; uint8_t fire01101100000; uint8_t fire01101100001; uint8_t fire01101100010; uint8_t fire01101100011; uint8_t fire01101100100; uint8_t fire01101100101; uint8_t fire01101100110; uint8_t fire01101100111; uint8_t fire01101101000; uint8_t fire01101101001; uint8_t fire01101101010; uint8_t fire01101101011; uint8_t fire01101101100; uint8_t fire01101101101; uint8_t fire01101101110; uint8_t fire01101101111; uint8_t fire01101110000; uint8_t fire01101110001; uint8_t fire01101110010; uint8_t fire01101110011; uint8_t fire01101110100; uint8_t fire01101110101; uint8_t fire01101110110; uint8_t fire01101110111; uint8_t fire01101111000; uint8_t fire01101111001; uint8_t fire01101111010; uint8_t fire01101111011; uint8_t fire01101111100; uint8_t fire01101111101; uint8_t fire01101111110; uint8_t fire01101111111; uint8_t fire01110000000; uint8_t fire01110000001; uint8_t fire01110000010; uint8_t fire01110000011; uint8_t fire01110000100; uint8_t fire01110000101; uint8_t fire01110000110; uint8_t fire01110000111; uint8_t fire01110001000; uint8_t fire01110001001; uint8_t fire01110001010; uint8_t fire01110001011; uint8_t fire01110001100; uint8_t fire01110001101; uint8_t fire01110001110; uint8_t fire01110001111; uint8_t fire01110010000; uint8_t fire01110010001; uint8_t fire01110010010; uint8_t fire01110010011; uint8_t fire01110010100; uint8_t fire01110010101; uint8_t fire01110010110; uint8_t fire01110010111; uint8_t fire01110011000; uint8_t fire01110011001; uint8_t fire01110011010; uint8_t fire01110011011; uint8_t fire01110011100; uint8_t fire01110011101; uint8_t fire01110011110; uint8_t fire01110011111; uint8_t fire01110100000; uint8_t fire01110100001; uint8_t fire01110100010; uint8_t fire01110100011; uint8_t fire01110100100; uint8_t fire01110100101; uint8_t fire01110100110; uint8_t fire01110100111; uint8_t fire01110101000; uint8_t fire01110101001; uint8_t fire01110101010; uint8_t fire01110101011; uint8_t fire01110101100; uint8_t fire01110101101; uint8_t fire01110101110; uint8_t fire01110101111; uint8_t fire01110110000; uint8_t fire01110110001; uint8_t fire01110110010; uint8_t fire01110110011; uint8_t fire01110110100; uint8_t fire01110110101; uint8_t fire01110110110; uint8_t fire01110110111; uint8_t fire01110111000; uint8_t fire01110111001; uint8_t fire01110111010; uint8_t fire01110111011; uint8_t fire01110111100; uint8_t fire01110111101; uint8_t fire01110111110; uint8_t fire01110111111; uint8_t fire01111000000; uint8_t fire01111000001; uint8_t fire01111000010; uint8_t fire01111000011; uint8_t fire01111000100; uint8_t fire01111000101; uint8_t fire01111000110; uint8_t fire01111000111; uint8_t fire01111001000; uint8_t fire01111001001; uint8_t fire01111001010; uint8_t fire01111001011; uint8_t fire01111001100; uint8_t fire01111001101; uint8_t fire01111001110; uint8_t fire01111001111; uint8_t fire01111010000; uint8_t fire01111010001; uint8_t fire01111010010; uint8_t fire01111010011; uint8_t fire01111010100; uint8_t fire01111010101; uint8_t fire01111010110; uint8_t fire01111010111; uint8_t fire01111011000; uint8_t fire01111011001; uint8_t fire01111011010; uint8_t fire01111011011; uint8_t fire01111011100; uint8_t fire01111011101; uint8_t fire01111011110; uint8_t fire01111011111; uint8_t fire01111100000; uint8_t fire01111100001; uint8_t fire01111100010; uint8_t fire01111100011; uint8_t fire01111100100; uint8_t fire01111100101; uint8_t fire01111100110; uint8_t fire01111100111; uint8_t fire01111101000; uint8_t fire01111101001; uint8_t fire01111101010; uint8_t fire01111101011; uint8_t fire01111101100; uint8_t fire01111101101; uint8_t fire01111101110; uint8_t fire01111101111; uint8_t fire01111110000; uint8_t fire01111110001; uint8_t fire01111110010; uint8_t fire01111110011; uint8_t fire01111110100; uint8_t fire01111110101; uint8_t fire01111110110; uint8_t fire01111110111; uint8_t fire01111111000; uint8_t fire01111111001; uint8_t fire01111111010; uint8_t fire01111111011; uint8_t fire01111111100; uint8_t fire01111111101; uint8_t fire01111111110; uint8_t fire01111111111; uint8_t fire10000000000; uint8_t fire10000000001; uint8_t fire10000000010; uint8_t fire10000000011; uint8_t fire10000000100; uint8_t fire10000000101; uint8_t fire10000000110; uint8_t fire10000000111; uint8_t fire10000001000; uint8_t fire10000001001; uint8_t fire10000001010; uint8_t fire10000001011; uint8_t fire10000001100; uint8_t fire10000001101; uint8_t fire10000001110; uint8_t fire10000001111; uint8_t fire10000010000; uint8_t fire10000010001; uint8_t fire10000010010; uint8_t fire10000010011; uint8_t fire10000010100; uint8_t fire10000010101; uint8_t fire10000010110; uint8_t fire10000010111; uint8_t fire10000011000; uint8_t fire10000011001; uint8_t fire10000011010; uint8_t fire10000011011; uint8_t fire10000011100; uint8_t fire10000011101; uint8_t fire10000011110; uint8_t fire10000011111; uint8_t fire10000100000; uint8_t fire10000100001; uint8_t fire10000100010; uint8_t fire10000100011; uint8_t fire10000100100; uint8_t fire10000100101; uint8_t fire10000100110; uint8_t fire10000100111; uint8_t fire10000101000; uint8_t fire10000101001; uint8_t fire10000101010; uint8_t fire10000101011; uint8_t fire10000101100; uint8_t fire10000101101; uint8_t fire10000101110; uint8_t fire10000101111; uint8_t fire10000110000; uint8_t fire10000110001; uint8_t fire10000110010; uint8_t fire10000110011; uint8_t fire10000110100; uint8_t fire10000110101; uint8_t fire10000110110; uint8_t fire10000110111; uint8_t fire10000111000; uint8_t fire10000111001; uint8_t fire10000111010; uint8_t fire10000111011; uint8_t fire10000111100; uint8_t fire10000111101; uint8_t fire10000111110; uint8_t fire10000111111; uint8_t fire10001000000; uint8_t fire10001000001; uint8_t fire10001000010; uint8_t fire10001000011; uint8_t fire10001000100; uint8_t fire10001000101; uint8_t fire10001000110; uint8_t fire10001000111; uint8_t fire10001001000; uint8_t fire10001001001; uint8_t fire10001001010; uint8_t fire10001001011; uint8_t fire10001001100; uint8_t fire10001001101; uint8_t fire10001001110; uint8_t fire10001001111; uint8_t fire10001010000; uint8_t fire10001010001; uint8_t fire10001010010; uint8_t fire10001010011; uint8_t fire10001010100; uint8_t fire10001010101; uint8_t fire10001010110; uint8_t fire10001010111; uint8_t fire10001011000; uint8_t fire10001011001; uint8_t fire10001011010; uint8_t fire10001011011; uint8_t fire10001011100; uint8_t fire10001011101; uint8_t fire10001011110; uint8_t fire10001011111; uint8_t fire10001100000; uint8_t fire10001100001; uint8_t fire10001100010; uint8_t fire10001100011; uint8_t fire10001100100; uint8_t fire10001100101; uint8_t fire10001100110; uint8_t fire10001100111; uint8_t fire10001101000; uint8_t fire10001101001; uint8_t fire10001101010; uint8_t fire10001101011; uint8_t fire10001101100; uint8_t fire10001101101; uint8_t fire10001101110; uint8_t fire10001101111; uint8_t fire10001110000; uint8_t fire10001110001; uint8_t fire10001110010; uint8_t fire10001110011; uint8_t fire10001110100; uint8_t fire10001110101; uint8_t fire10001110110; uint8_t fire10001110111; uint8_t fire10001111000; uint8_t fire10001111001; uint8_t fire10001111010; uint8_t fire10001111011; uint8_t fire10001111100; uint8_t fire10001111101; uint8_t fire10001111110; uint8_t fire10001111111; uint8_t fire10010000000; uint8_t fire10010000001; uint8_t fire10010000010; uint8_t fire10010000011; uint8_t fire10010000100; uint8_t fire10010000101; uint8_t fire10010000110; uint8_t fire10010000111; uint8_t fire10010001000; uint8_t fire10010001001; uint8_t fire10010001010; uint8_t fire10010001011; uint8_t fire10010001100; uint8_t fire10010001101; uint8_t fire10010001110; uint8_t fire10010001111; uint8_t fire10010010000; uint8_t fire10010010001; uint8_t fire10010010010; uint8_t fire10010010011; uint8_t fire10010010100; uint8_t fire10010010101; uint8_t fire10010010110; uint8_t fire10010010111; uint8_t fire10010011000; uint8_t fire10010011001; uint8_t fire10010011010; uint8_t fire10010011011; uint8_t fire10010011100; uint8_t fire10010011101; uint8_t fire10010011110; uint8_t fire10010011111; uint8_t fire10010100000; uint8_t fire10010100001; uint8_t fire10010100010; uint8_t fire10010100011; uint8_t fire10010100100; uint8_t fire10010100101; uint8_t fire10010100110; uint8_t fire10010100111; uint8_t fire10010101000; uint8_t fire10010101001; uint8_t fire10010101010; uint8_t fire10010101011; uint8_t fire10010101100; uint8_t fire10010101101; uint8_t fire10010101110; uint8_t fire10010101111; uint8_t fire10010110000; uint8_t fire10010110001; uint8_t fire10010110010; uint8_t fire10010110011; uint8_t fire10010110100; uint8_t fire10010110101; uint8_t fire10010110110; uint8_t fire10010110111; uint8_t fire10010111000; uint8_t fire10010111001; uint8_t fire10010111010; uint8_t fire10010111011; uint8_t fire10010111100; uint8_t fire10010111101; uint8_t fire10010111110; uint8_t fire10010111111; uint8_t fire10011000000; uint8_t fire10011000001; uint8_t fire10011000010; uint8_t fire10011000011; uint8_t fire10011000100; uint8_t fire10011000101; uint8_t fire10011000110; uint8_t fire10011000111; uint8_t fire10011001000; uint8_t fire10011001001; uint8_t fire10011001010; uint8_t fire10011001011; uint8_t fire10011001100; uint8_t fire10011001101; uint8_t fire10011001110; uint8_t fire10011001111; uint8_t fire10011010000; uint8_t fire10011010001; uint8_t fire10011010010; uint8_t fire10011010011; uint8_t fire10011010100; uint8_t fire10011010101; uint8_t fire10011010110; uint8_t fire10011010111; uint8_t fire10011011000; uint8_t fire10011011001; uint8_t fire10011011010; uint8_t fire10011011011; uint8_t fire10011011100; uint8_t fire10011011101; uint8_t fire10011011110; uint8_t fire10011011111; uint8_t fire10011100000; uint8_t fire10011100001; uint8_t fire10011100010; uint8_t fire10011100011; uint8_t fire10011100100; uint8_t fire10011100101; uint8_t fire10011100110; uint8_t fire10011100111; uint8_t fire10011101000; uint8_t fire10011101001; uint8_t fire10011101010; uint8_t fire10011101011; uint8_t fire10011101100; uint8_t fire10011101101; uint8_t fire10011101110; uint8_t fire10011101111; uint8_t fire10011110000; uint8_t fire10011110001; uint8_t fire10011110010; uint8_t fire10011110011; uint8_t fire10011110100; uint8_t fire10011110101; uint8_t fire10011110110; uint8_t fire10011110111; uint8_t fire10011111000; uint8_t fire10011111001; uint8_t fire10011111010; uint8_t fire10011111011; uint8_t fire10011111100; uint8_t fire10011111101; uint8_t fire10011111110; uint8_t fire10011111111; uint8_t fire10100000000; uint8_t fire10100000001; uint8_t fire10100000010; uint8_t fire10100000011; uint8_t fire10100000100; uint8_t fire10100000101; uint8_t fire10100000110; uint8_t fire10100000111; uint8_t fire10100001000; uint8_t fire10100001001; uint8_t fire10100001010; uint8_t fire10100001011; uint8_t fire10100001100; uint8_t fire10100001101; uint8_t fire10100001110; uint8_t fire10100001111; uint8_t fire10100010000; uint8_t fire10100010001; uint8_t fire10100010010; uint8_t fire10100010011; uint8_t fire10100010100; uint8_t fire10100010101; uint8_t fire10100010110; uint8_t fire10100010111; uint8_t fire10100011000; uint8_t fire10100011001; uint8_t fire10100011010; uint8_t fire10100011011; uint8_t fire10100011100; uint8_t fire10100011101; uint8_t fire10100011110; uint8_t fire10100011111; uint8_t fire10100100000; uint8_t fire10100100001; uint8_t fire10100100010; uint8_t fire10100100011; uint8_t fire10100100100; uint8_t fire10100100101; uint8_t fire10100100110; uint8_t fire10100100111; uint8_t fire10100101000; uint8_t fire10100101001; uint8_t fire10100101010; uint8_t fire10100101011; uint8_t fire10100101100; uint8_t fire10100101101; uint8_t fire10100101110; uint8_t fire10100101111; uint8_t fire10100110000; uint8_t fire10100110001; uint8_t fire10100110010; uint8_t fire10100110011; uint8_t fire10100110100; uint8_t fire10100110101; uint8_t fire10100110110; uint8_t fire10100110111; uint8_t fire10100111000; uint8_t fire10100111001; uint8_t fire10100111010; uint8_t fire10100111011; uint8_t fire10100111100; uint8_t fire10100111101; uint8_t fire10100111110; uint8_t fire10100111111; uint8_t fire10101000000; uint8_t fire10101000001; uint8_t fire10101000010; uint8_t fire10101000011; uint8_t fire10101000100; uint8_t fire10101000101; uint8_t fire10101000110; uint8_t fire10101000111; uint8_t fire10101001000; uint8_t fire10101001001; uint8_t fire10101001010; uint8_t fire10101001011; uint8_t fire10101001100; uint8_t fire10101001101; uint8_t fire10101001110; uint8_t fire10101001111; uint8_t fire10101010000; uint8_t fire10101010001; uint8_t fire10101010010; uint8_t fire10101010011; uint8_t fire10101010100; uint8_t fire10101010101; uint8_t fire10101010110; uint8_t fire10101010111; uint8_t fire10101011000; uint8_t fire10101011001; uint8_t fire10101011010; uint8_t fire10101011011; uint8_t fire10101011100; uint8_t fire10101011101; uint8_t fire10101011110; uint8_t fire10101011111; uint8_t fire10101100000; uint8_t fire10101100001; uint8_t fire10101100010; uint8_t fire10101100011; uint8_t fire10101100100; uint8_t fire10101100101; uint8_t fire10101100110; uint8_t fire10101100111; uint8_t fire10101101000; uint8_t fire10101101001; uint8_t fire10101101010; uint8_t fire10101101011; uint8_t fire10101101100; uint8_t fire10101101101; uint8_t fire10101101110; uint8_t fire10101101111; uint8_t fire10101110000; uint8_t fire10101110001; uint8_t fire10101110010; uint8_t fire10101110011; uint8_t fire10101110100; uint8_t fire10101110101; uint8_t fire10101110110; uint8_t fire10101110111; uint8_t fire10101111000; uint8_t fire10101111001; uint8_t fire10101111010; uint8_t fire10101111011; uint8_t fire10101111100; uint8_t fire10101111101; uint8_t fire10101111110; uint8_t fire10101111111; uint8_t fire10110000000; uint8_t fire10110000001; uint8_t fire10110000010; uint8_t fire10110000011; uint8_t fire10110000100; uint8_t fire10110000101; uint8_t fire10110000110; uint8_t fire10110000111; uint8_t fire10110001000; uint8_t fire10110001001; uint8_t fire10110001010; uint8_t fire10110001011; uint8_t fire10110001100; uint8_t fire10110001101; uint8_t fire10110001110; uint8_t fire10110001111; uint8_t fire10110010000; uint8_t fire10110010001; uint8_t fire10110010010; uint8_t fire10110010011; uint8_t fire10110010100; uint8_t fire10110010101; uint8_t fire10110010110; uint8_t fire10110010111; uint8_t fire10110011000; uint8_t fire10110011001; uint8_t fire10110011010; uint8_t fire10110011011; uint8_t fire10110011100; uint8_t fire10110011101; uint8_t fire10110011110; uint8_t fire10110011111; uint8_t fire10110100000; uint8_t fire10110100001; uint8_t fire10110100010; uint8_t fire10110100011; uint8_t fire10110100100; uint8_t fire10110100101; uint8_t fire10110100110; uint8_t fire10110100111; uint8_t fire10110101000; uint8_t fire10110101001; uint8_t fire10110101010; uint8_t fire10110101011; uint8_t fire10110101100; uint8_t fire10110101101; uint8_t fire10110101110; uint8_t fire10110101111; uint8_t fire10110110000; uint8_t fire10110110001; uint8_t fire10110110010; uint8_t fire10110110011; uint8_t fire10110110100; uint8_t fire10110110101; uint8_t fire10110110110; uint8_t fire10110110111; uint8_t fire10110111000; uint8_t fire10110111001; uint8_t fire10110111010; uint8_t fire10110111011; uint8_t fire10110111100; uint8_t fire10110111101; uint8_t fire10110111110; uint8_t fire10110111111; uint8_t fire10111000000; uint8_t fire10111000001; uint8_t fire10111000010; uint8_t fire10111000011; uint8_t fire10111000100; uint8_t fire10111000101; uint8_t fire10111000110; uint8_t fire10111000111; uint8_t fire10111001000; uint8_t fire10111001001; uint8_t fire10111001010; uint8_t fire10111001011; uint8_t fire10111001100; uint8_t fire10111001101; uint8_t fire10111001110; uint8_t fire10111001111; uint8_t fire10111010000; uint8_t fire10111010001; uint8_t fire10111010010; uint8_t fire10111010011; uint8_t fire10111010100; uint8_t fire10111010101; uint8_t fire10111010110; uint8_t fire10111010111; uint8_t fire10111011000; uint8_t fire10111011001; uint8_t fire10111011010; uint8_t fire10111011011; uint8_t fire10111011100; uint8_t fire10111011101; uint8_t fire10111011110; uint8_t fire10111011111; uint8_t fire10111100000; uint8_t fire10111100001; uint8_t fire10111100010; uint8_t fire10111100011; uint8_t fire10111100100; uint8_t fire10111100101; uint8_t fire10111100110; uint8_t fire10111100111; uint8_t fire10111101000; uint8_t fire10111101001; uint8_t fire10111101010; uint8_t fire10111101011; uint8_t fire10111101100; uint8_t fire10111101101; uint8_t fire10111101110; uint8_t fire10111101111; uint8_t fire10111110000; uint8_t fire10111110001; uint8_t fire10111110010; uint8_t fire10111110011; uint8_t fire10111110100; uint8_t fire10111110101; uint8_t fire10111110110; uint8_t fire10111110111; uint8_t fire10111111000; uint8_t fire10111111001; uint8_t fire10111111010; uint8_t fire10111111011; uint8_t fire10111111100; uint8_t fire10111111101; uint8_t fire10111111110; uint8_t fire10111111111; uint8_t fire11000000000; uint8_t fire11000000001; uint8_t fire11000000010; uint8_t fire11000000011; uint8_t fire11000000100; uint8_t fire11000000101; uint8_t fire11000000110; uint8_t fire11000000111; uint8_t fire11000001000; uint8_t fire11000001001; uint8_t fire11000001010; uint8_t fire11000001011; uint8_t fire11000001100; uint8_t fire11000001101; uint8_t fire11000001110; uint8_t fire11000001111; uint8_t fire11000010000; uint8_t fire11000010001; uint8_t fire11000010010; uint8_t fire11000010011; uint8_t fire11000010100; uint8_t fire11000010101; uint8_t fire11000010110; uint8_t fire11000010111; uint8_t fire11000011000; uint8_t fire11000011001; uint8_t fire11000011010; uint8_t fire11000011011; uint8_t fire11000011100; uint8_t fire11000011101; uint8_t fire11000011110; uint8_t fire11000011111; uint8_t fire11000100000; uint8_t fire11000100001; uint8_t fire11000100010; uint8_t fire11000100011; uint8_t fire11000100100; uint8_t fire11000100101; uint8_t fire11000100110; uint8_t fire11000100111; uint8_t fire11000101000; uint8_t fire11000101001; uint8_t fire11000101010; uint8_t fire11000101011; uint8_t fire11000101100; uint8_t fire11000101101; uint8_t fire11000101110; uint8_t fire11000101111; uint8_t fire11000110000; uint8_t fire11000110001; uint8_t fire11000110010; uint8_t fire11000110011; uint8_t fire11000110100; uint8_t fire11000110101; uint8_t fire11000110110; uint8_t fire11000110111; uint8_t fire11000111000; uint8_t fire11000111001; uint8_t fire11000111010; uint8_t fire11000111011; uint8_t fire11000111100; uint8_t fire11000111101; uint8_t fire11000111110; uint8_t fire11000111111; uint8_t fire11001000000; uint8_t fire11001000001; uint8_t fire11001000010; uint8_t fire11001000011; uint8_t fire11001000100; uint8_t fire11001000101; uint8_t fire11001000110; uint8_t fire11001000111; uint8_t fire11001001000; uint8_t fire11001001001; uint8_t fire11001001010; uint8_t fire11001001011; uint8_t fire11001001100; uint8_t fire11001001101; uint8_t fire11001001110; uint8_t fire11001001111; uint8_t fire11001010000; uint8_t fire11001010001; uint8_t fire11001010010; uint8_t fire11001010011; uint8_t fire11001010100; uint8_t fire11001010101; uint8_t fire11001010110; uint8_t fire11001010111; uint8_t fire11001011000; uint8_t fire11001011001; uint8_t fire11001011010; uint8_t fire11001011011; uint8_t fire11001011100; uint8_t fire11001011101; uint8_t fire11001011110; uint8_t fire11001011111; uint8_t fire11001100000; uint8_t fire11001100001; uint8_t fire11001100010; uint8_t fire11001100011; uint8_t fire11001100100; uint8_t fire11001100101; uint8_t fire11001100110; uint8_t fire11001100111; uint8_t fire11001101000; uint8_t fire11001101001; uint8_t fire11001101010; uint8_t fire11001101011; uint8_t fire11001101100; uint8_t fire11001101101; uint8_t fire11001101110; uint8_t fire11001101111; uint8_t fire11001110000; uint8_t fire11001110001; uint8_t fire11001110010; uint8_t fire11001110011; uint8_t fire11001110100; uint8_t fire11001110101; uint8_t fire11001110110; uint8_t fire11001110111; uint8_t fire11001111000; uint8_t fire11001111001; uint8_t fire11001111010; uint8_t fire11001111011; uint8_t fire11001111100; uint8_t fire11001111101; uint8_t fire11001111110; uint8_t fire11001111111; uint8_t fire11010000000; uint8_t fire11010000001; uint8_t fire11010000010; uint8_t fire11010000011; uint8_t fire11010000100; uint8_t fire11010000101; uint8_t fire11010000110; uint8_t fire11010000111; uint8_t fire11010001000; uint8_t fire11010001001; uint8_t fire11010001010; uint8_t fire11010001011; uint8_t fire11010001100; uint8_t fire11010001101; uint8_t fire11010001110; uint8_t fire11010001111; uint8_t fire11010010000; uint8_t fire11010010001; uint8_t fire11010010010; uint8_t fire11010010011; uint8_t fire11010010100; uint8_t fire11010010101; uint8_t fire11010010110; uint8_t fire11010010111; uint8_t fire11010011000; uint8_t fire11010011001; uint8_t fire11010011010; uint8_t fire11010011011; uint8_t fire11010011100; uint8_t fire11010011101; uint8_t fire11010011110; uint8_t fire11010011111; uint8_t fire11010100000; uint8_t fire11010100001; uint8_t fire11010100010; uint8_t fire11010100011; uint8_t fire11010100100; uint8_t fire11010100101; uint8_t fire11010100110; uint8_t fire11010100111; uint8_t fire11010101000; uint8_t fire11010101001; uint8_t fire11010101010; uint8_t fire11010101011; uint8_t fire11010101100; uint8_t fire11010101101; uint8_t fire11010101110; uint8_t fire11010101111; uint8_t fire11010110000; uint8_t fire11010110001; uint8_t fire11010110010; uint8_t fire11010110011; uint8_t fire11010110100; uint8_t fire11010110101; uint8_t fire11010110110; uint8_t fire11010110111; uint8_t fire11010111000; uint8_t fire11010111001; uint8_t fire11010111010; uint8_t fire11010111011; uint8_t fire11010111100; uint8_t fire11010111101; uint8_t fire11010111110; uint8_t fire11010111111; uint8_t fire11011000000; uint8_t fire11011000001; uint8_t fire11011000010; uint8_t fire11011000011; uint8_t fire11011000100; uint8_t fire11011000101; uint8_t fire11011000110; uint8_t fire11011000111; uint8_t fire11011001000; uint8_t fire11011001001; uint8_t fire11011001010; uint8_t fire11011001011; uint8_t fire11011001100; uint8_t fire11011001101; uint8_t fire11011001110; uint8_t fire11011001111; uint8_t fire11011010000; uint8_t fire11011010001; uint8_t fire11011010010; uint8_t fire11011010011; uint8_t fire11011010100; uint8_t fire11011010101; uint8_t fire11011010110; uint8_t fire11011010111; uint8_t fire11011011000; uint8_t fire11011011001; uint8_t fire11011011010; uint8_t fire11011011011; uint8_t fire11011011100; uint8_t fire11011011101; uint8_t fire11011011110; uint8_t fire11011011111; uint8_t fire11011100000; uint8_t fire11011100001; uint8_t fire11011100010; uint8_t fire11011100011; uint8_t fire11011100100; uint8_t fire11011100101; uint8_t fire11011100110; uint8_t fire11011100111; uint8_t fire11011101000; uint8_t fire11011101001; uint8_t fire11011101010; uint8_t fire11011101011; uint8_t fire11011101100; uint8_t fire11011101101; uint8_t fire11011101110; uint8_t fire11011101111; uint8_t fire11011110000; uint8_t fire11011110001; uint8_t fire11011110010; uint8_t fire11011110011; uint8_t fire11011110100; uint8_t fire11011110101; uint8_t fire11011110110; uint8_t fire11011110111; uint8_t fire11011111000; uint8_t fire11011111001; uint8_t fire11011111010; uint8_t fire11011111011; uint8_t fire11011111100; uint8_t fire11011111101; uint8_t fire11011111110; uint8_t fire11011111111; uint8_t fire11100000000; uint8_t fire11100000001; uint8_t fire11100000010; uint8_t fire11100000011; uint8_t fire11100000100; uint8_t fire11100000101; uint8_t fire11100000110; uint8_t fire11100000111; uint8_t fire11100001000; uint8_t fire11100001001; uint8_t fire11100001010; uint8_t fire11100001011; uint8_t fire11100001100; uint8_t fire11100001101; uint8_t fire11100001110; uint8_t fire11100001111; uint8_t fire11100010000; uint8_t fire11100010001; uint8_t fire11100010010; uint8_t fire11100010011; uint8_t fire11100010100; uint8_t fire11100010101; uint8_t fire11100010110; uint8_t fire11100010111; uint8_t fire11100011000; uint8_t fire11100011001; uint8_t fire11100011010; uint8_t fire11100011011; uint8_t fire11100011100; uint8_t fire11100011101; uint8_t fire11100011110; uint8_t fire11100011111; uint8_t fire11100100000; uint8_t fire11100100001; uint8_t fire11100100010; uint8_t fire11100100011; uint8_t fire11100100100; uint8_t fire11100100101; uint8_t fire11100100110; uint8_t fire11100100111; uint8_t fire11100101000; uint8_t fire11100101001; uint8_t fire11100101010; uint8_t fire11100101011; uint8_t fire11100101100; uint8_t fire11100101101; uint8_t fire11100101110; uint8_t fire11100101111; uint8_t fire11100110000; uint8_t fire11100110001; uint8_t fire11100110010; uint8_t fire11100110011; uint8_t fire11100110100; uint8_t fire11100110101; uint8_t fire11100110110; uint8_t fire11100110111; uint8_t fire11100111000; uint8_t fire11100111001; uint8_t fire11100111010; uint8_t fire11100111011; uint8_t fire11100111100; uint8_t fire11100111101; uint8_t fire11100111110; uint8_t fire11100111111; uint8_t fire11101000000; uint8_t fire11101000001; uint8_t fire11101000010; uint8_t fire11101000011; uint8_t fire11101000100; uint8_t fire11101000101; uint8_t fire11101000110; uint8_t fire11101000111; uint8_t fire11101001000; uint8_t fire11101001001; uint8_t fire11101001010; uint8_t fire11101001011; uint8_t fire11101001100; uint8_t fire11101001101; uint8_t fire11101001110; uint8_t fire11101001111; uint8_t fire11101010000; uint8_t fire11101010001; uint8_t fire11101010010; uint8_t fire11101010011; uint8_t fire11101010100; uint8_t fire11101010101; uint8_t fire11101010110; uint8_t fire11101010111; uint8_t fire11101011000; uint8_t fire11101011001; uint8_t fire11101011010; uint8_t fire11101011011; uint8_t fire11101011100; uint8_t fire11101011101; uint8_t fire11101011110; uint8_t fire11101011111; uint8_t fire11101100000; uint8_t fire11101100001; uint8_t fire11101100010; uint8_t fire11101100011; uint8_t fire11101100100; uint8_t fire11101100101; uint8_t fire11101100110; uint8_t fire11101100111; uint8_t fire11101101000; uint8_t fire11101101001; uint8_t fire11101101010; uint8_t fire11101101011; uint8_t fire11101101100; uint8_t fire11101101101; uint8_t fire11101101110; uint8_t fire11101101111; uint8_t fire11101110000; uint8_t fire11101110001; uint8_t fire11101110010; uint8_t fire11101110011; uint8_t fire11101110100; uint8_t fire11101110101; uint8_t fire11101110110; uint8_t fire11101110111; uint8_t fire11101111000; uint8_t fire11101111001; uint8_t fire11101111010; uint8_t fire11101111011; uint8_t fire11101111100; uint8_t fire11101111101; uint8_t fire11101111110; uint8_t fire11101111111; uint8_t fire11110000000; uint8_t fire11110000001; uint8_t fire11110000010; uint8_t fire11110000011; uint8_t fire11110000100; uint8_t fire11110000101; uint8_t fire11110000110; uint8_t fire11110000111; uint8_t fire11110001000; uint8_t fire11110001001; uint8_t fire11110001010; uint8_t fire11110001011; uint8_t fire11110001100; uint8_t fire11110001101; uint8_t fire11110001110; uint8_t fire11110001111; uint8_t fire11110010000; uint8_t fire11110010001; uint8_t fire11110010010; uint8_t fire11110010011; uint8_t fire11110010100; uint8_t fire11110010101; uint8_t fire11110010110; uint8_t fire11110010111; uint8_t fire11110011000; uint8_t fire11110011001; uint8_t fire11110011010; uint8_t fire11110011011; uint8_t fire11110011100; uint8_t fire11110011101; uint8_t fire11110011110; uint8_t fire11110011111; uint8_t fire11110100000; uint8_t fire11110100001; uint8_t fire11110100010; uint8_t fire11110100011; uint8_t fire11110100100; uint8_t fire11110100101; uint8_t fire11110100110; uint8_t fire11110100111; uint8_t fire11110101000; uint8_t fire11110101001; uint8_t fire11110101010; uint8_t fire11110101011; uint8_t fire11110101100; uint8_t fire11110101101; uint8_t fire11110101110; uint8_t fire11110101111; uint8_t fire11110110000; uint8_t fire11110110001; uint8_t fire11110110010; uint8_t fire11110110011; uint8_t fire11110110100; uint8_t fire11110110101; uint8_t fire11110110110; uint8_t fire11110110111; uint8_t fire11110111000; uint8_t fire11110111001; uint8_t fire11110111010; uint8_t fire11110111011; uint8_t fire11110111100; uint8_t fire11110111101; uint8_t fire11110111110; uint8_t fire11110111111; uint8_t fire11111000000; uint8_t fire11111000001; uint8_t fire11111000010; uint8_t fire11111000011; uint8_t fire11111000100; uint8_t fire11111000101; uint8_t fire11111000110; uint8_t fire11111000111; uint8_t fire11111001000; uint8_t fire11111001001; uint8_t fire11111001010; uint8_t fire11111001011; uint8_t fire11111001100; uint8_t fire11111001101; uint8_t fire11111001110; uint8_t fire11111001111; uint8_t fire11111010000; uint8_t fire11111010001; uint8_t fire11111010010; uint8_t fire11111010011; uint8_t fire11111010100; uint8_t fire11111010101; uint8_t fire11111010110; uint8_t fire11111010111; uint8_t fire11111011000; uint8_t fire11111011001; uint8_t fire11111011010; uint8_t fire11111011011; uint8_t fire11111011100; uint8_t fire11111011101; uint8_t fire11111011110; uint8_t fire11111011111; uint8_t fire11111100000; uint8_t fire11111100001; uint8_t fire11111100010; uint8_t fire11111100011; uint8_t fire11111100100; uint8_t fire11111100101; uint8_t fire11111100110; uint8_t fire11111100111; uint8_t fire11111101000; uint8_t fire11111101001; uint8_t fire11111101010; uint8_t fire11111101011; uint8_t fire11111101100; uint8_t fire11111101101; uint8_t fire11111101110; uint8_t fire11111101111; uint8_t fire11111110000; uint8_t fire11111110001; uint8_t fire11111110010; uint8_t fire11111110011; uint8_t fire11111110100; uint8_t fire11111110101; uint8_t fire11111110110; uint8_t fire11111110111; uint8_t fire11111111000; uint8_t fire11111111001; uint8_t fire11111111010; uint8_t fire11111111011; uint8_t fire11111111100; uint8_t fire11111111101; uint8_t fire11111111110; uint8_t fire11111111111;
|
Is the C preprocessor Turing complete?
The term "Turing complete" most often means that any real general-purpose computer or computer language can approximately simulate the computational aspects of any other real general-purpose computer or computer language.
No real system can have infinite memory, but if we neglect the memory limitation, most programming languages are otherwise Turing complete.
Not only the preprocessor memory is limited, but also the number of token rescanning levels (which we set with EVAL
).
However, it's just a form of memory limitation, so the preprocessor is quite Turing complete.
After I wrote this article, I have found another link to a project,
where devs have created a full-blown programming metalanguage on #define
macros.
Unfortunately, it's too complex for the introduction to the b1ack magic of the C preprocessor.
Above tricks might still go into prod, but metalang99's tricks - I doubt it :)
Bonus level
Modern optimizing compilers may be the most complex and impressive creation of humanity in the field of software engineering.
However, they are not magic.
An ordinary human brain will tell in a few seconds what the below very trivial code should be compiled into, but GCC will need many exabytes of memory and many years to give the answer.
GCC memory bomb
#define X1(x) X2(x)+X2(x)
#define X2(x) X3(x)+X3(x)
#define X3(x) X4(x)+X4(x)
#define X4(x) X5(x)+X5(x)
#define X5(x) X6(x)+X6(x)
#define X6(x) X7(x)+X7(x)
#define X7(x) X8(x)+X8(x)
#define X8(x) X9(x)+X9(x)
#define X9(x) X10(x)+X10(x)
#define X10(x) X11(x)+X11(x)
#define X11(x) X12(x)+X12(x)
#define X12(x) X13(x)+X13(x)
#define X13(x) X14(x)+X14(x)
#define X14(x) X15(x)+X15(x)
#define X15(x) X16(x)+X16(x)
#define X16(x) X17(x)+X17(x)
#define X17(x) X18(x)+X18(x)
#define X18(x) X19(x)+X19(x)
#define X19(x) X20(x)+X20(x)
#define X20(x) X21(x)+X21(x)
#define X21(x) X22(x)+X22(x)
#define X22(x) X23(x)+X23(x)
#define X23(x) X24(x)+X24(x)
#define X24(x) X25(x)+X25(x)
#define X25(x) X26(x)+X26(x)
#define X26(x) X27(x)+X27(x)
#define X27(x) X28(x)+X28(x)
#define X28(x) X29(x)+X29(x)
#define X29(x) X30(x)+X30(x)
#define X30(x) X31(x)+X31(x)
#define X31(x) X32(x)+X32(x)
#define X32(x) X33(x)+X33(x)
#define X33(x) X34(x)+X34(x)
#define X34(x) X35(x)+X35(x)
#define X35(x) X36(x)+X36(x)
#define X36(x) X37(x)+X37(x)
#define X37(x) X38(x)+X38(x)
#define X38(x) X39(x)+X39(x)
#define X39(x) X40(x)+X40(x)
#define X40(x) X41(x)+X41(x)
#define X41(x) X42(x)+X42(x)
#define X42(x) X43(x)+X43(x)
#define X43(x) X44(x)+X44(x)
#define X44(x) X45(x)+X45(x)
#define X45(x) X46(x)+X46(x)
#define X46(x) X47(x)+X47(x)
#define X47(x) X48(x)+X48(x)
#define X48(x) X49(x)+X49(x)
#define X49(x) X50(x)+X50(x)
#define X50(x) X51(x)+X51(x)
#define X51(x) X52(x)+X52(x)
#define X52(x) X53(x)+X53(x)
#define X53(x) X54(x)+X54(x)
#define X54(x) X55(x)+X55(x)
#define X55(x) X56(x)+X56(x)
#define X56(x) X57(x)+X57(x)
#define X57(x) X58(x)+X58(x)
#define X58(x) X59(x)+X59(x)
#define X59(x) X60(x)+X60(x)
#define X60(x) X61(x)+X61(x)
#define X61(x) X62(x)+X62(x)
#define X62(x) X63(x)+X63(x)
#define X63(x) X64(x)+X64(x)
#define X64(x) x+x
int main() {
return X1(0);
}
Comments