module Camfort.Specification.Stencils.InferenceBackend where
import Data.List
import Data.Maybe
import Algebra.Lattice (joins1)
import Camfort.Specification.Stencils.Model
import Camfort.Specification.Stencils.DenotationalSemantics
import Camfort.Helpers
import qualified Camfort.Helpers.Vec as V
import Camfort.Specification.Stencils.Syntax
type Span a = (a, a)
spansToApproxSpatial :: [ Span (V.Vec (V.S n) Int) ]
-> Either String (Approximation Spatial)
spansToApproxSpatial spans = sequence . fmap intervalsToRegions $ approxUnion
where
approxVecs =
toApprox . map (fmap absRepToInf . transposeVecInterval) $ spans
approxUnion = fmap (optimise . joins1 . map return) approxVecs
toApprox :: [ V.Vec n (Interval Arbitrary) ]
-> Approximation [ V.Vec n (Interval Standard) ]
toApprox vs
| parts <- (elongatedPartitions . map approxVec) vs =
case parts of
(orgs, []) -> Exact . map fromExact $ orgs
([], elongs) -> Bound Nothing (Just $ map upperBound elongs)
(orgs, elongs) -> Bound (Just . map upperBound $ orgs)
(Just . map upperBound $ orgs ++ elongs)
elongatedPartitions =
partition $ \case { Exact{} -> True; Bound{} -> False }
absRepToInf :: Interval Arbitrary -> Interval Arbitrary
absRepToInf interv@(IntervArbitrary a b)
| fromIntegral a == absoluteRep = IntervInfiniteArbitrary
| fromIntegral b == absoluteRep = IntervInfiniteArbitrary
| otherwise = interv
absRepToInf interv = interv
transposeVecInterval :: Span (V.Vec n Int) -> V.Vec n (Interval Arbitrary)
transposeVecInterval (us, vs) = V.zipWith IntervArbitrary us vs
mkTrivialSpan :: V.Vec n Int -> Span (V.Vec n Int)
mkTrivialSpan V.Nil = (V.Nil, V.Nil)
mkTrivialSpan (V.Cons x xs) =
if x == absoluteRep
then (V.Cons (absoluteRep) ys, V.Cons absoluteRep zs)
else (V.Cons x ys, V.Cons x zs)
where
(ys, zs) = mkTrivialSpan xs
inferFromIndices :: V.VecList Int -> Specification
inferFromIndices (V.VL ixs) = Specification $
case fromBool mult of
Linear -> Once $ inferCore ixs'
NonLinear -> Mult $ inferCore ixs'
where
(ixs', mult) = hasDuplicates ixs
inferFromIndicesWithoutLinearity :: V.VecList Int -> Specification
inferFromIndicesWithoutLinearity (V.VL ixs) =
Specification . Mult . inferCore $ ixs
inferCore :: [V.Vec n Int] -> Approximation Spatial
inferCore subs =
case V.proveNonEmpty . head $ subs of
Just (V.ExistsEqT V.ReflEq) ->
case spansToApproxSpatial . inferMinimalVectorRegions $ subs of
Right a -> a
Left msg -> error msg
Nothing -> error "Input vectors are empty!"
inferMinimalVectorRegions :: [V.Vec n Int] -> [Span (V.Vec n Int)]
inferMinimalVectorRegions = fixCoalesce . map mkTrivialSpan
where fixCoalesce spans =
let spans' = minimaliseRegions . coalesceContiguous $ spans
in if spans' == spans then spans' else fixCoalesce spans'
coalesceContiguous :: [Span (V.Vec n Int)] -> [Span (V.Vec n Int)]
coalesceContiguous [] = []
coalesceContiguous [x] = [x]
coalesceContiguous [x, y] =
case coalesce x y of
Nothing -> [x, y]
Just c -> [c]
coalesceContiguous (x:xs) =
case sequenceMaybes (map (coalesce x) xs) of
Nothing -> x : coalesceContiguous xs
Just cs -> coalesceContiguous (cs ++ xs)
sequenceMaybes :: Eq a => [Maybe a] -> Maybe [a]
sequenceMaybes xs | all (== Nothing) xs = Nothing
| otherwise = Just (catMaybes xs)
coalesce :: Span (V.Vec n Int) -> Span (V.Vec n Int) -> Maybe (Span (V.Vec n Int))
coalesce (V.Nil, V.Nil) (V.Nil, V.Nil) = Just (V.Nil, V.Nil)
coalesce x y | x == y = Nothing
coalesce (V.Cons l1 ls1, V.Cons u1 us1) (V.Cons l2 ls2, V.Cons u2 us2)
| l1 == l2 && u1 == u2
= case coalesce (ls1, us1) (ls2, us2) of
Just (l, u) -> Just (V.Cons l1 l, V.Cons u1 u)
Nothing -> Nothing
| (u1 + 1 == l2) && (us1 == us2) && (ls1 == ls2)
= Just (V.Cons l1 ls1, V.Cons u2 us2)
| (u2 + 1 == l1) && (us1 == us2) && (ls1 == ls2)
= Just (V.Cons l2 ls2, V.Cons u1 us1)
| otherwise
= Nothing
minimaliseRegions :: [Span (V.Vec n Int)] -> [Span (V.Vec n Int)]
minimaliseRegions [] = []
minimaliseRegions xss = nub . minimalise $ xss
where localMin x ys = filter' x (\y -> containedWithin x y && (x /= y)) xss ++ ys
minimalise = foldr localMin []
filter' r f xs = case filter f xs of
[] -> [r]
ys -> ys
containedWithin :: Span (V.Vec n Int) -> Span (V.Vec n Int) -> Bool
containedWithin (V.Nil, V.Nil) (V.Nil, V.Nil)
= True
containedWithin (V.Cons l1 ls1, V.Cons u1 us1) (V.Cons l2 ls2, V.Cons u2 us2)
= (l2 <= l1 && u1 <= u2) && containedWithin (ls1, us1) (ls2, us2)