Hello community,

here is the log from the commit of package ghc-scientific for openSUSE:Factory 
checked in at 2018-05-30 12:13:00
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ghc-scientific (Old)
 and      /work/SRC/openSUSE:Factory/.ghc-scientific.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ghc-scientific"

Wed May 30 12:13:00 2018 rev:18 rq:607878 version:0.3.6.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/ghc-scientific/ghc-scientific.changes    
2017-07-27 11:12:10.989988799 +0200
+++ /work/SRC/openSUSE:Factory/.ghc-scientific.new/ghc-scientific.changes       
2018-05-30 12:27:07.338660845 +0200
@@ -1,0 +2,51 @@
+Mon May 14 17:02:11 UTC 2018 - [email protected]
+
+- Update scientific to version 0.3.6.2.
+  * Due to a regression introduced in 0.3.4.14 the RealFrac methods
+    and floatingOrInteger became vulnerable to a space blowup when
+    applied to scientifics with huge exponents. This has now been
+    fixed again.
+
+  * Fix build on GHC < 8.
+
+  * Make the methods of the Hashable, Eq and Ord instances safe to
+  use when applied to scientific numbers coming from untrusted
+  sources. Previously these methods first converted their arguments
+  to Rational before applying the operation. This is unsafe because
+  converting a Scientific to a Rational could fill up all space and
+  crash your program when the Scientific has a huge base10Exponent.
+
+  Do note that the hash computation of the Hashable Scientific
+  instance has been changed because of this improvement!
+
+  Thanks to Tom Sydney Kerckhove (@NorfairKing) for pushing me to
+  fix this.
+
+  * fromRational :: Rational -> Scientific now throws an error
+  instead of diverging when applied to a repeating decimal. This
+  does mean it will consume space linear in the number of digits of
+  the resulting scientific. This makes "fromRational" and the other
+  Fractional methods "recip" and "/" a bit safer to use.
+
+  * To get the old unsafe but more efficient behaviour the following
+  function was added: unsafeFromRational :: Rational -> Scientific.
+
+  * Add alternatives for fromRationalRepetend:
+
+    fromRationalRepetendLimited
+        :: Int -- ^ limit
+        -> Rational
+        -> Either (Scientific, Rational)
+                (Scientific, Maybe Int)
+
+    and:
+
+    fromRationalRepetendUnlimited
+        :: Rational -> (Scientific, Maybe Int)
+
+    Thanks to Ian Jeffries (@seagreen) for the idea.
+
+  * Dropped upper version bounds of dependencies
+    because it's to much work to maintain.
+
+-------------------------------------------------------------------

Old:
----
  scientific-0.3.5.1.tar.gz

New:
----
  scientific-0.3.6.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ghc-scientific.spec ++++++
--- /var/tmp/diff_new_pack.22Ehxm/_old  2018-05-30 12:27:08.882607507 +0200
+++ /var/tmp/diff_new_pack.22Ehxm/_new  2018-05-30 12:27:08.886607369 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package ghc-scientific
 #
-# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,12 +19,12 @@
 %global pkg_name scientific
 %bcond_with tests
 Name:           ghc-%{pkg_name}
-Version:        0.3.5.1
+Version:        0.3.6.2
 Release:        0
 Summary:        Numbers represented using scientific notation
 License:        BSD-3-Clause
-Group:          Development/Languages/Other
-Url:            https://hackage.haskell.org/package/%{pkg_name}
+Group:          Development/Libraries/Haskell
+URL:            https://hackage.haskell.org/package/%{pkg_name}
 Source0:        
https://hackage.haskell.org/package/%{pkg_name}-%{version}/%{pkg_name}-%{version}.tar.gz
 BuildRequires:  ghc-Cabal-devel
 BuildRequires:  ghc-binary-devel
@@ -36,7 +36,6 @@
 BuildRequires:  ghc-primitive-devel
 BuildRequires:  ghc-rpm-macros
 BuildRequires:  ghc-text-devel
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 %if %{with tests}
 BuildRequires:  ghc-QuickCheck-devel
 BuildRequires:  ghc-smallcheck-devel
@@ -48,7 +47,7 @@
 %endif
 
 %description
-'Data.Scientific' provides the number type 'Scientific'. Scientific numbers are
+"Data.Scientific" provides the number type 'Scientific'. Scientific numbers are
 arbitrary precision and space efficient. They are represented using
 <http://en.wikipedia.org/wiki/Scientific_notation scientific notation>.
 The implementation uses a coefficient 'c :: 'Integer'' and a base-10 exponent
@@ -71,7 +70,7 @@
 '1e1000000000 :: 'Rational'' will fill up all space and crash your program.
 Scientific works as expected:
 
-> > read "1e1000000000" :: Scientific > 1.0e1000000000
+>>> read "1e1000000000" :: Scientific 1.0e1000000000
 
 * Also, the space usage of converting scientific numbers with huge exponents to
 ''Integral's' (like: 'Int') or ''RealFloat's' (like: 'Double' or 'Float') will
