Daniel Werner has submitted this change and it was merged.

Change subject: Introducing DecimalMath
......................................................................


Introducing DecimalMath

This adds functionality for doing basic arithemetics, rounding, etc
on DecimalValue objects. This is useful for automatically determining
precision/uncertainty based on the number of digits given, and ultimately,
for unit conversion.

Change-Id: Idc3006d7e0f5fd635233a3de57b8194f76f4b434
---
M DataValuesCommon/DataValuesCommon.classes.php
A DataValuesCommon/src/DataValues/DecimalMath.php
M DataValuesCommon/src/DataValues/DecimalValue.php
A DataValuesCommon/tests/DataValues/DecimalMathTest.php
M DataValuesCommon/tests/DataValues/DecimalValueTest.php
5 files changed, 626 insertions(+), 3 deletions(-)

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



diff --git a/DataValuesCommon/DataValuesCommon.classes.php 
b/DataValuesCommon/DataValuesCommon.classes.php
index 277c744..7dbd1a6 100644
--- a/DataValuesCommon/DataValuesCommon.classes.php
+++ b/DataValuesCommon/DataValuesCommon.classes.php
@@ -54,6 +54,7 @@
        'DataValues\LatLongValue' => 'src/DataValues/LatLongValue.php',
        'DataValues\MonolingualTextValue' => 
'src/DataValues/MonolingualTextValue.php',
        'DataValues\MultilingualTextValue' => 
'src/DataValues/MultilingualTextValue.php',
+       'DataValues\DecimalMath' => 'src/DataValues/DecimalMath.php',
        'DataValues\DecimalValue' => 'src/DataValues/DecimalValue.php',
        'DataValues\QuantityValue' => 'src/DataValues/QuantityValue.php',
        'DataValues\TimeValue' => 'src/DataValues/TimeValue.php',
