Add support for set of everything, and a lot of docs.

Set of everything is implemented by inverting the meaning of bits in the
bitmap: 1 becomes non-member, 0 member. This means that set_size and
set_first/set_next become inverted and represent non-members as counting
members becomes impossible :)
This commit is contained in:
Bill Currie 2012-12-06 19:32:31 +09:00
parent b6ae9867c2
commit b28ac6672b
2 changed files with 515 additions and 34 deletions

View file

@ -36,43 +36,286 @@
*/
//@{
/** Represent a set using a bitmap.
When \a inverted is zero, ones in the bitmap represent members, but when
\a inverted is non-zero, zeros in the bitmap represent members. However,
this really is all private implementation details and it is best to treat
set_t as a black box.
*/
typedef struct set_s {
struct set_s *next; //< private. for ALLOC
unsigned *map;
unsigned size;
unsigned defmap[8];
struct set_s *next; ///< private. for ALLOC
int inverted; ///< if true, 0 indicates membership
unsigned *map; ///< bitmap of set members
unsigned size; ///< number of representable members
unsigned defmap[8]; ///< backing store for small sets
} set_t;
/** Represent the state of a scan through a set.
Very useful in for-loops:
\code
set_t *set;
set_iter_t *iter;
create_and_populate (set);
for (iter = set_first (set); iter; iter = set_next (iter))
do_something (iter->member);
\endcode
*/
typedef struct set_iter_s {
struct set_iter_s *next; //< private. for ALLOC
const set_t *set;
struct set_iter_s *next; ///< private. for ALLOC
const set_t *set; ///< the set to which this iterator belongs
/** The result of set_first() or set_next(). set_next() will start at the
following member.
\note For inverted sets, indicates a non-member.
*/
unsigned member;
} set_iter_t;
void set_del_iter (set_iter_t *set_iter);
set_t *set_new (void);
void set_delete (set_t *set);
void set_add (set_t *set, unsigned x);
void set_remove (set_t *set, unsigned x);
/** Delete a set iterator that is no longer needed.
\param set_iter The set iterator to be deleted.
*/
void set_del_iter (set_iter_t *set_iter);
/** Create a new set.
The set is initialized to be the empty set.
\return The newly created, empty set.
*/
set_t *set_new (void);
/** Delete a set that is no longer needed.
\param set The set to be deleted.
*/
void set_delete (set_t *set);
/** Add a value to a set.
It is not an error to add a value that is already a member of the set.
\note \a set is modified.
\param set The set to which the value will be added.
\param x The value to be added.
\return The modified set.
*/
set_t *set_add (set_t *set, unsigned x);
/** Remove a value from a set.
It is not an error to remove a value that is not a member of the set.
\note \a set is modified.
\param set The set from which the value will be removed.
\param x The value to be removed.
\return The modified set.
*/
set_t *set_remove (set_t *set, unsigned x);
/** Compute the inverse of a set.
The computation is done as \a set = ~\a set.
\note \a set is modified.
\param set The set to be inverted.
\return The set modified to be ~\a src.
*/
set_t *set_invert (set_t *set);
/** Compute the union of two sets.
The computation is done as \a dst = \a dst | \a src.
\note \a dst is modified.
\param dst The destination set to be modified.
\param src The source set.
\return The destination set modified to be \a dst | \a src.
*/
set_t *set_union (set_t *dst, const set_t *src);
/** Compute the intersection of two sets.
The computation is done as \a dst = \a dst & \a src.
\note \a dst is modified.
\param dst The destination set to be modified.
\param src The source set.
\return The destination set modified to be \a dst & \a src.
*/
set_t *set_intersection (set_t *dst, const set_t *src);
/** Compute the diffedrence of two sets.
The computation is done as \a dst = \a dst - \a src.
\note \a dst is modified.
\param dst The destination set to be modified.
\param src The source set.
\return The destination set modified to be \a dst - \a src.
*/
set_t *set_difference (set_t *dst, const set_t *src);
/** Compute the diffedrence of two sets.
The computation is done as \a dst = \a src - \a dst.
\note \a dst is modified.
\param dst The destination set to be modified.
\param src The source set.
\return The destination set modified to be \a src - \a dst.
*/
set_t *set_reverse_difference (set_t *dst, const set_t *src);
/** Make a set equivalent to another.
\note \a dst is modified.
\param dst The destination set to make equivalent.
\param src The source set to assign to \a dst.
\return The modified destination set.
*/
set_t *set_assign (set_t *dst, const set_t *src);
/** Make a set the empty set.
\note \a set is modified.
\param set The set to make the empty set.
\return \a set.
*/
set_t *set_empty (set_t *set);
/** Make a set the set of everything.
\note \a set is modified.
\param set The set to make the set of everything.
\return \a set.
*/
set_t *set_everything (set_t *set);
/** Test if a set is the set of everything.
\param set The set to test.
\return 1 if \a set is empty (non-inverted).
*/
int set_is_empty (const set_t *set);
/** Test if a set is the set of everything.
\param set The set to test.
\return 1 if \a set is the set of everything (empty inverted set).
*/
int set_is_everything (const set_t *set);
/** Test if two sets are disjoint.
\param s1 The first set to test.
\param s2 The second set to test.
\return 1 if \a s2 is disjoint from \a s1, 0 if not.
*/
int set_is_disjoint (const set_t *s1, const set_t *s2);
/** Test if two sets intersect.
\param s1 The first set to test.
\param s2 The second set to test.
\return 1 if \a s2 intersects \a s1, 0 if not.
*/
int set_is_intersecting (const set_t *s1, const set_t *s2);
/** Test if two sets are equivalent.
\param s1 The first set to test.
\param s2 The second set to test.
\return 1 if \a s2 is equivalent to \a s1, 0 if not.
*/
int set_is_equivalent (const set_t *s1, const set_t *s2);
/** Test if a set is a subset of another set.
An equivalent set is considered to be a subset.
\param set The potential super-set.
\param sub The potential subset.
\return 1 if \a sub is a subset of \a set, or if the sets are
equivalent.
*/
int set_is_subset (const set_t *set, const set_t *sub);
/** Test a value for membership in a set.
\param set The set to test.
\param x The value to test.
\return 1 if the value is a member of the set, otherwise 0.
*/
int set_is_member (const set_t *set, unsigned x);
/** Obtain the number of members (or non-members) of a set.
Normal sets return the number of members, inverted sets return the number
of non-members.
\param set The set from which the number of (non-)members will be
obtained.
\return The number of (non-)members. Both empty sets and sets of
evertything will return 0.
*/
unsigned set_size (const set_t *set);
/** Find the first "member" of the set.
\warning For normal sets, the set iterator represents a member of the
set, but for inverted sets, the set iterator represetns a
<em>non</em>-member of the set.
\param set The set to scan.
\return A pointer to a set iterator indicating the first
(non-)member of the set, or null if the set is empty or
of everything.
*/
set_iter_t *set_first (const set_t *set);
/** Find the next "member" of the set.
\warning For normal sets, the set iterator represents a member of the
set, but for inverted sets, the set iterator represetns a
<em>non</em>-member of the set.
\param set_iter The set iterator representing the state of the current
scan.
\return A pointer to a set iterator indicating the next
(non-)member of the set, or null if no mor (non-)members
are available.
\note The set iterator is automatically deleted when the end of the set
is reached.
*/
set_iter_t *set_next (set_iter_t *set_iter);
/** Return a human-readable string representing the set.
Empty sets will be represented by the string "[empty]". Sets of everything
will be represented by the string "[everything]". Inverted sets will have
the first implicit member followed by "..." (eg, "256 ...").
\param set The set to be converted to a string.
\return The human readable representation of the string.
\warning The string is kept in a static variable, so subsequent calls
will overwrite the results of preceeding calls.
*/
const char *set_as_string (const set_t *set);
//@}

