/*

	Make FAX encode table.
	by SHIMA

    Modified from maketbl.c written by Gohodoji


	Usage: maketbl > encode.dat

*/

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <dos.h>


/* ----------------------------------------------------------------------- */
#define EOS 		0
#define TERM		1
#define MAKE		2
#define	BLACK		4
#define	WHITE		8
#define	MAX_TABLE	1024

struct decode {
	int shift;
	unsigned int flag;
	int run;
};

struct decode decode[MAX_TABLE];
int	last_decode = 256;

typedef struct _list {
	char s[16];
	unsigned flag;
	unsigned run;
} LIST;

struct encode	{
	int	data;
	int	length;
};

/* ----------------------------------------------------------------------- */
static LIST white_list[] = {
	"00110101",			TERM,	0,
	"000111",			TERM,	1,
	"0111",				TERM,	2,
	"1000",				TERM,	3,
	"1011",				TERM,	4,
	"1100",				TERM,	5,
	"1110",				TERM,	6,
	"1111",				TERM,	7,
	"10011",			TERM,	8,
	"10100",			TERM,	9,
	"00111",			TERM,	10,
	"01000",			TERM,	11,
	"001000",			TERM,	12,
	"000011",			TERM,	13,
	"110100",			TERM,	14,
	"110101",			TERM,	15,
	"101010",			TERM,	16,
	"101011",			TERM,	17,
	"0100111",			TERM,	18,
	"0001100",			TERM,	19,
	"0001000",			TERM,	20,
	"0010111",			TERM,	21,
	"0000011",			TERM,	22,
	"0000100",			TERM,	23,
	"0101000",			TERM,	24,
	"0101011",			TERM,	25,
	"0010011",			TERM,	26,
	"0100100",			TERM,	27,
	"0011000",			TERM,	28,
	"00000010",			TERM,	29,
	"00000011",			TERM,	30,
	"00011010",			TERM,	31,
	"00011011",			TERM,	32,
	"00010010",			TERM,	33,
	"00010011",			TERM,	34,
	"00010100",			TERM,	35,
	"00010101",			TERM,	36,
	"00010110",			TERM,	37,
	"00010111",			TERM,	38,
	"00101000",			TERM,	39,
	"00101001",			TERM,	40,
	"00101010",			TERM,	41,
	"00101011",			TERM,	42,
	"00101100",			TERM,	43,
	"00101101",			TERM,	44,
	"00000100",			TERM,	45,
	"00000101",			TERM,	46,
	"00001010",			TERM,	47,
	"00001011",			TERM,	48,
	"01010010",			TERM,	49,
	"01010011",			TERM,	50,
	"01010100",			TERM,	51,
	"01010101",			TERM,	52,
	"00100100",			TERM,	53,
	"00100101",			TERM,	54,
	"01011000",			TERM,	55,
	"01011001",			TERM,	56,
	"01011010",			TERM,	57,
	"01011011",			TERM,	58,
	"01001010",			TERM,	59,
	"01001011",			TERM,	60,
	"00110010",			TERM,	61,
	"00110011",			TERM,	62,
	"00110100",			TERM,	63,

	"11011",			MAKE,	64,
	"10010",			MAKE,	128,
	"010111",			MAKE,	192,
	"0110111",			MAKE,	256,
	"00110110",			MAKE,	320,
	"00110111",			MAKE,	384,
	"01100100",			MAKE,	448,
	"01100101",			MAKE,	512,
	"01101000",			MAKE,	576,
	"01100111",			MAKE,	640,
	"011001100",		MAKE,	704,
	"011001101",		MAKE,	768,
	"011010010",		MAKE,	832,
	"011010011",		MAKE,	896,
	"011010100",		MAKE,	960,
	"011010101",		MAKE,	1024,
	"011010110",		MAKE,	1088,
	"011010111",		MAKE,	1152,
	"011011000",		MAKE,	1216,
	"011011001",		MAKE,	1280,
	"011011010",		MAKE,	1344,
	"011011011",		MAKE,	1408,
	"010011000",		MAKE,	1472,
	"010011001",		MAKE,	1536,
	"010011010",		MAKE,	1600,
	"011000",			MAKE,	1664,
	"010011011",		MAKE,	1728,

    "00000001000",  	MAKE,   1792,
    "00000001100",  	MAKE,   1856,
    "00000001101",  	MAKE,   1920,
    "000000010010", 	MAKE,   1984,
    "000000010011", 	MAKE,   2048,
    "000000010100", 	MAKE,   2112,
    "000000010101", 	MAKE,   2176,
    "000000010110", 	MAKE,   2240,
    "000000010111", 	MAKE,   2304,
    "000000011100", 	MAKE,   2368,
    "000000011101", 	MAKE,   2432,
    "000000011110", 	MAKE,   2496,
    "000000011111", 	MAKE,   2560,

	EOS,			 	EOS,	 EOS,
};

