#include "function.h"

// -----------------------------------------------------------------------
list<Bdd_elem> Function::get_bdd_list(void)
{
    return bdd_list;
}

// -----------------------------------------------------------------------
list<PLA_elem *> Function::get_pla_list(void)
{
    return pla_list;
}

// -----------------------------------------------------------------------
void Function::print_array(char *arr, int size)
{
    for(int i = 0;i < size;i++)
        cout << arr[i];

    cout << endl;
}

// -----------------------------------------------------------------------
void Function::print_pla_list(void){
    for(list<PLA_elem *>::iterator it = pla_list.begin(); it != pla_list.end(); it++){
        for(int i = 0;i < manager->size;i++){
            cout << (*it)->pla_inputs[i];
        }
        cout << " ";
        for(int i = 0;i < outputs_num;i++){
            cout << (*it)->pla_outputs[i];
        }
        cout << endl;
    }
}

// -----------------------------------------------------------------------
void Function::print_input_list(vector<Input_elem> &var_list)
{
    for(vector<Input_elem>::iterator it = var_list.begin(); it != var_list.end(); it++){
        cout << "Jmeno promenne: " << it->name << endl;
        cout << "Typ: " << it->type << endl;

        for(list<MStr>::iterator j = it->in_vars.begin(); j != it->in_vars.end(); j++)
            cout << "Vstupni promenna hradla: " << *j << endl;

        cout << "--------------------------------------" << endl;
    }
}

// -----------------------------------------------------------------------
void Function::print_inputs(vector<Input_elem> &var_list)
{
    for(vector<Input_elem>::iterator it = var_list.begin(); it != var_list.end(); it++){
        if (it->type == T_INPUT)
            cout << it->name << " ";
    }
    cout << endl;
}

// -----------------------------------------------------------------------
void Function::print_bdd(list<Bdd_elem> &bdd)
{
    for(list<Bdd_elem>::iterator i = bdd.begin(); i != bdd.end(); i++){
        cout << "BDD pro promennou/hradlo " << i->gate << endl;
        //cuddP(manager, i->node);
        Cudd_PrintMinterm(manager, i->node);
        cout << "------------------------ " << endl;
    }
}

// -----------------------------------------------------------------------
DdNode * Function::find_node(MStr s, list<Bdd_elem>& pole)
{
    for(list<Bdd_elem>::iterator i = pole.begin(); i != pole.end(); i++){
        //str << "ahoj";
        if (i->gate == s){
            //cout << "v poli se jiz vyskytuje prvek: " << *i << endl;
            return i->node;
        }
    }
    return NULL;
}

// -----------------------------------------------------------------------
bool Function::is_output(MStr str, vector<Input_elem> &var_list)
{
    for(vector<Input_elem>::iterator i = var_list.begin(); i != var_list.end(); i++)
        if (str == i->name && i->type == T_OUTPUT)
            return true;

    return false;
}

// -----------------------------------------------------------------------
DdNode * Function::create_basic_bdd(void)
{
    DdNode *ff, *tmp, *var;

    ff = Cudd_ReadOne(manager);
    Cudd_Ref(ff);
    var = Cudd_bddNewVar(manager);
    tmp = Cudd_bddAnd(manager,var,ff);
    Cudd_Ref(tmp);
    Cudd_RecursiveDeref(manager,ff);
    //f = tmp;

    return tmp;
}

// -----------------------------------------------------------------------
DdNode * Function::create_bdd_recursive(MStr name, vector<Input_elem> &var_list)
{
    Bdd_elem b;
    list<DdNode *> n;
    DdNode *node;

    for(vector<Input_elem>::iterator i = var_list.begin(); i != var_list.end(); i++){
        if (name == i->name){
            if (i->in_vars.empty()){
                //cout << "vytvarim novy uzel" << endl;
                b.node = create_basic_bdd();
                b.gate = i->name;
                bdd_list.push_back(b);
                //temp.push_back(b.node);
                return b.node;
            }
            else {
                for(list<MStr>::iterator j = i->in_vars.begin(); j != i->in_vars.end(); j++){
                    if ((node = find_node(*j, bdd_list)) != NULL){
                        //cout << "uzel " << *j << " uz existuje" << endl;
                        n.push_back(node);
                    }
                    else {
                        //cout << "uzel " << *j << " neexistuje, musime ho vytvorit" << endl;
                        node = create_bdd_recursive(*j, var_list);
                        n.push_back(node);
                        //b.node = combine_bdds(manager, temp, type);
                    }
                    //return(combine_bdds(manager, n, i->type);
                }

                return(combine_bdds(n, i->type));
            }
        }
    }
    return NULL;
}

// -----------------------------------------------------------------------
void Function::create_bdd_list(vector<Input_elem> &var_list)
{
    int type;
    DdNode *node;
    //MStr gate;
    Bdd_elem b;

    for(vector<Input_elem>::iterator i = var_list.begin(); i != var_list.end(); i++){
        if ((node = find_node(i->name, bdd_list)) != NULL)
            continue;
        else {
            type = i->type;

            if (type == T_INPUT){
                b.node = create_basic_bdd();
                b.gate = i->name;
                b.type = T_INPUT;
                bdd_list.push_back(b);
//                 cout << "vstup: " << i->name << endl;
//                 in.push_back(i->name);

                continue;
            }

            if (type == T_OUTPUT){
//                 cout << "vystup: " << i->name << endl;
//                 out.push_back(i->name);
                continue;
            }

            for(list<MStr>::iterator j = i->in_vars.begin(); j != i->in_vars.end(); j++){
//                 cout << "hledam hradlo: " << *j << endl;
                if ((node = find_node(*j, bdd_list)) != NULL){
//                     cout << "hradlo " << *j << " nalezeno, vkladam do seznamu" << endl;
                    temp.push_back(node);
                }
                else {
//                     cout << "hradlo " << *j << " nenalezeno, budeme tvorit" << endl;
                    node = create_bdd_recursive(*j, var_list);
                    temp.push_back(node);
                }
            }
            b.node = combine_bdds(temp, type);
            b.gate = i->name;
            if (is_output(i->name, var_list)){
//                 cout << "hradlo " << i->name << " je vystup" << endl;
                b.type = T_OUTPUT;
            }
            else
                b.type = type;

            bdd_list.push_back(b);
        }
        temp.clear();
    }
}

