module Satchmo.SMT.Exotic.Natural where

import Prelude hiding ( not, and, or )

import Satchmo.SMT.Exotic.Dict
import qualified Satchmo.SMT.Exotic.Domain

import qualified Satchmo.SAT.Mini
import qualified Satchmo.Boolean as B

import qualified Satchmo.Unary.Op.Fixed
import qualified Satchmo.Unary.Op.Flexible
import qualified Satchmo.Unary as Un

import qualified Satchmo.Binary as Bin
import qualified Satchmo.Binary.Op.Fixed  
import qualified Satchmo.Binary.Op.Flexible

import Control.Monad ( foldM )

data Encoding = Unary | Binary deriving Show
data Unary_Addition = Odd_Even_Merge | Bitonic_Sort | Quadratic deriving Show
data Extension = Fixed | Flexible deriving Show

unary_fixed :: Int -> Unary_Addition 
            -> Dict Satchmo.SAT.Mini.SAT Un.Number B.Boolean
unary_fixed bits a = Dict
    { info = unwords [ "unary", "bits:", show bits, "(fixed)", "addition:", show a ]
    , domain = Satchmo.SMT.Exotic.Domain.Natural
    , fresh = Un.number bits
    , plus = foldM1 $ case a of
          Quadratic -> Satchmo.Unary.Op.Fixed.add_quadratic
          Bitonic_Sort -> Satchmo.Unary.Op.Fixed.add_by_bitonic_sort
          Odd_Even_Merge -> Satchmo.Unary.Op.Fixed.add_by_odd_even_merge
    , gg = Un.gt
    , ge = Un.ge
    }

unary_flexible :: Int -> Unary_Addition
               -> Dict Satchmo.SAT.Mini.SAT Un.Number B.Boolean
unary_flexible bits a = Dict
    { info = unwords [ "unary", "bits:", show bits, "(flexible)", "addition:", show a ]
    , domain = Satchmo.SMT.Exotic.Domain.Natural
    , fresh = Un.number bits
    , plus = foldM1 $ case a of
          Quadratic -> Satchmo.Unary.Op.Flexible.add_quadratic
          Bitonic_Sort -> Satchmo.Unary.Op.Flexible.add_by_bitonic_sort
          Odd_Even_Merge -> Satchmo.Unary.Op.Flexible.add_by_odd_even_merge
    , gg = Un.gt
    , ge = Un.ge
    }


binary_fixed :: Int -> Dict Satchmo.SAT.Mini.SAT Bin.Number B.Boolean
binary_fixed bits = Dict
    { info = unwords [ "binary", "bits:", show bits, "(fixed)" ]
    , domain = Satchmo.SMT.Exotic.Domain.Natural
    , fresh = Bin.number bits
    , plus = foldM1 $ Satchmo.Binary.Op.Fixed.add
    , times = foldM1 $ Satchmo.Binary.Op.Fixed.times
    , gg = Bin.gt
    , ge = Bin.ge
    , finite = \ n -> B.or $ Bin.bits n
    }


binary_flexible :: Int -> Dict Satchmo.SAT.Mini.SAT Bin.Number B.Boolean
binary_flexible bits = Dict
    { info = unwords [ "binary", "bits:", show bits, "(flexbible)" ]
    , domain = Satchmo.SMT.Exotic.Domain.Natural
    , fresh = Bin.number bits
    , plus = foldM1 $ Satchmo.Binary.Op.Flexible.add
    , times = foldM1 $ Satchmo.Binary.Op.Flexible.times
    , gg = Bin.gt
    , ge = Bin.ge
    , finite = \ n -> B.or $ Bin.bits n
    }

foldM1 :: Monad m => ( b -> b -> m b ) -> [b] -> m b
foldM1 f (x:xs) = foldM f x xs