home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / sort / b-sort / b-sort.c
Encoding:
Text File  |  1985-10-10  |  16.4 KB  |  474 lines

  1. /*                                                                   */
  2. /*B-SORT a sorting program                                           */
  3. /*                                                                   */
  4. /*    This is a self-contained file except for the standard included */
  5. /* definitions and is part of the larger development of a message-   */
  6. /* retrieval system. Since the algorithms involved are well known    */
  7. /* it is felt that putting this program in public domain would serve */
  8. /* a useful purpose, namely provide some examples of some programming*/
  9. /* techniques using the 'C' language. The compiler used is the BDS   */
  10. /* vs 1.46 and use is made of at least one special feature of this   */
  11. /* compiler(see 'printf' function).                                  */
  12. /*                                                                   */
  13. /*    Nominally this is a sort program and can certainly be used for */
  14. /* that purpose. However, since the main benefit of using a B-tree is*/
  15. /* when using disk-based structures, it is quite possible that this  */
  16. /* will not be your hot sort utility. It is relatively fast though   */
  17. /* and I would be interested in any comparisons anyone makes. Later  */
  18. /* effort is planned to build a virtual memory support package which */
  19. /* will then allow the 'nodes' of the b-tree to be stored as disk    */
  20. /* sectors and at that point this program will allow sorting of      */
  21. /* arbitrarily large text files. Again, that is still not the main   */
  22. /* objective for it, information retrieval is.                       */
  23. /*                                                                   */
  24. /*    I will not attempt to give an introduction to B-trees here.    */
  25. /* My primary reference and, in fact, the source of this program is  */
  26. /* N. Wirth's wonderful book "Algorithms+Data Structures=Programs".  */
  27. /* I passed this book up several times in the past, but when I was   */
  28. /* looking for a treatment of this subject, I found that it had a    */
  29. /* nice balance between explanation and example(the best way I learn)*/
  30. /* and since 'C' and PASCAL are 'kissin cousins', it was 'straight   */
  31. /* forward' to do the transcribing. So go to and read section        */
  32. /* 4.51 if you want to understand what is going on here. Also you can*/
  33. /* get a little contrast of the styles which result from the different*/
  34. /* features of the two languages(especially w re pointer variables). */
  35. /*                                                                   */
  36. /*    Things to note in this program which you may not already be    */
  37. /* familiar with are the following:                                  */
  38. /*                                                                   */
  39. /*    1) A nested data structure(PAGE) is used. This is possible     */
  40. /*       in PASCAL and PL/I too and makes life a lot more pleasant.  */
  41. /*       Try doing the same thing in BASIC or FORTRAN, what a        */
  42. /*       mess! In a sense it would be easier in assembler.           */
  43. /*                                                                   */
  44. /*    2) Note that the tree structure used is a balanced one         */
  45. /*       so that no single branch gets long at the expense of        */
  46. /*       others. To see the depth level of the tree, turn on         */
  47. /*       the SPRINT parameter and note the first column of the       */
  48. /*       output on any sort. There is a trade-off here, though       */
  49. /*       in that it takes longer to build the tree than if it        */
  50. /*       were just a binary tree(not an AVL though). Thats why       */
  51. /*       b-trees are best for retrieval rather than simple           */
  52. /*       sorts.                                                      */
  53. /*                                                                   */
  54. /*    3) The logic uses a rather interesting recursive structure     */
  55. /*       in which 'emerging' items are handed back through higher    */
  56. /*       levels until perhaps to the root of the tree. See the       */
  57. /*       variable called 'u' in function 'search'.                   */
  58. /*                                                                   */
  59. /*    4) The parameter KEYPTR allows the option of including the     */
  60. /*       string storage area in the nodes themselves as opposed      */
  61. /*       to allocated areas. The latter is faster since no move-     */
  62. /*       ment of strings is necessary after they have been given     */
  63. /*       initial allocations. However, for disk-based use the        */
  64. /*       strings(keys) would need to be in the nodes. To see the     */
  65. /*       difference in performance(big!) just undefine(comment       */
  66. /*       out) this parameter.                                        */
  67. /*                                                                   */
  68. /*    5) For further perfomance comparison play with the FAST        */
  69. /*       parameter. If undefined, the compiler will build a          */
  70. /*       version of B-sort with a high-level version of two          */
  71. /*       block move operations. These same functions are imple-      */
  72. /*       mented in assembler code in the file BDSNEW.CSM. By         */
  73. /*       linking with that special library, you can get a version    */
  74. /*       of this program which runs quite a bit faster. Let me       */
  75. /*       know if you get any amazing results.                        */
  76. /*                                                                   */
  77. /*    6) One peculiarity of the 'C' language came up in an earlier   */
  78. /*       version of B-sort, it would not sort itself! Clue, this     */
  79. /*       had to do with the way that 'printf' works. See if you      */
  80. /*       can figure out how this happened and how it was fixed.      */
  81. /*                                                                   */
  82. /*    I hope this program is useful for some in learning more about the */
  83. /* 'C' language and an interesting algorithm(the key to better software). */
  84. /* If you make any improvements give me a copy. If you mutilate it, keep */
  85. /* it to yourself.                                                   */
  86. /* Jack Riley (303) 499-9169 RCPM, Boulder, CO.                      */
  87.  
  88. /*     1983/10/14 09:45                                                 */
  89. /*     Adapted for IBM PC and Aztec C II Compiler by                    */
  90. /*           William Hutchison, President                               */
  91. /*           Algorithmic Technology, Inc.                               */
  92. /*           P.O. Box 278                                               */
  93. /*           Exton, PA 19341-0278                                       */
  94. /*           CompuServe [70665,1307]                                    */
  95. /*                                                                      */
  96. /*     Send an SASE if you would like a copy of our software catalog    */
  97. /*                                                                      */
  98.  
  99. #include <algodef.h>
  100. #ifdef MS_DOS
  101. #define FCREAT(x,y) fopen(x,y)
  102. VOID
  103. pause()
  104. {
  105. int x;
  106.  
  107. x= getchar();
  108. }                                      /* end pause */
  109. #endif
  110.  
  111. #define N 2 /* half-size of b-tree node */
  112.  
  113. /* options for customizing the program */
  114.  
  115. #undef FAST      /* uses external 'move' functions for assemlber vs */
  116. #undef SPRINT     /* provides statistics on keys in output */
  117. #define KEYPTR    /* uses pointer references to strings instead of */
  118.                   /* arrays in the b-tree nodes */
  119. #undef DIAGNOSE   /* turns on voluminous trace information on actions */
  120.                   /* taken by program logic.. perhaps instructive */
  121. /* special assigned values */
  122.  
  123. #define KEYSIZE 80
  124. #define MAX_LEN 20
  125. #define MAXPRINT 3000
  126.  
  127. /* dependent parameters */
  128.  
  129. #define NN N+N
  130. #define NM1 N-1
  131. #define NM2 N-2
  132. #define NP1 N+1
  133.  
  134. /* structure definitions */
  135.  
  136. #define ITEM struct sitem
  137. #define PAGE struct spage
  138.  
  139. ITEM  {
  140. #ifndef KEYPTR
  141.         char KEY[KEYSIZE];
  142. #endif 
  143. #ifdef KEYPTR
  144.         char *KEY;
  145. #endif 
  146.         PAGE *P;
  147.         unsigned COUNT;
  148. oneitem;
  149.  
  150. PAGE  {
  151.         unsigned M;
  152.         PAGE *P0;
  153.         ITEM E[NN];
  154. onepage;
  155.  
  156. /* external variables */
  157.  
  158. PAGE *root,*q;
  159. ITEM g;
  160.  
  161. FILE *infile, *outfile;
  162. char infilnam[MAX_LEN], instrg[MAXLINE], o_flg;
  163.  
  164. int sizitem,sizpage, maxcount, nkeys, tokcount;
  165.  
  166. /* beginning of programs */
  167.  
  168. use_err() /* Usage message: */
  169. {
  170.         fprintf(STDERR,"\nERROR: Invalid parameter specification\n\n");
  171.         fprintf(STDERR,"Usage: b-sort <filename> <flag(s)>\n\n");
  172.         fprintf(STDERR," -o <filename> = Write output to named file\n");
  173.         exit(0); 
  174. }
  175.  
  176. main(argc,argv)
  177. int argc;
  178. char **argv;
  179. {
  180.  
  181.         char *arg;
  182.  
  183.         o_flg=NO; /* output file flag */
  184.  
  185.         if (argc < 2) use_err();
  186.  
  187.         strcpy(infilnam,*++argv);
  188.  
  189.         if ((infile= fopen(infilnam, READ)) == NULL){
  190.                 fprintf(STDERR,"\nERROR: Unable to open input file: %s\n",infilnam);
  191.                 exit(1); 
  192.         }
  193.  
  194.         if (--argc > 0)
  195.                 if (*(arg=*++argv) == '-')
  196.                         if (tolower(*++arg) == 'o'){
  197.                                 o_flg++;
  198.                                 if (--argc == 0){
  199.                                         fprintf(STDERR,"\nneed output file name");
  200.                                         use_err();
  201.                                 }
  202.                                 if ((outfile= FCREAT(*++argv, WRITE)) == NULL){
  203.                                         fprintf(STDERR,"ERROR: Unable to create output file - %s\n",
  204.                                         *argv); 
  205.                                         exit(0);
  206.                                 }
  207.                         }
  208.  
  209.         root=NULL; 
  210.         sizitem=sizeof(oneitem); 
  211.         sizpage=sizeof(onepage);
  212.         tokcount=nkeys=0;
  213.  
  214. #ifdef DIAGNOSE
  215.         fprintf(STDERR,"\n&root=%x,g=%x,sizi=%d,sizp=%d",&root,g,sizitem,sizpage);
  216. #endif 
  217.  
  218.         while (fgets(instrg, MAXLINE, infile)){
  219.  
  220.                 if (trim(instrg) <= 0 ) continue;
  221.  
  222.                 instrg[KEYSIZE-1]='\0';
  223.  
  224. #ifdef DIAGNOSE
  225.                 fprintf(STDERR,"\n\nsearch key= %s",instrg);
  226. #endif 
  227.                 if (search(instrg,root,&g) ){
  228.                         q=root; 
  229.                         if ((root=alloc(sizpage)) == NULL){
  230.                                 fprintf(STDERR,"\nERROR unable to allocate page");
  231.                                 exit(2); 
  232.                         }
  233. #ifdef DIAGNOSE
  234.                         fprintf(STDERR," root=%x, q=%x",root,q);
  235. #endif 
  236.  
  237.                         root->M=1; 
  238.                         root->P0=q; 
  239.                         moveb((char *)&root->E[0], (char *)&g, sizitem);
  240.  
  241.                 }
  242.         }
  243.  
  244. #ifdef DIAGNOSE
  245.         fprintf(STDERR,"\nEnd of input\n");
  246.  
  247.         fprintf(STDERR,"\nnumber of unique keys=%d, total key count=%d\n",
  248.         nkeys,tokcount);
  249.         if (!o_flg) pause();
  250. #endif
  251.  
  252.         maxcount=MAXPRINT; 
  253.         printtree(root,1);
  254.  
  255.         fprintf(STDERR,"\n");
  256.         if (o_flg){
  257.  
  258.                 fflush(outfile); 
  259.                 fclose(outfile); 
  260.         }
  261. }
  262. char search(x,a,v)
  263. char *x;
  264. PAGE *a;
  265. ITEM *v;
  266. {
  267.  
  268.         int i,k,l,r,cmp; 
  269.         PAGE *q,*b; 
  270.         ITEM u; 
  271.         char *t;
  272.  
  273.         /* Search for key x on page a */
  274.  
  275.         if (a==NULL) /* ITEM with key x is not in tree */ {
  276.  
  277.                 ++tokcount; 
  278.                 ++nkeys; 
  279.                 defkey(&v->KEY,x);
  280.  
  281. #ifdef DIAGNOSE
  282.                 fprintf(STDERR,"\n a == null v(=%x)->KEY=%s",v,v->KEY);
  283. #endif 
  284.  
  285.                 v->COUNT=1;
  286.                 v->P=NULL; 
  287.                 return (YES) ; /* YES means not found */
  288.         }
  289.         else {
  290.                 l=0; 
  291.                 r=a->M-1; /* binary array search */
  292.                 do  {
  293.                         k=(l+r)/2;
  294.                         cmp= strcmp(x,t=(a->E[k].KEY));
  295.  
  296. #ifdef DIAGNOSE
  297.                         fprintf(STDERR,"\ncmp=%d,r=%d,l=%d,a(=%x)->P0=%x/E[k=%d].P=%x/E[k].KEY=%x=%s",
  298.                         cmp,r,l,a,a->P0,k,a->E[k].P,t,t );
  299. #endif 
  300.  
  301.                         if (cmp <= 0) r=k-1;
  302.                         if (cmp >= 0) l=k+1;
  303.  
  304.                 } 
  305.                 while (r >= l );
  306.  
  307.                 if (cmp == 0 ) /* found it, bump counter */ {
  308.                         ++tokcount; 
  309.                         ++a->E[k].COUNT; 
  310.                         return(NO); 
  311.                 }
  312.  
  313.                 else /* test if item is not on this page */ {
  314.  
  315.                         q = (r < 0 ) ? a->P0 : a->E[r].P;
  316.  
  317.                         if (!search(x,q,&u) ) return(NO);
  318.                 }
  319.         }
  320.  
  321.         /* ---- insert an item */
  322.  
  323.         if (a->M < NN){
  324.                 /* page not full yet, add to it. 'Bump' items from r+1 to
  325.                 M-1 */
  326.                 movdnb((char *)&a->E[r+2], (char *)&a->E[r+1],
  327.                         sizitem*((a->M++)-r-1) );
  328.                 moveb((char *)&a->E[r+1], (char *)&u, sizitem);
  329.  
  330.                 return(NO);
  331.         }
  332.         else {
  333.                 /* page full, split it and push center item upward in tree */
  334.                 if ((b = alloc(sizpage)) == NULL )
  335.                         fprintf(STDERR,"\nOut of memory");
  336.  
  337. #ifdef DIAGNOSE
  338.                 fprintf(STDERR,"\n\n ****** new node at %x",b);
  339. #endif 
  340.  
  341.                 if (r <= NM1 ) /* put new item in old page */ {
  342.  
  343.                         if (r == NM1 ) moveb((char *)v, (char *)&u, sizitem );
  344.                         else {
  345.                                 /* 'bump' down items from r+2 to N-1 */
  346.  
  347.                                 moveb((char *)v, (char *)&a->E[NM1], sizitem);
  348.                                 movdnb((char *)&a->E[r+2], (char *)&a->E[r+1],
  349.                                  sizitem*(NM2-r));
  350.                                 moveb((char *)&a->E[r+1], (char *)&u,sizitem);
  351.                         }
  352.                         moveb((char *)&b->E[0], (char *)&a->E[N], sizitem*N);
  353.                 }
  354.                 else {
  355.                         /* move upper N items and new item to new page */
  356.  
  357.                         moveb((char *)v, (char *)&a->E[N], sizitem ) ;
  358.  
  359.                         if ((r = r - N) > 0 ) 
  360.                                 moveb((char *)&b->E[0],
  361.                                       (char *)&a->E[NP1], sizitem*r);
  362.  
  363.                         moveb((char *)&b->E[r], (char *)&u, sizitem);
  364.  
  365.                         if ((i = NM1-r) > 0 )
  366.                                 moveb((char *)&b->E[r+1],
  367.                                       (char *)&a->E[NP1+r], sizitem * i);
  368.                 }
  369.  
  370.                 a->M = b->M = N ; 
  371.                 b->P0 = v->P; 
  372.                 v->P = b ;
  373.         }
  374.  
  375.         return (YES);
  376. }
  377. trim(strg)
  378. char *strg;
  379. {
  380.  
  381.         int l;
  382.  
  383.         l=strlen(strg);
  384.         while (strg[l] <= ' ' ){
  385.                 if (l <= 0 ) break; 
  386.                 strg[l--]='\0'; 
  387.         }
  388.         return(l);
  389. }
  390. moveb(a,b,c)
  391. char *a,*b;
  392. int c;
  393. {
  394.  
  395.         int i;
  396. #ifdef FAST /* use potentially faster move routine */
  397.         move(a,b,c); 
  398.         return;
  399. #else 
  400. /*      for (i=0 ; i < c ; ++i ) a[i]=b[i] ;     */
  401.         while (c-- > 0)
  402.             *a++= *b++;
  403. #endif 
  404. }
  405. movdnb(a,b,c)
  406. char *a,*b;
  407. int c;
  408. {
  409.  
  410.         int i;
  411. #ifdef FAST /* use potentially faster move routine */
  412.         movdn(a,b,c); 
  413.         return;
  414. #else 
  415. /*      for (; --c >= 0 ; c ) a[c]=b[c] ;        */
  416.         for (a+= c, b+= c; c-- > 0; )
  417.             *--a= *--b;
  418. #endif 
  419. }
  420.  
  421. printtree(p,l)
  422. PAGE *p;
  423. int l;
  424. {
  425.  
  426.         int i,j; 
  427.         char *t;
  428.  
  429.         if (maxcount <= 0) return;
  430.  
  431.         if (p != NULL ){
  432.  
  433.  
  434.                 printtree(p->P0, l+1 );
  435.  
  436.                 for (i=0; i <= (j=p->M-1) ; ++i ){
  437.                         --maxcount;
  438.                         fprintf(STDERR,"\n");
  439. #ifdef SPRINT
  440.                         fprintf(STDERR," %d %d ",l,p->E[i].COUNT );
  441. #endif 
  442.                         prints(p->E[i].KEY);
  443.  
  444.                         printtree(p->E[i].P,l+1); 
  445.                 }
  446.         }
  447. }
  448. defkey(a,b)
  449. char **a,*b;
  450. {
  451.  
  452. #ifdef KEYPTR
  453.         if ((*a=alloc(strlen(b)+1)) == NULL ){
  454.                 fprintf(STDERR,"\ninsufficient string storage in defkey\n");
  455.                 exit(3);
  456.         }
  457.         strcpy(*a,b);
  458. #endif 
  459. #ifndef KEYPTR
  460.         strcpy(a,b);
  461. #endif 
  462. }
  463. prints(str)
  464. char *str;
  465. {
  466.  
  467.         if (o_flg)
  468.                fputs(str,outfile);
  469.         else
  470.                fputs(str,STDOUT); /* and print out the line */
  471. }
  472.