@@ -79,7 +78,7 @@
 
 %package devel
 Summary:        Haskell %{pkg_name} library development files
-Group:          Development/Libraries/Other
+Group:          Development/Libraries/Haskell
 Requires:       %{name} = %{version}-%{release}
 Requires:       ghc-compiler = %{ghc_version}
 Requires(post): ghc-compiler = %{ghc_version}
@@ -107,11 +106,9 @@
 %ghc_pkg_recache
 
 %files -f %{name}.files
-%defattr(-,root,root,-)
-%doc LICENSE
+%license LICENSE
 
 %files devel -f %{name}-devel.files
-%defattr(-,root,root,-)
 %doc changelog
 
 %changelog

++++++ scientific-0.3.5.1.tar.gz -> scientific-0.3.6.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/scientific-0.3.5.1/changelog 
new/scientific-0.3.6.2/changelog
--- old/scientific-0.3.5.1/changelog    2017-07-08 00:06:03.000000000 +0200
+++ new/scientific-0.3.6.2/changelog    2018-05-08 01:38:26.000000000 +0200
@@ -1,3 +1,58 @@
+0.3.6.2
+       * Due to a regression introduced in 0.3.4.14 the RealFrac methods
+       and floatingOrInteger became vulnerable to a space blowup when
+       applied to scientifics with huge exponents. This has now been
+       fixed again.
+
+0.3.6.1
+       * Fix build on GHC < 8.
+
+0.3.6.0
+       * Make the methods of the Hashable, Eq and Ord instances safe to
+       use when applied to scientific numbers coming from untrusted
+       sources. Previously these methods first converted their arguments
+       to Rational before applying the operation. This is unsafe because
+       converting a Scientific to a Rational could fill up all space and
+       crash your program when the Scientific has a huge base10Exponent.
+
+       Do note that the hash computation of the Hashable Scientific
+       instance has been changed because of this improvement!
+
+       Thanks to Tom Sydney Kerckhove (@NorfairKing) for pushing me to
+       fix this.
+
+       * fromRational :: Rational -> Scientific now throws an error
+       instead of diverging when applied to a repeating decimal. This
+       does mean it will consume space linear in the number of digits of
+       the resulting scientific. This makes "fromRational" and the other
+       Fractional methods "recip" and "/" a bit safer to use.
+
+       * To get the old unsafe but more efficient behaviour the following
+       function was added: unsafeFromRational :: Rational -> Scientific.
+
+       * Add alternatives for fromRationalRepetend:
+
+         fromRationalRepetendLimited
+             :: Int -- ^ limit
+             -> Rational
+             -> Either (Scientific, Rational)
+                       (Scientific, Maybe Int)
+
+         and:
+
+         fromRationalRepetendUnlimited
+             :: Rational -> (Scientific, Maybe Int)
+
+         Thanks to Ian Jeffries (@seagreen) for the idea.
+
+0.3.5.3
+       * Dropped upper version bounds of dependencies
+         because it's to much work to maintain.
+
+0.3.5.2
+       * Remove unused ghc-prim dependency.
+       * Added unit tests for read and scientificP
+
 0.3.5.1
        * Replace use of Vector from vector with Array from primitive.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/scientific-0.3.5.1/scientific.cabal 
new/scientific-0.3.6.2/scientific.cabal
--- old/scientific-0.3.5.1/scientific.cabal     2017-07-08 00:06:03.000000000 
+0200
+++ new/scientific-0.3.6.2/scientific.cabal     2018-05-08 01:38:26.000000000 
+0200
@@ -1,8 +1,8 @@
 name:                scientific
-version:             0.3.5.1
+version:             0.3.6.2
 synopsis:            Numbers represented using scientific notation
 description:
-  @Data.Scientific@ provides the number type 'Scientific'. Scientific numbers 
are
+  "Data.Scientific" provides the number type 'Scientific'. Scientific numbers 
are
   arbitrary precision and space efficient. They are represented using
   <http://en.wikipedia.org/wiki/Scientific_notation scientific notation>.
   The implementation uses a coefficient @c :: 'Integer'@ and a base-10 exponent
@@ -25,8 +25,8 @@
   @1e1000000000 :: 'Rational'@ will fill up all space and crash your
   program. Scientific works as expected:
   .
-   > > read "1e1000000000" :: Scientific
-   > 1.0e1000000000
+  >>> read "1e1000000000" :: Scientific
+  1.0e1000000000
   .
   * Also, the space usage of converting scientific numbers with huge exponents 
to
   @'Integral's@ (like: 'Int') or @'RealFloat's@ (like: 'Double' or 'Float')
@@ -45,6 +45,13 @@
 extra-source-files:
   changelog
 
