namespace Php {
    /**
     * PHP addition with native Vala arguments: `$left + $right`.
     */
    public double add_static (double left, double right) {
        return left + right;
    }

    /**
     * PHP addition with zval arguments/result: `$left + $right`.
     */
    public Php.zval add_zval (Php.zval left, Php.zval right) {
        if (left.get_value_type () == Php.ValueType.INT && right.get_value_type () == Php.ValueType.INT) {
            return Php.zval.integer (left.as_int () + right.as_int ());
        }
        return Php.zval.double_number (add_static (zval_to_double (left), zval_to_double (right)));
    }

    /**
     * PHP subtraction with native Vala arguments: `$left - $right`.
     */
    public double sub_static (double left, double right) {
        return left - right;
    }

    /**
     * PHP subtraction with zval arguments/result: `$left - $right`.
     */
    public Php.zval sub_zval (Php.zval left, Php.zval right) {
        if (left.get_value_type () == Php.ValueType.INT && right.get_value_type () == Php.ValueType.INT) {
            return Php.zval.integer (left.as_int () - right.as_int ());
        }
        return Php.zval.double_number (sub_static (zval_to_double (left), zval_to_double (right)));
    }

    /**
     * PHP multiplication with native Vala arguments: `$left * $right`.
     */
    public double mul_static (double left, double right) {
        return left * right;
    }

    /**
     * PHP multiplication with zval arguments/result: `$left * $right`.
     */
    public Php.zval mul_zval (Php.zval left, Php.zval right) {
        if (left.get_value_type () == Php.ValueType.INT && right.get_value_type () == Php.ValueType.INT) {
            return Php.zval.integer (left.as_int () * right.as_int ());
        }
        return Php.zval.double_number (mul_static (zval_to_double (left), zval_to_double (right)));
    }

    /**
     * PHP division with native Vala arguments: `$dividend / $divisor`.
     */
    public double div_static (double dividend, double divisor, string php_file, int php_line) throws Error {
        if (divisor == 0.0) {
            StacktraceError.throw (with_php_source ("Division by zero", php_file, php_line));
        }

        return dividend / divisor;
    }

    /**
     * PHP division with zval arguments/result: `$dividend / $divisor`.
     */
    public Php.zval div_zval (Php.zval dividend, Php.zval divisor, string php_file, int php_line) throws Error {
        if (dividend.get_value_type () == Php.ValueType.INT && divisor.get_value_type () == Php.ValueType.INT) {
            int64 int_divisor = divisor.as_int ();
            if (int_divisor == 0) {
                StacktraceError.throw (with_php_source ("Division by zero", php_file, php_line));
            }
            int64 int_dividend = dividend.as_int ();
            if (int_dividend % int_divisor == 0) {
                return Php.zval.integer (int_dividend / int_divisor);
            }
        }
        return Php.zval.double_number (div_static (zval_to_double (dividend), zval_to_double (divisor), php_file, php_line));
    }

    /**
     * PHP unary negation with native Vala argument: `-$value`.
     */
    public double neg_static (double value) {
        return value * -1.0;
    }

    /**
     * PHP unary negation with zval argument/result: `-$value`.
     */
    public Php.zval neg_zval (Php.zval value) {
        if (value.get_value_type () == Php.ValueType.INT) {
            return Php.zval.integer (-value.as_int ());
        }
        return Php.zval.double_number (neg_static (zval_to_double (value)));
    }

}
