Daniel Werner has submitted this change and it was merged.

Change subject: (bug #55512) QuantityValue rewrite based on DecimalValue
......................................................................


(bug #55512) QuantityValue rewrite based on DecimalValue

NOTE: merge Ie7f15f3585fda into WikibaseDataModel first, to avoid
breaking tests.

Change-Id: I1d1cb7367d7de38530ab2890bb87d178efda2cf8
---
M DataValuesCommon/src/DataValues/DecimalValue.php
M DataValuesCommon/src/DataValues/QuantityValue.php
M DataValuesCommon/tests/DataValues/DecimalValueTest.php
M DataValuesCommon/tests/DataValues/QuantityValueTest.php
4 files changed, 552 insertions(+), 120 deletions(-)

Approvals:
  Daniel Werner: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/DataValuesCommon/src/DataValues/DecimalValue.php 
b/DataValuesCommon/src/DataValues/DecimalValue.php
index 29a6d93..dea0203 100644
--- a/DataValuesCommon/src/DataValues/DecimalValue.php
+++ b/DataValuesCommon/src/DataValues/DecimalValue.php
@@ -154,10 +154,8 @@
                }
 
                // compare the integer parts
-               $aIntDigits =  strpos( $a, '.' );
-               $bIntDigits =  strpos( $b, '.' );
-               $aInt = ltrim( substr( $a, 1, ( $aIntDigits ? $aIntDigits : 
strlen( $a ) ) -1 ), '0' );
-               $bInt = ltrim( substr( $b, 1, ( $bIntDigits ? $bIntDigits : 
strlen( $b ) ) -1 ), '0' );
+               $aInt = ltrim( $this->getIntegerPart(), '0' );
+               $bInt = ltrim( $that->getIntegerPart(), '0' );
 
                $sense = $a[0] === '+' ? 1 : -1;
 
@@ -171,21 +169,22 @@
                }
 
                // if both have equal length, compare alphanumerically
