Daniel Kinzler has uploaded a new change for review.
https://gerrit.wikimedia.org/r/89777
Change subject: Introducing QuantityValue::transform
......................................................................
Introducing QuantityValue::transform
This introduces a general purpose method for transforming quantities,
keeping the amount, bounds and number of digits consistent.
Change-Id: I3c49e98a9183c0af000bfbf1f5753f4396d1dad3
---
M DataValuesCommon/src/DataValues/QuantityValue.php
M DataValuesCommon/tests/DataValues/QuantityValueTest.php
2 files changed, 126 insertions(+), 12 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues
refs/changes/77/89777/1
diff --git a/DataValuesCommon/src/DataValues/QuantityValue.php
b/DataValuesCommon/src/DataValues/QuantityValue.php
index ca8462f..535afaa 100644
--- a/DataValuesCommon/src/DataValues/QuantityValue.php
+++ b/DataValuesCommon/src/DataValues/QuantityValue.php
@@ -404,6 +404,73 @@
}
/**
+ * Returns a transformed value derived from this QuantityValue by
applying
+ * the given transformation to the amount and the upper and lower
bounds.
+ * The resulting amount and bounds are rounded to the significant
number of
+ * digits. Note that for exact quantities (with at least one bound
equal to
+ * the amount), no rounding is applied (since they are considered to
have
+ * infinite precision)
+ *
+ * @param string $newUnit The unit of the transformed quantity.
+ *
+ * @param callable $transformation A callback that implements the
desired transformation.
+ * The transformation will be called with a DecimalValue to
transform as it's first
+ * parameter. In addition, any extra parameters passed to
transform() will be
+ * passed through to the transformation callback.
+ *
+ * @param mixed ... Any extra parameters will be passed to the
$transformation function.
+ *
+ * @throws \InvalidArgumentException
+ * @return QuantityValue
+ */
+ public function transform( $newUnit, $transformation ) {
+ if ( !is_callable( $transformation ) ) {
+ throw new \InvalidArgumentException( '$transformation
must be callable.' );
+ }
+
+ if ( !is_string( $newUnit ) ) {
+ throw new \InvalidArgumentException( '$newUnit must be
a string. Use "1" as the unit for unit-less quantities.' );
+ }
+
+ if ( $newUnit === '' ) {
+ throw new \InvalidArgumentException( '$newUnit must not
be empty. Use "1" as the unit for unit-less quantities.' );
+ }
+
+ $oldUnit = $this->getUnit();
+
+ if ( $newUnit === null ) {
+ $newUnit = $oldUnit;
+ }
+
+ // Apply transformation by calling the $transform callback.
+ // The first argument for the callback is teh DataValue to
transform. In addition,
+ // any extra arguments given for transform() are passed through.
+ $args = func_get_args();
+ array_shift( $args );
+
+ $args[0] = $this->getAmount();
+ $amount = call_user_func_array( $transformation, $args );
+
+ $args[0] = $this->getUpperBound();
+ $upperBound = call_user_func_array( $transformation, $args );
+
+ $args[0] = $this->getLowerBound();
+ $lowerBound = call_user_func_array( $transformation, $args );
+
+ // use a preliminary QuantityValue to determine the significant
number of digits
+ $transformed = new QuantityValue( $amount, $newUnit,
$upperBound, $lowerBound );
+ $digits = $transformed->getSignificantDigits();
+
+ // apply rounding to the significant digits
+ $math = new DecimalMath( ); //TODO: Perhaps transform() should
go into a QuantityTransformer class.
+ $amount = $math->round( $amount, $digits );
+ $upperBound = $math->round( $upperBound, $digits );
+ $lowerBound = $math->round( $lowerBound, $digits );
+
+ return new QuantityValue( $amount, $newUnit, $upperBound,
$lowerBound );
+ }
+
+ /**
* @see DataValue::getArrayValue
*
* @since 0.1
diff --git a/DataValuesCommon/tests/DataValues/QuantityValueTest.php
b/DataValuesCommon/tests/DataValues/QuantityValueTest.php
index dc1a8f9..f8f9b2f 100644
--- a/DataValuesCommon/tests/DataValues/QuantityValueTest.php
+++ b/DataValuesCommon/tests/DataValues/QuantityValueTest.php
@@ -197,11 +197,11 @@
return array(
array( QuantityValue::newFromDecimal( '+0', '1', '+0',
'+0' ), 0 ),
- array( QuantityValue::newFromDecimal( '+0', '1', '+1',
'-1' ), 2 ),
- array( QuantityValue::newFromDecimal( '+0.00', '1',
'+0.01', '-0.01' ), 0.02 ),
- array( QuantityValue::newFromDecimal( '+100', '1',
'+101', '+99' ), 2 ),
- array( QuantityValue::newFromDecimal( '+100.0', '1',
'+100.1', '+99.9' ), 0.2 ),
- array( QuantityValue::newFromDecimal( '+12.34', '1',
'+12.35', '+12.33' ), 0.02 ),
+ array( QuantityValue::newFromDecimal( '+0' ), 2 ),
+ array( QuantityValue::newFromDecimal( '+0.00' ), 0.02 ),
+ array( QuantityValue::newFromDecimal( '+100' ), 2 ),
+ array( QuantityValue::newFromDecimal( '+100.0' ), 0.2 ),
+ array( QuantityValue::newFromDecimal( '+12.34' ), 0.02
),
array( QuantityValue::newFromDecimal( '+0', '1',
'+0.2', '-0.6' ), 0.8 ),
array( QuantityValue::newFromDecimal( '+7.3', '1',
'+7.7', '+5.2' ), 2.5 ),
@@ -219,8 +219,8 @@
public function getUncertaintyMarginProvider() {
return array(
- array( QuantityValue::newFromDecimal( '+0', '1', '+1',
'-1' ), '+1' ),
- array( QuantityValue::newFromDecimal( '+0.00', '1',
'+0.01', '-0.01' ), '+0.01' ),
+ array( QuantityValue::newFromDecimal( '+0' ), '+1' ),
+ array( QuantityValue::newFromDecimal( '+0.00' ),
'+0.01' ),
array( QuantityValue::newFromDecimal( '+0', '1',
'+0.2', '-0.6' ), '+0.6' ),
array( QuantityValue::newFromDecimal( '+7.5', '1',
'+7.5', '+5.5' ), '+2' ),
@@ -241,12 +241,12 @@
public function getSignificantDigitsProvider() {
return array(
0 => array( QuantityValue::newFromDecimal( '+0' ), 1 ),
- 1 => array( QuantityValue::newFromDecimal( '-123', '1',
'-123', '-123' ), 3 ),
- 2 => array( QuantityValue::newFromDecimal( '-1.23',
'1', '-1.23', '-1.23' ), 4 ),
+ 1 => array( QuantityValue::newFromDecimal( '-123' ), 3
),
+ 2 => array( QuantityValue::newFromDecimal( '-1.23' ), 4
),
- 10 => array( QuantityValue::newFromDecimal( '-100',
'1', '-99', '-101' ), 3 ),
- 11 => array( QuantityValue::newFromDecimal( '+0.00',
'1', '+0.01', '-0.01' ), 4 ),
- 12 => array( QuantityValue::newFromDecimal( '-117.3',
'1', '-117.2', '-117.4' ), 5 ),
+ 10 => array( QuantityValue::newFromDecimal( '-100' ), 3
),
+ 11 => array( QuantityValue::newFromDecimal( '+0.00' ),
4 ),
+ 12 => array( QuantityValue::newFromDecimal( '-117.3' ),
5 ),
20 => array( QuantityValue::newFromDecimal( '+100',
'1', '+100.01', '+99.97' ), 6 ),
21 => array( QuantityValue::newFromDecimal( '-0.002',
'1', '-0.001', '-0.004' ), 5 ),
@@ -257,4 +257,51 @@
26 => array( QuantityValue::newFromDecimal( '+1000',
'1', '+1100', '+900' ), 2 ),
);
}
+
+ /**
+ * @dataProvider transformProvider
+ */
+ public function testTransform( QuantityValue $quantity,
$transformation, QuantityValue $expected ) {
+ $args = func_get_args();
+ $extraArgs = array_slice( $args, 3 );
+
+ $call = array( $quantity, 'transform' );
+ $callArgs = array_merge( array( 'x', $transformation ),
$extraArgs );
+ $actual = call_user_func_array( $call, $callArgs );
+
+ $this->assertEquals( 'x', $actual->getUnit() );
+ $this->assertEquals( $expected->getAmount()->getValue(),
$actual->getAmount()->getValue(), 'value' );
+ $this->assertEquals( $expected->getUpperBound()->getValue(),
$actual->getUpperBound()->getValue(), 'upper bound' );
+ $this->assertEquals( $expected->getLowerBound()->getValue(),
$actual->getLowerBound()->getValue(), 'lower bound' );
+ }
+
+ public function transformProvider() {
+ $identity = function ( DecimalValue $value ) {
+ return $value;
+ };
+
+ $double = function ( DecimalValue $value ) {
+ return new DecimalValue( $value->getValueFloat() * 2 );
+ };
+
+ $scale = function ( DecimalValue $value, $factor ) {
+ return new DecimalValue( $value->getValueFloat() *
$factor );
+ };
+
+ return array(
+ array( QuantityValue::newFromDecimal( '+0' ), $double,
QuantityValue::newFromDecimal( '+0', '?', '+2', '-2' ) ),
+ array( QuantityValue::newFromDecimal( '+10' ),
$identity, QuantityValue::newFromDecimal( '+10' ) ),
+ array( QuantityValue::newFromDecimal( '+10' ), $double,
QuantityValue::newFromDecimal( '+20', '?', '+22', '+18' ) ),
+ array( QuantityValue::newFromDecimal( '-0.5' ),
$identity, QuantityValue::newFromDecimal( '-0.5' ) ),
+ array( QuantityValue::newFromDecimal( '+0.5' ), $scale,
QuantityValue::newFromDecimal( '+0.25', '?', '+0.3', '+0.2' ), 0.5 ),
+
+ // note: absolutely exact values require conversion
with infinite precision!
+ array( QuantityValue::newFromDecimal( '+100', '1',
'+100', '+100' ), $scale, QuantityValue::newFromDecimal( '+12825.0', '?',
'+12825.0', '+12825.0' ), 128.25 ),
+
+ array( QuantityValue::newFromDecimal( '+100', '1',
'+110', '+90' ), $scale, QuantityValue::newFromDecimal( '+330', '?', '+370',
'+300' ), 3.3333 ),
+ array( QuantityValue::newFromDecimal( '+100', '1',
'+100.1', '+99.9' ), $scale, QuantityValue::newFromDecimal( '+333.3', '?',
'+333.7', '+333.0' ), 3.3333 ),
+ array( QuantityValue::newFromDecimal( '+100', '1',
'+100.01', '+99.99' ), $scale, QuantityValue::newFromDecimal( '+333.33', '?',
'+333.36', '+333.30' ), 3.3333 ),
+ );
+ }
+
}
--
To view, visit https://gerrit.wikimedia.org/r/89777
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I3c49e98a9183c0af000bfbf1f5753f4396d1dad3
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits