/***************************************************************
 * CSc230, Assignment 1 Part 2, May 2011
 *
 * Author: Chris jarrett
 * Student Number: V00763060
 *
 * This program reads performs arithmetic operations
 * (?, +, - and *)on integers  in an arbitrary base number 
 * system. (Base 2 to 36).
 ***************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

/* the maximum number of digits in an arbitrary base number */
#define MAXDIGITS 16

/* global variables */
int g_base = 10;
FILE *g_inputFile;

/* a data structure used for arbitrary base numbers */
typedef struct {
	char	negative;
	char	digit[MAXDIGITS];
} NumberType;

/* function prototypes */
NumberType add( NumberType arg1, NumberType arg2 );
NumberType subtract( NumberType arg1, NumberType arg2 );
NumberType multiply( NumberType arg1, NumberType arg2 );

/* Note: isspace(x) is a macro defined in the header file "ctype.h".
   If you had to code it yourself as a C function, it would be something like this:

	int isspace( char c ) {
		return c == ' ' || c == '\t' || c == '\r' || c == '\n':
	}
*/

/***************************************************************
 * Function: printNumber( NumberType number )
 *
 * Inputs:
 *	   number: a number in an arbitrary base
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: None
 *
 * Description:
 *  The function outputs the number supplied as an argument, 
 *  printing it to the standard output. Redundant leading 
 *  zeros are suppressed when printing.
 *  Each digit is checked that it is on the correct range for the
 *  current number system base before being printed.
 ***************************************************************/
void printNumber( NumberType number ) {
	int i;
	/* skip over leading zeros in the number */
    // STEP 2: Add code to print a number.
	int nonzero = 0;
	
	for(i=0; i < MAXDIGITS; i++) {
		if (number.digit[i] == 0 && nonzero == 0) {
			continue;
		}
		if(nonzero==0) {
			if(number.negative == 1) {
				printf("-");
			}
		}
		printf("%d", number.digit[i]);
		nonzero=1;
	}
}

/***************************************************************
 * Temp Function: randomNumber()
 *
 * Inputs: None
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: a value of type NumberType representing an arbitrary
 *         base number which has been read from an input file.
 *
 * Description:
 *  Delete this function before you hand in your code. It simply
 *  returns a randomly generated number in teh proper format.
 ***************************************************************/
NumberType randomNumber() {
	int c, i, numDigits, digitVal;
	NumberType result;
	char digitSequence[MAXDIGITS];

	/* initialize the storage for the result */
	result.negative = 0;
	for( i = 0; i < MAXDIGITS; i++ ) {
		result.digit[i] = 0;
	}

	if((rand()% 2) == 0)
		result.negative = 1;

	for( i = 8; i < MAXDIGITS; i++ ) {
			result.digit[i] = (rand() % g_base);
	}

	return result;
}
/***************************************************************
 * Function: readNumber()
 *
 * Inputs: None
 *
 * Global variables used:
 *  g_base: the number system base
 *  g_inputFile -- an open input stream from which input is read.
 *
 * Output: a value of type NumberType representing an arbitrary
 *         base number which has been read from an input file.
 *
 * Description:
 * This procedure compares the value of two numbers in base 
 * g_base
 *
 * Note:
 *  Skips over white space characters and then inputs a number 
 *  which has the lexical structure
 *      ['+' | '-'] digit+
 *  where digit is a character from the set
 *      {'0'-'9','A'-'Z','a'-'z'}
 *  to represent a digit in the given base.
 ***************************************************************/

NumberType readNumber( ) {
	// Step 12: (After lab 3). Get input from file rather than these random values.
	return randomNumber();
}

/***************************************************************
 * Procedure: compareMagnitude(NumberType arg1, NumberType arg2 )
 *
 * Inputs:
 *     arg1: an arbitrary base number
 *     arg2: a second arbitrary base number
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: an integer which is:
 * 		negative if |arg1| < |arg2|,
 *     	zero if |arg1| = |arg2|,
 *		or positive if |arg1| > |arg2|.
 *
 * Description:
 * This procedure compares the absolute value of two numbers in
 * base g_base
 *
 ***************************************************************/

int compareMagnitude( NumberType arg1, NumberType arg2 ) {
	int i, difference;
	for(i=0; i < MAXDIGITS; i++) {
		if(arg1.digit[i] > arg2.digit[i]) {
			return 1;
		}
		if (arg1.digit[i] < arg2.digit[i]) {
			return -1;
		}
		/*difference = arg1.digit[i] - arg2.digit[i];
		if(difference > 0) {
			return 1;
		} else if (difference < 0) {
			return -1;
		}*/
	}
	return 0;
}