-               if ( $aInt > $bInt ) {
+               $cmp = strcmp( $aInt, $bInt );
+               if ( $cmp > 0 ) {
                        return $sense;
                }
 
-               if ( $aInt < $bInt ) {
+               if ( $cmp < 0 ) {
                        return -$sense;
                }
 
                // compare fractional parts
-               $aFract = rtrim( substr( $a, $aIntDigits +1 ), '0' );
-               $bFract = rtrim( substr( $b, $bIntDigits +1 ), '0' );
+               $aFract = rtrim( $this->getFractionalPart(), '0' );
+               $bFract = rtrim( $that->getFractionalPart(), '0' );
 
                // the fractional part is left-aligned, so just check 
alphanumeric ordering
                $cmp = strcmp( $aFract, $bFract );
-               return  ( $cmp > 0 ? 1 : ( $cmp < 0 ? -1 : 0 ) );
+               return  ( $cmp > 0 ? $sense : ( $cmp < 0 ? -$sense : 0 ) );
        }
 
        /**
@@ -264,6 +263,42 @@
        }
 
        /**
+        * Returns the integer part of the value, that is, the part before the 
decimal point,
+        * without the sign.
+        *
+        * @since 0.1
+        *
+        * @return string
+        */
+       public function getIntegerPart() {
+               $n = strpos( $this->value, '.' );
+
+               if ( $n === false ) {
+                       $n = strlen( $this->value );
+               }
+
+               return substr( $this->value, 1, $n -1 );
+       }
+
+       /**
+        * Returns the fractional part of the value, that is, the part after 
the decimal point,
+        * if any.
+        *
+        * @since 0.1
+        *
+        * @return string
+        */
+       public function getFractionalPart() {
+               $n = strpos( $this->value, '.' );
+
+               if ( $n === false ) {
+                       return '';
+               }
+
+               return substr( $this->value, $n + 1 );
+       }
+
+       /**
         * Returns the value held by this object, as a float.
         * Equivalent to floatval( $this->getvalue() ).
         *
diff --git a/DataValuesCommon/src/DataValues/QuantityValue.php 
b/DataValuesCommon/src/DataValues/QuantityValue.php
index 5661c43..35a3edc 100644
--- a/DataValuesCommon/src/DataValues/QuantityValue.php
+++ b/DataValuesCommon/src/DataValues/QuantityValue.php
@@ -2,67 +2,196 @@
 
 namespace DataValues;
 
+use LogicException;
+
 /**
- * Class representing a numeric value with associated unit and accuracy.
+ * Class representing a quantity with associated unit and uncertainty interval.
+ * The amount is stored as a @see DecimalValue object.
  *
- * For simple numeric values use @see NumberValue.
+ * For simple numeric amounts use @see NumberValue.
  *
  * @since 0.1
  *
- * @file
- * @ingroup DataValue
- *
  * @licence GNU GPL v2+
- * @author Jeroen De Dauw < [email protected] >
+ * @author Daniel Kinzler
  */
 class QuantityValue extends DataValueObject {
 
        /**
-        * @since 0.1
+        * The quantity's amount
         *
-        * @var int|float
+        * @var DecimalValue
         */
-       protected $value;
+       protected $amount;
 
        /**
-        * @since 0.1
+        * The quantity's unit identifier (use "1" for unitless quantities).
         *
-        * @var string|null
+        * @var string
         */
        protected $unit;
 
        /**
-        * @since 0.1
+        * The quantity's upper bound
         *
-        * @var int|float|null
+        * @var DecimalValue
         */
-       protected $accuracy;
+       protected $upperBound;
 
        /**
+        * The quantity's lower bound
+        *
+        * @var DecimalValue
+        */
+       protected $lowerBound;
+
+       /**
+        * Constructs a new QuantityValue object, representing the given value.
+        *
         * @since 0.1
         *
-        * @param int|float $amount
-        * @param string|null $unit
-        * @param int|float|null $accuracy
+        * @param DecimalValue $amount
+        * @param string $unit A unit identifier. Must not be empty, use "1" 
for unit-less quantities.
+        * @param DecimalValue $upperBound The upper bound of the quantity, 
inclusive.
+        * @param DecimalValue $lowerBound The lower bound of the quantity, 
inclusive.
         *
         * @throws IllegalValueException
         */
-       public function __construct( $amount, $unit = null, $accuracy = null ) {
-               if ( !is_int( $amount ) && !is_float( $amount ) ) {
-                       throw new IllegalValueException( 'Can only construct 
QuantityValue from floats or integers' );
+       public function __construct( DecimalValue $amount, $unit, DecimalValue 
$upperBound, DecimalValue $lowerBound ) {
+               if ( $lowerBound->compare( $amount ) > 0 ) {
+                       throw new IllegalValueException( '$lowerBound must be 
<= $amount' );
                }
 
-               if ( $accuracy !== null && !is_int( $accuracy ) && !is_float( 
$accuracy ) ) {
-                       throw new IllegalValueException( 'The accuracy of a 
QuantityValue needs to be a float or integer' );
+               if ( $upperBound->compare( $amount ) < 0 ) {
+                       throw new IllegalValueException( '$upperBound must be 
>= $amount' );
                }
 
-               if ( $unit !== null && !is_string( $unit ) ) {
-                       throw new IllegalValueException( 'The unit of a 
QuantityValue needs to be a string' );
+               if ( !is_string( $unit ) ) {
+                       throw new IllegalValueException( '$unit needs to be a 
string' );
                }
 
-               $this->value = $amount;
+               if ( $unit === '' ) {
+                       throw new IllegalValueException( '$unit can not be an 
empty string (use "1" for unit-less quantities)' );
+               }
+
+               $this->amount = $amount;
                $this->unit = $unit;
-               $this->accuracy = $accuracy;
+               $this->upperBound = $upperBound;
+               $this->lowerBound = $lowerBound;
+       }
+
+       /**
+        * Returns a QuantityValue representing the given amount.
+        * If no upper or lower bound is given, the amount is assumed to be 
exact.
+        *
+        * @since 0.1
+        *
+        * @param int|float $amount
+        * @param string $unit
+        * @param int|float|null $upperBound
+        * @param int|float|null $lowerBound
+        *
+        * @return QuantityValue
+        * @throws IllegalValueException
+        */
+       public static function newFromNumber( $amount, $unit = '1', $upperBound 
= null, $lowerBound = null ) {
+               if ( !is_int( $amount ) && !is_float( $amount ) ) {
+                       throw new IllegalValueException( '$amount must be an 
int or float' );
+               }
+
+               if ( !is_null( $upperBound ) && !is_int( $upperBound ) && 
!is_float( $upperBound ) ) {
+                       throw new IllegalValueException( '$upperBound must be 
an int or float (or null)' );
+               }
+
+               if ( !is_null( $lowerBound ) && !is_int( $lowerBound ) && 
!is_float( $lowerBound ) ) {
+                       throw new IllegalValueException( '$lowerBound must be 
an int or float (or null)' );
+               }
+
+               $amount = new DecimalValue( $amount );
+
+               if ( $upperBound === null ) {
+                       $upperBound = $amount;
+               } else {
+                       $upperBound = new DecimalValue( $upperBound );
+               }
+
+               if ( $lowerBound === null ) {
+                       $lowerBound = $amount;
+               } else {
+                       $lowerBound = new DecimalValue( $lowerBound );
+               }
+
+               return new QuantityValue( $amount, $unit, $upperBound, 
$lowerBound );
+       }
+
+
+       /**
+        * Returns a QuantityValue representing the given amount.
+        * If no upper or lower bound is given, the amount is assumed to be 
exact.
+        *
+        * @since 0.1
+        *
+        * @param string $amount
+        * @param string $unit
+        * @param string|null $upperBound
+        * @param string|null $lowerBound
+        *
+        * @return QuantityValue
+        * @throws IllegalValueException
+        */
+       public static function newFromDecimal( $amount, $unit = '1', 
$upperBound = null, $lowerBound = null ) {
+               if ( !is_string( $amount ) ) {
+                       throw new IllegalValueException( '$amount must be a 
string' );
+               }
+
+               if ( !is_null( $upperBound ) && !is_string( $upperBound ) ) {
+                       throw new IllegalValueException( '$upperBound must be a 
string' );
+               }
+
+               if ( !is_null( $lowerBound ) && !is_string( $lowerBound ) ) {
+                       throw new IllegalValueException( '$lowerBound must be a 
string' );
+               }
+
+               $amount = new DecimalValue( self::normalizeDecimal( $amount ) );
+
+
+               if ( $upperBound === null ) {
+                       $upperBound = $amount;
+               } else {
+                       $upperBound = new DecimalValue( self::normalizeDecimal( 
$upperBound ) );
+               }
+
+               if ( $lowerBound === null ) {
+                       $lowerBound = $amount;
+               } else {
+                       $lowerBound = new DecimalValue( self::normalizeDecimal( 
$lowerBound ) );
+               }
+
+               return new QuantityValue( $amount, $unit, $upperBound, 
$lowerBound );
+       }
+
+       /**
+        * @param string $amount
+        *
+        * @return string
+        */
+       private static function normalizeDecimal( $amount ) {
+               $amount = preg_replace( '/^0+([^0])/', '\1', $amount );
+               $amount = preg_replace( '/^0+([^0])/', '\1', $amount );
+
+               if ( preg_match( '/^\./', $amount ) ) {
+                       $amount = '0' . $amount;
+               }
+
+               if ( preg_match( '/\.$/', $amount ) ) {
+                       $amount = $amount . '0';
+               }
+
+               if ( preg_match( '/^[0-9]/', $amount ) ) {
+                       $amount = '+' . $amount;
+               }
+
+               return $amount;
        }
 
        /**
@@ -70,18 +199,15 @@
         *
         * @since 0.1
         *
-        * @return int|float
+        * @return string
         */
        public function serialize() {
-               $data = array( $this->value );
-
-               if ( $this->accuracy !== null || $this->unit !== null ) {
-                       $data[] = $this->unit;
-               }
-
-               if ( $this->accuracy !== null ) {
-                       $data[] = $this->accuracy;
-               }
+               $data = array(
+                       $this->amount,
+                       $this->unit,
+                       $this->upperBound,
+                       $this->lowerBound,
+               );
 
                return serialize( $data );
        }
@@ -93,16 +219,17 @@
         *
         * @param string $data
         *
-        * @return NumberValue
+        * @return DecimalValue
         */
        public function unserialize( $data ) {
                $data = unserialize( $data );
 
-               $value = array_shift( $data );
+               $amount = array_shift( $data );
                $unit = array_shift( $data );
-               $accuracy = array_shift( $data );
+               $upperBound = array_shift( $data );
+               $lowerBound = array_shift( $data );
 
-               $this->__construct( $value, $unit, $accuracy );
+               $this->__construct( $amount, $unit, $upperBound, $lowerBound);
        }
 
        /**
@@ -121,10 +248,10 @@
         *
         * @since 0.1
         *
-        * @return string|float|int
+        * @return float
         */
        public function getSortKey() {
-               return $this->value;
+               return $this->getAmount()->getValueFloat();
        }
 
        /**
@@ -133,40 +260,139 @@
         *
         * @since 0.1
         *
-        * @return int|float
+        * @return QuantityValue
         */
        public function getValue() {
                return $this;
        }
 
        /**
-        * Returns the amount held by this quantity.
+        * Returns the amount represented by this quantity.
         *
         * @since 0.1
         *
-        * @return int|float
+        * @return DecimalValue
         */
        public function getAmount() {
-               return $this->value;
+               return $this->amount;
        }
 
        /**
-        * Returns the accuracy held by this quantity.
+        * Returns this quantity's upper bound.
         *
         * @since 0.1
         *
-        * @return int|float|null
+        * @return DecimalValue
         */
-       public function getAccuracy() {
-               return $this->accuracy;
+       public function getUpperBound() {
+               return $this->upperBound;
+       }
+
+       /**
+        * Returns this quantity's lower bound.
+        *
+        * @since 0.1
+        *
+        * @return DecimalValue
+        */
+       public function getLowerBound() {
+               return $this->lowerBound;
+       }
+
+       /**
+        * Returns the size of the uncertainty interval.
+        * This can roughly be interpreted as "amount +/- uncertainty/2".
+        *
+        * The exact interpretation of the uncertainty interval is left to the 
concrete application or
+        * data point. For example, the uncertainty interval may be defined to 
be that part of a
+        * normal distribution that is required to cover the 95th percentile.
+        *
+        * @since 0.1
+        *
+        * @return float
+        */
+       public function getUncertainty() {
+               return $this->getUpperBound()->getValueFloat() - 
$this->getLowerBound()->getValueFloat();
+       }
+
+       /**
+        * Returns a DecimalValue representing the symmetrical offset to be 
applied
+        * to the raw amount for a rough representation of the uncertainty 
interval,
+        * as in "amount +/- offset".
+        *
+        * The offset is calculated as max( amount - lowerBound, upperBound - 
amount ).
+        *
+        * @since 0.1
+        *
+        * @return DecimalValue
+        */
+       public function getUncertaintyMargin() {
+               //TODO: use bcmath if available
+               $amount = $this->getAmount()->getValueFloat();
+               $upperBound = $this->getUpperBound()->getValueFloat();
+               $lowerBound = $this->getLowerBound()->getValueFloat();
+
+               $offset = max( $amount - $lowerBound, $upperBound - $amount );
+               return new DecimalValue( $offset );
+       }
+
+       /**
+        * Returns the number of significant digits in the amount-string,
+        * counting the decimal point, but not counting the leading sign.
+        *
+        * Note that this calculation assumes a symmetric uncertainty interval, 
and can be misleading
+        *
+        * @since 0.1
+        *
+        * @return int
+        */
+       public function getSignificantDigits() {
+               // the desired precision is given by the distance between the 
amount and
+               // whatever is close, the uppoer or lower bound.
+               //TODO: use bcmath if available
+               $amount = $this->getAmount()->getValueFloat();
+               $upperBound = $this->getUpperBound()->getValueFloat();
+               $lowerBound = $this->getLowerBound()->getValueFloat();
+               $precision = min( $amount - $lowerBound, $upperBound - $amount 
);
+
+               if ( $precision === 0.0 ) {
+                       // include the decimal point, but not the sign
+                       $significantDigits = strlen( $this->amount->getValue() 
) -1;
+                       return $significantDigits;
+               }
+
+               // e.g. +/- 200 -> 2; +/- 0.02 -> -2
+               // note: we really want floor( $orderOfPrecision ), but have to 
account for
+               // small errors made in the floating point operations above
+               $orderOfPrecision = floor( log10( $precision + 0.0000000005 ) );
+
+               // the length of the integer part is the reference point
+               $significantDigits = strlen( $this->amount->getIntegerPart() );
+
+               if ( $orderOfPrecision >= 0 ) {
+                       // e.g. 3000 +/- 100 -> 2 digits
+                       $significantDigits -= (int)$orderOfPrecision;
+               } else {
+                       // e.g. 56.78 +/- 0.01 -> 5 digits
+                       $significantDigits += (int)(-$orderOfPrecision);
+                       $significantDigits += 1; // for the '.'
+               }
+
+               // assert sane value
+               if ( $significantDigits <= 0 ) {
+                       throw new LogicException( 'Invalid calculation of 
significant digits' );
+               }
+
+               return $significantDigits;
        }
 
        /**
         * Returns the unit held by this quantity.
+        * Unit-less quantities should use "1" as their unit.
         *
         * @since 0.1
         *
-        * @return string|null
+        * @return string
         */
        public function getUnit() {
                return $this->unit;
@@ -177,13 +403,14 @@
         *
         * @since 0.1
         *
-        * @return mixed
+        * @return array
         */
        public function getArrayValue() {
                return array(
+                       'amount' => $this->amount->getArrayValue(),
                        'unit' => $this->unit,
-                       'accuracy' => $this->accuracy,
-                       'value' => $this->value,
+                       'upperBound' => $this->upperBound->getArrayValue(),
+                       'lowerBound' => $this->lowerBound->getArrayValue(),
                );
        }
 
@@ -199,8 +426,13 @@
         * @throws IllegalValueException
         */
        public static function newFromArray( $data ) {
-               self::requireArrayFields( $data, array( 'value', 'unit', 
'accuracy' ) );
-               return new static( $data['value'], $data['unit'], 
$data['accuracy'] );
-       }
+               self::requireArrayFields( $data, array( 'amount', 'unit', 
'upperBound', 'lowerBound' ) );
 
+               return new static(
+                       DecimalValue::newFromArray( $data['amount'] ),
+                       $data['unit'],
+                       DecimalValue::newFromArray( $data['upperBound'] ),
+                       DecimalValue::newFromArray( $data['lowerBound'] )
+               );
+       }
 }
diff --git a/DataValuesCommon/tests/DataValues/DecimalValueTest.php 
b/DataValuesCommon/tests/DataValues/DecimalValueTest.php
index a13e22b..763d93c 100644
--- a/DataValuesCommon/tests/DataValues/DecimalValueTest.php
+++ b/DataValuesCommon/tests/DataValues/DecimalValueTest.php
@@ -45,8 +45,9 @@
                $argLists[] = array( '-0.42' );
                $argLists[] = array( '-0.0' );
                $argLists[] = array( '-0' );
-               $argLists[] = array( '+0.0' );
                $argLists[] = array( '+0' );
+               $argLists[] = array( '+0.0' );
+               $argLists[] = array( '+0.000' );
 
                return $argLists;
        }
@@ -107,6 +108,8 @@
                        'simple/greater' => array( new DecimalValue( '+2' ), 
new DecimalValue( '+1' ), +1 ),
                        'negative/greater' => array( new DecimalValue( '-1' ), 
new DecimalValue( '-2' ), +1 ),
                        'negative/smaller' => array( new DecimalValue( '-2' ), 
new DecimalValue( '-1' ), -1 ),
+                       'negative-small/greater' => array( new DecimalValue( 
'-0.5' ), new DecimalValue( '-0.7' ), +1 ),
+                       'negative-small/smaller' => array( new DecimalValue( 
'-0.7' ), new DecimalValue( '-0.5' ), -1 ),
 
                        'digits/greater' => array( new DecimalValue( '+11' ), 
new DecimalValue( '+8' ), +1 ),
                        'digits-sub/greater' => array( new DecimalValue( '+11' 
), new DecimalValue( '+8.0' ), +1 ),
@@ -115,6 +118,8 @@
 
                        'signs/greater' => array( new DecimalValue( '+1' ), new 
DecimalValue( '-8' ), +1 ),
                        'signs/less' => array( new DecimalValue( '-8' ), new 
DecimalValue( '+1' ), -1 ),
+
+            'with-and-without-point' => array( new DecimalValue( '+100' ), new 
DecimalValue( '+100.01' ), -1 ),
                );
        }
 
@@ -199,4 +204,47 @@
 
                return $argLists;
        }
+
+       /**
+        * @dataProvider getGetIntegerPartProvider
+        *
+        * @since 0.1
+        */
+       public function testGetIntegerPart( DecimalValue $value, $expected ) {
+               $actual = $value->getIntegerPart();
+               $this->assertSame( $expected, $actual );
+       }
+
+       public function getGetIntegerPartProvider() {
+               return array(
+                       array( new DecimalValue(  '+0' ),      '0' ),
+                       array( new DecimalValue(  '-0.0' ),    '0' ),
+                       array( new DecimalValue( '+10' ),     '10' ),
+                       array( new DecimalValue( '-10' ),     '10' ),
+                       array( new DecimalValue( '+10.663' ), '10' ),
+                       array( new DecimalValue( '-10.001' ), '10' ),
+                       array( new DecimalValue(  '+0.01' ),   '0' ),
+               );
+       }
+
+       /**
+        * @dataProvider getGetIntegerPartProvider
+        *
+        * @since 0.1
+        */
+       public function testGetFractionalPart( DecimalValue $value, $expected ) 
{
+               $actual = $value->getIntegerPart();
+               $this->assertSame( $expected, $actual );
+       }
+
+       public function getGetFractionalPartProvider() {
+               return array(
+                       array( new DecimalValue(  '+0' ),     '' ),
+                       array( new DecimalValue(  '-0.0' ),   '0' ),
+                       array( new DecimalValue( '+10' ),     '' ),
+                       array( new DecimalValue( '+10.663' ), '663' ),
+                       array( new DecimalValue( '-10.001' ), '001' ),
+                       array( new DecimalValue(  '+0.01' ),  '01' ),
+               );
+       }
 }
diff --git a/DataValuesCommon/tests/DataValues/QuantityValueTest.php 
b/DataValuesCommon/tests/DataValues/QuantityValueTest.php
index e45a339..2ce727e 100644
--- a/DataValuesCommon/tests/DataValues/QuantityValueTest.php
+++ b/DataValuesCommon/tests/DataValues/QuantityValueTest.php
@@ -2,6 +2,7 @@
 
 namespace DataValues\Tests;
 
+use DataValues\DecimalValue;
 use DataValues\QuantityValue;
 
 /**
@@ -16,7 +17,7 @@
  * @group DataValueExtensions
  *
  * @licence GNU GPL v2+
- * @author Jeroen De Dauw < [email protected] >
+ * @author Daniel Kinzler
  */
 class QuantityValueTest extends DataValueTest {
 
@@ -34,25 +35,9 @@
        public function validConstructorArgumentsProvider() {
                $argLists = array();
 
-               $argLists[] = array( 42 );
-               $argLists[] = array( -42 );
-               $argLists[] = array( 4.2 );
-               $argLists[] = array( -4.2 );
-               $argLists[] = array( 0 );
-
-               $argLists[] = array( 42, 'm' );
-               $argLists[] = array( -42, 'm' );
-               $argLists[] = array( 4.2, 'm' );
-               $argLists[] = array( -4.2, 'm' );
-               $argLists[] = array( 0, 'm' );
-
-               $argLists[] = array( 4.2, null );
-
-               $argLists[] = array( 42, 'm', 4.2 );
-               $argLists[] = array( -42, 'm', -4.2 );
-               $argLists[] = array( 4.2, 'm', -42 );
-               $argLists[] = array( -4.2, 'm', 42 );
-               $argLists[] = array( -42, 'm', null );
+               $argLists[] = array( new DecimalValue( '+42' ), '1', new 
DecimalValue( '+42' ), new DecimalValue( '+42' ) );
+               $argLists[] = array( new DecimalValue( '+0.01' ), '1', new 
DecimalValue( '+0.02' ), new DecimalValue( '+0.0001' ) );
+               $argLists[] = array( new DecimalValue( '-0.5' ), '1', new 
DecimalValue( '+0.02' ), new DecimalValue( '-0.7' ) );
 
                return $argLists;
        }
@@ -61,35 +46,11 @@
                $argLists = array();
 
                $argLists[] = array();
+               $argLists[] = array( new DecimalValue( '+0' ), '', new 
DecimalValue( '+0' ), new DecimalValue( '+0' ) );
+               $argLists[] = array( new DecimalValue( '+0' ), 1, new 
DecimalValue( '+0' ), new DecimalValue( '+0' ) );
 
-
-               $argLists[] = array( 'foo' );
-               $argLists[] = array( '' );
-               $argLists[] = array( '0' );
-               $argLists[] = array( '42' );
-               $argLists[] = array( '-42' );
-               $argLists[] = array( '4.2' );
-               $argLists[] = array( '-4.2' );
-               $argLists[] = array( false );
-               $argLists[] = array( true );
-               $argLists[] = array( null );
-               $argLists[] = array( '0x20' );
-
-               $argLists[] = array( 'foo', 'm' );
-               $argLists[] = array( '', 'm' );
-               $argLists[] = array( '0', 'm' );
-               $argLists[] = array( 42, 0 );
-               $argLists[] = array( -42, 0 );
-
-               $argLists[] = array( -4.2, false );
-               $argLists[] = array( 0, true );
-               $argLists[] = array( 'foo', array() );
-               $argLists[] = array( '', 4.2 );
-               $argLists[] = array( '0', -1 );
-
-               $argLists[] = array( 42, 'm', false );
-               $argLists[] = array( 4.2, 'm', '0' );
-               $argLists[] = array( -4.2, 'm', '-4.2' );
+               $argLists[] = array( new DecimalValue( '+0' ), '1', new 
DecimalValue( '-0.001' ), new DecimalValue( '-1' ) );
+               $argLists[] = array( new DecimalValue( '+0' ), '1', new 
DecimalValue( '+1' ), new DecimalValue( '+0.001' ) );
 
                return $argLists;
        }
@@ -118,8 +79,7 @@
         * @param array $arguments
         */
        public function testGetUnit( QuantityValue $quantity, array $arguments 
) {
-               $expected = count( $arguments ) > 1 ? $arguments[1] : null;
-               $this->assertEquals( $expected, $quantity->getUnit() );
+               $this->assertEquals( $arguments[1], $quantity->getUnit() );
        }
 
        /**
@@ -127,9 +87,166 @@
         * @param QuantityValue $quantity
         * @param array $arguments
         */
-       public function testGetAccuracy( QuantityValue $quantity, array 
$arguments ) {
-               $expected = count( $arguments ) > 2 ? $arguments[2] : null;
-               $this->assertEquals( $expected, $quantity->getAccuracy() );
+       public function testGetUpperBound( QuantityValue $quantity, array 
$arguments ) {
+               $this->assertEquals( $arguments[2], $quantity->getUpperBound() 
);
        }
 
+       /**
+        * @dataProvider instanceProvider
+        * @param QuantityValue $quantity
+        * @param array $arguments
+        */
+       public function testGetLowerBound( QuantityValue $quantity, array 
$arguments ) {
+               $this->assertEquals( $arguments[3], $quantity->getLowerBound() 
);
+       }
+
+       /**
+        * @dataProvider newFromNumberProvider
+        *
+        * @param $amount
+        * @param $unit
+        * @param $upperBound
+        * @param $lowerBound
+        * @param QuantityValue $expected
+        */
+       public function testNewFromNumber( $amount, $unit, $upperBound, 
$lowerBound, QuantityValue $expected ) {
+               $quantity = QuantityValue::newFromNumber( $amount, $unit, 
$upperBound, $lowerBound );
+
+               $this->assertEquals( $expected->getAmount()->getValue(), 
$quantity->getAmount()->getValue() );
+               $this->assertEquals( $expected->getUpperBound()->getValue(), 
$quantity->getUpperBound()->getValue() );
+               $this->assertEquals( $expected->getLowerBound()->getValue(), 
$quantity->getLowerBound()->getValue() );
+       }
+
+       public function newFromNumberProvider() {
+               return array(
+                       array(
+                               42, '1', null, null,
+                               new QuantityValue( new DecimalValue( '+42' ), 
'1', new DecimalValue( '+42' ), new DecimalValue( '+42' ) )
+                       ),
+                       array(
+                               -0.05, '1', null, null,
+                               new QuantityValue( new DecimalValue( '-0.05' ), 
'1', new DecimalValue( '-0.05' ), new DecimalValue( '-0.05' ) )
+                       ),
+                       array(
+                               0.0, 'm', 0.5, -0.5,
+                               new QuantityValue( new DecimalValue( '+0' ), 
'm', new DecimalValue( '+0.5' ), new DecimalValue( '-0.5' ) )
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider newFromDecimalProvider
+        *
+        * @param $amount
+        * @param $unit
+        * @param $upperBound
+        * @param $lowerBound
+        * @param QuantityValue $expected
+        */
+       public function testNewFromDecimal( $amount, $unit, $upperBound, 
$lowerBound, QuantityValue $expected ) {
+               $quantity = QuantityValue::newFromDecimal( $amount, $unit, 
$upperBound, $lowerBound );
+
+               $this->assertEquals( $expected->getAmount()->getValue(), 
$quantity->getAmount()->getValue() );
+               $this->assertEquals( $expected->getUpperBound()->getValue(), 
$quantity->getUpperBound()->getValue() );
+               $this->assertEquals( $expected->getLowerBound()->getValue(), 
$quantity->getLowerBound()->getValue() );
+       }
+
+       public function newFromDecimalProvider() {
+               return array(
+                       array(
+                               '+23', '1', null, null,
+                               new QuantityValue( new DecimalValue( '+23' ), 
'1', new DecimalValue( '+23' ), new DecimalValue( '+23' ) )
+                       ),
+                       array(
+                               '+42', '1', '+43', '+41',
+                               new QuantityValue( new DecimalValue( '+42' ), 
'1', new DecimalValue( '+43' ), new DecimalValue( '+41' ) )
+                       ),
+                       array(
+                               '-0.05', 'm', '-0.04', '-0.06',
+                               new QuantityValue( new DecimalValue( '-0.05' ), 
'm', new DecimalValue( '-0.04' ), new DecimalValue( '-0.06' ) )
+                       ),
+               );
+       }
+
+       /**
+        * @dataProvider instanceProvider
+        */
+       public function testGetSortKey( QuantityValue $quantity ) {
+               $this->assertEquals( $quantity->getAmount()->getValueFloat(), 
$quantity->getSortKey() );
+       }
+
+       /**
+        * @dataProvider getUncertaintyProvider
+        */
+       public function testGetUncertainty( QuantityValue $quantity, $expected 
) {
+               $actual = $quantity->getUncertainty();
+
+               // floats are wonkey, accept small differences here
+               $this->assertTrue( abs( $actual - $expected ) < 0.000000001, 
"expected $expected, got $actual" );
+       }
+
+       public function getUncertaintyProvider() {
+               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', '1', 
'+0.2', '-0.6' ), 0.8 ),
+                       array( QuantityValue::newFromDecimal( '+7.3', '1', 
'+7.7', '+5.2' ), 2.5 ),
+               );
+       }
+
+       /**
+        * @dataProvider getUncertaintyMarginProvider
+        */
+       public function testGetUncertaintyMargin( QuantityValue $quantity, 
$expected ) {
+               $actual = $quantity->getUncertaintyMargin();
+
+               $this->assertEquals( $expected, $actual->getValue() );
+       }
+
+       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', 
'+0.2', '-0.6' ), '+0.6' ),
+                       array( QuantityValue::newFromDecimal( '+7.5', '1', 
'+7.5', '+5.5' ), '+2' ),
+                       array( QuantityValue::newFromDecimal( '+11.5', '1', 
'+15', '+10.5' ), '+3.5' ),
+               );
+       }
+
+
+       /**
+        * @dataProvider getSignificantDigitsProvider
+        */
+       public function testGetSignificantDigits( QuantityValue $quantity, 
$expected ) {
+               $actual = $quantity->getSignificantDigits();
+
+               $this->assertEquals( $expected, $actual );
+       }
+
+       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 ),
+
+                       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 ),
+
+                       20 => array( QuantityValue::newFromDecimal( '+100', 
'1', '+100.01', '+99.97' ), 6 ),
+                       21 => array( QuantityValue::newFromDecimal( '-0.002', 
'1', '-0.001', '-0.004' ), 5 ),
+                       22 => array( QuantityValue::newFromDecimal( '-0.002', 
'1', '+0.001', '-0.06' ), 5 ),
+                       23 => array( QuantityValue::newFromDecimal( '-21', '1', 
'+1.1', '-120' ), 1 ),
+                       24 => array( QuantityValue::newFromDecimal( '-2', '1', 
'+1.1', '-120' ), 1 ),
+                       25 => array( QuantityValue::newFromDecimal( '+1000', 
'1', '+1100', '+900.03' ), 3 ),
+                       26 => array( QuantityValue::newFromDecimal( '+1000', 
'1', '+1100', '+900' ), 2 ),
+               );
+       }
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/88787
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I1d1cb7367d7de38530ab2890bb87d178efda2cf8
Gerrit-PatchSet: 10
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Addshore <[email protected]>
Gerrit-Reviewer: Aude <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Daniel Werner <[email protected]>
Gerrit-Reviewer: Jeroen De Dauw <[email protected]>
Gerrit-Reviewer: Tobias Gritschacher <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to