{-# OPTIONS_HADDOCK not-home #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Hedgehog.Internal.Range (
  -- * Size
    Size(..)

  -- * Range
  , Range(..)
  , origin
  , bounds
  , lowerBound
  , upperBound

  -- * Constant
  , singleton
  , constant
  , constantFrom
  , constantBounded

  -- * Linear
  , linear
  , linearFrom
  , linearFrac
  , linearFracFrom
  , linearBounded

  -- * Exponential
  , exponential
  , exponentialFrom
  , exponentialBounded
  , exponentialFloat
  , exponentialFloatFrom

  -- * Internal
  -- $internal
  , clamp
  , scaleLinear
  , scaleLinearFrac
  , scaleExponential
  , scaleExponentialFloat
  ) where

import           Data.Bifunctor (bimap)

import           Prelude hiding (minimum, maximum)

-- $setup
-- >>> import Data.Int (Int8)
-- >>> let x = 3

-- | Tests are parameterized by the size of the randomly-generated data. The
--   meaning of a 'Size' value depends on the particular generator used, but
--   it must always be a number between 0 and 99 inclusive.
--
newtype Size =
  Size {
      Size -> Int
unSize :: Int
    } deriving (Size -> Size -> Bool
(Size -> Size -> Bool) -> (Size -> Size -> Bool) -> Eq Size
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Size -> Size -> Bool
$c/= :: Size -> Size -> Bool
== :: Size -> Size -> Bool
$c== :: Size -> Size -> Bool
Eq, Eq Size
Eq Size
-> (Size -> Size -> Ordering)
-> (Size -> Size -> Bool)
-> (Size -> Size -> Bool)
-> (Size -> Size -> Bool)
-> (Size -> Size -> Bool)
-> (Size -> Size -> Size)
-> (Size -> Size -> Size)
-> Ord Size
Size -> Size -> Bool
Size -> Size -> Ordering
Size -> Size -> Size
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Size -> Size -> Size
$cmin :: Size -> Size -> Size
max :: Size -> Size -> Size
$cmax :: Size -> Size -> Size
>= :: Size -> Size -> Bool
$c>= :: Size -> Size -> Bool
> :: Size -> Size -> Bool
$c> :: Size -> Size -> Bool
<= :: Size -> Size -> Bool
$c<= :: Size -> Size -> Bool
< :: Size -> Size -> Bool
$c< :: Size -> Size -> Bool
compare :: Size -> Size -> Ordering
$ccompare :: Size -> Size -> Ordering
$cp1Ord :: Eq Size
Ord, Integer -> Size
Size -> Size
Size -> Size -> Size
(Size -> Size -> Size)
-> (Size -> Size -> Size)
-> (Size -> Size -> Size)
-> (Size -> Size)
-> (Size -> Size)
-> (Size -> Size)
-> (Integer -> Size)
-> Num Size
forall a.
(a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a)
-> (a -> a)
-> (a -> a)
-> (Integer -> a)
-> Num a
fromInteger :: Integer -> Size
$cfromInteger :: Integer -> Size
signum :: Size -> Size
$csignum :: Size -> Size
abs :: Size -> Size
$cabs :: Size -> Size
negate :: Size -> Size
$cnegate :: Size -> Size
* :: Size -> Size -> Size
$c* :: Size -> Size -> Size
- :: Size -> Size -> Size
$c- :: Size -> Size -> Size
+ :: Size -> Size -> Size
$c+ :: Size -> Size -> Size
Num, Num Size
Ord Size
Num Size -> Ord Size -> (Size -> Rational) -> Real Size
Size -> Rational
forall a. Num a -> Ord a -> (a -> Rational) -> Real a
toRational :: Size -> Rational
$ctoRational :: Size -> Rational
$cp2Real :: Ord Size
$cp1Real :: Num Size
Real, Int -> Size
Size -> Int
Size -> [Size]
Size -> Size
Size -> Size -> [Size]
Size -> Size -> Size -> [Size]
(Size -> Size)
-> (Size -> Size)
-> (Int -> Size)
-> (Size -> Int)
-> (Size -> [Size])
-> (Size -> Size -> [Size])
-> (Size -> Size -> [Size])
-> (Size -> Size -> Size -> [Size])
-> Enum Size
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: Size -> Size -> Size -> [Size]
$cenumFromThenTo :: Size -> Size -> Size -> [Size]
enumFromTo :: Size -> Size -> [Size]
$cenumFromTo :: Size -> Size -> [Size]
enumFromThen :: Size -> Size -> [Size]
$cenumFromThen :: Size -> Size -> [Size]
enumFrom :: Size -> [Size]
$cenumFrom :: Size -> [Size]
fromEnum :: Size -> Int
$cfromEnum :: Size -> Int
toEnum :: Int -> Size
$ctoEnum :: Int -> Size
pred :: Size -> Size
$cpred :: Size -> Size
succ :: Size -> Size
$csucc :: Size -> Size
Enum, Enum Size
Real Size
Real Size
-> Enum Size
-> (Size -> Size -> Size)
-> (Size -> Size -> Size)
-> (Size -> Size -> Size)
-> (Size -> Size -> Size)
-> (Size -> Size -> (Size, Size))
-> (Size -> Size -> (Size, Size))
-> (Size -> Integer)
-> Integral Size
Size -> Integer
Size -> Size -> (Size, Size)
Size -> Size -> Size
forall a.
Real a
-> Enum a
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> a)
-> (a -> a -> (a, a))
-> (a -> a -> (a, a))
-> (a -> Integer)
-> Integral a
toInteger :: Size -> Integer
$ctoInteger :: Size -> Integer
divMod :: Size -> Size -> (Size, Size)
$cdivMod :: Size -> Size -> (Size, Size)
quotRem :: Size -> Size -> (Size, Size)
$cquotRem :: Size -> Size -> (Size, Size)
mod :: Size -> Size -> Size
$cmod :: Size -> Size -> Size
div :: Size -> Size -> Size
$cdiv :: Size -> Size -> Size
rem :: Size -> Size -> Size
$crem :: Size -> Size -> Size
quot :: Size -> Size -> Size
$cquot :: Size -> Size -> Size
$cp2Integral :: Enum Size
$cp1Integral :: Real Size
Integral)

