/*TITLE test program for radix40 conversions */
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <alloc.h>
#include <time.h>

#define STRING_LENGTH 12
#define REPS 4000

#define S_OKAY 0
#define S_ILLEGAL 1

#define HYPHEN 2

typedef unsigned Radix40;

main()
{
	int i;
	unsigned char test[STRING_LENGTH+1] = "Radix40 Test";
	Radix40 temp[STRING_LENGTH];
	unsigned char test2[STRING_LENGTH+1];
	clock_t start, end;

	start = clock();
	for (i = 0; i < REPS; i ++)
		ascii_to_radix40_1(temp,test,STRING_LENGTH);
	end = clock();
	printf("Timer ticks for version 1: %d\n",end-start);

	radix40_to_ascii(temp,test2,STRING_LENGTH);
	if (stricmp(test2,test) != 0)
		printf("Match 1 failed\n");

	start = clock();
	for (i = 0; i < REPS; i ++)
		ascii_to_radix40_2(temp,test,STRING_LENGTH);
	end = clock();
	printf("Timer ticks for version 2: %d\n",end-start);

	radix40_to_ascii(temp,test2,STRING_LENGTH);
	if (stricmp(test2,test) != 0)
		printf("Match 2 failed\n");

	start = clock();
	for (i = 0; i < REPS; i ++)
		ascii_to_radix40_3(temp,test,STRING_LENGTH);
	end = clock();
	printf("Timer ticks for version 3: %d\n",end-start);
	
	radix40_to_ascii(temp,test2,STRING_LENGTH);
	if (stricmp(test2,test) != 0)
		printf("Match 3 failed\n");

	start = clock();
	for (i = 0; i < REPS; i ++)
		ascii_to_radix40_4(temp,test,STRING_LENGTH);
	end = clock();
	printf("Timer ticks for version 4: %d\n",end-start);
	
	radix40_to_ascii_4(temp,test2,STRING_LENGTH);
	if (stricmp(test2,test) != 0)
		printf("Match 4 failed\n");

}

unsigned char legal_chars[] = " ,-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

unsigned weights[3] = {1600, 40, 1};
#define STACK_DATA_SIZE 100

/* updoc ascii_to_radix40 */
int ascii_to_radix40_1(radix40_data,ascii_data,max_chars)
unsigned *radix40_data;
unsigned char *ascii_data;
int max_chars;
/* this routine converts a null-terminated ascii character string */
/* to radix 40 representation.  The allowable characters are: */
/* A-Z, 0-9, period, comma, hyphen, and space.  If any illegal characters */
/* are detected, they will be converted to hyphens, and the return value */
/* will be S_ILLEGAL. */
/* Lowercase letters will be upper-cased without error indication. */
/* The radix40 value will be padded with blanks to a multiple of three */
/* characters. If the number of characters in the ascii string is > max_chars */
/* only max_chars will be converted, and the return value will be S_ILLEGAL. */
/* If no error is detected, the return value will be S_OKAY. */
/* updoc end */
{
	int i;
	int j;
	int ascii_length;
	int result;
	int conversion_status;
	int words_to_convert;
	int words_to_clear;
	int cycle;
	unsigned current_word_index;

	result = 0;
	ascii_length = strlen(ascii_data);
	if (ascii_length > max_chars)
		{
		ascii_length = max_chars;
		result = S_ILLEGAL;
		}

	words_to_convert = ascii_length / 3;
	if (ascii_length % 3 != 0)
		words_to_convert ++;

	words_to_clear = max_chars / 3;
	if (max_chars % 3 != 0)
		words_to_clear ++;

	for (i = 0; i < words_to_clear; i ++)
		radix40_data[i] = 0; /* this blanks out the output string */

	current_word_index = -1;
	cycle = 0;
	for (i = 0; i < ascii_length; i ++)
		{
		if (cycle == 0)
			current_word_index ++;
		conversion_status = 0;
		for (j = 0; j < 40; j ++)
			if (legal_chars[j] == toupper(ascii_data[i]))
				{
				conversion_status = 1;
				break;
				}
			if (conversion_status == 0)
				{
				result = S_ILLEGAL;
				j = HYPHEN; /* code for hyphen */
				}
		radix40_data[current_word_index] += weights[cycle] * j;
		cycle = (cycle + 1) % 3;
		}

	return(result);
}


