/*-------------------------------------------------------------------------
 *
 * bitmapset.c
 *	  PostgreSQL generic bitmap set package
 *
 * A bitmap set can represent any set of nonnegative integers, although
 * it is mainly intended for sets where the maximum value is not large,
 * say at most a few hundred.  By convention, we always represent a set with
 * the minimum possible number of words, i.e, there are never any trailing
 * zero words.  Enforcing this requires that an empty set is represented as
 * NULL.  Because an empty Bitmapset is represented as NULL, a non-NULL
 * Bitmapset always has at least 1 Bitmapword.  We can exploit this fact to
 * speed up various loops over the Bitmapset's words array by using "do while"
 * loops instead of "for" loops.  This means the code does not waste time
 * checking the loop condition before the first iteration.  For Bitmapsets
 * containing only a single word (likely the majority of them) this halves the
 * number of loop condition checks.
 *
 * Callers must ensure that the set returned by functions in this file which
 * adjust the members of an existing set is assigned to all pointers pointing
 * to that existing set.  No guarantees are made that we'll ever modify the
 * existing set in-place and return it.
 *
 * To help find bugs caused by callers failing to record the return value of
 * the function which manipulates an existing set, we support building with
 * REALLOCATE_BITMAPSETS.  This results in the set being reallocated each time
 * the set is altered and the existing being pfreed.  This is useful as if any
 * references still exist to the old set, we're more likely to notice as
 * any users of the old set will be accessing pfree'd memory.  This option is
 * only intended to be used for debugging.
 *
 * Copyright (c) 2003-2024, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/backend/nodes/bitmapset.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "common/hashfn.h"
#include "nodes/bitmapset.h"
#include "nodes/pg_list.h"
#include "port/pg_bitutils.h"


#define WORDNUM(x)	((x) / BITS_PER_BITMAPWORD)
#define BITNUM(x)	((x) % BITS_PER_BITMAPWORD)

#define BITMAPSET_SIZE(nwords)	\
	(offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword))

/*----------
 * This is a well-known cute trick for isolating the rightmost one-bit
 * in a word.  It assumes two's complement arithmetic.  Consider any
 * nonzero value, and focus attention on the rightmost one.  The value is
 * then something like
 *				xxxxxx10000
 * where x's are unspecified bits.  The two's complement negative is formed
 * by inverting all the bits and adding one.  Inversion gives
 *				yyyyyy01111
 * where each y is the inverse of the corresponding x.  Incrementing gives
 *				yyyyyy10000
 * and then ANDing with the original value gives
 *				00000010000
 * This works for all cases except original value = zero, where of course
 * we get zero.
 *----------
 */
#define RIGHTMOST_ONE(x) ((signedbitmapword) (x) & -((signedbitmapword) (x)))

#define HAS_MULTIPLE_ONES(x)	((bitmapword) RIGHTMOST_ONE(x) != (x))

#ifdef USE_ASSERT_CHECKING
/*
 * bms_is_valid_set - for cassert builds to check for valid sets
 */
static bool
bms_is_valid_set(const Bitmapset *a)
{
	/* NULL is the correct representation of an empty set */
	if (a == NULL)
		return true;

	/* check the node tag is set correctly.  pfree'd pointer, maybe? */
	if (!IsA(a, Bitmapset))
		return false;

	/* trailing zero words are not allowed */
	if (a->words[a->nwords - 1] == 0)
		return false;

	return true;
}
#endif

#ifdef REALLOCATE_BITMAPSETS
/*
 * bms_copy_and_free
 *		Only required in REALLOCATE_BITMAPSETS builds.  Provide a simple way
 *		to return a freshly allocated set and pfree the original.
 *
 * Note: callers which accept multiple sets must be careful when calling this
 * function to clone one parameter as other parameters may point to the same
 * set.  A good option is to call this just before returning the resulting
 * set.
 */
static Bitmapset *
bms_copy_and_free(Bitmapset *a)
{
	Bitmapset  *c = bms_copy(a);

	bms_free(a);
	return c;
}
#endif

/*
 * bms_copy - make a palloc'd copy of a bitmapset
 */
Bitmapset *
bms_copy(const Bitmapset *a)
{
	Bitmapset  *result;
	size_t		size;

	Assert(bms_is_valid_set(a));

	if (a == NULL)
		return NULL;

	size = BITMAPSET_SIZE(a->nwords);
	result = (Bitmapset *) palloc(size);
	memcpy(result, a, size);
	return result;
}

