-module(ephp_lib_math). -author('manuel@altenwald.com'). -behaviour(ephp_lib). -export([init_func/0, init_config/0, init_const/0, php_round/3, php_ceil/3, php_floor/3, php_sqrt/3, php_abs/3, php_asin/3, php_asinh/3, php_atan/3, php_atan2/4, php_atanh/3, php_acos/3, php_acosh/3, bindec/3, php_sin/3, php_sinh/3, php_tan/3, php_tanh/3, php_cos/3, php_cosh/3, php_pow/4, base_convert/5, pi/2, php_max/4, php_min/4, php_exp/3, php_log/4]). -include("ephp.hrl"). -include("ephp_array.hrl"). -define(M_PI, 3.14159265358979323846). -define(M_E, 2.7182818284590452354). -define(M_LOG2E, 1.4426950408889634074). -define(M_LOG10E, 0.43429448190325182765). -define(M_LN2, 0.69314718055994530942). -define(M_LN10, 2.30258509299404568402). -define(M_PI_2, 1.57079632679489661923). -define(M_PI_4, 0.78539816339744830962). -define(M_1_PI, 0.31830988618379067154). -define(M_2_PI, 0.63661977236758134308). -define(M_QSRTPI, 1.77245385090551602729). -define(M_2_SQRTPI, 1.12837916709551257390). -define(M_SQRT2, 1.41421356237309504880). -define(M_SQRT3, 1.73205080756887729352). -define(M_SQRT1_2, 0.70710678118654752440). -define(M_LNPI, 1.14472988584940017414). -define(M_EULER, 0.57721566490153286061). -spec init_func() -> ephp_lib:php_function_results(). init_func() -> [{php_ceil, [{alias, <<"ceil">>}]}, {php_floor, [{alias, <<"floor">>}]}, {php_round, [{alias, <<"round">>}]}, {php_sqrt, [{args, [double]}, {alias, <<"sqrt">>}]}, {php_abs, [{alias, <<"abs">>}]}, {php_acos, [{args, [double]}, {alias, <<"acos">>}]}, {php_acosh, [{args, [double]}, {alias, <<"acosh">>}]}, {php_asin, [{args, [double]}, {alias, <<"asin">>}]}, {php_asinh, [{args, [double]}, {alias, <<"asinh">>}]}, {php_atan, [{args, [double]}, {alias, <<"atan">>}]}, {php_atan2, [{args, [double, double]}, {alias, <<"atan2">>}]}, {php_atanh, [{args, [double]}, {alias, <<"atanh">>}]}, {php_exp, [{alias, <<"exp">>}]}, {php_max, [{alias, <<"max">>}]}, {php_min, [{alias, <<"min">>}]}, bindec, {php_cos, [{args, [double]}, {alias, <<"cos">>}]}, {php_cosh, [{args, [double]}, {alias, <<"cosh">>}]}, {php_sin, [{args, [double]}, {alias, <<"sin">>}]}, {php_sinh, [{args, [double]}, {alias, <<"sinh">>}]}, {php_tan, [{args, [double]}, {alias, <<"tan">>}]}, {php_tanh, [{args, [double]}, {alias, <<"tanh">>}]}, {php_pow, [{alias, <<"pow">>}]}, {php_log, [{args, [double, {double, ?M_E}]}, {alias, <<"log">>}]}, base_convert, pi]. -spec init_config() -> ephp_lib:php_config_results(). init_config() -> [{<<"precision">>, 14}]. -spec init_const() -> ephp_lib:php_const_results(). init_const() -> [{<<"M_PI">>, ?M_PI}, {<<"M_E">>, ?M_E}, {<<"M_LOG2E">>, ?M_LOG2E}, {<<"M_LOG10E">>, ?M_LOG10E}, {<<"M_LN2">>, ?M_LN2}, {<<"M_LN10">>, ?M_LN10}, {<<"M_PI_2">>, ?M_PI_2}, {<<"M_PI_4">>, ?M_PI_4}, {<<"M_1_PI">>, ?M_1_PI}, {<<"M_2_PI">>, ?M_2_PI}, {<<"M_QSRTPI">>, ?M_QSRTPI}, {<<"M_2_SQRTPI">>, ?M_2_SQRTPI}, {<<"M_SQRT2">>, ?M_SQRT2}, {<<"M_SQRT3">>, ?M_SQRT3}, {<<"M_SQRT1_2">>, ?M_SQRT1_2}, {<<"M_LNPI">>, ?M_LNPI}, {<<"M_EULER">>, ?M_EULER}, {<<"PHP_ROUND_HALF_UP">>, 1}, {<<"PHP_ROUND_HALF_DOWN">>, 2}, {<<"PHP_ROUND_HALF_EVEN">>, 3}, {<<"PHP_ROUND_HALF_ODD">>, 4}, {<<"NAN">>, nan}, {<<"INF">>, infinity}, {<<"PHP_INT_SIZE">>, ?PHP_INT_SIZE}, {<<"PHP_INT_MAX">>, ?PHP_INT_MAX}]. -spec php_ceil(ephp:context_id(), line(), var_value()) -> integer(). php_ceil(_Context, _Line, {_, Value}) when is_number(Value) -> ephp_data:ceiling(Value); php_ceil(_Context, _Line, {_, Value}) -> ephp_data:ceiling( ephp_data:bin_to_number(Value)). -spec php_floor(ephp:context_id(), line(), var_value()) -> integer(). php_floor(_Context, _Line, {_, Value}) when is_number(Value) -> ephp_data:flooring(Value); php_floor(_Context, _Line, {_, Value}) -> ephp_data:flooring( ephp_data:bin_to_number(Value)). -spec php_round(ephp:context_id(), line(), var_value()) -> integer(). php_round(_Context, _Line, {_, Value}) when is_number(Value) -> round(Value); php_round(_Context, _Line, {_, Value}) -> round(ephp_data:bin_to_number(Value)). -spec php_sqrt(ephp:context_id(), line(), var_value()) -> float(). php_sqrt(_Context, _Line, {_, Value}) -> math:sqrt(float(Value)). -spec php_abs(ephp:context_id(), line(), number()) -> number(). php_abs(_Context, _Line, {_, Number}) when is_number(Number) -> abs(Number); php_abs(_Context, _Line, {_, String}) when is_binary(String) -> abs(ephp_data:bin_to_number(String)); php_abs(_Context, _Line, _NotANumber) -> false. -spec php_acos(ephp:context_id(), line(), {any(), number()}) -> php_float(). php_acos(_Context, _Line, {_, Number}) -> try math:acos(Number) catch error:badarith -> nan end. -spec php_acosh(ephp:context_id(), line(), {any(), number()}) -> float(). php_acosh(_Context, _Line, {_Var, Number}) -> math:acosh(Number). -spec php_asin(ephp:context_id(), line(), {any(), number()}) -> float(). php_asin(_Context, _Line, {_, Number}) -> math:asin(Number). -spec php_asinh(ephp:context_id(), line(), {any(), number()}) -> float(). php_asinh(_Context, _Line, {_, Number}) -> math:asinh(Number). -spec php_atan(ephp:context_id(), line(), {any(), number()}) -> float(). php_atan(_Context, _Line, {_, Number}) -> math:atan(Number). -spec php_atanh(ephp:context_id(), line(), {any(), number()}) -> float(). php_atanh(_Context, _Line, {_, Number}) -> 1 / 2 * math:log((1 + Number) / (1 - Number)). -spec php_atan2(ephp:context_id(), line(), {any(), number()}, {any(), number()}) -> float(). php_atan2(_Context, _Line, {_, Number1}, {_, Number2}) -> math:atan2(Number1, Number2). -spec filter_bin(binary(), binary()) -> binary(). %@hidden filter_bin(<<>>, B) -> B; filter_bin(<>, B) when A =:= $0 orelse A =:= $1 -> filter_bin(Rest, <>); filter_bin(<<_/utf8, Rest/binary>>, B) -> filter_bin(Rest, B). -spec bindec(ephp:context_id(), line(), var_value()) -> integer(). bindec(_Context, _Line, {_, String}) when is_binary(String) -> Filtered = filter_bin(String, <<"0">>), binary_to_integer(Filtered, 2); bindec(Context, Line, {Var, A}) when ?IS_ARRAY(A) -> Level = ?E_NOTICE, File = ephp_context:get_active_file(Context), Type = <<"string">>, Error = {error, earrayconv, Line, File, Level, {Type}}, ephp_error:handle_error(Context, Error), bindec(Context, Line, {Var, <<>>}); bindec(_Context, Line, {_, #obj_ref{pid = Objects, ref = ObjectId}}) -> ClassName = ephp_object:get_class_name(Objects, ObjectId), ephp_error:error({error, enotostring, Line, ?E_RECOVERABLE_ERROR, {ClassName}}); bindec(Context, Line, {Var, Other}) -> bindec(Context, Line, {Var, ephp_data:to_bin(Other)}). -spec php_cos(ephp:context_id(), line(), {any(), number()}) -> float(). php_cos(_Context, _Line, {_, Number}) -> math:cos(Number). -spec php_cosh(ephp:context_id(), line(), {any(), number()}) -> float(). php_cosh(_Context, _Line, {_Var, Number}) -> math:cosh(Number). -spec php_sin(ephp:context_id(), line(), {any(), number()}) -> float(). php_sin(_Context, _Line, {_, Number}) -> math:sin(Number). -spec php_sinh(ephp:context_id(), line(), {any(), number()}) -> float(). php_sinh(_Context, _Line, {_, Number}) -> math:sinh(Number). -spec php_tan(ephp:context_id(), line(), {any(), number()}) -> float(). php_tan(_Context, _Line, {_, Number}) -> math:tan(Number). -spec php_tanh(ephp:context_id(), line(), {any(), number()}) -> float(). php_tanh(_Context, _Line, {_, Number}) -> math:tanh(Number). -spec get_pow_value(ephp:context_id(), line(), mixed()) -> number(). get_pow_value(Context, Line, #obj_ref{pid = Objects, ref = ObjectId}) -> ClassName = ephp_object:get_class_name(Objects, ObjectId), File = ephp_context:get_active_file(Context), Level = ?E_NOTICE, Data = {ClassName, <<"int">>}, Error = {error, enocast, Line, File, Level, Data}, ephp_error:handle_error(Context, Error), 1; get_pow_value(_Context, _Line, N) -> ephp_data:bin_to_number( ephp_data:to_bin(N)). -spec php_pow(ephp:context_id(), line(), var_value(), var_value()) -> float() | integer(). php_pow(Context, Line, {_, Base}, {_, Power}) -> B = get_pow_value(Context, Line, Base), P = get_pow_value(Context, Line, Power), math:pow(B, P). -spec php_log(ephp:context_id(), line(), var_value(), var_value()) -> float(). php_log(_Context, _Line, {_, X}, {_, X}) -> 1.0; php_log(_Context, _Line, {_, X}, {_, B}) when B > 0 -> math:log(X) / math:log(B). base_convert_error(Context, Line, ArgNum, ArgData) -> Level = ?E_WARNING, Function = <<"base_convert">>, WrongType = ephp_data:gettype(ArgData), File = ephp_context:get_active_file(Context), Data = {Function, ArgNum, <<"long">>, WrongType}, Error = {error, ewrongarg, Line, File, Level, Data}, ephp_error:handle_error(Context, Error), <<>>. base_convert_invalid(Context, Line, Spec, Val) -> Level = ?E_WARNING, Function = <<"base_convert">>, File = ephp_context:get_active_file(Context), Data = {Function, Spec, Val}, Error = {error, einvalid, Line, File, Level, Data}, ephp_error:handle_error(Context, Error), <<>>. base(N) -> lists:sublist([$0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r, $s, $t, $u, $v, $w, $x, $y, $z], N). filter_base(Text, Base) -> filter_base(Text, base(Base), <<"0">>). filter_base(<<>>, _Base, Filtered) -> Filtered; filter_base(<>, Base, Filtered) when A >= $A andalso A =< $Z -> Char = A + ($a - $A), filter_base(<>, Base, Filtered); filter_base(<>, Base, Filtered) -> case lists:member(A, Base) of true -> filter_base(Rest, Base, <>); false -> filter_base(Rest, Base, Filtered) end. -spec base_convert(ephp:context_id(), line(), var_value(), var_value(), var_value()) -> binary(). base_convert(Context, Line, _, {_, From}, _) when not is_integer(From) -> base_convert_error(Context, Line, 2, From); base_convert(Context, Line, _, _, {_, To}) when not is_integer(To) -> base_convert_error(Context, Line, 3, To); base_convert(Context, Line, _, {_, From}, _) when From < 2 orelse From > 36 -> base_convert_invalid(Context, Line, <<"from base">>, From); base_convert(Context, Line, _, _, {_, To}) when To < 2 orelse To > 36 -> base_convert_invalid(Context, Line, <<"to base">>, To); base_convert(_Context, _Line, {_, String}, {_, From}, {_, To}) when is_binary(String) -> Filtered = filter_base(String, From), To10 = binary_to_integer(Filtered, From), integer_to_binary(To10, To); base_convert(Context, Line, {Var, A}, From, To) when ?IS_ARRAY(A) -> Level = ?E_NOTICE, File = ephp_context:get_active_file(Context), Type = <<"string">>, Error = {error, earrayconv, Line, File, Level, {Type}}, ephp_error:handle_error(Context, Error), base_convert(Context, Line, {Var, <<>>}, From, To); base_convert(_Context, Line, {_, #obj_ref{pid = Objects, ref = ObjectId}}, _From, _To) -> ClassName = ephp_object:get_class_name(Objects, ObjectId), ephp_error:error({error, enotostring, Line, ?E_RECOVERABLE_ERROR, {ClassName}}); base_convert(Context, Line, {Var, Other}, From, To) -> base_convert(Context, Line, {Var, ephp_data:to_bin(Other)}, From, To). -spec pi(ephp:context_id(), line()) -> float(). pi(Context, Line) -> ephp_context:get_const(Context, <<"M_PI">>, Line). -spec php_min(ephp:context_id(), line(), Num1 :: var_value(), Num2 :: var_value()) -> integer(). php_min(_Context, _Line, {_, Num1}, {_, Num2}) -> if Num1 < Num2 -> Num1; true -> Num2 end. -spec php_max(ephp:context_id(), line(), Num1 :: var_value(), Num2 :: var_value()) -> integer(). php_max(_Context, _Line, {_, Num1}, {_, Num2}) -> if Num1 > Num2 -> Num1; true -> Num2 end. -spec php_exp(ephp:context_id(), line(), Arg :: var_value()) -> float(). php_exp(_Context, _Line, {_, Arg}) -> math:exp(Arg).