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