diff --git a/lib/rdf/xsd/datatypes/numeric.ex b/lib/rdf/xsd/datatypes/numeric.ex index 6c678db..7078bde 100644 --- a/lib/rdf/xsd/datatypes/numeric.ex +++ b/lib/rdf/xsd/datatypes/numeric.ex @@ -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 diff --git a/test/support/test_datatypes.ex b/test/support/test_datatypes.ex index b21802c..9c50ca5 100644 --- a/test/support/test_datatypes.ex +++ b/test/support/test_datatypes.ex @@ -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 diff --git a/test/unit/xsd/datatypes/numeric_test.exs b/test/unit/xsd/datatypes/numeric_test.exs index 7cbb719..d9666af 100644 --- a/test/unit/xsd/datatypes/numeric_test.exs +++ b/test/unit/xsd/datatypes/numeric_test.exs @@ -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