View file

@ -108,16 +108,16 @@ set_expand (set_t *set, unsigned x)
free (map);
}
void
set_add (set_t *set, unsigned x)
static inline void
_set_add (set_t *set, unsigned x)
{
if (x >= set->size)
set_expand (set, x);
set->map[x / BITS] |= 1 << (x % BITS);
}
void
set_remove (set_t *set, unsigned x)
static inline void
_set_remove (set_t *set, unsigned x)
{
if (x >= set->size)
return;
@ -125,7 +125,34 @@ set_remove (set_t *set, unsigned x)
}
set_t *
set_union (set_t *dst, const set_t *src)
set_add (set_t *set, unsigned x)
{
if (set->inverted)
_set_remove (set, x);
else
_set_add (set, x);
return set;
}
set_t *
set_remove (set_t *set, unsigned x)
{
if (set->inverted)
_set_add (set, x);
else
_set_remove (set, x);
return set;
}
set_t *
set_invert (set_t *set)
{
set->inverted = !set->inverted;
return set;
}
static set_t *
_set_union (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
@ -137,8 +164,8 @@ set_union (set_t *dst, const set_t *src)
return dst;
}
set_t *
set_intersection (set_t *dst, const set_t *src)
static set_t *
_set_intersection (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
@ -152,8 +179,8 @@ set_intersection (set_t *dst, const set_t *src)
return dst;
}
set_t *
set_difference (set_t *dst, const set_t *src)
static set_t *
_set_difference (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
@ -165,6 +192,81 @@ set_difference (set_t *dst, const set_t *src)
return dst;
}
static set_t *
_set_reverse_difference (set_t *dst, const set_t *src)
{
unsigned size;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
for (i = 0; i < src->size / BITS; i++)
dst->map[i] = ~dst->map[i] & src->map[i];
return dst;
}
set_t *
set_union (set_t *dst, const set_t *src)
{
if (dst->inverted && src->inverted) {
return _set_intersection (dst, src);
} else if (src->inverted) {
dst->inverted = 1;
return _set_difference (dst, src);
} else if (dst->inverted) {
return _set_reverse_difference (dst, src);
} else {
return _set_union (dst, src);
}
}
set_t *
set_intersection (set_t *dst, const set_t *src)
{
if (dst->inverted && src->inverted) {
return _set_union (dst, src);
} else if (src->inverted) {
return _set_difference (dst, src);
} else if (dst->inverted) {
dst->inverted = 0;
return _set_reverse_difference (dst, src);
} else {
return _set_intersection (dst, src);
}
}
set_t *
set_difference (set_t *dst, const set_t *src)
{
if (dst->inverted && src->inverted) {
dst->inverted = 0;
return _set_reverse_difference (dst, src);
} else if (src->inverted) {
return _set_intersection (dst, src);
} else if (dst->inverted) {
return _set_union (dst, src);
} else {
return _set_difference (dst, src);
}
}
set_t *
set_reverse_difference (set_t *dst, const set_t *src)
{
if (dst->inverted && src->inverted) {
dst->inverted = 0;
return _set_difference (dst, src);
} else if (src->inverted) {
dst->inverted = 1;
return _set_union (dst, src);
} else if (dst->inverted) {
dst->inverted = 0;
return _set_intersection (dst, src);
} else {
return _set_reverse_difference (dst, src);
}
}
set_t *
set_assign (set_t *dst, const set_t *src)
{
@ -173,6 +275,7 @@ set_assign (set_t *dst, const set_t *src)
size = max (dst->size, src->size);
set_expand (dst, size);
dst->inverted = src->inverted;
for (i = 0; i < src->size / BITS; i++)
dst->map[i] = src->map[i];
for ( ; i < dst->size / BITS; i++)
@ -185,13 +288,25 @@ set_empty (set_t *set)
{
unsigned i;
set->inverted = 0;
for (i = 0; i < set->size / BITS; i++)
set->map[i] = 0;
return set;
}
int
set_is_empty (const set_t *set)
set_t *
set_everything (set_t *set)
{
unsigned i;
set->inverted = 1;
for (i = 0; i < set->size / BITS; i++)
set->map[i] = 0;
return set;
}
static inline int
_set_is_empty (const set_t *set)
{
unsigned i;
@ -201,6 +316,22 @@ set_is_empty (const set_t *set)
return 1;
}
int
set_is_empty (const set_t *set)
{
if (set->inverted)
return 0;
return _set_is_empty (set);
}
int
set_is_everything (const set_t *set)
{
if (!set->inverted)
return 0;
return _set_is_empty (set);
}
typedef enum {
set_equiv,
set_intersecting,
@ -208,7 +339,7 @@ typedef enum {
} set_test_e;
static int
set_test (const set_t *s1, const set_t *s2)
set_test_intersect_n_n (const set_t *s1, const set_t *s2)
{
unsigned i, end;
set_test_e rval = set_equiv;
@ -222,11 +353,88 @@ set_test (const set_t *s1, const set_t *s2)
rval = set_disjoint;
}
}
return rval;
}
static int
set_test_intersect_n_i (const set_t *s1, const set_t *s2)
{
unsigned i, end;
set_test_e rval = set_equiv;
end = min (s1->size, s2->size) / BITS;
for (i = 0; i < end; i++) {
if (s1->map[i] != ~s2->map[i]) {
if (s1->map[i] & ~s2->map[i])
return set_intersecting;
else
rval = set_disjoint;
}
}
return rval;
}
static int
set_test_intersect_i_n (const set_t *s1, const set_t *s2)
{
unsigned i, end;
set_test_e rval = set_equiv;
end = min (s1->size, s2->size) / BITS;
for (i = 0; i < end; i++) {
if (~s1->map[i] != s2->map[i]) {
if (~s1->map[i] & s2->map[i])
return set_intersecting;
else
rval = set_disjoint;
}
}
return rval;
}
static int
set_test_intersect_i_i (const set_t *s1, const set_t *s2)
{
unsigned i, end;
set_test_e rval = set_equiv;
end = min (s1->size, s2->size) / BITS;
for (i = 0; i < end; i++) {
if (~s1->map[i] != ~s2->map[i]) {
if (~s1->map[i] & ~s2->map[i])
return set_intersecting;
else
rval = set_disjoint;
}
}
return rval;
}
static int
set_test (const set_t *s1, const set_t *s2)
{
unsigned i, end;
set_test_e rval = set_equiv;
if (s1->inverted && s2->inverted) {
rval = set_test_intersect_i_i (s1, s2);
} else if (s2->inverted) {
rval = set_test_intersect_n_i (s1, s2);
if (rval == set_equiv)
rval = set_disjoint;
} else if (s1->inverted) {
rval = set_test_intersect_i_n (s1, s2);
if (rval == set_equiv)
rval = set_disjoint;
} else {
rval = set_test_intersect_n_n (s1, s2);
}
if (rval == set_equiv) {
for (; i < s1->size / BITS; i++)
end = min (s1->size, s2->size) / BITS;
for (i = end; i < s1->size / BITS; i++)
if (s1->map[i])
return set_disjoint;
for (; i < s2->size / BITS; i++)
for (i = end; i < s2->size / BITS; i++)
if (s2->map[i])
return set_disjoint;
}
@ -257,13 +465,31 @@ set_is_subset (const set_t *set, const set_t *sub)
unsigned i, end;
end = min (set->size, sub->size) / BITS;
for (i = 0; i < end; i++) {
if (sub->map[i] & ~set->map[i])
return 0;
if (set->inverted && sub->inverted) {
for (i = 0; i < end; i++) {
if (~sub->map[i] & set->map[i])
return 0;
}
for ( ; i < set->size / BITS; i++)
if (set->map[i])
return 0;
} else if (set->inverted) {
for (i = 0; i < end; i++) {
if (sub->map[i] & set->map[i])
return 0;
}
} else if (sub->inverted) {
// an inverted set cannot be a subset of a set that is not inverted
return 0;
} else {
for (i = 0; i < end; i++) {
if (sub->map[i] & ~set->map[i])
return 0;
}
for ( ; i < sub->size / BITS; i++)
if (sub->map[i])
return 0;
}
for (i = 0; i < sub->size / BITS; i++)
if (sub->map[i])
return 0;
return 1;
}
@ -278,6 +504,8 @@ _set_is_member (const set_t *set, unsigned x)
int
set_is_member (const set_t *set, unsigned x)
{
if (set->inverted)
return !_set_is_member (set, x);
return _set_is_member (set, x);
}
@ -288,7 +516,7 @@ set_size (const set_t *set)
unsigned i;
for (i = 0; i < set->size; i++)
if (set_is_member (set, i))
if (_set_is_member (set, i))
count++;
return count;
}
@ -300,7 +528,7 @@ set_first (const set_t *set)
set_iter_t *set_iter;
for (x = 0; x < set->size; x++) {
if (set_is_member (set, x)) {
if (_set_is_member (set, x)) {
set_iter = new_setiter ();
set_iter->set = set;
set_iter->member = x;
@ -316,7 +544,7 @@ set_next (set_iter_t *set_iter)
unsigned x;
for (x = set_iter->member + 1; x < set_iter->set->size; x++) {
if (set_is_member (set_iter->set, x)) {
if (_set_is_member (set_iter->set, x)) {
set_iter->member = x;
return set_iter;
}
@ -334,6 +562,14 @@ set_as_string (const set_t *set)
if (!str)
str = dstring_new ();
dstring_clearstr (str);
if (set_is_empty (set)) {
dstring_copystr (str, "[empty]");
return str->str;
}
if (set_is_everything (set)) {
dstring_copystr (str, "[everythign]");
return str->str;
}
for (i = 0; i < set->size; i++) {
if (set_is_member (set, i)) {
if (str->str[0])
@ -342,5 +578,7 @@ set_as_string (const set_t *set)
dsprintf (str, "%d", i);
}
}
if (set->inverted)
dasprintf (str, "%s%d ...", str->str[0] ? " " : "", i);
return str->str;
}