<commit_before>module Intermediate.ScopeAnalyzer (scopeAnalysis) where

import Common
import Data.Graph
import Data.List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import Data.Ord
import Data.Ratio
import Front.Absclafer
import Intermediate.Intclafer
import Prelude hiding (exp)
import Debug.Trace


isReference = isOverlapping . super
isConcrete = not . isReference
isSuperest clafers clafer = isNothing $ directSuper clafers clafer


-- Collects the global cardinality and hierarchy information into proper lower bounds.
-- If the model only has Clafers (ie. no constraints) then the lower bound is tight.
-- scopeAnalysis :: IModule -> Map IClafer Integer
scopeAnalysis IModule{mDecls = decls} =
    filter (isReferenceOrSuper . fst) finalAnalysis
    where
    finalAnalysis = Map.toList $ foldl analyzeComponent referenceAnalysis connectedComponents
    
    isReferenceOrSuper uid =
        isReference clafer || isSuperest clafers clafer
        where
        clafer = findClafer uid
        
    isConcrete' uid = isConcrete $ findClafer uid
    
    upperCards u =
        Map.findWithDefault (error $ "No upper cardinality for clafer named \"" ++ u ++ "\".") u upperCardsMap
    upperCardsMap = Map.fromList [(uid c, snd $ fromJust $ card c) | c <- clafers]
    
    referenceAnalysis = foldl (analyzeReferences clafers) Map.empty decls
    constraintAnalysis = analyzeConstraints constraints upperCards
    (subclaferMap, parentMap) = analyzeHierarchy clafers
    connectedComponents = analyzeDependencies clafers
    clafers = concatMap findClafers decls
    constraints = concatMap findConstraints decls
    findClafer uid = fromJust $ find (isEqClaferId uid) clafers
    
    lowCard clafer =
        max low constraintLow
        where
        low = fst $ fromJust $ card clafer
        constraintLow = Map.findWithDefault 0 (uid clafer) constraintAnalysis
    
    analyzeComponent analysis component =
        case flattenSCC component of
            [uid] -> analyzeSingleton uid analysis
            uids  ->
                foldr analyzeSingleton assume uids
                where
                -- assume that each of the scopes in the component is 1 while solving
                assume = foldr (`Map.insert` 1) analysis uids
        where
        analyzeSingleton uid analysis = analyze analysis $ findClafer uid
    
    analyze :: Map String Integer -> IClafer -> Map String Integer
    analyze analysis clafer =
        -- Take the max between the reference analysis and this analysis
        Map.insertWith max (uid clafer) scope analysis
        where
        scope
            | isAbstract clafer  = sum subclaferScopes
            | otherwise          = parentScope * lowCard clafer
        
        subclaferScopes = map (findOrError " subclafer scope not found" analysis) $ filter isConcrete' subclafers
        parentScope  =
            case parent of
                Just parent' -> findOrError " parent scope not found" analysis parent'
                Nothing      -> rootScope
        subclafers = Map.findWithDefault [] (uid clafer) subclaferMap
        parent     = Map.lookup (uid clafer) parentMap
        rootScope = 1
        findOrError message map key = Map.findWithDefault (error $ key ++ message) key map
        
    
analyzeReferences clafers analysis (IEClafer clafer) =
    foldl (analyzeReferences clafers) analysis' (elements clafer)
    where
    lowerBound = fst (fromJust $ card clafer)
    analysis'
        | isReference clafer = Map.insert (uid $ fromJust $ directSuper clafers clafer) lowerBound analysis
        | otherwise          = analysis
analyzeReferences _ analysis _ = analysis


analyzeConstraints :: [PExp] -> (String -> ExInteger) -> Map String Integer
analyzeConstraints constraints upperCards =
    foldr analyzeConstraint Map.empty $ filter isOneOrSomeConstraint constraints
    where
    isOneOrSomeConstraint PExp{exp = IDeclPExp{quant = quant}} =
        -- Only these two quantifiers requires an increase in scope to satisfy.
        case quant of
            IOne -> True
            ISome -> True
            _     -> False
    
    -- Only considers how quantifiers affect scope. Other types of constraints are not considered.
    -- Constraints of the type [some path1.path2] or [no path1.path2], etc.
    analyzeConstraint PExp{exp = IDeclPExp{quant = quant, oDecls = [], bpexp = bpexp}} analysis =
        foldr atLeastOne analysis path
        where
        path = dropThisAndParent $ unfoldJoins bpexp
        atLeastOne = Map.insertWith max `flip` 1
        
    -- Constraints of the type [all disj a : path1.path2] or [some b : path3.path4], etc.
    analyzeConstraint PExp{exp = IDeclPExp{oDecls = decls}} analysis =
        foldr analyzeDecl analysis decls
    analyzeConstraint _ analysis = analysis

    analyzeDecl IDecl{isDisj = isDisj, decls = decls, body = body} analysis =
        foldr (uncurry insert) analysis $ zip path scores
        where
        -- Take the first element in the path, and change its effective lower cardinality.
        -- Can overestimate the scope.
        path = dropThisAndParent $ unfoldJoins body
        -- "disj a;b;c" implies at least 3 whereas "a;b;c" implies at least one.
        minScope = if isDisj then fromIntegral $ length decls else 1
        insert = Map.insertWith max
        <commit_msg>Fix for bug 101. http://gsd.uwaterloo.ca:8888/question/467/non-exhaustive-patterns-in-function<commit_after>module Intermediate.ScopeAnalyzer (scopeAnalysis) where

