diff --git a/MathEval/node.cpp b/MathEval/node.cpp index 16c2678ce2b450e0604c47fbe0a6328757afa5f2..4b4ad735a8189a382cc9a26b8af4e05a277bf649 100644 --- a/MathEval/node.cpp +++ b/MathEval/node.cpp @@ -26,625 +26,626 @@ Node * node_create(char type,...) { - Node *node; /* New node. */ - va_list ap; /* Variable argument list. */ - + Node *node; /* New node. */ + va_list ap; /* Variable argument list. */ + + /* + * Allocate memory for node and initialize its type. + */ + node = XMALLOC(Node, 1); + node->type = type; + + /* + * According to node type, initialize rest of the node from variable + * argument list. + */ + va_start(ap, type); + switch (node->type) { + case 'c': /* - * Allocate memory for node and initialize its type. + * Initialize constant value. */ - node = XMALLOC(Node, 1); - node->type = type; - + node->data.constant = va_arg(ap, double); + break; + + case 'v': /* - * According to node type, initialize rest of the node from variable - * argument list. + * Remember pointer to symbol table record describing + * variable. */ - va_start(ap, type); - switch (node->type) { - case 'c': - /* - * Initialize constant value. - */ - node->data.constant = va_arg(ap, double); - - break; - - case 'v': - /* - * Remember pointer to symbol table record describing - * variable. - */ - node->data.variable = va_arg(ap, Record *); - break; - - case 'f': - /* - * Remember pointer to symbol table record describing - * function and initialize function argument. - */ - node->data.function.record = va_arg(ap, Record *); - node->data.function.child = va_arg(ap, Node *); - break; - - case 'u': - /* - * Initialize operator type and operand. - */ - node->data.un_op.operato = (char) va_arg(ap, int); - - node->data.un_op.child = va_arg(ap, Node *); - break; - - case 'b': - /* - * Initialize operator type and operands. - */ - node->data.un_op.operato = (char) va_arg(ap, int); - - node->data.bin_op.left = va_arg(ap, Node *); - node->data.bin_op.right = va_arg(ap, Node *); - break; - - default: - assert(0); - } - va_end(ap); - - return node; -} - -void -node_destroy(Node * node) -{ + node->data.variable = va_arg(ap, Record *); + break; + + case 'f': /* - * Skip if node already null (this may occur during simplification). + * Remember pointer to symbol table record describing + * function and initialize function argument. */ - if (!node) - return; - + node->data.function.record = va_arg(ap, Record *); + node->data.function.child = va_arg(ap, Node *); + break; + + case 'u': /* - * If necessary, destroy subtree rooted at node. + * Initialize operator type and operand. */ - switch (node->type) { - case 'c': - case 'v': - break; - - case 'f': - node_destroy(node->data.function.child); - break; - - case 'u': - node_destroy(node->data.un_op.child); - break; - - case 'b': - node_destroy(node->data.bin_op.left); - node_destroy(node->data.bin_op.right); - break; - } - + node->data.un_op.operatorr = (char) va_arg(ap, int); + node->data.un_op.child = va_arg(ap, Node *); + break; + + case 'b': /* - * Deallocate memory used by node. + * Initialize operator type and operands. */ - XFREE(node); + node->data.un_op.operatorr = (char) va_arg(ap, int); + node->data.bin_op.left = va_arg(ap, Node *); + node->data.bin_op.right = va_arg(ap, Node *); + break; + + default: + assert(0); + } + va_end(ap); + + return node; +} + +void +node_destroy(Node * node) +{ + /* + * Skip if node already null (this may occur during simplification). + */ + if (!node) + return; + + /* + * If necessary, destroy subtree rooted at node. + */ + switch (node->type) { + case 'c': + case 'v': + break; + + case 'f': + node_destroy(node->data.function.child); + break; + + case 'u': + node_destroy(node->data.un_op.child); + break; + + case 'b': + node_destroy(node->data.bin_op.left); + node_destroy(node->data.bin_op.right); + break; + } + + /* + * Deallocate memory used by node. + */ + XFREE(node); } Node * node_copy(Node * node) { - /* - * According to node type, create (deep) copy of subtree rooted at - * node. - */ - switch (node->type) { - case 'c': - return node_create('c', node->data.constant); - - case 'v': - return node_create('v', node->data.variable); - - case 'f': - return node_create('f', node->data.function.record, node_copy(node->data.function.child)); - - case 'u': - return node_create('u', node->data.un_op.operato, node_copy(node->data.un_op.child)); - - case 'b': - return node_create('b', node->data.bin_op.operato, node_copy(node->data.bin_op.left), node_copy(node->data.bin_op.right)); - } - - return NULL; + /* + * According to node type, create (deep) copy of subtree rooted at + * node. + */ + switch (node->type) { + case 'c': + return node_create('c', node->data.constant); + + case 'v': + return node_create('v', node->data.variable); + + case 'f': + return node_create('f', node->data.function.record, node_copy(node->data.function.child)); + + case 'u': + return node_create('u', node->data.un_op.operatorr, node_copy(node->data.un_op.child)); + + case 'b': + return node_create('b', node->data.bin_op.operatorr, node_copy(node->data.bin_op.left), node_copy(node->data.bin_op.right)); + } + + return NULL; } Node * node_simplify(Node * node) { + /* + * According to node type, apply further simplifications. + */ + switch (node->type) { + case 'c': + case 'v': + return node; + + case 'f': /* - * According to node type, apply further simplifications. + * Simplify function argument and if constant evaluate + * function and replace function node with constant node. */ - switch (node->type) { - case 'c': - case 'v': - return node; - - case 'f': - /* - * Simplify function argument and if constant evaluate - * function and replace function node with constant node. - */ - node->data.function.child = node_simplify(node->data.function.child); - if (node->data.function.child->type == 'c') { - double value = node_evaluate(node); - - node_destroy(node); - return node_create('c', value); - } else - return node; - - case 'u': - /* - * Simplify unary operator operand and if constant apply - * operator and replace operator node with constant node. - */ - node->data.un_op.child = node_simplify(node->data.un_op.child); - if (node->data.un_op.operato == '-' && node->data.un_op.child->type == 'c') { - double value = node_evaluate(node); - - node_destroy(node); - return node_create('c', value); - } else - return node; - - case 'b': - /* - * Simplify binary operator operands. - */ - node->data.bin_op.left = node_simplify(node->data.bin_op.left); - node->data.bin_op.right = node_simplify(node->data.bin_op.right); - - /* - * If operands constant apply operator and replace operator - * node with constant node. - */ - if (node->data.bin_op.left->type == 'c' && node->data.bin_op.right->type == 'c') { - double value = node_evaluate(node); - - node_destroy(node); - return node_create('c', value); - } - /* - * Eliminate 0 as neutral addition operand. - */ - else if (node->data.bin_op.operato == '+') - if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 0) { - Node *right; - - right = node->data.bin_op.right; - node->data.bin_op.right = NULL; - node_destroy(node); - return right; - } else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) { - Node *left; - - left = node->data.bin_op.left; - node->data.bin_op.left = NULL; - node_destroy(node); - return left; - } else - return node; - /* - * Eliminate 0 as neutral subtraction right operand. - */ - else if (node->data.bin_op.operato == '-') - if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) { - Node *left; - - left = node->data.bin_op.left; - node->data.bin_op.left = NULL; - node_destroy(node); - return left; - } else - return node; - /* - * Eliminate 1 as neutral multiplication operand. - */ - else if (node->data.bin_op.operato == '*') - if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 1) { - Node *right; - - right = node->data.bin_op.right; - node->data.bin_op.right = NULL; - node_destroy(node); - return right; - } else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) { - Node *left; - - left = node->data.bin_op.left; - node->data.bin_op.left = NULL; - node_destroy(node); - return left; - } else - return node; - /* - * Eliminate 1 as neutral division right operand. - */ - else if (node->data.bin_op.operato == '/') - if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) { - Node *left; - - left = node->data.bin_op.left; - node->data.bin_op.left = NULL; - node_destroy(node); - return left; - } else - return node; - /* - * Eliminate 0 and 1 as both left and right exponentiation - * operands. - */ - else if (node->data.bin_op.operato == '^') - if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 0) { - node_destroy(node); - return node_create('c', 0.0); - } else if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 1) { - node_destroy(node); - return node_create('c', 1.0); - } else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) { - node_destroy(node); - return node_create('c', 1.0); - } else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) { - Node *left; - - left = node->data.bin_op.left; - node->data.bin_op.left = NULL; - node_destroy(node); - return left; - } else - return node; - else - return node; + node->data.function.child = node_simplify(node->data.function.child); + if (node->data.function.child->type == 'c') { + double value = node_evaluate(node); + + node_destroy(node); + return node_create('c', value); } - - return NULL; + else + return node; + + case 'u': + /* + * Simplify unary operator operand and if constant apply + * operator and replace operator node with constant node. + */ + node->data.un_op.child = node_simplify(node->data.un_op.child); + if (node->data.un_op.operatorr == '-' && node->data.un_op.child->type == 'c') { + double value = node_evaluate(node); + + node_destroy(node); + return node_create('c', value); + } + else + return node; + + case 'b': + /* + * Simplify binary operator operands. + */ + node->data.bin_op.left = node_simplify(node->data.bin_op.left); + node->data.bin_op.right = node_simplify(node->data.bin_op.right); + + /* + * If operands constant apply operator and replace operator + * node with constant node. + */ + if (node->data.bin_op.left->type == 'c' && node->data.bin_op.right->type == 'c') { + double value = node_evaluate(node); + + node_destroy(node); + return node_create('c', value); + } + /* + * Eliminate 0 as neutral addition operand. + */ + else if (node->data.bin_op.operatorr == '+') + if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 0) { + Node *right; + right = node->data.bin_op.right; + node->data.bin_op.right = NULL; + node_destroy(node); + return right; + } + else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) { + Node *left; + left = node->data.bin_op.left; + node->data.bin_op.left = NULL; + node_destroy(node); + return left; + } + else + return node; + /* + * Eliminate 0 as neutral subtraction right operand. + */ + else if (node->data.bin_op.operatorr == '-') + if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) { + Node *left; + left = node->data.bin_op.left; + node->data.bin_op.left = NULL; + node_destroy(node); + return left; + } + else + return node; + /* + * Eliminate 1 as neutral multiplication operand. + */ + else if (node->data.bin_op.operatorr == '*') + if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 1) { + Node *right; + right = node->data.bin_op.right; + node->data.bin_op.right = NULL; + node_destroy(node); + return right; + } + else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) { + Node *left; + left = node->data.bin_op.left; + node->data.bin_op.left = NULL; + node_destroy(node); + return left; + } + else + return node; + /* + * Eliminate 1 as neutral division right operand. + */ + else if (node->data.bin_op.operatorr == '/') + if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) { + Node *left; + left = node->data.bin_op.left; + node->data.bin_op.left = NULL; + node_destroy(node); + return left; + } + else + return node; + /* + * Eliminate 0 and 1 as both left and right exponentiation + * operands. + */ + else if (node->data.bin_op.operatorr == '^') + if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 0) { + node_destroy(node); + return node_create('c', 0.0); + } + else if (node->data.bin_op.left->type == 'c' && node->data.bin_op.left->data.constant == 1) { + node_destroy(node); + return node_create('c', 1.0); + } + else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 0) { + node_destroy(node); + return node_create('c', 1.0); + } + else if (node->data.bin_op.right->type == 'c' && node->data.bin_op.right->data.constant == 1) { + Node *left; + left = node->data.bin_op.left; + node->data.bin_op.left = NULL; + node_destroy(node); + return left; + } + else + return node; + else + return node; + } + + return NULL; } double node_evaluate(Node * node) { + /* + * According to node type, evaluate subtree rooted at node. + */ + switch (node->type) { + case 'c': + return node->data.constant; + + case 'v': /* - * According to node type, evaluate subtree rooted at node. + * Variable values are used from symbol table. */ - switch (node->type) { - case 'c': - return node->data.constant; - - case 'v': - /* - * Variable values are used from symbol table. - */ - return node->data.variable->data.value; - - case 'f': - /* - * Functions are evaluated through symbol table. - */ - return (*node->data.function.record->data.function) (node_evaluate(node->data.function.child)); - - case 'u': - /* - * Unary operator node is evaluated according to operator - * type. - */ - switch (node->data.un_op.operato) { - case '-': - return -node_evaluate(node->data.un_op.child); - } - - case 'b': - /* - * Binary operator node is evaluated according to operator - * type. - */ - switch (node->data.un_op.operato) { - case '+': - return node_evaluate(node->data.bin_op.left) + node_evaluate(node->data.bin_op.right); - - case '-': - return node_evaluate(node->data.bin_op.left) - node_evaluate(node->data.bin_op.right); - - case '*': - return node_evaluate(node->data.bin_op.left) * node_evaluate(node->data.bin_op.right); - - case '/': - return node_evaluate(node->data.bin_op.left) / node_evaluate(node->data.bin_op.right); - - case '^': - return pow(node_evaluate(node->data.bin_op.left), node_evaluate(node->data.bin_op.right)); - } + return node->data.variable->data.value; + + case 'f': + /* + * Functions are evaluated through symbol table. + */ + return (*node->data.function.record->data.function) (node_evaluate(node->data.function.child)); + + case 'u': + /* + * Unary operator node is evaluated according to operator + * type. + */ + switch (node->data.un_op.operatorr) { + case '-': + return -node_evaluate(node->data.un_op.child); } - - return 0; + + case 'b': + /* + * Binary operator node is evaluated according to operator + * type. + */ + switch (node->data.un_op.operatorr) { + case '+': + return node_evaluate(node->data.bin_op.left) + node_evaluate(node->data.bin_op.right); + + case '-': + return node_evaluate(node->data.bin_op.left) - node_evaluate(node->data.bin_op.right); + + case '*': + return node_evaluate(node->data.bin_op.left) * node_evaluate(node->data.bin_op.right); + + case '/': + return node_evaluate(node->data.bin_op.left) / node_evaluate(node->data.bin_op.right); + + case '^': + return pow(node_evaluate(node->data.bin_op.left), node_evaluate(node->data.bin_op.right)); + } + } + + return 0; } Node * node_derivative(Node * node, char *name, SymbolTable * symbol_table) { + /* + * According to node type, derivative tree for subtree rooted at node + * is created. + */ + switch (node->type) { + case 'c': /* - * According to node type, derivative tree for subtree rooted at node - * is created. + * Derivative of constant equals 0. */ - switch (node->type) { - case 'c': - /* - * Derivative of constant equals 0. - */ - return node_create('c', 0.0); - - case 'v': - /* - * Derivative of variable equals 1 if variable is derivative - * variable, 0 otherwise. - */ - return node_create('c', (!strcmp(name, node->data.variable->name)) ? 1.0 : 0.0); - - case 'f': - /* - * Apply rule of exponential function derivative. - */ - if (!strcmp(node->data.function.record->name, "Exp")) - return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_copy(node)); - /* - * Apply rule of logarithmic function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Log")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_copy(node->data.function.child)); - /* - * Apply rule of square root function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Sqrt")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '*', node_create('c', 2.0), node_copy(node))); - /* - * Apply rule of sine function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Sin")) - return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Cos"), node_copy(node->data.function.child))); - /* - * Apply rule of cosine function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Cos")) - return node_create('u', '-', node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sin"), node_copy(node->data.function.child)))); - /* - * Apply rule of tangent function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Tan")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Cos"), node_copy(node->data.function.child)), node_create('c', 2.0))); - /* - * Apply rule of cotangent function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Ctan")) - return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Sin"), node_copy(node->data.function.child)), node_create('c', 2.0)))); - /* - * Apply rule of inverse sine function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Asin")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))))); - /* - * Apply rule of inverse cosine function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Acos")) - return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))))); - /* - * Apply rule of inverse tangent function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Atan")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '+', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))); - /* - * Apply rule of inverse cotanget function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Actan")) - return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '+', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))))); - /* - * Apply rule of hyperbolic sine function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Sinh")) - return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Cosh"), node_copy(node->data.function.child))); - /* - * Apply rule of hyperbolic cosine function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Cosh")) - return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sinh"), node_copy(node->data.function.child))); - /* - * Apply rule of hyperbolic tangent function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Tanh")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Cosh"), node_copy(node->data.function.child)), node_create('c', 2.0))); - /* - * Apply rule of hyperbolic cotangent function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Ctanh")) - return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "sinh"), node_copy(node->data.function.child)), node_create('c', 2.0)))); - /* - * Apply rule of inverse hyperbolic sine function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Asinh")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))))); - /* - * Apply rule of inverse hyperbolic cosine function - * derivative. - */ - else if (!strcmp(node->data.function.record->name, "Acosh")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)), node_create('c', 1.0)))); - /* - * Apply rule of inverse hyperbolic tangent function - * derivative. - */ - else if (!strcmp(node->data.function.record->name, "Atanh")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))); - /* - * Apply rule of inverse hyperbolic cotangent function - * derivative. - */ - else if (!strcmp(node->data.function.record->name, "Actanh")) - return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '-', node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)), node_create('c', 1.0))); - /* - * Apply rule of absolute value function derivative. - */ - else if (!strcmp(node->data.function.record->name, "Fabs")) - return node_create('b', '/', node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_copy(node->data.function.child)), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))); - - case 'u': - switch (node->data.un_op.operato) { - case '-': - /* - * Apply (-f)'=-f' derivative rule. - */ - return node_create('u', '-', node_derivative(node->data.un_op.child, name, symbol_table)); - } - - case 'b': - switch (node->data.bin_op.operato) { - case '+': - /* - * Apply (f+g)'=f'+g' derivative rule. - */ - return node_create('b', '+', node_derivative(node->data.bin_op.left, name, symbol_table), node_derivative(node->data.bin_op.right, name, symbol_table)); - - case '-': - /* - * Apply (f-g)'=f'-g' derivative rule. - */ - return node_create('b', '-', node_derivative(node->data.bin_op.left, name, symbol_table), node_derivative(node->data.bin_op.right, name, symbol_table)); - - case '*': - /* - * Apply (f*g)'=f'*g+f*g' derivative rule. - */ - return node_create('b', '+', node_create('b', '*', node_derivative(node->data.bin_op.left, name, symbol_table), node_copy(node->data.bin_op.right)), node_create('b', '*', node_copy(node->data.bin_op.left), node_derivative(node->data.bin_op.right, name, symbol_table))); - - case '/': - /* - * Apply (f/g)'=(f'*g-f*g')/g^2 derivative rule. - */ - return node_create('b', '/', node_create('b', '-', node_create('b', '*', node_derivative(node->data.bin_op.left, name, symbol_table), node_copy(node->data.bin_op.right)), node_create('b', '*', node_copy(node->data.bin_op.left), node_derivative(node->data.bin_op.right, name, symbol_table))), node_create('b', '^', node_copy(node->data.bin_op.right), node_create('c', 2.0))); - - case '^': - /* - * If right operand of exponentiation constant apply - * (f^n)'=n*f^(n-1)*f' derivative rule. - */ - if (node->data.bin_op.right->type == 'c') - return node_create('b', '*', node_create('b', '*', node_create('c', node->data.bin_op.right->data.constant), node_derivative(node->data.bin_op.left, name, symbol_table)), node_create('b', '^', node_copy(node->data.bin_op.left), node_create('c', node->data.bin_op.right->data.constant - 1.0))); - /* - * Otherwise, apply logaritmhic derivative rule: - * (log(f^g))'=(f^g)'/f^g => - * (f^g)'=f^g*(log(f^g))'=f^g*(g*log(f))' - */ - else { - Node *log_node, *derivative; - - log_node = node_create('b', '*', node_copy(node->data.bin_op.right), node_create('f', symbol_table_lookup(symbol_table, "Log"), node_copy(node->data.bin_op.left))); - derivative = node_create('b', '*', node_copy(node), node_derivative(log_node, name, symbol_table)); - node_destroy(log_node); - return derivative; - } - } + return node_create('c', 0.0); + + case 'v': + /* + * Derivative of variable equals 1 if variable is derivative + * variable, 0 otherwise. + */ + return node_create('c', (!strcmp(name, node->data.variable->name)) ? 1.0 : 0.0); + + case 'f': + /* + * Apply rule of exponential function derivative. + */ + if (!strcmp(node->data.function.record->name, "Exp")) + return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_copy(node)); + /* + * Apply rule of logarithmic function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Log")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_copy(node->data.function.child)); + /* + * Apply rule of square root function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Sqrt")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '*', node_create('c', 2.0), node_copy(node))); + /* + * Apply rule of sine function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Sin")) + return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Cos"), node_copy(node->data.function.child))); + /* + * Apply rule of cosine function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Cos")) + return node_create('u', '-', node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sin"), node_copy(node->data.function.child)))); + /* + * Apply rule of tangent function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Tan")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Cos"), node_copy(node->data.function.child)), node_create('c', 2.0))); + /* + * Apply rule of cotangent function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Ctan")) + return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Sin"), node_copy(node->data.function.child)), node_create('c', 2.0)))); + /* + * Apply rule of inverse sine function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Asin")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))))); + /* + * Apply rule of inverse cosine function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Acos")) + return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))))); + /* + * Apply rule of inverse tangent function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Atan")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '+', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))); + /* + * Apply rule of inverse cotanget function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Actan")) + return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '+', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))))); + /* + * Apply rule of hyperbolic sine function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Sinh")) + return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Cosh"), node_copy(node->data.function.child))); + /* + * Apply rule of hyperbolic cosine function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Cosh")) + return node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sinh"), node_copy(node->data.function.child))); + /* + * Apply rule of hyperbolic tangent function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Tanh")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "Cosh"), node_copy(node->data.function.child)), node_create('c', 2.0))); + /* + * Apply rule of hyperbolic cotangent function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Ctanh")) + return node_create('u', '-', node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '^', node_create('f', symbol_table_lookup(symbol_table, "sinh"), node_copy(node->data.function.child)), node_create('c', 2.0)))); + /* + * Apply rule of inverse hyperbolic sine function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Asinh")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0))))); + /* + * Apply rule of inverse hyperbolic cosine function + * derivative. + */ + else if (!strcmp(node->data.function.record->name, "Acosh")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '-', node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)), node_create('c', 1.0)))); + /* + * Apply rule of inverse hyperbolic tangent function + * derivative. + */ + else if (!strcmp(node->data.function.record->name, "Atanh")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '-', node_create('c', 1.0), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))); + /* + * Apply rule of inverse hyperbolic cotangent function + * derivative. + */ + else if (!strcmp(node->data.function.record->name, "Actanh")) + return node_create('b', '/', node_derivative(node->data.function.child, name, symbol_table), node_create('b', '-', node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)), node_create('c', 1.0))); + /* + * Apply rule of absolute value function derivative. + */ + else if (!strcmp(node->data.function.record->name, "Fabs")) + return node_create('b', '/', node_create('b', '*', node_derivative(node->data.function.child, name, symbol_table), node_copy(node->data.function.child)), node_create('f', symbol_table_lookup(symbol_table, "Sqrt"), node_create('b', '^', node_copy(node->data.function.child), node_create('c', 2.0)))); + + case 'u': + switch (node->data.un_op.operatorr) { + case '-': + /* + * Apply (-f)'=-f' derivative rule. + */ + return node_create('u', '-', node_derivative(node->data.un_op.child, name, symbol_table)); } - - return NULL; + + case 'b': + switch (node->data.bin_op.operatorr) { + case '+': + /* + * Apply (f+g)'=f'+g' derivative rule. + */ + return node_create('b', '+', node_derivative(node->data.bin_op.left, name, symbol_table), node_derivative(node->data.bin_op.right, name, symbol_table)); + + case '-': + /* + * Apply (f-g)'=f'-g' derivative rule. + */ + return node_create('b', '-', node_derivative(node->data.bin_op.left, name, symbol_table), node_derivative(node->data.bin_op.right, name, symbol_table)); + + case '*': + /* + * Apply (f*g)'=f'*g+f*g' derivative rule. + */ + return node_create('b', '+', node_create('b', '*', node_derivative(node->data.bin_op.left, name, symbol_table), node_copy(node->data.bin_op.right)), node_create('b', '*', node_copy(node->data.bin_op.left), node_derivative(node->data.bin_op.right, name, symbol_table))); + + case '/': + /* + * Apply (f/g)'=(f'*g-f*g')/g^2 derivative rule. + */ + return node_create('b', '/', node_create('b', '-', node_create('b', '*', node_derivative(node->data.bin_op.left, name, symbol_table), node_copy(node->data.bin_op.right)), node_create('b', '*', node_copy(node->data.bin_op.left), node_derivative(node->data.bin_op.right, name, symbol_table))), node_create('b', '^', node_copy(node->data.bin_op.right), node_create('c', 2.0))); + + case '^': + /* + * If right operand of exponentiation constant apply + * (f^n)'=n*f^(n-1)*f' derivative rule. + */ + if (node->data.bin_op.right->type == 'c') + return node_create('b', '*', node_create('b', '*', node_create('c', node->data.bin_op.right->data.constant), node_derivative(node->data.bin_op.left, name, symbol_table)), node_create('b', '^', node_copy(node->data.bin_op.left), node_create('c', node->data.bin_op.right->data.constant - 1.0))); + /* + * Otherwise, apply logaritmhic derivative rule: + * (log(f^g))'=(f^g)'/f^g => + * (f^g)'=f^g*(log(f^g))'=f^g*(g*log(f))' + */ + else { + Node *log_node, *derivative; + log_node = node_create('b', '*', node_copy(node->data.bin_op.right), node_create('f', symbol_table_lookup(symbol_table, "Log"), node_copy(node->data.bin_op.left))); + derivative = node_create('b', '*', node_copy(node), node_derivative(log_node, name, symbol_table)); + node_destroy(log_node); + return derivative; + } + } + } + + return NULL; } int node_calculate_length(Node * node) { - char string[1024]; /* String representing constant node + char string[1024]; /* String representing constant node * value. */ - int length; /* Length of above string. */ - - /* - * According to node type, calculate length of string representing - * subtree rooted at node. - */ - switch (node->type) { - case 'c': - length = 0; - if (node->data.constant < 0) - length += 1; - sprintf(string, "%g", node->data.constant); - length += strlen(string); - if (node->data.constant < 0) - length += 1; - return length; - - case 'v': - return strlen(node->data.variable->name); - - case 'f': - return strlen(node->data.function.record->name) + 1 + node_calculate_length(node->data.function.child) + 1; - break; - - case 'u': - return 1 + 1 + node_calculate_length(node->data.un_op.child) + 1; - - case 'b': - return 1 + node_calculate_length(node->data.bin_op.left) + 1 + node_calculate_length(node->data.bin_op.right) + 1; - } - - return 0; + int length; /* Length of above string. */ + + /* + * According to node type, calculate length of string representing + * subtree rooted at node. + */ + switch (node->type) { + case 'c': + length = 0; + if (node->data.constant < 0) + length += 1; + sprintf(string, "%g", node->data.constant); + length += strlen(string); + if (node->data.constant < 0) + length += 1; + return length; + + case 'v': + return strlen(node->data.variable->name); + + case 'f': + return strlen(node->data.function.record->name) + 1 + node_calculate_length(node->data.function.child) + 1; + break; + + case 'u': + return 1 + 1 + node_calculate_length(node->data.un_op.child) + 1; + + case 'b': + return 1 + node_calculate_length(node->data.bin_op.left) + 1 + node_calculate_length(node->data.bin_op.right) + 1; + } + + return 0; } void node_write(Node * node, char *string) { - /* - * According to node type, write subtree rooted at node to node - * string variable. Always use parenthesis to resolve operator - * precedence. - */ - switch (node->type) { - case 'c': - if (node->data.constant < 0) { - sprintf(string, "%c", '('); - string += strlen(string); - } - sprintf(string, "%g", node->data.constant); - string += strlen(string); - if (node->data.constant < 0) - sprintf(string, "%c", ')'); - break; - - case 'v': - sprintf(string, "%s", node->data.variable->name); - break; - - case 'f': - sprintf(string, "%s%c", node->data.function.record->name, '('); - string += strlen(string); - node_write(node->data.function.child, string); - string += strlen(string); - sprintf(string, "%c", ')'); - break; - - case 'u': - sprintf(string, "%c", '('); - string += strlen(string); - sprintf(string, "%c", node->data.un_op.operato); - string += strlen(string); - node_write(node->data.un_op.child, string); - string += strlen(string); - sprintf(string, "%c", ')'); - break; - - case 'b': - sprintf(string, "%c", '('); - string += strlen(string); - node_write(node->data.bin_op.left, string); - string += strlen(string); - sprintf(string, "%c", node->data.bin_op.operato); - string += strlen(string); - node_write(node->data.bin_op.right, string); - string += strlen(string); - sprintf(string, "%c", ')'); - break; + /* + * According to node type, write subtree rooted at node to node + * string variable. Always use parenthesis to resolve operator + * precedence. + */ + switch (node->type) { + case 'c': + if (node->data.constant < 0) { + sprintf(string, "%c", '('); + string += strlen(string); } + sprintf(string, "%g", node->data.constant); + string += strlen(string); + if (node->data.constant < 0) + sprintf(string, "%c", ')'); + break; + + case 'v': + sprintf(string, "%s", node->data.variable->name); + break; + + case 'f': + sprintf(string, "%s%c", node->data.function.record->name, '('); + string += strlen(string); + node_write(node->data.function.child, string); + string += strlen(string); + sprintf(string, "%c", ')'); + break; + + case 'u': + sprintf(string, "%c", '('); + string += strlen(string); + sprintf(string, "%c", node->data.un_op.operatorr); + string += strlen(string); + node_write(node->data.un_op.child, string); + string += strlen(string); + sprintf(string, "%c", ')'); + break; + + case 'b': + sprintf(string, "%c", '('); + string += strlen(string); + node_write(node->data.bin_op.left, string); + string += strlen(string); + sprintf(string, "%c", node->data.bin_op.operatorr); + string += strlen(string); + node_write(node->data.bin_op.right, string); + string += strlen(string); + sprintf(string, "%c", ')'); + break; + } } diff --git a/MathEval/node.h b/MathEval/node.h index f14b301bf13234d03547ea3a7b44efff7476f1a7..c2744aae19c8e06de253d8ad1df272067ab9a71d 100644 --- a/MathEval/node.h +++ b/MathEval/node.h @@ -41,13 +41,13 @@ typedef struct _Node { } function; /* Structure representing * function. */ struct { - char operato; /* Operator type ('-' + char operatorr;/* Operator type ('-' * for unary minus). */ struct _Node *child; /* Operand node. */ } un_op; /* Structure representing unary * operator. */ struct { - char operato; /* Operator type ('+' + char operatorr;/* Operator type ('+' * for adition, '-' for * subtraction, '*' for * multiplication, '/' diff --git a/TODO b/TODO index ab09ea29f42b8592ca7e5cc4e5552eb3de23ece3..45c1629a06a65056334b4638565ede70e76932e1 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,8 @@ -$Id: TODO,v 1.44 2004-05-12 03:22:13 geuzaine Exp $ +$Id: TODO,v 1.45 2004-05-13 05:34:06 geuzaine Exp $ + +add ternary operator and <,>,<=,>=,== tests in MathEval + +******************************************************************** generalize Plugin(Triangulate) to vector and tensor points