/*
 * bms_equal - are two bitmapsets equal? or both NULL?
 */
bool
bms_equal(const Bitmapset *a, const Bitmapset *b)
{
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
	{
		if (b == NULL)
			return true;
		return false;
	}
	else if (b == NULL)
		return false;

	/* can't be equal if the word counts don't match */
	if (a->nwords != b->nwords)
		return false;

	/* check each word matches */
	i = 0;
	do
	{
		if (a->words[i] != b->words[i])
			return false;
	} while (++i < a->nwords);

	return true;
}

/*
 * bms_compare - qsort-style comparator for bitmapsets
 *
 * This guarantees to report values as equal iff bms_equal would say they are
 * equal.  Otherwise, the highest-numbered bit that is set in one value but
 * not the other determines the result.  (This rule means that, for example,
 * {6} is greater than {5}, which seems plausible.)
 */
int
bms_compare(const Bitmapset *a, const Bitmapset *b)
{
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
		return (b == NULL) ? 0 : -1;
	else if (b == NULL)
		return +1;

	/* the set with the most words must be greater */
	if (a->nwords != b->nwords)
		return (a->nwords > b->nwords) ? +1 : -1;

	i = a->nwords - 1;
	do
	{
		bitmapword	aw = a->words[i];
		bitmapword	bw = b->words[i];

		if (aw != bw)
			return (aw > bw) ? +1 : -1;
	} while (--i >= 0);
	return 0;
}

/*
 * bms_make_singleton - build a bitmapset containing a single member
 */
Bitmapset *
bms_make_singleton(int x)
{
	Bitmapset  *result;
	int			wordnum,
				bitnum;

	if (x < 0)
		elog(ERROR, "negative bitmapset member not allowed");
	wordnum = WORDNUM(x);
	bitnum = BITNUM(x);
	result = (Bitmapset *) palloc0(BITMAPSET_SIZE(wordnum + 1));
	result->type = T_Bitmapset;
	result->nwords = wordnum + 1;
	result->words[wordnum] = ((bitmapword) 1 << bitnum);
	return result;
}

/*
 * bms_free - free a bitmapset
 *
 * Same as pfree except for allowing NULL input
 */
void
bms_free(Bitmapset *a)
{
	if (a)
		pfree(a);
}


/*
 * bms_union - create and return a new set containing all members from both
 * input sets.  Both inputs are left unmodified.
 */
Bitmapset *
bms_union(const Bitmapset *a, const Bitmapset *b)
{
	Bitmapset  *result;
	const Bitmapset *other;
	int			otherlen;
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
		return bms_copy(b);
	if (b == NULL)
		return bms_copy(a);
	/* Identify shorter and longer input; copy the longer one */
	if (a->nwords <= b->nwords)
	{
		result = bms_copy(b);
		other = a;
	}
	else
	{
		result = bms_copy(a);
		other = b;
	}
	/* And union the shorter input into the result */
	otherlen = other->nwords;
	i = 0;
	do
	{
		result->words[i] |= other->words[i];
	} while (++i < otherlen);
	return result;
}

/*
 * bms_intersect - create and return a new set containing members which both
 * input sets have in common.  Both inputs are left unmodified.
 */
Bitmapset *
bms_intersect(const Bitmapset *a, const Bitmapset *b)
{
	Bitmapset  *result;
	const Bitmapset *other;
	int			lastnonzero;
	int			resultlen;
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL || b == NULL)
		return NULL;

	/* Identify shorter and longer input; copy the shorter one */
	if (a->nwords <= b->nwords)
	{
		result = bms_copy(a);
		other = b;
	}
	else
	{
		result = bms_copy(b);
		other = a;
	}
	/* And intersect the longer input with the result */
	resultlen = result->nwords;
	lastnonzero = -1;
	i = 0;
	do
	{
		result->words[i] &= other->words[i];

		if (result->words[i] != 0)
			lastnonzero = i;
	} while (++i < resultlen);
	/* If we computed an empty result, we must return NULL */
	if (lastnonzero == -1)
	{
		pfree(result);
		return NULL;
	}

	/* get rid of trailing zero words */
	result->nwords = lastnonzero + 1;
	return result;
}

