Daniel Kinzler has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/97907


Change subject: (bug 57588) Scientific notation for quantities.
......................................................................

(bug 57588) Scientific notation for quantities.

This adds support scientific (exponent) notation to
DecimalParser and QuantityParser.

Change-Id: Iff70e26f46b10eb18b79861cc60dac8e4e15fa25
---
M DataValuesCommon/src/DataValues/DecimalMath.php
M DataValuesCommon/src/ValueParsers/DecimalParser.php
M DataValuesCommon/src/ValueParsers/QuantityParser.php
M DataValuesCommon/tests/DataValues/DecimalMathTest.php
M DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
M DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
6 files changed, 156 insertions(+), 5 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues 
refs/changes/07/97907/1

diff --git a/DataValuesCommon/src/DataValues/DecimalMath.php 
b/DataValuesCommon/src/DataValues/DecimalMath.php
index 9930146..4ec0c6c 100644
--- a/DataValuesCommon/src/DataValues/DecimalMath.php
+++ b/DataValuesCommon/src/DataValues/DecimalMath.php
@@ -482,7 +482,7 @@
                $slumped = substr( $value, 0, $i ) . $slumped;
 
                // strip leading zeros
-               $slumped = preg_replace( '/^([-+])(0+)([0-9]+(\.|$))/', '\1\3', 
$slumped );
+               $slumped = $this->stripLeadingZeros( $slumped );
 
                if ( $slumped === '-0' ) {
                        $slumped = '+0';
@@ -490,4 +490,60 @@
 
                return $slumped;
        }
+
+       /**
+        * @param string $digits
+        *
+        * @return string
+        */
+       protected function stripLeadingZeros( $digits ) {
+               $digits = preg_replace( '/^([-+])(0+)([0-9]+(\.|$))/', '\1\3', 
$digits );
+               return $digits;
+       }
+
+       /**
+        * Shift the decimal point according to the given exponent.
+        *
+        * @param DecimalValue $decimal
+        * @param int $exponent The exponent to apply (digits to shift by). A 
Positive exponent
+        * shifts the decimal point to the right, a negative exponent shifts to 
the left.
+        *
+        * @throws \InvalidArgumentException
+        * @return DecimalValue
+        */
+       public function shift( DecimalValue $decimal, $exponent ) {
+               if ( !is_int( $exponent ) ) {
+                       throw new \InvalidArgumentException( '$exponent must be 
an integer' );
+               }
+
+               if ( $exponent == 0 ) {
+                       return $decimal;
+               }
+
+               $sign = $decimal->getSign();
+               $intPart = $decimal->getIntegerPart();
+               $fractPart = $decimal->getFractionalPart();
+
+               if ( $exponent < 0 ) {
+                       // shift left
+                       if ( -$exponent < strlen( $intPart ) ) {
+                               $intPart = substr( $intPart, 0, -$exponent ) . 
'.' . substr( $intPart, -$exponent );
+                       } else {
+                               $intPart = '0.' . str_pad( $intPart, 
-$exponent, '0', STR_PAD_LEFT );
+                       }
+               } else {
+                       // shift right
+                       if ( $exponent < strlen( $fractPart ) ) {
+                               $fractPart = substr( $fractPart, 0, $exponent ) 
. '.' . substr( $fractPart, $exponent );
+                       } else {
+                               $fractPart = str_pad( $fractPart, $exponent, 
'0', STR_PAD_RIGHT );
+                       }
+               }
+
+               // assemble result
+               $digits = $sign . $intPart . $fractPart;
+               $digits = $this->stripLeadingZeros( $digits );
+
+               return new DecimalValue( $digits );
+       }
 }
diff --git a/DataValuesCommon/src/ValueParsers/DecimalParser.php 
b/DataValuesCommon/src/ValueParsers/DecimalParser.php
index 5d8e548..bc72ed8 100644
--- a/DataValuesCommon/src/ValueParsers/DecimalParser.php
+++ b/DataValuesCommon/src/ValueParsers/DecimalParser.php
@@ -2,6 +2,7 @@
 
 namespace ValueParsers;
 
+use DataValues\DecimalMath;
 use DataValues\DecimalValue;
 use DataValues\IllegalValueException;
 
