/* -*- mode: C -*-  */
/*
   IGraph library.
   Copyright (C) 2007-2012  Gabor Csardi <csardi.gabor@gmail.com>
   334 Harvard street, Cambridge, MA 02139 USA

   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 Street, Fifth Floor, Boston, MA
   02110-1301 USA

*/

#include "igraph_types.h"
#include "igraph_types_internal.h"
#include "igraph_complex.h"
#include "bigint.h"
#include "config.h"
#include <float.h>

#define BASE_IGRAPH_REAL
#include "igraph_pmt.h"
#include "vector.pmt"
#include "igraph_pmt_off.h"
#undef BASE_IGRAPH_REAL

#define BASE_FLOAT
#include "igraph_pmt.h"
#include "vector.pmt"
#include "igraph_pmt_off.h"
#undef BASE_FLOAT

#define BASE_LONG
#include "igraph_pmt.h"
#include "vector.pmt"
#include "igraph_pmt_off.h"
#undef BASE_LONG

#define BASE_CHAR
#include "igraph_pmt.h"
#include "vector.pmt"
#include "igraph_pmt_off.h"
#undef BASE_CHAR

#define BASE_BOOL
#include "igraph_pmt.h"
#include "vector.pmt"
#include "igraph_pmt_off.h"
#undef BASE_BOOL

#define BASE_INT
#include "igraph_pmt.h"
#include "vector.pmt"
#include "igraph_pmt_off.h"
#undef BASE_INT

#define BASE_COMPLEX
#include "igraph_pmt.h"
#include "vector.pmt"
#include "igraph_pmt_off.h"
#undef BASE_COMPLEX

#define BASE_LIMB
#include "igraph_pmt.h"
#include "vector.pmt"
#include "igraph_pmt_off.h"
#undef BASE_LIMB

#include "igraph_math.h"

int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to) {
    long int i, n = igraph_vector_size(from);

    IGRAPH_CHECK(igraph_vector_long_resize(to, n));
    for (i = 0; i < n; i++) {
        VECTOR(*to)[i] = (long int) floor(VECTOR(*from)[i]);
    }
    return 0;
}

int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to) {
    long int i, n = igraph_vector_size(from);

    IGRAPH_CHECK(igraph_vector_long_resize(to, n));
    for (i = 0; i < n; i++) {
        VECTOR(*to)[i] = (long int) round(VECTOR(*from)[i]);
    }
    return 0;
}

int igraph_vector_order2(igraph_vector_t *v) {

    igraph_indheap_t heap;

    igraph_indheap_init_array(&heap, VECTOR(*v), igraph_vector_size(v));
    IGRAPH_FINALLY(igraph_indheap_destroy, &heap);

    igraph_vector_clear(v);
    while (!igraph_indheap_empty(&heap)) {
        IGRAPH_CHECK(igraph_vector_push_back(v, igraph_indheap_max_index(&heap) - 1));
        igraph_indheap_delete_max(&heap);
    }

    igraph_indheap_destroy(&heap);
    IGRAPH_FINALLY_CLEAN(1);
    return 0;
}

/**
 * \ingroup vector
 * \function igraph_vector_order
 * \brief Calculate the order of the elements in a vector.
 *
 * </para><para>
 * The smallest element will have order zero, the second smallest
 * order one, etc.
 * \param v The original \type igraph_vector_t object.
 * \param v2 A secondary key, another \type igraph_vector_t object.
 * \param res An initialized \type igraph_vector_t object, it will be
 *    resized to match the size of \p v. The
 *    result of the computation will be stored here.
 * \param nodes Hint, the largest element in \p v.
 * \return Error code:
 *         \c IGRAPH_ENOMEM: out of memory
 *
 * Time complexity: O()
 */

int igraph_vector_order(const igraph_vector_t* v,
                        const igraph_vector_t *v2,
                        igraph_vector_t* res, igraph_real_t nodes) {
    long int edges = igraph_vector_size(v);
    igraph_vector_t ptr;
    igraph_vector_t rad;
    long int i, j;

    assert(v != NULL);
    assert(v->stor_begin != NULL);

    IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1);
    IGRAPH_VECTOR_INIT_FINALLY(&rad, edges);
    IGRAPH_CHECK(igraph_vector_resize(res, edges));

    for (i = 0; i < edges; i++) {
        long int radix = (long int) v2->stor_begin[i];
        if (VECTOR(ptr)[radix] != 0) {
            VECTOR(rad)[i] = VECTOR(ptr)[radix];
        }
        VECTOR(ptr)[radix] = i + 1;
    }

    j = 0;
    for (i = 0; i < nodes + 1; i++) {
        if (VECTOR(ptr)[i] != 0) {
            long int next = (long int) VECTOR(ptr)[i] - 1;
            res->stor_begin[j++] = next;
            while (VECTOR(rad)[next] != 0) {
                next = (long int) VECTOR(rad)[next] - 1;
                res->stor_begin[j++] = next;
            }
        }
    }

    igraph_vector_null(&ptr);
    igraph_vector_null(&rad);

    for (i = 0; i < edges; i++) {
        long int edge = (long int) VECTOR(*res)[edges - i - 1];
        long int radix = (long int) VECTOR(*v)[edge];
        if (VECTOR(ptr)[radix] != 0) {
            VECTOR(rad)[edge] = VECTOR(ptr)[radix];
        }
        VECTOR(ptr)[radix] = edge + 1;
    }

    j = 0;
    for (i = 0; i < nodes + 1; i++) {
        if (VECTOR(ptr)[i] != 0) {
            long int next = (long int) VECTOR(ptr)[i] - 1;
            res->stor_begin[j++] = next;
            while (VECTOR(rad)[next] != 0) {
                next = (long int) VECTOR(rad)[next] - 1;
                res->stor_begin[j++] = next;
            }
        }
    }

    igraph_vector_destroy(&ptr);
    igraph_vector_destroy(&rad);
    IGRAPH_FINALLY_CLEAN(2);

    return 0;
}

