#include <cstdio>

#include "espresso.h"
#include "signature.h"

int setup_bw(pset_family R, pset c); /** MM: int na indikaciu chyby */
int black_white(void);
void split_list(pset_family R, int v);
void merge_list(void);

static int alloc_list(int size); /** MM: int na indikaciu chyby */
static void free_list(void);
static void init_list(int size);
static void delete_elem(int element);
static void insert_elem(int element);

static int alloc_stack(int size); /** MM: int na indikaciu chyby */
static void free_stack(void);
static void clear(void);

static int white_head, white_tail;
static int black_head, black_tail;
static int forward_link, backward_link;
static int *forward, *backward;

static int *stack_head, *stack_tail, stack_p;

static pcover BB;

/** MM: int na kontrolu chyby */
/** MM: nic nedealokuje */
static int
alloc_list(int size)
{
	forward = (int *)calloc(size, sizeof(int));
	backward =(int *)calloc(size, sizeof(int));
	if(!forward || !backward){
		fatal2("alloc_list");
		return -1;
	}
	return 0;
}

static void
free_list(void)
{
	FREE(forward);
	FREE(backward);
}

static void
init_list(int size)
{
	int i;
	for(i = 0; i < size; i++){
		forward[i] = i+1;
		backward[i] = i-1;
	}
	forward[size-1] = -1;
	backward[0] = -1;
	white_head = 0;
	white_tail = size - 1;
	black_head = -1;
	black_tail = -1;
}

static void
delete_elem(int element)
{
	forward_link = forward[element];
	backward_link = backward[element];
	if(forward_link != -1){
		if(backward_link != -1){
			forward[backward_link] = forward_link;
			backward[forward_link] = backward_link;
		}
		else{
			white_head = forward_link;
			backward[forward_link] = -1;
		}
	}
	else{
		if(backward_link != -1){
			white_tail = backward_link;
			forward[backward_link] = -1;
		}
		else{
			white_head = white_tail = -1;
		}
	}	
}

static void
insert_elem(int element)
{
	if(black_head != -1){
		forward[element] = black_head;
		backward[element] = -1;
		backward[black_head] = element;
		black_head = element;
	}
	else{
		black_head = black_tail = element;
		forward[element] = backward[element] = -1;
	}
}

void
merge_list(void)
{
	if(white_head != -1){
		if(black_head != -1){
			forward[white_tail] = black_head;
			backward[black_head] = white_tail;
			white_tail = black_tail;
			black_head = black_tail = -1;
		}
	}
	else{
		white_head = black_head;
		white_tail = black_tail;
		black_head = black_tail = -1;
	}
}

/** MM: int na kontrolu chyby */
/** MM: nic nedealokuje */
static int
alloc_stack(int size)
{
	stack_head = (int *)calloc(size,sizeof(int));
	stack_tail = (int *)calloc(size,sizeof(int));
	if(!stack_head || !stack_tail){
		fatal2("alloc_stack");
		return -1;
	}
	return 0;
}

static void
free_stack(void)
{
	FREE(stack_head);
	FREE(stack_tail);
}

void
push_black_list(void)
{
	stack_head[stack_p] = black_head;
	stack_tail[stack_p++] = black_tail;
}

void
pop_black_list(void)
{
	black_head =  stack_head[--stack_p];
	black_tail =  stack_tail[stack_p];
}

static void
clear(void)
{
	stack_p = 0;
}

/** MM: int na indikaciu chyby; kontroluje alloc_list, alloc_stack a BB */
/** o zavolanie free_bw sa musi postarat volajuci [etr_order] */
int
setup_bw(pset_family R, pset c)
{
	pcube out_part_r;
	int size = R->count;
	register int i;
	pcube b,r;
    	register int w, last;
	register unsigned int x;

    /** MM: inicializacia ukaztelov na NULL, aby sa korektne dealokovali v pripade chyby */
    BB = NULL; stack_head = NULL; stack_tail = NULL; forward = NULL; backward = NULL;
	/* Allocate memory */
	if (alloc_list(size) == -1){ /** MM: chyba v alloc_list */
        return -1;
    }
	if (alloc_stack(cube->num_binary_vars) == -1) { /** MM: chyba v alloc_stack */
        return -1;
    }
	BB = new_cover(size);
	if (BB==NULL){ /** MM: chyba alokacie BB */
	   return -1;
    }
	BB->count = size;
	out_part_r = new_cube();

	init_list(size);
	clear();
	/* form BB */
	/* Blocking Matrix formed here is bit-complement of that in sigma  */
	foreachi_set(R,i,r){
		b = GETSET(BB,i);
		if ((last = cube->inword) != -1) {
			/* Check the partial word of binary variables */
			x = r[last] & c[last];
			x = ~(x | x >> 1) & cube->inmask;
			b[last] = r[last] & (x | x << 1);
			/* Check the full words of binary variables */
			for(w = 1; w < last; w++) {
	    			x = r[w] & c[w];
	    			x = ~(x | x >> 1) & DISJOINT;
				b[w] = r[w] & (x | x << 1);
			}
    		}
		PUTLOOP(b,LOOP(r));
		INLINEset_and(b,b,cube->binary_mask);
		INLINEset_and(out_part_r,cube->mv_mask,r);
		if(!setp_implies(out_part_r,c)){
			INLINEset_or(b,b,out_part_r);
		}
		set_not(b);
	}
	free_cube(out_part_r);
	return 0;
}

void
free_bw(void)
{
	free_list();
	free_stack();
	free_cover(BB);
}

int
black_white(void)
{
	int b_index, w_index;
	int containment;
	for(b_index = black_head; b_index != -1; b_index = forward[b_index]){
		containment = FALSE3;
		for(w_index = white_head; w_index != -1; w_index = forward[w_index]){
			if(setp_implies(GETSET(BB,b_index), GETSET(BB,w_index))){
				containment = TRUE3;
				break;
			}
		}	
		if(containment == FALSE3){
			return FALSE3;
		}
	}
	return TRUE3;
}

void
reset_black_list(void)
{
	black_head = black_tail = -1;
}


void
split_list(pset_family R, int v)
{
	int index, next_index;;

	for(index = white_head; index != -1; index = next_index){
		next_index = forward[index];
		if(!is_in_set(GETSET(R, index), v)){
			delete_elem(index);
			insert_elem(index);
		}
	}
}
