[util] Minimize set growth

At the low level, only unions can cause a set to grow. Of course, things
get interesting at the higher level when infinite (inverted) sets are
mixed in.
This commit is contained in:
Bill Currie 2021-08-10 17:10:16 +09:00
parent 37a5b475c0
commit a01cafe972
3 changed files with 92 additions and 13 deletions

View file

@ -162,6 +162,15 @@ void set_delete_r (set_pool_t *set_pool, set_t *set);
*/
void set_expand (set_t *set, unsigned size);
/** Shrink the set's backing memory to the minimum required to hold the set.
This does not affect (nor is affected by) whether the set is an infinite
set.
\param set The set to trim
*/
void set_trim (set_t *set);
/** Add an element to a set.
It is not an error to add an element that is already a member of the set.

View file

@ -128,6 +128,30 @@ set_expand (set_t *set, unsigned size)
free (map);
}
void
set_trim (set_t *set)
{
if (set->map == set->defmap) {
return;
}
unsigned words = SET_WORDS (set);
while (words > SET_DEFMAP_SIZE && !set->map[words - 1]) {
words--;
set->size -= SET_BITS;
}
if (words > SET_DEFMAP_SIZE) {
set_bits_t *map = realloc (set->map, words * sizeof (set_bits_t));
if (map && map != set->map) {
set->map = map;
}
} else {
memcpy (set->defmap, set->map, sizeof (set->defmap));
free (set->map);
set->map = set->defmap;
}
}
inline set_t *
set_new_size_r (set_pool_t *set_pool, unsigned size)
{
@ -204,13 +228,14 @@ _set_union (set_t *dst, const set_t *src)
static set_t *
_set_intersection (set_t *dst, const set_t *src)
{
unsigned size;
unsigned words;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
for (i = 0; i < SET_WORDS (src); i++)
words = min (SET_WORDS (dst), SET_WORDS (src));
for (i = 0; i < words; i++)
dst->map[i] &= src->map[i];
// if dst is larger than src, then none of the excess elements in dst
// can be in the intersection
for ( ; i < SET_WORDS (dst); i++)
dst->map[i] = 0;
return dst;
@ -219,26 +244,37 @@ _set_intersection (set_t *dst, const set_t *src)
static set_t *
_set_difference (set_t *dst, const set_t *src)
{
unsigned size;
unsigned words;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
for (i = 0; i < SET_WORDS (src); i++)
words = min (SET_WORDS (dst), SET_WORDS (src));
for (i = 0; i < words; i++)
dst->map[i] &= ~src->map[i];
// if src is larger than dst, excess elements in src cannot be in dst thus
// there is nothing to remove
// if dst is larger than src, there is nothing to remove regardless of what
// is in src
return dst;
}
static set_t *
_set_reverse_difference (set_t *dst, const set_t *src)
{
unsigned size;
unsigned words;
unsigned i;
size = max (dst->size, src->size);
set_expand (dst, size);
for (i = 0; i < SET_WORDS (src); i++)
words = min (SET_WORDS (dst), SET_WORDS (src));
set_expand (dst, src->size);
for (i = 0; i < words; i++)
dst->map[i] = ~dst->map[i] & src->map[i];
// if src is larger than dst, then dst cannot remove the excess elements
// from src and thus the src elements must be copied
for ( ; i < SET_WORDS (src); i++)
dst->map[i] = src->map[i];
// if dst is larger than src, then the excess elements in dst must be
// removed
for ( ; i < SET_WORDS (dst); i++)
dst->map[i] = 0;
return dst;
}

View file

@ -250,7 +250,30 @@ struct {
{make_not_1_2, make_0_1, set_union, check_count, 1, "{0 1 3 ...}"},
{make_not_1_2, make_0_1, set_intersection, check_count, 1, "{0}"},
{make_not_1_2, make_0_1, set_difference, check_count, 3, "{3 ...}"},
{make_not_1_2, make_0_1, set_reverse_difference, check_count, 1, "{1}"},
{make_not_1_2, make_0_1, set_reverse_difference, check_count, 1, "{1}"},//76
{make_SIZE, make_not_1_2, set_union, check_size, SIZE + SET_BITS,
"{0 3 ...}"},
{make_SIZE, make_not_1_2, set_intersection, check_size, SIZE + SET_BITS,
"{64}"},
{make_SIZE, make_not_1_2, set_difference, check_size, SIZE + SET_BITS,
"{}"},
{make_SIZE, make_not_1_2, set_reverse_difference, check_size,
SIZE + SET_BITS,
"{0 3 4 5 6 7 8 9 10 11 12 13 14 15"
" 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31"
" 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47"
" 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 65 ...}"
},//80
{make_not_1_2, make_SIZE, set_union, check_size, SIZE, "{0 3 ...}"},
{make_not_1_2, make_SIZE, set_intersection, check_size, SIZE + SET_BITS,
"{64}"},
{make_not_1_2, make_SIZE, set_difference, check_size, SIZE + SET_BITS,
"{0 3 4 5 6 7 8 9 10 11 12 13 14 15"
" 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31"
" 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47"
" 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 65 ...}"
},
{make_not_1_2, make_SIZE, set_reverse_difference, check_size, SIZE, "{}"},
};
#define num_tests (sizeof (tests) / sizeof (tests[0]))
@ -270,6 +293,8 @@ main (int argc, const char **argv)
tests[9].str_expect = tests[5].str_expect;
tests[10].str_expect = tests[5].str_expect;
tests[11].str_expect = tests[5].str_expect;
tests[78].str_expect = tests[5].str_expect;
tests[82].str_expect = tests[5].str_expect;
str = dstring_new ();
for (i = 0; i < SIZE; i++) {
@ -278,6 +303,15 @@ main (int argc, const char **argv)
dstring_appendstr (str, "}");
tests[6].str_expect = dstring_freeze (str);
str = dstring_new ();
dasprintf (str, "{0");
for (i = 3; i < SIZE; i++) {
dasprintf (str, " %zd", i);
}
dasprintf (str, " %zd ...}", SIZE + 1);
tests[80].str_expect = dstring_freeze (str);
tests[83].str_expect = tests[80].str_expect;
for (i = 0; i < num_tests; i++) {
set_t *s1, *s2 = 0;
const char *set_str;