// Copyright (c) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/comp/move_to_front.h"
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <sstream>
#include <unordered_set>
#include <utility>
namespace spvtools {
namespace comp {
bool MoveToFront::Insert(uint32_t value) {
auto it = value_to_node_.find(value);
if (it != value_to_node_.end() && IsInTree(it->second)) return false;
const uint32_t old_size = GetSize();
(void)old_size;
InsertNode(CreateNode(next_timestamp_++, value));
last_accessed_value_ = value;
last_accessed_value_valid_ = true;
assert(value_to_node_.count(value));
assert(old_size + 1 == GetSize());
return true;
}
bool MoveToFront::Remove(uint32_t value) {
auto it = value_to_node_.find(value);
if (it == value_to_node_.end()) return false;
if (!IsInTree(it->second)) return false;
if (last_accessed_value_ == value) last_accessed_value_valid_ = false;
const uint32_t orphan = RemoveNode(it->second);
(void)orphan;
// The node of |value| is still alive but it's orphaned now. Can still be
// reused later.
assert(!IsInTree(orphan));
assert(ValueOf(orphan) == value);
return true;
}
bool MoveToFront::RankFromValue(uint32_t value, uint32_t* rank) {
if (last_accessed_value_valid_ && last_accessed_value_ == value) {
*rank = 1;
return true;
}
const uint32_t old_size = GetSize();
if (old_size == 1) {
if (ValueOf(root_) == value) {
*rank = 1;
return true;
} else {
return false;
}
}
const auto it = value_to_node_.find(value);
if (it == value_to_node_.end()) {
return false;
}
uint32_t target = it->second;
if (!IsInTree(target)) {
return false;
}
uint32_t node = target;
*rank = 1 + SizeOf(LeftOf(node));
while (node) {
if (IsRightChild(node)) *rank += 1 + SizeOf(LeftOf(ParentOf(node)));
node = ParentOf(node);
}
// Don't update timestamp if the node has rank 1.
if (*rank != 1) {
// Update timestamp and reposition the node.
target = RemoveNode(target);
assert(ValueOf(target) == value);
assert(old_size == GetSize() + 1);
MutableTimestampOf(target) = next_timestamp_++;
InsertNode(target);
assert(old_size == GetSize());
}
last_accessed_value_ = value;
last_accessed_value_valid_ = true;
return true;
}
bool MoveToFront::HasValue(uint32_t value) const {
const auto it = value_to_node_.find(value);
if (it == value_to_node_.end()) {
return false;
}
return IsInTree(it->second);
}
bool MoveToFront::Promote(uint32_t value) {
if (last_accessed_value_valid_ && last_accessed_value_ == value) {
return true;
}
const uint32_t old_size = GetSize();
if (old_size == 1) return ValueOf(root_) == value;
const auto it = value_to_node_.find(value);
if (it == value_to_node_.end()) {
return false;
}
uint32_t target = it->second;
if (!IsInTree(target)) {
return false;
}
// Update timestamp and reposition the node.
target = RemoveNode(target);
assert(ValueOf(target) == value);
assert(old_size == GetSize() + 1);
MutableTimestampOf(target) = next_timestamp_++;
InsertNode(target);
assert(old_size == GetSize());
last_accessed_value_ = value;
last_accessed_value_valid_ = true;
return true;
}
bool MoveToFront::ValueFromRank(uint32_t rank, uint32_t* value) {
if (last_accessed_value_valid_ && rank == 1) {
*value = last_accessed_value_;
return true;
}
const uint32_t old_size = GetSize();
if (rank <= 0 || rank > old_size) {
return false;
}
if (old_size == 1) {
*value = ValueOf(root_);
return true;
}
const bool update_timestamp = (rank != 1);
uint32_t node = root_;
while (node) {
const uint32_t left_subtree_num_nodes = SizeOf(LeftOf(node));
if (rank == left_subtree_num_nodes + 1) {
// This is the node we are looking for.
// Don't update timestamp if the node has rank 1.
if (update_timestamp) {
node = RemoveNode(node);
assert(old_size == GetSize() + 1);
MutableTimestampOf(node) = next_timestamp_++;
InsertNode(node);
assert(old_size == GetSize());
}
*value = ValueOf(node);
last_accessed_value_ = *value;
last_accessed_value_valid_ = true;
return true;
}
if (rank < left_subtree_num_nodes + 1) {
// Descend into the left subtree. The rank is still valid.
node = LeftOf(node);
} else {
// Descend into the right subtree. We leave behind the left subtree and
// the current node, adjust the |rank| accordingly.
rank -= left_subtree_num_nodes + 1;
node = RightOf(node);
}
}
assert(0);
return false;
}
uint32_t MoveToFront::CreateNode(uint32_t timestamp, uint32_t value) {
uint32_t handle = static_cast<uint32_t>(nodes_.size());
const auto result = value_to_node_.emplace(value, handle);
if (result.second) {
// Create new node.
nodes_.emplace_back(Node());
Node& node = nodes_.back();
node.timestamp = timestamp;
node.value = value;
node.size = 1;
// Non-NIL nodes start with height 1 because their NIL children are
// leaves.
node.height = 1;
} else {
// Reuse old node.
handle = result.first->second;
assert(!IsInTree(handle));
assert(ValueOf(handle) == value);
assert(SizeOf(handle) == 1);
assert(HeightOf(handle) == 1);
MutableTimestampOf(handle) = timestamp;
}
return handle;
}
void MoveToFront::InsertNode(uint32_t node) {
assert(!IsInTree(node));
assert(SizeOf(node) == 1);
assert(HeightOf(node) == 1);
assert(TimestampOf(node));
if (!root_) {
root_ = node;
return;
}
uint32_t iter = root_;
uint32_t parent = 0;
// Will determine if |node| will become the right or left child after
// insertion (but before balancing).
bool right_child = true;
// Find the node which will become |node|'s parent after insertion
// (but before balancing).
while (iter) {
parent = iter;
assert(TimestampOf(iter) != TimestampOf(node));
right_child = TimestampOf(iter) > TimestampOf(node);
iter = right_child ? RightOf(iter) : LeftOf(iter);
}
assert(parent);
// Connect node and parent.
MutableParentOf(node) = parent;
if (right_child)
MutableRightOf(parent) = node;
else
MutableLeftOf(parent) = node;
// Insertion is finished. Start the balancing process.
bool needs_rebalancing = true;
parent = ParentOf(node);
while (parent) {
UpdateNode(parent);
if (needs_rebalancing) {
const int parent_balance = BalanceOf(parent);
if (RightOf(parent) == node) {
// Added node to the right subtree.
if (parent_balance > 1) {
// Parent is right heavy, rotate left.
if (BalanceOf(node) < 0) RotateRight(node);
parent = RotateLeft(parent);
} else if (parent_balance == 0 || parent_balance == -1) {
// Parent is balanced or left heavy, no need to balance further.
needs_rebalancing = false;
}
} else {
// Added node to the left subtree.
if (parent_balance < -1) {
// Parent is left heavy, rotate right.
if (BalanceOf(node) > 0) RotateLeft(node);
parent = RotateRight(parent);
} else if (parent_balance == 0 || parent_balance == 1) {
// Parent is balanced or right heavy, no need to balance further.
needs_rebalancing = false;
}
}
}
assert(BalanceOf(parent) >= -1 && (BalanceOf(parent) <= 1));
node = parent;
parent = ParentOf(parent);
}
}
uint32_t MoveToFront::RemoveNode(uint32_t node) {
if (LeftOf(node) && RightOf(node)) {
// If |node| has two children, then use another node as scapegoat and swap
// their contents. We pick the scapegoat on the side of the tree which has
// more nodes.
const uint32_t scapegoat = SizeOf(LeftOf(node)) >= SizeOf(RightOf(node))
? RightestDescendantOf(LeftOf(node))
: LeftestDescendantOf(RightOf(node));
assert(scapegoat);
std::swap(MutableValueOf(node), MutableValueOf(scapegoat));
std::swap(MutableTimestampOf(node), MutableTimestampOf(scapegoat));
value_to_node_[ValueOf(node)] = node;
value_to_node_[ValueOf(scapegoat)] = scapegoat;
node = scapegoat;
}
// |node| may have only one child at this point.
assert(!RightOf(node) || !LeftOf(node));
uint32_t parent = ParentOf(node);
uint32_t child = RightOf(node) ? RightOf(node) : LeftOf(node);
// Orphan |node| and reconnect parent and child.
if (child) MutableParentOf(child) = parent;
if (parent) {
if (LeftOf(parent) == node)
MutableLeftOf(parent) = child;
else
MutableRightOf(parent) = child;
}
MutableParentOf(node) = 0;
MutableLeftOf(node) = 0;
MutableRightOf(node) = 0;
UpdateNode(node);
const uint32_t orphan = node;
if (root_ == node) root_ = child;
// Removal is finished. Start the balancing process.
bool needs_rebalancing = true;
node = child;
while (parent) {
UpdateNode(parent);
if (needs_rebalancing) {
const int parent_balance = BalanceOf(parent);
if (parent_balance == 1 || parent_balance == -1) {
// The height of the subtree was not changed.
needs_rebalancing = false;
} else {
if (RightOf(parent) == node) {
// Removed node from the right subtree.
if (parent_balance < -1) {
// Parent is left heavy, rotate right.
const uint32_t sibling = LeftOf(parent);
if (BalanceOf(sibling) > 0) RotateLeft(sibling);
parent = RotateRight(parent);
}
} else {
// Removed node from the left subtree.
if (parent_balance > 1) {
// Parent is right heavy, rotate left.
const uint32_t sibling = RightOf(parent);
if (BalanceOf(sibling) < 0) RotateRight(sibling);
parent = RotateLeft(parent);
}
}
}
}
assert(BalanceOf(parent) >= -1 && (BalanceOf(parent) <= 1));
node = parent;
parent = ParentOf(parent);
}
return orphan;
}
uint32_t MoveToFront::RotateLeft(const uint32_t node) {
const uint32_t pivot = RightOf(node);
assert(pivot);
// LeftOf(pivot) gets attached to node in place of pivot.
MutableRightOf(node) = LeftOf(pivot);
if (RightOf(node)) MutableParentOf(RightOf(node)) = node;
// Pivot gets attached to ParentOf(node) in place of node.
MutableParentOf(pivot) = ParentOf(node);
if (!ParentOf(node))
root_ = pivot;
else if (IsLeftChild(node))
MutableLeftOf(ParentOf(node)) = pivot;
else
MutableRightOf(ParentOf(node)) = pivot;
// Node is child of pivot.
MutableLeftOf(pivot) = node;
MutableParentOf(node) = pivot;
// Update both node and pivot. Pivot is the new parent of node, so node should
// be updated first.
UpdateNode(node);
UpdateNode(pivot);
return pivot;
}
uint32_t MoveToFront::RotateRight(const uint32_t node) {
const uint32_t pivot = LeftOf(node);
assert(pivot);
// RightOf(pivot) gets attached to node in place of pivot.
MutableLeftOf(node) = RightOf(pivot);
if (LeftOf(node)) MutableParentOf(LeftOf(node)) = node;
// Pivot gets attached to ParentOf(node) in place of node.
MutableParentOf(pivot) = ParentOf(node);
if (!ParentOf(node))
root_ = pivot;
else if (IsLeftChild(node))
MutableLeftOf(ParentOf(node)) = pivot;
else
MutableRightOf(ParentOf(node)) = pivot;
// Node is child of pivot.
MutableRightOf(pivot) = node;
MutableParentOf(node) = pivot;
// Update both node and pivot. Pivot is the new parent of node, so node should
// be updated first.
UpdateNode(node);
UpdateNode(pivot);
return pivot;
}
void MoveToFront::UpdateNode(uint32_t node) {
MutableSizeOf(node) = 1 + SizeOf(LeftOf(node)) + SizeOf(RightOf(node));
MutableHeightOf(node) =
1 + std::max(HeightOf(LeftOf(node)), HeightOf(RightOf(node)));
}
} // namespace comp
} // namespace spvtools