{-# LANGUAGE CPP #-}
-- | Perform some simple optimisations on program
module Numeric.Limp.Canon.Simplify where
import Numeric.Limp.Canon.Program
import Numeric.Limp.Rep
import Numeric.Limp.Error

import Numeric.Limp.Canon.Analyse.Constants

import Numeric.Limp.Canon.Simplify.Bounder
import Numeric.Limp.Canon.Simplify.Crunch
import Numeric.Limp.Canon.Simplify.Subst

#if MIN_VERSION_base(4,9,0) && !MIN_VERSION_base(4,11,0)
import Data.Semigroup
#endif

simplify :: (Ord z, Ord r, Rep c) => Program z r c -> Either Infeasible (Assignment z r c, Program z r c)
simplify p
 = simplify' mempty p

simplify' :: (Ord z, Ord r, Rep c) => Assignment z r c -> Program z r c -> Either Infeasible (Assignment z r c, Program z r c)
simplify' sub1 p
 = do  let p'   = crunchProgram    p
       p''  <- bounderProgram   p'
       sub2 <- constantsProgram p''
       if   assSize sub2 == 0
       then return (sub1, p'')
       else simplify' (sub1 <> sub2) (substProgram sub2 p'')