Come to think of it, it's probably time to port Oscillizer to Lua and add it to Golly's scripts. It might end up a little slower, but at least it will be easy to bump up that arbitrary period limit when we need to.
Here's the relevant section for the volatility calculations -- seems simple enough, except for that pop_subperiod thing...
Code: Select all
// Oscillizer
// Game of Life oscillator analyzer
// By Jason Summers, <jason1@pobox.com>
// <http://pobox.com/~jason1/life/oscillizer/>
// Version 1.0
//
// Copyright (C) 2001 Jason Summers
// -------------
// The software is provided "AS IS" and without warranty of any kind.
// In particular, the author DOES NOT warrant that this software is
// free from security bugs when compiled as a CGI program. Although
// I've tried to write it securely, it may still contain bugs that
// could allow a malicious user to take control of your web server.
// If you put the CGI version on a public web server, you do so at
// your own risk.
//
// This software may be used and redistributed freely. You may
// distribute modified versions, but please ensure that they are
// easily identifiable as such.
// -------------
//
// Oscillizer can be compiled in one of two modes: command-line or
// CGI (web-based). This is handled by a mess of #ifdef's.
//
// To configure for CGI, uncomment the #define CGI line, and set
// THISPRG to the name of the CGI executable.
//
//
// To compile as a CGI program, the following library is
// required:
// * cgihtml <http://www.eekim.com/software/cgihtml/>
//
// Both modes are capable of creating images of the oscillator in
// PNG format. For image support, the following libraries are
// required:
// * gd (with PNG support enabled) <http://www.boutell.com/gd/>
// * libpng
// * zlib
//
// Use the HTML markup below (suitably modified) to create a front
// page for the CGI version:
//
// <form action="/cgi-bin/oscillizer.cgi" method=POST>
// <input type=reset value="Clear"> <input type=submit value="Analyze">
// <textarea name=p cols=50 rows=15 wrap=off>
// </textarea></form>
//
// CGI: Comment this out to compile the command-line version.
//#define CGI
// IMAGES: Comment out to compile without image creation support.
#define IMAGES
#ifdef WIN32
#define THISPRG "oscillizer.exe"
#else
#define THISPRG "oscillizer.cgi"
#endif
#ifdef IMAGES
// A temporary directory for the program to store images.
// Does not need to be web-accessible, but needs to be
// writeable by the CGI program.
// You should put an image named "notfound.png" in this directory.
#define IMAGE_BASE_SIZE 400 // roughly determines the image size
#ifdef CGI
#define IMAGEDIR "images"
#endif
#endif // IMAGES
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
#include <fcntl.h>
#include <io.h>
#include <process.h>
#else
#include <unistd.h>
#endif
#ifdef IMAGES
#include "gd.h"
#endif
#ifdef CGI
#include "cgi-lib.h"
#endif
#define uint8 unsigned char
#define uint16 unsigned short
#ifdef CGI
// The maximum period that will be detected.
#define MAXPER 300
// The maximum allowed dimension of a pattern (width or height).
#define MAXPATTERNSIZE 400
#else // !CGI
#define MAXPER 1200
#define MAXPATTERNSIZE 2000
#endif
#if (defined(CGI)) && (!defined(WIN32))
#define PRIORITY_ADJUST 10
// For setting (lowering) process priority.
// If you don't care if it hammers your web server,
// set PRIORITY_ADJUST to 0.
#else
#define PRIORITY_ADJUST 0
#endif
// BORDER: Extra cells around pattern will be allocated.
// Oscillators can expand by at most this many cells from
// their initial phase.
#define BORDER 20
// The maximum size of images that will be created.
// Larger patters will still be handled correctly; they just
// won't be completely drawn.
#define MAX_IMAGE_DIMENSION 800
#ifdef WIN32
#define INLINE __inline
#define STAT _stat
#define UNLINK _unlink
#define PATHSEP "\\"
#else
#define INLINE inline
#define STAT stat
#define UNLINK unlink
#define PATHSEP "/"
#endif
#define PCT(x,y) ((100.0*(double)(x))/((double)(y)))
#ifdef CGI
llist entries;
#endif
int rulestable[18];
int rulesused[18];
uint8* page[MAXPER];
uint16* cellperiod; // periods for each cell (size=numcells)
uint8* cellcount; // used in running the life algorithm
int width,height; // width of the playfield (not an oscillator property)
int numcells; // width*height
int* pop_phase; // population of each phase
int* heat_phase; // number of cells that will change state
int* pop_subperiod; // number of cells of each period
int period;
int period_nontrivial;
int pop_min, pop_max; // min(max) pop in any phase
double pop_avg; // avg pop in a phase
int pop_total; // count of all cells that are ever on
int pop_rotor; // (stator pop is pop_subperiod[1])
int heat_min, heat_max; // min(max) cell transitions in any phase
double heat_avg;
int extent_x_min, extent_x_max, extent_y_min, extent_y_max;
#define NUM_PERIOD_COLORS 10
static int subper_color_r[] = { 96,255,128,144,192,128,255,255,255,255 };
static int subper_color_g[] = { 96,255,255,144,192,255,192,192,255,128 };
static int subper_color_b[] = { 96,255,128,255, 0,255,255,192, 0, 0 };
int period_color[MAXPER+1];
int period_color_map[MAXPER+1];
// p is page number, v is cell state (1 or 0)
INLINE void setcell(p,x,y,v)
{
uint8* byte;
byte = &page[p][width*y+x];
if(v) {
*byte |= 1<<(p%8);
}
else {
*byte &= ~(1<<p%8);
}
}
INLINE int getcell(p,x,y)
{
uint8* byte;
byte = &page[p][width*y+x];
return ( (*byte) & (1<<p%8))?1:0;
}
char * hexcolor(int p)
{
static char hexc[10];
sprintf(hexc,"#%02x%02x%02x",
subper_color_r[period_color_map[p]],
subper_color_g[period_color_map[p]],
subper_color_b[period_color_map[p]]);
return hexc;
}
#ifdef IMAGES
int makeimage(char *fn, int *pict_widthp, int *pict_heightp)
{
int imwidth,imheight, imxpos, imypos;
gdImagePtr im;
FILE *out;
int next_period_color;
int color_oncell;
int lastcolor;
int i,x,y;
int scale, sborder, maxdim;
*pict_widthp = 200;
*pict_heightp = 200;
next_period_color=0;
// first find all the different periods
for(i=0;i<=MAXPER;i++) {
period_color[i]=0;
period_color_map[i]=0;
}
for(i=0;i<numcells;i++) {
period_color[cellperiod[i]]=1; // mark as used
}
imxpos=extent_x_min-1;
imwidth=extent_x_max-extent_x_min+3;
imypos=extent_y_min-1;
imheight=extent_y_max-extent_y_min+3;
if(imwidth>MAX_IMAGE_DIMENSION) imwidth=MAX_IMAGE_DIMENSION;
if(imheight>MAX_IMAGE_DIMENSION) imheight=MAX_IMAGE_DIMENSION;
maxdim= (imwidth>imheight)?imwidth:imheight;
scale = (IMAGE_BASE_SIZE/maxdim);
if(scale>10) scale=10;
if(scale<1) scale=1;
sborder = (scale/3);
if(scale==5) sborder=2;
if(scale==8) sborder=3;
im = gdImageCreate(imwidth*scale,imheight*scale);
*pict_widthp = imwidth*scale;
*pict_heightp = imheight*scale;
color_oncell = gdImageColorAllocate(im,0,0,0);
period_color[0] = gdImageColorAllocate(im, 192,192,192); // unused cells
period_color[period] = gdImageColorAllocate(im, subper_color_r[1],subper_color_g[1],subper_color_b[1]); // full period get special color
period_color[1] = gdImageColorAllocate(im, subper_color_r[0],subper_color_g[0],subper_color_b[0]); // stator
period_color_map[1]=0;
if(period>1)
period_color_map[period]=1;
next_period_color=2;
lastcolor=0;
for(i=period-1;i>=2;i--) {
if(period_color[i]) {
if(next_period_color<NUM_PERIOD_COLORS) {
period_color[i]=gdImageColorAllocate(im,
subper_color_r[next_period_color],
subper_color_g[next_period_color],
subper_color_b[next_period_color]);
period_color_map[i]=next_period_color; // hack
lastcolor=period_color[i];
next_period_color++;
}
else { // too many different periods; ran out of colors
period_color[i]=lastcolor;
period_color_map[i]=next_period_color-1;
}
}
}
for(y=0;y<imheight;y++) {
for(x=0;x<imwidth;x++) {
gdImageFilledRectangle(im,
x*scale,y*scale,x*scale+(scale-1),y*scale+(scale-1),
period_color[cellperiod[(imxpos+x)+(imypos+y)*width]]);
if(sborder>0) {
if(getcell(0,imxpos+x,imypos+y)) {
gdImageFilledRectangle(im,
x*scale+sborder,y*scale+sborder,
x*scale+(scale-sborder-1),y*scale+(scale-sborder-1),
color_oncell);
}
}
}
}
out=fopen(fn,"wb");
if(!out) {
//printf("can't write image file\n");
gdImageDestroy(im);
return 0;
}
gdImagePng(im,out);
fclose(out);
gdImageDestroy(im);
return 1;
}
#endif
// read a memory block line by line
int memgets(unsigned char *buf, int bufsize, unsigned char *m, int msize)
{
static unsigned char *lastm = NULL;
static int pos = 0; // number of bytes in m (lastm) already read
int n; // number of bytes to return to user
if(m==NULL) { // for rereading the current block
lastm=NULL;
return 0;
}
if(m != lastm) {
pos=0;
lastm=m;
}
n=0;
while(1) {
if(n>= (bufsize-1)) {
pos++;
buf[n]='\0';
break;
}
if(pos >= msize) {
buf[n]='\0';
break;
}
if(m[pos] == '\n') {
pos++;
buf[n]='\0';
break;
}
if(m[pos] == '\r') {
if(m[pos+1]=='\n') pos++;
pos++;
buf[n]='\0';
break;
}
buf[n]= m[pos];
pos++;
n++;
}
return n;
}
// Find the pattern-size and the format of the pattern file.
// This function probably shouldn't need to exist.
int getpatternsize(unsigned char*m, int msize, int *pfirstwidth,int *pfirstheight, int *pfileformat)
{
int row,col,count,i,n;
int fileformat;
int maxcol,maxrow;
char buf[600];
fileformat=1; /* 0=unkn 1=picture. 2=rle */
maxrow=0;
maxcol=0;
row=0;
col=0;
while(memgets(buf,500,m,msize)) {
if(buf[0]=='#') continue;
if(buf[0]=='x') {
fileformat=2;
continue;
}
if(fileformat==2) {
count=1;
for(i=0;buf[i];) {
if(buf[i]=='o' || buf[i]=='b') {
i++;
col+=count;
if(col>maxcol) maxcol=col;
count=1;
}
else if(buf[i]=='$' || buf[i]=='!') {
maxrow+=count;
col=0;
count=1;
i++;
}
else if(buf[i]>='0' && buf[i]<='9') {
count=atoi(&buf[i]);
while(buf[i]>='0' && buf[i]<='9') { i++; }
}
else {
i++;
}
}
if(strchr(buf,'!')) break;
}
else {
fileformat=1;
maxrow++;
n=0;
for(i=0;buf[i];i++) {
if(buf[i]=='.' || buf[i]=='*' || buf[i]=='o' || buf[i]=='O') n++;
if(buf[i]=='$') i+=10;
}
if(n>maxcol) maxcol=n;
}
}
(*pfirstwidth)=maxcol;
(*pfirstheight)=maxrow;
(*pfileformat)=fileformat;
return 1;
}
// picture format
void parserule1(char *s)
{
int i;
int in_s=1;
for(i=0;i<18;i++) rulestable[i]=0;
for(i=2;s[i];i++) {
if(s[i]=='b' || s[i]=='B') in_s=0;
if(s[i]=='s' || s[i]=='S') in_s=1;
if(s[i]>='0' && s[i]<='8') {
rulestable[(s[i]-'0')+in_s*9]=1;
}
}
return;
}
// RLE format
void parserule2(char *t)
{
int i;
int in_s=1;
char *s;
s=strstr(t,"rule");
if(!s) return;
for(i=0;i<18;i++) rulestable[i]=0;
for(i=4;s[i];i++) {
if(s[i]=='b' || s[i]=='B') in_s=0;
if(s[i]=='s' || s[i]=='S') in_s=1;
if(s[i]>='0' && s[i]<='8') {
rulestable[(s[i]-'0')+in_s*9]=1;
}
}
return;
}
int readpattern(unsigned char *m,int msize, int *pfileformat)
{
int fileformat;
int i,k;
int row,col,count;
char buf[600];
int pos;
fileformat=(*pfileformat);
if(fileformat==1) {
row=0;
while(memgets(buf,500,m,msize)) {
if(buf[0]=='#') {
if(buf[1]=='R' || buf[1]=='r') {
parserule1(buf);
}
continue;
}
pos=0;
for(i=0;buf[i];i++) {
if(buf[i]=='*' || buf[i]=='o' || buf[i]=='O') {
setcell(0,pos+BORDER,row+BORDER,1);
}
if(buf[i]=='$') pos+=9;
pos++;
}
row++;
}
}
else if(fileformat==2) {
row=0;
col=0;
while(memgets(buf,500,m,msize)) {
if(buf[0]=='x') {
parserule2(buf);
continue;
}
if(buf[0]=='b' || buf[0]=='o' || buf[0]=='$' || buf[0]=='!' || (buf[0]>='0' && buf[0]<='9')) {
count=1;
for(i=0;buf[i];) {
if(buf[i]=='o' || buf[i]=='b') {
for(k=0;k<count;k++) {
if(buf[i]=='o') {
setcell(0,col+k+BORDER,row+BORDER,1);
}
}
col+=count;
count=1;
i++;
}
else if(buf[i]=='$' || buf[i]=='!') {
row+=count;
col=0;
count=1;
i++;
}
else if(buf[i]>='0' && buf[i]<='9') {
count=atoi(&buf[i]);
while(buf[i]>='0' && buf[i]<='9') { i++; }
}
else {
i++;
}
}
}
if(strchr(buf,'!')) break;
}
}
return 1;
}
void allocpages(int numpages, int w, int h)
{
int i;
// each page is for 8 phases
for(i=0;i<numpages;i++) {
if(i%8==0) {
if(!page[i]) {
page[i]=(uint8*)calloc(numcells,1);
}
}
else page[i]= page[i-i%8];
}
}
void freepages(void)
{
int i;
for(i=0;i<MAXPER;i++) {
if(i%8==0) {
if(page[i]) {
free(page[i]);
page[i]=NULL;
}
}
}
}
// returns 1 if identical, 0 if not
int comparepages(p1,p2)
{
int x,y;
for(y=0;y<height;y++) {
for(x=0;x<width;x++) {
if(getcell(p1,x,y)!=getcell(p2,x,y)) return 0;
}
}
return 1;
}
// Print (part of) the pattern on the terminal.
// May be useful for debugging.
void printpat(g,x1,y1,w,h)
{
int i,j;
if(w>79) w=79;
if(h>20) h=20;
for(j=0;j<h;j++) {
for(i=0;i<w;i++) {
if(getcell(g,x1+i,y1+j))
printf("*");
else
printf(".");
}
printf("\n");
}
printf("-------------\n");
}
void run1(g1,g2)
{
int tmp,i,x,y;
for(i=0;i<numcells;i++) cellcount[i]=0;
for(y=1;y<(height-1);y++) {
for(x=1;x<(width-1);x++) {
if(getcell(g1,x,y)) {
cellcount[(y-1)*width + x-1]++;
cellcount[(y-1)*width + x ]++;
cellcount[(y-1)*width + x+1]++;
cellcount[(y )*width + x-1]++;
cellcount[(y )*width + x ]+=9;
cellcount[(y )*width + x+1]++;
cellcount[(y+1)*width + x-1]++;
cellcount[(y+1)*width + x ]++;
cellcount[(y+1)*width + x+1]++;
}
}
}
for(y=1;y<(height-1);y++) {
for(x=1;x<(width-1);x++) {
tmp=cellcount[y*width+x];
rulesused[tmp]=1;
setcell(g2,x,y,rulestable[tmp]);
}
}
}
int calculateperiod(void)
{
int gen;
int lastpage,thispage;
gen=0;
while(gen<MAXPER) {
if(gen==0) { lastpage=0; thispage=1; }
else if(gen%2) { lastpage=1; thispage=2; }
else { lastpage=2; thispage=1; }
run1(lastpage,thispage);
//printpat(thispage,BORDER-5,BORDER-5,20,20);
if(comparepages(0,thispage)) { return gen+1; } // found the period
gen++;
}
return 0; // period too high;
}
// returns 0 only if the pattern is completely empty
int calculate_things(void)
{
int x,y,i,p,f;
int any_life; // temp flag
int flag;
int thisstate,nextstate;
int heat_total;
int pop_total_allp; // sum of pop in all generations
for(i=0;i<period;i++) {
pop_phase[i]=0;
heat_phase[i]=0;
}
// max extents point to the extreme cells, not one past them
extent_x_max=extent_y_max=0;
extent_x_min=width-1;
extent_y_min=height-1;
pop_total=0;
// -- pass 1 --
// init all cells that are ever ON to the full period,
// and cells that are never ON to zero.
// also, calculate populations, etc.
for(y=0;y<height;y++) {
for(x=0;x<width;x++) {
any_life=0;
for(p=0;p<period;p++) {
thisstate=getcell(p,x,y);
nextstate=getcell((p+1)%period,x,y);
if(thisstate) {
pop_phase[p]++;
any_life=1;
if(x<extent_x_min) extent_x_min=x;
if(x>extent_x_max) extent_x_max=x;
if(y<extent_y_min) extent_y_min=y;
if(y>extent_y_max) extent_y_max=y;
}
if(thisstate!=nextstate) heat_phase[p]++;
}
if(any_life) {
cellperiod[y*width+x]= period;
pop_total++;
}
else {
cellperiod[y*width+x]= 0;
}
}
}
if(pop_phase[0]<1) { return 0; } // no cells!
heat_total=0;
pop_total_allp=0;
pop_min=pop_max=pop_phase[0];
heat_min=heat_max=heat_phase[0];
for(i=0;i<period;i++) {
pop_total_allp+=pop_phase[i];
if(pop_phase[i]<pop_min) pop_min=pop_phase[i];
if(pop_phase[i]>pop_max) pop_max=pop_phase[i];
if(heat_phase[i]<heat_min) heat_min=heat_phase[i];
if(heat_phase[i]>heat_max) heat_max=heat_phase[i];
heat_total+=heat_phase[i]++;
}
heat_avg = ((double)heat_total)/((double)period);
pop_avg = ((double)pop_total_allp)/((double)period);
// calculate factors of period (including 1 and period)
// calculate subperiods
for(f=1;f<=(period/2);f++) { // for each factor except the full period
if(period%f) continue; // not a possible subperiod
for(y=0;y<height;y++) { // for each cell...
for(x=0;x<width;x++) {
if(cellperiod[y*width+x]==period) { // skip cells that are never on,
//and those already calculated
flag=1;
for(p=0;p<=(period-f-1);p++) {
if(getcell(p,x,y)!=getcell(p+f,x,y)) {
flag=0;
break; // not this subperiod
}
}
if(flag) {
cellperiod[y*width+x]=f;
}
}
}
}
}
// count up the subperiod populations
for(i=0;i<=period;i++) {
pop_subperiod[i]=0;
}
for(y=0;y<height;y++) { // for each cell...
for(x=0;x<width;x++) {
i=cellperiod[y*width+x];
if(i) {
pop_subperiod[i]++;
}
}
}
// find the highest subperiod with nonzero population
period_nontrivial=0;
for(i=1;i<=period;i++) {
if(pop_subperiod[i]) period_nontrivial=i;
}
pop_rotor=pop_total-pop_subperiod[1];
return 1;
}
unsigned char* read_file_to_mem(char *fn, int *pat_size)
{
unsigned char *m;
struct STAT stbuf;
int size;
FILE *f;
(*pat_size)=0;
m=NULL;
if(STAT(fn,&stbuf)) { printf("File not found.\n"); return NULL; }
size=stbuf.st_size;
if(size<0 || size > 524288) { printf("File too large.\n"); return NULL; }
m=malloc(size); if(!m) { printf("Out of memory.\n"); return NULL; }
f=fopen(fn,"rb");
if(!f) { free(m); printf("Can't read file.\n"); return NULL; }
fread(m,1,size,f);
fclose(f);
(*pat_size)=size;
return m;
}
int analyze_main(int argc, char **argv)
{
int i;
int firstwidth, firstheight;
unsigned char* pat_in_mem;
int pat_size =0;
int fileformat;
char imagefn[100];
int flag;
#ifdef IMAGES
int pict_width, pict_height;
#endif
#ifdef CGI
char imagefid[100];
#else
char *fn;
#endif
for(i=0;i<18;i++) { rulestable[i]=0; rulesused[i]=0; }
// default to Conway's Life
rulestable[3 ]=1;
rulestable[2+9]=1;
rulestable[3+9]=1;
for(i=0;i<MAXPER;i++) {
page[i]=NULL;
}
cellcount=NULL;
cellperiod=NULL;
pop_phase=NULL;
heat_phase=NULL;
strcpy(imagefn,"");
#ifdef CGI
strcpy(imagefid,"");
#ifdef IMAGES
sprintf(imagefid,"%d",(abs(getpid()))%200);
sprintf(imagefn,"%s%s%s.png",IMAGEDIR,PATHSEP,imagefid);
#endif
#endif
#ifdef CGI
pat_in_mem=cgi_val(entries, "p");
if(!pat_in_mem) pat_in_mem="";
// cgihtml doesn't seem to have a function for retrieving
// the length of an entry. But there shouldn't be NULs here.
pat_size = strlen(pat_in_mem);
#else // !CGI
if(argc!=2 && argc!=3) {
printf("usage: %s <file.lif> [<outut-image.png>]\n",argv[0]);
return 1;
}
if(argc>=3) strcpy(imagefn,argv[2]);
fn=argv[1];
pat_in_mem = read_file_to_mem(fn, &pat_size);
if(!pat_in_mem) goto abort;
#endif
if(!pat_in_mem || pat_size<1) {
printf("Pattern is empty.\n");
goto abort;
}
getpatternsize(pat_in_mem,pat_size,&firstwidth,&firstheight,&fileformat);
memgets(NULL,0,NULL,0); // reset memory pointer
if(firstwidth>MAXPATTERNSIZE || firstheight>MAXPATTERNSIZE) {
printf("Pattern too large (maximum size: %d x %d)\n",
MAXPATTERNSIZE,MAXPATTERNSIZE);
goto abort;
}
width=BORDER*2+firstwidth;
height=BORDER*2+firstheight;
numcells=width*height;
allocpages(3,width,height);
readpattern(pat_in_mem,pat_size,&fileformat);
//printpat(0,BORDER,BORDER,firstwidth,firstheight);
cellcount = (uint8*)calloc(numcells,sizeof(uint8));
cellperiod = (uint16*)calloc(numcells,sizeof(uint16));
period=calculateperiod();
pop_phase = (int*)calloc(period,sizeof(int));
heat_phase = (int*)calloc(period,sizeof(int));
pop_subperiod = (int*)calloc(period+1,sizeof(int));
if(period<1) {
printf("Cannot determine period (maximum: %d).\n",MAXPER);
goto abort;
}
// save an image of each phase
allocpages(period,width,height);
for(i=0;i<(period-1);i++) {
run1(i,i+1);
}
if(!calculate_things()) {
printf("Pattern is empty");
goto abort;
}
if(strlen(imagefn)) {
#ifdef IMAGES
if(!makeimage(imagefn, &pict_width, &pict_height)) {
printf("Can't write image file (%s)\n",imagefn);
}
#else
printf("WARNING: Image support is not compiled in.\n");
#endif
}
#ifdef CGI
printf("<b>Oscillizer results</b>\n");
printf("<table border=0 cellpadding=3><tr>\n");
printf("<td valign=top align=center>");
printf("<table border=1 cellspacing=0>\n");
printf("<tr><td colspan=5 align=center><small><b>Cell periods</b></small></td></tr>\n");
printf("<tr><th>Period</th>");
#ifdef IMAGES
printf("<th>Color</th>");
#endif
printf("<th>Count</th><th>%% of total</th>\n<th>%% of rotor</th></tr>\n");
for(i=period;i>=1;i--) {
if(pop_subperiod[i]) {
printf("<tr>");
printf("<td align=center>%3d</td>\n",i);
#ifdef IMAGES
printf("<td bgcolor=\"%s\"> </td>",hexcolor(i));
#endif
printf("<td align=right>%4d</td>",pop_subperiod[i]);
printf("<td align=right>%6.2f%%</td>",PCT(pop_subperiod[i],pop_total));
if(i>1)
printf("<td align=right>%6.2f%%</td>",PCT(pop_subperiod[i],pop_rotor));
else
printf("<td></td>");
printf("</tr>\n");
}
}
printf("</table>\n");
printf("<table border=1 cellspacing=0>\n");
printf("<tr><td colspan=4 align=center><small><b>Vital statistics</b></small></td></tr>\n");
printf("<tr><td><b>Period</b></td><td colspan=3>");
if(period==period_nontrivial)
printf("%d",period);
else
printf("MIXED; LCM: %d, max: %d",period,period_nontrivial);
printf("</td></tr>\n");
printf("<tr><td><b>Population</b></td><td nowrap>min: %d</td>\n",pop_min);
printf("<td nowrap>max: %d</td>\n",pop_max);
printf("<td nowrap>avg: %.2f</td></tr>\n",pop_avg);
printf("<tr><td nowrap><b>Active cells</b></td>");
printf("<td nowrap>rotor: %d</td>\n",pop_rotor);
printf("<td nowrap>stator: %d</td>\n",pop_subperiod[1]);
printf("<td nowrap>total: %d</td></tr>\n",pop_total);
printf("<tr><td><b>Volatility</b></td><td>%.2f%%</td>\n",PCT(pop_rotor,pop_total));
printf("<td colspan=2 nowrap>strict: %.2f%%</td></tr>\n",PCT(pop_subperiod[period],pop_total));
printf("<tr><td><b>Heat</b></td><td nowrap>min: %d</td>\n",heat_min);
printf("<td nowrap>max: %d</td>\n",heat_max);
printf("<td nowrap>avg: %.2f</td></tr>\n",heat_avg);
printf("<tr><td><b>Temperature</b></td><td>%.2f%%</td>",PCT(heat_avg,pop_total));
printf("<td colspan=2 nowrap>rotor: ");
if(pop_rotor>0) printf("%.2f%%",PCT(heat_avg,pop_rotor));
else printf("n/a");
printf("</td></tr>\n");
printf("<tr><td nowrap><b>Bounding box</b></td><td colspan=3 nowrap>%d x %d = %d</td></tr>\n",
extent_x_max-extent_x_min+1,extent_y_max-extent_y_min+1,
(extent_x_max-extent_x_min+1)*(extent_y_max-extent_y_min+1));
flag=0;
printf("<tr><td><b>Rule</b></td><td colspan=3 nowrap>B");
for(i=1;i<18;i++) {
if(i==9) printf("/S");
if(rulesused[i]) {
if(rulestable[i]) printf("%d",i%9);
}
else {
flag=1;
}
}
if(flag) {
printf(" - B");
for(i=1;i<18;i++) {
if(i==9) printf("/S");
if(rulesused[i]) {
if(rulestable[i]) printf("%d",i%9);
}
else {
printf("<u>%d</u>",i%9);
}
}
}
//////////////////
printf("</td></tr>\n");
printf("</table>\n");
printf("</td>\n");
printf("<td valign=top>");
#ifdef IMAGES
printf("<img src=\"%s?a=i&f=%s&x=%d.png\" width=%d height=%d border=1><br>\n",
THISPRG,imagefid,(int)time(NULL),pict_width,pict_height);
#endif
printf("</td></tr></table>\n");
#endif // CGI
#ifndef CGI
printf("Oscillizer results:\n");
printf(" cells periods\n");
printf("period count %%total %%rotor\n");
printf("------ ----- ------ ------\n");
for(i=period;i>=1;i--) {
if(pop_subperiod[i]) {
printf("%4d %6d",i,pop_subperiod[i]);
printf(" %6.2f%%",PCT(pop_subperiod[i],pop_total));
if(i>1) {
printf(" %6.2f%%",PCT(pop_subperiod[i],pop_rotor));
}
printf("\n");
}
}
//printf("period=%d, nontrivial period=%d\n",period,period_nontrivial);
printf("period=");
if(period==period_nontrivial)
printf("%d\n",period);
else
printf("MIXED; LCM: %d, max: %d\n",period,period_nontrivial);
printf("pop: min=%d, max=%d, avg=%.2f \n",
pop_min,pop_max,pop_avg);
printf("active cells: rotor=%d, stator=%d, total=%d\n",
pop_rotor,pop_subperiod[1],pop_total);
printf("volatility=%.2f%%",PCT(pop_rotor,pop_total));
printf(", strict-volatility=");
printf("%.2f%%\n",PCT(pop_subperiod[period],pop_total));
printf("heat: min=%d, max=%d, avg=%.2f\n",
heat_min,heat_max,heat_avg);
printf("temperature=%.2f%%, ",PCT(heat_avg,pop_total));
printf("rotor-temperature=");
if(pop_rotor>0) printf("%.2f%%\n",PCT(heat_avg,pop_rotor));
else printf("n/a\n");
printf("bounding box: %d x %d = %d\n",
extent_x_max-extent_x_min+1,extent_y_max-extent_y_min+1,
(extent_x_max-extent_x_min+1)*(extent_y_max-extent_y_min+1));
flag=0;
printf("rule: B");
for(i=1;i<18;i++) {
if(i==9) printf("/S");
if(rulesused[i]) {
if(rulestable[i]) printf("%d",i%9);
}
else {
flag=1;
}
}
if(flag) {
printf(" - B");
for(i=1;i<18;i++) {
if(i==9) printf("/S");
if(rulesused[i]) {
if(rulestable[i]) printf("%d",i%9);
}
else {
printf("%d",i%9);
}
}
}
printf("\n");
#endif // !CGI
abort:
freepages();
if(pop_subperiod) free(pop_subperiod);
if(pop_phase) free(pop_phase);
if(heat_phase) free(heat_phase);
if(cellcount) free(cellcount);
if(cellperiod) free(cellperiod);
#ifndef CGI
if(pat_in_mem) free(pat_in_mem);
#endif
return 1;
}
#ifdef CGI
// dump a file to stdout (for serving images)
int cat_file(char *fn)
{
FILE *f;
int n;
unsigned char buf[2048];
f=fopen(fn,"rb");
if(!f) return 0;
while( (n=fread(buf,1,2048,f)) ) {
fwrite(buf,1,n,stdout);
}
fclose(f);
return 1;
}
#ifdef IMAGES
//respond to a browser request for the image
int GetImage(void)
{
char fn[300];
char *id;
char imagefid[20];
int i;
#ifdef WIN32
_setmode( _fileno( stdout ), _O_BINARY );
#endif
id = cgi_val(entries, "f");
// validate 'id' (since it came from the www visitor)
if(id) {
strncpy(imagefid,id,10); imagefid[10]='\0';
for(i=0;imagefid[i];i++) {
if(imagefid[i]<'0' || imagefid[i]>'9') imagefid[i]='0';
}
}
else { strcpy(imagefid,"x"); }
sprintf(fn,"%s%s%s.png",IMAGEDIR,PATHSEP,imagefid);
// Some bogus dates to try to convince the browser to
// cache the image and not try to revalidate it.
// Image URLs will never be repeated, so it doesn't matter.
printf("Last-Modified: Wed, 10 Jan 1996 10:00:00 GMT\n");
printf("Expires: Fri, 10 Jan 2020 10:00:00 GMT\n");
printf("Content-Type: image/png\n\n");
if(cat_file(fn)) {
UNLINK(fn);
}
else {
sprintf(fn,"%s%snotfound.png",IMAGEDIR,PATHSEP);
cat_file(fn);
}
return 1;
}
#endif // IMAGES
#endif // CGI
int main(int argc,char**argv)
{
#ifdef CGI
char *action;
int ishtml;
char *method;
#endif
#if PRIORITY_ADJUST != 0
nice(PRIORITY_ADJUST);
#endif
#ifdef CGI
ishtml=1;
read_cgi_input(&entries);
action = cgi_val(entries, "a");
if(!action) action="a";
if(!strlen(action)) action="a";
if(!strcmp(action,"i")) {
ishtml=0;
}
if(ishtml) {
printf("Content-Type: text/html\n\n");
printf("<html><head><title>Oscillizer</title>\n");
printf("</head><body topmargin=4 marginheight=4>\n");
}
if(!strcmp(action,"a")) {
method=getenv("REQUEST_METHOD");
if(!method) method="";
if(strcmp(method,"POST")) {
printf("Incorrect use of this program. Wrong REQUEST_METHOD.\n");
}
else {
analyze_main(argc,argv);
}
}
#ifdef IMAGES
else if(!strcmp(action,"i")) {
ishtml=0;
GetImage();
}
#endif
else {
printf("Incorrect use of this program.\n");
}
list_clear(&entries);
if(ishtml) {
printf("</body>\n</html>\n");
}
#else // !CGI
analyze_main(argc,argv);
#endif
return 0;
}