+Tested-With: GHC == 7.6.3
+           , GHC == 7.8.4
+           , GHC == 7.10.3
+           , GHC == 8.0.2
+           , GHC == 8.2.2
+           , GHC == 8.4.1
+
 source-repository head
   type:     git
   location: git://github.com/basvandijk/scientific.git
@@ -66,21 +73,20 @@
                        Utils
   other-extensions:    DeriveDataTypeable, BangPatterns
   ghc-options:         -Wall
-  build-depends:       base        >= 4.3   && < 4.11
-                     , ghc-prim
-                     , integer-logarithms >= 1 && <1.1
-                     , deepseq     >= 1.3   && < 1.5
-                     , text        >= 0.8   && < 1.3
-                     , hashable    >= 1.1.2 && < 1.3
-                     , primitive   >= 0.1   && < 0.7
-                     , containers  >= 0.1   && < 0.6
-                     , binary      >= 0.4.1 && < 0.9
+  build-depends:       base        >= 4.3 && < 5
+                     , integer-logarithms >= 1
+                     , deepseq     >= 1.3
+                     , text        >= 0.8
+                     , hashable    >= 1.1.2
+                     , primitive   >= 0.1
+                     , containers  >= 0.1
+                     , binary      >= 0.4.1
 
   if flag(bytestring-builder)
       build-depends: bytestring         >= 0.9    && < 0.10.4
                    , bytestring-builder >= 0.10.4 && < 0.11
   else
-      build-depends: bytestring         >= 0.10.4 && < 0.11
+      build-depends: bytestring         >= 0.10.4
 
   if flag(integer-simple)
       build-depends: integer-simple
@@ -98,22 +104,22 @@
   ghc-options:      -Wall
 
   build-depends: scientific
-               , base             >= 4.3   && < 4.11
-               , binary           >= 0.4.1 && < 0.9
-               , tasty            >= 0.5   && < 0.12
-               , tasty-ant-xml    >= 1.0   && < 1.2
-               , tasty-hunit      >= 0.8   && < 0.10
-               , tasty-smallcheck >= 0.2   && < 0.9
-               , tasty-quickcheck >= 0.8   && < 0.10
-               , smallcheck       >= 1.0   && < 1.2
-               , QuickCheck       >= 2.5   && < 2.11
-               , text             >= 0.8   && < 1.3
+               , base             >= 4.3 && < 5
+               , binary           >= 0.4.1
+               , tasty            >= 0.5
+               , tasty-ant-xml    >= 1.0
+               , tasty-hunit      >= 0.8
+               , tasty-smallcheck >= 0.2
+               , tasty-quickcheck >= 0.8
+               , smallcheck       >= 1.0
+               , QuickCheck       >= 2.5
+               , text             >= 0.8
 
   if flag(bytestring-builder)
       build-depends: bytestring         >= 0.9    && < 0.10.4
                    , bytestring-builder >= 0.10.4 && < 0.11
   else
-      build-depends: bytestring         >= 0.10.4 && < 0.11
+      build-depends: bytestring         >= 0.10.4
 
 benchmark bench-scientific
   type:             exitcode-stdio-1.0
@@ -122,5 +128,5 @@
   default-language: Haskell2010
   ghc-options:      -O2
   build-depends:    scientific
-                  , base        >= 4.3   && < 4.11
-                  , criterion   >= 0.5   && < 1.3
+                  , base        >= 4.3 && < 5
+                  , criterion   >= 0.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/scientific-0.3.5.1/src/Data/Scientific.hs 
new/scientific-0.3.6.2/src/Data/Scientific.hs
--- old/scientific-0.3.5.1/src/Data/Scientific.hs       2017-07-08 
00:06:03.000000000 +0200
+++ new/scientific-0.3.6.2/src/Data/Scientific.hs       2018-05-08 
01:38:26.000000000 +0200
@@ -22,6 +22,11 @@
 -- aren't truly arbitrary precision. I intend to change the type of the 
exponent
 -- to 'Integer' in a future release.
 --
+-- /WARNING:/ Although @Scientific@ has instances for all numeric classes the
+-- methods should be used with caution when applied to scientific numbers 
coming
+-- from untrusted sources. See the warnings of the instances belonging to
+-- 'Scientific'.
+--
 -- The main application of 'Scientific' is to be used as the target of parsing
 -- arbitrary precision numbers coming from an untrusted source. The advantages
 -- over using 'Rational' for this are that:
@@ -41,13 +46,6 @@
 -- to @'Integral's@ (like: 'Int') or @'RealFloat's@ (like: 'Double' or 'Float')
 -- will always be bounded by the target type.
 --
--- /WARNING:/ Although @Scientific@ is an instance of 'Fractional', the methods
--- are only partially defined! Specifically 'recip' and '/' will diverge
--- (i.e. loop and consume all space) when their outputs have an infinite 
decimal
--- expansion. 'fromRational' will diverge when the input 'Rational' has an
--- infinite decimal expansion. Consider using 'fromRationalRepetend' for these
--- rationals which will detect the repetition and indicate where it starts.
---
 -- This module is designed to be imported qualified:
 --
 -- @import Data.Scientific as Scientific@
