/*******************************************************************/
/* frequency                                                       */
/*                                                                 */
/* Prints cipher and US English alphabet frequencies.              */
/* Prints common cipher text letter pairs (e.g. "kk")              */
/* Prints common digrams, trigrams, quadgrams, pentagrams,         */
/*        and hexagrams (e.g. "jp", "jph", and "mpcn")             */
/*                                                                 */
/* Use this tool for initial cipher exploration.  There are tools  */
/*   on Dr. Stephens' website that are much better at interactive  */
/*   substitution.  But if you must, tweak ReplaceCipherText()     */
/*   and recompile. Original cipher text is displayed in           */
/*   lowercase, substituted plain text is displayed in uppercase.  */
/*   Be sure that associated .h file is called 'frequency.h'       */
/* Compile:  "cc frequency.c -o freq"                              */
/*                                                                 */
/* Execute:  freq file                                             */
/*                                                                 */
/* Runs under 32 bit Windows and Irix                              */
/*   If you run and compile under Microsoft Windows, "WIN32" is    */
/*   defined for you by default.  It is the same as the Irix       */
/*   compiler defining "unix" and "host_mips" under Irix.          */
/*******************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "frequency.h"

FILE* instream = NULL;
FILE* outstream = NULL;

#define MESSAGE_LEN 1000

/*******************************************************************/
/*******************************************************************/
int main(int argc, char* argv[]) {
FREQTABLE cipher;
FREQTABLE plain;
char message[MESSAGE_LEN];     /* size it as required */
int character = 0;
int char_count = 0;
fpos_t pos;

    /* Check the number of program arguments. */
    if ( argc != 3 && argc != 2 ) {
	    fprintf(stderr, "Usage: %s <input file> [output file]\n", argv[0]);
	    fprintf(stderr, "  output file is optional.  If not specified,\n");
	    fprintf(stderr, "  output is sent to stdout.  If specified, output\n");
	    fprintf(stderr, "  is sent to output file\n");
	    exit(-1);
    }
  
    /* Open the message file. */
    if ((instream = fopen(argv[1],"r")) == NULL ) {
	    fprintf(stderr, "Message file %s does not exist.\n", argv[1]);
	    exit(-1);
    }

    /* Test MSGLEN size */
    fseek(instream, 0L, SEEK_END);
    fgetpos(instream, &pos);
    if(MESSAGE_LEN < pos) {
	    fprintf(stderr, "Input file is too long.\n");
        fprintf(stderr, "Increase value of MESSAGE_LEN and recompile.\n");
        fprintf(stderr, "MESSAGE_LEN is %d.\n", MESSAGE_LEN);
        fprintf(stderr, "File size is %ld.\n", (unsigned long)pos);
        fprintf(stderr, "MESSAGE_LEN is defined in the *.c file.\n");
        exit(-1);
    }
    fseek(instream, 0L, SEEK_SET);    
  
    if(3 == argc) {
        /* Open the output file. */
        outstream = fopen(argv[2],"w");
        if(NULL == outstream) {
            fprintf(stderr, "Unable to open output file %s\n", argv[2]);
            fclose(instream);
            exit(-1);
        }
    } else {
        outstream = stdout;
    }
    /* boring */
    InitializeCipherTable(&cipher);
    InitializePlainTable(&plain);
    memset(message, 0, MESSAGE_LEN * sizeof(char));

    /* read file */
    while( EOF != (character = fgetc(instream)) ) {
        character = tolower(character);

        message[char_count++] = character;

        if(isalpha(character)) {
            character -= 'a';
            cipher.table[character].count++;
            cipher.total++;
        }
    }

    fclose(instream);

    fprintf(outstream, "File: %s (%d bytes)\n\n", argv[1], strlen(message));

    CommonPairs(message);
    CommonDigrams(message);
    CommonTrigrams(message);
    CommonQuadgrams(message);
    CommonPentagrams(message);
    CommonHexagrams(message);

    /* this function does nothing if */
    /* the array in the function is  */
    /* not populated                 */
    ReplaceCipherText(&cipher);

    DoSomeMath(&cipher);
    SortTable(&cipher);
    SortTable(&plain);

    PrintTables(&cipher, &plain);

    PrintCipherText(&cipher, message);

    PrintUnusedLetters(&cipher);

    return(0);
}
/*******************************************************************/
/*******************************************************************/
/**  Replace here as desired.  There are other tools on the       **/
/**    Dr. Stephens' website that do this better.                 **/
/*******************************************************************/
/*******************************************************************/
int ReplaceCipherText(FREQTABLE* t) {
int count = 0;
int looper = 0;
REPLACE character = { 0,0 };
REPLACE r[] = { 
             /* Ciper -> Plain */

                /* 'A', 'A', */
                /* 'B', 'B', */
                /* 'C', 'C', */
                /* 'D', 'D', */
                /* 'E', 'E', */

                '\0', '\0'  /* must be terminated by double nulls */
              };

    character.cipher_letter = r[count].cipher_letter;
    character.replace_letter = r[count].replace_letter;
    count++;

    while(character.cipher_letter) {
        
        for(looper=0; looper<ALPHABET; looper++) {
            if(t->table[looper].letter == character.cipher_letter) {
                t->table[looper].replacement = character.replace_letter;
                break;
            }
        }

        character.cipher_letter = r[count].cipher_letter;
        character.replace_letter = r[count].replace_letter;
        count++;
    }

    return(TRUE);
}