/* updoc ascii_to_radix40 */
int ascii_to_radix40_2(radix40_data,ascii_data,max_chars)
unsigned *radix40_data;
unsigned char *ascii_data;
int max_chars;
/* this routine converts a null-terminated ascii character string */
/* to radix 40 representation.  The allowable characters are: */
/* A-Z, 0-9, period, comma, hyphen, and space.  If any illegal characters */
/* are detected, they will be converted to hyphens, and the return value */
/* will be S_ILLEGAL. */
/* Lowercase letters will be upper-cased without error indication. */
/* The radix40 value will be padded with blanks to a multiple of three */
/* characters. If the number of characters in the ascii string is > max_chars */
/* only max_chars will be converted, and the return value will be S_ILLEGAL. */
/* If no error is detected, the return value will be S_OKAY. */
/* updoc end */
{
	int i;
	int j;
	int ascii_length;
	int result;
	int conversion_status;
	int words_to_convert;
	int words_to_clear;
	int cycle;
	unsigned current_word_index;
	unsigned char *temp_ascii_data;

	result = 0;
	ascii_length = strlen(ascii_data);
	if (ascii_length > max_chars)
		{
		ascii_length = max_chars;
		result = S_ILLEGAL;
		}

	temp_ascii_data = malloc(ascii_length+1);
	for (i = 0; i < ascii_length+1; i ++)
		temp_ascii_data[i] = toupper(ascii_data[i]);

	words_to_convert = ascii_length / 3;
	if (ascii_length % 3 != 0)
		words_to_convert ++;

	words_to_clear = max_chars / 3;
	if (max_chars % 3 != 0)
		words_to_clear ++;

	for (i = 0; i < words_to_clear; i ++)
		radix40_data[i] = 0; /* this blanks out the output string */

	current_word_index = -1;
	cycle = 0;
	for (i = 0; i < ascii_length; i ++)
		{
		if (cycle == 0)
			current_word_index ++;
		conversion_status = 0;
		for (j = 0; j < 40; j ++)
			if (legal_chars[j] == temp_ascii_data[i])
				{
				conversion_status = 1;
				break;
				}
			if (conversion_status == 0)
				{
				result = S_ILLEGAL;
				j = HYPHEN; /* code for hyphen */
				}
		radix40_data[current_word_index] += weights[cycle] * j;
		cycle = (cycle + 1) % 3;
		}

	free(temp_ascii_data);
	return(result);
}


/* updoc ascii_to_radix40 */
int ascii_to_radix40_3(radix40_data,ascii_data,max_chars)
unsigned *radix40_data;
unsigned char *ascii_data;
int max_chars;
/* this routine converts a null-terminated ascii character string */
/* to radix 40 representation.  The allowable characters are: */
/* A-Z, 0-9, period, comma, hyphen, and space.  If any illegal characters */
/* are detected, they will be converted to hyphens, and the return value */
/* will be S_ILLEGAL. */
/* Lowercase letters will be upper-cased without error indication. */
/* The radix40 value will be padded with blanks to a multiple of three */
/* characters. If the number of characters in the ascii string is > max_chars */
/* only max_chars will be converted, and the return value will be S_ILLEGAL. */
/* If no error is detected, the return value will be S_OKAY. */
/* updoc end */
{
	int i;
	int j;
	int ascii_length;
	int result;
	int conversion_status;
	int words_to_convert;
	int words_to_clear;
	int cycle;
	unsigned current_word_index;
	unsigned char *temp_ascii_data;
	unsigned char stack_ascii_data[STACK_DATA_SIZE];

	result = 0;
	ascii_length = strlen(ascii_data);
	if (ascii_length > max_chars)
		{
		ascii_length = max_chars;
		result = S_ILLEGAL;
		}

	if (ascii_length < STACK_DATA_SIZE)
		temp_ascii_data = stack_ascii_data;
	else
		temp_ascii_data = malloc(ascii_length+1);

	for (i = 0; i < ascii_length+1; i ++)
		temp_ascii_data[i] = toupper(ascii_data[i]);

	words_to_convert = ascii_length / 3;
	if (ascii_length % 3 != 0)
		words_to_convert ++;

	words_to_clear = max_chars / 3;
	if (max_chars % 3 != 0)
		words_to_clear ++;

	for (i = 0; i < words_to_clear; i ++)
		radix40_data[i] = 0; /* this blanks out the output string */

	current_word_index = -1;
	cycle = 0;
	for (i = 0; i < ascii_length; i ++)
		{
		if (cycle == 0)
			current_word_index ++;
		conversion_status = 0;
		for (j = 0; j < 40; j ++)
			if (legal_chars[j] == temp_ascii_data[i])
				{
				conversion_status = 1;
				break;
				}
			if (conversion_status == 0)
				{
				result = S_ILLEGAL;
				j = HYPHEN; /* code for hyphen */
				}
		radix40_data[current_word_index] += weights[cycle] * j;
		cycle = (cycle + 1) % 3;
		}

	if (ascii_length >= STACK_DATA_SIZE)
		free(temp_ascii_data);

	return(result);
}