/*
 * bms_difference - create and return a new set containing all the members of
 * 'a' without the members of 'b'.
 */
Bitmapset *
bms_difference(const Bitmapset *a, const Bitmapset *b)
{
	Bitmapset  *result;
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
		return NULL;
	if (b == NULL)
		return bms_copy(a);

	/*
	 * In Postgres' usage, an empty result is a very common case, so it's
	 * worth optimizing for that by testing bms_nonempty_difference().  This
	 * saves us a palloc/pfree cycle compared to checking after-the-fact.
	 */
	if (!bms_nonempty_difference(a, b))
		return NULL;

	/* Copy the left input */
	result = bms_copy(a);

	/* And remove b's bits from result */
	if (result->nwords > b->nwords)
	{
		/*
		 * We'll never need to remove trailing zero words when 'a' has more
		 * words than 'b' as the additional words must be non-zero.
		 */
		i = 0;
		do
		{
			result->words[i] &= ~b->words[i];
		} while (++i < b->nwords);
	}
	else
	{
		int			lastnonzero = -1;

		/* we may need to remove trailing zero words from the result. */
		i = 0;
		do
		{
			result->words[i] &= ~b->words[i];

			/* remember the last non-zero word */
			if (result->words[i] != 0)
				lastnonzero = i;
		} while (++i < result->nwords);

		/* trim off trailing zero words */
		result->nwords = lastnonzero + 1;
	}
	Assert(result->nwords != 0);

	/* Need not check for empty result, since we handled that case above */
	return result;
}

/*
 * bms_is_subset - is A a subset of B?
 */
bool
bms_is_subset(const Bitmapset *a, const Bitmapset *b)
{
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
		return true;			/* empty set is a subset of anything */
	if (b == NULL)
		return false;

	/* 'a' can't be a subset of 'b' if it contains more words */
	if (a->nwords > b->nwords)
		return false;

	/* Check all 'a' members are set in 'b' */
	i = 0;
	do
	{
		if ((a->words[i] & ~b->words[i]) != 0)
			return false;
	} while (++i < a->nwords);
	return true;
}

/*
 * bms_subset_compare - compare A and B for equality/subset relationships
 *
 * This is more efficient than testing bms_is_subset in both directions.
 */
BMS_Comparison
bms_subset_compare(const Bitmapset *a, const Bitmapset *b)
{
	BMS_Comparison result;
	int			shortlen;
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
	{
		if (b == NULL)
			return BMS_EQUAL;
		return BMS_SUBSET1;
	}
	if (b == NULL)
		return BMS_SUBSET2;

	/* Check common words */
	result = BMS_EQUAL;			/* status so far */
	shortlen = Min(a->nwords, b->nwords);
	i = 0;
	do
	{
		bitmapword	aword = a->words[i];
		bitmapword	bword = b->words[i];

		if ((aword & ~bword) != 0)
		{
			/* a is not a subset of b */
			if (result == BMS_SUBSET1)
				return BMS_DIFFERENT;
			result = BMS_SUBSET2;
		}
		if ((bword & ~aword) != 0)
		{
			/* b is not a subset of a */
			if (result == BMS_SUBSET2)
				return BMS_DIFFERENT;
			result = BMS_SUBSET1;
		}
	} while (++i < shortlen);
	/* Check extra words */
	if (a->nwords > b->nwords)
	{
		/* if a has more words then a is not a subset of b */
		if (result == BMS_SUBSET1)
			return BMS_DIFFERENT;
		return BMS_SUBSET2;
	}
	else if (a->nwords < b->nwords)
	{
		/* if b has more words then b is not a subset of a */
		if (result == BMS_SUBSET2)
			return BMS_DIFFERENT;
		return BMS_SUBSET1;
	}
	return result;
}

/*
 * bms_is_member - is X a member of A?
 */