instance Show Size where
  showsPrec :: Int -> Size -> ShowS
showsPrec Int
p (Size Int
x) =
    Bool -> ShowS -> ShowS
showParen (Int
p Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
10) (ShowS -> ShowS) -> ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$
      String -> ShowS
showString String
"Size " ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
      Int -> Int -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
11 Int
x

instance Read Size where
  readsPrec :: Int -> ReadS Size
readsPrec Int
p =
    Bool -> ReadS Size -> ReadS Size
forall a. Bool -> ReadS a -> ReadS a
readParen (Int
p Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
10) (ReadS Size -> ReadS Size) -> ReadS Size -> ReadS Size
forall a b. (a -> b) -> a -> b
$ \String
r0 -> do
      (String
"Size", String
r1) <- ReadS String
lex String
r0
      (Int
s, String
r2) <- Int -> ReadS Int
forall a. Read a => Int -> ReadS a
readsPrec Int
11 String
r1
      (Size, String) -> [(Size, String)]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int -> Size
Size Int
s, String
r2)

-- | A range describes the bounds of a number to generate, which may or may not
--   be dependent on a 'Size'.
--
--   The constructor takes an origin between the lower and upper bound, and a
--   function from 'Size' to bounds.  As the size goes towards @0@, the values
--   go towards the origin.
--
data Range a =
  Range !a (Size -> (a, a))

instance Functor Range where
  fmap :: (a -> b) -> Range a -> Range b
fmap a -> b
f (Range a
z Size -> (a, a)
g) =
    b -> (Size -> (b, b)) -> Range b