#define IL 0x82
 /* this is the code for a -, but with the illegal flag set */

char lookup_chars[256] = 
{IL, IL, IL, IL, IL, IL, IL, IL,/* 00 */
IL, IL, IL, IL, IL, IL, IL, IL, /* 08 */
IL, IL, IL, IL, IL, IL, IL, IL, /* 10 */
IL, IL, IL, IL, IL, IL, IL, IL, /* 18 */
 0, IL, IL, IL, IL, IL, IL, IL, /* 20 */
IL, IL, IL, IL,  1,  2,  3, IL, /* 28 */
 4,  5,  6,  7,  8,  9, 10, 11, /* 30 */
12, 13 ,IL, IL, IL, IL, IL, IL, /* 38 */
IL, 14, 15, 16, 17, 18, 19, 20, /* 40 */
21, 22, 23, 24, 25, 26, 27, 28, /* 48 */
29, 30, 31, 32, 33, 34, 35, 36, /* 50 */
37, 38, 39, IL, IL, IL, IL, IL, /* 58 */
IL, 14, 15, 16, 17, 18, 19, 20, /* 60 */
21, 22, 23, 24, 25, 26, 27, 28, /* 68 */
29, 30, 31, 32, 33, 34, 35, 36, /* 70 */
37, 38, 39, IL, IL, IL, IL, IL, /* 78 */
IL, IL, IL, IL, IL, IL, IL, IL, /* 80 */
IL, IL, IL, IL, IL, IL, IL, IL, /* 88 */
IL, IL, IL, IL, IL, IL, IL, IL, /* 90 */
IL, IL, IL, IL, IL, IL, IL, IL, /* 98 */
IL, IL, IL, IL, IL, IL, IL, IL, /* A0 */
IL, IL, IL, IL, IL, IL, IL, IL, /* A8 */
IL, IL, IL, IL, IL, IL, IL, IL, /* B0 */
IL, IL, IL, IL, IL, IL, IL, IL, /* B8 */
IL, IL, IL, IL, IL, IL, IL, IL, /* C0 */
IL, IL, IL, IL, IL, IL, IL, IL, /* C8 */
IL, IL, IL, IL, IL, IL, IL, IL, /* D0 */
IL, IL, IL, IL, IL, IL, IL, IL, /* D8 */
IL, IL, IL, IL, IL, IL, IL, IL, /* E0 */
IL, IL, IL, IL, IL, IL, IL, IL, /* E8 */
IL, IL, IL, IL, IL, IL, IL, IL, /* F0 */
IL, IL, IL, IL, IL, IL, IL, IL};/* F8 */

