struct sml_variables_list *
sml_fuzzy_variables_list_new(struct sml_fuzzy *fuzzy, struct sol_vector *indexes)
{
    fl::Engine *engine = (fl::Engine*)fuzzy->engine;
    uint16_t i, *idx;
    void *v;
    std::vector<fl::OutputVariable*> *changed =
        new (std::nothrow) std::vector<fl::OutputVariable*>();

    if (!changed) {
        sml_critical("Could not alloc the variables list!");
        return NULL;
    }

    if (indexes) {
        try {
            SOL_VECTOR_FOREACH_IDX(indexes, v, i) {
                idx = (uint16_t *)v;
                changed->push_back(engine->getOutputVariable(*idx));
            }
        } catch (const std::out_of_range &oor) {
            sml_critical("Array out of bounds. error:%s", oor.what());
            goto err_exit;
        }
    }
    return (struct sml_variables_list *)changed;

err_exit:
    delete changed;
    return NULL;
}
static int
_sml_fuzzy_fill_membership_values(struct sml_matrix *variables,
                                  struct sml_variables_list *list)
{
    uint16_t i, j, len;
    float f, *ptr;

    len = sml_fuzzy_variables_list_get_length(list);
    try {
        for (i = 0; i < len; i++) {
            struct sml_variable *variable = sml_fuzzy_variables_list_index(list, i);
            uint16_t terms_len = sml_fuzzy_variable_terms_count(variable);
            for (j = 0; j < terms_len; j++) {
                fl::Term *term = (fl::Term *)
                    sml_fuzzy_variable_get_term(variable, j);
                f = term->membership(sml_fuzzy_variable_get_value(variable));
                ptr = (float*) sml_matrix_insert(variables, i, j);
                if (!ptr) {
                    sml_critical("Could not fill membership to variable");
                    return -ENOMEM;
                }
                *ptr = f;
            }
        }
    } catch (fl::Exception e) {
        sml_critical("%s", e.getWhat().c_str());
        return SML_INTERNAL_ERROR;
    }
    return 0;
}
bool
sml_fuzzy_load_file(struct sml_fuzzy *fuzzy, const char *filename)
{
    fl::Engine *engine;
    uint16_t i, len;
    void *width;

    try {
        fl::FllImporter importer;
        engine = importer.fromFile(filename);

    } catch (fl::Exception e) {
        sml_critical("%s", e.getWhat().c_str());
        return false;
    }

    if (engine->numberOfInputVariables() == 0 ||
        engine->numberOfOutputVariables() == 0) {
        sml_critical("Input and output variables must be provided!");
        goto error;
    }

    if (engine->numberOfRuleBlocks() == 0) {
      sml_critical("Rule blocks must be provided!");
      goto error;
    }

    if (fuzzy->engine)
        delete (fl::Engine*) fuzzy->engine;

    fuzzy->engine = engine;
    fuzzy->input_list = (struct sml_variables_list*)&(engine->inputVariables());
    fuzzy->output_list = (struct sml_variables_list*)&(engine->outputVariables());
    fuzzy->input_terms_count = _calc_terms_count(engine->inputVariables());
    fuzzy->output_terms_count = _calc_terms_count(engine->outputVariables());
    _remove_rule_blocks(engine);

    sol_vector_clear(&fuzzy->input_terms_width);
    sol_vector_clear(&fuzzy->output_terms_width);

    len = engine->inputVariables().size();
    for (i = 0; i < len; i++) {
        width = sol_vector_append(&fuzzy->input_terms_width);
        ON_NULL_RETURN_VAL(width, false);
    }

    len = engine->outputVariables().size();
    for (i = 0; i < len; i++) {
        width = sol_vector_append(&fuzzy->output_terms_width);
        ON_NULL_RETURN_VAL(width, false);
    }
    return true;

error:
    delete engine;
    return false;
}
bool
sml_fuzzy_bridge_output_set_defuzzifier(struct sml_variable *variable,
                                        enum sml_fuzzy_defuzzifier defuzzifier,
                                        int defuzzifier_resolution)
{
    fl::Variable *fl_var = (fl::Variable*) variable;
    fl::Defuzzifier *fl_defuzzifier = NULL;
    fl::OutputVariable *output_var = dynamic_cast<fl::OutputVariable*>(fl_var);
    if (!output_var) {
        sml_critical("Not a output variable!");
        return false;
    }

    switch (defuzzifier) {
    case SML_FUZZY_DEFUZZIFIER_BISECTOR:
        fl_defuzzifier =
            new (std::nothrow) fl::Bisector(defuzzifier_resolution);
        break;
    case SML_FUZZY_DEFUZZIFIER_CENTROID:
        fl_defuzzifier =
            new (std::nothrow) fl::Centroid(defuzzifier_resolution);
        break;
    case SML_FUZZY_DEFUZZIFIER_LARGEST_OF_MAXIMUM:
        fl_defuzzifier =
            new (std::nothrow) fl::LargestOfMaximum(defuzzifier_resolution);
        break;
    case SML_FUZZY_DEFUZZIFIER_MEAN_OF_MAXIMUM:
        fl_defuzzifier =
            new (std::nothrow) fl::MeanOfMaximum(defuzzifier_resolution);
        break;
    case SML_FUZZY_DEFUZZIFIER_SMALLEST_OF_MAXIMUM:
        fl_defuzzifier =
            new (std::nothrow) fl::SmallestOfMaximum(defuzzifier_resolution);
        break;
    case SML_FUZZY_DEFUZZIFIER_WEIGHTED_AVERAGE:
        fl_defuzzifier = new (std::nothrow) fl::WeightedAverage();
        break;
    case SML_FUZZY_DEFUZZIFIER_WEIGHTED_SUM:
        fl_defuzzifier = new (std::nothrow) fl::WeightedSum();
        break;
    default:
        sml_critical("Unknown defuzzifier %d", defuzzifier);
    }

    if (!fl_defuzzifier) {
        sml_critical("Failed to create defuzzifier");
        return false;
    }
    output_var->setDefuzzifier(fl_defuzzifier);
    return true;
}
static fl::SNorm *
_get_snorm(enum sml_fuzzy_snorm norm)
{
    fl::SNorm *fl_norm;

    switch (norm) {
    case SML_FUZZY_SNORM_ALGEBRAIC_SUM:
        sml_debug("SNorm is algebraic sum");
        fl_norm = new (std::nothrow) fl::AlgebraicSum();
        break;
    case SML_FUZZY_SNORM_BOUNDED_SUM:
        sml_debug("SNorm is bounded sum");
        fl_norm = new (std::nothrow) fl::BoundedSum();
        break;
    case SML_FUZZY_SNORM_DRASTIC_SUM:
        sml_debug("SNorm is drastic sum");
        fl_norm = new (std::nothrow) fl::DrasticSum();
        break;
    case SML_FUZZY_SNORM_EINSTEIN_SUM:
        sml_debug("SNorm is einstein sum");
        fl_norm = new (std::nothrow) fl::EinsteinSum();
        break;
    case SML_FUZZY_SNORM_HAMACHER_SUM:
        sml_debug("SNorm is hamacher sum");
        fl_norm = new (std::nothrow) fl::HamacherSum();
        break;
    case SML_FUZZY_SNORM_MAXIMUM:
        sml_debug("SNorm is maximum");
        fl_norm = new (std::nothrow) fl::Maximum();
        break;
    case SML_FUZZY_SNORM_NILPOTENT_MAXIMUM:
        sml_debug("SNorm is nilpotent maximum");
        fl_norm = new (std::nothrow) fl::NilpotentMaximum();
        break;
    case SML_FUZZY_SNORM_NORMALIZED_SUM:
        sml_debug("SNorm is normalized sum");
        fl_norm = new (std::nothrow) fl::NormalizedSum();
        break;
    default:
        sml_critical("Unknown SNorm %d", norm);
        return NULL;
    }

    if (!fl_norm)
        sml_critical("Could not alloc the snorm");

    return fl_norm;
}
API_EXPORT bool
sml_fuzzy_set_variable_terms_auto_balance(struct sml_object *sml,
    bool variable_terms_auto_balance)
{
    sml_critical("Fuzzy engine not supported.");
    return false;
}
bool
sml_fuzzy_remove_variable(struct sml_fuzzy *fuzzy, struct sml_variable *variable)
{
    fl::Variable *fl_var = (fl::Variable*) variable;
    fl::Variable *removed_var = NULL;
    fl::Engine *engine = (fl::Engine*)fuzzy->engine;
    uint16_t index;
    bool ret = true;

    if (_find_variable((std::vector<fl::Variable*> *) fuzzy->input_list,
                fl_var, &index)) {
        removed_var = engine->removeInputVariable(index);
        fuzzy->input_terms_count -= removed_var->numberOfTerms();
        if (sol_vector_del(&fuzzy->input_terms_width, index))
            ret = false;
    } else if (_find_variable((std::vector<fl::Variable*> *)
               fuzzy->output_list, fl_var, &index)) {
        removed_var = engine->removeOutputVariable(index);
        fuzzy->output_terms_count -= removed_var->numberOfTerms();
        if (sol_vector_del(&fuzzy->output_terms_width, index))
            ret = false;
    } else {
        sml_critical("Variable is not a valid input or output");
        return false;
    }

    delete removed_var;
    return ret;
}
struct sml_fuzzy_term*
sml_fuzzy_bridge_variable_add_term_cosine(struct sml_fuzzy *fuzzy,
                                          struct sml_variable *variable,
                                          const char *name, float center,
                                          float width)
{
    fl::Variable *fl_var = (fl::Variable*) variable;
    fl::Term *term;

    if (!_check_name(name))
        return NULL;

    term = new (std::nothrow) fl::Cosine(name, center, width, 1.0);
    if (!term) {
        sml_critical("Failed to create term");
        return NULL;
    }

    if (sml_fuzzy_is_input(fuzzy, variable, NULL))
        fuzzy->input_terms_count++;
    else if (sml_fuzzy_is_output(fuzzy, variable, NULL))
        fuzzy->output_terms_count++;
    else {
        delete term;
        return NULL;
    }

    fl_var->addTerm(term);
    return (struct sml_fuzzy_term *) term;
}
static bool
_sml_ann_bridge_calculate_confidence_interval(struct sml_ann_bridge *iann,
    struct sml_variables_list *inputs,
    unsigned int observations)
{
    unsigned int i, j, inputs_len;
    float mean, sd, value;
    struct sml_variable *var;
    Confidence_Interval *ci;
    char var_name[SML_VARIABLE_NAME_MAX_LEN + 1];

    sml_debug("Calculating confidence interval");
    inputs_len = sml_ann_variables_list_get_length(inputs);
    for (i = 0; i < inputs_len; i++) {
        mean = sd = 0.0;
        var = sml_ann_variables_list_index(inputs, i);
        ci = sol_vector_append(&iann->confidence_intervals);
        if (!ci) {
            sml_critical("Could not alloc the Confidence_Interval");
            goto err_exit;
        }

        for (j = 0; j < observations; j++) {
            value = sml_ann_variable_get_value_by_index(var, j);
            if (isnan(value))
                sml_ann_variable_get_range(var, &value, NULL);
            mean += value;
        }
        mean /= observations;

        //Now the standard deviation
        for (j = 0; j < observations; j++) {
            value = sml_ann_variable_get_value_by_index(var, j);
            if (isnan(value))
                sml_ann_variable_get_range(var, &value, NULL);
            sd += pow(value - mean, 2.0);
        }
        sd /= observations;
        sd = sqrt(sd);

        //Confidence interval of 95%
        ci->lower_limit = mean - (1.96 * (sd / sqrt(observations)));
        ci->upper_limit = mean + (1.96 * (sd / sqrt(observations)));
        iann->ci_length_sum += (ci->upper_limit - ci->lower_limit);

        if (sml_ann_variable_get_name(var, var_name, sizeof(var_name))) {
            sml_warning("Failed to get variable name for %p", var);
            continue;
        }

        sml_debug("Variable:%s mean:%f sd:%f lower:%f upper:%f",
            var_name, mean, sd, ci->lower_limit,
            ci->upper_limit);
    }

    return true;
err_exit:
    sol_vector_clear(&iann->confidence_intervals);
    return false;
}
API_EXPORT bool
sml_fuzzy_set_rule_weight_threshold(struct sml_object *sml, float
    weight_threshold)
{
    sml_critical("Fuzzy engine not supported.");
    return false;
}
bool
sml_matrix_equals(struct sml_matrix *m1, struct sml_matrix *m2, struct
    sol_vector *changed,
    sml_cmp_cb cmp_cb)
{
    uint16_t i, j, len_i, len_j, *idx;
    struct sol_vector *vec1, *vec2;
    bool r = false;

    len_i = max(m1->data.len, m2->data.len);
    for (i = 0; i < len_i; i++) {
        vec1 = sol_vector_get(&m1->data, i);
        vec2 = sol_vector_get(&m2->data, i);
        len_j = max((vec1 ? vec1->len : 0), (vec2 ? vec2->len : 0));
        for (j = 0; j < len_j; j++) {
            if (!cmp_cb(sol_vector_get(vec1, j),
                sol_vector_get(vec2, j))) {
                if (changed) {
                    idx = sol_vector_append(changed);
                    if (!idx) {
                        sml_critical("Could not append the index:%d to the" \
                            " changed vector", i);
                        return false;
                    }
                    *idx = i;
                }
                r = true;
                break;
            }
        }
    }

    return r;
}
struct sml_fuzzy_term*
sml_fuzzy_bridge_variable_add_term_gaussian(struct sml_fuzzy *fuzzy,
                                            struct sml_variable *variable,
                                            const char *name, float mean,
                                            float standard_deviation)
{
    fl::Variable *fl_var = (fl::Variable*) variable;
    fl::Term *term;

    if (!_check_name(name))
        return NULL;

    term = new (std::nothrow) fl::Gaussian(name, mean, standard_deviation,
                                           1.0);
    if (!term) {
        sml_critical("Failed to create term");
        return NULL;
    }

    if (sml_fuzzy_is_input(fuzzy, variable, NULL))
        fuzzy->input_terms_count++;
    else if (sml_fuzzy_is_output(fuzzy, variable, NULL))
        fuzzy->output_terms_count++;
    else {
        delete term;
        return NULL;
    }

    fl_var->addTerm(term);
    return (struct sml_fuzzy_term *) term;
}
API_EXPORT bool
sml_fuzzy_variable_remove_term(struct sml_object *sml, struct sml_variable *var,
    struct sml_fuzzy_term *term)
{
    sml_critical("Fuzzy engine not supported.");
    return false;
}
API_EXPORT bool
sml_fuzzy_output_set_default_value(struct sml_object *sml, struct
    sml_variable *var,
    float default_value)
{
    sml_critical("Fuzzy engine not supported.");
    return false;
}
API_EXPORT bool
sml_fuzzy_output_set_accumulation(struct sml_object *sml, struct
    sml_variable *var,
    enum sml_fuzzy_snorm accumulation)
{
    sml_critical("Fuzzy engine not supported.");
    return false;
}
API_EXPORT bool
sml_fuzzy_output_set_defuzzifier(struct sml_object *sml, struct
    sml_variable *var,
    enum sml_fuzzy_defuzzifier defuzzifier,
    int defuzzifier_resolution)
{
    sml_critical("Fuzzy engine not supported.");
    return false;
}
API_EXPORT struct sml_fuzzy_term *
sml_fuzzy_variable_add_term_gaussian(struct sml_object *sml, struct
    sml_variable *variable,
    const char *name, float mean,
    float standard_deviation, float height)
{
    sml_critical("Fuzzy engine not supported.");
    return NULL;
}
API_EXPORT struct sml_fuzzy_term *
sml_fuzzy_variable_add_term_cosine(struct sml_object *sml, struct
    sml_variable *variable,
    const char *name, float center, float width,
    float height)
{
    sml_critical("Fuzzy engine not supported.");
    return NULL;
}
API_EXPORT struct sml_fuzzy_term *
sml_fuzzy_variable_add_term_rectangle(struct sml_object *sml, struct
    sml_variable *variable,
    const char *name, float start, float end,
    float height)
{
    sml_critical("Fuzzy engine not supported.");
    return NULL;
}
int
main(int argc, char **argv)
{
    Air_Conditioner_Controller acc;
    struct sml_variable *temp, *presence, *power;
    struct sml_object *sml;

    _initialize_acc(&acc);

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <engine_type (0 fuzzy, 1 ann)>\n", argv[0]);
        fprintf(stderr, "Fuzzy Test: %s 0\n", argv[0]);
        fprintf(stderr, "Ann Test: %s 1\n", argv[0]);
        return -1;
    }

    sml = _sml_new(atoi(argv[1]));
    if (!sml) {
        sml_critical("Failed to create sml");
        return -1;
    }

    sml_main_loop_init();

    //Set stabilization hits and initial required obs to a low value because
    //this is a simulation and we don't want to wait for a long time before
    //getting interesting results
    sml_set_stabilization_hits(sml, STABILIZATION_HITS);
    sml_ann_set_initial_required_observations(sml, INITIAL_REQUIRED_OBS);

    //Create temperature input
    temp = sml_new_input(sml, "Temperature");
    sml_variable_set_range(sml, temp, 0, 48);
    sml_fuzzy_variable_set_default_term_width(sml, temp, 16);

    //Create presence input
    presence = sml_new_input(sml, "Presence");
    sml_variable_set_range(sml, presence, 0, 1);
    sml_fuzzy_variable_set_default_term_width(sml, presence, 0.5);

    //Create power output
    power = sml_new_output(sml, "Power");
    sml_variable_set_range(sml, power, 0, 3);
    sml_fuzzy_variable_set_default_term_width(sml, power, 1);
    sml_fuzzy_variable_set_is_id(sml, power, true);

    //set SML callbacks
    sml_set_read_state_callback(sml, _read_state_cb, &acc);
    sml_main_loop_schedule_sml_process(sml, READ_TIMEOUT);
    sml_set_output_state_changed_callback(sml, _output_state_changed_cb, &acc);

    sml_main_loop_run();

    sml_free(sml);
    sml_main_loop_shutdown();
    return 0;
}
static fl::TNorm *
_get_tnorm(enum sml_fuzzy_tnorm norm) {
    fl::TNorm *fl_norm;

    switch (norm) {
    case SML_FUZZY_TNORM_ALGEBRAIC_PRODUCT:
        sml_debug("Conjunction set to algebraic product");
        fl_norm =  new (std::nothrow) fl::AlgebraicProduct();
        break;
    case SML_FUZZY_TNORM_BOUNDED_DIFFERENCE:
        sml_debug("Conjunction set to bounded difference");
        fl_norm =  new (std::nothrow) fl::BoundedDifference();
        break;
    case SML_FUZZY_TNORM_DRASTIC_PRODUCT:
        sml_debug("Conjunction set to drastic product");
        fl_norm =  new (std::nothrow) fl::DrasticProduct();
        break;
    case SML_FUZZY_TNORM_EINSTEIN_PRODUCT:
        sml_debug("Conjunction set to einstein product");
        fl_norm =  new (std::nothrow) fl::EinsteinProduct();
        break;
    case SML_FUZZY_TNORM_HAMACHER_PRODUCT:
        sml_debug("Conjunction set to hamacher product");
        fl_norm =  new (std::nothrow) fl::HamacherProduct();
        break;
    case SML_FUZZY_TNORM_MINIMUM:
        sml_debug("Conjunction set to minimum");
        fl_norm =  new (std::nothrow) fl::Minimum();
        break;
    case SML_FUZZY_TNORM_NILPOTENT_MINIMUM:
        sml_debug("Conjunction set to nilpotent minimum");
        fl_norm = new (std::nothrow) fl::NilpotentMinimum();
        break;
    default:
        sml_critical("Unknown TNorm %d", norm);
        return NULL;
    }

    if (!fl_norm)
        sml_critical("Could not alloc the tnorm");

    return fl_norm;
}
int
sml_fuzzy_process_output(struct sml_fuzzy *fuzzy)
{
    fl::Engine *engine = (fl::Engine*)fuzzy->engine;

    try {
        engine->process();
    } catch (fl::Exception e) {
        sml_critical("%s", e.getWhat().c_str());
        return SML_INTERNAL_ERROR;
    }

    return 0;
}
static struct sml_ann_bridge *
_sml_ann_bridge_new(struct fann *ann, bool trained)
{
    struct sml_ann_bridge *iann = calloc(1, sizeof(struct sml_ann_bridge));

    if (!iann) {
        sml_critical("Could not create an struct sml_ann_bridge");
        return NULL;
    }
    sol_vector_init(&iann->confidence_intervals, sizeof(Confidence_Interval));
    iann->ann = ann;
    iann->trained = trained;
    iann->last_train_error = NAN;
    return iann;
}
bool
sml_fuzzy_bridge_output_set_accumulation(struct sml_variable *variable,
                                         enum sml_fuzzy_snorm accumulation)
{
    fl::Variable *fl_var = (fl::Variable*) variable;
    fl::OutputVariable *output_var = dynamic_cast<fl::OutputVariable*>(fl_var);
    if (!output_var) {
        sml_critical("Not a output variable!");
        return false;
    }

