diff --git a/answer.sql b/answer.sql new file mode 100644 index 0000000..9ba083c --- /dev/null +++ b/answer.sql @@ -0,0 +1,9 @@ +SELECT projects.project_name as Project, SUM(employees.salary) as Total, AVG(employees.age) as "Avg" +FROM employees +JOIN employee_projects on employees.id = employee_projects.employee_id +JOIN projects on projects.project_id = employee_projects.project_id +GROUP BY projects.project_id; + +/* I would index employees.id, employee_projects.employee_id, employee_projects.project_id, +and project.project_id +*/ \ No newline at end of file diff --git a/math_tree.py b/math_tree.py new file mode 100644 index 0000000..20fecac --- /dev/null +++ b/math_tree.py @@ -0,0 +1,138 @@ +#!/usr/bin/python + +from numbers import Number + + +class InvalidOperator(Exception): + """ An invalid operator was passed """ + pass + + +class InvalidNode(Exception): + """ The node was created with no value """ + pass + + +class InvalidNumber(Exception): + """ The node was not passed a number """ + pass + + +class InvalidTree(Exception): + """ The tree dictionary didn't have the proper structure """ + pass + + +class NoChildren(Exception): + """ Tried to calculate node with no children """ + pass + + +class Node(object): + """ + Object with basic Node properties that will be extended with OperatorNode and NumberNode. + Can contain any arbitrary number of Nodes. + """ + def __init__(self, val): + if val == "": + raise InvalidNode("Node cannot be empty") + self.val = val + self.children = [] + + def add_child(self, child): + self.children.append(child) + + def __str__(self): + return str(self.val) + + +class OperatorNode(Node): + """ + OperatorNode has a value of +, -, /, or * + calculate() will return the value of all nodes under the root node. + """ + operators = ["+", "-", "/", "*"] + + def __init__(self, val): + if val not in self.operators: + raise InvalidOperator("Invalid Operator. Must be in set(+ - / *)") + self._operator = val + super(OperatorNode, self).__init__(val) + + @property + def operator(self): + return self._operator + + def calculate(self): + """ + Recursively walk through all children in OperatorNode and solve until only + a NumberNode is left. + """ + if len(self.children) < 2: + raise NoChildren("Must have at least two children NumberNodes to calculate") + + def calc_children(children, operator): + number_nodes = [] + for node in children: + if isinstance(node, OperatorNode): + number_nodes.append(calc_children(node.children, node.operator)) + else: + number_nodes.append(node) + return self.solve_expression(number_nodes, operator) + return calc_children(self.children, self.operator) + + def solve_expression(self, number_nodes, operator): + """ + Create an expression to calculate with eval() + """ + expr = (" %s " % operator).join([str(float(n)) for n in number_nodes]) + result = eval(expr) + return NumberNode(result) + + +class NumberNode(Node): + """ + A node that stores a number instead of an operator. + """ + def __init__(self, val): + if not isinstance(val, Number): + raise InvalidNumber("Value must be a number") + super(NumberNode, self).__init__(val) + + def __int__(self): + return int(self.val) + + def __float__(self): + return float(self.val) + + +class Tree(OperatorNode): + """ + Create a Tree object from a dictionary. + """ + def __init__(self, tree_dict): + if len(tree_dict.keys()) > 1: + raise InvalidTree("Tree can only have one root node") + super(Tree, self).__init__(tree_dict.keys()[0]) + for node in self.tree(tree_dict).children: + self.add_child(node) + + def tree(self, tree): + def walk_tree(new_tree): + op_node = OperatorNode(new_tree.keys()[0]) + for item in new_tree[new_tree.keys()[0]]: + if isinstance(item, dict): + op_node.add_child(walk_tree(item)) + else: + op_node.add_child(NumberNode(item)) + return op_node + + root = tree.keys()[0] + root_node = OperatorNode(root) + for child in tree[root]: + if isinstance(child, dict): + root_node.add_child(walk_tree(child)) + else: + root_node.add_child(NumberNode(child)) + + return root_node \ No newline at end of file diff --git a/test_math_tree.py b/test_math_tree.py new file mode 100644 index 0000000..b7841a5 --- /dev/null +++ b/test_math_tree.py @@ -0,0 +1,233 @@ +#!/usr/bin/python + +from math_tree import * + +import unittest + + +class TreeGraph(unittest.TestCase): + def test_create_node(self): + node = Node("test") + self.assertEquals(node.val, "test") + + def test_create_node_exception(self): + self.assertRaises(InvalidNode, Node, "") + + def test_create_operator_node(self): + node = OperatorNode("+") + self.assertEquals("+", node.val) + + def test_create_op_node_exception(self): + self.assertRaises(InvalidOperator, OperatorNode, "A") + + def test_create_number_node(self): + node = NumberNode(2) + self.assertEquals(node.val, 2) + + def test_create_number_node_exception(self): + self.assertRaises(InvalidNumber, NumberNode, "A") + + def test_calculate_node_no_children(self): + node = OperatorNode("+") + self.assertRaises(NoChildren, node.calculate) + + def test_calculate_node_one_child(self): + node = OperatorNode("+") + node.add_child(NumberNode(2)) + self.assertRaises(NoChildren, node.calculate) + + def test_calculate_tree1(self): + """ + + + / \ + 3 - + / \ + + 5 + / \ + 4 10 + + answer: 12 + + """ + root_node = OperatorNode("+") + a_node = NumberNode(3) + b_node = OperatorNode("-") + c_node = OperatorNode("+") + d_node = NumberNode(5) + e_node = NumberNode(4) + f_node = NumberNode(10) + root_node.add_child(a_node) + root_node.add_child(b_node) + b_node.add_child(c_node) + b_node.add_child(d_node) + c_node.add_child(e_node) + c_node.add_child(f_node) + + self.assertEquals(float(root_node.calculate()), float(12)) + + def test_calculate_tree2(self): + """ + + + / \ + 3 - 3 + 10 = 13 + / \ + + 5 15 - 5 = 10 + / \ + - 10 5 + 10 = 15 + / \ + 9 + 9 - 4 = 5 + / \ + - 6 -2 + 6 = 4 + / \ + 2 4 2 - 4 = -2 + + """ + root_node = OperatorNode("+") + a = NumberNode(3) + b = OperatorNode("-") + c = OperatorNode("+") + d = NumberNode(5) + e = OperatorNode("-") + f = NumberNode(10) + g = NumberNode(9) + h = OperatorNode("+") + i = OperatorNode("-") + j = NumberNode(6) + k = NumberNode(2) + l = NumberNode(4) + + root_node.add_child(a) + root_node.add_child(b) + b.add_child(c) + b.add_child(d) + c.add_child(e) + c.add_child(f) + e.add_child(g) + e.add_child(h) + h.add_child(i) + h.add_child(j) + i.add_child(k) + i.add_child(l) + + self.assertEquals(float(root_node.calculate()), float(13)) + + def test_calculate_tree3(self): + """ + - + / \ + 3 * 3 - 150 = -147 + / \ + * 5 30 * 5 = 150 + / \ + (div) 10 3 * 10 = 30 + / \ + 9 3 9 / 3 = 3 + + """ + root_node = OperatorNode("-") + a = NumberNode(3) + b = OperatorNode("*") + c = OperatorNode("*") + d = NumberNode(5) + e = OperatorNode("/") + f = NumberNode(10) + g = NumberNode(9) + h = NumberNode(3) + + root_node.add_child(a) + root_node.add_child(b) + b.add_child(c) + b.add_child(d) + c.add_child(e) + c.add_child(f) + e.add_child(g) + e.add_child(h) + + self.assertEquals(float(root_node.calculate()), float(-147)) + + def test_calculate_tree4(self): + """ + (div) + / \ + 3 * 3 / 150 = -147 + / \ + * 5 30 * 5 = 150 + / \ + (div) 10 3 * 10 = 30 + / \ + 9 3 9 / 3 = 3 + + """ + root_node = OperatorNode("/") + a = NumberNode(3) + b = OperatorNode("*") + c = OperatorNode("*") + d = NumberNode(5) + e = OperatorNode("/") + f = NumberNode(10) + g = NumberNode(9) + h = NumberNode(3) + + root_node.add_child(a) + root_node.add_child(b) + b.add_child(c) + b.add_child(d) + c.add_child(e) + c.add_child(f) + e.add_child(g) + e.add_child(h) + + self.assertEquals(float(root_node.calculate()), 0.02) + + def test_calculate_tree5(self): + """ + + + / | \ + 3 - 3 + / \ + + 5 + / / \ + 5 4 10 + + answer: 20 + + """ + root_node = OperatorNode("+") + a_node = NumberNode(3) + b_node = OperatorNode("-") + c_node = OperatorNode("+") + d_node = NumberNode(5) + e_node = NumberNode(4) + f_node = NumberNode(10) + g_node = NumberNode(3) + h_node = NumberNode(5) + root_node.add_child(a_node) + root_node.add_child(b_node) + root_node.add_child(g_node) + b_node.add_child(c_node) + b_node.add_child(d_node) + c_node.add_child(e_node) + c_node.add_child(f_node) + c_node.add_child(h_node) + + self.assertEquals(float(root_node.calculate()), float(20)) + + def test_create_tree_from_dict(self): + """ + + + / \ + 2 - + / \ + 4 + + / \ + - * + / \ / \ + 7 8 9 5 + """ + + tree_dict = {"+": [2, {"-": [4, {"+": [{"-": [7, 8]}, {"*": [9, 5]}]}]}]} + tree = Tree(tree_dict) + self.assertEquals(float(tree.calculate()), float(-38)) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file