diff --git a/DataValuesCommon/src/DataValues/DecimalMath.php 
b/DataValuesCommon/src/DataValues/DecimalMath.php
new file mode 100644
index 0000000..ccbd664
--- /dev/null
+++ b/DataValuesCommon/src/DataValues/DecimalMath.php
@@ -0,0 +1,319 @@
+<?php
+
+namespace DataValues;
+
+/**
+ * Class for performing basic arithmetic and other transformations
+ * on DecimalValues.
+ *
+ * @see DecimalValue
+ *
+ * @since 0.1
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Kinzler
+ */
+class DecimalMath {
+
+       /**
+        * Returns the product of the two values.
+        *
+        * @param DecimalValue $a
+        * @param DecimalValue $b
+        *
+        * @return DecimalValue
+        */
+       public function product( DecimalValue $a, DecimalValue $b ) {
+               //TODO: use bcmath if available
+               $product = $a->getValueFloat() * $b->getValueFloat();
+
+               return new DecimalValue( $product );
+       }
+
+       /**
+        * Returns the sum of the two values.
+        *
+        * @param DecimalValue $a
+        * @param DecimalValue $b
+        *
+        * @return DecimalValue
+        */
+       public function sum( DecimalValue $a, DecimalValue $b ) {
+               //TODO: use bcmath if available
+               $product = $a->getValueFloat() + $b->getValueFloat();
+
+               return new DecimalValue( $product );
+       }
+
+       /**
+        * Returns the given value, with any insignificant digits removed or 
zeroed.
+        * Rounding is applied  using the "round half away from zero" rule 
(that is, +0.5 is
+        * rounded to +1 and -0.5 is rounded to -1).
+        *
+        * @since 0.1
+        *
+        * @param DecimalValue $decimal
+        * @param int $significantDigits
+        *
+        * @throws \InvalidArgumentException
+        * @return DecimalValue
+        */
+       public function round( DecimalValue $decimal, $significantDigits ) {
+               $value = $decimal->getValue();
+               $rounded = $this->roundDigits( $value, $significantDigits );
+               return new DecimalValue( $rounded );
+       }
+
+       /**
+        * Returns the given value, with any insignificant digits removed or 
zeroed.
+        * Rounding is applied using the "round half away from zero" rule (that 
is, +0.5 is
+        * rounded to +1 and -0.5 is rounded to -1).
+        *
+        * @see round()
+        *
+        * @param string $value
+        * @param int $significantDigits
+        *
+        * @throws \InvalidArgumentException
+        * @return string
+        */
+       protected function roundDigits( $value, $significantDigits ) {
+               if ( !is_int( $significantDigits ) ) {
+                       throw new \InvalidArgumentException( 
'$significantDigits must be an integer' );
+               }
+
+               if ( $significantDigits <= 0 ) {
+                       throw new \InvalidArgumentException( 
'$significantDigits must be larger than zero.' );
+               }
+
+               // whether the last character is already part of the integer 
part of the decimal value
+               $inIntPart = ( strpos( $value, '.' ) === false );
+
+               $rounded = '';
+
+               // Iterate over characters from right to left and build the 
result back to front.
+               for ( $i = strlen( $value ) -1; $i > 0 && $i > 
$significantDigits; $i-- ) {
+
+                       list( $value, $i, $inIntPart, $next ) = 
$this->roundNextDigit( $value, $i, $inIntPart );
+
+                       $rounded = $next . $rounded;
+               }
+
+               // just keep the remainder of the value as is (this includes 
the sign)
+               $rounded = substr( $value, 0, $i +1 ) . $rounded;
+
+               // strip trailing decimal point
+               $rounded = rtrim( $rounded, '.' );
+               return $rounded;
+       }
+
+       /**
+        * Extracts the next character to add to the result of a rounding run:
+        * $value[$] will be examined and processed in order to determine the 
next
+        * character to prepend to the result (returned in the $nextCharacter 
field).
+        *
+        * Updated values for the parameters are returned as well as the next
+        * character.
+        *
+        * @param string $value
+        * @param int $i
+        * @param bool $inIntPart
+        *
+        * @return array ( $value, $i, $inIntPart, $nextCharacter )
+        */
+       private function roundNextDigit( $value, $i, $inIntPart ) {
+               // next digit
+               $ch = $value[$i];
+
+               if ( $ch === '.' ) {
+                       // just transition from the fractional to the integer 
part
+                       $inIntPart = true;
+                       $nextCharacter = '.';
+               } else {
+                       if ( $inIntPart ) {
+                               // in the integer part, zero out insignificant 
digits
+                               $nextCharacter = '0';
+                       } else {
+                               // in the fractional part, strip insignificant 
digits
+                               $nextCharacter = '';
+                       }
+
+                       if ( ord( $ch ) >= ord( '5' ) ) {
+                               // when stripping a character >= 5, bump up the 
next digit to the left.
+                               list( $value, $i, $inIntPart ) = 
$this->bumpDigitsForRounding( $value, $i, $inIntPart );
+                       }
+               }
+
+               return array( $value, $i, $inIntPart, $nextCharacter );
+       }
+
+       /**
+        * Bumps the last digit of a value that is being processed for rounding 
while taking
+        * care of edge cases and updating the state of the rounding process.
+        *
+        * - $value is truncated to $i digits, so we can safely increment 
(bump) the last digit.
+        * - if the last character of $value is '.', it's trimmed (and 
$inIntPart is set to true)
+        *   to handle the transition from the fractional to the integer part 
of $value.
+        * - the last digit of $value is bumped using bumpDigits() - this is 
where the magic happens.
+        * - $i is set to strln( $value ) to make the index consistent in case 
a trailing decimal
+        *   point got removed.
+        *
+        * Updated values for the parameters are returned.
+        * Note: when returning, $i is always one greater than the greatest 
valid index in $value.
+        *
+        * @param string $value
+        * @param int $i
+        * @param bool $inIntPart
+        *
+        * @return array ( $value, $i, $inIntPart, $next )
+        */
+       private function bumpDigitsForRounding( $value, $i, $inIntPart ) {
+               $remaining = substr( $value, 0, $i );
+
+               // If there's a '.' at the end, strip it and note that we are 
in the
+               // integer part of $value now.
+               if ( $remaining[ strlen( $remaining ) -1 ] === '.' ) {
+                       $remaining = rtrim( $remaining, '.' );
+                       $inIntPart = true;
+               }
+
+               // Rounding may add digits, adjust $i for that.
+               $value = $this->bumpDigits( $remaining );
+               $i = strlen( $value );
+
+               return array( $value, $i, $inIntPart );
+       }
+
+       /**
+        * Increment the least significant digit by one if it is less than 9, 
and
+        * set it to zero and continue to the next more significant digit if it 
is 9.
+        * Exception: bump( 0 ) == 1;
+        *
+        * E.g.: bump( 0.2 ) == 0.3, bump( -0.09 ) == -0.10, bump( 9.99 ) == 
10.00
+        *
+        * This is the inverse of @see slump()
+        *
+        * @since 0.1
+        *
+        * @param DecimalValue $decimal
+        *
+        * @return DecimalValue
+        */
+       public function bump( DecimalValue $decimal ) {
+               $value = $decimal->getValue();
+               $bumped = $this->bumpDigits( $value );
+               return new DecimalValue( $bumped );
+       }
+
+       /**
+        * Increment the least significant digit by one if it is less than 9, 
and
+        * set it to zero and continue to the next more significant digit if it 
is 9.
+        *
+        * @see bump()
+        *
+        * @param string $value
+        * @return string
+        */
+       protected function bumpDigits( $value ) {
+               if ( $value === '+0' ) {
+                       return '+1';
+               }
+
+               $bumped = '';
+
+               for ( $i = strlen( $value ) -1; $i >= 0; $i-- ) {
+                       $ch = $value[$i];
+
+                       if ( $ch === '.' ) {
+                               $bumped = '.' . $bumped;
+                               continue;
+                       } elseif ( $ch === '9' ) {
+                               $bumped = '0' . $bumped;
+                               continue;
+                       } elseif ( $ch === '+' || $ch === '-' ) {
+                               $bumped = $ch . '1' . $bumped;
+                               break;
+                       } else {
+                               $bumped =  chr( ord( $ch ) + 1 ) . $bumped;
+                               break;
+                       }
+               }
+
+               $bumped = substr( $value, 0, $i ) . $bumped;
+               return $bumped;
+       }
+
+       /**
+        * Decrement the least significant digit by one if it is more than 0, 
and
+        * set it to 9 and continue to the next more significant digit if it is 
0.
+        * Exception: slump( 0 ) == -1;
+        *
+        * E.g.: slump( 0.2 ) == 0.1, slump( -0.10 ) == -0.01, slump( 0.0 ) == 
-1.0
+        *
+        * This is the inverse of @see bump()
+        *
+        * @since 0.1
+        *
+        * @param DecimalValue $decimal
+        *
+        * @return DecimalValue
+        */
+       public function slump( DecimalValue $decimal ) {
+               $value = $decimal->getValue();
+               $slumped = $this->slumpDigits( $value );
+               return new DecimalValue( $slumped );
+       }
+
+       /**
+        * Decrement the least significant digit by one if it is more than 0, 
and
+        * set it to 9 and continue to the next more significant digit if it is 
0.
+        *
+        * @see slump()
+        *
+        * @param string $value
+        * @return string
+        */
+       protected function slumpDigits( $value ) {
+               if ( $value === '+0' ) {
+                       return '-1';
+               }
+
+               // a "precise zero" will become negative
+               if ( preg_match( '/^\+0\.(0*)0$/', $value, $m ) ) {
+                       return '-0.' . $m[1] . '1';
+               }
+
+               $slumped = '';
+
+               for ( $i = strlen( $value ) -1; $i >= 0; $i-- ) {
+                       $ch = substr( $value, $i, 1 );
+
+                       if ( $ch === '.' ) {
+                               $slumped = '.' . $slumped;
+                               continue;
+                       } elseif ( $ch === '0' ) {
+                               $slumped = '9' . $slumped;
+                               continue;
+                       } elseif ( $ch === '+' || $ch === '-' ) {
+                               $slumped = '0';
+                               break;
+                       } else {
+                               $slumped =  chr( ord( $ch ) - 1 ) . $slumped;
+                               break;
+                       }
+               }
+
+               // preserve prefix
+               $slumped = substr( $value, 0, $i ) . $slumped;
+
+               // strip leading zeros
+               $slumped = preg_replace( '/^([-+])(0+)([0-9]+(\.|$))/', '\1\3', 
$slumped );
+
+               if ( $slumped === '-0' ) {
+                       $slumped = '+0';
+               }
+
+               return $slumped;
+       }
+
+}
\ No newline at end of file
diff --git a/DataValuesCommon/src/DataValues/DecimalValue.php 
b/DataValuesCommon/src/DataValues/DecimalValue.php
index dea0203..6db74b5 100644
--- a/DataValuesCommon/src/DataValues/DecimalValue.php
+++ b/DataValuesCommon/src/DataValues/DecimalValue.php
@@ -263,6 +263,37 @@
        }
 
        /**
+        * Determines whether this DecimalValue is zero.
+        *
+        * @return bool
+        */
+       public function isZero() {
+               return (bool)preg_match( '/^[-+]0+(\.0+)?$/', $this->value );
+       }
+
+       /**
+        * Returns a new DecimalValue that represents the complement of this 
DecimalValue.
+        * That is, it constructs a new DecimalValue with the same digits as 
this,
+        * but with the sign inverted.
+        *
+        * Note that if isZero() returns true, this method returns this
+        * DecimalValue itself (because zero is it's own complement).
+        *
+        * @return DecimalValue
+        */
+       public function computeComplement() {
+               if ( $this->isZero() ) {
+                       return $this;
+               }
+
+               $sign = $this->getSign();
+               $invertedSign = ( $sign === '+' ? '-' : '+' );
+
+               $inverseDigits = $invertedSign . substr( $this->value, 1 );
+               return new DecimalValue( $inverseDigits );
+       }
+
+       /**
         * Returns the integer part of the value, that is, the part before the 
decimal point,
         * without the sign.
         *
diff --git a/DataValuesCommon/tests/DataValues/DecimalMathTest.php 
b/DataValuesCommon/tests/DataValues/DecimalMathTest.php
new file mode 100644
index 0000000..4156b93
--- /dev/null
+++ b/DataValuesCommon/tests/DataValues/DecimalMathTest.php
@@ -0,0 +1,230 @@
+<?php
+
+namespace DataValues\Tests;
+
+use DataValues\DecimalMath;
+use DataValues\DecimalValue;
+
+/**
+ * @covers DataValues\DecimalMathTest
+ *
+ * @since 0.1
+ *
+ * @group DataValue
+ * @group DataValueExtensions
+ *
+ * @licence GNU GPL v2+
+ *
+ * @author Daniel Kinzler
+ */
+class DecimalMathTest extends \PHPUnit_Framework_TestCase {
+
+       /**
+        * @dataProvider bumpProvider
+        *
+        * @since 0.1
+        */
+       public function testBump( DecimalValue $value, $expected ) {
+               $math = new DecimalMath();
+               $actual = $math->bump( $value );
+               $this->assertSame( $expected, $actual->getValue() );
+       }
+
+       public function bumpProvider() {
+               return array(
+                       array( new DecimalValue(  '+0' ),   '+1' ),
+                       array( new DecimalValue(  '-0' ),   '+1' ),
+                       array( new DecimalValue(  '+0.0' ), '+0.1' ),
+                       array( new DecimalValue(  '-0.0' ), '+0.1' ),
+                       array( new DecimalValue(  '+1' ),   '+2' ),
+                       array( new DecimalValue(  '-1' ),   '-2' ),
+                       array( new DecimalValue( '+10' ),  '+11' ),
+                       array( new DecimalValue( '-10' ),  '-11' ),
+                       array( new DecimalValue(  '+9' ),  '+10' ),
+                       array( new DecimalValue(  '-9' ),  '-10' ),
+                       array( new DecimalValue( '+0.01' ), '+0.02' ),
+                       array( new DecimalValue( '-0.01' ), '-0.02' ),
+                       array( new DecimalValue( '+0.09' ), '+0.10' ),
+                       array( new DecimalValue( '-0.09' ), '-0.10' ),
+                       array( new DecimalValue( '+0.9' ),  '+1.0' ),
+                       array( new DecimalValue( '-0.9' ),  '-1.0' ),
+               );
+       }
+
+       /**
+        * @dataProvider slumpProvider
+        *
+        * @since 0.1
+        */
+       public function testSlump( DecimalValue $value, $expected ) {
+               $math = new DecimalMath();
+               $actual = $math->slump( $value );
+               $this->assertSame( $expected, $actual->getValue() );
+       }
+
+       public function slumpProvider() {
+               return array(
+                       array( new DecimalValue(  '+0' ),    '-1' ),
+                       array( new DecimalValue(  '-0' ),    '-1' ),
+                       array( new DecimalValue(  '+0.0' ),  '-0.1' ),
+                       array( new DecimalValue(  '-0.0' ),  '-0.1' ),
+                       array( new DecimalValue(  '+0.00' ),  '-0.01' ),
+                       array( new DecimalValue(  '-0.00' ),  '-0.01' ),
+                       array( new DecimalValue(  '+1' ),    '+0' ),
+                       array( new DecimalValue(  '-1' ),    '+0' ),
+                       array( new DecimalValue(  '+1.0' ),  '+0.9' ),
+                       array( new DecimalValue(  '-1.0' ),  '-0.9' ),
+                       array( new DecimalValue(  '+0.1' ),  '+0.0' ),
+                       array( new DecimalValue(  '-0.1' ),  '+0.0' ), // zero 
is always normalized to be positive
+                       array( new DecimalValue(  '+0.01' ), '+0.00' ),
+                       array( new DecimalValue(  '-0.01' ), '+0.00' ), // zero 
is always normalized to be positive
+                       array( new DecimalValue( '+12' ),   '+11' ),
+                       array( new DecimalValue( '-12' ),   '-11' ),
+                       array( new DecimalValue( '+10' ),    '+9' ),
+                       array( new DecimalValue( '-10' ),    '-9' ),
+                       array( new DecimalValue('+100' ),   '+99' ),
+                       array( new DecimalValue('-100' ),   '-99' ),
+                       array( new DecimalValue(  '+0.02' ), '+0.01' ),
+                       array( new DecimalValue(  '-0.02' ), '-0.01' ),
+                       array( new DecimalValue(  '+0.10' ), '+0.09' ),
+                       array( new DecimalValue(  '-0.10' ), '-0.09' ),
+               );
+       }
+
+       /**
+        * @dataProvider productProvider
+        */
+       public function testProduct( DecimalValue $a, DecimalValue $b, $value ) 
{
+               $math = new DecimalMath();
+
+               $actual = $math->product( $a, $b );
+               $this->assertEquals( $value, $actual->getValue() );
+
+               $actual = $math->product( $b, $a );
+               $this->assertEquals( $value, $actual->getValue() );
+       }
+
+       public function productProvider() {
+               return array(
+                       array( new DecimalValue(  '+0'  ), new DecimalValue(  
'+0'  ), '+0' ),
+                       array( new DecimalValue(  '+0'  ), new DecimalValue(  
'+1'  ), '+0' ),
+                       array( new DecimalValue(  '+0'  ), new DecimalValue(  
'+2'  ), '+0' ),
+
+                       array( new DecimalValue(  '+1'  ), new DecimalValue(  
'+0'  ), '+0' ),
+                       array( new DecimalValue(  '+1'  ), new DecimalValue(  
'+1'  ), '+1' ),
+                       array( new DecimalValue(  '+1'  ), new DecimalValue(  
'+2'  ), '+2' ),
+
+                       array( new DecimalValue(  '+2'  ), new DecimalValue(  
'+0'  ), '+0' ),
+                       array( new DecimalValue(  '+2'  ), new DecimalValue(  
'+1'  ), '+2' ),
+                       array( new DecimalValue(  '+2'  ), new DecimalValue(  
'+2'  ), '+4' ),
+
+                       array( new DecimalValue(  '+0.5'  ), new DecimalValue(  
'+0'  ), '+0' ),
+                       array( new DecimalValue(  '+0.5'  ), new DecimalValue(  
'+1'  ), '+0.5' ),
+                       array( new DecimalValue(  '+0.5'  ), new DecimalValue(  
'+2'  ), '+1' ),
+               );
+       }
+
+       /**
+        * @dataProvider sumProvider
+        */
+       public function testSum( DecimalValue $a, DecimalValue $b, $value ) {
+               $math = new DecimalMath();
+
+               $actual = $math->sum( $a, $b );
+               $this->assertEquals( $value, $actual->getValue() );
+
+               $actual = $math->sum( $b, $a );
+               $this->assertEquals( $value, $actual->getValue() );
+       }
+
+       public function sumProvider() {
+               return array(
+                       array( new DecimalValue(  '+0'  ), new DecimalValue(  
'+0'  ), '+0' ),
+                       array( new DecimalValue(  '+0'  ), new DecimalValue(  
'+1'  ), '+1' ),
+                       array( new DecimalValue(  '+0'  ), new DecimalValue(  
'+2'  ), '+2' ),
+
+                       array( new DecimalValue(  '+2'  ), new DecimalValue(  
'+0'  ), '+2' ),
+                       array( new DecimalValue(  '+2'  ), new DecimalValue(  
'+1'  ), '+3' ),
+                       array( new DecimalValue(  '+2'  ), new DecimalValue(  
'+2'  ), '+4' ),
+
+                       array( new DecimalValue(  '+0.5'  ), new DecimalValue(  
'+0'  ),  '+0.5' ),
+                       array( new DecimalValue(  '+0.5'  ), new DecimalValue(  
'+0.5' ), '+1.0' ),
+                       array( new DecimalValue(  '+0.5'  ), new DecimalValue(  
'+2'  ),  '+2.5' ),
+               );
+       }
+
+       /**
+        * @dataProvider roundProvider
+        *
+        * @since 0.1
+        */
+       public function testRound( DecimalValue $value, $digits, $expected ) {
+               $math = new DecimalMath();
+
+               $actual = $math->round( $value, $digits );
+               $this->assertSame( $expected, $actual->getValue() );
+       }
+
+       public function roundProvider() {
+               $argLists = array();
+
+               //NOTE: Rounding is applied using the "round half away from 
zero" logic.
+
+               $argLists[] = array( new DecimalValue( '+0' ), 1, '+0' );
+               $argLists[] = array( new DecimalValue( '+0' ), 2, '+0' );
+               $argLists[] = array( new DecimalValue( '+0.0' ), 1, '+0' );
+               $argLists[] = array( new DecimalValue( '+0.0' ), 2, '+0' );
+               $argLists[] = array( new DecimalValue( '+0.0' ), 3, '+0.0' );
+
+               $argLists[] = array( new DecimalValue( '-2' ), 1, '-2' );
+               $argLists[] = array( new DecimalValue( '-2' ), 2, '-2' );
+
+               $argLists[] = array( new DecimalValue( '+23' ), 1, '+20' );
+               $argLists[] = array( new DecimalValue( '+23' ), 2, '+23' );
+               $argLists[] = array( new DecimalValue( '+23' ), 3, '+23' );
+
+               $argLists[] = array( new DecimalValue( '-234' ), 1, '-200' );
+               $argLists[] = array( new DecimalValue( '-234' ), 2, '-230' );
+               $argLists[] = array( new DecimalValue( '-234' ), 3, '-234' );
+
+               $argLists[] = array( new DecimalValue( '-2.0' ), 1, '-2' );
+               $argLists[] = array( new DecimalValue( '-2.0' ), 2, '-2' );   
// edge case, may change
+               $argLists[] = array( new DecimalValue( '-2.0' ), 3, '-2.0' );
+               $argLists[] = array( new DecimalValue( '-2.0' ), 4, '-2.0' ); 
// edge case, may change
+
+               $argLists[] = array( new DecimalValue( '-2.000' ), 1, '-2' );
+               $argLists[] = array( new DecimalValue( '-2.000' ), 2, '-2' );
+               $argLists[] = array( new DecimalValue( '-2.000' ), 3, '-2.0' );
+               $argLists[] = array( new DecimalValue( '-2.000' ), 4, '-2.00' );
+
+               $argLists[] = array( new DecimalValue( '+2.5' ), 1, '+3' ); // 
rounded up
+               $argLists[] = array( new DecimalValue( '+2.5' ), 2, '+3' );
+               $argLists[] = array( new DecimalValue( '+2.5' ), 3, '+2.5' );
+               $argLists[] = array( new DecimalValue( '+2.5' ), 4, '+2.5' );
+
+               $argLists[] = array( new DecimalValue( '+2.05' ), 1, '+2' );
+               $argLists[] = array( new DecimalValue( '+2.05' ), 2, '+2' );
+               $argLists[] = array( new DecimalValue( '+2.05' ), 3, '+2.1' ); 
// rounded up
+               $argLists[] = array( new DecimalValue( '+2.05' ), 4, '+2.05' );
+
+               $argLists[] = array( new DecimalValue( '-23.05' ), 1, '-20' );
+               $argLists[] = array( new DecimalValue( '-23.05' ), 2, '-23' );
+               $argLists[] = array( new DecimalValue( '-23.05' ), 3, '-23' ); 
// edge case, may change
+               $argLists[] = array( new DecimalValue( '-23.05' ), 4, '-23.1' 
); // rounded down
+               $argLists[] = array( new DecimalValue( '-23.05' ), 5, '-23.05' 
);
+
+               $argLists[] = array( new DecimalValue( '+9.33' ), 1, '+9' ); // 
no rounding
+               $argLists[] = array( new DecimalValue( '+9.87' ), 1, '+10' ); 
// rounding ripples up
+               $argLists[] = array( new DecimalValue( '+9.87' ), 3, '+9.9' ); 
// rounding ripples up
+               $argLists[] = array( new DecimalValue( '+99' ), 1, '+100' ); // 
rounding ripples up
+               $argLists[] = array( new DecimalValue( '+99' ), 2, '+99' ); // 
rounding ripples up
+
+               $argLists[] = array( new DecimalValue( '-9.33' ), 1, '-9' ); // 
no rounding
+               $argLists[] = array( new DecimalValue( '-9.87' ), 1, '-10' ); 
// rounding ripples down
+               $argLists[] = array( new DecimalValue( '-9.87' ), 3, '-9.9' ); 
// rounding ripples down
+               $argLists[] = array( new DecimalValue( '-99' ), 1, '-100' ); // 
rounding ripples down
+               $argLists[] = array( new DecimalValue( '-99' ), 2, '-99' ); // 
rounding ripples down
+
+               return $argLists;
+       }
+}
diff --git a/DataValuesCommon/tests/DataValues/DecimalValueTest.php 
b/DataValuesCommon/tests/DataValues/DecimalValueTest.php
index 763d93c..4a8e685 100644
--- a/DataValuesCommon/tests/DataValues/DecimalValueTest.php
+++ b/DataValuesCommon/tests/DataValues/DecimalValueTest.php
@@ -9,8 +9,6 @@
  *
  * @since 0.1
  *
