/*******************************************************************************
 * Copyright (c) 2008 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * IBM Corporation - Initial API and implementation
 *******************************************************************************/

/*
 * ID set implementation. These routines implement a set abstraction using bitsets.
 */

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

#include "compat.h"
#include "bitset.h"
#include "sdm.h"

struct sdm_idset {
	bitset *	set;
	int			iterator;
};

sdm_idset
sdm_set_new(void)
{
	sdm_idset set = (sdm_idset)malloc(sizeof(struct sdm_idset));
	set->set = bitset_new(sdm_route_get_size());
	set->iterator = 0;
	return set;
}

void
sdm_set_free(sdm_idset set)
{
	bitset_free(set->set);
	free(set);
}

sdm_idset
sdm_set_clear(sdm_idset set)
{
	bitset_clear(set->set);
	return set;
}

int
sdm_set_size(const sdm_idset set)
{
	return bitset_count(set->set);
}

int
sdm_set_is_subset(const sdm_idset set1, const sdm_idset set2)
{
	return bitset_eq(set1->set, set2->set);
}

int
sdm_set_compare(const sdm_idset set1, const sdm_idset set2)
{
	return bitset_compare(set1->set, set2->set);
}

int
sdm_set_contains(const sdm_idset set, const sdm_id id)
{
	return bitset_test(set->set, id) == 1;
}

sdm_id
sdm_set_first(const sdm_idset set)
{
	set->iterator = bitset_firstset(set->set);
	return set->iterator;
}

sdm_id
sdm_set_next(const sdm_idset set)
{
	while (set->iterator < bitset_size(set->set)) {
		set->iterator++;
		if (bitset_test(set->set, set->iterator)) {
			break;
		}
	}

	return set->iterator;
}

int
sdm_set_done(const sdm_idset set)
{
	return set->iterator < 0 || set->iterator >= bitset_size(set->set);
}

int
sdm_set_is_empty(const sdm_idset set)
{
	return bitset_isempty(set->set);
}

sdm_idset
sdm_set_add_element(const sdm_idset set, const sdm_id id)
{
	bitset_set(set->set, id);
	return set;
}

sdm_idset
sdm_set_remove_element(const sdm_idset set, const sdm_id id)
{
	bitset_unset(set->set, id);
	return set;
}

sdm_idset
sdm_set_add_all(const sdm_idset set, const sdm_id id)
{
	bitset *b = bitset_new(id+1);
	bitset_invert(b);
	bitset_oreq(set->set, b);
	bitset_free(b);
	return set;
}

sdm_idset
sdm_set_union(const sdm_idset set1, const sdm_idset set2)
{
	bitset_oreq(set1->set, set2->set);
	return set1;
}

sdm_idset
sdm_set_intersect(const sdm_idset set1, const sdm_idset set2)
{
	/*
	 * If either set is empty, the result is U (the universe)
	 */
	if (bitset_isempty(set1->set) || bitset_isempty(set2->set)) {
		bitset_clear(set1->set);
		bitset_invert(set1->set);
	} else {
		bitset_andeq(set1->set, set2->set);
	}
	return set1;
}

sdm_idset
sdm_set_diff(const sdm_idset set1, const sdm_idset set2)
{
	bitset_andeqnot(set1->set, set2->set);
	return set1;
}

sdm_id
sdm_set_max(const sdm_idset set)
{
	return bitset_size(set->set) - 1;
}

int
sdm_set_serialize(const sdm_idset set, unsigned char *buf, unsigned char **end)
{
	char *	str = bitset_to_str(set->set);
	int		len = strlen(str);

	memcpy(buf, str, len);
	free(str);
	if (end != NULL) {
		*end = buf + len;
	}
	return len;
}

int
sdm_set_serialized_length(const sdm_idset set)
{
	return sizeof(int) * 2 + 1 + bitset_size(set->set) / 4;
}

int
sdm_set_deserialize(sdm_idset set, unsigned char *buf, unsigned char **end)
{
	unsigned char *	e;
	bitset *		b = bitset_decode(buf, &e);

	bitset_copy(set->set, b);
	bitset_free(b);
	if (end != NULL) {
		*end = e;
	}
	return e - buf;
}

char *
_set_to_str(sdm_idset set)
{
	char *			p;
	static int		str_count = 0;
	static char *	str[5];

	if (str_count >= 5) {
		str_count = 0;
	}
	if (str[str_count] != NULL) {
		free(str[str_count]);
	}
	p = str[str_count++] = bitset_to_set(set->set);
	return p;
}
