Add RDF.Numeric.ceil/1 and RDF.Numeric.floor/1

This commit is contained in:
Marcel Otto 2018-07-14 00:56:25 +02:00
parent cb9585d87a
commit 974b2c8360
2 changed files with 128 additions and 0 deletions

View file

@ -319,6 +319,80 @@ defmodule RDF.Numeric do
defp xpath_round(decimal, precision),
do: D.round(decimal, precision)
@doc """
Rounds a numeric literal upwards to a whole number literal.
If the argument is not a valid numeric literal `nil` is returned.
see <http://www.w3.org/TR/xpath-functions/#func-ceil>
"""
def ceil(literal)
def ceil(%Literal{datatype: @xsd_decimal} = literal) do
if RDF.Decimal.valid?(literal) do
literal.value
|> D.round(0, (if literal.value.sign == -1, do: :down, else: :up))
|> D.to_integer()
|> RDF.Integer.new()
end
end
def ceil(%Literal{datatype: @xsd_double, value: value} = literal)
when value in ~w[nan positive_infinity negative_infinity]a, do: literal
def ceil(%Literal{datatype: @xsd_double} = literal) do
if RDF.Double.valid?(literal) do
literal.value
|> Float.ceil()
|> trunc()
|> RDF.Integer.new()
end
end
def ceil(%Literal{datatype: datatype} = literal) do
if type?(datatype) and Literal.valid?(literal) do
literal
end
end
@doc """
Rounds a numeric literal downwards to a whole number literal.
If the argument is not a valid numeric literal `nil` is returned.
see <http://www.w3.org/TR/xpath-functions/#func-floor>
"""
def floor(literal)
def floor(%Literal{datatype: @xsd_decimal} = literal) do
if RDF.Decimal.valid?(literal) do
literal.value
|> D.round(0, (if literal.value.sign == -1, do: :up, else: :down))
|> D.to_integer()
|> RDF.Integer.new()
end
end
def floor(%Literal{datatype: @xsd_double, value: value} = literal)
when value in ~w[nan positive_infinity negative_infinity]a, do: literal
def floor(%Literal{datatype: @xsd_double} = literal) do
if RDF.Double.valid?(literal) do
literal.value
|> Float.floor()
|> trunc()
|> RDF.Integer.new()
end
end
def floor(%Literal{datatype: datatype} = literal) do
if type?(datatype) and Literal.valid?(literal) do
literal
end
end
defp arithmetic_operation(op, arg1, arg2, fun) do
if literal?(arg1) && literal?(arg2) do

View file

@ -409,4 +409,58 @@ defmodule RDF.NumericTest do
end
end
describe "ceil/1" do
test "with xsd:integer" do
assert RDF.integer(42) |> Numeric.ceil() == RDF.integer(42)
assert RDF.integer(-42) |> Numeric.ceil() == RDF.integer(-42)
end
test "with xsd:double" do
assert RDF.double(10.5) |> Numeric.ceil() == RDF.integer(11)
assert RDF.double(-10.5) |> Numeric.ceil() == RDF.integer(-10)
assert RDF.double("INF") |> Numeric.ceil() == RDF.double("INF")
assert RDF.double("-INF") |> Numeric.ceil() == RDF.double("-INF")
assert RDF.double("NAN") |> Numeric.ceil() == RDF.double("NAN")
end
test "with xsd:decimal" do
assert RDF.decimal(10.5) |> Numeric.ceil() == RDF.integer(11)
assert RDF.decimal(-10.5) |> Numeric.ceil() == RDF.integer(-10)
end
test "with invalid numeric literals" do
assert RDF.integer("-3.14") |> Numeric.ceil() == nil
assert RDF.double("foo") |> Numeric.ceil() == nil
assert RDF.decimal("foo") |> Numeric.ceil() == nil
end
end
describe "floor/1" do
test "with xsd:integer" do
assert RDF.integer(42) |> Numeric.floor() == RDF.integer(42)
assert RDF.integer(-42) |> Numeric.floor() == RDF.integer(-42)
end
test "with xsd:double" do
assert RDF.double(10.5) |> Numeric.floor() == RDF.integer(10)
assert RDF.double(-10.5) |> Numeric.floor() == RDF.integer(-11)
assert RDF.double("INF") |> Numeric.floor() == RDF.double("INF")
assert RDF.double("-INF") |> Numeric.floor() == RDF.double("-INF")
assert RDF.double("NAN") |> Numeric.floor() == RDF.double("NAN")
end
test "with xsd:decimal" do
assert RDF.decimal(10.5) |> Numeric.floor() == RDF.integer(10)
assert RDF.decimal(-10.5) |> Numeric.floor() == RDF.integer(-11)
end
test "with invalid numeric literals" do
assert RDF.integer("-3.14") |> Numeric.floor() == nil
assert RDF.double("foo") |> Numeric.floor() == nil
assert RDF.decimal("foo") |> Numeric.floor() == nil
end
end
end