pelib  2.0.0
src/sort.c
Go to the documentation of this file.
00001 /*
00002  * sort.c
00003  *
00004  *  Created on: 25 Jan 2012
00005  *  Copyright 2012 Nicolas Melot
00006  *
00007  * This file is part of pelib.
00008  * 
00009  *     pelib is free software: you can redistribute it and/or modify
00010  *     it under the terms of the GNU General Public License as published by
00011  *     the Free Software Foundation, either version 3 of the License, or
00012  *     (at your option) any later version.
00013  * 
00014  *     pelib is distributed in the hope that it will be useful,
00015  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *     GNU General Public License for more details.
00018  * 
00019  *     You should have received a copy of the GNU General Public License
00020  *     along with pelib. If not, see <http://www.gnu.org/licenses/>.
00021  * 
00022  */
00023 #include <assert.h>
00024 
00025 #include <pelib/sort.h>
00026 
00027 static int
00028 swap(array_t(int)* array, int left, int right)
00029 {
00030   int pivot;
00031   pivot = pelib_array_read(int)(array, left);
00032   pelib_array_write(int)(array, left, pelib_array_read(int)(array, right));
00033   pelib_array_write(int)(array, right, pivot);
00034 
00035   return 0;
00036 }
00037 
00038 struct qsort_param
00039 {
00040   array_t
00041   (int)
00042   *array;
00043   qsort_tune_t tune;
00044 };
00045 typedef struct qsort_param qsort_param_t;
00046 
00047 struct qsort_next_step
00048 {
00049   char done;
00050   qsort_param_t left, right;
00051 };
00052 typedef struct qsort_next_step qsort_next_step_t;
00053 
00054 struct qsort_bound
00055 {
00056   int begin, end;
00057 };
00058 typedef struct qsort_bound qsort_bound_t;
00059 
00060 // Generate stack for qsort_param_t structure
00061 #define STRUCT_T qsort_bound_t
00062 #include <pelib/structure.h>
00063 
00064 #define STACK_T qsort_bound_t
00065 #include <pelib/stack.h>
00066 
00067 #define STACK_T qsort_bound_t
00068 #include <pelib/stack.c>
00069 
00070 int
00071 pelib_copy
00072 ( qsort_bound_t)(qsort_bound_t s, qsort_bound_t *d)
00073 {
00074   d->begin = s.begin;
00075   d->end = s.end;
00076 
00077   return 1;
00078 }
00079 int
00080 pelib_init
00081 ( qsort_bound_t)(qsort_bound_t *d)
00082 {
00083   d->begin = 0;
00084   d->end = 0;
00085 
00086   return 1;
00087 }
00088 
00089 static void
00090 move(array_t(int)* array, int pos, int dest)
00091 {
00092   int pivot;
00093   int i;
00094 
00095   assert(pos >= dest);
00096 
00097   pivot = pelib_array_read(int)(array, pos);
00098   for (i = pos; i > dest; i--)
00099     {
00100       pelib_array_write(int)(array, i, pelib_array_read(int)(array, i - 1));
00101     }
00102   pelib_array_write(int)(array, dest, pivot);
00103 }
00104 
00105 void
00106 pelib_insertsort_window(array_t(int)* array, int start, int stop)
00107 {
00108   int i, j;
00109 
00110   assert(stop >= start);
00111 
00112   i = 0;
00113   j = 0;
00114 
00115   for (i = start + 1; i < stop; i++)
00116     {
00117       j = i;
00118       while (pelib_compare(int)(pelib_array_read(int)(array, i),
00119           pelib_array_read(int)(array, j - 1)) < 0 && j > start)
00120         {
00121           j--;
00122         }
00123       move(array, i, j);
00124     }
00125 }
00126 
00127 void
00128 pelib_insertsort(array_t(int)* array)
00129 {
00130   pelib_insertsort_window(array, 0, pelib_array_length(int)(array));
00131 }
00132 
00133 int
00134 pelib_sample(array_t(int)* array, int ratio, int start, int stop)
00135 {
00136   double pivot;
00137   int i;
00138   int read;
00139 
00140   // Sample input to find a good pivot
00141   pivot = 0;
00142   if (stop - start >= ratio)
00143     {
00144       array_t(int)* sample = pelib_alloc_collection(array_t(int))(ratio);
00145       for (i = start; i < stop; i += (int)((float) (stop - start) / ratio))
00146         {
00147           read = pelib_array_read(int)(array, (int)i);
00148           pelib_array_append(int)(sample, read);
00149         }
00150 
00151       for (i = 0; i < pelib_array_length(int)(sample); i++)
00152         {
00153           pivot += (double) pelib_array_read(int)(sample, i);
00154         }
00155       pivot /= (double) pelib_array_length(int)(sample);
00156       pelib_free(array_t(int))(sample);
00157     }
00158   else
00159     {
00160       pivot = 0;
00161       for (i = start; i < stop; i++)
00162         {
00163           pivot += (double) pelib_array_read(int)(array, i);
00164 
00165         }
00166       pivot /= (double) (stop - start);
00167     }
00168 
00169   return (int) pivot;
00170 }
00171 
00172 static qsort_next_step_t
00173 quicksort_tune(qsort_param_t param)
00174 {
00175   qsort_next_step_t next;
00176   int i, j;
00177 
00178 #if 0
00179   if((param.tune.end > 16777216 && param.tune.begin < 16777216 && param.tune.end - param.tune.begin < param.tune.threshold) || param.tune.begin == 0 && param.tune.end == 100000000)
00180   {
00181   char s[30];
00182   struct tm tim;
00183   time_t now;
00184   
00185   now = time(NULL);
00186   tim = *(localtime(&now));
00187   strftime(s,30,"%b %d, %Y; %H:%M:%S",&tim);
00188   printf("[%s, %s] %d->%d\n", s, __FUNCTION__, param.tune.begin, param.tune.end);
00189   }
00190 #endif
00191 
00192   if (param.tune.end - param.tune.begin > param.tune.threshold) // Threshold to be decided experimentally
00193     {
00194       int pivot, pv;
00195       pivot = pelib_sample(param.array, param.tune.num_sample,
00196           param.tune.begin, param.tune.end);
00197 
00198       next.done = 0;
00199       next.left = param;
00200       next.right = param;
00201 
00202       i = param.tune.begin;
00203       j = param.tune.end;
00204       pv = j;
00205 
00206       int read;
00207 
00208       while (i < j)
00209         {
00210           read = pelib_array_read(int)(param.array, i);
00211           if (read < pivot)
00212             {
00213               i++;
00214             }
00215           else
00216             {
00217               // Alloc a j
00218               j--;
00219               swap(param.array, i, j);
00220 
00221               if (pelib_array_read(int)(param.array, j) == pivot)
00222                 {
00223                   // Alloc a pv
00224                   pv--;
00225                   swap(param.array, j, pv);
00226                 }
00227             }
00228         }
00229       next.left.tune.end = i;
00230 
00231       for (i = pv; i < param.tune.end; i++)
00232         {
00233           swap(param.array, j, pv);
00234           j++;
00235           pv++;
00236         }
00237 
00238       next.right.tune.begin = j;
00239       return next;
00240     }
00241   else
00242     {
00243       pelib_insertsort_window(param.array, param.tune.begin, param.tune.end);
00244 
00245       next.done = 1;
00246 
00247       return next;
00248     }
00249 }
00250 
00251 static qsort_next_step_t
00252 quicksort_tune_recursive(qsort_param_t param)
00253 {
00254   qsort_next_step_t next;
00255   int i, j;
00256 
00257 #if 0
00258   if((param.tune.end > 16777216 && param.tune.begin < 16777216 && param.tune.end - param.tune.begin < param.tune.threshold) || param.tune.begin == 0 && param.tune.end == 100000000)
00259   {
00260   char s[30];
00261   struct tm tim;
00262   time_t now;
00263   
00264   now = time(NULL);
00265   tim = *(localtime(&now));
00266   strftime(s,30,"%b %d, %Y; %H:%M:%S",&tim);
00267   printf("[%s, %s] %d->%d\n", s, __FUNCTION__, param.tune.begin, param.tune.end);
00268   }
00269 #endif
00270 
00271   if (param.tune.end - param.tune.begin > param.tune.threshold) // Threshold to be decided experimentally
00272     {
00273       int pivot, pv;
00274       pivot = pelib_sample(param.array, param.tune.num_sample,
00275           param.tune.begin, param.tune.end);
00276 
00277       next.done = 0;
00278       next.left = param;
00279       next.right = param;
00280 
00281       i = param.tune.begin;
00282       j = param.tune.end;
00283       pv = j;
00284 
00285       while (i < j)
00286         {
00287           if (pelib_array_read(int)(param.array, i) < pivot)
00288             {
00289               i++;
00290             }
00291           else
00292             {
00293               // Alloc a j
00294               j--;
00295               swap(param.array, i, j);
00296 
00297               if (pelib_array_read(int)(param.array, j) == pivot)
00298                 {
00299                   // Alloc a pv
00300                   pv--;
00301                   swap(param.array, j, pv);
00302                 }
00303             }
00304         }
00305       next.left.tune.end = i;
00306 
00307       for (i = pv; i < param.tune.end; i++)
00308         {
00309           swap(param.array, j, pv);
00310           j++;
00311           pv++;
00312         }
00313 
00314       next.right.tune.begin = j;
00315 
00316       quicksort_tune_recursive(next.left);
00317       quicksort_tune_recursive(next.right);
00318       next.done = 1;
00319 
00320       return next;
00321     }
00322   else
00323     {
00324       pelib_insertsort_window(param.array, param.tune.begin, param.tune.end);
00325 
00326       next.done = 1;
00327 
00328       return next;
00329     }
00330 }
00331 
00332 void
00333 pelib_quicksort_tune(array_t(int)* array, qsort_tune_t p)
00334 {
00335   qsort_next_step_t res;
00336   qsort_param_t param;
00337   qsort_bound_t start;
00338   stack_t(qsort_bound_t) * stack;
00339 
00340   stack = pelib_alloc_collection(stack_t(qsort_bound_t))(0);
00341   pelib_init(stack_t(qsort_bound_t))(stack);
00342 
00343   param.array = array;
00344   param.tune = p;
00345 
00346   start.begin = p.begin;
00347   start.end = p.end;
00348   pelib_stack_push(qsort_bound_t)(stack, start);
00349 
00350   res.done = 1;
00351   while (res.done == 0 || !pelib_stack_isempty(qsort_bound_t)(stack))
00352     {
00353       if (res.done == 1)
00354         {
00355         int ret = pelib_stack_pop(qsort_bound_t)(stack, &start);
00356           param.tune.begin = start.begin;
00357           param.tune.end = start.end;
00358 
00359           res = quicksort_tune(param);
00360         }
00361       else
00362         {
00363           start.begin = res.right.tune.begin;
00364           start.end = res.right.tune.end;
00365           pelib_stack_push(qsort_bound_t)(stack, start);
00366 
00367           start.begin = res.left.tune.begin;
00368           start.end = res.left.tune.end;
00369           pelib_stack_push(qsort_bound_t)(stack, start);
00370 
00371           res.done = 1;
00372         }
00373     }
00374 
00375   pelib_free(stack_t(qsort_bound_t))(stack);
00376 }
00377 
00378 void
00379 pelib_quicksort_tune_recursive(array_t(int)* array, qsort_tune_t p)
00380 {
00381   qsort_param_t param;
00382 
00383   param.array = array;
00384   param.tune = p;
00385 
00386   quicksort_tune_recursive(param);
00387 }
00388 
00389 void
00390 pelib_quicksort_window(array_t(int)* array, int start, int stop)
00391 {
00392   qsort_tune_t p;
00393 
00394   p.begin = start;
00395   p.end = stop;
00396   p.threshold = PELIB_QUICKSORT_THRESHOLD;
00397   p.num_sample = PELIB_QUICKSORT_NUM_SAMPLE;
00398 
00399   pelib_quicksort_tune(array, p);
00400 }
00401 
00402 void
00403 pelib_quicksort(array_t(int)* array)
00404 {
00405   qsort_tune_t p;
00406 
00407   p.begin = 0;
00408   p.end = pelib_array_length(int)(array);
00409   p.threshold = PELIB_QUICKSORT_THRESHOLD;
00410   p.num_sample = PELIB_QUICKSORT_NUM_SAMPLE;
00411 
00412   pelib_quicksort_tune(array, p);
00413 }
00414 
00415 int
00416 pelib_mergesort(array_t(int)* array)
00417 {
00418         if(pelib_array_length(int)(array) <= 1)
00419         {
00420                 return 1;
00421         }
00422         else
00423         {
00424                 return pelib_array_length(int)(array) / 2;
00425 //              left = pelib_alloc_from(
00426         }
00427 }
00428 
00429 
00430 int
00431 pelib_is_increasing(array_t(int)* array)
00432 {
00433   int val, ref;
00434   int i, res;
00435   res = 1;
00436 
00437   if (pelib_array_length(int)(array) > 0)
00438     {
00439       val = pelib_array_read(int)(array, 0);
00440       for (i = 1; i < pelib_array_length(int)(array); i++)
00441         {
00442           ref = pelib_array_read(int)(array, i);
00443           res = res && (pelib_compare(int)(val, ref) <= 0);
00444           val = ref;
00445         }
00446 
00447       return res;
00448     }
00449   else
00450     {
00451       return 1;
00452     }
00453 }