/*>>> srch.c: "srch" command output analyzer */

/* Revised: 1994.02.02 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <sys/stat.h>

#define nonstatic

#ifdef NULL
#undef NULL
#endif
#define NULL ((void *) 0)

typedef short int siT, *siptrT;

typedef long int liT, *liptrT;
typedef unsigned long int uliT, *uliptrT;

typedef double lrT, *lrptrT;

typedef char *charptrT;

/* input buffer line length */

#define lineL 256

/* text output column limit */

#define cL 80

/* search termination reason codes (primary reason) */

#define strcL 16
typedef siT strcT;

#define strc_none  0 /* no reason, or nonterminated search */
#define strc_bigl  1 /* big loss detected */
#define strc_bigw  2 /* big win detected */
#define strc_book  3 /* book move located */
#define strc_cert  4 /* certain move located */
#define strc_frcd  5 /* forced move located */
#define strc_full  6 /* full search completed */
#define strc_intr  7 /* interrupted and not resumed */
#define strc_iter  8 /* iteration level limit */
#define strc_mate  9 /* mating move located */
#define strc_node 10 /* node limit */
#define strc_obvs 11 /* obvious move located */
#define strc_save 12 /* saving drawing move located */
#define strc_sing 13 /* single move exists */
#define strc_targ 14 /* target move located */
#define strc_time 15 /* general time expiration */

/* the search append record */

typedef struct sarS
	{
	liT          sar_prob; /* position number */
	struct sarS *sar_next; /* next append record */
	} sarT, *sarptrT;

/* the search statistic structure */

typedef struct ssS
	{
	liT	ss_count;    /* number of instances */
	liT	ss_time;     /* total time (seconds) */
	lrT	ss_ssq_time; /* sigma (squared time) */
	lrT	ss_ave_time; /* average time (seconds) */
	lrT	ss_std_time; /* standard deviation time (seconds) */
	liT	ss_exec;     /* total exec (nodes) */
	lrT	ss_ssq_exec; /* sigma (squared exec) */
	lrT	ss_ave_exec; /* average exec (nodes) */
	lrT	ss_std_exec; /* standard deviation exec (nodes) */
	lrT	ss_portion;	 /* fraction of total instances */
	} ssT, *ssptrT;

/* definitions for the statistics array */

#define saL 9
typedef siT saT;

#define sa_match 0
#define sa_delay 1
#define sa_fault 2
#define sa_mates 3
#define sa_mate0 4
#define sa_mate1 5
#define sa_solve 6
#define sa_wrong 7
#define sa_total 8

/* the program name */

static charptrT progname;

/* search report file name suffix */

static charptrT suffix = ".sr";

/* the statistics array */

static ssT sav[saL];

/* the label array */

static charptrT sastrv[saL] =
	{
	"match",
	"delay",
	"fault",
	"mates",
	"mate0",
	"mate1",
	"solve",
	"wrong",
	"total"
	};

/* the line buffer */

static char line[lineL];

/* the useful line count */

static liT stat_n;

/* the append head and tail pointers */

static sarptrT sarheadv[saL];
static sarptrT sartailv[saL];

/*--> Diagnose: diagnostic exit */
static
void
Diagnose(charptrT s)
{
fprintf(stderr, "%s: fatal: %s\n", progname, s);

exit(1);
}

/*--> Diagnose2: diagnostic exit (two strings) */
static
void
Diagnose2(charptrT s0, charptrT s1)
{
fprintf(stderr, "%s: fatal: %s%s\n", progname, s0, s1);

exit(1);
}

/*--> Square: square a double */
static
lrT
Square(lrT x)
{

return (x * x);
}

/*--> AppendChain: connect a chain record for the given position number */
static
void
AppendChain(saT sa, liT n)
{
sarptrT sarptr;

sarptr = (sarptrT) malloc(sizeof(sarT));
if (sarptr == NULL)
	Diagnose("Out of memory");
sarptr->sar_prob = n;
sarptr->sar_next = NULL;
if (sarheadv[sa] == NULL)
	sarheadv[sa] = sarptr;
else
	sartailv[sa]->sar_next = sarptr;
sartailv[sa] = sarptr;

return;
}

/*--> FreeChain: free sar chain */
static
void
FreeChain(saT sa)
{
sarptrT sarptr0, sarptr1;

sarptr0 = sarheadv[sa];
while (sarptr0 != NULL)
	{
	sarptr1 = sarptr0->sar_next;
	free(sarptr0);
	sarptr0 = sarptr1;
	};

sarheadv[sa] = sartailv[sa] = NULL;

return;
}