// -----------------------------------------------------------------------
DdNode * Function::combine_bdds(list<DdNode*> &_bdd, int eval_param)
{
    DdNode *tmp, *g, *tmp1;
    list<DdNode*>::iterator i = _bdd.begin();

    if (eval_param == T_NAND){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = Cudd_Not(*i);
        Cudd_Ref(tmp);
        i++;
        if (i != _bdd.end())
            g = Cudd_Not(*i);

        while(i != _bdd.end()){
            tmp1 = Cudd_bddOr(manager, tmp, g);
//             Cudd_Ref(tmp1);
//             Cudd_RecursiveDeref(manager, tmp);
            i++;
            g = Cudd_Not(*i);
            Cudd_Ref(tmp1);
            Cudd_IterDerefBdd(manager, tmp);
            tmp = tmp1;
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_AND){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        Cudd_Ref(tmp);
        i++;
        g = *i;

        while(i != _bdd.end()){
            tmp1 = Cudd_bddAnd(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp1);
            Cudd_IterDerefBdd(manager, tmp);
            tmp = tmp1;
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_NOR){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = Cudd_Not(*i);
        Cudd_Ref(tmp);
        i++;
        if (i != _bdd.end())
            g = Cudd_Not(*i);

        while(i != _bdd.end()){
            tmp1 = Cudd_bddAnd(manager, tmp, g);
            i++;
            g = Cudd_Not(*i);
            Cudd_Ref(tmp1);
            Cudd_IterDerefBdd(manager, tmp);
            tmp = tmp1;
        }
//        Cudd_Ref(tmp);
        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_OR){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        Cudd_Ref(tmp);
        i++;
        g = *i;

        while(i != _bdd.end()){
            tmp1 = Cudd_bddOr(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp1);
            Cudd_IterDerefBdd(manager, tmp);
            tmp = tmp1;
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_XNOR){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        Cudd_Ref(tmp);
        i++;
        g = *i;

        while(i != _bdd.end()){
            tmp1 = Cudd_bddXnor(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp1);
            Cudd_IterDerefBdd(manager, tmp);
            tmp = tmp1;
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_XOR){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        Cudd_Ref(tmp);
        i++;
        g = *i;

        while(i != _bdd.end()){
            tmp1 = Cudd_bddXor(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp1);
            Cudd_IterDerefBdd(manager, tmp);
            tmp = tmp1;
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_NOT){
        tmp = *i;
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = Cudd_Not(tmp);
        Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_BUF){
        tmp = *i;
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        //tmp = Cudd_Not(tmp);
        Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    return NULL;
}

// -----------------------------------------------------------------------
DdNode * Function::combine_bdds_2(list<DdNode*> &_bdd, int eval_param)
{
    DdNode *tmp, *g;
    list<DdNode*>::iterator i = _bdd.begin();

    if (eval_param == T_NAND){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        i++;
        if (i != _bdd.end())
            g = *i;

        while(i != _bdd.end()){
            tmp = Cudd_bddAnd(manager, tmp, g);
//             Cudd_Ref(tmp1);
//             Cudd_RecursiveDeref(manager, tmp);
            i++;
            g = *i;
            Cudd_Ref(tmp);
//             tmp = tmp1;
        }
        
//         Cudd_Ref(tmp);
        tmp = Cudd_Not(tmp);
        Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_AND){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        i++;
        g = *i;

        while(i != _bdd.end()){
            tmp = Cudd_bddAnd(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp);
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_NOR){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        i++;
        if (i != _bdd.end())
            g = *i;

        while(i != _bdd.end()){
            tmp = Cudd_bddOr(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp);
        }
        tmp = Cudd_Not(tmp);
        Cudd_Ref(tmp);
        
//        Cudd_Ref(tmp);
        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_OR){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        i++;
        g = *i;

        while(i != _bdd.end()){
            tmp = Cudd_bddOr(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp);
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_XNOR){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        i++;
        g = *i;

        while(i != _bdd.end()){
            tmp = Cudd_bddXnor(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp);
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_XOR){
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = *i;
        i++;
        g = *i;

        while(i != _bdd.end()){
            tmp = Cudd_bddXor(manager, tmp, g);
            i++;
            g = *i;
            Cudd_Ref(tmp);
        }
//         Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_NOT){
        tmp = *i;
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        tmp = Cudd_Not(tmp);
        Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    if (eval_param == T_BUF){
        tmp = *i;
        //cout << "Typ hradla: " << eval_param << endl;
        //tmp = Cudd_bddNewVar(manager);
        //tmp = Cudd_Not(tmp);
        Cudd_Ref(tmp);

        //Cudd_PrintDebug(manager,bdd1,4,4);
        //Cudd_PrintDebug(manager,bdd2,4,4);
        //Cudd_PrintDebug(manager,tmp,4,4);
        //cuddP(manager,bdd1);
        //cuddP(manager,bdd2);
        //cuddP(manager,tmp);
        return tmp;
    }

    return NULL;
}

// -----------------------------------------------------------------------
int Function::compare_minterms(char *minterm1, char *minterm2)
{
    int diff = 0;

    for(int i = 0;i < manager->size;i++){
        if (minterm1[i] != minterm2[i])
            diff++;
    }

    return diff;
}

// -----------------------------------------------------------------------
void Function::merge_minterms(char *minterm1, char *minterm2)
{
    int i;

    for(i = 0;i < manager->size;i++){
        if (minterm1[i] != minterm2[i])
            break;
    }
    if (i != manager->size)
        minterm1[i] = '2';
}

// -----------------------------------------------------------------------
int Function::absorp_minterms(char *minterm1, char *minterm2)
{
    int i, i1, i2;

    for(i = 0;i < manager->size;i++){
        if (minterm1[i] != minterm2[i])
            break;
    }
    i1 = i;

    for(i = i1+1;i < manager->size;i++){
        if (minterm1[i] != minterm2[i])
            break;
    }
    i2 = i;

//     cout << "--------------------" << endl;
//     cout << i1 << ", " << i2 << endl;
//     cout << minterm1[i1] << minterm1[i2] << endl;
//     cout << minterm2[i1] << minterm2[i2] << endl;
//     print_array(minterm1, manager->size);
//     print_array(minterm2, manager->size);

    if ((minterm1[i1] == '0' && minterm2[i1] == '2') && (minterm1[i2] == '0' && minterm2[i2] == '1')){
        minterm1[i1] = '0';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '1';
        return 0;
    }
    if ((minterm1[i1] == '1' && minterm2[i1] == '2') && (minterm1[i2] == '0' && minterm2[i2] == '1')){
        minterm1[i1] = '1';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '1';
        return 0;
    }
    if ((minterm1[i1] == '2' && minterm2[i1] == '0') && (minterm1[i2] == '0' && minterm2[i2] == '1')){
        minterm1[i1] = '2';
        minterm1[i2] = '0';
        minterm2[i1] = '0';
        minterm2[i2] = '2';
        return 0;
    }
    if ((minterm1[i1] == '2' && minterm2[i1] == '1') && (minterm1[i2] == '0' && minterm2[i2] == '1')){
        minterm1[i1] = '2';
        minterm1[i2] = '0';
        minterm2[i1] = '1';
        minterm2[i2] = '2';
        return 0;
    }

    if ((minterm1[i1] == '0' && minterm2[i1] == '2') && (minterm1[i2] == '1' && minterm2[i2] == '0')){
        minterm1[i1] = '0';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '0';
        return 0;
    }
    if ((minterm1[i1] == '1' && minterm2[i1] == '2') && (minterm1[i2] == '1' && minterm2[i2] == '0')){
        minterm1[i1] = '1';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '0';
        return 0;
    }
    if ((minterm1[i1] == '2' && minterm2[i1] == '0') && (minterm1[i2] == '1' && minterm2[i2] == '0')){
        minterm1[i1] = '2';
        minterm1[i2] = '1';
        minterm2[i1] = '0';
        minterm2[i2] = '2';
        return 0;
    }
    if ((minterm1[i1] == '2' && minterm2[i1] == '1') && (minterm1[i2] == '1' && minterm2[i2] == '0')){
        minterm1[i1] = '2';
        minterm1[i2] = '1';
        minterm2[i1] = '1';
        minterm2[i2] = '2';
        return 0;
    }

    if ((minterm1[i1] == '0' && minterm2[i1] == '1') && (minterm1[i2] == '0' && minterm2[i2] == '2')){
        minterm1[i1] = '2';
        minterm1[i2] = '0';
        minterm2[i1] = '1';
        minterm2[i2] = '2';
        return 0;
    }
    if ((minterm1[i1] == '0' && minterm2[i1] == '1') && (minterm1[i2] == '1' && minterm2[i2] == '2')){
        minterm1[i1] = '2';
        minterm1[i2] = '1';
        minterm2[i1] = '1';
        minterm2[i2] = '2';
        return 0;
    }
    if ((minterm1[i1] == '0' && minterm2[i1] == '1') && (minterm1[i2] == '2' && minterm2[i2] == '0')){
        minterm1[i1] = '0';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '0';
        return 0;
    }
    if ((minterm1[i1] == '0' && minterm2[i1] == '1') && (minterm1[i2] == '2' && minterm2[i2] == '1')){
        minterm1[i1] = '0';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '1';
        return 0;
    }

    if ((minterm1[i1] == '1' && minterm2[i1] == '0') && (minterm1[i2] == '0' && minterm2[i2] == '2')){
        minterm1[i1] = '2';
        minterm1[i2] = '0';
        minterm2[i1] = '0';
        minterm2[i2] = '2';
        return 0;
    }
    if ((minterm1[i1] == '1' && minterm2[i1] == '0') && (minterm1[i2] == '1' && minterm2[i2] == '2')){
        minterm1[i1] = '2';
        minterm1[i2] = '1';
        minterm2[i1] = '0';
        minterm2[i2] = '2';
        return 0;
    }
    if ((minterm1[i1] == '1' && minterm2[i1] == '0') && (minterm1[i2] == '2' && minterm2[i2] == '0')){
        minterm1[i1] = '1';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '0';
        return 0;
    }
    if ((minterm1[i1] == '1' && minterm2[i1] == '0') && (minterm1[i2] == '2' && minterm2[i2] == '1')){
        minterm1[i1] = '1';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '1';
//         cout << "jeduu" << endl;
        return 0;
    }

    if ((minterm1[i1] == '1' && minterm2[i1] == '2') && (minterm1[i2] == '1' && minterm2[i2] == '2')){
        minterm1[i1] = '2';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '2';
        return 1;
    }
    if ((minterm1[i1] == '0' && minterm2[i1] == '2') && (minterm1[i2] == '0' && minterm2[i2] == '2')){
        minterm1[i1] = '2';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '2';
        return 1;
    }
    if ((minterm1[i1] == '2' && minterm2[i1] == '1') && (minterm1[i2] == '2' && minterm2[i2] == '1')){
        minterm1[i1] = '2';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '2';
        return 2;
    }
    if ((minterm1[i1] == '2' && minterm2[i1] == '0') && (minterm1[i2] == '2' && minterm2[i2] == '0')){
        minterm1[i1] = '2';
        minterm1[i2] = '2';
        minterm2[i1] = '2';
        minterm2[i2] = '2';
        return 2;
    }
//     cout << minterm1[i1] << minterm1[i2] << endl;
//     cout << minterm2[i1] << minterm2[i2] << endl;
    return 0;
}

// // -----------------------------------------------------------------------
// int Function::mt_count(I_buffer *m_t)
// {
//     int i = 0;
//     if (m_t->zero) i++;
//     if (m_t->one) i++;
//     if (m_t->dc) i++;
// 
//     return i;
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_merge_leaves_recursive(I_buffer *akt, I_buffer *parent)
// {
//     if (akt->is_leaf()){
//         if (mt_count(parent) >= 2){
// //             cout << "slucuji listy" << endl;
// //             parent->dc = parent->zero;
//             if (parent->zero){
//                 if (parent->dc){
//                     delete parent->zero;
//                     count--;
//                 }
//                 else {
//                     parent->dc = parent->zero;
//                 }
//                 if (parent->one){
//                     delete parent->one;
//                     count--;
//                 }
//             }
//             else {
//                 if (parent->dc){
//                     delete parent->one;
//                     count--;
//                 }
//                 else {
//                     parent->dc = parent->one;
//                 }
//                 if (parent->zero){
//                     delete parent->zero;
//                     count--;
//                 }
//             }
//             parent->zero = parent->one = NULL;
//         }
//     }
//     else {
// /*        term[index] = '0';*/
//         if (akt->zero){
// //             cout << "0";
//             mt_merge_leaves_recursive(akt->zero, akt);
//         }
// //         term[index] = '1';
//         if (akt->one){
// //             cout << "1";
//             mt_merge_leaves_recursive(akt->one, akt);
//         }
// //         term[index] = '2';
//         if (akt->dc){
// //             cout << "-";
//             mt_merge_leaves_recursive(akt->dc, akt);
//         }
//     }
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_merge_leaves(void)
// {
//     mt_merge_leaves_recursive(min_tree, min_tree);
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_insert_leaf_recursive(I_buffer *akt, int val)
// {
//     if (akt->is_leaf()){
//         switch(val){
//             case 0:
//                 akt->zero = new I_buffer();
// //                 cout << "vkladam 0" << endl;
// //                 count++;
//                 break;
//             case 1:
//                 akt->one = new I_buffer();
// //                 cout << "vkladam 1" << endl;
// //                 count++;
//                 break;
//             case 2:
//                 akt->dc = new I_buffer();
// //                 cout << "vkladam -" << endl;
// //                 count++;
//                 break;
//             default:
//                 cout << "chyba pri rozdelovani stromu" << endl;
//                 break;
//         }
//     }
//     else {
// /*        term[index] = '0';*/
//         if (akt->zero){
// //             cout << "0";
//             mt_insert_leaf_recursive(akt->zero, val);
//         }
// //         term[index] = '1';
//         if (akt->one){
// //             cout << "1";
//             mt_insert_leaf_recursive(akt->one, val);
//         }
// //         term[index] = '2';
//         if (akt->dc){
// //             cout << "-";
//             mt_insert_leaf_recursive(akt->dc, val);
//         }
//     }
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_divide_tree(void)
// {
//     I_buffer *tree1, *tree2, *tree3;
//     clock_t t1, t2;
// 
//     tree1 = tree2 = tree3 = NULL;
// 
//     if (min_tree->zero)
//         tree1 = min_tree->zero;
//     if (min_tree->one)
//         tree2 = min_tree->one;
//     if (min_tree->dc)
//         tree3 = min_tree->dc;
// 
// //     min_tree->zero = min_tree->one = min_tree->dc = NULL;
// //     delete min_tree;
// //     min_tree = NULL;
// //     min_tree = tree1;
// 
//     t1 = clock();
// //     cout << "prvni" << endl;
//     if (tree1)
//         mt_insert_leaf_recursive(tree1, 0);
// //     cout << "druhy" << endl;
//     if (tree2)
//         mt_insert_leaf_recursive(tree2, 1);
// //     cout << "treti" << endl;
//     if (tree3)
//         mt_insert_leaf_recursive(tree3, 2);
//     t2 = clock();
// 
// //     cout << "nove listy vlozeny: "<< difftime(t2, t1)/CLOCKS_PER_SEC << " secs" << endl;
// 
//     if (min_tree->zero)
//         min_tree->zero = NULL;
//     if (min_tree->one)
//         min_tree->one = NULL;
//     if (min_tree->dc)
//         min_tree->dc = NULL;
// 
//     delete min_tree;
// 
//     t1 = clock();
//     if (tree1){
//         min_tree = tree1;
// 
// //         cout << "ctvrty" << endl;
//         if (tree2 && tree3)
//             mt_merge_trees(tree3, tree2, tree1);
//         else
//             if (tree2)
//                 mt_merge_trees(tree2, tree1);
//             else
//                 if (tree3)
//                     mt_merge_trees(tree3, tree1);
//     }
//     else {
//         if (tree2){
//             min_tree = tree2;
// 
//             if (tree1 && tree3)
//                 mt_merge_trees(tree3, tree1, tree2);
//             else
//                 if (tree1)
//                     mt_merge_trees(tree1, tree2);
//                 else
//                     if (tree3)
//                         mt_merge_trees(tree3, tree2);
//         }
//         else {
//             if (tree3){
//                 min_tree = tree3;
// 
//                 if (tree1 && tree2)
//                     mt_merge_trees(tree1, tree2, tree3);
//                 else
//                     if (tree2)
//                         mt_merge_trees(tree2, tree3);
//                     else
//                         if (tree1)
//                             mt_merge_trees(tree1, tree3);
//             }
//         }
//     }
//     t2 = clock();
// //     cout << "stromy slouceny: "<< difftime(t2, t1)/CLOCKS_PER_SEC << " secs" << endl;
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_divide_and_merge(void)
// {
//     I_buffer *tree1, *tree2, *tree3;
// //     clock_t t1, t2;
// 
//     tree1 = tree2 = tree3 = NULL;
// 
//     if (min_tree->zero)
//         tree1 = min_tree->zero;
//     if (min_tree->one)
//         tree2 = min_tree->one;
//     if (min_tree->dc)
//         tree3 = min_tree->dc;
// 
// //     t1 = clock();
// //     cout << "prvni" << endl;
//     if (tree1)
//         mt_insert_leaf_recursive(tree1, 0);
// //     cout << "druhy" << endl;
//     if (tree2)
//         mt_insert_leaf_recursive(tree2, 1);
// //     cout << "treti" << endl;
//     if (tree3)
//         mt_insert_leaf_recursive(tree3, 2);
// //     t2 = clock();
// 
// //     cout << "nove listy vlozeny: "<< difftime(t2, t1)/CLOCKS_PER_SEC << " secs" << endl;
// 
//     if (min_tree->zero)
//         min_tree->zero = NULL;
//     if (min_tree->one)
//         min_tree->one = NULL;
//     if (min_tree->dc)
//         min_tree->dc = NULL;
// 
//     delete min_tree;
// 
// //     t1 = clock();
//     if (tree1){
//         min_tree = tree1;
// 
//         if (tree2)
//             mt_merge_trees(tree2, tree1);
//         if (tree3)
//             mt_merge_trees(tree3, tree1);
//     }
//     else {
//         if (tree2){
//             min_tree = tree2;
// 
//             if (tree1)
//                 mt_merge_trees(tree1, tree2);
//             if (tree3)
//                 mt_merge_trees(tree3, tree2);
//         }
//         else {
//             if (tree3){
//                 min_tree = tree3;
// 
//                 if (tree1)
//                     mt_merge_trees(tree1, tree3);
//                 if (tree2)
//                     mt_merge_trees(tree2, tree3);
//             }
//         }
//     }
// //     t2 = clock();
// //     cout << "stromy slouceny: "<< difftime(t2, t1)/CLOCKS_PER_SEC << " secs" << endl;
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_merge_trees(I_buffer *src1, I_buffer *src2, I_buffer *dest)
// {
//     // nejdriv sloucime podstrom pro hodnotu NULA
//     if (src1->zero && src2->zero){
//         if (dest->zero)
//             mt_merge_trees(src1->zero, src2->zero, dest->zero);
//         else {
//             mt_merge_trees(src1->zero, src2->zero);
//             dest->zero = src2->zero;
//         }
//     }
//     else {
//         if (src1->zero){
//             if (dest->zero)
//                 mt_merge_trees(src1->zero, dest->zero);
//             else
//                 dest->zero = src1->zero;
//         }
//         else
//             if (src2->zero){
//                 if (dest->zero)
//                     mt_merge_trees(src2->zero, dest->zero);
//                 else
//                     dest->zero = src2->zero;
//             }
//     }
// 
//     // potom sloucime podstrom pro hodnotu JEDNA
//     if (src1->one && src2->one){
//         if (dest->one)
//             mt_merge_trees(src1->one, src2->one, dest->one);
//         else {
//             mt_merge_trees(src1->one, src2->one);
//             dest->one = src2->one;
//         }
//     }
//     else {
//         if (src1->one){
//             if (dest->one)
//                 mt_merge_trees(src1->one, dest->one);
//             else
//                 dest->one = src1->one;
//         }
//         else
//             if (src2->one){
//                 if (dest->one)
//                     mt_merge_trees(src2->one, dest->one);
//                 else
//                     dest->one = src2->one;
//             }
//     }
// 
//     // a nakonec sloucime podstrom pro hodnotu DON'T CARE
//     if (src1->dc && src2->dc){
//         if (dest->dc)
//             mt_merge_trees(src1->dc, src2->dc, dest->dc);
//         else {
//             mt_merge_trees(src1->dc, src2->dc);
//             dest->dc = src2->dc;
//         }
//     }
//     else {
//         if (src1->dc){
//             if (dest->dc)
//                 mt_merge_trees(src1->dc, dest->dc);
//             else
//                 dest->dc = src1->dc;
//         }
//         else
//             if (src2->dc){
//                 if (dest->dc)
//                     mt_merge_trees(src2->dc, dest->dc);
//                 else
//                     dest->dc = src2->dc;
//             }
//     }
// 
//     if (src1->zero) src1->zero = NULL;
//     if (src1->one) src1->one = NULL;
//     if (src1->dc) src1->dc = NULL;
//     if (src2->zero) src2->zero = NULL;
//     if (src2->one) src2->one = NULL;
//     if (src2->dc) src2->dc = NULL;
// 
//     delete src1;
//     delete src2;
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_merge_trees(I_buffer *src, I_buffer *dest)
// {
//     if (src->zero){
//         if (dest->zero)
//             mt_merge_trees(src->zero, dest->zero);
//         else
//             dest->zero = src->zero;
//     }
//     if (src->one){
//         if (dest->one)
//             mt_merge_trees(src->one, dest->one);
//         else
//             dest->one = src->one;
//     }
//     if (src->dc){
//         if (dest->dc)
//             mt_merge_trees(src->dc, dest->dc);
//         else
//             dest->dc = src->dc;
//     }
//     if (src->zero) src->zero = NULL;
//     if (src->one) src->one = NULL;
//     if (src->dc) src->dc = NULL;
//     delete src;
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_insert(char *term)
// {
//     I_buffer *akt = min_tree;
// 
//     for(int i = 0;i < manager->size;i++){
//         switch(term[i]){
//             case '0':
//                 if (akt->zero == NULL){
// //                     cout << "vkladam novy uzel do nuly" << endl;
// //                     cout << "new" << endl;
//                     if (i == (manager->size-1)){
//                         if (akt->one || akt->dc){
//                             if (akt->one)
//                                 delete akt->one;
//                             if (!akt->dc)
//                                 akt->dc = new I_buffer();
//                             else
//                                 break;
//                         }
//                         else
//                             akt->zero = new I_buffer();
//                     }
//                     else
//                         akt->zero = new I_buffer();
//                 }
//                 akt = akt->zero;
//                 break;
//             case '1':
//                 if (akt->one == NULL){
// //                     cout << "vkladam novy uzel do jednicky" << endl;
// //                     cout << "new" << endl;
//                     if (i == (manager->size-1)){
//                         if (akt->zero || akt->dc){
//                             if (akt->zero)
//                                 delete akt->zero;
//                             if (!akt->dc)
//                                 akt->dc = new I_buffer();
//                             else
//                                 break;
//                         }
//                         else
//                             akt->one = new I_buffer();
//                     }
//                     else
//                         akt->one = new I_buffer();
//                 }
//                 akt = akt->one;
//                 break;
//             case '2':
//                 if (akt->dc == NULL){
// //                     cout << "vkladam novy uzel do don't care" << endl;
// //                     cout << "new" << endl;
//                     if (i == (manager->size-1)){
//                         if (akt->one || akt->zero){
//                             if (akt->one){
//                                 akt->dc = akt->one;
//                                 if (akt->zero)
//                                     delete akt->zero;
//                                 break;
//                             }
//                             if (akt->zero){
//                                 akt->dc = akt->zero;
//                                 if (akt->one)
//                                     delete akt->one;
//                                 break;
//                             }
//                         }
//                         else
//                             akt->dc = new I_buffer();
//                     }
//                     else
//                         akt->dc = new I_buffer();
//                 }
//                 akt = akt->dc;
//                 break;
//             default:
// //                 cout << "chyba v termu" << endl;
//                 throw std::runtime_error("chyba v termu");
//         }
//     }
//     if (mt_count(akt) >= 2){
//         if (akt->zero){
//             if (akt->dc){
//                 delete akt->zero;
// //                 count--;
//             }
//             else {
//                 akt->dc = akt->zero;
//             }
//             if (akt->one){
//                 delete akt->one;
// //                 count--;
//             }
//         }
//         else {
//             if (akt->dc){
//                 delete akt->one;
// //                 count--;
//             }
//             else {
//                 akt->dc = akt->one;
//             }
//             if (akt->zero){
//                 delete akt->zero;
// //                 count--;
//             }
//         }
//     }
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_print_recursive(I_buffer *m_t, char *term, int index, char *out)
// {
//     if (m_t->is_leaf()){
//         print_array(term, manager->size);
// //         cout << endl;
// //         PLA_elem *pla = new PLA_elem(manager->size, outputs_num);
// //         memcpy((char *)pla->pla_inputs, (char *)term, manager->size*sizeof(char));
// //         memcpy((char *)pla->pla_outputs, (char *)out, outputs_num*sizeof(char));
// //         pla_list.push_back(pla);
//     }
//     else {
//         term[index] = '0';
//         if (m_t->zero){
// //             cout << "0";
//             mt_print_recursive(m_t->zero, term, index+1, out);
//         }
//         term[index] = '1';
//         if (m_t->one){
// //             cout << "1";
//             mt_print_recursive(m_t->one, term, index+1, out);
//         }
//         term[index] = '2';
//         if (m_t->dc){
// //             cout << "-";
//             mt_print_recursive(m_t->dc, term, index+1, out);
//         }
//     }
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_print(char *out)
// {
//     char *term;
// 
//     term = new char[manager->size];
//     for(int i = 0;i < manager->size;i++)
//         term[i] = '3';
// 
//     mt_print_recursive(min_tree, term, 0, out);
// 
//     delete[] term;
// }
// 
// // -----------------------------------------------------------------------
// void Function::mt_export(I_buffer *m_t, Export *o_file, char *term, char *out, int index)
// {
//     if (m_t->is_leaf()){
//         o_file->export_to_pla(o_file->get_file_min(), term, out, manager->size, outputs_num);
//     }
//     else {
//         term[index] = '0';
//         if (m_t->zero){
// //             cout << "0";
//             mt_export(m_t->zero, o_file, term, out, index+1);
//         }
//         term[index] = '1';
//         if (m_t->one){
// //             cout << "1";
//             mt_export(m_t->one, o_file, term, out, index+1);
//         }
//         term[index] = '2';
//         if (m_t->dc){
// //             cout << "-";
//             mt_export(m_t->dc, o_file, term, out, index+1);
//         }
//     }
// }

// -----------------------------------------------------------------------
void Function::minimize_output_recursive(DdNode * node, char * minterm, Export *o_file)
{
    DdNode *N, *Nv, *Nnv;
    int i = 0, index;
//     int index;
    
    N = Cudd_Regular(node);

//     cout << N << endl;

    if (cuddIsConstant(N)) {
// //         cout << "list " << N->index << endl;
// //         print_array(minterm, manager->size);
// //         cout << outputs_num << endl;
        if (node != background && node != zero) {
            count++;
// //             cout << "vytvarim vstupy" << endl;
            pla = new PLA_elem(manager->size, outputs_num);
//             cout << "vstupy vytvoreny" << endl;

            for(list<Bdd_elem>::iterator it = output_list.begin(); it != output_list.end(); it++){
//                 cout << it->node->index << ":" << node->index << endl;
                if (it->type == T_OUTPUT){
//                     cout << it->gate << " == " << out_var << endl;
                    if (it->gate == out_var){
//                         cout << "mam spravny vystup " << it->gate << endl;
                        pla->pla_outputs[i] = '1';
                    }
                    else {
                        pla->pla_outputs[i] = '0';
                    }
                    i++;
                }
            }

            memcpy((char *)pla->pla_inputs, (char *)minterm, manager->size*sizeof(char));
//             cout << "vkladam" << endl;
            pla_list.push_back(pla);
//             cout << "vlozeno" << endl;

            if (count%1000000 == 0){
                    o_file->export_to_pla(o_file->get_file(), pla_list, manager->size, outputs_num);
                    for(list<PLA_elem*>::iterator it = pla_list.begin();it != pla_list.end();it++)
                        delete *it;
                    pla_list.clear();
            }
        }
    }
    else {
        Nv  = cuddT(N);
        Nnv = cuddE(N);
        if (Cudd_IsComplement(node)) {
            Nv  = Cudd_Not(Nv);
            Nnv = Cudd_Not(Nnv);
        }
        index = N->index;
//         cout << "uzel " << N->index << endl;
        minterm[index] = '0';
//         cout << N->index << " E ";
        minimize_output_recursive(Nnv, minterm, o_file);
//         cout << N->index << " T ";
        minterm[index] = '1';
        minimize_output_recursive(Nv, minterm, o_file);
        minterm[index] = '2';
    }
//     cout << count << " delete" << endl;
//     print_pla_list();
//     print_array(pla->pla_inputs, manager->size);
//     cout << "nodopici" << endl;
//     cout << "seznam: " << pla_list.size() << endl;
}

// -----------------------------------------------------------------------
void Function::minimize_output_pupik(DdNode *node, char *minterm, Export *o_file, Pupik *pupik)
{
    DdNode *N, *Nv, *Nnv;
    int index;
//     int i;
//     char *out;

    N = Cudd_Regular(node);

//     cout << N << endl;

    if (cuddIsConstant(N)) {
        if (node != background && node != zero) {
//             if (count < 10000000){
                pupik->mt_insert(minterm);
//                 count++;
//             }
/*            else {
                cout << "treshold reached" << endl;
                pupik->mt_insert(minterm);
                
                pupik->mt_divide_and_merge();

                for(int j = 1;j < manager->size;j++){
//                 t1 = clock();
//                 cout << j+1 << ". pruchod" << endl;
//                 cout << "pocet listu: " << count << endl;
//                 mt_print();
                    pupik->mt_merge_leaves();
//                 cout << "--------------" << endl;
//                 mt_divide_tree();
                    pupik->mt_divide_and_merge();
//                 mt_print();
//                 t2 = clock();
//                 cout << j+1 << ".iterace trvala "<< difftime(t2, t1)/CLOCKS_PER_SEC << " secs" << endl;
                }
//             t2 = clock();

                out = new char[outputs_num];
                i = 0;
                for(list<Bdd_elem>::iterator it = output_list.begin(); it != output_list.end(); it++){
//                 cout << i;
                    if (it->gate == out_var){
                        out[i] = '1';
                    }
                    else {
                        out[i] = '0';
                    }
                    i++;
                }
//                 mt_export(min_tree, o_file, minterm, out, 0);
//                 delete min_tree;
                delete[] out;
                count = 0;
//                 min_tree = new I_buffer();
            }*/
        }
    }
    else {
        Nv  = cuddT(N);
        Nnv = cuddE(N);
        if (Cudd_IsComplement(node)) {
            Nv  = Cudd_Not(Nv);
            Nnv = Cudd_Not(Nnv);
        }
        index = N->index;
        minterm[index] = '0';
        minimize_output_pupik(Nnv, minterm, o_file, pupik);
        minterm[index] = '1';
        minimize_output_pupik(Nv, minterm, o_file, pupik);
        minterm[index] = '2';
    }
}

// -----------------------------------------------------------------------
int Function::minimize_output(DdNode * node, Export *o_file, int method)
{
    int i;
    char *minterm_temp, *out;
//     clock_t t1, t2;
    Pupik *pupik;

    background = manager->background;
    zero = Cudd_Not(manager->one);
    minterm_temp = new char[manager->size];

    if (minterm_temp == NULL) {
        manager->errorCode = CUDD_MEMORY_OUT;
        return 0;
    }
    for (i = 0; i < manager->size; i++)
        minterm_temp[i] = '2';

    out = new char[outputs_num];

    switch(method){
        case 1:
            minimize_output_recursive(node, minterm_temp, o_file);
            break;
        case 2:
            // minimalizace ternarnim stromem
            count = 0;
//             cout << manager->size << "::" << outputs_num << endl;
            
            pupik = new Pupik(manager->size, outputs_num);

//             t1 = clock();
            try {
                minimize_output_pupik(node, minterm_temp, o_file, pupik);
            }
            catch(std::runtime_error &e){
                cout << "error occured: " << e.what() << endl;
                exit (2);
            }
            
//             cout << "pocet termu: " << count << endl;
//             if (count > 1){

//             pupik->pocet_sloucenych = 0;
//             pupik->mt_print(NULL);
//             cout << "pocet termu: " << pupik->pocet_sloucenych << endl;
            
//             t1 = clock();
            pupik->mt_divide_and_merge();
            
            for(int j = 1;j < manager->size;j++){
//                 t1 = clock();
//                 cout << j+1 << ". pruchod" << endl;
//                 cout << "pocet listu: " << count << endl;
//                 mt_print();
//                 pupik->pocet_sloucenych = 0;
                pupik->mt_merge_leaves();
//                 cout << "--------------" << endl;
//                 mt_divide_tree();
                pupik->mt_divide_and_merge();
//                 mt_print();
//                 t2 = clock();
//                 cout << j+1 << ".iterace trvala "<< difftime(t2, t1)/CLOCKS_PER_SEC << " secs" << endl;
            }
//             cout << pupik->pocet_sloucenych << endl;
//             t2 = clock();
//             cout << "zakladni minimalizace:  " << difftime(t2, t1)/CLOCKS_PER_SEC << " secs" << endl;
            
//             pupik->pocet_sloucenych = 0;
//             pupik->mt_print(NULL);
//             cout << "pocet termu: " << pupik->pocet_sloucenych << endl;
            
//             pupik->pocet_sloucenych = 0;
//             t1 = clock();
            if (subsets){
//                 cout << "podmnoziny" << endl;
                pupik->mt_minimize_subset();
            }
//             t2 = clock();
//             cout << "podmnoziny: " << difftime(t2, t1)/CLOCKS_PER_SEC << " secs" << endl;
//             cout << "pocet termu: " << pupik->pocet_sloucenych << endl;
            
            i = 0;
            for(list<Bdd_elem>::iterator it = output_list.begin(); it != output_list.end(); it++){
//                 cout << i;
                if (it->gate == out_var){
                    out[i] = '1';
                }
                else {
                    out[i] = '0';
                }
                i++;
            }
            pupik->mt_export(o_file, pupik->get_tree(), minterm_temp, out, 0);
            delete pupik;
            pupik = NULL;
            break;
        default:
            break;
    }

    delete[] minterm_temp;
//     delete pupik;
    delete[] out;

    return method;
}

// -----------------------------------------------------------------------
bool Function::run(vector<Input_elem> &var_list, Export *o_file, int out)
{
//     clock_t t_start, t_end, start, end;
//     time_t t1, t2;
    list<Bdd_elem>::iterator i;
//     ofstream &file = o_file->get_file();

    outputs_num = out;

//     t_start = clock();
//     t1 = time(NULL);
    create_bdd_list(var_list);
//     t2 = time(NULL);
//     t_end = clock();

//     cout << "trees built in " << difftime(t2, t1) << " secs" << endl;
//     cout << "trees built in " << difftime(t_end, t_start)/CLOCKS_PER_SEC << " secs" << endl;

    o_file->PLA_print_header(o_file->get_file(), bdd_list, manager->size, outputs_num);

    i = bdd_list.begin();
    
//     for(list<Bdd_elem>::iterator i = bdd_list.begin(); i != bdd_list.end(); i++){
    while(i != bdd_list.end()){
        if (i->type == T_OUTPUT){
//             Cudd_RecursiveDeref(manager, i->node);
            output_list.push_back(*i);
//             Cudd_PrintDebug(manager, i->node, manager->size, 3); 
//             Cudd_PrintMinterm(manager, i->node);
            i++;
//             cout << "pocet uzlu: " << Cudd_DagSize(i->node) << endl;
        }
        else {
//             Cudd_PrintDebug(manager, i->node, manager->size, 1);
//             cout << "pocet referenci na uzel " << i->node->index << ": " << i->node->ref << endl;
//             Cudd_RecursiveDeref(manager, i->node);
            Cudd_IterDerefBdd(manager, i->node);
            i = bdd_list.erase(i);
//             i--;
//             i++;
        }
    }

//     cout << "----------------------------------------------------" << endl;
    
//     for(list<Bdd_elem>::iterator i = bdd_list.begin(); i != bdd_list.end(); i++){
//         Cudd_PrintDebug(manager, i->node, manager->size, 1); 
//     }

    // reordering
//     Cudd_EnableReorderingReporting(manager);
    Cudd_ReduceHeap(manager, CUDD_REORDER_SIFT, 1);

    //-----------------------------------------------------------------------------------
//     o_file->PLA_print_header(o_file->get_file(), bdd_list, manager->size, outputs_num);
    
//     start = clock();
//     t1 = time(NULL);
//     nejdriv bez pupika
    count = 0;
    for(list<Bdd_elem>::iterator i = output_list.begin(); i != output_list.end(); i++){
        out_var = i->gate;
//         if (i->gate == "Z"){
//             Cudd_PrintMinterm(manager, i->node);
//             continue;
//             Cudd_PrintDebug(manager, i->node, manager->size, 4);
//             t_start = clock();
            if (minimize){
                minimize_output(i->node, o_file, 2);
            }
            else {
                minimize_output(i->node, o_file, 1);
    
                if (!pla_list.empty()){
                    o_file->export_to_pla(o_file->get_file(), pla_list, manager->size, outputs_num);
                    for(list<PLA_elem*>::iterator it = pla_list.begin();it != pla_list.end();it++)
                        delete *it;
                    pla_list.clear();
                }
            }
//             t_end = clock();
//             cout << i->gate << " exported in " << difftime(t_end, t_start)/CLOCKS_PER_SEC << " secs" << endl;
//         }
    }
//     cout << "number of terms: " << count << endl;

    o_file->PLA_print_footer(o_file->get_file());

//     end = clock();
//     t2 = time(NULL);

//     cout << "collapsing before reordering finished in " << difftime(end, start)/CLOCKS_PER_SEC << " secs" << endl;
//     cout << "collapsing before reordering finished in " << difftime(t2, t1) << " secs" << endl;



    //-----------------------------------------------------------------------------------
/*    o_file->PLA_print_header(o_file->get_file_min(), bdd_list, manager->size, outputs_num);

    start = clock();
    t1 = time(NULL);
//     ...potom s pupikem
    for(list<Bdd_elem>::iterator iter = output_list.begin(); iter != output_list.end(); iter++){
        t_start = clock();
        minimize_output(iter->node, o_file, 2);
        t_end = clock();
        cout << iter->gate << " exported in " << difftime(t_end, t_start)/CLOCKS_PER_SEC << " secs" << endl;
    }

    o_file->PLA_print_footer(o_file->get_file_min());

    end = clock();
    t2 = time(NULL);

    cout << "collapsing after reordering finished in " << difftime(end, start)/CLOCKS_PER_SEC << " secs" << endl;*/
// //     cout << "collapsing after reordering finished in " << difftime(t2, t1) << " secs" << endl;

    return true;
}
