/*
 * Copyright 2011 Steven Gribble
 *
 *  This file is part of the UW CSE 333 course project sequence
 *  (333proj).
 *
 *  333proj is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  333proj is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with 333proj.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include "sort.h"

// swap array elements i and j
static void Swap(void *arr, uint32_t element_size, uint32_t i, uint32_t j) {
  unsigned char tmp[element_size];

  // Handle the degenerate case.
  if (i == j)
    return;

  memcpy(tmp,
         arr+(element_size*i),
         element_size);
  memcpy(arr+(element_size*i),
         arr+(element_size*j),
         element_size);
  memcpy(arr+(element_size*j),
         tmp,
         element_size);
}

// See wikipedia's entry for quicksort (in-place version)
// for algorithm details.
uint32_t QuickSortPartition(void *arr,
                            uint32_t element_size,
                            uint32_t left,
                            uint32_t right,
                            uint32_t pivot_index,
                            ComparatorFnPtr f) {
  unsigned char pivot_value[element_size];
  uint32_t store_index, i;

  memcpy(pivot_value, arr+(pivot_index*element_size), element_size);
  // Move pivot to end
  Swap(arr, element_size, pivot_index, right);
  store_index = left;
  for (i = left; i < right; i++) {
    if (f(arr+(i*element_size), pivot_value) <= 0) {
      Swap(arr, element_size, i, store_index);
      store_index++;
    }
  }
  // Move pivot to its final place
  Swap(arr, element_size, store_index, right);
  return store_index;
}

void QuickSortRecurse(void *arr, uint32_t element_size, uint32_t left,
                      uint32_t right, ComparatorFnPtr f) {
  if (right > left) {
    uint32_t pivot_index = (left + right) / 2;
    uint32_t pivot_new_index = QuickSortPartition(arr,
                                                  element_size,
                                                  left,
                                                  right,
                                                  pivot_index,
                                                  f);
    if (pivot_new_index > 0)
      QuickSortRecurse(arr, element_size, left, pivot_new_index-1, f);
    QuickSortRecurse(arr, element_size, pivot_new_index+1, right, f);
  }
}

void QuickSort(void *arr, uint32_t num_elements, uint32_t element_size,
               ComparatorFnPtr f) {
  // Handle the easy degenerate case.
  if (num_elements <= 1)
    return;

  // Dive into the recursive quicksort implementation.
  QuickSortRecurse(arr, element_size, 0, num_elements-1, f);
}

void BubbleSort(void *arr, uint32_t num_elements, uint32_t element_size,
                ComparatorFnPtr f) {
  uint32_t i;
  int done = 0;

  // Handle the easy degenerate case.
  if (num_elements <= 1)
    return;

  // Non-degenerate case.
  while (!done) {
    done = 1;
    for (i = 0; i < num_elements-1; i++) {
      if (f(arr+(element_size*i),
            arr+(element_size*(i+1))) > 0) {
        Swap(arr, element_size, i, i+1);
        done = 0;
      }
    }
  }
}