ref: 9f08986cf1b5dd58fc618e54ba45b85e7ba10cc9
parent: 68d242c5875ec1133429c656520b2d173c05e387
author: Simon Tatham <anakin@pobox.com>
date: Thu Apr 20 11:13:06 EDT 2023
Update the documentation for the dsf functions. Easier to do this all in one go after I've finished messing about.
--- a/devel.but
+++ b/devel.but
@@ -4488,15 +4488,16 @@
updates the structure so that that work won't be needed a second time.
Use dsf operations without worrying about how long they take!
-These functions also support an \q{extended} version of a dsf (spelled-\q{edsf}), in which each equivalence class is itself partitioned into-two sub-classes. When you merge two elements, you say whether they're
-in the same class or in opposite classes; when you test equivalence,
-you can find out whether the two elements you're asking about are in
-the same or opposite classes. For example, in a puzzle containing
-black and white squares, you might use an edsf to track the solver's
-knowledge about whether each pair of squares were (a) the same colour;
-(b) opposite colours; (c) currently not known to be related.
+For some puzzle-game applications, it's useful to augment this data
+structure with extra information about how the elements of an
+equivalence class relate to each other. There's more than one way you
+might do this; the one supported here is useful in cases where the
+objects you're tracking are going to end up in one of two states (say,
+black/white, or on/off), and for any two objects you either know that
+they're in the same one of those states, or you know they're in
+opposite states, or you don't know which yet. Puzzles calls this a
+\q{flip dsf}: it tracks whether objects in the same equivalence class+are flipped relative to each other.
As well as querying whether two elements are equivalent, this dsf
implementation also allows you to ask for the number of elements in a
@@ -4504,41 +4505,67 @@
latter is used, for example, to decide which square to print the clue
in each region of a Keen puzzle.)
-\S{utils-dsf-new} \cw{snew_dsf()}+\S{utils-dsf-new} \cw{dsf_new()}, \cw{dsf_new_flip()}, \cw{dsf_new_min()}-\c int *snew_dsf(int size);
+\c DSF *dsf_new(int size);
+\c DSF *dsf_new_flip(int size);
+\c DSF *dsf_new_min(int size);
-Allocates space for a dsf describing \c{size} elements, and-initialises it so that every element is in an equivalence class by
-itself.
+Each of these functions allocates space for a dsf describing \c{size}+elements, and initialises it so that every element is in an
+equivalence class by itself.
The elements described by the dsf are represented by the integers from
\cw{0} to \cw{size-1} inclusive.-The returned memory is a single allocation, so you can free it easily
-using \cw{sfree()}.+\cw{dsf_new_flip()} will create a dsf which has the extra ability to+track whether objects in the same equivalence class are flipped
+relative to each other.
-Dsfs and edsfs are represented in the same way, so this function can
-be used to allocate either kind.
+\cw{dsf_new_min()} will create a dsf which has the extra ability to+track the smallest element of each equivalence class.
-\S{utils-dsf-init} \cw{dsf_init()}+The returned object from any of these functions must be freed using
+\cw{dsf_free()}.-\c void dsf_init(int *dsf, int size);
+\S{utils-dsf-free} \cw{dsf_free()}+\c void dsf_free(DSF *dsf);
+
+Frees a dsf allocated by any of the \cw{dsf_new()} functions.+
+\S{utils-dsf-reinit} \cw{dsf_reinit()}+
+\c void dsf_reinit(DSF *dsf);
+
Reinitialises an existing dsf to the state in which all elements are
distinct, without having to free and reallocate it.
+\S{utils-dsf-copy} \cw{dsf_copy()}+
+\c void dsf_copy(DSF *to, DSF *from);
+
+Copies the contents of one dsf over the top of another. Everything
+previously stored in \c{to} is overwritten.+
+The two dsfs must have been created with the same size, and the
+destination dsf may not have any extra information that the source dsf
+does not have.
+
\S{utils-dsf-merge} \cw{dsf_merge()}-\c void dsf_merge(int *dsf, int v1, int v2);
+\c void dsf_merge(DSF *dsf, int v1, int v2);
Updates a dsf so that elements \c{v1} and \c{v2} will now beconsidered to be in the same equivalence class. If they were already
in the same class, this function will safely do nothing.
+This function may not be called on a flip dsf. Use \cw{dsf_merge_flip}+instead.
+
\S{utils-dsf-canonify} \cw{dsf_canonify()}-\c int dsf_canonify(int *dsf, int val);
+\c int dsf_canonify(DSF *dsf, int val);
Returns the \q{canonical} element of the equivalence class in the dsf containing \c{val}. This will be some element of the same equivalence@@ -4550,12 +4577,9 @@
mutated via \c{dsf_merge}. But between two calls to \c{dsf_merge},they stay the same.
-In this implementation, the canonical element is always the element
-with smallest index in the equivalence class.
-
\S{utils-dsf-size} \cw{dsf_size()}-\c int dsf_size(int *dsf, int val);
+\c int dsf_size(DSF *dsf, int val);
Returns the number of elements currently in the equivalence class
containing \c{val}.@@ -4563,59 +4587,71 @@
\c{val} itself counts, so in a newly created dsf, the return valuewill be 1.
-\S{utils-edsf-merge} \cw{edsf_merge()}+\S{utils-dsf-merge-flip} \cw{dsf_merge_flip()}-\c void edsf_merge(int *dsf, int v1, int v2, bool inverse);
+\c void edsf_merge(DSF *dsf, int v1, int v2, bool flip);
-Updates an edsf so that elements \c{v1} and \c{v2} are in the same-equivalence class. If \c{inverse} is \cw{false}, they will be regarded-as also being in the same subclass of that class; if \c{inverse} is-\cw{true} then they will be regarded as being in opposite subclasses.+Updates a flip dsf so that elements \c{v1} and \c{v2} are in the same+equivalence class. If \c{flip} is \cw{false}, they will be regarded as+in the same state as each other; if \c{flip} is \cw{true} then they+will be regarded as being in opposite states.
If \c{v1} and \c{v2} were already in the same equivalence class, then-the new value of \c{inverse} will be checked against what the edsf+the new value of \c{flip} will be checked against what the edsfpreviously believed, and an assertion failure will occur if you
contradict that.
-For example, if you start from a blank edsf and do this:
+For example, if you start from a blank flip dsf and do this:
-\c edsf_merge(dsf, 0, 1, false);
-\c edsf_merge(dsf, 1, 2, true);
+\c dsf_merge_flip(dsf, 0, 1, false);
+\c dsf_merge_flip(dsf, 1, 2, true);
then it will create a dsf in which elements 0,1,2 are all in the same
-class, with one subclasses containing 0,1 and the other containing 2.
-And then this call will do nothing, because it agrees with what the
-edsf already knew:
+class, with 0,1 in the same state as each other and 2 in the opposite
+state from both. And then this call will do nothing, because it agrees
+with what the dsf already knew:
-\c edsf_merge(dsf, 0, 2, true);
+\c dsf_merge_flip(dsf, 0, 2, true);
But this call will fail an assertion:
-\c edsf_merge(dsf, 0, 2, false);
+\c dsf_merge_flip(dsf, 0, 2, false);
-\S{utils-edsf-canonify} \cw{edsf_canonify()}+\S{utils-dsf-canonify-flip} \cw{dsf_canonify_flip()}-\c int edsf_canonify(int *dsf, int val, bool *inverse);
+\c int dsf_canonify_flip(DSF *dsf, int val, bool *inverse);
Like \c{dsf_canonify()}, this returns the canonical element of the-equivalence class of an edsf containing \c{val}. It also fills in-\c{*inverse} with a flag indicating whether \c{val} and the canonical-element are in opposite subclasses: \cw{true} if they are in opposite-subclasses, or \cw{false} if they're in the same subclass.+equivalence class of a dsf containing \c{val}.+However, it may only be called on a flip dsf, and it also fills in
+\c{*flip} with a flag indicating whether \c{val} and the canonical+element are in opposite states: \cw{true} if they are in opposite+states, or \cw{false} if they're in the same state.+
So if you want to know the relationship between \c{v1} and \c{v2}, youcan do this:
\c bool inv1, inv2;
-\c int canon1 = edsf_canonify(dsf, v1, &inv1);
-\c int canon2 = edsf_canonify(dsf, v2, &inv2);
+\c int canon1 = dsf_canonify_flip(dsf, v1, &inv1);
+\c int canon2 = dsf_canonify_flip(dsf, v2, &inv2);
\c if (canon1 != canon2) {\c // v1 and v2 have no known relation
\c } else if (inv1 == inv2) {-\c // v1 and v2 are in the same subclass of the same class
+\c // v1 and v2 are known to be in the same state as each other
\c } else {-\c // v1 and v2 are in opposite subclasses of the same class
+\c // v1 and v2 are known to be in opposite states
\c }
+
+\S{utils-dsf-minimal} \cw{dsf_minimal()}+
+\c int dsf_minimal(DSF *dsf, int val);
+
+Returns the smallest element of the equivalence class in the dsf
+containing \c{val}.+
+For this function to work, the dsf must have been created using
+\cw{dsf_new_min()}. \H{utils-tdq} To-do queues--
⑨