#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef char reseni_t;

/** Struktura predstavujici jednu klausuli */
struct klausule_s {
        unsigned short promennych;
        signed short promenne[];
};

/** Struktura predstavujici reseny problem - jednu instanci logicke formule a vahy*/
struct problem_s {
        unsigned short promennych;
        unsigned short klausuli;
        unsigned short klausuli_nacteno;
        unsigned short prvni_reseni;

        unsigned short *vaha;
        struct klausule_s *klausule[];
};

/** Konstruktor
 * @param promennych - pocet literalu (literaly jsou reprezentovany cisly 1,2,..pocet literalu)
 * @param klausuli - pocet klausuli
 * @param prvni_reseni - pokud je nenulovy, vrati se prvni nalezene reseni
 * */
struct problem_s *problem_new(int promennych, int klausuli, int prvni_reseni)
{
        struct problem_s *problem = (struct problem_s *)malloc(sizeof(struct problem_s) +
                        klausuli * sizeof(struct klausule_s*));
        if( problem != NULL ){
                problem->vaha = (unsigned short *)malloc(sizeof(short) * (promennych + 1));

                if( problem->vaha == NULL ){
                        free(problem);
                        return NULL;
                }

                problem->promennych = promennych;
                problem->klausuli = klausuli;
                problem->klausuli_nacteno = 0;
                problem->prvni_reseni = prvni_reseni ? 1 : 0;

                while( promennych-- )
                        problem->vaha[promennych + 1] = 1;//1 + (int) ((float)max_vaha * (rand()/(RAND_MAX + 1.0)));
        }
        return problem;
}

/** Destruktor */
void problem_znic(struct problem_s *problem)
{
        int i;
        for( i = 0; i < problem->klausuli_nacteno; i++) free( problem->klausule[i] );
        free( problem->vaha );
        free( problem );
}

/** Prida klausuli do formule reseneho problemu, predpoklada konjunktivní normální formu
 * @param problem - problem, do ktereho se pridava klausule
 * @param promennych - pocet literalu formule
 * @param promenne - pole literalu, kazdy literalu, pro negovanou hodnotu je cislo literalu zaporne
 * */
void problem_pridej_klausuli(struct problem_s *problem, int promennych, signed short promenne[])
{
        struct klausule_s *klausule;
        if( problem->klausuli_nacteno == problem->klausuli ){
                fprintf(stderr, "Vsechny klauzule uz byly nacteny\n");
        } else if ( (klausule = (struct klausule_s *)malloc( sizeof(struct klausule_s)
                                        + sizeof(signed short) * promennych ) ) == NULL ){
                fprintf(stderr, "Dosla pamet\n");
        } else {
                problem->klausule[problem->klausuli_nacteno++] = klausule;
                klausule->promennych = promennych;
                while( promennych-- ){
                        if( promenne[ promennych ] > problem->promennych || promenne[ promennych ] < -problem->promennych){
                                fprintf(stderr, "Neexistujici promenna\n");
                                klausule->promenne[ promennych ] = 0;
                        } else {
                                klausule->promenne[ promennych ] = promenne[ promennych ];
                        }
                }
        }

}

/** Vypise formuli problemu na stdout */
void problem_vypis(struct problem_s *problem)
{
        int i, j;
        for(i = 0; i < problem->klausuli; i++){
                if( problem->klausule[i]->promennych == 0 ) continue;
                printf("(");
                printf("%d", problem->klausule[i]->promenne[0]);
                for( j = 1; j < problem->klausule[i]->promennych; j++ )
                        printf("%+d", problem->klausule[i]->promenne[j]);
                printf(")");
                
        }
        printf("\n");
}

/** Otestuje, zda je klausule splnena
 * @param klausule - testovana klausule
 * @param reseni - ohodnoceni promennych
 * @return 1 kdyz je splnena, 0 jinak
 */
int klausule_je_splnena(const struct klausule_s *klausule, const reseni_t *reseni)
{
        int j;
        for( j = 0; j < klausule->promennych; j++ )
                if( klausule->promenne[j] > 0 ){
                        if( reseni[ klausule->promenne[j] ] ){
                                return 1;
                        }
                } else if ( ! reseni[ - klausule->promenne[j] ] ){
                        return 1;
                }
        return 0;
}

/** Urci pocet splnenych klausuli
 * @param problem - problem s dotazovanou formuli
 * @param reseni - ohodnoceni promennych
 * @return pocet splnenych klauzuli
 * */
int problem_splneno_klausuli(const struct problem_s *problem, const reseni_t *reseni)
{
        int i, sum = 0;
        for( i = 0; i < problem->klausuli; i++ )
                if( klausule_je_splnena( problem->klausule[i], reseni ) ) sum++;
        return sum;
}

/** Urci pocet splnenych klausuli a aktualizuje vyskyt
 * @param problem - problem s dotazovanou formuli
 * @param reseni - ohodnoceni promennych
 * @param vyskyt - v pripade ze je i-ta formule splnena, zapise do vyskyt[i] krok
 * @param krok - zapisovana hodnota
 * @return pocet splnenych klauzuli
 * */
int problem_splneno_aktualizuj_vyskyt(const struct problem_s *problem, const reseni_t *reseni, int *vyskyt, int krok)
{
        int i, sum = 0;
        for( i = 0; i < problem->klausuli; i++ )
                if( klausule_je_splnena( problem->klausule[i], reseni ) ){
                        sum++;
                        vyskyt[i] = krok;
                        break;
                }
        return sum;
}

/** Nahodne ohodnoti promenne
 * @param problem - problem k jehoz formuli je ohodnoceni generovano
 * @param reseni - sem zapise ohodnoceni
 * @param pravdepodobnost_jednicky - pravdepodobnost nastaveni promenne na 1
 */
void problem_nahodne_ohodnoceni(const struct problem_s *problem, reseni_t *reseni, float pravdepodobnost_jednicky)
{
        int i;
        for( i = 0; i <= problem->promennych; i++ )
                if( rand() < pravdepodobnost_jednicky*RAND_MAX ) reseni[i] = 1;
                else reseni[i] = 0;
}

/** Spocita vahu ohodnoceni
 * @param problem - problem s dotazovanou formuli
 * @param reseni - ohodnoceni promennych
 * @return vahu ohodnoceni
 */
int problem_vaha(const struct problem_s *problem, const reseni_t *reseni)
{
        int i, sum = 0;
        for( i = 1; i <= problem->promennych; i++ )
                if( reseni[i] ) sum += problem->vaha[i];
        return sum;
}

/** Vytvori novy vektor promennych (alokuje pamet) */
reseni_t* problem_vektor_reseni(const struct problem_s *problem)
{
        return (reseni_t*)malloc( sizeof(reseni_t)*(problem->promennych+1) );
}

/** Zkopiruje ohodnoceni */
void problem_kopiruj_reseni(const struct problem_s *problem, reseni_t *cil, const reseni_t *zdroj)
{
        memcpy(cil, zdroj, sizeof(reseni_t)*(problem->promennych + 1) );
}