forall a. a -> (Size -> (a, a)) -> Range a
Range (a -> b
f a
z) ((Size -> (b, b)) -> Range b) -> (Size -> (b, b)) -> Range b
forall a b. (a -> b) -> a -> b
$ \Size
sz ->
      (a -> b) -> (a -> b) -> (a, a) -> (b, b)
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap a -> b
f a -> b
f (Size -> (a, a)
g Size
sz)

-- | Get the origin of a range. This might be the mid-point or the lower bound,
--   depending on what the range represents.
--
--   The 'bounds' of a range are scaled around this value when using the
--   'linear' family of combinators.
--
--   When using a 'Range' to generate numbers, the shrinking function will
--   shrink towards the origin.
--
origin :: Range a -> a
origin :: Range a -> a
origin (Range a
z Size -> (a, a)
_) =
  a
z

-- | Get the extents of a range, for a given size.
--
bounds :: Size -> Range a -> (a, a)
bounds :: Size -> Range a -> (a, a)
bounds Size
sz (Range a
_ Size -> (a, a)
f) =
  Size -> (a, a)
f Size
sz

-- | Get the lower bound of a range for the given size.
--
lowerBound :: Ord a => Size -> Range a -> a
lowerBound :: Size -> Range a -> a
lowerBound Size
sz Range a
range =
  let
    (a
x, a
y) =
      Size -> Range a -> (a, a)
forall a. Size -> Range a -> (a, a)
bounds Size
sz Range a
range
  in
    a -> a -> a
forall a. Ord a => a -> a -> a
min a
x a
y

-- | Get the upper bound of a range for the given size.
--
upperBound :: Ord a => Size -> Range a -> a
upperBound :: Size -> Range a -> a
upperBound Size
sz Range a
range =
  let
    (a
x, a
y) =
      Size -> Range a -> (a, a)
forall a. Size -> Range a -> (a, a)
bounds Size
sz Range a
range
  in
    a -> a -> a
forall a. Ord a => a -> a -> a
max a
x a
y

-- | Construct a range which represents a constant single value.
--
--   >>> bounds x $ singleton 5
--   (5,5)
--
--   >>> origin $ singleton 5
--   5
--
singleton :: a -> Range a
singleton :: a -> Range a
singleton a
x =
  a -> (Size -> (a, a)) -> Range a
forall a. a -> (Size -> (a, a)) -> Range a
Range a
x ((Size -> (a, a)) -> Range a) -> (Size -> (a, a)) -> Range a
forall a b. (a -> b) -> a -> b
$ \Size
_ -> (a
x, a
x)

-- | Construct a range which is unaffected by the size parameter.
--
--   A range from @0@ to @10@, with the origin at @0@:
--
--   >>> bounds x $ constant 0 10
--   (0,10)
--
--   >>> origin $ constant 0 10
--   0
--
constant :: a -> a -> Range a
constant :: a -> a -> Range a
constant a
x a
y =
  a -> a -> a -> Range a
forall a. a -> a -> a -> Range a
constantFrom a
x a
x a
y

-- | Construct a range which is unaffected by the size parameter with a origin
--   point which may differ from the bounds.
--
--   A range from @-10@ to @10@, with the origin at @0@:
--
--   >>> bounds x $ constantFrom 0 (-10) 10
--   (-10,10)
--
--   >>> origin $ constantFrom 0 (-10) 10
--   0
--
--   A range from @1970@ to @2100@, with the origin at @2000@:
--
--   >>> bounds x $ constantFrom 2000 1970 2100
--   (1970,2100)
--
--   >>> origin $ constantFrom 2000 1970 2100
--   2000
--
constantFrom ::
     a -- ^ Origin (the value produced when the size parameter is 0).
  -> a -- ^ Lower bound (the bottom of the range when the size parameter is 99).
  -> a -- ^ Upper bound (the top of the range when the size parameter is 99).
  -> Range a