/*--> ClearEntry: clear the indicated stat entry */
static
void
ClearEntry(saT sa)
{
sav[sa].ss_count = 0;
sav[sa].ss_time = 0;
sav[sa].ss_ssq_time = sav[sa].ss_ave_time = sav[sa].ss_std_time = 0.0;
sav[sa].ss_exec = 0;
sav[sa].ss_ssq_exec = sav[sa].ss_ave_exec = sav[sa].ss_std_exec = 0.0;
sav[sa].ss_portion = 0.0;

return;
}

/*--> AccumulateEntry: accumulate data for the indicated stat entry */
static
void
AccumulateEntry(saT sa, liT secs, liT node)
{
sav[sa].ss_count++;
sav[sa].ss_time += secs;
sav[sa].ss_ssq_time += Square(secs);
sav[sa].ss_exec += node;
sav[sa].ss_ssq_exec += Square(node);

return;
}
	
/*--> AccumulateEntryAppend: accumulate/append stat entry */
static
void
AccumulateEntryAppend(saT sa, liT secs, liT node)
{
AccumulateEntry(sa, secs, node);
AppendChain(sa, stat_n);

return;
}
	
/*--> CalculateEntry: process entry totals */
static
void
CalculateEntry(saT sa)
{
lrT dn;

/* check for nonzero count */

if (sav[sa].ss_count != 0)
	{
	/* get a floating point copy of the entry count */

	dn = sav[sa].ss_count;

	/* calculate time mean and sdev */

	sav[sa].ss_ave_time = sav[sa].ss_time / dn;
	sav[sa].ss_std_time =
		sqrt((sav[sa].ss_ssq_time / dn) - Square(sav[sa].ss_ave_time));

	/* calculate exec mean and sdev */

	sav[sa].ss_ave_exec = sav[sa].ss_exec / dn;
	sav[sa].ss_std_exec =
		sqrt((sav[sa].ss_ssq_exec / dn) - Square(sav[sa].ss_ave_exec));

	/* calculate portion */

	sav[sa].ss_portion = sav[sa].ss_count / (lrT) stat_n;
	};

return;
}

/*--> ReportEntry: process entry report */
static
void
ReportEntry(FILE *fp, saT sa)
{
fprintf(fp, "%s:", sastrv[sa]);
fprintf(fp, " %4ld", sav[sa].ss_count);
fprintf(fp, " %7ld", sav[sa].ss_time);
fprintf(fp, " %8.2f", sav[sa].ss_ave_time);
fprintf(fp, " %8.2f", sav[sa].ss_std_time);
fprintf(fp, " %10ld", sav[sa].ss_exec);
fprintf(fp, " %11.2f", sav[sa].ss_ave_exec);
fprintf(fp, " %11.2f", sav[sa].ss_std_exec);
fprintf(fp, " %6.3f", sav[sa].ss_portion);
fprintf(fp, "\n");

return;
}

/*--> ReportHeader: process entry report header */
static
void
ReportHeader(FILE *fp, uliT timeval, charptrT fn_input)
{
fprintf(fp, "Search Summary Report\n");
fprintf(fp, "\n");
fprintf(fp, "Date: %s", ctime(&timeval));
fprintf(fp, "File: %s\n", fn_input);
fprintf(fp, "\n");

fprintf(fp, "%s%s%s%s\n",
	"         PN",
	"    Secs   Mean S   SDev S",
	"      Nodes      Mean N      SDev N",
	"  Fract");

fprintf(fp, "%s%s%s%s\n",
	"         --",
	"    ----   ------   ------",
	"      -----      ------      ------",
	"  -----");

return;
}

/*--> ReportChain: process entry report chain */
static
void
ReportChain(FILE *fp, saT sa)
{
sarptrT sarptr;
char dv[20];
liT cc;
liT length;
liT pid0, pid1;

fprintf(fp, "\n");

if (sarheadv[sa] == NULL)
	fprintf(fp, "[%s] None detected.\n", sastrv[sa]);
else
	{
	fprintf(fp, "[%s] detected for the following positions:\n", sastrv[sa]);
	cc = 0;
	sarptr = sarheadv[sa];
	while (sarptr != NULL)
		{
		pid0 = pid1 = sarptr->sar_prob;
		sarptr = sarptr->sar_next;
		while ((sarptr != NULL) && (sarptr->sar_prob == (pid1 + 1)))
			{
			pid1++;
			sarptr = sarptr->sar_next;
			};
		if (pid0 == pid1)
			sprintf(dv, "%ld", pid0);
		else
			sprintf(dv, "%ld-%ld", pid0, pid1);
		length = strlen(dv);
		if (cc == 0)
			{
			fprintf(fp, "%s", dv);
			cc = length;
			}
		else
			if ((cc + 1 + length) < cL)
				{
				fprintf(fp, " %s", dv);
				cc += 1 + length;
				}
			else
				{
				fprintf(fp, "\n");
				fprintf(fp, "%s", dv);
				cc = length;
				};
		};
	if (cc != 0)
		fprintf(fp, "\n");
	};

return;
}