/***************************************************************
 * Procedure: compare( NumberType arg1, NumberType arg2 )
 *
 * Inputs:
 *     arg1: an arbitrary base number
 *     arg2: a second arbitrary base number
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: an integer which is:
 * 		negative if arg1 < arg2,
 *     	zero if arg1 = arg2,
 *		or positive if arg1 > arg2.
 *
 * Description:
 * This procedure compares the value of two numbers in base
 * g_base
 ***************************************************************/
int compare( NumberType arg1, NumberType arg2 ) {
	// STEP 4: Add code to compare 2 numbers.
	// Hint: If both numbers are postive compareMagnitude
 	//       If both numbers are negitive compareMagnitude (remember -4>-7)
	//       If one is positive and the other is negitive then the postive 
	//            arguemtn is greater except -0 = 0.
	if(arg1.negative == arg2.negative) {
		if(arg1.negative == 1) {
			return compareMagnitude(arg2, arg1);
		}
		return compareMagnitude(arg1, arg2);
	}
	
	if(compareMagnitude(arg1, arg2) == 0) {
		return 0;
	}
	if(arg1.negative == 1) {
		return -1;
	}
	
	return 1;
}

/***************************************************************
 * Procedure: NumberType add( NumberType arg1, NumberType arg2 )
 *
 * Inputs:
 *     arg1: an arbitrary base number
 *     arg2: a second arbitrary base number
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: an arbitrary base number equal to arg1+arg2.
 *
 * Description:
 * This procedure adds arg1 and arg2 in base g_base
 *
 * Notes:
 *   It is assumed that the two arguments are valid (i.e.
 *   that all digits are in the range 0 to g_base-1 in value).
 *   If an overflow occurs (i.e. the result does not fit into the
 *   array of digits provided), an error message is printed and
 *   the program halts.
 ***************************************************************/

NumberType add( NumberType arg1, NumberType arg2 ) {
	NumberType result;
	int i, carry, sum;

	/* if the arguments have opposite signs, use the subtract function */
	// STEP 5: Add code to compare the signs of 2 numbers.
	if(arg1.negative != arg2.negative) {
		if(arg1.negative == 0) {
			arg2.negative = 0;
			return subtract(arg1, arg2);
		}
		arg1.negative = 0;
		return subtract(arg2, arg1);
	}


	/* to get here, both arguments must have the same sign */
	// STEP 6: add two numbers
	result.negative = arg1.negative;
	
	carry = 0;
	for(i=MAXDIGITS - 1; i >= 0; i=i-1) {

		sum = arg1.digit[i] + arg2.digit[i] + carry;
		if(sum >= g_base) {
			if(i == 0) {
				fprintf(stderr, "Overflow error.");
				result.negative = 0;
				int j;
				for(j = 0; j < MAXDIGITS; j++ ) {
					result.digit[j] = 0;
				}
				return result;
			}
			carry = 1;
			sum = sum - g_base;
		} else {
			carry = 0;
		}
		result.digit[i] = sum;
	}

	return result;
}


/***************************************************************
 * NumberType subtract( NumberType arg1, NumberType arg2 )
 *
 * Inputs:
 *     arg1: an arbitrary base number
 *     arg2: a second arbitrary base number
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: an arbitrary base number equal to arg1-arg2.
 *
 * Description:
 * This procedure subtracts arg2 from arg1 in base g_base
 *
 * Notes:
 *   It is assumed that the two arguments are valid (i.e.
 *   that all digits are in the range 0 to g_base-1 in value).
 *   If an overflow occurs (i.e. the result does not fit into the
 *   array of digits provided), an error message is printed and
 *   the program halts.
 ***************************************************************/

NumberType subtract( NumberType arg1, NumberType arg2 ) {
	NumberType result;
	int i, borrow, diff;

	/* if the arguments have opposite signs, use the add function */
	// STEP 7: Add code to compare the signs of 2 numbers.
	if(arg1.negative == 0 && arg2.negative == 1) {
		arg2.negative = 0;
		result = add(arg1, arg2);
		
		return result;
	}
	
	if(arg1.negative == 1 && arg2.negative == 0) {
		arg2.negative = 1;
		result = add(arg1, arg2);
		
		return result;
	}
    /* to get here, both arguments have the same sign; now we
       want to subtract the smaller in magnitude from the larger
       and give the result the appropriate sign.
    */
	// STEP 8: subtract the smaller in magnitude from the larger

	return randomNumber();
}

/***************************************************************
 * Procedure: NumberType multiplyByDigit(NumberType arg1, int d )
 *
 * Inputs:
 *     arg1: an arbitrary base number
 *     d:    a small integer in the range 0 to g_base-1
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: an arbitrary base number equal to arg1*d
 *
 * Description:
 * This procedure multiplies arg1 by d
 ***************************************************************/
NumberType multiplyByDigit( NumberType arg1, int d ) {
	NumberType result;
	int i, carry;

	// STEP 10: multiply the number by a single digit number
	return randomNumber();
}

