2019-06-19 14:34:30 +02:00
|
|
|
|
/*
|
|
|
|
|
* Copyright © 2004 Ondra Kamenik
|
2024-01-05 18:32:18 +01:00
|
|
|
|
* Copyright © 2019-2024 Dynare Team
|
2019-06-19 14:34:30 +02:00
|
|
|
|
*
|
|
|
|
|
* This file is part of Dynare.
|
|
|
|
|
*
|
|
|
|
|
* Dynare is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* Dynare is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2021-06-09 17:33:48 +02:00
|
|
|
|
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
|
2019-06-19 14:34:30 +02:00
|
|
|
|
*/
|
2019-01-08 16:09:25 +01:00
|
|
|
|
|
|
|
|
|
#include "equivalence.hh"
|
|
|
|
|
#include "permutation.hh"
|
|
|
|
|
#include "tl_exception.hh"
|
|
|
|
|
|
2019-02-12 17:30:10 +01:00
|
|
|
|
#include <iostream>
|
2019-01-08 16:09:25 +01:00
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
OrdSequence::operator[](int i) const
|
|
|
|
|
{
|
2023-11-29 19:00:21 +01:00
|
|
|
|
TL_RAISE_IF((i < 0 || i >= length()), "Index out of range in OrdSequence::operator[]");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
return data[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Here we implement the ordering. It can be changed, or various
|
|
|
|
|
orderings can be used for different problem sizes. We order them
|
|
|
|
|
according to the average, and then according to the first item. */
|
|
|
|
|
|
2024-01-05 18:32:18 +01:00
|
|
|
|
std::partial_ordering
|
|
|
|
|
OrdSequence::operator<=>(const OrdSequence& s) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
double ta = average();
|
|
|
|
|
double sa = s.average();
|
2024-01-05 18:32:18 +01:00
|
|
|
|
if (auto cmp1 = ta <=> sa; cmp1 != 0)
|
|
|
|
|
return cmp1;
|
|
|
|
|
return operator[](0) <=> s[0];
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2023-11-29 19:00:21 +01:00
|
|
|
|
OrdSequence::operator==(const OrdSequence& s) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
if (length() != s.length())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (i < length() && operator[](i) == s[i])
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
return (i == length());
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* The first add() adds a given integer to the class, the second
|
2019-01-08 16:09:25 +01:00
|
|
|
|
iterates through a given sequence and adds everything found in the
|
|
|
|
|
given class. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OrdSequence::add(int i)
|
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto vit = data.begin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
while (vit != data.end() && *vit < i)
|
|
|
|
|
++vit;
|
|
|
|
|
if (vit != data.end() && *vit == i)
|
|
|
|
|
return;
|
|
|
|
|
data.insert(vit, i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
OrdSequence::add(const OrdSequence& s)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto vit = s.data.begin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
while (vit != s.data.end())
|
|
|
|
|
{
|
|
|
|
|
add(*vit);
|
|
|
|
|
++vit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Answers true if a given number is in the class. */
|
2019-01-08 16:09:25 +01:00
|
|
|
|
bool
|
|
|
|
|
OrdSequence::has(int i) const
|
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto vit = data.begin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
while (vit != data.end())
|
|
|
|
|
{
|
|
|
|
|
if (*vit == i)
|
|
|
|
|
return true;
|
|
|
|
|
++vit;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return an average of the class. */
|
|
|
|
|
double
|
|
|
|
|
OrdSequence::average() const
|
|
|
|
|
{
|
|
|
|
|
double res = 0;
|
2019-01-09 15:44:26 +01:00
|
|
|
|
for (int i : data)
|
|
|
|
|
res += i;
|
2023-11-29 19:00:21 +01:00
|
|
|
|
TL_RAISE_IF(data.size() == 0, "Attempt to take average of empty class in OrdSequence::average");
|
|
|
|
|
return res / data.size();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Debug print. */
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
OrdSequence::print(const std::string& prefix) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2019-02-12 17:30:10 +01:00
|
|
|
|
std::cout << prefix;
|
2019-01-09 15:44:26 +01:00
|
|
|
|
for (int i : data)
|
2019-02-12 17:30:10 +01:00
|
|
|
|
std::cout << i << ' ';
|
|
|
|
|
std::cout << '\n';
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 19:00:21 +01:00
|
|
|
|
Equivalence::Equivalence(int num) : n(num)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < num; i++)
|
|
|
|
|
{
|
|
|
|
|
OrdSequence s;
|
|
|
|
|
s.add(i);
|
|
|
|
|
classes.push_back(s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 19:00:21 +01:00
|
|
|
|
Equivalence::Equivalence(int num, [[maybe_unused]] const std::string& dummy) : n(num)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
OrdSequence s;
|
|
|
|
|
for (int i = 0; i < num; i++)
|
|
|
|
|
s.add(i);
|
|
|
|
|
classes.push_back(s);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-12 17:30:10 +01:00
|
|
|
|
/* Copy constructor that also glues a given couple. */
|
2019-01-08 16:09:25 +01:00
|
|
|
|
|
2023-11-29 19:00:21 +01:00
|
|
|
|
Equivalence::Equivalence(const Equivalence& e, int i1, int i2) : n(e.n), classes(e.classes)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto s1 = find(i1);
|
|
|
|
|
auto s2 = find(i2);
|
2019-01-08 16:09:25 +01:00
|
|
|
|
if (s1 != s2)
|
|
|
|
|
{
|
|
|
|
|
OrdSequence ns(*s1);
|
|
|
|
|
ns.add(*s2);
|
|
|
|
|
classes.erase(s1);
|
|
|
|
|
classes.erase(s2);
|
|
|
|
|
insert(ns);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2023-11-29 19:00:21 +01:00
|
|
|
|
Equivalence::operator==(const Equivalence& e) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
if (!std::operator==(classes, e.classes))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (n != e.n)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return an iterator pointing to a class having a given integer. */
|
|
|
|
|
|
|
|
|
|
Equivalence::const_seqit
|
|
|
|
|
Equivalence::findHaving(int i) const
|
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto si = classes.begin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
while (si != classes.end())
|
|
|
|
|
{
|
2019-02-12 17:30:10 +01:00
|
|
|
|
if (si->has(i))
|
2019-01-08 16:09:25 +01:00
|
|
|
|
return si;
|
|
|
|
|
++si;
|
|
|
|
|
}
|
2023-11-29 19:00:21 +01:00
|
|
|
|
TL_RAISE_IF(si == classes.end(), "Couldn't find equivalence class in Equivalence::findHaving");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
return si;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Equivalence::seqit
|
|
|
|
|
Equivalence::findHaving(int i)
|
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto si = classes.begin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
while (si != classes.end())
|
|
|
|
|
{
|
2019-02-12 17:30:10 +01:00
|
|
|
|
if (si->has(i))
|
2019-01-08 16:09:25 +01:00
|
|
|
|
return si;
|
|
|
|
|
++si;
|
|
|
|
|
}
|
2023-11-29 19:00:21 +01:00
|
|
|
|
TL_RAISE_IF(si == classes.end(), "Couldn't find equivalence class in Equivalence::findHaving");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
return si;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Find j-th class for a given j. */
|
2019-01-08 16:09:25 +01:00
|
|
|
|
|
|
|
|
|
Equivalence::const_seqit
|
|
|
|
|
Equivalence::find(int j) const
|
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto si = classes.begin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
int i = 0;
|
|
|
|
|
while (si != classes.end() && i < j)
|
|
|
|
|
{
|
|
|
|
|
++si;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
2023-11-29 19:00:21 +01:00
|
|
|
|
TL_RAISE_IF(si == classes.end(), "Couldn't find equivalence class in Equivalence::find");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
return si;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Equivalence::seqit
|
|
|
|
|
Equivalence::find(int j)
|
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto si = classes.begin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
int i = 0;
|
|
|
|
|
while (si != classes.end() && i < j)
|
|
|
|
|
{
|
|
|
|
|
++si;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
2023-11-29 19:00:21 +01:00
|
|
|
|
TL_RAISE_IF(si == classes.end(), "Couldn't find equivalence class in Equivalence::find");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
return si;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Insert a new class yielding the ordering. */
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
Equivalence::insert(const OrdSequence& s)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto si = classes.begin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
while (si != classes.end() && *si < s)
|
|
|
|
|
++si;
|
|
|
|
|
classes.insert(si, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Trace the equivalence into the integer sequence. The classes are in
|
|
|
|
|
some order (described earlier), and items within classes are ordered,
|
|
|
|
|
so this implies, that the data can be linearized. This method
|
2019-03-27 19:22:35 +01:00
|
|
|
|
“prints” them to the sequence. We allow for tracing only a given
|
2019-01-08 16:09:25 +01:00
|
|
|
|
number of classes from the beginning. */
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
Equivalence::trace(IntSequence& out, int num) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
int nc = 0;
|
2019-01-09 15:51:19 +01:00
|
|
|
|
for (auto it = begin(); it != end() && nc < num; ++it, ++nc)
|
2019-02-12 17:30:10 +01:00
|
|
|
|
for (int j = 0; j < it->length(); j++, i++)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2023-11-29 19:00:21 +01:00
|
|
|
|
TL_RAISE_IF(i >= out.size(), "Wrong size of output sequence in Equivalence::trace");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
out[i] = (*it)[j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
Equivalence::trace(IntSequence& out, const Permutation& per) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2023-11-29 19:00:21 +01:00
|
|
|
|
TL_RAISE_IF(out.size() != n, "Wrong size of output sequence in Equivalence::trace");
|
|
|
|
|
TL_RAISE_IF(per.size() != numClasses(), "Wrong permutation for permuted Equivalence::trace");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
int i = 0;
|
|
|
|
|
for (int iclass = 0; iclass < numClasses(); iclass++)
|
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto itper = find(per.getMap()[iclass]);
|
2019-02-12 17:30:10 +01:00
|
|
|
|
for (int j = 0; j < itper->length(); j++, i++)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
out[i] = (*itper)[j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Debug print. */
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
Equivalence::print(const std::string& prefix) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
2023-11-29 19:00:21 +01:00
|
|
|
|
for (auto it = classes.begin(); it != classes.end(); ++it, i++)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2019-02-12 17:30:10 +01:00
|
|
|
|
std::cout << prefix << "class " << i << ": ";
|
|
|
|
|
it->print("");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Here we construct a set of all equivalences over n-element set. The
|
|
|
|
|
construction proceeds as follows. We maintain a list of added equivalences.
|
|
|
|
|
At each iteration we pop front of the list, try to add all parents of the
|
|
|
|
|
popped equivalence. This action adds new equivalences to the object and also
|
|
|
|
|
to the added list. We finish the iterations when the added list is empty.
|
2019-01-08 16:09:25 +01:00
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
In the beginning we start with { {0}, {1}, …, {n-1} }. Adding of parents is
|
|
|
|
|
an action which for a given equivalence tries to glue all possible couples
|
|
|
|
|
and checks whether a new equivalence is already in the equivalence set. This
|
|
|
|
|
is not effective, but we will do the construction only ones.
|
2019-01-08 16:09:25 +01:00
|
|
|
|
|
|
|
|
|
In this way we breath-first search a lattice of all equivalences. Note
|
|
|
|
|
that the lattice is modular, that is why the result of a construction
|
|
|
|
|
is a list with a property that between two equivalences with the same
|
|
|
|
|
number of classes there are only equivalences with that number of
|
|
|
|
|
classes. Obviously, the list is decreasing in a number of classes
|
|
|
|
|
(since it is constructed by gluing attempts). */
|
|
|
|
|
|
2023-11-29 19:00:21 +01:00
|
|
|
|
EquivalenceSet::EquivalenceSet(int num) : n(num)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2019-02-06 15:50:01 +01:00
|
|
|
|
std::list<Equivalence> added;
|
2019-01-08 16:09:25 +01:00
|
|
|
|
Equivalence first(n);
|
|
|
|
|
equis.push_back(first);
|
|
|
|
|
addParents(first, added);
|
|
|
|
|
while (!added.empty())
|
|
|
|
|
{
|
|
|
|
|
addParents(added.front(), added);
|
|
|
|
|
added.pop_front();
|
|
|
|
|
}
|
|
|
|
|
if (n > 1)
|
2019-02-12 17:30:10 +01:00
|
|
|
|
equis.emplace_back(n, "");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* This method is used in addParents() and returns true if the object
|
2019-01-08 16:09:25 +01:00
|
|
|
|
already has that equivalence. We trace list of equivalences in reverse
|
|
|
|
|
order since equivalences are ordered in the list from the most
|
|
|
|
|
primitive (nothing equivalent) to maximal (all is equivalent). Since
|
2019-03-27 19:22:35 +01:00
|
|
|
|
we will have much more results of has() method as true, and
|
|
|
|
|
operator==() between equivalences is quick if number of classes
|
2019-01-08 16:09:25 +01:00
|
|
|
|
differ, and in time we will compare with equivalences with less
|
|
|
|
|
classes, then it is more efficient to trace the equivalences from less
|
|
|
|
|
classes to more classes. hence the reverse order. */
|
|
|
|
|
|
|
|
|
|
bool
|
2023-11-29 19:00:21 +01:00
|
|
|
|
EquivalenceSet::has(const Equivalence& e) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2019-01-09 15:51:19 +01:00
|
|
|
|
auto rit = equis.rbegin();
|
2019-01-08 16:09:25 +01:00
|
|
|
|
while (rit != equis.rend() && *rit != e)
|
|
|
|
|
++rit;
|
|
|
|
|
if (rit != equis.rend())
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Responsibility of this methods is to try to glue all possible
|
|
|
|
|
couples within a given equivalence and add those which are not in the
|
2019-03-27 19:22:35 +01:00
|
|
|
|
list yet. These are added also to the ‘added’ list.
|
2019-01-08 16:09:25 +01:00
|
|
|
|
|
|
|
|
|
If number of classes is 2 or 1, we exit, because there is nothing to
|
|
|
|
|
be added. */
|
|
|
|
|
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
EquivalenceSet::addParents(const Equivalence& e, std::list<Equivalence>& added)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
if (e.numClasses() == 2 || e.numClasses() == 1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (int i1 = 0; i1 < e.numClasses(); i1++)
|
2023-11-29 19:00:21 +01:00
|
|
|
|
for (int i2 = i1 + 1; i2 < e.numClasses(); i2++)
|
|
|
|
|
if (Equivalence ns(e, i1, i2); !has(ns))
|
2023-07-19 18:00:42 +02:00
|
|
|
|
{
|
|
|
|
|
added.push_back(ns);
|
|
|
|
|
equis.push_back(std::move(ns));
|
|
|
|
|
}
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Debug print. */
|
|
|
|
|
void
|
2023-11-29 19:00:21 +01:00
|
|
|
|
EquivalenceSet::print(const std::string& prefix) const
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
2023-11-29 19:00:21 +01:00
|
|
|
|
for (auto it = equis.begin(); it != equis.end(); ++it, i++)
|
2019-01-08 16:09:25 +01:00
|
|
|
|
{
|
2023-11-29 19:00:21 +01:00
|
|
|
|
std::cout << prefix << "equivalence " << i << ":(classes " << it->numClasses() << ")\n";
|
2019-02-12 17:30:10 +01:00
|
|
|
|
it->print(prefix + " ");
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Construct the bundle. nmax is a maximum size of underlying set. */
|
2019-01-08 16:09:25 +01:00
|
|
|
|
EquivalenceBundle::EquivalenceBundle(int nmax)
|
|
|
|
|
{
|
2019-02-06 15:50:01 +01:00
|
|
|
|
nmax = std::max(nmax, 1);
|
2019-01-08 16:09:25 +01:00
|
|
|
|
generateUpTo(nmax);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Remember, that the first item is EquivalenceSet(1). */
|
2023-11-29 19:00:21 +01:00
|
|
|
|
const EquivalenceSet&
|
2019-01-08 16:09:25 +01:00
|
|
|
|
EquivalenceBundle::get(int n) const
|
|
|
|
|
{
|
2019-02-20 18:07:07 +01:00
|
|
|
|
TL_RAISE_IF(n > static_cast<int>(bundle.size()) || n < 1,
|
|
|
|
|
"Equivalence set not found in EquivalenceBundle::get");
|
2023-11-29 19:00:21 +01:00
|
|
|
|
return bundle[n - 1];
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-27 19:22:35 +01:00
|
|
|
|
/* Get ‘curmax’ which is a maximum size in the bundle, and generate for
|
|
|
|
|
all sizes from curmax+1 up to nmax. */
|
2019-01-08 16:09:25 +01:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
EquivalenceBundle::generateUpTo(int nmax)
|
|
|
|
|
{
|
|
|
|
|
int curmax = bundle.size();
|
2023-11-29 19:00:21 +01:00
|
|
|
|
for (int i = curmax + 1; i <= nmax; i++)
|
2019-01-31 17:58:54 +01:00
|
|
|
|
bundle.emplace_back(i);
|
2019-01-08 16:09:25 +01:00
|
|
|
|
}
|