diff options
| author | Joshua Bakita <bakitajoshua@gmail.com> | 2019-10-07 19:13:39 -0400 |
|---|---|---|
| committer | Joshua Bakita <bakitajoshua@gmail.com> | 2019-10-07 19:13:39 -0400 |
| commit | 386b7d3366f1359a265da207a9cafa3edf553b64 (patch) | |
| tree | c76120c2c138faed822e4ae386be6ef22a738a78 /baseline/source/anagram/anagram.c | |
| parent | 54a3f7091a2146b29c73a6fdc4b62a5c4ad7a3d8 (diff) | |
Reorganize and commit all the modified TACLeBench code and run scripts
Diffstat (limited to 'baseline/source/anagram/anagram.c')
| -rw-r--r-- | baseline/source/anagram/anagram.c | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/baseline/source/anagram/anagram.c b/baseline/source/anagram/anagram.c new file mode 100644 index 0000000..8f140a3 --- /dev/null +++ b/baseline/source/anagram/anagram.c | |||
| @@ -0,0 +1,670 @@ | |||
| 1 | /* | ||
| 2 | |||
| 3 | This program is part of the TACLeBench benchmark suite. | ||
| 4 | Version 2.0 | ||
| 5 | |||
| 6 | Name: anagram | ||
| 7 | |||
| 8 | Author: Raymond Chen | ||
| 9 | |||
| 10 | Function: A program that computes anagrams. | ||
| 11 | |||
| 12 | Source: See below. | ||
| 13 | |||
| 14 | Original name: anagram | ||
| 15 | |||
| 16 | Changes: See ChangeLog.txt | ||
| 17 | |||
| 18 | License: See below. | ||
| 19 | |||
| 20 | */ | ||
| 21 | |||
| 22 | /* | ||
| 23 | Anagram program by Raymond Chen, | ||
| 24 | inspired by a similar program by Brian Scearce | ||
| 25 | |||
| 26 | This program is Copyright 1991 by Raymond Chen. | ||
| 27 | (rjc@math.princeton.edu) | ||
| 28 | |||
| 29 | This program may be freely distributed provided all alterations | ||
| 30 | to the original are clearly indicated as such. | ||
| 31 | */ | ||
| 32 | |||
| 33 | /* There are two tricks. First is the Basic Idea: | ||
| 34 | |||
| 35 | When the user types in a phrase, the phrase is first preprocessed to | ||
| 36 | determine how many of each letter appears. A bit field is then constructed | ||
| 37 | dynamically, such that each field is large enough to hold the next power | ||
| 38 | of two larger than the number of times the character appears. For example, | ||
| 39 | if the phrase is hello, world, the bit field would be | ||
| 40 | |||
| 41 | 00 00 00 000 000 00 00 | ||
| 42 | d e h l o r w | ||
| 43 | |||
| 44 | The phrase hello, world, itself would be encoded as | ||
| 45 | |||
| 46 | 01 01 01 011 010 01 01 | ||
| 47 | d e h l o r w | ||
| 48 | |||
| 49 | and the word hollow would be encoded as | ||
| 50 | |||
| 51 | 00 00 01 010 010 00 01 | ||
| 52 | d e h l o r w | ||
| 53 | |||
| 54 | The top bit of each field is set in a special value called the sign. | ||
| 55 | Here, the sign would be | ||
| 56 | |||
| 57 | 10 10 10 100 100 10 10 | ||
| 58 | d e h l o r w | ||
| 59 | |||
| 60 | |||
| 61 | The reason for packing the values into a bit field is that the operation | ||
| 62 | of subtracting out the letters of a word from the current phrase can be | ||
| 63 | carried out in parallel. for example, subtracting the word hello from | ||
| 64 | the phrase hello, world, is merely | ||
| 65 | |||
| 66 | d e h l o r w | ||
| 67 | 01 01 01 011 010 01 01 (dehllloorw) | ||
| 68 | - 00 00 01 010 010 00 01 (hlloow) | ||
| 69 | ======================== | ||
| 70 | 01 01 00 001 000 01 00 (delr) | ||
| 71 | |||
| 72 | Since none of the sign bits is set, the word fits, and we can continue. | ||
| 73 | Suppose the next word we tried was hood. | ||
| 74 | |||
| 75 | d e h l o r w | ||
| 76 | 01 01 00 001 000 01 00 (delr) | ||
| 77 | - 01 00 01 000 010 00 00 (hood) | ||
| 78 | ======================== | ||
| 79 | 00 00 11 000 110 01 00 | ||
| 80 | ^ ^ | ||
| 81 | A sign bit is set. (Two, actually.) This means that hood does not | ||
| 82 | fit in delr, so we skip it and try another word. (Observe that | ||
| 83 | when a sign bit becomes set, it screws up the values for the letters to | ||
| 84 | the left of that bit, but that's not important.) | ||
| 85 | |||
| 86 | The inner loop of an anagram program is testing to see if a | ||
| 87 | word fits in the collection of untried letters. Traditional methods | ||
| 88 | keep an array of 26 integers, which are then compared in turn. This | ||
| 89 | means that there are 26 comparisons per word. | ||
| 90 | |||
| 91 | This method reduces the number of comparisons to MAX_QUAD, typically 2. | ||
| 92 | Instead of looping through an array, we merely perform the indicated | ||
| 93 | subtraction and test if any of the sign bits is set. | ||
| 94 | */ | ||
| 95 | |||
| 96 | /* The nuts and bolts: | ||
| 97 | |||
| 98 | The dictionary is loaded and preprocessed. The preprocessed dictionary | ||
| 99 | is a concatenation of copies of the structure: | ||
| 100 | |||
| 101 | struct dictword { | ||
| 102 | char bStructureSize; -- size of this structure | ||
| 103 | char cLetters; -- number of letters in the word | ||
| 104 | char achWord[]; -- the word itself (0-terminated) | ||
| 105 | } | ||
| 106 | |||
| 107 | Since this is a variable-sized structure, we keep its size in the structure | ||
| 108 | itself for rapid stepping through the table. | ||
| 109 | |||
| 110 | When a phrase is typed in, it is first preprocessed as described in the | ||
| 111 | Basic Idea. We then go through the dictionary, testing each word. If | ||
| 112 | the word fits in our phrase, we build the bit field for its frequency | ||
| 113 | table and add it to the list of candidates. | ||
| 114 | */ | ||
| 115 | |||
| 116 | /* | ||
| 117 | The Second Trick: | ||
| 118 | |||
| 119 | Before diving into our anagram search, we then tabulate how many times | ||
| 120 | each letter appears in our list of candidates, and sort the table, with | ||
| 121 | the rarest letter first. | ||
| 122 | |||
| 123 | We then do our anagram search. | ||
| 124 | |||
| 125 | Like most anagram programs, this program does a depth-first search. | ||
| 126 | Although most anagram programs do some sort of heuristics to decide what | ||
| 127 | order to place words in the list_of_candidates, the search itself proceeds | ||
| 128 | according to a greedy algorithm. That is, once you find a word that fits, | ||
| 129 | subtract it and recurse. | ||
| 130 | |||
| 131 | This anagram program exercises some restraint and does not march down | ||
| 132 | every branch that shows itself. Instead, it only goes down branches | ||
| 133 | that use the rarest unused letter. This helps to find dead ends faster. | ||
| 134 | |||
| 135 | FindAnagram(unused_letters, list_of_candidates) { | ||
| 136 | l = the rarest letter as yet unused | ||
| 137 | For word in list_of_candidates { | ||
| 138 | if word does not fit in unused_letters, go on to the next word. | ||
| 139 | if word does not contain l, defer. | ||
| 140 | FindAnagram(unused_letters - word, list_of_candidates[word,...]) | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | |||
| 145 | The heuristic of the Second Trick can probably be improved. I invite | ||
| 146 | anyone willing to improve it to do so. | ||
| 147 | */ | ||
| 148 | |||
| 149 | /* Before compiling, make sure Quad and MASK_BITS are set properly. For best | ||
| 150 | results, make Quad the largest integer size supported on your machine. | ||
| 151 | So if your machine has long longs, make Quad an unsigned long long. | ||
| 152 | (I called it Quad because on most machines, the largest integer size | ||
| 153 | supported is a four-byte unsigned long.) | ||
| 154 | |||
| 155 | If you need to be able to anagram larger phrases, increase MAX_QUADS. | ||
| 156 | If you increase it beyond 4, you'll have to add a few more loop unrolling | ||
| 157 | steps to FindAnagram. | ||
| 158 | */ | ||
| 159 | |||
| 160 | #include "../extra.h" | ||
| 161 | #include "anagram_ctype.h" | ||
| 162 | #include "anagram_stdlib.h" | ||
| 163 | #include "anagram_strings.h" | ||
| 164 | |||
| 165 | #include "anagram_compare.h" | ||
| 166 | |||
| 167 | |||
| 168 | /* | ||
| 169 | Defines | ||
| 170 | */ | ||
| 171 | |||
| 172 | #define anagram_DICTWORDS 2279 | ||
| 173 | #define anagram_MASK_BITS 32 /* number of bits in a Quad */ | ||
| 174 | #define anagram_MAX_QUADS 2 /* controls largest phrase */ | ||
| 175 | #define anagram_MAXCAND 100 /* candidates */ | ||
| 176 | #define anagram_MAXSOL 51 /* words in the solution */ | ||
| 177 | #define anagram_ALPHABET 26 /* letters in the alphabet */ | ||
| 178 | |||
| 179 | #define anagram_OneStep( i ) \ | ||
| 180 | if ( ( aqNext[ i ] = pqMask[ i ] - pw->aqMask[ i ] ) & anagram_aqMainSign[ i ] ) { \ | ||
| 181 | ppwStart ++; \ | ||
| 182 | continue; \ | ||
| 183 | } | ||
| 184 | |||
| 185 | |||
| 186 | /* | ||
| 187 | Type definitions | ||
| 188 | */ | ||
| 189 | |||
| 190 | typedef unsigned int anagram_Quad; /* for building our bit mask */ | ||
| 191 | |||
| 192 | /* A Word remembers the information about a candidate word. */ | ||
| 193 | typedef struct { | ||
| 194 | char *pchWord; /* the word itself */ | ||
| 195 | anagram_Quad aqMask[ anagram_MAX_QUADS ]; /* the word's mask */ | ||
| 196 | unsigned cchLength; /* letters in the word */ | ||
| 197 | char padding[4]; | ||
| 198 | } anagram_Word; | ||
| 199 | typedef anagram_Word *anagram_PWord; | ||
| 200 | typedef anagram_Word **anagram_PPWord; | ||
| 201 | |||
| 202 | /* A Letter remembers information about each letter in the phrase to | ||
| 203 | be anagrammed. */ | ||
| 204 | typedef struct { | ||
| 205 | unsigned uFrequency; /* how many times it appears */ | ||
| 206 | unsigned uShift; /* how to mask */ | ||
| 207 | unsigned uBits; /* the bit mask itself */ | ||
| 208 | unsigned iq; /* which Quad to inspect? */ | ||
| 209 | } anagram_Letter; | ||
| 210 | typedef anagram_Letter *anagram_PLetter; | ||
| 211 | |||
| 212 | |||
| 213 | /* | ||
| 214 | Forward declaration of functions | ||
| 215 | */ | ||
| 216 | |||
| 217 | void anagram_init( void ); | ||
| 218 | void anagram_main( void ); | ||
| 219 | int anagram_return( void ); | ||
| 220 | int anagram_ch2i( int ch ); | ||
| 221 | void anagram_AddWords( void ); | ||
| 222 | void anagram_BuildMask( char const *pchPhrase ); | ||
| 223 | void anagram_BuildWord( char *pchWord ); | ||
| 224 | void anagram_DumpWords( void ); | ||
| 225 | void anagram_FindAnagram( anagram_Quad *pqMask, | ||
| 226 | anagram_PPWord ppwStart, | ||
| 227 | int iLetter ); | ||
| 228 | anagram_PWord anagram_NewWord( void ); | ||
| 229 | anagram_PWord anagram_NextWord( void ); | ||
| 230 | void anagram_ReadDict( void ); | ||
| 231 | void anagram_Reset( void ); | ||
| 232 | void anagram_SortCandidates( void ); | ||
| 233 | |||
| 234 | |||
| 235 | /* | ||
| 236 | Declaration of global variables | ||
| 237 | */ | ||
| 238 | |||
| 239 | extern char const *anagram_achPhrase[ 3 ]; | ||
| 240 | extern char const *anagram_dictionary[ anagram_DICTWORDS ]; | ||
| 241 | |||
| 242 | /* candidates we've found so far */ | ||
| 243 | static anagram_PWord anagram_apwCand[ anagram_MAXCAND ]; | ||
| 244 | /* how many of them? */ | ||
| 245 | static unsigned anagram_cpwCand; | ||
| 246 | |||
| 247 | /* statistics on the current phrase */ | ||
| 248 | static anagram_Letter anagram_alPhrase[ anagram_ALPHABET ]; | ||
| 249 | |||
| 250 | /* number of letters in phrase */ | ||
| 251 | static int anagram_cchPhraseLength; | ||
| 252 | |||
| 253 | /* the bit field for the full phrase */ | ||
| 254 | static anagram_Quad anagram_aqMainMask[ anagram_MAX_QUADS ]; | ||
| 255 | /* where the sign bits are */ | ||
| 256 | static anagram_Quad anagram_aqMainSign[ anagram_MAX_QUADS ]; | ||
| 257 | |||
| 258 | static const int anagram_cchMinLength = 3; | ||
| 259 | |||
| 260 | /* auGlobalFrequency counts the number of times each letter appears, | ||
| 261 | summed over all candidate words. This is used to decide which letter | ||
| 262 | to attack first. */ | ||
| 263 | static unsigned anagram_auGlobalFrequency[ anagram_ALPHABET ]; | ||
| 264 | static int anagram_achByFrequency[ anagram_ALPHABET ]; /* for sorting */ | ||
| 265 | |||
| 266 | /* the dictionary is read here */ | ||
| 267 | static char *anagram_pchDictionary; | ||
| 268 | |||
| 269 | /* the answers */ | ||
| 270 | static anagram_PWord anagram_apwSol[ anagram_MAXSOL ]; | ||
| 271 | static int anagram_cpwLast; | ||
| 272 | |||
| 273 | /* buffer to write an answer */ | ||
| 274 | static char anagram_buffer[30]; | ||
| 275 | |||
| 276 | /* | ||
| 277 | Initialization- and return-value-related functions | ||
| 278 | */ | ||
| 279 | |||
| 280 | /* ReadDict -- read the dictionary file into memory and preprocess it | ||
| 281 | |||
| 282 | A word of length cch in the dictionary is encoded as follows: | ||
| 283 | |||
| 284 | byte 0 = cch + 3 | ||
| 285 | byte 1 = number of letters in the word | ||
| 286 | byte 2... = the word itself, null-terminated | ||
| 287 | |||
| 288 | Observe that cch+3 is the length of the total encoding. These | ||
| 289 | byte streams are concatenated, and terminated with a 0. | ||
| 290 | */ | ||
| 291 | void anagram_ReadDict( void ) | ||
| 292 | { | ||
| 293 | char *pch; | ||
| 294 | char *pchBase; | ||
| 295 | unsigned len; | ||
| 296 | unsigned cWords = 0; | ||
| 297 | unsigned cLetters; | ||
| 298 | int i; | ||
| 299 | volatile char bitmask = 0; | ||
| 300 | |||
| 301 | len = 0; | ||
| 302 | _Pragma( "loopbound min 2279 max 2279" ) | ||
| 303 | for ( i = 0; i < anagram_DICTWORDS; i ++ ) { | ||
| 304 | unsigned strlen = 0; | ||
| 305 | _Pragma( "loopbound min 1 max 5" ) | ||
| 306 | while ( anagram_dictionary[ i ][ strlen ] != 0 ) | ||
| 307 | strlen ++; | ||
| 308 | len += strlen + 2; | ||
| 309 | } | ||
| 310 | |||
| 311 | pchBase = anagram_pchDictionary = ( char * )anagram_malloc( len ); | ||
| 312 | |||
| 313 | _Pragma( "loopbound min 2279 max 2279" ) | ||
| 314 | for ( i = 0; i < anagram_DICTWORDS; i ++ ) { | ||
| 315 | int index = 0; | ||
| 316 | pch = pchBase + 2; /* reserve for length */ | ||
| 317 | cLetters = 0; | ||
| 318 | |||
| 319 | _Pragma( "loopbound min 1 max 5" ) | ||
| 320 | while ( anagram_dictionary[ i ][ index ] != '\0' ) { | ||
| 321 | if ( anagram_isalpha( anagram_dictionary[ i ][ index ] ) ) | ||
| 322 | cLetters ++; | ||
| 323 | *pch ++ = anagram_dictionary[ i ][ index ]; | ||
| 324 | index ++; | ||
| 325 | *( pch - 1 ) ^= bitmask; | ||
| 326 | } | ||
| 327 | *pch ++ = '\0'; | ||
| 328 | *pchBase = ( char )( pch - pchBase ); | ||
| 329 | pchBase[ 1 ] = ( char )cLetters; | ||
| 330 | pchBase = pch; | ||
| 331 | cWords ++; | ||
| 332 | } | ||
| 333 | |||
| 334 | *pchBase ++ = 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | |||
| 338 | void anagram_init( void ) | ||
| 339 | { | ||
| 340 | anagram_ReadDict(); | ||
| 341 | } | ||
| 342 | |||
| 343 | |||
| 344 | int anagram_return( void ) | ||
| 345 | { | ||
| 346 | int i; | ||
| 347 | char const *answer = "duke rip amy"; | ||
| 348 | |||
| 349 | for ( i = 0; i < 12; i++ ) | ||
| 350 | if ( answer[ i ] != anagram_buffer[ i ] ) | ||
| 351 | return 1; | ||
| 352 | |||
| 353 | return 0; | ||
| 354 | } | ||
| 355 | |||
| 356 | |||
| 357 | /* | ||
| 358 | Core benchmark functions | ||
| 359 | */ | ||
| 360 | |||
| 361 | /* convert letter to index */ | ||
| 362 | int anagram_ch2i( int ch ) | ||
| 363 | { | ||
| 364 | return ch - 'a'; | ||
| 365 | } | ||
| 366 | |||
| 367 | |||
| 368 | int anagram_CompareFrequency( char *pch1, char *pch2 ) | ||
| 369 | { | ||
| 370 | return anagram_auGlobalFrequency[ ( (int) *pch1 ) ] < | ||
| 371 | anagram_auGlobalFrequency[ ( (int) *pch2 ) ] | ||
| 372 | ? -1 : | ||
| 373 | anagram_auGlobalFrequency[ ( (int) *pch1 ) ] == | ||
| 374 | anagram_auGlobalFrequency[ ( (int) *pch2 ) ] | ||
| 375 | ? 0 : 1; | ||
| 376 | } | ||
| 377 | |||
| 378 | |||
| 379 | void anagram_Reset( void ) | ||
| 380 | { | ||
| 381 | anagram_bzero( ( char * )anagram_alPhrase, | ||
| 382 | sizeof( anagram_Letter ) * anagram_ALPHABET ); | ||
| 383 | anagram_bzero( ( char * )anagram_aqMainMask, | ||
| 384 | sizeof( anagram_Quad ) * anagram_MAX_QUADS ); | ||
| 385 | anagram_bzero( ( char * )anagram_aqMainSign, | ||
| 386 | sizeof( anagram_Quad ) * anagram_MAX_QUADS ); | ||
| 387 | anagram_bzero( ( char * )anagram_auGlobalFrequency, | ||
| 388 | sizeof( unsigned ) * anagram_ALPHABET ); | ||
| 389 | anagram_bzero( ( char * )anagram_achByFrequency, | ||
| 390 | sizeof( int ) * anagram_ALPHABET ); | ||
| 391 | anagram_bzero( ( char * )anagram_apwCand, | ||
| 392 | sizeof( anagram_PWord ) * anagram_MAXCAND ); | ||
| 393 | anagram_cchPhraseLength = 0; | ||
| 394 | anagram_cpwCand = 0; | ||
| 395 | } | ||
| 396 | |||
| 397 | |||
| 398 | void anagram_BuildMask( char const *pchPhrase ) | ||
| 399 | { | ||
| 400 | int i; | ||
| 401 | int ch; | ||
| 402 | unsigned iq; /* which Quad? */ | ||
| 403 | unsigned int cbtUsed; /* bits used in the current Quad */ | ||
| 404 | unsigned int cbtNeed; /* bits needed for current letter */ | ||
| 405 | anagram_Quad qNeed; /* used to build the mask */ | ||
| 406 | |||
| 407 | /* Tabulate letter frequencies in the phrase */ | ||
| 408 | anagram_cchPhraseLength = 0; | ||
| 409 | _Pragma( "loopbound min 11 max 12" ) | ||
| 410 | while ( ( ch = *pchPhrase ++ ) != '\0' ) { | ||
| 411 | if ( anagram_isalpha( ch ) ) { | ||
| 412 | ch = anagram_tolower( ch ); | ||
| 413 | anagram_alPhrase[ anagram_ch2i( ch ) ].uFrequency ++; | ||
| 414 | anagram_cchPhraseLength ++; | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | /* Build masks */ | ||
| 419 | iq = 0; /* which quad being used */ | ||
| 420 | cbtUsed = 0; /* bits used so far */ | ||
| 421 | |||
| 422 | _Pragma( "loopbound min 26 max 26" ) | ||
| 423 | for ( i = 0; i < anagram_ALPHABET; i ++ ) { | ||
| 424 | if ( anagram_alPhrase[ i ].uFrequency == 0 ) { | ||
| 425 | anagram_auGlobalFrequency[ i ] = ~0u; /* to make it sort last */ | ||
| 426 | } else { | ||
| 427 | anagram_auGlobalFrequency[ i ] = 0u; | ||
| 428 | _Pragma( "loopbound min 1 max 2" ) | ||
| 429 | for ( cbtNeed = 1, qNeed = 1; | ||
| 430 | anagram_alPhrase[ i ].uFrequency >= qNeed; | ||
| 431 | cbtNeed ++, qNeed <<= 1 ) | ||
| 432 | ; | ||
| 433 | if ( cbtUsed + cbtNeed > anagram_MASK_BITS ) | ||
| 434 | cbtUsed = 0; | ||
| 435 | anagram_alPhrase[ i ].uBits = qNeed - 1; | ||
| 436 | if ( cbtUsed ) | ||
| 437 | qNeed <<= cbtUsed; | ||
| 438 | anagram_aqMainSign[ iq ] |= qNeed; | ||
| 439 | anagram_aqMainMask[ iq ] |= | ||
| 440 | ( anagram_Quad )anagram_alPhrase[ i ].uFrequency << cbtUsed; | ||
| 441 | anagram_alPhrase[ i ].uShift = cbtUsed; | ||
| 442 | anagram_alPhrase[ i ].iq = iq; | ||
| 443 | cbtUsed += cbtNeed; | ||
| 444 | } | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | |||
| 449 | anagram_PWord anagram_NewWord( void ) | ||
| 450 | { | ||
| 451 | anagram_PWord pw; | ||
| 452 | |||
| 453 | pw = ( anagram_Word * )anagram_malloc( sizeof( anagram_Word ) ); | ||
| 454 | return pw; | ||
| 455 | } | ||
| 456 | |||
| 457 | |||
| 458 | /* NextWord -- get another candidate entry, creating if necessary */ | ||
| 459 | anagram_PWord anagram_NextWord( void ) | ||
| 460 | { | ||
| 461 | anagram_PWord pw; | ||
| 462 | pw = anagram_apwCand[ anagram_cpwCand ++ ]; | ||
| 463 | if ( pw != 0 ) | ||
| 464 | return pw; | ||
| 465 | anagram_apwCand[ anagram_cpwCand - 1 ] = anagram_NewWord(); | ||
| 466 | return anagram_apwCand[ anagram_cpwCand - 1 ]; | ||
| 467 | } | ||
| 468 | |||
| 469 | |||
| 470 | /* BuildWord -- build a Word structure from an ASCII word | ||
| 471 | If the word does not fit, then do nothing. */ | ||
| 472 | void anagram_BuildWord( char *pchWord ) | ||
| 473 | { | ||
| 474 | unsigned char cchFrequency[ anagram_ALPHABET ]; | ||
| 475 | int i; | ||
| 476 | char *pch = pchWord; | ||
| 477 | anagram_PWord pw; | ||
| 478 | unsigned int cchLength = 0; | ||
| 479 | |||
| 480 | anagram_bzero( ( char * )cchFrequency, | ||
| 481 | sizeof( unsigned char ) * anagram_ALPHABET ); | ||
| 482 | |||
| 483 | /* Build frequency table */ | ||
| 484 | _Pragma( "loopbound min 3 max 636" ) | ||
| 485 | while ( ( i = *pch ++ ) != '\0' ) { | ||
| 486 | if ( !anagram_isalpha( i ) ) | ||
| 487 | continue; | ||
| 488 | i = anagram_ch2i( anagram_tolower( i ) ); | ||
| 489 | if ( ++ cchFrequency[ i ] > anagram_alPhrase[ i ].uFrequency ) | ||
| 490 | return ; | ||
| 491 | ++ cchLength; | ||
| 492 | } | ||
| 493 | |||
| 494 | /* Update global count */ | ||
| 495 | _Pragma( "loopbound min 26 max 26" ) | ||
| 496 | for ( i = 0; i < anagram_ALPHABET; i ++ ) | ||
| 497 | anagram_auGlobalFrequency[ i ] += cchFrequency[ i ]; | ||
| 498 | |||
| 499 | /* Create a Word structure and fill it in, including building the | ||
| 500 | bitfield of frequencies. */ | ||
| 501 | pw = anagram_NextWord(); | ||
| 502 | anagram_bzero( ( char * )( pw->aqMask ), | ||
| 503 | sizeof( anagram_Quad ) * anagram_MAX_QUADS ); | ||
| 504 | |||
| 505 | pw->pchWord = pchWord; | ||
| 506 | pw->cchLength = cchLength; | ||
| 507 | _Pragma( "loopbound min 26 max 26" ) | ||
| 508 | for ( i = 0; i < anagram_ALPHABET; i ++ ) { | ||
| 509 | pw->aqMask[ anagram_alPhrase[i].iq ] |= | ||
| 510 | ( anagram_Quad )cchFrequency[ i ] << anagram_alPhrase[ i ].uShift; | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | |||
| 515 | /* AddWords -- build the list of candidates */ | ||
| 516 | void anagram_AddWords( void ) | ||
| 517 | { | ||
| 518 | char *pch = anagram_pchDictionary; /* walk through the dictionary */ | ||
| 519 | |||
| 520 | anagram_cpwCand = 0; | ||
| 521 | |||
| 522 | _Pragma( "loopbound min 1967 max 1967" ) | ||
| 523 | while ( *pch ) { | ||
| 524 | if ( ( pch[ 1 ] >= anagram_cchMinLength && | ||
| 525 | pch[ 1 ] + anagram_cchMinLength <= anagram_cchPhraseLength ) | ||
| 526 | || pch[ 1 ] == anagram_cchPhraseLength ) | ||
| 527 | anagram_BuildWord( pch + 2 ); | ||
| 528 | pch += *pch; | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | |||
| 533 | void anagram_DumpWords( void ) | ||
| 534 | { | ||
| 535 | int i, j; | ||
| 536 | int offset = 0; | ||
| 537 | _Pragma( "loopbound min 3 max 3" ) | ||
| 538 | for ( i = 0; i < anagram_cpwLast; i ++ ) { | ||
| 539 | _Pragma( "loopbound min 3 max 5" ) | ||
| 540 | for ( j = 0; anagram_apwSol[ i ]->pchWord[ j ] != '\0'; j ++ ) | ||
| 541 | anagram_buffer[ offset + j ] = anagram_apwSol[ i ]->pchWord[ j ]; | ||
| 542 | offset += j; | ||
| 543 | |||
| 544 | anagram_buffer[ offset ++ ] = ' '; | ||
| 545 | } | ||
| 546 | anagram_buffer[ offset ++ ] = '\0'; | ||
| 547 | } | ||
| 548 | |||
| 549 | |||
| 550 | void anagram_FindAnagram( anagram_Quad *pqMask, anagram_PPWord ppwStart, | ||
| 551 | int iLetter ) | ||
| 552 | { | ||
| 553 | anagram_Quad aqNext[ anagram_MAX_QUADS ]; | ||
| 554 | register anagram_PWord pw; | ||
| 555 | anagram_Quad qMask; | ||
| 556 | unsigned iq; | ||
| 557 | anagram_PPWord ppwEnd = &anagram_apwCand[ 0 ]; | ||
| 558 | ppwEnd += anagram_cpwCand; | ||
| 559 | |||
| 560 | _Pragma( "loopbound min 1 max 7" ) | ||
| 561 | while ( 1 ) { | ||
| 562 | iq = anagram_alPhrase[ anagram_achByFrequency[iLetter] ].iq; | ||
| 563 | qMask = anagram_alPhrase[ anagram_achByFrequency[iLetter] ].uBits << | ||
| 564 | anagram_alPhrase[ anagram_achByFrequency[iLetter] ].uShift; | ||
| 565 | if ( pqMask[ iq ] & qMask ) | ||
| 566 | break; | ||
| 567 | iLetter ++; | ||
| 568 | } | ||
| 569 | |||
| 570 | _Pragma( "loopbound min 0 max 114" ) | ||
| 571 | while ( ppwStart < ppwEnd ) { | ||
| 572 | pw = *ppwStart; | ||
| 573 | |||
| 574 | #if anagram_MAX_QUADS > 0 | ||
| 575 | anagram_OneStep( 0 ); | ||
| 576 | #endif | ||
| 577 | |||
| 578 | #if anagram_MAX_QUADS > 1 | ||
| 579 | anagram_OneStep( 1 ); | ||
| 580 | #endif | ||
| 581 | |||
| 582 | #if anagram_MAX_QUADS > 2 | ||
| 583 | anagram_OneStep( 2 ); | ||
| 584 | #endif | ||
| 585 | |||
| 586 | #if anagram_MAX_QUADS > 3 | ||
| 587 | anagram_OneStep( 3 ); | ||
| 588 | #endif | ||
| 589 | |||
| 590 | #if anagram_MAX_QUADS > 4 | ||
| 591 | @@"Add more unrolling steps here, please."@@ | ||
| 592 | #endif | ||
| 593 | |||
| 594 | /* If the pivot letter isn't present, defer this word until later */ | ||
| 595 | if ( ( pw->aqMask[ iq ] & qMask ) == 0 ) { | ||
| 596 | *ppwStart = *( -- ppwEnd ); | ||
| 597 | *ppwEnd = pw; | ||
| 598 | continue; | ||
| 599 | } | ||
| 600 | |||
| 601 | /* If we get here, this means the word fits. */ | ||
| 602 | anagram_apwSol[ anagram_cpwLast ++ ] = pw; | ||
| 603 | if ( anagram_cchPhraseLength -= pw->cchLength ) { /* recurse */ | ||
| 604 | /* The recursive call scrambles the tail, so we have to be | ||
| 605 | pessimistic. */ | ||
| 606 | ppwEnd = &anagram_apwCand[ 0 ]; | ||
| 607 | ppwEnd += anagram_cpwCand; | ||
| 608 | anagram_FindAnagram( &aqNext[ 0 ], ppwStart, iLetter ); | ||
| 609 | } else { /* found one */ | ||
| 610 | anagram_DumpWords(); | ||
| 611 | } | ||
| 612 | anagram_cchPhraseLength += pw->cchLength; | ||
| 613 | -- anagram_cpwLast; | ||
| 614 | ppwStart ++; | ||
| 615 | continue; | ||
| 616 | } | ||
| 617 | } | ||
| 618 | |||
| 619 | |||
| 620 | void anagram_SortCandidates( void ) | ||
| 621 | { | ||
| 622 | int i; | ||
| 623 | |||
| 624 | /* Sort the letters by frequency */ | ||
| 625 | _Pragma( "loopbound min 26 max 26" ) | ||
| 626 | for ( i = 0; i < anagram_ALPHABET; i ++ ) | ||
| 627 | anagram_achByFrequency[ i ] = i; | ||
| 628 | anagram_qsort( anagram_achByFrequency, anagram_ALPHABET, sizeof( int ) ); | ||
| 629 | } | ||
| 630 | |||
| 631 | |||
| 632 | void _Pragma( "entrypoint" ) anagram_main( void ) | ||
| 633 | { | ||
| 634 | int i; | ||
| 635 | |||
| 636 | _Pragma( "loopbound min 3 max 3" ) | ||
| 637 | for ( i = 0; i < 3; i ++ ) { | ||
| 638 | anagram_Reset(); | ||
| 639 | anagram_BuildMask( anagram_achPhrase[ i ] ); | ||
| 640 | anagram_AddWords(); | ||
| 641 | if ( anagram_cpwCand == 0 || anagram_cchPhraseLength == 0 ) | ||
| 642 | continue; | ||
| 643 | |||
| 644 | anagram_cpwLast = 0; | ||
| 645 | anagram_SortCandidates(); | ||
| 646 | _Pragma( "marker call_find" ) | ||
| 647 | anagram_FindAnagram( anagram_aqMainMask, anagram_apwCand, 0 ); | ||
| 648 | _Pragma( "flowrestriction 1*anagram_FindAnagram <= 51*call_find" ) | ||
| 649 | } | ||
| 650 | } | ||
| 651 | |||
| 652 | |||
| 653 | /* | ||
| 654 | Main function | ||
| 655 | */ | ||
| 656 | |||
| 657 | int main(int argc, char **argv) | ||
| 658 | { | ||
| 659 | SET_UP | ||
| 660 | //int jobsComplete; | ||
| 661 | //int maxJobs=100; | ||
| 662 | //for(jobsComplete=-1; jobsComplete<maxJobs; jobsComplete++){ | ||
| 663 | START_LOOP | ||
| 664 | anagram_init(); | ||
| 665 | anagram_main(); | ||
| 666 | STOP_LOOP | ||
| 667 | //} | ||
| 668 | WRITE_TO_FILE | ||
| 669 | return anagram_return(); | ||
| 670 | } | ||
