221 lines
7.0 KiB
C++
221 lines
7.0 KiB
C++
/*
|
||
* Copyright © 2004 Ondra Kamenik
|
||
* Copyright © 2019 Dynare Team
|
||
*
|
||
* 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
|
||
* along with Dynare. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
// Integer sequence.
|
||
|
||
/* Here we define an auxiliary abstraction for a sequence of integers. The
|
||
basic functionality is to hold an ordered sequence of integers with
|
||
constant length. We prefer using this simple class rather than
|
||
std::vector<int> since it is more efficient for our purposes.
|
||
|
||
The class is used in index of a tensor, in symmetry definition, in
|
||
Kronecker product dimensions, or as a class of an equivalence. The
|
||
latter case is not ordered, but we always order equivalence classes in
|
||
order to ensure unique representativeness. For almost all cases we
|
||
need the integer sequence to be ordered (sort), or monotonize (indices
|
||
of folded tensors), or partially monotonize (indices of folded tensors
|
||
not fully symmetric), or calculate a product of all members or only of
|
||
a part (used in Kronecker product dimensions). When we calculate
|
||
offsets in folded tensors, we need to obtain a number of the same
|
||
items in the front (getPrefixLength()), and also to add some integer
|
||
number to all items.
|
||
|
||
Also, we need to construct a subsequence of a sequence. */
|
||
|
||
#ifndef INT_SEQUENCE_H
|
||
#define INT_SEQUENCE_H
|
||
|
||
#include <vector>
|
||
#include <algorithm>
|
||
#include <initializer_list>
|
||
#include <utility>
|
||
|
||
/* The implementation of IntSequence is straightforward. It has a pointer
|
||
‘data’, an ‘offset’ integer indicating the beginning of the data relatively
|
||
to the pointer and a ‘length’ of the sequence.
|
||
|
||
WARNING: IntSequence(n) and IntSequence{n} are not the same (parentheses
|
||
versus braces). The former initializes a sequence of length n, while the
|
||
latter constructs a sequence of a single element equal to n. This is similar
|
||
to the behaviour of std::vector. */
|
||
|
||
class Symmetry;
|
||
class IntSequence
|
||
{
|
||
int *data;
|
||
int length;
|
||
bool destroy{true};
|
||
public:
|
||
// Constructor allocating a given length of (uninitialized) data
|
||
explicit IntSequence(int l)
|
||
: data{new int[l]}, length{l}
|
||
{
|
||
}
|
||
// Constructor allocating and then initializing all members to a given number
|
||
IntSequence(int l, int n)
|
||
: data{new int[l]}, length{l}
|
||
{
|
||
std::fill_n(data, length, n);
|
||
}
|
||
/* Constructor using an initializer list (gives the contents of the
|
||
IntSequence, similarly to std::vector) */
|
||
IntSequence(std::initializer_list<int> init)
|
||
: data{new int[init.size()]},
|
||
length{static_cast<int>(init.size())}
|
||
{
|
||
std::copy(init.begin(), init.end(), data);
|
||
}
|
||
// Copy constructor
|
||
IntSequence(const IntSequence &s)
|
||
: data{new int[s.length]}, length{s.length}
|
||
{
|
||
std::copy_n(s.data, length, data);
|
||
}
|
||
// Move constructor
|
||
IntSequence(IntSequence &&s)
|
||
: data{std::exchange(s.data, nullptr)}, length{std::exchange(s.length, 0)},
|
||
destroy{std::exchange(s.destroy, false)}
|
||
{
|
||
}
|
||
// Subsequence constructor (which shares the data pointer)
|
||
IntSequence(IntSequence &s, int i1, int i2)
|
||
: data{s.data+i1}, length{i2-i1}, destroy{false}
|
||
{
|
||
}
|
||
// Subsequence constructor (without pointer sharing)
|
||
IntSequence(const IntSequence &s, int i1, int i2)
|
||
: data{new int[i2-i1]}, length{i2-i1}
|
||
{
|
||
std::copy_n(s.data+i1, length, data);
|
||
}
|
||
/* Unfolds a given integer sequence with respect to a given symmetry. If for
|
||
example the sequence is (a,b) and the symmetry is (2,3), then the
|
||
result is (a,a,b,b,b). */
|
||
IntSequence unfold(const Symmetry &sy) const;
|
||
|
||
/* Constructs a symmetry from the integer sequence (supposed to be ordered) as
|
||
a symmetry counting successively equal items. For instance the sequence
|
||
(a,a,a,b,c,c,d,d,d,d) produces symmetry (3,1,2,4). */
|
||
Symmetry getSymmetry() const;
|
||
|
||
IntSequence &operator=(const IntSequence &s);
|
||
IntSequence &operator=(IntSequence &&s);
|
||
virtual ~IntSequence()
|
||
{
|
||
if (destroy)
|
||
delete[] data;
|
||
}
|
||
bool operator==(const IntSequence &s) const;
|
||
bool
|
||
operator!=(const IntSequence &s) const
|
||
{
|
||
return !operator==(s);
|
||
}
|
||
int &
|
||
operator[](int i)
|
||
{
|
||
return data[i];
|
||
}
|
||
int
|
||
operator[](int i) const
|
||
{
|
||
return data[i];
|
||
}
|
||
int
|
||
size() const
|
||
{
|
||
return length;
|
||
}
|
||
|
||
/* We provide two orderings. The first operator<() is the linear
|
||
lexicographic ordering, the second less() is the non-linear Cartesian
|
||
ordering. */
|
||
bool operator<(const IntSequence &s) const;
|
||
bool
|
||
operator<=(const IntSequence &s) const
|
||
{
|
||
return (operator==(s) || operator<(s));
|
||
}
|
||
bool lessEq(const IntSequence &s) const;
|
||
bool less(const IntSequence &s) const;
|
||
|
||
// Inserts an element into an ordered sequence
|
||
IntSequence insert(int i) const;
|
||
// Inserts an element at a given position
|
||
/* For appending at the end, use pos = size() */
|
||
IntSequence insert(int i, int pos) const;
|
||
|
||
// In-place sort the sequence in increasing order
|
||
void sort();
|
||
|
||
/* Monotonize the sequence: if an item is less then its predecessor, it is
|
||
equalized. */
|
||
void monotone();
|
||
|
||
/* Partially monotonize the sequence. The partitioning is done by a
|
||
symmetry. So the subsequence given by the symmetry classes are
|
||
monotonized. For example, if the symmetry is y²u³, and the
|
||
IntSequence is (5,3,1,6,4), the result is (5,5,1,6,6). */
|
||
void pmonotone(const Symmetry &s);
|
||
|
||
|
||
// Returns the sum of all elements. Useful for symmetries
|
||
int sum() const;
|
||
|
||
/* This returns product of elements between indices i1 (included) and i2
|
||
(excluded). Useful for Kronecker product dimensions. */
|
||
int mult(int i1, int i2) const;
|
||
|
||
// Returns the product of all elements
|
||
int
|
||
mult() const
|
||
{
|
||
return mult(0, length);
|
||
}
|
||
void add(int i);
|
||
void add(int f, const IntSequence &s);
|
||
|
||
/* Return the number of identical elements at the beginning of the sequence. */
|
||
int getPrefixLength() const;
|
||
|
||
/* This returns a number of distinct items in the sequence. It supposes that
|
||
the sequence is ordered. Returns zero on the empty sequence. */
|
||
int getNumDistinct() const;
|
||
|
||
/* This returns a maximum of the sequence. If the sequence is empty, it
|
||
returns the least possible int value. */
|
||
int getMax() const;
|
||
|
||
bool isPositive() const;
|
||
bool isConstant() const;
|
||
bool isSorted() const;
|
||
|
||
void print() const;
|
||
/* ⎛sum(this)⎞
|
||
Computes multinomial coefficient: ⎝ this ⎠
|
||
(where the lower line consists of the sequence of integers stored by ‘this’)
|
||
|
||
WARNING: this operation is destructive; make a copy if you want to keep
|
||
the original sequence */
|
||
int noverseq();
|
||
};
|
||
|
||
#endif
|