bool
bms_is_member(int x, const Bitmapset *a)
{
	int			wordnum,
				bitnum;

	Assert(bms_is_valid_set(a));

	/* XXX better to just return false for x<0 ? */
	if (x < 0)
		elog(ERROR, "negative bitmapset member not allowed");
	if (a == NULL)
		return false;

	wordnum = WORDNUM(x);
	bitnum = BITNUM(x);
	if (wordnum >= a->nwords)
		return false;
	if ((a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0)
		return true;
	return false;
}

/*
 * bms_member_index
 *		determine 0-based index of member x in the bitmap
 *
 * Returns (-1) when x is not a member.
 */
int
bms_member_index(Bitmapset *a, int x)
{
	int			i;
	int			bitnum;
	int			wordnum;
	int			result = 0;
	bitmapword	mask;

	Assert(bms_is_valid_set(a));

	/* return -1 if not a member of the bitmap */
	if (!bms_is_member(x, a))
		return -1;

	wordnum = WORDNUM(x);
	bitnum = BITNUM(x);

	/* count bits in preceding words */
	for (i = 0; i < wordnum; i++)
	{
		bitmapword	w = a->words[i];

		/* No need to count the bits in a zero word */
		if (w != 0)
			result += bmw_popcount(w);
	}

	/*
	 * Now add bits of the last word, but only those before the item. We can
	 * do that by applying a mask and then using popcount again. To get
	 * 0-based index, we want to count only preceding bits, not the item
	 * itself, so we subtract 1.
	 */
	mask = ((bitmapword) 1 << bitnum) - 1;
	result += bmw_popcount(a->words[wordnum] & mask);

	return result;
}

/*
 * bms_overlap - do sets overlap (ie, have a nonempty intersection)?
 */
bool
bms_overlap(const Bitmapset *a, const Bitmapset *b)
{
	int			shortlen;
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL || b == NULL)
		return false;
	/* Check words in common */
	shortlen = Min(a->nwords, b->nwords);
	i = 0;
	do
	{
		if ((a->words[i] & b->words[i]) != 0)
			return true;
	} while (++i < shortlen);
	return false;
}

/*
 * bms_overlap_list - does a set overlap an integer list?
 */