int igraph_vector_order1(const igraph_vector_t* v,
                         igraph_vector_t* res, igraph_real_t nodes) {
    long int edges = igraph_vector_size(v);
    igraph_vector_t ptr;
    igraph_vector_t rad;
    long int i, j;

    assert(v != NULL);
    assert(v->stor_begin != NULL);

    IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1);
    IGRAPH_VECTOR_INIT_FINALLY(&rad, edges);
    IGRAPH_CHECK(igraph_vector_resize(res, edges));

    for (i = 0; i < edges; i++) {
        long int radix = (long int) v->stor_begin[i];
        if (VECTOR(ptr)[radix] != 0) {
            VECTOR(rad)[i] = VECTOR(ptr)[radix];
        }
        VECTOR(ptr)[radix] = i + 1;
    }

    j = 0;
    for (i = 0; i < nodes + 1; i++) {
        if (VECTOR(ptr)[i] != 0) {
            long int next = (long int) VECTOR(ptr)[i] - 1;
            res->stor_begin[j++] = next;
            while (VECTOR(rad)[next] != 0) {
                next = (long int) VECTOR(rad)[next] - 1;
                res->stor_begin[j++] = next;
            }
        }
    }

    igraph_vector_destroy(&ptr);
    igraph_vector_destroy(&rad);
    IGRAPH_FINALLY_CLEAN(2);

    return 0;
}

int igraph_vector_order1_int(const igraph_vector_t* v,
                             igraph_vector_int_t* res,
                             igraph_real_t nodes) {
    long int edges = igraph_vector_size(v);
    igraph_vector_t ptr;
    igraph_vector_t rad;
    long int i, j;

    assert(v != NULL);
    assert(v->stor_begin != NULL);

    IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1);
    IGRAPH_VECTOR_INIT_FINALLY(&rad, edges);
    IGRAPH_CHECK(igraph_vector_int_resize(res, edges));

    for (i = 0; i < edges; i++) {
        long int radix = (long int) v->stor_begin[i];
        if (VECTOR(ptr)[radix] != 0) {
            VECTOR(rad)[i] = VECTOR(ptr)[radix];
        }
        VECTOR(ptr)[radix] = i + 1;
    }

    j = 0;
    for (i = 0; i < nodes + 1; i++) {
        if (VECTOR(ptr)[i] != 0) {
            long int next = (long int) VECTOR(ptr)[i] - 1;
            res->stor_begin[j++] = next;
            while (VECTOR(rad)[next] != 0) {
                next = (long int) VECTOR(rad)[next] - 1;
                res->stor_begin[j++] = next;
            }
        }
    }

    igraph_vector_destroy(&ptr);
    igraph_vector_destroy(&rad);
    IGRAPH_FINALLY_CLEAN(2);

    return 0;
}

int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res,
                       long int nodes) {

    igraph_vector_t rad;
    igraph_vector_t ptr;
    long int edges = igraph_vector_size(v);
    long int i, c = 0;

    IGRAPH_VECTOR_INIT_FINALLY(&rad, nodes);
    IGRAPH_VECTOR_INIT_FINALLY(&ptr, edges);
    IGRAPH_CHECK(igraph_vector_resize(res, edges));

    for (i = 0; i < edges; i++) {
        long int elem = (long int) VECTOR(*v)[i];
        VECTOR(ptr)[i] = VECTOR(rad)[elem];
        VECTOR(rad)[elem] = i + 1;
    }

    for (i = 0; i < nodes; i++) {
        long int p = (long int) VECTOR(rad)[i];
        while (p != 0) {
            VECTOR(*res)[p - 1] = c++;
            p = (long int) VECTOR(ptr)[p - 1];
        }
    }

    igraph_vector_destroy(&ptr);
    igraph_vector_destroy(&rad);
    IGRAPH_FINALLY_CLEAN(2);
    return 0;
}

