Add proper handling for derived datatypes on numeric operations

with type promotion
This commit is contained in:
Marcel Otto 2020-05-17 21:33:12 +02:00
parent bf932be26d
commit 09dceb0e35
3 changed files with 236 additions and 150 deletions

View file

@ -5,11 +5,12 @@ defmodule RDF.XSD.Numeric do
@type t :: module
alias RDF.{XSD, Literal}
alias Elixir.Decimal, as: D
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 """
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
def equal_value?(left, right)
def equal_value?(left, %RDF.Literal{literal: right}), do: equal_value?(left, right)
def equal_value?(%RDF.Literal{literal: left}, right), do: equal_value?(left, right)
def equal_value?(left, %Literal{literal: 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
@ -34,7 +35,7 @@ defmodule RDF.XSD.Numeric do
end
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)
def equal_value?(%left_datatype{value: left}, %right_datatype{value: right}) do
@ -44,7 +45,7 @@ defmodule RDF.XSD.Numeric do
end
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)
@ -68,13 +69,13 @@ defmodule RDF.XSD.Numeric do
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, %RDF.Literal{literal: right}), do: compare(left, right)
def compare(%RDF.Literal{literal: left}, right), do: compare(left, right)
def compare(left, %Literal{literal: right}), do: compare(left, right)
def compare(%Literal{literal: left}, right), do: compare(left, right)
def compare(
%RDF.XSD.Decimal{value: left},
%XSD.Decimal{value: left},
%right_datatype{value: right}
) do
if datatype?(right_datatype) do
@ -84,7 +85,7 @@ defmodule RDF.XSD.Numeric do
def compare(
%left_datatype{value: left},
%RDF.XSD.Decimal{value: right}
%XSD.Decimal{value: right}
) do
if datatype?(left_datatype) do
compare_decimal_value(left, right)
@ -118,14 +119,14 @@ defmodule RDF.XSD.Numeric do
defp compare_decimal_value(_, _), do: nil
@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)
defp zero_value?(zero) when zero == 0, do: true
defp zero_value?(%D{coef: 0}), do: true
defp zero_value?(_), do: false
@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: %D{sign: -1, coef: 0}}), do: true
def negative_zero?(_), do: false
@ -254,7 +255,7 @@ defmodule RDF.XSD.Numeric do
arg1, arg2, result_type ->
if zero_value?(arg2) 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
negative_zero and arg1 < 0 -> :positive_infinity
negative_zero -> :negative_infinity
@ -278,22 +279,13 @@ defmodule RDF.XSD.Numeric do
"""
def abs(literal)
def abs(%RDF.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(%Literal{literal: literal}), do: abs(literal)
def abs(nil), do: nil
def abs(value) do
cond do
datatype?(value) ->
if RDF.Literal.Datatype.valid?(value) do
if Literal.Datatype.valid?(value) do
%datatype{} = value
case value.value do
@ -304,21 +296,29 @@ defmodule RDF.XSD.Numeric do
literal(value)
:negative_infinity ->
datatype.new(:positive_infinity)
datatype.base_primitive().new(:positive_infinity)
%D{} = value ->
value
|> D.abs()
|> datatype.base_primitive().new()
value ->
target_datatype = if XSD.Float.datatype?(datatype),
do: XSD.Float, else: datatype.base_primitive()
value
|> Kernel.abs()
|> datatype.new()
|> target_datatype.new()
end
end
RDF.Literal.datatype?(value) ->
# non-numeric datatypes
Literal.datatype?(value) ->
nil
true ->
value
|> RDF.Literal.coerce()
|> Literal.coerce()
|> abs()
end
end
@ -340,55 +340,56 @@ defmodule RDF.XSD.Numeric do
"""
def round(literal, precision \\ 0)
def round(%RDF.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(%Literal{literal: literal}, precision), do: round(literal, precision)
def round(nil, _), do: nil
def round(value, precision) do
cond do
datatype?(value) ->
if RDF.Literal.Datatype.valid?(value) do
if precision < 0 do
value.value
|> new_decimal()
|> xpath_round(precision)
|> D.to_integer()
|> RDF.XSD.Integer.new()
else
literal(value)
if Literal.Datatype.valid?(value) do
%datatype{value: literal_value} = value
cond do
XSD.Integer.datatype?(datatype) ->
if precision < 0 do
literal_value
|> new_decimal()
|> xpath_round(precision)
|> D.to_integer()
|> XSD.Integer.new()
else
literal(value)
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
RDF.Literal.datatype?(value) ->
# non-numeric datatypes
Literal.datatype?(value) ->
nil
true ->
value
|> RDF.Literal.coerce()
|> Literal.coerce()
|> round(precision)
end
end
@ -410,47 +411,48 @@ defmodule RDF.XSD.Numeric do
"""
def ceil(literal)
def ceil(%RDF.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(%Literal{literal: literal}), do: ceil(literal)
def ceil(nil), do: nil
def ceil(value) do
cond do
datatype?(value) ->
if RDF.Literal.Datatype.valid?(value) do
literal(value)
if Literal.Datatype.valid?(value) do
%datatype{value: literal_value} = value
cond do
XSD.Integer.datatype?(datatype) ->
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
RDF.Literal.datatype?(value) ->
# non-numeric datatypes
Literal.datatype?(value) ->
nil
true ->
value
|> RDF.Literal.coerce()
|> Literal.coerce()
|> ceil()
end
end
@ -466,51 +468,54 @@ defmodule RDF.XSD.Numeric do
"""
def floor(literal)
def floor(%RDF.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(%Literal{literal: literal}), do: floor(literal)
def floor(nil), do: nil
def floor(value) do
cond do
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
true ->
value
|> RDF.Literal.coerce()
|> Literal.coerce()
|> floor()
end
end
defp arithmetic_operation(op, %RDF.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, %Literal{literal: literal1}, 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
if datatype?(datatype1) and datatype?(datatype2) do
result_type = result_type(op, datatype1, datatype2)
@ -524,36 +529,36 @@ defmodule RDF.XSD.Numeric do
cond do
is_nil(left) -> nil
is_nil(right) -> nil
not RDF.Literal.datatype?(left) -> arithmetic_operation(op, RDF.Literal.coerce(left), right, fun)
not RDF.Literal.datatype?(right) -> arithmetic_operation(op, left, RDF.Literal.coerce(right), fun)
not Literal.datatype?(left) -> arithmetic_operation(op, Literal.coerce(left), right, fun)
not Literal.datatype?(right) -> arithmetic_operation(op, left, Literal.coerce(right), fun)
true -> false
end
end
defp type_conversion(%RDF.XSD.Decimal{} = left_decimal, %{value: right_value}, RDF.XSD.Decimal),
do: {left_decimal, RDF.XSD.Decimal.new(right_value).literal}
defp type_conversion(%XSD.Decimal{} = left_decimal, %{value: right_value}, XSD.Decimal),
do: {left_decimal, XSD.Decimal.new(right_value).literal}
defp type_conversion(%{value: left_value}, %RDF.XSD.Decimal{} = right_decimal, RDF.XSD.Decimal),
do: {RDF.XSD.Decimal.new(left_value).literal, right_decimal}
defp type_conversion(%{value: left_value}, %XSD.Decimal{} = right_decimal, XSD.Decimal),
do: {XSD.Decimal.new(left_value).literal, right_decimal}
defp type_conversion(%RDF.XSD.Decimal{value: left_decimal}, right, datatype)
when datatype in [RDF.XSD.Double, RDF.XSD.Float],
do: {(left_decimal |> D.to_float() |> RDF.XSD.Double.new()).literal, right}
defp type_conversion(%XSD.Decimal{value: left_decimal}, right, datatype)
when datatype in [XSD.Double, XSD.Float],
do: {(left_decimal |> D.to_float() |> XSD.Double.new()).literal, right}
defp type_conversion(left, %RDF.XSD.Decimal{value: right_decimal}, datatype)
when datatype in [RDF.XSD.Double, RDF.XSD.Float],
do: {left, (right_decimal |> D.to_float() |> RDF.XSD.Double.new()).literal}
defp type_conversion(left, %XSD.Decimal{value: right_decimal}, datatype)
when datatype in [XSD.Double, XSD.Float],
do: {left, (right_decimal |> D.to_float() |> XSD.Double.new()).literal}
defp type_conversion(left, right, _), do: {left, right}
defp result_type(_, RDF.XSD.Double, _), do: RDF.XSD.Double
defp result_type(_, _, RDF.XSD.Double), do: RDF.XSD.Double
defp result_type(_, RDF.XSD.Float, _), do: RDF.XSD.Float
defp result_type(_, _, RDF.XSD.Float), do: RDF.XSD.Float
defp result_type(_, RDF.XSD.Decimal, _), do: RDF.XSD.Decimal
defp result_type(_, _, RDF.XSD.Decimal), do: RDF.XSD.Decimal
defp result_type(:/, _, _), do: RDF.XSD.Decimal
defp result_type(_, _, _), do: RDF.XSD.Integer
defp result_type(_, XSD.Double, _), do: XSD.Double
defp result_type(_, _, XSD.Double), do: XSD.Double
defp result_type(_, XSD.Float, _), do: XSD.Float
defp result_type(_, _, XSD.Float), do: XSD.Float
defp result_type(_, XSD.Decimal, _), do: XSD.Decimal
defp result_type(_, _, XSD.Decimal), do: XSD.Decimal
defp result_type(:/, _, _), do: XSD.Decimal
defp result_type(_, _, _), do: XSD.Integer
defp literal(value), do: %RDF.Literal{literal: value}
defp literal(value), do: %Literal{literal: value}
end

View file

@ -7,4 +7,34 @@ defmodule RDF.TestDatatypes do
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 150
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

View file

@ -3,6 +3,7 @@ defmodule RDF.XSD.NumericTest do
alias RDF.XSD
alias XSD.Numeric
alias RDF.TestDatatypes.{Age, DecimalUnitInterval, DoubleUnitInterval, FloatUnitInterval}
alias Decimal, as: D
@ -59,16 +60,26 @@ defmodule RDF.XSD.NumericTest do
describe "add/2" 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.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
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.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
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_in_delta RDF.Literal.value(result),
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
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(XSD.double(0), @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
test "if both operands are INF, INF is returned" do
@ -130,10 +142,13 @@ defmodule RDF.XSD.NumericTest do
describe "subtract/2" 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.short(2)) == XSD.integer(1)
assert Numeric.subtract(XSD.integer(3), Age.new(2)) == XSD.integer(1)
end
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.positiveInteger(2)) == XSD.decimal(1.3)
end
test "xsd:double literal - xsd:integer literal" do
@ -181,6 +196,7 @@ defmodule RDF.XSD.NumericTest do
describe "multiply/2" do
test "xsd:integer literal * xsd:integer literal" do
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
test "xsd:decimal literal * xsd:integer literal" do
@ -247,6 +263,7 @@ defmodule RDF.XSD.NumericTest do
describe "divide/2" 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), Age.new(2)) == XSD.decimal(2.0)
end
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)
end
@tag skip: "TODO: type-promotion"
test "with derived numerics" do
assert XSD.byte(-42) |> Numeric.abs() == XSD.byte(42)
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.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
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")
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
assert XSD.integer("-3.14") |> 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")
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
assert XSD.integer("-3.14") |> Numeric.round(1) == 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")
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
assert XSD.integer("-3.14") |> 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")
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
assert XSD.integer("-3.14") |> Numeric.floor() == nil
assert XSD.double("foo") |> Numeric.floor() == nil