/***************************************************************
 * Procedure: NumberType shiftLeft( NumberType arg1 )
 *
 * Inputs:
 *     arg1: an arbitrary base number
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: an arbitrary base number equal to arg1*g_base.
 *
 * Description:
 * This procedure multiplies arg1 and g_base
 ***************************************************************/

NumberType shiftLeft( NumberType arg1 ) {
	NumberType result;
	int i;

	// STEP 9: shift all the digits of a number left.
	return randomNumber();
}

/***************************************************************
 * Procedure: NumberType multiply( NumberType arg1, 
 *                                 NumberType arg2 )
 *
 * Inputs:
 *     arg1: an arbitrary base number
 *     arg2: a second arbitrary base number
 *
 * Global variables used:
 *     g_base: the number system base
 *
 * Output: an arbitrary base number equal to arg1*arg2.
 *
 * Description:
 * This procedure multiplies arg1 and arg2 in base g_base
 *
 * Notes:
 *   It is assumed that the two arguments are valid (i.e.
 *   that all digits are in the range 0 to g_base-1 in value).
 *   If an overflow occurs (i.e. the result does not fit into the
 *   array of digits provided), an error message is printed and
 *   the program halts.
 ***************************************************************/

NumberType multiply( NumberType arg1, NumberType arg2 ) {
	NumberType result;
	int i, d;

    // STEP 11: multiply the two numbers
    /* clear result digits to zero */
    /* sign of result is negative if arg1 & arg2 have different signs */
    /* force arg1 to same sign as result so that calls to the add function
       inside the loop do not become subtractions */

	return randomNumber();
}

/***************************************************************
 * Procedure: processInput()
 *
 * Inputs: 	None
 *
 * Global variables used:
 *     g_inputfile: the stream used as the source of input
 *     g_base: the number system base
 *
 * Output: None
 *
 * Description:
 *  This function repeatedly reads input from inputfile, decodes
 *  that input as commands to set the number system base or to 
 *  perform arithmetic operations and interprets those commands, 
 *  outputting the results to the standard output.
 ***************************************************************/

void processInput() {
	NumberType arg1, arg2, result;

	// Step 12: (After lab 3). Get input from file rather than these random values.

	// Set up random number generator for random input. Remove!!
	long ltime;
	int stime;
	ltime = time(NULL);   stime = (unsigned) ltime/2;   srand(stime);

	g_base = 10;
	printf("     base = %d\n", g_base);
	arg1 = readNumber( );
	arg2 = readNumber( );

	printNumber(arg1);
	if(compare( arg1, arg2 ) < 0)
		printf("  <  ");
	if(compare( arg1, arg2 ) == 0)
		printf("  =  ");
	if(compare( arg1, arg2 ) > 0)
		printf("  >  ");
	printNumber(arg2); printf("\n");

	printf("|"); printNumber(arg1);printf("|");
	if(compareMagnitude( arg1, arg2 ) < 0)
		printf("  <  ");
	if(compareMagnitude( arg1, arg2 ) == 0)
		printf("  =  ");
	if(compareMagnitude( arg1, arg2 ) > 0)
		printf("  >  ");
	printf("|");printNumber(arg2); printf("|");printf("\n");


	printNumber(arg1); printf("  +  ");  printNumber(arg2); printf("  =  "); printNumber(add(arg1,arg2)); printf("\n");
	printNumber(arg1); printf("  -  ");  printNumber(arg2); printf("  =  "); printNumber(subtract(arg1,arg2)); printf("\n");
	printNumber(arg1); printf("  *  ");  printNumber(arg2); printf("  =  "); printNumber(multiply(arg1,arg2)); printf("\n");
}


/***************************************************************
 * Procedure: main( int argc, char *argv[] )
 *
 * Inputs: 	argc - The number of arguments in argv
 *         	     - If argc == 1, the only arguement is 
 *                          the executable name so there is no
 *                          input file: use stdin.
 * 			argv -arguments are passed by the operating 
 *                    system
 *     			  (see a Unix manual for their meaning.)
 *			 - If argc == 1, the second arguement (argv[1])
 *                 is the input file
 *
 * Output: 0 indicates successful completion to the operating 
 *         system.
 *
 * Description:
 *   Entry point for the program.
 ***************************************************************/

 int main( int argc, char *argv[] ) {
	// Step 1: update the welcome message:
	printf("CSc230 Assignment 1 Part 2. Chris Jarrett (V00763060).\n");
	if (argc == 1) {
		g_inputFile = stdin;
	} else if (argc == 2) {
		g_inputFile = fopen(argv[1], "r");
		if (g_inputFile == NULL) {
			fprintf(stderr, "*** unable to open file %s for input\n", argv[1]);
			exit(1);
		}
	} else {
		fprintf(stderr, "*** program can be invoked with at most one argument\n");
		exit(1);
	}
	processInput();
	return 0;
}

