    {-# OPTIONS_HADDOCK not-home, show-extensions #-}

    {-# LANGUAGE AutoDeriveTypeable #-}
    {-# LANGUAGE ConstraintKinds #-}
    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE DeriveDataTypeable #-}
    {-# LANGUAGE DeriveGeneric #-}
    {-# LANGUAGE KindSignatures #-}
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE TypeOperators #-}

    {- |
       Copyright  : Copyright (C) 2006-2018 Bjorn Buckwalter
       License    : BSD3

       Maintainer : bjorn@buckwalter.se
       Stability  : Stable
       Portability: GHC only

    Provides a type level representation of 'Variant's of dimensional values,
    which may be quantities or units.
    -}
    module Numeric.Units.Dimensional.Variants
    (
      type Variant(..),
      Metricality(..),
      type (*), type (/), type Weaken,
      type CompatibleVariants
    )
    where

    import Control.DeepSeq
    import Data.Data
    import qualified Data.ExactPi.TypeLevel as E
    import GHC.Generics
    import Prelude

    -- | Encodes whether a unit is a metric unit, that is, whether it can be combined

    -- with a metric prefix to form a related unit.

    data Metricality = Metric    -- ^ Capable of receiving a metric prefix.

                     | NonMetric -- ^ Incapable of receiving a metric prefix.

      deriving (Metricality -> Metricality -> Bool
    (Metricality -> Metricality -> Bool)
    -> (Metricality -> Metricality -> Bool) -> Eq Metricality
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    /= :: Metricality -> Metricality -> Bool
    $c/= :: Metricality -> Metricality -> Bool
    == :: Metricality -> Metricality -> Bool
    $c== :: Metricality -> Metricality -> Bool
    Eq, Eq Metricality
    Eq Metricality =>
    (Metricality -> Metricality -> Ordering)
    -> (Metricality -> Metricality -> Bool)
    -> (Metricality -> Metricality -> Bool)
    -> (Metricality -> Metricality -> Bool)
    -> (Metricality -> Metricality -> Bool)
    -> (Metricality -> Metricality -> Metricality)
    -> (Metricality -> Metricality -> Metricality)
    -> Ord Metricality
    Metricality -> Metricality -> Bool
    Metricality -> Metricality -> Ordering
    Metricality -> Metricality -> Metricality
    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 :: Metricality -> Metricality -> Metricality
    $cmin :: Metricality -> Metricality -> Metricality
    max :: Metricality -> Metricality -> Metricality
    $cmax :: Metricality -> Metricality -> Metricality
    >= :: Metricality -> Metricality -> Bool
    $c>= :: Metricality -> Metricality -> Bool
    > :: Metricality -> Metricality -> Bool
    $c> :: Metricality -> Metricality -> Bool
    <= :: Metricality -> Metricality -> Bool
    $c<= :: Metricality -> Metricality -> Bool
    < :: Metricality -> Metricality -> Bool
    $c< :: Metricality -> Metricality -> Bool
    compare :: Metricality -> Metricality -> Ordering
    $ccompare :: Metricality -> Metricality -> Ordering
    $cp1Ord :: Eq Metricality
    Ord, Typeable Metricality
    Constr
    DataType
    Typeable Metricality =>
    (forall (c :: Type -> Type).
     (forall d b. Data d => c (d -> b) -> d -> c b)
     -> (forall g. g -> c g) -> Metricality -> c Metricality)
    -> (forall (c :: Type -> Type).
        (forall b r. Data b => c (b -> r) -> c r)
        -> (forall r. r -> c r) -> Constr -> c Metricality)
    -> (Metricality -> Constr)
    -> (Metricality -> DataType)
    -> (forall (t :: Type -> Type) (c :: Type -> Type).
        Typeable t =>
        (forall d. Data d => c (t d)) -> Maybe (c Metricality))
    -> (forall (t :: Type -> Type -> Type) (c :: Type -> Type).
        Typeable t =>
        (forall d e. (Data d, Data e) => c (t d e))
        -> Maybe (c Metricality))
    -> ((forall b. Data b => b -> b) -> Metricality -> Metricality)
    -> (forall r r'.
        (r -> r' -> r)
        -> r -> (forall d. Data d => d -> r') -> Metricality -> r)
    -> (forall r r'.
        (r' -> r -> r)
        -> r -> (forall d. Data d => d -> r') -> Metricality -> r)
    -> (forall u. (forall d. Data d => d -> u) -> Metricality -> [u])
    -> (forall u.
        Int -> (forall d. Data d => d -> u) -> Metricality -> u)
    -> (forall (m :: Type -> Type).
        Monad m =>
        (forall d. Data d => d -> m d) -> Metricality -> m Metricality)
    -> (forall (m :: Type -> Type).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> Metricality -> m Metricality)
    -> (forall (m :: Type -> Type).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> Metricality -> m Metricality)
    -> Data Metricality
    Metricality -> Constr
    Metricality -> DataType
    (forall b. Data b => b -> b) -> Metricality -> Metricality
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Metricality -> c Metricality
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Metricality
    forall a.
    Typeable a =>
    (forall (c :: Type -> Type).
     (forall d b. Data d => c (d -> b) -> d -> c b)
     -> (forall g. g -> c g) -> a -> c a)
    -> (forall (c :: Type -> Type).
        (forall b r. Data b => c (b -> r) -> c r)
        -> (forall r. r -> c r) -> Constr -> c a)
    -> (a -> Constr)
    -> (a -> DataType)
    -> (forall (t :: Type -> Type) (c :: Type -> Type).
        Typeable t =>
        (forall d. Data d => c (t d)) -> Maybe (c a))
    -> (forall (t :: Type -> Type -> Type) (c :: Type -> Type).
        Typeable t =>
        (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
    -> ((forall b. Data b => b -> b) -> a -> a)
    -> (forall r r'.
        (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
    -> (forall r r'.
        (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
    -> (forall u. (forall d. Data d => d -> u) -> a -> [u])
    -> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
    -> (forall (m :: Type -> Type).
        Monad m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> (forall (m :: Type -> Type).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> (forall (m :: Type -> Type).
        MonadPlus m =>
        (forall d. Data d => d -> m d) -> a -> m a)
    -> Data a
    forall u. Int -> (forall d. Data d => d -> u) -> Metricality -> u
    forall u. (forall d. Data d => d -> u) -> Metricality -> [u]
    forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> Metricality -> r
    forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> Metricality -> r
    forall (m :: Type -> Type).
    Monad m =>
    (forall d. Data d => d -> m d) -> Metricality -> m Metricality
    forall (m :: Type -> Type).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Metricality -> m Metricality
    forall (c :: Type -> Type).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Metricality
    forall (c :: Type -> Type).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Metricality -> c Metricality
    forall (t :: Type -> Type) (c :: Type -> Type).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Metricality)
    forall (t :: Type -> Type -> Type) (c :: Type -> Type).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e))
    -> Maybe (c Metricality)
    $cNonMetric :: Constr
    $cMetric :: Constr
    $tMetricality :: DataType
    gmapMo :: (forall d. Data d => d -> m d) -> Metricality -> m Metricality
    $cgmapMo :: forall (m :: Type -> Type).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Metricality -> m Metricality
    gmapMp :: (forall d. Data d => d -> m d) -> Metricality -> m Metricality
    $cgmapMp :: forall (m :: Type -> Type).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Metricality -> m Metricality
    gmapM :: (forall d. Data d => d -> m d) -> Metricality -> m Metricality
    $cgmapM :: forall (m :: Type -> Type).
    Monad m =>
    (forall d. Data d => d -> m d) -> Metricality -> m Metricality
    gmapQi :: Int -> (forall d. Data d => d -> u) -> Metricality -> u
    $cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Metricality -> u
    gmapQ :: (forall d. Data d => d -> u) -> Metricality -> [u]
    $cgmapQ :: forall u. (forall d. Data d => d -> u) -> Metricality -> [u]
    gmapQr :: (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> Metricality -> r
    $cgmapQr :: forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> Metricality -> r
    gmapQl :: (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> Metricality -> r
    $cgmapQl :: forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> Metricality -> r
    gmapT :: (forall b. Data b => b -> b) -> Metricality -> Metricality
    $cgmapT :: (forall b. Data b => b -> b) -> Metricality -> Metricality
    dataCast2 :: (forall d e. (Data d, Data e) => c (t d e))
    -> Maybe (c Metricality)
    $cdataCast2 :: forall (t :: Type -> Type -> Type) (c :: Type -> Type).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e))
    -> Maybe (c Metricality)
    dataCast1 :: (forall d. Data d => c (t d)) -> Maybe (c Metricality)
    $cdataCast1 :: forall (t :: Type -> Type) (c :: Type -> Type).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Metricality)
    dataTypeOf :: Metricality -> DataType
    $cdataTypeOf :: Metricality -> DataType
    toConstr :: Metricality -> Constr
    $ctoConstr :: Metricality -> Constr
    gunfold :: (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Metricality
    $cgunfold :: forall (c :: Type -> Type).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Metricality
    gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Metricality -> c Metricality
    $cgfoldl :: forall (c :: Type -> Type).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Metricality -> c Metricality
    $cp1Data :: Typeable Metricality
    Data, Typeable, (forall x. Metricality -> Rep Metricality x)
    -> (forall x. Rep Metricality x -> Metricality)
    -> Generic Metricality
    forall x. Rep Metricality x -> Metricality
    forall x. Metricality -> Rep Metricality x
    forall a.
    (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
    $cto :: forall x. Rep Metricality x -> Metricality
    $cfrom :: forall x. Metricality -> Rep Metricality x
    Generic)

    instance NFData Metricality where -- instance is derived from Generic instance

    {-
    The variety 'v' of 'Dimensional'

    The phantom type variable v is used to distinguish between units
    and quantities. It must be one of the following:
    -}

    -- | The kind of variants of dimensional values.

    data Variant = DQuantity E.ExactPi' -- ^ The value is a quantity, stored as an `ExactPi` multiple of its value in its dimension's SI coherent unit.

                 | DUnit Metricality  -- ^ The value is a unit, possibly a 'Metric' unit.

      deriving (Typeable, (forall x. Variant -> Rep Variant x)
    -> (forall x. Rep Variant x -> Variant) -> Generic Variant
    forall x. Rep Variant x -> Variant
    forall x. Variant -> Rep Variant x
    forall a.
    (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
    $cto :: forall x. Rep Variant x -> Variant
    $cfrom :: forall x. Variant -> Rep Variant x
    Generic)

    {-
    We will reuse the operators and function names from the Prelude.
    To prevent unpleasant surprises we give operators the same fixity
    as the Prelude.
    -}

    infixl 7  *

    -- | Forms the product of two 'Variant's.

    --

    -- The product of units is a non-metric unit.

    --

    -- The product of quantities is a quantity.

    type family (v1 :: Variant) * (v2 :: Variant) :: Variant where
      'DUnit m1 * 'DUnit m2 = 'DUnit 'NonMetric
      'DQuantity s1 * 'DQuantity s2 = 'DQuantity (s1 E.* s2)

    type family (v1 :: Variant) / (v2 :: Variant) :: Variant where
      'DUnit m1 / 'DUnit m2 = 'DUnit 'NonMetric
      'DQuantity s1 / 'DQuantity s2 = 'DQuantity (s1 E./ s2)

    -- | Weakens a 'Variant' by forgetting possibly uninteresting type-level information.

    type family Weaken (v :: Variant) :: Variant where
      Weaken ('DQuantity s) = 'DQuantity s
      Weaken ('DUnit m) = 'DUnit 'NonMetric

    -- | Two 'Variant's are compatible when dimensional values of the first may be converted

    -- into the second merely by changing the representation of their values.

    type family AreCompatible (v1 :: Variant) (v2 :: Variant) :: Bool where
      AreCompatible ('DQuantity s1)  ('DQuantity s2) = 'True
      AreCompatible ('DUnit m) ('DUnit 'NonMetric)   = 'True
      AreCompatible s s   = 'True
      AreCompatible s1 s2 = 'False

    -- | Two 'Variant's are compatible when dimensional values of the first may be converted

    -- into the second merely by changing the representation of their values.

    type CompatibleVariants v1 v2 = ('True ~ AreCompatible v1 v2)