Skip to content

Commit 6f7415e

Browse files
authored
Merge pull request #307 from jacksherling/build-b-tree
add build tree functionality
2 parents 6222c96 + 7eb621f commit 6f7415e

File tree

2 files changed

+192
-11
lines changed

2 files changed

+192
-11
lines changed

src/algo/Algorithm.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ export default class Algorithm {
327327
return input.substr(-maxLen);
328328
}
329329

330-
returnSubmit(field, funct, maxsize, intOnly) {
330+
returnSubmit(field, funct, maxsize, intOnly, ignoreMaxSize = false) {
331331
if (maxsize !== undefined) {
332332
field.size = maxsize;
333333
}
@@ -340,6 +340,7 @@ export default class Algorithm {
340340
// Netscape/Firefox/Opera
341341
keyASCII = event.which;
342342
}
343+
const sizeGood = ignoreMaxSize || field.value.length < maxsize;
343344

344345
if (keyASCII === 13 && funct != null) {
345346
funct();
@@ -352,7 +353,7 @@ export default class Algorithm {
352353
) {
353354
return false;
354355
} else if (
355-
(maxsize !== undefined && field.value.length >= maxsize) ||
356+
!sizeGood ||
356357
(intOnly &&
357358
!((keyASCII >= 48 && keyASCII <= 57) || (keyASCII >= 96 && keyASCII <= 105)))
358359
) {

src/algo/BTree.js

Lines changed: 189 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import Algorithm, {
3333
} from './Algorithm.js';
3434
import { act } from '../anim/AnimationMain';
3535

36+
const INFO_MSG_X = 25;
37+
const INFO_MSG_Y = 15;
38+
3639
const FIRST_PRINT_POS_X = 50;
3740
const PRINT_VERTICAL_GAP = 20;
3841
const PRINT_MAX = 990;
@@ -74,12 +77,16 @@ export default class BTree extends Algorithm {
7477
this.cmd(act.createLabel, this.messageID, '', MESSAGE_X, MESSAGE_Y, 0);
7578
this.moveLabel1ID = this.nextIndex++;
7679
this.moveLabel2ID = this.nextIndex++;
80+
this.infoLabelID = this.nextIndex++;
81+
this.cmd(act.createLabel, this.infoLabelID, '', INFO_MSG_X, INFO_MSG_Y, 0);
7782

7883
this.animationManager.startNewAnimation(this.commands);
7984
this.animationManager.skipForward();
8085
this.animationManager.clearHistory();
8186
this.commands = [];
8287

88+
this.resetIndex = 4;
89+
8390
this.first_print_pos_y = h - 3 * PRINT_VERTICAL_GAP;
8491

8592
this.xPosOfNextLabel = 100;
@@ -135,11 +142,11 @@ export default class BTree extends Algorithm {
135142
this.findButton.onclick = this.findCallback.bind(this);
136143
this.controls.push(this.findButton);
137144

138-
addDivisorToAlgorithmBar();
145+
// addDivisorToAlgorithmBar();
139146

140-
this.printButton = addControlToAlgorithmBar('Button', 'Print');
141-
this.printButton.onclick = this.printCallback.bind(this);
142-
this.controls.push(this.printButton);
147+
// this.printButton = addControlToAlgorithmBar('Button', 'Print');
148+
// this.printButton.onclick = this.printCallback.bind(this);
149+
// this.controls.push(this.printButton);
143150

144151
addDivisorToAlgorithmBar();
145152

@@ -156,9 +163,9 @@ export default class BTree extends Algorithm {
156163
this.controls.push(this.clearButton);
157164

158165
addDivisorToAlgorithmBar();
159-
addLabelToAlgorithmBar('Promote with');
166+
addLabelToAlgorithmBar('Promote');
160167
const splitIndexButtonList = addRadioButtonGroupToAlgorithmBar(
161-
['second data', 'third data'],
168+
['second', 'third'],
162169
'Split Index',
163170
);
164171

@@ -183,10 +190,180 @@ export default class BTree extends Algorithm {
183190
this.succButton.onclick = this.succCallback.bind(this);
184191
this.succButton.checked = true;
185192
this.predSucc = 'succ';
193+
194+
addDivisorToAlgorithmBar();
195+
196+
const verticalGroup3 = addGroupToAlgorithmBar(false);
197+
addLabelToAlgorithmBar(
198+
'| separated list of nodes with comma separated keys (e.g. "1,3|0|2|4")',
199+
verticalGroup3,
200+
);
201+
202+
const horizontalGroup = addGroupToAlgorithmBar(true, verticalGroup3);
203+
204+
this.buildTreeField = addControlToAlgorithmBar('Text', '', horizontalGroup);
205+
this.buildTreeField.onkeydown = this.returnSubmit(
206+
this.buildTreeField,
207+
this.buildTreeCallback.bind(this),
208+
30,
209+
false,
210+
true,
211+
);
212+
this.controls.push(this.buildTreeField);
213+
214+
this.buildTreeButton = addControlToAlgorithmBar('Button', 'Build Tree', horizontalGroup);
215+
this.buildTreeButton.onclick = this.buildTreeCallback.bind(this);
216+
this.controls.push(this.buildTreeButton);
217+
}
218+
219+
buildTreeCallback() {
220+
const insertedValue = this.buildTreeField.value;
221+
this.implementAction(this.clearTree.bind(this));
222+
this.insertField.value = '';
223+
this.implementAction(this.buildTree.bind(this), insertedValue);
224+
}
225+
226+
buildTree(insertedValue) {
227+
this.commands = [];
228+
this.cmd(act.setText, this.infoLabelID, '');
229+
230+
const nodes = insertedValue.split('|').map(n => n.split(',').map(q => parseInt(q)));
231+
232+
this.treeRoot = new BTreeNode(this.nextIndex++, this.starting_x, STARTING_Y);
233+
this.cmd(
234+
act.createBTreeNode,
235+
this.treeRoot.graphicID,
236+
WIDTH_PER_ELEM,
237+
NODE_HEIGHT,
238+
1,
239+
this.starting_x,
240+
STARTING_Y,
241+
BACKGROUND_COLOR,
242+
FOREGROUND_COLOR,
243+
);
244+
this.treeRoot.keys = nodes[0];
245+
this.treeRoot.numKeys = nodes[0].length;
246+
this.treeRoot.children = [];
247+
this.cmd(act.setText, this.treeRoot.graphicID, nodes[0].join(' '), 0);
248+
this.cmd(act.setNumElements, this.treeRoot.graphicID, nodes[0].length);
249+
this.resizeTree();
250+
for (let i = 0; i < nodes[0].length; i++) {
251+
this.cmd(act.setText, this.treeRoot.graphicID, nodes[0][i], i);
252+
this.treeRoot.numKeys = nodes[0].length;
253+
}
254+
255+
for (let i = 1; i < nodes.length; i++) {
256+
const newNode = new BTreeNode(this.nextIndex++, this.starting_x, STARTING_Y);
257+
newNode.keys = nodes[i];
258+
newNode.numKeys = nodes[i].length;
259+
newNode.children = [];
260+
this.buildTreeRecurse(this.treeRoot, nodes[i][0], newNode);
261+
}
262+
263+
const problem = this.treeHasProblem(this.treeRoot);
264+
if (problem) {
265+
this.commands = [];
266+
this.cmd(act.setText, this.infoLabelID, problem);
267+
this.reset();
268+
this.shake(this.buildTreeButton);
269+
return this.commands;
270+
}
271+
272+
this.cmd(act.setText, this.messageID, '');
273+
274+
return this.commands;
275+
}
276+
277+
treeHasProblem(node) {
278+
if (node == null) {
279+
return '';
280+
}
281+
if (node.numKeys > this.max_keys) {
282+
return `Nodes can have at most ${this.max_keys} keys`;
283+
}
284+
for (let i = 0; i < node.numKeys - 1; i++) {
285+
if (node.keys[i] >= node.keys[i + 1]) {
286+
return `Tree must respect order property within nodes. ${node.keys[i]} >= ${
287+
node.keys[i + 1]
288+
}`;
289+
}
290+
}
291+
if (node.isLeaf) {
292+
return '';
293+
}
294+
if (node.numKeys !== node.children.length - 1) {
295+
return `Node with ${node.numKeys} keys must have ${
296+
node.numKeys + 1
297+
} children if not a leaf`;
298+
}
299+
for (let i = 0; i < node.numKeys; i++) {
300+
const key = node.keys[i];
301+
const c1 = node.children[i];
302+
const c2 = node.children[i + 1];
303+
for (const k of c1.keys) {
304+
if (k >= key) return `Tree must respect order property. ${k} >= ${key}`;
305+
}
306+
for (const k of c2.keys) {
307+
if (k <= key) return `Tree must respect order property. ${k} <= ${key}`;
308+
}
309+
}
310+
311+
for (const child of node.children) {
312+
const childProblem = this.treeHasProblem(child);
313+
if (childProblem) return childProblem;
314+
}
315+
return '';
316+
}
317+
318+
// ex 2,9,13|1|5,6,7|11,12|14,16
319+
buildTreeRecurse(node, val, nodeToAdd) {
320+
let index = 0;
321+
while (index < node.numKeys && node.keys[index] < val) {
322+
index++;
323+
}
324+
const nextNode = node.children[index];
325+
if (nextNode == null) {
326+
node.isLeaf = false;
327+
node.children.push(nodeToAdd);
328+
nodeToAdd.y = node.y + 100;
329+
nodeToAdd.x = node.x + (index - 1.5) * 150;
330+
nodeToAdd.parent = node;
331+
332+
this.cmd(
333+
act.createBTreeNode,
334+
nodeToAdd.graphicID,
335+
WIDTH_PER_ELEM,
336+
NODE_HEIGHT,
337+
1,
338+
nodeToAdd.x,
339+
nodeToAdd.y,
340+
BACKGROUND_COLOR,
341+
FOREGROUND_COLOR,
342+
);
343+
344+
this.cmd(act.setNumElements, nodeToAdd.graphicID, nodeToAdd.keys.length);
345+
this.resizeTree();
346+
for (let i = 0; i < nodeToAdd.keys.length; i++) {
347+
this.cmd(act.setText, nodeToAdd.graphicID, nodeToAdd.keys[i], i);
348+
}
349+
350+
this.cmd(
351+
act.connect,
352+
node.graphicID,
353+
nodeToAdd.graphicID,
354+
FOREGROUND_COLOR,
355+
0, // Curve
356+
0, // Directed
357+
'', // Label
358+
index,
359+
);
360+
} else {
361+
this.buildTreeRecurse(nextNode, val, nodeToAdd);
362+
}
186363
}
187364

188365
reset() {
189-
this.nextIndex = 3;
366+
this.nextIndex = this.resetIndex;
190367
this.max_degree = 4;
191368
this.max_keys = 3;
192369
this.min_keys = 1;
@@ -387,14 +564,17 @@ export default class BTree extends Algorithm {
387564
this.commands = [];
388565
this.deleteTree(this.treeRoot);
389566
this.treeRoot = null;
390-
this.nextIndex = 3;
567+
this.nextIndex = this.resetIndex;
391568
return this.commands;
392569
}
393570

394571
deleteTree(tree) {
395572
if (tree != null) {
396573
if (!tree.isLeaf) {
397574
for (let i = 0; i <= tree.numKeys; i++) {
575+
if (!tree.children[i]) {
576+
continue;
577+
}
398578
this.cmd(act.disconnect, tree.graphicID, tree.children[i].graphicID);
399579
this.deleteTree(tree.children[i]);
400580
}
@@ -407,7 +587,7 @@ export default class BTree extends Algorithm {
407587
this.commands = [];
408588
this.deleteTree(this.treeRoot);
409589
this.treeRoot = null;
410-
this.nextIndex = 3;
590+
this.nextIndex = this.resetIndex;
411591
const newDegree = degree;
412592
this.ignoreInputs = true;
413593
this.maxDegreeRadioButtons[newDegree - MIN_MAX_DEGREE].checked = true;

0 commit comments

Comments
 (0)