static LIST black_list[] = {
	"0000110111",		TERM,	0,
	"010",				TERM,	1,
	"11",				TERM,	2,
	"10",				TERM,	3,
	"011",				TERM,	4,
	"0011",				TERM,	5,
	"0010",				TERM,	6,
	"00011",			TERM,	7,
	"000101",			TERM,	8,
	"000100",			TERM,	9,
	"0000100",			TERM,	10,
	"0000101",			TERM,	11,
	"0000111",			TERM,	12,
	"00000100",			TERM,	13,
	"00000111",			TERM,	14,
	"000011000",		TERM,	15,
	"0000010111",		TERM,	16,
	"0000011000",		TERM,	17,
	"0000001000",		TERM,	18,
	"00001100111",		TERM,	19,
	"00001101000",		TERM,	20,
	"00001101100",		TERM,	21,
	"00000110111",		TERM,	22,
	"00000101000",		TERM,	23,
	"00000010111",		TERM,	24,
	"00000011000",		TERM,	25,
	"000011001010",		TERM,	26,
	"000011001011",		TERM,	27,
	"000011001100",		TERM,	28,
	"000011001101",		TERM,	29,
	"000001101000",		TERM,	30,
	"000001101001",		TERM,	31,
	"000001101010",		TERM,	32,
	"000001101011",		TERM,	33,
	"000011010010",		TERM,	34,
	"000011010011",		TERM,	35,
	"000011010100",		TERM,	36,
	"000011010101",		TERM,	37,
	"000011010110",		TERM,	38,
	"000011010111",		TERM,	39,
	"000001101100",		TERM,	40,
	"000001101101",		TERM,	41,
	"000011011010",		TERM,	42,
	"000011011011",		TERM,	43,
	"000001010100",		TERM,	44,
	"000001010101",		TERM,	45,
	"000001010110",		TERM,	46,
	"000001010111",		TERM,	47,
	"000001100100",		TERM,	48,
	"000001100101",		TERM,	49,
	"000001010010",		TERM,	50,
	"000001010011",		TERM,	51,
	"000000100100",		TERM,	52,
	"000000110111",		TERM,	53,
	"000000111000",		TERM,	54,
	"000000100111",		TERM,	55,
	"000000101000",		TERM,	56,
	"000001011000",		TERM,	57,
	"000001011001",		TERM,	58,
	"000000101011",		TERM,	59,
	"000000101100",		TERM,	60,
	"000001011010",		TERM,	61,
	"000001100110",		TERM,	62,
	"000001100111",		TERM,	63,

	"0000001111",		MAKE,	64,
	"000011001000",		MAKE,	128,
	"000011001001",		MAKE,	192,
	"000001011011",		MAKE,	256,
	"000000110011",		MAKE,	320,
	"000000110100",		MAKE,	384,
	"000000110101",		MAKE,	448,
	"0000001101100",	MAKE,	512,
	"0000001101101",	MAKE,	576,
	"0000001001010",	MAKE,	640,
	"0000001001011",	MAKE,	704,
	"0000001001100",	MAKE,	768,
	"0000001001101",	MAKE,	832,
	"0000001110010",	MAKE,	896,
	"0000001110011",	MAKE,	960,
	"0000001110100",	MAKE,	1024,
	"0000001110101",	MAKE,	1088,
	"0000001110110",	MAKE,	1152,
	"0000001110111",	MAKE,	1216,
	"0000001010010",	MAKE,	1280,
	"0000001010011",	MAKE,	1344,
	"0000001010100",	MAKE,	1408,
	"0000001010101",	MAKE,	1472,
	"0000001011010",	MAKE,	1536,
	"0000001011011",	MAKE,	1600,
	"0000001100100",	MAKE,	1664,
	"0000001100101",	MAKE,	1728,

    "00000001000",	  	MAKE,   1792,
    "00000001100", 		MAKE,   1856,
    "00000001101",  	MAKE,   1920,
    "000000010010", 	MAKE,   1984,
    "000000010011", 	MAKE,   2048,
    "000000010100", 	MAKE,   2112,
    "000000010101", 	MAKE,   2176,
    "000000010110", 	MAKE,   2240,
    "000000010111", 	MAKE,   2304,
    "000000011100", 	MAKE,   2368,
    "000000011101", 	MAKE,   2432,
    "000000011110",		MAKE,   2496,
    "000000011111",		MAKE,   2560,

	EOS,				EOS,	EOS,
};
/* ----------------------------------------------------------------------- */