@@ -66,8 +64,14 @@
     , isInteger
 
       -- * Conversions
+      -- ** Rational
+    , unsafeFromRational
     , fromRationalRepetend
+    , fromRationalRepetendLimited
+    , fromRationalRepetendUnlimited
     , toRationalRepetend
+
+      -- ** Floating & integer
     , floatingOrInteger
     , toRealFloat
     , toBoundedRealFloat
@@ -99,7 +103,6 @@
 import           Data.Binary                  (Binary, get, put)
 import           Data.Char                    (intToDigit, ord)
 import           Data.Data                    (Data)
-import           Data.Function                (on)
 import           Data.Hashable                (Hashable(..))
 import           Data.Int                     (Int8, Int16, Int32, Int64)
 import qualified Data.Map            as M     (Map, empty, insert, lookup)
@@ -116,6 +119,10 @@
 import           Text.ParserCombinators.ReadP     ( ReadP )
 import           Data.Text.Lazy.Builder.RealFloat (FPFormat(..))
 
+#if !MIN_VERSION_base(4,9,0)
+import           Control.Applicative          ((*>))
+#endif
+
 #if !MIN_VERSION_base(4,8,0)
 import           Data.Functor                 ((<$>))
 import           Data.Word                    (Word)
@@ -174,42 +181,63 @@
 instance NFData Scientific where
     rnf (Scientific _ _) = ()
 
+-- | A hash can be safely calculated from a @Scientific@. No magnitude @10^e@ 
is
+-- calculated so there's no risk of a blowup in space or time when hashing
+-- scientific numbers coming from untrusted sources.
 instance Hashable Scientific where
-    hashWithSalt salt = hashWithSalt salt . toRational
+    hashWithSalt salt s = salt `hashWithSalt` c `hashWithSalt` e
+      where
+        Scientific c e = normalize s
 
+-- | Note that in the future I intend to change the type of the 
'base10Exponent'
+-- from @Int@ to @Integer@. To be forward compatible the @Binary@ instance
+-- already encodes the exponent as 'Integer'.
 instance Binary Scientific where
-    put (Scientific c e) = do
-      put c
-      -- In the future I intend to change the type of the base10Exponent e from
-      -- Int to Integer. To support backward compatability I already convert e
-      -- to Integer here:
-      put $ toInteger e
-
+    put (Scientific c e) = put c *> put (toInteger e)
     get = Scientific <$> get <*> (fromInteger <$> get)
 
+-- | Scientific numbers can be safely compared for equality. No magnitude 
@10^e@
+-- is calculated so there's no risk of a blowup in space or time when comparing
+-- scientific numbers coming from untrusted sources.
 instance Eq Scientific where