bool
bms_overlap_list(const Bitmapset *a, const List *b)
{
	ListCell   *lc;
	int			wordnum,
				bitnum;

	Assert(bms_is_valid_set(a));

	if (a == NULL || b == NIL)
		return false;

	foreach(lc, b)
	{
		int			x = lfirst_int(lc);

		if (x < 0)
			elog(ERROR, "negative bitmapset member not allowed");
		wordnum = WORDNUM(x);
		bitnum = BITNUM(x);
		if (wordnum < a->nwords)
			if ((a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0)
				return true;
	}

	return false;
}

/*
 * bms_nonempty_difference - do sets have a nonempty difference?
 *
 * i.e., are any members set in 'a' that are not also set in 'b'.
 */
bool
bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
{
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
		return false;
	if (b == NULL)
		return true;
	/* if 'a' has more words then it must contain additional members */
	if (a->nwords > b->nwords)
		return true;
	/* Check all 'a' members are set in 'b' */
	i = 0;
	do
	{
		if ((a->words[i] & ~b->words[i]) != 0)
			return true;
	} while (++i < a->nwords);
	return false;
}

/*
 * bms_singleton_member - return the sole integer member of set
 *
 * Raises error if |a| is not 1.
 */
int
bms_singleton_member(const Bitmapset *a)
{
	int			result = -1;
	int			nwords;
	int			wordnum;

	Assert(bms_is_valid_set(a));

	if (a == NULL)
		elog(ERROR, "bitmapset is empty");

	nwords = a->nwords;
	wordnum = 0;
	do
	{
		bitmapword	w = a->words[wordnum];

		if (w != 0)
		{
			if (result >= 0 || HAS_MULTIPLE_ONES(w))
				elog(ERROR, "bitmapset has multiple members");
			result = wordnum * BITS_PER_BITMAPWORD;
			result += bmw_rightmost_one_pos(w);
		}
	} while (++wordnum < nwords);

	/* we don't expect non-NULL sets to be empty */
	Assert(result >= 0);
	return result;
}

/*
 * bms_get_singleton_member
 *
 * Test whether the given set is a singleton.
 * If so, set *member to the value of its sole member, and return true.
 * If not, return false, without changing *member.
 *
 * This is more convenient and faster than calling bms_membership() and then
 * bms_singleton_member(), if we don't care about distinguishing empty sets
 * from multiple-member sets.
 */
bool
bms_get_singleton_member(const Bitmapset *a, int *member)
{
	int			result = -1;
	int			nwords;
	int			wordnum;

	Assert(bms_is_valid_set(a));

	if (a == NULL)
		return false;

	nwords = a->nwords;
	wordnum = 0;
	do
	{
		bitmapword	w = a->words[wordnum];

		if (w != 0)
		{
			if (result >= 0 || HAS_MULTIPLE_ONES(w))
				return false;
			result = wordnum * BITS_PER_BITMAPWORD;
			result += bmw_rightmost_one_pos(w);
		}
	} while (++wordnum < nwords);

	/* we don't expect non-NULL sets to be empty */
	Assert(result >= 0);
	*member = result;
	return true;
}

/*
 * bms_num_members - count members of set
 */
int
bms_num_members(const Bitmapset *a)
{
	int			result = 0;
	int			nwords;
	int			wordnum;

	Assert(bms_is_valid_set(a));

	if (a == NULL)
		return 0;

	nwords = a->nwords;
	wordnum = 0;
	do
	{
		bitmapword	w = a->words[wordnum];

		/* No need to count the bits in a zero word */
		if (w != 0)
			result += bmw_popcount(w);
	} while (++wordnum < nwords);
	return result;
}

/*
 * bms_membership - does a set have zero, one, or multiple members?
 *
 * This is faster than making an exact count with bms_num_members().
 */
BMS_Membership
bms_membership(const Bitmapset *a)
{
	BMS_Membership result = BMS_EMPTY_SET;
	int			nwords;
	int			wordnum;

	Assert(bms_is_valid_set(a));

	if (a == NULL)
		return BMS_EMPTY_SET;

	nwords = a->nwords;
	wordnum = 0;
	do
	{
		bitmapword	w = a->words[wordnum];

		if (w != 0)
		{
			if (result != BMS_EMPTY_SET || HAS_MULTIPLE_ONES(w))
				return BMS_MULTIPLE;
			result = BMS_SINGLETON;
		}
	} while (++wordnum < nwords);
	return result;
}


/*
 * bms_add_member - add a specified member to set
 *
 * 'a' is recycled when possible.
 */
Bitmapset *
bms_add_member(Bitmapset *a, int x)
{
	int			wordnum,
				bitnum;

	Assert(bms_is_valid_set(a));

	if (x < 0)
		elog(ERROR, "negative bitmapset member not allowed");
	if (a == NULL)
		return bms_make_singleton(x);

	wordnum = WORDNUM(x);
	bitnum = BITNUM(x);

	/* enlarge the set if necessary */
	if (wordnum >= a->nwords)
	{
		int			oldnwords = a->nwords;
		int			i;

		a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(wordnum + 1));
		a->nwords = wordnum + 1;
		/* zero out the enlarged portion */
		i = oldnwords;
		do
		{
			a->words[i] = 0;
		} while (++i < a->nwords);
	}

	a->words[wordnum] |= ((bitmapword) 1 << bitnum);

#ifdef REALLOCATE_BITMAPSETS

	/*
	 * There's no guarantee that the repalloc returned a new pointer, so copy
	 * and free unconditionally here.
	 */
	a = bms_copy_and_free(a);
#endif

	return a;
}

/*
 * bms_del_member - remove a specified member from set
 *
 * No error if x is not currently a member of set
 *
 * 'a' is recycled when possible.
 */
Bitmapset *
bms_del_member(Bitmapset *a, int x)
{
	int			wordnum,
				bitnum;

	Assert(bms_is_valid_set(a));

	if (x < 0)
		elog(ERROR, "negative bitmapset member not allowed");
	if (a == NULL)
		return NULL;

	wordnum = WORDNUM(x);
	bitnum = BITNUM(x);

#ifdef REALLOCATE_BITMAPSETS
	a = bms_copy_and_free(a);
#endif

	/* member can't exist.  Return 'a' unmodified */
	if (unlikely(wordnum >= a->nwords))
		return a;

	a->words[wordnum] &= ~((bitmapword) 1 << bitnum);

	/* when last word becomes empty, trim off all trailing empty words */
	if (a->words[wordnum] == 0 && wordnum == a->nwords - 1)
	{
		/* find the last non-empty word and make that the new final word */
		for (int i = wordnum - 1; i >= 0; i--)
		{
			if (a->words[i] != 0)
			{
				a->nwords = i + 1;
				return a;
			}
		}

		/* the set is now empty */
		pfree(a);
		return NULL;
	}
	return a;
}