/*******************************************************************/
int PrintCipherText(FREQTABLE* t, char* message) {
int  looper = 0;
int  count = 0;
char c;

    fprintf(outstream, "\nMessage Below:\n\n");
    for(looper=0; looper<(signed)strlen(message); looper++) {
        c = message[looper];

        if( (!isalpha(c)) || (isspace(c)) ) { 
            fprintf(outstream, "%c", c);
            continue;
        }

        for(count=0; count<ALPHABET; count++) {
            if(t->table[count].letter == toupper(c)) {
                if(t->table[count].replacement) {
                    fprintf(outstream, "%c", toupper(t->table[count].replacement));
                } else {
                    fprintf(outstream, "%c", c);
                }
                break;
            }
        }
    }

    return(TRUE);
}

/*******************************************************************/
int InitializeCipherTable(FREQTABLE* t) {
int looper = 0;

    memset(t, 0, sizeof(FREQTABLE) );

    for( looper = 0 ; looper < ALPHABET; looper++) {
        t->table[looper].letter = 'A' + looper;
        t->table[looper].frequency = 0.0F;
    }

    return(TRUE);
}

/*******************************************************************/
int InitializePlainTable(FREQTABLE* t) {
int   looper = 0;

#ifdef WIN32                      /* Micro$oft Specific                     */
  #pragma warning(disable: 4305)  /* double to float truncation warning off */
#endif

float frequencies[] = { 8.2F, 1.5F, 2.8F, 4.3F, 12.7F,
                        2.2F, 2.0F, 6.1F, 7.0F,  0.2F,
                        0.8F, 4.0F, 2.4F, 6.7F,  7.5F,
                        1.9F, 0.1F, 6.0F, 6.3F,  9.1F,
                        2.8F, 1.0F, 2.4F, 0.2F,  2.0F,
                        0.1F };
#ifdef WIN32                      /* Micro$oft Specific                     */
  #pragma warning(default: 4305)  /* double to float truncation warning on */
#endif

    memset(t, 0, sizeof(FREQTABLE) );

    for(looper = 0 ; looper < ALPHABET; looper++) {
        t->table[looper].letter = 'A' + looper;
        t->table[looper].frequency = frequencies[looper];
        t->table[looper].count = (int)(frequencies[looper]*200);
    }

    return(TRUE);
}

/*******************************************************************/
int PrintTables(FREQTABLE* c, FREQTABLE* p) {
int looper = 0;

    fprintf(outstream, "Plain\t\t\t  Cipher\n");
    for(looper=0; looper<ALPHABET; looper++) {
        fprintf(outstream,"%c(%5.2f)\t\t  %c(%7.4f)\n", p->table[looper].letter,
                                                     p->table[looper].frequency,
                                                     c->table[looper].letter,
                                                     c->table[looper].frequency);
    }
    return(0);
}

/*******************************************************************/
int SortTable(FREQTABLE* t) {

    qsort(t->table, ALPHABET, sizeof(LETTER), CompareFreq);

    return(TRUE);
}

