#include <stdio.h>
#include <unistd.h>
#include "std-macros.h"

/**
 *
 * This program exercises the system qsort function.  The focus is on the
 * comparator functions that qsort calls, as an illustration of using function
 * pointers in C.
 *
 */


/**
 * Compare two void* values as ints, by casting them and using normal numeric
 * comparison.
 */
int intcmp(const void* a1, const void* a2) {
    int v1 = *((int*) a1);
    int v2 = *((int*) a2);

    if (v1 < v2) return -1;
    if (v1 > v2) return 1;
    return 0;
} 

/**
 * Like intcmp, but reverses the sense of the comparison.  This allows the
 * sorting order to defined as descending, without changing the implementation
 * of the sort function.
 */
int intcmp_reverse(const void* a1, const void* a2) {
    int v1 = *((int*) a1);
    int v2 = *((int*) a2);

    if (v1 > v2) return -1;
    if (v1 < v2) return 1;
    return 0;
} 

/**
 * Compare two void* values as numeric strings, by casting them to char*, then
 * converting them using atoi, then using normal numeric comparison.
 *
 * Note here the intermediate use of char** as a cast, then the deference down
 * to char*.  I.e,. the following code is used to cast the incoming void*
 * argument a1 into the char* variable s1:
 *
 *     char* s1 = *((char**) a1);
 *
 * The deal is that qsort works with pointers to the array values it's sorting,
 * not the values themselves.  In this case, the array being sorted contains
 * char* values.  This means that qsort is working with element pointers of
 * type char**, since it's pointing to each char* element of the arrays.
 * Hence, this function receives values of type char**, carried in the generic
 * pointers of type void*.
 */
int str_intcmp(const void* a1, const void* a2) {
    char* s1 = *((char**) a1);
    char* s2 = *((char**) a2);
    int v1 = atoi(s1);
    int v2 = atoi(s2);

    if (v1 < v2) return -1;
    if (v1 > v2) return 1;
    return 0;
} 


/**
 * The main function defines unsorted int and string arrays.  It then calls the
 * system qsort function to sort the int array in ascending and descending
 * orders, using the preceding two int comparison functions.  It also calls
 * qsort to sort the string array in ascending order, using str_intcmp.
 */
int main() {
    int i;
    int int_data[6] = {1, 8, 3, 4, 2, 1};
    char* str_data[6] = {"1", "8", "3", "4", "2", "1"};
    size_t int_nelems = sizeof(int_data) / sizeof(int);
    size_t str_nelems = sizeof(str_data) / sizeof(int);

    /*
     * Print the int array before sorting.
     */
    for (i = 0; i < int_nelems; i++) printf("%d ", int_data[i]);
    printf("\n");

    /*
     * Sort the int array using intcmp comparator and print results.
     */
    qsort((void*) int_data, int_nelems, sizeof(int), intcmp);
    for (i = 0; i < int_nelems; i++) printf("%d ", int_data[i]);
    printf("\n");

    /*
     * Sort the int array using intcmp_reverse comparator and print results.
     */
    qsort((void*) int_data, int_nelems, sizeof(int), intcmp_reverse);
    for (i = 0; i < int_nelems; i++) printf("%d ", int_data[i]);
    printf("\n");


    /*
     * Print the string array before sorting.
     */
    for (i = 0; i < str_nelems; i++) printf("%s ", str_data[i]);
    printf("\n");

    /*
     * Sort the string array using str_intcmp comparator and print results.
     */
    qsort((void*) str_data, str_nelems, sizeof(char*), str_intcmp);
    for (i = 0; i < str_nelems; i++) printf("%s ", str_data[i]);
    printf("\n");

    /*
     * Sort the string array using strcmp as the comparator and print results.
     * Note the cast applied to strcmp as a function pointer.  It's required in
     * order for the compiler not to issue a warning.  It works because 'const
     * void *' is compatible as a parameter type with the 'const char *' type
     * that strcmp's parameters are declared with.  "Compatible" means that a
     * void* formal parameter may be safely passed a pointer of any other
     * type.  See Section A.6.8 on Page 199 of K&R.
     */
    qsort((void*) str_data, str_nelems, sizeof(char*),
	  (int (*) (const void*, const void*)) strcmp);
    for (i = 0; i < str_nelems; i++) printf("%s ", str_data[i]);
    printf("\n");

    exit(0);
}