/* $Id: list.c,v 1.19 2013-05-16 08:40:07 cgarcia Exp $
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/*
 * $Author: cgarcia $
 * $Date: 2013-05-16 08:40:07 $
 * $Revision: 1.19 $
 * $Name: not supported by cvs2svn $
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <list_void.h>

#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/**
 * @defgroup multiset container type
 *
 * The order of elements is undefined and there may be duplicates.
 *
 * This container is polymorphic and typesafe. That is, the elements are typed,
 * not void pointers.
 *
 * A few things to be aware of:
 * - It is left to the user to define whether the list owns (i.e. is responsible
 *   for deallocating) its elements. Therefore the list_delete() function
 *   takes an additional argument which is the element destructor function. If
 *   a NULL function pointer is given, the elements are not deleted.
 * - Iterations: The current position is stored in the list's internal cache.
 *   Therefore, more than one simultaneous iteration over a list is not possible. 
 *   To achieve this functionality, just duplicate the list and iterate over the
 *   different copies. (An alternative solution would be to introduce iterator
 *   objects with the overhead of that.)
 *
 * To define a class called e.g. integer_list, put in a source file
 * @code
 *     #define LIST_DEFINE
 *     #define LIST_ELEM integer 
 *     #include <list.h>
 * @endcode
 *
 * And put in the header
 * @code
 *     #define LIST_ELEM integer 
 *     #include <list.h>
 * @endcode
 *
 */

/**@{*/

/* Same data structure as C++ STL's vector */
struct list
{
    void **elements;
    int size;
    int back;    /* Extra allocated space */

    int current; /* 1 element iteration */

    int current_p1, current_p2; /* pair iteration */
};

/**
   Define memory handling here
 */
//static void *(*const list_malloc)(size_t) = malloc;
//static void *(*const list_calloc)(size_t, size_t) = calloc;
//static void *(*const list_realloc)(void *, size_t) = realloc;
//static void  (*const list_free)(const void *) = (void (*)(const void *))free;
static void *(*list_malloc)(size_t) = malloc;
static void *(*list_calloc)(size_t, size_t) = calloc;
static void *(*list_realloc)(void *, size_t) = realloc;
static void  (*list_free)(const void *) = (void (*)(const void *))free;

/**
   Define error handling here
*/
#include <assert.h>
#define assure(EXPR) assert(EXPR)

/**
 * @brief Constructor
 * @return  newly allocated list
 */
list *
list_new(void)
{
    list *l = list_malloc(sizeof(*l));

    l->elements = NULL;
    l->size = 0;
    l->back = 0;

    return l;
}

/**
 * @brief Copy constructor
 * @param l             list to duplicate
 * @param duplicate     element copy constructor, or NULL for no deep copy
 * @return newly allocated list
 */
list *
list_duplicate(const list *l, void * (*duplicate)(const void *))
{
    assure( l != NULL );

    {
        list *dupl = list_malloc(sizeof(*dupl));
        
        dupl->elements = list_malloc((l->size+l->back) * sizeof(*dupl->elements));
        dupl->size = l->size;
        dupl->back = l->back;
        dupl->current = l->current;
        dupl->current_p1 = l->current_p1;
        dupl->current_p2 = l->current_p2;
        
        {
            int i;
            for (i = 0; i < l->size; i++) {
                if (duplicate != NULL) {
                    dupl->elements[i] = duplicate(l->elements[i]);
                }
                else {
                    dupl->elements[i] = l->elements[i];
                }
            }
        }
        
        return dupl;
    }
}

/*
 * @brief Destructor
 * @param l         list to delete
 * @param delete    element copy constructor. If NULL,
 *                  elements are not deleted.
 */
void
list_delete_const(const list **l, void (*ldelete)(void **))
{
    if (l != NULL && *l != NULL) {

        if (ldelete != NULL) {
            
            int i;
            for (i = 0; i < (*l)->size; i++) {
                ldelete(&((*l)->elements[i]));
            }
        }
        list_free((*l)->elements);
        list_free((*l)); *l = NULL;
    }
    return;
}