/*******************************************************************/
int CompareFreq(const void* one, const void* two) {
int A = 0;
int B = 0;

    A = ((LETTER*)one)->count;
    B = ((LETTER*)two)->count;

    if( A > B ) {
        return(-1);
    } else if( A < B ) {
        return(1);
    }

    A = (char) ((LETTER*)one)->letter;
    B = (char) ((LETTER*)two)->letter;
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }
    
    return(0);
}

/*******************************************************************/
int DoSomeMath(FREQTABLE* t) {
int looper = 0;
int total = t->total;

    for(looper=0; looper<ALPHABET; looper++) {
        t->table[looper].frequency = (float) t->table[looper].count/total * 100;
    }

    return(TRUE);
}

/*******************************************************************/
int PrintUnusedLetters(FREQTABLE* t) {
int looper = 0;

    fprintf(outstream, "\nUnused Cipher Letters\n");
    for(looper=0; looper<ALPHABET; looper++) {
        if(t->table[looper].replacement == 0) {
            if( (ALPHABET-1) != looper) {
                fprintf(outstream, "%c, ", t->table [looper].letter);
            } else {
                fprintf(outstream, "%c\n", t->table [looper].letter);
            }
        }
    }
    fprintf(outstream, "\n");
    return(TRUE);
}

/*******************************************************************/
int CommonDigrams(char* message) {
DIGRMTABLE table;
int count = 0;
int looper = 0;
int found = FALSE;

    memset(&table, 0, sizeof(DIGRMTABLE));

    while(message[count+1] != '\0') {
        
        if(! isalpha(message[count])) {
            count++;
            continue;
        }
        
        if(! isalpha(message[count+1])) {
            count++;
            continue;
        }

        found = FALSE;

        for(looper=0; looper<table.total; looper++) {
            if(table.pairs[looper].digram[0] == message[count] &&
               table.pairs[looper].digram[1] == message[count + 1]) {

                table.pairs[looper].count++;
                found = TRUE;
                break;
            }
        }
        if(FALSE == found) {
            table.pairs[table.total].digram[0] = message[count];
            table.pairs[table.total].digram[1] = message[count + 1];
            table.pairs[table.total].count++;
            table.total++;
        }

        count++;
    }

    qsort(table.pairs, table.total, sizeof(DIGRAM), CompareDigrams);

    fprintf(outstream, "Common Diagram Counts\n");
    for(count=0; count<15; count++) {
        fprintf(outstream, "%c%c (%d)\n", table.pairs[count].digram[0],table.pairs[count].digram[1], table.pairs[count].count);
    }
    fprintf(outstream, "\n");
    return(TRUE);
}

/*******************************************************************/
int CompareDigrams(const void* one, const void* two) {
int A;
int B;

    A = ((DIGRAM*)one)->count;
    B = ((DIGRAM*)two)->count;

    if(A < B) {
        return(1);
    } else if (A>B) {
        return(-1);
    }

    A = (char)((DIGRAM*)one)->digram[0];
    B = (char)((DIGRAM*)two)->digram[0];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }
    
    A = (char)((DIGRAM*)one)->digram[1];
    B = (char)((DIGRAM*)two)->digram[1];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    /* They are equal */
    return(0);
}

/*******************************************************************/
int CommonTrigrams(char* message) {
TRIGRMTABLE table;
int count = 0;
int looper = 0;
int found = FALSE;

    memset(&table, 0, sizeof(TRIGRMTABLE));

    while(message[count] != '\0' && message[count+1] != '\0' &&
          message[count+2]) {
        
        if(! isalpha(message[count])) {
            count++;
            continue;
        }
        
        if(! isalpha(message[count+1])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+2])) {
            count++;
            continue;
        }

        found = FALSE;

        for(looper=0; looper<table.total; looper++) {
            if(table.triplets[looper].trigram[0] == message[count] &&
               table.triplets[looper].trigram[1] == message[count + 1] &&
               table.triplets[looper].trigram[2] == message[count + 2]) {

                table.triplets[looper].count++;
                found = TRUE;
                break;
            }
        }
        if(FALSE == found) {
            table.triplets[table.total].trigram[0] = message[count];
            table.triplets[table.total].trigram[1] = message[count + 1];
            table.triplets[table.total].trigram[2] = message[count + 2];
            table.triplets[table.total].count++;
            table.total++;
        }

        count++;
    }

    qsort(table.triplets, table.total, sizeof(TRIGRAM), CompareTrigrams);

    fprintf(outstream, "Common Trigram Counts\n");
    for(count=0; count<15; count++) {
        /* No need to look at a single occurence - its a stream of characters */
        if(table.triplets[count].count == 1) { break; }
        fprintf(outstream, "%c%c%c (%d)\n", table.triplets[count].trigram[0],
                                         table.triplets[count].trigram[1],
                                         table.triplets[count].trigram[2],
                                         table.triplets[count].count);
    }
    fprintf(outstream, "\n");
    return(TRUE);
}

