-- | Analyse a program to find all constants
module Numeric.Limp.Canon.Analyse.Constants where
import Numeric.Limp.Canon.Program
import Numeric.Limp.Rep
import Numeric.Limp.Error

import qualified Data.Map as M


-- | Find the constants in a program, only by looking at the bounds with lo==up.
-- (See "Numeric.Limp.Canon.Simplify.Stride" to convert constraints to bounds)
constantsProgram :: (Ord z, Ord r, Rep c) => Program z r c -> Either Infeasible (Assignment z r c)
constantsProgram p
 = mkAss $ concatMap eq $ M.toList $ _bounds p
 where

  eq (var, (Just lo, Just up))
   | lo == up
   = [(var, lo)]

  eq _
   = []

  mkAss ms
   = do zs  <- mapM tkLeft  ms
        rs  <- mapM tkRight ms
        return $ Assignment (M.fromList $ concat zs)
                            (M.fromList $ concat rs)

  tkLeft (Left z, v)

   -- Wow! What if the bounds aren't integral?
   -- Well, I guess the ILP solver will eventually figure out it's infeasible.
   -- Maybe it would be nice to trigger that error here.
   | v /= (fromZ $ truncate v)
   = Left InfeasibleNotIntegral

   | otherwise
   = return [(z, truncate v)]

  tkLeft _
   = return []

  tkRight (Right r, v)
   = return [(r, v)]
  tkRight _
   = return []