/* 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 #include #include #include #include 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,"\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,"\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,"\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,"\n",x,y,w,h,color,alpha); } static inline void nt_text(double x,double y,const char *text) { fprintf(nt_out,"",x,y); for (;*text;text++) { if (*text=='&') fputs("&",nt_out); else if (*text=='<') fputs("<",nt_out); else if (*text=='>') fputs(">",nt_out); else fputc(*text,nt_out); } fputs("\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(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,"=0;--j) { heading+=(chain[j]&1U)?-TURN:TURN; x+=cos(heading);y+=sin(heading); if(!pass) { if(xxmax)xmax=x; if(yymax)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("\n" "\n", nt_out); draw(); fputs("\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; }