/* Steven Andrews, 4/20/95, modified 9/98, modified 1/21/02 */
/* Performs basic set operations with an unordered linked list */
/* See documentation called Set doc */
/* Copyright 2003 by Steven Andrews.  Permission is granted
   for non-commercial use of and modifications to the code. */

#include <stdlib.h>
#include "Set.h"

int FindCell(void *k,void *x,set s,struct scell **prevptr,struct scell **cellptr);
int FindKey(void *k,set s,struct scell **prevptr,struct scell **cellptr);
int FindItem(void *x,set s,struct scell **prevptr,struct scell **cellptr);
struct scell *SetCellAlloc();
void SetCellFree(struct scell *cell,int freek,int freex);


struct scell *SetCellAlloc() {
	struct scell *cell;

	cell=(struct scell *) malloc(sizeof(struct scell));
	if(!cell) return NULL;
	cell->key=NULL;
	cell->item=NULL;
	cell->next=NULL;
	return cell; }

void SetCellFree(struct scell *cell,int freek,int freex) {
	if(!cell) return;
	if(freek && cell->key) free(cell->key);
	if(freex && cell->item) free(cell->item);
	free(cell);
	return; }

int FindCell(void *k,void *x,set s,struct scell **prevptr,struct scell **cellptr) {
/* If not in set, returns 0, prev points to last filled cell if any, cell points to final empty cell. */
/* If in set, returns 1 and prev points before or NULL if none before, cell points to member. */
	struct scell *c;
	
	*prevptr=NULL;
	*cellptr=c=s->list;
	while(c->next && ((s->keycmp && (*s->keycmp)(k,c->key)) || (s->itemcmp && (*s->itemcmp)(x,c->item))))	{
		*prevptr=c;
		*cellptr=c=c->next; }
	if(c->next) return 1;
	return 0; }

int FindKey(void *k,set s,struct scell **prevptr,struct scell **cellptr) {
/* See FindCell */
	struct scell *c;
	
	*prevptr=NULL;
	*cellptr=c=s->list;
	if(!s->keycmp) return 0;
	while(c->next && (*s->keycmp)(k,c->key))	{
		*prevptr=c;
		*cellptr=c=c->next; }
	if(c->next) return 1;
	return 0; }

int FindItem(void *x,set s,struct scell **prevptr,struct scell **cellptr) {
/* See FindCell */
	struct scell *c;

	*prevptr=NULL;
	*cellptr=c=s->list;
	if(!s->itemcmp) return 0;
	while(c->next && (*s->itemcmp)(x,c->item))	{
		*prevptr=c;
		*cellptr=c=c->next; }
	if(c->next) return 1;
	return 0; }


set SetAlloc(int (*keycmp)(void *,void *),int (*itemcmp)(void *,void *)) {
	set s;

	if(!keycmp && !itemcmp) return NULL;
	s=(set) malloc(sizeof(struct sstruct));
	if(!s) return NULL;
	s->keycmp=keycmp;
	s->itemcmp=itemcmp;
	s->list=SetCellAlloc();
	if(!s->list) {SetFree(s,0,0);return NULL;}
	return s; }

void SetFree(set s,int freek,int freex) {
	if(!s) return;
	if(s->list) {
		SetNull(s,freek,freex);
		SetCellFree(s->list,freek,freex); }
	free(s); }

void SetNull(set s,int freek,int freex) {
	struct scell *cell,*prev;
	
	cell=s->list;
	while(cell->next)	{
		prev=cell;
		cell=cell->next;
		SetCellFree(prev,freek,freex);	}
	s->list=cell; }

int SetInsert(void *k,void *x,set s) {
	struct scell *cell,*prev,*cz;
	int i;
	
	i=FindCell(k,x,s,&prev,&cell);
	if(i) return 2;	
	if(!prev)	{
		prev=SetCellAlloc();
		if(!prev) return 0;
		s->list=prev;	}
	else	{
		cz=SetCellAlloc();
		if(!cz) return 0;
		prev->next=cz;
		prev=cz; }
	prev->key=k;
	prev->item=x;
	prev->next=cell;
	return 1; }

int SetDelete(void *k,void *x,set s,int freek,int freex) {
	struct scell *cell,*prev;
	int i;
	
	i=FindCell(k,x,s,&prev,&cell);
	if(!i) return 0;
	if(!prev)	s->list=cell->next;
	else	prev->next=cell->next;
	SetCellFree(cell,freek,freex);
	return 1; }

int SetMember(void *k,void *x,set s) {
	struct scell *cell,*prev;
	
	return FindCell(k,x,s,&prev,&cell); }

void *SetItem(void *k,set s) {
	struct scell *cell,*prev;
	
	return(FindKey(k,s,&prev,&cell)?cell->item:0); }

void **SetItemAt(void *k,set s) {
	struct scell *cell,*prev;

	return(FindKey(k,s,&prev,&cell)?&(cell->item):0); }

void *SetKey(void *x,set s) {
	struct scell *cell,*prev;
	
	return(FindItem(x,s,&prev,&cell)?cell->key:0); }

struct scell *SetNext(struct scell *ptr,void **k,void **x,set s) {
	if(!ptr)
		ptr=s->list;
	*k=ptr->key;
	*x=ptr->item;
	ptr=ptr->next;
	return ptr; }

int Subset(set s1,set s2) {
	struct scell *cell1,*cell2,*prev2;
	
	cell1=s1->list;
	while(cell1->next && FindCell(cell1->key,cell1->item,s2,&cell2,&prev2))
		cell1=cell1->next;
	return(cell1->next)?0:1; }

int SetEqual(set s1,set s2) {
	return(Subset(s1,s2) && Subset(s2,s1))?1:0; }

set SetUnion(set s1,set s2) {
	struct scell *cell;
	set s3;
	int ok;

	s3=SetAlloc(s1->keycmp,s1->itemcmp);
	if(!s3) return 0;
	ok=1;
	for(cell=s1->list;ok && cell->next;cell=cell->next)
		ok=SetInsert(cell->key,cell->item,s3);
	for(cell=s2->list;ok && cell->next;cell=cell->next)
		ok=SetInsert(cell->key,cell->item,s3);
	if(!ok) {SetFree(s3,0,0);return 0;}
	return s3; }

set SetIntersect(set s1,set s2) {
	struct scell *cell,*cell2,*prev2;
	set s3;
	int ok;
	
	s3=SetAlloc(s1->keycmp,s1->itemcmp);
	if(!s3) return 0;
	ok=1;
	for(cell=s1->list;ok && cell->next;cell=cell->next)
		if(FindCell(cell->key,cell->item,s2,&prev2,&cell2))
			ok=SetInsert(cell->key,cell->item,s3);
	if(!ok) {SetFree(s3,0,0);return 0;}
	return s3; }

set SetDiff(set s1,set s2) {
	struct scell *cell,*cell2,*prev2;
	set s3;
	int ok;
	
	s3=SetAlloc(s1->keycmp,s1->itemcmp);
	if(!s3) return 0;
	ok=1;
	for(cell=s1->list;ok && cell->next;cell=cell->next)
		if(!FindCell(cell->key,cell->item,s2,&prev2,&cell2))
			ok=SetInsert(cell->key,cell->item,s3);
	if(!ok) {SetFree(s3,0,0);return 0;}
	return s3; }

int SetCard(set s) {
	struct scell *cell;
	int i=0;
	
	for(cell=s->list;cell->next;cell=cell->next)
		i++;
	return i; }

