SpaceNode.php:
-Added coordinate type check (infra inherits ultra)
-Redid get_location(T_STRING) to be more generic.
Coords.php:
-Added convert() to all classes TODO: Make this less duplicative?
-Fixed conversion of Cartesian to Spherical because I was stupid before.
-Added basic vector arithmetic to the abstract
UniSim.php:
-Added some trivial vector arithmetic test cases.

file:a/Coords.php -> file:b/Coords.php
```--- a/Coords.php
+++ b/Coords.php
@@ -2,6 +2,27 @@
abstract class Coords {
// This is a stub that may contain functions or parameters that apply universally to all coordinate systems,
// but may never be instantiated itself.
+
+		\$class = get_class(\$this);
+		\$vector1 = \$this->convert("CartesianCoords");
+		\$vector2 = \$vector2->convert("CartesianCoords");
+		list(\$x1, \$y1, \$z1) = \$vector1->triplet();
+		list(\$x2, \$y2, \$z2) = \$vector2->triplet();
+		\$vector3 = new CartesianCoords(\$x1+\$x2, \$y1+\$y2, \$z1+\$z2);
+		return \$vector3->convert(\$class);
+	}
+
+	public function vector_subtract(\$vector2) {
+		\$class = get_class(\$this);
+		\$vector1 = \$this->convert("CartesianCoords");
+		\$vector2 = \$vector2->convert("CartesianCoords");
+		list(\$x1, \$y1, \$z1) = \$vector1->triplet();
+		list(\$x2, \$y2, \$z2) = \$vector2->triplet();
+		\$vector3 = new CartesianCoords(\$x1-\$x2, \$y1-\$y2, \$z1-\$z2);
+		return \$vector3->convert(\$class);
+	}
+
}

class SphericalCoords extends Coords {
@@ -21,12 +42,33 @@
);
}

-	public function to_Cartesian() {
-		return new CartesianCoords(
-			\$this->radius * sin(\$this->theta) * cos(\$this->phi),
-			\$this->radius * sin(\$this->theta) * sin(\$this->phi),
-		);
+	public function convert(\$class) {
+		switch (\$class) {
+			case "SphericalCoords":
+				return \$this;
+				break;
+			case "CartesianCoords":
+				return new CartesianCoords(
+					\$this->radius * sin(\$this->theta) * cos(\$this->phi),
+					\$this->radius * sin(\$this->theta) * sin(\$this->phi),
+				);
+				break;
+			case "LinearCoords":
+				break;
+			default:
+				throw new Exception(sprintf("Cannot convert from type '%s' to type '%s'.", get_class(\$this), \$class));
+				break;
+		}
+	}
+
+	public function string() {
+		return sprintf("r = %.2f, ϑ = %.2f, ϕ = %.2f", \$this->radius, \$this->theta, \$this->phi);
+	}
+
+	public function magnitude() {
}

@@ -50,12 +92,33 @@
\$this->z
);
}
+
+	public function string() {
+		return sprintf("x = %.2f, y = %.2f, z = %.2f", \$this->x, \$this->y, \$this->z);
+	}
+
+	public function magnitude() {
+		return sqrt(pow(\$this->x,2) + pow(\$this->y,2) + pow(\$this->z,2));
+	}

-	public function to_Spherical() {
-		\$r = pow(\$this->x,2) * pow(\$this->y,2) * pow(\$this->z,2);
-		\$t = acos(\$this->z / \$r);
-		\$p = acos(\$this->y / \$this->x);
-		return new SphericalCoords(\$r, \$t, \$p);
+	public function convert(\$class) {
+		switch (\$class) {
+			case "SphericalCoords":
+				\$r = sqrt(pow(\$this->x,2) + pow(\$this->y,2) + pow(\$this->z,2));
+				\$t = acos(\$this->z / \$r);
+				\$p = atan2(\$this->y, \$this->x);
+				return new SphericalCoords(\$r, \$t, \$p);
+				break;
+			case "CartesianCoords":
+				return \$this;
+				break;
+			case "LinearCoords":
+				return new LinearCoords(\$this->magnitude());
+				break;
+			default:
+				throw new Exception(sprintf("Cannot convert from type '%s' to type '%s'.", get_class(\$this), \$class));
+				break;
+		}
}

protected \$x = 0;
@@ -65,7 +128,7 @@

class LinearCoords extends CartesianCoords {
public function __construct(\$d) {
-		// make a car;tesian object, with just one axis
+		// make a cartesian object, with just one axis
\$this->y = 0;
\$this->z = 0;
\$this->x = \$d;
@@ -73,6 +136,10 @@
return \$this;
}

+	public function string() {
+		return "x = " . \$this->x;
+	}
+
public function magnitude() {
return \$this->x;
}

```
```--- a/SpaceNode.php
+++ b/SpaceNode.php
@@ -45,9 +45,16 @@
public function get_ultra() {

return \$this->ultra;

}

+

+	public function get_infra_list() {

+		return \$this->child_list;

+	}

if (empty(\$this->child_list[\$infra->id()])) {

+			if (!\$infra->check_coords_type(get_class(\$this->relative_coords))) {

+				throw new Exception("Failed to get coordinates in the same reference!");

+			}

@@ -75,16 +82,27 @@
}

public function get_location(\$type) {

-		list(\$a, \$b, \$c) = \$this->relative_coords->triplet();

switch (\$type) {

case T_STRING:

-				return "x = \$a, y = \$b, z = \$c";

+				return \$this->relative_coords->string();

break;

case T_DNUMBER:

return \$this->relative_coords->magnitude();

default:

break;

}

+	}

+

+	protected function check_coords_type(\$class) {

+		if (get_class(\$this->relative_coords) == \$class) {

+			return true;

+		} else {

+			return (\$this->relative_coords = \$this->relative_coords->convert(\$class));

+		}

+	}

+

+	public function describe() {

+		return sprintf("Node #%s '%s', with radius %.2f at relative location %s.\n", \$this->id, \$this->name, \$this->mean_radius, \$this->get_location(T_STRING));

}

protected \$name = "";

```
file:a/UniSim.php -> file:b/UniSim.php
```--- a/UniSim.php
+++ b/UniSim.php
@@ -3,12 +3,24 @@
require_once("SpaceNode.php");
require_once("Coords.php");

-\$universe = new SpaceNode("universe node", 0, new LinearCoords(0), null);
+\$universe = new SpaceNode("universe node", 0, new SphericalCoords(0,0,0), null);

\$node1 = new SpaceNode("node1", 40, new LinearCoords(40), \$universe);
-printf("Made new node '%s' with parent '%s' with radius %.2g at coordinates %s.\n", \$node1->name(), \$node1->get_ultra()->name(), \$node1->get_radius(), \$node1->get_location(T_STRING));

+echo \$universe->describe();
+
+\$infra_list = \$universe->get_infra_list();
+foreach (\$infra_list as \$infra) {
+	echo \$infra->describe();
+}
+
+\$vector1 = new SphericalCoords(5, M_PI/4, M_PI/8);
+printf("Vector1: %s\n", \$vector1->string());
+\$vector2 = new CartesianCoords(7, 6, 5);
+printf("Vector2: %s\n", \$vector2->string());