#include <float.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// Find the minimum among two int
int min_2(int a, int b) {return b < a ? b : a;}

// Allocate memory or abord on failure
void* xmalloc(size_t size)
{
    if (size == 0)
        fprintf(stderr,"xmalloc: zero size");
    void *p = malloc(size);
    if (!p)
    {
        double sm = size / (0x100000 * 1.0);
        fprintf(stderr,"xmalloc: out of memory when requesting "
                "%zu bytes (%gMB)",//:\"%s\"",
                size, sm);//, strerror(errno));
    }
    return p;
}

// Reallocate memory of abort on failure
void* xrealloc(void* p, size_t size)
{
    void *r = realloc(p, size);
    if (!r) fprintf(stderr,"realloc failed");
    return r;
}

// Free memory allocated by xmalloc or xrealloc.
void xfree(void* p)
{
    if (!p)
        fprintf(stderr,"trying to free a null pointer");
    free(p);
}

// Write a formatted message to standard error and abort the program, for
// example, fatal_error("Failed to open file %s for writing", filename);
void fatal_error(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    fprintf(stderr, "Fatal error: ");
    vfprintf(stderr, format, args);
    fprintf(stderr, "\n");
    va_end(args);
    exit(1);
}

// Write formatted message to standard error for debugging.
void debug(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    fprintf(stderr, "Debug: ");
    vfprintf(stderr, format, args);
    fprintf(stderr, "\n");
    va_end(args);
}

// Find the maximum value of an array and store its index in the array.
float find_array_max(const float* array, int length, int *position)
{
    double max = -FLT_MAX;
    for(int i = 0; i < length; i++){
        if(array[i] > max){
            *position = i;
            max = array[i];
        }
    }
    return max;
}

// Find the minimum value of an array and store its index in the array.
float find_array_min(const float* array, int length, int *position)
{
    double min = FLT_MAX;
    for(int i = 0; i < length; i++){
        if(array[i] < min){
            min = array[i];
            *position = i;
        }
    }
    return min;
}

// Find the maximum value of an array.
float array_max(const float* array, int length)
{
    int i;
    float max = find_array_max(array, length, &i);
    (void)i;
    return max;
}

// Find the minimum value of an array.
float array_min(const float* array, int length)
{
    int i;
    float min = find_array_min(array, length, &i);
    (void)i;
    return min;
}

// Find the two minimal values of an array.
void find_array_two_min(const float* array, int length, float* minA, float* minB, int* iA, int* iB)
{
    if(array[0] < array[1]){
        *iA = 0;
        *iB = 1;
        *minA = array[0];
        *minB = array[1];
    }
    else{
        *iA = 1;
        *iB = 0;
        *minA = array[1];
        *minB = array[0];
    }
    for(int i = 2; i < length; i++){
        if(array[i] < *minA){
            *minB = *minA;
            *minA = array[i];
            *iB = *iA;
            *iA = i;
        }
        else if( array[i] < *minB){
            *minB = array[i];
            *iB = i;
        }
    }
}

// Compute the SQUARE Euclidean distance.
float euclidean_distance_square(const float* x, const float* y, int length)
{
    float d = 0.0;
    for (int i = 0; i < length; i++) {
        float t = (x[i] - y[i]);
        d += t*t;
    }
    return d;
}

// Compute the Euclidean distance.
float euclidean_distance(const float* x, const float* y, int length)
{
    return sqrt(euclidean_distance_square(x, y, length));
}

// L2 norm of a vector
float array_l2norm(const float* array, int length)
{
    float l2norm = 0;
    for(int i = 0; i < length; i++){
        l2norm += array[i]*array[i];
    }
    l2norm = sqrt(l2norm);
    return l2norm;
}

// Linearly rescale input such that its values are in the range [0, 1].
void linear_conversion(const float *in, float *out, int l)
{
    float a, b;
    float min = array_min(in, l);
    float max = array_max(in, l);

    // skip the normalization if max = min
    if( max > min + 0.00001){
        a = (1.0 - 0.0) / (max - min);
        b = -a * min;
        for (int i = 0; i < l; i++)
            out[i] = a * in[i] + b;
    }
    else{
        for (int i = 0; i < l; i++)
            out[i] = in[i];
    }
}

// Compute the x modulus y
float modulus(float x, float y)
{
    float z = x;
    int n;
    if(z < 0){
        n = (int)((-z)/y)+1;
        z += n*y;
    }
    n = (int)(z/y);
    z -= n*y;
    return z;
}

// Multiply the rotation matric R_alpha with [x,y]^T
void apply_rotation(float x, float y, float *rx, float *ry, float alpha)
{
    float c = cos(alpha);
    float s = sin(alpha);
    float tx = c * x - s * y;
    float ty = s * x + c * y;
    *rx = tx;
    *ry = ty;
}