void count_length(LIST make_list[], int flag)
{
	unsigned i;
	int	j;
	int diff, shift;
	unsigned code, length;
	char *p;

	for(i = 0; make_list[i].flag; i++){
		length = strlen(p = make_list[i].s);
		for (code = 0, j = length; j > 0; )
			code += code + p[--j] - '0';
		code &= 0xff;

		if (!(decode[code].flag & flag) || decode[code].shift != length){
			if (length <= 8 && decode[code].shift > 8  ){
				fprintf(stderr, "Error in DATA(%d run length-)!\n", i);
				exit(1);
			}
			if (length > 8 && (decode[code].flag & flag)
				&& decode[code].shift <= 8){
				fprintf(stderr, "Error in DATA(%d run length+)\n", i);
				exit(1);
			}
			if (decode[code].shift < length)
				shift = length;
			else shift = decode[code].shift;
			diff = (1 << length);
			while(code < 256){
				decode[code].flag  |= flag;
				decode[code].shift  = shift;
				code += diff;
			}
		}
	}
}

void make_decode_data(LIST make_list[], int flag)
{
	unsigned i;
	int	j;
	int diff, top, total, over, shift;
	unsigned code, code0, length;
	char *p;

	for(i = 0; make_list[i].flag; i++){
		length = strlen(p = make_list[i].s);
		for (code0 = 0, j = length; j > 0; )
			code0 += code0 + p[--j] - '0';
		code = code0 & 0xff;
		if (length <= 8){
			if (decode[code].shift != length){
				fprintf(stderr, "Error in DATA(%d double length)\n", i);
				exit(1);
			}
			diff = (1 << length);
			while(code < 256){
				if (decode[code].run
					&& decode[code].run != make_list[i].run){
					fprintf(stderr, "Error in DATA(%d double run 0)!\n", i);
					exit(1);
				}
				decode[code].run   = make_list[i].run;
				decode[code].flag  |= make_list[i].flag;
				code += diff;
			}
			continue;
		}

		if (decode[code].shift < length || decode[code].shift <= 8){
				fprintf(stderr, "Error in DATA(internal 1)!");
				exit(1);
		}

		over = decode[code].shift - 8;
		total = (1 << over);
		if (!decode[code].run){
			decode[code].run = last_decode;
			last_decode += total;
			if (last_decode >= MAX_TABLE){
				fprintf(stderr, "Too many DATA!\n");
				exit(1);
			}
		}

		top = decode[code].run;
		shift = (1 << (length - 8));
		code = (code0 >> 8);
		while(code < total){
			if (decode[top+code].run
				&& decode[top+code].run != make_list[i].run){
				fprintf(stderr, "Error in DATA(double run 1)!\n");
				exit(1);
			}
			decode[top+code].run    = make_list[i].run;
			decode[top+code].flag   = (make_list[i].flag|flag);
			decode[top+code].shift  = length - 8;
			code += shift;
		}
	}
}

void print_table_data(char *s)
{
	int i;
	char c;

	printf("%s[] = \{\n", s);
	c = ',';
	for(i = 0; i < last_decode; i++){
		if (i == last_decode -1) c = ' ';
		printf(
			"\t\{%4d, %2d\}%c\t/* %03X */\n",
			decode[i].run, decode[i].shift, c, i
		);
	}
	printf("};\n\n");
}

void clear_list(void)
{
	int	i;

	for(i=0; i < last_decode; i++){
		decode[i].run = decode[i].flag = decode[i].shift = 0;
	}
	last_decode = 256;
}

void make_encode_data(LIST make_list[], char *fname)
{
	unsigned i;
	int	j;
	unsigned code, length, num;
	char *p;

	printf("%s[]={\n", fname);

	for(i = 0; ; ){
		length = strlen(p = make_list[i].s);
		for (code = 0, j = length; j > 0; )
			code += code + p[--j] - '0';
		num = make_list[i].run;
		if(make_list[++i].flag != EOS)
			printf("\t0x%04X,%7d,\t/* %4d */\n", code, length, num);
		else{
			printf("\t0x%04X,%7d \t/* %4d */\n", code, length, num);
			break;
		}
	}
	printf("};\n\n");
}


/* ----------------------------------------------------------------------- */
void main(void)
{
	make_encode_data(white_list, "white_count_list");
	make_encode_data(black_list, "black_count_list");

	count_length(white_list, WHITE);
	make_decode_data(white_list, WHITE);
	print_table_data("white_decode_list");

	clear_list();

	count_length(black_list, BLACK);
	make_decode_data(black_list, BLACK);
	print_table_data("black_decode_list");

}