void
list_delete(list **l, void (*ldelete)(void **))
{
    list_delete_const((const list **)l, ldelete);
    return;
}

/*
 * @brief Get list size
 * @param l         list
 * @return  number of elements in list 
 *
 * Time: O(1)
 */
int
list_size(const list *l)
{
    assure( l != NULL );

    return l->size;
}

/*
 * @brief Insert element
 * @param l         list 
 * @param e         element to insert. Must be non-NULL
 * @return  number of elements in list 
 *
 * Time: Amortized O(1)
 */
void
list_insert(list *l, void *e)
{
    assure( e != NULL );

    if (l->back == 0) {
        l->back = l->size + 1;
        l->elements = list_realloc(l->elements, (l->size + l->back) * sizeof(*l->elements));
    }
    
    l->size++;
    l->back--;
    l->elements[l->size - 1] = e;

    return;
}

/*
 * @brief Remove element
 * @param l         list
 * @param e         element to remove. Note: pointer comparison
 * @return e
 *
 * The provided element must exist in the list.
 * Only one occurrence of e is removed.
 * The element is removed from the list, but not deallocated.
 *
 * For convenience, the function returns the provided element pointer.
 * This is to allow code like
 * @code
 *     element *e = element_list_remove(l, element_list_first(l));
 * @endcode
 *
 * Time: Worst case O(n), O(1) for removing the element returned by list_first()
 */
const void *
list_remove_const(list *l, const void *e)
{
    assure( l != NULL );
    assure( e != NULL );

    {
        int i;
        int indx = -1;
        for (i = l->size - 1; i >= 0 && indx < 0; i--) {
            if (l->elements[i] == e) {
                indx = i;
            }
        }
        
        assure( indx >= 0 );
        
        for (i = indx; i < l->size-1; i++) {
            l->elements[i] = l->elements[i+1];
        }
    }        
    
    l->size--;
    l->back++;
    
    if (l->back > 4 * l->size) {
        /* Note: amortized constant time */
        l->back = l->size;
        l->elements = list_realloc(l->elements, 
                                   (l->size + l->back) * sizeof(*l->elements));
    }        
    
    return e;
}

void *
list_remove(list *l, void *e)
{
    return (void *)list_remove_const(l, e);    
}

/*
 * @brief Reverse the order of list elements
 * @param l         list
 * @return  Nothing 
 *
 * Time: O(N)
 */
void
list_reverse(list *l)
{
    int i, k;
    
    assure( l != NULL );
    
    for (i = 0, k = l->size-1; i < k; i++, k--)
    {
        void    *tmp;
        tmp = l->elements[i];
        l->elements[i] = l->elements[k];
        l->elements[k] = tmp;
    }

    return;
}

/*
 * @brief Iterate
 * @param l         list
 * @return first element, or NULL if list empty
 *
 * @code
 *     for(element *e = list_first(l);
 *         e != NULL; 
 *         e = list_next(l)) {...}
 * @endcode
 *
 * The list must not be modified between calls to list_first() or list_next()
 *
 * Note: It is not possible to have more simultaneous iterations over the same
 * list. This functionality can be achived by duplicating the list.
 *
 * Time: O(1)
 */
const void *
list_first_const(const list *l)
{
    assure( l != NULL );

    if (l->size == 0) return NULL;

    /* Loop backwards, faster if user
       erases the first element */

    ((list *)l)->current = l->size - 1;
    return l->elements[l->current];
}

void *
list_first(list *l)
{
    return (void *)list_first_const(l);
}

/*
 * @brief Iterate
 * @param l         list
 * @return next element, or NULL if no more elements
 *
 * See list_first().
 *
 * Time: O(1)
 */
const void *
list_next_const(const list *l)
{
    assure( l != NULL );
  
    if (l->size == 0) return NULL;

    ((list *)l)->current -= 1;
    
    if (l->current < 0) return NULL;
    else return l->elements[l->current];
}

void *
list_next(list *l)
{
    return (void *)list_next_const(l);
}

/*
 * @brief Iterate through pairs
 * @param l         list
 * @param e1        (output) first pair 1st element, or NULL if list
                    has less than two elements
 * @param e2        (output) first pair 2nd element
 *
 * The iteration is through the K(n,2) different pairs, i.e. the
 * pair (e1,e2) is considered equal to the pair (e2,e1) and only
 * visited once.
 *
 * @code
 *     for(list_first_pair(l, &e1, &e2);
 *         e1 != NULL; 
 *         list_next_pair(l, &e1, &e2))
 * @endcode
 *
 * The list must not be modified between calls to
 * list_first_pair() or list_next_pair()
 *
 * The current position is cached in the list object. Therefore simultaneous
 * pair iterations over the same list is not allowed.
 *
 * However, iterating pairs simultaneously with a 1 element iteration (using
 * list_first() and list_next()) is allowed.
 *
 * Time: O(1)
 */
void
list_first_pair_const(const list *l,
                      const void **e1,
                      const void **e2)
{
    assure( l != NULL );
    assure( e1 != NULL );
    assure( e2 != NULL );
    
    if (l->size <= 1) {
        *e1 = NULL;
        *e2 = NULL;
        return;
    }

    ((list *)l)->current_p1 = l->size - 1;
    ((list *)l)->current_p2 = l->size - 2;

    *e1 = l->elements[l->current_p1];
    *e2 = l->elements[l->current_p2];
    
    return;
}

void
list_first_pair(list *l,
                void **e1,
                void **e2)
{
    list_first_pair_const(l, 
                          (const void **)e1, 
                          (const void **)e2);

    return;
}

/*
 * @brief Iterate through pairs
 * @param l         list
 * @param e1        (output) next pair 1st element, or NULL if no more pairs
 * @param e2        (output) next pair 2nd element, or NULL if no more pairs
 *
 * See list_first_pair().
 *
 * Time: O(1)
 */
void
list_next_pair_const(const list *l,
                     const void **e1,
                     const void **e2)
{
    assure( l != NULL );
    assure( e1 != NULL );
    assure( e2 != NULL );
  
    if (l->size <= 1) {
        *e1 = NULL;
        *e2 = NULL;
        return;
    }
    
    ((list *)l)->current_p2 -= 1;

    if (l->current_p2 < 0) {
        ((list *)l)->current_p1 -= 1;
        ((list *)l)->current_p2 = l->current_p1 - 1;
        if (l->current_p2 < 0) {
            *e1 = NULL;
            *e2 = NULL;
            return;
        }
        *e1 = l->elements[l->current_p1];
        *e2 = l->elements[l->current_p2];
        return;
    }
    
    *e2 = l->elements[l->current_p2];
    return;
}

void
list_next_pair(list *l,
               void **e1,
               void **e2)
{
    list_next_pair_const(l, (const void **)e1, (const void **)e2);
    return;
}

/**
 * @brief Extract elements
 * @param l          list
 * @param duplicate  element copy constructor
 * @param predicate  function returning true iff the element (given as the 1st)
 *                   argument must be extracted.
 * @param data       Auxillary data sent to the selection function. May be NULL.
 * @return Newly allocated, possibly empty, list containing the elements which
 *         satisfy the given predicate
 *
 * Time: O(n)
 */
list *
list_extract(const list *l,
             void *(*duplicate)(const void *),
             bool (*predicate)(const void *, void *),
             void *data)
{
    assure( l != NULL );
    assure( duplicate != NULL);
    assure( predicate != NULL);

    {
        list *ex = list_new();
        int i;

        for (i = 0; i < l->size; i++) {
            if (predicate(l->elements[i], data)) {
                list_insert(ex, duplicate(l->elements[i]));
            }
        }

        return ex;
    }
}

/*
 * @brief Find minimum element
 * @param l          non-empty list
 * @param less_than  comparison function which must return true, iff
 *                   its first argument is considered less than the second argument.
 *                   The 3rd argument is auxillary data used for the comparison.
 *                   The provided function should be a strict ordering (i.e. behave
 *                   like <)
 * @param data       Auxillary data sent to the comparison function. May be NULL.
 * @return minimum element
 *
 * Time: O(n)
 */
void *
list_min(list *l, list_func_lt less_than, void *data)
{
    assure( l != NULL );
    assure( less_than != NULL);
    assure( list_size(l) > 0);

    {
        int minindex = 0;
        int i;
        for (i = 1; i < l->size; i++) {
            if (less_than(l->elements[i], l->elements[minindex], data))
                minindex = i;
        }
        
        return l->elements[minindex];
    }
}

/*
 * @brief Find minimum element
 * @param l          non-empty list
 * @param eval       evaluation function
 * @param data       Auxillary data sent to the evaluation function. May be NULL.
 * @return minimum (as defined by the evaluation function) element
 *
 * Time: O(n)
 */
void *
list_min_val(list *l, list_func_eval eval, void *data)
{
    assure( l != NULL );
    assure( eval != NULL);
    assure( list_size(l) > 0);
    
    {
        int minindex = 0;
        double minval = eval(l->elements[0], data);
        int i;

        for (i = 1; i < l->size; i++) {
            double val = eval(l->elements[i], data);
            if (val < minval) {
                minval = val;
                minindex = i;
            }
        }
        
        return l->elements[minindex];
    }
}

/*
 * @brief Find maximum element
 * @param l          non-empty list
 * @param eval       evaluation function
 * @param data       Auxillary data sent to the evaluation function. May be NULL.
 * @return maximum (as defined by the evaluation function) element
 *
 * Time: O(n)
 */
void *
list_max_val(list *l, list_func_eval eval, void *data)
{
    assure( l != NULL );
    assure( eval != NULL);
    assure( list_size(l) > 0);
    
    {
        int maxindex = 0;
        double maxval = eval(l->elements[0], data);
        int i;
        
        for (i = 1; i < l->size; i++) {
            double val = eval(l->elements[i], data);
            if (val > maxval) {
                maxval = val;
                maxindex = i;
            }
        }
        
        return l->elements[maxindex];
    }
}

/*
 * @brief Find maximum element
 * @param l          see list_min()
 * @param less_than  see list_min()
 * @param data       see list_min()
 * @return maximum element
 *
 * Time: O(n)
 */
const void *
list_max_const(const list *l, list_func_lt less_than, void *data)
{
    assure( l != NULL );
    assure( less_than != NULL);
    assure( list_size(l) > 0);
    
    {
        int maxindex = 0;
        int i;
        for (i = 1; i < l->size; i++) {
            if (!less_than(l->elements[i], l->elements[maxindex], data))
                maxindex = i;
        }
        
        return l->elements[maxindex];
    }
}

void *
list_max(list *l, list_func_lt less_than, void *data)
{
    return (void *)list_max_const(l, less_than, data);
}


/*
 * @brief Find k'th element
 * @param l          non-empty list
 * @param k          between 1 and list size, inclusive.
 * @param n          list size
 * @param less_than  see list_min()
 * @param data       see list_min()
 * @return k'th element
 *
 * Be careful to provide an irreflexive comparison function (i.e.
 * x < x must always be false), or this function may not return.
 *
 * Time: Worst case O(n*n). Average over all input: O(n)
 */
static const void *
kth(const void *a[], int k, int n, list_func_lt less_than, void *data)
{
    int i, j, lo, hi;
    
    k -= 1;

    lo = 0;
    hi = n - 1;
    while (lo < hi) {
        const void *pivot = a[k];//fixme select randomly, swap with a[k]
        i = lo;
        j = hi;
        do {
            while (less_than(a[i], pivot, data)) i++;
            while (less_than(pivot, a[j], data)) {
                j--;
            }
            if (i <= j) {
                const void *tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
                i++; j--;
            }
        } while (i <= j);
        if (j < k) lo = i;
        if (k < i) hi = j;
    }
    return a[k];
}

