z3-z3-4.13.0.src.util.vector.h Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2006 Microsoft Corporation
Module Name:
vector.h
Abstract:
Dynamic array implementation.
Remarks:
- Empty arrays consume only sizeof(T *) bytes.
- There is the option of disabling the destructor invocation for elements stored in the vector.
This is useful for vectors of int.
Author:
Leonardo de Moura (leonardo) 2006-09-11.
Daniel Schemmel 2019-2-23
Revision History:
--*/
#pragma once
#include "util/debug.h"
#include
#include
#include
#include
#include
#include "util/memory_manager.h"
#include "util/hash.h"
#include "util/z3_exception.h"
#include
// disable warning for constant 'if' expressions.
// these are used heavily in templates.
#ifdef _MSC_VER
#pragma warning(disable:4127)
#endif
template
using std_vector = std::vector>;
#if 0
// portability guide to std::vector.
// memory allocator should be based on memory_allocator
//
// template
// struct memory_allocator {
// typedef T value_type;
// etc (interface seems to change between C++17, 20 versions)
// };
//
// Note:
// polynomial.h contains declaration
// typedef svector numeral_vector;
// it is crucial that it uses svector and not vector. The destructors on elements of the numeral vector are handled outside.
// Numeral gets instantiated by mpz and mpz does not support copy constructors.
// porting svector to vector is therefore blocked on the semantics of svector being
// copy-constructor free.
//
#include
template
class vector : public std::vector {
public:
typedef T data_t;
typedef typename std::vector::iterator iterator;
vector() {}
vector(SZ s) {
// TODO resize(s, T());
}
vector(SZ s, T const& e) {
// TODO resize(s, e);
}
vector(SZ s, T const* e) {
// TODO
}
void reset() { clear(); }
void finalize() { clear(); }
void reserve(SZ s, T const & d) {
if (s > size())
resize(s, d);
}
void reserve(SZ s) {
}
void setx(SZ idx, T const & elem, T const & d) {
if (idx >= size())
resize(idx+1, d);
(*this)[idx] = elem;
}
T const & get(SZ idx, T const & d) const {
if (idx >= size()) {
return d;
}
return (*this)[idx];
}
void insert(T const & elem) {
push_back(elem);
}
void erase(iterator pos) {
// TODO
}
void erase(T const& e) {
// TODO
}
void fill(T const & elem) {
for (auto& e : *this)
e = elem;
}
void fill(unsigned sz, T const & elem) {
resize(sz);
fill(elem);
}
void shrink(SZ s) {
resize(s);
}
void reverse() {
SZ sz = size();
for (SZ i = 0; i < sz/2; ++i) {
std::swap((*this)[i], (*this)[sz-i-1]);
}
}
void append(vector const & other) {
for(SZ i = 0; i < other.size(); ++i) {
push_back(other[i]);
}
}
void append(unsigned n, T const* elems) {
// TODO
}
bool contains(T const & elem) const {
for (auto const& e : *this)
if (e == elem)
return true;
return false;
}
};
#else
template
class vector {
#define SIZE_IDX -1
#define CAPACITY_IDX -2
T * m_data = nullptr;
void destroy_elements() {
std::destroy_n(m_data, size());
}
void free_memory() {
memory::deallocate(reinterpret_cast(reinterpret_cast(m_data) - 2));
}
void expand_vector() {
// ensure that the data is sufficiently aligned
// better fail to compile than produce code that may crash
static_assert((sizeof(SZ) * 2) % alignof(T) == 0);
if (m_data == nullptr) {
SZ capacity = 2;
SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2));
*mem = capacity;
mem++;
*mem = 0;
mem++;
m_data = reinterpret_cast(mem);
}
else {
static_assert(std::is_nothrow_move_constructible::value);
SASSERT(capacity() > 0);
SZ old_capacity = reinterpret_cast(m_data)[CAPACITY_IDX];
SZ old_capacity_T = sizeof(T) * old_capacity + sizeof(SZ) * 2;
SZ new_capacity = (3 * old_capacity + 1) >> 1;
SZ new_capacity_T = sizeof(T) * new_capacity + sizeof(SZ) * 2;
if (new_capacity <= old_capacity || new_capacity_T <= old_capacity_T) {
throw default_exception("Overflow encountered when expanding vector");
}
SZ *mem, *old_mem = reinterpret_cast(m_data) - 2;
if (std::is_trivially_copyable::value) {
mem = (SZ*)memory::reallocate(old_mem, new_capacity_T);
m_data = reinterpret_cast(mem + 2);
} else {
mem = (SZ*)memory::allocate(new_capacity_T);
auto old_size = size();
mem[1] = old_size;
auto new_data = reinterpret_cast(mem + 2);
std::uninitialized_move_n(m_data, old_size, new_data);
destroy();
m_data = new_data;
}
*mem = new_capacity;
}
}
void copy_core(vector const & source) {
SZ size = source.size();
SZ capacity = source.capacity();
SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2));
*mem = capacity;
mem++;
*mem = size;
mem++;
m_data = reinterpret_cast(mem);
std::uninitialized_copy(source.begin(), source.end(), begin());
}
void destroy() {
if (m_data) {
if (CallDestructors) {
destroy_elements();
}
free_memory();
}
}
public:
typedef T data_t;
typedef T * iterator;
typedef const T * const_iterator;
vector() = default;
vector(SZ s) {
init(s);
}
void init(SZ s) {
SASSERT(m_data == nullptr);
if (s == 0) {
return;
}
SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * s + sizeof(SZ) * 2));
*mem = s;
mem++;
*mem = s;
mem++;
m_data = reinterpret_cast(mem);
// initialize elements
iterator it = begin();
iterator e = end();
for (; it != e; ++it) {
new (it) T();
}
}
vector(SZ s, T const & elem) {
resize(s, elem);
}
vector(vector const & source) {
if (source.m_data) {
copy_core(source);
}
SASSERT(size() == source.size());
}
vector(vector&& other) noexcept {
std::swap(m_data, other.m_data);
}
vector(SZ s, T const * data) {
for (SZ i = 0; i < s; i++) {
push_back(data[i]);
}
}
~vector() {
destroy();
}
void finalize() {
destroy();
m_data = nullptr;
}
bool operator==(vector const & other) const {
if (this == &other) {
return true;
}
if (size() != other.size())
return false;
for (unsigned i = 0; i < size(); i++) {
if ((*this)[i] != other[i])
return false;
}
return true;
}
bool operator!=(vector const & other) const {
return !(*this == other);
}
vector & operator=(vector const & source) {
if (this == &source) {
return *this;
}
destroy();
if (source.m_data) {
copy_core(source);
}
else {
m_data = nullptr;
}
return *this;
}
vector & operator=(vector && source) noexcept {
if (this == &source) {
return *this;
}
destroy();
m_data = nullptr;
std::swap(m_data, source.m_data);
return *this;
}
bool containsp(std::function& predicate) const {
for (auto const& t : *this)
if (predicate(t))
return true;
return false;
}
/**
* retain elements that satisfy predicate. aka 'where'.
*/
vector filter_pure(std::function& predicate) const {
vector result;
for (auto& t : *this)
if (predicate(t))
result.push_back(t);
return result;
}
vector& filter_update(std::function& predicate) {
unsigned j = 0;
for (auto& t : *this)
if (predicate(t))
set(j++, t);
shrink(j);
return *this;
}
/**
* update elements using f, aka 'select'
*/
template
vector map_pure(std::function& f) const {
vector result;
for (auto& t : *this)
result.push_back(f(t));
return result;
}
vector& map_update(std::function& f) {
unsigned j = 0;
for (auto& t : *this)
set(j++, f(t));
return *this;
}
void reset() {
if (m_data) {
if (CallDestructors) {
destroy_elements();
}
reinterpret_cast(m_data)[SIZE_IDX] = 0;
}
}
void clear() { reset(); }
bool empty() const {
return m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == 0;
}
SZ size() const {
if (m_data == nullptr) {
return 0;
}
return reinterpret_cast(m_data)[SIZE_IDX];
}
SZ capacity() const {
if (m_data == nullptr) {
return 0;
}
return reinterpret_cast(m_data)[CAPACITY_IDX];
}
iterator begin() {
return m_data;
}
iterator end() {
return m_data + size();
}
const_iterator begin() const {
return m_data;
}
const_iterator end() const {
return m_data + size();
}
class reverse_iterator {
T* v;
public:
reverse_iterator(T* v):v(v) {}
T operator*() { return *v; }
reverse_iterator operator++(int) {
reverse_iterator tmp = *this;
--v;
return tmp;
}
reverse_iterator& operator++() {
--v;
return *this;
}
bool operator==(reverse_iterator const& other) const {
return other.v == v;
}
bool operator!=(reverse_iterator const& other) const {
return other.v != v;
}
};
reverse_iterator rbegin() { return reverse_iterator(end() - 1); }
reverse_iterator rend() { return reverse_iterator(begin() - 1); }
void set_end(iterator it) {
if (m_data) {
SZ new_sz = static_cast(it - m_data);
if (CallDestructors) {
iterator e = end();
for(; it != e; ++it) {
it->~T();
}
}
reinterpret_cast(m_data)[SIZE_IDX] = new_sz;
}
else {
SASSERT(it == 0);
}
}
T & operator[](SZ idx) {
SASSERT(idx < size());
return m_data[idx];
}
T const & operator[](SZ idx) const {
SASSERT(idx < size());
return m_data[idx];
}
T & get(SZ idx) {
SASSERT(idx < size());
return m_data[idx];
}
T const & get(SZ idx) const {
SASSERT(idx < size());
return m_data[idx];
}
void set(SZ idx, T const & val) {
SASSERT(idx < size());
m_data[idx] = val;
}
void set(SZ idx, T && val) {
SASSERT(idx < size());
m_data[idx] = std::move(val);
}
T & back() {
SASSERT(!empty());
return operator[](size() - 1);
}
T const & back() const {
SASSERT(!empty());
return operator[](size() - 1);
}
void pop_back() {
SASSERT(!empty());
if (CallDestructors) {
back().~T();
}
reinterpret_cast(m_data)[SIZE_IDX]--;
}
vector& push_back(T const & elem) {
if (m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) {
expand_vector();
}
new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(elem);
reinterpret_cast(m_data)[SIZE_IDX]++;
return *this;
}
template
vector& push_back(T const& elem, T elem2, Args ... elems) {
push_back(elem);
push_back(elem2, elems ...);
return *this;
}
vector& push_back(T && elem) {
if (m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) {
expand_vector();
}
new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(std::move(elem));
reinterpret_cast(m_data)[SIZE_IDX]++;
return *this;
}
void insert(T const & elem) {
push_back(elem);
}
void erase(iterator pos) {
SASSERT(pos >= begin() && pos < end());
iterator prev = pos;
++pos;
iterator e = end();
for(; pos != e; ++pos, ++prev) {
*prev = std::move(*pos);
}
pop_back();
}
void erase(T const & elem) {
iterator it = std::find(begin(), end(), elem);
if (it != end()) {
erase(it);
}
}
/** Erase all elements that satisfy the given predicate. Returns the number of erased elements. */
template
SZ erase_if(UnaryPredicate should_erase) {
iterator i = begin();
iterator const e = end();
for (iterator j = begin(); j != e; ++j)
if (!should_erase(std::as_const(*j)))
*(i++) = std::move(*j);
SZ const count = e - i;
SASSERT_EQ(i - begin(), size() - count);
shrink(size() - count);
return count;
}
void shrink(SZ s) {
if (m_data) {
SASSERT(s <= reinterpret_cast(m_data)[SIZE_IDX]);
if (CallDestructors) {
iterator it = m_data + s;
iterator e = end();
for(; it != e; ++it) {
it->~T();
}
}
reinterpret_cast(m_data)[SIZE_IDX] = s;
}
else {
SASSERT(s == 0);
}
}
template
void resize(SZ s, Args args...) {
SZ sz = size();
if (s <= sz) { shrink(s); return; }
while (s > capacity()) {
expand_vector();
}
SASSERT(m_data != 0);
reinterpret_cast(m_data)[SIZE_IDX] = s;
iterator it = m_data + sz;
iterator end = m_data + s;
for (; it != end; ++it) {
new (it) T(std::forward(args));
}
}
void resize(SZ s) {
SZ sz = size();
if (s <= sz) { shrink(s); return; }
while (s > capacity()) {
expand_vector();
}
SASSERT(m_data != 0);
reinterpret_cast(m_data)[SIZE_IDX] = s;
iterator it = m_data + sz;
iterator end = m_data + s;
for (; it != end; ++it) {
new (it) T();
}
}
void append(vector const & other) {
for(SZ i = 0; i < other.size(); ++i) {
push_back(other[i]);
}
}
void append(SZ sz, T const * data) {
for(SZ i = 0; i < sz; ++i) {
push_back(data[i]);
}
}
void init(vector const& other) {
if (this == &other)
return;
reset();
append(other);
}
void init(SZ sz, T const* data) {
reset();
append(sz, data);
}
T * data() const {
return m_data;
}
void swap(vector & other) noexcept {
std::swap(m_data, other.m_data);
}
void reverse() {
SZ sz = size();
for (SZ i = 0; i < sz/2; ++i) {
std::swap(m_data[i], m_data[sz-i-1]);
}
}
void fill(T const & elem) {
iterator i = begin();
iterator e = end();
for (; i != e; ++i) {
*i = elem;
}
}
void fill(unsigned sz, T const & elem) {
resize(sz);
fill(elem);
}
bool contains(T const & elem) const {
const_iterator it = begin();
const_iterator e = end();
for (; it != e; ++it) {
if (*it == elem) {
return true;
}
}
return false;
}
// set pos idx with elem. If idx >= size, then expand using default.
void setx(SZ idx, T const & elem, T const & d) {
if (idx >= size()) {
resize(idx+1, d);
}
m_data[idx] = elem;
}
// return element at position idx, if idx >= size, then return default
T const & get(SZ idx, T const & d) const {
if (idx >= size()) {
return d;
}
return m_data[idx];
}
void reserve(SZ s, T const & d) {
if (s > size())
resize(s, d);
}
void reserve(SZ s) {
if (s > size())
resize(s);
}
struct scoped_stack {
vector& s;
unsigned sz;
scoped_stack(vector& s):s(s), sz(s.size()) {}
~scoped_stack() { s.shrink(sz); }
};
};
#endif
template
class ptr_vector : public vector {
public:
ptr_vector():vector() {}
ptr_vector(unsigned s):vector(s) {}
ptr_vector(unsigned s, T * elem):vector(s, elem) {}
ptr_vector(unsigned s, T * const * data):vector(s, const_cast(data)) {}
std::ostream& display(std::ostream& out, char const* delim = " ") const {
char const* d = "";
for (auto const* u : *this) {
if (u)
out << d << *u;
else
out << d << "";
d = delim;
}
return out;
}
};
template
class svector : public vector {
public:
svector():vector() {}
svector(SZ s):vector(s) {}
svector(SZ s, T const & elem):vector(s, elem) {}
svector(SZ s, T const * data):vector(s, data) {}
};
using int_vector = svector;
using unsigned_vector = svector;
using char_vector = svector;
using signed_char_vector = svector;
using double_vector = svector;
using bool_vector = svector;
template
inline std::ostream& operator<<(std::ostream& out, svector const& v) {
for (auto const& x : v)
out << x << " ";
return out;
}
template
inline std::ostream& operator<<(std::ostream& out, ptr_vector const& v) {
return v.display(out);
}
template
struct vector_hash_tpl {
Hash m_hash;
typedef Vec data;
unsigned operator()(data const& v, unsigned idx) const { return m_hash(v[idx]); }
vector_hash_tpl(Hash const& h = Hash()):m_hash(h) {}
unsigned operator()(data const& v) const {
if (v.empty()) {
return 778;
}
return get_composite_hash, vector_hash_tpl>(v, v.size());
}
};
template
struct vector_hash : public vector_hash_tpl > {};
template
struct svector_hash : public vector_hash_tpl > {};
template
inline std::ostream& operator<<(std::ostream& out, vector const& v) {
bool first = true;
for (auto const& t : v) {
if (first) first = false; else out << " ";
out << t;
}
return out;
}