Files
secondo/Algebras/KeyValueStore/QuadTreeDistribution.cpp
2026-01-23 17:03:45 +08:00

1180 lines
32 KiB
C++

/*
----
This file is part of SECONDO.
Copyright (C) 2015,
Faculty of Mathematics and Computer Science,
Database Systems for New Applications.
SECONDO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
SECONDO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
*/
#include "QuadTreeDistribution.h"
#include <iostream>
#include <sstream>
#include <algorithm>
using namespace std;
namespace KVS {
QuadNode::QuadNode() : QuadNode(100, 100, 10, 10) {}
QuadNode::QuadNode(double x, double y, double width, double height)
: QuadNode(0, x, y, width, height) {}
QuadNode::QuadNode(QuadNode* parent, double x, double y, double width,
double height)
: parent(parent),
x(x),
y(y),
width(width),
height(height),
serverId(-1),
weight(0),
maxGlobalId(0) {
children[0] = children[1] = children[2] = children[3] = 0;
}
QuadNode::QuadNode(QuadNode* node)
: parent(0),
x(node->x),
y(node->y),
width(node->width),
height(node->height),
serverId(node->serverId),
weight(node->weight),
maxGlobalId(node->maxGlobalId) {
for (int i = 0; i < 4; ++i) {
if (node->children[i]) {
children[i] = new QuadNode(node->children[i]);
children[i]->parent = this;
}
}
}
QuadNode::QuadNode(const QuadNode& node)
: parent(0),
x(node.x),
y(node.y),
width(node.width),
height(node.height),
serverId(node.serverId),
weight(node.weight),
maxGlobalId(node.maxGlobalId) {
for (int i = 0; i < 4; ++i) {
if (node.children[i]) {
children[i] = new QuadNode(node.children[i]);
children[i]->parent = this;
}
}
}
QuadNode::~QuadNode() {
for (int i = 0; i < 4; ++i) {
delete children[i];
}
}
bool QuadNode::isLeaf() {
return !(children[0] || children[1] || children[2] || children[3]);
}
bool QuadNode::isOverlapping(double* mbb) {
return (mbb[0] < x + width && mbb[2] >= x) &&
(mbb[1] < y + height && mbb[3] >= y);
}
bool QuadNode::isOverlappingDebug(double* mbb) {
cout << mbb[0] << " < " << x + width << " && " << mbb[2] << " > " << x
<< endl;
cout << mbb[1] << " < " << y + height << " && " << mbb[3] << " > " << y
<< endl;
cout << "=> " << ((mbb[0] < x + width && mbb[2] > x) &&
(mbb[1] < y + height && mbb[3] > y))
<< endl;
return (mbb[0] < x + width && mbb[2] > x) &&
(mbb[1] < y + height && mbb[3] > y);
}
bool QuadNode::isInside(double* mbb) {
return ((mbb[0] >= x && mbb[0] <= x + width) &&
(mbb[2] >= x && mbb[2] <= x + width)) &&
((mbb[1] > y && mbb[1] < y + height) &&
(mbb[3] > y && mbb[3] < y + height));
}
QuadNode* QuadNode::get(const int& i) {
if (children[i] == 0) {
children[i] = new QuadNode(this, x + (i % 2 * width / 2),
y + (i / 2 * height / 2), width / 2, height / 2);
}
return children[i];
}
void QuadNode::init(QuadNode* prototype) {
x = prototype->x;
y = prototype->y;
width = prototype->width;
height = prototype->height;
serverId = prototype->serverId;
weight = prototype->weight;
maxGlobalId = prototype->maxGlobalId;
for (int i = 0; i < 4; ++i) {
if (prototype->children[i]) {
children[i] = new QuadNode(0, 0, 0, 0);
children[i]->init(prototype->children[i]);
}
}
}
QuadNode* QuadNode::root() {
QuadNode* temp = this;
while (temp->parent != 0) {
temp = temp->parent;
}
return temp;
}
int QuadNode::level() {
int result = 1;
QuadNode* temp = this;
while (temp->parent != 0) {
result++;
temp = temp->parent;
}
return result;
}
int QuadNode::levels() {
QuadNode* temp = root();
int result = 0;
levels(temp, 0, &result);
return result;
}
void QuadNode::levels(QuadNode* node, const int& level, int* maxLevel) {
if (level + 1 > *maxLevel) {
*maxLevel = level + 1;
}
for (int i = 0; i < 4; ++i) {
if (node->children[i] != 0) {
levels(node->children[i], level + 1, maxLevel);
}
}
}
void QuadNode::split() {
children[0] = new QuadNode(this, x, y, width / 2.0, height / 2.0);
children[1] =
new QuadNode(this, x + width / 2.0, y, width / 2.0, height / 2.0);
children[2] =
new QuadNode(this, x, y + height / 2.0, width / 2.0, height / 2.0);
children[3] = new QuadNode(this, x + width / 2.0, y + height / 2.0,
width / 2.0, height / 2.0);
for (int i = 0; i < 4; ++i) {
children[i]->weight = weight / 4;
children[i]->serverId = serverId;
children[i]->maxGlobalId = maxGlobalId;
}
maxGlobalId = 0;
}
void propagateUp(QuadNode* node, function<void(QuadNode*)> f) {
f(node);
if (node->parent != 0) {
propagateUp(node->parent, f);
}
}
void propagateDown(QuadNode* node, function<void(QuadNode*)> f) {
f(node);
for (int i = 0; i < 4; ++i) {
if (node->children[i] != 0) {
propagateDown(node->children[i], f);
}
}
}
void propagateDownB(QuadNode* node, function<bool(QuadNode*)> f) {
if (!f(node)) {
for (int i = 0; i < 4; ++i) {
if (node->children[i] != 0) {
propagateDownB(node->children[i], f);
}
}
}
}
void propagateDownPost(QuadNode* node, function<void(QuadNode*)> f) {
for (int i = 0; i < 4; ++i) {
if (node->children[i] != 0) {
propagateDownPost(node->children[i], f);
}
}
f(node);
}
bool findDescendant(QuadNode* node, function<bool(QuadNode*)> f) {
if (f(node)) {
return true;
} else {
for (int i = 0; i < 4; ++i) {
if (node->children[i] != 0) {
if (findDescendant(node->children[i], f)) {
return true;
}
}
}
return false;
}
}
bool touching2(QuadNode* a, QuadNode* b) {
bool temp = ((a->x <= b->x + b->width && a->x + a->width >= b->x) &&
(a->y <= b->y + b->height && a->y + a->height >= b->y)) &&
!((a->x + a->width == b->x || a->x == b->x + b->width) &&
(a->y + a->height == b->y || a->y == b->y + b->height));
return temp;
}
static const double eps = 0.001;
bool touching(QuadNode* a, QuadNode* b) {
bool temp =
((a->x <= b->x + b->width && a->x + a->width >= b->x) &&
(a->y <= b->y + b->height && a->y + a->height >= b->y)) &&
!((a->x + a->width - b->x < eps || a->x - b->x - b->width < eps) &&
(a->y + a->height - b->y < eps || a->y - b->y - b->height < eps));
return temp;
}
QuadTreeDistribution::QuadTreeDistribution() : QuadTreeDistribution(10, 10, 1) {
needsSync = true;
}
QuadTreeDistribution::QuadTreeDistribution(int initialWidth, int initialHeight,
int nrServers)
: Distribution(TYPE_QUADTREE),
root(0),
initialWidth(initialWidth),
initialHeight(initialHeight) {
needsSync = true;
serverIdOrder.assign(nrServers, 0);
}
QuadTreeDistribution::QuadTreeDistribution(const QuadTreeDistribution& qtd)
: Distribution(TYPE_QUADTREE, qtd.serverIdOrder, qtd.serverWeight),
initialWidth(qtd.initialWidth),
initialHeight(qtd.initialHeight) {
if (qtd.root) {
root = new QuadNode(qtd.root);
}
needsSync = true;
}
QuadTreeDistribution::~QuadTreeDistribution() {
if (root) {
delete root;
}
}
string QuadTreeDistribution::toBin() {
stringstream data;
data.write((char*)&type, sizeof(type));
data.write((char*)&initialWidth, sizeof(initialWidth));
data.write((char*)&initialHeight, sizeof(initialHeight));
unsigned int listLen = serverIdOrder.size();
data.write((char*)&listLen, sizeof(listLen));
for (unsigned int i = 0; i < listLen; ++i) {
int tempData = serverIdOrder[i];
data.write((char*)&tempData, sizeof(tempData));
}
data << quadnodeToBin(root);
return data.str();
}
string QuadTreeDistribution::quadnodeToBin(QuadNode* node) {
stringstream data;
bool available = false;
if (node == 0) {
data.write((char*)&available, sizeof(available));
} else {
available = true;
data.write((char*)&available, sizeof(available));
data.write((char*)&node->x, sizeof(node->x));
data.write((char*)&node->y, sizeof(node->y));
data.write((char*)&node->width, sizeof(node->width));
data.write((char*)&node->height, sizeof(node->height));
data.write((char*)&node->weight, sizeof(node->weight));
data.write((char*)&node->serverId, sizeof(node->serverId));
data.write((char*)&node->maxGlobalId, sizeof(node->maxGlobalId));
data << quadnodeToBin(node->children[0]);
data << quadnodeToBin(node->children[1]);
data << quadnodeToBin(node->children[2]);
data << quadnodeToBin(node->children[3]);
}
return data.str();
}
bool QuadTreeDistribution::fromBin(const string& data) {
stringstream dataStream(data);
int tempType = -1;
dataStream.read((char*)&tempType, sizeof(tempType));
if (tempType == type) {
dataStream.read((char*)&initialWidth, sizeof(initialWidth));
dataStream.read((char*)&initialHeight, sizeof(initialHeight));
serverIdOrder.clear();
unsigned int listLen = 0;
dataStream.read((char*)&listLen, sizeof(listLen));
for (unsigned int i = 0; i < listLen; ++i) {
int tempData = 0;
dataStream.read((char*)&tempData, sizeof(tempData));
serverIdOrder.push_back(tempData);
}
delete root;
root = quadnodeFromBin(dataStream, 0);
updateWeightVector();
fixNodeWeights();
return true;
} else {
return false;
}
}
QuadNode* QuadTreeDistribution::quadnodeFromBin(stringstream& data,
QuadNode* parent) {
bool available = false;
data.read((char*)&available, sizeof(available));
if (available) {
double x = 0;
double y = 0;
double width = 0;
double height = 0;
int weight = 0;
int serverId = -1;
unsigned int maxGlobalId = 0;
data.read((char*)&x, sizeof(x));
data.read((char*)&y, sizeof(y));
data.read((char*)&width, sizeof(width));
data.read((char*)&height, sizeof(height));
data.read((char*)&weight, sizeof(weight));
data.read((char*)&serverId, sizeof(serverId));
data.read((char*)&maxGlobalId, sizeof(maxGlobalId));
QuadNode* temp = new QuadNode(parent, x, y, width, height);
temp->weight = weight;
temp->serverId = serverId;
temp->maxGlobalId = maxGlobalId;
for (int i = 0; i < 4; ++i) {
temp->children[i] = quadnodeFromBin(data, temp);
}
return temp;
} else {
return 0;
}
}
void QuadTreeDistribution::init(QuadTreeDistribution* prototype) {
initialWidth = prototype->initialWidth;
initialHeight = prototype->initialHeight;
serverWeight.clear();
serverWeight.insert(prototype->serverWeight.begin(),
prototype->serverWeight.end());
serverIdOrder.clear();
serverIdOrder.assign(prototype->serverIdOrder.begin(),
prototype->serverIdOrder.end());
if (prototype->root) {
root = new QuadNode(0, 0, 0, 0);
root->init(prototype->root);
}
}
void QuadTreeDistribution::resetWeight() {
if (root) {
propagateDown(root, [](QuadNode* node) { node->weight = 0; });
}
}
void QuadTreeDistribution::updateWeightVector() {
if (root) {
serverWeight.clear();
propagateDown(root, [this](QuadNode* node) {
if (node->isLeaf()) {
if (node->serverId > -1) {
this->serverWeight[node->serverId] += node->weight;
}
}
});
}
}
void QuadTreeDistribution::addWeight(Distribution* dist, const int& id) {
QuadTreeDistribution* qtd = static_cast<QuadTreeDistribution*>(dist);
addWeightNode(root, qtd->root, id);
}
void QuadTreeDistribution::addWeightNode(QuadNode* base, QuadNode* add,
const int& id) {
if (base && add) {
if (base->serverId == id && add->serverId == id) {
base->weight += add->weight;
}
for (int childIdx = 0; childIdx < 4; ++childIdx) {
if (base->children[childIdx] && add->children[childIdx]) {
addWeightNode(base->children[childIdx], add->children[childIdx], id);
}
}
}
}
void QuadTreeDistribution::resetMaxGlobalIds() {
if (root) {
propagateDown(root, [](QuadNode* node) { node->maxGlobalId = 0; });
}
}
void QuadTreeDistribution::addMaxGlobalIds(Distribution* dist, const int& id) {
QuadTreeDistribution* qtd = static_cast<QuadTreeDistribution*>(dist);
addGlobalIdNode(root, qtd->root, id);
}
void QuadTreeDistribution::addGlobalIdNode(QuadNode* base, QuadNode* add,
const int& id) {
if (base && add) {
if (base->serverId == id && add->serverId == id) {
if (add->maxGlobalId > base->maxGlobalId) {
base->maxGlobalId = add->maxGlobalId;
}
}
for (int childIdx = 0; childIdx < 4; ++childIdx) {
if (base->children[childIdx] && add->children[childIdx]) {
addGlobalIdNode(base->children[childIdx], add->children[childIdx], id);
}
}
}
}
bool QuadTreeDistribution::filter(int nrcoords, double* coords,
const unsigned int& globalId, bool update) {
bool result = false;
if (root) {
if (update) {
filterUpdate(root, coords, globalId);
} else {
filterCheck(root, coords, globalId, result);
}
}
return result;
}
void QuadTreeDistribution::filterUpdate(QuadNode* node, double* mbb,
const unsigned int& globalId) {
if (node->isLeaf()) {
if (node->isInside(mbb)) {
if (node->maxGlobalId < globalId) {
node->maxGlobalId = globalId;
}
}
} else {
for (int i = 0; i < 4; ++i) {
if (node->children[i] && node->children[i]->isInside(mbb)) {
filterUpdate(node->children[i], mbb, globalId);
}
}
}
}
void QuadTreeDistribution::filterCheck(QuadNode* node, double* mbb,
const unsigned int& globalId,
bool& result) {
if (!result) {
if (node->isLeaf()) {
if (globalId > node->maxGlobalId) {
result = true;
}
} else {
for (int i = 0; i < 4; ++i) {
if (node->children[i] && node->children[i]->isOverlapping(mbb)) {
filterCheck(node->children[i], mbb, globalId, result);
}
}
}
}
}
string QuadTreeDistribution::serverIdAssignment(string attributeName,
string distributionName,
bool requestOnly) {
if (requestOnly) {
return string("extendstream[" + attributeName + ": kvsServerId('" +
distributionName + "', bbox(.GeoData), TRUE)] ");
} else {
return string("extendstream[" + attributeName + ": kvsServerId('" +
distributionName + "', bbox(.GeoData), FALSE)] ");
}
}
int QuadTreeDistribution::nextServerId() {
unsigned int id = 0;
while (find(serverIdOrder.begin(), serverIdOrder.end(), id) !=
serverIdOrder.end()) {
id++;
}
return id;
}
// left first
int QuadTreeDistribution::neighbourId(int id) {
vector<int>::iterator idPosition =
find(serverIdOrder.begin(), serverIdOrder.end(), id);
if (idPosition != serverIdOrder.end()) {
if (idPosition == serverIdOrder.begin()) {
if ((idPosition + 1) != serverIdOrder.end()) {
return *(idPosition + 1);
} else {
return -1;
}
} else {
return *(idPosition - 1);
}
} else {
return -1;
}
}
void QuadTreeDistribution::changeServerId(int oldid, int newid) {
if (root) {
vector<int>::iterator idPos =
find(serverIdOrder.begin(), serverIdOrder.end(), oldid);
if (idPos != serverIdOrder.end()) {
*idPos = newid;
propagateDown(root, [oldid, newid](QuadNode* node) {
if (node->serverId == oldid) {
node->serverId = newid;
}
});
}
}
}
int QuadTreeDistribution::pointId(double x, double y) {
int result = -1;
if (root) {
propagateDownB(root, [x, y, &result](QuadNode* node) -> bool {
if ((node->x <= x && x < node->x + node->width) &&
(node->y <= y && y < node->y + node->height)) {
if (node->isLeaf()) {
result = node->serverId;
return true; // stop node found
}
return false; // continue search
} else {
return true; // stop
}
});
}
return result;
}
void QuadTreeDistribution::fixNodeServerIds() {
// assign server numbers for non leafs
if (root) {
propagateDownPost(root, [](QuadNode* node) {
int serverId = -2;
int assignments = 0;
for (int i = 0; i < 4; ++i) {
if (node->children[i] != 0 && node->children[i]->serverId != serverId) {
serverId = node->children[i]->serverId;
assignments++;
}
}
if (assignments == 1) {
node->serverId = serverId;
} else if (assignments > 1) {
node->serverId = -1;
}
});
}
}
void QuadTreeDistribution::fixNodeWeights() {
if (root) {
propagateDownPost(root, [](QuadNode* node) {
if (!node->isLeaf()) {
node->weight = 0;
for (int i = 0; i < 4; ++i) {
if (node->children[i]) {
node->weight += node->children[i]->weight;
}
}
}
});
}
}
void QuadTreeDistribution::expand(double* mbb) {
int leftright = -1;
int updown = -1;
double moveleft = 0;
double moveup = 0;
// left right?
if (root->x > mbb[0]) {
leftright = 1;
moveleft = -root->width;
} else if ((root->x + root->width) < mbb[0]) {
leftright = 0;
}
// up down?
if (root->y > mbb[1]) {
updown = 2;
moveup = -root->height;
} else if ((root->y + root->height) < mbb[1]) {
updown = 0;
}
if (leftright > -1 || updown > -1) {
// decide by center
if (leftright == -1) {
if ((root->x + root->width / 2) < ((mbb[2] - mbb[0]) / 2)) {
leftright = 0;
} else {
leftright = 1;
moveleft = -root->width;
}
} else if (updown == -1) {
if ((root->y + root->height / 2) < ((mbb[3] - mbb[1]) / 2)) {
updown = 0;
} else {
updown = 2;
moveup = -root->height;
}
}
// expand
QuadNode* temp = new QuadNode(root->x + moveleft, root->y + moveup,
root->width * 2, root->height * 2);
temp->children[leftright + updown] = root;
root->parent = temp;
root = temp;
distributionUpdated();
// recursive
expand(mbb);
}
}
void QuadTreeDistribution::insert(QuadNode* node, double* mbb,
set<int>* results) {
if (node->isLeaf()) { // && node->width <= initialWidth) {
propagateUp(node, [](QuadNode* node) { node->weight++; });
if (node->serverId < 0) {
// this might be a bit expensive : go through all leafes in cluster order,
// when we reach the insert node assign last used serverId
int lastId = -1;
QuadNode* insertNode = node;
leafesInClusterOrder(root, [insertNode, &lastId](QuadNode* node) -> bool {
if (node == insertNode) {
insertNode->serverId = lastId;
} else if (node->serverId > -1) {
lastId = node->serverId;
}
return true; // we don't want the tree to split so we always return
// true
});
if (node->serverId == -1) {
node->serverId = 0;
}
}
serverWeight[node->serverId]++;
// TODO:remove
assert(node->serverId >= 0 && node->serverId < 100);
results->insert(node->serverId);
} else {
for (int i = 0; i < 4; ++i) {
QuadNode* child = node->get(i);
if (child->isOverlapping(mbb)) {
insert(child, mbb, results);
}
}
}
}
void QuadTreeDistribution::insertDebug(QuadNode* node, double* mbb,
set<int>* results) {
if (node->isLeaf()) { // && node->width <= initialWidth) {
cout << "Reached Leaf..." << endl;
propagateUp(node, [](QuadNode* node) { node->weight++; });
if (node->serverId < 0) {
// this might be a bit expensive : go through all leafes in cluster order,
// when we reach the insert node assign last used serverId
int lastId = -1;
QuadNode* insertNode = node;
leafesInClusterOrder(root, [insertNode, &lastId](QuadNode* node) -> bool {
if (node == insertNode) {
insertNode->serverId = lastId;
} else if (node->serverId > -1) {
lastId = node->serverId;
}
return true; // we don't want the tree to split so we always return
// true
});
if (node->serverId == -1) {
node->serverId = 0;
}
}
serverWeight[node->serverId]++;
results->insert(node->serverId);
} else {
for (int i = 0; i < 4; ++i) {
QuadNode* child = node->get(i);
if (child->isOverlappingDebug(mbb)) {
insertDebug(child, mbb, results);
}
}
}
}
void QuadTreeDistribution::retrieveIds(QuadNode* node, double* mbb,
set<int>* results) {
if (node->isLeaf()) { // && node->width <= initialWidth) {
if (node->serverId >= 0) {
results->insert(node->serverId);
}
} else {
for (int i = 0; i < 4; ++i) {
if (node->children[i]) {
if (node->children[i]->isOverlapping(mbb)) {
retrieveIds(node->children[i], mbb, results);
}
}
}
}
}
void QuadTreeDistribution::retrieveIdsDebug(QuadNode* node, double* mbb,
set<int>* results) {
if (node->isLeaf()) { // && node->width <= initialWidth) {
cout << "reached leaf" << endl;
if (node->serverId >= 0) {
results->insert(node->serverId);
} else {
cout << "but server isnt assigned..." << endl;
}
} else {
for (int i = 0; i < 4; ++i) {
if (node->children[i]) {
if (node->children[i]->isOverlappingDebug(mbb)) {
retrieveIdsDebug(node->children[i], mbb, results);
}
}
}
}
}
void QuadTreeDistribution::add(int value, set<int>* resultIds) {}
void QuadTreeDistribution::add(int nrcoords, double* coords,
set<int>* resultIds) {
if (root == 0) {
root = new QuadNode(coords[0], coords[1], initialWidth, initialHeight);
}
expand(coords);
insert(root, coords, resultIds);
}
void QuadTreeDistribution::addDebug(int nrcoords, double* coords,
set<int>* resultIds) {
if (root == 0) {
root = new QuadNode(coords[0], coords[1], initialWidth, initialHeight);
}
expand(coords);
cout << "calling insertDebug..." << endl;
insertDebug(root, coords, resultIds);
}
void QuadTreeDistribution::request(int value, set<int>* resultIds) {}
void QuadTreeDistribution::request(int nrcoords, double* coords,
set<int>* resultIds) {
if (root) {
retrieveIds(root, coords, resultIds);
}
}
void QuadTreeDistribution::requestDebug(int nrcoords, double* coords,
set<int>* resultIds) {
if (root != 0) {
retrieveIdsDebug(root, coords, resultIds);
}
}
int QuadTreeDistribution::split(int serverId) {
if (root && serverId >= 0) {
vector<int>::iterator idPos =
find(serverIdOrder.begin(), serverIdOrder.end(), serverId);
if (idPos != serverIdOrder.end()) {
updateWeightVector();
int idx = nextServerId();
vector<int>::iterator insertPos = serverIdOrder.insert(idPos, idx);
int targetWeight = serverWeight[serverId] / 2;
int currentWeight = targetWeight;
// assign
leafesInClusterOrder(root, [&targetWeight, &currentWeight, &insertPos,
serverId](QuadNode* node) -> bool {
if (node->serverId == serverId) {
// arbitrary 5% margin (fewer splits)
int margin = targetWeight * 0.05;
if (node->weight < currentWeight + margin) {
node->serverId = *insertPos;
currentWeight -= node->weight;
if (currentWeight <= 0) {
currentWeight = targetWeight;
insertPos++;
}
return true;
}
return false;
}
return true;
});
fixNodeServerIds();
return idx;
}
}
return -1;
}
// n: number of servers after serverId (referencing serverIdOrder)
void QuadTreeDistribution::redistribute(int serverId, int n) {
if (root && serverId >= 0 &&
serverId < static_cast<int>(serverIdOrder.size())) {
updateWeightVector();
vector<int>::iterator currentPos =
find(serverIdOrder.begin(), serverIdOrder.end(), serverId);
set<int> idSet;
int targetWeight = 0;
for (int i = 0; i < n; ++i) {
idSet.insert(*(currentPos + i));
targetWeight += serverWeight[*(currentPos + i)];
}
targetWeight = targetWeight / n;
int currentWeight = targetWeight;
cout << "!starting redistribute:" << targetWeight << endl;
// assign
leafesInClusterOrder(root, [&targetWeight, &currentWeight, &currentPos,
&idSet](QuadNode* node) -> bool {
if (idSet.find(node->serverId) != idSet.end()) {
// arbitrary 5% margin (fewer splits)
int margin = targetWeight * 0.05;
if (node->weight < currentWeight + margin) {
node->serverId = *currentPos;
// TODO:
assert(node->serverId > -1);
currentWeight -= node->weight;
if (currentWeight <= 0) {
currentWeight = targetWeight;
currentPos++;
}
return true;
}
cout << "!Splitting while restructuring: POS:" << *currentPos
<< " currentWeight:" << currentWeight
<< " node->weight:" << node->weight
<< " node->serverid:" << node->serverId << " margin:" << margin
<< endl;
return false;
}
return true;
});
fixNodeServerIds();
}
}
// bugged!
void QuadTreeDistribution::consolidateLayers() {
if (root) {
propagateDownPost(root, [](QuadNode* node) {
int serverId = -2;
int assignments = 0;
int weight = 0;
for (int i = 0; i < 4; ++i) {
if (node->children[i] && (node->children[i]->serverId != -1 &&
node->children[i]->serverId != serverId)) {
serverId = node->children[i]->serverId;
weight += node->children[i]->weight;
assignments++;
}
}
if (assignments == 1) {
node->serverId = serverId;
node->weight = weight;
for (int i = 0; i < 4; ++i) {
if (node->children[i]) {
delete node->children[i];
node->children[i] = 0;
}
}
} else if (assignments > 1) {
node->serverId = -1;
}
});
}
}
void QuadTreeDistribution::redistributeCluster() {
if (root) {
// reset server assignment
propagateDown(root, [](QuadNode* node) { node->serverId = -1; });
int weightcheck = 0;
propagateDown(root, [&weightcheck](QuadNode* node) {
if (node->isLeaf()) {
weightcheck += node->weight;
}
});
cout << "root->weight: " << root->weight
<< "vs weightcheck: " << weightcheck << "\n";
// assign server numbers for all leafs
int targetWeight = (weightcheck / serverIdOrder.size());
int currentWeight = targetWeight;
vector<int>::iterator currentServerId(serverIdOrder.begin());
cout << "targetWeight:" << targetWeight << "\n";
leafesInClusterOrder(root, [&targetWeight, &currentWeight,
&currentServerId](QuadNode* node) -> bool {
// arbitrary 5% margin (fewer splits)
int margin = targetWeight * 0.05;
if (node->weight < currentWeight + margin) {
node->serverId = *currentServerId;
currentWeight -= node->weight;
if (currentWeight <= 0) {
currentWeight = targetWeight;
currentServerId++;
cout << "Switching to next server:" << *currentServerId << "\n";
}
return true;
}
return false; // split
});
fixNodeServerIds();
resetWeight();
}
}
void QuadTreeDistribution::leafesInClusterOrder(QuadNode* node,
function<bool(QuadNode*)> f) {
// default (clockwise): upper left -> upper right -> lower right -> lower left
QuadNode* lastVisited;
if (node->isLeaf()) {
if (!f(node)) {
node->split();
leafesInClusterOrder(node, f);
}
} else {
if (node->children[0] != 0) {
leafesInClusterOrderR(node->children[0], 0, 1, &lastVisited, f);
}
if (node->children[1] != 0) {
leafesInClusterOrderR(node->children[1], 1, 3, &lastVisited, f);
}
if (node->children[3] != 0) {
leafesInClusterOrderR(node->children[3], 3, 2, &lastVisited, f);
}
if (node->children[2] != 0) {
leafesInClusterOrderR(node->children[2], 2, 0, &lastVisited, f);
}
}
}
// Turns out this might be doing something similar to the "Hilbert-Curve"
// so there probably exists a more efficient algorithm but i don't have time to
// check or implement it
// f returning false leads to split
void QuadTreeDistribution::leafesInClusterOrderR(QuadNode* node,
const int& currentIdx,
const int& nextIdx,
QuadNode** lastVisited,
function<bool(QuadNode*)> f) {
if (node->isLeaf()) {
if (f(node)) {
*lastVisited = node;
return;
} else {
node->split();
}
}
// determine where we have to start the "loop" if we want to stay in "touch"
int defaultPath[4] = {0, 1, 3, 2};
int endPoint[2] = {0, 0};
int startPoint = 0;
if (*lastVisited != 0) {
for (int i = 0; i < 4; ++i) {
if (node->children[i] && touching(node->children[i], *lastVisited)) {
startPoint = i;
break;
}
}
}
if (abs(currentIdx - nextIdx) == 1) {
// horizontal movement
endPoint[0] = nextIdx;
endPoint[1] = (nextIdx + 2) % 4;
} else {
// vertical movement
endPoint[0] = (nextIdx / 2) * 2;
endPoint[1] = endPoint[0] + 1;
}
// determine start position and direction
int pathIdx = 0;
while (defaultPath[pathIdx] != startPoint) {
pathIdx++;
}
int step = 1;
if (defaultPath[(pathIdx + 1) % 4] == endPoint[0] ||
defaultPath[(pathIdx + 1) % 4] == endPoint[1]) {
step = -1;
}
pathIdx += 4;
// process children
for (int i = 0; i < 4; ++i) {
int childNr = defaultPath[(pathIdx + (i * step)) % 4];
if (node->children[childNr] != 0) {
if (i == 3) {
// change direction at the end (following level above)
leafesInClusterOrderR(node->children[childNr], currentIdx, nextIdx,
lastVisited, f);
} else {
leafesInClusterOrderR(
node->children[childNr], defaultPath[(pathIdx + (i * step)) % 4],
defaultPath[(pathIdx + ((i + 1) * step)) % 4], lastVisited, f);
}
}
}
}
void QuadTreeDistribution::createAreaObjectCountList(
std::list<std::pair<double*, int> >* areaList) {
if (root) {
propagateDown(root, [areaList](QuadNode* node) {
double* rect = new double[4];
rect[0] = node->x;
rect[1] = node->y;
rect[2] = node->x + node->width;
rect[3] = node->y + node->height;
areaList->push_back(make_pair(rect, node->weight));
});
}
}
}