/* updoc ascii_to_radix40 */
int ascii_to_radix40_4(radix40_data,ascii_data,max_chars)
unsigned *radix40_data;
unsigned char *ascii_data;
int max_chars;
/* this routine converts a null-terminated ascii character string */
/* to radix 40 representation.  The allowable characters are: */
/* A-Z, 0-9, period, comma, hyphen, and space.  If any illegal characters */
/* are detected, they will be converted to hyphens, and the return value */
/* will be S_ILLEGAL. */
/* Lowercase letters will be upper-cased without error indication. */
/* The radix40 value will be padded with blanks to a multiple of three */
/* characters. If the number of characters in the ascii string is > max_chars */
/* only max_chars will be converted, and the return value will be S_ILLEGAL. */
/* If no error is detected, the return value will be S_OKAY. */
/* updoc end */
{
	int i;
	int j;
	int ascii_length;
	int result;
	int words_to_convert;
	int words_to_clear;
	int cycle;
	unsigned current_word_index;

	result = S_OKAY;
	ascii_length = strlen(ascii_data);
	if (ascii_length > max_chars)
		{
		ascii_length = max_chars;
		result = S_ILLEGAL;
		}

	words_to_convert = ascii_length / 3;
	if (ascii_length % 3 != 0)
		words_to_convert ++;

	words_to_clear = max_chars / 3;
	if (max_chars % 3 != 0)
		words_to_clear ++;

	memset(radix40_data,0,words_to_clear*sizeof(Radix40));

	current_word_index = -1;
	cycle = 0;
	for (i = 0; i < ascii_length; i ++)
		{
		if (cycle == 0)
			current_word_index ++;
		j = lookup_chars[ascii_data[i]];
		if (j & 0x80)
			{
			j = j & 0x7f;
			result = S_ILLEGAL;
			}
		radix40_data[current_word_index] += weights[cycle] * j;
		cycle = (cycle + 1) % 3;
		}

	return(result);
}


/* updoc radix40_to_ascii */
int radix40_to_ascii(radix40_data,ascii_data,max_chars)
unsigned *radix40_data;
unsigned char *ascii_data;
int max_chars;
/* this routine converts a radix 40 character string */
/* to ascii representation.  Trailing blanks will be deleted. */
/* updoc end */
{
	int i;
	int j;
	int ascii_length;
	int new_ascii_length;
	int words_to_convert;
	int cycle;
	unsigned current_word_index;
	unsigned current_word;
	unsigned current_char;

	ascii_length = max_chars;

	words_to_convert = ascii_length / 3;
	if (ascii_length % 3 != 0)
		words_to_convert ++;

	for (i = 0; i < max_chars + 1; i ++)
		ascii_data[i] = 0; /* this nulls out the output string */

	current_word_index = -1;
	cycle = 0;
	for (i = 0; i < ascii_length; i ++)
		{
		if (cycle == 0)
			{
			current_word_index ++;
			current_word = radix40_data[current_word_index];
			}
		current_char = current_word / weights[cycle];
		current_word -= current_char * weights[cycle];
		
		ascii_data[i] = legal_chars[current_char];
		cycle = (cycle + 1) % 3;
		}

	new_ascii_length = strlen(ascii_data);
	for (i = new_ascii_length - 1; i >= 0; i --)
		{
		if (ascii_data[i] != ' ')
			break;
		ascii_data[i] = 0;
		}

	return(S_OKAY);
}



/* updoc radix40_to_ascii */
int radix40_to_ascii_4(radix40_data,ascii_data,max_chars)
unsigned *radix40_data;
unsigned char *ascii_data;
int max_chars;
/* this routine converts a radix 40 character string */
/* to ascii representation.  Trailing blanks will be deleted. */
/* updoc end */
{
	int i;
	int j;
	int ascii_length;
	int new_ascii_length;
	int words_to_convert;
	int cycle;
	unsigned current_word_index;
	unsigned current_word;
	unsigned current_char;

	ascii_length = max_chars;

	words_to_convert = ascii_length / 3;
	if (ascii_length % 3 != 0)
		words_to_convert ++;

	for (i = 0; i < max_chars + 1; i ++)
		ascii_data[i] = 0; /* this nulls out the output string */

	current_word_index = -1;
	cycle = 0;
	for (i = 0; i < ascii_length; i ++)
		{
		if (cycle == 0)
			{
			current_word_index ++;
			current_word = radix40_data[current_word_index];
			}
		current_char = current_word / weights[cycle];
		current_word -= current_char * weights[cycle];
		
		ascii_data[i] = legal_chars[current_char];
		cycle = (cycle + 1) % 3;
		}

	new_ascii_length = strlen(ascii_data);
	for (i = new_ascii_length - 1; i >= 0; i --)
		{
		if (ascii_data[i] != ' ')
			break;
		ascii_data[i] = 0;
		}

	return(S_OKAY);
}

