{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Haskus.Number.NaturalRange
( NatRange
, pattern NatRange
, natRange
, safeMakeNatRange
, makeNatRange
, unsafeMakeNatRange
, widenNatRange
, (.++.)
)
where
import Haskus.Number.BitNat
import Haskus.Utils.Types
import Numeric.Natural
newtype NatRange (f :: Nat) (t :: Nat) = NatRange' (BitNat (NatBitCount (t-f+1)))
instance
( KnownNat (t-f)
, KnownNat t
, KnownNat f
, Num (BitNatWord (NatBitCount (t-f+1)))
, Integral (BitNatWord (NatBitCount (t-f+1)))
) => Show (NatRange f t) where
showsPrec d x = showParen (d /= 0)
$ showString "NatRange @"
. showsPrec 0 (natValue' @f)
. showString " @"
. showsPrec 0 (natValue' @t)
. showString " "
. showsPrec 0 (toNaturalNatRange x)
type CheckInRange f t n =
( Assert (n <=? t) (() :: Constraint)
('ShowType n
':<>: 'Text " isn't in the range ["
':<>: 'ShowType f
':<>: 'Text ","
':<>: 'ShowType t
':<>: 'Text "]"
)
, Assert (f <=? n) (() :: Constraint)
('ShowType n
':<>: 'Text " isn't in the range ["
':<>: 'ShowType f
':<>: 'Text ","
':<>: 'ShowType t
':<>: 'Text "]"
)
)
type NatRangeBitCount f t = NatBitCount (t-f+1)
type MakeNatRange f t =
( Integral (BitNatWord (NatRangeBitCount f t))
, MakeBitNat (NatRangeBitCount f t)
, KnownNat f
, KnownNat t
, Assert (f <=? t) (() :: Constraint)
('Text "["
':<>: 'ShowType f
':<>: 'Text ","
':<>: 'ShowType t
':<>: 'Text "] isn't a valid range"
)
)
unsafeMakeNatRange :: forall f t.
( MakeNatRange f t
) => Natural -> NatRange f t
unsafeMakeNatRange v = NatRange' (BitNat @(NatRangeBitCount f t) (v - natValue @f))
safeMakeNatRange :: forall f t.
( MakeNatRange f t
) => Natural -> Maybe (NatRange f t)
safeMakeNatRange v
| v < natValue @f || v > natValue @t = Nothing
| otherwise = Just (unsafeMakeNatRange @f @t v)
makeNatRange :: forall f t.
( MakeNatRange f t
) => Natural -> NatRange f t
makeNatRange v = case safeMakeNatRange @f @t v of
Nothing ->error $ show v ++ " isn't in the range ["
++ show (natValue @f :: Natural)
++ ","
++ show (natValue @t :: Natural)
++ "]"
Just x -> x
natRange :: forall (n :: Nat) f t.
( MakeNatRange f t
, CheckInRange f t n
, KnownNat n
) => NatRange f t
natRange = unsafeMakeNatRange (natValue @n)
toNaturalNatRange :: forall f t.
( KnownNat f
, Integral (BitNatWord (NatBitCount (t-f+1)))
) => NatRange f t -> Natural
toNaturalNatRange (NatRange' x) = natValue @f + bitNatToNatural x
pattern NatRange :: forall (f :: Nat) (t :: Nat).
( MakeNatRange f t
) => Natural -> NatRange f t
{-# COMPLETE NatRange #-}
pattern NatRange x <- (toNaturalNatRange -> x)
where
NatRange x = makeNatRange @f @t x
widenNatRange :: forall f2 t2 f1 t1.
( WidenNatRange f1 t1 f2 t2
) => NatRange f1 t1 -> NatRange f2 t2
widenNatRange (NatRange a) = NatRange a
type WidenNatRange f1 t1 f2 t2 =
( Assert ((f2 <=? f1) `AndB` (t1 <=? t2)) (() :: Constraint)
('Text "Can't widen a natural range ["
':<>: 'ShowType f1
':<>: 'Text ","
':<>: 'ShowType t1
':<>: 'Text "] into range ["
':<>: 'ShowType f2
':<>: 'Text ","
':<>: 'ShowType t2
':<>: 'Text "]"
)
, MakeNatRange f1 t1
, MakeNatRange f2 t2
)
(.++.) ::
( MakeNatRange f1 t1
, MakeNatRange f2 t2
, MakeNatRange (f1+f2) (t1+t2)
) => NatRange f1 t1 -> NatRange f2 t2 -> NatRange (f1+f2) (t1+t2)
(.++.) (NatRange x) (NatRange y) = NatRange (x+y)