MAKE THE MATHEMATICS YOURSELF

Collatz branches in C

A complete program. Standard C, a compiler, and the rule behind the shape.

Output generated by this C program: Collatz branches
Generated by the C program below Download image ↗
FROM SOURCE TO SHAPE

Run it in three steps.

  1. Save the source.

    Download collatz.c into a folder on your computer.

  2. Compile it.

    In that folder, run this with GCC or Clang:

    cc -std=c11 -O2 collatz.c -lm -o collatz
  3. Make the image.
    ./collatz

    Open collatz.svg in a browser to see the result.

On Windows with GCC, name the executable collatz.exe and run it from the same folder.

THE RULE IN THE PROGRAM

How the picture is built

For starts 2 through STARTS, repeatedly halve an even value or replace an odd value by 3n+1. Reverse each completed trajectory from 1, and turn by ±TURN according to the next value’s parity. Overlay the fitted unit-step paths.

Make it your own

STARTS=300; CAP=2000 values per trajectory; TURN=11 degrees. Try turn angles from 7 to 17 degrees.

Only trajectories reaching 1 within CAP are drawn. The program checks unsigned 64-bit overflow before 3n+1. All default starts complete, but this finite visualization does not establish the unsolved universal Collatz conjecture. Each run produces one image; use Graphic mode for the interactive animation.

The complete source

collatz.c · 122 lines · no graphics libraries
/* Arithmos: collatz
 * Compile: cc -std=c11 -O2 collatz.c -lm -o collatz
 * Run:     ./collatz
 * Output:  collatz.svg (open this file in a browser)
 * Optional output path: ./collatz my-image.svg
 * Edit the constants in draw() to explore another case.
 */
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static FILE *nt_out;
#define NT_PI 3.14159265358979323846

/* The small SVG writer keeps this program free of graphics dependencies.
 * Coordinates are pixels on a 1000 x 700 drawing surface.
 * t runs from 0 to 1 through mint, blue, rose, and gold.
 */
static inline void nt_color(double t, char hex[8]) {
    const double stops[4][3] = {
        {91,227,201}, {128,146,240}, {218,138,220}, {244,200,127}
    };
    t = fmax(0.0, fmin(1.0,t)) * 3.0;
    int band = (int)fmin(2.0,floor(t));
    double blend = t - band;
    int r[3];
    for (int k=0;k<3;k++) r[k]=(int)lround(stops[band][k]*(1.0-blend)+stops[band+1][k]*blend);
    snprintf(hex,8,"#%02x%02x%02x",r[0],r[1],r[2]);
}
static inline void nt_line(double x,double y,double X,double Y,double t,double alpha,double width) {
    char color[8];nt_color(t,color);
    fprintf(nt_out,"<path d=\"M%.3f %.3f L%.3f %.3f\" fill=\"none\" stroke=\"%s\" stroke-opacity=\"%.3f\" stroke-width=\"%.3f\"/>\n",x,y,X,Y,color,alpha,width);
}
static inline void nt_dot(double x,double y,double r,double t,double alpha) {
    char color[8];nt_color(t,color);
    fprintf(nt_out,"<circle cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\" fill=\"%s\" fill-opacity=\"%.3f\"/>\n",x,y,r,color,alpha);
}
static inline void nt_circle(double x,double y,double r,double t,double alpha,double width) {
    char color[8];nt_color(t,color);
    fprintf(nt_out,"<circle cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\" fill=\"none\" stroke=\"%s\" stroke-opacity=\"%.3f\" stroke-width=\"%.3f\"/>\n",x,y,r,color,alpha,width);
}
static inline void nt_rect(double x,double y,double w,double h,double t,double alpha) {
    char color[8];nt_color(t,color);
    fprintf(nt_out,"<rect x=\"%.3f\" y=\"%.3f\" width=\"%.3f\" height=\"%.3f\" fill=\"%s\" fill-opacity=\"%.3f\"/>\n",x,y,w,h,color,alpha);
}
static inline void nt_text(double x,double y,const char *text) {
    fprintf(nt_out,"<text x=\"%.3f\" y=\"%.3f\" fill=\"#ededf3\" font-family=\"monospace\" font-size=\"16\">",x,y);
    for (;*text;text++) {
        if (*text=='&') fputs("&amp;",nt_out);
        else if (*text=='<') fputs("&lt;",nt_out);
        else if (*text=='>') fputs("&gt;",nt_out);
        else fputc(*text,nt_out);
    }
    fputs("</text>\n",nt_out);
}
static inline int nt_gcd(int a,int b) {a=abs(a);b=abs(b);while(b){int r=a%b;a=b;b=r;}return a;}
static inline int nt_prime(int n) {if(n<2)return 0;for(int d=2;d<=n/d;d++)if(n%d==0)return 0;return 1;}

