/*++ 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 "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 class std_vector : public 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) { 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); } reinterpret_cast(m_data)[SIZE_IDX]--; } void erase(T const & elem) { iterator it = std::find(begin(), end(), elem); if (it != end()) { erase(it); } } 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 (unsigned u : v) out << u << " "; 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; }