Add proper handling for derived datatypes on numeric operations
with type promotion
This commit is contained in:
parent
bf932be26d
commit
09dceb0e35
3 changed files with 236 additions and 150 deletions
|
@ -5,11 +5,12 @@ defmodule RDF.XSD.Numeric do
|
||||||
|
|
||||||
@type t :: module
|
@type t :: module
|
||||||
|
|
||||||
|
alias RDF.{XSD, Literal}
|
||||||
alias Elixir.Decimal, as: D
|
alias Elixir.Decimal, as: D
|
||||||
|
|
||||||
import Kernel, except: [abs: 1, floor: 1, ceil: 1]
|
import Kernel, except: [abs: 1, floor: 1, ceil: 1]
|
||||||
|
|
||||||
defdelegate datatype?(value), to: RDF.Literal.Datatype.Registry, as: :numeric_datatype?
|
defdelegate datatype?(value), to: Literal.Datatype.Registry, as: :numeric_datatype?
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Tests for numeric value equality of two numeric XSD datatyped literals.
|
Tests for numeric value equality of two numeric XSD datatyped literals.
|
||||||
|
@ -21,8 +22,8 @@ defmodule RDF.XSD.Numeric do
|
||||||
"""
|
"""
|
||||||
@spec equal_value?(t() | any, t() | any) :: boolean
|
@spec equal_value?(t() | any, t() | any) :: boolean
|
||||||
def equal_value?(left, right)
|
def equal_value?(left, right)
|
||||||
def equal_value?(left, %RDF.Literal{literal: right}), do: equal_value?(left, right)
|
def equal_value?(left, %Literal{literal: right}), do: equal_value?(left, right)
|
||||||
def equal_value?(%RDF.Literal{literal: left}, right), do: equal_value?(left, right)
|
def equal_value?(%Literal{literal: left}, right), do: equal_value?(left, right)
|
||||||
def equal_value?(nil, _), do: nil
|
def equal_value?(nil, _), do: nil
|
||||||
def equal_value?(_, nil), do: nil
|
def equal_value?(_, nil), do: nil
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ defmodule RDF.XSD.Numeric do
|
||||||
end
|
end
|
||||||
|
|
||||||
def equal_value?(%left_datatype{value: left}, %right_datatype{value: right})
|
def equal_value?(%left_datatype{value: left}, %right_datatype{value: right})
|
||||||
when left_datatype == RDF.XSD.Decimal or right_datatype == RDF.XSD.Decimal,
|
when left_datatype == XSD.Decimal or right_datatype == XSD.Decimal,
|
||||||
do: not is_nil(left) and not is_nil(right) and equal_decimal_value?(left, right)
|
do: not is_nil(left) and not is_nil(right) and equal_decimal_value?(left, right)
|
||||||
|
|
||||||
def equal_value?(%left_datatype{value: left}, %right_datatype{value: right}) do
|
def equal_value?(%left_datatype{value: left}, %right_datatype{value: right}) do
|
||||||
|
@ -44,7 +45,7 @@ defmodule RDF.XSD.Numeric do
|
||||||
end
|
end
|
||||||
|
|
||||||
def equal_value?(left, right),
|
def equal_value?(left, right),
|
||||||
do: equal_value?(RDF.Literal.coerce(left), RDF.Literal.coerce(right))
|
do: equal_value?(Literal.coerce(left), Literal.coerce(right))
|
||||||
|
|
||||||
defp equal_decimal_value?(%D{} = left, %D{} = right), do: D.equal?(left, right)
|
defp equal_decimal_value?(%D{} = left, %D{} = right), do: D.equal?(left, right)
|
||||||
|
|
||||||
|
@ -68,13 +69,13 @@ defmodule RDF.XSD.Numeric do
|
||||||
Returns `nil` when the given arguments are not comparable datatypes.
|
Returns `nil` when the given arguments are not comparable datatypes.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@spec compare(RDF.Literal.t | t, RDF.Literal.t | t) :: RDF.XSD.Datatype.comparison_result() | nil
|
@spec compare(Literal.t | t, Literal.t | t) :: XSD.Datatype.comparison_result() | nil
|
||||||
def compare(left, right)
|
def compare(left, right)
|
||||||
def compare(left, %RDF.Literal{literal: right}), do: compare(left, right)
|
def compare(left, %Literal{literal: right}), do: compare(left, right)
|
||||||
def compare(%RDF.Literal{literal: left}, right), do: compare(left, right)
|
def compare(%Literal{literal: left}, right), do: compare(left, right)
|
||||||
|
|
||||||
def compare(
|
def compare(
|
||||||
%RDF.XSD.Decimal{value: left},
|
%XSD.Decimal{value: left},
|
||||||
%right_datatype{value: right}
|
%right_datatype{value: right}
|
||||||
) do
|
) do
|
||||||
if datatype?(right_datatype) do
|
if datatype?(right_datatype) do
|
||||||
|
@ -84,7 +85,7 @@ defmodule RDF.XSD.Numeric do
|
||||||
|
|
||||||
def compare(
|
def compare(
|
||||||
%left_datatype{value: left},
|
%left_datatype{value: left},
|
||||||
%RDF.XSD.Decimal{value: right}
|
%XSD.Decimal{value: right}
|
||||||
) do
|
) do
|
||||||
if datatype?(left_datatype) do
|
if datatype?(left_datatype) do
|
||||||
compare_decimal_value(left, right)
|
compare_decimal_value(left, right)
|
||||||
|
@ -118,14 +119,14 @@ defmodule RDF.XSD.Numeric do
|
||||||
defp compare_decimal_value(_, _), do: nil
|
defp compare_decimal_value(_, _), do: nil
|
||||||
|
|
||||||
@spec zero?(any) :: boolean
|
@spec zero?(any) :: boolean
|
||||||
def zero?(%RDF.Literal{literal: literal}), do: zero?(literal)
|
def zero?(%Literal{literal: literal}), do: zero?(literal)
|
||||||
def zero?(%{value: value}), do: zero_value?(value)
|
def zero?(%{value: value}), do: zero_value?(value)
|
||||||
defp zero_value?(zero) when zero == 0, do: true
|
defp zero_value?(zero) when zero == 0, do: true
|
||||||
defp zero_value?(%D{coef: 0}), do: true
|
defp zero_value?(%D{coef: 0}), do: true
|
||||||
defp zero_value?(_), do: false
|
defp zero_value?(_), do: false
|
||||||
|
|
||||||
@spec negative_zero?(any) :: boolean
|
@spec negative_zero?(any) :: boolean
|
||||||
def negative_zero?(%RDF.Literal{literal: literal}), do: negative_zero?(literal)
|
def negative_zero?(%Literal{literal: literal}), do: negative_zero?(literal)
|
||||||
def negative_zero?(%{value: zero, uncanonical_lexical: "-" <> _}) when zero == 0, do: true
|
def negative_zero?(%{value: zero, uncanonical_lexical: "-" <> _}) when zero == 0, do: true
|
||||||
def negative_zero?(%{value: %D{sign: -1, coef: 0}}), do: true
|
def negative_zero?(%{value: %D{sign: -1, coef: 0}}), do: true
|
||||||
def negative_zero?(_), do: false
|
def negative_zero?(_), do: false
|
||||||
|
@ -254,7 +255,7 @@ defmodule RDF.XSD.Numeric do
|
||||||
arg1, arg2, result_type ->
|
arg1, arg2, result_type ->
|
||||||
if zero_value?(arg2) do
|
if zero_value?(arg2) do
|
||||||
cond do
|
cond do
|
||||||
result_type not in [RDF.XSD.Double, RDF.XSD.Float] -> nil
|
result_type not in [XSD.Double, XSD.Float] -> nil
|
||||||
zero_value?(arg1) -> :nan
|
zero_value?(arg1) -> :nan
|
||||||
negative_zero and arg1 < 0 -> :positive_infinity
|
negative_zero and arg1 < 0 -> :positive_infinity
|
||||||
negative_zero -> :negative_infinity
|
negative_zero -> :negative_infinity
|
||||||
|
@ -278,22 +279,13 @@ defmodule RDF.XSD.Numeric do
|
||||||
"""
|
"""
|
||||||
def abs(literal)
|
def abs(literal)
|
||||||
|
|
||||||
def abs(%RDF.Literal{literal: literal}), do: abs(literal)
|
def abs(%Literal{literal: literal}), do: abs(literal)
|
||||||
|
|
||||||
def abs(%RDF.XSD.Decimal{} = literal) do
|
|
||||||
if RDF.XSD.Decimal.valid?(literal) do
|
|
||||||
literal.value
|
|
||||||
|> D.abs()
|
|
||||||
|> RDF.XSD.Decimal.new()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def abs(nil), do: nil
|
def abs(nil), do: nil
|
||||||
|
|
||||||
def abs(value) do
|
def abs(value) do
|
||||||
cond do
|
cond do
|
||||||
datatype?(value) ->
|
datatype?(value) ->
|
||||||
if RDF.Literal.Datatype.valid?(value) do
|
if Literal.Datatype.valid?(value) do
|
||||||
%datatype{} = value
|
%datatype{} = value
|
||||||
|
|
||||||
case value.value do
|
case value.value do
|
||||||
|
@ -304,21 +296,29 @@ defmodule RDF.XSD.Numeric do
|
||||||
literal(value)
|
literal(value)
|
||||||
|
|
||||||
:negative_infinity ->
|
:negative_infinity ->
|
||||||
datatype.new(:positive_infinity)
|
datatype.base_primitive().new(:positive_infinity)
|
||||||
|
|
||||||
|
%D{} = value ->
|
||||||
|
value
|
||||||
|
|> D.abs()
|
||||||
|
|> datatype.base_primitive().new()
|
||||||
|
|
||||||
value ->
|
value ->
|
||||||
|
target_datatype = if XSD.Float.datatype?(datatype),
|
||||||
|
do: XSD.Float, else: datatype.base_primitive()
|
||||||
value
|
value
|
||||||
|> Kernel.abs()
|
|> Kernel.abs()
|
||||||
|> datatype.new()
|
|> target_datatype.new()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
RDF.Literal.datatype?(value) ->
|
# non-numeric datatypes
|
||||||
|
Literal.datatype?(value) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
value
|
value
|
||||||
|> RDF.Literal.coerce()
|
|> Literal.coerce()
|
||||||
|> abs()
|
|> abs()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -340,55 +340,56 @@ defmodule RDF.XSD.Numeric do
|
||||||
"""
|
"""
|
||||||
def round(literal, precision \\ 0)
|
def round(literal, precision \\ 0)
|
||||||
|
|
||||||
def round(%RDF.Literal{literal: literal}, precision), do: round(literal, precision)
|
def round(%Literal{literal: literal}, precision), do: round(literal, precision)
|
||||||
|
|
||||||
def round(%RDF.XSD.Decimal{} = literal, precision) do
|
|
||||||
if RDF.XSD.Decimal.valid?(literal) do
|
|
||||||
literal.value
|
|
||||||
|> xpath_round(precision)
|
|
||||||
|> to_string()
|
|
||||||
|> RDF.XSD.Decimal.new()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def round(%datatype{value: value} = datatype_literal, _)
|
|
||||||
when datatype in [RDF.XSD.Double, RDF.XSD.Float] and
|
|
||||||
value in ~w[nan positive_infinity negative_infinity]a,
|
|
||||||
do: literal(datatype_literal)
|
|
||||||
|
|
||||||
def round(%datatype{} = literal, precision) when datatype in [RDF.XSD.Double, RDF.XSD.Float] do
|
|
||||||
if datatype.valid?(literal) do
|
|
||||||
literal.value
|
|
||||||
|> new_decimal()
|
|
||||||
|> xpath_round(precision)
|
|
||||||
|> D.to_float()
|
|
||||||
|> datatype.new()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def round(nil, _), do: nil
|
def round(nil, _), do: nil
|
||||||
|
|
||||||
def round(value, precision) do
|
def round(value, precision) do
|
||||||
cond do
|
cond do
|
||||||
datatype?(value) ->
|
datatype?(value) ->
|
||||||
if RDF.Literal.Datatype.valid?(value) do
|
if Literal.Datatype.valid?(value) do
|
||||||
|
%datatype{value: literal_value} = value
|
||||||
|
|
||||||
|
cond do
|
||||||
|
XSD.Integer.datatype?(datatype) ->
|
||||||
if precision < 0 do
|
if precision < 0 do
|
||||||
value.value
|
literal_value
|
||||||
|> new_decimal()
|
|> new_decimal()
|
||||||
|> xpath_round(precision)
|
|> xpath_round(precision)
|
||||||
|> D.to_integer()
|
|> D.to_integer()
|
||||||
|> RDF.XSD.Integer.new()
|
|> XSD.Integer.new()
|
||||||
else
|
else
|
||||||
literal(value)
|
literal(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
XSD.Decimal.datatype?(datatype) ->
|
||||||
|
literal_value
|
||||||
|
|> xpath_round(precision)
|
||||||
|
|> to_string()
|
||||||
|
|> XSD.Decimal.new()
|
||||||
|
|
||||||
|
(float_datatype = XSD.Float.datatype?(datatype)) or
|
||||||
|
XSD.Double.datatype?(datatype) ->
|
||||||
|
if literal_value in ~w[nan positive_infinity negative_infinity]a do
|
||||||
|
literal(value)
|
||||||
|
else
|
||||||
|
target_datatype = if float_datatype, do: XSD.Float, else: XSD.Double
|
||||||
|
|
||||||
|
literal_value
|
||||||
|
|> new_decimal()
|
||||||
|
|> xpath_round(precision)
|
||||||
|
|> D.to_float()
|
||||||
|
|> target_datatype.new()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
RDF.Literal.datatype?(value) ->
|
# non-numeric datatypes
|
||||||
|
Literal.datatype?(value) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
value
|
value
|
||||||
|> RDF.Literal.coerce()
|
|> Literal.coerce()
|
||||||
|> round(precision)
|
|> round(precision)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -410,47 +411,48 @@ defmodule RDF.XSD.Numeric do
|
||||||
"""
|
"""
|
||||||
def ceil(literal)
|
def ceil(literal)
|
||||||
|
|
||||||
def ceil(%RDF.Literal{literal: literal}), do: ceil(literal)
|
def ceil(%Literal{literal: literal}), do: ceil(literal)
|
||||||
|
|
||||||
def ceil(%RDF.XSD.Decimal{} = literal) do
|
|
||||||
if RDF.XSD.Decimal.valid?(literal) do
|
|
||||||
literal.value
|
|
||||||
|> D.round(0, if(literal.value.sign == -1, do: :down, else: :up))
|
|
||||||
|> D.to_string()
|
|
||||||
|> RDF.XSD.Decimal.new()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ceil(%datatype{value: value} = datatype_literal)
|
|
||||||
when datatype in [RDF.XSD.Double, RDF.XSD.Float] and
|
|
||||||
value in ~w[nan positive_infinity negative_infinity]a,
|
|
||||||
do: literal(datatype_literal)
|
|
||||||
|
|
||||||
def ceil(%datatype{} = literal) when datatype in [RDF.XSD.Double, RDF.XSD.Float] do
|
|
||||||
if datatype.valid?(literal) do
|
|
||||||
literal.value
|
|
||||||
|> Float.ceil()
|
|
||||||
|> trunc()
|
|
||||||
|> to_string()
|
|
||||||
|> datatype.new()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ceil(nil), do: nil
|
def ceil(nil), do: nil
|
||||||
|
|
||||||
def ceil(value) do
|
def ceil(value) do
|
||||||
cond do
|
cond do
|
||||||
datatype?(value) ->
|
datatype?(value) ->
|
||||||
if RDF.Literal.Datatype.valid?(value) do
|
if Literal.Datatype.valid?(value) do
|
||||||
|
%datatype{value: literal_value} = value
|
||||||
|
|
||||||
|
cond do
|
||||||
|
XSD.Integer.datatype?(datatype) ->
|
||||||
literal(value)
|
literal(value)
|
||||||
|
|
||||||
|
XSD.Decimal.datatype?(datatype) ->
|
||||||
|
literal_value
|
||||||
|
|> D.round(0, if(literal_value.sign == -1, do: :down, else: :up))
|
||||||
|
|> D.to_string()
|
||||||
|
|> XSD.Decimal.new()
|
||||||
|
|
||||||
|
(float_datatype = XSD.Float.datatype?(datatype)) or
|
||||||
|
XSD.Double.datatype?(datatype) ->
|
||||||
|
if literal_value in ~w[nan positive_infinity negative_infinity]a do
|
||||||
|
literal(value)
|
||||||
|
else
|
||||||
|
target_datatype = if float_datatype, do: XSD.Float, else: XSD.Double
|
||||||
|
|
||||||
|
literal_value
|
||||||
|
|> Float.ceil()
|
||||||
|
|> trunc()
|
||||||
|
|> to_string()
|
||||||
|
|> target_datatype.new()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
RDF.Literal.datatype?(value) ->
|
# non-numeric datatypes
|
||||||
|
Literal.datatype?(value) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
value
|
value
|
||||||
|> RDF.Literal.coerce()
|
|> Literal.coerce()
|
||||||
|> ceil()
|
|> ceil()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -466,51 +468,54 @@ defmodule RDF.XSD.Numeric do
|
||||||
"""
|
"""
|
||||||
def floor(literal)
|
def floor(literal)
|
||||||
|
|
||||||
def floor(%RDF.Literal{literal: literal}), do: floor(literal)
|
def floor(%Literal{literal: literal}), do: floor(literal)
|
||||||
|
|
||||||
def floor(%RDF.XSD.Decimal{} = literal) do
|
|
||||||
if RDF.XSD.Decimal.valid?(literal) do
|
|
||||||
literal.value
|
|
||||||
|> D.round(0, if(literal.value.sign == -1, do: :up, else: :down))
|
|
||||||
|> D.to_string()
|
|
||||||
|> RDF.XSD.Decimal.new()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def floor(%datatype{value: value} = datatype_literal)
|
|
||||||
when datatype in [RDF.XSD.Double, RDF.XSD.Float] and
|
|
||||||
value in ~w[nan positive_infinity negative_infinity]a,
|
|
||||||
do: literal(datatype_literal)
|
|
||||||
|
|
||||||
def floor(%datatype{} = literal) when datatype in [RDF.XSD.Double, RDF.XSD.Float] do
|
|
||||||
if datatype.valid?(literal) do
|
|
||||||
literal.value
|
|
||||||
|> Float.floor()
|
|
||||||
|> trunc()
|
|
||||||
|> to_string()
|
|
||||||
|> datatype.new()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def floor(nil), do: nil
|
def floor(nil), do: nil
|
||||||
|
|
||||||
def floor(value) do
|
def floor(value) do
|
||||||
cond do
|
cond do
|
||||||
datatype?(value) ->
|
datatype?(value) ->
|
||||||
if RDF.Literal.Datatype.valid?(value), do: literal(value)
|
if Literal.Datatype.valid?(value) do
|
||||||
|
%datatype{value: literal_value} = value
|
||||||
|
|
||||||
RDF.Literal.datatype?(value) ->
|
cond do
|
||||||
|
XSD.Integer.datatype?(datatype) ->
|
||||||
|
literal(value)
|
||||||
|
|
||||||
|
XSD.Decimal.datatype?(datatype) ->
|
||||||
|
literal_value
|
||||||
|
|> D.round(0, if(literal_value.sign == -1, do: :up, else: :down))
|
||||||
|
|> D.to_string()
|
||||||
|
|> XSD.Decimal.new()
|
||||||
|
|
||||||
|
(float_datatype = XSD.Float.datatype?(datatype)) or
|
||||||
|
XSD.Double.datatype?(datatype) ->
|
||||||
|
if literal_value in ~w[nan positive_infinity negative_infinity]a do
|
||||||
|
literal(value)
|
||||||
|
else
|
||||||
|
target_datatype = if float_datatype, do: XSD.Float, else: XSD.Double
|
||||||
|
|
||||||
|
literal_value
|
||||||
|
|> Float.floor()
|
||||||
|
|> trunc()
|
||||||
|
|> to_string()
|
||||||
|
|> target_datatype.new()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# non-numeric datatypes
|
||||||
|
Literal.datatype?(value) ->
|
||||||
nil
|
nil
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
value
|
value
|
||||||
|> RDF.Literal.coerce()
|
|> Literal.coerce()
|
||||||
|> floor()
|
|> floor()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp arithmetic_operation(op, %RDF.Literal{literal: literal1}, literal2, fun), do: arithmetic_operation(op, literal1, literal2, fun)
|
defp arithmetic_operation(op, %Literal{literal: literal1}, literal2, fun), do: arithmetic_operation(op, literal1, literal2, fun)
|
||||||
defp arithmetic_operation(op, literal1, %RDF.Literal{literal: literal2}, fun), do: arithmetic_operation(op, literal1, literal2, fun)
|
defp arithmetic_operation(op, literal1, %Literal{literal: literal2}, fun), do: arithmetic_operation(op, literal1, literal2, fun)
|
||||||
defp arithmetic_operation(op, %datatype1{} = literal1, %datatype2{} = literal2, fun) do
|
defp arithmetic_operation(op, %datatype1{} = literal1, %datatype2{} = literal2, fun) do
|
||||||
if datatype?(datatype1) and datatype?(datatype2) do
|
if datatype?(datatype1) and datatype?(datatype2) do
|
||||||
result_type = result_type(op, datatype1, datatype2)
|
result_type = result_type(op, datatype1, datatype2)
|
||||||
|
@ -524,36 +529,36 @@ defmodule RDF.XSD.Numeric do
|
||||||
cond do
|
cond do
|
||||||
is_nil(left) -> nil
|
is_nil(left) -> nil
|
||||||
is_nil(right) -> nil
|
is_nil(right) -> nil
|
||||||
not RDF.Literal.datatype?(left) -> arithmetic_operation(op, RDF.Literal.coerce(left), right, fun)
|
not Literal.datatype?(left) -> arithmetic_operation(op, Literal.coerce(left), right, fun)
|
||||||
not RDF.Literal.datatype?(right) -> arithmetic_operation(op, left, RDF.Literal.coerce(right), fun)
|
not Literal.datatype?(right) -> arithmetic_operation(op, left, Literal.coerce(right), fun)
|
||||||
true -> false
|
true -> false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp type_conversion(%RDF.XSD.Decimal{} = left_decimal, %{value: right_value}, RDF.XSD.Decimal),
|
defp type_conversion(%XSD.Decimal{} = left_decimal, %{value: right_value}, XSD.Decimal),
|
||||||
do: {left_decimal, RDF.XSD.Decimal.new(right_value).literal}
|
do: {left_decimal, XSD.Decimal.new(right_value).literal}
|
||||||
|
|
||||||
defp type_conversion(%{value: left_value}, %RDF.XSD.Decimal{} = right_decimal, RDF.XSD.Decimal),
|
defp type_conversion(%{value: left_value}, %XSD.Decimal{} = right_decimal, XSD.Decimal),
|
||||||
do: {RDF.XSD.Decimal.new(left_value).literal, right_decimal}
|
do: {XSD.Decimal.new(left_value).literal, right_decimal}
|
||||||
|
|
||||||
defp type_conversion(%RDF.XSD.Decimal{value: left_decimal}, right, datatype)
|
defp type_conversion(%XSD.Decimal{value: left_decimal}, right, datatype)
|
||||||
when datatype in [RDF.XSD.Double, RDF.XSD.Float],
|
when datatype in [XSD.Double, XSD.Float],
|
||||||
do: {(left_decimal |> D.to_float() |> RDF.XSD.Double.new()).literal, right}
|
do: {(left_decimal |> D.to_float() |> XSD.Double.new()).literal, right}
|
||||||
|
|
||||||
defp type_conversion(left, %RDF.XSD.Decimal{value: right_decimal}, datatype)
|
defp type_conversion(left, %XSD.Decimal{value: right_decimal}, datatype)
|
||||||
when datatype in [RDF.XSD.Double, RDF.XSD.Float],
|
when datatype in [XSD.Double, XSD.Float],
|
||||||
do: {left, (right_decimal |> D.to_float() |> RDF.XSD.Double.new()).literal}
|
do: {left, (right_decimal |> D.to_float() |> XSD.Double.new()).literal}
|
||||||
|
|
||||||
defp type_conversion(left, right, _), do: {left, right}
|
defp type_conversion(left, right, _), do: {left, right}
|
||||||
|
|
||||||
defp result_type(_, RDF.XSD.Double, _), do: RDF.XSD.Double
|
defp result_type(_, XSD.Double, _), do: XSD.Double
|
||||||
defp result_type(_, _, RDF.XSD.Double), do: RDF.XSD.Double
|
defp result_type(_, _, XSD.Double), do: XSD.Double
|
||||||
defp result_type(_, RDF.XSD.Float, _), do: RDF.XSD.Float
|
defp result_type(_, XSD.Float, _), do: XSD.Float
|
||||||
defp result_type(_, _, RDF.XSD.Float), do: RDF.XSD.Float
|
defp result_type(_, _, XSD.Float), do: XSD.Float
|
||||||
defp result_type(_, RDF.XSD.Decimal, _), do: RDF.XSD.Decimal
|
defp result_type(_, XSD.Decimal, _), do: XSD.Decimal
|
||||||
defp result_type(_, _, RDF.XSD.Decimal), do: RDF.XSD.Decimal
|
defp result_type(_, _, XSD.Decimal), do: XSD.Decimal
|
||||||
defp result_type(:/, _, _), do: RDF.XSD.Decimal
|
defp result_type(:/, _, _), do: XSD.Decimal
|
||||||
defp result_type(_, _, _), do: RDF.XSD.Integer
|
defp result_type(_, _, _), do: XSD.Integer
|
||||||
|
|
||||||
defp literal(value), do: %RDF.Literal{literal: value}
|
defp literal(value), do: %Literal{literal: value}
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,4 +7,34 @@ defmodule RDF.TestDatatypes do
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 150
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 150
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule DecimalUnitInterval do
|
||||||
|
use RDF.XSD.Datatype.Restriction,
|
||||||
|
name: "decimal_unit_interval",
|
||||||
|
id: "http://example.com/decimalUnitInterval",
|
||||||
|
base: RDF.XSD.Decimal
|
||||||
|
|
||||||
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
||||||
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 1
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule DoubleUnitInterval do
|
||||||
|
use RDF.XSD.Datatype.Restriction,
|
||||||
|
name: "double_unit_interval",
|
||||||
|
id: "http://example.com/doubleUnitInterval",
|
||||||
|
base: RDF.XSD.Double
|
||||||
|
|
||||||
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
||||||
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 1
|
||||||
|
end
|
||||||
|
|
||||||
|
defmodule FloatUnitInterval do
|
||||||
|
use RDF.XSD.Datatype.Restriction,
|
||||||
|
name: "float_unit_interval",
|
||||||
|
id: "http://example.com/floatUnitInterval",
|
||||||
|
base: RDF.XSD.Float
|
||||||
|
|
||||||
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
||||||
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ defmodule RDF.XSD.NumericTest do
|
||||||
|
|
||||||
alias RDF.XSD
|
alias RDF.XSD
|
||||||
alias XSD.Numeric
|
alias XSD.Numeric
|
||||||
|
alias RDF.TestDatatypes.{Age, DecimalUnitInterval, DoubleUnitInterval, FloatUnitInterval}
|
||||||
|
|
||||||
alias Decimal, as: D
|
alias Decimal, as: D
|
||||||
|
|
||||||
|
@ -59,16 +60,26 @@ defmodule RDF.XSD.NumericTest do
|
||||||
describe "add/2" do
|
describe "add/2" do
|
||||||
test "xsd:integer literal + xsd:integer literal" do
|
test "xsd:integer literal + xsd:integer literal" do
|
||||||
assert Numeric.add(XSD.integer(1), XSD.integer(2)) == XSD.integer(3)
|
assert Numeric.add(XSD.integer(1), XSD.integer(2)) == XSD.integer(3)
|
||||||
|
assert Numeric.add(XSD.integer(1), XSD.byte(2)) == XSD.integer(3)
|
||||||
|
assert Numeric.add(XSD.byte(1), XSD.integer(2)) == XSD.integer(3)
|
||||||
|
assert Numeric.add(XSD.integer(1), Age.new(2)) == XSD.integer(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "xsd:decimal literal + xsd:integer literal" do
|
test "xsd:decimal literal + xsd:integer literal" do
|
||||||
assert Numeric.add(XSD.decimal(1.1), XSD.integer(2)) == XSD.decimal(3.1)
|
assert Numeric.add(XSD.decimal(1.1), XSD.integer(2)) == XSD.decimal(3.1)
|
||||||
|
assert Numeric.add(XSD.decimal(1.1), XSD.positiveInteger(2)) == XSD.decimal(3.1)
|
||||||
|
assert Numeric.add(XSD.decimal(1.1), Age.new(2)) == XSD.decimal(3.1)
|
||||||
|
assert Numeric.add(XSD.positiveInteger(2), XSD.decimal(1.1)) == XSD.decimal(3.1)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "xsd:double literal + xsd:integer literal" do
|
test "xsd:double literal + xsd:integer literal" do
|
||||||
assert result = %RDF.Literal{literal: %XSD.Double{}} = Numeric.add(XSD.double(1.1), XSD.integer(2))
|
assert result = %RDF.Literal{literal: %XSD.Double{}} = Numeric.add(XSD.double(1.1), XSD.integer(2))
|
||||||
assert_in_delta RDF.Literal.value(result),
|
assert_in_delta RDF.Literal.value(result),
|
||||||
RDF.Literal.value(XSD.double(3.1)), 0.000000000000001
|
RDF.Literal.value(XSD.double(3.1)), 0.000000000000001
|
||||||
|
|
||||||
|
assert result = %RDF.Literal{literal: %XSD.Double{}} = Numeric.add(XSD.double(1.1), Age.new(2))
|
||||||
|
assert_in_delta RDF.Literal.value(result),
|
||||||
|
RDF.Literal.value(XSD.double(3.1)), 0.000000000000001
|
||||||
end
|
end
|
||||||
|
|
||||||
test "xsd:decimal literal + xsd:double literal" do
|
test "xsd:decimal literal + xsd:double literal" do
|
||||||
|
@ -99,6 +110,7 @@ defmodule RDF.XSD.NumericTest do
|
||||||
assert Numeric.add(@negative_infinity, XSD.double(3.14)) == @negative_infinity
|
assert Numeric.add(@negative_infinity, XSD.double(3.14)) == @negative_infinity
|
||||||
assert Numeric.add(XSD.double(0), @negative_infinity) == @negative_infinity
|
assert Numeric.add(XSD.double(0), @negative_infinity) == @negative_infinity
|
||||||
assert Numeric.add(XSD.double(3.14), @negative_infinity) == @negative_infinity
|
assert Numeric.add(XSD.double(3.14), @negative_infinity) == @negative_infinity
|
||||||
|
assert Numeric.add(@negative_infinity, Age.new(0)) == @negative_infinity
|
||||||
end
|
end
|
||||||
|
|
||||||
test "if both operands are INF, INF is returned" do
|
test "if both operands are INF, INF is returned" do
|
||||||
|
@ -130,10 +142,13 @@ defmodule RDF.XSD.NumericTest do
|
||||||
describe "subtract/2" do
|
describe "subtract/2" do
|
||||||
test "xsd:integer literal - xsd:integer literal" do
|
test "xsd:integer literal - xsd:integer literal" do
|
||||||
assert Numeric.subtract(XSD.integer(3), XSD.integer(2)) == XSD.integer(1)
|
assert Numeric.subtract(XSD.integer(3), XSD.integer(2)) == XSD.integer(1)
|
||||||
|
assert Numeric.subtract(XSD.integer(3), XSD.short(2)) == XSD.integer(1)
|
||||||
|
assert Numeric.subtract(XSD.integer(3), Age.new(2)) == XSD.integer(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "xsd:decimal literal - xsd:integer literal" do
|
test "xsd:decimal literal - xsd:integer literal" do
|
||||||
assert Numeric.subtract(XSD.decimal(3.3), XSD.integer(2)) == XSD.decimal(1.3)
|
assert Numeric.subtract(XSD.decimal(3.3), XSD.integer(2)) == XSD.decimal(1.3)
|
||||||
|
assert Numeric.subtract(XSD.decimal(3.3), XSD.positiveInteger(2)) == XSD.decimal(1.3)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "xsd:double literal - xsd:integer literal" do
|
test "xsd:double literal - xsd:integer literal" do
|
||||||
|
@ -181,6 +196,7 @@ defmodule RDF.XSD.NumericTest do
|
||||||
describe "multiply/2" do
|
describe "multiply/2" do
|
||||||
test "xsd:integer literal * xsd:integer literal" do
|
test "xsd:integer literal * xsd:integer literal" do
|
||||||
assert Numeric.multiply(XSD.integer(2), XSD.integer(3)) == XSD.integer(6)
|
assert Numeric.multiply(XSD.integer(2), XSD.integer(3)) == XSD.integer(6)
|
||||||
|
assert Numeric.multiply(Age.new(2), XSD.integer(3)) == XSD.integer(6)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "xsd:decimal literal * xsd:integer literal" do
|
test "xsd:decimal literal * xsd:integer literal" do
|
||||||
|
@ -247,6 +263,7 @@ defmodule RDF.XSD.NumericTest do
|
||||||
describe "divide/2" do
|
describe "divide/2" do
|
||||||
test "xsd:integer literal / xsd:integer literal" do
|
test "xsd:integer literal / xsd:integer literal" do
|
||||||
assert Numeric.divide(XSD.integer(4), XSD.integer(2)) == XSD.decimal(2.0)
|
assert Numeric.divide(XSD.integer(4), XSD.integer(2)) == XSD.decimal(2.0)
|
||||||
|
assert Numeric.divide(XSD.integer(4), Age.new(2)) == XSD.decimal(2.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "xsd:decimal literal / xsd:integer literal" do
|
test "xsd:decimal literal / xsd:integer literal" do
|
||||||
|
@ -374,11 +391,13 @@ defmodule RDF.XSD.NumericTest do
|
||||||
assert XSD.decimal(-3.14) |> Numeric.abs() == XSD.decimal(3.14)
|
assert XSD.decimal(-3.14) |> Numeric.abs() == XSD.decimal(3.14)
|
||||||
end
|
end
|
||||||
|
|
||||||
@tag skip: "TODO: type-promotion"
|
|
||||||
test "with derived numerics" do
|
test "with derived numerics" do
|
||||||
assert XSD.byte(-42) |> Numeric.abs() == XSD.byte(42)
|
assert XSD.byte(-42) |> Numeric.abs() == XSD.integer(42)
|
||||||
assert XSD.byte("-42") |> Numeric.abs() == XSD.byte(42)
|
assert XSD.byte("-42") |> Numeric.abs() == XSD.integer(42)
|
||||||
assert XSD.non_positive_integer(-42) |> Numeric.abs() == XSD.integer(42)
|
assert XSD.non_positive_integer(-42) |> Numeric.abs() == XSD.integer(42)
|
||||||
|
assert DecimalUnitInterval.new(0.14) |> Numeric.abs() == XSD.decimal(0.14)
|
||||||
|
assert DoubleUnitInterval.new(0.14) |> Numeric.abs() == XSD.double(0.14)
|
||||||
|
assert FloatUnitInterval.new(0.14) |> Numeric.abs() == XSD.float(0.14)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with invalid numeric literals" do
|
test "with invalid numeric literals" do
|
||||||
|
@ -419,6 +438,14 @@ defmodule RDF.XSD.NumericTest do
|
||||||
assert XSD.decimal(-2.5) |> Numeric.round() == XSD.decimal("-2")
|
assert XSD.decimal(-2.5) |> Numeric.round() == XSD.decimal("-2")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "with derived numerics" do
|
||||||
|
assert XSD.byte(42) |> Numeric.round() == XSD.byte(42)
|
||||||
|
assert XSD.non_positive_integer(-42) |> Numeric.round() == XSD.non_positive_integer(-42)
|
||||||
|
assert DecimalUnitInterval.new(0.14) |> Numeric.round() == XSD.decimal("0")
|
||||||
|
assert DoubleUnitInterval.new(0.14) |> Numeric.round() == XSD.double(0)
|
||||||
|
assert FloatUnitInterval.new(0.14) |> Numeric.round() == XSD.float(0)
|
||||||
|
end
|
||||||
|
|
||||||
test "with invalid numeric literals" do
|
test "with invalid numeric literals" do
|
||||||
assert XSD.integer("-3.14") |> Numeric.round() == nil
|
assert XSD.integer("-3.14") |> Numeric.round() == nil
|
||||||
assert XSD.double("foo") |> Numeric.round() == nil
|
assert XSD.double("foo") |> Numeric.round() == nil
|
||||||
|
@ -466,6 +493,14 @@ defmodule RDF.XSD.NumericTest do
|
||||||
assert XSD.decimal(-2.55) |> Numeric.round(1) == XSD.decimal("-2.5")
|
assert XSD.decimal(-2.55) |> Numeric.round(1) == XSD.decimal("-2.5")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "with derived numerics" do
|
||||||
|
assert XSD.byte(42) |> Numeric.round(2) == XSD.byte(42)
|
||||||
|
assert XSD.non_positive_integer(-42) |> Numeric.round(2) == XSD.non_positive_integer(-42)
|
||||||
|
assert DecimalUnitInterval.new(0.14) |> Numeric.round(1) == XSD.decimal(0.1)
|
||||||
|
assert DoubleUnitInterval.new(0.14) |> Numeric.round(1) == XSD.double(0.1)
|
||||||
|
assert FloatUnitInterval.new(0.14) |> Numeric.round(1) == XSD.float(0.1)
|
||||||
|
end
|
||||||
|
|
||||||
test "with invalid numeric literals" do
|
test "with invalid numeric literals" do
|
||||||
assert XSD.integer("-3.14") |> Numeric.round(1) == nil
|
assert XSD.integer("-3.14") |> Numeric.round(1) == nil
|
||||||
assert XSD.double("foo") |> Numeric.round(2) == nil
|
assert XSD.double("foo") |> Numeric.round(2) == nil
|
||||||
|
@ -510,6 +545,14 @@ defmodule RDF.XSD.NumericTest do
|
||||||
assert XSD.decimal(-10.5) |> Numeric.ceil() == XSD.decimal("-10")
|
assert XSD.decimal(-10.5) |> Numeric.ceil() == XSD.decimal("-10")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "with derived numerics" do
|
||||||
|
assert XSD.byte(42) |> Numeric.ceil() == XSD.byte(42)
|
||||||
|
assert XSD.non_positive_integer(-42) |> Numeric.ceil() == XSD.non_positive_integer(-42)
|
||||||
|
assert DoubleUnitInterval.new(0.14) |> Numeric.ceil() == XSD.double("1")
|
||||||
|
assert DoubleUnitInterval.new(0.4) |> Numeric.ceil() == XSD.double("1")
|
||||||
|
assert FloatUnitInterval.new(0.5) |> Numeric.ceil() == XSD.float("1")
|
||||||
|
end
|
||||||
|
|
||||||
test "with invalid numeric literals" do
|
test "with invalid numeric literals" do
|
||||||
assert XSD.integer("-3.14") |> Numeric.ceil() == nil
|
assert XSD.integer("-3.14") |> Numeric.ceil() == nil
|
||||||
assert XSD.double("foo") |> Numeric.ceil() == nil
|
assert XSD.double("foo") |> Numeric.ceil() == nil
|
||||||
|
@ -554,6 +597,14 @@ defmodule RDF.XSD.NumericTest do
|
||||||
assert XSD.decimal(-10.5) |> Numeric.floor() == XSD.decimal("-11")
|
assert XSD.decimal(-10.5) |> Numeric.floor() == XSD.decimal("-11")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "with derived numerics" do
|
||||||
|
assert XSD.byte(42) |> Numeric.floor() == XSD.byte(42)
|
||||||
|
assert XSD.non_positive_integer(-42) |> Numeric.floor() == XSD.non_positive_integer(-42)
|
||||||
|
assert DoubleUnitInterval.new(0.14) |> Numeric.floor() == XSD.double("0")
|
||||||
|
assert DoubleUnitInterval.new(0.4) |> Numeric.floor() == XSD.double("0")
|
||||||
|
assert FloatUnitInterval.new(0.5) |> Numeric.floor() == XSD.float("0")
|
||||||
|
end
|
||||||
|
|
||||||
test "with invalid numeric literals" do
|
test "with invalid numeric literals" do
|
||||||
assert XSD.integer("-3.14") |> Numeric.floor() == nil
|
assert XSD.integer("-3.14") |> Numeric.floor() == nil
|
||||||
assert XSD.double("foo") |> Numeric.floor() == nil
|
assert XSD.double("foo") |> Numeric.floor() == nil
|
||||||
|
|
Loading…
Reference in a new issue