#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 *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 Treap::bulk_get_line(count_type r, count_type count) { vector 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); }