diff --git a/language/de-DE/de-DE.plg_vmshipment_rules_shipping.ini b/language/de-DE/de-DE.plg_vmshipment_rules_shipping.ini
index 2473863725cf57411cead5ed1725b64c23ecaed0..05b96444bf88182e741fbc08f85ce4b431d86d4d 100644
--- a/language/de-DE/de-DE.plg_vmshipment_rules_shipping.ini
+++ b/language/de-DE/de-DE.plg_vmshipment_rules_shipping.ini
@@ -37,6 +37,7 @@ VMSHIPMENT_RULES_UNKNOWN_VARIABLE="Unknown variable '%s' in rule '%s'"
 VMSHIPMENT_RULES_UNKNOWN_OPERATOR="Unknown operator '%s' in shipment rule '%s'"
 VMSHIPMENT_RULES_PARSE_MISSING_PAREN="Error during parsing expression '%s': Opening parenthesis cannot be found!"
 VMSHIPMENT_RULES_PARSE_PAREN_NOT_CLOSED="Error during parsing expression '%s': A parenthesis was not closed properly!"
+VMSHIPMENT_RULES_PARSE_FUNCTION_NOT_CLOSED="Error during parsing expression '%s': A function call was not closed properly!"
 
 VMSHIPMENT_RULES_EVALUATE_NONNUMERIC="Encountered term '%s' during evaluation, that does not evaluate to a numeric value! (Full rule: '%s')"
 VMSHIPMENT_RULES_EVALUATE_SYNTAXERROR="Syntax error during evaluation, RPN is not well formed! (Full rule: '%s')"
diff --git a/language/en-GB/en-GB.plg_vmshipment_rules_shipping.ini b/language/en-GB/en-GB.plg_vmshipment_rules_shipping.ini
index 696fe5f6cdc88ccbf074c0cc6f6ac48f10ba2fc9..79bf94190eb51dc35cb21989852d5a785437b288 100644
--- a/language/en-GB/en-GB.plg_vmshipment_rules_shipping.ini
+++ b/language/en-GB/en-GB.plg_vmshipment_rules_shipping.ini
@@ -40,10 +40,12 @@ VMSHIPMENT_RULES_UNKNOWN_VARIABLE="Unknown variable '%s' in rule '%s'"
 VMSHIPMENT_RULES_UNKNOWN_OPERATOR="Unknown operator '%s' in shipment rule '%s'"
 VMSHIPMENT_RULES_PARSE_MISSING_PAREN="Error during parsing expression '%s': Opening parenthesis cannot be found!"
 VMSHIPMENT_RULES_PARSE_PAREN_NOT_CLOSED="Error during parsing expression '%s': A parenthesis was not closed properly!"
+VMSHIPMENT_RULES_PARSE_FUNCTION_NOT_CLOSED="Error during parsing expression '%s': A function call was not closed properly!"
 
 VMSHIPMENT_RULES_EVALUATE_NONNUMERIC="Encountered term '%s' during evaluation, that does not evaluate to a numeric value! (Full rule: '%s')"
 VMSHIPMENT_RULES_EVALUATE_SYNTAXERROR="Syntax error during evaluation, RPN is not well formed! (Full rule: '%s')"
 VMSHIPMENT_RULES_EVALUATE_UNKNOWN_OPERATOR="Unknown operator '%s' encountered during evaluation of rule '%s'."
+VMSHIPMENT_RULES_EVALUATE_UNKNOWN_FUNCTION="Unknown function '%s' encountered during evaluation of rule '%s'."
 VMSHIPMENT_RULES_EVALUATE_UNKNOWN_ERROR="Unknown error occurred during evaluation of rule '%s'."
 VMSHIPMENT_RULES_EVALUATE_ASSIGNMENT_TOPLEVEL="Assignments are not allows inside expressions (rule given was '%s')"
 VMSHIPMENT_RULES_EVALUATE_UNKNOWN_VALUE="Evaluation yields unknown value while evaluating rule part '%s'."
\ No newline at end of file
diff --git a/rules_shipping_advanced.php b/rules_shipping_advanced.php
index 30ff7314eca24fb3b927c5dd88b32885921a8dea..c95e74a1e8bfb33d9692a462d02e23f48829ddc0 100644
--- a/rules_shipping_advanced.php
+++ b/rules_shipping_advanced.php
@@ -28,6 +28,26 @@ if (!class_exists ('plgVmShipmentRules_Shipping_Base')) {
 	require (dirname(__FILE__).DS.'rules_shipping_base.php');
 }
 
