Files
puzzles/dsf.c
Simon Tatham 14e1e05510 Introduce a new dsf_equivalent() function.
Not very interesting, but the idiom for checking equivalence via two
calls to dsf_canonify is cumbersome enough to be worth abbreviating.
2023-04-20 18:39:35 +01:00

154 lines
3.8 KiB
C

/*
* dsf.c: some functions to handle a disjoint set forest,
* which is a data structure useful in any solver which has to
* worry about avoiding closed loops.
*/
#include <assert.h>
#include <string.h>
#include "puzzles.h"
struct DSF {
int size;
int *p;
};
void dsf_reinit(DSF *dsf)
{
int i;
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. */
}
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));
}
DSF *snew_dsf(int size)
{
DSF *ret = snew(DSF);
ret->size = size;
ret->p = snewn(size, int);
dsf_reinit(ret);
return ret;
}
void dsf_free(DSF *dsf)
{
if (dsf) {
sfree(dsf->p);
sfree(dsf);
}
}
int dsf_canonify(DSF *dsf, int index)
{
return edsf_canonify(dsf, index, NULL);
}
bool dsf_equivalent(DSF *dsf, int i1, int i2)
{
return edsf_canonify(dsf, i1, NULL) == edsf_canonify(dsf, i2, NULL);
}
void dsf_merge(DSF *dsf, int v1, int v2)
{
edsf_merge(dsf, v1, v2, false);
}
int dsf_size(DSF *dsf, int index) {
return dsf->p[dsf_canonify(dsf, index)] >> 2;
}
int edsf_canonify(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;
}
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(!inverse);
return index;
}
void edsf_merge(DSF *dsf, int v1, int v2, bool inverse)
{
bool i1, i2;
assert(0 <= v1 && v1 < dsf->size && "Overrun in edsf_merge");
assert(0 <= v2 && v2 < dsf->size && "Overrun in edsf_merge");
v1 = edsf_canonify(dsf, v1, &i1);
assert(dsf->p[v1] & 2);
inverse ^= i1;
v2 = edsf_canonify(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 = edsf_canonify(dsf, v2, &i2);
assert(v2 == v1);
assert(i2 == inverse);
}