/*
 * bms_add_members - like bms_union, but left input is recycled when possible
 */
Bitmapset *
bms_add_members(Bitmapset *a, const Bitmapset *b)
{
	Bitmapset  *result;
	const Bitmapset *other;
	int			otherlen;
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
		return bms_copy(b);
	if (b == NULL)
	{
#ifdef REALLOCATE_BITMAPSETS
		a = bms_copy_and_free(a);
#endif

		return a;
	}
	/* Identify shorter and longer input; copy the longer one if needed */
	if (a->nwords < b->nwords)
	{
		result = bms_copy(b);
		other = a;
	}
	else
	{
		result = a;
		other = b;
	}
	/* And union the shorter input into the result */
	otherlen = other->nwords;
	i = 0;
	do
	{
		result->words[i] |= other->words[i];
	} while (++i < otherlen);
	if (result != a)
		pfree(a);
#ifdef REALLOCATE_BITMAPSETS
	else
		result = bms_copy_and_free(result);
#endif

	return result;
}

/*
 * bms_replace_members
 *		Remove all existing members from 'a' and repopulate the set with members
 *		from 'b', recycling 'a', when possible.
 */
Bitmapset *
bms_replace_members(Bitmapset *a, const Bitmapset *b)
{
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	if (a == NULL)
		return bms_copy(b);
	if (b == NULL)
	{
		pfree(a);
		return NULL;
	}

	if (a->nwords < b->nwords)
		a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(b->nwords));

	i = 0;
	do
	{
		a->words[i] = b->words[i];
	} while (++i < b->nwords);

	a->nwords = b->nwords;

#ifdef REALLOCATE_BITMAPSETS

	/*
	 * There's no guarantee that the repalloc returned a new pointer, so copy
	 * and free unconditionally here.
	 */
	a = bms_copy_and_free(a);
#endif

	return a;
}

/*
 * bms_add_range
 *		Add members in the range of 'lower' to 'upper' to the set.
 *
 * Note this could also be done by calling bms_add_member in a loop, however,
 * using this function will be faster when the range is large as we work at
 * the bitmapword level rather than at bit level.
 */
Bitmapset *
bms_add_range(Bitmapset *a, int lower, int upper)
{
	int			lwordnum,
				lbitnum,
				uwordnum,
				ushiftbits,
				wordnum;

	Assert(bms_is_valid_set(a));

	/* do nothing if nothing is called for, without further checking */
	if (upper < lower)
	{
#ifdef REALLOCATE_BITMAPSETS
		a = bms_copy_and_free(a);
#endif

		return a;
	}

	if (lower < 0)
		elog(ERROR, "negative bitmapset member not allowed");
	uwordnum = WORDNUM(upper);

	if (a == NULL)
	{
		a = (Bitmapset *) palloc0(BITMAPSET_SIZE(uwordnum + 1));
		a->type = T_Bitmapset;
		a->nwords = uwordnum + 1;
	}
	else if (uwordnum >= a->nwords)
	{
		int			oldnwords = a->nwords;
		int			i;

		/* ensure we have enough words to store the upper bit */
		a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(uwordnum + 1));
		a->nwords = uwordnum + 1;
		/* zero out the enlarged portion */
		i = oldnwords;
		do
		{
			a->words[i] = 0;
		} while (++i < a->nwords);
	}

	wordnum = lwordnum = WORDNUM(lower);

	lbitnum = BITNUM(lower);
	ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(upper) + 1);

	/*
	 * Special case when lwordnum is the same as uwordnum we must perform the
	 * upper and lower masking on the word.
	 */
	if (lwordnum == uwordnum)
	{
		a->words[lwordnum] |= ~(bitmapword) (((bitmapword) 1 << lbitnum) - 1)
			& (~(bitmapword) 0) >> ushiftbits;
	}
	else
	{
		/* turn on lbitnum and all bits left of it */
		a->words[wordnum++] |= ~(bitmapword) (((bitmapword) 1 << lbitnum) - 1);

		/* turn on all bits for any intermediate words */
		while (wordnum < uwordnum)
			a->words[wordnum++] = ~(bitmapword) 0;

		/* turn on upper's bit and all bits right of it. */
		a->words[uwordnum] |= (~(bitmapword) 0) >> ushiftbits;
	}

