Script request thread

For scripts to aid with computation or simulation in cellular automata.
User avatar
dvgrn
Moderator
Posts: 5895
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Script request thread

Post by dvgrn » February 7th, 2019, 7:01 pm

Moosey wrote:problem:
{screenshot} (text might be better for a problem report, by the way, since it's searchable)

Code: Select all

SyntaxError: Non-ASCII character '\xc2' in file ...simkin random G collisions.py on line 58, but no encoding declared: see http://python.org/dev/peps/pep-0263/ for details
This is a problem with almost any python script I get from Conwaylife.com
Should be solvable. Did you look at the web page the error suggested you should look at? I guess it's pretty opaque... a Google search on the error message text will probably work a lot better. Try adding

Code: Select all

# -*- coding: utf-8 -*-
as a new first line of your Python scripts. (And if that doesn't work, keep doing similar searches and try whatever suggestions come up, especially on Stack Exchange.)

You might be saving Python files using a text editor or IDE that has a different default character encoding than conwaylife'com ("<meta http-equiv="content-type" content="text/html; charset=UTF-8" />"). You could probably change that default encoding in your text editor, as another way to solve the problem.

User avatar
calcyman
Posts: 2097
Joined: June 1st, 2009, 4:32 pm

Re: Script request thread

Post by calcyman » February 8th, 2019, 7:23 am

dvgrn wrote:
Moosey wrote:A script that throws together random n-glider synthesis in CGoL, please.
Python.
You’d give it a number n and it would output a random n-G synth.
Check out simsim314's script from... yikes, that was almost five years ago already. It might not precisely match your request, but it might be close enough, and it should be easy to make adjustments.
Another thing you can do nowadays, that seems to be under-utilised: if you write a program (in any language) which prints RLEs of glider syntheses, you can pipe it into apgsearch (compiled with a symmetry containing 'stdin' as a substring) and it will search those as 'soups' instead of 16x16 pseudorandom soups. Everything else is unchanged, so it still uploads to Catagolue (and /hashsoup gives you back the original RLEs as intended). Here's an example:

https://catagolue.appspot.com/object/ov_q6/b3s23

I think there's a limit of 2000 characters per RLE, so you can't search huge soups in this manner, but it certainly gives you much more freedom than the regular symmetries (and accepts multistate RLEs). These RLEs are considered to begin with a line starting 'x' (as RLEs do, because they have a header line "x = ..., y = ...") and end with the next exclamation mark.

Note: this is not compatible with apgsearch's parallel mode.
Also not completely relevant: is the code checked in somewhere that would get apgsearch to submit random unidirectional slow-salvo "soups"? Or is that something only calcyman can do? (I haven't tried looking around in gitlab/apgoucher yet.)
HoneySearch has certainly been open-source for a while now, hiding in slmake. But it's not particularly user-friendly.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
Moosey
Posts: 2514
Joined: January 27th, 2019, 5:54 pm
Location: A house, or perhaps the OCA board.
Contact:

Re: Script request thread

Post by Moosey » February 11th, 2019, 6:47 pm

dvgrn wrote:
Moosey wrote:problem:
{screenshot} (text might be better for a problem report, by the way, since it's searchable)

Code: Select all

SyntaxError: Non-ASCII character '\xc2' in file ...simkin random G collisions.py on line 58, but no encoding declared: see http://python.org/dev/peps/pep-0263/ for details
This is a problem with almost any python script I get from Conwaylife.com
Should be solvable. Did you look at the web page the error suggested you should look at? I guess it's pretty opaque... a Google search on the error message text will probably work a lot better. Try adding

Code: Select all

# -*- coding: utf-8 -*-
as a new first line of your Python scripts. (And if that doesn't work, keep doing similar searches and try whatever suggestions come up, especially on Stack Exchange.)

You might be saving Python files using a text editor or IDE that has a different default character encoding than conwaylife'com ("<meta http-equiv="content-type" content="text/html; charset=UTF-8" />"). You could probably change that default encoding in your text editor, as another way to solve the problem.
I tried fixing that on a different file, and it worked, but now I have a new problem:

Code: Select all

File "/Users/Moosey/Desktop/cellular automata/Golly-3.2-Mac/Scripts/Python/Brett Breger APGcode.py", line 9
      initpop = int(g.getpop())
  ^
SyntaxError: invalid syntax
Something similar happens in the simkin G collisions script.,

Also, can I have a python code (without either problem) that finds 20x20 or smaller methuselahs in a supplied nontotalistic rule?
It should display the smallest methuselah.
I am a prolific creator of many rather pathetic googological functions

My CA rules can be found here

Also, the tree game
Bill Watterson once wrote: "How do soldiers killing each other solve the world's problems?"

User avatar
dvgrn
Moderator
Posts: 5895
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Script request thread

Post by dvgrn » February 11th, 2019, 7:49 pm

Moosey wrote:I tried fixing that on a different file, and it worked, but now I have a new problem:

Code: Select all

File "/Users/Moosey/Desktop/cellular automata/Golly-3.2-Mac/Scripts/Python/Brett Breger APGcode.py", line 9
      initpop = int(g.getpop())
  ^
SyntaxError: invalid syntax
Can't help you on that one without more context -- except to say that this is one of Python's least user-friendly details. See where the ^ pointer is? The error is called out as line 9, but the actual error is something missing at the end of line 8 -- usually a missing right parenthesis or something along those lines.
Moosey wrote:Also, can I have a python code (without either problem) that finds 20x20 or smaller methuselahs in some nontotalistic rule?
How about a Larger than Life rule? This could be adapted to search other rules, though it's not trivial because methuseblobs tend to disappear cleanly where methuselahs in most rules leave a lot of mess behind.

To solve that problem, you might do something along the lines of what the methuselah fingerprinter script does, starting with random soups in whatever rule you want. You can find out exactly how long a methuselah takes to settle by taking its population-delta fingerprint and removing all the trailing zeroes.

That will generally get you to within one "stride" of the settling point, anyway, whatever number of ticks that might be for your chosen rule. It depends on the LCD of all the oscillator periods that show up sufficiently often in random ash.

I'm guessing it will probably seem like a difficult task to adapt either of these pieces of code to do exactly what you want. On the other hand, putting in some serious time trying to do this kind of thing yourself is a very good way of clarifying what you want, and how much you want it -- and it also tends to get other people more interested in helping with any problems that might come up.

User avatar
Gustone
Posts: 474
Joined: March 6th, 2019, 2:26 am

Re: Script request thread

Post by Gustone » May 10th, 2019, 6:20 am

A script that outputs the min-max rules for a pattern, then (if it is a oscillator or spaceship in the original rule) list of rules where it evolves into itself but not as in the original rule and is not a still life.
Range-1 Moore isotropic only, also no B0.
I like making color palettes for rules

fluffykitty
Posts: 639
Joined: June 14th, 2014, 5:03 pm

Re: Script request thread

Post by fluffykitty » May 10th, 2019, 1:36 pm

Pretty sure that's uncomputable in general (set the main rule to one where gliders are spinners or something and blocks are stable, and have the pattern be a seed for an arbitrary TM emulator in Life that is also programmed to return to the original configuration upon halting)
I like making rules

Ian07
Posts: 354
Joined: September 22nd, 2018, 8:48 am

Re: Script request thread

Post by Ian07 » May 10th, 2019, 2:57 pm

Gustone wrote:A script that outputs the min-max rules for a pattern, then (if it is a oscillator or spaceship in the original rule) list of rules where it evolves into itself but not as in the original rule and is not a still life.
Range-1 Moore isotropic only, also no B0.
isorule.py might be what you're looking for for the first one. I understand what you mean in the latter case, (e.g. DryLife goldenhead) though I'm not sure how to modify it to count those cases since most of the code was written by Rhombic.

fluffykitty
Posts: 639
Joined: June 14th, 2014, 5:03 pm

Re: Script request thread

Post by fluffykitty » May 10th, 2019, 3:01 pm

Well, if it's "with the same period and displacement" then it's (somewhat) efficiently computable by DFSing the isotropic rulespace, but that would probably work better with a dedicated program due to all the ruleswitching
I like making rules

User avatar
Gustone
Posts: 474
Joined: March 6th, 2019, 2:26 am

Re: Script request thread

Post by Gustone » May 11th, 2019, 5:33 am

fluffykitty wrote:Well, if it's "with the same period and displacement" then it's (somewhat) efficiently computable by DFSing the isotropic rulespace, but that would probably work better with a dedicated program due to all the ruleswitching
No, it does NOT have to be the same period and/or displacement.
I like making color palettes for rules

User avatar
Moosey
Posts: 2514
Joined: January 27th, 2019, 5:54 pm
Location: A house, or perhaps the OCA board.
Contact:

Re: Script request thread

Post by Moosey » May 11th, 2019, 7:52 am

Gustone wrote:
fluffykitty wrote:Well, if it's "with the same period and displacement" then it's (somewhat) efficiently computable by DFSing the isotropic rulespace, but that would probably work better with a dedicated program due to all the ruleswitching
No, it does NOT have to be the same period and/or displacement.
They might be difficult—
an ambitious searcher wrote:Find all the rules where the LWSS...
Travels at c in any orthogonal direction
Travels at c/2 in any direction
Travels at 2c/2 in any orthogonal direction
Travels at c/3 in any direction
Travels at 2c/3 in any direction (possibly only orthogonal)
Travels at 3c/3 in any orthogonal direction
...
Oscillates
It would probably be hard to code for, especially if you want some fancy deluxe features (e.g. Outputting the object’s RLE when it is at minimum population)
I am a prolific creator of many rather pathetic googological functions

My CA rules can be found here

Also, the tree game
Bill Watterson once wrote: "How do soldiers killing each other solve the world's problems?"

User avatar
Gustone
Posts: 474
Joined: March 6th, 2019, 2:26 am

Re: Script request thread

Post by Gustone » May 11th, 2019, 8:08 am

Moosey wrote:
Gustone wrote:
fluffykitty wrote:Well, if it's "with the same period and displacement" then it's (somewhat) efficiently computable by DFSing the isotropic rulespace, but that would probably work better with a dedicated program due to all the ruleswitching
No, it does NOT have to be the same period and/or displacement.
They might be difficult—
an ambitious searcher wrote:Find all the rules where the LWSS...
Travels at c in any orthogonal direction
Travels at c/2 in any direction
Travels at 2c/2 in any orthogonal direction
Travels at c/3 in any direction
Travels at 2c/3 in any direction (possibly only orthogonal)
Travels at 3c/3 in any orthogonal direction
...
Oscillates
It would probably be hard to code for, especially if you want some fancy deluxe features (e.g. Outputting the object’s RLE when it is at minimum population)
No I dont want minimum rle, it will just run a pattern in every non b0 and/or b1 and/or s012345678 rule where it evolves diffirently and find in what rules it evolves into itself, no matter the displacement or rotation.
I like making color palettes for rules

User avatar
Moosey
Posts: 2514
Joined: January 27th, 2019, 5:54 pm
Location: A house, or perhaps the OCA board.
Contact:

Re: Script request thread

Post by Moosey » May 11th, 2019, 8:13 am

Gustone wrote:snip
No I dont want minimum rle, it will just run a pattern in every non b0 and/or b1 and/or s012345678 rule and find in what rules it evolves into itself, no matter the displacement or rotation.
Oh good, then you want the most barebones version of your script. That might be doable for someone who actually knows how to make scripts.
Last edited by Moosey on June 7th, 2019, 3:38 pm, edited 1 time in total.
I am a prolific creator of many rather pathetic googological functions

My CA rules can be found here

Also, the tree game
Bill Watterson once wrote: "How do soldiers killing each other solve the world's problems?"

User avatar
dvgrn
Moderator
Posts: 5895
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Script request thread

Post by dvgrn » May 11th, 2019, 9:02 am

Gustone wrote:No I dont want minimum rle, it will just run a pattern in every non b0 and/or b1 and/or s012345678 rule where it evolves [differently] and find in what rules it evolves into itself, no matter the displacement or rotation.
That script is writable as described, but it couldn't be run to completion. If I'm thinking about it right, you've excluded roughly four isotropic bits, leaving about 2^98 isotropic range-1 Moore-neighborhood rules to search through.

That's over 10^29 rules, which is more than the number of stars in the universe or the number of microseconds since the Big Bang. You could maybe exclude the B2 transitions as well, but it wouldn't make much practical difference.

So even if you can check one rule per microsecond (which is more than a little unlikely with a Golly script, especially since you haven't specified a maximum number of ticks to check for matches) you're going to be waiting a mighty long time for a complete report of workable rules.

Maybe test randomly chosen rules for N seconds? That probably wouldn't find anything in a reasonable amount of time, though. Or just test all rules that are [N=1, 2, 3...] transitions away from a given rule? That's doable for small N, but gets a couple of orders of magnitude harder every time N increases by 1, so that search would get bogged down fairly quickly too -- and it would probably find mostly trivial matches, where a particular isotropic bit is barely used and just produces some change that dies away and returns to the same evolutionary sequence.

There are only a few tens of thousands of Life-Like rules, so a script to survey those would be a much more reasonable goal.

fluffykitty
Posts: 639
Joined: June 14th, 2014, 5:03 pm

Re: Script request thread

Post by fluffykitty » May 11th, 2019, 1:21 pm

fluffykitty wrote:Pretty sure that's uncomputable in general (set the main rule to one where gliders are spinners or something and blocks are stable, and have the pattern be a seed for an arbitrary TM emulator in Life that is also programmed to return to the original configuration upon halting)
so you can't even do it in theory, if the maximum period is unbounded. However, if there's a maximum period, then it is possible, by just doing a depth first search of the isotropic rulespace and looking for rules where the pattern repeats before the period limit (and possibly optimizing out B1e/B2a explosions, B0, S01234, and other isotropic transition combinations that make spaceships impossible), which would be much fore efficient than just brute force searching all isotropic rules (which is computationally infeasible)
I like making rules

User avatar
Gustone
Posts: 474
Joined: March 6th, 2019, 2:26 am

Re: Script request thread

Post by Gustone » May 11th, 2019, 3:59 pm

dvgrn wrote:
Gustone wrote:No I dont want minimum rle, it will just run a pattern in every non b0 and/or b1 and/or s012345678 rule where it evolves [differently] and find in what rules it evolves into itself, no matter the displacement or rotation.
That script is writable as described, but it couldn't be run to completion. If I'm thinking about it right, you've excluded roughly four isotropic bits, leaving about 2^98 isotropic range-1 Moore-neighborhood rules to search through.

That's over 10^29 rules, which is more than the number of stars in the universe or the number of microseconds since the Big Bang. You could maybe exclude the B2 transitions as well, but it wouldn't make much practical difference.

So even if you can check one rule per microsecond (which is more than a little unlikely with a Golly script, especially since you haven't specified a maximum number of ticks to check for matches) you're going to be waiting a mighty long time for a complete report of workable rules.

Maybe test randomly chosen rules for N seconds? That probably wouldn't find anything in a reasonable amount of time, though. Or just test all rules that are [N=1, 2, 3...] transitions away from a given rule? That's doable for small N, but gets a couple of orders of magnitude harder every time N increases by 1, so that search would get bogged down fairly quickly too -- and it would probably find mostly trivial matches, where a particular isotropic bit is barely used and just produces some change that dies away and returns to the same evolutionary sequence.

There are only a few tens of thousands of Life-Like rules, so a script to survey those would be a much more reasonable goal.
Well, a set period in some thousands of rules closest to the original one, but it wont survey rules where it evolves exactly like in the original one.
I like making color palettes for rules

User avatar
Moosey
Posts: 2514
Joined: January 27th, 2019, 5:54 pm
Location: A house, or perhaps the OCA board.
Contact:

Re: Script request thread

Post by Moosey » May 11th, 2019, 4:01 pm

Gustone wrote:
dvgrn wrote:<snip>
Well, a set period in some thousands of rules closest to the original one, but it wont survey rules where it evolves exactly like in the original one.
How would you know in which rules it evolves exactly like in the base rule?
I am a prolific creator of many rather pathetic googological functions

My CA rules can be found here

Also, the tree game
Bill Watterson once wrote: "How do soldiers killing each other solve the world's problems?"

fluffykitty
Posts: 639
Joined: June 14th, 2014, 5:03 pm

Re: Script request thread

Post by fluffykitty » May 11th, 2019, 9:51 pm

Why is everyone ignoring me?
I like making rules

User avatar
dvgrn
Moderator
Posts: 5895
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Script request thread

Post by dvgrn » May 11th, 2019, 10:25 pm

fluffykitty wrote:Why is everyone ignoring me?
Don't think anyone is. You've been saying things that are true, and therefore they didn't seem to need to be replied to.

I wasn't sure if "uncomputable" meant very much to Moosey and Gustone, let alone the reference to Halting Problem-type uncomputability. So I tried explaining in more detail how impractical it would be to try to survey all isotropic rules, or any significant fraction of them.

fluffykitty
Posts: 639
Joined: June 14th, 2014, 5:03 pm

Re: Script request thread

Post by fluffykitty » May 11th, 2019, 10:48 pm

I guess that makes sense.
I like making rules

Ian07
Posts: 354
Joined: September 22nd, 2018, 8:48 am

Re: Script request thread

Post by Ian07 » June 7th, 2019, 3:29 pm

Does anyone have an RLE-to-apgcode script for Golly that allows for the extended format?
EDIT: Was able to do this using Python lifelib.

wildmyron
Posts: 1274
Joined: August 9th, 2013, 12:45 am

Re: Script request thread

Post by wildmyron » June 7th, 2019, 10:01 pm

Ian07 wrote:Does anyone have an RLE-to-apgcode script for Golly that allows for the extended format?
EDIT: Was able to do this using Python lifelib.
Lifelib is probably the preferred option for generating canonical apgcodes, but if that's not available, or inconvenient, I believe this is the Python version which matches the greedy apgcodes implemented in Lifelib. I haven't verified this to be the case - would be good if someone can do so and we should probably clarify that on the LifeWiki.
The latest version of the 5S Project contains over 221,000 spaceships. Tabulated pages up to period 160 are available on the LifeWiki.

Ian07
Posts: 354
Joined: September 22nd, 2018, 8:48 am

Re: Script request thread

Post by Ian07 » June 8th, 2019, 8:27 am

wildmyron wrote:I believe this is the Python version which matches the greedy apgcodes implemented in Lifelib. I haven't verified this to be the case - would be good if someone can do so and we should probably clarify that on the LifeWiki.
I just tried this with the asymmetric pre-pulsar spaceship and can confirm that it produces the exact same apgcode as lifelib.

Ian07
Posts: 354
Joined: September 22nd, 2018, 8:48 am

Re: Script request thread

Post by Ian07 » July 3rd, 2019, 6:23 pm

Does anyone have a script to calculate volatility and strict volatility? Oscillizer only works for periods less than 300, which is why stats are still missing for 60P312 on the wiki.

User avatar
dvgrn
Moderator
Posts: 5895
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Script request thread

Post by dvgrn » July 3rd, 2019, 7:20 pm

Ian07 wrote:Does anyone have a script to calculate volatility and strict volatility? Oscillizer only works for periods less than 300, which is why stats are still missing for 60P312 on the wiki.
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

	printf("volatility=%.2f%%",PCT(pop_rotor,pop_total));

	printf(", strict-volatility=");
	printf("%.2f%%\n",PCT(pop_subperiod[period],pop_total));
Full source code:

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\">&nbsp;</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;
}

User avatar
testitemqlstudop
Posts: 1206
Joined: July 21st, 2016, 11:45 am
Location: in catagolue
Contact:

Re: Script request thread

Post by testitemqlstudop » July 27th, 2019, 6:21 am

I would like a bulk apgcode to RLE, which converts a very long list of apgcodes (think 600K) to RLE.

Post Reply