#ifndef USING_R
int igraph_vector_complex_print(const igraph_vector_complex_t *v) {
    long int i, n = igraph_vector_complex_size(v);
    if (n != 0) {
        igraph_complex_t z = VECTOR(*v)[0];
        printf("%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z));
    }
    for (i = 1; i < n; i++) {
        igraph_complex_t z = VECTOR(*v)[i];
        printf(" %g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z));
    }
    printf("\n");
    return 0;
}
#endif

int igraph_vector_complex_fprint(const igraph_vector_complex_t *v,
                                 FILE *file) {
    long int i, n = igraph_vector_complex_size(v);
    if (n != 0) {
        igraph_complex_t z = VECTOR(*v)[0];
        fprintf(file, "%g%+g", IGRAPH_REAL(z), IGRAPH_IMAG(z));
    }
    for (i = 1; i < n; i++) {
        igraph_complex_t z = VECTOR(*v)[i];
        fprintf(file, " %g%+g", IGRAPH_REAL(z), IGRAPH_IMAG(z));
    }
    fprintf(file, "\n");
    return 0;
}

int igraph_vector_complex_real(const igraph_vector_complex_t *v,
                               igraph_vector_t *real) {
    int i, n = (int) igraph_vector_complex_size(v);
    IGRAPH_CHECK(igraph_vector_resize(real, n));
    for (i = 0; i < n; i++) {
        VECTOR(*real)[i] = IGRAPH_REAL(VECTOR(*v)[i]);
    }

    return 0;
}

int igraph_vector_complex_imag(const igraph_vector_complex_t *v,
                               igraph_vector_t *imag) {
    int i, n = (int) igraph_vector_complex_size(v);
    IGRAPH_CHECK(igraph_vector_resize(imag, n));
    for (i = 0; i < n; i++) {
        VECTOR(*imag)[i] = IGRAPH_IMAG(VECTOR(*v)[i]);
    }

    return 0;
}

int igraph_vector_complex_realimag(const igraph_vector_complex_t *v,
                                   igraph_vector_t *real,
                                   igraph_vector_t *imag) {
    int i, n = (int) igraph_vector_complex_size(v);
    IGRAPH_CHECK(igraph_vector_resize(real, n));
    IGRAPH_CHECK(igraph_vector_resize(imag, n));
    for (i = 0; i < n; i++) {
        igraph_complex_t z = VECTOR(*v)[i];
        VECTOR(*real)[i] = IGRAPH_REAL(z);
        VECTOR(*imag)[i] = IGRAPH_IMAG(z);
    }

    return 0;
}

int igraph_vector_complex_create(igraph_vector_complex_t *v,
                                 const igraph_vector_t *real,
                                 const igraph_vector_t *imag) {
    int i, n = (int) igraph_vector_size(real);
    if (n != igraph_vector_size(imag)) {
        IGRAPH_ERROR("Real and imag vector sizes don't match", IGRAPH_EINVAL);
    }

    IGRAPH_CHECK(igraph_vector_complex_init(v, n));
    /* FINALLY not needed */

    for (i = 0; i < n; i++) {
        VECTOR(*v)[i] = igraph_complex(VECTOR(*real)[i], VECTOR(*imag)[i]);
    }

    return 0;
}

int igraph_vector_complex_create_polar(igraph_vector_complex_t *v,
                                       const igraph_vector_t *r,
                                       const igraph_vector_t *theta) {
    int i, n = (int) igraph_vector_size(r);
    if (n != igraph_vector_size(theta)) {
        IGRAPH_ERROR("'r' and 'theta' vector sizes don't match", IGRAPH_EINVAL);
    }

    IGRAPH_CHECK(igraph_vector_complex_init(v, n));
    /* FINALLY not needed */

    for (i = 0; i < n; i++) {
        VECTOR(*v)[i] = igraph_complex_polar(VECTOR(*r)[i], VECTOR(*theta)[i]);
    }

    return 0;
}

igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs,
                                  const igraph_vector_t *rhs,
                                  igraph_real_t tol) {
    long int i, s;
    assert(lhs != 0);
    assert(rhs != 0);
    assert(lhs->stor_begin != 0);
    assert(rhs->stor_begin != 0);

    s = igraph_vector_size(lhs);
    if (s != igraph_vector_size(rhs)) {
        return 0;
    } else {
        if (tol == 0) {
            tol = DBL_EPSILON;
        }
        for (i = 0; i < s; i++) {
            igraph_real_t l = VECTOR(*lhs)[i];
            igraph_real_t r = VECTOR(*rhs)[i];
            if (l < r - tol || l > r + tol) {
                return 0;
            }
        }
        return 1;
    }
}

int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) {
    int i, n = igraph_vector_size(v);
    if (tol < 0.0) {
        IGRAPH_ERROR("`tol' tolerance must be non-negative", IGRAPH_EINVAL);
    }
    if (tol == 0.0) {
        tol = sqrt(DBL_EPSILON);
    }
    for (i = 0; i < n; i++) {
        igraph_real_t val = VECTOR(*v)[i];
        if (val < tol && val > -tol) {
            VECTOR(*v)[i] = 0.0;
        }
    }
    return 0;
}