- * @ingroup DataValue
- *
  * @group DataValue
  * @group DataValueExtensions
  *
@@ -247,4 +245,48 @@
                        array( new DecimalValue(  '+0.01' ),  '01' ),
                );
        }
-}
+
+       /**
+        * @dataProvider computeComplementProvider
+        *
+        * @since 0.1
+        */
+       public function testInverse( DecimalValue $value, $expected ) {
+               $complement = $value->computeComplement();
+               $this->assertSame( $expected, $complement->getValue() );
+
+               $actual = $complement->computeComplement();
+               $this->assertSame( $value->getValue(), $actual->getValue() );
+       }
+
+       public function computeComplementProvider() {
+               return array(
+                       array( new DecimalValue(   '+0' ),       '+0' ),
+                       array( new DecimalValue(   '+0.00' ),    '+0.00' ),
+                       array( new DecimalValue(   '+1' ),       '-1' ),
+                       array( new DecimalValue( '+100.663' ), '-100.663' ),
+                       array( new DecimalValue(   '-0.001' ),   '+0.001' ),
+               );
+       }
+
+       /**
+        * @dataProvider isZeroProvider
+        *
+        * @since 0.1
+        */
+       public function testIsZero( DecimalValue $value, $expected ) {
+               $actual = $value->isZero();
+               $this->assertSame( $expected, $actual );
+       }
+
+       public function isZeroProvider() {
+               return array(
+                       array( new DecimalValue(  '+0' ),    true ),
+                       array( new DecimalValue(  '-0.00' ), true ),
+
+                       array( new DecimalValue( '+1' ),       false ),
+                       array( new DecimalValue( '+100.663' ), false ),
+                       array( new DecimalValue( '-0.001' ),   false ),
+               );
+       }
+}
\ No newline at end of file

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Idc3006d7e0f5fd635233a3de57b8194f76f4b434
Gerrit-PatchSet: 7
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