-- |
-- Module      :  Cryptol.TypeCheck.Solver.Utils
-- Copyright   :  (c) 2013-2016 Galois, Inc.
-- License     :  BSD3
-- Maintainer  :  cryptol@galois.com
-- Stability   :  provisional
-- Portability :  portable

module Cryptol.TypeCheck.Solver.Utils where

import Cryptol.TypeCheck.AST hiding (tMul)
import Cryptol.TypeCheck.SimpType(tAdd,tMul)
import Control.Monad(mplus,guard)
import Data.Maybe(listToMaybe)



-- | All ways to split a type in the form: `a + t1`, where `a` is a variable.
splitVarSummands :: Type -> [(TVar,Type)]
splitVarSummands ty0 = [ (x,t1) | (x,t1) <- go ty0, tNum (0::Int) /= t1 ]
  where
  go ty = case ty of
            TVar x      -> return (x, tNum (0::Int))
            TRec {}     -> []
            TUser _ _ t -> go t
            TCon (TF TCAdd) [t1,t2] ->
              do (a,yes) <- go t1
                 return (a, tAdd yes t2)
              `mplus`
              do (a,yes) <- go t2
                 return (a, tAdd t1 yes)
            TCon _ _    -> [] -- XXX: we could do some distributivity etc


-- | Check if we can express a type in the form: `a + t1`.
splitVarSummand :: TVar -> Type -> Maybe Type
splitVarSummand a ty = listToMaybe [ t | (x,t) <- splitVarSummands ty, x == a ]

{- | Check if we can express a type in the form: `k + t1`,
where `k` is a constant > 0.
This assumes that the type has been simplified already,
so that constants are floated to the left. -}
splitConstSummand :: Type -> Maybe (Integer, Type)
splitConstSummand ty =
  case ty of
    TVar {}     -> Nothing
    TRec {}     -> Nothing
    TUser _ _ t -> splitConstSummand t
    TCon (TF TCAdd) [t1,t2] ->
      do (k,t1') <- splitConstSummand t1
         case t1' of
           TCon (TC (TCNum 0)) [] -> return (k, t2)
           _                      -> return (k, tAdd t1' t2)
    TCon (TC (TCNum k)) [] -> guard (k > 0) >> return (k, tNum (0::Int))
    TCon {}     -> Nothing

{- | Check if we can express a type in the form: `k * t1`,
where `k` is a constant > 1
This assumes that the type has been simplified already,
so that constants are floated to the left. -}
splitConstFactor :: Type -> Maybe (Integer, Type)
splitConstFactor ty =
  case ty of
    TVar {}     -> Nothing
    TRec {}     -> Nothing
    TUser _ _ t -> splitConstFactor t
    TCon (TF TCMul) [t1,t2] ->
      do (k,t1') <- splitConstFactor t1
         return (k, tMul t1' t2)
    TCon (TC (TCNum k)) [] -> guard (k > 1) >> return (k, tNum (1::Int))
    TCon {}     -> Nothing