/******************************************************************************* * Copyright (c) 2012-present Jakub Kováč, Jozef Brandýs, Katarína Kotrlová, * Pavol Lukča, Ladislav Pápay, Viktor Tomkovič, Tatiana Tóthová * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package algvis.ds.dictionaries.btree; import java.awt.Color; import java.util.Hashtable; import algvis.core.DataStructure; import algvis.core.Node; import algvis.core.history.HashtableStoreSupport; import algvis.ui.Fonts; import algvis.ui.view.View; public class BNode extends Node { private int width; private int leftw; private int rightw; BNode parent = null; int numKeys = 1, numChildren = 0; int[] keys; BNode[] c; // View V; // statistics int nkeys = 1, nnodes = 1, height = 1; public BNode(DataStructure D, int key, int x, int y) { super(D, key, x, y); this.keys = new int[((BTree) D).order + 5]; c = new BNode[((BTree) D).order + 5]; this.keys[0] = key; numKeys = 1; // this.V = D.panel.S.V; width = _width(); } public BNode(DataStructure D, int key) { this(D, key, 0, UPY); } public BNode(BNode v) { this(v.D, v.keys[0], v.tox, v.toy); } public BNode(BNode u, BNode v, BNode w) { this(u.D, Node.NOKEY, v.tox, v.toy); int n1 = u.numKeys, n2 = w.numKeys; numKeys = n1 + 1 + n2; System.arraycopy(u.keys, 0, keys, 0, n1); keys[n1] = v.keys[0]; for (int i = 0; i < n2; ++i) { keys[n1 + 1 + i] = w.keys[i]; } n1 = u.numChildren; n2 = w.numChildren; numChildren = n1 + n2; System.arraycopy(u.c, 0, c, 0, n1); System.arraycopy(w.c, 0, c, n1, n2); for (int i = 0; i < numChildren; ++i) { c[i].parent = this; } width = _width(); } public boolean isRoot() { return parent == null; } public boolean isLeaf() { return numChildren == 0; } /* * public boolean isLeft() { return parent.left==this; } public void * linkleft(BSTNode v) { left = v; v.parent = this; } public void * linkright(BSTNode v) { right = v; v.parent = this; } public void * isolate() { left = right = parent = null; } */ public void calcTree() { nkeys = numKeys; nnodes = 1; for (int i = 0; i < numChildren; ++i) { c[i].calcTree(); nkeys += c[i].nkeys; nnodes += c[i].nnodes; } height = 1 + (isLeaf() ? 0 : c[0].height); } public void addLeaf(int x) { keys[numKeys++] = x; for (int i = numKeys - 1; i > 0; --i) { if (keys[i] < keys[i - 1]) { final int tmp = keys[i]; keys[i] = keys[i - 1]; keys[i - 1] = tmp; } } width = _width(); } public int order() { for (int i = 0; i < parent.numChildren; ++i) { if (parent.c[i] == this) { return i; } } return -5; // TODO: vypindat exception } public void add(int k, BNode v) { for (int i = numKeys; i > k; --i) { keys[i] = keys[i - 1]; c[i + 1] = c[i]; } ++numKeys; ++numChildren; keys[k] = v.keys[0]; c[k] = v.c[0]; c[k].parent = this; c[k + 1] = v.c[1]; c[k + 1].parent = this; width = _width(); } public boolean isIn(int x) { for (int i = 0; i < numKeys; ++i) { if (keys[i] == x) { return true; } } return false; } public BNode way(int x) { if (x < keys[0]) { return c[0]; } for (int i = 1; i < numKeys; ++i) { if (x < keys[i]) { return c[i]; } } return c[numKeys]; } public int search(int x) { if (x < keys[0]) { return 0; } for (int i = 1; i < numKeys; ++i) { if (x < keys[i]) { return i; } } return numKeys; } public BNode split() { final int k = numKeys, ku = numKeys / 2; // , kw = numKeys - ku - 1; final BNode u = new BNode(D, keys[0], tox, toy), v = new BNode(D, keys[ku], tox, toy), w = new BNode(D, keys[k - 1], tox, toy); for (int i = 1; i < ku; ++i) { u.addLeaf(keys[i]); } for (int i = ku + 1; i < k - 1; ++i) { w.addLeaf(keys[i]); } if (isLeaf()) { u.numChildren = w.numChildren = 0; } else { u.numChildren = (numChildren + 1) / 2; w.numChildren = numChildren / 2; for (int i = 0; i < u.numChildren; ++i) { u.c[i] = c[i]; u.c[i].parent = u; } for (int i = 0; i < w.numChildren; ++i) { w.c[i] = c[u.numChildren + i]; w.c[i].parent = w; } } u.parent = w.parent = v; v.numChildren = 2; v.parent = parent; v.c[0] = u; v.c[1] = w; u.width = u._width(); w.width = w._width(); u.x = u.tox = tox - u.width / 2 - Node.RADIUS; w.x = w.tox = tox + w.width / 2 + Node.RADIUS; return v; } public BNode del(int k) { int i = -1; while (keys[++i] != k) { } final int p = i; for (--numKeys; i < numKeys; i++) { keys[i] = keys[i + 1]; } width = _width(); return new BNode(D, k, tox - (numKeys + 1 - 2 * p) * Node.RADIUS, toy); } public BNode delMin() { final int r = keys[0]; --numKeys; System.arraycopy(keys, 1, keys, 0, numKeys); width = _width(); return new BNode(D, r, tox - (numKeys - 1) * Node.RADIUS, toy); } public BNode delMinCh() { final BNode r = c[0]; --numChildren; System.arraycopy(c, 1, c, 0, numChildren); width = _width(); return r; } public BNode delMax() { final BNode r = new BNode(D, keys[--numKeys], tox + (numKeys - 1) * Node.RADIUS, toy); width = _width(); return r; } public BNode delMaxCh() { final BNode r = c[--numChildren]; width = _width(); return r; } public void insMin(int k) { System.arraycopy(keys, 0, keys, 1, numKeys++); keys[0] = k; width = _width(); } public void insMinCh(BNode v) { System.arraycopy(c, 0, c, 1, numChildren++); c[0] = v; width = _width(); } public void insMax(int k) { keys[numKeys++] = k; width = _width(); } public void insMaxCh(BNode v) { c[numChildren++] = v; width = _width(); } public void replace(int x, int y) { int i = -1; while (keys[++i] != x) { } keys[i] = y; width = _width(); } String toString(int max) { if (numKeys == 0 || max == 0) { return ""; } String str = ""; if (keys[0] == INF) { str = "\u221e"; } else if (keys[0] == -INF) { str = "-\u221e"; } else { str = "" + keys[0]; } for (int i = 1; i < Math.min(numKeys, max); ++i) { str = str + " " + keys[i]; } return str; } @Override public String toString() { return toString(numKeys); } int _width() { if (keys[0] != Node.NOKEY && numKeys > 0) { return Math.max(Fonts.NORMAL.fm.stringWidth(toString()) + 4, 2 * Node.RADIUS); } else { return 2 * Node.RADIUS; } } int pos(int i) { if (i < 0) { return tox - D.panel.screen.V.stringWidth(toString(), Fonts.NORMAL) / 2 - Node.RADIUS; } if (i >= numKeys) { return tox + D.panel.screen.V.stringWidth(toString(), Fonts.NORMAL) / 2 + Node.RADIUS; } if (numKeys <= 1) { return tox; } final String s = toString(i); String t; if (i == 0) { t = "" + keys[0]; } else { t = " " + keys[i]; } return tox - D.panel.screen.V.stringWidth(toString(), Fonts.NORMAL) / 2 + D.panel.screen.V.stringWidth(s, Fonts.NORMAL) + D.panel.screen.V.stringWidth(t, Fonts.NORMAL) / 2; } @Override public void drawBg(View V) { V.setColor(getBgColor()); V.fillRoundRectangle(x, y, width / 2, Node.RADIUS, 2 * Node.RADIUS, 2 * Node.RADIUS); V.setColor(getFgColor()); V.drawRoundRectangle(x, y, width / 2, Node.RADIUS, 2 * Node.RADIUS, 2 * Node.RADIUS); // g.drawLine (x-leftw, y+2, x+rightw, y-2); } @Override public void drawKey(View V) { if (keys[0] != Node.NOKEY && numKeys > 0) { V.drawString(toString(), x, y, Fonts.NORMAL); } } public void drawTree(View v) { for (int i = 0; i < numChildren; ++i) { v.setColor(Color.black); /* * int xx, yy; if (i==0 || i==numChildren-1) { xx = x; yy = y; } * else { xx = (pos(i-1)+pos(i))/2; yy = y+D.RADIUS; } */ v.drawLine(x, y, c[i].x, c[i].y - Node.RADIUS); c[i].drawTree(v); } draw(v); } public void moveTree() { for (int i = 0; i < numChildren; ++i) { c[i].moveTree(); } move(); } void rebox() { if (numChildren == 0) { leftw = rightw = width / 2 + ((BTree) D).xspan; // numKeys * // D.RADIUS + // D.xspan; } else { if (numChildren % 2 == 0) { leftw = rightw = 0; } else { leftw = c[numChildren / 2].leftw; rightw = c[numChildren / 2].rightw; } for (int i = 0; i < numChildren / 2; ++i) { leftw += c[i].leftw + c[i].rightw; } for (int i = (numChildren + 1) / 2; i < numChildren; ++i) { rightw += c[i].leftw + c[i].rightw; } } } void reboxTree() { for (int i = 0; i < numChildren; ++i) { c[i].reboxTree(); } rebox(); } private void repos() { if (isRoot()) { goToRoot(); D.x1 = -leftw; D.x2 = rightw; D.y2 = this.toy; } if (this.toy > D.y2) { D.y2 = this.toy; } int x = this.tox, x2 = this.tox; final int y = this.toy + 2 * Node.RADIUS + ((BTree) D).yspan; if (numChildren == 0) { return; } if (numChildren % 2 == 0) { int k = numChildren / 2 - 1; c[k].goTo(x -= c[k].rightw, y); c[k].repos(); for (int i = k - 1; i >= 0; --i) { c[i].goTo(x -= c[i + 1].leftw + c[i].rightw, y); c[i].repos(); } c[++k].goTo(x2 += c[k].leftw, y); c[k].repos(); for (int i = k + 1; i < numChildren; ++i) { c[i].goTo(x2 += c[i - 1].rightw + c[i].leftw, y); c[i].repos(); } } else { final int k = numChildren / 2; c[k].goTo(x, y); c[k].repos(); for (int i = 1; i <= k; ++i) { c[k - i].goTo(x -= c[k - i].rightw + c[k - i + 1].leftw, y); c[k - i].repos(); c[k + i].goTo(x2 += c[k + i].leftw + c[k + i - 1].rightw, y); c[k + i].repos(); } } } public void _reposition() { reboxTree(); repos(); } int _goToX(BNode v) { final int x = keys[0]; int p = v.numKeys; for (int i = 0; i < p; ++i) { if (x <= v.keys[i]) { p = i; } } return (v.pos(p - 1) + v.pos(p)) / 2; } public void goTo(BNode v) { goTo(_goToX(v), v.toy); } public void goAbove(BNode v) { goTo(_goToX(v), v.toy - 2 * Node.RADIUS + 2); } public void goBelow(BNode v) { goTo(_goToX(v), v.toy + 2 * Node.RADIUS - 2); } /* * public void goToRoot() { if (((BTree)D).root == null) { goTo (D.rootx, * D.rooty); } else { goTo(_goToX(((BTree)D).root), D.rooty); } } * * public void goAboveRoot() { if (((BTree)D).root == null) { goTo (D.rootx, * D.rooty - 2*D.RADIUS); } else { goTo(_goToX(((BTree)D).root), * D.rooty-2*D.RADIUS); } } */ @Override public void storeState(Hashtable<Object, Object> state) { super.storeState(state); HashtableStoreSupport.store(state, hash + "parent", parent); HashtableStoreSupport.store(state, hash + "c", c.clone()); for (final BNode node : c) { if (node != null) { node.storeState(state); } } HashtableStoreSupport.store(state, hash + "numKeys", numKeys); HashtableStoreSupport.store(state, hash + "numChildren", numChildren); HashtableStoreSupport.store(state, hash + "keys", keys.clone()); HashtableStoreSupport.store(state, hash + "leftw", leftw); HashtableStoreSupport.store(state, hash + "rightw", rightw); HashtableStoreSupport.store(state, hash + "width", width); HashtableStoreSupport.store(state, hash + "nkeys", nkeys); HashtableStoreSupport.store(state, hash + "nnodes", nnodes); HashtableStoreSupport.store(state, hash + "height", height); } @Override public void restoreState(Hashtable<?, ?> state) { super.restoreState(state); final Object parent = state.get(hash + "parent"); if (parent != null) { this.parent = (BNode) HashtableStoreSupport.restore(parent); } final Object c = state.get(hash + "c"); if (c != null) { this.c = (BNode[]) HashtableStoreSupport.restore(c); } for (final BNode node : this.c) { if (node != null) { node.restoreState(state); } } final Object numKeys = state.get(hash + "numKeys"); if (numKeys != null) { this.numKeys = (Integer) HashtableStoreSupport.restore(numKeys); } final Object numChildren = state.get(hash + "numChildren"); if (numChildren != null) { this.numChildren = (Integer) HashtableStoreSupport .restore(numChildren); } final Object keys = state.get(hash + "keys"); if (keys != null) { this.keys = (int[]) HashtableStoreSupport.restore(keys); } final Object leftw = state.get(hash + "leftw"); if (leftw != null) { this.leftw = (Integer) HashtableStoreSupport.restore(leftw); } final Object rightw = state.get(hash + "rightw"); if (rightw != null) { this.rightw = (Integer) HashtableStoreSupport.restore(rightw); } final Object width = state.get(hash + "width"); if (width != null) { this.width = (Integer) HashtableStoreSupport.restore(width); } final Object nkeys = state.get(hash + "nkeys"); if (nkeys != null) { this.nkeys = (Integer) HashtableStoreSupport.restore(nkeys); } final Object height = state.get(hash + "height"); if (height != null) { this.height = (Integer) HashtableStoreSupport.restore(height); } final Object nnodes = state.get(hash + "nnodes"); if (nnodes != null) { this.nnodes = (Integer) HashtableStoreSupport.restore(nnodes); } } }