File: multitree_impl.hpp

package info (click to toggle)
terraphast 0.1.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 844 kB
  • sloc: cpp: 5,923; sh: 92; ansic: 55; makefile: 27
file content (158 lines) | stat: -rw-r--r-- 4,470 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#ifndef MULTITREE_IMPL_HPP
#define MULTITREE_IMPL_HPP

#include "multitree.hpp"

#include <memory>

namespace terraces {
namespace multitree_impl {

template <typename T>
struct array_deleter {
	void operator()(T* ptr) { delete[] ptr; }
};

template <typename T>
std::unique_ptr<T[], array_deleter<T>> make_unique_array(std::size_t size) {
	// this may leak memory if allocation or construction of a T fails
	return std::unique_ptr<T[], array_deleter<T>>{new T[size]};
}

template <typename T>
class storage_block {
private:
	std::unique_ptr<T[], array_deleter<T>> begin;
	index_t size;
	index_t max_size;

public:
	storage_block(index_t max_size)
	        : begin{make_unique_array<T>(max_size)}, size{0}, max_size{max_size} {}

	bool has_space(index_t required = 1) { return size + required <= max_size; }

	T* get() {
		assert(has_space());
		return begin.get() + (size++);
	}

	T* get_range(index_t required) {
		assert(has_space(required));
		auto result = begin.get() + size;
		size += required;
		return result;
	}
};

template <typename T>
class storage_blocks {
private:
	std::vector<storage_block<T>> m_blocks;
	index_t m_block_size;
	index_t m_total_size;
	index_t m_memory_limit;

public:
	storage_blocks(index_t block_size = 1024)
	        : m_blocks{}, m_block_size{block_size}, m_total_size{block_size},
	          m_memory_limit{std::numeric_limits<index_t>::max()} {
		m_blocks.emplace_back(m_block_size);
	}
	storage_blocks(const storage_blocks<T>& other) : storage_blocks{other.m_block_size} {}
	storage_blocks(storage_blocks<T>&& other) = default;
	storage_blocks<T>& operator=(const storage_blocks<T>& other) {
		m_block_size = other.m_block_size;
		return *this;
	}
	storage_blocks<T>& operator=(storage_blocks<T>&& other) = default;
	index_t total_size() const { return sizeof(T) * m_total_size; }

	T* get() {
		if (!m_blocks.back().has_space()) {
			m_blocks.emplace_back(m_block_size);
			m_total_size += m_block_size;
		}
		return m_blocks.back().get();
	}

	T* get_range(index_t required) {
		if (!m_blocks.back().has_space(required)) {
			if (sizeof(T) * (m_total_size + required) > m_memory_limit) {
				// fail allocation if we require too much memory
				throw std::bad_alloc{};
			}
			m_blocks.emplace_back(required);
			m_total_size += required;
			auto result = m_blocks.back().get_range(required);
			auto last_it = --m_blocks.end();
			auto prev_it = --(--m_blocks.end());
			std::iter_swap(
			        last_it,
			        prev_it); // TODO this might lead to some bad worst-case behaviour
			return result;
		}
		return m_blocks.back().get_range(required);
	}

	void set_memory_limit(index_t memory_limit) { m_memory_limit = memory_limit; }
};

inline multitree_node* make_single_leaf(multitree_node* n, index_t i) {
	n->type = multitree_node_type::base_single_leaf;
	n->single_leaf = i;
	n->num_leaves = 1;
	n->num_trees = 1;
	return n;
}

inline multitree_node* make_two_leaves(multitree_node* n, index_t i, index_t j) {
	n->type = multitree_node_type::base_two_leaves;
	n->two_leaves = {i, j};
	n->num_leaves = 2;
	n->num_trees = 1;
	return n;
}

inline multitree_node* make_unconstrained(multitree_node* n, std::pair<index_t*, index_t*> range) {
	auto begin = range.first;
	auto end = range.second;
	n->type = multitree_node_type::base_unconstrained;
	n->unconstrained = {begin, end};
	n->num_leaves = (index_t)(end - begin);
	n->num_trees = count_unrooted_trees<index_t>(n->num_leaves);
	return n;
}

inline multitree_node* make_inner_node(multitree_node* n, multitree_node* left,
                                       multitree_node* right) {
	n->type = multitree_node_type::inner_node;
	n->inner_node = {left, right};
	n->num_leaves = left->num_leaves + right->num_leaves;
	n->num_trees = left->num_trees * right->num_trees;
	return n;
}

inline multitree_node* make_alternative_array(multitree_node* n, multitree_node* begin,
                                              index_t leaves) {
	n->type = multitree_node_type::alternative_array;
	n->alternative_array = {begin, begin};
	n->num_leaves = leaves;
	n->num_trees = 0;
	return n;
}

inline multitree_node* make_unexplored(multitree_node* n, std::pair<index_t*, index_t*> range) {
	auto begin = range.first;
	auto end = range.second;
	n->type = multitree_node_type::unexplored;
	n->unexplored = {begin, end};
	n->num_leaves = (index_t)(end - begin);
	n->num_trees = 0;
	return n;
}

} // namespace multitree_impl
} // namespace terraces

#endif // MULTITREE_IMPL_HPP