/*--> ScanFile: scan a file and produce a report */
static
void
ScanFile(charptrT fn_input)
{
liT i, length;
siT flag, match, mate;
strcT strc;
liT secs, node;
saT sa;
struct stat statusblock;
uliT timeval;
FILE *fp_input;
FILE *fp_output;
charptrT fn_output;

flag = 1;
fp_input = fp_output = NULL;

/* fetch the status block for the input file */

if (stat(fn_input, &statusblock) != 0)
	Diagnose2("Can't status: ", fn_input);

/* open the input file */

if ((fp_input = fopen(fn_input, "r")) == NULL)
	Diagnose2("Can't open for reading: ", fn_input);

/* set the output file name */

length = strlen(fn_input);
i = length - 1;
while ((i > 0) && (fn_input[i] != '.') && (fn_input[i] != '/'))
	i--;
if ((i == 0) || (fn_input[i] != '.'))
	{
	fn_output = malloc(length + strlen(suffix) + 1);
	if (fn_output == NULL)
		Diagnose("Out of Memory");
	strcpy(fn_output, fn_input);
	}
else
	{
	fn_output = malloc(i + strlen(suffix) + 1);
	if (fn_output == NULL)
		Diagnose("Out of Memory");
	strncpy(fn_output, fn_input, i);
	fn_output[i] = '\0';
	};
strcat(fn_output, suffix);

/* open the output file */

if ((fp_output = fopen(fn_output, "w")) == NULL)
	Diagnose2("Can't open for writing: ", fn_output);

/* copy input file modification time */

timeval = statusblock.st_mtime;

/* clear entries */

for (sa = 0; sa < saL; sa++)
	{
	ClearEntry(sa);
	sarheadv[sa] = sartailv[sa] = NULL;
	};

/* set stat count */

stat_n = 0;

/* scan input file until eof encountered */

while (flag && fgets(line, lineL, fp_input) != NULL)
	{
	/* check if input line matches template */

	length = strlen(line);
	if ((length > 5) && (strncmp(line, "*srch", 5) == 0))
		{
		/* bump stat count */

		stat_n++;

		/* scan fields for matching line */

		sscanf(line, "*srch: %hd   [%hd/%hd]   secs: %ld   node: %ld",
			&match, &strc, &mate, &secs, &node);

		/* accumulate statistics */
		
		AccumulateEntry(sa_total, secs, node);

		if (match)
			if ((strc == strc_iter) || (strc == strc_node) ||
				(strc == strc_time)) 
				AccumulateEntryAppend(sa_delay, secs, node);
			else
				AccumulateEntryAppend(sa_match, secs, node);
		else
			AccumulateEntryAppend(sa_fault, secs, node);

		if (mate)
			{
			AccumulateEntryAppend(sa_mates, secs, node);
			if (match)
				AccumulateEntryAppend(sa_mate1, secs, node);
			else
				AccumulateEntryAppend(sa_mate0, secs, node);
			};

		if (match || mate)
			AccumulateEntryAppend(sa_solve, secs, node);
		else
			AccumulateEntryAppend(sa_wrong, secs, node);
		};
	};

/* anything found? */

if (stat_n > 0)
	{
	/* calculate moments */

	for (sa = 0; sa < saL; sa++)
		CalculateEntry(sa);
	};

/* report */

if (flag)
	{
	ReportHeader(fp_output, timeval, fn_input);
	for (sa = 0; sa < saL; sa++)
		ReportEntry(fp_output, sa);
	for (sa = sa_match; sa < sa_total; sa++)
		ReportChain(fp_output, sa);

	ReportHeader(stdout, timeval, fn_input);
	for (sa = 0; sa < saL; sa++)
		ReportEntry(stdout, sa);
	for (sa = sa_match; sa < sa_total; sa++)
		ReportChain(stdout, sa);
	
	for (sa = 0; sa < saL; sa++)
		FreeChain(sa);
	};

/* close the files */

if (fp_input != NULL)
	fclose(fp_input);
if (fp_output != NULL)
	fclose(fp_output);

/* deallocate output file name */

if (fn_output != NULL)
	free(fn_output);

return;
}

/*--> main: main entry for srch analyzer */
nonstatic
int
main(int argc, char *argv[])
{
liT i;

progname = argv[0];
for (i = 1; i < argc; i++)
	ScanFile(argv[i]);

return (0);
}

/* srch.c: eof */