constantFrom :: a -> a -> a -> Range a
constantFrom a
z a
x a
y =
  a -> (Size -> (a, a)) -> Range a
forall a. a -> (Size -> (a, a)) -> Range a
Range a
z ((Size -> (a, a)) -> Range a) -> (Size -> (a, a)) -> Range a
forall a b. (a -> b) -> a -> b
$ \Size
_ -> (a
x, a
y)

-- | Construct a range which is unaffected by the size parameter using the full
--   range of a data type.
--
--   A range from @-128@ to @127@, with the origin at @0@:
--
--   >>> bounds x (constantBounded :: Range Int8)
--   (-128,127)
--
--   >>> origin (constantBounded :: Range Int8)
--   0
--
constantBounded :: (Bounded a, Num a) => Range a
constantBounded :: Range a
constantBounded =
  a -> a -> a -> Range a
forall a. a -> a -> a -> Range a
constantFrom a
0 a
forall a. Bounded a => a
minBound a
forall a. Bounded a => a
maxBound

-- | Construct a range which scales the second bound relative to the size
--   parameter.
--
--   >>> bounds 0 $ linear 0 10
--   (0,0)
--
--   >>> bounds 50 $ linear 0 10
--   (0,5)
--
--   >>> bounds 99 $ linear 0 10
--   (0,10)
--
linear :: Integral a => a -> a -> Range a
linear :: a -> a -> Range a
linear a
x a
y =
  a -> a -> a -> Range a
forall a. Integral a => a -> a -> a -> Range a
linearFrom a
x a
x a
y

-- | Construct a range which scales the bounds relative to the size parameter.
--
--   >>> bounds 0 $ linearFrom 0 (-10) 10
--   (0,0)
--
--   >>> bounds 50 $ linearFrom 0 (-10) 20
--   (-5,10)
--
--   >>> bounds 99 $ linearFrom 0 (-10) 20
--   (-10,20)
--
linearFrom :: Integral a
  => a -- ^ Origin (the value produced when the size parameter is 0).
  -> a -- ^ Lower bound (the bottom of the range when the size parameter is 99).
  -> a -- ^ Upper bound (the top of the range when the size parameter is 99).
  -> Range a
linearFrom :: a -> a -> a -> Range a
linearFrom a
z a
x a
y =
  a -> (Size -> (a, a)) -> Range a
forall a. a -> (Size -> (a, a)) -> Range a
Range a
z ((Size -> (a, a)) -> Range a) -> (Size -> (a, a)) -> Range a
forall a b. (a -> b) -> a -> b
$ \Size
sz ->
    let
      x_sized :: a
x_sized =
        a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