    output_var->fuzzyOutput()->setAccumulation(_get_snorm(accumulation));

    return true;
}
struct sml_fuzzy *
sml_fuzzy_bridge_new(void)
{
    struct sml_fuzzy *fuzzy = (struct sml_fuzzy *) calloc(1, sizeof(struct sml_fuzzy));
    fl::Engine *engine = NULL;

    if (!fuzzy) {
        sml_critical("Failed to create fuzzy");
        return NULL;
    }

    engine = new (std::nothrow) fl::Engine();
    if (!engine)
        goto new_error;

    if (!_create_rule_block(engine)) {
        sml_critical("Could not alloc the rule block");
        goto new_error;
    }

    engine->setName("EngineDefault");
    fuzzy->engine = engine;
    fuzzy->input_list = (struct sml_variables_list*)
        &(engine->inputVariables());
    fuzzy->output_list = (struct sml_variables_list*)
        &(engine->outputVariables());
    sol_vector_init(&fuzzy->input_terms_width, sizeof(struct terms_width));
    sol_vector_init(&fuzzy->output_terms_width, sizeof(struct terms_width));

    return fuzzy;

new_error:
    free(fuzzy);
    delete engine;
    return NULL;
}
bool
sml_fuzzy_bridge_conjunction_set(struct sml_fuzzy *fuzzy, enum sml_fuzzy_tnorm norm)
{
    fl::TNorm *fl_norm = _get_tnorm(norm);
    fl::Engine *engine = (fl::Engine*)fuzzy->engine;
    if (!fl_norm)
        return false;

    if (!_create_rule_block(engine)) {
        sml_critical("Could not alloc the rule block");
        return false;
    }

    engine->getRuleBlock(0)->setConjunction(fl_norm);
    return true;
}
struct sml_fuzzy_rule *
sml_fuzzy_rule_add(struct sml_fuzzy *fuzzy, const char *rule)
{
    fl::Engine *engine = (fl::Engine*)fuzzy->engine;
    fl::RuleBlock *block = engine->getRuleBlock(0);
    fl::Rule *rule_obj;

    try {
        rule_obj = fl::Rule::parse(rule, engine);
        block->addRule(rule_obj);
    } catch (fl::Exception e) {
        sml_critical("%s", e.getWhat().c_str());
        return NULL;
    }

    return (struct sml_fuzzy_rule *) rule_obj;
}
API_EXPORT struct sml_object *
sml_fuzzy_new(void)
{
    sml_critical("Fuzzy engine not supported.");
    return NULL;
}
API_EXPORT bool
sml_is_fuzzy(struct sml_object *sml)
{
    sml_critical("Fuzzy engine not supported.");
    return false;
}
API_EXPORT bool
sml_fuzzy_disjunction_set(struct sml_object *sml, enum sml_fuzzy_snorm norm)
{
    sml_critical("Fuzzy engine not supported.");
    return false;
}