325 lines
7.2 KiB
C++
325 lines
7.2 KiB
C++
#include "everything.hpp"
|
|
|
|
// Get size uf a subtreap
|
|
count_type Treap::get_size(line *l) {
|
|
if(l == nullptr)
|
|
return 0;
|
|
return l->size;
|
|
}
|
|
count_type Treap::get_priority(line *l) {
|
|
if(l == nullptr)
|
|
return 0;
|
|
return l->priority;
|
|
}
|
|
|
|
// Treap node rotations
|
|
line *rotate_left(line *l) {
|
|
line *r = l->right, *mid = r->left;
|
|
|
|
// Switch
|
|
l->set_right(mid);
|
|
r->set_left(l);
|
|
|
|
return r;
|
|
}
|
|
line *rotate_right(line *r) {
|
|
line *l = r->left, *mid = l->right;
|
|
|
|
// Switch
|
|
r->set_left(mid);
|
|
l->set_right(r);
|
|
|
|
return l;
|
|
}
|
|
|
|
// Meta-operations
|
|
line* Treap::insert(line *l, count_type k, string s, bool new_version) {
|
|
if(l == nullptr)
|
|
return new line(rand(), s);
|
|
|
|
// Insert into left subtree
|
|
if(k <= get_size(l->left)) {
|
|
line *son = insert(l->left, k, s, new_version);
|
|
|
|
// Update the node
|
|
if(new_version)
|
|
l = new line(l->priority, l->text, son, l->right, son);
|
|
else
|
|
l->set_left(son);
|
|
|
|
// Balance heap-like structure
|
|
if(get_priority(l->left) > get_priority(l))
|
|
l = rotate_right(l);
|
|
}
|
|
|
|
// Insert into right subtree
|
|
else {
|
|
line *son = insert(l->right, k - (get_size(l->left)+1), s, new_version);
|
|
|
|
// Update the node
|
|
if(new_version)
|
|
l = new line(l->priority, l->text, l->left, son, son);
|
|
else
|
|
l->set_right(son);
|
|
|
|
// Balance heap-like structure
|
|
if(get_priority(l->right) > get_priority(l))
|
|
l = rotate_left(l);
|
|
}
|
|
return l;
|
|
}
|
|
line* Treap::remove(line *l, count_type k) {
|
|
line *lson = l->left, *rson = l->right;
|
|
|
|
// Remove from left subtree
|
|
if(k < get_size(lson)) {
|
|
line *son = remove(lson, k);
|
|
return new line(l->priority, l->text, son, rson, son);
|
|
}
|
|
// Remove this line
|
|
else if(k == get_size(l->left)) {
|
|
// Picking the son we can (the easy cases)
|
|
if(lson == nullptr && rson == nullptr)
|
|
return nullptr;
|
|
else if(lson == nullptr)
|
|
return new line(l->priority, rson->text, rson->left, rson->right, nullptr);
|
|
else if(rson == nullptr)
|
|
return new line(l->priority, lson->text, lson->left, lson->right, nullptr);
|
|
|
|
// Picking the son with higher priority
|
|
else if(get_priority(lson) >= get_priority(rson)) {
|
|
line *son = new line(lson);
|
|
line l_copy = line(l->priority, l->text, son, l->right, nullptr);
|
|
|
|
// Rotate to get the son higher
|
|
rotate_right(&l_copy);
|
|
|
|
// Remove under
|
|
son->right = remove(&l_copy, get_size(l_copy.left));
|
|
son->next_in_version = son->right;
|
|
son->update_size();
|
|
|
|
return son;
|
|
}
|
|
else {
|
|
line *son = new line(rson);
|
|
line l_copy = line(l->priority, l->text, l->left, son, nullptr);
|
|
|
|
// Rotate to get the son higher
|
|
rotate_left(&l_copy);
|
|
|
|
// Remove under
|
|
son->left = remove(&l_copy, get_size(l_copy.left));
|
|
son->next_in_version = son->left;
|
|
son->update_size();
|
|
|
|
return son;
|
|
}
|
|
}
|
|
// Remove from right subtree
|
|
else {
|
|
line *son = remove(rson, k - (get_size(lson)+1) );
|
|
return new line(l->priority, l->text, lson, son, son);
|
|
}
|
|
}
|
|
line* Treap::update(line *l, count_type k, string s) {
|
|
if(k < get_size(l->left)) {
|
|
line *son = update(l->left, k, s);
|
|
return new line(l->priority, l->text, son, l->right, son);
|
|
}
|
|
else if(k == get_size(l->left))
|
|
return new line(l->priority, s, l->left, l->right, nullptr);
|
|
else {
|
|
line *son = update(l->right, k - (1+get_size(l->left)), s);
|
|
return new line(l->priority, l->text, l->left, son, son);
|
|
}
|
|
}
|
|
|
|
// Find k-th line of file
|
|
line* Treap::find(line *l, count_type k) {
|
|
if(l == nullptr)
|
|
return nullptr;
|
|
|
|
if(k < get_size(l->left))
|
|
return find(l->left, k);
|
|
else if(k == get_size(l->left))
|
|
return l;
|
|
else
|
|
return find(l->right, k - (1+get_size(l->left)) );
|
|
}
|
|
|
|
// Version control
|
|
void Treap::delete_version(line *l) {
|
|
if(l == nullptr)
|
|
return;
|
|
|
|
line *next = l->next_in_version;
|
|
delete l;
|
|
delete_version(next);
|
|
}
|
|
count_type Treap::undo() {
|
|
if(changes.size() == 0)
|
|
return 0;
|
|
|
|
// Get number of changes
|
|
change last = changes.top();
|
|
changes.pop();
|
|
|
|
// Delete all treap versions within the change
|
|
for(count_type i = 0; i < last.count; i++) {
|
|
line *last_root = root.top();
|
|
root.pop();
|
|
delete_version(last_root);
|
|
}
|
|
return last.row;
|
|
}
|
|
void Treap::compress_versions(count_type count) {
|
|
// Remove all compressed change info
|
|
for(count_type i = 1; i < count; ++i)
|
|
changes.pop();
|
|
|
|
// Convert them into one grand-info
|
|
if(count > 0)
|
|
changes.top().count = count;
|
|
}
|
|
|
|
// File access
|
|
void Treap::clear() {
|
|
// Clear all versions
|
|
while(root.size() > 1)
|
|
undo();
|
|
|
|
// Clear original file
|
|
while(size() > 0)
|
|
remove(0);
|
|
}
|
|
|
|
// Line insert
|
|
void Treap::insert(count_type k, string s, bool new_version) {
|
|
if(k > size()) {
|
|
std::cerr << "Inserting out of file range\n";
|
|
return;
|
|
}
|
|
|
|
line* new_root = insert(get_root(), k, s, new_version);
|
|
|
|
if(new_version) {
|
|
root.push(new_root);
|
|
changes.push({k, 1});
|
|
}
|
|
else
|
|
root.top() = new_root;
|
|
}
|
|
// Line removal
|
|
void Treap::remove(count_type k) {
|
|
if(k >= size()) {
|
|
std::cerr << "Removing out of file range\n";
|
|
return;
|
|
}
|
|
|
|
if(size() == 1)
|
|
update(0, "");
|
|
else {
|
|
root.push(remove(get_root(), k));
|
|
changes.push({k, 1});
|
|
}
|
|
}
|
|
|
|
// Line text update
|
|
void Treap::update(count_type k, string s) {
|
|
if(k >= size()) {
|
|
std::cerr << "Updating out of file range\n";
|
|
return;
|
|
}
|
|
|
|
if(s == find(get_root(), k)->text)
|
|
return;
|
|
|
|
line *new_root = update(get_root(), k, s);
|
|
|
|
root.push(new_root);
|
|
changes.push({k, 1});
|
|
}
|
|
|
|
// Accessing the file
|
|
string Treap::get_line(count_type r, bool substitute_tab) {
|
|
string line = find(get_root(), r)->text;
|
|
|
|
if(substitute_tab)
|
|
return substitute_tabs(line);
|
|
else
|
|
return line;
|
|
}
|
|
// Get multiple adjacent vertices
|
|
count_type Treap::bulk_find(vector<string> *vec, line *l, count_type k, count_type count) {
|
|
if(l == nullptr) return count;
|
|
|
|
// Wanted vertex is on the left (this may be wanted afterwards)
|
|
if(k <= get_size(l->left)) {
|
|
count_type r = bulk_find(vec, l->left, k, count);
|
|
if(r > 0)
|
|
vec->push_back(substitute_tabs(l->text));
|
|
if(r > 1)
|
|
return bulk_find(vec, l->right, 0, r-1);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// This is a wanted vertex
|
|
else if(k == get_size(l->left)+1) {
|
|
vec->push_back(substitute_tabs(l->text));
|
|
if(count > 1)
|
|
return bulk_find(vec, l->right, 0, count-1);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// Wanted vertex is on the right (this is never wanted)
|
|
else
|
|
return bulk_find(vec, l->right, k - (1+get_size(l->left)), count);
|
|
}
|
|
// Access multiple adjacent vertices
|
|
vector<string> Treap::bulk_get_line(count_type r, count_type count) {
|
|
vector<string> ret(0);
|
|
bulk_find(&ret, get_root(), r+1, count);
|
|
return ret;
|
|
}
|
|
|
|
void Treap::insert(position p, string t) {
|
|
string text = get_line(p.r, false);
|
|
text.insert(p.c-get_tab_offset(p), t);
|
|
|
|
if(text != get_line(p.r, false))
|
|
update(p.r, text);
|
|
}
|
|
void Treap::remove(position p, count_type len) {
|
|
string text = get_line(p.r, false);
|
|
text.erase(p.c-get_tab_offset(p),len);
|
|
|
|
if(text != get_line(p.r, false))
|
|
update(p.r, text);
|
|
}
|
|
// Split line
|
|
void Treap::split_line(position p, bool should_compress) {
|
|
string line = get_line(p.r, false);
|
|
count_type real_c = p.c - get_tab_offset(p);
|
|
string newline = line.substr(real_c, line.size());
|
|
|
|
if(line.size()-real_c > 0)
|
|
remove(p, line.size()-real_c);
|
|
insert(p.r+1, newline);
|
|
|
|
if(should_compress)
|
|
compress_versions(2);
|
|
}
|
|
void Treap::merge_line(count_type k, bool should_compress) {
|
|
if(k == 0)
|
|
return;
|
|
|
|
string text = get_line(k, false);
|
|
remove(k);
|
|
update(k-1, get_line(k-1, false) + text);
|
|
|
|
if(should_compress)
|
|
compress_versions(2);
|
|
}
|