/*******************************************************************/
int CompareTrigrams(const void* one, const void* two) {
int A;
int B;

    A = ((TRIGRAM*)one)->count;
    B = ((TRIGRAM*)two)->count;

    if(A < B) {
        return(1);
    } else if (A>B) {
        return(-1);
    }

    A = (char)((TRIGRAM*)one)->trigram[0];
    B = (char)((TRIGRAM*)two)->trigram[0];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }
    
    A = (char)((TRIGRAM*)one)->trigram[1];
    B = (char)((TRIGRAM*)two)->trigram[1];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((TRIGRAM*)one)->trigram[2];
    B = (char)((TRIGRAM*)two)->trigram[2];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    return(0);
}

/*******************************************************************/
int CommonQuadgrams(char* message) {
QUADGRMTABLE table;
int count = 0;
int looper = 0;
int found = FALSE;

    memset(&table, 0, sizeof(QUADGRMTABLE));

    while(message[count] != '\0' && message[count+1] != '\0' &&
          message[count+2] != '\0' && message[count+3] != '\0') {
        
        if(! isalpha(message[count])) {
            count++;
            continue;
        }
        
        if(! isalpha(message[count+1])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+2])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+3])) {
            count++;
            continue;
        }

        found = FALSE;

        for(looper=0; looper<table.total; looper++) {
            if(table.quads[looper].quadgram[0] == message[count] &&
               table.quads[looper].quadgram[1] == message[count + 1] &&
               table.quads[looper].quadgram[2] == message[count + 2] &&
               table.quads[looper].quadgram[3] == message[count + 3]) {

                table.quads[looper].count++;
                found = TRUE;
                break;
            }
        }
        if(FALSE == found) {
            table.quads[table.total].quadgram[0] = message[count];
            table.quads[table.total].quadgram[1] = message[count + 1];
            table.quads[table.total].quadgram[2] = message[count + 2];
            table.quads[table.total].quadgram[3] = message[count + 3];
            table.quads[table.total].count++;
            table.total++;
        }

        count++;
    }

    qsort(table.quads, table.total, sizeof(QUADGRAM), CompareQuadgrams);

    fprintf(outstream, "Common Quadgram Counts\n");
    for(count=0; count<15; count++) {
        /* No need to look at a single occurence - its a stream of characters */
        if(table.quads[count].count == 1) { break; }
        fprintf(outstream, "%c%c%c%c (%d)\n", table.quads[count].quadgram[0],
                                           table.quads[count].quadgram[1],
                                           table.quads[count].quadgram[2],
                                           table.quads[count].quadgram[3],
                                           table.quads[count].count);
    }
    fprintf(outstream, "\n");
    return(TRUE);
}

/*******************************************************************/
int CompareQuadgrams(const void* one, const void* two) {
int A;
int B;

    A = ((QUADGRAM*)one)->count;
    B = ((QUADGRAM*)two)->count;

    if(A < B) {
        return(1);
    } else if (A>B) {
        return(-1);
    }

    A = (char)((QUADGRAM*)one)->quadgram[0];
    B = (char)((QUADGRAM*)two)->quadgram[0];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }
    
    A = (char)((QUADGRAM*)one)->quadgram[1];
    B = (char)((QUADGRAM*)two)->quadgram[1];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((QUADGRAM*)one)->quadgram[2];
    B = (char)((QUADGRAM*)two)->quadgram[2];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }
    A = (char)((QUADGRAM*)one)->quadgram[3];
    B = (char)((QUADGRAM*)two)->quadgram[3];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    /* They are equal */
    return(0); 
}

/*******************************************************************/
int CommonPairs(char* message) {
DIGRMTABLE table;
int count = 0;
int looper = 0;
int found = FALSE;

    memset(&table, 0, sizeof(DIGRMTABLE));

    while(message[count] != '\0' && message[count+1] != '\0') {
        
        if(! isalpha(message[count])) {
            count++;
            continue;
        }
        
        if(! isalpha(message[count+1])) {
            count++;
            continue;
        }

        if(message[count] != message[count + 1]) {
            count++;
            continue;
        }

        found = FALSE;

        for(looper=0; looper<table.total; looper++) {
            if(table.pairs[looper].digram[0] == message[count] &&
               table.pairs[looper].digram[1] == message[count + 1]) {

                table.pairs[looper].count++;
                found = TRUE;
                break;
            }
        }
        if(FALSE == found) {
            table.pairs[table.total].digram[0] = message[count];
            table.pairs[table.total].digram[1] = message[count + 1];
            table.pairs[table.total].count++;
            table.total++;
        }

        count++;
    }

    qsort(table.pairs, table.total, sizeof(DIGRAM), CompareDigrams);

    fprintf(outstream, "Common Diagram Pairs\n");
    for(count=0; count<15; count++) {
        if(table.pairs[count].digram[0]) {
            fprintf(outstream, "%c%c (%d)\n", table.pairs[count].digram[0],table.pairs[count].digram[1], table.pairs[count].count);
        }
    }
    fprintf(outstream, "\n");
    return(TRUE);
}

/*******************************************************************/
int CommonPentagrams(char* message) {
PENTGRMTABLE table;
int count = 0;
int looper = 0;
int found = FALSE;

    memset(&table, 0, sizeof(PENTGRMTABLE));

    while(message[count] != '\0' && message[count+1] != '\0' &&
          message[count+2] != '\0' && message[count+3] != '\0' &&
          message[count+4]) {
        
        if(! isalpha(message[count])) {
            count++;
            continue;
        }
        
        if(! isalpha(message[count+1])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+2])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+3])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+4])) {
            count++;
            continue;
        }
        found = FALSE;

        for(looper=0; looper<table.total; looper++) {
            if(table.pentas[looper].pentagram[0] == message[count] &&
               table.pentas[looper].pentagram[1] == message[count + 1] &&
               table.pentas[looper].pentagram[2] == message[count + 2] &&
               table.pentas[looper].pentagram[3] == message[count + 3] &&
               table.pentas[looper].pentagram[4] == message[count + 4]) {

                table.pentas[looper].count++;
                found = TRUE;
                break;
            }
        }
        if(FALSE == found) {
            table.pentas[table.total].pentagram[0] = message[count];
            table.pentas[table.total].pentagram[1] = message[count + 1];
            table.pentas[table.total].pentagram[2] = message[count + 2];
            table.pentas[table.total].pentagram[3] = message[count + 3];
            table.pentas[table.total].pentagram[4] = message[count + 4];
            table.pentas[table.total].count++;
            table.total++;
        }

        count++;
    }

    qsort(table.pentas, table.total, sizeof(PENTGRAM), ComparePentagrams);

    fprintf(outstream, "Common Pentagram Counts\n");
    for(count=0; count<15; count++) {
        /* No need to look at a single occurence - its a stream of characters */
        if(table.pentas[count].count == 1) { break; }
        fprintf(outstream, "%c%c%c%c%c (%d)\n", table.pentas[count].pentagram[0],
                                           table.pentas[count].pentagram[1],
                                           table.pentas[count].pentagram[2],
                                           table.pentas[count].pentagram[3],
                                           table.pentas[count].pentagram[4],
                                           table.pentas[count].count);
    }

    fprintf(outstream, "\n");
    return(TRUE);
}

/*******************************************************************/
int ComparePentagrams(const void* one, const void* two) {
int A;
int B;

    A = ((PENTGRAM*)one)->count;
    B = ((PENTGRAM*)two)->count;

    if(A < B) {
        return(1);
    } else if (A>B) {
        return(-1);
    }

    A = (char)((PENTGRAM*)one)->pentagram[0];
    B = (char)((PENTGRAM*)two)->pentagram[0];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }
    
    A = (char)((PENTGRAM*)one)->pentagram[1];
    B = (char)((PENTGRAM*)two)->pentagram[1];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((PENTGRAM*)one)->pentagram[2];
    B = (char)((PENTGRAM*)two)->pentagram[2];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((PENTGRAM*)one)->pentagram[3];
    B = (char)((PENTGRAM*)two)->pentagram[3];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((PENTGRAM*)one)->pentagram[4];
    B = (char)((PENTGRAM*)two)->pentagram[4];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    /* They are equal */
    return(0); 
}

