packages feed

finite-1.4.1.1: src/lib/Finite/Class.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Finite.Class
-- Maintainer  :  Felix Klein
--
-- 'Finite' main class decleration including generics support.
--
-----------------------------------------------------------------------------

{-# LANGUAGE

    MultiWayIf
  , TypeOperators
  , DefaultSignatures
  , MultiParamTypeClasses
  , FlexibleContexts
  , FlexibleInstances

  #-}

-----------------------------------------------------------------------------

module Finite.Class
  ( T
  , Finite(..)
  , GFinite(..)
  ) where

-----------------------------------------------------------------------------

import Control.Exception
  ( assert
  )

import Finite.Type
  ( T
  , FiniteBounds
  , (#<<)
  , (<<#)
  , v2t
  , (\#)
  , (#)
  )

import GHC.Generics
  ( Generic
  , Rep
  , (:*:)(..)
  , (:+:)(..)
  , U1(..)
  , M1(..)
  , K1(..)
  , from
  , to
  )

import qualified Data.IntSet as S
  ( toList
  , fromList
  , fromAscList
  , difference
  )

-----------------------------------------------------------------------------

-- | The 'Finite' class.

class Finite b a where

  -- | Returns the number of elements associated with the given type.
  elements
    :: FiniteBounds b
    => T a -> Int

  default elements
    :: (Generic a, GFinite b (Rep a), FiniteBounds b)
    => T a -> Int

  elements t = gelements #<< from <<# t

  -- | Turns the value in the associated range into an Int uniquely
  -- identifiying the value.
  index
    :: FiniteBounds b
    => a -> Int

  default index
    :: (Generic a, GFinite b (Rep a), FiniteBounds b)
    => a -> Int

  index v = (+ (offset $ v2t v)) $ gindex $ from v

  -- | Turns an Int back to the value that is associated with it.
  value
    :: FiniteBounds b => Int -> a

  default value
    :: (Generic a, GFinite b (Rep a), FiniteBounds b)
    => Int -> a

  value v =
    let
      o = offset $ v2t r
      e = elements $ v2t r
      r = to $ gvalue (v - o)
    in
      assert (v >= o && v < o + e) r

  -- | Allows to put an offset to the integer mapping. Per default the
  -- offset is zero.
  offset
    :: FiniteBounds b
    => T a -> Int

  offset _ = 0

  -- | Returns a finite list of all elements of that type.
  values
    :: FiniteBounds b
    => [a]

  values =
    let
      rs = map value xs
      o = offset $ f rs
      n = elements $ f rs
      xs = [o, o + 1 .. o + n - 1]
    in
      rs

    where
      f :: [a] -> T a
      f _ = (#)

  -- | Complements a given list of elements of that type
  complement
    :: FiniteBounds b
    => [a] -> [a]

  complement xs =
    let
      o = offset $ f rs
      n = elements $ f rs
      s = S.fromList $ map index xs
      a = S.fromAscList [o, o + 1 .. o + n - 1]
      rs = map value $ S.toList $ S.difference a s
    in
      rs

    where
      f :: [a] -> T a
      f _ = (#)

  -- | Less than operator according to the implicit total index order.
  (|<|)
    :: FiniteBounds b
    => a -> a -> Bool

  (|<|) x y =
    index x < index y

  infixr |<|

  -- | Less or equal than operator according to the implicit total
  -- index order.
  (|<=|)
    :: FiniteBounds b
    => a -> a -> Bool

  (|<=|) x y =
    index x <= index y

  infixr |<=|

  -- | Greater or equal than operator according to the implicit total
  -- index order.
  (|>=|)
    :: FiniteBounds b
    => a -> a -> Bool

  (|>=|) x y =
    index x >= index y

  infixr |>=|

  -- | Greater than operator according to the implicit total index
  -- order.
  (|>|)
    :: FiniteBounds b
    => a -> a -> Bool

  (|>|) x y =
    index x > index y

  infixr |>|

  -- | Equal operator according to the implicit total index order.
  (|==|)
    :: FiniteBounds b
    => a -> a -> Bool

  (|==|) x y =
    index x == index y

  infixr |==|

  -- | Unequal operator according to the implicit total index order.
  (|/=|)
    :: FiniteBounds b
    => a -> a -> Bool

  (|/=|) x y =
    index x /= index y

  infixr |/=|


  -- | First element according to the total index order.
  initial
    :: FiniteBounds b
    => T a -> a

  initial t =
    value $ offset t

  -- | Last element according to the total index order.
  final
    :: FiniteBounds b
    => T a -> a

  final t =
    value $ offset t + elements t - 1

  -- | Next element according to the total index order (undefined for
  -- the last element).
  next
    :: FiniteBounds b
    => a -> a

  next x =
    let i = index x
    in assert (i < offset (v2t x) + elements (v2t x) - 1)
       $ value (i + 1)

  -- | Previous element according to the total index order (undefined
  -- for the first element).
  previous
    :: FiniteBounds b
    => a -> a

  previous x =
    let i = index x
    in assert (i > offset (v2t x))
       $ value (i - 1)

  -- | The upper and lower bounds of the instance.
  bounds
    :: FiniteBounds b
    => T a -> (a, a)

  bounds t =
    (initial t, final t)

-----------------------------------------------------------------------------

-- | Generics implementation for the 'Finite' class. The
-- realization is closely related to the one presented at
-- https://wiki.haskell.org/GHC.Generics.

class GFinite b f where
  gelements :: FiniteBounds b => T (f a) -> Int
  gindex :: FiniteBounds b => f a -> Int
  gvalue :: FiniteBounds b => Int -> f a

-----------------------------------------------------------------------------

-- | :*: instance.

instance
  (GFinite b f, GFinite b g)
    => GFinite b (f :*: g) where

  gelements x =
    gelements (((\#) :: T ((f :*: g) a) -> T (f a)) x) *
    gelements (((\#) :: T ((f :*: g) a) -> T (g a)) x)

  gindex (f :*: g) =
    (gindex f * (gelements #<< g)) + gindex g

  gvalue n =
    let
      m = gelements #<< g
      f = gvalue (n `div` m)
      g = gvalue (n `mod` m)
    in
     (f :*: g)

-----------------------------------------------------------------------------

-- | :+: instance.

instance
  (GFinite b f, GFinite b g)
    => GFinite b (f :+: g) where

  gelements x =
    gelements (((\#) :: T ((f :+: g) a) -> T (f a)) x) +
    gelements (((\#) :: T ((f :+: g) a) -> T (g a)) x)

  gindex x = case x of
    R1 y -> gindex y
    L1 y -> gindex y + gelements (((\#) :: (f :+: g) a -> T (g a)) x)

  gvalue n =
    let
      m = gelements #<< g
      g = gvalue (n `mod` m)
      f = gvalue (n - m)
    in if
      | n < m     -> R1 g
      | otherwise -> L1 f

-----------------------------------------------------------------------------

-- | U1 instance.

instance
  GFinite c U1 where

  gelements _ = 1

  gindex U1 = 0

  gvalue _ = U1

-----------------------------------------------------------------------------

-- | M1 instance.

instance
  (GFinite c f)
    => GFinite c (M1 i v f) where

  gelements =
    gelements . ((\#) :: T ((M1 i v f) p) -> T (f p))

  gindex (M1 x) = gindex x

  gvalue = M1 . gvalue

-----------------------------------------------------------------------------

-- | K1 instance.

instance
  (Finite b a)
    => GFinite b (K1 i a) where

  gelements =
    elements . ((\#) :: T ((K1 i a) c) -> T a)

  gindex (K1 x) = index x - (offset #<< x)

  gvalue n =
    let
      m = offset #<< x
      x = value (n + m)
    in
      K1 x

-----------------------------------------------------------------------------