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 <daniel.kinz...@wikimedia.de>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to