/*******************************************************************/
int CommonHexagrams(char* message) {
HEXAGRMTABLE table;
int count = 0;
int looper = 0;
int found = FALSE;

    memset(&table, 0, sizeof(HEXAGRMTABLE));

    while(message[count  ] != '\0' && message[count+1] != '\0' &&
          message[count+2] != '\0' && message[count+3] != '\0' &&
          message[count+4] != '\0' && message[count+5] != '\0'){
        
        if(! isalpha(message[count])) {
            count++;
            continue;
        }
        
        if(! isalpha(message[count+1])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+2])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+3])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+4])) {
            count++;
            continue;
        }

        if(! isalpha(message[count+5])) {
            count++;
            continue;
        }

        found = FALSE;

        for(looper=0; looper<table.total; looper++) {
            if(table.hexas[looper].hexagram[0] == message[count] &&
               table.hexas[looper].hexagram[1] == message[count + 1] &&
               table.hexas[looper].hexagram[2] == message[count + 2] &&
               table.hexas[looper].hexagram[3] == message[count + 3] &&
               table.hexas[looper].hexagram[4] == message[count + 4] &&
               table.hexas[looper].hexagram[5] == message[count + 5]) {

                table.hexas[looper].count++;
                found = TRUE;
                break;
            }
        }
        if(FALSE == found) {
            table.hexas[table.total].hexagram[0] = message[count];
            table.hexas[table.total].hexagram[1] = message[count + 1];
            table.hexas[table.total].hexagram[2] = message[count + 2];
            table.hexas[table.total].hexagram[3] = message[count + 3];
            table.hexas[table.total].hexagram[4] = message[count + 4];
            table.hexas[table.total].hexagram[5] = message[count + 5];
            table.hexas[table.total].count++;
            table.total++;
        }

        count++;
    }

    qsort(table.hexas, table.total, sizeof(HEXAGRAM), CompareHexagrams);

    fprintf(outstream, "Common Hexagram Counts\n");
    for(count=0; count<15; count++) {
        /* No need to look at a single occurence - its a stream of characters */
        if(table.hexas[count].count == 1) { break; }
        fprintf(outstream, "%c%c%c%c%c%c (%d)\n", table.hexas[count].hexagram[0],
                                               table.hexas[count].hexagram[1],
                                               table.hexas[count].hexagram[2],
                                               table.hexas[count].hexagram[3],
                                               table.hexas[count].hexagram[4],
                                               table.hexas[count].hexagram[5],
                                               table.hexas[count].count);
    }

    fprintf(outstream, "\n");
    return(TRUE);
}

/*******************************************************************/
int CompareHexagrams(const void* one, const void* two) {
int A;
int B;

    A = ((HEXAGRAM*)one)->count;
    B = ((HEXAGRAM*)two)->count;

    if(A < B) {
        return(1);
    } else if (A>B) {
        return(-1);
    }

    A = (char)((HEXAGRAM*)one)->hexagram[0];
    B = (char)((HEXAGRAM*)two)->hexagram[0];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }
    
    A = (char)((HEXAGRAM*)one)->hexagram[1];
    B = (char)((HEXAGRAM*)two)->hexagram[1];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((HEXAGRAM*)one)->hexagram[2];
    B = (char)((HEXAGRAM*)two)->hexagram[2];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((HEXAGRAM*)one)->hexagram[3];
    B = (char)((HEXAGRAM*)two)->hexagram[3];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((HEXAGRAM*)one)->hexagram[4];
    B = (char)((HEXAGRAM*)two)->hexagram[4];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }

    A = (char)((HEXAGRAM*)one)->hexagram[5];
    B = (char)((HEXAGRAM*)two)->hexagram[5];
    
    if(A > B) {
        return(1);
    } else if (A < B) {
        return(-1);
    }    
    
    /* They are equal */
    return(0); 
}