const void *
list_kth_const(const list *l, int k,
               list_func_lt less_than, void *data)
{
    void    *kp;
    void    **elemcpy;
    int     len;
    
    assure( l != NULL );
    assure( 1 <= k && k <= l->size );
    
    /* the function kth() scrambles the elements, so copy them */
    len = l->size * sizeof(*l->elements);
    elemcpy = list_malloc(len);
    memcpy(elemcpy, l->elements, len);
    
    kp = (void*)kth((const void **)elemcpy, k, l->size, less_than, data);
    
    list_free(elemcpy);
    
    return kp;
}

void *
list_kth(list *l, int k,
         list_func_lt less_than,
         void *data)
{
    return (void *)list_kth_const(l, k, less_than, data);
}

/*
 * @brief Determine order of elements given an evaluation function
 * @param e1         first element
 * @param e2         second element
 * @param data       containing the evaluation function, and
 *                   additional data which is passed to the evaluation function
 * @return true iff e1 evaluates to a number less than e2
 */
static
bool val_less_than(const void *e1, const void *e2, void *data)
{
    struct {
        list_func_eval f;
        void *aux_data;
    } *d = data;
    /* Cast is safe, see caller of this function */

    
    /* Unfortunately, as the following commented code demonstrated,
       with GCC-4.2.0 calling the evaluation function two times with the same
       input can give two numbers which satisfy both equality (==) *and*
       less than (<) (!) but not greater than (>). This causes the kth function
       to loop infinitely.

       Avoid that by handling explicitly this special case.
    */

    if (e1 == e2) return false;

    /*
    double d1 = d->f(e1, d->aux_data);
    fprintf(stderr, "%s got %f %g \n", __func__, d1, d1);
    fprintf(stderr, "%.100f\n", d1);

    double d2 = d->f(e2, d->aux_data);
    fprintf(stderr, "%s %d %d %d\n", __func__, i1, i2, i3);

    fprintf(stderr, "%s got %f %g %d  %d\n", __func__, d2, d2, d1 < d2, e1 == e2);
    fprintf(stderr, "%.100f  %d %d  %d  %d %d\n", d2, 
            d1 < d2, d2 > d1, d1 == d2, d1 > d2, d2 < d1);

    fprintf(stderr, "l1 = %ld\n", d1);
    fprintf(stderr, "l2 = %ld\n", d2);
    */    

    return (d->f(e1, d->aux_data) < d->f(e2, d->aux_data));
}

/*
 * @brief k'th element
 * @param    l        list
 * @param    k        counting from 1
 * @param    eval     returns the value of an element
 * @param    data     sent to eval
 * @return   a k'th element according to the evaluation function
 */
const void *
list_kth_val_const(const list *l, int k, list_func_eval eval, void *data)
{
    assure( l != NULL );
    assure( 1 <= k && k <= l->size );
    assure( eval != NULL );

    struct {
        list_func_eval f;
        void *aux_data;
    } d;
    d.f = eval;
    d.aux_data = data;
    
    return list_kth_const(l, k, val_less_than, &d);    
}
void *
list_kth_val(list *l, int k, list_func_eval eval, void *data)
{
    return (void *) list_kth_val_const(l, k, eval, data);
}

/*
 * @brief Compute median
 * @param l          list
 * @param eval       returns the value of an element
 * @param data       additional data passed to eval
 * @return median
 *
 * Time: Average O(n).
 *
 * For an even number of elements, the median is the
 * average of the two central values.
 */
