/*
 * Create and return a svm_csr_problem struct from a scipy.sparse.csr matrix. It is
 * up to the user to free resulting structure.
 *
 * TODO: precomputed kernel.
 */
struct svm_csr_problem * csr_set_problem (char *values, npy_intp *n_indices,
		char *indices, npy_intp *n_indptr, char *indptr, char *Y,
                char *sample_weight, int kernel_type) {

    struct svm_csr_problem *problem;
    problem = malloc (sizeof (struct svm_csr_problem));
    if (problem == NULL) return NULL;
    problem->l = (int) n_indptr[0] - 1;
    problem->y = (double *) Y;
    problem->x = csr_to_libsvm((double *) values, (int *) indices,
                               (int *) indptr, problem->l);
    /* should be removed once we implement weighted samples */
    problem->W = (double *) sample_weight;

    if (problem->x == NULL) {
        free(problem);
        return NULL;
    }
    return problem;
}
/*
 * Predict using a model, where data is expected to be enconded into a csr matrix.
 */
int csr_copy_predict (npy_intp *data_size, char *data, npy_intp *index_size,
		char *index, npy_intp *intptr_size, char *intptr, struct svm_csr_model *model,
		char *dec_values) {
    double *t = (double *) dec_values;
    struct svm_csr_node **predict_nodes;
    npy_intp i;

    predict_nodes = csr_to_libsvm((double *) data, (int *) index,
                                  (int *) intptr, intptr_size[0]-1);

    if (predict_nodes == NULL)
        return -1;
    for(i=0; i < intptr_size[0] - 1; ++i) {
        *t = svm_csr_predict(model, predict_nodes[i]);
        free(predict_nodes[i]);
        ++t;
    }
    free(predict_nodes);
    return 0;
}
int csr_copy_predict_proba (npy_intp *data_size, char *data, npy_intp *index_size,
		char *index, npy_intp *intptr_size, char *intptr, struct svm_csr_model *model,
		char *dec_values) {

    struct svm_csr_node **predict_nodes;
    npy_intp i;
    int m = model->nr_class;

    predict_nodes = csr_to_libsvm((double *) data, (int *) index,
                                  (int *) intptr, intptr_size[0]-1);

    if (predict_nodes == NULL)
        return -1;
    for(i=0; i < intptr_size[0] - 1; ++i) {
        svm_csr_predict_probability(
		model, predict_nodes[i], ((double *) dec_values) + i*m);
        free(predict_nodes[i]);
    }
    free(predict_nodes);
    return 0;
}
struct svm_csr_model *csr_set_model(struct svm_parameter *param, int nr_class,
                            char *SV_data, npy_intp *SV_indices_dims,
                            char *SV_indices, npy_intp *SV_indptr_dims,
                            char *SV_intptr,
                            char *sv_coef, char *rho, char *nSV, char *label,
                            char *probA, char *probB)
{
    struct svm_csr_model *model;
    double *dsv_coef = (double *) sv_coef;
    int i, m;

    m = nr_class * (nr_class-1)/2;

    if ((model = malloc(sizeof(struct svm_csr_model))) == NULL)
        goto model_error;
    if ((model->nSV = malloc(nr_class * sizeof(int))) == NULL)
        goto nsv_error;
    if ((model->label = malloc(nr_class * sizeof(int))) == NULL)
        goto label_error;
    if ((model->sv_coef = malloc((nr_class-1)*sizeof(double *))) == NULL)
        goto sv_coef_error;
    if ((model->rho = malloc( m * sizeof(double))) == NULL)
        goto rho_error;

    /* in the case of precomputed kernels we do not use
       dense_to_precomputed because we don't want the leading 0. As
       indices start at 1 (not at 0) this will work */
    model->l = (int) SV_indptr_dims[0] - 1;
    model->SV = csr_to_libsvm((double *) SV_data, (int *) SV_indices,
                              (int *) SV_intptr, model->l);
    model->nr_class = nr_class;
    model->param = *param;

    /*
     * regression and one-class does not use nSV, label.
     */
    if (param->svm_type < 2) {
        memcpy(model->nSV,   nSV,   model->nr_class * sizeof(int));
        memcpy(model->label, label, model->nr_class * sizeof(int));
    }

    for (i=0; i < model->nr_class-1; i++) {
        /*
         * We cannot squash all this mallocs in a single call since
         * svm_destroy_model will free each element of the array.
         */
        if ((model->sv_coef[i] = malloc((model->l) * sizeof(double))) == NULL) {
            int j;
            for (j=0; j<i; j++)
                free(model->sv_coef[j]);
            goto sv_coef_i_error;
        }
        memcpy(model->sv_coef[i], dsv_coef, (model->l) * sizeof(double));
        dsv_coef += model->l;
    }

    for (i=0; i<m; ++i) {
        (model->rho)[i] = -((double *) rho)[i];
    }

    /*
     * just to avoid segfaults, these features are not wrapped but
     * svm_destroy_model will try to free them.
     */

    if (param->probability) {
        if ((model->probA = malloc(m * sizeof(double))) == NULL)
            goto probA_error;
        memcpy(model->probA, probA, m * sizeof(double));
        if ((model->probB = malloc(m * sizeof(double))) == NULL)
            goto probB_error;
        memcpy(model->probB, probB, m * sizeof(double));
    } else {
        model->probA = NULL;
        model->probB = NULL;
    }

    /* We'll free SV ourselves */
    model->free_sv = 0;
    return model;

probB_error:
    free(model->probA);
probA_error:
    for (i=0; i < model->nr_class-1; i++)
        free(model->sv_coef[i]);
sv_coef_i_error:
    free(model->rho);
rho_error:
    free(model->sv_coef);
sv_coef_error:
    free(model->label);
label_error:
    free(model->nSV);
nsv_error:
    free(model);
model_error:
    return NULL;
}
struct svm_csr_model *csr_set_model(struct svm_parameter *param, int nr_class,
                            char *SV_data, npy_intp *SV_indices_dims,
                            char *SV_indices, npy_intp *SV_indptr_dims,
                            char *SV_intptr,
                            char *sv_coef, char *rho, char *nSV, char *label,
                            char *probA, char *probB)
{
    struct svm_csr_model *model;
    double *dsv_coef = (double *) sv_coef;
    int i, m;

    m = nr_class * (nr_class-1)/2;

    model = (struct svm_csr_model *)  malloc(sizeof(struct svm_csr_model));
    model->nSV =     (int *)      malloc(nr_class * sizeof(int));
    model->label =   (int *)      malloc(nr_class * sizeof(int));;
    model->sv_coef = (double **)  malloc((nr_class-1)*sizeof(double *));
    model->rho =     (double *)   malloc( m * sizeof(double));

    /* in the case of precomputed kernels we do not use
       dense_to_precomputed because we don't want the leading 0. As
       indices start at 1 (not at 0) this will work */
    model->SV = csr_to_libsvm((double *) SV_data, SV_indices_dims,
    		(int *) SV_indices, SV_indptr_dims, (int *) SV_intptr);
    model->nr_class = nr_class;
    model->param = *param;
    model->l = (int) SV_indptr_dims[0] - 1;

    /*
     * regression and one-class does not use nSV, label.
     * TODO: does this provoke memory leaks (we just malloc'ed them)?
     */
    if (param->svm_type < 2) {
        memcpy(model->nSV,   nSV,   model->nr_class * sizeof(int));
        memcpy(model->label, label, model->nr_class * sizeof(int));
    }

    for (i=0; i < model->nr_class-1; i++) {
        /*
         * We cannot squash all this mallocs in a single call since
         * svm_destroy_model will free each element of the array.
         */
        model->sv_coef[i] = (double *) malloc((model->l) * sizeof(double));
        memcpy(model->sv_coef[i], dsv_coef, (model->l) * sizeof(double));
        dsv_coef += model->l;
    }

    for (i=0; i<m; ++i) {
        (model->rho)[i] = -((double *) rho)[i];
    }

    /*
     * just to avoid segfaults, these features are not wrapped but
     * svm_destroy_model will try to free them.
     */

    if (param->probability) {
        model->probA = (double *) malloc(m * sizeof(double));
        memcpy(model->probA, probA, m * sizeof(double));
        model->probB = (double *) malloc(m * sizeof(double));
        memcpy(model->probB, probB, m * sizeof(double));
    } else {
        model->probA = NULL;
        model->probB = NULL;
    }

    /* We'll free SV ourselves */
    model->free_sv = 0;
    return model;
}