#ifdef REALLOCATE_BITMAPSETS

	/*
	 * There's no guarantee that the repalloc returned a new pointer, so copy
	 * and free unconditionally here.
	 */
	a = bms_copy_and_free(a);
#endif

	return a;
}

/*
 * bms_int_members - like bms_intersect, but left input is recycled when
 * possible
 */
Bitmapset *
bms_int_members(Bitmapset *a, const Bitmapset *b)
{
	int			lastnonzero;
	int			shortlen;
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
		return NULL;
	if (b == NULL)
	{
		pfree(a);
		return NULL;
	}

	/* Intersect b into a; we need never copy */
	shortlen = Min(a->nwords, b->nwords);
	lastnonzero = -1;
	i = 0;
	do
	{
		a->words[i] &= b->words[i];

		if (a->words[i] != 0)
			lastnonzero = i;
	} while (++i < shortlen);

	/* If we computed an empty result, we must return NULL */
	if (lastnonzero == -1)
	{
		pfree(a);
		return NULL;
	}

	/* get rid of trailing zero words */
	a->nwords = lastnonzero + 1;

#ifdef REALLOCATE_BITMAPSETS
	a = bms_copy_and_free(a);
#endif

	return a;
}

/*
 * bms_del_members - delete members in 'a' that are set in 'b'.  'a' is
 * recycled when possible.
 */
Bitmapset *
bms_del_members(Bitmapset *a, const Bitmapset *b)
{
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
		return NULL;
	if (b == NULL)
	{
#ifdef REALLOCATE_BITMAPSETS
		a = bms_copy_and_free(a);
#endif

		return a;
	}

	/* Remove b's bits from a; we need never copy */
	if (a->nwords > b->nwords)
	{
		/*
		 * We'll never need to remove trailing zero words when 'a' has more
		 * words than 'b'.
		 */
		i = 0;
		do
		{
			a->words[i] &= ~b->words[i];
		} while (++i < b->nwords);
	}
	else
	{
		int			lastnonzero = -1;

		/* we may need to remove trailing zero words from the result. */
		i = 0;
		do
		{
			a->words[i] &= ~b->words[i];

			/* remember the last non-zero word */
			if (a->words[i] != 0)
				lastnonzero = i;
		} while (++i < a->nwords);

		/* check if 'a' has become empty */
		if (lastnonzero == -1)
		{
			pfree(a);
			return NULL;
		}

		/* trim off any trailing zero words */
		a->nwords = lastnonzero + 1;
	}

#ifdef REALLOCATE_BITMAPSETS
	a = bms_copy_and_free(a);
#endif

	return a;
}

/*
 * bms_join - like bms_union, but *either* input *may* be recycled
 */
Bitmapset *
bms_join(Bitmapset *a, Bitmapset *b)
{
	Bitmapset  *result;
	Bitmapset  *other;
	int			otherlen;
	int			i;

	Assert(bms_is_valid_set(a));
	Assert(bms_is_valid_set(b));

	/* Handle cases where either input is NULL */
	if (a == NULL)
	{
#ifdef REALLOCATE_BITMAPSETS
		b = bms_copy_and_free(b);
#endif

		return b;
	}
	if (b == NULL)
	{
#ifdef REALLOCATE_BITMAPSETS
		a = bms_copy_and_free(a);
#endif

		return a;
	}

	/* Identify shorter and longer input; use longer one as result */
	if (a->nwords < b->nwords)
	{
		result = b;
		other = a;
	}
	else
	{
		result = a;
		other = b;
	}
	/* And union the shorter input into the result */
	otherlen = other->nwords;
	i = 0;
	do
	{
		result->words[i] |= other->words[i];
	} while (++i < otherlen);
	if (other != result)		/* pure paranoia */
		pfree(other);

#ifdef REALLOCATE_BITMAPSETS
	result = bms_copy_and_free(result);
#endif

	return result;
}

/*
 * bms_next_member - find next member of a set
 *
 * Returns smallest member greater than "prevbit", or -2 if there is none.
 * "prevbit" must NOT be less than -1, or the behavior is unpredictable.
 *
 * This is intended as support for iterating through the members of a set.
 * The typical pattern is
 *
 *			x = -1;
 *			while ((x = bms_next_member(inputset, x)) >= 0)
 *				process member x;
 *
 * Notice that when there are no more members, we return -2, not -1 as you
 * might expect.  The rationale for that is to allow distinguishing the
 * loop-not-started state (x == -1) from the loop-completed state (x == -2).
 * It makes no difference in simple loop usage, but complex iteration logic
 * might need such an ability.
 */
