Current section
Files
Jump to
Current section
Files
src/xqerl_mod_math.erl
%% -------------------------------------------------------------------
%%
%% xqerl - XQuery processor
%%
%% Copyright (c) 2017-2020 Zachary N. Dean All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% -------------------------------------------------------------------
%% @doc Implementation of the "http://www.w3.org/2005/xpath-functions/math"
%% namespace.
%% Pretty much just wraps the math module from Erlang and adds NaN, inf and -0.
-module(xqerl_mod_math).
-include("xqerl.hrl").
-export([acos/2]).
-export([asin/2]).
-export([atan/2]).
-export([atan2/3]).
-export([cos/2]).
-export([exp/2]).
-export([exp10/2]).
-export([log/2]).
-export([log10/2]).
-export(['pi'/1]).
-export([pow/3]).
-export([sin/2]).
-export([sqrt/2]).
-export([tan/2]).
-define(NS, <<"http://www.w3.org/2005/xpath-functions/math">>).
-define(PX, <<"math">>).
-'module-namespace'({?NS, ?PX}).
-namespaces([{"xqerl_mod_xs", "xs"}]).
-variables([]).
-functions([
{{qname, ?NS, ?PX, <<"acos">>}, {seqType, 'xs:double', zero_or_one}, [], {acos, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"asin">>}, {seqType, 'xs:double', zero_or_one}, [], {asin, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"atan">>}, {seqType, 'xs:double', zero_or_one}, [], {atan, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"atan2">>}, {seqType, 'xs:double', one}, [], {atan2, 3}, 2, [
{seqType, 'xs:double', one},
{seqType, 'xs:double', one}
]},
{{qname, ?NS, ?PX, <<"cos">>}, {seqType, 'xs:double', zero_or_one}, [], {cos, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"exp">>}, {seqType, 'xs:double', zero_or_one}, [], {exp, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"exp10">>}, {seqType, 'xs:double', zero_or_one}, [], {exp10, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"log">>}, {seqType, 'xs:double', zero_or_one}, [], {log, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"log10">>}, {seqType, 'xs:double', zero_or_one}, [], {log10, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"pi">>}, {seqType, 'xs:double', one}, [], {'pi', 1}, 0, []},
{{qname, ?NS, ?PX, <<"pow">>}, {seqType, 'xs:double', zero_or_one}, [], {pow, 3}, 2, [
{seqType, 'xs:double', zero_or_one},
{seqType, 'xs:numeric', one}
]},
{{qname, ?NS, ?PX, <<"sin">>}, {seqType, 'xs:double', zero_or_one}, [], {sin, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"sqrt">>}, {seqType, 'xs:double', zero_or_one}, [], {sqrt, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]},
{{qname, ?NS, ?PX, <<"tan">>}, {seqType, 'xs:double', zero_or_one}, [], {tan, 2}, 1, [
{seqType, 'xs:double', zero_or_one}
]}
]).
%% Returns the arc cosine of the argument.
acos(_, Arg) -> acos(Arg).
acos([]) ->
[];
acos([Seq]) ->
acos(Seq);
acos(#xqAtomicValue{value = X}) ->
acos(X);
acos(neg_zero) ->
math:acos(0);
acos(nan) ->
nan;
acos(X) when is_number(X), abs(X) > 1 ->
nan;
acos(X) ->
case catch math:acos(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the arc sine of the argument.
asin(_, Arg) -> asin(Arg).
asin([]) ->
[];
asin([Arg]) ->
asin(Arg);
asin(#xqAtomicValue{value = X}) ->
asin(X);
asin(A) when A == 0 ->
A;
asin(neg_zero) ->
neg_zero;
asin(nan) ->
nan;
asin(X) when abs(X) > 1 ->
nan;
asin(X) ->
case catch math:asin(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the arc tangent of the argument.
atan(_, Arg) -> atan(Arg).
atan([]) ->
[];
atan([Seq]) ->
atan(Seq);
atan(#xqAtomicValue{value = X}) ->
atan(X);
atan(A) when A == 0 ->
A;
atan(neg_zero) ->
neg_zero;
atan(nan) ->
nan;
atan(infinity) ->
math:pi() / 2;
atan(neg_infinity) ->
-math:pi() / 2;
atan(X) ->
case catch math:atan(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the angle in radians subtended at the origin by the point on a
%% plane with coordinates (x, y) and the positive x-axis.
atan2(_, Arg1, Arg2) -> atan2(Arg1, Arg2).
atan2([X], Y) ->
atan2(X, Y);
atan2(X, [Y]) ->
atan2(X, Y);
atan2(#xqAtomicValue{value = X}, Y) ->
atan2(X, Y);
atan2(X, #xqAtomicValue{value = Y}) ->
atan2(X, Y);
% special values
atan2(neg_zero, V) when V == 0.0 ->
neg_zero;
atan2(neg_zero, V) when V == -1.0 ->
-math:pi();
atan2(V, neg_zero) when V == -1.0 ->
math:atan2(-1.0, 0.0);
atan2(neg_zero, V) when V == 1.0 ->
neg_zero;
atan2(V, neg_zero) when V == 0.0 ->
math:pi();
atan2(neg_zero, neg_zero) ->
-math:pi();
atan2(X, Y) ->
case catch math:atan2(X, Y) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the cosine of the argument. The argument is an angle in radians.
cos(_, Arg) -> cos(Arg).
cos([]) ->
[];
cos([Seq]) ->
cos(Seq);
cos(#xqAtomicValue{value = X}) ->
cos(X);
cos(neg_zero) ->
1.0;
cos(X) ->
case catch math:cos(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the value of ex.
exp(_, Arg) -> exp(Arg).
exp([]) ->
[];
exp([Seq]) ->
exp(Seq);
exp(#xqAtomicValue{value = X}) ->
exp(X);
exp(nan = X) ->
X;
exp(infinity = X) ->
X;
exp(neg_infinity) ->
0.0;
exp(X) ->
case catch math:exp(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the value of 10x.
exp10(_, Arg) -> exp10(Arg).
exp10([]) ->
[];
exp10([Seq]) ->
exp10(Seq);
exp10(#xqAtomicValue{value = X}) ->
exp10(X);
exp10(nan = X) ->
X;
exp10(infinity = X) ->
X;
exp10(neg_infinity) ->
0.0;
exp10(X) ->
case catch math:pow(10, X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the natural logarithm of the argument.
log(_, Arg) -> log(Arg).
log([]) ->
[];
log([Seq]) ->
log(Seq);
log(#xqAtomicValue{value = X}) ->
log(X);
log(nan = X) ->
X;
log(infinity = X) ->
X;
log(neg_infinity) ->
nan;
log(X) when X == 0 ->
neg_infinity;
log(X) when X < 0 ->
nan;
log(X) ->
case catch math:log(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the base-ten logarithm of the argument.
log10(_, Arg) -> log10(Arg).
log10([]) ->
[];
log10([Seq]) ->
log10(Seq);
log10(#xqAtomicValue{value = X}) ->
log10(X);
log10(nan = X) ->
X;
log10(infinity = X) ->
X;
log10(neg_infinity) ->
nan;
log10(X) when X == 0 ->
neg_infinity;
log10(X) when X < 0 ->
nan;
log10(X) ->
case catch math:log10(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns an approximation to the mathematical constant π.
pi(_Ctx) -> math:pi().
%% Returns the result of raising the first argument to the power of the second.
pow(_, Arg1, Arg2) -> pow(Arg1, Arg2).
pow([], _) ->
[];
pow([X], Y) ->
pow(X, Y);
pow(X, [Y]) ->
pow(X, Y);
pow(#xqAtomicValue{value = X}, Y) ->
pow(X, Y);
pow(X, #xqAtomicValue{value = Y}) ->
pow(X, Y);
pow(infinity, Y) when Y == 0 ->
1.0;
pow(neg_infinity, Y) when Y == 0 ->
1.0;
pow(nan, Y) when Y == 0 ->
1.0;
pow(X, Y) when X == 0, Y < 0 ->
infinity;
pow(neg_zero, Y) when Y < 0, trunc(Y) == Y, trunc(Y) rem 2 == -1 ->
neg_infinity;
pow(neg_zero, Y) when Y < 0, trunc(Y) == Y ->
infinity;
pow(neg_zero, Y) when Y < 0 ->
infinity;
pow(neg_zero, Y) when Y > 0, trunc(Y) == Y, trunc(Y) rem 2 == 1 ->
neg_zero;
pow(neg_zero, Y) when Y > 0, trunc(Y) == Y ->
0.0;
pow(neg_zero, Y) when Y > 0 ->
0.0;
pow(X, _Y) when abs(X) == 1 ->
1.0;
pow(X, Y) ->
case catch math:pow(X, Y) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the sine of the argument. The argument is an angle in radians.
sin(_, Arg) -> sin(Arg).
sin([]) ->
[];
sin([Seq]) ->
sin(Seq);
sin(#xqAtomicValue{value = X}) ->
sin(X);
sin(X) when X == 0 ->
X;
sin(neg_zero = X) ->
X;
sin(nan = X) ->
X;
sin(neg_infinity) ->
nan;
sin(X) ->
case catch math:sin(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the non-negative square root of the argument.
sqrt(_, Arg) -> sqrt(Arg).
sqrt([]) ->
[];
sqrt([Seq]) ->
sqrt(Seq);
sqrt(#xqAtomicValue{value = X}) ->
sqrt(X);
sqrt(nan = X) ->
X;
sqrt(infinity = X) ->
X;
sqrt(neg_zero = X) ->
X;
sqrt(neg_infinity) ->
nan;
sqrt(X) ->
case catch math:sqrt(X) of
{'EXIT', _} -> nan;
Z -> Z
end.
%% Returns the tangent of the argument. The argument is an angle in radians.
tan(_, Arg) -> tan(Arg).
tan([]) ->
[];
tan([Seq]) ->
tan(Seq);
tan(#xqAtomicValue{value = X}) ->
tan(X);
tan(neg_zero) ->
neg_zero;
tan(X) ->
case catch math:tan(X) of
{'EXIT', _} -> nan;
Z -> Z
end.