summaryrefslogtreecommitdiffstats
path: root/baseline/source/anagram/anagram.c
diff options
context:
space:
mode:
authorJoshua Bakita <bakitajoshua@gmail.com>2019-10-07 19:13:39 -0400
committerJoshua Bakita <bakitajoshua@gmail.com>2019-10-07 19:13:39 -0400
commit386b7d3366f1359a265da207a9cafa3edf553b64 (patch)
treec76120c2c138faed822e4ae386be6ef22a738a78 /baseline/source/anagram/anagram.c
parent54a3f7091a2146b29c73a6fdc4b62a5c4ad7a3d8 (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.c670
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
190typedef unsigned int anagram_Quad; /* for building our bit mask */
191
192/* A Word remembers the information about a candidate word. */
193typedef 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;
199typedef anagram_Word *anagram_PWord;
200typedef anagram_Word **anagram_PPWord;
201
202/* A Letter remembers information about each letter in the phrase to
203 be anagrammed. */
204typedef 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;
210typedef anagram_Letter *anagram_PLetter;
211
212
213/*
214 Forward declaration of functions
215*/
216
217void anagram_init( void );
218void anagram_main( void );
219int anagram_return( void );
220int anagram_ch2i( int ch );
221void anagram_AddWords( void );
222void anagram_BuildMask( char const *pchPhrase );
223void anagram_BuildWord( char *pchWord );
224void anagram_DumpWords( void );
225void anagram_FindAnagram( anagram_Quad *pqMask,
226 anagram_PPWord ppwStart,
227 int iLetter );
228anagram_PWord anagram_NewWord( void );
229anagram_PWord anagram_NextWord( void );
230void anagram_ReadDict( void );
231void anagram_Reset( void );
232void anagram_SortCandidates( void );
233
234
235/*
236 Declaration of global variables
237*/
238
239extern char const *anagram_achPhrase[ 3 ];
240extern char const *anagram_dictionary[ anagram_DICTWORDS ];
241
242/* candidates we've found so far */
243static anagram_PWord anagram_apwCand[ anagram_MAXCAND ];
244/* how many of them? */
245static unsigned anagram_cpwCand;
246
247/* statistics on the current phrase */
248static anagram_Letter anagram_alPhrase[ anagram_ALPHABET ];
249
250/* number of letters in phrase */
251static int anagram_cchPhraseLength;
252
253/* the bit field for the full phrase */
254static anagram_Quad anagram_aqMainMask[ anagram_MAX_QUADS ];
255/* where the sign bits are */
256static anagram_Quad anagram_aqMainSign[ anagram_MAX_QUADS ];
257
258static 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. */
263static unsigned anagram_auGlobalFrequency[ anagram_ALPHABET ];
264static int anagram_achByFrequency[ anagram_ALPHABET ]; /* for sorting */
265
266/* the dictionary is read here */
267static char *anagram_pchDictionary;
268
269/* the answers */
270static anagram_PWord anagram_apwSol[ anagram_MAXSOL ];
271static int anagram_cpwLast;
272
273/* buffer to write an answer */
274static 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*/
291void 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
338void anagram_init( void )
339{
340 anagram_ReadDict();
341}
342
343
344int 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 */
362int anagram_ch2i( int ch )
363{
364 return ch - 'a';
365}
366
367
368int 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
379void 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
398void 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
449anagram_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 */
459anagram_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. */
472void 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 */
516void 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
533void 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
550void 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
620void 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
632void _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
657int 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}