clamp a
x a
y (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ Size -> a -> a -> a
forall a. Integral a => Size -> a -> a -> a
scaleLinear Size
sz a
z a
x

      y_sized :: a
y_sized =
        a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
clamp a
x a
y (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ Size -> a -> a -> a
forall a. Integral a => Size -> a -> a -> a
scaleLinear Size
sz a
z a
y
    in
      (a
x_sized, a
y_sized)

-- | Construct a range which is scaled relative to the size parameter and uses
--   the full range of a data type.
--
--   >>> bounds 0 (linearBounded :: Range Int8)
--   (0,0)
--
--   >>> bounds 50 (linearBounded :: Range Int8)
--   (-64,64)
--
--   >>> bounds 99 (linearBounded :: Range Int8)
--   (-128,127)
--
linearBounded :: (Bounded a, Integral a) => Range a
linearBounded :: Range a
linearBounded =
  a -> a -> a -> Range a
forall a. Integral a => a -> a -> a -> Range a
linearFrom a
0 a
forall a. Bounded a => a
minBound a
forall a. Bounded a => a
maxBound

-- | Construct a range which scales the second bound relative to the size
--   parameter.
--
--   /This works the same as 'linear', but for fractional values./
--
linearFrac :: (Fractional a, Ord a) => a -> a -> Range a
linearFrac :: a -> a -> Range a
linearFrac a
x a
y =
  a -> a -> a -> Range a
forall a. (Fractional a, Ord a) => a -> a -> a -> Range a
linearFracFrom a
x a
x a
y

-- | Construct a range which scales the bounds relative to the size parameter.
--
--   /This works the same as 'linearFrom', but for fractional values./
--
linearFracFrom :: (Fractional a, Ord a) => a -> a -> a -> Range a
linearFracFrom :: a -> a -> a -> Range a
linearFracFrom a
z a
x a
y =
  a -> (Size -> (a, a)) -> Range a
forall a. a -> (Size -> (a, a)) -> Range a
Range a
z ((Size -> (a, a)) -> Range a) -> (Size -> (a, a)) -> Range a
forall a b. (a -> b) -> a -> b
$ \Size
sz ->
    let
      x_sized :: a
x_sized =
        a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
clamp a
x a
y (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ Size -> a -> a -> a
forall a. Fractional a => Size -> a -> a -> a
scaleLinearFrac Size
sz a
z a
x

      y_sized :: a
y_sized =
        a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
clamp a
x a
y (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ Size -> a -> a -> a
forall a. Fractional a => Size -> a -> a -> a
scaleLinearFrac Size
sz a
z a
y
    in
      (a
x_sized, a
y_sized)

-- | Truncate a value so it stays within some range.
--
--   >>> clamp 5 10 15
--   10
--
--   >>> clamp 5 10 0
--   5
--
clamp :: Ord a => a -> a -> a -> a
clamp :: a -> a -> a -> a
clamp a
x a
y a
n =
  if a
x a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
y then
    a -> a -> a
forall a. Ord a => a -> a -> a
min a
x (a -> a -> a
forall a. Ord a => a -> a -> a
max a
y a
n)
  else
    a -> a -> a
forall a. Ord a => a -> a -> a
min a
y (a -> a -> a
forall a. Ord a => a -> a -> a
max a
x a
n)

-- | Scale an integral linearly with the size parameter.
--
scaleLinear :: Integral a => Size -> a -> a -> a
scaleLinear :: Size -> a -> a -> a
scaleLinear Size
sz0 a
z0 a
n0 =
  let
    sz :: Size
sz =
      Size -> Size -> Size
forall a. Ord a => a -> a -> a
max Size
0 (Size -> Size -> Size
forall a. Ord a => a -> a -> a
min Size
99 Size
sz0)

    z :: Integer
z =
      a -> Integer
forall a. Integral a => a -> Integer
toInteger a
z0

    n :: Integer
n =
      a -> Integer
forall a. Integral a => a -> Integer
toInteger a
n0

    -- @rng@ has magnitude 1 bigger than the biggest diff
    -- i.e. it specifies the range the diff can be in [0,rng)
    -- with the upper bound being exclusive.
    rng :: Integer
rng =
      Integer
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
z Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer -> Integer
forall a. Num a => a -> a
signum (Integer
n Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
z)

    diff :: Integer
diff =
      (Integer
rng Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Size -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Size
sz) Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`quot` Integer
100
  in
    Integer -> a
forall a. Num a => Integer -> a
fromInteger (Integer -> a) -> Integer -> a
forall a b. (a -> b) -> a -> b
$ Integer
z Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
diff

-- | Scale a fractional number linearly with the size parameter.
--
scaleLinearFrac :: Fractional a => Size -> a -> a -> a
scaleLinearFrac :: Size -> a -> a -> a
scaleLinearFrac Size
sz0 a
z a
n =
  let
    sz :: Size
sz =
      Size -> Size -> Size
forall a. Ord a => a -> a -> a
max Size
0 (Size -> Size -> Size
forall a. Ord a => a -> a -> a
min Size
99 Size
sz0)

    diff :: a
diff =
      (a
n a -> a -> a
forall a. Num a => a -> a -> a
- a
z) a -> a -> a
forall a. Num a => a -> a -> a
* (Size -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Size
sz a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
99)
  in
    a
z a -> a -> a
forall a. Num a => a -> a -> a
+ a
diff

-- | Construct a range which scales the second bound exponentially relative to
--   the size parameter.
--
--   >>> bounds 0 $ exponential 1 512
--   (1,1)
--
--   >>> bounds 11 $ exponential 1 512
--   (1,2)
--
--   >>> bounds 22 $ exponential 1 512
--   (1,4)
--
--   >>> bounds 77 $ exponential 1 512
--   (1,128)
--
--   >>> bounds 88 $ exponential 1 512
--   (1,256)
--
--   >>> bounds 99 $ exponential 1 512
--   (1,512)
--
exponential :: Integral a => a -> a -> Range a
exponential :: a -> a -> Range a
exponential a
x a
y =
  a -> a -> a -> Range a
forall a. Integral a => a -> a -> a -> Range a
exponentialFrom a
x a
x a
y

-- | Construct a range which scales the bounds exponentially relative to the
-- size parameter.
--
--   >>> bounds 0 $ exponentialFrom 0 (-128) 512
--   (0,0)
--
--   >>> bounds 25 $ exponentialFrom 0 (-128) 512
--   (-2,4)
--
--   >>> bounds 50 $ exponentialFrom 0 (-128) 512
--   (-11,22)
--
--   >>> bounds 75 $ exponentialFrom 0 (-128) 512
--   (-39,112)
--
--   >>> bounds 99 $ exponentialFrom x (-128) 512
--   (-128,512)
--
exponentialFrom :: Integral a
  => a -- ^ Origin (the value produced when the size parameter is 0).
  -> a -- ^ Lower bound (the bottom of the range when the size parameter is 99).
  -> a -- ^ Upper bound (the top of the range when the size parameter is 99).
  -> Range a
exponentialFrom :: a -> a -> a -> Range a
exponentialFrom a
z a
x a
y =
  a -> (Size -> (a, a)) -> Range a
forall a. a -> (Size -> (a, a)) -> Range a
Range a
z ((Size -> (a, a)) -> Range a) -> (Size -> (a, a)) -> Range a
forall a b. (a -> b) -> a -> b
$ \Size
sz ->
    let
      sized_x :: a
sized_x =
        a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
clamp a
x a
y (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ Size -> a -> a -> a
forall a. Integral a => Size -> a -> a -> a
scaleExponential Size
sz a
z a
x

      sized_y :: a
sized_y =
        a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
clamp a
x a
y (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ Size -> a -> a -> a
forall a. Integral a => Size -> a -> a -> a
scaleExponential Size
sz a
z a
y
    in
      (a
sized_x, a
sized_y)

-- | Construct a range which is scaled exponentially relative to the size
--   parameter and uses the full range of a data type.
--
--   >>> bounds 0 (exponentialBounded :: Range Int8)
--   (0,0)
--
--   >>> bounds 50 (exponentialBounded :: Range Int8)
--   (-11,11)
--
--   >>> bounds 99 (exponentialBounded :: Range Int8)
--   (-128,127)
--
exponentialBounded :: (Bounded a, Integral a) => Range a
exponentialBounded :: Range a
exponentialBounded =
  a -> a -> a -> Range a
forall a. Integral a => a -> a -> a -> Range a
exponentialFrom a
0 a
forall a. Bounded a => a
minBound a
forall a. Bounded a => a
maxBound

-- | Construct a range which scales the second bound exponentially relative to
--   the size parameter.
--
--   /This works the same as 'exponential', but for floating-point values./
--
--   >>> bounds 0 $ exponentialFloat 0 10
--   (0.0,0.0)
--
--   >>> bounds 50 $ exponentialFloat 0 10
--   (0.0,2.357035250656098)
--
--   >>> bounds 99 $ exponentialFloat 0 10
--   (0.0,10.0)
--
exponentialFloat :: (Floating a, Ord a) => a -> a -> Range a
exponentialFloat :: a -> a -> Range a
exponentialFloat a
x a
y =
  a -> a -> a -> Range a
forall a. (Floating a, Ord a) => a -> a -> a -> Range a
exponentialFloatFrom a
x a
x a
y

-- | Construct a range which scales the bounds exponentially relative to the
--   size parameter.
--
--   /This works the same as 'exponentialFrom', but for floating-point values./
--
--   >>> bounds 0 $ exponentialFloatFrom 0 (-10) 20
--   (0.0,0.0)
--
--   >>> bounds 50 $ exponentialFloatFrom 0 (-10) 20
--   (-2.357035250656098,3.6535836249197002)
--
--   >>> bounds 99 $ exponentialFloatFrom x (-10) 20
--   (-10.0,20.0)
--
exponentialFloatFrom :: (Floating a, Ord a) => a -> a -> a -> Range a
exponentialFloatFrom :: a -> a -> a -> Range a
exponentialFloatFrom a
z a
x a
y =
  a -> (Size -> (a, a)) -> Range a
forall a. a -> (Size -> (a, a)) -> Range a
Range a
z ((Size -> (a, a)) -> Range a) -> (Size -> (a, a)) -> Range a
forall a b. (a -> b) -> a -> b
$ \Size
sz ->
    let
      sized_x :: a
sized_x =
        a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
clamp a
x a
y (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ Size -> a -> a -> a
forall a. Floating a => Size -> a -> a -> a
scaleExponentialFloat Size
sz a
z a
x

      sized_y :: a
sized_y =
        a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
clamp a
x a
y (a -> a) -> a -> a
forall a b. (a -> b) -> a -> b
$ Size -> a -> a -> a
forall a. Floating a => Size -> a -> a -> a
scaleExponentialFloat Size
sz a
z a
y
    in
      (a
sized_x, a
sized_y)

-- | Scale an integral exponentially with the size parameter.
--
scaleExponential :: Integral a => Size -> a -> a -> a
scaleExponential :: Size -> a -> a -> a
scaleExponential Size
sz a
z0 a
n0 =
  let
    z :: Double
z =
      a -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
z0

    n :: Double
n =
      a -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
n0
  in
    Double -> a
forall a b. (RealFrac a, Integral b) => a -> b
round (Size -> Double -> Double -> Double
forall a. Floating a => Size -> a -> a -> a
scaleExponentialFloat Size
sz Double
z Double
n :: Double)

-- | Scale a floating-point number exponentially with the size parameter.
--
scaleExponentialFloat :: Floating a => Size -> a -> a -> a
scaleExponentialFloat :: Size -> a -> a -> a
scaleExponentialFloat Size
sz0 a
z a
n =
  let
    sz :: Size
sz =
      Size -> Size -> Size -> Size
forall a. Ord a => a -> a -> a -> a
clamp Size
0 Size
99 Size
sz0

    diff :: a
diff =
      (((a -> a
forall a. Num a => a -> a
abs (a
n a -> a -> a
forall a. Num a => a -> a -> a
- a
z) a -> a -> a
forall a. Num a => a -> a -> a
+ a
1) a -> a -> a
forall a. Floating a => a -> a -> a
** (Size -> a
forall a b. (Real a, Fractional b) => a -> b
realToFrac Size
sz a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
99)) a -> a -> a
forall a. Num a => a -> a -> a
- a
1) a -> a -> a
forall a. Num a => a -> a -> a
* a -> a
forall a. Num a => a -> a
signum (a
n a -> a -> a
forall a. Num a => a -> a -> a
- a
z)
  in
    a
z a -> a -> a
forall a. Num a => a -> a -> a
+ a
diff


------------------------------------------------------------------------
-- Internal

-- $internal
--
-- These functions are exported in case you need them in a pinch, but are not
-- part of the public API and may change at any time, even as part of a minor
-- update.