int
bms_next_member(const Bitmapset *a, int prevbit)
{
	int			nwords;
	int			wordnum;
	bitmapword	mask;

	Assert(bms_is_valid_set(a));

	if (a == NULL)
		return -2;
	nwords = a->nwords;
	prevbit++;
	mask = (~(bitmapword) 0) << BITNUM(prevbit);
	for (wordnum = WORDNUM(prevbit); wordnum < nwords; wordnum++)
	{
		bitmapword	w = a->words[wordnum];

		/* ignore bits before prevbit */
		w &= mask;

		if (w != 0)
		{
			int			result;

			result = wordnum * BITS_PER_BITMAPWORD;
			result += bmw_rightmost_one_pos(w);
			return result;
		}

		/* in subsequent words, consider all bits */
		mask = (~(bitmapword) 0);
	}
	return -2;
}

/*
 * bms_prev_member - find prev member of a set
 *
 * Returns largest member less than "prevbit", or -2 if there is none.
 * "prevbit" must NOT be more than one above the highest possible bit that can
 * be set at the Bitmapset at its current size.
 *
 * To ease finding the highest set bit for the initial loop, the special
 * prevbit value of -1 can be passed to have the function find the highest
 * valued member in the set.
 *
 * This is intended as support for iterating through the members of a set in
 * reverse.  The typical pattern is
 *
 *			x = -1;
 *			while ((x = bms_prev_member(inputset, x)) >= 0)
 *				process member x;
 *
 * Notice that when there are no more members, we return -2, not -1 as you
 * might expect.  The rationale for that is to allow distinguishing the
 * loop-not-started state (x == -1) from the loop-completed state (x == -2).
 * It makes no difference in simple loop usage, but complex iteration logic
 * might need such an ability.
 */

int
bms_prev_member(const Bitmapset *a, int prevbit)
{
	int			wordnum;
	int			ushiftbits;
	bitmapword	mask;

	Assert(bms_is_valid_set(a));

	/*
	 * If set is NULL or if there are no more bits to the right then we've
	 * nothing to do.
	 */
	if (a == NULL || prevbit == 0)
		return -2;

	/* transform -1 to the highest possible bit we could have set */
	if (prevbit == -1)
		prevbit = a->nwords * BITS_PER_BITMAPWORD - 1;
	else
		prevbit--;

	ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(prevbit) + 1);
	mask = (~(bitmapword) 0) >> ushiftbits;
	for (wordnum = WORDNUM(prevbit); wordnum >= 0; wordnum--)
	{
		bitmapword	w = a->words[wordnum];

		/* mask out bits left of prevbit */
		w &= mask;

		if (w != 0)
		{
			int			result;

			result = wordnum * BITS_PER_BITMAPWORD;
			result += bmw_leftmost_one_pos(w);
			return result;
		}

		/* in subsequent words, consider all bits */
		mask = (~(bitmapword) 0);
	}
	return -2;
}

/*
 * bms_hash_value - compute a hash key for a Bitmapset
 */
uint32
bms_hash_value(const Bitmapset *a)
{
	Assert(bms_is_valid_set(a));

	if (a == NULL)
		return 0;				/* All empty sets hash to 0 */
	return DatumGetUInt32(hash_any((const unsigned char *) a->words,
								   a->nwords * sizeof(bitmapword)));
}

/*
 * bitmap_hash - hash function for keys that are (pointers to) Bitmapsets
 *
 * Note: don't forget to specify bitmap_match as the match function!
 */
uint32
bitmap_hash(const void *key, Size keysize)
{
	Assert(keysize == sizeof(Bitmapset *));
	return bms_hash_value(*((const Bitmapset *const *) key));
}

/*
 * bitmap_match - match function to use with bitmap_hash
 */
int
bitmap_match(const void *key1, const void *key2, Size keysize)
{
	Assert(keysize == sizeof(Bitmapset *));
	return !bms_equal(*((const Bitmapset *const *) key1),
					  *((const Bitmapset *const *) key2));
}
