Skip to content

Commit

Permalink
Fix QuantityType arithmetic of mixed units
Browse files Browse the repository at this point in the history
Signed-off-by: Jimmy Tanagra <[email protected]>
  • Loading branch information
jimtng committed Sep 16, 2023
1 parent 501445e commit 9ce4e11
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 42 deletions.
28 changes: 8 additions & 20 deletions lib/openhab/core/types/quantity_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,15 @@ def coerce(other)
# def +(other)
# logger.trace("#{self} + #{other} (#{other.class})")
# if other.is_a?(QuantityType)
# add_quantity(other)
# add(other)
# elsif other.is_a?(DecimalType)
# other = other.to_big_decimal
# add_quantity(self.class.new(other, DSL.unit(dimension) || unit))
# add(self.class.new(other, DSL.unit(dimension) || unit))
# elsif other.is_a?(java.math.BigDecimal)
# add_quantity(self.class.new(other, DSL.unit(dimension) || unit))
# add(self.class.new(other, DSL.unit(dimension) || unit))
# elsif other.respond_to?(:to_d)
# other = other.to_d.to_java
# add_quantity(self.class.new(other, DSL.unit(dimension) || unit))
# add(self.class.new(other, DSL.unit(dimension) || unit))
# elsif other.respond_to?(:coerce) && (lhs, rhs = other.coerce(to_d))
# lhs + rhs
# else
Expand All @@ -199,15 +199,15 @@ def coerce(other)
def #{ruby_op}(other)
logger.trace("\#{self} #{ruby_op} \#{other} (\#{other.class})")
if other.is_a?(QuantityType)
#{java_op}_quantity(other)
#{java_op}(other)
elsif other.is_a?(DecimalType)
other = other.to_big_decimal
#{java_op}_quantity(#{convert})
#{java_op}(#{convert})
elsif other.is_a?(java.math.BigDecimal)
#{java_op}_quantity(#{convert})
#{java_op}(#{convert})
elsif other.respond_to?(:to_d)
other = other.to_d.to_java
#{java_op}_quantity(#{convert})
#{java_op}(#{convert})
elsif other.respond_to?(:coerce) && (lhs, rhs = other.coerce(to_d))
lhs #{ruby_op} rhs
else
Expand Down Expand Up @@ -288,18 +288,6 @@ def deunitize

private

# do addition directly against a QuantityType while ensuring we unitize
# both sides
def add_quantity(other)
unitize(other.unit).add(other.unitize(unit))
end

# do subtraction directly against a QuantityType while ensuring we
# unitize both sides
def subtract_quantity(other)
unitize(other.unit).subtract(other.unitize(unit))
end

# do multiplication directly against a QuantityType while ensuring
# we deunitize both sides, and also invert the operation if one side
# isn't actually a unit
Expand Down
61 changes: 39 additions & 22 deletions spec/openhab/core/types/quantity_type_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,45 @@
expect(50.to_d | "°F").to eql QuantityType.new("50.0 °F") # rubocop:disable Performance/BigDecimalWithNumericArgument
end

it "responds to math operations" do
# quantity type operand
expect(QuantityType.new("50 °F") + QuantityType.new("50 °F")).to eql QuantityType.new("100.0 °F")
expect(QuantityType.new("50 °F") - QuantityType.new("25 °F")).to eql QuantityType.new("25.0 °F")
expect((QuantityType.new("100 W") / QuantityType.new("2 W")).to_i).to be 50
expect(QuantityType.new("50 °F") + -QuantityType.new("25 °F")).to eql QuantityType.new("25.0 °F")

# numeric operand
expect(QuantityType.new("50 W") * 2).to eql QuantityType.new("100.0 W")
expect(QuantityType.new("100 W") / 2).to eql QuantityType.new("50.0 W")
expect(QuantityType.new("50 W") * 2.0).to eql QuantityType.new("100.0 W")
expect(QuantityType.new("100 W") / 2.0).to eql QuantityType.new("50.0 W")

# DecimalType operand
expect(QuantityType.new("50 W") * DecimalType.new(2)).to eql QuantityType.new("100.0 W")
expect(QuantityType.new("100 W") / DecimalType.new(2)).to eql QuantityType.new("50.0 W")
expect(QuantityType.new("50 W") * DecimalType.new(2.0)).to eql QuantityType.new("100.0 W")
expect(QuantityType.new("100 W") / DecimalType.new(2.0)).to eql QuantityType.new("50.0 W")
describe "math operations" do
it "supports quantity type operand" do
expect(QuantityType.new("50 °F") + QuantityType.new("50 °F")).to eql QuantityType.new("100.0 °F")
expect(QuantityType.new("50 °F") - QuantityType.new("25 °F")).to eql QuantityType.new("25.0 °F")
expect((QuantityType.new("100 W") / QuantityType.new("2 W")).to_i).to be 50
expect(QuantityType.new("50 °F") + -QuantityType.new("25 °F")).to eql QuantityType.new("25.0 °F")
end

it "supports numeric operand" do
expect(QuantityType.new("50 W") * 2).to eql QuantityType.new("100.0 W")
expect(QuantityType.new("100 W") / 2).to eql QuantityType.new("50.0 W")
expect(QuantityType.new("50 W") * 2.0).to eql QuantityType.new("100.0 W")
expect(QuantityType.new("100 W") / 2.0).to eql QuantityType.new("50.0 W")
end

it "supports DecimalType operand" do
expect(QuantityType.new("50 W") * DecimalType.new(2)).to eql QuantityType.new("100.0 W")
expect(QuantityType.new("100 W") / DecimalType.new(2)).to eql QuantityType.new("50.0 W")
expect(QuantityType.new("50 W") * DecimalType.new(2.0)).to eql QuantityType.new("100.0 W")
expect(QuantityType.new("100 W") / DecimalType.new(2.0)).to eql QuantityType.new("50.0 W")
end

describe "with mixed units" do
it "normalizes units in complex expression" do
expect(((23 | "°C") | "°F") - (70 | "°F")).to be < 4 | "°F"
end

it "supports arithmetic" do
expect((20 | "°C") + (9 | "°F")).to eql 25 | "°C"
expect((25 | "°C") - (9 | "°F")).to eql 20 | "°C"
end

it "works in a unit block" do
unit("°C") do
expect((20 | "°C") + (9 | "°F")).to eql 25 | "°C"
expect((25 | "°C") - (9 | "°F")).to eql 20 | "°C"
end
end
end
end

it "can be compared" do
Expand Down Expand Up @@ -71,10 +92,6 @@
expect((0 | "W")..(10 | "W")).to cover(10 | "W")
end

it "normalizes units in complex expression" do
expect(((23 | "°C") | "°F") - (70 | "°F")).to be < 4 | "°F"
end

describe "comparisons" do
let(:ten_c) { QuantityType.new("10 °C") }
let(:five_c) { QuantityType.new("5 °C") }
Expand Down

0 comments on commit 9ce4e11

Please sign in to comment.