-    (==) = (==) `on` toRational
-    {-# INLINABLE (==) #-}
-
-    (/=) = (/=) `on` toRational
-    {-# INLINABLE (/=) #-}
+    s1 == s2 = c1 == c2 && e1 == e2
+      where
+        Scientific c1 e1 = normalize s1
+        Scientific c2 e2 = normalize s2
 
+-- | Scientific numbers can be safely compared for ordering. No magnitude 
@10^e@
+-- is calculated so there's no risk of a blowup in space or time when comparing
+-- scientific numbers coming from untrusted sources.
 instance Ord Scientific where
-    (<) = (<) `on` toRational
-    {-# INLINABLE (<) #-}
-
-    (<=) = (<=) `on` toRational
-    {-# INLINABLE (<=) #-}
+    compare s1 s2
+        | c1 == c2 && e1 == e2 = EQ
+        | c1 < 0    = if c2 < 0 then cmp (-c2) e2 (-c1) e1 else LT
+        | c1 > 0    = if c2 > 0 then cmp   c1  e1   c2  e2 else GT
+        | otherwise = if c2 > 0 then LT else GT
+      where
+        Scientific c1 e1 = normalize s1
+        Scientific c2 e2 = normalize s2
 
-    (>) = (>) `on` toRational
-    {-# INLINABLE (>) #-}
+        cmp cx ex cy ey
+            | log10sx < log10sy = LT
+            | log10sx > log10sy = GT
+            | d < 0     = if cx <= (cy `quotInteger` magnitude (-d)) then LT 
else GT
+            | d > 0     = if cy >  (cx `quotInteger` magnitude   d)  then LT 
else GT
+            | otherwise = if cx < cy                                 then LT 
else GT
+          where
+            log10sx = log10cx + ex
+            log10sy = log10cy + ey
 
-    (>=) = (>=) `on` toRational
-    {-# INLINABLE (>=) #-}
+            log10cx = integerLog10' cx
+            log10cy = integerLog10' cy
 
-    compare = compare `on` toRational
-    {-# INLINABLE compare #-}
+            d = log10cx - log10cy
 
+-- | /WARNING:/ '+' and '-' compute the 'Integer' magnitude: @10^e@ where @e@ 
is
+-- the difference between the @'base10Exponent's@ of the arguments. If these
+-- methods are applied to arguments which have huge exponents this could fill 
up
+-- all space and crash your program! So don't apply these methods to scientific
+-- numbers coming from untrusted sources. The other methods can be used safely.
 instance Num Scientific where
     Scientific c1 e1 + Scientific c2 e2
        | e1 < e2   = Scientific (c1   + c2*l) e1
@@ -264,12 +292,12 @@
   "realToFrac_toRealFloat_Float"
    realToFrac = toRealFloat :: Scientific -> Float #-}
 
--- | /WARNING:/ 'recip' and '/' will diverge (i.e. loop and consume all space)
--- when their outputs are <https://en.wikipedia.org/wiki/Repeating_decimal 
repeating decimals>.
+-- | /WARNING:/ 'recip' and '/' will throw an error when their outputs are
+-- <https://en.wikipedia.org/wiki/Repeating_decimal repeating decimals>.
 --
--- 'fromRational' will diverge when the input 'Rational' is a repeating 
decimal.
--- Consider using 'fromRationalRepetend' for these rationals which will detect
--- the repetition and indicate where it starts.
+-- 'fromRational' will throw an error when the input 'Rational' is a repeating
+-- decimal.  Consider using 'fromRationalRepetend' for these rationals which
+-- will detect the repetition and indicate where it starts.
 instance Fractional Scientific where
     recip = fromRational . recip . toRational
     {-# INLINABLE recip #-}
@@ -277,23 +305,45 @@
     x / y = fromRational $ toRational x / toRational y
     {-# INLINABLE (/) #-}
 
-    fromRational rational
-        | d == 0    = throw DivideByZero
-        | otherwise = positivize (longDiv 0 0) (numerator rational)
+    fromRational rational =
+        case mbRepetendIx of
+          Nothing -> s
+          Just _ix -> error $
+            "fromRational has been applied to a repeating decimal " ++
+            "which can't be represented as a Scientific! " ++
+            "It's better to avoid performing fractional operations on 
Scientifics " ++
+            "and convert them to other fractional types like Double as early 
as possible."
       where
-        -- Divide the numerator by the denominator using long division.
-        longDiv :: Integer -> Int -> (Integer -> Scientific)
-        longDiv !c !e  0 = Scientific c e
-        longDiv !c !e !n
-                          -- TODO: Use a logarithm here!
-            | n < d     = longDiv (c * 10) (e - 1) (n * 10)
-            | otherwise = case n `quotRemInteger` d of
-                            (#q, r#) -> longDiv (c + q) e r
+        (s, mbRepetendIx) = fromRationalRepetendUnlimited rational
 
-        d = denominator rational
-
--- | Like 'fromRational', this function converts a `Rational` to a `Scientific`
--- but instead of diverging (i.e loop and consume all space) on
+-- | Although 'fromRational' is unsafe because it will throw errors on
+-- <https://en.wikipedia.org/wiki/Repeating_decimal repeating decimals>,
+-- @unsafeFromRational@ is even more unsafe because it will diverge instead 
(i.e
+-- loop and consume all space). Though it will be more efficient because it
+-- doesn't need to consume space linear in the number of digits in the 
resulting
+-- scientific to detect the repetition.
+--
+-- Consider using 'fromRationalRepetend' for these rationals which will detect
+-- the repetition and indicate where it starts.
+unsafeFromRational :: Rational -> Scientific
+unsafeFromRational rational
+    | d == 0    = throw DivideByZero
+    | otherwise = positivize (longDiv 0 0) (numerator rational)
+  where
+    -- Divide the numerator by the denominator using long division.
+    longDiv :: Integer -> Int -> (Integer -> Scientific)
+    longDiv !c !e  0 = Scientific c e
+    longDiv !c !e !n
+                      -- TODO: Use a logarithm here!
+        | n < d     = longDiv (c * 10) (e - 1) (n * 10)
+        | otherwise = case n `quotRemInteger` d of
+                        (#q, r#) -> longDiv (c + q) e r
+
+    d = denominator rational
+
+-- | Like 'fromRational' and 'unsafeFromRational', this function converts a
+-- `Rational` to a `Scientific` but instead of failing or diverging (i.e loop
+-- and consume all space) on
 -- <https://en.wikipedia.org/wiki/Repeating_decimal repeating decimals>
 -- it detects the repeating part, the /repetend/, and returns where it starts.
 --
@@ -335,7 +385,18 @@
     -> Rational
     -> Either (Scientific, Rational)
               (Scientific, Maybe Int)
-fromRationalRepetend mbLimit rational
+fromRationalRepetend mbLimit rational =
+    case mbLimit of
+      Nothing -> Right $ fromRationalRepetendUnlimited rational
+      Just l  -> fromRationalRepetendLimited l rational
+
+-- | Like 'fromRationalRepetend' but always accepts a limit.
+fromRationalRepetendLimited
+    :: Int -- ^ limit
+    -> Rational
+    -> Either (Scientific, Rational)
+              (Scientific, Maybe Int)
+fromRationalRepetendLimited l rational
         | d == 0    = throw DivideByZero
         | num < 0   = case longDiv (-num) of
                         Left  (s, r)  -> Left  (-s, -r)
@@ -345,11 +406,38 @@
         num = numerator rational
 
         longDiv :: Integer -> Either (Scientific, Rational) (Scientific, Maybe 
Int)
-        longDiv n = case mbLimit of
-                      Nothing -> Right $ longDivNoLimit 0 0 M.empty n
-                      Just l  -> longDivWithLimit (-l) n
+        longDiv = longDivWithLimit 0 0 M.empty
+
+        longDivWithLimit
+            :: Integer
+            -> Int
+            -> M.Map Integer Int
+            -> (Integer -> Either (Scientific, Rational)
+                                  (Scientific, Maybe Int))
+        longDivWithLimit !c !e _ns 0 = Right (Scientific c e, Nothing)
+        longDivWithLimit !c !e  ns !n
+            | Just e' <- M.lookup n ns = Right (Scientific c e, Just (-e'))
+            | e <= (-l) = Left (Scientific c e, n % (d * magnitude (-e)))
+            | n < d = let !ns' = M.insert n e ns
+                      in longDivWithLimit (c * 10) (e - 1) ns' (n * 10)
+            | otherwise = case n `quotRemInteger` d of
+                            (#q, r#) -> longDivWithLimit (c + q) e ns r
+
+        d = denominator rational
+
+-- | Like 'fromRationalRepetend' but doesn't accept a limit.
+fromRationalRepetendUnlimited :: Rational -> (Scientific, Maybe Int)
+fromRationalRepetendUnlimited rational
+        | d == 0    = throw DivideByZero
+        | num < 0   = case longDiv (-num) of
+                        (s, mb) -> (-s, mb)
+        | otherwise = longDiv num
+      where
+        num = numerator rational
+
+        longDiv :: Integer -> (Scientific, Maybe Int)
+        longDiv = longDivNoLimit 0 0 M.empty
 
-        -- Divide the numerator by the denominator using long division.
         longDivNoLimit :: Integer
                        -> Int
                        -> M.Map Integer Int
@@ -362,22 +450,6 @@
             | otherwise = case n `quotRemInteger` d of
                             (#q, r#) -> longDivNoLimit (c + q) e ns r
 
-        longDivWithLimit :: Int -> Integer -> Either (Scientific, Rational) 
(Scientific, Maybe Int)
-        longDivWithLimit l = go 0 0 M.empty
-            where
-              go :: Integer
-                 -> Int
-                 -> M.Map Integer Int
-                 -> (Integer -> Either (Scientific, Rational) (Scientific, 
Maybe Int))
-              go !c !e _ns 0 = Right (Scientific c e, Nothing)
-              go !c !e  ns !n
-                  | Just e' <- M.lookup n ns = Right (Scientific c e, Just 
(-e'))
-                  | e <= l    = Left (Scientific c e, n % (d * magnitude (-e)))
-                  | n < d     = let !ns' = M.insert n e ns
-                                in go (c * 10) (e - 1) ns' (n * 10)
-                  | otherwise = case n `quotRemInteger` d of
-                                  (#q, r#) -> go (c + q) e ns r
-
         d = denominator rational
 
 -- |
@@ -393,6 +465,11 @@
 --
 -- * @r < -(base10Exponent s)@
 --
+-- /WARNING:/ @toRationalRepetend@ needs to compute the 'Integer' magnitude:
+-- @10^^n@. Where @n@ is based on the 'base10Exponent` of the scientific. If
+-- applied to a huge exponent this could fill up all space and crash your
+-- program! So don't apply this function to untrusted input.
+--
 -- The formula to convert the @Scientific@ @s@
 -- with a repetend starting at index @r@ is described in the paper:
 -- 
<http://fiziko.bureau42.com/teaching_tidbits/turning_repeating_decimals_into_fractions.pdf
 turning_repeating_decimals_into_fractions.pdf>
@@ -443,6 +520,10 @@
 
     nines = m - 1
 
+-- | /WARNING:/ the methods of the @RealFrac@ instance need to compute the
+-- magnitude @10^e@. If applied to a huge exponent this could take a long
+-- time. Even worse, when the destination type is unbounded (i.e. 'Integer') it
+-- could fill up all space and crash your program!
 instance RealFrac Scientific where
     -- | The function 'properFraction' takes a Scientific number @s@
     -- and returns a pair @(n,f)@ such that @s = n+f@, and:
@@ -566,7 +647,7 @@
 -- | Precondition: the 'Scientific' @s@ needs to be an integer:
 -- @base10Exponent (normalize s) >= 0@
 toIntegral :: (Num a) => Scientific -> a
-toIntegral (Scientific c e) = fromInteger c * fromInteger (magnitude e)
+toIntegral (Scientific c e) = fromInteger c * magnitude e
 {-# INLINE toIntegral #-}
 
 
@@ -603,11 +684,11 @@
 uninitialised = error "Data.Scientific: uninitialised element"
 
 -- | @magnitude e == 10 ^ e@
-magnitude :: Int -> Integer
+magnitude :: Num a => Int -> a
 magnitude e | e < maxExpt = cachedPow10 e
             | otherwise   = cachedPow10 hi * 10 ^ (e - hi)
     where
-      cachedPow10 = Primitive.indexArray expts10
+      cachedPow10 = fromInteger . Primitive.indexArray expts10
 
       hi = maxExpt - 1
 
@@ -754,9 +835,20 @@
 {-# SPECIALIZE toBoundedInteger :: Scientific -> Maybe Word32 #-}
 {-# SPECIALIZE toBoundedInteger :: Scientific -> Maybe Word64 #-}
 
--- | @floatingOrInteger@ determines if the scientific is floating point
--- or integer. In case it's floating-point the scientific is converted
--- to the desired 'RealFloat' using 'toRealFloat'.
+-- | @floatingOrInteger@ determines if the scientific is floating point or
+-- integer.
+--
+-- In case it's floating-point the scientific is converted to the desired
+-- 'RealFloat' using 'toRealFloat' and wrapped in 'Left'.
+--
+-- In case it's integer to scientific is converted to the desired 'Integral' 
and
+-- wrapped in 'Right'.
+--
+-- /WARNING:/ To convert the scientific to an integral the magnitude @10^e@
+-- needs to be computed. If applied to a huge exponent this could take a long
+-- time. Even worse, when the destination type is unbounded (i.e. 'Integer') it
+-- could fill up all space and crash your program! So don't apply this function
+-- to untrusted input but use 'toBoundedInteger' instead.
 --
 -- Also see: 'isFloating' or 'isInteger'.
 floatingOrInteger :: (RealFloat r, Integral i) => Scientific -> Either r i
@@ -885,6 +977,7 @@
 -- Pretty Printing
 ----------------------------------------------------------------------
 
+-- | See 'formatScientific' if you need more control over the rendering.
 instance Show Scientific where
     show s | coefficient s < 0 = '-':showPositive (-s)
            | otherwise         =     showPositive   s
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/scientific-0.3.5.1/test/test.hs 
new/scientific-0.3.6.2/test/test.hs
--- old/scientific-0.3.5.1/test/test.hs 2017-07-08 00:06:03.000000000 +0200
+++ new/scientific-0.3.6.2/test/test.hs 2018-05-08 01:38:26.000000000 +0200
@@ -19,7 +19,7 @@
 import           Data.Scientific                    as Scientific
 import           Test.Tasty
 import           Test.Tasty.Runners.AntXML
-import           Test.Tasty.HUnit                          (testCase, (@?=), 
Assertion)
+import           Test.Tasty.HUnit                          (testCase, (@?=), 
Assertion, assertBool)
 import qualified Test.SmallCheck                    as SC
 import qualified Test.SmallCheck.Series             as SC
 import qualified Test.Tasty.SmallCheck              as SC  (testProperty)
@@ -34,10 +34,60 @@
 import qualified Data.ByteString.Lazy.Char8         as BLC8
 import qualified Data.ByteString.Builder.Scientific as B
 import qualified Data.ByteString.Builder            as B
+import           Text.ParserCombinators.ReadP (readP_to_S)
 
 main :: IO ()
 main = testMain $ testGroup "scientific"
-  [ smallQuick "normalization"
+  [ testGroup "DoS protection"
+    [ testGroup "Eq"
+      [ testCase "1e1000000" $ assertBool "" $
+          (read "1e1000000" :: Scientific) == (read "1e1000000" :: Scientific)
+      ]
+    , testGroup "Ord"
+      [ testCase "compare 1234e1000000 123e1000001" $
+          compare (read "1234e1000000" :: Scientific) (read "123e1000001" :: 
Scientific) @?= GT
+      ]
+
+    , testGroup "RealFrac"
+      [ testGroup "floor"
+        [ testCase "1e1000000"   $ (floor (read "1e1000000"   :: Scientific) 
:: Int) @?= 0
+        , testCase "-1e-1000000" $ (floor (read "-1e-1000000" :: Scientific) 
:: Int) @?= (-1)
+        , testCase "1e-1000000"  $ (floor (read "1e-1000000"  :: Scientific) 
:: Int) @?= 0
+        ]
+      , testGroup "ceiling"
+        [ testCase "1e1000000"   $ (ceiling (read "1e1000000"   :: Scientific) 
:: Int) @?= 0
+        , testCase "-1e-1000000" $ (ceiling (read "-1e-1000000" :: Scientific) 
:: Int) @?= 0
+        , testCase "1e-1000000"  $ (ceiling (read "1e-1000000"  :: Scientific) 
:: Int) @?= 1
+        ]
+      , testGroup "round"
+        [ testCase "1e1000000"   $ (round (read "1e1000000"   :: Scientific) 
:: Int) @?= 0
+        , testCase "-1e-1000000" $ (round (read "-1e-1000000" :: Scientific) 
:: Int) @?= 0
+        , testCase "1e-1000000"  $ (round (read "1e-1000000"  :: Scientific) 
:: Int) @?= 0
+        ]
+      , testGroup "truncate"
+        [ testCase "1e1000000"   $ (truncate (read "1e1000000"   :: 
Scientific) :: Int) @?= 0
+        , testCase "-1e-1000000" $ (truncate (read "-1e-1000000" :: 
Scientific) :: Int) @?= 0
+        , testCase "1e-1000000"  $ (truncate (read "1e-1000000"  :: 
Scientific) :: Int) @?= 0
+        ]
+      , testGroup "properFracton"
+        [ testCase "1e1000000"   $ properFraction (read "1e1000000" :: 
Scientific) @?= (0 :: Int, 0)
+        , testCase "-1e-1000000" $ let s = read "-1e-1000000" :: Scientific
+                                   in properFraction s @?= (0 :: Int, s)
+        , testCase "1e-1000000"  $ let s = read "1e-1000000" :: Scientific
+                                   in properFraction s @?= (0 :: Int, s)
+        ]
+      ]
+    , testGroup "toRealFloat"
+      [ testCase "1e1000000"  $ assertBool "Should be infinity!" $ isInfinite $
+                                  (toRealFloat (read "1e1000000" :: 
Scientific) :: Double)
+      , testCase "1e-1000000" $ (toRealFloat (read "1e-1000000" :: Scientific) 
:: Double) @?= 0
+      ]
+    , testGroup "toBoundedInteger"
+      [ testCase "1e1000000"  $ (toBoundedInteger (read "1e1000000" :: 
Scientific) :: Maybe Int) @?= Nothing
+      ]
+    ]
+
+  , smallQuick "normalization"
        (SC.over   normalizedScientificSeries $ \s ->
             s /= 0 SC.==> abs (Scientific.coefficient s) `mod` 10 /= 0)
        (QC.forAll normalizedScientificGen    $ \s ->
@@ -55,6 +105,11 @@
     , testCase "reads \"(1.3 )\""  $ testReads "(1.3 )"  [(1.3, "")]
     , testCase "reads \"((1.3))\"" $ testReads "((1.3))" [(1.3, "")]
     , testCase "reads \" 1.3\""    $ testReads " 1.3"    [(1.3, "")]
+    , testCase "read \" ( ((  -1.0e+3 ) ))\"" $ testRead " ( ((  -1.0e+3 ) ))" 
(-1000.0)
+    , testCase "scientificP \"3\""       $ testScientificP "3"       [(3.0, 
"")]
+    , testCase "scientificP \"3.0e2\""   $ testScientificP "3.0e2"   [(3.0, 
"e2"), (300.0, "")]
+    , testCase "scientificP \"+3.0e+2\"" $ testScientificP "+3.0e+2" [(3.0, 
"e+2"), (300.0, "")]
+    , testCase "scientificP \"-3.0e-2\"" $ testScientificP "-3.0e-2" [(-3.0, 
"e-2"), (-3.0e-2, "")]
     ]
 
   , testGroup "Formatting"
@@ -91,6 +146,17 @@
     --     show d
     ]
 
+  , testGroup "Eq"
+    [ testProperty "==" $ \(s1 :: Scientific) (s2 :: Scientific) ->
+        (s1 == s2) == (toRational s1 == toRational s2)
+    , testProperty "s == s" $ \(s :: Scientific) -> s == s
+    ]
+
+  , testGroup "Ord"
+    [ testProperty "compare" $ \(s1 :: Scientific) (s2 :: Scientific) ->
+        compare s1 s2 == compare (toRational s1) (toRational s2)
+    ]
+
   , testGroup "Num"
     [ testGroup "Equal to Rational"
       [ testProperty "fromInteger" $ \i -> fromInteger i === fromRational 
(fromInteger i)
@@ -218,6 +284,12 @@
 testReads :: String -> [(Scientific, String)] -> Assertion
 testReads inp out = reads inp @?= out
 
+testRead :: String -> Scientific -> Assertion
+testRead inp out = read inp @?= out
+
+testScientificP :: String -> [(Scientific, String)] -> Assertion
+testScientificP inp out = readP_to_S Scientific.scientificP inp @?= out
+
 genericIsFloating :: RealFrac a => a -> Bool
 genericIsFloating a = fromInteger (floor a :: Integer) /= a
 


Reply via email to