+
+function print_array($obj) {
+	$res = "";
+	if (is_array($obj)) {
+		$res .= "array(";
+		$sep = "";
+		foreach ($obj as $e) {
+			$res .= $sep . print_array($e);
+			$sep = ", ";
+		}
+		$res .= ")";
+	} elseif (is_string($obj)) {
+		$res .= "\"$obj\"";
+	} else {
+		$res .= (string)$obj;
+	}
+	return $res;
+}
+
+
 /** Shipping costs according to general rules.
  *  Derived from the standard plugin, no need to change anything! The standard plugin already uses the advanced rules class defined below, if it can be found
  */
@@ -85,6 +105,7 @@ class plgVmShipmentRules_Shipping_Advanced extends plgVmShipmentRules_Shipping_B
  */
 class ShippingRule_Advanced extends ShippingRule {
 	var $operators = array(
+		".-" => 100, ".+" => 100,
 		"^"  => 70, 
 		"*"  => 60, "/"  => 60, "%"  => 60, 
 		"+"  => 50, "-"  => 50, 
@@ -94,7 +115,11 @@ class ShippingRule_Advanced extends ShippingRule {
 		" OR "  => 20, "OR" => 20,
 		"="  => 10,
 		
-		"("  =>  0, ")"  =>0 );
+		"("  =>  0, ")"  =>0 
+	);
+	var $unary_ops = array(
+		"-" => ".-", "+" => ".+"
+	);
 
 	function __construct ($rule, $countries, $tax_id) {
 		parent::__construct ($rule, $countries, $tax_id);
@@ -105,7 +130,7 @@ class ShippingRule_Advanced extends ShippingRule {
 		$str_re = '/("[^"]*"|\'[^\']*\')/';
 		$strings = preg_split($str_re, $expression, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
 		// Then split all other parts of the expression at the operators
-		$op_re = ':\s*( OR |&&| AND |<=|=>|>=|=>|<>|!=|==|<|=|>|~|\+|-|\*|/|%|\(|\)|\^)\s*:';
+		$op_re = ':\s*( OR |&&| AND |<=|=>|>=|=>|<>|!=|==|<|=|>|~|\+|-|\*|/|%|\(|\)|\^|,)\s*:';
 		$atoms = array();
 		foreach ($strings as $s) {
 			if (preg_match($str_re, $s)) {
@@ -115,30 +140,13 @@ class ShippingRule_Advanced extends ShippingRule {
 				$atoms = array_merge ($atoms, $newatoms);
 			}
 		}
-		// Finally handle the double meaning of -: After an operator and before an operand, it is NOT an operator, but belongs to the operand
-		$prevop = true;
-		$result = array();
-		$leftover = '';
-		foreach ($atoms as $a) {
-			if ($a == "-") {
-				$prev = end($result);
-				if (is_null ($prev) || (preg_match ($op_re, $prev) && $prev != ')')) {
-					$leftover = $a;
-				} else {
-					$result[] = $leftover.$a;
-					$leftover = '';
-				}
-			} else {
-				$result[] = $leftover.$a;
-				$leftover = '';
-			}
-		}
-		return $result;
+		return $atoms;
 	}
 	
 
-	/** parse the mathematical expressions (following Knuth 1962):
+	/** parse the mathematical expressions using the Shunting Yard Algorithm by Dijkstra (with some extensions to allow arbitrary functions):
 	 *  First parse the string into an array of tokens (operators and operands) by a simple regexp with known operators as separators)
+	 * TODO: Update this description to include unary operators and general function calls
 	 *  Then convert the infix notation into postfix (RPN), taking care of operator precedence
 	 *    1) Initialize empty stack and empty result variable
 	 *    2) Read infix expression from left to right, one atom at a time
@@ -153,7 +161,10 @@ class ShippingRule_Advanced extends ShippingRule {
 	 *        6b) push them to the result (not the opening parenthesis, of course)
 	 *    7) At the end of the input, pop all operators from the stack and onto the result
 	 *
-	 *  Afterwards, convert this RPN list into an expression tree to be evaluate
+	 *  Afterwards, convert this RPN list into an expression tree to be evaluated
+	 * 
+	 *  For the full algorithm, including function parsing, see Wikipedia:
+	 *    http://en.wikipedia.org/wiki/Shunting_yard_algorithm
 	 *
 	 */
 	function parseRulePart($rulepart) {
@@ -180,32 +191,104 @@ class ShippingRule_Advanced extends ShippingRule {
 		$is_assignment = false;
 		
 		$stack = array ();  // 1)
-		$rpn = array ();
+		$prev_token_operator = false;
+		$function_args = array();
+		$out_stack = array();
 		foreach ($atoms as $a) { // 2)
-			if (!isset($this->operators[$a])) { // 3) Operand
-				array_push ($rpn, $a);
-			} elseif ($a == "(") { // 5) parenthesis
-				array_push ($stack, $a);
+		
+			if ($a == ",") { // A function argument separator
+				// pop-and-apply all operators back to the left function paren
+				while (count($stack)>0) { // 4a)
+					$op = array_pop ($stack);
+					if ($op != "FUNCTION(") {
+						array_push ($out_stack, $op);
+					} else {
+						// No unary operator -> add it back to stack, exit loop
+						array_push ($stack, $op);
+						break;
+					}
+				} while (0);
+				$this_func = array_pop($function_args);
+				// Add current output stack as argument, reset temporary output stack
+				if (!empty($out_stack)) $this_func[] = $out_stack;
+				$function_args[] = $this_func;
+				$out_stack = array();
+				$prev_token_operator = true;
+				
+			} elseif ($a == "(" and !$prev_token_operator) { // 5) parenthesis after operand -> FUNCTION CALL
+				array_push ($stack, "FUNCTION(");
+				// retrieve function name from RPN list (remove last entry from operand stack!)
+				$function = array_pop ($out_stack);
+				$new_stack = array();
+				// Set up function call data structure on function_args stack:
+				$function_args[] = array(/* old operand stack: */$out_stack, $function);
+				// Use a the temporary operand stack until the closing paren restores the previous operand stack again
+				$out_stack = array();
+				$prev_token_operator = true;
+
+			} elseif ($a == "(" and $prev_token_operator) { // 5) real parenthesis 
+				$stack[] = $a;
+				$prev_token_operator = true;
+				
 			} elseif ($a == ")") { // 6) parenthesis
 				do {
 					$op=array_pop($stack); // 6a)
-					if (!is_null($op) && ($op != "(")) {
-						array_push ($rpn, $op); // 6b)
-					} else {
-						if ($op != "(") {
-							// If no ( can be found, the expression is wrong!
-							JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_PARSE_MISSING_PAREN', $rulepart), 'error');
+					if ($op == "(") {
+						break; // We have found the opening parenthesis
+					} elseif ($op =="FUNCTION(") { // Function call
+						// Remove function info from the functions stack; Format is array(PREVIOUS_OPERAND_STACK, FUNCTION, ARGS...)
+						$this_func = array_pop ($function_args);
+						// Append last argument (if not empty)
+						if (!empty($out_stack)) $this_func[] = $out_stack;
+						// restore old output/operand stack
+						$out_stack = array_shift($this_func);
+						// Function name is the next entry
+						$function = array_shift($this_func);
+						// All other entries are function arguments, so append them to the current operand stack
+						foreach ($this_func as $a) {
+							foreach ($a as $aa) {
+								$out_stack[] = $aa;
+							}
 						}
+						$out_stack[] = array("FUNCTION", $function, count($this_func));
 						break; // We have found the opening parenthesis
+					} elseif (!is_null($op)) {
+						$out_stack[]=$op; // 6b) "normal" operators
+					} else {
+						// no ( and no operator, so the expression is wrong!
+						JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_PARSE_MISSING_PAREN', $rulepart), 'error');
+						break;
 					}
 				} while (true);
-			} else { // 4) operators
-				// For operators, pop operators from the stack until you reach an opening parenthesis, 
+				$prev_token_operator = false;
+				
+			} elseif (isset($this->unary_ops[$a]) && $prev_token_operator) { // 4) UNARY operators
+				// Unary and binary operators need to be handled differently: 
+				// Unary operators must only pop other unary operators, never any binary operator
+				$unary_op = $this->unary_ops[$a];
+				// For unary operators, pop other unary operators from the stack until you reach an opening parenthesis, 
 				// an operator of lower precedence, or a right associative symbol of equal precedence. 
+				while (count($stack)>0) { // 4a)
+					$op = array_pop ($stack);
+					// Remove all other unary operators:
+					if (in_array ($op, $this->unary_ops)) {
+						array_push ($out_stack, $op);
+					} else {
+						// No unary operator -> add it back to stack, exit loop
+						array_push ($stack, $op);
+						break;
+					}
+				} while (0);
+				array_push ($stack, $unary_op); // 4b)
+				$prev_token_operator = true;
+				
+			} elseif (isset($this->operators[$a])) { // 4) BINARY operators
 				$prec = $this->operators[$a];
 				$is_condition |= in_array($a, $condition_ops);
 				$is_assignment |= ($a == "=");
 				
+				// For operators, pop operators from the stack until you reach an opening parenthesis, 
+				// an operator of lower precedence, or a right associative symbol of equal precedence. 
 				while (count($stack)>0) { // 4a)
 					$op = array_pop ($stack);
 					// The only right-associative operator is =, which we allow at most once!
@@ -218,10 +301,15 @@ class ShippingRule_Advanced extends ShippingRule {
 						array_push ($stack, $op); // 4b)
 						break;
 					} else {
-						array_push ($rpn, $op);
+						array_push ($out_stack, $op);
 					}
 				} while (0);
 				array_push ($stack, $a); // 4b)
+				$prev_token_operator = true;
+				
+			} else { // 3) Everything else is an Operand
+				$out_stack[] = $a;
+				$prev_token_operator = false;
 			}
 		}
 		// Finally, pop all operators from the stack and append them to the result
@@ -230,11 +318,15 @@ class ShippingRule_Advanced extends ShippingRule {
 			if ($op == "(") {
 				JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_PARSE_PAREN_NOT_CLOSED', $rulepart), 'error');
 			} else {
-				array_push ($rpn, $op);
+				array_push ($out_stack, $op);
 			}
 		}
+		if (!empty($function_args)) {
+				JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_PARSE_FUNCTION_NOT_CLOSED', $rulepart), 'error');
+		}
+
 
-		/** Now, turn the RPN into an expression tree (i.e. "evaluate" it into a tree structure, according to Knuth:
+		/** Now, turn the RPN into an expression tree (i.e. "evaluate" it into a tree structure), according to Knuth:
 		 *   1) Initialize an empty stack
 		 *   2) Read the RPN from left to right
 		 *   3) If operand, push it onto the stack
@@ -248,11 +340,31 @@ class ShippingRule_Advanced extends ShippingRule {
 		 */
 
 		$stack=array(); // 1)
-		foreach ($rpn as $e) { // 2)
-			if (!isset($this->operators[$e])) { // 3)
-				// Operand => push onto stack
-				array_push ($stack, $e);
-			} else { // 4)
+		foreach ($out_stack as $e) { // 2)
+			if (is_array($e) && $e[0]=="FUNCTION") { // A function call (#args is saved as $e[2], so remove that number of operands from the stack)
+				$function = $e[1];
+				$argc = $e[2];
+				$args = array();
+				for ($i = $argc; $i > 0; $i--) {
+					$a = array_pop($stack);
+					array_unshift($args, $a);
+				}
+				array_unshift($args, $function);
+				array_unshift($args, "FUNCTION"); 
+				$stack[] = $args;
+			} elseif (in_array($e, $this->unary_ops)) { // 4) unary operators
+				// Operator => apply to the last value on the stack
+				if (count($stack)<1) { // 4d)
+					JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_EVALUATE_SYNTAXERROR', $rulepart), 'error');
+					array_push($stack, 0);
+					continue;
+				}
+				$o1 = array_pop($stack);
+				// Special-case chained comparisons: if e is a comparison, and operator(o1) is also a comparison, 
+				// insert the arguments to the existing comparison instead of creating a new one
+				$op = array ($e, $o1); // 4b)
+				array_push ($stack, $op); // 4c)
+			} elseif (isset($this->operators[$e])) { // 4) binary operators
 				// Operator => apply to the last two values on the stack
 				if (count($stack)<2) { // 4d)
 					JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_EVALUATE_SYNTAXERROR', $rulepart), 'error');
@@ -276,7 +388,11 @@ class ShippingRule_Advanced extends ShippingRule {
 					$op = array ($e, $o1, $o2); // 4b)
 				}
 				array_push ($stack, $op); // 4c)
+			} else { // 3)
+				// Operand => push onto stack
+				array_push ($stack, $e);
 			}
+			
 		}
 		// 5a)
 		if (count($stack) != 1) {
@@ -307,3 +423,20 @@ class ShippingRule_Advanced extends ShippingRule {
 }
 
 // No closing tag
+/*$rule = new ShippingRule_Advanced("", array(), 0);
+$rp="a+b+(-1+d)";
+$rp="1+maxx(a,b)";
+$rp="f(1,2,+)";
+$rp="1+year()";
+$rp = "1+max( 1,2,3,4,5) + min(9,10,101)";
+$rp = "abs(0-1.9999)";
+$rp="max(1,min(0,5,7), 9)";
+// $rp="f()";
+print_r($rule->tokenize_expression($rp)); 
+
+$rule->parseRulePart($rp);
+print_r($rule->conditions);
+print_r($rule->shipping);
+
+print_r($rule->evaluateTerm($rule->shipping, array()));
+*/
\ No newline at end of file
diff --git a/rules_shipping_base.php b/rules_shipping_base.php
index ab55f094c6aaad5ffc698449caa18f62374af68a..7c4b97f09230c451c5057a226d205ea0a8971418 100644
--- a/rules_shipping_base.php
+++ b/rules_shipping_base.php
@@ -704,6 +704,40 @@ class ShippingRule {
 		return true;
 	}
 	
+	function evaluateFunction ($function, $args) {
+		$func = strtolower($function);
+		// Functions with no argument:
+		if (count($args) == 0) {
+			switch ($func) {
+				case "second": return getdate()["seconds"]; break;
+				case "minute": return getdate()["minutes"]; break;
+				case "hour":   return getdate()["hours"]; break;
+				case "day":    return getdate()["mday"]; break;
+				case "weekday":return getdate()["wday"]; break;
+				case "month":  return getdate()["mon"]; break;
+				case "year":   return getdate()["year"]; break;
+				case "yearday":return getdate()["yday"]; break;
+			}
+		}
+		// Functions with exactly one argument:
+		if (count($args) == 1) {
+			switch ($func) {
+				case "round": return round($args[0]); break;
+				case "ceil":  return ceil ($args[0]); break;
+				case "floor": return floor($args[0]); break;
+				case "abs":   return abs($args[0]); break;
+			}
+		}
+		// Functions with variable number of args
+		switch ($func) {
+			case "max": return max($args); break;
+			case "min": return min($args); break;
+		}
+		// No known function matches => print an error, return 0
+		JFactory::getApplication()->enqueueMessage(JText::sprintf('VMSHIPMENT_RULES_EVALUATE_UNKNOWN_FUNCTION', $function, $this->rulestring), 'error');
+		return 0;
+	}
+	
 	function evaluateTerm ($expr, $vals) {
 		if (is_null($expr)) {
 			return $expr;
@@ -724,17 +758,24 @@ class ShippingRule {
 			$op = array_shift($expr);
 			$args = array();
 			$evaluate = true;
+			if ($op == "FUNCTION") {
+				$evaluate = false;
+			}
 			foreach ($expr as $e) {
 				$term = $evaluate ? ($this->evaluateTerm($e, $vals)) : $e;
 				if ($op == 'comparison') {
 					// For comparisons, we only evaluate every other term (the operators are NOT evaluated!)
 					$evaluate = !$evaluate;
 				}
+				if ($op == "FUNCTION") {
+					$evaluate = true;
+				}
 				if (is_null($term)) return null;
 				$args[] = $term;
 			}
 			$res = false;
 			switch ($op) {
+				// Logical operators:
 				case 'OR':
 				case ' OR ':  foreach ($args as $a) { $res = ($res || $a); }; break;
 				case '&&':
@@ -742,6 +783,7 @@ class ShippingRule {
 				case ' AND ':  $res = true; foreach ($args as $a) { $res = ($res && $a); }; break;
 				case 'in': $needle = array_shift($args); $res = in_array($needle, $args); break;
 				
+				// Comparisons:
 				case '<':
 				case '<=':
 				case '=<':
@@ -756,6 +798,11 @@ class ShippingRule {
 				case 'comparison':
 					$res = $this->evaluateComparison($args, $vals); break;
 				
+				// Unary operators:
+				case '.-': $res = -$args[0]; break;
+				case '.+': $res = $args[0]; break;
+				
+				// Binary operators
 				case "+":  $res = ($args[0] +  $args[1]); break;
 				case "-":  $res = ($args[0] -  $args[1]); break;
 				case "*":  $res = ($args[0] *  $args[1]); break;
@@ -763,12 +810,8 @@ class ShippingRule {
 				case "%":  $res = (fmod($args[0],  $args[1])); break;
 				case "^":  $res = ($args[0] ^  $args[1]); break;
 				
-				# TODO: Document these functions:
-				case "round": $res = round($args[0]); break;
-				case "ceil": $res = ceil($args[0]); break;
-				case "floor": $res = floor($args[0]); break;
-				case "max": $res = max($args); break;
-				case "min": $res = min($args); break;
+				// Functions:
+				case "FUNCTION": $func = array_shift($args); $res = $this->evaluateFunction($func, $args); break;
 				
 				default:   $res = false;
 			}