double
list_median(const list *l, list_func_eval eval, void *data)
{
    assure( l != NULL );
    assure( eval != NULL );
    assure( l->size > 0 );

    const void *median_element = list_kth_val_const(l, (l->size+1)/2, eval, data);
    
    double median_val = eval(median_element, data);

    if (list_size(l) && 2 == 0)
        {
            const void *other_median_element = 
                list_kth_val_const(l, (l->size+2)/2, eval, data);
            
            median_val = (median_val + eval(other_median_element, data) ) / 2.0;
        }
    
    return median_val;
}

/*
 * @brief Compute mean
 * @param l          list
 * @param eval       returns the value of an element
 * @param data       additional data passed to eval
 * @return arithmetic average
 *
 * Time: O(n).
 */
double
list_mean(const list *l, list_func_eval eval, void *data)
{
    assure( l != NULL );
    assure( eval != NULL );
    assure( l->size > 0 );
    
    double result = eval(l->elements[0], data);
    int i;
    
    for (i = 1; i < l->size; i++) {
        result += eval(l->elements[i], data);
    }
    
    result /= l->size;

    return result;
}

/*
 * @brief Compute optimally weighted mean
 * @param l          list with two or more elements
 * @param eval       returns the value of an element
 * @param data       additional data passed to eval
 * @param eval_err   returns the error (1 sigma) of an element,
 *                   must be positive
 * @param data_err   additional data passed to eval_err
 * @param err        (output) square root variance
 * @param red_chisq  (output) reduced chi square, may be NULL
 * @return optimal mean
 *
 *  average  =  [ sum  x_i / sigma_i^2 ] / [sum 1 / sigma_i^2]
 *  variance =                         1 / [sum 1 / sigma_i^2]
 *
 *  chi^2/(n-1) = sum ((x_i - average) / sigma_i)^2  / (n-1)
 *
 * Time: O(n).
 */
double
list_mean_optimal(const list *l, 
                  list_func_eval eval, void *data_eval,
                  list_func_eval eval_err, void *data_err,
                  double *err,
                  double *red_chisq)
{
    assure( l != NULL );
    assure( l->size >= 1 );
    assure( red_chisq == NULL || l->size >= 2 );
    assure( eval != NULL );
    assure( eval_err != NULL );
    assure( err != NULL );

    double sum_weights = 0;
    double opt_average = 0;
    int i;

    for (i = 0; i < l->size; i++) {
        void *e = l->elements[i];
        double sigma  = eval_err(e, data_err);

        assure( sigma > 0 );
        
        double weight = 1/(sigma*sigma);
        
        opt_average += eval(e, data_eval) * weight;
        sum_weights += weight;
    }
    opt_average /= sum_weights;
    *err = 1 / sqrt(sum_weights);

    if (red_chisq != NULL) {
        *red_chisq = 0;
        for (i = 0; i < l->size; i++) {
            void *e = l->elements[i];
            double residual = ((eval(e, data_eval) - opt_average)) / eval_err(e, data_err);
            *red_chisq += residual * residual;
        }
        *red_chisq /= (l->size - 1);
    }

    return opt_average;
}



/*
 * @brief Compute absolute deviation
 * @param e1        element
 * @param data      reference value, and evaluation function
 *                  and additional data passed to the evaluation function
 * @return absolute difference between e1 and reference point
 */
static double
abs_dev(const void *e1, void *data)
{
    struct {
        double ref;
        list_func_eval f;
        void *aux_data;
    } *d = data;
    /* Cast is safe, see caller */

    return fabs(d->f(e1, d->aux_data) - d->ref);
}

/*
 * @brief Compute median absolute deviation wrt median
 * @param l          list
 * @param eval       returns the value of an element
 * @param data       additional data passed to eval
 * @return mad
 *
 * Time: Average O(n).
 */
double
list_mad(list *l, list_func_eval eval, void *data)
{
    assure( l != NULL );
    assure( eval != NULL );

    double median = list_median(l, eval, data);

    struct {
        double ref;
        list_func_eval f;
        void *aux_data;
    } d;

    d.ref = median;
    d.f = eval;
    d.aux_data = data;

    return list_median(l, abs_dev, &d);
}

/**@}*/
