diff --git a/dsf.c b/dsf.c index f1bd61f..bed564f 100644 --- a/dsf.c +++ b/dsf.c @@ -5,157 +5,301 @@ */ #include +#include #include #include "puzzles.h" +#define DSF_INDEX_MASK (UINT_MAX >> 1) +#define DSF_FLAG_CANONICAL (UINT_MAX & ~(UINT_MAX >> 1)) +#define DSF_MAX (DSF_INDEX_MASK + 1) + struct DSF { - int size; - int *p; + /* + * Size of the dsf. + */ + size_t size; + + /* + * Main array storing the data structure. + * + * If n is the canonical element of an equivalence class, + * parent_or_size[n] holds the number of elements in that class, + * bitwise-ORed with DSF_FLAG_CANONICAL. + * + * If n is not the canonical element, parent_or_size[n] holds the + * index of another element nearer to the root of the tree for + * that class. + */ + unsigned *parent_or_size; + + /* + * Extra storage for flip tracking. + * + * If n is not a canonical element, flip[n] indicates whether the + * sense of this element is flipped relative to parent_or_size[n]. + * + * If n is a canonical element, flip[n] is unused. + */ + unsigned char *flip; + + /* + * Extra storage for minimal-element tracking. + * + * If n is a canonical element, min[n] holds the index of the + * smallest value in n's equivalence class. + * + * If n is not a canonical element, min[n] is unused. + */ + unsigned *min; }; +static DSF *dsf_new_internal(int size, bool flip, bool min) +{ + DSF *dsf; + + assert(0 < size && size <= DSF_MAX && "Bad dsf size"); + + dsf = snew(DSF); + dsf->size = size; + dsf->parent_or_size = snewn(size, unsigned); + dsf->flip = flip ? snewn(size, unsigned char) : NULL; + dsf->min = min ? snewn(size, unsigned) : NULL; + + dsf_reinit(dsf); + + return dsf; +} + +DSF *dsf_new(int size) +{ + return dsf_new_internal(size, false, false); +} + +DSF *dsf_new_flip(int size) +{ + return dsf_new_internal(size, true, false); +} + +DSF *dsf_new_min(int size) +{ + return dsf_new_internal(size, false, true); +} + void dsf_reinit(DSF *dsf) { - int i; + size_t i; + /* Every element starts as the root of an equivalence class of size 1 */ for (i = 0; i < dsf->size; i++) - dsf->p[i] = 6; - /* Bottom bit of each element of this array stores whether that - * element is opposite to its parent, which starts off as - * false. Second bit of each element stores whether that element - * is the root of its tree or not. If it's not the root, the - * remaining 30 bits are the parent, otherwise the remaining 30 - * bits are the number of elements in the tree. */ + dsf->parent_or_size[i] = DSF_FLAG_CANONICAL | 1; + + /* If we're tracking minima then every element is also its own min */ + if (dsf->min) + for (i = 0; i < dsf->size; i++) + dsf->min[i] = i; + + /* No need to initialise dsf->flip, even if it exists, because + * only the entries for non-root elements are meaningful, and + * currently there are none. */ } void dsf_copy(DSF *to, DSF *from) { assert(to->size == from->size && "Mismatch in dsf_copy"); - memcpy(to->p, from->p, to->size * sizeof(int)); + memcpy(to->parent_or_size, from->parent_or_size, + to->size * sizeof(*to->parent_or_size)); + if (to->flip) { + assert(from->flip && "Copying a non-flip dsf to a flip one"); + memcpy(to->flip, from->flip, to->size * sizeof(*to->flip)); + } + if (to->min) { + assert(from->min && "Copying a non-min dsf to a min one"); + memcpy(to->min, from->min, to->size * sizeof(*to->min)); + } } -DSF *dsf_new(int size) -{ - DSF *ret = snew(DSF); - ret->size = size; - ret->p = snewn(size, int); - - dsf_reinit(ret); - - return ret; -} - -DSF *dsf_new_min(int size) { return dsf_new(size); } -DSF *dsf_new_flip(int size) { return dsf_new(size); } void dsf_free(DSF *dsf) { if (dsf) { - sfree(dsf->p); + sfree(dsf->parent_or_size); + sfree(dsf->flip); + sfree(dsf->min); sfree(dsf); } } -int dsf_canonify(DSF *dsf, int index) +static inline size_t dsf_find_root(DSF *dsf, size_t n) { - return dsf_canonify_flip(dsf, index, NULL); + while (!(dsf->parent_or_size[n] & DSF_FLAG_CANONICAL)) + n = dsf->parent_or_size[n]; + return n; } -int dsf_minimal(DSF *dsf, int index) +static inline void dsf_path_compress(DSF *dsf, size_t n, size_t root) { - return dsf_canonify_flip(dsf, index, NULL); -} - -bool dsf_equivalent(DSF *dsf, int i1, int i2) -{ - return dsf_canonify(dsf, i1) == dsf_canonify(dsf, i2); -} - -void dsf_merge(DSF *dsf, int v1, int v2) -{ - dsf_merge_flip(dsf, v1, v2, false); -} - -int dsf_size(DSF *dsf, int index) { - return dsf->p[dsf_canonify(dsf, index)] >> 2; -} - -int dsf_canonify_flip(DSF *dsf, int index, bool *inverse_return) -{ - int start_index = index, canonical_index; - bool inverse = false; - - assert(0 <= index && index < dsf->size && "Overrun in edsf_canonify"); - - /* Find the index of the canonical element of the 'equivalence class' of - * which start_index is a member, and figure out whether start_index is the - * same as or inverse to that. */ - while ((dsf->p[index] & 2) == 0) { - inverse ^= (dsf->p[index] & 1); - index = dsf->p[index] >> 2; + while (!(dsf->parent_or_size[n] & DSF_FLAG_CANONICAL)) { + size_t prev = n; + n = dsf->parent_or_size[n]; + dsf->parent_or_size[prev] = root; } - canonical_index = index; - - if (inverse_return) - *inverse_return = inverse; - - /* Update every member of this 'equivalence class' to point directly at the - * canonical member. */ - index = start_index; - while (index != canonical_index) { - int nextindex = dsf->p[index] >> 2; - bool nextinverse = inverse ^ (dsf->p[index] & 1); - dsf->p[index] = (canonical_index << 2) | inverse; - inverse = nextinverse; - index = nextindex; + assert(n == root); +} + +int dsf_canonify(DSF *dsf, int n) +{ + size_t root; + + assert(0 <= n && n < dsf->size && "Overrun in dsf_canonify"); + + root = dsf_find_root(dsf, n); + dsf_path_compress(dsf, n, root); + return root; +} + +void dsf_merge(DSF *dsf, int n1, int n2) +{ + size_t r1, r2, s1, s2, root; + + assert(0 <= n1 && n1 < dsf->size && "Overrun in dsf_merge"); + assert(0 <= n2 && n2 < dsf->size && "Overrun in dsf_merge"); + assert(!dsf->flip && "dsf_merge on a flip dsf"); + + /* Find the root elements */ + r1 = dsf_find_root(dsf, n1); + r2 = dsf_find_root(dsf, n2); + + if (r1 == r2) { + /* Classes are already the same, so we have a common root */ + root = r1; + } else { + /* Classes must be merged */ + + /* Decide which one to use as the overall root, based on size */ + s1 = dsf->parent_or_size[r1] & DSF_INDEX_MASK; + s2 = dsf->parent_or_size[r2] & DSF_INDEX_MASK; + if (s1 > s2) { + dsf->parent_or_size[r2] = root = r1; + } else { + dsf->parent_or_size[r1] = root = r2; + } + dsf->parent_or_size[root] = (s1 + s2) | DSF_FLAG_CANONICAL; + + if (dsf->min) { + /* Update the min of the merged class */ + unsigned m1 = dsf->min[r1], m2 = dsf->min[r2]; + dsf->min[root] = m1 < m2 ? m1 : m2; + } } - assert(!inverse); - - return index; + /* Path-compress both paths from n1 and n2 so they point at the new root */ + dsf_path_compress(dsf, n1, root); + dsf_path_compress(dsf, n2, root); } -void dsf_merge_flip(DSF *dsf, int v1, int v2, bool inverse) +bool dsf_equivalent(DSF *dsf, int n1, int n2) { - bool i1, i2; - - assert(0 <= v1 && v1 < dsf->size && "Overrun in edsf_merge"); - assert(0 <= v2 && v2 < dsf->size && "Overrun in edsf_merge"); - - v1 = dsf_canonify_flip(dsf, v1, &i1); - assert(dsf->p[v1] & 2); - inverse ^= i1; - v2 = dsf_canonify_flip(dsf, v2, &i2); - assert(dsf->p[v2] & 2); - inverse ^= i2; - - if (v1 == v2) - assert(!inverse); - else { - /* - * We always make the smaller of v1 and v2 the new canonical - * element. This ensures that the canonical element of any - * class in this structure is always the first element in - * it. 'Keen' depends critically on this property. - * - * (Jonas Koelker previously had this code choosing which - * way round to connect the trees by examining the sizes of - * the classes being merged, so that the root of the - * larger-sized class became the new root. This gives better - * asymptotic performance, but I've changed it to do it this - * way because I like having a deterministic canonical - * element.) - */ - if (v1 > v2) { - int v3 = v1; - v1 = v2; - v2 = v3; - } - dsf->p[v1] += (dsf->p[v2] >> 2) << 2; - dsf->p[v2] = (v1 << 2) | inverse; - } - - v2 = dsf_canonify_flip(dsf, v2, &i2); - assert(v2 == v1); - assert(i2 == inverse); + return dsf_canonify(dsf, n1) == dsf_canonify(dsf, n2); +} + +int dsf_size(DSF *dsf, int n) +{ + size_t root = dsf_canonify(dsf, n); + return dsf->parent_or_size[root] & DSF_INDEX_MASK; +} + +static inline size_t dsf_find_root_flip(DSF *dsf, size_t n, unsigned *flip) +{ + *flip = 0; + while (!(dsf->parent_or_size[n] & DSF_FLAG_CANONICAL)) { + *flip ^= dsf->flip[n]; + n = dsf->parent_or_size[n]; + } + return n; +} + +static inline void dsf_path_compress_flip(DSF *dsf, size_t n, size_t root, + unsigned flip) +{ + while (!(dsf->parent_or_size[n] & DSF_FLAG_CANONICAL)) { + size_t prev = n; + unsigned flip_prev = flip; + n = dsf->parent_or_size[n]; + flip ^= dsf->flip[prev]; + dsf->flip[prev] = flip_prev; + dsf->parent_or_size[prev] = root; + } + assert(n == root); +} + +int dsf_canonify_flip(DSF *dsf, int n, bool *inverse) +{ + size_t root; + unsigned flip; + + assert(0 <= n && n < dsf->size && "Overrun in dsf_canonify_flip"); + assert(dsf->flip && "dsf_canonify_flip on a non-flip dsf"); + + root = dsf_find_root_flip(dsf, n, &flip); + dsf_path_compress_flip(dsf, n, root, flip); + *inverse = flip; + return root; +} + +void dsf_merge_flip(DSF *dsf, int n1, int n2, bool inverse) +{ + size_t r1, r2, s1, s2, root; + unsigned f1, f2; + + assert(0 <= n1 && n1 < dsf->size && "Overrun in dsf_merge_flip"); + assert(0 <= n2 && n2 < dsf->size && "Overrun in dsf_merge_flip"); + assert(dsf->flip && "dsf_merge_flip on a non-flip dsf"); + + /* Find the root elements */ + r1 = dsf_find_root_flip(dsf, n1, &f1); + r2 = dsf_find_root_flip(dsf, n2, &f2); + + if (r1 == r2) { + /* Classes are already the same, so we have a common root */ + assert((f1 ^ f2 ^ inverse) == 0 && "Inconsistency in dsf_merge_flip"); + root = r1; + } else { + /* Classes must be merged */ + + /* Decide which one to use as the overall root, based on size */ + s1 = dsf->parent_or_size[r1] & DSF_INDEX_MASK; + s2 = dsf->parent_or_size[r2] & DSF_INDEX_MASK; + if (s1 > s2) { + dsf->parent_or_size[r2] = root = r1; + dsf->flip[r2] = f1 ^ f2 ^ inverse; + f2 ^= dsf->flip[r2]; + } else { + root = r2; + dsf->parent_or_size[r1] = root = r2; + dsf->flip[r1] = f1 ^ f2 ^ inverse; + f1 ^= dsf->flip[r1]; + } + dsf->parent_or_size[root] = (s1 + s2) | DSF_FLAG_CANONICAL; + + if (dsf->min) { + /* Update the min of the merged class */ + unsigned m1 = dsf->min[r1], m2 = dsf->min[r2]; + dsf->min[root] = m1 < m2 ? m1 : m2; + } + } + + /* Path-compress both paths from n1 and n2 so they point at the new root */ + dsf_path_compress_flip(dsf, n1, root, f1); + dsf_path_compress_flip(dsf, n2, root, f2); +} + +int dsf_minimal(DSF *dsf, int n) +{ + size_t root; + + assert(dsf->min && "dsf_minimal on a non-min dsf"); + + root = dsf_canonify(dsf, n); + return dsf->min[root]; }