import Common
import Data.Graph
import Data.List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import Data.Ord
import Data.Ratio
import Front.Absclafer
import Intermediate.Intclafer
import Prelude hiding (exp)
import Debug.Trace


isReference = isOverlapping . super
isConcrete = not . isReference
isSuperest clafers clafer = isNothing $ directSuper clafers clafer


-- Collects the global cardinality and hierarchy information into proper lower bounds.
-- If the model only has Clafers (ie. no constraints) then the lower bound is tight.
-- scopeAnalysis :: IModule -> Map IClafer Integer
scopeAnalysis IModule{mDecls = decls} =
    filter (isReferenceOrSuper . fst) finalAnalysis
    where
    finalAnalysis = Map.toList $ foldl analyzeComponent referenceAnalysis connectedComponents
    
    isReferenceOrSuper uid =
        isReference clafer || isSuperest clafers clafer
        where
        clafer = findClafer uid
        
    isConcrete' uid = isConcrete $ findClafer uid
    
    upperCards u =
        Map.findWithDefault (error $ "No upper cardinality for clafer named \"" ++ u ++ "\".") u upperCardsMap
    upperCardsMap = Map.fromList [(uid c, snd $ fromJust $ card c) | c <- clafers]
    
    referenceAnalysis = foldl (analyzeReferences clafers) Map.empty decls
    constraintAnalysis = analyzeConstraints constraints upperCards
    (subclaferMap, parentMap) = analyzeHierarchy clafers
    connectedComponents = analyzeDependencies clafers
    clafers = concatMap findClafers decls
    constraints = concatMap findConstraints decls
    findClafer uid = fromJust $ find (isEqClaferId uid) clafers
    
    lowCard clafer =
        max low constraintLow
        where
        low = fst $ fromJust $ card clafer
        constraintLow = Map.findWithDefault 0 (uid clafer) constraintAnalysis
    
    analyzeComponent analysis component =
        case flattenSCC component of
            [uid] -> analyzeSingleton uid analysis
            uids  ->
                foldr analyzeSingleton assume uids
                where
                -- assume that each of the scopes in the component is 1 while solving
                assume = foldr (`Map.insert` 1) analysis uids
        where
        analyzeSingleton uid analysis = analyze analysis $ findClafer uid
    
    analyze :: Map String Integer -> IClafer -> Map String Integer
    analyze analysis clafer =
        -- Take the max between the reference analysis and this analysis
        Map.insertWith max (uid clafer) scope analysis
        where
        scope
            | isAbstract clafer  = sum subclaferScopes
            | otherwise          = parentScope * lowCard clafer
        
        subclaferScopes = map (findOrError " subclafer scope not found" analysis) $ filter isConcrete' subclafers
        parentScope  =
            case parent of
                Just parent' -> findOrError " parent scope not found" analysis parent'
                Nothing      -> rootScope
        subclafers = Map.findWithDefault [] (uid clafer) subclaferMap
        parent     = Map.lookup (uid clafer) parentMap
        rootScope = 1
        findOrError message map key = Map.findWithDefault (error $ key ++ message) key map
        
    
analyzeReferences clafers analysis (IEClafer clafer) =
    foldl (analyzeReferences clafers) analysis' (elements clafer)
    where
    lowerBound = fst (fromJust $ card clafer)
    analysis'
        | isReference clafer = Map.insert (uid $ fromJust $ directSuper clafers clafer) lowerBound analysis
        | otherwise          = analysis
analyzeReferences _ analysis _ = analysis


analyzeConstraints :: [PExp] -> (String -> ExInteger) -> Map String Integer
analyzeConstraints constraints upperCards =
    foldr analyzeConstraint Map.empty $ filter isOneOrSomeConstraint constraints
    where
    isOneOrSomeConstraint PExp{exp = IDeclPExp{quant = quant}} =
        -- Only these two quantifiers requires an increase in scope to satisfy.
        case quant of
            IOne -> True
            ISome -> True
            _     -> False
    isOneOrSomeConstraint _ = False
    
    -- Only considers how quantifiers affect scope. Other types of constraints are not considered.
    -- Constraints of the type [some path1.path2] or [no path1.path2], etc.
    analyzeConstraint PExp{exp = IDeclPExp{quant = quant, oDecls = [], bpexp = bpexp}} analysis =
        foldr atLeastOne analysis path
        where
        path = dropThisAndParent $ unfoldJoins bpexp
        atLeastOne = Map.insertWith max `flip` 1
        
    -- Constraints of the type [all disj a : path1.path2] or [some b : path3.path4], etc.
    analyzeConstraint PExp{exp = IDeclPExp{oDecls = decls}} analysis =
        foldr analyzeDecl analysis decls
    analyzeConstraint _ analysis = analysis

    analyzeDecl IDecl{isDisj = isDisj, decls = decls, body = body} analysis =
        foldr (uncurry insert) analysis $ zip path scores
        where
        -- Take the first element in the path, and change its effective lower cardinality.
        -- Can overestimate the scope.
        path = dropThisAndParent $ unfoldJoins body
        -- "disj a;b;c" implies at least 3 whereas "a;b;c" implies at least one.
        minScope = if isDisj then fromIntegral $ length decls else 1
        insert = Map.insertWith max
        
{'fraction_non_alphanumeric': 0.060765329644997694, 'fraction_numerical': 0.0036883356385431073, 'mean_word_length': 2.6310679611650487, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3497878', 'n_tokens_mistral': 2993, 'n_tokens_neox': 2890, 'n_words': 1032}