@@ -16,13 +17,29 @@
 class DecimalParser extends StringValueParser {
 
        /**
+        * @var DecimalMath
+        */
+       private $math;
+
+       /**
+        * @return DecimalMath
+        */
+       private function getMath() {
+               if ( $this->math === null ) {
+                       $this->math = new DecimalMath();
+               }
+
+               return $this->math;
+       }
+
+       /**
         * Creates a DecimalValue from a given string.
         *
         * The decimal notation for the value is based on ISO 31-0, with some 
modifications:
         * - the decimal separator is '.' (period). Comma is not used anywhere.
         * - leading and trailing as well as any internal whitespace is ignored
         * - the following characters are ignored: comma (","), apostrophe 
("'").
-        * - scientific (exponential) notation is not used.
+        * - scientific (exponential) notation is supported using the pattern 
/e[-+][0-9]+/
         * - the number may start (or end) with a decimal point.
         * - leading zeroes are stripped, except directly before the decimal 
point
         * - trailing zeroes are stripped, except directly after the decimal 
point
@@ -38,6 +55,17 @@
         * @throws ParseException
         */
        protected function stringParse( $value ) {
+
+               //handle scientific notation
+               if ( preg_match( '/^(.*)([eE]|x10\^)([-+]?[,\d]+)$/', $value, 
$matches ) ) {
+                       $exponent = $this->normalizeDecimal( $matches[3] );
+                       $exponent = intval( $exponent );
+
+                       $value = $matches[1];
+               } else {
+                       $exponent = 0;
+               }
+
                $value = $this->normalizeDecimal( $value );
 
                if ( $value === '' ) {
@@ -46,6 +74,12 @@
 
                try {
                        $decimal = new DecimalValue( $value );
+
+                       if ( $exponent ) {
+                               $math = $this->getMath();
+                               $decimal = $math->shift( $decimal, $exponent );
+                       }
+
                        return $decimal;
                } catch ( IllegalValueException $ex ) {
                        throw new ParseException( $ex->getMessage() );
@@ -80,5 +114,4 @@
 
                return $number;
        }
-
 }
diff --git a/DataValuesCommon/src/ValueParsers/QuantityParser.php 
b/DataValuesCommon/src/ValueParsers/QuantityParser.php
index 3d1d1b5..611ddfb 100644
--- a/DataValuesCommon/src/ValueParsers/QuantityParser.php
+++ b/DataValuesCommon/src/ValueParsers/QuantityParser.php
@@ -17,7 +17,7 @@
  */
 class QuantityParser extends StringValueParser {
 
-       const NUMBER_PATTERN = 
'(?:[-+]\s*)?(?:[0-9,\'`]+\.[0-9,\'`]*|\.?[0-9,\'`]+)?';
+       const NUMBER_PATTERN = 
'(?:[-+]\s*)?(?:[0-9,\'`]+\.[0-9,\'`]*|\.?[0-9,\'`]+)(?:[eE][-+]?[0-9,\'`]+)?';
 
        const UNIT_PATTERN = '[a-zA-ZµåÅöÖ°%][-.a-zA-Z0-9åÅöÖ°%²³^]*';
 
@@ -105,7 +105,7 @@
                $pattern = '@^'
                        . '\s*(' . self::NUMBER_PATTERN . ')' // $1: amount
                        . '\s*(?:'
-                               . '([!~])'  // $2: '!' for "exact", '~' for 
"approx", or nothing
+                               . '([~!])'  // $2: '!' for "exact", '~' for 
"approx", or nothing
                                . '|(?:\+/?-|±)\s*(' . self::NUMBER_PATTERN . 
')' // $3: plus/minus offset (uncertainty margin)
                                . '|' // or nothing
                        . ')'
diff --git a/DataValuesCommon/tests/DataValues/DecimalMathTest.php 
b/DataValuesCommon/tests/DataValues/DecimalMathTest.php
index 4ca9c9c..f9078e8 100644
--- a/DataValuesCommon/tests/DataValues/DecimalMathTest.php
+++ b/DataValuesCommon/tests/DataValues/DecimalMathTest.php
@@ -387,4 +387,55 @@
 
                return $argLists;
        }
+
+       /**
+        * @dataProvider shiftProvider
+        *
+        * @param DecimalValue $value
+        * @param $exponent
+        * @param $expected
+        */
+       public function testShift( DecimalValue $value, $exponent, $expected ) {
+               $math = new DecimalMath();
+
+               $actual = $math->shift( $value, $exponent );
+               $this->assertEquals( $expected, $actual->getValue() );
+       }
+
+       public function shiftProvider() {
+               $argLists = array();
+
+               $argLists[] = array( new DecimalValue( '+0' ), 0, '+0' );
+               $argLists[] = array( new DecimalValue( '+0' ), 1, '+0' );
+               $argLists[] = array( new DecimalValue( '+0' ), 2, '+0' );
+               $argLists[] = array( new DecimalValue( '+0' ), -1, '+0.0' );
+               $argLists[] = array( new DecimalValue( '+0' ), -2, '+0.00' );
+
+               $argLists[] = array( new DecimalValue( '+0.0' ), 0, '+0.0' );
+               $argLists[] = array( new DecimalValue( '+0.0' ), 1, '+0' );
+               $argLists[] = array( new DecimalValue( '+0.0' ), 2, '+0' );
+               $argLists[] = array( new DecimalValue( '+0.0' ), -1, '+0.00' );
+               $argLists[] = array( new DecimalValue( '+0.0' ), -2, '+0.000' );
+
+               $argLists[] = array( new DecimalValue( '-25' ), 0, '-25' );
+               $argLists[] = array( new DecimalValue( '-25' ), 1, '-250' );
+               $argLists[] = array( new DecimalValue( '-25' ), 2, '-2500' );
+               $argLists[] = array( new DecimalValue( '-25' ), -1, '-2.5' );
+               $argLists[] = array( new DecimalValue( '-25' ), -2, '-0.25' );
+               $argLists[] = array( new DecimalValue( '-25' ), -3, '-0.025' );
+               $argLists[] = array( new DecimalValue( '-25' ), -4, '-0.0025' );
+
+               $argLists[] = array( new DecimalValue( '-2.5' ), 0, '-2.5' );
+               $argLists[] = array( new DecimalValue( '-2.5' ), 1, '-25' );
+               $argLists[] = array( new DecimalValue( '-2.5' ), 2, '-250' );
+               $argLists[] = array( new DecimalValue( '-2.5' ), -1, '-0.25' );
+               $argLists[] = array( new DecimalValue( '-2.5' ), -2, '-0.025' );
+               $argLists[] = array( new DecimalValue( '-2.5' ), -3, '-0.0025' 
);
+
+               $argLists[] = array( new DecimalValue( '+5' ), -4, '+0.0005' );
+               $argLists[] = array( new DecimalValue( '+5.0' ), -4, '+0.00050' 
);
+               $argLists[] = array( new DecimalValue( '+5.00' ), -4, 
'+0.000500' );
+
+               return $argLists;
+       }
 }
diff --git a/DataValuesCommon/tests/ValueParsers/DecimalParserTest.php 
b/DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
index bcca87d..5ce83e1 100644
--- a/DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
+++ b/DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
@@ -45,6 +45,10 @@
                        ',3,' => 3,
                        '2.125' => 2.125,
                        '2.1250' => '+2.1250',
+                       '2.1250e0' => '+2.1250',
+                       '2.1250e3' => '+2125.0',
+                       '2.1250e+3' => '+2125.0',
+                       '2.1250e-2' => '+0.021250',
                        ' 5 ' => 5,
                        '100,000' => 100000,
                        '100 000' => 100000,
diff --git a/DataValuesCommon/tests/ValueParsers/QuantityParserTest.php 
b/DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
index 3b2082a..fcda9e4 100644
--- a/DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
+++ b/DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
@@ -50,6 +50,13 @@
                        '100,003' => QuantityValue::newFromNumber( 100003, '1', 
100004, 100002 ),
                        '100\'003' => QuantityValue::newFromNumber( 100003, 
'1', 100004, 100002 ),
 
+                       '1.4e-2' => QuantityValue::newFromNumber( '+0.014', 
'1', '+0.015', '+0.013' ),
+                       '1.4e3' => QuantityValue::newFromNumber( '+1400', '1', 
'+1401', '+1399' ),
+                       '1.4e3!m' => QuantityValue::newFromNumber( '+1400', 
'm', '+1400', '+1400' ),
+                       '1.4e3m2' => QuantityValue::newFromNumber( '+1400', 
'm2', '+1401', '+1399' ),
+                       '1.4ev' => QuantityValue::newFromNumber( '+1.4', 'ev', 
'+1.5', '+1.3' ),
+                       '1.4e' => QuantityValue::newFromNumber( '+1.4', 'e', 
'+1.5', '+1.3' ),
+
                        // precision
                        '0!' => QuantityValue::newFromNumber( 0, '1', 0, 0 ),
                        '10.003!' => QuantityValue::newFromNumber( '+10.003', 
'1', '+10.003', '+10.003' ),

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Iff70e26f46b10eb18b79861cc60dac8e4e15fa25
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <daniel.kinz...@wikimedia.de>

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

Reply via email to