Tesseract  3.02
tesseract-ocr/ccstruct/statistc.cpp
Go to the documentation of this file.
00001 /**********************************************************************
00002  * File:        statistc.c  (Formerly stats.c)
00003  * Description: Simple statistical package for integer values.
00004  * Author:                                      Ray Smith
00005  * Created:                                     Mon Feb 04 16:56:05 GMT 1991
00006  *
00007  * (C) Copyright 1991, Hewlett-Packard Ltd.
00008  ** Licensed under the Apache License, Version 2.0 (the "License");
00009  ** you may not use this file except in compliance with the License.
00010  ** You may obtain a copy of the License at
00011  ** http://www.apache.org/licenses/LICENSE-2.0
00012  ** Unless required by applicable law or agreed to in writing, software
00013  ** distributed under the License is distributed on an "AS IS" BASIS,
00014  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  ** See the License for the specific language governing permissions and
00016  ** limitations under the License.
00017  *
00018  **********************************************************************/
00019 
00020 #include          "mfcpch.h"     //precompiled headers
00021 #include          "statistc.h"
00022 #include          <string.h>
00023 #include          <math.h>
00024 #include          <stdlib.h>
00025 #include          "helpers.h"
00026 #include          "scrollview.h"
00027 #include          "tprintf.h"
00028 
00029 // Include automatically generated configuration file if running autoconf.
00030 #ifdef HAVE_CONFIG_H
00031 #include "config_auto.h"
00032 #endif
00033 
00034 /**********************************************************************
00035  * STATS::STATS
00036  *
00037  * Construct a new stats element by allocating and zeroing the memory.
00038  **********************************************************************/
00039 STATS::STATS(inT32 min_bucket_value, inT32 max_bucket_value_plus_1) {
00040   if (max_bucket_value_plus_1 <= min_bucket_value) {
00041     min_bucket_value = 0;
00042     max_bucket_value_plus_1 = 1;
00043   }
00044   rangemin_ = min_bucket_value;                // setup
00045   rangemax_ = max_bucket_value_plus_1;
00046   buckets_ = new inT32[rangemax_ - rangemin_];
00047   clear();
00048 }
00049 
00050 STATS::STATS() {
00051   rangemax_ = 0;
00052   rangemin_ = 0;
00053   buckets_ = NULL;
00054 }
00055 
00056 /**********************************************************************
00057  * STATS::set_range
00058  *
00059  * Alter the range on an existing stats element.
00060  **********************************************************************/
00061 bool STATS::set_range(inT32 min_bucket_value, inT32 max_bucket_value_plus_1) {
00062   if (max_bucket_value_plus_1 <= min_bucket_value) {
00063     return false;
00064   }
00065   if (rangemax_ - rangemin_ != max_bucket_value_plus_1 - min_bucket_value) {
00066     delete [] buckets_;
00067     buckets_ = new inT32[max_bucket_value_plus_1 - min_bucket_value];
00068   }
00069   rangemin_ = min_bucket_value;                // setup
00070   rangemax_ = max_bucket_value_plus_1;
00071   clear();                // zero it
00072   return true;
00073 }
00074 
00075 /**********************************************************************
00076  * STATS::clear
00077  *
00078  * Clear out the STATS class by zeroing all the buckets.
00079  **********************************************************************/
00080 void STATS::clear() {  // clear out buckets
00081   total_count_ = 0;
00082   if (buckets_ != NULL)
00083     memset(buckets_, 0, (rangemax_ - rangemin_) * sizeof(buckets_[0]));
00084 }
00085 
00086 /**********************************************************************
00087  * STATS::~STATS
00088  *
00089  * Destructor for a stats class.
00090  **********************************************************************/
00091 STATS::~STATS () {
00092   if (buckets_ != NULL) {
00093     delete [] buckets_;
00094     buckets_ = NULL;
00095   }
00096 }
00097 
00098 /**********************************************************************
00099  * STATS::add
00100  *
00101  * Add a set of samples to (or delete from) a pile.
00102  **********************************************************************/
00103 void STATS::add(inT32 value, inT32 count) {
00104   if (buckets_ == NULL) {
00105     return;
00106   }
00107   value = ClipToRange(value, rangemin_, rangemax_ - 1);
00108   buckets_[value - rangemin_] += count;
00109   total_count_ += count;          // keep count of total
00110 }
00111 
00112 /**********************************************************************
00113  * STATS::mode
00114  *
00115  * Find the mode of a stats class.
00116  **********************************************************************/
00117 inT32 STATS::mode() const {  // get mode of samples
00118   if (buckets_ == NULL) {
00119     return rangemin_;
00120   }
00121   inT32 max = buckets_[0];           // max cell count
00122   inT32 maxindex = 0;                // index of max
00123   for (int index = rangemax_ - rangemin_ - 1; index > 0; --index) {
00124     if (buckets_[index] > max) {
00125       max = buckets_[index];      // find biggest
00126       maxindex = index;
00127     }
00128   }
00129   return maxindex + rangemin_;    // index of biggest
00130 }
00131 
00132 /**********************************************************************
00133  * STATS::mean
00134  *
00135  * Find the mean of a stats class.
00136  **********************************************************************/
00137 double STATS::mean() const {  //get mean of samples
00138   if (buckets_ == NULL || total_count_ <= 0) {
00139     return static_cast<double>(rangemin_);
00140   }
00141   inT64 sum = 0;
00142   for (int index = rangemax_ - rangemin_ - 1; index >= 0; --index) {
00143     sum += static_cast<inT64>(index) * buckets_[index];
00144   }
00145   return static_cast<double>(sum) / total_count_ + rangemin_;
00146 }
00147 
00148 /**********************************************************************
00149  * STATS::sd
00150  *
00151  * Find the standard deviation of a stats class.
00152  **********************************************************************/
00153 double STATS::sd() const {  //standard deviation
00154   if (buckets_ == NULL || total_count_ <= 0) {
00155     return 0.0;
00156   }
00157   inT64 sum = 0;
00158   double sqsum = 0.0;
00159   for (int index = rangemax_ - rangemin_ - 1; index >= 0; --index) {
00160     sum += static_cast<inT64>(index) * buckets_[index];
00161     sqsum += static_cast<double>(index) * index * buckets_[index];
00162   }
00163   double variance = static_cast<double>(sum) / total_count_;
00164   variance = sqsum / total_count_ - variance * variance;
00165   if (variance > 0.0)
00166     return sqrt(variance);
00167   return 0.0;
00168 }
00169 
00170 /**********************************************************************
00171  * STATS::ile
00172  *
00173  * Returns the fractile value such that frac fraction (in [0,1]) of samples
00174  * has a value less than the return value.
00175  **********************************************************************/
00176 double STATS::ile(double frac) const {
00177   if (buckets_ == NULL || total_count_ == 0) {
00178     return static_cast<double>(rangemin_);
00179   }
00180 #if 0
00181   // TODO(rays) The existing code doesn't seem to be doing the right thing
00182   // with target a double but this substitute crashes the code that uses it.
00183   // Investigate and fix properly.
00184   int target = IntCastRounded(frac * total_count_);
00185   target = ClipToRange(target, 1, total_count_);
00186 #else
00187   double target = frac * total_count_;
00188   target = ClipToRange(target, 1.0, static_cast<double>(total_count_));
00189 #endif
00190   int sum = 0;
00191   int index = 0;
00192   for (index = 0; index < rangemax_ - rangemin_ && sum < target;
00193        sum += buckets_[index++]);
00194   if (index > 0) {
00195     ASSERT_HOST(buckets_[index - 1] > 0);
00196     return rangemin_ + index -
00197         static_cast<double>(sum - target) / buckets_[index - 1];
00198   } else {
00199     return static_cast<double>(rangemin_);
00200   }
00201 }
00202 
00203 /**********************************************************************
00204  * STATS::min_bucket
00205  *
00206  * Find REAL minimum bucket - ile(0.0) isnt necessarily correct
00207  **********************************************************************/
00208 inT32 STATS::min_bucket() const {  // Find min
00209   if (buckets_ == NULL || total_count_ == 0) {
00210     return rangemin_;
00211   }
00212   inT32 min = 0;
00213   for (min = 0; (min < rangemax_ - rangemin_) && (buckets_[min] == 0); min++);
00214   return rangemin_ + min;
00215 }
00216 
00217 
00218 /**********************************************************************
00219  * STATS::max_bucket
00220  *
00221  * Find REAL maximum bucket - ile(1.0) isnt necessarily correct
00222  **********************************************************************/
00223 
00224 inT32 STATS::max_bucket() const {  // Find max
00225   if (buckets_ == NULL || total_count_ == 0) {
00226     return rangemin_;
00227   }
00228   inT32 max;
00229   for (max = rangemax_ - rangemin_ - 1; max > 0 && buckets_[max] == 0; max--);
00230   return rangemin_ + max;
00231 }
00232 
00233 /**********************************************************************
00234  * STATS::median
00235  *
00236  * Finds a more useful estimate of median than ile(0.5).
00237  *
00238  * Overcomes a problem with ile() - if the samples are, for example,
00239  * 6,6,13,14 ile(0.5) return 7.0 - when a more useful value would be midway
00240  * between 6 and 13 = 9.5
00241  **********************************************************************/
00242 double STATS::median() const {  //get median
00243   if (buckets_ == NULL) {
00244     return static_cast<double>(rangemin_);
00245   }
00246   double median = ile(0.5);
00247   int median_pile = static_cast<int>(floor(median));
00248   if ((total_count_ > 1) && (pile_count(median_pile) == 0)) {
00249     inT32 min_pile;
00250     inT32 max_pile;
00251     /* Find preceeding non zero pile */
00252     for (min_pile = median_pile; pile_count(min_pile) == 0; min_pile--);
00253     /* Find following non zero pile */
00254     for (max_pile = median_pile; pile_count(max_pile) == 0; max_pile++);
00255     median = (min_pile + max_pile) / 2.0;
00256   }
00257   return median;
00258 }
00259 
00260 /**********************************************************************
00261  * STATS::local_min
00262  *
00263  * Return TRUE if this point is a local min.
00264  **********************************************************************/
00265 bool STATS::local_min(inT32 x) const {
00266   if (buckets_ == NULL) {
00267     return false;
00268   }
00269   x = ClipToRange(x, rangemin_, rangemax_ - 1) - rangemin_;
00270   if (buckets_[x] == 0)
00271     return true;
00272   inT32 index;                   // table index
00273   for (index = x - 1; index >= 0 && buckets_[index] == buckets_[x]; --index);
00274   if (index >= 0 && buckets_[index] < buckets_[x])
00275     return false;
00276   for (index = x + 1; index < rangemax_ - rangemin_ &&
00277        buckets_[index] == buckets_[x]; ++index);
00278   if (index < rangemax_ - rangemin_ && buckets_[index] < buckets_[x])
00279     return false;
00280   else
00281     return true;
00282 }
00283 
00284 /**********************************************************************
00285  * STATS::smooth
00286  *
00287  * Apply a triangular smoothing filter to the stats.
00288  * This makes the modes a bit more useful.
00289  * The factor gives the height of the triangle, i.e. the weight of the
00290  * centre.
00291  **********************************************************************/
00292 void STATS::smooth(inT32 factor) {
00293   if (buckets_ == NULL || factor < 2) {
00294     return;
00295   }
00296   STATS result(rangemin_, rangemax_);
00297   int entrycount = rangemax_ - rangemin_;
00298   for (int entry = 0; entry < entrycount; entry++) {
00299                                  //centre weight
00300     int count = buckets_[entry] * factor;
00301     for (int offset = 1; offset < factor; offset++) {
00302       if (entry - offset >= 0)
00303         count += buckets_[entry - offset] * (factor - offset);
00304       if (entry + offset < entrycount)
00305         count += buckets_[entry + offset] * (factor - offset);
00306     }
00307     result.add(entry + rangemin_, count);
00308   }
00309   total_count_ = result.total_count_;
00310   memcpy(buckets_, result.buckets_, entrycount * sizeof(buckets_[0]));
00311 }
00312 
00313 /**********************************************************************
00314  * STATS::cluster
00315  *
00316  * Cluster the samples into max_cluster clusters.
00317  * Each call runs one iteration. The array of clusters must be
00318  * max_clusters+1 in size as cluster 0 is used to indicate which samples
00319  * have been used.
00320  * The return value is the current number of clusters.
00321  **********************************************************************/
00322 
00323 inT32 STATS::cluster(float lower,         // thresholds
00324                      float upper,
00325                      float multiple,      // distance threshold
00326                      inT32 max_clusters,  // max no to make
00327                      STATS *clusters) {   // array of clusters
00328   BOOL8 new_cluster;             // added one
00329   float *centres;                // cluster centres
00330   inT32 entry;                   // bucket index
00331   inT32 cluster;                 // cluster index
00332   inT32 best_cluster;            // one to assign to
00333   inT32 new_centre = 0;          // residual mode
00334   inT32 new_mode;                // pile count of new_centre
00335   inT32 count;                   // pile to place
00336   float dist;                    // from cluster
00337   float min_dist;                // from best_cluster
00338   inT32 cluster_count;           // no of clusters
00339 
00340   if (buckets_ == NULL || max_clusters < 1)
00341     return 0;
00342   centres = new float[max_clusters + 1];
00343   for (cluster_count = 1; cluster_count <= max_clusters
00344        && clusters[cluster_count].buckets_ != NULL
00345        && clusters[cluster_count].total_count_ > 0;
00346        cluster_count++) {
00347     centres[cluster_count] =
00348       static_cast<float>(clusters[cluster_count].ile(0.5));
00349     new_centre = clusters[cluster_count].mode();
00350     for (entry = new_centre - 1; centres[cluster_count] - entry < lower
00351          && entry >= rangemin_
00352          && pile_count(entry) <= pile_count(entry + 1);
00353          entry--) {
00354       count = pile_count(entry) - clusters[0].pile_count(entry);
00355       if (count > 0) {
00356         clusters[cluster_count].add(entry, count);
00357         clusters[0].add (entry, count);
00358       }
00359     }
00360     for (entry = new_centre + 1; entry - centres[cluster_count] < lower
00361          && entry < rangemax_
00362          && pile_count(entry) <= pile_count(entry - 1);
00363          entry++) {
00364       count = pile_count(entry) - clusters[0].pile_count(entry);
00365       if (count > 0) {
00366         clusters[cluster_count].add(entry, count);
00367         clusters[0].add(entry, count);
00368       }
00369     }
00370   }
00371   cluster_count--;
00372 
00373   if (cluster_count == 0) {
00374     clusters[0].set_range(rangemin_, rangemax_);
00375   }
00376   do {
00377     new_cluster = FALSE;
00378     new_mode = 0;
00379     for (entry = 0; entry < rangemax_ - rangemin_; entry++) {
00380       count = buckets_[entry] - clusters[0].buckets_[entry];
00381       //remaining pile
00382       if (count > 0) {           //any to handle
00383         min_dist = static_cast<float>(MAX_INT32);
00384         best_cluster = 0;
00385         for (cluster = 1; cluster <= cluster_count; cluster++) {
00386           dist = entry + rangemin_ - centres[cluster];
00387           //find distance
00388           if (dist < 0)
00389             dist = -dist;
00390           if (dist < min_dist) {
00391             min_dist = dist;     //find least
00392             best_cluster = cluster;
00393           }
00394         }
00395         if (min_dist > upper     //far enough for new
00396           && (best_cluster == 0
00397           || entry + rangemin_ > centres[best_cluster] * multiple
00398         || entry + rangemin_ < centres[best_cluster] / multiple)) {
00399           if (count > new_mode) {
00400             new_mode = count;
00401             new_centre = entry + rangemin_;
00402           }
00403         }
00404       }
00405     }
00406                                  // need new and room
00407     if (new_mode > 0 && cluster_count < max_clusters) {
00408       cluster_count++;
00409       new_cluster = TRUE;
00410       if (!clusters[cluster_count].set_range(rangemin_, rangemax_))
00411         return 0;
00412       centres[cluster_count] = static_cast<float>(new_centre);
00413       clusters[cluster_count].add(new_centre, new_mode);
00414       clusters[0].add(new_centre, new_mode);
00415       for (entry = new_centre - 1; centres[cluster_count] - entry < lower
00416         && entry >= rangemin_
00417       && pile_count (entry) <= pile_count(entry + 1); entry--) {
00418         count = pile_count(entry) - clusters[0].pile_count(entry);
00419         if (count > 0) {
00420           clusters[cluster_count].add(entry, count);
00421           clusters[0].add(entry, count);
00422         }
00423       }
00424       for (entry = new_centre + 1; entry - centres[cluster_count] < lower
00425         && entry < rangemax_
00426       && pile_count (entry) <= pile_count(entry - 1); entry++) {
00427         count = pile_count(entry) - clusters[0].pile_count(entry);
00428         if (count > 0) {
00429           clusters[cluster_count].add(entry, count);
00430           clusters[0].add (entry, count);
00431         }
00432       }
00433       centres[cluster_count] =
00434         static_cast<float>(clusters[cluster_count].ile(0.5));
00435     }
00436   } while (new_cluster && cluster_count < max_clusters);
00437   delete [] centres;
00438   return cluster_count;
00439 }
00440 
00441 /**********************************************************************
00442  * STATS::print
00443  *
00444  * Prints a summary and table of the histogram.
00445  **********************************************************************/
00446 void STATS::print() const {
00447   if (buckets_ == NULL) {
00448     return;
00449   }
00450   inT32 min = min_bucket() - rangemin_;
00451   inT32 max = max_bucket() - rangemin_;
00452 
00453   int num_printed = 0;
00454   for (int index = min; index <= max; index++) {
00455     if (buckets_[index] != 0) {
00456       tprintf("%4d:%-3d ", rangemin_ + index, buckets_[index]);
00457       if (++num_printed % 8 == 0)
00458         tprintf ("\n");
00459     }
00460   }
00461   tprintf ("\n");
00462   print_summary();
00463 }
00464 
00465 
00466 
00467 /**********************************************************************
00468  * STATS::print_summary
00469  *
00470  * Print a summary of the stats.
00471  **********************************************************************/
00472 void STATS::print_summary() const {
00473   if (buckets_ == NULL) {
00474     return;
00475   }
00476   inT32 min = min_bucket();
00477   inT32 max = max_bucket();
00478   tprintf("Total count=%d\n", total_count_);
00479   tprintf("Min=%.2f Really=%d\n", ile(0.0), min);
00480   tprintf("Lower quartile=%.2f\n", ile(0.25));
00481   tprintf("Median=%.2f, ile(0.5)=%.2f\n", median(), ile(0.5));
00482   tprintf("Upper quartile=%.2f\n", ile(0.75));
00483   tprintf("Max=%.2f Really=%d\n", ile(1.0), max);
00484   tprintf("Range=%d\n", max + 1 - min);
00485   tprintf("Mean= %.2f\n", mean());
00486   tprintf("SD= %.2f\n", sd());
00487 }
00488 
00489 
00490 /**********************************************************************
00491  * STATS::plot
00492  *
00493  * Draw a histogram of the stats table.
00494  **********************************************************************/
00495 
00496 #ifndef GRAPHICS_DISABLED
00497 void STATS::plot(ScrollView* window,  // to draw in
00498                  float xorigin,       // bottom left
00499                  float yorigin,
00500                  float xscale,        // one x unit
00501                  float yscale,        // one y unit
00502                  ScrollView::Color colour) const {   // colour to draw in
00503   if (buckets_ == NULL) {
00504     return;
00505   }
00506   window->Pen(colour);
00507 
00508   for (int index = 0; index < rangemax_ - rangemin_; index++) {
00509     window->Rectangle( xorigin + xscale * index, yorigin,
00510       xorigin + xscale * (index + 1),
00511       yorigin + yscale * buckets_[index]);
00512   }
00513 }
00514 #endif
00515 
00516 
00517 /**********************************************************************
00518  * STATS::plotline
00519  *
00520  * Draw a histogram of the stats table. (Line only)
00521  **********************************************************************/
00522 
00523 #ifndef GRAPHICS_DISABLED
00524 void STATS::plotline(ScrollView* window,  // to draw in
00525                      float xorigin,       // bottom left
00526                      float yorigin,
00527                      float xscale,        // one x unit
00528                      float yscale,        // one y unit
00529                      ScrollView::Color colour) const {  // colour to draw in
00530   if (buckets_ == NULL) {
00531     return;
00532   }
00533   window->Pen(colour);
00534   window->SetCursor(xorigin, yorigin + yscale * buckets_[0]);
00535   for (int index = 0; index < rangemax_ - rangemin_; index++) {
00536     window->DrawTo(xorigin + xscale * index,
00537                    yorigin + yscale * buckets_[index]);
00538   }
00539 }
00540 #endif
00541 
00542 
00543 /**********************************************************************
00544  * choose_nth_item
00545  *
00546  * Returns the index of what would b the nth item in the array
00547  * if the members were sorted, without actually sorting.
00548  **********************************************************************/
00549 
00550 inT32 choose_nth_item(inT32 index, float *array, inT32 count) {
00551   inT32 next_sample;             // next one to do
00552   inT32 next_lesser;             // space for new
00553   inT32 prev_greater;            // last one saved
00554   inT32 equal_count;             // no of equal ones
00555   float pivot;                   // proposed median
00556   float sample;                  // current sample
00557 
00558   if (count <= 1)
00559     return 0;
00560   if (count == 2) {
00561     if (array[0] < array[1]) {
00562       return index >= 1 ? 1 : 0;
00563     }
00564     else {
00565       return index >= 1 ? 0 : 1;
00566     }
00567   }
00568   else {
00569     if (index < 0)
00570       index = 0;                 // ensure legal
00571     else if (index >= count)
00572       index = count - 1;
00573     equal_count = (inT32) (rand() % count);
00574     pivot = array[equal_count];
00575                                  // fill gap
00576     array[equal_count] = array[0];
00577     next_lesser = 0;
00578     prev_greater = count;
00579     equal_count = 1;
00580     for (next_sample = 1; next_sample < prev_greater;) {
00581       sample = array[next_sample];
00582       if (sample < pivot) {
00583                                  // shuffle
00584         array[next_lesser++] = sample;
00585         next_sample++;
00586       }
00587       else if (sample > pivot) {
00588         prev_greater--;
00589                                  // juggle
00590         array[next_sample] = array[prev_greater];
00591         array[prev_greater] = sample;
00592       }
00593       else {
00594         equal_count++;
00595         next_sample++;
00596       }
00597     }
00598     for (next_sample = next_lesser; next_sample < prev_greater;)
00599       array[next_sample++] = pivot;
00600     if (index < next_lesser)
00601       return choose_nth_item (index, array, next_lesser);
00602     else if (index < prev_greater)
00603       return next_lesser;        // in equal bracket
00604     else
00605       return choose_nth_item (index - prev_greater,
00606         array + prev_greater,
00607         count - prev_greater) + prev_greater;
00608   }
00609 }
00610 
00611 /**********************************************************************
00612  * choose_nth_item
00613  *
00614  * Returns the index of what would be the nth item in the array
00615  * if the members were sorted, without actually sorting.
00616  **********************************************************************/
00617 inT32 choose_nth_item(inT32 index, void *array, inT32 count, size_t size,
00618                       int (*compar)(const void*, const void*)) {
00619   int result;                    // of compar
00620   inT32 next_sample;             // next one to do
00621   inT32 next_lesser;             // space for new
00622   inT32 prev_greater;            // last one saved
00623   inT32 equal_count;             // no of equal ones
00624   inT32 pivot;                   // proposed median
00625 
00626   if (count <= 1)
00627     return 0;
00628   if (count == 2) {
00629     if (compar (array, (char *) array + size) < 0) {
00630       return index >= 1 ? 1 : 0;
00631     }
00632     else {
00633       return index >= 1 ? 0 : 1;
00634     }
00635   }
00636   if (index < 0)
00637     index = 0;                   // ensure legal
00638   else if (index >= count)
00639     index = count - 1;
00640   pivot = (inT32) (rand () % count);
00641   swap_entries (array, size, pivot, 0);
00642   next_lesser = 0;
00643   prev_greater = count;
00644   equal_count = 1;
00645   for (next_sample = 1; next_sample < prev_greater;) {
00646     result =
00647       compar ((char *) array + size * next_sample,
00648       (char *) array + size * next_lesser);
00649     if (result < 0) {
00650       swap_entries (array, size, next_lesser++, next_sample++);
00651       // shuffle
00652     }
00653     else if (result > 0) {
00654       prev_greater--;
00655       swap_entries(array, size, prev_greater, next_sample);
00656     }
00657     else {
00658       equal_count++;
00659       next_sample++;
00660     }
00661   }
00662   if (index < next_lesser)
00663     return choose_nth_item (index, array, next_lesser, size, compar);
00664   else if (index < prev_greater)
00665     return next_lesser;          // in equal bracket
00666   else
00667     return choose_nth_item (index - prev_greater,
00668       (char *) array + size * prev_greater,
00669       count - prev_greater, size,
00670       compar) + prev_greater;
00671 }
00672 
00673 /**********************************************************************
00674  * swap_entries
00675  *
00676  * Swap 2 entries of arbitrary size in-place in a table.
00677  **********************************************************************/
00678 void swap_entries(void *array,   // array of entries
00679                   size_t size,   // size of entry
00680                   inT32 index1,  // entries to swap
00681                   inT32 index2) {
00682   char tmp;
00683   char *ptr1;                    // to entries
00684   char *ptr2;
00685   size_t count;                  // of bytes
00686 
00687   ptr1 = reinterpret_cast<char*>(array) + index1 * size;
00688   ptr2 = reinterpret_cast<char*>(array) + index2 * size;
00689   for (count = 0; count < size; count++) {
00690     tmp = *ptr1;
00691     *ptr1++ = *ptr2;
00692     *ptr2++ = tmp;               // tedious!
00693   }
00694 }