mirror of
https://github.com/sbrl/Pepperminty-Wiki.git
synced 2024-11-25 17:23:00 +00:00
BkTree: Add trace, improve shell, and bugfix delete.
The shell has given me an idea..... We could have a CLI module to Pepperminty Wiki that allow admins to run longer-running tasks without them getting killed 1/2 way through because of a timeout.
This commit is contained in:
parent
2f901642fe
commit
296139049a
3 changed files with 78 additions and 15 deletions
|
@ -104,7 +104,7 @@ class BkTree
|
||||||
// if($string == "bunny") echo("\nStart $string\n");
|
// if($string == "bunny") echo("\nStart $string\n");
|
||||||
|
|
||||||
$next_node = $this->box->get("node|$starting_node_id"); // Grab the root to start with
|
$next_node = $this->box->get("node|$starting_node_id"); // Grab the root to start with
|
||||||
$next_node_id = 0;
|
$next_node_id = $starting_node_id;
|
||||||
$depth = 0; $visted = 0;
|
$depth = 0; $visted = 0;
|
||||||
while(true) {
|
while(true) {
|
||||||
$visted++;
|
$visted++;
|
||||||
|
@ -160,10 +160,11 @@ class BkTree
|
||||||
|
|
||||||
$node_target_id = $node_target->children->$distance;
|
$node_target_id = $node_target->children->$distance;
|
||||||
$node_target = $this->box->get("node|$node_target_id");
|
$node_target = $this->box->get("node|$node_target_id");
|
||||||
$stack[] = [ "node" => $node_target, "id" => $node_target->children->$distance ];
|
$stack[] = [ "node" => $node_target, "id" => $node_target_id ];
|
||||||
}
|
}
|
||||||
|
|
||||||
$parent = end($stack); // The last item on the stack is the parent node
|
// The last item but 1 on the stack is the parent node
|
||||||
|
$parent = $stack[count($stack) - 2];
|
||||||
|
|
||||||
// 1. Delete the connection from parent -> target
|
// 1. Delete the connection from parent -> target
|
||||||
foreach($parent["node"]->children as $distance -> $id) {
|
foreach($parent["node"]->children as $distance -> $id) {
|
||||||
|
@ -180,16 +181,53 @@ class BkTree
|
||||||
// 2. Iterate over the target's children (if any) and re-hang them from the parent
|
// 2. Iterate over the target's children (if any) and re-hang them from the parent
|
||||||
// NOTE: We need to be careful that the characteristics of the tree are preserved. We should test this by tracing a node's location in the tree and purposefully removing nodes in the chain and see if the results returned as still the same
|
// NOTE: We need to be careful that the characteristics of the tree are preserved. We should test this by tracing a node's location in the tree and purposefully removing nodes in the chain and see if the results returned as still the same
|
||||||
//
|
//
|
||||||
// Hang the now orphaned children from the parent
|
// Hang the now orphaned children and all their decendants from the parent
|
||||||
foreach($node_target->children as $distance -> $id) {
|
foreach($node_target->children as $distance -> $id) {
|
||||||
$orphan = $this->box->get("node|$id");
|
$orphan = $this->box->get("node|$id");
|
||||||
// TODO: Optimise this
|
$substack = [ [ "node" => $orphan, "id" => $id ] ]; $substack_top = 0;
|
||||||
$this->box->delete("node|$id"); // Delete the orphan node
|
while($substack_top >= 0) {
|
||||||
$this->add($orphan->value, $parent["id"]); // Re-hang it from the parent
|
$next = $substack[$substack_top];
|
||||||
|
unset($substack[$substack_top]);
|
||||||
|
$substack_top--;
|
||||||
|
|
||||||
|
$this->box->delete("node|{$next["id"]}"); // Delete the orphan node
|
||||||
|
$this->add($next["node"], $parent["id"]); // Re-hang it from the parent
|
||||||
|
|
||||||
|
foreach($next["node"]->children as $distance => $sub_id) {
|
||||||
|
$substack[++$substack_top] = [
|
||||||
|
"node" => $this->box->get("node|$sub_id"),
|
||||||
|
"id" => $sub_id
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the target node
|
// Delete the target node
|
||||||
$this->box->delete("node|$node_target_id");
|
$this->box->delete("node|$node_target_id");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function trace(string $string) {
|
||||||
|
$stack = [
|
||||||
|
(object) [ "node" => $this->box->get("node|0"), "id" => 0 ]
|
||||||
|
];
|
||||||
|
$node_target = $stack[0]->node;
|
||||||
|
|
||||||
|
while($node_target->value !== $string) {
|
||||||
|
$distance = levenshtein($string, $node_target->value, $this->cost_insert, $this->cost_replace, $this->cost_delete);
|
||||||
|
|
||||||
|
var_dump($node_target);
|
||||||
|
|
||||||
|
// Failed to recurse to find the node with the value in question
|
||||||
|
if(!isset($node_target->children->$distance))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$node_target_id = $node_target->children->$distance;
|
||||||
|
$node_target = $this->box->get("node|$node_target_id");
|
||||||
|
$stack[] = (object) [ "node" => $node_target, "id" => $node_target_id ];
|
||||||
|
}
|
||||||
|
return $stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -259,8 +297,7 @@ class BkTree
|
||||||
// echo("[lookup] Recursing on child ".$this->box->get("node|$child->id")->value." (distance $child->distance)\n");
|
// echo("[lookup] Recursing on child ".$this->box->get("node|$child->id")->value." (distance $child->distance)\n");
|
||||||
// Push the node onto the stack
|
// Push the node onto the stack
|
||||||
// Note that it doesn't actually matter that the stack isn't an accurate representation of ancestor nodes at any given time here. The stack is really a hybrid between a stack and a queue, having features of both.
|
// Note that it doesn't actually matter that the stack isn't an accurate representation of ancestor nodes at any given time here. The stack is really a hybrid between a stack and a queue, having features of both.
|
||||||
$stack_top++;
|
$stack[++$stack_top] = $this->box->get("node|{$node_current->children->$child_distance}");
|
||||||
$stack[$stack_top] = $this->box->get("node|{$node_current->children->$child_distance}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ class JsonStorageBox {
|
||||||
/**
|
/**
|
||||||
* Deletes an item from the data store.
|
* Deletes an item from the data store.
|
||||||
* @param string $key The key of the item to delete.
|
* @param string $key The key of the item to delete.
|
||||||
* @return bool Whether it was really deleted or not. Note that if it doesn't exist, then it can't be deleted.
|
* @return bool Whether it was really deleted or not. Note that if it doesn't exist, then it can't be deleted. Note also that if a node is deleted before being persisted to disk, this will return false when in actuality it was deleted successfully.
|
||||||
*/
|
*/
|
||||||
public function delete(string $key) : bool {
|
public function delete(string $key) : bool {
|
||||||
// Remove it from the cache
|
// Remove it from the cache
|
||||||
|
|
|
@ -73,7 +73,7 @@ else
|
||||||
echo("Tree created in ".($tree["time"]*1000)."ms\n");
|
echo("Tree created in ".($tree["time"]*1000)."ms\n");
|
||||||
$tree = $tree["value"];
|
$tree = $tree["value"];
|
||||||
|
|
||||||
writegraph(); exit();
|
// writegraph(); exit();
|
||||||
|
|
||||||
function test_auto() {
|
function test_auto() {
|
||||||
global $tree;
|
global $tree;
|
||||||
|
@ -98,7 +98,8 @@ while(true) {
|
||||||
readline_add_history($line);
|
readline_add_history($line);
|
||||||
|
|
||||||
if($line[0] == ".") {
|
if($line[0] == ".") {
|
||||||
switch ($line) {
|
$parts = explode(" ", $line, 2);
|
||||||
|
switch ($parts[0]) {
|
||||||
case ".quit":
|
case ".quit":
|
||||||
case ".exit":
|
case ".exit":
|
||||||
$result = time_callable(function() use ($tree) {
|
$result = time_callable(function() use ($tree) {
|
||||||
|
@ -113,6 +114,9 @@ while(true) {
|
||||||
.exit Exit, saving edits to the tree to disk
|
.exit Exit, saving edits to the tree to disk
|
||||||
.writegraph Write a graphviz dot file to disk representing the tree in the current directory
|
.writegraph Write a graphviz dot file to disk representing the tree in the current directory
|
||||||
.stats Compute statistics about the tree
|
.stats Compute statistics about the tree
|
||||||
|
.trace {{string}} Trace the path through the tree to {{string}}
|
||||||
|
.remove {{string}} Delete {{string}} from the tree
|
||||||
|
.add {{string}} Add {{string}} to the tree
|
||||||
");
|
");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -120,10 +124,32 @@ while(true) {
|
||||||
writegraph();
|
writegraph();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ".remove":
|
||||||
|
$start_time = microtime(true);
|
||||||
|
$result = $tree->remove($parts[1]);
|
||||||
|
echo("{$parts[1]}".($result?"":" not")." successfully deleted from the tree in ".round((microtime(true) - $start_time)*1000)."ms\n");
|
||||||
|
break;
|
||||||
|
case ".add":
|
||||||
|
$start_time = microtime(true);
|
||||||
|
$depth = $tree->add($parts[1]);
|
||||||
|
echo("{$parts[1]} successfully added to the tree at depth $depth in ".round((microtime(true) - $start_time)*1000)."ms\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ".trace":
|
||||||
|
$trace = $tree->trace($parts[1]);
|
||||||
|
foreach($trace as $depth => $item) {
|
||||||
|
echo("$depth: {$item->node->value} (#$item->id)\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ".stats":
|
case ".stats":
|
||||||
echo("Tree stats: ");
|
echo("Tree stats: ");
|
||||||
var_dump($tree->stats());
|
var_dump($tree->stats());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo("Error: Unknown dot-command {$parts[0]} (try .help)\n");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue