Result criticalCellsAreSelfLabelled(VolumeData const& candidate)
{
    CubicalComplex const& complex = candidate.complex;
    Field const& field = candidate.field;

    for (int up = 0; up <= 1; ++up)
    {
        Labels const labels = markCells(candidate, up);
        for (Cell v = 0; v < complex.cellIdLimit(); ++v)
        {
            if (complex.isCell(v) and field.isCritical(v))
            {
                if (labels.count(v) == 0 or labels.at(v).count(v) < 1)
                {
                    std::stringstream msg;
                    msg << "Critical cell " << complex.cellPosition(v)
                        << " is not in its own " << (up ? "unstable" : "stable")
                        << " set";
                    return failure(msg.str());
                }
                else if (labels.at(v).size() > 1)
                {
                    std::stringstream msg;
                    msg << "Critical cell " << complex.cellPosition(v)
                        << " is in other " << (up ? "unstable" : "stable")
                        << " sets";
                    return failure(msg.str());
                }
            }
        }
    }

    return success();
}
Result traversalsAreConsistent(VolumeData const& candidate)
{
    CubicalComplex const& complex = candidate.complex;
    Field const& field = candidate.field;

    for (int up = 0; up <= 1; ++up)
    {
        Labels const labels = markCells(candidate, up);
        Field::Vectors V = up ? field.coV() : field.V();
        Facets I(complex.xdim(), complex.ydim(), complex.zdim(), up);

        for (Cell v = 0; v < complex.cellIdLimit(); ++v)
        {
            if (not complex.isCell(v))
                continue;

            if (labels.count(v) > 0)
            {
                int const n = I.count(v);
                for (int i = 0; i < n; ++i) {
                    Cell const b = I(v, i);

                    if (not V.defined(b) or V(b) == b)
                        continue;

                    Result r = labelsPropagate(labels, v, V(b), up, complex);
                    if (!r)
                        return r;
                }
            }
        }
    }
    return success();
}
Result checkLabelCounts(
    VolumeData const& candidate,
    int const dim,
    bool const upstream,
    int const min = 1,
    int const max = 1)
{
    CubicalComplex const& complex = candidate.complex;
    Labels const labels = markCells(candidate, upstream);

    for (Cell v = 0; v < complex.cellIdLimit(); ++v)
    {
        if (complex.isCell(v) and complex.cellDimension(v) == dim)
        {
            int const count = labels.count(v) == 0 ? 0 : labels.at(v).size();

            if (count < min)
            {
                std::stringstream msg;
                msg << "Cell " << complex.cellPosition(v);
                if (min == 1)
                    msg << " has no label";
                else
                    msg << " has too few labels (" << count << ")";
                return failure(msg.str());
            }
            else if (count > max)
            {
                std::stringstream msg;
                msg << "Cell " << complex.cellPosition(v)
                    << " has too many labels (" << count << ")";
                return failure(msg.str());
            }
        }
    }

    return success();
}