// This program will compile a Traditional DAWG encoding from the "Word-List.txt" file.
// Updated on Monday, December 29, 2011.
// A graph compression algorithm this FAST is perfectly suited for record-keeping-compression while solving an NP-Complete.
// 7 Major concerns addressed:
// 0) A user defined character set of up to 256 letters is now supported. This accomodates certain foreign lexicons.
// 1) Allowance for medium sized word lists. 2^22 DAWG node count is the new upper limit.
// 2) Superior "ReplaceMeWith" scheme.
// 3) The use of CRC-Digest calculation, "Tnode" segmentation, and stable group sorting render DAWG creation INSTANTANEOUS.
// 4) Certain Graph configurataions led the previous version of this program to crash... NO MORE.
// 5) A new DAWG int-node format is used to reduce the number of bitwise operations + add direct "char" extraction.
// "Word-List.txt" is a text file with the number of words written on the very first line, and 1 word per line after that.
// The words are case-insensitive for English letters, and the text file may have Windows or Linux format.
// *** MAX is the length of the longest word in the list. Change this value.
// *** MIN is the length of the shortest word in the list. Change this value.
// Include the big-three header files.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// General high-level program constants.
#define MERGE_SORT_THRESHOLD 1
#define MIN 2
#define MAX 15
#define SIZE_OF_CHARACTER_SET 26
#define INPUT_LIMIT 35
#define LOWER_IT 32
#define TEN 10
#define INT_BITS 32
#define CHILD_BIT_SHIFT 10
// CHILD_INDEX_BIT_MASK is designed NEVER to be used.
#define CHILD_INDEX_BIT_MASK 0XFFFFFC00
#define END_OF_WORD_BIT_MASK 0X00000200
#define END_OF_LIST_BIT_MASK 0X00000100
#define LETTER_BIT_MASK 0X000000FF
#define CHILD_CYPHER 0X1EDC6F41
#define NEXT_CYPHER 0X741B8CD7
#define TWO_UP_EIGHT 256
#define LEFT_BYTE_SHIFT 24
#define BYTE_WIDTH 8
// C requires a boolean variable type so use C's typedef concept to create one.
typedef enum { FALSE = 0, TRUE = 1 } Bool;
typedef Bool* BoolPtr;
// The lexicon text file.
#define RAW_LEXICON "Word-List.txt"
// This program will create "1" binary-data file for use, and "1" text-data file for inspection.
#define TRADITIONAL_DAWG_DATA "Traditional_Dawg_For_Word-List.dat"
#define TRADITIONAL_DAWG_TEXT_DATA "Traditional_Dawg_Explicit_Text_For_Word-List.txt"
// An explicit table-lookup CRC calculation will be used to identify unique graph branch configurations.
#define LOOKUP_TABLE_DATA "CRC-32.dat"
unsigned int TheLookupTable[TWO_UP_EIGHT];
// Lookup tables used for node encoding and number-string decoding.
const int PowersOfTwo[INT_BITS] = { 0X1, 0X2, 0X4, 0X8, 0X10, 0X20, 0X40, 0X80, 0X100, 0X200, 0X400, 0X800,
0X1000, 0X2000, 0X4000, 0X8000, 0X10000, 0X20000, 0X40000, 0X80000, 0X100000, 0X200000, 0X400000, 0X800000, 0X1000000,
0X2000000, 0X4000000, 0X8000000, 0X10000000, 0X20000000, 0X40000000, 0X80000000 };
const int PowersOfTen[TEN] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
const unsigned char CharacterSet[SIZE_OF_CHARACTER_SET] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
// Some word lists will contain letters that NO words begin with. Place "0"s in the corresponding positions.
const unsigned char EntryNodeIndex[SIZE_OF_CHARACTER_SET] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26 };
// This simple function clips off the extra chars for each "fgets()" line. Works for Linux and Windows text format.
void CutOffExtraChars(char *ThisLine){
if ( ThisLine[strlen(ThisLine) - 2] == '\r' ) ThisLine[strlen(ThisLine) - 2] = '\0';
else if ( ThisLine[strlen(ThisLine) - 1] == '\n' ) ThisLine[strlen(ThisLine) - 1] = '\0';
}
// Returns "FALSE" if "TheWord" contains any character not defined in "CharacterSet", and "TRUE" otherwise.
Bool TestForValidWord(const unsigned char *TheWord){
int Length = strlen(TheWord);
int X;
int Y;
for ( X = 0; X < Length; X++ ) {
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
if ( TheWord[X] == CharacterSet[Y] ) goto NestedContinue;
}
return FALSE;
NestedContinue:;
}
return TRUE;
}
// Return the index position of character "ThisChar", as it appears in "CharacterSet", and it must exist in the set.
unsigned char CharToIndexConversion(unsigned char ThisChar){
int Y;
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
if ( ThisChar == CharacterSet[Y] ) return Y;
}
}
// "TheWord" must consist of valid letters within "CharacterSet".
// This function converts "TheWord" from actual characters into index values and stores them into "TheWordByIndex".
void LettersToIndexConversion(const unsigned char *TheWord, unsigned char *TheWordByIndex){
int Length = strlen(TheWord);
int X;
for ( X = 0; X < Length; X++ ) {
TheWordByIndex[X] = CharToIndexConversion(TheWord[X]);
}
}
// Returns the positive "int" rerpresented by "TheNumberNotYet" string. An invalid "TheNumberNotYet" returns "0".
int StringToPositiveInt(char* TheNumberNotYet){
int Result = 0;
int X;
int Length = strlen(TheNumberNotYet);
if ( Length > TEN ) return 0;
for ( X = 0; X < Length; X++ ) {
if ( TheNumberNotYet[X] < '0' || TheNumberNotYet[X] > '9' ) return 0;
Result += ((TheNumberNotYet[X] - '0')*PowersOfTen[Length - X - 1 ]);
}
return Result;
}
// The "BinaryNode" string must be at least 32 + 5 + 1 bytes in length. Space for the bits,
// the seperation pipes, and the end of string char.
// This function is used to fill the text file used to inspect the graph created in the first segment of the program.
void ConvertIntNodeToBinaryString(int TheNode, char *BinaryNode){
int X;
int Bit;
BinaryNode[0] = '[';
// 22 Bits, (31-->10) hold the First-Child index.
Bit = 31;
for ( X = 1; X <= 22; X++, Bit-- ) BinaryNode[X] = (TheNode & PowersOfTwo[Bit])?'1':'0';
BinaryNode[23] = '|';
// Bit 9 holds the End-Of-Word flag.
BinaryNode[24] = (TheNode & END_OF_WORD_BIT_MASK)?'1':'0';
BinaryNode[25] = '|';
// Bit 8 holds the End-Of-List flag.
BinaryNode[26] = (TheNode & END_OF_LIST_BIT_MASK)?'1':'0';
BinaryNode[27] = '|';
// The Letter is held in the final 8 bits, (7->0).
Bit = 7;
for ( X = 28; X <= 35; X++, Bit-- ) BinaryNode[X] = (TheNode & PowersOfTwo[Bit])?'1':'0';
BinaryNode[36] = ']';
BinaryNode[37] = '\0';
}
//This Function converts any lower case letters inside "RawWord" to capitals, so that the whole string is made of capital letters.
void MakeMeAllCapital(char *RawWord){
int Count = 0;
int Length = strlen(RawWord);
for ( Count = 0; Count < Length; Count++ ) {
if ( RawWord[Count] >= 'a' && RawWord[Count] <= 'z' ) RawWord[Count] -= LOWER_IT;
}
}
// This function performs a Byte-wise lookup table CRC calculation on "NumberOfBytes" Bytes, starting at "DataMessage".
// The Polynomial used to generate the lookup table is CRC-32 = 0X04C11DB7.
// The value returned by the function is the "CRC-Digest".
unsigned int LookupTableCrc(const unsigned char *DataMessage, int NumberOfBytes, Bool Print){
int X;
if ( Print ) {
printf("|");
for ( X = 0; X < NumberOfBytes; X++ ) {
printf("%02X", DataMessage[X]);
if ( X%4 == 3 ) printf("|");
}
printf(" - Length |%d|\n", NumberOfBytes);
}
// Because looking up "0" in the table returns "0", it is safe to use a table lookup to fill the "WorkingRegister" with its initial "DataMessage" value.
register unsigned int WorkingRegister = 0;
// Query the "LookupTable" exactly "NumberOfBytes" times. Perform lookups using the value inside of "WorkingRegister" as the index.
// After each table query, "XOR" the value returned by "TheLookupTable" with "WorkingRegister" after pulling in the next Byte of "DataMessage".
// "X" is the location of the next data Byte to pull into the calculation.
for ( X = 0; X < NumberOfBytes; X++ ) WorkingRegister = TheLookupTable[WorkingRegister >> LEFT_BYTE_SHIFT] ^ ((WorkingRegister << BYTE_WIDTH) ^ DataMessage[X]);
if ( Print ) printf("Calculated Digest = |%08X|\n", WorkingRegister);
return WorkingRegister;
}
/*Trie to Dawg TypeDefs*/
struct tnode {
struct tnode* Next;
struct tnode* Child;
struct tnode* ParentalUnit;
struct tnode* ReplaceMeWith;
// When populating the DAWG array, you must know the index assigned to each "Child".
// "ArrayIndex" Is stored in every node, so that we can mine the information from the Trie.
int ArrayIndex;
int InternalValues;
char DirectChild;
unsigned char LetterIndex;
char MaxChildDepth;
char Level;
unsigned char NumberOfChildren;
unsigned char DistanceToEndOfList;
char Dangling;
char Protected;
char EndOfWordFlag;
unsigned int CrcDigest;
// To streamline checking if "Protected" "Tnode"s are up for "Dangling", filter "ProtectedUnderCount" up to the root "Tnode"; do it on the fly.
int ProtectedUnderCount;
};
typedef struct tnode Tnode;
typedef Tnode* TnodePtr;
// Functions to access internal "Tnode" members.
int TnodeArrayIndex(TnodePtr ThisTnode){
return ThisTnode->ArrayIndex;
}
char TnodeDirectChild(TnodePtr ThisTnode){
return ThisTnode->DirectChild;
}
TnodePtr TnodeNext(TnodePtr ThisTnode){
return ThisTnode->Next;
}
TnodePtr TnodeChild(TnodePtr ThisTnode){
return ThisTnode->Child;
}
TnodePtr TnodeParentalUnit(TnodePtr ThisTnode){
return ThisTnode->ParentalUnit;
}
TnodePtr TnodeReplaceMeWith(TnodePtr ThisTnode){
return ThisTnode->ReplaceMeWith;
}
unsigned char TnodeLetterIndex(TnodePtr ThisTnode){
return ThisTnode->LetterIndex;
}
char TnodeMaxChildDepth(TnodePtr ThisTnode){
return ThisTnode->MaxChildDepth;
}
unsigned char TnodeNumberOfChildren(TnodePtr ThisTnode){
return ThisTnode->NumberOfChildren;
}
unsigned char TnodeDistanceToEndOfList(TnodePtr ThisTnode){
return ThisTnode->DistanceToEndOfList;
}
char TnodeEndOfWordFlag(TnodePtr ThisTnode){
return ThisTnode->EndOfWordFlag;
}
char TnodeLevel(TnodePtr ThisTnode){
return ThisTnode->Level;
}
char TnodeDangling(TnodePtr ThisTnode){
return ThisTnode->Dangling;
}
char TnodeProtected(TnodePtr ThisTnode){
return ThisTnode->Protected;
}
unsigned int TnodeCrcDigest(TnodePtr ThisTnode){
return ThisTnode->CrcDigest;
}
// Allocate a "Tnode" and fill it with initial values.
TnodePtr TnodeInit(unsigned char ChapIndex, TnodePtr OverOne, char WordEnding, char Leveler, int StarterDepth, TnodePtr Parent, char IsaChild, char StartListPosition){
TnodePtr Result = (Tnode *)malloc(sizeof(Tnode));
Result->LetterIndex = ChapIndex;
Result->ArrayIndex = 0;
Result->InternalValues = 0;
Result->NumberOfChildren = 0;
Result->DistanceToEndOfList = StartListPosition;
Result->MaxChildDepth = StarterDepth;
Result->Next = OverOne;
Result->Child = NULL;
Result->ParentalUnit = Parent;
Result->Dangling = FALSE;
Result->Protected = FALSE;
Result->ReplaceMeWith = NULL;
Result->EndOfWordFlag = WordEnding;
Result->Level = Leveler;
Result->DirectChild = IsaChild;
Result->CrcDigest = 0;
Result->ProtectedUnderCount = 0;
return Result;
}
// Use this for debugging any program modifications.
void TnodeOutput(TnodePtr ThisTnode){
printf("|%c|%d|%d|%d|%X|-|%X|\n", CharacterSet[ThisTnode->LetterIndex], ThisTnode->EndOfWordFlag, ThisTnode->NumberOfChildren,
ThisTnode->DistanceToEndOfList, ThisTnode->InternalValues, ThisTnode->CrcDigest);
if ( ThisTnode->Child != NULL ) TnodeOutput(ThisTnode->Child);
}
// Modify internal "Tnode" member values.
void TnodeSetArrayIndex(TnodePtr ThisTnode, int TheWhat){
ThisTnode->ArrayIndex = TheWhat;
}
void TnodeSetChild(TnodePtr ThisTnode, TnodePtr Chump){
ThisTnode->Child = Chump;
}
void TnodeSetNext(TnodePtr ThisTnode, TnodePtr Nexis){
ThisTnode->Next = Nexis;
}
void TnodeSetParentalUnit(TnodePtr ThisTnode, TnodePtr Parent){
ThisTnode->ParentalUnit = Parent;
}
void TnodeSetReplaceMeWith(TnodePtr ThisTnode, TnodePtr Living){
ThisTnode->ReplaceMeWith = Living;
}
void TnodeSetMaxChildDepth(TnodePtr ThisTnode, int NewDepth){
ThisTnode->MaxChildDepth = NewDepth;
}
void TnodeSetDirectChild(TnodePtr ThisTnode, char Status){
ThisTnode->DirectChild = Status;
}
void TnodeFlyEndOfWordFlag(TnodePtr ThisTnode){
ThisTnode->EndOfWordFlag = TRUE;
}
// This statement evaluates to TRUE when the CRC at "one" has a higher value than the CRC at "two". "one" and "two" are indicies of "arrayone", and "arraytwo".
#define COMPARE_TNODES(arrayone, one, arraytwo, two) ( arrayone[one]->CrcDigest > arraytwo[two]->CrcDigest )
void TnodeArrayMergeSortRecurse(TnodePtr *OriginalArray, int TheSize, TnodePtr *ExtraArray){
int FirstSize = TheSize>>1;
int SecondSize = TheSize - FirstSize;
int FirstIndex = 0;
int SecondIndex = 0;
int InsertIndex = 0;
TnodePtr *TheFirst = OriginalArray;
TnodePtr *TheSecond = OriginalArray + FirstSize;
// Testing the escape condition before calling "TnodeArrayMergeSort" reduces stack overhead.
if ( FirstSize > MERGE_SORT_THRESHOLD ) TnodeArrayMergeSortRecurse(TheFirst, FirstSize, ExtraArray);
if ( SecondSize > MERGE_SORT_THRESHOLD ) TnodeArrayMergeSortRecurse(TheSecond, SecondSize, ExtraArray);
// We can now conclude that the two lists are sorted, so merge them into the "ExtraArray".
while ( FirstIndex < FirstSize && SecondIndex < SecondSize) {
// Using this comparison macro ensures that the sort will be stable.
if ( COMPARE_TNODES(TheSecond, SecondIndex, TheFirst, FirstIndex) ) {
ExtraArray[InsertIndex] = TheSecond[SecondIndex];
SecondIndex += 1;
InsertIndex += 1;
}
else {
ExtraArray[InsertIndex] = TheFirst[FirstIndex];
FirstIndex += 1;
InsertIndex += 1;
}
}
// This instruction copies the remaining elements from the unfinished list into the "ExtraArray".
if ( FirstIndex == FirstSize) memcpy(ExtraArray + InsertIndex, TheSecond + SecondIndex, (SecondSize - SecondIndex)*sizeof(TnodePtr));
else memcpy(ExtraArray + InsertIndex, TheFirst + FirstIndex, (FirstSize - FirstIndex)*sizeof(TnodePtr));
memcpy(OriginalArray, ExtraArray, TheSize*sizeof(TnodePtr));
}
// After all words have been added to the initial Trie, this function will combine the internal comparison values of "ThisTnode" into its "InternalValues".
void TnodeCalculateInternalValues(TnodePtr ThisTnode){
char *TheBytes = (char *)&(ThisTnode->InternalValues);
TheBytes[0] = ThisTnode->LetterIndex;
TheBytes[1] = ThisTnode->NumberOfChildren;
TheBytes[2] = ThisTnode->DistanceToEndOfList;
TheBytes[3] = ((ThisTnode->MaxChildDepth) << 1) + ThisTnode->EndOfWordFlag;
}
// Recursively calculate all "InternalValues" within a "Tnode" graph. "ThisTnode" must not be NULL.
void TnodeCalculateInternalValuesRecurse(TnodePtr ThisTnode){
TnodeCalculateInternalValues(ThisTnode);
if ( ThisTnode->Child != NULL ) TnodeCalculateInternalValuesRecurse(ThisTnode->Child);
if ( ThisTnode->Next != NULL ) TnodeCalculateInternalValuesRecurse(ThisTnode->Next);
}
// The "CrcDigest" of a "Tnode" is heavily based on its "InternalValues".
// Further, when a "Tnode" has a "Child" list OR it is NOT at the end of a list,
// a data message is created using a series of "Child" and "NEXT" "CrcDigest"s.
// These packets of data are seperated by "CYPHER" "int"s to distinguish "Tnode" branch structures.
// Finally, a Byte-Wise Crc-Lookup-Table is used to convert the resulting data-message into a 32 bit "CrcDigest".
void TnodeCalculateCrcDigest(TnodePtr ThisTnode, Bool Print){
static unsigned int TheMessage[(SIZE_OF_CHARACTER_SET + 2)<<1];
int MessageLength;
int FillSpace;
int X;
TnodePtr Current;
if ( ThisTnode->DistanceToEndOfList == 0 ) {
// "ThisTnode" is a terminal node, so just use its "InternalValues" as its "CrcDigest".
if ( ThisTnode->NumberOfChildren == 0 ) {
ThisTnode->CrcDigest = (unsigned int)ThisTnode->InternalValues;
return;
}
// "ThisTnode" has a "Child" list, but is located at the end of its own "Tnode" list.
else {
TheMessage[0] = (unsigned int)ThisTnode->InternalValues;
TheMessage[1] = CHILD_CYPHER;
MessageLength = ThisTnode->NumberOfChildren + 2;
Current = ThisTnode->Child;
for ( FillSpace = 2; FillSpace < MessageLength; FillSpace++ ) {
TheMessage[FillSpace] = Current->CrcDigest;
if ( TheMessage[FillSpace] == 0 ) printf("ZERO in CRC of Child.\n");
Current = Current->Next;
}
TheMessage[MessageLength] = (unsigned int)ThisTnode->InternalValues;
MessageLength += 1;
if ( Print == TRUE ) {
for ( X = 0; X < MessageLength; X++ ) printf("|%08X", TheMessage[X]);
printf("|\n");
}
ThisTnode->CrcDigest = LookupTableCrc((unsigned char *)TheMessage, MessageLength<<2, Print);
if ( Print == TRUE ) printf("Inherited Digest = |%X| - Length|%d|\n", ThisTnode->CrcDigest, MessageLength<<2);
return;
}
}
// "ThisTnode" has no "Child" list, but has following "Tnode"s in its own list.
if ( ThisTnode->NumberOfChildren == 0 ) {
TheMessage[0] = (unsigned int)ThisTnode->InternalValues;
TheMessage[1] = NEXT_CYPHER;
MessageLength = ThisTnode->DistanceToEndOfList + 2;
Current = ThisTnode->Next;
for ( FillSpace = 2; FillSpace < MessageLength; FillSpace++ ) {
TheMessage[FillSpace] = Current->CrcDigest;
if ( TheMessage[FillSpace] == 0 ) printf("ZERO in CRC of Next.\n");
Current = Current->Next;
}
TheMessage[MessageLength] = (unsigned int)ThisTnode->InternalValues;
MessageLength += 1;
ThisTnode->CrcDigest = LookupTableCrc((unsigned char *)TheMessage, MessageLength<<2, Print);
return;
}
// "ThisTnode" has a "Child" list, and also has following "Tnode"s in its own list.
TheMessage[0] = (unsigned int)ThisTnode->InternalValues;
TheMessage[1] = CHILD_CYPHER;
MessageLength = ThisTnode->NumberOfChildren + 2;
Current = ThisTnode->Child;
for ( FillSpace = 2; FillSpace < MessageLength; FillSpace++ ) {
TheMessage[FillSpace] = Current->CrcDigest;
if ( TheMessage[FillSpace] == 0 ) printf("ZERO in CRC of BChild.\n");
Current = Current->Next;
}
TheMessage[MessageLength] = NEXT_CYPHER;
MessageLength += ThisTnode->DistanceToEndOfList + 1;
Current = ThisTnode->Next;
for ( FillSpace += 1; FillSpace < MessageLength; FillSpace++ ) {
TheMessage[FillSpace] = Current->CrcDigest;
if ( TheMessage[FillSpace] == 0 ) printf("ZERO in CRC of BNext.\n");
Current = Current->Next;
}
TheMessage[MessageLength] = (unsigned int)ThisTnode->InternalValues;
MessageLength += 1;
ThisTnode->CrcDigest = LookupTableCrc((unsigned char *)TheMessage, MessageLength<<2, Print);
return;
}
// When calculating the "CrcDigest" of a "Tnode", its "Next" list and "Child" list must already have calculated "CrcDigest"s.
void TnodeCalculateCrcDigestRecurse(TnodePtr ThisTnode){
if ( ThisTnode->Next != NULL ) TnodeCalculateCrcDigestRecurse(ThisTnode->Next);
if ( ThisTnode->Child != NULL ) TnodeCalculateCrcDigestRecurse(ThisTnode->Child);
TnodeCalculateCrcDigest(ThisTnode, FALSE);
}
// This function Dangles a "Tnode", but also recursively dangles every "Tnode" after and below it as well.
// Dangling a "Tnode" means that it will be exculded from the final "DAWG" encoding.
// By recursion, nodes that are not direct children will get dangled.
// The function returns the total number of nodes dangled as a result.
int TnodeDangleRecurse(TnodePtr ThisTnode){
int Result = 0;
if ( ThisTnode->Dangling == TRUE ) return 0;
if ( ThisTnode->Protected == TRUE ) {
printf(" There is NO scenario where Dangling a Protected node should happen. ERROR, ERROR, ERROR.\n");
return 0;
}
if ( (ThisTnode->Next) != NULL ) Result += TnodeDangleRecurse(ThisTnode->Next);
if ( (ThisTnode->Child) != NULL ) Result += TnodeDangleRecurse(ThisTnode->Child);
if ( ThisTnode->Dangling == FALSE )Result += 1;
ThisTnode->Dangling = TRUE;
return Result;
}
// This function "Protects" a node being directly referenced in the elimination process.
// "Protected" "Tnode"s should NEVER be "Dangling".
// Make sure to increment "ProtectedUnderCount" by "1" all the way up to the root "Tnode".
void TnodeProtect(TnodePtr ThisTnode){
TnodePtr Current = ThisTnode;
if ( ThisTnode->Protected == FALSE ) {
ThisTnode->Protected = TRUE;
while ( Current != NULL ) {
Current->ProtectedUnderCount += 1;
Current = Current->ParentalUnit;
}
}
}
// This function returns the pointer to the "Tnode" in a parallel list of "Tnodes" with the "LetterIndex" "ThisLetterIndex",
// and returns "NULL" if the "Tnode" does not exist.
// If the function returns "NULL", then an insertion is required.
TnodePtr TnodeFindParaNode(TnodePtr ThisTnode, unsigned char ThisLetterIndex){
TnodePtr Result = ThisTnode;
if ( ThisTnode == NULL ) return NULL;
if ( Result->LetterIndex == ThisLetterIndex ) return Result;
while ( Result->LetterIndex < ThisLetterIndex ) {
Result = Result->Next;
if ( Result == NULL ) return NULL;
}
if ( Result->LetterIndex == ThisLetterIndex ) return Result;
else return NULL;
}
// This function inserts a new "Tnode" before a larger "LetterIndex" "Tnode" or at the end of a para list.
// If the list does not exist, then it is put at the beginnung.
// The new "Tnode" has "ThisLetterIndex" in it. "AboveTnode" is the "Tnode" 1 level above where the new node will be placed.
// This function should never be passed a "NULL" pointer. "ThisLetterIndex" should never exist in the "Child" "Tnode" list.
void TnodeInsertParaNode(TnodePtr AboveTnode, unsigned char ThisLetterIndex, char WordEnder, int StartDepth){
AboveTnode->NumberOfChildren += 1;
TnodePtr Holder = NULL;
TnodePtr Currently = NULL;
// Case 1: ParaList does not exist yet so start it.
if ( AboveTnode->Child == NULL ) AboveTnode->Child = TnodeInit(ThisLetterIndex, NULL, WordEnder, AboveTnode->Level + 1,
StartDepth, AboveTnode, TRUE, 0);
// Case 2: "ThisLetterIndex" should be the first in the ParaList.
else if ( AboveTnode->Child->LetterIndex > ThisLetterIndex ) {
Holder = AboveTnode->Child;
// The holder node is no longer a direct child so set it as such.
TnodeSetDirectChild(Holder, FALSE);
AboveTnode->Child = TnodeInit(ThisLetterIndex, Holder, WordEnder, AboveTnode->Level + 1, StartDepth, AboveTnode, TRUE, TnodeDistanceToEndOfList(Holder) + 1);
// The parent node needs to be changed on what used to be the child. it is the Tnode in "Holder".
Holder->ParentalUnit = AboveTnode->Child;
}
// Case 3: The ParaList exists and "ThisLetterIndex" is not first in the list.
else {
Currently = AboveTnode->Child;
while ( Currently->Next !=NULL ) {
if ( Currently->Next->LetterIndex > ThisLetterIndex ) break;
Currently->DistanceToEndOfList += 1;
Currently = Currently->Next;
}
Holder = Currently->Next;
Currently->Next = TnodeInit(ThisLetterIndex, Holder, WordEnder, AboveTnode->Level + 1, StartDepth, Currently, FALSE, Currently->DistanceToEndOfList);
Currently->DistanceToEndOfList += 1;
if ( Holder != NULL ) Holder->ParentalUnit = Currently->Next;
}
}
struct dawg {
int NumberOfTotalWords;
int NumberOfTotalNodes;
TnodePtr First;
};
typedef struct dawg Dawg;
typedef Dawg* DawgPtr;
// Set up the parent nodes in the Dawg.
DawgPtr DawgInit(void){
DawgPtr Result = (Dawg *)malloc(sizeof(Dawg));
Result->NumberOfTotalWords = 0;
Result->NumberOfTotalNodes = 0;
Result->First = TnodeInit('0', NULL, FALSE, 0, 0, NULL, FALSE, 0);
return Result;
}
// Return the root node of "ThisDawg", which is a direct child of the "First" node.
TnodePtr DawgRootNode(DawgPtr ThisDawg){
return TnodeChild(ThisDawg->First);
}
// This function is responsible for adding "WordByIndexes" to the "Dawg" under its root node.
// It returns the number of new nodes inserted.
int TnodeDawgAddWord(TnodePtr ParentNode, const unsigned char *WordByIndexes, int WordSize){
int Result = 0;
int X;
int Y;
TnodePtr HangPoint = NULL;
TnodePtr Current = ParentNode;
for ( X = 0; X < WordSize; X++){
HangPoint = TnodeFindParaNode(TnodeChild(Current), WordByIndexes[X]);
if ( HangPoint == NULL ) {
TnodeInsertParaNode(Current, WordByIndexes[X], (X == WordSize - 1 ? TRUE : FALSE), WordSize - X - 1);
Result++;
Current = TnodeFindParaNode(TnodeChild(Current), WordByIndexes[X]);
for ( Y = X + 1; Y < WordSize; Y++ ) {
TnodeInsertParaNode(Current, WordByIndexes[Y], (Y == WordSize - 1 ? TRUE : FALSE), WordSize - Y - 1);
Result += 1;
Current = TnodeChild(Current);
}
break;
}
else {
if ( TnodeMaxChildDepth(HangPoint) < WordSize - X - 1 ) TnodeSetMaxChildDepth(HangPoint, WordSize - X - 1);
}
Current = HangPoint;
// The path for the "WordByIndexes" that we are trying to insert already exists,
// so just make sure that the end flag is flying on the last node.
// This should never happen if we are to add words in alphabetical order and increasing "WordByIndexes" length.
if ( X == WordSize - 1 ) TnodeFlyEndOfWordFlag(Current);
}
return Result;
}
// Add "NewWord" to "ThisDawg", which at this point is a "Trie" with a lot of information in each node.
// "NewWord" must not exist in "ThisDawg" already.
void DawgAddWord(DawgPtr ThisDawg, unsigned char *NewWordByIndexes, int WordLength){
ThisDawg->NumberOfTotalWords += 1;
ThisDawg->NumberOfTotalNodes += TnodeDawgAddWord(ThisDawg->First, NewWordByIndexes, WordLength);
}
// This is a standard depth first inorder tree traversal.
// Count un"Dangling" "Tnodes" into the 780 groups by "MaxChildDepth", "LetterIndex", and "DirectChild", then store values into "Tabulator".
void TnodeGraphTabulateRecurse(TnodePtr ThisTnode, int ***Tabulator){
// We will only ever be concerned with "Living" nodes. "Dangling" Nodes will be eliminated, so don't count them.
if ( ThisTnode->Dangling == FALSE ) {
Tabulator[ThisTnode->MaxChildDepth][ThisTnode->LetterIndex][ThisTnode->DirectChild] += 1;
// Go Down if possible.
if ( ThisTnode->Child != NULL ) TnodeGraphTabulateRecurse(TnodeChild(ThisTnode), Tabulator);
// Go Right if possible.
if ( ThisTnode->Next != NULL ) TnodeGraphTabulateRecurse(TnodeNext(ThisTnode), Tabulator);
}
}
// Count the "Living" "Tnode"s into the 780 groups by "MaxChildDepth", "LetterIndex", and "DirectChild", then store values into "Count".
void DawgGraphTabulate(DawgPtr ThisDawg, int ***Count){
if ( ThisDawg->NumberOfTotalWords > 0 ) {
TnodeGraphTabulateRecurse(TnodeChild(ThisDawg->First), Count);
}
}
// Recursively replaces all redundant "Tnode"s under "ThisTnode", in one penetrating assult.
// "DirectChild" "Tnode"s in a "Dangling" state have "ReplaceMeWith" set within them.
void TnodeBlitzAttackRecurse(TnodePtr ThisTnode){
if ( ThisTnode->Next == NULL && ThisTnode->Child == NULL ) return;
// The first "Tnode" being eliminated will always be a "DirectChild".
if ( ThisTnode->Child != NULL ) {
// The node is tagged to be excised, so replace it with "ReplaceMeWith".
if ( ThisTnode->Child->Dangling == TRUE ) {
ThisTnode->Child = ThisTnode->Child->ReplaceMeWith;
}
else {
TnodeBlitzAttackRecurse(ThisTnode->Child);
}
}
if ( ThisTnode->Next != NULL ){
TnodeBlitzAttackRecurse(ThisTnode->Next);
}
}
// Replaces all pointers to "Dangling" "Child" "Tnodes" in the "ThisDawg" Trie with living ones.
void BlitzkriegTrieAttack(DawgPtr ThisDawg){
TnodeBlitzAttackRecurse(ThisDawg->First->Child);
}
// A recursive function which Exchanges a single "Protected" "Tnode" under "ToDangle" with the corresponding "Tnode" under "ToKeep".
// Remember to update "ProtectedUnderCount" for each line of "Tnodes" after the exchange.
void TnodeExchangeProtectedNodeRecurse(TnodePtr ToDangle, TnodePtr ToKeep){
int ProtectedUnderCountParity;
TnodePtr Holder;
if ( ToDangle->Protected == TRUE) {
if ( ToDangle->DirectChild == TRUE ) {
//printf("Protected ToDangle = DirectChild");
if ( ToKeep->ReplaceMeWith == ToDangle ) {
//printf(" - Standard Crosslink");
ProtectedUnderCountParity = ToDangle->ProtectedUnderCount - ToKeep->ProtectedUnderCount;
Holder = ToDangle->ParentalUnit;
while ( Holder != NULL ) {
Holder->ProtectedUnderCount -= ProtectedUnderCountParity;
Holder = Holder->ParentalUnit;
}
Holder = ToKeep->ParentalUnit;
while ( Holder != NULL ) {
Holder->ProtectedUnderCount += ProtectedUnderCountParity;
Holder = Holder->ParentalUnit;
}
(ToKeep->ParentalUnit)->Child = ToDangle;
(ToDangle->ParentalUnit)->Child = ToKeep;
Holder = ToKeep->ParentalUnit;
ToKeep->ParentalUnit = ToDangle->ParentalUnit;
ToDangle->ParentalUnit = Holder;
return;
}
// This case is not possible.
else {
return;
}
}
// The "Protected" "Tnode" is not a "DirectChild".
else {
if ( ToKeep->Dangling == TRUE) {
//printf(" - ToKeep = Dangling - Something is FUCKED up.");
return;
}
else {
//printf(" - ToKeep != Dangling");
ProtectedUnderCountParity = ToDangle->ProtectedUnderCount - ToKeep->ProtectedUnderCount;
Holder = ToDangle->ParentalUnit;
while ( Holder != NULL ) {
Holder->ProtectedUnderCount -= ProtectedUnderCountParity;
Holder = Holder->ParentalUnit;
}
Holder = ToKeep->ParentalUnit;
while ( Holder != NULL ) {
Holder->ProtectedUnderCount += ProtectedUnderCountParity;
Holder = Holder->ParentalUnit;
}
(ToKeep->ParentalUnit)->Next = ToDangle;
(ToDangle->ParentalUnit)->Next = ToKeep;
Holder = ToKeep->ParentalUnit;
ToKeep->ParentalUnit = ToDangle->ParentalUnit;
ToDangle->ParentalUnit = Holder;
return;
}
}
}
if ( ToDangle->Child != NULL ) TnodeExchangeProtectedNodeRecurse(ToDangle->Child, ToKeep->Child);
if ( ToDangle->Next != NULL ) TnodeExchangeProtectedNodeRecurse(ToDangle->Next, ToKeep->Next);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// A queue is required for breadth first traversal, and the rest is self-evident.
struct breadthqueuenode {
TnodePtr Element;
struct breadthqueuenode *Next;
};
typedef struct breadthqueuenode BreadthQueueNode;
typedef BreadthQueueNode* BreadthQueueNodePtr;
void BreadthQueueNodeSetNext(BreadthQueueNodePtr ThisBreadthQueueNode, BreadthQueueNodePtr Nexit){
ThisBreadthQueueNode->Next = Nexit;
}
BreadthQueueNodePtr BreadthQueueNodeNext(BreadthQueueNodePtr ThisBreadthQueueNode){
return ThisBreadthQueueNode->Next;
}
TnodePtr BreadthQueueNodeElement(BreadthQueueNodePtr ThisBreadthQueueNode){
return ThisBreadthQueueNode->Element;
}
BreadthQueueNodePtr BreadthQueueNodeInit(TnodePtr NewElement){
BreadthQueueNodePtr Result = (BreadthQueueNode *)malloc(sizeof(BreadthQueueNode));
Result->Element = NewElement;
Result->Next = NULL;
return Result;
}
struct breadthqueue {
BreadthQueueNodePtr Front;
BreadthQueueNodePtr Back;
int Size;
};
typedef struct breadthqueue BreadthQueue;
typedef BreadthQueue* BreadthQueuePtr;
BreadthQueuePtr BreadthQueueInit(void){
BreadthQueuePtr Result = (BreadthQueue *)malloc(sizeof(BreadthQueue));
Result->Front = NULL;
Result->Back = NULL;
Result->Size = 0;
return Result;
}
void BreadthQueuePush(BreadthQueuePtr ThisBreadthQueue, TnodePtr NewElemental){
BreadthQueueNodePtr Noob = BreadthQueueNodeInit(NewElemental);
if ( (ThisBreadthQueue->Back) != NULL ) BreadthQueueNodeSetNext(ThisBreadthQueue->Back, Noob);
else ThisBreadthQueue->Front = Noob;
ThisBreadthQueue->Back = Noob;
(ThisBreadthQueue->Size) += 1;
}
TnodePtr BreadthQueuePop(BreadthQueuePtr ThisBreadthQueue){
if ( ThisBreadthQueue->Size == 0 ) return NULL;
if ( ThisBreadthQueue->Size == 1 ) {
ThisBreadthQueue->Back = NULL;
ThisBreadthQueue->Size = 0;
TnodePtr Result = (ThisBreadthQueue->Front)->Element;
free(ThisBreadthQueue->Front);
ThisBreadthQueue->Front = NULL;
return Result;
}
TnodePtr Result = (ThisBreadthQueue->Front)->Element;
BreadthQueueNodePtr Holder = ThisBreadthQueue->Front;
ThisBreadthQueue->Front = (ThisBreadthQueue->Front)->Next;
free(Holder);
ThisBreadthQueue->Size -= 1;
return Result;
}
// For the "Tnode" "Dangling" process, arrange the "Tnodes" in the "Holder" array, with breadth-first traversal order.
void BreadthQueuePopulateReductionArray(BreadthQueuePtr ThisBreadthQueue, TnodePtr Root, TnodePtr ****Holder){
int InsertionPosition[MAX][SIZE_OF_CHARACTER_SET][2];
int CMCD;
char CLetterIndex;
char CDCstatus;
memset(InsertionPosition, 0, 2*MAX*SIZE_OF_CHARACTER_SET*sizeof(int));
TnodePtr Current = Root;
// Push the first row onto the queue.
while ( Current != NULL ) {
BreadthQueuePush(ThisBreadthQueue, Current);
Current = Current->Next;
}
// Initiate the pop followed by push all children loop.
while ( (ThisBreadthQueue->Size) != 0 ) {
Current = BreadthQueuePop(ThisBreadthQueue);
CMCD = Current->MaxChildDepth;
CLetterIndex = Current->LetterIndex;
CDCstatus = Current->DirectChild;
Holder[CMCD][CLetterIndex][CDCstatus][InsertionPosition[CMCD][CLetterIndex][CDCstatus]] = Current;
InsertionPosition[CMCD][CLetterIndex][CDCstatus] += 1;
Current = TnodeChild(Current);
while ( Current != NULL ) {
BreadthQueuePush(ThisBreadthQueue, Current);
Current = TnodeNext(Current);
}
}
}
// It is of absolutely critical importance that only "DirectChild" nodes are pushed onto the queue as child nodes.
// This will not always be the case.
// In a DAWG, a child pointer may point to an internal node in a longer list. Check for this.
int BreadthQueueUseToIndex(BreadthQueuePtr ThisBreadthQueue, TnodePtr Root){
int IndexNow = 0;
TnodePtr Current = Root;
// Push the first row onto the queue.
while ( Current != NULL ) {
BreadthQueuePush(ThisBreadthQueue, Current);
Current = Current->Next;
}
// Pop each element off of the queue and only push its children if its first "Child" is a "DirectChild", without a set "ArrayIndex".
// Assign index if one has not been given to it yet.
while ( (ThisBreadthQueue->Size) != 0 ) {
Current = BreadthQueuePop(ThisBreadthQueue);
// A traversal of the Trie will never land on "Dangling" "Tnodes", but it will try to visit certain "Tnodes" many times.
// Even if we only "Push" "Tnode"s without an assigned "ArrayIndex", many "Tnode"s will have this value set while in the queue.
if ( TnodeArrayIndex(Current) == 0 ) {
IndexNow += 1;
TnodeSetArrayIndex(Current, IndexNow);
Current = TnodeChild(Current);
if ( Current != NULL ) {
// The graph will lead to intermediate positions, but we cannot start numbering "Tnodes" from the middle of a list.
if ( TnodeDirectChild(Current) == TRUE && TnodeArrayIndex(Current) == 0 ) {
while ( Current != NULL ) {
if ( TnodeArrayIndex(Current) != 0 ) printf("Pushed Tnode with a non-zero ArrayIndex.\n");
BreadthQueuePush(ThisBreadthQueue, Current);
Current = Current->Next;
}
}
}
}
}
return IndexNow;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Next and Child become indices.
struct arraydnode{
int Next;
int Child;
unsigned char LetterIndex;
char EndOfWordFlag;
char Level;
unsigned char ChildCount;
unsigned char Position;
};
typedef struct arraydnode ArrayDnode;
typedef ArrayDnode* ArrayDnodePtr;
void ArrayDnodeInit(ArrayDnodePtr ThisArrayDnode, unsigned char Chap, int Nextt, int Childd, char EndingFlag, char Breadth, unsigned char Posit, unsigned char Ccount){
ThisArrayDnode->LetterIndex = Chap;
ThisArrayDnode->EndOfWordFlag = EndingFlag;
ThisArrayDnode->Next = Nextt;
ThisArrayDnode->Child = Childd;
ThisArrayDnode->Level = Breadth;
ThisArrayDnode->Position = Posit;
ThisArrayDnode->ChildCount = Ccount;
}
void ArrayDnodeTnodeTranspose(ArrayDnodePtr ThisArrayDnode, TnodePtr ThisTnode){
ThisArrayDnode->LetterIndex = ThisTnode->LetterIndex;
ThisArrayDnode->EndOfWordFlag = ThisTnode->EndOfWordFlag;
ThisArrayDnode->Level = ThisTnode->Level;
ThisArrayDnode->Position = ThisTnode->DistanceToEndOfList;
ThisArrayDnode->ChildCount = ThisTnode->NumberOfChildren;
if ( ThisTnode->Next == NULL ) ThisArrayDnode->Next = 0;
else ThisArrayDnode->Next = (ThisTnode->Next)->ArrayIndex;
if ( ThisTnode->Child == NULL ) ThisArrayDnode->Child = 0;
else ThisArrayDnode->Child = (ThisTnode->Child)->ArrayIndex;
}
struct arraydawg {
int NumberOfStrings;
ArrayDnodePtr DawgArray;
int First;
};
typedef struct arraydawg ArrayDawg;
typedef ArrayDawg* ArrayDawgPtr;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This function is the core of the DAWG creation procedure. Pay close attention to the order of the steps involved.
ArrayDawgPtr ArrayDawgInit(unsigned char **Dictionary, int *SegmentLenghts){
int X;
int Y;
int Z;
int W;
printf("Step 0 - Allocate the framework for the intermediate Array-Data-Structure.\n");
// Dynamically allocate the upper Data-Structure.
ArrayDawgPtr Result = (ArrayDawgPtr)malloc(sizeof(ArrayDawg));
// Set NumberOfStrings.
Result->NumberOfStrings = 0;
for ( X = MIN; X <= MAX ; X++ ) Result->NumberOfStrings += SegmentLenghts[X];
printf("\nStep 1 - Create a TemporaryTrie and begin filling it with the |%d| words.\n", Result->NumberOfStrings);
/// Create a Temp Trie structure and then feed in the given dictionary.
DawgPtr TemporaryTrie = DawgInit();
for ( Y = MIN; Y <= MAX; Y++ ) {
for ( X = 0; X < SegmentLenghts[Y]; X++ ) {
DawgAddWord(TemporaryTrie, &(Dictionary[Y][Y*X]), Y);
}
}
printf("\nStep 2 - Finished filling TemporaryTrie, so calculate the InternalValues comparison integers.\n");
TnodeCalculateInternalValuesRecurse(DawgRootNode(TemporaryTrie));
printf("\nStep 3 - Eliminate recursion by calculating the recursive CrcDigest for each Tnode.\n");
TnodeCalculateCrcDigestRecurse(DawgRootNode(TemporaryTrie));
printf("\nStep 4 - Count Tnodes into 780 groups, segmented by MaxChildDepth, Letter, and DirectChild.\n");
// Allocate 3D arrays of "int"s to count the "Tnodes" into groups.
int ***NodeGroupCounter= (int ***)malloc(MAX*sizeof(int **));
int ***NodeGroupCounterInit = (int ***)malloc(MAX*sizeof(int **));
for ( X = 0; X < MAX; X++ ) {
NodeGroupCounterInit[X] = (int **)malloc(SIZE_OF_CHARACTER_SET*sizeof(int *));
NodeGroupCounter[X] = (int **)malloc(SIZE_OF_CHARACTER_SET*sizeof(int *));
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
NodeGroupCounterInit[X][Y] = (int *)calloc(2, sizeof(int));
NodeGroupCounter[X][Y] = (int *)calloc(2, sizeof(int));
}
}
DawgGraphTabulate(TemporaryTrie, NodeGroupCounterInit);
printf("\nStep 5 - Initial Tnode counting is complete, so display results:\n");
int TotalNodeSum = 0;
int MaxGroupSize = 0;
int CurrentGroupSize;
for ( X = 0; X < MAX; X++ ) {
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
CurrentGroupSize = NodeGroupCounterInit[X][Y][0] + NodeGroupCounterInit[X][Y][1];
TotalNodeSum += CurrentGroupSize;
if ( CurrentGroupSize > MaxGroupSize ) MaxGroupSize = CurrentGroupSize;
}
}
printf("\n Total Tnode Count For The Raw-Trie = |%d|, MaxGroupSize = |%d| \n", TotalNodeSum, MaxGroupSize);
// We will have exactly enough space for all of the Tnode pointers.
printf("\nStep 6 - Allocate a 4-D array of Tnode pointers to tag redundant Tnodes for replacement.\n");
TnodePtr ****HolderOfAllTnodePointers = (TnodePtr ****)malloc(MAX*sizeof(TnodePtr ***));
for ( X = 0; X < MAX; X++ ) {
HolderOfAllTnodePointers[X] = (TnodePtr ***)malloc(SIZE_OF_CHARACTER_SET*sizeof(TnodePtr **));
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
HolderOfAllTnodePointers[X][Y] = (TnodePtr **)malloc(3*sizeof(TnodePtr *));
CurrentGroupSize = NodeGroupCounterInit[X][Y][0] + NodeGroupCounterInit[X][Y][1];
if ( CurrentGroupSize ) {
HolderOfAllTnodePointers[X][Y][2] = (TnodePtr *)malloc(CurrentGroupSize*sizeof(TnodePtr));
if ( NodeGroupCounterInit[X][Y][0] ) HolderOfAllTnodePointers[X][Y][0] = HolderOfAllTnodePointers[X][Y][2];
else HolderOfAllTnodePointers[X][Y][0] = NULL;
if ( NodeGroupCounterInit[X][Y][1] ) {
HolderOfAllTnodePointers[X][Y][1] = HolderOfAllTnodePointers[X][Y][2] + NodeGroupCounterInit[X][Y][0];
}
else HolderOfAllTnodePointers[X][Y][1] = NULL;
}
else {
HolderOfAllTnodePointers[X][Y][0] = NULL;
HolderOfAllTnodePointers[X][Y][1] = NULL;
HolderOfAllTnodePointers[X][Y][2] = NULL;
}
}
}
// A breadth-first traversal is used when populating the final array.
// It is then much more likely for living "Tnode"s to appear first, if we fill "HolderOfAllTnodePointers" breadth first.
printf("\nStep 7 - Populate the 4 dimensional Tnode pointer array, keeping DirectChild nodes closer to the end.\n");
// Use a breadth first traversal to populate the "HolderOfAllTnodePointers" array.
BreadthQueuePtr Populator = BreadthQueueInit();
BreadthQueuePopulateReductionArray(Populator, DawgRootNode(TemporaryTrie), HolderOfAllTnodePointers);
free(Populator);
// "HolderOfAllTnodePointers" Population procedure is complete.
printf("\nStep 8 - Use the stable Merge-Sort algorithm to sort [MaxChildDepth][LetterIndex] groups by CrcDigest values.\n");
TnodePtr *SupplementalArray = (TnodePtr *)malloc(MaxGroupSize*sizeof(TnodePtr));
for ( X = 0; X < MAX; X++ ) {
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
TnodeArrayMergeSortRecurse(HolderOfAllTnodePointers[X][Y][2], (NodeGroupCounterInit[X][Y][0] + NodeGroupCounterInit[X][Y][1]), SupplementalArray);
}
}
free(SupplementalArray);
// Flag all of the reduntant "Tnode"s, and store a "ReplaceMeWith" "Tnode" reference inside the "Dangling" "Tnode"s.
// "Tnode"s are compared using their "CrcDigest" values, which incorporate information from entire branch structures.
int NumberDangled;
int DangledNow;
int DirectDangled;
int TotalDangled = 0;
unsigned int CurrentCrcDigest;
TnodePtr CorrectReplacementTnode;
printf("\nStep 9 - Tag entire Tnode branch structures as Dangling - Elimination begins with DirectChild Tnodes and filters down:\n");
printf("\n This procedure is at the very heart of DAWG genesis, where the Blitzkrieg Algorithm shines with CRC, and Tnode Segmentation.\n");
printf(" Seperate groups exist for each [MaxChildDepth]-[LetterIndex] pair.\n");
printf(" Groups of similar Tnodes, are now sorted by 3 values, [BreadthFirst], [DirectChild], [CrcDigest].\n");
printf(" This Blitzkrieg Scheme means that each redundant Tnode patch will directly follow its living Tnode replacement.\n");
printf("\n ---------------------------------------------------------------------------------------------------------------------------\n");
// "X" is the current "MaxChildDepth".
for ( X = MAX - 1; X >= 0; X-- ) {
NumberDangled = 0;
DirectDangled = 0;
// "Y" is the current "LetterIndex", starting at "0".
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
CurrentGroupSize = NodeGroupCounterInit[X][Y][0] + NodeGroupCounterInit[X][Y][1];
CorrectReplacementTnode = NULL;
CurrentCrcDigest = 0;
// "Z" Will move through the current "Tnode" group, identifying the "CorrectReplacementTnode".
for ( Z = 0; Z < CurrentGroupSize; Z++ ) {
if ( HolderOfAllTnodePointers[X][Y][2][Z]->Dangling ) continue;
CorrectReplacementTnode = HolderOfAllTnodePointers[X][Y][2][Z];
CurrentCrcDigest = CorrectReplacementTnode->CrcDigest;
// "W" Tracks the "Tnodes" that will be Dangled, and shifts "Z" when it finds a new "CrcDigest".
for ( W = Z + 1; W < CurrentGroupSize; W++ ) {
if ( HolderOfAllTnodePointers[X][Y][2][W]->CrcDigest == CurrentCrcDigest) {
if ( HolderOfAllTnodePointers[X][Y][2][W]->Dangling ) continue;
if ( HolderOfAllTnodePointers[X][Y][2][W]->DirectChild == FALSE ) continue;
// If the potential replacement "Tnode" has "Protected" "Tnode"s under it, then proceed to exchange the offending branch.
if ( HolderOfAllTnodePointers[X][Y][2][W]->ProtectedUnderCount ) {
printf(" Attempting to Dangle Protected, Count = |%d|", HolderOfAllTnodePointers[X][Y][2][W]->ProtectedUnderCount);
TnodeExchangeProtectedNodeRecurse(HolderOfAllTnodePointers[X][Y][2][W], CorrectReplacementTnode);
//printf(", after swap Count = |%d|.\n", HolderOfAllTnodePointers[X][Y][2][W]->ProtectedUnderCount);
if ( HolderOfAllTnodePointers[X][Y][2][W]->ProtectedUnderCount ) {
printf("\n Exchanging the first protected Tnode did not solve the problem. Fix the Exchange procedure.\n");
break;
}
else printf(" - FIXED.\n");
}
HolderOfAllTnodePointers[X][Y][2][W]->ReplaceMeWith = CorrectReplacementTnode;
TnodeProtect(CorrectReplacementTnode);
DirectDangled += 1;
DangledNow = TnodeDangleRecurse(HolderOfAllTnodePointers[X][Y][2][W]);
NumberDangled += DangledNow;
}
else {
Z = W - 1;
break;
}
}
}
}
printf(" DirectDangled |%5d| Tnodes, and |%5d| through recursion - MCD|%2d|\n", DirectDangled, NumberDangled, X);
TotalDangled += NumberDangled;
}
printf(" ---------------------------------------------------------------------------------------------------------------------------\n\n");
int NumberOfLivingNodes;
printf(" |%6d| = Original # of Tnodes.\n", TotalNodeSum);
printf(" |%6d| = Dangled # of Tnodes.\n", TotalDangled);
printf(" |%6d| = Remaining # of Tnodes.\n", NumberOfLivingNodes = TotalNodeSum - TotalDangled);
printf("\nStep 10 - Count the number of living Tnodes by traversing the Raw-Trie to check the Dangling numbers.\n\n");
DawgGraphTabulate(TemporaryTrie, NodeGroupCounter);
int TotalDangledCheck = 0;
for ( X = 0; X < MAX; X++ ) {
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
for ( Z = 0; Z < 2; Z++ ) {
TotalDangledCheck += (NodeGroupCounterInit[X][Y][Z] - NodeGroupCounter[X][Y][Z]);
}
}
}
if ( TotalDangled == TotalDangledCheck ) printf(" Tnode Dangling count is consistent, TotalDangledCheck = |%d|.\n", TotalDangledCheck);
else printf(" MISMATCH for Tnode Dangling count.\n");
printf("\nstep 11 - Using the BlitzkriegTrieAttack, substitute Dangling Tnodes with internal \"ReplaceMeWith\" values.\n");
// Node replacement has to take place before indices are set up, so nothing points to redundant nodes.
// This step is absolutely critical. Attack the Raw Trie using the Blitzkrieg single penetration. Then Index.
BlitzkriegTrieAttack(TemporaryTrie);
printf("\n Killing complete.\n");
printf("\nStep 12 - Blitzkrieg Attack is victorious, so assign array indicies to all living Tnodes using a Breadth-First-Queue.\n");
BreadthQueuePtr OrderMatters = BreadthQueueInit();
// The Breadth-First-Queue must assign an index value to each living "Tnode" only once.
// Make sure to feed the root Tnode of "TemporaryTrie" into the "BreadthQueueUseToIndex()" function.
int IndexCount = BreadthQueueUseToIndex(OrderMatters, DawgRootNode(TemporaryTrie));
free(OrderMatters);
printf("\n Index assignment is now complete.\n");
printf("\n |%d| = NumberOfLivingNodes from after the Dangling process.\n", NumberOfLivingNodes);
printf(" |%d| = IndexCount from the breadth-first assignment function.\n", IndexCount);
// Allocate the space needed to store the "DawgArray".
Result->DawgArray = (ArrayDnodePtr)calloc((NumberOfLivingNodes + 1), sizeof(ArrayDnode));
int IndexFollow = 0;
int IndexFollower = 0;
int TransposeCount = 0;
// Roll through the pointer arrays and use the "ArrayDnodeTnodeTranspose" function to populate it.
// Set the dummy entry at the beginning of the array.
ArrayDnodeInit(&(Result->DawgArray[0]), 0, 0, 0, 0, 0, 0, 0);
Result->First = 1;
printf("\nStep 13 - Populate the new Working-Array-Dawg structure, used to verify validity and create the final integer-graph-encodings.\n");
// Scroll through "HolderOfAllTnodePointers" and look for un"Dangling" "Tnodes", if so then transpose them into "Result->DawgArray".
for ( X = MAX - 1; X >= 0; X-- ) {
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
for (Z = 0; Z < 2; Z++ ) {
for ( W = 0; W < NodeGroupCounterInit[X][Y][Z]; W++ ) {
if ( TnodeDangling(HolderOfAllTnodePointers[X][Y][Z][W]) == FALSE ) {
IndexFollow = TnodeArrayIndex(HolderOfAllTnodePointers[X][Y][Z][W]);
ArrayDnodeTnodeTranspose(&(Result->DawgArray[IndexFollow]), HolderOfAllTnodePointers[X][Y][Z][W]);
TransposeCount += 1;
if ( IndexFollow > IndexFollower ) IndexFollower = IndexFollow;
}
}
}
}
}
printf("\n |%d| = IndexFollower, which is the largest index assigned in the Working-Array-Dawg.\n", IndexFollower);
printf(" |%d| = TransposeCount, holds the number of Tnodes transposed into the Working-Array-Dawg.\n", TransposeCount);
printf(" |%d| = NumberOfLivingNodes. Make sure that these three values are equal, because they must be.\n", NumberOfLivingNodes);
if ( (IndexFollower == TransposeCount) && (IndexFollower == NumberOfLivingNodes) ) printf("\n Equality assertion passed.\n");
else printf("\n Equality assertion failed.\n");
// Conduct dynamic-memory-cleanup and free the whole Raw-Trie, which is no longer needed.
for ( X = MAX - 1; X >= 0; X-- ) {
for ( Y = 0; Y < SIZE_OF_CHARACTER_SET; Y++ ) {
for ( W = 0; W < (NodeGroupCounterInit[X][Y][0] + NodeGroupCounterInit[X][Y][1]); W++ ) {
free(HolderOfAllTnodePointers[X][Y][2][W]);
}
free(HolderOfAllTnodePointers[X][Y][2]);
free(HolderOfAllTnodePointers[X][Y]);
}
free(HolderOfAllTnodePointers[X]);
}
free(HolderOfAllTnodePointers);
free(TemporaryTrie);
printf("\nStep 14 - Creation of the traditional-DAWG is complete, so store it in a binary file for use.\n");
FILE *Data;
Data = fopen( TRADITIONAL_DAWG_DATA,"wb" );
// The "NULL" node in position "0" must be counted now.
int CurrentNodeInteger = NumberOfLivingNodes + 1;
// It is critical, especially in a binary file, that the first integer written to the file be the number of nodes stored in the file.
fwrite( &CurrentNodeInteger, sizeof(int), 1, Data );
// Write the "NULL" node to the file first.
CurrentNodeInteger = 0;
fwrite( &CurrentNodeInteger, sizeof(int), 1, Data );
for ( X = 1; X <= NumberOfLivingNodes ; X++ ){
CurrentNodeInteger = (Result->DawgArray)[X].Child;
CurrentNodeInteger <<= CHILD_BIT_SHIFT;
CurrentNodeInteger |= CharacterSet[Result->DawgArray[X].LetterIndex];
if ( Result->DawgArray[X].EndOfWordFlag ) CurrentNodeInteger |= END_OF_WORD_BIT_MASK;
if ( !Result->DawgArray[X].Next ) CurrentNodeInteger |= END_OF_LIST_BIT_MASK;
fwrite(&CurrentNodeInteger, sizeof(int), 1, Data);
}
fclose(Data);
printf( "\n The Traditional-DAWG-Encoding data file is now written.\n" );
printf("\nStep 15 - Output a text file with all the node information explicitly layed out.\n");
FILE *Text;
Text = fopen(TRADITIONAL_DAWG_TEXT_DATA,"w");
char TheNodeInBinary[32+5+1];
int CompleteThirtyTwoBitNode;
fprintf(Text, "Behold, the |%d| Traditional DAWG nodes are decoded below:\r\n\r\n", NumberOfLivingNodes);
// We are now ready to output to the text file, and the "Main" binary data file.
for ( X = 1; X <= NumberOfLivingNodes ; X++ ){
CompleteThirtyTwoBitNode = (Result->DawgArray)[X].Child;
CompleteThirtyTwoBitNode <<= CHILD_BIT_SHIFT;
CompleteThirtyTwoBitNode |= CharacterSet[Result->DawgArray[X].LetterIndex];
if ( (Result->DawgArray)[X].EndOfWordFlag == TRUE ) CompleteThirtyTwoBitNode |= END_OF_WORD_BIT_MASK;
if ( (Result->DawgArray)[X].Next == 0 ) CompleteThirtyTwoBitNode |= END_OF_LIST_BIT_MASK;
ConvertIntNodeToBinaryString(CompleteThirtyTwoBitNode, TheNodeInBinary);
fprintf(Text, "N%6d-%s, DistanceToEndOfList|%2d|", X, TheNodeInBinary, Result->DawgArray[X].Position);
fprintf(Text, ", NumberOfChildren|%2d|", Result->DawgArray[X].ChildCount);
fprintf(Text, ", Lev|%2d|", (Result->DawgArray)[X].Level);
fprintf(Text, ", {'%c',%d,%6d", CharacterSet[Result->DawgArray[X].LetterIndex], Result->DawgArray[X].EndOfWordFlag, Result->DawgArray[X].Next);
fprintf(Text, ",%6d}", (Result->DawgArray)[X].Child);
fprintf(Text, ".\r\n");
if ( CompleteThirtyTwoBitNode == 0 ) printf("\n Error in node encoding process.\n");
}
fprintf(Text, "\r\nNumber Of Living Nodes |%d| Plus The NULL Node.\r\n\r\n", NumberOfLivingNodes);
fclose(Text);
return Result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main(){
int X;
int Y;
unsigned char ThisLine[INPUT_LIMIT] = "\0";
printf("\n Hit \"Enter\" to begin the Blitzkrieg Attack Algorithm:\n\n Be dazzled, your DAWG will be created in the PAST:");
fgets(ThisLine, INPUT_LIMIT, stdin);
// All of the words of similar length will be stored sequentially in the same array so that there will be (MAX + 1) arrays in total.
// The Smallest length of a string is assumed to be 2.
unsigned char *AllWordsInEnglish[MAX + 1];
for ( X = 0; X < (MAX + 1); X++ ) AllWordsInEnglish[X] = NULL;
// Read the precompiled lookup-table from "CRC-32.dat" directly into "TheLookupTable".
FILE *TableFile;
TableFile = fopen(LOOKUP_TABLE_DATA, "rb");
fread(TheLookupTable, sizeof(unsigned int), TWO_UP_EIGHT, TableFile);
fclose(TableFile);
FILE *Input;
Input = fopen(RAW_LEXICON,"r");
int FirstLineIsSize;
int LineLength;
fgets(ThisLine, INPUT_LIMIT, Input);
CutOffExtraChars(ThisLine);
FirstLineIsSize = StringToPositiveInt(ThisLine);
printf("\n FirstLineIsSize = Number-Of-Words = |%d|\n", FirstLineIsSize);
int DictionarySizeIndex[MAX + 1];
for ( X = 0; X <= MAX; X++ ) DictionarySizeIndex[X] = 0;
char **LexiconInRam = (char**)malloc(FirstLineIsSize*sizeof(char *));
// The first line is the Number-Of-Words, so read them all into RAM, temporarily.
for ( X = 0; X < FirstLineIsSize; X++ ) {
fgets(ThisLine, INPUT_LIMIT, Input);
CutOffExtraChars(ThisLine);
MakeMeAllCapital(ThisLine);
if ( !TestForValidWord(ThisLine) ) printf("Invalid Word @ |%d|-|%s|\n", X, ThisLine);
LineLength = strlen(ThisLine);
if ( LineLength <= MAX ) DictionarySizeIndex[LineLength] += 1;
else {
printf("The word in position |%d| is too long. EXIT.\n", X);
return 0;
}
LexiconInRam[X] = (unsigned char *)malloc((LineLength + 1)*sizeof(unsigned char));
strcpy(LexiconInRam[X], ThisLine);
}
printf("\n Word-List.txt is now in RAM.\n");
// Allocate enough space to hold all of the words in "unsigned char" arrays holding character indexes.
for ( X = 2; X < (MAX + 1); X++ ) AllWordsInEnglish[X] = (unsigned char *)malloc(X*DictionarySizeIndex[X]*sizeof(unsigned char));
int CurrentTracker[MAX + 1];
memset(CurrentTracker, 0, (MAX + 1)*sizeof(int));
unsigned char CurrentWordByIndex[MAX];
int CurrentLength;
// Copy all of the words into the "LetterIndex" format "AllWordsInEnglish" array.
for ( X = 0; X < FirstLineIsSize; X++ ) {
CurrentLength = strlen(LexiconInRam[X]);
// Convert each string from its temporary ram location into the "LetterIndex" format, and copy that into "AllWordsInEnglish".
LettersToIndexConversion(LexiconInRam[X], CurrentWordByIndex);
memcpy(AllWordsInEnglish[CurrentLength] + CurrentTracker[CurrentLength]*CurrentLength, CurrentWordByIndex, CurrentLength);
CurrentTracker[CurrentLength] += 1;
}
printf("\n The words are now stored in an array according to length.\n\n");
// Make sure that the counting has resulted in all of the strings being placed correctly.
for ( X = 0; X < (MAX + 1); X++ ) {
if ( DictionarySizeIndex[X] == CurrentTracker[X] ) printf(" |%2d| Letter word count = |%5d| is verified.\n", X, CurrentTracker[X]);
else printf(" Something went wrong with |%2d| letter words.\n", X);
}
// Free the the initial dynamically allocated memory.
for ( X = 0; X < FirstLineIsSize; X++ ) free(LexiconInRam[X]);
free(LexiconInRam);
printf("\n Begin Creator init function.\n\n");
ArrayDawgPtr Adoggy = ArrayDawgInit(AllWordsInEnglish, DictionarySizeIndex);
printf("\nStep 16 - Display the Mask-Format for the DAWG int-nodes:\n\n");
char Something[32+5+1];
ConvertIntNodeToBinaryString(CHILD_INDEX_BIT_MASK, Something);
printf(" %s - CHILD_INDEX_BIT_MASK\n", Something);
ConvertIntNodeToBinaryString(END_OF_WORD_BIT_MASK, Something);
printf(" %s - END_OF_WORD_BIT_MASK\n", Something);
ConvertIntNodeToBinaryString(END_OF_LIST_BIT_MASK, Something);
printf(" %s - END_OF_LIST_BIT_MASK\n", Something);
ConvertIntNodeToBinaryString(LETTER_BIT_MASK, Something);
printf(" %s - LETTER_BIT_MASK\n", Something);
return 0;
}