/*
 * Fill an svm_problem struct. problem->x will be malloc'd.
 */
void set_problem(struct svm_problem *problem, char *X, char *Y, char *sample_weight, npy_intp *dims, int kernel_type)
{
    if (problem == NULL) return;
    problem->l = (int) dims[0]; /* number of samples */
    problem->y = (double *) Y;
    problem->x = dense_to_libsvm((double *) X, dims); /* implicit call to malloc */
    problem->W = (double *) sample_weight;
}
/*
 * Create and return a svm_problem struct. It is up to the user to free resulting
 * structure.
 */
struct svm_problem * set_problem(char *X, char *Y, npy_intp *dims, int kernel_type)
{
    struct svm_problem *problem;
    problem = (struct svm_problem *) malloc(sizeof(struct svm_problem));
    if (problem == NULL) return NULL;
    problem->l = (int) dims[0]; /* number of samples */
    problem->y = (double *) Y;
    problem->x = dense_to_libsvm((double *) X, dims);
    if (problem->x == NULL) {
        free(problem);
        return NULL;
    }
    return problem;
}
int copy_predict_values(char *predict, struct svm_model *model,
                        npy_intp *predict_dims, char *dec_values, int nr_class)
{
    npy_intp i;
    struct svm_node *predict_nodes;
    predict_nodes = dense_to_libsvm((double *) predict, predict_dims);
    if (predict_nodes == NULL)
        return -1;
    for(i=0; i<predict_dims[0]; ++i) {
        svm_predict_values(model, &predict_nodes[i],
                                ((double *) dec_values) + i*nr_class);
    }

    free(predict_nodes);
    return 0;
}
int copy_predict_proba(char *predict, struct svm_model *model, npy_intp *predict_dims,
                 char *dec_values)
{
    npy_intp i, n, m;
    struct svm_node *predict_nodes;
    n = predict_dims[0];
    m = (npy_intp) model->nr_class;
    predict_nodes = dense_to_libsvm((double *) predict, predict_dims);
    if (predict_nodes == NULL)
        return -1;
    for(i=0; i<n; ++i) {
        svm_predict_probability(model, &predict_nodes[i],
                                ((double *) dec_values) + i*m);
    }
    free(predict_nodes);
    return 0;
}
/*
 * Predict using model.
 *
 *  It will return -1 if we run out of memory.
 */
int copy_predict(char *predict, struct svm_model *model, npy_intp *predict_dims,
                 char *dec_values)
{
    double *t = (double *) dec_values;
    struct svm_node *predict_nodes;
    npy_intp i;

    predict_nodes = dense_to_libsvm((double *) predict, predict_dims);

    if (predict_nodes == NULL)
        return -1;
    for(i=0; i<predict_dims[0]; ++i) {
        *t = svm_predict(model, &predict_nodes[i]);
        ++t;
    }
    free(predict_nodes);
    return 0;
}
/*
 * Create and return an instance of svm_model.
 *
 * The copy of model->sv_coef should be straightforward, but
 * unfortunately to represent a matrix numpy and libsvm use different
 * approaches, so it requires some iteration.
 *
 * Possible issue: on 64 bits, the number of columns that numpy can
 * store is a long, but libsvm enforces this number (model->l) to be
 * an int, so we might have numpy matrices that do not fit into libsvm's
 * data structure.
 *
 */
struct svm_model *set_model(struct svm_parameter *param, int nr_class,
                            char *SV, npy_intp *SV_dims,
                            char *support, npy_intp *support_dims,
                            npy_intp *sv_coef_strides,
                            char *sv_coef, char *rho, char *nSV, char *label,
                            char *probA, char *probB)
{
    struct svm_model *model;
    double *dsv_coef = (double *) sv_coef;
    int i, m;

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

    if ((model = malloc(sizeof(struct svm_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;

    model->nr_class = nr_class;
    model->param = *param;
    model->l = (int) support_dims[0];

    if (param->kernel_type == PRECOMPUTED) {
        if ((model->SV = malloc ((model->l) * sizeof(struct svm_node))) == NULL)
            goto SV_error;
        for (i=0; i<model->l; ++i) {
            model->SV[i].ind = ((int *) support)[i];
            model->SV[i].values = NULL;
        }
    } else {
        model->SV = dense_to_libsvm((double *) SV, SV_dims);
    }
    /*
     * 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++) {
        model->sv_coef[i] = dsv_coef + i*(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:
    free(model->SV);
SV_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;
}