Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.livae.util.tree.BtreePage Maven / Gradle / Ivy
package com.livae.util.tree;
import com.livae.util.ResourcesFactory;
import java.util.Vector;
public class BtreePage> {
private BtreePage parentPage;
private int parentPosition;
private BtreePage[] offspringPages;
private k[] nodes;
private int size;
private ResourcesFactory> resourcesFactory;
protected BtreePage(int numberOfNodes, ResourcesFactory> resourcesFactory) {
size = 0;
//noinspection unchecked
nodes = (k[]) (new Comparable[numberOfNodes]);
//noinspection unchecked
offspringPages = new BtreePage[numberOfNodes + 1];
parentPosition = -1;
parentPage = null;
this.resourcesFactory = resourcesFactory;
}
private BtreePage(BtreePage pageToClone, BtreePage parentPage,
ResourcesFactory> resourcesFactory) {
this.resourcesFactory = resourcesFactory;
this.parentPage = parentPage;
parentPosition = pageToClone.parentPosition;
size = pageToClone.size;
//noinspection unchecked
nodes = (k[]) new Comparable[pageToClone.nodes.length];
System.arraycopy(pageToClone.nodes, 0, nodes, 0, nodes.length);
//noinspection unchecked
offspringPages = (BtreePage[]) new BtreePage[pageToClone.offspringPages.length];
for (int i = 0; i < offspringPages.length; i++) {
if (pageToClone.offspringPages[i] != null) {
offspringPages[i] = pageToClone.offspringPages[i].clone(this, resourcesFactory);
}
}
}
protected BtreePage clone(BtreePage parentPage,
ResourcesFactory> resourcesFactory) {
return new BtreePage(this, parentPage, resourcesFactory);
}
public void visitInOrder(BtreeVisitor visitor, int deep) {
for (int i = 0; i < size; i++) {
if (offspringPages[i] != null) {
offspringPages[i].visitInOrder(visitor, deep + 1);
}
visitor.visit(nodes[i], deep);
}
if (offspringPages[size] != null) {
offspringPages[size].visitInOrder(visitor, deep + 1);
}
}
private boolean isFull() {
return size == nodes.length;
}
private boolean isLeave() {
return offspringPages[0] == null;
}
private void setParentPage(BtreePage parent, int parentPosition) {
this.parentPage = parent;
this.parentPosition = parentPosition;
}
private void setRootPage() {
parentPage = null;
parentPosition = -1;
}
protected BtreePage getLastPage() {
if (isLeave()) {
return this;
} else {
return offspringPages[size].getLastPage();
}
}
protected BtreePage getFirstPage() {
if (isLeave()) {
return this;
} else {
return offspringPages[0].getFirstPage();
}
}
protected k getFirstFromPage() {
return nodes[0];
}
private int findOne(k object) {
int left = 0;
int right = size - 1;
int mid = (left + right + 1) / 2;
int comparison;
while (left <= right && (comparison = nodes[mid].compareTo(object)) != 0) {
if (comparison < 0) {
left = mid + 1;
} else { // comparison > 0
right = mid - 1;
}
mid = (left + right + 1) / 2;
}
return mid;
}
protected int findFirstPosition(k object) {
int pos = findOne(object);
while (pos > 0 && nodes[pos - 1].compareTo(object) == 0) {
pos--;
}
return pos;
}
protected int findNextPosition(k object) {
int pos = findOne(object);
while (pos < size && nodes[pos].compareTo(object) == 0) {
pos++;
}
return pos;
}
protected boolean contains(k object) {
return contains(findFirstPosition(object), object);
}
private boolean contains(int initialSearchPosition, k object) {
int pos = initialSearchPosition;
if (nodes[pos] == object) {
return true;
}
while (pos < size && nodes[pos].compareTo(object) == 0) {
if (offspringPages[pos].contains(0, object)) {
return true;
}
pos++;
if (nodes[pos] == object) {
return true;
}
}
return offspringPages[pos].contains(0, object);
}
public void add(k object) {
int pos = size == 0 ? 0 : findNextPosition(object);
if (isLeave()) {
insert(pos, object, null);
} else {
offspringPages[pos].add(object);
}
}
private void insert(int position, k object, BtreePage page) {
if (!isFull()) {
shiftRight(position, 1);
nodes[position] = object;
if (!isLeave()) {
offspringPages[position + 1] = page;
page.setParentPage(this, position + 1);
}
} else {
if (parentPage == null) {
splitRoot(position, object, page);
} else {
BtreePage left = null;
BtreePage right = null;
if (parentPosition > 0 &&
!(left = parentPage.offspringPages[parentPosition - 1]).isFull()) {
// rotate left and insert
left.nodes[left.size] = parentPage.nodes[parentPosition - 1];
left.size++;
if (position == 0) {
// current node to parent page
parentPage.nodes[parentPosition - 1] = object;
} else {
parentPage.nodes[parentPosition - 1] = nodes[0];
// make a gap
System.arraycopy(nodes, 1, nodes, 0, position - 1);
nodes[position - 1] = object;
}
if (!left.isLeave()) {
left.offspringPages[left.size] = offspringPages[0];
left.offspringPages[left.size].setParentPage(left, left.size);
if (position != 0) {
// make a gap
System.arraycopy(offspringPages, 1, offspringPages, 0, position);
for (int i = 0; i < position; i++) {
offspringPages[i].parentPosition = i;
}
}
offspringPages[position] = page;
offspringPages[position].setParentPage(this, position);
}
} else if (parentPosition < parentPage.size &&
!(right = parentPage.offspringPages[parentPosition + 1]).isFull()) {
// rotate right and insert
right.shiftRight(0, 1);
right.nodes[0] = parentPage.nodes[parentPosition];
if (position == size) {
// current node to parent page
parentPage.nodes[parentPosition] = object;
} else {
parentPage.nodes[parentPosition] = nodes[size - 1];
shiftRight(position, 1);
nodes[position] = object;
}
if (!right.isLeave()) {
if (position == size) {
right.offspringPages[0] = page;
} else {
right.offspringPages[0] = offspringPages[size];
offspringPages[position + 1] = page;
offspringPages[position + 1].setParentPage(this, position + 1);
}
right.offspringPages[0].setParentPage(right, 0);
}
} else {
if (left != null) {
// split with left
parentPage.split(parentPosition - 1, left.size + 1 + position, object,
page);
} else {
// split with right page
parentPage.split(parentPosition, position, object, page);
}
}
}
}
}
private void shiftRight(int initialPosition, int displacement) {
System.arraycopy(nodes, initialPosition, nodes, initialPosition + displacement,
size - initialPosition);
for (int i = initialPosition; i < initialPosition + displacement; i++) {
nodes[i] = null;
}
if (!isLeave()) {
System.arraycopy(offspringPages, initialPosition + 1, offspringPages,
initialPosition + 1 + displacement, size - initialPosition);
for (int i = initialPosition + 1; i < initialPosition + 1 + displacement; i++) {
offspringPages[i] = null;
}
for (int i = initialPosition + 1 + displacement; i < size + 1 + displacement; i++) {
offspringPages[i].parentPosition = i;
}
}
size += displacement;
}
private void shiftLeft(int initialPosition, int displacement) {
System.arraycopy(nodes, initialPosition, nodes, initialPosition - displacement,
size - initialPosition);
for (int i = size - displacement; i < size; i++) {
nodes[i] = null; // safety
}
if (!isLeave()) {
System.arraycopy(offspringPages, initialPosition, offspringPages,
initialPosition - displacement, size - initialPosition + 1);
for (int i = initialPosition - displacement; i < size - displacement + 1; i++) {
offspringPages[i].parentPosition = i;
}
for (int i = size - displacement + 1; i <= size; i++) {
offspringPages[i] = null;
}
}
size -= displacement;
}
protected boolean remove(k object) {
return remove(findFirstPosition(object), object);
}
private boolean remove(int initialSearchPosition, k object) {
int pos = initialSearchPosition;
if (pos < size && nodes[pos].compareTo(object) == 0) {
removeFromThisPage(pos);
return true;
}
while (pos < size && nodes[pos].compareTo(object) == 0) {
if (offspringPages[pos].remove(object)) {
return true;
}
pos++;
if (nodes[pos].compareTo(object) == 0) {
removeFromThisPage(pos);
return true;
}
}
if (offspringPages[pos].remove(object)) {
return true;
}
return false;
}
private void removeFromThisPage(int pos) {
if (isLeave()) {
size--;
// remove the element from the page
System.arraycopy(nodes, pos + 1, nodes, pos, size - pos);
nodes[size] = null; // just to avoid future problems
if (size < (nodes.length * 2 / 3) && parentPage != null) {
performPostRemovingOperations();
}
} else {
BtreePage lastPage = offspringPages[pos].getLastPage();
nodes[pos] = lastPage.nodes[lastPage.size - 1];
lastPage.removeFromThisPage(lastPage.size - 1);
}
}
private void rotateRight(int nodePos) {
BtreePage leftPage = offspringPages[nodePos];
BtreePage rightPage = offspringPages[nodePos + 1];
rightPage.shiftRight(0, 1);
rightPage.nodes[0] = nodes[nodePos];
nodes[nodePos] = leftPage.nodes[leftPage.size - 1];
leftPage.nodes[leftPage.size - 1] = null;
if (!rightPage.isLeave()) {
BtreePage[] rightPages = rightPage.offspringPages;
BtreePage[] leftPages = leftPage.offspringPages;
rightPages[1] = rightPages[0];
rightPages[1].parentPosition = 1;
rightPages[0] = leftPages[leftPage.size];
rightPages[0].setParentPage(rightPage, 0);
leftPages[leftPage.size] = null;
}
leftPage.size--;
}
private void rotateLeft(int nodePos) {
BtreePage leftPage = offspringPages[nodePos];
BtreePage rightPage = offspringPages[nodePos + 1];
leftPage.nodes[leftPage.size] = nodes[nodePos];
nodes[nodePos] = rightPage.nodes[0];
leftPage.size++;
if (!rightPage.isLeave()) {
BtreePage[] rightPages = rightPage.offspringPages;
BtreePage[] leftPages = leftPage.offspringPages;
leftPages[leftPage.size] = rightPages[0];
leftPages[leftPage.size].setParentPage(leftPage, leftPage.size);
}
rightPage.shiftLeft(1, 1);
}
private void splitRoot(int objectPosition, k object, BtreePage page) {
assert parentPage == null || size == nodes.length;
int nodesFirstPage = (nodes.length + 1) / 2;
int nodesSecondPage = nodes.length / 2;
BtreePage left = resourcesFactory.getResource();
BtreePage right = resourcesFactory.getResource();
left.size = nodesFirstPage;
right.size = nodesSecondPage;
boolean isLeave = isLeave();
if (objectPosition < nodesFirstPage) {
// object to left page
// left page
System.arraycopy(nodes, 0, left.nodes, 0, objectPosition);
left.nodes[objectPosition] = object;
int nodesLeftSecondPart = nodesFirstPage - objectPosition - 1;
System.arraycopy(nodes, objectPosition, left.nodes, objectPosition + 1,
nodesLeftSecondPart);
// root
nodes[0] = nodes[nodesFirstPage - 1];
// right page
System.arraycopy(nodes, nodesFirstPage, right.nodes, 0, nodesSecondPage);
if (!isLeave) {
// left page
System.arraycopy(offspringPages, 0, left.offspringPages, 0, objectPosition + 1);
left.offspringPages[objectPosition + 1] = page;
System.arraycopy(offspringPages, objectPosition + 1, left.offspringPages,
objectPosition + 2, nodesLeftSecondPart);
// right page
System.arraycopy(offspringPages, nodesFirstPage, right.offspringPages, 0,
nodesSecondPage + 1);
}
} else if (objectPosition > nodesFirstPage) {
// object to right
// left page
System.arraycopy(nodes, 0, left.nodes, 0, nodesFirstPage);
// root
nodes[0] = nodes[nodesFirstPage];
// right page
int nodesRightFirstPart = objectPosition - nodesFirstPage - 1;
System.arraycopy(nodes, nodesFirstPage + 1, right.nodes, 0, nodesRightFirstPart);
right.nodes[nodesRightFirstPart] = object;
int nodesRightSecondPart = nodesSecondPage - objectPosition + nodesFirstPage;
System.arraycopy(nodes, nodesFirstPage + nodesRightFirstPart + 1, right.nodes,
nodesRightFirstPart + 1, nodesRightSecondPart);
if (!isLeave) {
// left page
System.arraycopy(offspringPages, 0, left.offspringPages, 0, nodesFirstPage + 1);
// right page
System.arraycopy(offspringPages, nodesFirstPage + 1, right.offspringPages, 0,
nodesRightFirstPart + 1);
right.offspringPages[nodesRightFirstPart + 1] = page;
System.arraycopy(offspringPages, nodesFirstPage + nodesRightFirstPart + 2,
right.offspringPages, nodesRightFirstPart + 2,
nodesRightSecondPart);
}
} else {
// object to root page
// left page
System.arraycopy(nodes, 0, left.nodes, 0, nodesFirstPage);
// root
nodes[0] = object;
// right page
System.arraycopy(nodes, nodesFirstPage, right.nodes, 0, nodesSecondPage);
if (!isLeave) {
// left page
System.arraycopy(offspringPages, 0, left.offspringPages, 0, nodesFirstPage + 1);
// right page
right.offspringPages[0] = page;
System.arraycopy(offspringPages, nodesFirstPage + 1, right.offspringPages, 1,
nodesSecondPage);
}
}
offspringPages[0] = left;
offspringPages[1] = right;
offspringPages[0].setParentPage(this, 0);
offspringPages[1].setParentPage(this, 1);
size = 1;
int index = 1;
while (index < nodes.length) {
nodes[index] = null;
index++;
offspringPages[index] = null;
}
if (!isLeave) {
BtreePage[] leftPages = left.offspringPages;
for (int i = 0; i < left.size + 1; i++) {
leftPages[i].setParentPage(left, i);
}
BtreePage[] rightPages = right.offspringPages;
for (int i = 0; i < right.size + 1; i++) {
rightPages[i].setParentPage(right, i);
}
}
}
private void split(int pagePosition, int objectPositionInMergedPage, k object,
BtreePage page) {
BtreePage left = offspringPages[pagePosition];
BtreePage right = offspringPages[pagePosition + 1];
k nodeInsertParent;
BtreePage middle = resourcesFactory.getResource();
int totalSize = left.size + right.size;
int nodesSecondPage = totalSize / 3;
int nodesFirstPage = (totalSize - nodesSecondPage + 1) / 2;
int nodesThirdPage = totalSize - nodesFirstPage - nodesSecondPage;
boolean isLeave = left.isLeave();
if (objectPositionInMergedPage < nodesFirstPage) {
nodeInsertParent = splitNodeInLeftPage(pagePosition, objectPositionInMergedPage, object,
page, left, right, middle, nodesFirstPage,
nodesThirdPage, isLeave);
} else if (objectPositionInMergedPage == nodesFirstPage) {
nodeInsertParent = splitNodeInParentLeft(pagePosition, object, page, left, right,
middle, nodesFirstPage, nodesThirdPage,
isLeave);
} else if (objectPositionInMergedPage < nodesFirstPage + 1 + nodesSecondPage) {
nodeInsertParent = splitNodeInCenterPage(pagePosition, objectPositionInMergedPage,
object, page, left, right, middle,
nodesFirstPage, nodesThirdPage, isLeave);
} else if (objectPositionInMergedPage == nodesFirstPage + 1 + nodesSecondPage) {
nodeInsertParent = splitNodeInParentRight(pagePosition, object, page, left, right,
middle, nodesFirstPage, nodesThirdPage,
isLeave);
} else {
nodeInsertParent = splitNodeInRightPage(pagePosition, objectPositionInMergedPage,
object, page, left, right, middle,
nodesFirstPage, nodesSecondPage, nodesThirdPage,
isLeave);
}
middle.size = nodesSecondPage;
if (!isLeave) {
for (int i = 0; i <= middle.size; i++) {
middle.offspringPages[i].setParentPage(middle, i);
}
}
insert(pagePosition, nodeInsertParent, middle);
}
private k splitNodeInLeftPage(int pagePosition, int objectPositionInMergedPage, k object,
BtreePage page, BtreePage left, BtreePage right,
BtreePage middle, int nodesFirstPage, int nodesThirdPage,
boolean isLeave) {
k nodeInsertParent;// inside left page
nodeInsertParent = left.nodes[nodesFirstPage - 1];
int nodesFromLeft = left.size - nodesFirstPage;
int nodesFromRight = right.size - nodesThirdPage - 1;
// centre page
System.arraycopy(left.nodes, nodesFirstPage, middle.nodes, 0, nodesFromLeft);
middle.nodes[nodesFromLeft] = nodes[pagePosition];
nodes[pagePosition] = right.nodes[nodesFromRight];
System.arraycopy(right.nodes, 0, middle.nodes, nodesFromLeft + 1, nodesFromRight);
if (!isLeave) {
System.arraycopy(left.offspringPages, nodesFirstPage, middle.offspringPages, 0,
nodesFromLeft + 1);
System.arraycopy(right.offspringPages, 0, middle.offspringPages, nodesFromLeft + 1,
nodesFromRight + 1);
}
// left
for (int i = nodesFirstPage - 1; i < left.size; i++) {
left.nodes[i] = null;
left.offspringPages[i + 1] = null;
}
left.size = nodesFirstPage - 1;
left.insert(objectPositionInMergedPage, object, page);
// right
right.shiftLeft(nodesFromRight + 1, nodesFromRight + 1);
return nodeInsertParent;
}
private k splitNodeInParentLeft(int pagePosition, k object, BtreePage page,
BtreePage left, BtreePage right, BtreePage middle,
int nodesFirstPage, int nodesThirdPage, boolean isLeave) {
k nodeInsertParent;// in parent page in left position
nodeInsertParent = object;
int nodesFromLeft = left.size - nodesFirstPage;
int nodesFromRight = right.size - nodesThirdPage - 1;
// centre page
System.arraycopy(left.nodes, nodesFirstPage, middle.nodes, 0, nodesFromLeft);
middle.nodes[nodesFromLeft] = nodes[pagePosition];
nodes[pagePosition] = right.nodes[nodesFromRight];
System.arraycopy(right.nodes, 0, middle.nodes, nodesFromLeft + 1, nodesFromRight);
if (!isLeave) {
middle.offspringPages[0] = page;
page.setParentPage(middle, 0);
System.arraycopy(left.offspringPages, nodesFirstPage + 1, middle.offspringPages, 1,
nodesFromLeft);
System.arraycopy(right.offspringPages, 0, middle.offspringPages, nodesFromLeft + 1,
nodesFromRight + 1);
}
// left
for (int i = nodesFirstPage; i < left.size; i++) {
left.nodes[i] = null;
left.offspringPages[i + 1] = null;
}
left.size = nodesFirstPage;
// right
right.shiftLeft(nodesFromRight + 1, nodesFromRight + 1);
return nodeInsertParent;
}
private k splitNodeInCenterPage(int pagePosition, int objectPositionInMergedPage, k object,
BtreePage page, BtreePage left, BtreePage right,
BtreePage middle, int nodesFirstPage, int nodesThirdPage,
boolean isLeave) {
k nodeInsertParent;// inside center page
nodeInsertParent = left.nodes[nodesFirstPage];
int nodesFromLeft = left.size - nodesFirstPage - 1;
int nodesFromRight = right.size - nodesThirdPage - 1;
// centre page
int centerPagePos = objectPositionInMergedPage - nodesFirstPage - 1;
if (centerPagePos <= nodesFromLeft) {
// inside the left nodes
System.arraycopy(left.nodes, nodesFirstPage, middle.nodes, 0, centerPagePos);
middle.nodes[centerPagePos] = object;
System.arraycopy(left.nodes, nodesFirstPage + centerPagePos + 1, middle.nodes,
centerPagePos + 1, nodesFromLeft - centerPagePos);
middle.nodes[nodesFromLeft + 1] = nodes[pagePosition];
nodes[pagePosition] = right.nodes[nodesFromRight];
System.arraycopy(right.nodes, 0, middle.nodes, nodesFromLeft + 2, nodesFromRight);
if (!isLeave) {
System.arraycopy(left.offspringPages, nodesFirstPage + 1, middle.offspringPages, 0,
centerPagePos + 1);
middle.offspringPages[centerPagePos + 1] = page;
System.arraycopy(left.offspringPages, nodesFirstPage + centerPagePos + 2,
middle.offspringPages, centerPagePos + 2,
nodesFromLeft - centerPagePos);
System.arraycopy(right.offspringPages, 0, middle.offspringPages, nodesFromLeft + 2,
nodesFromRight + 1);
}
} else if (centerPagePos == nodesFromLeft + 1) {
// after the parent node
System.arraycopy(left.nodes, nodesFirstPage + 1, middle.nodes, 0, nodesFromLeft);
middle.nodes[nodesFromLeft] = nodes[pagePosition];
middle.nodes[centerPagePos] = object;
nodes[pagePosition] = right.nodes[nodesFromRight];
System.arraycopy(right.nodes, 0, middle.nodes, nodesFromLeft + 2, nodesFromRight);
if (!isLeave) {
System.arraycopy(left.offspringPages, nodesFirstPage + 1, middle.offspringPages, 0,
nodesFromLeft + 1);
middle.offspringPages[centerPagePos] = right.offspringPages[0];
middle.offspringPages[centerPagePos + 1] = page;
System.arraycopy(right.offspringPages, 1, middle.offspringPages, centerPagePos + 2,
nodesFromRight);
}
} else {
// inside the right nodes
System.arraycopy(left.nodes, nodesFirstPage + 1, middle.nodes, 0, nodesFromLeft);
middle.nodes[nodesFromLeft] = nodes[pagePosition];
nodes[pagePosition] = right.nodes[nodesFromRight];
int splitRightNodes = centerPagePos - nodesFromLeft - 1;
System.arraycopy(right.nodes, 0, middle.nodes, nodesFromLeft + 1, splitRightNodes);
middle.nodes[centerPagePos] = object;
System.arraycopy(right.nodes, 0, middle.nodes, centerPagePos + 1,
nodesFromRight - splitRightNodes);
if (!isLeave) {
System.arraycopy(left.offspringPages, nodesFirstPage + 1, middle.offspringPages, 0,
nodesFromLeft + 1);
System.arraycopy(right.offspringPages, 0, middle.offspringPages, nodesFromLeft + 1,
splitRightNodes + 1);
middle.offspringPages[centerPagePos + 1] = page;
System.arraycopy(right.offspringPages, splitRightNodes + 1, middle.offspringPages,
centerPagePos + 2, nodesFromRight - splitRightNodes);
}
}
// left
for (int i = nodesFirstPage; i < left.size; i++) {
left.nodes[i] = null;
left.offspringPages[i + 1] = null;
}
left.size = nodesFirstPage;
// right
if (!isLeave) {
right.offspringPages[0] = right.offspringPages[nodesFromRight];
right.offspringPages[0].setParentPage(right, 0);
}
right.shiftLeft(nodesFromRight + 1, nodesFromRight + 1);
return nodeInsertParent;
}
private k splitNodeInParentRight(int pagePosition, k object, BtreePage page,
BtreePage left, BtreePage right, BtreePage middle,
int nodesFirstPage, int nodesThirdPage, boolean isLeave) {
k nodeInsertParent;// in parent page in right position
nodeInsertParent = left.nodes[nodesFirstPage];
int nodesFromLeft = left.size - nodesFirstPage - 1;
int nodesFromRight = right.size - nodesThirdPage;
// centre page
System.arraycopy(left.nodes, nodesFirstPage + 1, middle.nodes, 0, nodesFromLeft);
middle.nodes[nodesFromLeft] = nodes[pagePosition];
nodes[pagePosition] = object;
System.arraycopy(right.nodes, 0, middle.nodes, nodesFromLeft + 1, nodesFromRight);
if (!isLeave) {
System.arraycopy(left.offspringPages, nodesFirstPage + 1, middle.offspringPages, 0,
nodesFromLeft + 1);
System.arraycopy(right.offspringPages, 0, middle.offspringPages, nodesFromLeft + 1,
nodesFromRight + 1);
}
// left
for (int i = nodesFirstPage; i < left.size; i++) {
left.nodes[i] = null;
left.offspringPages[i + 1] = null;
}
left.size = nodesFirstPage;
// right
right.shiftLeft(nodesFromRight, nodesFromRight);
if (!isLeave) {
right.offspringPages[0] = page;
page.setParentPage(right, 0);
}
return nodeInsertParent;
}
private k splitNodeInRightPage(int pagePosition, int objectPositionInMergedPage, k object,
BtreePage page, BtreePage left, BtreePage right,
BtreePage middle, int nodesFirstPage, int nodesSecondPage,
int nodesThirdPage, boolean isLeave) {
k nodeInsertParent;// inside right page
nodeInsertParent = left.nodes[nodesFirstPage];
int nodesFromLeft = left.size - nodesFirstPage - 1;
int nodesFromRight = right.size - nodesThirdPage;
// centre page
System.arraycopy(left.nodes, nodesFirstPage + 1, middle.nodes, 0, nodesFromLeft);
middle.nodes[nodesFromLeft] = nodes[pagePosition];
nodes[pagePosition] = right.nodes[nodesFromRight];
System.arraycopy(right.nodes, 0, middle.nodes, nodesFromLeft + 1, nodesFromRight);
if (!isLeave) {
System.arraycopy(left.offspringPages, nodesFirstPage + 1, middle.offspringPages, 0,
nodesFromLeft + 1);
System.arraycopy(right.offspringPages, 0, middle.offspringPages, nodesFromLeft + 1,
nodesFromRight + 1);
}
// left
for (int i = nodesFirstPage; i < left.size; i++) {
left.nodes[i] = null;
left.offspringPages[i + 1] = null;
}
left.size = nodesFirstPage;
// right
int rightPos = objectPositionInMergedPage - (nodesFirstPage + nodesSecondPage + 2);
int shiftSize = nodesFromRight + 1;
System.arraycopy(right.nodes, shiftSize, right.nodes, 0, rightPos);
right.nodes[rightPos] = object;
System.arraycopy(right.nodes, shiftSize + rightPos, right.nodes, rightPos + 1,
right.size - shiftSize - rightPos);
if (!isLeave) {
System.arraycopy(right.offspringPages, shiftSize, right.offspringPages, 0,
rightPos + 1);
right.offspringPages[rightPos + 1] = page;
page.setParentPage(right, rightPos + 1);
System.arraycopy(right.offspringPages, shiftSize + rightPos + 1, right.offspringPages,
rightPos + 2, right.size - shiftSize - rightPos);
for (int i = 0; i <= nodesThirdPage; i++) {
right.offspringPages[i].setParentPage(right, i);
}
}
for (int i = nodesThirdPage; i < right.size; i++) {
right.nodes[i] = null;
right.offspringPages[i + 1] = null;
}
right.size = nodesThirdPage;
return nodeInsertParent;
}
private void balanceThreePages(int middlePagePosition) {
// check if merge makes sense
if (offspringPages[middlePagePosition - 1].size +
offspringPages[middlePagePosition].size +
offspringPages[middlePagePosition + 1].size + 1 <= nodes.length * 2) {
merge(middlePagePosition);
if (parentPage != null) {
performPostRemovingOperations();
}
} else {
int minNodes = nodes.length * 2 / 3;
// rotate to left until everything is ok
if (offspringPages[middlePagePosition - 1].size < minNodes) {
rotateLeft(middlePagePosition - 1);
}
if (offspringPages[middlePagePosition].size < minNodes) {
rotateLeft(middlePagePosition);
}
// rotate to the right until everything is ok
if (offspringPages[middlePagePosition + 1].size < minNodes) {
rotateRight(middlePagePosition);
}
if (offspringPages[middlePagePosition].size < minNodes) {
rotateRight(middlePagePosition - 1);
}
}
}
private void balanceTwoPagesRoot() {
if (offspringPages[0].size + offspringPages[1].size + 1 <= nodes.length) {
mergeTwoPagesRoot();
} else {
int minNodes = nodes.length * 2 / 3;
if (offspringPages[0].size < minNodes) {
rotateLeft(0);
} else if (offspringPages[1].size < minNodes) {
rotateRight(0);
}
}
}
private void balanceThreePagesRoot() {
if (offspringPages[0].size + offspringPages[1].size + offspringPages[2].size + 2 <=
nodes.length) {
merge(1);
} else {
balanceThreePages(1);
}
}
private void performPostRemovingOperations() {
if (parentPage.parentPage == null && parentPage.size == 1) {
// special case parent page is root and could merge it self
parentPage.balanceTwoPagesRoot();
} else if (parentPage.parentPage == null && parentPage.size == 2) {
parentPage.balanceThreePagesRoot();
} else {
if (parentPage.size == 1 && parentPage.parentPage.parentPage == null) {
if (parentPage.parentPosition == 0) {
parentPage.parentPage.rotateLeft(0);
} else {
parentPage.parentPage.rotateRight(parentPage.parentPosition);
}
}
if (parentPosition == 0) {
// first page in parent
parentPage.balanceThreePages(1);
} else if (parentPosition == parentPage.size) {
// last page in parent
parentPage.balanceThreePages(parentPosition - 1);
} else {
// middle page in parent
parentPage.balanceThreePages(parentPosition);
}
}
}
private void mergeTwoPagesRoot() {
BtreePage left = offspringPages[0];
BtreePage right = offspringPages[1];
assert size == 1 && left.size + right.size + 1 <= nodes.length;
nodes[left.size] = nodes[0];
System.arraycopy(left.nodes, 0, nodes, 0, left.size);
System.arraycopy(right.nodes, 0, nodes, left.size + 1, right.size);
size = left.size + right.size + 1;
if (!left.isLeave()) {
System.arraycopy(left.offspringPages, 0, offspringPages, 0, left.size + 1);
System.arraycopy(right.offspringPages, 0, offspringPages, left.size + 1,
right.size + 1);
for (int i = 0; i <= size; i++) {
offspringPages[i].setParentPage(this, i);
}
} else {
offspringPages[0] = null;
offspringPages[1] = null;
}
clear(left);
clear(right);
}
private void merge(int middlePagePos) {
BtreePage left = offspringPages[middlePagePos - 1];
BtreePage middle = offspringPages[middlePagePos];
BtreePage right = offspringPages[middlePagePos + 1];
int sumSizes = left.size + middle.size + right.size + 1;
assert sumSizes <= nodes.length * 2;
int leftNodes = sumSizes / 2 - left.size - 1;
int rightNodes = middle.size - leftNodes;
if (leftNodes < 0) {
leftNodes = 0;
rightNodes = middle.size;
}
if (left.size < left.nodes.length) {
left.nodes[left.size] = nodes[middlePagePos - 1];
System.arraycopy(middle.nodes, 0, left.nodes, left.size + 1, leftNodes);
left.size += leftNodes + 1;
right.shiftRight(0, rightNodes);
if (rightNodes > 0) {
right.nodes[rightNodes - 1] = nodes[middlePagePos];
System.arraycopy(middle.nodes, leftNodes + 1, right.nodes, 0, rightNodes - 1);
nodes[middlePagePos] = middle.nodes[leftNodes];
}
if (!middle.isLeave()) {
System.arraycopy(middle.offspringPages, 0, left.offspringPages,
left.size - leftNodes, leftNodes + 1);
for (int i = left.size - leftNodes; i <= left.size; i++) {
left.offspringPages[i].setParentPage(left, i);
}
if (rightNodes > 0) {
right.offspringPages[rightNodes] = right.offspringPages[0]; // not done in shift right
System.arraycopy(middle.offspringPages, leftNodes + 1, right.offspringPages, 0,
rightNodes);
for (int i = 0; i <= rightNodes; i++) {
right.offspringPages[i].setParentPage(right, i);
}
}
}
} else {
right.shiftRight(0, rightNodes + 1);
right.nodes[rightNodes] = nodes[middlePagePos];
nodes[middlePagePos] = nodes[middlePagePos - 1];
System.arraycopy(middle.nodes, 0, right.nodes, 0, rightNodes);
if (!middle.isLeave()) {
right.offspringPages[rightNodes +
1] = right.offspringPages[0]; // not done in shift right
System.arraycopy(middle.offspringPages, 0, right.offspringPages, 0, rightNodes + 1);
for (int i = 0; i <= rightNodes + 1; i++) {
right.offspringPages[i].setParentPage(right, i);
}
}
}
offspringPages[middlePagePos] = left;
shiftLeft(middlePagePos, 1);
clear(middle);
}
public String getDebugString() {
BtreePrinter v = new BtreePrinter();
visitInOrder(v, 0);
return v.getString();
}
protected void checkIntegrity() {
if (parentPage != null) {
if (parentPage.offspringPages[parentPosition] != this) {
System.err.println(getDebugString());
throw new RuntimeException("parent page of an offspring is not this");
}
if (parentPage.parentPage == null) {
if (size < nodes.length / 2 - 1) {
System.err.println(getDebugString());
throw new RuntimeException("wrong minimum size");
}
} else {
if (size < nodes.length * 2 / 3) {
System.err.println(getDebugString());
throw new RuntimeException("wrong minimum size");
}
}
}
for (int i = 0; i < size; i++) {
if (nodes[i] == null) {
System.err.println(getDebugString());
throw new RuntimeException();
}
}
if (offspringPages[0] != null || offspringPages[1] != null || offspringPages[2] != null) {
for (int i = 0; i <= size; i++) {
if (offspringPages[i] == null) {
System.err.println(getDebugString());
throw new RuntimeException("offspring page is null");
} else if (offspringPages[i].parentPage != this) {
System.err.println(getDebugString());
throw new RuntimeException("offspring parent page is not this");
} else if (offspringPages[i].size == 0) {
System.err.println(getDebugString());
throw new RuntimeException("offspring page is empty");
} else {
offspringPages[i].checkIntegrity();
}
}
}
}
protected void clear(BtreePage page) {
for (int i = 0; i < page.nodes.length; i++) {
page.nodes[i] = null;
page.offspringPages[i] = null;
}
page.offspringPages[page.nodes.length] = null;
page.size = 0;
resourcesFactory.releaseResource(page);
}
protected void clear() {
for (int i = 0; i < size; i++) {
nodes[i] = null;
}
for (int i = 0; i <= size; i++) {
if (offspringPages[i] != null) {
offspringPages[i].clear();
resourcesFactory.releaseResource(offspringPages[i]);
}
offspringPages[i] = null;
}
}
protected TestUtils getTestUtils() {
return new TestUtils();
}
private class BtreePrinter implements BtreeVisitor {
private Vector stringBuilders;
private int charactersAdded;
private BtreePrinter() {
stringBuilders = new Vector();
charactersAdded = 0;
}
public void visit(k object, int deep) {
while (stringBuilders.size() <= deep) {
StringBuilder sb = new StringBuilder();
stringBuilders.add(sb);
}
StringBuilder sb = stringBuilders.get(deep);
while (sb.length() < charactersAdded) {
sb.append(' ');
}
String objectString = "null ";
if (object != null) {
objectString = object.toString() + " ";
}
sb.append(objectString);
charactersAdded += objectString.length();
}
protected String getString() {
StringBuilder sb = new StringBuilder();
for (StringBuilder stringBuilder : stringBuilders) {
sb.append(stringBuilder).append('\n');
}
return sb.toString();
}
}
class TestUtils {
public void setNode(BtreePage o, int pos, k object) {
o.nodes[pos] = object;
}
public void setOffspringPage(BtreePage o, int pos, BtreePage page) {
o.offspringPages[pos] = page;
o.offspringPages[pos].setParentPage(o, pos);
}
public void size(BtreePage o, int size) {
o.size = size;
}
}
}