/* Only trajectories reaching 1 within CAP are rendered. Unsigned overflow is
   checked before 3n+1. Finite examples do not prove the Collatz conjecture. */
static int nt_collatz_chain(uint64_t start,uint64_t *chain,int cap) {
    int n=0;uint64_t value=start;
    while(n<cap) {
        chain[n++]=value;if(value==1)return n;
        if(value&1U) {
            if(value>(UINT64_MAX-1)/3)return 0;
            value=3*value+1;
        } else value/=2;
    }
    return 0;
}
static void draw(void) {
    enum { STARTS=300, CAP=2000 };
    const double TURN=11.0*NT_PI/180.0; /* Editable turn angle, in degrees. */
    uint64_t chain[CAP];
    double xmin=0,xmax=0,ymin=0,ymax=0,scale=1,mx=0,my=0;
    /* First pass fits every path; second pass draws the same exact rules. */
    for(int pass=0;pass<2;++pass) {
        if(pass){scale=fmin(900/fmax(1,xmax-xmin),580/fmax(1,ymax-ymin));
            mx=.5*(xmin+xmax);my=.5*(ymin+ymax);}
        for(int start=2;start<=STARTS;++start) {
            int count=nt_collatz_chain((uint64_t)start,chain,CAP);
            if(!count)continue;
            double x=0,y=0,heading=-NT_PI/2;
            char color[8];nt_color(start/(double)STARTS,color);
            if(pass)fprintf(nt_out,"<path d=\"M%.3f %.3f",500-mx*scale,340-my*scale);
            /* Reverse the trajectory from 1; predecessor parity bends it. */
            for(int j=count-2;j>=0;--j) {
                heading+=(chain[j]&1U)?-TURN:TURN;
                x+=cos(heading);y+=sin(heading);
                if(!pass) {
                    if(x<xmin)xmin=x;if(x>xmax)xmax=x;
                    if(y<ymin)ymin=y;if(y>ymax)ymax=y;
                } else fprintf(nt_out," L%.3f %.3f",500+(x-mx)*scale,340+(y-my)*scale);
            }
            if(pass)fprintf(nt_out,"\" fill=\"none\" stroke=\"%s\" stroke-opacity=\"0.18\" stroke-width=\"1.15\"/>\n",color);
        }
    }
    nt_text(50,675,"Collatz: reverse finite trajectories; parity supplies each bend");
}

int main(int argc, char **argv) {
    if (argc > 2) {
        fprintf(stderr, "Usage: %s [OUTPUT.svg]\n", argv[0]);
        return EXIT_FAILURE;
    }
    const char *filename = argc == 2 ? argv[1] : "collatz.svg";
    nt_out = fopen(filename, "wb");
    if (!nt_out) { perror(filename); return EXIT_FAILURE; }
    fputs("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1000\" height=\"700\" viewBox=\"0 0 1000 700\">\n"
          "<rect width=\"1000\" height=\"700\" fill=\"#171721\"/>\n", nt_out);
    draw();
    fputs("</svg>\n", nt_out);
    int failed = ferror(nt_out);
    if (fclose(nt_out) != 0) failed = 1;
    if (failed) { fputs("Could not finish writing the image.\n", stderr); return EXIT_FAILURE; }
    printf("Wrote %s\n", filename);
    return EXIT_SUCCESS;
}
All 40 explorations, ready to compile.Download all C examples ↓