From bf22047b4d49b3d61109cd41105b620865a703c0 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Mon, 8 May 2023 17:29:09 -0400 Subject: [PATCH 01/51] quick and dirty solver caching --- saw-remote-api/src/SAWServer.hs | 1 + src/SAWScript/Builtins.hs | 22 ++++++++++- src/SAWScript/Interpreter.hs | 9 +++++ src/SAWScript/Value.hs | 65 ++++++++++++++++++++++++++++++++- 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/saw-remote-api/src/SAWServer.hs b/saw-remote-api/src/SAWServer.hs index 0f87671c20..578514368c 100644 --- a/saw-remote-api/src/SAWServer.hs +++ b/saw-remote-api/src/SAWServer.hs @@ -233,6 +233,7 @@ initialState readFileFn = , rwMRSolverEnv = emptyMREnv , rwPPOpts = defaultPPOpts , rwTheoremDB = db + , rwPropCache = Nothing , rwSharedContext = sc , rwJVMTrans = jvmTrans , rwPrimsAvail = mempty diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 6849359be5..3104c8b828 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -958,7 +958,20 @@ applyProverToGoal :: (Sequent -> TopLevel (Maybe CEX, SolverStats)) -> ProofGoal -> TopLevel (SolverStats, SolveResult) applyProverToGoal f g = do - (mb, stats) <- f (goalSequent g) + sc <- getSharedContext + g_prop <- io . Ex.try $ sequentToProp sc (goalSequent g) + (mb, stats) <- case g_prop of + -- If we can convert our goal into a prop, we can use caching + Right prop -> SV.lookupInPropCache prop >>= \case + Just () -> -- Use a cached result! + return (Nothing, mempty) + _ -> f (goalSequent g) >>= \case + (Nothing, stats) -> -- Cache an unsat result! + SV.insertInPropCache prop >> + return (Nothing, stats) + (mb, stats) -> return (mb, stats) + -- Otherwise we just call the solver + Left (_ :: Ex.SomeException) -> f (goalSequent g) case mb of Nothing -> return (stats, SolveSuccess (SolverEvidence stats (goalSequent g))) Just a -> return (stats, SolveCounterexample a) @@ -2405,6 +2418,13 @@ set_path_sat_solver nm = "yices" -> modify (\rw -> rw{ rwPathSatSolver = PathSat_Yices }) _ -> fail $ "Unknown path sat solver: " ++ show nm +set_proof_cache :: String -> TopLevel () +set_proof_cache nm = + case map toLower nm of + "z3" -> modify (\rw -> rw{ rwPathSatSolver = PathSat_Z3 }) + "yices" -> modify (\rw -> rw{ rwPathSatSolver = PathSat_Yices }) + _ -> fail $ "Unknown path sat solver: " ++ show nm + summarize_verification :: TopLevel () summarize_verification = do values <- rwProofs <$> getTopLevelRW diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index e63271db55..56da8f5c6a 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -402,6 +402,7 @@ interpretFile file runMain = mapM_ stmtWithPrint stmts when runMain interpretMain writeVerificationSummary + savePropCache stmtWithPrint s = do let withPos str = unlines $ ("[output] at " ++ show (SS.getPos s) ++ ": ") : @@ -488,6 +489,7 @@ buildTopLevelEnv proxy opts = , rwPPOpts = SAWScript.Value.defaultPPOpts , rwSharedContext = sc , rwTheoremDB = thmDB + , rwPropCache = Nothing , rwJVMTrans = jvmTrans , rwPrimsAvail = primsAvail , rwSMTArrayMemoryModel = False @@ -1057,6 +1059,13 @@ primitives = Map.fromList , "currently are 'z3' and 'yices'." ] + , prim "set_solver_cache_path" "String -> TopLevel ()" + (pureVal loadPropCache) + Experimental + [ "Enable solver result caching and set the path the cache should be" + , " loaded from and saved to." + ] + , prim "enable_debug_intrinsics" "TopLevel ()" (pureVal enable_debug_intrinsics) Current diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index d7c018d980..161fbd4262 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -42,13 +42,17 @@ import Control.Monad.Except (ExceptT(..), runExceptT, MonadError(..)) import Control.Monad.Reader (MonadReader) import qualified Control.Exception as X import qualified System.IO.Error as IOError +import System.Directory (doesDirectoryExist, listDirectory, createDirectory) +import System.FilePath (()) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader (ReaderT(..), ask, asks, local) import Control.Monad.State (StateT(..), gets) import Control.Monad.Trans.Class (MonadTrans(lift)) import Data.IORef import Data.Foldable(foldrM) -import Data.List ( intersperse ) +import Data.List ( intersperse, sortOn ) +import qualified Data.HashMap.Strict as HM +import Data.HashMap.Strict (HashMap) import qualified Data.Map as M import Data.Map ( Map ) import Data.Set ( Set ) @@ -94,6 +98,7 @@ import Verifier.SAW.SharedTerm hiding (PPOpts(..), defaultPPOpts, import qualified Verifier.SAW.Term.Pretty as SAWCorePP import Verifier.SAW.TypedTerm import Verifier.SAW.Term.Functor (ModuleName) +import Verifier.SAW.ExternalFormat import qualified Verifier.SAW.Simulator.Concrete as Concrete import qualified Cryptol.Eval as C @@ -517,6 +522,7 @@ data TopLevelRW = , rwPPOpts :: PPOpts , rwSharedContext :: SharedContext , rwTheoremDB :: TheoremDB + , rwPropCache :: Maybe (FilePath, HashMap Term ()) -- , rwCrucibleLLVMCtx :: Crucible.LLVMContext , rwJVMTrans :: CJ.JVMContext @@ -749,6 +755,63 @@ addJVMTrans trans = do let jvmt = rwJVMTrans rw putTopLevelRW ( rw { rwJVMTrans = trans <> jvmt }) +-- | Generalize over the all external constants in a given term by +-- wrapping the term with foralls and replacing the external constant +-- occurrences with the appropriate local variables. +-- FIXME: Move to SharedTerm.hs +scGeneralizeAllExts :: SharedContext -> Term -> IO Term +scGeneralizeAllExts sc tm = + let allexts = sortOn (toShortName . ecName) $ getAllExts tm + in scGeneralizeExts sc allexts tm + +-- | Lookup a 'Prop' in the 'PropCache' +lookupInPropCache :: Prop -> TopLevel (Maybe ()) +lookupInPropCache p = + rwPropCache <$> get >>= \case + Just (_, cache) -> do sc <- getSharedContext + rw <- getTopLevelRW + gen_tm <- io $ scGeneralizeAllExts sc (unProp p) + let opts = sawPPOpts $ rwPPOpts rw + gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm + printOutLnTop Info $ "Using cached result for: " ++ gen_tm_str + return $ HM.lookup gen_tm cache + _ -> return Nothing + +-- | Add a 'Prop' to the 'PropCache' +insertInPropCache :: Prop -> TopLevel () +insertInPropCache p = + do sc <- getSharedContext + rw <- getTopLevelRW + gen_tm <- io $ scGeneralizeAllExts sc (unProp p) + let opts = sawPPOpts $ rwPPOpts rw + gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm + printOutLnTop Info $ "Adding cached result: " ++ gen_tm_str + putTopLevelRW (rw { rwPropCache = fmap (HM.insert gen_tm ()) <$> rwPropCache rw }) + +-- | Load a 'PropCache' from a file +loadPropCache :: FilePath -> TopLevel () +loadPropCache path = + io (doesDirectoryExist path) >>= \case + True -> do sc <- getSharedContext + files <- io $ listDirectory path + tms <- mapM (\f -> io $ readFile f >>= scReadExternal sc) files + let cache = HM.fromList $ map (,()) tms + rw <- getTopLevelRW + putTopLevelRW (rw { rwPropCache = Just (path, cache) }) + False -> do rw <- getTopLevelRW + putTopLevelRW (rw { rwPropCache = Just (path, HM.empty) }) + +-- | Save the current 'PropCache' to a file +savePropCache :: TopLevel () +savePropCache = + rwPropCache <$> getTopLevelRW >>= \case + Just (path, cache) -> do + io $ createDirectory path + mapM_ (\((tm,_),i) -> io $ writeFile (path ("t" ++ show i)) + (scWriteExternal tm)) + (zip (HM.toList cache) ([0..] :: [Integer])) + Nothing -> return () + maybeInsert :: Ord k => k -> Maybe a -> Map k a -> Map k a maybeInsert _ Nothing m = m maybeInsert k (Just x) m = M.insert k x m From e6a4513d59553e35fe790724556caad3f2c865f6 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Mon, 8 May 2023 18:07:24 -0400 Subject: [PATCH 02/51] move files, fix cache loading --- src/SAWScript/Builtins.hs | 68 +++++++++++++++++++++++++++++++++++++-- src/SAWScript/Value.hs | 63 +----------------------------------- 2 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 3104c8b828..899674934b 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -36,7 +36,8 @@ import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy.UTF8 as B import qualified Data.IntMap as IntMap import Data.IORef -import Data.List (isPrefixOf, isInfixOf) +import Data.List (isPrefixOf, isInfixOf, sortOn) +import qualified Data.HashMap.Strict as HM import qualified Data.Map as Map import Data.Maybe (fromMaybe) import Data.Set (Set) @@ -954,6 +955,67 @@ proveUnintSBV conf unints = unintSet <- SV.scriptTopLevel (resolveNames unints) wrapProver (Prover.proveUnintSBV conf unintSet timeout) +-- | Generalize over the all external constants in a given term by +-- wrapping the term with foralls and replacing the external constant +-- occurrences with the appropriate local variables. +-- FIXME: Move to SharedTerm.hs +scGeneralizeAllExts :: SharedContext -> Term -> IO Term +scGeneralizeAllExts sc tm = + let allexts = sortOn (toShortName . ecName) $ getAllExts tm + in scGeneralizeExts sc allexts tm + +-- | Lookup a 'Prop' in the 'PropCache' +-- FIXME: Move somewhere else? +lookupInPropCache :: Prop -> TopLevel (Maybe ()) +lookupInPropCache p = + rwPropCache <$> get >>= \case + Just (_, cache) -> do + sc <- getSharedContext + opts <- getTopLevelPPOpts + gen_tm <- io $ scGeneralizeAllExts sc (unProp p) + gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm + printOutLnTop Info $ "Using cached result for: " ++ gen_tm_str + return $ HM.lookup gen_tm cache + _ -> return Nothing + +-- | Add a 'Prop' to the 'PropCache' +-- FIXME: Move somewhere else? +insertInPropCache :: Prop -> TopLevel () +insertInPropCache p = + rwPropCache <$> get >>= \case + Just (path, cache) -> do + sc <- getSharedContext + opts <- getTopLevelPPOpts + gen_tm <- io $ scGeneralizeAllExts sc (unProp p) + gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm + printOutLnTop Info $ "Adding cached result: " ++ gen_tm_str + modify (\rw -> rw { rwPropCache = Just (path, HM.insert gen_tm () cache) }) + Nothing -> return () + +-- | Load a 'PropCache' from a file +-- FIXME: Move somewhere else? +loadPropCache :: FilePath -> TopLevel () +loadPropCache path = + io (doesDirectoryExist path) >>= \case + True -> do sc <- getSharedContext + files <- io $ listDirectory path + tms <- mapM (\f -> io $ readFile (path f) >>= scReadExternal sc) files + let cache = HM.fromList $ map (,()) tms + modify (\rw -> rw { rwPropCache = Just (path, cache) }) + False -> modify (\rw -> rw { rwPropCache = Just (path, HM.empty) }) + +-- | Save the current 'PropCache' to a file +-- FIXME: Move somewhere else? +savePropCache :: TopLevel () +savePropCache = + rwPropCache <$> getTopLevelRW >>= \case + Just (path, cache) -> do + io $ doesDirectoryExist path >>= flip unless (createDirectory path) + mapM_ (\((tm,_),i) -> io $ writeFile (path ("t" ++ show i)) + (scWriteExternal tm)) + (zip (HM.toList cache) ([0..] :: [Integer])) + Nothing -> return () + applyProverToGoal :: (Sequent -> TopLevel (Maybe CEX, SolverStats)) -> ProofGoal -> TopLevel (SolverStats, SolveResult) @@ -962,12 +1024,12 @@ applyProverToGoal f g = do g_prop <- io . Ex.try $ sequentToProp sc (goalSequent g) (mb, stats) <- case g_prop of -- If we can convert our goal into a prop, we can use caching - Right prop -> SV.lookupInPropCache prop >>= \case + Right prop -> lookupInPropCache prop >>= \case Just () -> -- Use a cached result! return (Nothing, mempty) _ -> f (goalSequent g) >>= \case (Nothing, stats) -> -- Cache an unsat result! - SV.insertInPropCache prop >> + insertInPropCache prop >> return (Nothing, stats) (mb, stats) -> return (mb, stats) -- Otherwise we just call the solver diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 161fbd4262..56029d94b5 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -42,16 +42,13 @@ import Control.Monad.Except (ExceptT(..), runExceptT, MonadError(..)) import Control.Monad.Reader (MonadReader) import qualified Control.Exception as X import qualified System.IO.Error as IOError -import System.Directory (doesDirectoryExist, listDirectory, createDirectory) -import System.FilePath (()) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader (ReaderT(..), ask, asks, local) import Control.Monad.State (StateT(..), gets) import Control.Monad.Trans.Class (MonadTrans(lift)) import Data.IORef import Data.Foldable(foldrM) -import Data.List ( intersperse, sortOn ) -import qualified Data.HashMap.Strict as HM +import Data.List ( intersperse ) import Data.HashMap.Strict (HashMap) import qualified Data.Map as M import Data.Map ( Map ) @@ -98,7 +95,6 @@ import Verifier.SAW.SharedTerm hiding (PPOpts(..), defaultPPOpts, import qualified Verifier.SAW.Term.Pretty as SAWCorePP import Verifier.SAW.TypedTerm import Verifier.SAW.Term.Functor (ModuleName) -import Verifier.SAW.ExternalFormat import qualified Verifier.SAW.Simulator.Concrete as Concrete import qualified Cryptol.Eval as C @@ -755,63 +751,6 @@ addJVMTrans trans = do let jvmt = rwJVMTrans rw putTopLevelRW ( rw { rwJVMTrans = trans <> jvmt }) --- | Generalize over the all external constants in a given term by --- wrapping the term with foralls and replacing the external constant --- occurrences with the appropriate local variables. --- FIXME: Move to SharedTerm.hs -scGeneralizeAllExts :: SharedContext -> Term -> IO Term -scGeneralizeAllExts sc tm = - let allexts = sortOn (toShortName . ecName) $ getAllExts tm - in scGeneralizeExts sc allexts tm - --- | Lookup a 'Prop' in the 'PropCache' -lookupInPropCache :: Prop -> TopLevel (Maybe ()) -lookupInPropCache p = - rwPropCache <$> get >>= \case - Just (_, cache) -> do sc <- getSharedContext - rw <- getTopLevelRW - gen_tm <- io $ scGeneralizeAllExts sc (unProp p) - let opts = sawPPOpts $ rwPPOpts rw - gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm - printOutLnTop Info $ "Using cached result for: " ++ gen_tm_str - return $ HM.lookup gen_tm cache - _ -> return Nothing - --- | Add a 'Prop' to the 'PropCache' -insertInPropCache :: Prop -> TopLevel () -insertInPropCache p = - do sc <- getSharedContext - rw <- getTopLevelRW - gen_tm <- io $ scGeneralizeAllExts sc (unProp p) - let opts = sawPPOpts $ rwPPOpts rw - gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm - printOutLnTop Info $ "Adding cached result: " ++ gen_tm_str - putTopLevelRW (rw { rwPropCache = fmap (HM.insert gen_tm ()) <$> rwPropCache rw }) - --- | Load a 'PropCache' from a file -loadPropCache :: FilePath -> TopLevel () -loadPropCache path = - io (doesDirectoryExist path) >>= \case - True -> do sc <- getSharedContext - files <- io $ listDirectory path - tms <- mapM (\f -> io $ readFile f >>= scReadExternal sc) files - let cache = HM.fromList $ map (,()) tms - rw <- getTopLevelRW - putTopLevelRW (rw { rwPropCache = Just (path, cache) }) - False -> do rw <- getTopLevelRW - putTopLevelRW (rw { rwPropCache = Just (path, HM.empty) }) - --- | Save the current 'PropCache' to a file -savePropCache :: TopLevel () -savePropCache = - rwPropCache <$> getTopLevelRW >>= \case - Just (path, cache) -> do - io $ createDirectory path - mapM_ (\((tm,_),i) -> io $ writeFile (path ("t" ++ show i)) - (scWriteExternal tm)) - (zip (HM.toList cache) ([0..] :: [Integer])) - Nothing -> return () - maybeInsert :: Ord k => k -> Maybe a -> Map k a -> Map k a maybeInsert _ Nothing m = m maybeInsert k (Just x) m = M.insert k x m From d64897086a18290dcd5898c8137cede52fc18ff4 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Mon, 8 May 2023 18:50:24 -0400 Subject: [PATCH 03/51] add --cache command-line option --- src/SAWScript/Builtins.hs | 28 ++++++++++++++++++---------- src/SAWScript/Interpreter.hs | 4 +++- src/SAWScript/Options.hs | 7 +++++++ 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 899674934b..136ec6c548 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -38,6 +38,7 @@ import qualified Data.IntMap as IntMap import Data.IORef import Data.List (isPrefixOf, isInfixOf, sortOn) import qualified Data.HashMap.Strict as HM +import Data.HashMap.Strict (HashMap) import qualified Data.Map as Map import Data.Maybe (fromMaybe) import Data.Set (Set) @@ -964,7 +965,7 @@ scGeneralizeAllExts sc tm = let allexts = sortOn (toShortName . ecName) $ getAllExts tm in scGeneralizeExts sc allexts tm --- | Lookup a 'Prop' in the 'PropCache' +-- | Lookup a 'Prop' in the solver result cache -- FIXME: Move somewhere else? lookupInPropCache :: Prop -> TopLevel (Maybe ()) lookupInPropCache p = @@ -978,7 +979,7 @@ lookupInPropCache p = return $ HM.lookup gen_tm cache _ -> return Nothing --- | Add a 'Prop' to the 'PropCache' +-- | Add a 'Prop' to the solver result cache -- FIXME: Move somewhere else? insertInPropCache :: Prop -> TopLevel () insertInPropCache p = @@ -992,19 +993,26 @@ insertInPropCache p = modify (\rw -> rw { rwPropCache = Just (path, HM.insert gen_tm () cache) }) Nothing -> return () --- | Load a 'PropCache' from a file +-- | Load a solver result cache from a file -- FIXME: Move somewhere else? loadPropCache :: FilePath -> TopLevel () loadPropCache path = - io (doesDirectoryExist path) >>= \case - True -> do sc <- getSharedContext - files <- io $ listDirectory path - tms <- mapM (\f -> io $ readFile (path f) >>= scReadExternal sc) files + do sc <- getSharedContext + cache <- io $ loadPropCacheH sc path + modify (\rw -> rw { rwPropCache = cache }) + +-- | Construct a solver result cache from a file +-- FIXME: Move somewhere else? +loadPropCacheH :: SharedContext -> FilePath -> IO (Maybe (FilePath, HashMap Term ())) +loadPropCacheH sc path = + doesDirectoryExist path >>= \case + True -> do files <- listDirectory path + tms <- mapM (\f -> readFile (path f) >>= scReadExternal sc) files let cache = HM.fromList $ map (,()) tms - modify (\rw -> rw { rwPropCache = Just (path, cache) }) - False -> modify (\rw -> rw { rwPropCache = Just (path, HM.empty) }) + return $ Just (path, cache) + False -> return $ Just (path, HM.empty) --- | Save the current 'PropCache' to a file +-- | Save the current solver result cache to a file -- FIXME: Move somewhere else? savePropCache :: TopLevel () savePropCache = diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 56da8f5c6a..2c5d0a9d94 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -477,6 +477,8 @@ buildTopLevelEnv proxy opts = jvmTrans <- CJ.mkInitialJVMContext halloc + cache <- maybe (return Nothing) (loadPropCacheH sc) (solverCache opts) + let rw0 = TopLevelRW { rwValues = valueEnv primsAvail opts bic , rwTypes = primTypeEnv primsAvail @@ -489,7 +491,7 @@ buildTopLevelEnv proxy opts = , rwPPOpts = SAWScript.Value.defaultPPOpts , rwSharedContext = sc , rwTheoremDB = thmDB - , rwPropCache = Nothing + , rwPropCache = cache , rwJVMTrans = jvmTrans , rwPrimsAvail = primsAvail , rwSMTArrayMemoryModel = False diff --git a/src/SAWScript/Options.hs b/src/SAWScript/Options.hs index ab22d815e0..6c190a977e 100644 --- a/src/SAWScript/Options.hs +++ b/src/SAWScript/Options.hs @@ -42,6 +42,7 @@ data Options = Options , printOutFn :: Verbosity -> String -> IO () , summaryFile :: Maybe FilePath , summaryFormat :: SummaryFormat + , solverCache :: Maybe FilePath } deriving (Show) -- | Verbosity is currently a linear setting (vs a mask or tree). Any given @@ -79,6 +80,7 @@ defaultOptions , useColor = True , summaryFile = Nothing , summaryFormat = Pretty + , solverCache = Nothing } printOutWith :: Verbosity -> Verbosity -> String -> IO () @@ -172,6 +174,11 @@ options = ) "either 'json' or 'pretty'") "Specify the format in which the verification summary should be written in ('json' or 'pretty'; defaults to 'json')" + , Option [] ["cache"] + (ReqArg + (\path opts -> return opts { solverCache = Just path }) + "path") + "Enable solver result caching and set the path the cache should be loaded from and saved to" ] -- Try to read verbosity as either a string or number and default to 'Debug'. From 92aee033b0f1f99a50f73dd8f08ad8ec5fe29fa2 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Mon, 8 May 2023 23:37:24 -0400 Subject: [PATCH 04/51] save SolverStats when caching solver results --- src/SAWScript/Builtins.hs | 59 ++++++++++++++++++++++++++------------- src/SAWScript/Value.hs | 2 +- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 136ec6c548..fb87bce6b8 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -40,7 +40,7 @@ import Data.List (isPrefixOf, isInfixOf, sortOn) import qualified Data.HashMap.Strict as HM import Data.HashMap.Strict (HashMap) import qualified Data.Map as Map -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, catMaybes) import Data.Set (Set) import qualified Data.Set as Set import Data.Text (Text) @@ -967,7 +967,7 @@ scGeneralizeAllExts sc tm = -- | Lookup a 'Prop' in the solver result cache -- FIXME: Move somewhere else? -lookupInPropCache :: Prop -> TopLevel (Maybe ()) +lookupInPropCache :: Prop -> TopLevel (Maybe SolverStats) lookupInPropCache p = rwPropCache <$> get >>= \case Just (_, cache) -> do @@ -975,14 +975,17 @@ lookupInPropCache p = opts <- getTopLevelPPOpts gen_tm <- io $ scGeneralizeAllExts sc (unProp p) gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm - printOutLnTop Info $ "Using cached result for: " ++ gen_tm_str - return $ HM.lookup gen_tm cache + case HM.lookup gen_tm cache of + Just ret -> do printOutLnTop Info $ "Using cached result for: " ++ gen_tm_str + return $ Just ret + _ -> do -- printOutLnTop Info $ "Cache miss on: " ++ show gen_tm_str + return Nothing _ -> return Nothing -- | Add a 'Prop' to the solver result cache -- FIXME: Move somewhere else? -insertInPropCache :: Prop -> TopLevel () -insertInPropCache p = +insertInPropCache :: Prop -> SolverStats -> TopLevel () +insertInPropCache p stats = rwPropCache <$> get >>= \case Just (path, cache) -> do sc <- getSharedContext @@ -990,7 +993,7 @@ insertInPropCache p = gen_tm <- io $ scGeneralizeAllExts sc (unProp p) gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm printOutLnTop Info $ "Adding cached result: " ++ gen_tm_str - modify (\rw -> rw { rwPropCache = Just (path, HM.insert gen_tm () cache) }) + modify (\rw -> rw { rwPropCache = Just (path, HM.insert gen_tm stats cache) }) Nothing -> return () -- | Load a solver result cache from a file @@ -1003,25 +1006,41 @@ loadPropCache path = -- | Construct a solver result cache from a file -- FIXME: Move somewhere else? -loadPropCacheH :: SharedContext -> FilePath -> IO (Maybe (FilePath, HashMap Term ())) +loadPropCacheH :: SharedContext -> FilePath -> + IO (Maybe (FilePath, HashMap Term SolverStats)) loadPropCacheH sc path = doesDirectoryExist path >>= \case - True -> do files <- listDirectory path - tms <- mapM (\f -> readFile (path f) >>= scReadExternal sc) files - let cache = HM.fromList $ map (,()) tms - return $ Just (path, cache) + True -> do index <- lines <$> readFile (path "index") + files <- listDirectory path + cache <- catMaybes <$> mapM (processFile index) files + return $ Just (path, HM.fromList cache) False -> return $ Just (path, HM.empty) + where processFile :: [String] -> FilePath -> IO (Maybe (Term, SolverStats)) + processFile index f = case readMaybe (drop 1 f) of + Just i | i < length index -> do + str <- readFile (path f) + tm <- scReadExternal sc str + let (s0, s1) = break (== ' ') (index !! i) + case (,) <$> readMaybe s0 <*> readMaybe (drop 1 s1) of + Just (sz, ss) -> + return $ Just (length ss `seq` tm, + SolverStats (Set.fromList ss) sz) + _ -> return Nothing + _ -> return Nothing -- | Save the current solver result cache to a file -- FIXME: Move somewhere else? savePropCache :: TopLevel () savePropCache = rwPropCache <$> getTopLevelRW >>= \case - Just (path, cache) -> do - io $ doesDirectoryExist path >>= flip unless (createDirectory path) - mapM_ (\((tm,_),i) -> io $ writeFile (path ("t" ++ show i)) - (scWriteExternal tm)) - (zip (HM.toList cache) ([0..] :: [Integer])) + Just (path, cache) -> io $ do + doesDirectoryExist path >>= flip unless (createDirectory path) + index <- mapM (\((tm,stats),i) -> writeFile (path ("t" ++ show i)) + (scWriteExternal tm) >> + return (show (solverStatsGoalSize stats) ++ " " ++ + show (Set.toList $ solverStatsSolvers stats))) + (zip (HM.toList cache) ([0..] :: [Integer])) + writeFile (path "index") (unlines index) Nothing -> return () applyProverToGoal :: (Sequent -> TopLevel (Maybe CEX, SolverStats)) @@ -1033,11 +1052,11 @@ applyProverToGoal f g = do (mb, stats) <- case g_prop of -- If we can convert our goal into a prop, we can use caching Right prop -> lookupInPropCache prop >>= \case - Just () -> -- Use a cached result! - return (Nothing, mempty) + Just stats -> -- Use a cached result! + return (Nothing, stats) _ -> f (goalSequent g) >>= \case (Nothing, stats) -> -- Cache an unsat result! - insertInPropCache prop >> + insertInPropCache prop stats >> return (Nothing, stats) (mb, stats) -> return (mb, stats) -- Otherwise we just call the solver diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 56029d94b5..7167dc385e 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -518,7 +518,7 @@ data TopLevelRW = , rwPPOpts :: PPOpts , rwSharedContext :: SharedContext , rwTheoremDB :: TheoremDB - , rwPropCache :: Maybe (FilePath, HashMap Term ()) + , rwPropCache :: Maybe (FilePath, HashMap Term SolverStats) -- , rwCrucibleLLVMCtx :: Crucible.LLVMContext , rwJVMTrans :: CJ.JVMContext From 70b9be0826d75fa82396d85b1fd60c9ac8b9f5fb Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 9 May 2023 00:16:21 -0400 Subject: [PATCH 05/51] use strings as solver result cache keys since Terms have non-unique hashes --- src/SAWScript/Builtins.hs | 25 +++++++++++-------------- src/SAWScript/Interpreter.hs | 2 +- src/SAWScript/Value.hs | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index fb87bce6b8..f19dc2b03c 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -975,7 +975,7 @@ lookupInPropCache p = opts <- getTopLevelPPOpts gen_tm <- io $ scGeneralizeAllExts sc (unProp p) gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm - case HM.lookup gen_tm cache of + case HM.lookup (scWriteExternal gen_tm) cache of Just ret -> do printOutLnTop Info $ "Using cached result for: " ++ gen_tm_str return $ Just ret _ -> do -- printOutLnTop Info $ "Cache miss on: " ++ show gen_tm_str @@ -993,37 +993,34 @@ insertInPropCache p stats = gen_tm <- io $ scGeneralizeAllExts sc (unProp p) gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm printOutLnTop Info $ "Adding cached result: " ++ gen_tm_str - modify (\rw -> rw { rwPropCache = Just (path, HM.insert gen_tm stats cache) }) + modify (\rw -> rw { rwPropCache = Just (path, HM.insert (scWriteExternal gen_tm) stats cache) }) Nothing -> return () -- | Load a solver result cache from a file -- FIXME: Move somewhere else? loadPropCache :: FilePath -> TopLevel () loadPropCache path = - do sc <- getSharedContext - cache <- io $ loadPropCacheH sc path + do cache <- io $ loadPropCacheH path modify (\rw -> rw { rwPropCache = cache }) -- | Construct a solver result cache from a file -- FIXME: Move somewhere else? -loadPropCacheH :: SharedContext -> FilePath -> - IO (Maybe (FilePath, HashMap Term SolverStats)) -loadPropCacheH sc path = +loadPropCacheH :: FilePath -> IO (Maybe (FilePath, HashMap String SolverStats)) +loadPropCacheH path = doesDirectoryExist path >>= \case True -> do index <- lines <$> readFile (path "index") files <- listDirectory path cache <- catMaybes <$> mapM (processFile index) files return $ Just (path, HM.fromList cache) False -> return $ Just (path, HM.empty) - where processFile :: [String] -> FilePath -> IO (Maybe (Term, SolverStats)) + where processFile :: [String] -> FilePath -> IO (Maybe (String, SolverStats)) processFile index f = case readMaybe (drop 1 f) of Just i | i < length index -> do str <- readFile (path f) - tm <- scReadExternal sc str let (s0, s1) = break (== ' ') (index !! i) case (,) <$> readMaybe s0 <*> readMaybe (drop 1 s1) of Just (sz, ss) -> - return $ Just (length ss `seq` tm, + return $ Just (length ss `seq` str, SolverStats (Set.fromList ss) sz) _ -> return Nothing _ -> return Nothing @@ -1035,10 +1032,10 @@ savePropCache = rwPropCache <$> getTopLevelRW >>= \case Just (path, cache) -> io $ do doesDirectoryExist path >>= flip unless (createDirectory path) - index <- mapM (\((tm,stats),i) -> writeFile (path ("t" ++ show i)) - (scWriteExternal tm) >> - return (show (solverStatsGoalSize stats) ++ " " ++ - show (Set.toList $ solverStatsSolvers stats))) + index <- mapM (\((str,stats),i) -> + writeFile (path ("t" ++ show i)) str >> + return (show (solverStatsGoalSize stats) ++ " " ++ + show (Set.toList $ solverStatsSolvers stats))) (zip (HM.toList cache) ([0..] :: [Integer])) writeFile (path "index") (unlines index) Nothing -> return () diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 2c5d0a9d94..61396b3007 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -477,7 +477,7 @@ buildTopLevelEnv proxy opts = jvmTrans <- CJ.mkInitialJVMContext halloc - cache <- maybe (return Nothing) (loadPropCacheH sc) (solverCache opts) + cache <- maybe (return Nothing) loadPropCacheH (solverCache opts) let rw0 = TopLevelRW { rwValues = valueEnv primsAvail opts bic diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 7167dc385e..4e106d89e1 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -518,7 +518,7 @@ data TopLevelRW = , rwPPOpts :: PPOpts , rwSharedContext :: SharedContext , rwTheoremDB :: TheoremDB - , rwPropCache :: Maybe (FilePath, HashMap Term SolverStats) + , rwPropCache :: Maybe (FilePath, HashMap String SolverStats) -- , rwCrucibleLLVMCtx :: Crucible.LLVMContext , rwJVMTrans :: CJ.JVMContext From 072cf1a1594573d95ecd5b46437f1e8b0fed7aa5 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 9 May 2023 14:58:46 -0400 Subject: [PATCH 06/51] save solver result cache on :q --- saw/SAWScript/REPL/Monad.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/saw/SAWScript/REPL/Monad.hs b/saw/SAWScript/REPL/Monad.hs index 294ea88e60..ed86cc5ee5 100644 --- a/saw/SAWScript/REPL/Monad.hs +++ b/saw/SAWScript/REPL/Monad.hs @@ -104,6 +104,7 @@ import SAWScript.Value , ProofScript(..), showsProofResult, toValue ) import Verifier.SAW (SharedContext) +import SAWScript.Builtins (savePropCache) deriving instance Typeable AIG.Proxy @@ -362,7 +363,8 @@ shouldContinue :: REPL Bool shouldContinue = readRef eContinue stop :: REPL () -stop = modifyRef eContinue (const False) +stop = liftTopLevel savePropCache >> + modifyRef eContinue (const False) unlessBatch :: REPL () -> REPL () unlessBatch body = From d870ea625f28bea800290aad9dfb4126040b9a3c Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 9 May 2023 20:13:12 -0400 Subject: [PATCH 07/51] use sha256 for solver result caching, save cache after each REPL command --- saw-remote-api/src/SAWServer.hs | 2 +- saw-script.cabal | 3 + saw/SAWScript/REPL/Command.hs | 4 +- saw/SAWScript/REPL/Monad.hs | 4 +- src/SAWScript/Builtins.hs | 113 +++------------------------ src/SAWScript/Interpreter.hs | 10 +-- src/SAWScript/SolverCache.hs | 131 ++++++++++++++++++++++++++++++++ src/SAWScript/Value.hs | 16 +++- 8 files changed, 169 insertions(+), 114 deletions(-) create mode 100644 src/SAWScript/SolverCache.hs diff --git a/saw-remote-api/src/SAWServer.hs b/saw-remote-api/src/SAWServer.hs index 578514368c..f448d03ac8 100644 --- a/saw-remote-api/src/SAWServer.hs +++ b/saw-remote-api/src/SAWServer.hs @@ -233,7 +233,7 @@ initialState readFileFn = , rwMRSolverEnv = emptyMREnv , rwPPOpts = defaultPPOpts , rwTheoremDB = db - , rwPropCache = Nothing + , rwSolverCache = Nothing , rwSharedContext = sc , rwJVMTrans = jvmTrans , rwPrimsAvail = mempty diff --git a/saw-script.cabal b/saw-script.cabal index cff9ea9dc1..8d2bd084f5 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -32,6 +32,7 @@ library , bv-sized >= 1.0 && < 1.1 , containers , constraints >= 0.6 + , cryptohash-sha256 , cryptol , cryptol-saw-core , crucible >= 0.4 @@ -50,6 +51,7 @@ library , free , haskeline , heapster-saw + , hex-text , hobbits >= 1.3.1 , galois-dwarf >= 0.2.2 , IfElse @@ -125,6 +127,7 @@ library SAWScript.Position SAWScript.SBVParser SAWScript.SBVModel + SAWScript.SolverCache SAWScript.Token SAWScript.TopLevel SAWScript.MGU diff --git a/saw/SAWScript/REPL/Command.hs b/saw/SAWScript/REPL/Command.hs index 31aff23112..3912f5fe18 100644 --- a/saw/SAWScript/REPL/Command.hs +++ b/saw/SAWScript/REPL/Command.hs @@ -60,6 +60,7 @@ import SAWScript.Interpreter (interpretStmt) import qualified SAWScript.Lexer (lexSAW) import qualified SAWScript.Parser (parseStmtSemi, parseExpression) import SAWScript.TopLevel (TopLevelRW(..)) +import SAWScript.SolverCache (saveSolverCache) -- Commands -------------------------------------------------------------------- @@ -144,7 +145,8 @@ genHelp cs = map cmdHelp cs runCommand :: Command -> REPL () runCommand c = case c of - Command cmd -> exceptionProtect cmd + Command cmd -> do exceptionProtect cmd + void $ liftTopLevel (saveSolverCache True) Unknown cmd -> io (putStrLn ("Unknown command: " ++ cmd)) diff --git a/saw/SAWScript/REPL/Monad.hs b/saw/SAWScript/REPL/Monad.hs index ed86cc5ee5..294ea88e60 100644 --- a/saw/SAWScript/REPL/Monad.hs +++ b/saw/SAWScript/REPL/Monad.hs @@ -104,7 +104,6 @@ import SAWScript.Value , ProofScript(..), showsProofResult, toValue ) import Verifier.SAW (SharedContext) -import SAWScript.Builtins (savePropCache) deriving instance Typeable AIG.Proxy @@ -363,8 +362,7 @@ shouldContinue :: REPL Bool shouldContinue = readRef eContinue stop :: REPL () -stop = liftTopLevel savePropCache >> - modifyRef eContinue (const False) +stop = modifyRef eContinue (const False) unlessBatch :: REPL () -> REPL () unlessBatch body = diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index f19dc2b03c..e0173e690c 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -36,11 +36,9 @@ import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy.UTF8 as B import qualified Data.IntMap as IntMap import Data.IORef -import Data.List (isPrefixOf, isInfixOf, sortOn) -import qualified Data.HashMap.Strict as HM -import Data.HashMap.Strict (HashMap) +import Data.List (isPrefixOf, isInfixOf) import qualified Data.Map as Map -import Data.Maybe (fromMaybe, catMaybes) +import Data.Maybe (fromMaybe) import Data.Set (Set) import qualified Data.Set as Set import Data.Text (Text) @@ -127,6 +125,7 @@ import SAWScript.Crucible.Common (PathSatSolver(..)) import SAWScript.TopLevel import qualified SAWScript.Value as SV import SAWScript.Value (ProofScript, printOutLnTop, AIGNetwork) +import SAWScript.SolverCache import SAWScript.Crucible.Common.MethodSpec (ppTypedTermType) import SAWScript.Prover.Util(checkBooleanSchema) @@ -956,108 +955,18 @@ proveUnintSBV conf unints = unintSet <- SV.scriptTopLevel (resolveNames unints) wrapProver (Prover.proveUnintSBV conf unintSet timeout) --- | Generalize over the all external constants in a given term by --- wrapping the term with foralls and replacing the external constant --- occurrences with the appropriate local variables. --- FIXME: Move to SharedTerm.hs -scGeneralizeAllExts :: SharedContext -> Term -> IO Term -scGeneralizeAllExts sc tm = - let allexts = sortOn (toShortName . ecName) $ getAllExts tm - in scGeneralizeExts sc allexts tm - --- | Lookup a 'Prop' in the solver result cache --- FIXME: Move somewhere else? -lookupInPropCache :: Prop -> TopLevel (Maybe SolverStats) -lookupInPropCache p = - rwPropCache <$> get >>= \case - Just (_, cache) -> do - sc <- getSharedContext - opts <- getTopLevelPPOpts - gen_tm <- io $ scGeneralizeAllExts sc (unProp p) - gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm - case HM.lookup (scWriteExternal gen_tm) cache of - Just ret -> do printOutLnTop Info $ "Using cached result for: " ++ gen_tm_str - return $ Just ret - _ -> do -- printOutLnTop Info $ "Cache miss on: " ++ show gen_tm_str - return Nothing - _ -> return Nothing - --- | Add a 'Prop' to the solver result cache --- FIXME: Move somewhere else? -insertInPropCache :: Prop -> SolverStats -> TopLevel () -insertInPropCache p stats = - rwPropCache <$> get >>= \case - Just (path, cache) -> do - sc <- getSharedContext - opts <- getTopLevelPPOpts - gen_tm <- io $ scGeneralizeAllExts sc (unProp p) - gen_tm_str <- liftIO $ scShowTerm sc opts gen_tm - printOutLnTop Info $ "Adding cached result: " ++ gen_tm_str - modify (\rw -> rw { rwPropCache = Just (path, HM.insert (scWriteExternal gen_tm) stats cache) }) - Nothing -> return () - --- | Load a solver result cache from a file --- FIXME: Move somewhere else? -loadPropCache :: FilePath -> TopLevel () -loadPropCache path = - do cache <- io $ loadPropCacheH path - modify (\rw -> rw { rwPropCache = cache }) - --- | Construct a solver result cache from a file --- FIXME: Move somewhere else? -loadPropCacheH :: FilePath -> IO (Maybe (FilePath, HashMap String SolverStats)) -loadPropCacheH path = - doesDirectoryExist path >>= \case - True -> do index <- lines <$> readFile (path "index") - files <- listDirectory path - cache <- catMaybes <$> mapM (processFile index) files - return $ Just (path, HM.fromList cache) - False -> return $ Just (path, HM.empty) - where processFile :: [String] -> FilePath -> IO (Maybe (String, SolverStats)) - processFile index f = case readMaybe (drop 1 f) of - Just i | i < length index -> do - str <- readFile (path f) - let (s0, s1) = break (== ' ') (index !! i) - case (,) <$> readMaybe s0 <*> readMaybe (drop 1 s1) of - Just (sz, ss) -> - return $ Just (length ss `seq` str, - SolverStats (Set.fromList ss) sz) - _ -> return Nothing - _ -> return Nothing - --- | Save the current solver result cache to a file --- FIXME: Move somewhere else? -savePropCache :: TopLevel () -savePropCache = - rwPropCache <$> getTopLevelRW >>= \case - Just (path, cache) -> io $ do - doesDirectoryExist path >>= flip unless (createDirectory path) - index <- mapM (\((str,stats),i) -> - writeFile (path ("t" ++ show i)) str >> - return (show (solverStatsGoalSize stats) ++ " " ++ - show (Set.toList $ solverStatsSolvers stats))) - (zip (HM.toList cache) ([0..] :: [Integer])) - writeFile (path "index") (unlines index) - Nothing -> return () - applyProverToGoal :: (Sequent -> TopLevel (Maybe CEX, SolverStats)) -> ProofGoal -> TopLevel (SolverStats, SolveResult) applyProverToGoal f g = do - sc <- getSharedContext - g_prop <- io . Ex.try $ sequentToProp sc (goalSequent g) - (mb, stats) <- case g_prop of - -- If we can convert our goal into a prop, we can use caching - Right prop -> lookupInPropCache prop >>= \case - Just stats -> -- Use a cached result! - return (Nothing, stats) - _ -> f (goalSequent g) >>= \case - (Nothing, stats) -> -- Cache an unsat result! - insertInPropCache prop stats >> - return (Nothing, stats) - (mb, stats) -> return (mb, stats) - -- Otherwise we just call the solver - Left (_ :: Ex.SomeException) -> f (goalSequent g) + mb_cache_key <- propToSolverCacheKey (goalSequent g) + (mb, stats) <- case mb_cache_key of + Just k -> lookupInSolverCache k >>= \case + Just res -> return res + _ -> do res <- f (goalSequent g) + insertInSolverCache k res + return res + _ -> f (goalSequent g) case mb of Nothing -> return (stats, SolveSuccess (SolverEvidence stats (goalSequent g))) Just a -> return (stats, SolveCounterexample a) diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 61396b3007..d55d3ba0ff 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -67,6 +67,7 @@ import SAWScript.Parser (parseSchema) import SAWScript.TopLevel import SAWScript.Utils import SAWScript.Value +import SAWScript.SolverCache import SAWScript.Proof (newTheoremDB) import SAWScript.Prover.Rewrite(basic_ss) import SAWScript.Prover.Exporter @@ -402,7 +403,6 @@ interpretFile file runMain = mapM_ stmtWithPrint stmts when runMain interpretMain writeVerificationSummary - savePropCache stmtWithPrint s = do let withPos str = unlines $ ("[output] at " ++ show (SS.getPos s) ++ ": ") : @@ -477,7 +477,7 @@ buildTopLevelEnv proxy opts = jvmTrans <- CJ.mkInitialJVMContext halloc - cache <- maybe (return Nothing) loadPropCacheH (solverCache opts) + cache <- maybe (return Nothing) loadSolverCacheH (solverCache opts) let rw0 = TopLevelRW { rwValues = valueEnv primsAvail opts bic @@ -491,7 +491,7 @@ buildTopLevelEnv proxy opts = , rwPPOpts = SAWScript.Value.defaultPPOpts , rwSharedContext = sc , rwTheoremDB = thmDB - , rwPropCache = cache + , rwSolverCache = cache , rwJVMTrans = jvmTrans , rwPrimsAvail = primsAvail , rwSMTArrayMemoryModel = False @@ -531,7 +531,7 @@ processFile proxy opts file mbSubshell mbProofSubshell = do Nothing -> ro' Just m -> ro'{ roProofSubshell = m } file' <- canonicalizePath file - _ <- runTopLevel (interpretFile file' True) ro'' rw + _ <- runTopLevel (interpretFile file' True >> saveSolverCache False) ro'' rw `X.catch` (handleException opts) return () @@ -1062,7 +1062,7 @@ primitives = Map.fromList ] , prim "set_solver_cache_path" "String -> TopLevel ()" - (pureVal loadPropCache) + (pureVal loadSolverCache) Experimental [ "Enable solver result caching and set the path the cache should be" , " loaded from and saved to." diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs new file mode 100644 index 0000000000..f917da8345 --- /dev/null +++ b/src/SAWScript/SolverCache.hs @@ -0,0 +1,131 @@ +{- | +Module : SAWScript.SolverCache +Description : Caching SMT solver results for SAWScript +License : BSD3 +Maintainer : m-yac +Stability : provisional +-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} + +module SAWScript.SolverCache where + +import Control.Monad (unless) +import Control.Monad.State (get, modify) +import Control.Exception (try, SomeException) +import Data.List (sortOn) +import Data.Maybe (catMaybes) +import System.Directory (doesDirectoryExist, listDirectory, createDirectory) +import System.FilePath (()) +import Text.Read (readMaybe) + +import qualified Data.HashMap.Strict as HM +import qualified Data.HashSet as HS +import qualified Data.ByteString as BS +import qualified Data.Text as T +import Data.Text.Encoding +import Text.Hex + +import Crypto.Hash.SHA256 as SHA256 + +import Verifier.SAW.ExternalFormat +import Verifier.SAW.SharedTerm +import Verifier.SAW.TypedAST + +import SAWScript.Options +import SAWScript.Value +import SAWScript.Proof +import SAWScript.Prover.SolverStats + + +-- Helper Functions ------------------------------------------------------------ + +-- | Generalize over the all external constants in a given term by +-- wrapping the term with foralls and replacing the external constant +-- occurrences with the appropriate local variables. +-- FIXME: Move to SharedTerm.hs +scGeneralizeAllExts :: SharedContext -> Term -> IO Term +scGeneralizeAllExts sc tm = + let allexts = sortOn (toShortName . ecName) $ getAllExts tm + in scGeneralizeExts sc allexts tm + + +-- Conversions ----------------------------------------------------------------- + +-- | Convert a 'SolverCacheKey' to a hexadecimal 'String' +solverCacheKeyToHex :: SolverCacheKey -> String +solverCacheKeyToHex = T.unpack . encodeHex + +-- | Convert a hexadecimal 'String' to a 'SolverCacheKey' +solverCacheKeyFromHex :: String -> Maybe SolverCacheKey +solverCacheKeyFromHex = decodeHex . T.pack + +-- | Convert a 'Sequent' to a 'SolverCacheKey' +propToSolverCacheKey :: Sequent -> TopLevel (Maybe SolverCacheKey) +propToSolverCacheKey sq = getSharedContext >>= \sc -> io $ do + try (sequentToProp sc sq) >>= \case + Right p -> Just . SHA256.hash . encodeUtf8 . T.pack . scWriteExternal <$> + scGeneralizeAllExts sc (unProp p) + Left (_ :: SomeException) -> return Nothing + + +-- Modifying the SolverCache --------------------------------------------------- + +-- | Lookup a 'SolverCacheKey' in the solver result cache +lookupInSolverCache :: SolverCacheKey -> TopLevel (Maybe SolverCacheValue) +lookupInSolverCache k = + rwSolverCache <$> get >>= \case + Just cache | Just v <- HM.lookup k (solverCacheMap cache) -> do + printOutLnTop Info $ "Using cached result (" ++ solverCacheKeyToHex k ++ ")" + return $ Just v + _ -> return Nothing + +-- | Add a 'SolverCacheValue' to the solver result cache +insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> TopLevel () +insertInSolverCache k v@(Nothing, _) = + rwSolverCache <$> get >>= \case + Just cache -> do + printOutLnTop Info $ "Adding cached result (" ++ solverCacheKeyToHex k ++ ")" + let cache' = cache { solverCacheMap = HM.insert k v (solverCacheMap cache) + , solverCacheUnsaved = HS.insert k <$> solverCacheUnsaved cache } + modify (\rw -> rw { rwSolverCache = Just cache' }) + Nothing -> return () +insertInSolverCache _ _ = return () + +-- | Load a solver result cache from a file +loadSolverCache :: FilePath -> TopLevel () +loadSolverCache path = + do cache <- io $ loadSolverCacheH path + modify (\rw -> rw { rwSolverCache = cache }) + +-- | Construct a solver result cache from a file +loadSolverCacheH :: FilePath -> IO (Maybe SolverCache) +loadSolverCacheH path = + doesDirectoryExist path >>= \case + True -> do files <- listDirectory path + cache <- catMaybes <$> mapM processFile files + return $ Just $ SolverCache path (HM.fromList cache) Nothing + False -> return $ Just $ SolverCache path HM.empty Nothing + where processFile :: FilePath -> IO (Maybe (SolverCacheKey, SolverCacheValue)) + processFile f = case solverCacheKeyFromHex (drop 1 f) of + Just k | f !! 0 == 'x' && BS.length k == 32 -> + fmap (\(sz,ss) -> (k, (Nothing, SolverStats ss sz))) <$> + readMaybe <$> readFile (path f) + _ -> return Nothing + +-- | Save the current solver result cache to a file, tracking unsaved results +-- from now on iff the given flag is 'True' +saveSolverCache :: Bool -> TopLevel () +saveSolverCache trackUnsaved = + rwSolverCache <$> getTopLevelRW >>= \case + Just cache -> do + io $ doesDirectoryExist (solverCachePath cache) >>= flip unless (createDirectory (solverCachePath cache)) + mapM_ (\(k,v) -> io $ writeFile (solverCachePath cache ("x" ++ solverCacheKeyToHex k)) (showValue v)) + (maybe (HM.toList $ solverCacheMap cache) + (map (\k -> (k, (solverCacheMap cache) HM.! k)) . HS.toList) + (solverCacheUnsaved cache)) + let cache' = cache { solverCacheUnsaved = if trackUnsaved then Just HS.empty else Nothing } + modify (\rw -> rw { rwSolverCache = Just cache' }) + Nothing -> return () + where showValue (_, SolverStats ss sz) = show (sz, ss) \ No newline at end of file diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 4e106d89e1..8d1b3c0dd9 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -49,11 +49,13 @@ import Control.Monad.Trans.Class (MonadTrans(lift)) import Data.IORef import Data.Foldable(foldrM) import Data.List ( intersperse ) -import Data.HashMap.Strict (HashMap) +import Data.HashMap.Strict ( HashMap ) +import Data.HashSet ( HashSet ) import qualified Data.Map as M import Data.Map ( Map ) import Data.Set ( Set ) import Data.Text (Text, pack, unpack) +import Data.ByteString (ByteString) import Data.Parameterized.Some import Data.Typeable import GHC.Generics (Generic, Generic1) @@ -505,6 +507,16 @@ data TopLevelRO = , roLocalEnv :: LocalEnv } +type SolverCacheKey = ByteString +type SolverCacheValue = (Maybe CEX, SolverStats) + +data SolverCache = + SolverCache + { solverCachePath :: FilePath + , solverCacheMap :: HashMap SolverCacheKey SolverCacheValue + , solverCacheUnsaved :: Maybe (HashSet SolverCacheKey) + } + data TopLevelRW = TopLevelRW { rwValues :: Map SS.LName Value @@ -518,7 +530,7 @@ data TopLevelRW = , rwPPOpts :: PPOpts , rwSharedContext :: SharedContext , rwTheoremDB :: TheoremDB - , rwPropCache :: Maybe (FilePath, HashMap String SolverStats) + , rwSolverCache :: Maybe SolverCache -- , rwCrucibleLLVMCtx :: Crucible.LLVMContext , rwJVMTrans :: CJ.JVMContext From f05bc4001032be9f2ac430bac48e492faf429106 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 9 May 2023 20:25:40 -0400 Subject: [PATCH 08/51] remove 'x' prefix from solver result cache files --- src/SAWScript/SolverCache.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index f917da8345..16a9e9d5e5 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -108,8 +108,8 @@ loadSolverCacheH path = return $ Just $ SolverCache path (HM.fromList cache) Nothing False -> return $ Just $ SolverCache path HM.empty Nothing where processFile :: FilePath -> IO (Maybe (SolverCacheKey, SolverCacheValue)) - processFile f = case solverCacheKeyFromHex (drop 1 f) of - Just k | f !! 0 == 'x' && BS.length k == 32 -> + processFile f = case solverCacheKeyFromHex f of + Just k | BS.length k == 32 -> fmap (\(sz,ss) -> (k, (Nothing, SolverStats ss sz))) <$> readMaybe <$> readFile (path f) _ -> return Nothing @@ -120,8 +120,10 @@ saveSolverCache :: Bool -> TopLevel () saveSolverCache trackUnsaved = rwSolverCache <$> getTopLevelRW >>= \case Just cache -> do - io $ doesDirectoryExist (solverCachePath cache) >>= flip unless (createDirectory (solverCachePath cache)) - mapM_ (\(k,v) -> io $ writeFile (solverCachePath cache ("x" ++ solverCacheKeyToHex k)) (showValue v)) + ex <- io $ doesDirectoryExist (solverCachePath cache) + unless ex $ io $ createDirectory (solverCachePath cache) + mapM_ (\(k,v) -> io $ writeFile (solverCachePath cache solverCacheKeyToHex k) + (showValue v)) (maybe (HM.toList $ solverCacheMap cache) (map (\k -> (k, (solverCacheMap cache) HM.! k)) . HS.toList) (solverCacheUnsaved cache)) From cd5cc1b2fb4fe4c9e8e00135750e1211db29ae33 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 10 May 2023 14:14:47 -0400 Subject: [PATCH 09/51] improve hash function for SolverCacheKey, reorganize SolverCache.hs --- saw-script.cabal | 1 + saw/SAWScript/REPL/Command.hs | 5 +- src/SAWScript/Builtins.hs | 29 +++++-- src/SAWScript/Interpreter.hs | 9 +- src/SAWScript/SolverCache.hs | 156 +++++++++++++++++++--------------- src/SAWScript/Value.hs | 26 +++--- 6 files changed, 134 insertions(+), 92 deletions(-) diff --git a/saw-script.cabal b/saw-script.cabal index 8d2bd084f5..c8855ab201 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -49,6 +49,7 @@ library , filepath , flexdis86 , free + , hashable , haskeline , heapster-saw , hex-text diff --git a/saw/SAWScript/REPL/Command.hs b/saw/SAWScript/REPL/Command.hs index 3912f5fe18..49aadf0a63 100644 --- a/saw/SAWScript/REPL/Command.hs +++ b/saw/SAWScript/REPL/Command.hs @@ -59,6 +59,7 @@ import SAWScript.MGU (checkDecl) import SAWScript.Interpreter (interpretStmt) import qualified SAWScript.Lexer (lexSAW) import qualified SAWScript.Parser (parseStmtSemi, parseExpression) +import SAWScript.Value (onSolverCache) import SAWScript.TopLevel (TopLevelRW(..)) import SAWScript.SolverCache (saveSolverCache) @@ -145,8 +146,8 @@ genHelp cs = map cmdHelp cs runCommand :: Command -> REPL () runCommand c = case c of - Command cmd -> do exceptionProtect cmd - void $ liftTopLevel (saveSolverCache True) + Command cmd -> exceptionProtect cmd >> + void (liftTopLevel $ onSolverCache $ saveSolverCache True) Unknown cmd -> io (putStrLn ("Unknown command: " ++ cmd)) diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index e0173e690c..8a24cc7818 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -955,17 +955,34 @@ proveUnintSBV conf unints = unintSet <- SV.scriptTopLevel (resolveNames unints) wrapProver (Prover.proveUnintSBV conf unintSet timeout) +-- | Load a solver result cache from a file and set it as the current +-- 'SolverCache', or if no such file exists, set the current 'SolverCache' to +-- an empty 'SolverCache' with its path set to the given value +setSolverCachePath :: FilePath -> TopLevel () +setSolverCachePath path = do + opts <- getOptions + cache <- io $ loadSolverCache opts path + modify (\rw -> rw { rwSolverCache = Just cache }) + +-- | Given a continuation which calls a prover, call the continuation on the +-- 'goalSequent' of the given 'ProofGoal' and return a 'SolveResult'. If there +-- is a 'SolverCache', do not call the continuation if the goal has an already +-- cached result, and otherwise save the result of the call to the cache. applyProverToGoal :: (Sequent -> TopLevel (Maybe CEX, SolverStats)) -> ProofGoal -> TopLevel (SolverStats, SolveResult) applyProverToGoal f g = do - mb_cache_key <- propToSolverCacheKey (goalSequent g) + sc <- getSharedContext + mb_cache_key <- io $ propToSolverCacheKey sc (goalSequent g) (mb, stats) <- case mb_cache_key of - Just k -> lookupInSolverCache k >>= \case - Just res -> return res - _ -> do res <- f (goalSequent g) - insertInSolverCache k res - return res + -- If we have a solver result cache and a valid key... + Just k -> SV.onSolverCache (lookupInSolverCache k) >>= \case + Just v -> -- use a cached result if we have one, + return v + _ -> -- or cache the result of the call otherwise. + f (goalSequent g) >>= \v -> + SV.onSolverCache (insertInSolverCache k v) >> + return v _ -> f (goalSequent g) case mb of Nothing -> return (stats, SolveSuccess (SolverEvidence stats (goalSequent g))) diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index d55d3ba0ff..5f0005a4f3 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -477,7 +477,9 @@ buildTopLevelEnv proxy opts = jvmTrans <- CJ.mkInitialJVMContext halloc - cache <- maybe (return Nothing) loadSolverCacheH (solverCache opts) + cache <- maybe (return Nothing) + (fmap Just . loadSolverCache opts) + (solverCache opts) let rw0 = TopLevelRW { rwValues = valueEnv primsAvail opts bic @@ -531,7 +533,8 @@ processFile proxy opts file mbSubshell mbProofSubshell = do Nothing -> ro' Just m -> ro'{ roProofSubshell = m } file' <- canonicalizePath file - _ <- runTopLevel (interpretFile file' True >> saveSolverCache False) ro'' rw + _ <- runTopLevel (interpretFile file' True >> + onSolverCache (saveSolverCache False)) ro'' rw `X.catch` (handleException opts) return () @@ -1062,7 +1065,7 @@ primitives = Map.fromList ] , prim "set_solver_cache_path" "String -> TopLevel ()" - (pureVal loadSolverCache) + (pureVal setSolverCachePath) Experimental [ "Enable solver result caching and set the path the cache should be" , " loaded from and saved to." diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 16a9e9d5e5..11f2121325 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -11,8 +11,7 @@ Stability : provisional module SAWScript.SolverCache where -import Control.Monad (unless) -import Control.Monad.State (get, modify) +import Control.Monad (unless, when) import Control.Exception (try, SomeException) import Data.List (sortOn) import Data.Maybe (catMaybes) @@ -20,21 +19,28 @@ import System.Directory (doesDirectoryExist, listDirectory, createDirectory) import System.FilePath (()) import Text.Read (readMaybe) +import Data.Hashable +import Data.Bits (shiftL, (.|.)) + import qualified Data.HashMap.Strict as HM -import qualified Data.HashSet as HS -import qualified Data.ByteString as BS -import qualified Data.Text as T +import Data.HashMap.Strict (HashMap) + +import qualified Data.HashSet as HS +import Data.HashSet (HashSet) + +import qualified Data.Text as T import Data.Text.Encoding import Text.Hex -import Crypto.Hash.SHA256 as SHA256 +import qualified Data.ByteString as BS + +import qualified Crypto.Hash.SHA256 as SHA256 import Verifier.SAW.ExternalFormat import Verifier.SAW.SharedTerm import Verifier.SAW.TypedAST import SAWScript.Options -import SAWScript.Value import SAWScript.Proof import SAWScript.Prover.SolverStats @@ -51,83 +57,97 @@ scGeneralizeAllExts sc tm = in scGeneralizeExts sc allexts tm --- Conversions ----------------------------------------------------------------- +-- Solver Cache Keys ----------------------------------------------------------- + +-- | The key type for 'SolverCache': SHA256 hashes +newtype SolverCacheKey = SolverCacheKey ByteString deriving Eq + +-- | Truncate a 'SolverCacheKey' (i.e. a SHA256 hash) to an 'Int', used to give +-- the type a fast 'Hashable' instance +solverCacheKeyInt :: SolverCacheKey -> Int +solverCacheKeyInt (SolverCacheKey bs) = + BS.foldl' (\a b -> a `shiftL` 8 .|. fromIntegral b) 0 (BS.take 8 bs) + +instance Hashable SolverCacheKey where + hash = solverCacheKeyInt + hashWithSalt s = hashWithSalt s . solverCacheKeyInt -- | Convert a 'SolverCacheKey' to a hexadecimal 'String' solverCacheKeyToHex :: SolverCacheKey -> String -solverCacheKeyToHex = T.unpack . encodeHex +solverCacheKeyToHex (SolverCacheKey bs) = T.unpack $ encodeHex bs -- | Convert a hexadecimal 'String' to a 'SolverCacheKey' solverCacheKeyFromHex :: String -> Maybe SolverCacheKey -solverCacheKeyFromHex = decodeHex . T.pack +solverCacheKeyFromHex x = fmap SolverCacheKey $ decodeHex $ T.pack x --- | Convert a 'Sequent' to a 'SolverCacheKey' -propToSolverCacheKey :: Sequent -> TopLevel (Maybe SolverCacheKey) -propToSolverCacheKey sq = getSharedContext >>= \sc -> io $ do +-- | Hash the 'String' representation ('scWriteExternal') of a 'Sequent' using +-- SHA256 to get a 'SolverCacheKey' +propToSolverCacheKey :: SharedContext -> Sequent -> IO (Maybe SolverCacheKey) +propToSolverCacheKey sc sq = do try (sequentToProp sc sq) >>= \case - Right p -> Just . SHA256.hash . encodeUtf8 . T.pack . scWriteExternal <$> + Right p -> Just . SolverCacheKey . SHA256.hash . + encodeUtf8 . T.pack . scWriteExternal <$> scGeneralizeAllExts sc (unProp p) Left (_ :: SomeException) -> return Nothing --- Modifying the SolverCache --------------------------------------------------- +-- The Solver Cache ------------------------------------------------------------ + +type SolverCacheValue = (Maybe CEX, SolverStats) + +-- | A set of cached solver results as well as a 'FilePath' indicating where +-- new additions to the cache should be saved +data SolverCache = + SolverCache + { solverCachePath :: FilePath + , solverCacheMap :: HashMap SolverCacheKey SolverCacheValue + , solverCacheUnsaved :: Maybe (HashSet SolverCacheKey) + } + +-- | Load a solver result cache from a file, or if no such file exists, +-- construct an empty 'SolverCache' with its path set to the given value +loadSolverCache :: Options -> FilePath -> IO SolverCache +loadSolverCache opts path = do + ex <- doesDirectoryExist path + mbs <- if ex then listDirectory path >>= mapM processFile + else return [] + when ex $ printOutLn opts Info ("Loaded solver result cache with " ++ show (length mbs) ++ " entries") + return $ SolverCache path (HM.fromList $ catMaybes mbs) Nothing + where processFile :: FilePath -> IO (Maybe (SolverCacheKey, SolverCacheValue)) + processFile f = case solverCacheKeyFromHex f of + Just k -> fmap (\(sz,ss) -> (k, (Nothing, SolverStats ss sz))) <$> + readMaybe <$> readFile (path f) + _ -> return Nothing + +-- | A stateful operation on a 'SolverCache', returning a value of the given type +type SolverCacheOp a = Options -> SolverCache -> IO (a, SolverCache) -- | Lookup a 'SolverCacheKey' in the solver result cache -lookupInSolverCache :: SolverCacheKey -> TopLevel (Maybe SolverCacheValue) -lookupInSolverCache k = - rwSolverCache <$> get >>= \case - Just cache | Just v <- HM.lookup k (solverCacheMap cache) -> do - printOutLnTop Info $ "Using cached result (" ++ solverCacheKeyToHex k ++ ")" - return $ Just v - _ -> return Nothing +lookupInSolverCache :: SolverCacheKey -> SolverCacheOp (Maybe SolverCacheValue) +lookupInSolverCache k opts cache = + case HM.lookup k (solverCacheMap cache) of + Just v -> printOutLn opts Info ("Using cached result (" ++ solverCacheKeyToHex k ++ ")" ++ show (solverCacheKeyInt k)) >> + return (Just v, cache) + _ -> return (Nothing, cache) -- | Add a 'SolverCacheValue' to the solver result cache -insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> TopLevel () -insertInSolverCache k v@(Nothing, _) = - rwSolverCache <$> get >>= \case - Just cache -> do - printOutLnTop Info $ "Adding cached result (" ++ solverCacheKeyToHex k ++ ")" - let cache' = cache { solverCacheMap = HM.insert k v (solverCacheMap cache) - , solverCacheUnsaved = HS.insert k <$> solverCacheUnsaved cache } - modify (\rw -> rw { rwSolverCache = Just cache' }) - Nothing -> return () -insertInSolverCache _ _ = return () - --- | Load a solver result cache from a file -loadSolverCache :: FilePath -> TopLevel () -loadSolverCache path = - do cache <- io $ loadSolverCacheH path - modify (\rw -> rw { rwSolverCache = cache }) - --- | Construct a solver result cache from a file -loadSolverCacheH :: FilePath -> IO (Maybe SolverCache) -loadSolverCacheH path = - doesDirectoryExist path >>= \case - True -> do files <- listDirectory path - cache <- catMaybes <$> mapM processFile files - return $ Just $ SolverCache path (HM.fromList cache) Nothing - False -> return $ Just $ SolverCache path HM.empty Nothing - where processFile :: FilePath -> IO (Maybe (SolverCacheKey, SolverCacheValue)) - processFile f = case solverCacheKeyFromHex f of - Just k | BS.length k == 32 -> - fmap (\(sz,ss) -> (k, (Nothing, SolverStats ss sz))) <$> - readMaybe <$> readFile (path f) - _ -> return Nothing +insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () +insertInSolverCache k v@(Nothing, _) opts cache = + printOutLn opts Info ("Adding cached result (" ++ solverCacheKeyToHex k ++ ")" ++ show (solverCacheKeyInt k)) >> + return ((), cache { solverCacheMap = HM.insert k v (solverCacheMap cache) + , solverCacheUnsaved = HS.insert k <$> solverCacheUnsaved cache }) +insertInSolverCache _ _ _ cache = return ((), cache) -- | Save the current solver result cache to a file, tracking unsaved results -- from now on iff the given flag is 'True' -saveSolverCache :: Bool -> TopLevel () -saveSolverCache trackUnsaved = - rwSolverCache <$> getTopLevelRW >>= \case - Just cache -> do - ex <- io $ doesDirectoryExist (solverCachePath cache) - unless ex $ io $ createDirectory (solverCachePath cache) - mapM_ (\(k,v) -> io $ writeFile (solverCachePath cache solverCacheKeyToHex k) - (showValue v)) - (maybe (HM.toList $ solverCacheMap cache) - (map (\k -> (k, (solverCacheMap cache) HM.! k)) . HS.toList) - (solverCacheUnsaved cache)) - let cache' = cache { solverCacheUnsaved = if trackUnsaved then Just HS.empty else Nothing } - modify (\rw -> rw { rwSolverCache = Just cache' }) - Nothing -> return () - where showValue (_, SolverStats ss sz) = show (sz, ss) \ No newline at end of file +saveSolverCache :: Bool -> SolverCacheOp () +saveSolverCache trackUnsaved _ cache = do + ex <- doesDirectoryExist (solverCachePath cache) + unless ex $ createDirectory (solverCachePath cache) + mapM_ (\(k,v) -> writeFile (solverCachePath cache solverCacheKeyToHex k) + (showValue v)) + (maybe (HM.toList $ solverCacheMap cache) + (map (\k -> (k, (solverCacheMap cache) HM.! k)) . HS.toList) + (solverCacheUnsaved cache)) + return ((), cache { solverCacheUnsaved = if trackUnsaved then Just HS.empty else Nothing }) + where showValue (_, SolverStats ss sz) = show (sz, ss) diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 8d1b3c0dd9..d0b6eee45b 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -49,13 +49,10 @@ import Control.Monad.Trans.Class (MonadTrans(lift)) import Data.IORef import Data.Foldable(foldrM) import Data.List ( intersperse ) -import Data.HashMap.Strict ( HashMap ) -import Data.HashSet ( HashSet ) import qualified Data.Map as M import Data.Map ( Map ) import Data.Set ( Set ) import Data.Text (Text, pack, unpack) -import Data.ByteString (ByteString) import Data.Parameterized.Some import Data.Typeable import GHC.Generics (Generic, Generic1) @@ -81,6 +78,7 @@ import SAWScript.Options (Options(printOutFn),printOutLn,Verbosity(..)) import SAWScript.Proof import SAWScript.Prover.SolverStats import SAWScript.Prover.MRSolver.Term as MRSolver +import SAWScript.SolverCache import SAWScript.Crucible.LLVM.Skeleton import SAWScript.X86 (X86Unsupported(..), X86Error(..)) import SAWScript.Yosys.IR @@ -507,16 +505,6 @@ data TopLevelRO = , roLocalEnv :: LocalEnv } -type SolverCacheKey = ByteString -type SolverCacheValue = (Maybe CEX, SolverStats) - -data SolverCache = - SolverCache - { solverCachePath :: FilePath - , solverCacheMap :: HashMap SolverCacheKey SolverCacheValue - , solverCacheUnsaved :: Maybe (HashSet SolverCacheKey) - } - data TopLevelRW = TopLevelRW { rwValues :: Map SS.LName Value @@ -746,6 +734,18 @@ recordProof v = do rw <- getTopLevelRW putTopLevelRW rw { rwProofs = toValue v : rwProofs rw } +-- | Perform a (possibly) stateful operation on the 'SolverCache' returning a +-- value of type @a@, or @mempty@ if there is no 'SolverCache' +onSolverCache :: Monoid a => SolverCacheOp a -> TopLevel a +onSolverCache f = + do opts <- getOptions + rw <- getTopLevelRW + case rwSolverCache rw of + Just cache -> do (ret, cache') <- io $ f opts cache + putTopLevelRW rw { rwSolverCache = Just cache' } + return ret + Nothing -> return mempty + -- | Access the current state of Java Class translation getJVMTrans :: TopLevel CJ.JVMContext getJVMTrans = gets rwJVMTrans From e5dcdda6e905579d83ac37b835878381585022e6 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 10 May 2023 17:23:35 -0400 Subject: [PATCH 10/51] load and save cached results only when needed --- saw/SAWScript/REPL/Command.hs | 5 +- src/SAWScript/Builtins.hs | 9 ---- src/SAWScript/Interpreter.hs | 34 +++++++++++--- src/SAWScript/SolverCache.hs | 86 +++++++++++++++++------------------ 4 files changed, 72 insertions(+), 62 deletions(-) diff --git a/saw/SAWScript/REPL/Command.hs b/saw/SAWScript/REPL/Command.hs index 49aadf0a63..31aff23112 100644 --- a/saw/SAWScript/REPL/Command.hs +++ b/saw/SAWScript/REPL/Command.hs @@ -59,9 +59,7 @@ import SAWScript.MGU (checkDecl) import SAWScript.Interpreter (interpretStmt) import qualified SAWScript.Lexer (lexSAW) import qualified SAWScript.Parser (parseStmtSemi, parseExpression) -import SAWScript.Value (onSolverCache) import SAWScript.TopLevel (TopLevelRW(..)) -import SAWScript.SolverCache (saveSolverCache) -- Commands -------------------------------------------------------------------- @@ -146,8 +144,7 @@ genHelp cs = map cmdHelp cs runCommand :: Command -> REPL () runCommand c = case c of - Command cmd -> exceptionProtect cmd >> - void (liftTopLevel $ onSolverCache $ saveSolverCache True) + Command cmd -> exceptionProtect cmd Unknown cmd -> io (putStrLn ("Unknown command: " ++ cmd)) diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 8a24cc7818..4c1893d153 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -955,15 +955,6 @@ proveUnintSBV conf unints = unintSet <- SV.scriptTopLevel (resolveNames unints) wrapProver (Prover.proveUnintSBV conf unintSet timeout) --- | Load a solver result cache from a file and set it as the current --- 'SolverCache', or if no such file exists, set the current 'SolverCache' to --- an empty 'SolverCache' with its path set to the given value -setSolverCachePath :: FilePath -> TopLevel () -setSolverCachePath path = do - opts <- getOptions - cache <- io $ loadSolverCache opts path - modify (\rw -> rw { rwSolverCache = Just cache }) - -- | Given a continuation which calls a prover, call the continuation on the -- 'goalSequent' of the given 'ProofGoal' and return a 'SolveResult'. If there -- is a 'SolverCache', do not call the continuation if the goal has an already diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 5f0005a4f3..ea9c489e23 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -41,6 +41,7 @@ import Control.Applicative ( (<|>) ) import qualified Control.Exception as X import Control.Monad (unless, (>=>), when) import Control.Monad.IO.Class (liftIO) +import Data.Maybe (fromMaybe) import qualified Data.ByteString as BS import qualified Data.Map as Map import Data.Map ( Map ) @@ -478,7 +479,8 @@ buildTopLevelEnv proxy opts = jvmTrans <- CJ.mkInitialJVMContext halloc cache <- maybe (return Nothing) - (fmap Just . loadSolverCache opts) + (\p -> Just . snd <$> + setSolverCachePath p opts emptySolverCache) (solverCache opts) let rw0 = TopLevelRW @@ -533,8 +535,7 @@ processFile proxy opts file mbSubshell mbProofSubshell = do Nothing -> ro' Just m -> ro'{ roProofSubshell = m } file' <- canonicalizePath file - _ <- runTopLevel (interpretFile file' True >> - onSolverCache (saveSolverCache False)) ro'' rw + _ <- runTopLevel (interpretFile file' True) ro'' rw `X.catch` (handleException opts) return () @@ -644,6 +645,17 @@ disable_lax_loads_and_stores = do rw <- getTopLevelRW putTopLevelRW rw { rwLaxLoadsAndStores = False } +enable_solver_cache :: TopLevel () +enable_solver_cache = do + rw <- getTopLevelRW + putTopLevelRW rw { rwSolverCache = Just emptySolverCache } + +set_solver_cache_path :: FilePath -> TopLevel () +set_solver_cache_path path = do + rw <- getTopLevelRW + putTopLevelRW rw { rwSolverCache = Just $ fromMaybe emptySolverCache (rwSolverCache rw) } + onSolverCache (setSolverCachePath path) + enable_debug_intrinsics :: TopLevel () enable_debug_intrinsics = do rw <- getTopLevelRW @@ -1064,11 +1076,21 @@ primitives = Map.fromList , "currently are 'z3' and 'yices'." ] + , prim "enable_solver_cache" "TopLevel ()" + (pureVal enable_solver_cache) + Experimental + [ "Enable solver result caching. Unless set_solver_cache_path is" + , " later called, the cache will not persist between sessions." + ] + , prim "set_solver_cache_path" "String -> TopLevel ()" - (pureVal setSolverCachePath) + (pureVal set_solver_cache_path) Experimental - [ "Enable solver result caching and set the path the cache should be" - , " loaded from and saved to." + [ "Enable solver result caching if it is not already enabled, set a" + , " path to use for loading and saving cached results, and save to" + , " that path any results cached so far from the current session." + , " If there is already a path set for solver caching, this will" + , " error." ] , prim "enable_debug_intrinsics" "TopLevel ()" diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 11f2121325..781f3029a1 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -8,14 +8,14 @@ Stability : provisional {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} +{-# LANGUAGE ViewPatterns #-} module SAWScript.SolverCache where -import Control.Monad (unless, when) +import Control.Monad (forM_) import Control.Exception (try, SomeException) import Data.List (sortOn) -import Data.Maybe (catMaybes) -import System.Directory (doesDirectoryExist, listDirectory, createDirectory) +import System.Directory (makeAbsolute, doesFileExist, createDirectoryIfMissing) import System.FilePath (()) import Text.Read (readMaybe) @@ -25,9 +25,6 @@ import Data.Bits (shiftL, (.|.)) import qualified Data.HashMap.Strict as HM import Data.HashMap.Strict (HashMap) -import qualified Data.HashSet as HS -import Data.HashSet (HashSet) - import qualified Data.Text as T import Data.Text.Encoding import Text.Hex @@ -99,25 +96,13 @@ type SolverCacheValue = (Maybe CEX, SolverStats) -- new additions to the cache should be saved data SolverCache = SolverCache - { solverCachePath :: FilePath - , solverCacheMap :: HashMap SolverCacheKey SolverCacheValue - , solverCacheUnsaved :: Maybe (HashSet SolverCacheKey) + { solverCacheMap :: HashMap SolverCacheKey SolverCacheValue + , solverCachePath :: Maybe FilePath } --- | Load a solver result cache from a file, or if no such file exists, --- construct an empty 'SolverCache' with its path set to the given value -loadSolverCache :: Options -> FilePath -> IO SolverCache -loadSolverCache opts path = do - ex <- doesDirectoryExist path - mbs <- if ex then listDirectory path >>= mapM processFile - else return [] - when ex $ printOutLn opts Info ("Loaded solver result cache with " ++ show (length mbs) ++ " entries") - return $ SolverCache path (HM.fromList $ catMaybes mbs) Nothing - where processFile :: FilePath -> IO (Maybe (SolverCacheKey, SolverCacheValue)) - processFile f = case solverCacheKeyFromHex f of - Just k -> fmap (\(sz,ss) -> (k, (Nothing, SolverStats ss sz))) <$> - readMaybe <$> readFile (path f) - _ -> return Nothing +-- | An empty 'SolverCache' with no associated 'FilePath' +emptySolverCache :: SolverCache +emptySolverCache = SolverCache HM.empty Nothing -- | A stateful operation on a 'SolverCache', returning a value of the given type type SolverCacheOp a = Options -> SolverCache -> IO (a, SolverCache) @@ -125,29 +110,44 @@ type SolverCacheOp a = Options -> SolverCache -> IO (a, SolverCache) -- | Lookup a 'SolverCacheKey' in the solver result cache lookupInSolverCache :: SolverCacheKey -> SolverCacheOp (Maybe SolverCacheValue) lookupInSolverCache k opts cache = - case HM.lookup k (solverCacheMap cache) of - Just v -> printOutLn opts Info ("Using cached result (" ++ solverCacheKeyToHex k ++ ")" ++ show (solverCacheKeyInt k)) >> - return (Just v, cache) + case (HM.lookup k (solverCacheMap cache), solverCachePath cache) of + (Just v, _) -> do + printOutLn opts Info ("Using cached result from memory (" ++ solverCacheKeyToHex k ++ ")") + return (Just v, cache) + (_, Just path) -> do + ex <- doesFileExist (path solverCacheKeyToHex k) + if not ex then return (Nothing, cache) + else readMaybe <$> readFile (path solverCacheKeyToHex k) >>= \case + Just (sz, ss) -> do + let v = (Nothing, SolverStats ss sz) + printOutLn opts Info ("Using cached result from disk (" ++ solverCacheKeyToHex k ++ ")") + return (Just v, cache { solverCacheMap = HM.insert k v (solverCacheMap cache) }) + Nothing -> return (Nothing, cache) _ -> return (Nothing, cache) -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () -insertInSolverCache k v@(Nothing, _) opts cache = - printOutLn opts Info ("Adding cached result (" ++ solverCacheKeyToHex k ++ ")" ++ show (solverCacheKeyInt k)) >> - return ((), cache { solverCacheMap = HM.insert k v (solverCacheMap cache) - , solverCacheUnsaved = HS.insert k <$> solverCacheUnsaved cache }) +insertInSolverCache k v@(Nothing, SolverStats ss sz) opts cache = do + printOutLn opts Info ("Caching result (" ++ solverCacheKeyToHex k ++ ")") + case solverCachePath cache of + Just path -> createDirectoryIfMissing False path >> + writeFile (path solverCacheKeyToHex k) (show (sz, ss)) + _ -> return () + return ((), cache { solverCacheMap = HM.insert k v (solverCacheMap cache) }) insertInSolverCache _ _ _ cache = return ((), cache) --- | Save the current solver result cache to a file, tracking unsaved results --- from now on iff the given flag is 'True' -saveSolverCache :: Bool -> SolverCacheOp () -saveSolverCache trackUnsaved _ cache = do - ex <- doesDirectoryExist (solverCachePath cache) - unless ex $ createDirectory (solverCachePath cache) - mapM_ (\(k,v) -> writeFile (solverCachePath cache solverCacheKeyToHex k) - (showValue v)) - (maybe (HM.toList $ solverCacheMap cache) - (map (\k -> (k, (solverCacheMap cache) HM.! k)) . HS.toList) - (solverCacheUnsaved cache)) - return ((), cache { solverCacheUnsaved = if trackUnsaved then Just HS.empty else Nothing }) - where showValue (_, SolverStats ss sz) = show (sz, ss) +-- | Set the 'FilePath' of the solver result cache, erroring if it is already +-- set, and save all results cached so far +setSolverCachePath :: FilePath -> SolverCacheOp () +setSolverCachePath path opts cache = + makeAbsolute path >>= \pathAbs -> + case solverCachePath cache of + Just path' -> fail $ "Solver cache already has a set path: " ++ path' + _ | HM.null (solverCacheMap cache) -> return ((), cache { solverCachePath = Just pathAbs }) + _ -> let to_save = HM.toList $ solverCacheMap cache in + createDirectoryIfMissing False pathAbs >> + let (s0, s1) = (show (length to_save), if length to_save > 1 then "s" else "") in + printOutLn opts Info ("Saving " ++ s0 ++ " cached result" ++ s1 ++ " to disk") >> + forM_ to_save (\(k,(_, SolverStats ss sz)) -> + writeFile (pathAbs solverCacheKeyToHex k) (show (sz, ss))) >> + return ((), cache { solverCachePath = Just pathAbs }) From 63eed8892ad96f3db0723bee03fc4280c5253b6c Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 10 May 2023 18:01:17 -0400 Subject: [PATCH 11/51] add print_solver_cache_stats --- src/SAWScript/Interpreter.hs | 16 +++++++++++----- src/SAWScript/SolverCache.hs | 37 ++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index ea9c489e23..d6b5705796 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -648,13 +648,12 @@ disable_lax_loads_and_stores = do enable_solver_cache :: TopLevel () enable_solver_cache = do rw <- getTopLevelRW - putTopLevelRW rw { rwSolverCache = Just emptySolverCache } + let cache = fromMaybe emptySolverCache (rwSolverCache rw) + putTopLevelRW rw { rwSolverCache = Just cache } set_solver_cache_path :: FilePath -> TopLevel () -set_solver_cache_path path = do - rw <- getTopLevelRW - putTopLevelRW rw { rwSolverCache = Just $ fromMaybe emptySolverCache (rwSolverCache rw) } - onSolverCache (setSolverCachePath path) +set_solver_cache_path path = + enable_solver_cache >> onSolverCache (setSolverCachePath path) enable_debug_intrinsics :: TopLevel () enable_debug_intrinsics = do @@ -1092,6 +1091,13 @@ primitives = Map.fromList , " If there is already a path set for solver caching, this will" , " error." ] + + , prim "print_solver_cache_stats" "TopLevel ()" + (pureVal (onSolverCache printSolverCacheStats)) + Experimental + [ "Print out statistics about how the solver cache was used, namely" + , " how many results are cached and how many cache hits have" + , " occurred - both in memory and on disk" ] , prim "enable_debug_intrinsics" "TopLevel ()" (pureVal enable_debug_intrinsics) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 781f3029a1..adfb14a35c 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -15,7 +15,8 @@ module SAWScript.SolverCache where import Control.Monad (forM_) import Control.Exception (try, SomeException) import Data.List (sortOn) -import System.Directory (makeAbsolute, doesFileExist, createDirectoryIfMissing) +import Data.Tuple.Extra (first, second) +import System.Directory import System.FilePath (()) import Text.Read (readMaybe) @@ -98,11 +99,12 @@ data SolverCache = SolverCache { solverCacheMap :: HashMap SolverCacheKey SolverCacheValue , solverCachePath :: Maybe FilePath + , solverCacheHits :: (Integer, Integer) } -- | An empty 'SolverCache' with no associated 'FilePath' emptySolverCache :: SolverCache -emptySolverCache = SolverCache HM.empty Nothing +emptySolverCache = SolverCache HM.empty Nothing (0,0) -- | A stateful operation on a 'SolverCache', returning a value of the given type type SolverCacheOp a = Options -> SolverCache -> IO (a, SolverCache) @@ -113,7 +115,7 @@ lookupInSolverCache k opts cache = case (HM.lookup k (solverCacheMap cache), solverCachePath cache) of (Just v, _) -> do printOutLn opts Info ("Using cached result from memory (" ++ solverCacheKeyToHex k ++ ")") - return (Just v, cache) + return (Just v, cache { solverCacheHits = first (+1) (solverCacheHits cache) }) (_, Just path) -> do ex <- doesFileExist (path solverCacheKeyToHex k) if not ex then return (Nothing, cache) @@ -121,7 +123,8 @@ lookupInSolverCache k opts cache = Just (sz, ss) -> do let v = (Nothing, SolverStats ss sz) printOutLn opts Info ("Using cached result from disk (" ++ solverCacheKeyToHex k ++ ")") - return (Just v, cache { solverCacheMap = HM.insert k v (solverCacheMap cache) }) + return (Just v, cache { solverCacheMap = HM.insert k v (solverCacheMap cache) + , solverCacheHits = second (+1) (solverCacheHits cache) }) Nothing -> return (Nothing, cache) _ -> return (Nothing, cache) @@ -151,3 +154,29 @@ setSolverCachePath path opts cache = forM_ to_save (\(k,(_, SolverStats ss sz)) -> writeFile (pathAbs solverCacheKeyToHex k) (show (sz, ss))) >> return ((), cache { solverCachePath = Just pathAbs }) + +-- | Print out statistics about how the solver cache was used +printSolverCacheStats :: SolverCacheOp () +printSolverCacheStats opts cache = do + let memSize = HM.size $ solverCacheMap cache + let (memHits, diskHits) = solverCacheHits cache + printOutLn opts Info ("== Solver result cache statistics ==") + case solverCachePath cache of + Just path -> do + diskSize <- length <$> listDirectory path + printOutLn opts Info ("- " ++ show diskSize ++ " results cached on disk " + ++ "(" ++ path ++ ")") + printOutLn opts Info ("- " ++ show memSize ++ " results cached in memory " + ++ "(" ++ show (100*memSize `div` diskSize) + ++ "% of total cache)") + let totalHits = max 1 (memHits+diskHits) + printOutLn opts Info ("- " ++ show diskHits ++ " cache hits from disk " + ++ "(" ++ show (100*diskHits `div` totalHits) + ++ "% of total hits)") + printOutLn opts Info ("- " ++ show memHits ++ " cache hits from memory " + ++ "(" ++ show (100*memHits `div` totalHits) + ++ "% of total hits)") + Nothing -> do + printOutLn opts Info ("- " ++ show memSize ++ " results cached in memory") + printOutLn opts Info ("- " ++ show memHits ++ " cache hits") + return ((), cache) From 9fb0c6d5b1da9165d425750f443307ca97979b4a Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Thu, 11 May 2023 14:41:57 -0400 Subject: [PATCH 12/51] use prettyprinting functions for showing FiniteValues and FirstOrderValues --- .../src/Verifier/SAW/Simulator/SBV.hs | 4 ++- saw-core/src/Verifier/SAW/FiniteValue.hs | 29 +++++++------------ src/SAWScript/Builtins.hs | 8 ++--- src/SAWScript/Prover/ABC.hs | 4 +-- src/SAWScript/Prover/RME.hs | 2 +- src/SAWScript/Prover/What4.hs | 4 ++- src/SAWScript/Value.hs | 24 ++++++--------- 7 files changed, 31 insertions(+), 44 deletions(-) diff --git a/saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs b/saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs index 979bb21af2..2cd701ad64 100644 --- a/saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs +++ b/saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs @@ -71,6 +71,7 @@ import Verifier.SAW.TypedAST (FieldName, toShortName, identBaseName) import Verifier.SAW.FiniteValue (FirstOrderType(..), FirstOrderValue(..) , fovVec, firstOrderTypeOf, asFirstOrderType + , showFirstOrderValues ) import Verifier.SAW.Utils (panic) @@ -768,7 +769,8 @@ getLabels ls d args | length args == length xs = Just (zip args xs) | otherwise = error $ unwords [ "SBV SAT results do not match expected arguments " - , show (map (toShortName . ecName) args), show xs] + , show (map (toShortName . ecName) args) + , showFirstOrderValues xs] where xs = fmap getLabel ls diff --git a/saw-core/src/Verifier/SAW/FiniteValue.hs b/saw-core/src/Verifier/SAW/FiniteValue.hs index 7a0c9c85dc..14e4722e2c 100644 --- a/saw-core/src/Verifier/SAW/FiniteValue.hs +++ b/saw-core/src/Verifier/SAW/FiniteValue.hs @@ -19,10 +19,8 @@ import Control.Monad (mzero) import Control.Monad.Trans (lift) import Control.Monad.Trans.Maybe import qualified Control.Monad.State as S -import Data.List (intersperse) import Data.Map (Map) import qualified Data.Map as Map -import qualified Data.Text as Text import Numeric.Natural (Natural) import Prettyprinter hiding (Doc) @@ -99,23 +97,16 @@ toFiniteType FOTInt{} = Nothing toFiniteType FOTIntMod{} = Nothing toFiniteType FOTArray{} = Nothing -instance Show FiniteValue where - showsPrec p fv = showsPrec p (toFirstOrderValue fv) - -instance Show FirstOrderValue where - showsPrec _ fv = - case fv of - FOVBit b -> shows b - FOVInt i -> shows i - FOVIntMod _ i -> shows i - FOVWord _ x -> shows x - FOVVec _ vs -> showString "[" . commaSep (map shows vs) . showString "]" - FOVArray{} -> shows $ firstOrderTypeOf fv - FOVTuple vs -> showString "(" . commaSep (map shows vs) . showString ")" - FOVRec vm -> showString "{" . commaSep (map showField (Map.assocs vm)) . showString "}" - where - commaSep ss = foldr (.) id (intersperse (showString ",") ss) - showField (field, v) = showString (Text.unpack field) . showString " = " . shows v +showFiniteValues :: [FiniteValue] -> String +showFiniteValues = showFirstOrderValues . map toFirstOrderValue + +showFirstOrderValues :: [FirstOrderValue] -> String +showFirstOrderValues = renderSawDoc defaultPPOpts . list . + map (ppFirstOrderValue defaultPPOpts) + +showFirstOrderValue :: FirstOrderValue -> String +showFirstOrderValue = renderSawDoc defaultPPOpts . + ppFirstOrderValue defaultPPOpts ppFiniteValue :: PPOpts -> FiniteValue -> SawDoc ppFiniteValue opts fv = ppFirstOrderValue opts (toFirstOrderValue fv) diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 4c1893d153..d36e482ba6 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -1491,11 +1491,9 @@ quickCheckPrintPrim opts sc numTests tt = testGen <- prepareSATQuery sc satq runManyTests testGen numTests >>= \case Nothing -> printOutLn opts Info $ "All " ++ show numTests ++ " tests passed!" - Just cex -> - do let cex' = [ (Text.unpack (toShortName (ecName ec)), v) | (ec,v) <- cex ] - printOutLn opts OnlyCounterExamples $ - "----------Counterexample----------\n" ++ - showList cex' "" + Just cex -> printOutLn opts OnlyCounterExamples $ + "----------Counterexample----------\n" ++ + SV.showsCEX SV.defaultPPOpts cex "" cryptolSimpset :: TopLevel SV.SAWSimpset cryptolSimpset = diff --git a/src/SAWScript/Prover/ABC.hs b/src/SAWScript/Prover/ABC.hs index d1e4c79814..80c60db4a2 100644 --- a/src/SAWScript/Prover/ABC.hs +++ b/src/SAWScript/Prover/ABC.hs @@ -79,7 +79,7 @@ getModel argNames shapes satRes = | otherwise -> fail $ unwords [ "ABC SAT results do not match expected arguments" - , show argNames, show vs] + , show argNames, showFiniteValues vs] AIG.SatUnknown -> fail "Unknown result from ABC" @@ -200,7 +200,7 @@ abcSatExternal proxy sc doCNF execName args g = liftIO $ Right vs | length ecs == length vs -> do return (Just (zip ecs (map toFirstOrderValue vs)), stats) - | otherwise -> fail $ unwords ["external SAT results do not match expected arguments", show argNames, show vs] + | otherwise -> fail $ unwords ["external SAT results do not match expected arguments", show argNames, showFiniteValues vs] (["s UNSATISFIABLE"], []) -> return (Nothing, stats) _ -> fail $ "Unexpected result from SAT solver:\n" ++ out diff --git a/src/SAWScript/Prover/RME.hs b/src/SAWScript/Prover/RME.hs index 3f4c9b7fd4..b5cb500b6d 100644 --- a/src/SAWScript/Prover/RME.hs +++ b/src/SAWScript/Prover/RME.hs @@ -35,4 +35,4 @@ proveRME goal = getSharedContext >>= \sc -> liftIO $ | length shapes == length vs -> do let model = zip (map fst shapes) (map toFirstOrderValue vs) return (Just model, stats) - | otherwise -> fail $ unwords ["RME SAT results do not match expected arguments", show shapes, show vs] + | otherwise -> fail $ unwords ["RME SAT results do not match expected arguments", show shapes, showFiniteValues vs] diff --git a/src/SAWScript/Prover/What4.hs b/src/SAWScript/Prover/What4.hs index 73c4e11047..07bdde6c3b 100644 --- a/src/SAWScript/Prover/What4.hs +++ b/src/SAWScript/Prover/What4.hs @@ -236,4 +236,6 @@ printValue _ _ (Nothing, _) = return () printValue _ f (Just (W.TypedExpr (ty :: BaseTypeRepr ty) (bv :: B.Expr t ty)), orig) = do gv <- groundEval f @ty bv putStr $ orig ++ "=?" - print (groundToFOV ty gv) + case (groundToFOV ty gv) of + Left err -> putStrLn $ "Left " ++ show err + Right fov -> putStrLn $ "Right " ++ showFirstOrderValue fov diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index d0b6eee45b..95a20476eb 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -215,7 +215,6 @@ data SatResult = Unsat SolverStats | Sat SolverStats [(ExtCns Term, FirstOrderValue)] | SatUnknown - deriving (Show) isVUnit :: Value -> Bool isVUnit (VTuple []) = True @@ -259,12 +258,8 @@ showBrackets s = showString "[" . s . showString "]" showBraces :: ShowS -> ShowS showBraces s = showString "{" . s . showString "}" -showsProofResult :: PPOpts -> ProofResult -> ShowS -showsProofResult opts r = - case r of - ValidProof _ _ -> showString "Valid" - InvalidProof _ ts _ -> showString "Invalid: [" . showMulti "" ts - UnfinishedProof st -> showString "Unfinished: " . shows (length (psGoals st)) . showString " goals remaining" +showsCEX :: PPOpts -> CEX -> ShowS +showsCEX opts ts = showString "[" . showMulti "" ts where opts' = sawPPOpts opts showVal t = shows (ppFirstOrderValue opts' t) @@ -274,20 +269,19 @@ showsProofResult opts r = showMulti _ [] = showString "]" showMulti s (eqn : eqns) = showString s . showEqn eqn . showMulti ", " eqns +showsProofResult :: PPOpts -> ProofResult -> ShowS +showsProofResult opts r = + case r of + ValidProof _ _ -> showString "Valid" + InvalidProof _ ts _ -> showString "Invalid: " . showsCEX opts ts + UnfinishedProof st -> showString "Unfinished: " . shows (length (psGoals st)) . showString " goals remaining" showsSatResult :: PPOpts -> SatResult -> ShowS showsSatResult opts r = case r of Unsat _ -> showString "Unsat" - Sat _ ts -> showString "Sat: [" . showMulti "" ts + Sat _ ts -> showString "Sat: " . showsCEX opts ts SatUnknown -> showString "Unknown" - where - opts' = sawPPOpts opts - showVal t = shows (ppFirstOrderValue opts' t) - showEC ec = showString (unpack (toShortName (ecName ec))) - showEqn (x, t) = showEC x . showString " = " . showVal t - showMulti _ [] = showString "]" - showMulti s (eqn : eqns) = showString s . showEqn eqn . showMulti ", " eqns showSimpset :: PPOpts -> Simpset a -> String showSimpset opts ss = From 37be6ca78dc6a94d06616316a85ee160a3b33f4c Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 16 May 2023 19:56:04 -0400 Subject: [PATCH 13/51] factor `sequentToSATQuery` out of SMT backends, cache counterexamples --- saw-core/src/Verifier/SAW/FiniteValue.hs | 4 +- saw-core/src/Verifier/SAW/SATQuery.hs | 12 +++ src/SAWScript/Builtins.hs | 49 +++++------ src/SAWScript/Proof.hs | 7 ++ src/SAWScript/Prover/ABC.hs | 41 ++++----- src/SAWScript/Prover/MRSolver/SMT.hs | 5 +- src/SAWScript/Prover/RME.hs | 42 +++++---- src/SAWScript/Prover/SBV.hs | 47 +++------- src/SAWScript/Prover/What4.hs | 68 +++++++-------- src/SAWScript/SolverCache.hs | 104 +++++++++++++---------- 10 files changed, 189 insertions(+), 190 deletions(-) diff --git a/saw-core/src/Verifier/SAW/FiniteValue.hs b/saw-core/src/Verifier/SAW/FiniteValue.hs index 14e4722e2c..d3e736ca5b 100644 --- a/saw-core/src/Verifier/SAW/FiniteValue.hs +++ b/saw-core/src/Verifier/SAW/FiniteValue.hs @@ -56,7 +56,7 @@ data FirstOrderType | FOTArray FirstOrderType FirstOrderType | FOTTuple [FirstOrderType] | FOTRec (Map FieldName FirstOrderType) - deriving (Eq, Show) + deriving (Eq, Read, Show) -- | Values inhabiting those first-order types. data FirstOrderValue @@ -68,7 +68,7 @@ data FirstOrderValue | FOVArray FirstOrderType FirstOrderType | FOVTuple [FirstOrderValue] | FOVRec (Map FieldName FirstOrderValue) - deriving Eq + deriving (Eq, Read, Show) toFirstOrderType :: FiniteType -> FirstOrderType toFirstOrderType ft = diff --git a/saw-core/src/Verifier/SAW/SATQuery.hs b/saw-core/src/Verifier/SAW/SATQuery.hs index 1ddc69a843..650738659c 100644 --- a/saw-core/src/Verifier/SAW/SATQuery.hs +++ b/saw-core/src/Verifier/SAW/SATQuery.hs @@ -3,10 +3,12 @@ module Verifier.SAW.SATQuery , SATResult(..) , SATAssert(..) , satQueryAsTerm +, satQueryAsPropTerm ) where import Data.Map (Map) import Data.Set (Set) +import Data.Foldable (foldrM) import Verifier.SAW.Name import Verifier.SAW.FiniteValue @@ -87,3 +89,13 @@ satQueryAsTerm sc satq = do x' <- scAnd sc x y loop x' xs loop _ (UniversalAssert{} : _) = univFail + +-- | Compute the conjunction of all the assertions +-- in this SAT query as a single term of type Prop. +satQueryAsPropTerm :: SharedContext -> SATQuery -> IO Term +satQueryAsPropTerm sc satq = + scTupleType sc =<< mapM assertAsPropTerm (satAsserts satq) + where assertAsPropTerm (BoolAssert b) = scEqTrue sc b + assertAsPropTerm (UniversalAssert ecs hs g) = + scGeneralizeExts sc (map fst ecs) =<< + scEqTrue sc =<< foldrM (scImplies sc) g hs diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index d36e482ba6..f710fa10c5 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -908,7 +908,7 @@ goal_num_ite n s1 s2 = proveABC :: ProofScript () proveABC = do SV.AIGProxy proxy <- SV.scriptTopLevel SV.getProxy - wrapProver (Prover.proveABC proxy) + wrapProver (Prover.proveABC proxy) Set.empty satExternal :: Bool -> String -> [String] -> ProofScript () satExternal doCNF execName args = @@ -933,7 +933,7 @@ writeSAIGComputedPrim = Prover.writeSAIG -- | Bit-blast a proposition check its validity using the RME library. proveRME :: ProofScript () -proveRME = wrapProver Prover.proveRME +proveRME = wrapProver Prover.proveRME Set.empty codegenSBV :: SharedContext -> FilePath -> [String] -> String -> TypedTerm -> TopLevel () codegenSBV sc path unints fname (TypedTerm _schema t) = @@ -953,50 +953,51 @@ proveUnintSBV :: SBV.SMTConfig -> [String] -> ProofScript () proveUnintSBV conf unints = do timeout <- psTimeout <$> get unintSet <- SV.scriptTopLevel (resolveNames unints) - wrapProver (Prover.proveUnintSBV conf unintSet timeout) + wrapProver (Prover.proveUnintSBV conf timeout) unintSet -- | Given a continuation which calls a prover, call the continuation on the -- 'goalSequent' of the given 'ProofGoal' and return a 'SolveResult'. If there -- is a 'SolverCache', do not call the continuation if the goal has an already -- cached result, and otherwise save the result of the call to the cache. -applyProverToGoal :: (Sequent -> TopLevel (Maybe CEX, SolverStats)) +applyProverToGoal :: (SATQuery -> TopLevel (Maybe CEX, String)) + -> Set VarIndex -> ProofGoal -> TopLevel (SolverStats, SolveResult) -applyProverToGoal f g = do +applyProverToGoal f unintSet g = do sc <- getSharedContext - mb_cache_key <- io $ propToSolverCacheKey sc (goalSequent g) - (mb, stats) <- case mb_cache_key of - -- If we have a solver result cache and a valid key... - Just k -> SV.onSolverCache (lookupInSolverCache k) >>= \case - Just v -> -- use a cached result if we have one, - return v - _ -> -- or cache the result of the call otherwise. - f (goalSequent g) >>= \v -> - SV.onSolverCache (insertInSolverCache k v) >> - return v - _ -> f (goalSequent g) + satq <- io $ sequentToSATQuery sc unintSet (goalSequent g) + k <- io $ satQueryToSolverCacheKey sc satq + (mb, solver_name) <- SV.onSolverCache (lookupInSolverCache k) >>= \case + -- Use a cached result if one exists (and it's valid w.r.t our query) + Just v -> return $ fromSolverCacheValue satq v + -- Otherwise try to cache the result of the call + _ -> f satq >>= \res -> + case toSolverCacheValue satq res of + Just v -> SV.onSolverCache (insertInSolverCache k v) >> + return res + Nothing -> return res + let stats = solverStats solver_name (sequentSharedSize (goalSequent g)) case mb of Nothing -> return (stats, SolveSuccess (SolverEvidence stats (goalSequent g))) Just a -> return (stats, SolveCounterexample a) wrapProver :: - (Sequent -> TopLevel (Maybe CEX, SolverStats)) -> + (SATQuery -> TopLevel (Maybe CEX, String)) -> + Set VarIndex -> ProofScript () -wrapProver f = execTactic $ tacticSolve $ applyProverToGoal f +wrapProver f unintSet = execTactic $ tacticSolve $ applyProverToGoal f unintSet wrapW4Prover :: - ( Set VarIndex -> Bool -> - Sequent -> TopLevel (Maybe CEX, SolverStats)) -> + ( Bool -> SATQuery -> TopLevel (Maybe CEX, String) ) -> [String] -> ProofScript () wrapW4Prover f unints = do hashConsing <- SV.scriptTopLevel $ gets SV.rwWhat4HashConsing unintSet <- SV.scriptTopLevel $ resolveNames unints - wrapProver $ f unintSet hashConsing + wrapProver (f hashConsing) unintSet wrapW4ProveExporter :: - ( Set VarIndex -> Bool -> FilePath -> - Sequent -> TopLevel (Maybe CEX, SolverStats)) -> + ( Bool -> FilePath -> SATQuery -> TopLevel (Maybe CEX, String) ) -> [String] -> String -> String -> @@ -1006,7 +1007,7 @@ wrapW4ProveExporter f unints path ext = do unintSet <- SV.scriptTopLevel $ resolveNames unints execTactic $ tacticSolve $ \g -> do let file = path ++ "." ++ goalType g ++ show (goalNum g) ++ ext - applyProverToGoal (f unintSet hashConsing file) g + applyProverToGoal (f hashConsing file) unintSet g -------------------------------------------------- proveABC_SBV :: ProofScript () diff --git a/src/SAWScript/Proof.hs b/src/SAWScript/Proof.hs index edccec690d..7063912642 100644 --- a/src/SAWScript/Proof.hs +++ b/src/SAWScript/Proof.hs @@ -48,6 +48,7 @@ module SAWScript.Proof , traverseSequentWithFocus , checkSequent , sequentConstantSet + , sequentAllExtSet , booleansToSequent , unfocusSequent , focusOnConcl @@ -612,6 +613,12 @@ sequentConstantSet sqt = foldr (\p m -> Map.union (getConstantSet (unProp p)) m) where RawSequent hs gs = sequentToRawSequent sqt +-- | Return the 'Set' of all 'ExtCns' in the given 'Sequent' +sequentAllExtSet :: Sequent -> Set (ExtCns Term) +sequentAllExtSet sqt = foldMap getAllExtSet (map unProp (hs ++ gs)) + where + RawSequent hs gs = sequentToRawSequent sqt + convertibleProps :: SharedContext -> [Prop] -> [Prop] -> IO Bool convertibleProps _sc [] [] = return True convertibleProps sc (p1:ps1) (p2:ps2) = diff --git a/src/SAWScript/Prover/ABC.hs b/src/SAWScript/Prover/ABC.hs index 80c60db4a2..70f7c5368d 100644 --- a/src/SAWScript/Prover/ABC.hs +++ b/src/SAWScript/Prover/ABC.hs @@ -12,7 +12,6 @@ import Data.Char (isSpace) import Data.List (isPrefixOf) import qualified Data.Map as Map import Data.Maybe -import Data.Set (Set) import qualified Data.Text as Text import Text.Read (readMaybe) @@ -32,7 +31,7 @@ import qualified Verifier.SAW.Simulator.BitBlast as BBSim import SAWScript.Proof ( sequentToSATQuery, goalSequent, ProofGoal , goalType, goalNum, CEX - , Sequent, sequentSharedSize + , sequentSharedSize ) import SAWScript.Prover.SolverStats (SolverStats, solverStats) import qualified SAWScript.Prover.Exporter as Exporter @@ -48,15 +47,13 @@ import Lang.JVM.ProcessUtils (readProcessExitIfFailure) proveABC :: (AIG.IsAIG l g) => AIG.Proxy l g -> - Sequent -> - TopLevel (Maybe CEX, SolverStats) -proveABC proxy goal = getSharedContext >>= \sc -> liftIO $ - do satq <- sequentToSATQuery sc mempty goal - BBSim.withBitBlastedSATQuery proxy sc mempty satq $ \be lit shapes -> - do let (ecs,fts) = unzip shapes - res <- getModel ecs fts =<< AIG.checkSat be lit - let stats = solverStats "ABC" (sequentSharedSize goal) - return (res, stats) + SATQuery -> + TopLevel (Maybe CEX, String) +proveABC proxy satq = getSharedContext >>= \sc -> liftIO $ + BBSim.withBitBlastedSATQuery proxy sc mempty satq $ \be lit shapes -> + do let (ecs,fts) = unzip shapes + res <- getModel ecs fts =<< AIG.checkSat be lit + return (res, "ABC") getModel :: @@ -85,20 +82,18 @@ getModel argNames shapes satRes = w4AbcVerilog :: - Set VarIndex -> Bool -> - Sequent -> - TopLevel (Maybe CEX, SolverStats) + SATQuery -> + TopLevel (Maybe CEX, String) w4AbcVerilog = w4AbcExternal Exporter.writeVerilogSAT cmd where cmd tmp tmpCex = "%read " ++ tmp ++ "; %blast; &sweep -C 5000; &syn4; &cec -m; write_aiger_cex " ++ tmpCex w4AbcAIGER :: - Set VarIndex -> Bool -> - Sequent -> - TopLevel (Maybe CEX, SolverStats) + SATQuery -> + TopLevel (Maybe CEX, String) w4AbcAIGER = do w4AbcExternal Exporter.writeAIG_SAT cmd where cmd tmp tmpCex = "read_aiger " ++ tmp ++ "; sat; write_cex " ++ tmpCex @@ -106,19 +101,16 @@ w4AbcAIGER = w4AbcExternal :: (FilePath -> SATQuery -> TopLevel [(ExtCns Term, FiniteType)]) -> (String -> String -> String) -> - Set VarIndex -> Bool -> - Sequent -> - TopLevel (Maybe CEX, SolverStats) -w4AbcExternal exporter argFn unints _hashcons goal = + SATQuery -> + TopLevel (Maybe CEX, String) +w4AbcExternal exporter argFn _hashcons satq = -- Create temporary files do let tpl = "abc_verilog.v" tplCex = "abc_verilog.cex" - sc <- getSharedContext tmp <- liftIO $ emptySystemTempFile tpl tmpCex <- liftIO $ emptySystemTempFile tplCex - satq <- liftIO $ sequentToSATQuery sc unints goal (argNames, argTys) <- unzip <$> exporter tmp satq -- Run ABC and remove temporaries @@ -130,7 +122,6 @@ w4AbcExternal exporter argFn unints _hashcons goal = liftIO $ removeFile tmpCex -- Parse and report results - let stats = solverStats "abc_verilog" (sequentSharedSize goal) res <- if all isSpace cexText then return Nothing else do cex <- liftIO $ parseAigerCex cexText argTys @@ -138,7 +129,7 @@ w4AbcExternal exporter argFn unints _hashcons goal = Left parseErr -> fail parseErr Right vs -> return $ Just model where model = zip argNames (map toFirstOrderValue vs) - return (res, stats) + return (res, "abc_verilog") parseAigerCex :: String -> [FiniteType] -> IO (Either String [FiniteValue]) parseAigerCex text tys = diff --git a/src/SAWScript/Prover/MRSolver/SMT.hs b/src/SAWScript/Prover/MRSolver/SMT.hs index f5f8be763f..369f4a26b2 100644 --- a/src/SAWScript/Prover/MRSolver/SMT.hs +++ b/src/SAWScript/Prover/MRSolver/SMT.hs @@ -49,7 +49,7 @@ import Verifier.SAW.Module import Verifier.SAW.Prelude.Constants import Verifier.SAW.FiniteValue -import SAWScript.Proof (termToProp, propToTerm, prettyProp, propToSequent) +import SAWScript.Proof (termToProp, propToTerm, prettyProp, propToSequent, sequentToSATQuery) import What4.Solver import SAWScript.Prover.What4 @@ -388,12 +388,13 @@ mrProvableRaw prop_term = nenv <- liftIO (scGetNamingEnv sc) debugPrint 2 ("Calling SMT solver with proposition: " ++ prettyProp defaultPPOpts nenv prop) + satq <- liftIO $ sequentToSATQuery sc unints (propToSequent prop) sym <- liftIO $ setupWhat4_sym True -- If there are any saw-core `error`s in the term, this will throw a -- Haskell error - in this case we want to just return False, not stop -- execution smt_res <- liftIO $ - (Right <$> proveWhat4_solver z3Adapter sym unints sc (propToSequent prop) (return ())) + (Right <$> proveWhat4_solver z3Adapter sym sc satq (return ())) `X.catch` \case UserError msg -> return $ Left msg e -> X.throw e diff --git a/src/SAWScript/Prover/RME.hs b/src/SAWScript/Prover/RME.hs index b5cb500b6d..758266e160 100644 --- a/src/SAWScript/Prover/RME.hs +++ b/src/SAWScript/Prover/RME.hs @@ -8,31 +8,29 @@ import qualified Data.RME as RME import Verifier.SAW.FiniteValue import qualified Verifier.SAW.Simulator.RME as RME +import Verifier.SAW.SATQuery (SATQuery(..)) -import SAWScript.Proof(Sequent, sequentToSATQuery, sequentSharedSize, CEX) -import SAWScript.Prover.SolverStats +import SAWScript.Proof (CEX) import SAWScript.Prover.Util import SAWScript.Value -- | Bit-blast a proposition and check its validity using RME. proveRME :: - Sequent {- ^ A proposition to be proved -} -> - TopLevel (Maybe CEX, SolverStats) -proveRME goal = getSharedContext >>= \sc -> liftIO $ - do satq <- sequentToSATQuery sc mempty goal - RME.withBitBlastedSATQuery sc Map.empty satq $ \lit shapes -> - let stats = solverStats "RME" (sequentSharedSize goal) - in case RME.sat lit of - Nothing -> return (Nothing, stats) - Just cex -> do - let m = Map.fromList cex - let n = sum (map (sizeFiniteType . snd) shapes) - let bs = map (maybe False id . flip Map.lookup m) $ take n [0..] - let r = liftCexBB (map snd shapes) bs - case r of - Left err -> fail $ "Can't parse counterexample: " ++ err - Right vs - | length shapes == length vs -> do - let model = zip (map fst shapes) (map toFirstOrderValue vs) - return (Just model, stats) - | otherwise -> fail $ unwords ["RME SAT results do not match expected arguments", show shapes, showFiniteValues vs] + SATQuery {- ^ The query to be proved -} -> + TopLevel (Maybe CEX, String) +proveRME satq = getSharedContext >>= \sc -> liftIO $ + RME.withBitBlastedSATQuery sc Map.empty satq $ \lit shapes -> + case RME.sat lit of + Nothing -> return (Nothing, "RME") + Just cex -> do + let m = Map.fromList cex + let n = sum (map (sizeFiniteType . snd) shapes) + let bs = map (maybe False id . flip Map.lookup m) $ take n [0..] + let r = liftCexBB (map snd shapes) bs + case r of + Left err -> fail $ "Can't parse counterexample: " ++ err + Right vs + | length shapes == length vs -> do + let model = zip (map fst shapes) (map toFirstOrderValue vs) + return (Just model, "RME") + | otherwise -> fail $ unwords ["RME SAT results do not match expected arguments", show shapes, showFiniteValues vs] diff --git a/src/SAWScript/Prover/SBV.hs b/src/SAWScript/Prover/SBV.hs index 3e4d6114ba..45f214070d 100644 --- a/src/SAWScript/Prover/SBV.hs +++ b/src/SAWScript/Prover/SBV.hs @@ -3,13 +3,11 @@ module SAWScript.Prover.SBV , proveUnintSBVIO , SBV.SMTConfig , SBV.z3, SBV.cvc4, SBV.cvc5, SBV.yices, SBV.mathSAT, SBV.boolector - , prepNegatedSBV ) where import System.Directory import Data.Maybe -import Data.Set ( Set ) import Control.Monad import qualified Data.SBV.Dynamic as SBV @@ -18,9 +16,9 @@ import qualified Data.SBV.Internals as SBV import qualified Verifier.SAW.Simulator.SBV as SBVSim import Verifier.SAW.SharedTerm +import Verifier.SAW.SATQuery (SATQuery(..)) -import SAWScript.Proof(Sequent, sequentSharedSize, sequentToSATQuery, CEX) -import SAWScript.Prover.SolverStats +import SAWScript.Proof (CEX) import SAWScript.Value -- | Bit-blast a proposition and check its validity using SBV. @@ -28,24 +26,22 @@ import SAWScript.Value -- functions. proveUnintSBV :: SBV.SMTConfig {- ^ SBV configuration -} -> - Set VarIndex {- ^ Uninterpreted functions -} -> Maybe Integer {- ^ Timeout in milliseconds -} -> - Sequent {- ^ A proposition to be proved -} -> - TopLevel (Maybe CEX, SolverStats) + SATQuery {- ^ A query to be proved -} -> + TopLevel (Maybe CEX, String) -- ^ (example/counter-example, solver statistics) -proveUnintSBV conf unintSet timeout goal = +proveUnintSBV conf timeout satq = do sc <- getSharedContext - io $ proveUnintSBVIO sc conf unintSet timeout goal + io $ proveUnintSBVIO sc conf timeout satq proveUnintSBVIO :: SharedContext -> SBV.SMTConfig {- ^ SBV configuration -} -> - Set VarIndex {- ^ Uninterpreted functions -} -> Maybe Integer {- ^ Timeout in milliseconds -} -> - Sequent {- ^ A proposition to be proved -} -> - IO (Maybe CEX, SolverStats) + SATQuery {- ^ A query to be proved -} -> + IO (Maybe CEX, String) -- ^ (example/counter-example, solver statistics) -proveUnintSBVIO sc conf unintSet timeout goal = +proveUnintSBVIO sc conf timeout satq = do p <- findExecutable . SBV.executable $ SBV.solver conf unless (isJust p) . fail $ mconcat [ "Unable to locate the executable \"" @@ -54,21 +50,21 @@ proveUnintSBVIO sc conf unintSet timeout goal = , show . SBV.name $ SBV.solver conf ] - (labels, argNames, lit) <- prepNegatedSBV sc unintSet goal + (labels, argNames, lit) <- SBVSim.sbvSATQuery sc mempty satq let script = maybe (return ()) SBV.setTimeOut timeout >> lit SBV.SatResult r <- SBV.satWith conf script - let stats = solverStats ("SBV->" ++ show (SBV.name (SBV.solver conf))) - (sequentSharedSize goal) + let solver_name = "SBV->" ++ show (SBV.name (SBV.solver conf)) + case r of - SBV.Unsatisfiable {} -> return (Nothing, stats) + SBV.Unsatisfiable {} -> return (Nothing, solver_name) SBV.Satisfiable {} -> do let dict = SBV.getModelDictionary r r' = SBVSim.getLabels labels dict argNames - return (r', stats) + return (r', solver_name) SBV.DeltaSat{} -> fail "Prover returned an unexpected DeltaSat result" @@ -77,18 +73,3 @@ proveUnintSBVIO sc conf unintSet timeout goal = SBV.Unknown {} -> fail "Prover returned Unknown" SBV.ProofError _ ls _ -> fail . unlines $ "Prover returned error: " : ls - - --- | Convert a saw-core proposition to a logically-negated SBV --- symbolic boolean formula with existentially quantified variables. --- The returned formula is suitable for checking satisfiability. The --- specified function names are left uninterpreted. - -prepNegatedSBV :: - SharedContext -> - Set VarIndex {- ^ Uninterpreted function names -} -> - Sequent {- ^ Proposition to prove -} -> - IO ([SBVSim.Labeler], [ExtCns Term], SBV.Symbolic SBV.SVal) -prepNegatedSBV sc unintSet goal = - do satq <- sequentToSATQuery sc unintSet goal - SBVSim.sbvSATQuery sc mempty satq diff --git a/src/SAWScript/Prover/What4.hs b/src/SAWScript/Prover/What4.hs index 07bdde6c3b..4cef6eccfe 100644 --- a/src/SAWScript/Prover/What4.hs +++ b/src/SAWScript/Prover/What4.hs @@ -18,8 +18,7 @@ import Verifier.SAW.SharedTerm import Verifier.SAW.FiniteValue import Verifier.SAW.SATQuery (SATQuery(..)) -import SAWScript.Proof(Sequent, sequentToSATQuery, sequentSharedSize, CEX) -import SAWScript.Prover.SolverStats +import SAWScript.Proof(Sequent, sequentToSATQuery, CEX) import SAWScript.Value (TopLevel, io, getSharedContext) import Data.Parameterized.Nonce @@ -83,42 +82,39 @@ evalTheories pf = [ nm | (nm,f) <- xs, hasProblemFeature pf f ] proveWhat4_sym :: SolverAdapter St -> - Set VarIndex -> Bool -> - Sequent -> - TopLevel (Maybe CEX, SolverStats) -proveWhat4_sym solver un hashConsing t = + SATQuery -> + TopLevel (Maybe CEX, String) +proveWhat4_sym solver hashConsing satq = getSharedContext >>= \sc -> io $ do sym <- setupWhat4_sym hashConsing - proveWhat4_solver solver sym un sc t (return ()) + proveWhat4_solver solver sym sc satq (return ()) proveExportWhat4_sym :: SolverAdapter St -> - Set VarIndex -> Bool -> FilePath -> - Sequent-> - TopLevel (Maybe CEX, SolverStats) -proveExportWhat4_sym solver un hashConsing outFilePath t = + SATQuery -> + TopLevel (Maybe CEX, String) +proveExportWhat4_sym solver hashConsing outFilePath satq = getSharedContext >>= \sc -> io $ do sym <- setupWhat4_sym hashConsing -- Write smt out - (_, _, lits, stats) <- setupWhat4_solver solver sym un sc t + (_, _, lits, solver_name) <- setupWhat4_solver solver sym sc satq withFile outFilePath WriteMode $ \handle -> solver_adapter_write_smt2 solver sym handle lits -- Assume unsat - return (Nothing, stats) + return (Nothing, solver_name) proveWhat4_z3, proveWhat4_boolector, proveWhat4_cvc4, proveWhat4_cvc5, proveWhat4_dreal, proveWhat4_stp, proveWhat4_yices, proveWhat4_abc :: - Set VarIndex {- ^ Uninterpreted functions -} -> Bool {- ^ Hash-consing of What4 terms -}-> - Sequent {- ^ A proposition to be proved -} -> - TopLevel (Maybe CEX, SolverStats) + SATQuery {- ^ The query to be proved -} -> + TopLevel (Maybe CEX, String) proveWhat4_z3 = proveWhat4_sym z3Adapter proveWhat4_boolector = proveWhat4_sym boolectorAdapter @@ -131,14 +127,13 @@ proveWhat4_abc = proveWhat4_sym externalABCAdapter proveWhat4_z3_using :: String {- ^ Solver tactic -} -> - Set VarIndex {- ^ Uninterpreted functions -} -> Bool {- ^ Hash-consing of What4 terms -}-> - Sequent {- ^ A proposition to be proved -} -> - TopLevel (Maybe CEX, SolverStats) -proveWhat4_z3_using tactic un hashConsing t = + SATQuery {- ^ The query to be proved -} -> + TopLevel (Maybe CEX, String) +proveWhat4_z3_using tactic hashConsing satq = getSharedContext >>= \sc -> io $ do sym <- setupWhat4_sym hashConsing - proveWhat4_solver z3Adapter sym un sc t $ + proveWhat4_solver z3Adapter sym sc satq $ do z3TacticSetting <- getOptionSetting z3Tactic $ getConfiguration sym _ <- setOpt z3TacticSetting $ Text.pack tactic return () @@ -146,11 +141,10 @@ proveWhat4_z3_using tactic un hashConsing t = proveExportWhat4_z3, proveExportWhat4_boolector, proveExportWhat4_cvc4, proveExportWhat4_cvc5, proveExportWhat4_dreal, proveExportWhat4_stp, proveExportWhat4_yices :: - Set VarIndex {- ^ Uninterpreted functions -} -> Bool {- ^ Hash-consing of ExportWhat4 terms -}-> FilePath {- ^ Path of file to write SMT to -}-> - Sequent {- ^ A proposition to be proved -} -> - TopLevel (Maybe CEX, SolverStats) + SATQuery {- ^ The query to be proved -} -> + TopLevel (Maybe CEX, String) proveExportWhat4_z3 = proveExportWhat4_sym z3Adapter proveExportWhat4_boolector = proveExportWhat4_sym boolectorAdapter @@ -164,17 +158,15 @@ proveExportWhat4_yices = proveExportWhat4_sym yicesAdapter setupWhat4_solver :: forall st t ff. SolverAdapter st {- ^ Which solver to use -} -> B.ExprBuilder t st ff {- ^ The glorious sym -} -> - Set VarIndex {- ^ Uninterpreted functions -} -> SharedContext {- ^ Context for working with terms -} -> - Sequent {- ^ A proposition to be proved/checked. -} -> + SATQuery {- ^ The query to be proved/checked. -} -> IO ( [ExtCns Term] , [W.Labeler (B.ExprBuilder t st ff)] , [Pred (B.ExprBuilder t st ff)] - , SolverStats) -setupWhat4_solver solver sym unintSet sc goal = + , String) +setupWhat4_solver solver sym sc satq = do -- symbolically evaluate - satq <- sequentToSATQuery sc unintSet goal let varList = Map.toList (satVariables satq) let argNames = map fst varList (varMap, lits) <- W.w4Solve sym sc satq @@ -183,25 +175,23 @@ setupWhat4_solver solver sym unintSet sc goal = extendConfig (solver_adapter_config_options solver) (getConfiguration sym) - let stats = solverStats ("W4 ->" ++ solver_adapter_name solver) - (sequentSharedSize goal) + let solver_name = "W4 ->" ++ solver_adapter_name solver - return (argNames, bvs, lits, stats) + return (argNames, bvs, lits, solver_name) -- | Check the validity of a proposition using What4. proveWhat4_solver :: forall st t ff. SolverAdapter st {- ^ Which solver to use -} -> B.ExprBuilder t st ff {- ^ The glorious sym -} -> - Set VarIndex {- ^ Uninterpreted functions -} -> SharedContext {- ^ Context for working with terms -} -> - Sequent {- ^ A proposition to be proved/checked. -} -> + SATQuery {- ^ The query to be proved/checked. -} -> IO () {- ^ Extra setup actions -} -> - IO (Maybe CEX, SolverStats) + IO (Maybe CEX, String) -- ^ (example/counter-example, solver statistics) -proveWhat4_solver solver sym unintSet sc goal extraSetup = +proveWhat4_solver solver sym sc satq extraSetup = do - (argNames, bvs, lits, stats) <- setupWhat4_solver solver sym unintSet sc goal + (argNames, bvs, lits, solver_name) <- setupWhat4_solver solver sym sc satq extraSetup -- log to stdout @@ -214,9 +204,9 @@ proveWhat4_solver solver sym unintSet sc goal extraSetup = Sat (gndEvalFcn,_) -> do mvals <- mapM (getValues @(B.ExprBuilder t st ff) gndEvalFcn) (zip bvs argNames) - return (Just mvals, stats) where + return (Just mvals, solver_name) where - Unsat _ -> return (Nothing, stats) + Unsat _ -> return (Nothing, solver_name) Unknown -> fail "Prover returned Unknown" diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index adfb14a35c..bc421783d7 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -12,17 +12,16 @@ Stability : provisional module SAWScript.SolverCache where -import Control.Monad (forM_) -import Control.Exception (try, SomeException) -import Data.List (sortOn) -import Data.Tuple.Extra (first, second) import System.Directory import System.FilePath (()) import Text.Read (readMaybe) +import Data.Tuple.Extra (first, second, firstM) +import Data.List (elemIndex) import Data.Hashable import Data.Bits (shiftL, (.|.)) +import qualified Data.Map as Map import qualified Data.HashMap.Strict as HM import Data.HashMap.Strict (HashMap) @@ -34,25 +33,13 @@ import qualified Data.ByteString as BS import qualified Crypto.Hash.SHA256 as SHA256 +import Verifier.SAW.FiniteValue +import Verifier.SAW.SATQuery import Verifier.SAW.ExternalFormat import Verifier.SAW.SharedTerm -import Verifier.SAW.TypedAST import SAWScript.Options import SAWScript.Proof -import SAWScript.Prover.SolverStats - - --- Helper Functions ------------------------------------------------------------ - --- | Generalize over the all external constants in a given term by --- wrapping the term with foralls and replacing the external constant --- occurrences with the appropriate local variables. --- FIXME: Move to SharedTerm.hs -scGeneralizeAllExts :: SharedContext -> Term -> IO Term -scGeneralizeAllExts sc tm = - let allexts = sortOn (toShortName . ecName) $ getAllExts tm - in scGeneralizeExts sc allexts tm -- Solver Cache Keys ----------------------------------------------------------- @@ -78,21 +65,60 @@ solverCacheKeyToHex (SolverCacheKey bs) = T.unpack $ encodeHex bs solverCacheKeyFromHex :: String -> Maybe SolverCacheKey solverCacheKeyFromHex x = fmap SolverCacheKey $ decodeHex $ T.pack x --- | Hash the 'String' representation ('scWriteExternal') of a 'Sequent' using --- SHA256 to get a 'SolverCacheKey' -propToSolverCacheKey :: SharedContext -> Sequent -> IO (Maybe SolverCacheKey) -propToSolverCacheKey sc sq = do - try (sequentToProp sc sq) >>= \case - Right p -> Just . SolverCacheKey . SHA256.hash . - encodeUtf8 . T.pack . scWriteExternal <$> - scGeneralizeAllExts sc (unProp p) - Left (_ :: SomeException) -> return Nothing +-- | Hash using SHA256 a 'String' representation of a 'SATQuery' to get a +-- 'SolverCacheKey' +satQueryToSolverCacheKey :: SharedContext -> SATQuery -> IO SolverCacheKey +satQueryToSolverCacheKey sc satq = do + body <- satQueryAsPropTerm sc satq + let ecs = Map.keys (satVariables satq) ++ + filter (\ec -> ecVarIndex ec `elem` satUninterp satq) + (getAllExts body) + tm <- scGeneralizeExts sc ecs body + let str_to_hash = show (Map.size (satVariables satq)) ++ " " ++ + show (length (satUninterp satq)) ++ "\n" ++ + scWriteExternal tm + return $ SolverCacheKey $ SHA256.hash $ encodeUtf8 $ T.pack $ str_to_hash + + +-- Solver Cache Values --------------------------------------------------------- + +-- | The value type for 'SolverCache': a pair of the 'String' representing the +-- solver used and an optional list of counterexamples, represented as pairs +-- of indexes into the list of 'satVariables' +type SolverCacheValue = (String, Maybe [(Int, FirstOrderValue)]) + +-- | Convert the result of a solver call on the given 'SATQuery' to a +-- 'SolverCacheValue' +toSolverCacheValue :: SATQuery -> (Maybe CEX, String) -> Maybe SolverCacheValue +toSolverCacheValue satq (cexs, solver_name) = + (solver_name,) <$> mapM (mapM (firstM (`elemIndex` ecs))) cexs + where ecs = Map.keys $ satVariables satq + +-- | Convert a 'SolverCacheValue' to something which has the same form as the +-- result of a solver call on the given 'SATQuery' +fromSolverCacheValue :: SATQuery -> SolverCacheValue -> (Maybe CEX, String) +fromSolverCacheValue satq (solver_name, cexs) = + (fmap (fmap (first (ecs !!))) cexs ,solver_name) + where ecs = Map.keys $ satVariables satq + +-- | Given a path to a cache and a 'SolverCacheKey', return a +-- 'SolverCacheValue' if the given key has been cached, or 'Nothing' otherwise +readCacheEntryFromDisk :: FilePath -> SolverCacheKey -> IO (Maybe SolverCacheValue) +readCacheEntryFromDisk path k = do + ex <- doesFileExist (path solverCacheKeyToHex k) + if not ex then return Nothing + else readMaybe <$> readFile (path solverCacheKeyToHex k) + +-- | Given a path to a cache and a 'SolverCacheKey'/'SolverCacheValue' pair, +-- add an approriate entry to the cache on disk +writeCacheEntryToDisk :: FilePath -> (SolverCacheKey, SolverCacheValue) -> IO () +writeCacheEntryToDisk path (k, v) = + createDirectoryIfMissing False path >> + writeFile (path solverCacheKeyToHex k) (show v) -- The Solver Cache ------------------------------------------------------------ -type SolverCacheValue = (Maybe CEX, SolverStats) - -- | A set of cached solver results as well as a 'FilePath' indicating where -- new additions to the cache should be saved data SolverCache = @@ -116,28 +142,22 @@ lookupInSolverCache k opts cache = (Just v, _) -> do printOutLn opts Info ("Using cached result from memory (" ++ solverCacheKeyToHex k ++ ")") return (Just v, cache { solverCacheHits = first (+1) (solverCacheHits cache) }) - (_, Just path) -> do - ex <- doesFileExist (path solverCacheKeyToHex k) - if not ex then return (Nothing, cache) - else readMaybe <$> readFile (path solverCacheKeyToHex k) >>= \case - Just (sz, ss) -> do - let v = (Nothing, SolverStats ss sz) + (_, Just path) -> readCacheEntryFromDisk path k >>= \case + Just v -> do printOutLn opts Info ("Using cached result from disk (" ++ solverCacheKeyToHex k ++ ")") return (Just v, cache { solverCacheMap = HM.insert k v (solverCacheMap cache) , solverCacheHits = second (+1) (solverCacheHits cache) }) - Nothing -> return (Nothing, cache) + Nothing -> return (Nothing, cache) _ -> return (Nothing, cache) -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () -insertInSolverCache k v@(Nothing, SolverStats ss sz) opts cache = do +insertInSolverCache k v opts cache = do printOutLn opts Info ("Caching result (" ++ solverCacheKeyToHex k ++ ")") case solverCachePath cache of - Just path -> createDirectoryIfMissing False path >> - writeFile (path solverCacheKeyToHex k) (show (sz, ss)) + Just path -> writeCacheEntryToDisk path (k, v) _ -> return () return ((), cache { solverCacheMap = HM.insert k v (solverCacheMap cache) }) -insertInSolverCache _ _ _ cache = return ((), cache) -- | Set the 'FilePath' of the solver result cache, erroring if it is already -- set, and save all results cached so far @@ -148,11 +168,9 @@ setSolverCachePath path opts cache = Just path' -> fail $ "Solver cache already has a set path: " ++ path' _ | HM.null (solverCacheMap cache) -> return ((), cache { solverCachePath = Just pathAbs }) _ -> let to_save = HM.toList $ solverCacheMap cache in - createDirectoryIfMissing False pathAbs >> let (s0, s1) = (show (length to_save), if length to_save > 1 then "s" else "") in printOutLn opts Info ("Saving " ++ s0 ++ " cached result" ++ s1 ++ " to disk") >> - forM_ to_save (\(k,(_, SolverStats ss sz)) -> - writeFile (pathAbs solverCacheKeyToHex k) (show (sz, ss))) >> + mapM_ (writeCacheEntryToDisk path) to_save >> return ((), cache { solverCachePath = Just pathAbs }) -- | Print out statistics about how the solver cache was used From 7e264c57d70c8380dbf448e7605c65900bd48666 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 17 May 2023 11:45:40 -0400 Subject: [PATCH 14/51] anonymize local names in satQueryToSolverCacheKey --- src/SAWScript/SolverCache.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index bc421783d7..267fa32924 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -76,8 +76,11 @@ satQueryToSolverCacheKey sc satq = do tm <- scGeneralizeExts sc ecs body let str_to_hash = show (Map.size (satVariables satq)) ++ " " ++ show (length (satUninterp satq)) ++ "\n" ++ - scWriteExternal tm + anonLocalNames (scWriteExternal tm) return $ SolverCacheKey $ SHA256.hash $ encodeUtf8 $ T.pack $ str_to_hash + where anonLocalNames = unlines . map (unwords . go . words) . lines + go (x:y:_:xs) | y `elem` ["Pi", "Lam"] = x:y:"_":xs + go xs = xs -- Solver Cache Values --------------------------------------------------------- From 3cc4d1e979745419394c4c17356a71aa08111ee9 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Thu, 18 May 2023 13:04:41 -0400 Subject: [PATCH 15/51] make SolverCache an IORef in TopLevel so entries persist after failure --- saw-remote-api/src/SAWServer.hs | 4 +++- src/SAWScript/Interpreter.hs | 15 ++++++--------- src/SAWScript/Value.hs | 11 +++++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/saw-remote-api/src/SAWServer.hs b/saw-remote-api/src/SAWServer.hs index f448d03ac8..6f8f9267ee 100644 --- a/saw-remote-api/src/SAWServer.hs +++ b/saw-remote-api/src/SAWServer.hs @@ -27,6 +27,7 @@ import qualified Crypto.Hash as Hash --import qualified Crypto.Hash.Conduit as Hash import System.Directory (getCurrentDirectory) import System.IO.Silently (silence) +import Data.IORef import qualified Cryptol.Parser.AST as P import qualified Cryptol.TypeCheck.AST as Cryptol (Schema) @@ -205,6 +206,7 @@ initialState readFileFn = halloc <- Crucible.newHandleAllocator jvmTrans <- CJ.mkInitialJVMContext halloc cwd <- getCurrentDirectory + cache <- newIORef Nothing db <- newTheoremDB let ro = TopLevelRO { roJavaCodebase = jcb @@ -218,6 +220,7 @@ initialState readFileFn = #endif , roInitWorkDir = cwd , roBasicSS = ss + , roSolverCache = cache , roStackTrace = [] , roSubshell = fail "SAW server does not support subshells." , roProofSubshell = fail "SAW server does not support subshells." @@ -233,7 +236,6 @@ initialState readFileFn = , rwMRSolverEnv = emptyMREnv , rwPPOpts = defaultPPOpts , rwTheoremDB = db - , rwSolverCache = Nothing , rwSharedContext = sc , rwJVMTrans = jvmTrans , rwPrimsAvail = mempty diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index d6b5705796..5932b02b36 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -41,6 +41,7 @@ import Control.Applicative ( (<|>) ) import qualified Control.Exception as X import Control.Monad (unless, (>=>), when) import Control.Monad.IO.Class (liftIO) +import Data.IORef import Data.Maybe (fromMaybe) import qualified Data.ByteString as BS import qualified Data.Map as Map @@ -454,6 +455,8 @@ buildTopLevelEnv proxy opts = ss <- basic_ss sc jcb <- JCB.loadCodebase (jarList opts) (classPath opts) (javaBinDirs opts) currDir <- getCurrentDirectory + cache <- mapM (\p -> snd <$> setSolverCachePath p opts emptySolverCache) + (solverCache opts) >>= newIORef thmDB <- newTheoremDB Crucible.withHandleAllocator $ \halloc -> do let ro0 = TopLevelRO @@ -464,6 +467,7 @@ buildTopLevelEnv proxy opts = , roProxy = proxy , roInitWorkDir = currDir , roBasicSS = ss + , roSolverCache = cache , roStackTrace = [] , roSubshell = fail "Subshells not supported" , roProofSubshell = fail "Proof subshells not supported" @@ -478,11 +482,6 @@ buildTopLevelEnv proxy opts = jvmTrans <- CJ.mkInitialJVMContext halloc - cache <- maybe (return Nothing) - (\p -> Just . snd <$> - setSolverCachePath p opts emptySolverCache) - (solverCache opts) - let rw0 = TopLevelRW { rwValues = valueEnv primsAvail opts bic , rwTypes = primTypeEnv primsAvail @@ -495,7 +494,6 @@ buildTopLevelEnv proxy opts = , rwPPOpts = SAWScript.Value.defaultPPOpts , rwSharedContext = sc , rwTheoremDB = thmDB - , rwSolverCache = cache , rwJVMTrans = jvmTrans , rwPrimsAvail = primsAvail , rwSMTArrayMemoryModel = False @@ -647,9 +645,8 @@ disable_lax_loads_and_stores = do enable_solver_cache :: TopLevel () enable_solver_cache = do - rw <- getTopLevelRW - let cache = fromMaybe emptySolverCache (rwSolverCache rw) - putTopLevelRW rw { rwSolverCache = Just cache } + ref <- getSolverCache + io $ atomicModifyIORef ref $ (,()) . Just . fromMaybe emptySolverCache set_solver_cache_path :: FilePath -> TopLevel () set_solver_cache_path path = diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 95a20476eb..8b2086dcb9 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -481,6 +481,7 @@ data TopLevelRO = , roProxy :: AIGProxy , roInitWorkDir :: FilePath , roBasicSS :: SAWSimpset + , roSolverCache :: IORef (Maybe SolverCache) , roStackTrace :: [String] -- ^ SAWScript-internal backtrace for use -- when displaying exceptions and such @@ -512,7 +513,6 @@ data TopLevelRW = , rwPPOpts :: PPOpts , rwSharedContext :: SharedContext , rwTheoremDB :: TheoremDB - , rwSolverCache :: Maybe SolverCache -- , rwCrucibleLLVMCtx :: Crucible.LLVMContext , rwJVMTrans :: CJ.JVMContext @@ -683,6 +683,9 @@ getSharedContext = TopLevel_ (rwSharedContext <$> get) getJavaCodebase :: TopLevel JSS.Codebase getJavaCodebase = TopLevel_ (asks roJavaCodebase) +getSolverCache :: TopLevel (IORef (Maybe SolverCache)) +getSolverCache = TopLevel_ (asks roSolverCache) + getTheoremDB :: TopLevel TheoremDB getTheoremDB = TopLevel_ (rwTheoremDB <$> get) @@ -733,10 +736,10 @@ recordProof v = onSolverCache :: Monoid a => SolverCacheOp a -> TopLevel a onSolverCache f = do opts <- getOptions - rw <- getTopLevelRW - case rwSolverCache rw of + ref <- getSolverCache + io (readIORef ref) >>= \case Just cache -> do (ret, cache') <- io $ f opts cache - putTopLevelRW rw { rwSolverCache = Just cache' } + io $ atomicWriteIORef ref (Just cache') return ret Nothing -> return mempty From 084dd14e17e37c3b20cf4cf3f8bf7572b1d02316 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Fri, 19 May 2023 12:34:07 -0400 Subject: [PATCH 16/51] add documentation to SolverCache.hs --- src/SAWScript/SolverCache.hs | 71 ++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 267fa32924..241b521c4b 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -4,6 +4,34 @@ Description : Caching SMT solver results for SAWScript License : BSD3 Maintainer : m-yac Stability : provisional + +This file defines an interface for caching SMT/SAT solver results in memory and +on disk. The interface, as used in 'applyProverToGoal', works as follows: + +1. An 'SMTQuery' is converted into a string using 'scWriteExternal' and + then hashed using SHA256 ('satQueryToSolverCacheKey'). +2. The 'SolverCache' contains a map from these hashes to previously obtained + results ('solverCacheMap'). If the hash corresponding to the 'SATQuery' can + be found in the map, then the corresponding result is used. +3. Otherwise, if the 'SolverCache' was given a path to a directory + ('solverCachePath') and a file whose name is the hash can be found in that + directory, the file's contents are 'read' and used as the result. +4. Otherwise, the 'SATQuery' is dispatched to the requested backend and a + result is obtained. Then: + - This result is added to the 'SolverCache' map using the hash of the + 'SATQuery' as the key. + - If the 'SolverCache' was given a path to a directory ('solverCachePath'), + then a file whose name is the hash and whose contents are 'show' of the + result is added to the directory. + +A interesting detail is how results are represented. For all of the backends +which use 'applyProverToGoal', the type of a result is: +@Maybe [(ExtCns Term, FirstOrderValue)]@, where 'Nothing' represents a result +of "unsat," and 'Just' represents a result of "sat" along with a list of +counterexamples. Since 'ExtCns' contains execution-specific 'VarIndex'es, we +don't want to save these results directly. Instead, we represent each 'ExtCns' +as its index in the 'satVariables' field of 'SATQuery' (which is where they +were obtained by the backends in the first place). -} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -14,6 +42,7 @@ module SAWScript.SolverCache where import System.Directory import System.FilePath (()) +import Control.Exception import Text.Read (readMaybe) import Data.Tuple.Extra (first, second, firstM) @@ -44,7 +73,8 @@ import SAWScript.Proof -- Solver Cache Keys ----------------------------------------------------------- --- | The key type for 'SolverCache': SHA256 hashes +-- | The key type for 'SolverCache': SHA256 hashes of 'SATQuery's - see +-- 'satQueryToSolverCacheKey' newtype SolverCacheKey = SolverCacheKey ByteString deriving Eq -- | Truncate a 'SolverCacheKey' (i.e. a SHA256 hash) to an 'Int', used to give @@ -66,7 +96,16 @@ solverCacheKeyFromHex :: String -> Maybe SolverCacheKey solverCacheKeyFromHex x = fmap SolverCacheKey $ decodeHex $ T.pack x -- | Hash using SHA256 a 'String' representation of a 'SATQuery' to get a --- 'SolverCacheKey' +-- 'SolverCacheKey'. In particular the 'String' representation used is the +-- number of 'satVariables', followed by the number of 'satUninterp's, followed +-- by the 'scWriteExternal' representation of the 'satQueryAsPropTerm'. +-- However, for this last step, we do two additional things: +-- 1. Before calling 'scWriteExternal', we generalize ('scGeneralizeExts') over +-- all 'satVariables' and 'satUninterp's. This ensures the hash does not +-- depend on any execution-specific 'VarIndex'es. +-- 2. After calling 'scWriteExternal', all 'LocalName's in 'Pi' and 'Lam' +-- constructors are removed. This ensures that two terms which are alpha +-- equivalent are given the same hash. satQueryToSolverCacheKey :: SharedContext -> SATQuery -> IO SolverCacheKey satQueryToSolverCacheKey sc satq = do body <- satQueryAsPropTerm sc satq @@ -105,19 +144,27 @@ fromSolverCacheValue satq (solver_name, cexs) = where ecs = Map.keys $ satVariables satq -- | Given a path to a cache and a 'SolverCacheKey', return a --- 'SolverCacheValue' if the given key has been cached, or 'Nothing' otherwise +-- 'SolverCacheValue' if the given key has been cached, or 'Nothing' otherwise. +-- Note that if we encounter an 'IOException', we simply return 'Nothing', +-- meaning we fall back to calling the solver backend. The idea is that solver +-- result caching is an optional step, so if we fail during a read from the disk +-- we don't want execution to stop, we just want to not use caching. readCacheEntryFromDisk :: FilePath -> SolverCacheKey -> IO (Maybe SolverCacheValue) -readCacheEntryFromDisk path k = do - ex <- doesFileExist (path solverCacheKeyToHex k) - if not ex then return Nothing - else readMaybe <$> readFile (path solverCacheKeyToHex k) +readCacheEntryFromDisk path k = + catch (readMaybe <$> readFile (path solverCacheKeyToHex k)) + (\(_ :: IOException) -> return Nothing) -- | Given a path to a cache and a 'SolverCacheKey'/'SolverCacheValue' pair, --- add an approriate entry to the cache on disk +-- add an approriate entry to the cache on disk. Note that if we encounter an +-- 'IOException, we simply do nothing, meaning we do not cache this result. The +-- idea is that solver result caching is an optional step, so if we fail during +-- a write to the disk we don't want execution to stop, we just want to not use +-- caching. writeCacheEntryToDisk :: FilePath -> (SolverCacheKey, SolverCacheValue) -> IO () -writeCacheEntryToDisk path (k, v) = - createDirectoryIfMissing False path >> - writeFile (path solverCacheKeyToHex k) (show v) +writeCacheEntryToDisk path (k, v) = + catch (createDirectoryIfMissing False path >> + writeFile (path solverCacheKeyToHex k) (show v)) + (\(_ :: IOException) -> return ()) -- The Solver Cache ------------------------------------------------------------ @@ -126,7 +173,7 @@ writeCacheEntryToDisk path (k, v) = -- new additions to the cache should be saved data SolverCache = SolverCache - { solverCacheMap :: HashMap SolverCacheKey SolverCacheValue + { solverCacheMap :: HashMap SolverCacheKey SolverCacheValue , solverCachePath :: Maybe FilePath , solverCacheHits :: (Integer, Integer) } From fcef4ec2e8fefede4d87a08e5ecec998eb34aae4 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Fri, 19 May 2023 21:29:47 -0400 Subject: [PATCH 17/51] include solver backend versions in solver caching --- Setup.hs | 42 +++++++++++--- saw-script.cabal | 6 ++ src/SAWScript/Builtins.hs | 57 +++++++++++-------- src/SAWScript/SolverCache.hs | 98 ++++++++++++++++++++++----------- src/SAWScript/SolverVersions.hs | 80 +++++++++++++++++++++++++++ 5 files changed, 217 insertions(+), 66 deletions(-) create mode 100644 src/SAWScript/SolverVersions.hs diff --git a/Setup.hs b/Setup.hs index 10e9dc83a7..7ff5a23a38 100644 --- a/Setup.hs +++ b/Setup.hs @@ -1,9 +1,10 @@ import Control.Exception import Control.Monad (unless) +import Data.List (find) import Distribution.Simple import Distribution.Simple.BuildPaths (autogenPackageModulesDir) import Distribution.PackageDescription (emptyHookedBuildInfo, allBuildInfo) -import System.Directory (createDirectoryIfMissing, findExecutable) +import System.Directory (createDirectoryIfMissing, findExecutable, withCurrentDirectory) import System.FilePath (()) import System.Process (readProcess) import System.Exit @@ -11,24 +12,47 @@ import System.Exit main = defaultMainWithHooks myHooks where myHooks = simpleUserHooks { buildHook = myBuild } +withExe dir exe_str on_no_exe on_fail m = do + mb <- findExecutable exe_str + + let onfailure :: SomeException -> IO String + onfailure _e = return on_fail + + case mb of + Just exe -> withCurrentDirectory dir (m exe) + `catch` onfailure + Nothing -> return on_no_exe + myBuild pd lbi uh flags = do let dir = autogenPackageModulesDir lbi createDirectoryIfMissing True dir - hasGit <- findExecutable "git" + desc <- withExe "." "git" "" "" $ \git -> + init <$> readProcess git ["describe", "--always", "--dirty"] "" - let gitfailure :: SomeException -> IO String - gitfailure _e = return " " + aig_desc <- withExe "deps/aig" "git" "unknown" "unknown" $ \git -> do + init <$> readProcess git ["describe", "--always", "--dirty"] "" + + w4_desc <- withExe "deps/what4" "git" "unknown" "unknown" $ \git -> do + init <$> readProcess git ["describe", "--always", "--dirty"] "" - desc <- case hasGit of - Just git -> readProcess "git" ["describe", "--always", "--dirty"] "" - `catch` gitfailure - Nothing -> return " " + sbv_ver <- withExe "." "cabal" "unknown" "unknown" $ \cabal -> do + ls <- lines <$> readProcess cabal ["freeze"] "" + wss <- fmap words <$> lines <$> readFile (ls !! 1) + case find (\ws -> (ws !! 0) == "any.sbv") wss of + Just ws -> return $ init $ drop 2 $ ws !! 1 + Nothing -> return "unknown" writeFile (dir "GitRev.hs") $ unlines [ "module GitRev where" , "hash :: String" - , "hash = " ++ show (init desc) + , "hash = " ++ show desc + , "aigHash :: String" + , "aigHash = " ++ show aig_desc + , "w4Hash :: String" + , "w4Hash = " ++ show w4_desc + , "sbvVer :: String" + , "sbvVer = " ++ show sbv_ver ] unless (null $ allBuildInfo pd) $ diff --git a/saw-script.cabal b/saw-script.cabal index c8855ab201..b6f07e3e7c 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -129,6 +129,7 @@ library SAWScript.SBVParser SAWScript.SBVModel SAWScript.SolverCache + SAWScript.SolverVersions SAWScript.Token SAWScript.TopLevel SAWScript.MGU @@ -189,6 +190,11 @@ library SAWScript.Yosys.TransitionSystem SAWScript.Yosys.Utils + other-modules: + GitRev + autogen-modules: + GitRev + GHC-options: -O2 -Wall -fno-ignore-asserts -fno-spec-constr-count if impl(ghc == 8.0.1) ghc-options: -Wno-redundant-constraints diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index f710fa10c5..5e4ec96c65 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -126,6 +126,7 @@ import SAWScript.TopLevel import qualified SAWScript.Value as SV import SAWScript.Value (ProofScript, printOutLnTop, AIGNetwork) import SAWScript.SolverCache +import SAWScript.SolverVersions import SAWScript.Crucible.Common.MethodSpec (ppTypedTermType) import SAWScript.Prover.Util(checkBooleanSchema) @@ -908,7 +909,7 @@ goal_num_ite n s1 s2 = proveABC :: ProofScript () proveABC = do SV.AIGProxy proxy <- SV.scriptTopLevel SV.getProxy - wrapProver (Prover.proveABC proxy) Set.empty + wrapProver AIG (Prover.proveABC proxy) Set.empty satExternal :: Bool -> String -> [String] -> ProofScript () satExternal doCNF execName args = @@ -933,7 +934,7 @@ writeSAIGComputedPrim = Prover.writeSAIG -- | Bit-blast a proposition check its validity using the RME library. proveRME :: ProofScript () -proveRME = wrapProver Prover.proveRME Set.empty +proveRME = wrapProver RME Prover.proveRME Set.empty codegenSBV :: SharedContext -> FilePath -> [String] -> String -> TypedTerm -> TopLevel () codegenSBV sc path unints fname (TypedTerm _schema t) = @@ -953,20 +954,22 @@ proveUnintSBV :: SBV.SMTConfig -> [String] -> ProofScript () proveUnintSBV conf unints = do timeout <- psTimeout <$> get unintSet <- SV.scriptTopLevel (resolveNames unints) - wrapProver (Prover.proveUnintSBV conf timeout) unintSet + wrapProver (SBV conf) (Prover.proveUnintSBV conf timeout) unintSet -- | Given a continuation which calls a prover, call the continuation on the -- 'goalSequent' of the given 'ProofGoal' and return a 'SolveResult'. If there -- is a 'SolverCache', do not call the continuation if the goal has an already -- cached result, and otherwise save the result of the call to the cache. -applyProverToGoal :: (SATQuery -> TopLevel (Maybe CEX, String)) +applyProverToGoal :: SolverBackend + -> (SATQuery -> TopLevel (Maybe CEX, String)) -> Set VarIndex -> ProofGoal -> TopLevel (SolverStats, SolveResult) -applyProverToGoal f unintSet g = do +applyProverToGoal backend f unintSet g = do sc <- getSharedContext + bkvs <- io $ getSolverBackendVersions backend satq <- io $ sequentToSATQuery sc unintSet (goalSequent g) - k <- io $ satQueryToSolverCacheKey sc satq + k <- io $ mkSolverCacheKey sc bkvs satq (mb, solver_name) <- SV.onSolverCache (lookupInSolverCache k) >>= \case -- Use a cached result if one exists (and it's valid w.r.t our query) Just v -> return $ fromSolverCacheValue satq v @@ -982,19 +985,21 @@ applyProverToGoal f unintSet g = do Just a -> return (stats, SolveCounterexample a) wrapProver :: + SolverBackend -> (SATQuery -> TopLevel (Maybe CEX, String)) -> Set VarIndex -> ProofScript () -wrapProver f unintSet = execTactic $ tacticSolve $ applyProverToGoal f unintSet +wrapProver backend f = execTactic . tacticSolve . applyProverToGoal backend f wrapW4Prover :: + W4BackendExtra -> SBV.Solver -> ( Bool -> SATQuery -> TopLevel (Maybe CEX, String) ) -> [String] -> ProofScript () -wrapW4Prover f unints = do +wrapW4Prover x s f unints = do hashConsing <- SV.scriptTopLevel $ gets SV.rwWhat4HashConsing unintSet <- SV.scriptTopLevel $ resolveNames unints - wrapProver (f hashConsing) unintSet + wrapProver (W4 x s) (f hashConsing) unintSet wrapW4ProveExporter :: ( Bool -> FilePath -> SATQuery -> TopLevel (Maybe CEX, String) ) -> @@ -1007,7 +1012,11 @@ wrapW4ProveExporter f unints path ext = do unintSet <- SV.scriptTopLevel $ resolveNames unints execTactic $ tacticSolve $ \g -> do let file = path ++ "." ++ goalType g ++ show (goalNum g) ++ ext - applyProverToGoal (f hashConsing file) unintSet g + sc <- getSharedContext + satq <- io $ sequentToSATQuery sc unintSet (goalSequent g) + (_, solver_name) <- f hashConsing file satq + let stats = solverStats solver_name (sequentSharedSize (goalSequent g)) + return (stats, SolveSuccess (SolverEvidence stats (goalSequent g))) -------------------------------------------------- proveABC_SBV :: ProofScript () @@ -1052,40 +1061,40 @@ proveUnintYices = proveUnintSBV SBV.yices -------------------------------------------------- w4_abc_smtlib2 :: ProofScript () -w4_abc_smtlib2 = wrapW4Prover Prover.proveWhat4_abc [] +w4_abc_smtlib2 = wrapW4Prover W4_SMTLib2 ABC Prover.proveWhat4_abc [] w4_boolector :: ProofScript () -w4_boolector = wrapW4Prover Prover.proveWhat4_boolector [] +w4_boolector = wrapW4Prover W4_Base Boolector Prover.proveWhat4_boolector [] w4_z3 :: ProofScript () -w4_z3 = wrapW4Prover Prover.proveWhat4_z3 [] +w4_z3 = wrapW4Prover W4_Base Z3 Prover.proveWhat4_z3 [] w4_cvc4 :: ProofScript () -w4_cvc4 = wrapW4Prover Prover.proveWhat4_cvc4 [] +w4_cvc4 = wrapW4Prover W4_Base CVC4 Prover.proveWhat4_cvc4 [] w4_cvc5 :: ProofScript () -w4_cvc5 = wrapW4Prover Prover.proveWhat4_cvc5 [] +w4_cvc5 = wrapW4Prover W4_Base CVC5 Prover.proveWhat4_cvc5 [] w4_yices :: ProofScript () -w4_yices = wrapW4Prover Prover.proveWhat4_yices [] +w4_yices = wrapW4Prover W4_Base Yices Prover.proveWhat4_yices [] w4_unint_boolector :: [String] -> ProofScript () -w4_unint_boolector = wrapW4Prover Prover.proveWhat4_boolector +w4_unint_boolector = wrapW4Prover W4_Base Boolector Prover.proveWhat4_boolector w4_unint_z3 :: [String] -> ProofScript () -w4_unint_z3 = wrapW4Prover Prover.proveWhat4_z3 +w4_unint_z3 = wrapW4Prover W4_Base Z3 Prover.proveWhat4_z3 w4_unint_z3_using :: String -> [String] -> ProofScript () -w4_unint_z3_using tactic = wrapW4Prover (Prover.proveWhat4_z3_using tactic) +w4_unint_z3_using tactic = wrapW4Prover (W4_Tactic tactic) Z3 (Prover.proveWhat4_z3_using tactic) w4_unint_cvc4 :: [String] -> ProofScript () -w4_unint_cvc4 = wrapW4Prover Prover.proveWhat4_cvc4 +w4_unint_cvc4 = wrapW4Prover W4_Base CVC4 Prover.proveWhat4_cvc4 w4_unint_cvc5 :: [String] -> ProofScript () -w4_unint_cvc5 = wrapW4Prover Prover.proveWhat4_cvc5 +w4_unint_cvc5 = wrapW4Prover W4_Base CVC5 Prover.proveWhat4_cvc5 w4_unint_yices :: [String] -> ProofScript () -w4_unint_yices = wrapW4Prover Prover.proveWhat4_yices +w4_unint_yices = wrapW4Prover W4_Base Yices Prover.proveWhat4_yices offline_w4_unint_z3 :: [String] -> String -> ProofScript () offline_w4_unint_z3 unints path = @@ -1168,10 +1177,10 @@ offline_verilog path = proveWithSATExporter Prover.writeVerilogSAT mempty path "." ".v" w4_abc_aiger :: ProofScript () -w4_abc_aiger = wrapW4Prover Prover.w4AbcAIGER [] +w4_abc_aiger = wrapW4Prover W4_AIGER ABC Prover.w4AbcAIGER [] w4_abc_verilog :: ProofScript () -w4_abc_verilog = wrapW4Prover Prover.w4AbcVerilog [] +w4_abc_verilog = wrapW4Prover W4_Verilog ABC Prover.w4AbcVerilog [] set_timeout :: Integer -> ProofScript () set_timeout to = modify (setProofTimeout to) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 241b521c4b..cd9a05e68d 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -8,18 +8,21 @@ Stability : provisional This file defines an interface for caching SMT/SAT solver results in memory and on disk. The interface, as used in 'applyProverToGoal', works as follows: -1. An 'SMTQuery' is converted into a string using 'scWriteExternal' and - then hashed using SHA256 ('satQueryToSolverCacheKey'). +1. An 'SMTQuery' is converted into a string using 'scWriteExternal', and + along with any relevant 'SolverBackendVersions' (obtained using + 'getSolverBackendVersions' from @SAWScript.SolverVersions@), is then hashed + using SHA256 ('mkSolverCacheKey'). 2. The 'SolverCache' contains a map from these hashes to previously obtained - results ('solverCacheMap'). If the hash corresponding to the 'SATQuery' can - be found in the map, then the corresponding result is used. + results ('solverCacheMap'). If the hash corresponding to the 'SATQuery' and + 'SolverBackendVersions' can be found in the map, then the corresponding + result is used. 3. Otherwise, if the 'SolverCache' was given a path to a directory ('solverCachePath') and a file whose name is the hash can be found in that directory, the file's contents are 'read' and used as the result. 4. Otherwise, the 'SATQuery' is dispatched to the requested backend and a result is obtained. Then: - This result is added to the 'SolverCache' map using the hash of the - 'SATQuery' as the key. + 'SATQuery' and 'SolverBackendVersions' as the key. - If the 'SolverCache' was given a path to a directory ('solverCachePath'), then a file whose name is the hash and whose contents are 'show' of the result is added to the directory. @@ -38,7 +41,22 @@ were obtained by the backends in the first place). {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-} -module SAWScript.SolverCache where +module SAWScript.SolverCache + ( SolverBackendVersions + , SolverCacheKey(..) + , solverCacheKeyToHex + , mkSolverCacheKey + , SolverCacheValue + , toSolverCacheValue + , fromSolverCacheValue + , SolverCache(..) + , emptySolverCache + , SolverCacheOp + , lookupInSolverCache + , insertInSolverCache + , setSolverCachePath + , printSolverCacheStats + ) where import System.Directory import System.FilePath (()) @@ -46,7 +64,7 @@ import Control.Exception import Text.Read (readMaybe) import Data.Tuple.Extra (first, second, firstM) -import Data.List (elemIndex) +import Data.List (elemIndex, intercalate) import Data.Hashable import Data.Bits (shiftL, (.|.)) @@ -73,50 +91,63 @@ import SAWScript.Proof -- Solver Cache Keys ----------------------------------------------------------- --- | The key type for 'SolverCache': SHA256 hashes of 'SATQuery's - see --- 'satQueryToSolverCacheKey' -newtype SolverCacheKey = SolverCacheKey ByteString deriving Eq +-- | A set of solver backend versions obtained using 'getSolverBackendVersions' +-- from @SAWScript.SolverVersions@ +type SolverBackendVersions = [String] + +-- | The key type for 'SolverCache'. Each is a SHA256 hashes of 'SATQuery' and +-- a set of 'SolverBackendVersions' - see 'mkSolverCacheKey' +data SolverCacheKey = + SolverCacheKey + { solverCacheKeyBackendVersions :: SolverBackendVersions + , solverCacheKeyHash :: ByteString + } + +instance Eq SolverCacheKey where + (SolverCacheKey _ bs1) == (SolverCacheKey _ bs2) = bs1 == bs2 -- | Truncate a 'SolverCacheKey' (i.e. a SHA256 hash) to an 'Int', used to give -- the type a fast 'Hashable' instance solverCacheKeyInt :: SolverCacheKey -> Int -solverCacheKeyInt (SolverCacheKey bs) = +solverCacheKeyInt (SolverCacheKey _ bs) = BS.foldl' (\a b -> a `shiftL` 8 .|. fromIntegral b) 0 (BS.take 8 bs) instance Hashable SolverCacheKey where hash = solverCacheKeyInt hashWithSalt s = hashWithSalt s . solverCacheKeyInt +instance Show SolverCacheKey where + show (SolverCacheKey vs bs) = T.unpack (encodeHex (BS.take 8 bs)) ++ + " (" ++ intercalate ", " vs ++ ")" + -- | Convert a 'SolverCacheKey' to a hexadecimal 'String' solverCacheKeyToHex :: SolverCacheKey -> String -solverCacheKeyToHex (SolverCacheKey bs) = T.unpack $ encodeHex bs - --- | Convert a hexadecimal 'String' to a 'SolverCacheKey' -solverCacheKeyFromHex :: String -> Maybe SolverCacheKey -solverCacheKeyFromHex x = fmap SolverCacheKey $ decodeHex $ T.pack x - --- | Hash using SHA256 a 'String' representation of a 'SATQuery' to get a --- 'SolverCacheKey'. In particular the 'String' representation used is the --- number of 'satVariables', followed by the number of 'satUninterp's, followed --- by the 'scWriteExternal' representation of the 'satQueryAsPropTerm'. --- However, for this last step, we do two additional things: +solverCacheKeyToHex (SolverCacheKey _ bs) = T.unpack $ encodeHex bs + +-- | Hash using SHA256 a 'String' representation of a 'SATQuery' and a set of +-- 'SolverBackendVersions' to get a 'SolverCacheKey'. In particular, this +-- 'String' representation contains all the 'SolverBackendVersions', the +-- number of 'satVariables' in the 'SATQuery', the number of 'satUninterp's in +-- the 'SATQuery, and finally the 'scWriteExternal' representation of the +-- 'satQueryAsPropTerm' of the 'SATQuery' - with two additional things: -- 1. Before calling 'scWriteExternal', we generalize ('scGeneralizeExts') over -- all 'satVariables' and 'satUninterp's. This ensures the hash does not -- depend on any execution-specific 'VarIndex'es. -- 2. After calling 'scWriteExternal', all 'LocalName's in 'Pi' and 'Lam' -- constructors are removed. This ensures that two terms which are alpha -- equivalent are given the same hash. -satQueryToSolverCacheKey :: SharedContext -> SATQuery -> IO SolverCacheKey -satQueryToSolverCacheKey sc satq = do +mkSolverCacheKey :: SharedContext -> SolverBackendVersions -> SATQuery -> + IO SolverCacheKey +mkSolverCacheKey sc vs satq = do body <- satQueryAsPropTerm sc satq let ecs = Map.keys (satVariables satq) ++ filter (\ec -> ecVarIndex ec `elem` satUninterp satq) (getAllExts body) tm <- scGeneralizeExts sc ecs body - let str_to_hash = show (Map.size (satVariables satq)) ++ " " ++ - show (length (satUninterp satq)) ++ "\n" ++ - anonLocalNames (scWriteExternal tm) - return $ SolverCacheKey $ SHA256.hash $ encodeUtf8 $ T.pack $ str_to_hash + let str_prefix = vs ++ [ "satVariables " ++ show (Map.size (satVariables satq)) + , "satUninterp " ++ show (length (satUninterp satq)) ] + str_to_hash = unlines str_prefix ++ anonLocalNames (scWriteExternal tm) + return $ SolverCacheKey vs $ SHA256.hash $ encodeUtf8 $ T.pack $ str_to_hash where anonLocalNames = unlines . map (unwords . go . words) . lines go (x:y:_:xs) | y `elem` ["Pi", "Lam"] = x:y:"_":xs go xs = xs @@ -151,7 +182,7 @@ fromSolverCacheValue satq (solver_name, cexs) = -- we don't want execution to stop, we just want to not use caching. readCacheEntryFromDisk :: FilePath -> SolverCacheKey -> IO (Maybe SolverCacheValue) readCacheEntryFromDisk path k = - catch (readMaybe <$> readFile (path solverCacheKeyToHex k)) + catch (readMaybe . last . lines <$> readFile (path solverCacheKeyToHex k)) (\(_ :: IOException) -> return Nothing) -- | Given a path to a cache and a 'SolverCacheKey'/'SolverCacheValue' pair, @@ -163,7 +194,8 @@ readCacheEntryFromDisk path k = writeCacheEntryToDisk :: FilePath -> (SolverCacheKey, SolverCacheValue) -> IO () writeCacheEntryToDisk path (k, v) = catch (createDirectoryIfMissing False path >> - writeFile (path solverCacheKeyToHex k) (show v)) + writeFile (path solverCacheKeyToHex k) + (unlines $ solverCacheKeyBackendVersions k ++ [show v])) (\(_ :: IOException) -> return ()) @@ -190,11 +222,11 @@ lookupInSolverCache :: SolverCacheKey -> SolverCacheOp (Maybe SolverCacheValue) lookupInSolverCache k opts cache = case (HM.lookup k (solverCacheMap cache), solverCachePath cache) of (Just v, _) -> do - printOutLn opts Info ("Using cached result from memory (" ++ solverCacheKeyToHex k ++ ")") + printOutLn opts Info ("Using cached result from memory: " ++ show k) return (Just v, cache { solverCacheHits = first (+1) (solverCacheHits cache) }) (_, Just path) -> readCacheEntryFromDisk path k >>= \case Just v -> do - printOutLn opts Info ("Using cached result from disk (" ++ solverCacheKeyToHex k ++ ")") + printOutLn opts Info ("Using cached result from disk: " ++ show k) return (Just v, cache { solverCacheMap = HM.insert k v (solverCacheMap cache) , solverCacheHits = second (+1) (solverCacheHits cache) }) Nothing -> return (Nothing, cache) @@ -203,7 +235,7 @@ lookupInSolverCache k opts cache = -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () insertInSolverCache k v opts cache = do - printOutLn opts Info ("Caching result (" ++ solverCacheKeyToHex k ++ ")") + printOutLn opts Info ("Caching result: " ++ show k) case solverCachePath cache of Just path -> writeCacheEntryToDisk path (k, v) _ -> return () diff --git a/src/SAWScript/SolverVersions.hs b/src/SAWScript/SolverVersions.hs new file mode 100644 index 0000000000..31f5a45a29 --- /dev/null +++ b/src/SAWScript/SolverVersions.hs @@ -0,0 +1,80 @@ +{- | +Module : SAWScript.SolverVersions +Description : Determining SMT solver backend versions +License : BSD3 +Maintainer : m-yac +Stability : provisional +-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module SAWScript.SolverVersions + ( Solver(..) + , SolverBackend(..) + , W4BackendExtra(..) + , getSolverBackendVersions + ) where + +import Control.Exception +import System.Process (readProcessWithExitCode) +import System.Exit (ExitCode(..)) +import Data.List (group) + +import qualified Data.SBV.Dynamic as SBV +import Data.SBV.Dynamic (Solver(..)) + +import qualified GitRev as GitRev +import GitRev (aigHash, w4Hash, sbvVer) + +-- | A datatype representing one of the solver backends available in SAW +data SolverBackend = SBV SBV.SMTConfig + | W4 W4BackendExtra Solver + | AIG + | RME + +-- | A datatype representing one of the ways the what4 backend can be used in +-- SAW - i.e. directly ('W4_Base'), with a tactic ('W4_Tactic'), by converting +-- to SMTLib2 then calling ABC ('W4_SMTLib2'), by converting to Verilog then +-- calling ABC ('W4_Verilog'), or by converting to AIGER then calling ABC +-- ('W4_AIGER') +data W4BackendExtra = W4_Base + | W4_Tactic String + | W4_SMTLib2 + | W4_Verilog + | W4_AIGER + +-- | Given an 'SBV.SMTConfig' from @SBV@, query the solver for its version and +-- return the result as a string. +-- Adapted from @what4/test/ProbeSolvers.hs@ +getSolverVersion :: SBV.SMTConfig -> IO String +getSolverVersion c = + let cmd = SBV.executable (SBV.solver c) + args = case SBV.name (SBV.solver c) of + -- n.b. abc will return a non-zero exit code if asked + -- for command usage. + SBV.ABC -> ["s", "-q", "version;quit"] + _ -> ["--version"] + in try (readProcessWithExitCode cmd args "") >>= \case + Right (ExitSuccess,o,_) | [l] <- lines o -> return l + Right _ -> return "unknown" + Left (_ :: SomeException) -> return "unknown" + +-- | Given a solver backend, return a list of all relevant version information. +-- For example, @SBV SBV.z3@ returns @["SBV ?.?", "Z3 version ?.?.? - ? bit"]@ +getSolverBackendVersions :: SolverBackend -> IO [String] +getSolverBackendVersions backend = map (unwords . map head . group) <$> + case backend of + SBV c -> do solver_ver <- getSolverVersion c + return [ ["SBV", sbvVer] + , (show $ SBV.name $ SBV.solver c) : words solver_ver ] + W4 x s -> do solver_ver <- getSolverVersion (SBV.defaultSolverConfig s) + let (ex1, ex2) = w4Extra x + return $ [ ["what4", w4Hash] ++ ex1 ] ++ ex2 ++ + [ show s : words solver_ver ] + AIG -> do return [["AIG", aigHash]] + RME -> do return [["RME", GitRev.hash]] + where w4Extra W4_Base = ([], []) + w4Extra (W4_Tactic t) = (["using", t], []) + w4Extra W4_SMTLib2 = (["to", "SMTLib2"], []) + w4Extra W4_Verilog = (["to", "Verilog"], []) + w4Extra W4_AIGER = (["to", "AIGER"], [["AIG", aigHash]]) From e5ac59dc7aa5c4158cbac6372a4254910e3e1048 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 23 May 2023 18:09:50 -0400 Subject: [PATCH 18/51] use LMDB for solver result caching --- .github/ci.sh | 8 + README.md | 3 +- saw-core/saw-core.cabal | 1 + saw-core/src/Verifier/SAW/FiniteValue.hs | 11 +- saw-script.cabal | 2 + src/SAWScript/Builtins.hs | 4 +- src/SAWScript/SolverCache.hs | 229 ++++++++++++++--------- src/SAWScript/Value.hs | 27 ++- 8 files changed, 187 insertions(+), 98 deletions(-) diff --git a/.github/ci.sh b/.github/ci.sh index 6d86bb867d..bbfbefe3ca 100755 --- a/.github/ci.sh +++ b/.github/ci.sh @@ -75,6 +75,14 @@ build() { } install_system_deps() { + (curl -o lmdb.tar.bz2 -sL https://git.openldap.org/openldap/openldap/-/archive/LMDB_0.9.30/openldap-LMDB_0.9.30.tar.bz2 && tar --strip-components=1 -xf lmdb.tar.bz2 && rm lmdb.tar.bz2) + cd $(pwd)/libraries/liblmdb + chmod +x ./* + if [[ "$RUNNER_OS" == "macOS" ]]; then + make SOEXT=.dylib && make install SOEXT=.dylib + else + make && make install + fi (cd $BIN && curl -o bins.zip -sL "https://github.com/GaloisInc/what4-solvers/releases/download/$SOLVER_PKG_VERSION/$BUILD_TARGET_OS-bin.zip" && unzip -o bins.zip && rm bins.zip) chmod +x $BIN/* cp $BIN/yices_smt2$EXT $BIN/yices-smt2$EXT diff --git a/README.md b/README.md index 6bd97dbba6..77637b2ce1 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ To build SAWScript and related utilities from source: * Ensure that you have the C libraries and header files for `terminfo`, which generally comes as part of `ncurses` on most platforms. On Fedora, it is part of the `ncurses-compat-libs` package. - You will also need the C headers for `zlib`. + You will also need the C headers for `zlib` as well as the C library + and header files for `lmdb`. * Ensure that you have the programs `javac` and `z3` on your `PATH`. Z3 binaries are available at diff --git a/saw-core/saw-core.cabal b/saw-core/saw-core.cabal index 9e6d9d77d9..11acdcb360 100644 --- a/saw-core/saw-core.cabal +++ b/saw-core/saw-core.cabal @@ -44,6 +44,7 @@ library prettyprinter-ansi-terminal >= 1.1.2, random, rme, + serialise, template-haskell, text, th-lift-instances, diff --git a/saw-core/src/Verifier/SAW/FiniteValue.hs b/saw-core/src/Verifier/SAW/FiniteValue.hs index d3e736ca5b..0e19b929db 100644 --- a/saw-core/src/Verifier/SAW/FiniteValue.hs +++ b/saw-core/src/Verifier/SAW/FiniteValue.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveGeneric #-} {- | Module : Verifier.SAW.FiniteValue @@ -22,6 +23,8 @@ import qualified Control.Monad.State as S import Data.Map (Map) import qualified Data.Map as Map import Numeric.Natural (Natural) +import GHC.Generics (Generic) +import Codec.Serialise import Prettyprinter hiding (Doc) @@ -56,7 +59,9 @@ data FirstOrderType | FOTArray FirstOrderType FirstOrderType | FOTTuple [FirstOrderType] | FOTRec (Map FieldName FirstOrderType) - deriving (Eq, Read, Show) + deriving (Eq, Generic, Read, Show) + +instance Serialise FirstOrderType -- automatically derived -- | Values inhabiting those first-order types. data FirstOrderValue @@ -68,7 +73,9 @@ data FirstOrderValue | FOVArray FirstOrderType FirstOrderType | FOVTuple [FirstOrderValue] | FOVRec (Map FieldName FirstOrderValue) - deriving (Eq, Read, Show) + deriving (Eq, Generic, Read, Show) + +instance Serialise FirstOrderValue -- automatically derived toFirstOrderType :: FiniteType -> FirstOrderType toFirstOrderType ft = diff --git a/saw-script.cabal b/saw-script.cabal index b6f07e3e7c..276df0e775 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -61,6 +61,7 @@ library , lens , llvm-pretty >= 0.8 , llvm-pretty-bc-parser >= 0.1.3.1 + , lmdb-simple , macaw-base , macaw-x86 , macaw-symbolic @@ -84,6 +85,7 @@ library , saw-core-sbv , saw-core-what4 , sbv >= 9.1 && < 9.3 + , serialise , split , temporary , template-haskell diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 5e4ec96c65..ec60d946fd 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -970,12 +970,12 @@ applyProverToGoal backend f unintSet g = do bkvs <- io $ getSolverBackendVersions backend satq <- io $ sequentToSATQuery sc unintSet (goalSequent g) k <- io $ mkSolverCacheKey sc bkvs satq - (mb, solver_name) <- SV.onSolverCache (lookupInSolverCache k) >>= \case + (mb, solver_name) <- SV.askSolverCache (lookupInSolverCache k) >>= \case -- Use a cached result if one exists (and it's valid w.r.t our query) Just v -> return $ fromSolverCacheValue satq v -- Otherwise try to cache the result of the call _ -> f satq >>= \res -> - case toSolverCacheValue satq res of + case toSolverCacheValue bkvs satq res of Just v -> SV.onSolverCache (insertInSolverCache k v) >> return res Nothing -> return res diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index cd9a05e68d..b72ae12861 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -36,6 +36,7 @@ don't want to save these results directly. Instead, we represent each 'ExtCns' as its index in the 'satVariables' field of 'SATQuery' (which is where they were obtained by the backends in the first place). -} +{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} @@ -59,11 +60,11 @@ module SAWScript.SolverCache ) where import System.Directory -import System.FilePath (()) import Control.Exception -import Text.Read (readMaybe) +import GHC.Generics (Generic) -import Data.Tuple.Extra (first, second, firstM) +import Control.Monad (forM_) +import Data.Tuple.Extra (first, firstM) import Data.List (elemIndex, intercalate) import Data.Hashable import Data.Bits (shiftL, (.|.)) @@ -80,6 +81,10 @@ import qualified Data.ByteString as BS import qualified Crypto.Hash.SHA256 as SHA256 +import qualified Database.LMDB.Simple as LMDB +import qualified Database.LMDB.Simple.Extra as LMDB +import Codec.Serialise + import Verifier.SAW.FiniteValue import Verifier.SAW.SATQuery import Verifier.SAW.ExternalFormat @@ -106,6 +111,10 @@ data SolverCacheKey = instance Eq SolverCacheKey where (SolverCacheKey _ bs1) == (SolverCacheKey _ bs2) = bs1 == bs2 +instance Serialise SolverCacheKey where + encode (SolverCacheKey _ bs) = encode bs + decode = SolverCacheKey [] <$> decode + -- | Truncate a 'SolverCacheKey' (i.e. a SHA256 hash) to an 'Int', used to give -- the type a fast 'Hashable' instance solverCacheKeyInt :: SolverCacheKey -> Int @@ -155,64 +164,132 @@ mkSolverCacheKey sc vs satq = do -- Solver Cache Values --------------------------------------------------------- --- | The value type for 'SolverCache': a pair of the 'String' representing the --- solver used and an optional list of counterexamples, represented as pairs --- of indexes into the list of 'satVariables' -type SolverCacheValue = (String, Maybe [(Int, FirstOrderValue)]) +-- | The value type for 'SolverCache': a set of 'SolverBackendVersions', a +-- 'String' representing the solver used, and an optional list of +-- counterexamples, represented as pairs of indexes into the list of +-- 'satVariables' of an associated 'SATQuery' +data SolverCacheValue = SolverCacheValue SolverBackendVersions + String (Maybe [(Int, FirstOrderValue)]) + deriving (Eq, Generic) + +instance Serialise SolverCacheValue -- automatically derived + +instance Semigroup SolverCacheValue where + (<>) = error "semigroup SolverCacheValue" + +instance Monoid SolverCacheValue where + mempty = SolverCacheValue [] "" Nothing -- | Convert the result of a solver call on the given 'SATQuery' to a -- 'SolverCacheValue' -toSolverCacheValue :: SATQuery -> (Maybe CEX, String) -> Maybe SolverCacheValue -toSolverCacheValue satq (cexs, solver_name) = - (solver_name,) <$> mapM (mapM (firstM (`elemIndex` ecs))) cexs +toSolverCacheValue :: SolverBackendVersions -> SATQuery -> (Maybe CEX, String) -> + Maybe SolverCacheValue +toSolverCacheValue vs satq (cexs, solver_name) = + fmap (SolverCacheValue vs solver_name) + (mapM (mapM (firstM (`elemIndex` ecs))) cexs) where ecs = Map.keys $ satVariables satq -- | Convert a 'SolverCacheValue' to something which has the same form as the -- result of a solver call on the given 'SATQuery' fromSolverCacheValue :: SATQuery -> SolverCacheValue -> (Maybe CEX, String) -fromSolverCacheValue satq (solver_name, cexs) = - (fmap (fmap (first (ecs !!))) cexs ,solver_name) +fromSolverCacheValue satq (SolverCacheValue _ solver_name cexs) = + (fmap (fmap (first (ecs !!))) cexs, solver_name) where ecs = Map.keys $ satVariables satq --- | Given a path to a cache and a 'SolverCacheKey', return a --- 'SolverCacheValue' if the given key has been cached, or 'Nothing' otherwise. --- Note that if we encounter an 'IOException', we simply return 'Nothing', --- meaning we fall back to calling the solver backend. The idea is that solver --- result caching is an optional step, so if we fail during a read from the disk --- we don't want execution to stop, we just want to not use caching. -readCacheEntryFromDisk :: FilePath -> SolverCacheKey -> IO (Maybe SolverCacheValue) -readCacheEntryFromDisk path k = - catch (readMaybe . last . lines <$> readFile (path solverCacheKeyToHex k)) - (\(_ :: IOException) -> return Nothing) - --- | Given a path to a cache and a 'SolverCacheKey'/'SolverCacheValue' pair, --- add an approriate entry to the cache on disk. Note that if we encounter an --- 'IOException, we simply do nothing, meaning we do not cache this result. The --- idea is that solver result caching is an optional step, so if we fail during --- a write to the disk we don't want execution to stop, we just want to not use --- caching. -writeCacheEntryToDisk :: FilePath -> (SolverCacheKey, SolverCacheValue) -> IO () -writeCacheEntryToDisk path (k, v) = - catch (createDirectoryIfMissing False path >> - writeFile (path solverCacheKeyToHex k) - (unlines $ solverCacheKeyBackendVersions k ++ [show v])) - (\(_ :: IOException) -> return ()) + +-- The Database Behind the Solver Cache ---------------------------------------- + +-- | The database behind the 'SolverCache' - either a 'HashMap' or a +-- 'LMDB.Database' from 'SolverCacheKey's to 'SolverCacheValue's. We refer to +-- the former as "in memory" and the latter as "on disk". For the latter, we +-- also save the 'FilePath' of the LMDB database as well as the +-- 'LMDB.Environment'. +data SolverCacheDB + = SolverCacheMem (HashMap SolverCacheKey SolverCacheValue) + | SolverCacheDisk FilePath (LMDB.Environment LMDB.ReadWrite) + (LMDB.Database SolverCacheKey SolverCacheValue) + +-- | An empty 'SolverCacheDB' in memory +emptyDB :: SolverCacheDB +emptyDB = SolverCacheMem HM.empty + +-- | Get the 'FilePath' of the 'SolverCacheDB's on-disk database, if it exists +getDBPath :: SolverCacheDB -> Maybe FilePath +getDBPath (SolverCacheMem _ ) = Nothing +getDBPath (SolverCacheDisk path _ _) = Just path + +-- | If the 'SolverCacheDB' does not currently have an associated on-disk +-- database, create one at the associated 'FilePath' and copy all entries in +-- memory on to disk +setPathDB :: FilePath -> SolverCacheDB -> IO SolverCacheDB +setPathDB path (SolverCacheMem hm) = do + createDirectoryIfMissing False path + let limits = LMDB.defaultLimits { LMDB.mapSize = 2 ^ (32 :: Int) } + env <- LMDB.openReadWriteEnvironment path limits + LMDB.readWriteTransaction env $ do + db <- LMDB.getDatabase Nothing + forM_ (HM.toList hm) $ \(k,v) -> LMDB.put db k (Just v) + return $ SolverCacheDisk path env db +setPathDB _ (SolverCacheDisk path _ _) = + fail $ "Solver cache already has a set path: " ++ path + +-- | A general function for querying a 'SolverCacheDB' +askDB :: (HashMap SolverCacheKey SolverCacheValue -> a) -> + (LMDB.Database SolverCacheKey SolverCacheValue -> + LMDB.Transaction LMDB.ReadOnly a) -> + a -> SolverCacheDB -> IO a +askDB f _ _ (SolverCacheMem hm) = return $ f hm +askDB _ g dflt (SolverCacheDisk _ env db) = + catch (LMDB.readOnlyTransaction env $ g db) + (\(_ :: IOException) -> return dflt) + +-- | Get the size of a 'SolverCacheDB' +sizeDB :: SolverCacheDB -> IO Int +sizeDB = askDB (HM.size) (LMDB.size) 0 + +-- | Check whether a 'SolverCacheKey' is in a 'SolverCacheDB' +lookupInDB :: SolverCacheKey -> SolverCacheDB -> IO (Maybe SolverCacheValue) +lookupInDB k = askDB (HM.lookup k) (LMDB.lookup k) Nothing + +-- | A general function for modifying a 'SolverCacheDB' +onDB :: (HashMap SolverCacheKey SolverCacheValue -> + HashMap SolverCacheKey SolverCacheValue) -> + (LMDB.Database SolverCacheKey SolverCacheValue -> + LMDB.Transaction LMDB.ReadWrite ()) -> + SolverCacheDB -> IO SolverCacheDB +onDB f _ (SolverCacheMem hm) = return $ SolverCacheMem $ f hm +onDB _ g c@(SolverCacheDisk _ env db) = + catch (LMDB.transaction env $ g db >>= \r -> return $ r `seq` c) + (\(_ :: IOException) -> return c) + +-- | Insert a 'SolverCacheValue' at the given 'SolverCacheKey' into a +-- 'SolverCacheDB' +insertInDB :: SolverCacheKey -> SolverCacheValue -> SolverCacheDB -> + IO SolverCacheDB +insertInDB k v = onDB (HM.insert k v) (LMDB.insert k v) + +-- -- | Filter the entries in a 'SolverCacheDB' +-- filterDB :: (SolverCacheKey -> SolverCacheValue -> Bool) -> SolverCacheDB -> +-- IO SolverCacheDB +-- filterDB f = onDB (HM.filterWithKey f) $ \db -> do +-- kvs <- LMDB.toList db +-- forM_ kvs $ \(k,v) -> if f k v then return False else LMDB.delete k db -- The Solver Cache ------------------------------------------------------------ --- | A set of cached solver results as well as a 'FilePath' indicating where --- new additions to the cache should be saved +-- | A 'SolverCacheDB' of cached solver results as well as counters for how +-- many cache hits and how many new entry creations have occurred data SolverCache = SolverCache - { solverCacheMap :: HashMap SolverCacheKey SolverCacheValue - , solverCachePath :: Maybe FilePath - , solverCacheHits :: (Integer, Integer) + { solverCacheDB :: SolverCacheDB + , solverCacheHits :: Integer + , solverCacheCreated :: Integer } -- | An empty 'SolverCache' with no associated 'FilePath' emptySolverCache :: SolverCache -emptySolverCache = SolverCache HM.empty Nothing (0,0) +emptySolverCache = SolverCache emptyDB 0 0 -- | A stateful operation on a 'SolverCache', returning a value of the given type type SolverCacheOp a = Options -> SolverCache -> IO (a, SolverCache) @@ -220,63 +297,45 @@ type SolverCacheOp a = Options -> SolverCache -> IO (a, SolverCache) -- | Lookup a 'SolverCacheKey' in the solver result cache lookupInSolverCache :: SolverCacheKey -> SolverCacheOp (Maybe SolverCacheValue) lookupInSolverCache k opts cache = - case (HM.lookup k (solverCacheMap cache), solverCachePath cache) of - (Just v, _) -> do - printOutLn opts Info ("Using cached result from memory: " ++ show k) - return (Just v, cache { solverCacheHits = first (+1) (solverCacheHits cache) }) - (_, Just path) -> readCacheEntryFromDisk path k >>= \case - Just v -> do - printOutLn opts Info ("Using cached result from disk: " ++ show k) - return (Just v, cache { solverCacheMap = HM.insert k v (solverCacheMap cache) - , solverCacheHits = second (+1) (solverCacheHits cache) }) - Nothing -> return (Nothing, cache) - _ -> return (Nothing, cache) + lookupInDB k (solverCacheDB cache) >>= \case + Just v -> do + printOutLn opts Info ("Using cached result: " ++ show k) + return (Just v, cache { solverCacheHits = solverCacheHits cache + 1 }) + Nothing -> return (Nothing, cache) -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () insertInSolverCache k v opts cache = do printOutLn opts Info ("Caching result: " ++ show k) - case solverCachePath cache of - Just path -> writeCacheEntryToDisk path (k, v) - _ -> return () - return ((), cache { solverCacheMap = HM.insert k v (solverCacheMap cache) }) + db' <- insertInDB k v (solverCacheDB cache) + return ((), cache { solverCacheDB = db' + , solverCacheCreated = solverCacheCreated cache + 1 }) -- | Set the 'FilePath' of the solver result cache, erroring if it is already -- set, and save all results cached so far setSolverCachePath :: FilePath -> SolverCacheOp () -setSolverCachePath path opts cache = - makeAbsolute path >>= \pathAbs -> - case solverCachePath cache of - Just path' -> fail $ "Solver cache already has a set path: " ++ path' - _ | HM.null (solverCacheMap cache) -> return ((), cache { solverCachePath = Just pathAbs }) - _ -> let to_save = HM.toList $ solverCacheMap cache in - let (s0, s1) = (show (length to_save), if length to_save > 1 then "s" else "") in - printOutLn opts Info ("Saving " ++ s0 ++ " cached result" ++ s1 ++ " to disk") >> - mapM_ (writeCacheEntryToDisk path) to_save >> - return ((), cache { solverCachePath = Just pathAbs }) +setSolverCachePath path opts cache = do + pathAbs <- makeAbsolute path + sz <- sizeDB (solverCacheDB cache) + let (s0, s1) = (show sz, if sz > 1 then "s" else "") + db' <- setPathDB pathAbs (solverCacheDB cache) + if sz == 0 then return () + else printOutLn opts Info ("Saved " ++ s0 ++ " cached result" ++ s1 ++ " to disk") + return ((), cache { solverCacheDB = db' }) -- | Print out statistics about how the solver cache was used printSolverCacheStats :: SolverCacheOp () printSolverCacheStats opts cache = do - let memSize = HM.size $ solverCacheMap cache - let (memHits, diskHits) = solverCacheHits cache printOutLn opts Info ("== Solver result cache statistics ==") - case solverCachePath cache of + sz <- sizeDB (solverCacheDB cache) + case getDBPath (solverCacheDB cache) of + Nothing -> + printOutLn opts Info ("- " ++ show sz ++ " results cached in memory") Just path -> do - diskSize <- length <$> listDirectory path - printOutLn opts Info ("- " ++ show diskSize ++ " results cached on disk " - ++ "(" ++ path ++ ")") - printOutLn opts Info ("- " ++ show memSize ++ " results cached in memory " - ++ "(" ++ show (100*memSize `div` diskSize) - ++ "% of total cache)") - let totalHits = max 1 (memHits+diskHits) - printOutLn opts Info ("- " ++ show diskHits ++ " cache hits from disk " - ++ "(" ++ show (100*diskHits `div` totalHits) - ++ "% of total hits)") - printOutLn opts Info ("- " ++ show memHits ++ " cache hits from memory " - ++ "(" ++ show (100*memHits `div` totalHits) - ++ "% of total hits)") - Nothing -> do - printOutLn opts Info ("- " ++ show memSize ++ " results cached in memory") - printOutLn opts Info ("- " ++ show memHits ++ " cache hits") + printOutLn opts Info ("- " ++ show sz ++ " results cached on disk " + ++ "(" ++ path ++ ")") + let created = solverCacheCreated cache + printOutLn opts Info ("- " ++ show created ++ " cache entries created this run") + let hits = solverCacheHits cache + printOutLn opts Info ("- " ++ show hits ++ " times cached results were used this run") return ((), cache) diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 8b2086dcb9..2756eb8d4e 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -731,17 +731,28 @@ recordProof v = do rw <- getTopLevelRW putTopLevelRW rw { rwProofs = toValue v : rwProofs rw } --- | Perform a (possibly) stateful operation on the 'SolverCache' returning a --- value of type @a@, or @mempty@ if there is no 'SolverCache' -onSolverCache :: Monoid a => SolverCacheOp a -> TopLevel a -onSolverCache f = +-- | Perform a possibly stateful operation on the 'SolverCache', returning a +-- value of type @Maybe a@, or 'Nothing' if there is no 'SolverCache' +askSolverCache :: SolverCacheOp (Maybe a) -> TopLevel (Maybe a) +askSolverCache f = do opts <- getOptions ref <- getSolverCache - io (readIORef ref) >>= \case - Just cache -> do (ret, cache') <- io $ f opts cache - io $ atomicWriteIORef ref (Just cache') + io $ readIORef ref >>= \case + Just cache -> do (ret, cache') <- f opts cache + atomicWriteIORef ref (Just cache') return ret - Nothing -> return mempty + Nothing -> return Nothing + +-- | Perform a stateful operation on the 'SolverCache', or do nothing if +-- there is no 'SolverCache' +onSolverCache :: SolverCacheOp () -> TopLevel() +onSolverCache f = + do opts <- getOptions + ref <- getSolverCache + io $ readIORef ref >>= \case + Just cache -> do ((), cache') <- f opts cache + atomicWriteIORef ref (Just cache') + Nothing -> return () -- | Access the current state of Java Class translation getJVMTrans :: TopLevel CJ.JVMContext From 7de326ac1341ff8cead6e32f1598da142811dad6 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 24 May 2023 09:00:06 -0400 Subject: [PATCH 19/51] use CI scripts from input-output-hk/haskell-lmdb --- .github/ci.sh | 8 -------- .github/workflows/ci.yml | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/ci.sh b/.github/ci.sh index bbfbefe3ca..6d86bb867d 100755 --- a/.github/ci.sh +++ b/.github/ci.sh @@ -75,14 +75,6 @@ build() { } install_system_deps() { - (curl -o lmdb.tar.bz2 -sL https://git.openldap.org/openldap/openldap/-/archive/LMDB_0.9.30/openldap-LMDB_0.9.30.tar.bz2 && tar --strip-components=1 -xf lmdb.tar.bz2 && rm lmdb.tar.bz2) - cd $(pwd)/libraries/liblmdb - chmod +x ./* - if [[ "$RUNNER_OS" == "macOS" ]]; then - make SOEXT=.dylib && make install SOEXT=.dylib - else - make && make install - fi (cd $BIN && curl -o bins.zip -sL "https://github.com/GaloisInc/what4-solvers/releases/download/$SOLVER_PKG_VERSION/$BUILD_TARGET_OS-bin.zip" && unzip -o bins.zip && rm bins.zip) chmod +x $BIN/* cp $BIN/yices_smt2$EXT $BIN/yices-smt2$EXT diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c27e27ba4..4c885c4288 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -119,6 +119,31 @@ jobs: cabal user-config update -a "extra-include-dirs: \"\"" cabal user-config update -a "extra-lib-dirs: \"\"" + # Adapted from: https://github.com/input-output-hk/haskell-lmdb/ + - name: "Linux: Install lmdb (apt-get)" + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get -y install liblmdb-dev + sudo apt-get -y autoremove + - name: "MacOS: Install lmdb (brew)" + if: runner.os == 'macOS' + run: | + brew install pkg-config lmdb + cp ./.github/workflows/lmdb.pc /usr/local/lib/pkgconfig + - name: "Windows: Install lmdb via pacman (msys2)" + if: runner.os == 'Windows' + shell: pwsh + run: | + ghcup run -- pacman --noconfirm -S ` + mingw-w64-x86_64-pkg-config ` + mingw-w64-x86_64-lmdb + - name: "Windows: Update PATH after lmdb install" + if: runner.os == 'Windows' + run: | + $env:PATH=("C:\msys64\mingw64\bin;{0}" -f $env:PATH) + echo "PATH=$env:PATH" >> $env:GITHUB_ENV + - shell: bash run: .github/ci.sh install_system_deps env: From b3b46f8af5e3f6d4721f38fcce73f25eddf9ddd6 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 24 May 2023 09:19:40 -0400 Subject: [PATCH 20/51] remove cp of lmdb pkgconfig, add `lib` and `include` to PATH for Windows --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c885c4288..dae427d722 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,7 +130,6 @@ jobs: if: runner.os == 'macOS' run: | brew install pkg-config lmdb - cp ./.github/workflows/lmdb.pc /usr/local/lib/pkgconfig - name: "Windows: Install lmdb via pacman (msys2)" if: runner.os == 'Windows' shell: pwsh @@ -142,6 +141,8 @@ jobs: if: runner.os == 'Windows' run: | $env:PATH=("C:\msys64\mingw64\bin;{0}" -f $env:PATH) + $env:PATH=("C:\msys64\mingw64\lib;{0}" -f $env:PATH) + $env:PATH=("C:\msys64\mingw64\include;{0}" -f $env:PATH) echo "PATH=$env:PATH" >> $env:GITHUB_ENV - shell: bash From 03eec2e252d1f3faa3ba75850a622fb59f878cca Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 24 May 2023 10:10:22 -0400 Subject: [PATCH 21/51] cleanup, revert most of 9fb0c6d5b1da9165d425750f443307ca97979b4a --- .github/workflows/ci.yml | 4 +-- .../src/Verifier/SAW/Simulator/SBV.hs | 4 +-- saw-core/src/Verifier/SAW/FiniteValue.hs | 33 ++++++++++++------- saw-script.cabal | 4 +-- src/SAWScript/Builtins.hs | 15 +++------ src/SAWScript/Proof.hs | 7 ---- src/SAWScript/Prover/ABC.hs | 4 +-- src/SAWScript/Prover/RME.hs | 2 +- src/SAWScript/Prover/What4.hs | 4 +-- src/SAWScript/Value.hs | 25 ++++++++------ 10 files changed, 49 insertions(+), 53 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dae427d722..e0dedce26a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,7 +129,7 @@ jobs: - name: "MacOS: Install lmdb (brew)" if: runner.os == 'macOS' run: | - brew install pkg-config lmdb + brew install lmdb - name: "Windows: Install lmdb via pacman (msys2)" if: runner.os == 'Windows' shell: pwsh @@ -141,8 +141,6 @@ jobs: if: runner.os == 'Windows' run: | $env:PATH=("C:\msys64\mingw64\bin;{0}" -f $env:PATH) - $env:PATH=("C:\msys64\mingw64\lib;{0}" -f $env:PATH) - $env:PATH=("C:\msys64\mingw64\include;{0}" -f $env:PATH) echo "PATH=$env:PATH" >> $env:GITHUB_ENV - shell: bash diff --git a/saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs b/saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs index 2cd701ad64..979bb21af2 100644 --- a/saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs +++ b/saw-core-sbv/src/Verifier/SAW/Simulator/SBV.hs @@ -71,7 +71,6 @@ import Verifier.SAW.TypedAST (FieldName, toShortName, identBaseName) import Verifier.SAW.FiniteValue (FirstOrderType(..), FirstOrderValue(..) , fovVec, firstOrderTypeOf, asFirstOrderType - , showFirstOrderValues ) import Verifier.SAW.Utils (panic) @@ -769,8 +768,7 @@ getLabels ls d args | length args == length xs = Just (zip args xs) | otherwise = error $ unwords [ "SBV SAT results do not match expected arguments " - , show (map (toShortName . ecName) args) - , showFirstOrderValues xs] + , show (map (toShortName . ecName) args), show xs] where xs = fmap getLabel ls diff --git a/saw-core/src/Verifier/SAW/FiniteValue.hs b/saw-core/src/Verifier/SAW/FiniteValue.hs index 0e19b929db..1419ad97f3 100644 --- a/saw-core/src/Verifier/SAW/FiniteValue.hs +++ b/saw-core/src/Verifier/SAW/FiniteValue.hs @@ -20,8 +20,10 @@ import Control.Monad (mzero) import Control.Monad.Trans (lift) import Control.Monad.Trans.Maybe import qualified Control.Monad.State as S +import Data.List (intersperse) import Data.Map (Map) import qualified Data.Map as Map +import qualified Data.Text as Text import Numeric.Natural (Natural) import GHC.Generics (Generic) import Codec.Serialise @@ -59,7 +61,7 @@ data FirstOrderType | FOTArray FirstOrderType FirstOrderType | FOTTuple [FirstOrderType] | FOTRec (Map FieldName FirstOrderType) - deriving (Eq, Generic, Read, Show) + deriving (Eq, Show, Generic) instance Serialise FirstOrderType -- automatically derived @@ -73,7 +75,7 @@ data FirstOrderValue | FOVArray FirstOrderType FirstOrderType | FOVTuple [FirstOrderValue] | FOVRec (Map FieldName FirstOrderValue) - deriving (Eq, Generic, Read, Show) + deriving (Eq, Generic) instance Serialise FirstOrderValue -- automatically derived @@ -104,16 +106,23 @@ toFiniteType FOTInt{} = Nothing toFiniteType FOTIntMod{} = Nothing toFiniteType FOTArray{} = Nothing -showFiniteValues :: [FiniteValue] -> String -showFiniteValues = showFirstOrderValues . map toFirstOrderValue - -showFirstOrderValues :: [FirstOrderValue] -> String -showFirstOrderValues = renderSawDoc defaultPPOpts . list . - map (ppFirstOrderValue defaultPPOpts) - -showFirstOrderValue :: FirstOrderValue -> String -showFirstOrderValue = renderSawDoc defaultPPOpts . - ppFirstOrderValue defaultPPOpts +instance Show FiniteValue where + showsPrec p fv = showsPrec p (toFirstOrderValue fv) + +instance Show FirstOrderValue where + showsPrec _ fv = + case fv of + FOVBit b -> shows b + FOVInt i -> shows i + FOVIntMod _ i -> shows i + FOVWord _ x -> shows x + FOVVec _ vs -> showString "[" . commaSep (map shows vs) . showString "]" + FOVArray{} -> shows $ firstOrderTypeOf fv + FOVTuple vs -> showString "(" . commaSep (map shows vs) . showString ")" + FOVRec vm -> showString "{" . commaSep (map showField (Map.assocs vm)) . showString "}" + where + commaSep ss = foldr (.) id (intersperse (showString ",") ss) + showField (field, v) = showString (Text.unpack field) . showString " = " . shows v ppFiniteValue :: PPOpts -> FiniteValue -> SawDoc ppFiniteValue opts fv = ppFirstOrderValue opts (toFirstOrderValue fv) diff --git a/saw-script.cabal b/saw-script.cabal index 276df0e775..127a40b777 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -32,7 +32,7 @@ library , bv-sized >= 1.0 && < 1.1 , containers , constraints >= 0.6 - , cryptohash-sha256 + , cryptohash-sha256 >= 0.11.102.1 , cryptol , cryptol-saw-core , crucible >= 0.4 @@ -61,7 +61,7 @@ library , lens , llvm-pretty >= 0.8 , llvm-pretty-bc-parser >= 0.1.3.1 - , lmdb-simple + , lmdb-simple >= 0.4.0.0 , macaw-base , macaw-x86 , macaw-symbolic diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index ec60d946fd..b7f4b13a21 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -1501,9 +1501,11 @@ quickCheckPrintPrim opts sc numTests tt = testGen <- prepareSATQuery sc satq runManyTests testGen numTests >>= \case Nothing -> printOutLn opts Info $ "All " ++ show numTests ++ " tests passed!" - Just cex -> printOutLn opts OnlyCounterExamples $ - "----------Counterexample----------\n" ++ - SV.showsCEX SV.defaultPPOpts cex "" + Just cex -> + do let cex' = [ (Text.unpack (toShortName (ecName ec)), v) | (ec,v) <- cex ] + printOutLn opts OnlyCounterExamples $ + "----------Counterexample----------\n" ++ + showList cex' "" cryptolSimpset :: TopLevel SV.SAWSimpset cryptolSimpset = @@ -2429,13 +2431,6 @@ set_path_sat_solver nm = "yices" -> modify (\rw -> rw{ rwPathSatSolver = PathSat_Yices }) _ -> fail $ "Unknown path sat solver: " ++ show nm -set_proof_cache :: String -> TopLevel () -set_proof_cache nm = - case map toLower nm of - "z3" -> modify (\rw -> rw{ rwPathSatSolver = PathSat_Z3 }) - "yices" -> modify (\rw -> rw{ rwPathSatSolver = PathSat_Yices }) - _ -> fail $ "Unknown path sat solver: " ++ show nm - summarize_verification :: TopLevel () summarize_verification = do values <- rwProofs <$> getTopLevelRW diff --git a/src/SAWScript/Proof.hs b/src/SAWScript/Proof.hs index 7063912642..edccec690d 100644 --- a/src/SAWScript/Proof.hs +++ b/src/SAWScript/Proof.hs @@ -48,7 +48,6 @@ module SAWScript.Proof , traverseSequentWithFocus , checkSequent , sequentConstantSet - , sequentAllExtSet , booleansToSequent , unfocusSequent , focusOnConcl @@ -613,12 +612,6 @@ sequentConstantSet sqt = foldr (\p m -> Map.union (getConstantSet (unProp p)) m) where RawSequent hs gs = sequentToRawSequent sqt --- | Return the 'Set' of all 'ExtCns' in the given 'Sequent' -sequentAllExtSet :: Sequent -> Set (ExtCns Term) -sequentAllExtSet sqt = foldMap getAllExtSet (map unProp (hs ++ gs)) - where - RawSequent hs gs = sequentToRawSequent sqt - convertibleProps :: SharedContext -> [Prop] -> [Prop] -> IO Bool convertibleProps _sc [] [] = return True convertibleProps sc (p1:ps1) (p2:ps2) = diff --git a/src/SAWScript/Prover/ABC.hs b/src/SAWScript/Prover/ABC.hs index 70f7c5368d..7fda525b56 100644 --- a/src/SAWScript/Prover/ABC.hs +++ b/src/SAWScript/Prover/ABC.hs @@ -76,7 +76,7 @@ getModel argNames shapes satRes = | otherwise -> fail $ unwords [ "ABC SAT results do not match expected arguments" - , show argNames, showFiniteValues vs] + , show argNames, show vs] AIG.SatUnknown -> fail "Unknown result from ABC" @@ -191,7 +191,7 @@ abcSatExternal proxy sc doCNF execName args g = liftIO $ Right vs | length ecs == length vs -> do return (Just (zip ecs (map toFirstOrderValue vs)), stats) - | otherwise -> fail $ unwords ["external SAT results do not match expected arguments", show argNames, showFiniteValues vs] + | otherwise -> fail $ unwords ["external SAT results do not match expected arguments", show argNames, show vs] (["s UNSATISFIABLE"], []) -> return (Nothing, stats) _ -> fail $ "Unexpected result from SAT solver:\n" ++ out diff --git a/src/SAWScript/Prover/RME.hs b/src/SAWScript/Prover/RME.hs index 758266e160..d21db63491 100644 --- a/src/SAWScript/Prover/RME.hs +++ b/src/SAWScript/Prover/RME.hs @@ -33,4 +33,4 @@ proveRME satq = getSharedContext >>= \sc -> liftIO $ | length shapes == length vs -> do let model = zip (map fst shapes) (map toFirstOrderValue vs) return (Just model, "RME") - | otherwise -> fail $ unwords ["RME SAT results do not match expected arguments", show shapes, showFiniteValues vs] + | otherwise -> fail $ unwords ["RME SAT results do not match expected arguments", show shapes, show vs] diff --git a/src/SAWScript/Prover/What4.hs b/src/SAWScript/Prover/What4.hs index 4cef6eccfe..3c33d2f47c 100644 --- a/src/SAWScript/Prover/What4.hs +++ b/src/SAWScript/Prover/What4.hs @@ -226,6 +226,4 @@ printValue _ _ (Nothing, _) = return () printValue _ f (Just (W.TypedExpr (ty :: BaseTypeRepr ty) (bv :: B.Expr t ty)), orig) = do gv <- groundEval f @ty bv putStr $ orig ++ "=?" - case (groundToFOV ty gv) of - Left err -> putStrLn $ "Left " ++ show err - Right fov -> putStrLn $ "Right " ++ showFirstOrderValue fov + print (groundToFOV ty gv) diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 2756eb8d4e..a629511720 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -215,6 +215,7 @@ data SatResult = Unsat SolverStats | Sat SolverStats [(ExtCns Term, FirstOrderValue)] | SatUnknown + deriving (Show) isVUnit :: Value -> Bool isVUnit (VTuple []) = True @@ -258,8 +259,12 @@ showBrackets s = showString "[" . s . showString "]" showBraces :: ShowS -> ShowS showBraces s = showString "{" . s . showString "}" -showsCEX :: PPOpts -> CEX -> ShowS -showsCEX opts ts = showString "[" . showMulti "" ts +showsProofResult :: PPOpts -> ProofResult -> ShowS +showsProofResult opts r = + case r of + ValidProof _ _ -> showString "Valid" + InvalidProof _ ts _ -> showString "Invalid: [" . showMulti "" ts + UnfinishedProof st -> showString "Unfinished: " . shows (length (psGoals st)) . showString " goals remaining" where opts' = sawPPOpts opts showVal t = shows (ppFirstOrderValue opts' t) @@ -269,19 +274,19 @@ showsCEX opts ts = showString "[" . showMulti "" ts showMulti _ [] = showString "]" showMulti s (eqn : eqns) = showString s . showEqn eqn . showMulti ", " eqns -showsProofResult :: PPOpts -> ProofResult -> ShowS -showsProofResult opts r = - case r of - ValidProof _ _ -> showString "Valid" - InvalidProof _ ts _ -> showString "Invalid: " . showsCEX opts ts - UnfinishedProof st -> showString "Unfinished: " . shows (length (psGoals st)) . showString " goals remaining" - showsSatResult :: PPOpts -> SatResult -> ShowS showsSatResult opts r = case r of Unsat _ -> showString "Unsat" - Sat _ ts -> showString "Sat: " . showsCEX opts ts + Sat _ ts -> showString "Sat: [" . showMulti "" ts SatUnknown -> showString "Unknown" + where + opts' = sawPPOpts opts + showVal t = shows (ppFirstOrderValue opts' t) + showEC ec = showString (unpack (toShortName (ecName ec))) + showEqn (x, t) = showEC x . showString " = " . showVal t + showMulti _ [] = showString "]" + showMulti s (eqn : eqns) = showString s . showEqn eqn . showMulti ", " eqns showSimpset :: PPOpts -> Simpset a -> String showSimpset opts ss = From 751f54d0911c8538126aec1b024289b16a544785 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 24 May 2023 12:09:33 -0400 Subject: [PATCH 22/51] get lmdb-simple working with GHC 9.2.7 --- Setup.hs | 10 ++++++---- cabal.project | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Setup.hs b/Setup.hs index 7ff5a23a38..ee789efdba 100644 --- a/Setup.hs +++ b/Setup.hs @@ -4,9 +4,9 @@ import Data.List (find) import Distribution.Simple import Distribution.Simple.BuildPaths (autogenPackageModulesDir) import Distribution.PackageDescription (emptyHookedBuildInfo, allBuildInfo) -import System.Directory (createDirectoryIfMissing, findExecutable, withCurrentDirectory) +import System.Directory import System.FilePath (()) -import System.Process (readProcess) +import System.Process (readProcess, callProcess) import System.Exit main = defaultMainWithHooks myHooks @@ -37,8 +37,10 @@ myBuild pd lbi uh flags = do init <$> readProcess git ["describe", "--always", "--dirty"] "" sbv_ver <- withExe "." "cabal" "unknown" "unknown" $ \cabal -> do - ls <- lines <$> readProcess cabal ["freeze"] "" - wss <- fmap words <$> lines <$> readFile (ls !! 1) + ex <- doesFileExist "cabal.project.freeze" + unless ex $ callProcess cabal ["freeze"] + wss <- fmap words <$> lines <$> readFile "cabal.project.freeze" + unless ex $ removeFile "cabal.project.freeze" case find (\ws -> (ws !! 0) == "any.sbv") wss of Just ws -> return $ init $ drop 2 $ ws !! 1 Nothing -> return "unknown" diff --git a/cabal.project b/cabal.project index 7ed7119a69..5eb5f00c05 100644 --- a/cabal.project +++ b/cabal.project @@ -43,6 +43,9 @@ packages: deps/cryptol/cryptol-remote-api deps/language-rust +allow-newer: + lmdb-simple:bytestring + source-repository-package type: git location: https://github.com/eddywestbrook/hobbits.git From 0719bda32f5ede6204bdbc042cc13085098bb616 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 24 May 2023 18:32:19 -0400 Subject: [PATCH 23/51] add clean_solver_cache command --- Setup.hs | 28 ++--- src/SAWScript/Builtins.hs | 19 +-- src/SAWScript/Interpreter.hs | 14 +++ src/SAWScript/SolverCache.hs | 205 +++++++++++++++++++++++++------- src/SAWScript/SolverVersions.hs | 82 +++++-------- 5 files changed, 231 insertions(+), 117 deletions(-) diff --git a/Setup.hs b/Setup.hs index ee789efdba..f2aa19b446 100644 --- a/Setup.hs +++ b/Setup.hs @@ -12,15 +12,15 @@ import System.Exit main = defaultMainWithHooks myHooks where myHooks = simpleUserHooks { buildHook = myBuild } +onfailure :: a -> SomeException -> IO a +onfailure on_fail _e = return on_fail + withExe dir exe_str on_no_exe on_fail m = do mb <- findExecutable exe_str - let onfailure :: SomeException -> IO String - onfailure _e = return on_fail - case mb of Just exe -> withCurrentDirectory dir (m exe) - `catch` onfailure + `catch` onfailure on_fail Nothing -> return on_no_exe myBuild pd lbi uh flags = do @@ -30,30 +30,30 @@ myBuild pd lbi uh flags = do desc <- withExe "." "git" "" "" $ \git -> init <$> readProcess git ["describe", "--always", "--dirty"] "" - aig_desc <- withExe "deps/aig" "git" "unknown" "unknown" $ \git -> do - init <$> readProcess git ["describe", "--always", "--dirty"] "" + aig_desc <- withExe "deps/aig" "git" Nothing Nothing $ \git -> do + Just . init <$> readProcess git ["describe", "--always", "--dirty"] "" - w4_desc <- withExe "deps/what4" "git" "unknown" "unknown" $ \git -> do - init <$> readProcess git ["describe", "--always", "--dirty"] "" + w4_desc <- withExe "deps/what4" "git" Nothing Nothing $ \git -> do + Just . init <$> readProcess git ["describe", "--always", "--dirty"] "" - sbv_ver <- withExe "." "cabal" "unknown" "unknown" $ \cabal -> do + sbv_ver <- withExe "." "cabal" Nothing Nothing $ \cabal -> do ex <- doesFileExist "cabal.project.freeze" unless ex $ callProcess cabal ["freeze"] wss <- fmap words <$> lines <$> readFile "cabal.project.freeze" unless ex $ removeFile "cabal.project.freeze" case find (\ws -> (ws !! 0) == "any.sbv") wss of - Just ws -> return $ init $ drop 2 $ ws !! 1 - Nothing -> return "unknown" + Just ws -> return . Just . init . drop 2 $ ws !! 1 + Nothing -> return Nothing writeFile (dir "GitRev.hs") $ unlines [ "module GitRev where" , "hash :: String" , "hash = " ++ show desc - , "aigHash :: String" + , "aigHash :: Maybe String" , "aigHash = " ++ show aig_desc - , "w4Hash :: String" + , "w4Hash :: Maybe String" , "w4Hash = " ++ show w4_desc - , "sbvVer :: String" + , "sbvVer :: Maybe String" , "sbvVer = " ++ show sbv_ver ] diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index b7f4b13a21..3ce44cd2d2 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -909,7 +909,7 @@ goal_num_ite n s1 s2 = proveABC :: ProofScript () proveABC = do SV.AIGProxy proxy <- SV.scriptTopLevel SV.getProxy - wrapProver AIG (Prover.proveABC proxy) Set.empty + wrapProver [AIG] (Prover.proveABC proxy) Set.empty satExternal :: Bool -> String -> [String] -> ProofScript () satExternal doCNF execName args = @@ -934,7 +934,7 @@ writeSAIGComputedPrim = Prover.writeSAIG -- | Bit-blast a proposition check its validity using the RME library. proveRME :: ProofScript () -proveRME = wrapProver RME Prover.proveRME Set.empty +proveRME = wrapProver [RME] Prover.proveRME Set.empty codegenSBV :: SharedContext -> FilePath -> [String] -> String -> TypedTerm -> TopLevel () codegenSBV sc path unints fname (TypedTerm _schema t) = @@ -954,20 +954,21 @@ proveUnintSBV :: SBV.SMTConfig -> [String] -> ProofScript () proveUnintSBV conf unints = do timeout <- psTimeout <$> get unintSet <- SV.scriptTopLevel (resolveNames unints) - wrapProver (SBV conf) (Prover.proveUnintSBV conf timeout) unintSet + wrapProver [SBV, Solver $ SBV.name $ SBV.solver conf] + (Prover.proveUnintSBV conf timeout) unintSet -- | Given a continuation which calls a prover, call the continuation on the -- 'goalSequent' of the given 'ProofGoal' and return a 'SolveResult'. If there -- is a 'SolverCache', do not call the continuation if the goal has an already -- cached result, and otherwise save the result of the call to the cache. -applyProverToGoal :: SolverBackend +applyProverToGoal :: [SolverBackend] -> (SATQuery -> TopLevel (Maybe CEX, String)) -> Set VarIndex -> ProofGoal -> TopLevel (SolverStats, SolveResult) -applyProverToGoal backend f unintSet g = do +applyProverToGoal backends f unintSet g = do sc <- getSharedContext - bkvs <- io $ getSolverBackendVersions backend + bkvs <- io $ getSolverBackendVersions backends satq <- io $ sequentToSATQuery sc unintSet (goalSequent g) k <- io $ mkSolverCacheKey sc bkvs satq (mb, solver_name) <- SV.askSolverCache (lookupInSolverCache k) >>= \case @@ -985,21 +986,21 @@ applyProverToGoal backend f unintSet g = do Just a -> return (stats, SolveCounterexample a) wrapProver :: - SolverBackend -> + [SolverBackend] -> (SATQuery -> TopLevel (Maybe CEX, String)) -> Set VarIndex -> ProofScript () wrapProver backend f = execTactic . tacticSolve . applyProverToGoal backend f wrapW4Prover :: - W4BackendExtra -> SBV.Solver -> + W4Backend -> Solver -> ( Bool -> SATQuery -> TopLevel (Maybe CEX, String) ) -> [String] -> ProofScript () wrapW4Prover x s f unints = do hashConsing <- SV.scriptTopLevel $ gets SV.rwWhat4HashConsing unintSet <- SV.scriptTopLevel $ resolveNames unints - wrapProver (W4 x s) (f hashConsing) unintSet + wrapProver (w4Backends x s) (f hashConsing) unintSet wrapW4ProveExporter :: ( Bool -> FilePath -> SATQuery -> TopLevel (Maybe CEX, String) ) -> diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 5932b02b36..fc73ea2744 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -70,6 +70,7 @@ import SAWScript.TopLevel import SAWScript.Utils import SAWScript.Value import SAWScript.SolverCache +import SAWScript.SolverVersions import SAWScript.Proof (newTheoremDB) import SAWScript.Prover.Rewrite(basic_ss) import SAWScript.Prover.Exporter @@ -652,6 +653,11 @@ set_solver_cache_path :: FilePath -> TopLevel () set_solver_cache_path path = enable_solver_cache >> onSolverCache (setSolverCachePath path) +clean_solver_cache :: TopLevel () +clean_solver_cache = do + vs <- io $ getSolverBackendVersions baseBackends + onSolverCache (cleanSolverCache vs) + enable_debug_intrinsics :: TopLevel () enable_debug_intrinsics = do rw <- getTopLevelRW @@ -1089,6 +1095,14 @@ primitives = Map.fromList , " error." ] + , prim "clean_solver_cache" "TopLevel ()" + (pureVal clean_solver_cache) + Experimental + [ "Remove all entries in the solver result cache which were created" + , " using solver backend version(s) which do not match the version(s)" + , " in the current environment." + ] + , prim "print_solver_cache_stats" "TopLevel ()" (pureVal (onSolverCache printSolverCacheStats)) Experimental diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index b72ae12861..cbc875f904 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -9,12 +9,12 @@ This file defines an interface for caching SMT/SAT solver results in memory and on disk. The interface, as used in 'applyProverToGoal', works as follows: 1. An 'SMTQuery' is converted into a string using 'scWriteExternal', and - along with any relevant 'SolverBackendVersions' (obtained using + along with any relevant 'SolverBackendVersion's (obtained using 'getSolverBackendVersions' from @SAWScript.SolverVersions@), is then hashed using SHA256 ('mkSolverCacheKey'). 2. The 'SolverCache' contains a map from these hashes to previously obtained results ('solverCacheMap'). If the hash corresponding to the 'SATQuery' and - 'SolverBackendVersions' can be found in the map, then the corresponding + 'SolverBackendVersion's can be found in the map, then the corresponding result is used. 3. Otherwise, if the 'SolverCache' was given a path to a directory ('solverCachePath') and a file whose name is the hash can be found in that @@ -22,7 +22,7 @@ on disk. The interface, as used in 'applyProverToGoal', works as follows: 4. Otherwise, the 'SATQuery' is dispatched to the requested backend and a result is obtained. Then: - This result is added to the 'SolverCache' map using the hash of the - 'SATQuery' and 'SolverBackendVersions' as the key. + 'SATQuery' and 'SolverBackendVersion's as the key. - If the 'SolverCache' was given a path to a directory ('solverCachePath'), then a file whose name is the hash and whose contents are 'show' of the result is added to the directory. @@ -37,13 +37,22 @@ as its index in the 'satVariables' field of 'SATQuery' (which is where they were obtained by the backends in the first place). -} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE GeneralisedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + module SAWScript.SolverCache - ( SolverBackendVersions + ( Solver(..) + , SolverBackend(..) + , W4Backend(..) + , w4Backends + , baseBackends + , SolverBackendVersion(..) , SolverCacheKey(..) , solverCacheKeyToHex , mkSolverCacheKey @@ -56,6 +65,7 @@ module SAWScript.SolverCache , lookupInSolverCache , insertInSolverCache , setSolverCachePath + , cleanSolverCache , printSolverCacheStats ) where @@ -65,9 +75,14 @@ import GHC.Generics (Generic) import Control.Monad (forM_) import Data.Tuple.Extra (first, firstM) -import Data.List (elemIndex, intercalate) -import Data.Hashable +import Data.List (isPrefixOf, elemIndex, intercalate) +import Data.Hashable (Hashable(..)) import Data.Bits (shiftL, (.|.)) +import Data.Maybe (fromMaybe, isJust) +import Data.Functor ((<&>)) + +import qualified Data.Set as Set +import Data.Set (Set) import qualified Data.Map as Map import qualified Data.HashMap.Strict as HM @@ -85,6 +100,8 @@ import qualified Database.LMDB.Simple as LMDB import qualified Database.LMDB.Simple.Extra as LMDB import Codec.Serialise +import Data.SBV.Dynamic (Solver(..)) + import Verifier.SAW.FiniteValue import Verifier.SAW.SATQuery import Verifier.SAW.ExternalFormat @@ -94,18 +111,99 @@ import SAWScript.Options import SAWScript.Proof --- Solver Cache Keys ----------------------------------------------------------- +-- Solver Backends ------------------------------------------------------------- + +deriving instance Eq Solver +deriving instance Ord Solver +deriving instance Read Solver + +-- | A datatype representing one of the ways the what4 backend can be used in +-- SAW - i.e. directly ('W4_Base'), with a tactic ('W4_Tactic'), by converting +-- to SMTLib2 then calling ABC ('W4_SMTLib2'), by converting to Verilog then +-- calling ABC ('W4_Verilog'), or by converting to AIGER then calling ABC +-- ('W4_AIGER') +data W4Backend = W4_Base + | W4_Tactic String + | W4_SMTLib2 + | W4_Verilog + | W4_AIGER + deriving (Eq, Ord, Read, Show) + +-- | A datatype representing one of the solver backends available in SAW. Note +-- that for 'SBV' and 'W4', multiple backends will be used (e.g. 'SBV' with +-- @Solver Z3@ or @W4 W4_AIGER@ with 'AIG' and @Solver ABC@), though not all +-- comtinations of backends are valid (e.g. @W4 W4_SMTLib2@ with anything but +-- @Solver Z3@) +data SolverBackend = Solver Solver + | W4 W4Backend + | SBV + | AIG + | RME + deriving (Eq, Ord, Read, Show) + +instance Serialise SolverBackend where + encode (Solver s) = encode (1 :: Int, show s) + encode (W4 w) = encode (2 :: Int, show w) + encode backend = encode (0 :: Int, show backend) + decode = decode <&> \case (1 :: Int, s) -> Solver (read s) + (2 :: Int, s) -> W4 (read s) + (_ :: Int, s) -> read s + +-- | Given a 'W4Backend' and a 'Solver', return the list of 'SolverBackend's +-- used. In particular, this includes 'AIG' for 'W4_AIGER'. +w4Backends :: W4Backend -> Solver -> [SolverBackend] +w4Backends w s | W4_AIGER <- w = [W4 w, AIG, Solver s] + | otherwise = [W4 w, Solver s] + +-- | Convert a 'SolverBackend' into its base form, namely replace any 'W4' +-- backend with @W4 W4_Base@. +baseBackend :: SolverBackend -> SolverBackend +baseBackend (W4 _) = W4 W4_Base +baseBackend backend = backend + +-- | The finite list of all base 'SolverBackend's (as per 'baseBackend') +baseBackends :: [SolverBackend] +baseBackends = (Solver <$> enumFrom minBound) ++ + [W4 W4_Base, SBV, AIG, RME] + +-- | A 'SolverBackend' and its version 'String', if one could be obtained +data SolverBackendVersion = + SolverBackendVersion + { solverBackend :: SolverBackend + , solverVersion :: Maybe String + } deriving (Eq, Ord, Generic) + +instance Serialise SolverBackendVersion -- automatically derived + +instance Show SolverBackendVersion where + show (SolverBackendVersion backend mb_v) = case backend of + Solver s | show s `isPrefixOf` v_str -> v_str + | otherwise -> show s ++ " " ++ v_str + W4 W4_Base -> unwords ["what4", v_str] + W4 (W4_Tactic t) -> unwords ["what4", v_str, "using", t] + W4 W4_SMTLib2 -> unwords ["what4", v_str, "to", "SMTLib2"] + W4 W4_Verilog -> unwords ["what4", v_str, "to", "Verilog"] + W4 W4_AIGER -> unwords ["what4", v_str, "to", "AIGER"] + SBV -> unwords ["SBV", v_str] + AIG -> unwords ["AIG", v_str] + RME -> unwords ["RME", v_str] + where v_str = fromMaybe "[unknown version]" mb_v + +-- | Convert the 'SolverBackend' of a 'SolverBackendVersion' into its base +-- form using 'baseBackend' +baseBackendVersion :: SolverBackendVersion -> SolverBackendVersion +baseBackendVersion (SolverBackendVersion backend v) = + SolverBackendVersion (baseBackend backend) v --- | A set of solver backend versions obtained using 'getSolverBackendVersions' --- from @SAWScript.SolverVersions@ -type SolverBackendVersions = [String] + +-- Solver Cache Keys ----------------------------------------------------------- -- | The key type for 'SolverCache'. Each is a SHA256 hashes of 'SATQuery' and --- a set of 'SolverBackendVersions' - see 'mkSolverCacheKey' +-- a 'Set' of 'SolverBackendVersion's - see 'mkSolverCacheKey' data SolverCacheKey = SolverCacheKey - { solverCacheKeyBackendVersions :: SolverBackendVersions - , solverCacheKeyHash :: ByteString + { solverCacheKeyVersions :: Set SolverBackendVersion + , solverCacheKeyHash :: ByteString } instance Eq SolverCacheKey where @@ -113,7 +211,7 @@ instance Eq SolverCacheKey where instance Serialise SolverCacheKey where encode (SolverCacheKey _ bs) = encode bs - decode = SolverCacheKey [] <$> decode + decode = SolverCacheKey Set.empty <$> decode -- | Truncate a 'SolverCacheKey' (i.e. a SHA256 hash) to an 'Int', used to give -- the type a fast 'Hashable' instance @@ -127,15 +225,17 @@ instance Hashable SolverCacheKey where instance Show SolverCacheKey where show (SolverCacheKey vs bs) = T.unpack (encodeHex (BS.take 8 bs)) ++ - " (" ++ intercalate ", " vs ++ ")" + if Set.null vs then "" + else " (" ++ intercalate ", " vstrs ++ ")" + where vstrs = show <$> Set.elems vs -- | Convert a 'SolverCacheKey' to a hexadecimal 'String' solverCacheKeyToHex :: SolverCacheKey -> String solverCacheKeyToHex (SolverCacheKey _ bs) = T.unpack $ encodeHex bs --- | Hash using SHA256 a 'String' representation of a 'SATQuery' and a set of --- 'SolverBackendVersions' to get a 'SolverCacheKey'. In particular, this --- 'String' representation contains all the 'SolverBackendVersions', the +-- | Hash using SHA256 a 'String' representation of a 'SATQuery' and a 'Set' of +-- 'SolverBackendVersion's to get a 'SolverCacheKey'. In particular, this +-- 'String' representation contains all the 'SolverBackendVersion's, the -- number of 'satVariables' in the 'SATQuery', the number of 'satUninterp's in -- the 'SATQuery, and finally the 'scWriteExternal' representation of the -- 'satQueryAsPropTerm' of the 'SATQuery' - with two additional things: @@ -145,7 +245,7 @@ solverCacheKeyToHex (SolverCacheKey _ bs) = T.unpack $ encodeHex bs -- 2. After calling 'scWriteExternal', all 'LocalName's in 'Pi' and 'Lam' -- constructors are removed. This ensures that two terms which are alpha -- equivalent are given the same hash. -mkSolverCacheKey :: SharedContext -> SolverBackendVersions -> SATQuery -> +mkSolverCacheKey :: SharedContext -> Set SolverBackendVersion -> SATQuery -> IO SolverCacheKey mkSolverCacheKey sc vs satq = do body <- satQueryAsPropTerm sc satq @@ -153,8 +253,9 @@ mkSolverCacheKey sc vs satq = do filter (\ec -> ecVarIndex ec `elem` satUninterp satq) (getAllExts body) tm <- scGeneralizeExts sc ecs body - let str_prefix = vs ++ [ "satVariables " ++ show (Map.size (satVariables satq)) - , "satUninterp " ++ show (length (satUninterp satq)) ] + let str_prefix = map show (Set.elems vs) ++ + [ "satVariables " ++ show (Map.size (satVariables satq)) + , "satUninterp " ++ show (length (satUninterp satq)) ] str_to_hash = unlines str_prefix ++ anonLocalNames (scWriteExternal tm) return $ SolverCacheKey vs $ SHA256.hash $ encodeUtf8 $ T.pack $ str_to_hash where anonLocalNames = unlines . map (unwords . go . words) . lines @@ -164,26 +265,30 @@ mkSolverCacheKey sc vs satq = do -- Solver Cache Values --------------------------------------------------------- --- | The value type for 'SolverCache': a set of 'SolverBackendVersions', a +-- | The value type for 'SolverCache': a 'Set' of 'SolverBackendVersion's, a -- 'String' representing the solver used, and an optional list of -- counterexamples, represented as pairs of indexes into the list of -- 'satVariables' of an associated 'SATQuery' -data SolverCacheValue = SolverCacheValue SolverBackendVersions - String (Maybe [(Int, FirstOrderValue)]) - deriving (Eq, Generic) +data SolverCacheValue = + SolverCacheValue + { solverCacheValueVersions :: Set SolverBackendVersion + , solverCacheValueSolverName :: String + , solverCacheValueCEXs :: Maybe [(Int, FirstOrderValue)] + } deriving (Eq, Generic) instance Serialise SolverCacheValue -- automatically derived instance Semigroup SolverCacheValue where - (<>) = error "semigroup SolverCacheValue" + (SolverCacheValue vs1 nm1 cs1) <> (SolverCacheValue vs2 nm2 cs2) = + SolverCacheValue (vs1 <> vs2) (nm1 <> nm2) (cs1 <> cs2) instance Monoid SolverCacheValue where - mempty = SolverCacheValue [] "" Nothing + mempty = SolverCacheValue mempty mempty mempty -- | Convert the result of a solver call on the given 'SATQuery' to a -- 'SolverCacheValue' -toSolverCacheValue :: SolverBackendVersions -> SATQuery -> (Maybe CEX, String) -> - Maybe SolverCacheValue +toSolverCacheValue :: Set SolverBackendVersion -> SATQuery -> + (Maybe CEX, String) -> Maybe SolverCacheValue toSolverCacheValue vs satq (cexs, solver_name) = fmap (SolverCacheValue vs solver_name) (mapM (mapM (firstM (`elemIndex` ecs))) cexs) @@ -268,12 +373,12 @@ insertInDB :: SolverCacheKey -> SolverCacheValue -> SolverCacheDB -> IO SolverCacheDB insertInDB k v = onDB (HM.insert k v) (LMDB.insert k v) --- -- | Filter the entries in a 'SolverCacheDB' --- filterDB :: (SolverCacheKey -> SolverCacheValue -> Bool) -> SolverCacheDB -> --- IO SolverCacheDB --- filterDB f = onDB (HM.filterWithKey f) $ \db -> do --- kvs <- LMDB.toList db --- forM_ kvs $ \(k,v) -> if f k v then return False else LMDB.delete k db +-- | Filter the entries in a 'SolverCacheDB' +filterDB :: (SolverCacheValue -> Bool) -> SolverCacheDB -> + IO SolverCacheDB +filterDB f = onDB (HM.filter f) $ \db -> do + kvs <- LMDB.toList db + forM_ kvs $ \(k,v) -> if f v then return False else LMDB.delete k db -- The Solver Cache ------------------------------------------------------------ @@ -317,25 +422,45 @@ setSolverCachePath :: FilePath -> SolverCacheOp () setSolverCachePath path opts cache = do pathAbs <- makeAbsolute path sz <- sizeDB (solverCacheDB cache) - let (s0, s1) = (show sz, if sz > 1 then "s" else "") + let (s0, s1) = (show sz, if sz == 1 then "" else "s") db' <- setPathDB pathAbs (solverCacheDB cache) if sz == 0 then return () else printOutLn opts Info ("Saved " ++ s0 ++ " cached result" ++ s1 ++ " to disk") return ((), cache { solverCacheDB = db' }) +-- | Remove all entries in the solver result cache which have version(s) that +-- do not match the current version(s). +cleanSolverCache :: Set SolverBackendVersion -> SolverCacheOp () +cleanSolverCache current_base_vs opts cache = do + sz0 <- sizeDB (solverCacheDB cache) + db' <- filterDB (f . solverCacheValueVersions) (solverCacheDB cache) + sz1 <- sizeDB (solverCacheDB cache) + let s = if (sz0 - sz1) == 1 then "" else "s" + printOutLn opts Info ("Removed " ++ show (sz0 - sz1) ++ + " cached result" ++ s ++ " with mismatched version" ++ s) + return ((), cache { solverCacheDB = db' }) + where f vs = Set.isSubsetOf (Set.filter (\v -> solverBackend v `Set.member` currently_known_backends) + (Set.map baseBackendVersion vs)) + current_base_vs + currently_known_backends = Set.map solverBackend (Set.filter (isJust . solverVersion) + current_base_vs) + -- | Print out statistics about how the solver cache was used printSolverCacheStats :: SolverCacheOp () printSolverCacheStats opts cache = do printOutLn opts Info ("== Solver result cache statistics ==") sz <- sizeDB (solverCacheDB cache) + let sz_s = if sz == 1 then "" else "s" case getDBPath (solverCacheDB cache) of Nothing -> - printOutLn opts Info ("- " ++ show sz ++ " results cached in memory") + printOutLn opts Info ("- " ++ show sz ++ " result" ++ sz_s ++ " cached in memory") Just path -> do - printOutLn opts Info ("- " ++ show sz ++ " results cached on disk " + printOutLn opts Info ("- " ++ show sz ++ " result" ++ sz_s ++ " cached on disk " ++ "(" ++ path ++ ")") let created = solverCacheCreated cache - printOutLn opts Info ("- " ++ show created ++ " cache entries created this run") + created_s = if created == 1 then "y" else "ies" + printOutLn opts Info ("- " ++ show created ++ " cache entr" ++ created_s ++ " created this run") let hits = solverCacheHits cache - printOutLn opts Info ("- " ++ show hits ++ " times cached results were used this run") + (hits_s1, hits_s2) = if hits == 1 then (" a"," was") else ("s","s were") + printOutLn opts Info ("- " ++ show hits ++ " time" ++ hits_s1 ++ " cached result" ++ hits_s2 ++ " used this run") return ((), cache) diff --git a/src/SAWScript/SolverVersions.hs b/src/SAWScript/SolverVersions.hs index 31f5a45a29..2e0e87075d 100644 --- a/src/SAWScript/SolverVersions.hs +++ b/src/SAWScript/SolverVersions.hs @@ -8,73 +8,47 @@ Stability : provisional {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} -module SAWScript.SolverVersions - ( Solver(..) - , SolverBackend(..) - , W4BackendExtra(..) - , getSolverBackendVersions - ) where +module SAWScript.SolverVersions where import Control.Exception import System.Process (readProcessWithExitCode) import System.Exit (ExitCode(..)) -import Data.List (group) + +import qualified Data.Set as Set +import Data.Set (Set) import qualified Data.SBV.Dynamic as SBV -import Data.SBV.Dynamic (Solver(..)) +import SAWScript.SolverCache import qualified GitRev as GitRev import GitRev (aigHash, w4Hash, sbvVer) --- | A datatype representing one of the solver backends available in SAW -data SolverBackend = SBV SBV.SMTConfig - | W4 W4BackendExtra Solver - | AIG - | RME - --- | A datatype representing one of the ways the what4 backend can be used in --- SAW - i.e. directly ('W4_Base'), with a tactic ('W4_Tactic'), by converting --- to SMTLib2 then calling ABC ('W4_SMTLib2'), by converting to Verilog then --- calling ABC ('W4_Verilog'), or by converting to AIGER then calling ABC --- ('W4_AIGER') -data W4BackendExtra = W4_Base - | W4_Tactic String - | W4_SMTLib2 - | W4_Verilog - | W4_AIGER - --- | Given an 'SBV.SMTConfig' from @SBV@, query the solver for its version and --- return the result as a string. +-- | Given an 'SBV.Solver' from @SBV@, attempt to query the solver for its +-- version and return the result as a string. -- Adapted from @what4/test/ProbeSolvers.hs@ -getSolverVersion :: SBV.SMTConfig -> IO String -getSolverVersion c = - let cmd = SBV.executable (SBV.solver c) - args = case SBV.name (SBV.solver c) of +getSolverVersion :: SBV.Solver -> IO (Maybe String) +getSolverVersion s = + let s' = SBV.solver $ SBV.defaultSolverConfig s + args = case SBV.name s' of -- n.b. abc will return a non-zero exit code if asked -- for command usage. SBV.ABC -> ["s", "-q", "version;quit"] _ -> ["--version"] - in try (readProcessWithExitCode cmd args "") >>= \case - Right (ExitSuccess,o,_) | [l] <- lines o -> return l - Right _ -> return "unknown" - Left (_ :: SomeException) -> return "unknown" - --- | Given a solver backend, return a list of all relevant version information. --- For example, @SBV SBV.z3@ returns @["SBV ?.?", "Z3 version ?.?.? - ? bit"]@ -getSolverBackendVersions :: SolverBackend -> IO [String] -getSolverBackendVersions backend = map (unwords . map head . group) <$> + in try (readProcessWithExitCode (SBV.executable s') args "") >>= \case + Right (ExitSuccess,o,_) | [l] <- lines o -> return $ Just l + Right _ -> return Nothing + Left (_ :: SomeException) -> return Nothing + +-- | Get the 'SolverBackendVersion' of a 'SolverBackend' +getSolverBackendVersion :: SolverBackend -> IO SolverBackendVersion +getSolverBackendVersion backend = SolverBackendVersion backend <$> case backend of - SBV c -> do solver_ver <- getSolverVersion c - return [ ["SBV", sbvVer] - , (show $ SBV.name $ SBV.solver c) : words solver_ver ] - W4 x s -> do solver_ver <- getSolverVersion (SBV.defaultSolverConfig s) - let (ex1, ex2) = w4Extra x - return $ [ ["what4", w4Hash] ++ ex1 ] ++ ex2 ++ - [ show s : words solver_ver ] - AIG -> do return [["AIG", aigHash]] - RME -> do return [["RME", GitRev.hash]] - where w4Extra W4_Base = ([], []) - w4Extra (W4_Tactic t) = (["using", t], []) - w4Extra W4_SMTLib2 = (["to", "SMTLib2"], []) - w4Extra W4_Verilog = (["to", "Verilog"], []) - w4Extra W4_AIGER = (["to", "AIGER"], [["AIG", aigHash]]) + Solver s -> getSolverVersion s + W4 _ -> return w4Hash + SBV -> return sbvVer + AIG -> return aigHash + RME -> return (Just GitRev.hash) + +-- | Get the 'Set' of 'SolverBackendVersion's of a list of 'SolverBackend's +getSolverBackendVersions :: [SolverBackend] -> IO (Set SolverBackendVersion) +getSolverBackendVersions = fmap Set.fromList . mapM getSolverBackendVersion From dec00ff74abbcab59b2ec4e4d67fe30aa0ade1eb Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 24 May 2023 19:22:08 -0400 Subject: [PATCH 24/51] try to get LMDB on Windows working on CI --- .github/lmdb.pc | 11 +++++++++++ .github/workflows/ci.yml | 11 ++--------- 2 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 .github/lmdb.pc diff --git a/.github/lmdb.pc b/.github/lmdb.pc new file mode 100644 index 0000000000..fc4838ed47 --- /dev/null +++ b/.github/lmdb.pc @@ -0,0 +1,11 @@ +prefix=/usr/local +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${exec_prefix}/include + +Name: liblmdb +Description: Lightning Memory-Mapped Database +URL: https://symas.com/products/lightning-memory-mapped-database/ +Version: 0.9.29 +Libs: -L${libdir} -llmdb +Cflags: -I${includedir} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0dedce26a..5377d2d335 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,14 +111,6 @@ jobs: ghc-version: ${{ matrix.ghc }} cabal-version: ${{ matrix.cabal }} - - name: Post-GHC installation fixups on Windows - shell: bash - if: runner.os == 'Windows' - run: | - # A workaround for https://github.com/Mistuke/CabalChoco/issues/5 - cabal user-config update -a "extra-include-dirs: \"\"" - cabal user-config update -a "extra-lib-dirs: \"\"" - # Adapted from: https://github.com/input-output-hk/haskell-lmdb/ - name: "Linux: Install lmdb (apt-get)" if: runner.os == 'Linux' @@ -129,7 +121,8 @@ jobs: - name: "MacOS: Install lmdb (brew)" if: runner.os == 'macOS' run: | - brew install lmdb + brew install pkg-config lmdb + cp ./.github/lmdb.pc /usr/local/lib/pkgconfig - name: "Windows: Install lmdb via pacman (msys2)" if: runner.os == 'Windows' shell: pwsh From 3a77eb82994084b4a9bfaae4c3cb7238ccd2a0e7 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 20 Jun 2023 12:05:14 -0400 Subject: [PATCH 25/51] use Python LMDB bindings for solver caching (TODO docs) --- .github/lmdb.pc | 11 - .github/workflows/ci.yml | 27 +- README.md | 3 +- Setup.hs | 8 +- cabal.project | 3 - saw-core/saw-core.cabal | 1 - saw-core/src/Verifier/SAW/FiniteValue.hs | 10 +- saw-script.cabal | 4 +- src/SAWScript/Builtins.hs | 56 +- src/SAWScript/Interpreter.hs | 40 +- src/SAWScript/SolverCache.hs | 550 +++++++++--------- src/SAWScript/SolverCache/LMDBOptDatabase.hs | 264 +++++++++ .../SolverCache/lmdb_opt_database.py | 202 +++++++ src/SAWScript/SolverVersions.hs | 41 +- src/SAWScript/Value.hs | 24 +- 15 files changed, 851 insertions(+), 393 deletions(-) delete mode 100644 .github/lmdb.pc create mode 100644 src/SAWScript/SolverCache/LMDBOptDatabase.hs create mode 100644 src/SAWScript/SolverCache/lmdb_opt_database.py diff --git a/.github/lmdb.pc b/.github/lmdb.pc deleted file mode 100644 index fc4838ed47..0000000000 --- a/.github/lmdb.pc +++ /dev/null @@ -1,11 +0,0 @@ -prefix=/usr/local -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${exec_prefix}/include - -Name: liblmdb -Description: Lightning Memory-Mapped Database -URL: https://symas.com/products/lightning-memory-mapped-database/ -Version: 0.9.29 -Libs: -L${libdir} -llmdb -Cflags: -I${includedir} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5377d2d335..6c27e27ba4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,30 +111,13 @@ jobs: ghc-version: ${{ matrix.ghc }} cabal-version: ${{ matrix.cabal }} - # Adapted from: https://github.com/input-output-hk/haskell-lmdb/ - - name: "Linux: Install lmdb (apt-get)" - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get -y install liblmdb-dev - sudo apt-get -y autoremove - - name: "MacOS: Install lmdb (brew)" - if: runner.os == 'macOS' - run: | - brew install pkg-config lmdb - cp ./.github/lmdb.pc /usr/local/lib/pkgconfig - - name: "Windows: Install lmdb via pacman (msys2)" - if: runner.os == 'Windows' - shell: pwsh - run: | - ghcup run -- pacman --noconfirm -S ` - mingw-w64-x86_64-pkg-config ` - mingw-w64-x86_64-lmdb - - name: "Windows: Update PATH after lmdb install" + - name: Post-GHC installation fixups on Windows + shell: bash if: runner.os == 'Windows' run: | - $env:PATH=("C:\msys64\mingw64\bin;{0}" -f $env:PATH) - echo "PATH=$env:PATH" >> $env:GITHUB_ENV + # A workaround for https://github.com/Mistuke/CabalChoco/issues/5 + cabal user-config update -a "extra-include-dirs: \"\"" + cabal user-config update -a "extra-lib-dirs: \"\"" - shell: bash run: .github/ci.sh install_system_deps diff --git a/README.md b/README.md index 77637b2ce1..6bd97dbba6 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,7 @@ To build SAWScript and related utilities from source: * Ensure that you have the C libraries and header files for `terminfo`, which generally comes as part of `ncurses` on most platforms. On Fedora, it is part of the `ncurses-compat-libs` package. - You will also need the C headers for `zlib` as well as the C library - and header files for `lmdb`. + You will also need the C headers for `zlib`. * Ensure that you have the programs `javac` and `z3` on your `PATH`. Z3 binaries are available at diff --git a/Setup.hs b/Setup.hs index f2aa19b446..b518bc348c 100644 --- a/Setup.hs +++ b/Setup.hs @@ -51,10 +51,10 @@ myBuild pd lbi uh flags = do , "hash = " ++ show desc , "aigHash :: Maybe String" , "aigHash = " ++ show aig_desc - , "w4Hash :: Maybe String" - , "w4Hash = " ++ show w4_desc - , "sbvVer :: Maybe String" - , "sbvVer = " ++ show sbv_ver + , "what4Hash :: Maybe String" + , "what4Hash = " ++ show w4_desc + , "sbvVersion :: Maybe String" + , "sbvVersion = " ++ show sbv_ver ] unless (null $ allBuildInfo pd) $ diff --git a/cabal.project b/cabal.project index 5eb5f00c05..7ed7119a69 100644 --- a/cabal.project +++ b/cabal.project @@ -43,9 +43,6 @@ packages: deps/cryptol/cryptol-remote-api deps/language-rust -allow-newer: - lmdb-simple:bytestring - source-repository-package type: git location: https://github.com/eddywestbrook/hobbits.git diff --git a/saw-core/saw-core.cabal b/saw-core/saw-core.cabal index 11acdcb360..9e6d9d77d9 100644 --- a/saw-core/saw-core.cabal +++ b/saw-core/saw-core.cabal @@ -44,7 +44,6 @@ library prettyprinter-ansi-terminal >= 1.1.2, random, rme, - serialise, template-haskell, text, th-lift-instances, diff --git a/saw-core/src/Verifier/SAW/FiniteValue.hs b/saw-core/src/Verifier/SAW/FiniteValue.hs index 1419ad97f3..cbf2e86678 100644 --- a/saw-core/src/Verifier/SAW/FiniteValue.hs +++ b/saw-core/src/Verifier/SAW/FiniteValue.hs @@ -25,8 +25,6 @@ import Data.Map (Map) import qualified Data.Map as Map import qualified Data.Text as Text import Numeric.Natural (Natural) -import GHC.Generics (Generic) -import Codec.Serialise import Prettyprinter hiding (Doc) @@ -61,9 +59,7 @@ data FirstOrderType | FOTArray FirstOrderType FirstOrderType | FOTTuple [FirstOrderType] | FOTRec (Map FieldName FirstOrderType) - deriving (Eq, Show, Generic) - -instance Serialise FirstOrderType -- automatically derived + deriving (Eq, Show) -- | Values inhabiting those first-order types. data FirstOrderValue @@ -75,9 +71,7 @@ data FirstOrderValue | FOVArray FirstOrderType FirstOrderType | FOVTuple [FirstOrderValue] | FOVRec (Map FieldName FirstOrderValue) - deriving (Eq, Generic) - -instance Serialise FirstOrderValue -- automatically derived + deriving Eq toFirstOrderType :: FiniteType -> FirstOrderType toFirstOrderType ft = diff --git a/saw-script.cabal b/saw-script.cabal index 127a40b777..fe64601aae 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -10,6 +10,7 @@ License-file: LICENSE extra-source-files: src/SAWScript/Parser.y src/SAWScript/Lexer.x + src/SAWScript/SolverCache/lmdb_opt_database.py custom-setup setup-depends: @@ -61,7 +62,6 @@ library , lens , llvm-pretty >= 0.8 , llvm-pretty-bc-parser >= 0.1.3.1 - , lmdb-simple >= 0.4.0.0 , macaw-base , macaw-x86 , macaw-symbolic @@ -85,7 +85,6 @@ library , saw-core-sbv , saw-core-what4 , sbv >= 9.1 && < 9.3 - , serialise , split , temporary , template-haskell @@ -131,6 +130,7 @@ library SAWScript.SBVParser SAWScript.SBVModel SAWScript.SolverCache + SAWScript.SolverCache.LMDBOptDatabase SAWScript.SolverVersions SAWScript.Token SAWScript.TopLevel diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 3ce44cd2d2..9c610deffb 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -909,7 +909,7 @@ goal_num_ite n s1 s2 = proveABC :: ProofScript () proveABC = do SV.AIGProxy proxy <- SV.scriptTopLevel SV.getProxy - wrapProver [AIG] (Prover.proveABC proxy) Set.empty + wrapProver [AIG] [] (Prover.proveABC proxy) Set.empty satExternal :: Bool -> String -> [String] -> ProofScript () satExternal doCNF execName args = @@ -934,7 +934,7 @@ writeSAIGComputedPrim = Prover.writeSAIG -- | Bit-blast a proposition check its validity using the RME library. proveRME :: ProofScript () -proveRME = wrapProver [RME] Prover.proveRME Set.empty +proveRME = wrapProver [RME] [] Prover.proveRME Set.empty codegenSBV :: SharedContext -> FilePath -> [String] -> String -> TypedTerm -> TopLevel () codegenSBV sc path unints fname (TypedTerm _schema t) = @@ -954,29 +954,30 @@ proveUnintSBV :: SBV.SMTConfig -> [String] -> ProofScript () proveUnintSBV conf unints = do timeout <- psTimeout <$> get unintSet <- SV.scriptTopLevel (resolveNames unints) - wrapProver [SBV, Solver $ SBV.name $ SBV.solver conf] + wrapProver (sbvBackends conf) [] (Prover.proveUnintSBV conf timeout) unintSet -- | Given a continuation which calls a prover, call the continuation on the -- 'goalSequent' of the given 'ProofGoal' and return a 'SolveResult'. If there -- is a 'SolverCache', do not call the continuation if the goal has an already -- cached result, and otherwise save the result of the call to the cache. -applyProverToGoal :: [SolverBackend] +applyProverToGoal :: [SolverBackend] -> [SolverBackendOption] -> (SATQuery -> TopLevel (Maybe CEX, String)) -> Set VarIndex -> ProofGoal -> TopLevel (SolverStats, SolveResult) -applyProverToGoal backends f unintSet g = do +applyProverToGoal backends opts f unintSet g = do sc <- getSharedContext - bkvs <- io $ getSolverBackendVersions backends + let opt_backends = concatMap optionBackends opts + vs <- io $ getSolverBackendVersions (backends ++ opt_backends) satq <- io $ sequentToSATQuery sc unintSet (goalSequent g) - k <- io $ mkSolverCacheKey sc bkvs satq + k <- io $ mkSolverCacheKey sc vs opts satq (mb, solver_name) <- SV.askSolverCache (lookupInSolverCache k) >>= \case -- Use a cached result if one exists (and it's valid w.r.t our query) Just v -> return $ fromSolverCacheValue satq v -- Otherwise try to cache the result of the call _ -> f satq >>= \res -> - case toSolverCacheValue bkvs satq res of + case toSolverCacheValue vs opts satq res of Just v -> SV.onSolverCache (insertInSolverCache k v) >> return res Nothing -> return res @@ -986,21 +987,22 @@ applyProverToGoal backends f unintSet g = do Just a -> return (stats, SolveCounterexample a) wrapProver :: - [SolverBackend] -> + [SolverBackend] -> [SolverBackendOption] -> (SATQuery -> TopLevel (Maybe CEX, String)) -> Set VarIndex -> ProofScript () -wrapProver backend f = execTactic . tacticSolve . applyProverToGoal backend f +wrapProver backends opts f = + execTactic . tacticSolve . applyProverToGoal backends opts f wrapW4Prover :: - W4Backend -> Solver -> + SolverBackend -> [SolverBackendOption] -> ( Bool -> SATQuery -> TopLevel (Maybe CEX, String) ) -> [String] -> ProofScript () -wrapW4Prover x s f unints = do +wrapW4Prover backend opts f unints = do hashConsing <- SV.scriptTopLevel $ gets SV.rwWhat4HashConsing unintSet <- SV.scriptTopLevel $ resolveNames unints - wrapProver (w4Backends x s) (f hashConsing) unintSet + wrapProver [What4, backend] opts (f hashConsing) unintSet wrapW4ProveExporter :: ( Bool -> FilePath -> SATQuery -> TopLevel (Maybe CEX, String) ) -> @@ -1062,40 +1064,40 @@ proveUnintYices = proveUnintSBV SBV.yices -------------------------------------------------- w4_abc_smtlib2 :: ProofScript () -w4_abc_smtlib2 = wrapW4Prover W4_SMTLib2 ABC Prover.proveWhat4_abc [] +w4_abc_smtlib2 = wrapW4Prover ABC [W4_SMTLib2] Prover.proveWhat4_abc [] w4_boolector :: ProofScript () -w4_boolector = wrapW4Prover W4_Base Boolector Prover.proveWhat4_boolector [] +w4_boolector = wrapW4Prover Boolector [] Prover.proveWhat4_boolector [] w4_z3 :: ProofScript () -w4_z3 = wrapW4Prover W4_Base Z3 Prover.proveWhat4_z3 [] +w4_z3 = wrapW4Prover Z3 [] Prover.proveWhat4_z3 [] w4_cvc4 :: ProofScript () -w4_cvc4 = wrapW4Prover W4_Base CVC4 Prover.proveWhat4_cvc4 [] +w4_cvc4 = wrapW4Prover CVC4 [] Prover.proveWhat4_cvc4 [] w4_cvc5 :: ProofScript () -w4_cvc5 = wrapW4Prover W4_Base CVC5 Prover.proveWhat4_cvc5 [] +w4_cvc5 = wrapW4Prover CVC5 [] Prover.proveWhat4_cvc5 [] w4_yices :: ProofScript () -w4_yices = wrapW4Prover W4_Base Yices Prover.proveWhat4_yices [] +w4_yices = wrapW4Prover Yices [] Prover.proveWhat4_yices [] w4_unint_boolector :: [String] -> ProofScript () -w4_unint_boolector = wrapW4Prover W4_Base Boolector Prover.proveWhat4_boolector +w4_unint_boolector = wrapW4Prover Boolector [] Prover.proveWhat4_boolector w4_unint_z3 :: [String] -> ProofScript () -w4_unint_z3 = wrapW4Prover W4_Base Z3 Prover.proveWhat4_z3 +w4_unint_z3 = wrapW4Prover Z3 [] Prover.proveWhat4_z3 w4_unint_z3_using :: String -> [String] -> ProofScript () -w4_unint_z3_using tactic = wrapW4Prover (W4_Tactic tactic) Z3 (Prover.proveWhat4_z3_using tactic) +w4_unint_z3_using tactic = wrapW4Prover Z3 [W4_Tactic tactic] (Prover.proveWhat4_z3_using tactic) w4_unint_cvc4 :: [String] -> ProofScript () -w4_unint_cvc4 = wrapW4Prover W4_Base CVC4 Prover.proveWhat4_cvc4 +w4_unint_cvc4 = wrapW4Prover CVC4 [] Prover.proveWhat4_cvc4 w4_unint_cvc5 :: [String] -> ProofScript () -w4_unint_cvc5 = wrapW4Prover W4_Base CVC5 Prover.proveWhat4_cvc5 +w4_unint_cvc5 = wrapW4Prover CVC5 [] Prover.proveWhat4_cvc5 w4_unint_yices :: [String] -> ProofScript () -w4_unint_yices = wrapW4Prover W4_Base Yices Prover.proveWhat4_yices +w4_unint_yices = wrapW4Prover Yices [] Prover.proveWhat4_yices offline_w4_unint_z3 :: [String] -> String -> ProofScript () offline_w4_unint_z3 unints path = @@ -1178,10 +1180,10 @@ offline_verilog path = proveWithSATExporter Prover.writeVerilogSAT mempty path "." ".v" w4_abc_aiger :: ProofScript () -w4_abc_aiger = wrapW4Prover W4_AIGER ABC Prover.w4AbcAIGER [] +w4_abc_aiger = wrapW4Prover ABC [W4_AIGER] Prover.w4AbcAIGER [] w4_abc_verilog :: ProofScript () -w4_abc_verilog = wrapW4Prover W4_Verilog ABC Prover.w4AbcVerilog [] +w4_abc_verilog = wrapW4Prover ABC [W4_Verilog] Prover.w4AbcVerilog [] set_timeout :: Integer -> ProofScript () set_timeout to = modify (setProofTimeout to) diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index fc73ea2744..ce1313105a 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -10,6 +10,7 @@ Stability : provisional {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ImplicitParams #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} #if !MIN_VERSION_base(4,8,0) {-# LANGUAGE OverlappingInstances #-} @@ -39,10 +40,8 @@ import Data.Traversable hiding ( mapM ) #endif import Control.Applicative ( (<|>) ) import qualified Control.Exception as X -import Control.Monad (unless, (>=>), when) +import Control.Monad (unless, (>=>), when, forM) import Control.Monad.IO.Class (liftIO) -import Data.IORef -import Data.Maybe (fromMaybe) import qualified Data.ByteString as BS import qualified Data.Map as Map import Data.Map ( Map ) @@ -456,8 +455,10 @@ buildTopLevelEnv proxy opts = ss <- basic_ss sc jcb <- JCB.loadCodebase (jarList opts) (classPath opts) (javaBinDirs opts) currDir <- getCurrentDirectory - cache <- mapM (\p -> snd <$> setSolverCachePath p opts emptySolverCache) - (solverCache opts) >>= newIORef + mb_cache <- forM (solverCache opts) $ \p -> do + cache <- emptySolverCache + setSolverCachePath p opts cache + return cache thmDB <- newTheoremDB Crucible.withHandleAllocator $ \halloc -> do let ro0 = TopLevelRO @@ -468,7 +469,6 @@ buildTopLevelEnv proxy opts = , roProxy = proxy , roInitWorkDir = currDir , roBasicSS = ss - , roSolverCache = cache , roStackTrace = [] , roSubshell = fail "Subshells not supported" , roProofSubshell = fail "Proof subshells not supported" @@ -494,6 +494,7 @@ buildTopLevelEnv proxy opts = , rwProofs = [] , rwPPOpts = SAWScript.Value.defaultPPOpts , rwSharedContext = sc + , rwSolverCache = mb_cache , rwTheoremDB = thmDB , rwJVMTrans = jvmTrans , rwPrimsAvail = primsAvail @@ -646,16 +647,22 @@ disable_lax_loads_and_stores = do enable_solver_cache :: TopLevel () enable_solver_cache = do - ref <- getSolverCache - io $ atomicModifyIORef ref $ (,()) . Just . fromMaybe emptySolverCache + rw <- getTopLevelRW + getSolverCache >>= \case + Just _ -> return () + Nothing -> do emptyCache <- io $ emptySolverCache + putTopLevelRW rw { rwSolverCache = Just emptyCache } set_solver_cache_path :: FilePath -> TopLevel () set_solver_cache_path path = enable_solver_cache >> onSolverCache (setSolverCachePath path) +print_solver_cache :: String -> TopLevel () +print_solver_cache = onSolverCache . printSolverCacheByHex + clean_solver_cache :: TopLevel () clean_solver_cache = do - vs <- io $ getSolverBackendVersions baseBackends + vs <- io $ getSolverBackendVersions allBackends onSolverCache (cleanSolverCache vs) enable_debug_intrinsics :: TopLevel () @@ -1091,8 +1098,6 @@ primitives = Map.fromList [ "Enable solver result caching if it is not already enabled, set a" , " path to use for loading and saving cached results, and save to" , " that path any results cached so far from the current session." - , " If there is already a path set for solver caching, this will" - , " error." ] , prim "clean_solver_cache" "TopLevel ()" @@ -1103,12 +1108,21 @@ primitives = Map.fromList , " in the current environment." ] + , prim "print_solver_cache" "String -> TopLevel ()" + (pureVal print_solver_cache) + Experimental + [ "Print all entries in the solver result cache whose SHA256 hash" + , " keys start with the given string. Thus, given an empty string," + , " all entries in the cache will be printed." + ] + , prim "print_solver_cache_stats" "TopLevel ()" (pureVal (onSolverCache printSolverCacheStats)) Experimental [ "Print out statistics about how the solver cache was used, namely" - , " how many results are cached and how many cache hits have" - , " occurred - both in memory and on disk" ] + , " how many entries are in the cache, how many insertions into the" + , " cache have been made so far this session, and how many times" + , " cached results have been used so far this session." ] , prim "enable_debug_intrinsics" "TopLevel ()" (pureVal enable_debug_intrinsics) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index cbc875f904..f89e37ccc6 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -39,6 +39,8 @@ were obtained by the backends in the first place). {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralisedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TupleSections #-} @@ -47,16 +49,15 @@ were obtained by the backends in the first place). {-# OPTIONS_GHC -fno-warn-orphans #-} module SAWScript.SolverCache - ( Solver(..) - , SolverBackend(..) - , W4Backend(..) - , w4Backends - , baseBackends - , SolverBackendVersion(..) + ( SolverBackend(..) + , allBackends + , sbvBackends + , SolverBackendOption(..) + , optionBackends + , SolverBackendVersions , SolverCacheKey(..) - , solverCacheKeyToHex , mkSolverCacheKey - , SolverCacheValue + , SolverCacheValue(..) , toSolverCacheValue , fromSolverCacheValue , SolverCache(..) @@ -65,135 +66,185 @@ module SAWScript.SolverCache , lookupInSolverCache , insertInSolverCache , setSolverCachePath + , printSolverCacheByHex , cleanSolverCache , printSolverCacheStats ) where -import System.Directory -import Control.Exception -import GHC.Generics (Generic) +import System.Directory (createDirectoryIfMissing, makeAbsolute) +import Control.Exception (try, SomeException) +import Control.Monad (when, forM_) +import System.Timeout (timeout) -import Control.Monad (forM_) -import Data.Tuple.Extra (first, firstM) +import GHC.Generics (Generic) +import Data.IORef (IORef, newIORef, modifyIORef, readIORef) +import Data.Tuple.Extra (first, firstM, both) import Data.List (isPrefixOf, elemIndex, intercalate) import Data.Hashable (Hashable(..)) import Data.Bits (shiftL, (.|.)) -import Data.Maybe (fromMaybe, isJust) +import Data.Maybe (fromMaybe) import Data.Functor ((<&>)) -import qualified Data.Set as Set -import Data.Set (Set) - -import qualified Data.Map as Map -import qualified Data.HashMap.Strict as HM -import Data.HashMap.Strict (HashMap) +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as M import qualified Data.Text as T import Data.Text.Encoding -import Text.Hex +import Data.ByteString (ByteString) import qualified Data.ByteString as BS import qualified Crypto.Hash.SHA256 as SHA256 -import qualified Database.LMDB.Simple as LMDB -import qualified Database.LMDB.Simple.Extra as LMDB -import Codec.Serialise +import Data.Aeson ( FromJSON(..), ToJSON(..), FromJSONKey(..), ToJSONKey(..) + , (.:), (.:?), (.=) ) +import qualified Data.Aeson as JSON -import Data.SBV.Dynamic (Solver(..)) +import qualified Data.SBV.Dynamic as SBV import Verifier.SAW.FiniteValue import Verifier.SAW.SATQuery import Verifier.SAW.ExternalFormat import Verifier.SAW.SharedTerm +import SAWScript.SolverCache.LMDBOptDatabase (encodeHex, LMDBOptDatabase) +import qualified SAWScript.SolverCache.LMDBOptDatabase as LMDBOpt + import SAWScript.Options import SAWScript.Proof --- Solver Backends ------------------------------------------------------------- +-- Orphan Instances and Helper Functions --------------------------------------- -deriving instance Eq Solver -deriving instance Ord Solver -deriving instance Read Solver +deriving instance Generic FirstOrderType +deriving instance Generic FirstOrderValue + +-- | ... +firstOrderJSONOptions :: JSON.Options +firstOrderJSONOptions = + JSON.defaultOptions { JSON.sumEncoding = JSON.TwoElemArray + , JSON.constructorTagModifier = dropFO } + where dropFO ('F':'O':tv:cs) | tv `elem` ['T', 'V'] = cs + dropFO cs = cs + +instance FromJSON FirstOrderType where + parseJSON = JSON.genericParseJSON firstOrderJSONOptions +instance FromJSON FirstOrderValue where + parseJSON = JSON.genericParseJSON firstOrderJSONOptions + +instance ToJSON FirstOrderType where + toJSON = JSON.genericToJSON firstOrderJSONOptions + toEncoding = JSON.genericToEncoding firstOrderJSONOptions +instance ToJSON FirstOrderValue where + toJSON = JSON.genericToJSON firstOrderJSONOptions + toEncoding = JSON.genericToEncoding firstOrderJSONOptions + +-- | ... +tryWithTimeout :: Int -> IO a -> IO (Either String a) +tryWithTimeout t_ms m = try (timeout (t_ms * 1000) m) <&> \case + Right (Just a) -> Right a + Right Nothing -> Left $ "Operation timed out (" ++ show t_ms ++ "ms)" + Left (exn :: SomeException) -> Left $ show exn --- | A datatype representing one of the ways the what4 backend can be used in --- SAW - i.e. directly ('W4_Base'), with a tactic ('W4_Tactic'), by converting --- to SMTLib2 then calling ABC ('W4_SMTLib2'), by converting to Verilog then --- calling ABC ('W4_Verilog'), or by converting to AIGER then calling ABC --- ('W4_AIGER') -data W4Backend = W4_Base - | W4_Tactic String - | W4_SMTLib2 - | W4_Verilog - | W4_AIGER - deriving (Eq, Ord, Read, Show) + +-- Solver Backends ------------------------------------------------------------- -- | A datatype representing one of the solver backends available in SAW. Note -- that for 'SBV' and 'W4', multiple backends will be used (e.g. 'SBV' with -- @Solver Z3@ or @W4 W4_AIGER@ with 'AIG' and @Solver ABC@), though not all -- comtinations of backends are valid (e.g. @W4 W4_SMTLib2@ with anything but -- @Solver Z3@) -data SolverBackend = Solver Solver - | W4 W4Backend +data SolverBackend = What4 | SBV | AIG | RME - deriving (Eq, Ord, Read, Show) - -instance Serialise SolverBackend where - encode (Solver s) = encode (1 :: Int, show s) - encode (W4 w) = encode (2 :: Int, show w) - encode backend = encode (0 :: Int, show backend) - decode = decode <&> \case (1 :: Int, s) -> Solver (read s) - (2 :: Int, s) -> W4 (read s) - (_ :: Int, s) -> read s - --- | Given a 'W4Backend' and a 'Solver', return the list of 'SolverBackend's --- used. In particular, this includes 'AIG' for 'W4_AIGER'. -w4Backends :: W4Backend -> Solver -> [SolverBackend] -w4Backends w s | W4_AIGER <- w = [W4 w, AIG, Solver s] - | otherwise = [W4 w, Solver s] - --- | Convert a 'SolverBackend' into its base form, namely replace any 'W4' --- backend with @W4 W4_Base@. -baseBackend :: SolverBackend -> SolverBackend -baseBackend (W4 _) = W4 W4_Base -baseBackend backend = backend - --- | The finite list of all base 'SolverBackend's (as per 'baseBackend') -baseBackends :: [SolverBackend] -baseBackends = (Solver <$> enumFrom minBound) ++ - [W4 W4_Base, SBV, AIG, RME] - --- | A 'SolverBackend' and its version 'String', if one could be obtained -data SolverBackendVersion = - SolverBackendVersion - { solverBackend :: SolverBackend - , solverVersion :: Maybe String - } deriving (Eq, Ord, Generic) - -instance Serialise SolverBackendVersion -- automatically derived - -instance Show SolverBackendVersion where - show (SolverBackendVersion backend mb_v) = case backend of - Solver s | show s `isPrefixOf` v_str -> v_str - | otherwise -> show s ++ " " ++ v_str - W4 W4_Base -> unwords ["what4", v_str] - W4 (W4_Tactic t) -> unwords ["what4", v_str, "using", t] - W4 W4_SMTLib2 -> unwords ["what4", v_str, "to", "SMTLib2"] - W4 W4_Verilog -> unwords ["what4", v_str, "to", "Verilog"] - W4 W4_AIGER -> unwords ["what4", v_str, "to", "AIGER"] - SBV -> unwords ["SBV", v_str] - AIG -> unwords ["AIG", v_str] - RME -> unwords ["RME", v_str] - where v_str = fromMaybe "[unknown version]" mb_v - --- | Convert the 'SolverBackend' of a 'SolverBackendVersion' into its base --- form using 'baseBackend' -baseBackendVersion :: SolverBackendVersion -> SolverBackendVersion -baseBackendVersion (SolverBackendVersion backend v) = - SolverBackendVersion (baseBackend backend) v + -- External solvers (copied from SBV.Solver) + | ABC + | Boolector + | Bitwuzla + | CVC4 + | CVC5 + | DReal + | MathSAT + | Yices + | Z3 + deriving (Eq, Ord, Enum, Bounded, Show, Generic) + +instance FromJSON SolverBackend where + parseJSON = JSON.genericParseJSON JSON.defaultOptions +instance ToJSON SolverBackend where + toJSON = JSON.genericToJSON JSON.defaultOptions + toEncoding = JSON.genericToEncoding JSON.defaultOptions +instance FromJSONKey SolverBackend where + fromJSONKey = JSON.genericFromJSONKey JSON.defaultJSONKeyOptions +instance ToJSONKey SolverBackend where + toJSONKey = JSON.genericToJSONKey JSON.defaultJSONKeyOptions + +-- | ... +allBackends :: [SolverBackend] +allBackends = [minBound..] + +-- | ... +sbvBackends :: SBV.SMTConfig -> [SolverBackend] +sbvBackends conf = [SBV, cvtSolver $ SBV.name $ SBV.solver conf] + where cvtSolver SBV.ABC = ABC + cvtSolver SBV.Boolector = Boolector + cvtSolver SBV.Bitwuzla = Bitwuzla + cvtSolver SBV.CVC4 = CVC4 + cvtSolver SBV.CVC5 = CVC5 + cvtSolver SBV.DReal = DReal + cvtSolver SBV.MathSAT = MathSAT + cvtSolver SBV.Yices = Yices + cvtSolver SBV.Z3 = Z3 + +-- | A datatype representing one of the ways the what4 backend can be used in +-- SAW - i.e. directly ('W4_Base'), with a tactic ('W4_Tactic'), by converting +-- to SMTLib2 then calling ABC ('W4_SMTLib2'), by converting to Verilog then +-- calling ABC ('W4_Verilog'), or by converting to AIGER then calling ABC +-- ('W4_AIGER') +data SolverBackendOption = W4_Tactic String + | W4_SMTLib2 + | W4_Verilog + | W4_AIGER + deriving (Eq, Ord, Show, Generic) + +instance FromJSON SolverBackendOption where + parseJSON = JSON.genericParseJSON JSON.defaultOptions +instance ToJSON SolverBackendOption where + toJSON = JSON.genericToJSON JSON.defaultOptions + toEncoding = JSON.genericToEncoding JSON.defaultOptions + +-- | ... +optionBackends :: SolverBackendOption -> [SolverBackend] +optionBackends W4_AIGER = [AIG] +optionBackends _ = [] + +-- | ... +-- A 'SolverBackend' and its version 'String', if one could be obtained +type SolverBackendVersions = Map SolverBackend (Maybe String) + +-- | ... +showSolverBackendVersion :: SolverBackend -> Maybe String -> [String] -> String +showSolverBackendVersion backend (Just v_str) opt_words = + if show backend `isPrefixOf` v_str + then unwords $ v_str : opt_words + else unwords $ show backend : v_str : opt_words +showSolverBackendVersion backend Nothing opt_words = + showSolverBackendVersion backend (Just "[unknown version]") opt_words + +-- | ... +showBackendVersionsWithOptions :: String -> SolverBackendVersions -> + [SolverBackendOption] -> String +showBackendVersionsWithOptions sep vs opts = + let entries = M.unionWith (<>) (M.map (\v -> (v, [])) vs) + (M.fromList $ optEntry <$> opts) + in intercalate sep $ showEntry <$> M.toList entries + where optEntry (W4_Tactic t) = (What4, (Nothing, ["using", t])) + optEntry W4_SMTLib2 = (What4, (Nothing, ["to", "SMTLib2"])) + optEntry W4_Verilog = (What4, (Nothing, ["to", "Verilog"])) + optEntry W4_AIGER = (What4, (Nothing, ["to", "AIGER"])) + showEntry (backend, (mb_v_str, opt_words)) = + showSolverBackendVersion backend mb_v_str opt_words -- Solver Cache Keys ----------------------------------------------------------- @@ -202,21 +253,18 @@ baseBackendVersion (SolverBackendVersion backend v) = -- a 'Set' of 'SolverBackendVersion's - see 'mkSolverCacheKey' data SolverCacheKey = SolverCacheKey - { solverCacheKeyVersions :: Set SolverBackendVersion + { solverCacheKeyVersions :: SolverBackendVersions + , solverCacheKeyOptions :: [SolverBackendOption] , solverCacheKeyHash :: ByteString } instance Eq SolverCacheKey where - (SolverCacheKey _ bs1) == (SolverCacheKey _ bs2) = bs1 == bs2 - -instance Serialise SolverCacheKey where - encode (SolverCacheKey _ bs) = encode bs - decode = SolverCacheKey Set.empty <$> decode + (SolverCacheKey _ _ bs1) == (SolverCacheKey _ _ bs2) = bs1 == bs2 -- | Truncate a 'SolverCacheKey' (i.e. a SHA256 hash) to an 'Int', used to give -- the type a fast 'Hashable' instance solverCacheKeyInt :: SolverCacheKey -> Int -solverCacheKeyInt (SolverCacheKey _ bs) = +solverCacheKeyInt (SolverCacheKey _ _ bs) = BS.foldl' (\a b -> a `shiftL` 8 .|. fromIntegral b) 0 (BS.take 8 bs) instance Hashable SolverCacheKey where @@ -224,14 +272,9 @@ instance Hashable SolverCacheKey where hashWithSalt s = hashWithSalt s . solverCacheKeyInt instance Show SolverCacheKey where - show (SolverCacheKey vs bs) = T.unpack (encodeHex (BS.take 8 bs)) ++ - if Set.null vs then "" - else " (" ++ intercalate ", " vstrs ++ ")" - where vstrs = show <$> Set.elems vs - --- | Convert a 'SolverCacheKey' to a hexadecimal 'String' -solverCacheKeyToHex :: SolverCacheKey -> String -solverCacheKeyToHex (SolverCacheKey _ bs) = T.unpack $ encodeHex bs + show (SolverCacheKey vs opts bs) = encodeHex (BS.take 8 bs) ++ + if M.null vs && null opts then "" + else " (" ++ showBackendVersionsWithOptions ", " vs opts ++ ")" -- | Hash using SHA256 a 'String' representation of a 'SATQuery' and a 'Set' of -- 'SolverBackendVersion's to get a 'SolverCacheKey'. In particular, this @@ -245,19 +288,19 @@ solverCacheKeyToHex (SolverCacheKey _ bs) = T.unpack $ encodeHex bs -- 2. After calling 'scWriteExternal', all 'LocalName's in 'Pi' and 'Lam' -- constructors are removed. This ensures that two terms which are alpha -- equivalent are given the same hash. -mkSolverCacheKey :: SharedContext -> Set SolverBackendVersion -> SATQuery -> - IO SolverCacheKey -mkSolverCacheKey sc vs satq = do +mkSolverCacheKey :: SharedContext -> SolverBackendVersions -> + [SolverBackendOption] -> SATQuery -> IO SolverCacheKey +mkSolverCacheKey sc vs opts satq = do body <- satQueryAsPropTerm sc satq - let ecs = Map.keys (satVariables satq) ++ + let ecs = M.keys (satVariables satq) ++ filter (\ec -> ecVarIndex ec `elem` satUninterp satq) (getAllExts body) tm <- scGeneralizeExts sc ecs body - let str_prefix = map show (Set.elems vs) ++ - [ "satVariables " ++ show (Map.size (satVariables satq)) - , "satUninterp " ++ show (length (satUninterp satq)) ] + let str_prefix = [ showBackendVersionsWithOptions "\n" vs opts + , "satVariables " ++ show (M.size (satVariables satq)) + , "satUninterp " ++ show (length (satUninterp satq)) ] str_to_hash = unlines str_prefix ++ anonLocalNames (scWriteExternal tm) - return $ SolverCacheKey vs $ SHA256.hash $ encodeUtf8 $ T.pack $ str_to_hash + return $ SolverCacheKey vs opts $ SHA256.hash $ encodeUtf8 $ T.pack $ str_to_hash where anonLocalNames = unlines . map (unwords . go . words) . lines go (x:y:_:xs) | y `elem` ["Pi", "Lam"] = x:y:"_":xs go xs = xs @@ -271,114 +314,43 @@ mkSolverCacheKey sc vs satq = do -- 'satVariables' of an associated 'SATQuery' data SolverCacheValue = SolverCacheValue - { solverCacheValueVersions :: Set SolverBackendVersion + { solverCacheValueVersions :: SolverBackendVersions + , solverCacheValueOptions :: [SolverBackendOption] , solverCacheValueSolverName :: String , solverCacheValueCEXs :: Maybe [(Int, FirstOrderValue)] - } deriving (Eq, Generic) - -instance Serialise SolverCacheValue -- automatically derived - -instance Semigroup SolverCacheValue where - (SolverCacheValue vs1 nm1 cs1) <> (SolverCacheValue vs2 nm2 cs2) = - SolverCacheValue (vs1 <> vs2) (nm1 <> nm2) (cs1 <> cs2) - -instance Monoid SolverCacheValue where - mempty = SolverCacheValue mempty mempty mempty + } deriving Eq + +instance FromJSON SolverCacheValue where + parseJSON = JSON.withObject "SolverCacheValue" $ \v -> do + vs <- v .: "vs" + opts <- v .:? "opts" + nm <- v .: "nm" + mb_cexs <- v .:? "cexs" + return $ SolverCacheValue vs (fromMaybe [] opts) nm mb_cexs + +instance ToJSON SolverCacheValue where + toJSON (SolverCacheValue vs opts nm mb_cexs) = JSON.object $ + ["vs" .= vs] ++ (if null opts then [] else ["opts" .= opts]) ++ + ["nm" .= nm] ++ maybe [] (\cexs -> ["cexs" .= cexs]) mb_cexs + toEncoding (SolverCacheValue vs opts nm mb_cexs) = JSON.pairs $ + "vs" .= vs <> (if null opts then mempty else "opts" .= opts) <> + "nm" .= nm <> maybe mempty (\cexs -> "cexs" .= cexs) mb_cexs -- | Convert the result of a solver call on the given 'SATQuery' to a -- 'SolverCacheValue' -toSolverCacheValue :: Set SolverBackendVersion -> SATQuery -> - (Maybe CEX, String) -> Maybe SolverCacheValue -toSolverCacheValue vs satq (cexs, solver_name) = - fmap (SolverCacheValue vs solver_name) +toSolverCacheValue :: SolverBackendVersions -> [SolverBackendOption] -> + SATQuery -> (Maybe CEX, String) -> Maybe SolverCacheValue +toSolverCacheValue vs opts satq (cexs, solver_name) = + fmap (SolverCacheValue vs opts solver_name) (mapM (mapM (firstM (`elemIndex` ecs))) cexs) - where ecs = Map.keys $ satVariables satq + where ecs = M.keys $ satVariables satq -- | Convert a 'SolverCacheValue' to something which has the same form as the -- result of a solver call on the given 'SATQuery' fromSolverCacheValue :: SATQuery -> SolverCacheValue -> (Maybe CEX, String) -fromSolverCacheValue satq (SolverCacheValue _ solver_name cexs) = +fromSolverCacheValue satq (SolverCacheValue _ _ solver_name cexs) = (fmap (fmap (first (ecs !!))) cexs, solver_name) - where ecs = Map.keys $ satVariables satq - - --- The Database Behind the Solver Cache ---------------------------------------- - --- | The database behind the 'SolverCache' - either a 'HashMap' or a --- 'LMDB.Database' from 'SolverCacheKey's to 'SolverCacheValue's. We refer to --- the former as "in memory" and the latter as "on disk". For the latter, we --- also save the 'FilePath' of the LMDB database as well as the --- 'LMDB.Environment'. -data SolverCacheDB - = SolverCacheMem (HashMap SolverCacheKey SolverCacheValue) - | SolverCacheDisk FilePath (LMDB.Environment LMDB.ReadWrite) - (LMDB.Database SolverCacheKey SolverCacheValue) - --- | An empty 'SolverCacheDB' in memory -emptyDB :: SolverCacheDB -emptyDB = SolverCacheMem HM.empty - --- | Get the 'FilePath' of the 'SolverCacheDB's on-disk database, if it exists -getDBPath :: SolverCacheDB -> Maybe FilePath -getDBPath (SolverCacheMem _ ) = Nothing -getDBPath (SolverCacheDisk path _ _) = Just path - --- | If the 'SolverCacheDB' does not currently have an associated on-disk --- database, create one at the associated 'FilePath' and copy all entries in --- memory on to disk -setPathDB :: FilePath -> SolverCacheDB -> IO SolverCacheDB -setPathDB path (SolverCacheMem hm) = do - createDirectoryIfMissing False path - let limits = LMDB.defaultLimits { LMDB.mapSize = 2 ^ (32 :: Int) } - env <- LMDB.openReadWriteEnvironment path limits - LMDB.readWriteTransaction env $ do - db <- LMDB.getDatabase Nothing - forM_ (HM.toList hm) $ \(k,v) -> LMDB.put db k (Just v) - return $ SolverCacheDisk path env db -setPathDB _ (SolverCacheDisk path _ _) = - fail $ "Solver cache already has a set path: " ++ path - --- | A general function for querying a 'SolverCacheDB' -askDB :: (HashMap SolverCacheKey SolverCacheValue -> a) -> - (LMDB.Database SolverCacheKey SolverCacheValue -> - LMDB.Transaction LMDB.ReadOnly a) -> - a -> SolverCacheDB -> IO a -askDB f _ _ (SolverCacheMem hm) = return $ f hm -askDB _ g dflt (SolverCacheDisk _ env db) = - catch (LMDB.readOnlyTransaction env $ g db) - (\(_ :: IOException) -> return dflt) - --- | Get the size of a 'SolverCacheDB' -sizeDB :: SolverCacheDB -> IO Int -sizeDB = askDB (HM.size) (LMDB.size) 0 - --- | Check whether a 'SolverCacheKey' is in a 'SolverCacheDB' -lookupInDB :: SolverCacheKey -> SolverCacheDB -> IO (Maybe SolverCacheValue) -lookupInDB k = askDB (HM.lookup k) (LMDB.lookup k) Nothing - --- | A general function for modifying a 'SolverCacheDB' -onDB :: (HashMap SolverCacheKey SolverCacheValue -> - HashMap SolverCacheKey SolverCacheValue) -> - (LMDB.Database SolverCacheKey SolverCacheValue -> - LMDB.Transaction LMDB.ReadWrite ()) -> - SolverCacheDB -> IO SolverCacheDB -onDB f _ (SolverCacheMem hm) = return $ SolverCacheMem $ f hm -onDB _ g c@(SolverCacheDisk _ env db) = - catch (LMDB.transaction env $ g db >>= \r -> return $ r `seq` c) - (\(_ :: IOException) -> return c) - --- | Insert a 'SolverCacheValue' at the given 'SolverCacheKey' into a --- 'SolverCacheDB' -insertInDB :: SolverCacheKey -> SolverCacheValue -> SolverCacheDB -> - IO SolverCacheDB -insertInDB k v = onDB (HM.insert k v) (LMDB.insert k v) - --- | Filter the entries in a 'SolverCacheDB' -filterDB :: (SolverCacheValue -> Bool) -> SolverCacheDB -> - IO SolverCacheDB -filterDB f = onDB (HM.filter f) $ \db -> do - kvs <- LMDB.toList db - forM_ kvs $ \(k,v) -> if f v then return False else LMDB.delete k db + where ecs = M.keys $ satVariables satq -- The Solver Cache ------------------------------------------------------------ @@ -387,80 +359,118 @@ filterDB f = onDB (HM.filter f) $ \db -> do -- many cache hits and how many new entry creations have occurred data SolverCache = SolverCache - { solverCacheDB :: SolverCacheDB - , solverCacheHits :: Integer - , solverCacheCreated :: Integer + { solverCacheDB :: LMDBOptDatabase SolverCacheValue + , solverCacheStats :: IORef (Map SolverCacheStat Integer) + , solverCacheTimeout :: Int } +-- | ... +data SolverCacheStat = Lookups | FailedLookups | Inserts | FailedInserts + deriving (Eq, Ord, Bounded, Enum) + -- | An empty 'SolverCache' with no associated 'FilePath' -emptySolverCache :: SolverCache -emptySolverCache = SolverCache emptyDB 0 0 +emptySolverCache :: IO SolverCache +emptySolverCache = do + db <- LMDBOpt.new + stats <- newIORef $ M.fromList ((,0) <$> [minBound..]) + return $ SolverCache db stats 1000 -- | A stateful operation on a 'SolverCache', returning a value of the given type -type SolverCacheOp a = Options -> SolverCache -> IO (a, SolverCache) +type SolverCacheOp a = Options -> SolverCache -> IO a -- | Lookup a 'SolverCacheKey' in the solver result cache lookupInSolverCache :: SolverCacheKey -> SolverCacheOp (Maybe SolverCacheValue) -lookupInSolverCache k opts cache = - lookupInDB k (solverCacheDB cache) >>= \case - Just v -> do +lookupInSolverCache k opts SolverCache{..} = + tryWithTimeout solverCacheTimeout + (LMDBOpt.lookup (solverCacheKeyHash k) + solverCacheDB) >>= \case + Right (Just v) -> do printOutLn opts Info ("Using cached result: " ++ show k) - return (Just v, cache { solverCacheHits = solverCacheHits cache + 1 }) - Nothing -> return (Nothing, cache) + modifyIORef solverCacheStats $ M.adjust (+1) Lookups + return (Just v) + Left err -> do + printOutLn opts Warn ("Solver cache lookup failed:\n" ++ err) + modifyIORef solverCacheStats $ M.adjust (+1) FailedLookups + return Nothing + Right Nothing -> do + return Nothing -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () -insertInSolverCache k v opts cache = do - printOutLn opts Info ("Caching result: " ++ show k) - db' <- insertInDB k v (solverCacheDB cache) - return ((), cache { solverCacheDB = db' - , solverCacheCreated = solverCacheCreated cache + 1 }) +insertInSolverCache k v opts SolverCache{..} = + printOutLn opts Info ("Caching result: " ++ show k) >> + tryWithTimeout solverCacheTimeout + (LMDBOpt.insert (solverCacheKeyHash k) v + solverCacheDB) >>= \case + Right () -> do + modifyIORef solverCacheStats $ M.adjust (+1) Inserts + Left err -> do + printOutLn opts Warn ("Solver cache insert failed:\n" ++ err) + modifyIORef solverCacheStats $ M.adjust (+1) FailedInserts -- | Set the 'FilePath' of the solver result cache, erroring if it is already -- set, and save all results cached so far setSolverCachePath :: FilePath -> SolverCacheOp () -setSolverCachePath path opts cache = do +setSolverCachePath path opts SolverCache{..} = do pathAbs <- makeAbsolute path - sz <- sizeDB (solverCacheDB cache) - let (s0, s1) = (show sz, if sz == 1 then "" else "s") - db' <- setPathDB pathAbs (solverCacheDB cache) - if sz == 0 then return () - else printOutLn opts Info ("Saved " ++ s0 ++ " cached result" ++ s1 ++ " to disk") - return ((), cache { solverCacheDB = db' }) + createDirectoryIfMissing True pathAbs + eith_sz <- tryWithTimeout solverCacheTimeout + (LMDBOpt.size solverCacheDB) + eith_db <- tryWithTimeout solverCacheTimeout + (LMDBOpt.setPath pathAbs 4096 solverCacheDB) + case (,) <$> eith_sz <*> eith_db of + Left err -> fail $ "Could not set solver cache path:\n" ++ err + Right (sz, ()) | sz == 0 -> return () + Right (sz, ()) -> do + let (s0, s1) = (show sz, if sz == 1 then "" else "s") + printOutLn opts Info ("Saved " ++ s0 ++ " cached result" ++ s1 ++ " to disk") + +-- | ... +printSolverCacheByHex :: String -> SolverCacheOp () +printSolverCacheByHex hex_prefix opts SolverCache{..} = do + kvs <- LMDBOpt.filterByHexPrefix hex_prefix solverCacheDB + when (length kvs == 0) $ printOutLn opts Info "No keys found" + forM_ kvs $ \(k_hash, SolverCacheValue vs bk_opts nm mb_cexs) -> do + let vs_str = showBackendVersionsWithOptions ", " vs bk_opts + res_str = maybe "unsat" (("sat " ++) . show) mb_cexs + printOutLn opts Info $ "SHA: " ++ encodeHex k_hash + printOutLn opts Info $ "- Result: " ++ res_str + printOutLn opts Info $ "- Solver: " ++ show nm + printOutLn opts Info $ "- Versions: " ++ vs_str ++ "\n" -- | Remove all entries in the solver result cache which have version(s) that -- do not match the current version(s). -cleanSolverCache :: Set SolverBackendVersion -> SolverCacheOp () -cleanSolverCache current_base_vs opts cache = do - sz0 <- sizeDB (solverCacheDB cache) - db' <- filterDB (f . solverCacheValueVersions) (solverCacheDB cache) - sz1 <- sizeDB (solverCacheDB cache) - let s = if (sz0 - sz1) == 1 then "" else "s" - printOutLn opts Info ("Removed " ++ show (sz0 - sz1) ++ - " cached result" ++ s ++ " with mismatched version" ++ s) - return ((), cache { solverCacheDB = db' }) - where f vs = Set.isSubsetOf (Set.filter (\v -> solverBackend v `Set.member` currently_known_backends) - (Set.map baseBackendVersion vs)) - current_base_vs - currently_known_backends = Set.map solverBackend (Set.filter (isJust . solverVersion) - current_base_vs) +cleanSolverCache :: SolverBackendVersions -> SolverCacheOp () +cleanSolverCache curr_base_vs opts SolverCache{..} = do + let curr_base_vs_obj = M.fromList [("vs", curr_base_vs)] + fs0 <- LMDBOpt.cleanByJSONObjValues curr_base_vs_obj solverCacheDB + let fs1 = concatMap (fmap (both (M.! ("vs" :: String))) . snd) fs0 + fs2 = M.unions $ fmap (uncurry $ M.intersectionWith (,)) fs1 + s0 = if length fs0 == 1 then "" else "s" + s1 = if M.size fs2 == 0 then "" else ":" + printOutLn opts Info $ + "Removed " ++ show (length fs0) ++ + " cached result" ++ s0 ++ " with mismatched version" ++ s0 ++ s1 + forM_ (M.toList fs2) $ \(backend, (v1, v2)) -> + printOutLn opts Info $ + "- " ++ showSolverBackendVersion backend v1 [] ++ + " (Current: " ++ showSolverBackendVersion backend v2 [] ++ ")" -- | Print out statistics about how the solver cache was used printSolverCacheStats :: SolverCacheOp () -printSolverCacheStats opts cache = do +printSolverCacheStats opts SolverCache{..} = do printOutLn opts Info ("== Solver result cache statistics ==") - sz <- sizeDB (solverCacheDB cache) - let sz_s = if sz == 1 then "" else "s" - case getDBPath (solverCacheDB cache) of - Nothing -> - printOutLn opts Info ("- " ++ show sz ++ " result" ++ sz_s ++ " cached in memory") - Just path -> do - printOutLn opts Info ("- " ++ show sz ++ " result" ++ sz_s ++ " cached on disk " - ++ "(" ++ path ++ ")") - let created = solverCacheCreated cache - created_s = if created == 1 then "y" else "ies" - printOutLn opts Info ("- " ++ show created ++ " cache entr" ++ created_s ++ " created this run") - let hits = solverCacheHits cache - (hits_s1, hits_s2) = if hits == 1 then (" a"," was") else ("s","s were") - printOutLn opts Info ("- " ++ show hits ++ " time" ++ hits_s1 ++ " cached result" ++ hits_s2 ++ " used this run") - return ((), cache) + sz <- LMDBOpt.size solverCacheDB + loc <- fromMaybe "memory" <$> LMDBOpt.getPath solverCacheDB + printOutLn opts Info ("- " ++ show sz ++ " result" ++ pl sz + ++ " cached in " ++ loc) + stats <- readIORef solverCacheStats + let (ls, ls_f) = (stats M.! Lookups, stats M.! FailedLookups) + (is, is_f) = (stats M.! Inserts, stats M.! FailedInserts) + printOutLn opts Info $ "- " ++ show is ++ " insertion" ++ pl is + ++ " into the cache so far this run (" + ++ show is_f ++ " failed attempt" ++ pl is_f ++ ")" + printOutLn opts Info $ "- " ++ show ls ++ " usage" ++ pl ls + ++ " of cached results so far this run (" + ++ show ls_f ++ " failed attempt" ++ pl ls_f ++ ")" + where pl i = if i == 1 then "" else "s" diff --git a/src/SAWScript/SolverCache/LMDBOptDatabase.hs b/src/SAWScript/SolverCache/LMDBOptDatabase.hs new file mode 100644 index 0000000000..295fa787ed --- /dev/null +++ b/src/SAWScript/SolverCache/LMDBOptDatabase.hs @@ -0,0 +1,264 @@ +{- | +Module : SAWScript.SolverCache +Description : Optionally LMDB-backed databases +License : BSD3 +Maintainer : m-yac +Stability : provisional + +... +-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE ViewPatterns #-} + +module SAWScript.SolverCache.LMDBOptDatabase + ( encodeHex + , decodeHex + , LMDBOptDatabase + , new + , setPath + , getPath + , size + , lookup + , insert + , delete + , toList + , filterByHexPrefix + , cleanByJSONObjValues + ) where + +import Prelude hiding (lookup) +import System.Timeout (timeout) +import System.IO.Temp (withSystemTempFile) +import System.Process +import System.IO + +import Control.Concurrent (forkIO, ThreadId) +import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar) +import Control.DeepSeq (deepseq) +import Control.Monad ((>=>), forever) + +import Language.Haskell.TH (runIO) +import Language.Haskell.TH.Syntax (qAddDependentFile, liftString) + +import qualified Data.Text as T +import qualified Text.Hex as Hex + +import Data.ByteString (ByteString) +import qualified Data.ByteString as BS + +import Data.Aeson (FromJSON, ToJSON) +import qualified Data.Aeson as JSON + +-- | ... +lmdb_opt_database__py :: String +lmdb_opt_database__py = $(do + let fp = "src/SAWScript/SolverCache/lmdb_opt_database.py" + qAddDependentFile fp + s <- runIO $ readFile fp + liftString s) + + +-- Helper functions for encoding/decoding hex strings -------------------------- + +-- | ... +sanitizeHex :: String -> String +sanitizeHex = filter (`elem` "0123456789abcdefABCDEF") + +-- | ... +encodeHex :: ByteString -> String +encodeHex bs = T.unpack $ Hex.encodeHex bs + +-- | ... +decodeHex :: String -> Either String ByteString +decodeHex s = case Hex.decodeHex $ T.pack s of + Just bs -> Right bs + Nothing -> Left $ "Hex decoding failure on:\n" ++ s + +-- | ... +encodeJSONHex :: ToJSON a => a -> String +encodeJSONHex = encodeHex . BS.toStrict . JSON.encode + +-- | ... +decodeJSONHex :: FromJSON a => String -> Either String a +decodeJSONHex = decodeHex >=> JSON.eitherDecodeStrict' + +-- | ... +decodePairs :: (String -> Either String a) -> + (String -> Either String b) -> + [String] -> Either String [(a, b)] +decodePairs f1 f2 = mapM $ \l -> case words l of + [s1, s2] -> (,) <$> f1 s1 <*> f2 s2 + _ -> Left $ "Expected two strings separated by a space, got: " ++ show l + +-- | ... +decodeMaybe :: (String -> Either String a) -> [String] -> + Either String (Maybe a) +decodeMaybe f [l] = Just <$> f l +decodeMaybe _ [] = Right Nothing +decodeMaybe _ ls = Left $ "Expected at most one line, got: " ++ show (unlines ls) + +-- | ... +decodeSingle :: (String -> Either String a) -> [String] -> Either String a +decodeSingle f [l] = f l +decodeSingle _ ls = Left $ "Expected one line, got: " ++ show (unlines ls) + +-- | ... +decodeEmpty :: a -> [String] -> Either String a +decodeEmpty b [] = Right b +decodeEmpty _ ls = Left $ "Expected nothing, got: " ++ show (unlines ls) + + +-- Helper functions for IO ----------------------------------------------------- + +-- | ... +hGetLinesUntil :: String -> Handle -> IO [String] +hGetLinesUntil stop_str h = do + l <- hGetLine h + if l == stop_str then return [] + else (l:) <$> hGetLinesUntil stop_str h + +-- | ... +hGetLinesTimeout :: Int -> Handle -> IO [String] +hGetLinesTimeout t_micros h = + timeout t_micros (hGetLine h) >>= \case + Just l -> hReady h >>= \case + True -> (l:) <$> hGetLinesTimeout t_micros h + False -> return [l] + Nothing -> return [] + + +-- Internal definition of and operations on LMDBOptDatabase -------------------- + +-- | ... +data LMDBOptDatabase a = + LMDBOptDatabase + { dbIOVar :: MVar (String, MVar [String]) + , dbIOTID :: ThreadId + , dbErr :: Handle + , dbShell :: ProcessHandle + } + +-- | ... +db_internal_timeout :: Int +db_internal_timeout = 100000 + +-- | ... +dbOpen :: IO (LMDBOptDatabase a) +dbOpen = withSystemTempFile "lmdb_opt_database.py" $ \fnm fh -> do + hPutStr fh lmdb_opt_database__py >> hClose fh + (mb_i, mb_o, mb_e, p) <- createProcess $ + (shell $ "python3 " ++ fnm ++ " shell") + {std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe} + mb_exit <- getProcessExitCode p + case (mb_i, mb_o, mb_e, mb_exit) of + -- If pipes and process were created successfully... + (Just i, Just o, Just e, Nothing) -> do + hSetBuffering i NoBuffering + -- First we create a thread which, when given a string and an MVar to + -- which to send output, will send the string to the Python process, + -- gather the output, and send it back over the MVar + i_var <- newEmptyMVar + io_tid <- forkIO $ forever $ do + (inp_str, outp_var) <- takeMVar i_var + hPutStrLn i inp_str + outp_ls <- hGetLinesUntil ">>><<<" o + putMVar outp_var outp_ls + -- Next we initialize the python process with a one-liner which sets the + -- REPL's command prompt to ">>><<<\n" (which the process above waits for) + let db = LMDBOptDatabase i_var io_tid e p + decodeInit l = if l == ">>> ()" then Right () + else Left $ "Unexpected: " ++ l + u <- dbExec (decodeSingle decodeInit) + "import sys; sys.ps1 = '>>><<<\\n'; sys.ps2 = ''; print(())" db + return $ u `deepseq` db + -- Otherwise, either a pipe or the process was not created, so we clean up + -- and fail + _ -> do + mapM_ hClose mb_i + terminateProcess p + mb_e_ls <- mapM (hGetLinesTimeout db_internal_timeout) mb_e + fail $ "Failed to spawn Python LMDB process" + ++ maybe ": Pipe not created" (unlines . (":":)) mb_e_ls + +-- | ... +dbExec :: ([String] -> Either String b) -> String -> LMDBOptDatabase a -> IO b +dbExec fn inp_str LMDBOptDatabase{..} = do + outp_var <- newEmptyMVar + putMVar dbIOVar (inp_str, outp_var) + mb_outp_ls <- timeout db_internal_timeout (takeMVar outp_var) + case mb_outp_ls of + Just outp_ls -> case outp_ls `deepseq` fn outp_ls of + Right b -> return b + Left read_err -> do + e_ls <- hGetLinesTimeout db_internal_timeout dbErr + fail $ if length e_ls == 0 then read_err + else unlines e_ls + Nothing -> do + e_ls <- hGetLinesTimeout db_internal_timeout dbErr + fail $ unlines e_ls + +-- | ... +-- dbClose :: LMDBOptDatabase a -> IO () +-- dbClose LMDBOptDatabase{..} = do +-- hClose dbIn +-- killThread dbOutTID +-- terminateProcess dbProc + + +-- Exposed interface of LMDBOptDatabase ---------------------------------------- + +-- | ... +new :: IO (LMDBOptDatabase a) +new = dbOpen >>= \db -> flip (dbExec (decodeEmpty db)) db + "db = LMDBOptDatabase()" + +-- | ... +setPath :: FilePath -> Int -> LMDBOptDatabase a -> IO () +setPath path map_size = dbExec (decodeEmpty ()) $ + "db.setPath(jsonHex('" ++ encodeJSONHex path ++ "'), " ++ + "jsonHex('" ++ encodeJSONHex map_size ++ "'))" + +-- | ... +getPath :: LMDBOptDatabase a -> IO (Maybe FilePath) +getPath = dbExec (decodeSingle decodeJSONHex) + "pJSONHex(db.getPath())" + +-- | ... +size :: LMDBOptDatabase a -> IO Int +size = dbExec (decodeSingle decodeJSONHex) + "pJSONHex(len(db))" + +-- | ... +lookup :: FromJSON a => ByteString -> LMDBOptDatabase a -> IO (Maybe a) +lookup k = dbExec (decodeMaybe decodeJSONHex) $ + "pHex(db.get(hex('" ++ encodeHex k ++ "')))" + +-- | ... +insert :: ToJSON a => ByteString -> a -> LMDBOptDatabase a -> IO () +insert k v = dbExec (decodeEmpty ()) $ + "db[hex('" ++ encodeHex k ++ "')] = hex('" ++ encodeJSONHex v ++ "')" + +-- | ... +delete :: ByteString -> LMDBOptDatabase a -> IO () +delete k = dbExec (decodeEmpty ()) $ + "del db[hex('" ++ encodeHex k ++ "')]" + +-- | ... +toList :: FromJSON a => LMDBOptDatabase a -> IO [(ByteString, a)] +toList = dbExec (decodePairs decodeHex decodeJSONHex) $ + "pHexPairs(db.items())" + +-- | ... +filterByHexPrefix :: FromJSON a => String -> LMDBOptDatabase a -> + IO [(ByteString, a)] +filterByHexPrefix s = dbExec (decodePairs decodeHex decodeJSONHex) $ + "pHexPairs(db.filterByKeyHexPrefix('" ++ sanitizeHex s ++ "'))" + +-- | ... +cleanByJSONObjValues :: (ToJSON b, FromJSON b) => b -> LMDBOptDatabase a -> + IO [(ByteString, [(b, b)])] +cleanByJSONObjValues ref = dbExec (decodePairs decodeHex decodeJSONHex) $ + "pHexJSONPairs(db.filterByMismatchedJSONObjValues(" ++ + "jsonHex('" ++ encodeJSONHex ref ++ "'), delete = True))" diff --git a/src/SAWScript/SolverCache/lmdb_opt_database.py b/src/SAWScript/SolverCache/lmdb_opt_database.py new file mode 100644 index 0000000000..167cdd788b --- /dev/null +++ b/src/SAWScript/SolverCache/lmdb_opt_database.py @@ -0,0 +1,202 @@ +from collections.abc import MutableMapping +import json +import sys + +def mismatchedJSONObjValues(obj1, obj2): + """A generator which, given two JSON-serializable objects, yields a sequence + of pairs of JSON-serailizable objects which indicate which object values + are mismatched between the two objects. This is done recursively on + sub-objects and keys which are present in one object but not the other + are ignored. + Example + ------- + >>> list(mismatchedJSONObjValues({'a': 4, 'b': {'c': 5}, 'd': 6 }, + {'a': 3, 'b': {'c': 2, 'z': 1}, 'd': 6})) + [ ({'a': 4}, {'a': 3}), ({'b': {'c': 5}}, {'b': {'c': 2}}) ] + """ + if isinstance(obj1, dict) and isinstance(obj2, dict): + for k in obj1.keys() & obj2.keys(): + for ms1, ms2 in mismatchedJSONObjValues(obj1[k], obj2[k]): + yield {k: ms1}, {k: ms2} + elif obj1 != obj2: + yield obj1, obj2 + +# ------------------------------------------------------------------------------ +# A Database optionally backed by LMDB +# ------------------------------------------------------------------------------ + +class LMDBOptDatabase(MutableMapping): + """A key-value database which is either implemented as a Python ``dict``, or + if the user has supplied a path (see ``setPath``/``getPath``), as an LMDB + database. Keys and values are represented as ``bytes``.""" + + def __init__(self): + """Create an empty database with no set path, implemented as a ``dict``""" + self._path = None + self._impl = {} + + def setPath(self, path, map_size = 10): + """Open an LMDB database at the given ``path`` with the given ``map_size``, + add all current entries in the database to the LMDB database, then + set the current implementation of this database to be the LMDB database. + Parameters + ---------- + path : str + Location of the directory to store the database + map_size : str, optional + Maximum size the database may grow to, in MiB. If at any point the + database grows larger, an exception will be raised and ``setPath`` must + be called again with a larger ``map_size``. Default is 10(MiB). + Raises + ------ + ImportError + If the Python ``lmdb`` library cannot be loaded + """ + import lmdb + # NOTE: If the LMDB database to load is the same as the currently loaded + # database, we must make sure to close the old ``lmdb.Environment`` first + if self._path == path: self._impl.close() + new_impl = lmdb.Environment(path, map_size = map_size * 1048576) + if self._path != path: + with new_impl.begin(write = True) as txn: + for k,v in self.items(): + txn.put(k, v, overwrite=True) + self._path = path + self._impl = new_impl + + def getPath(self): + """Return the location of the directory in which this database is stored, + if this database is implemented as an LMDB database. Return ``None`` + otherwise.""" + return self._path + + # Implementation of the methods of the MutableMapping base class + + def __len__(self): + if self._path is None: return len(self._impl) + with self._impl.begin() as txn: return txn.stat()['entries'] + + def __getitem__(self, k): + if self._path is None: return self._impl[k] + with self._impl.begin() as txn: + v = txn.get(k) + if v is None: raise KeyError + return v + + def __setitem__(self, k, v): + if self._path is None: self._impl[k] = v; return + with self._impl.begin(write = True) as txn: txn.put(k, v, overwrite=True) + + def __delitem__(self, k): + if self._path is None: del self._impl[k]; return + with self._impl.begin(write = True) as txn: txn.delete(k) + + def _iter(self, fn, keys, values): + if self._path is None: yield from fn(self._impl); return + with self._impl.begin() as txn: + with txn.cursor() as curs: + yield from curs.iternext(keys, values) + + def __iter__(self): yield from self._iter(iter, True, False) + def keys (self): yield from self._iter(dict.keys, True, False) + def values (self): yield from self._iter(dict.values, False, True ) + def items (self): yield from self._iter(dict.items, True, True ) + + # Methods for performing special filters on keys or values + + def filterByKeyHexPrefix(self, p_hex): + """A generator which yields all key-value pairs whose keys have the given + string as a prefix of their hex representation. If there is a key whose + hex representation exactly matches the given string, it and its value + are returned immediately, without whole database being searched.""" + try: + p = bytes.fromhex(p_hex) + yield (p, self[p]) + except Exception: + for k,v in self.items(): + if k.hex().startswith(p_hex): + yield (k, v) + + def filterByMismatchedJSONObjValues(self, obj_ref, delete = False): + """This generator finds all entries ``k, v`` in the database for which + ``v``, when deserialized as JSON, does not match ``obj_ref`` - in the + sense that there is some key in both ``v`` and ``obj_ref`` which has + differing values in each (see ``mismatchedJSONObjValues``). Each such + ``k``, paired with ``v``'s list of mismatches (the list returned by + ``mismatchedJSONObjValues``), is then yielded. If ``delete`` is ``True``, + each entry is deleted from the database before being yielded.""" + # NOTE: This function could just as well be defined as the body of + # ``onKVs`` below with ``self.items()`` for ``kvs`` and ``del self[k]`` + # for `deleteFn`. However, that would result in creating an additional + # LMDB transaction for every deleted element. Instead, we have a special + # case for LMDB-backed databases which uses only a single tranaction. + def onKVs(kvs, deleteFn): + for k,v in kvs: + try: + ms = list(mismatchedJSONObjValues(json.loads(v), obj_ref)) + if len(ms) > 0: + if delete: deleteFn(k) + yield (k, ms) + except Exception: + pass + if self._path is None: + yield from onKVs(self._impl.items(), self._impl.__delitem__); return + with self._impl.begin(write = True) as txn: + with txn.cursor() as curs: + yield from onKVs(curs, txn.delete) + +# ------------------------------------------------------------------------------ +# Helper functions for using LMDBOptDatabase objects with hex strings +# ------------------------------------------------------------------------------ + +def hex(hex_str): + """Convert a hex string to ``bytes``""" + return bytes.fromhex(hex_str) + +def jsonHex(obj_hex_str): + """Convert a hex string to ``bytes`` then deserialize a the result as JSON""" + return json.loads(hex(obj_hex_str)) + +def pHex(v): + """Print the given value as a hex string, if it is a ``bytes`` object""" + if isinstance(v, bytes): print(v.hex()) + +def pJSONHex(obj, pretty = False): + """Serialize the given object as JSON to get a ``bytes`` object then print + the result as a hex string""" + print(bytes(json.dumps(obj), 'utf-8').hex()) + +def pHexPair(v1, v2, pretty = False): + """Print the given pair of values as a hex strings, if both are ``bytes`` + objects. For the second element, if ``pretty`` is ``True``, deserialise it + as JSON and print that instead.""" + if isinstance(v1, bytes) and isinstance(v2, bytes): + print(v1.hex(), json.loads(v2) if pretty else v2.hex()) + +def pHexJSONPair(v1, obj2, pretty = False): + """Print the given pair of values as a hex strings, if the first element is a + ``bytes`` object. For the second element, if ``pretty`` is ``True``, print + it directly, otherwise serialize it as JSON to get a ``bytes`` object and + use that to print its hex string.""" + if isinstance(v1, bytes): + print(v1.hex(), obj2 if pretty else bytes(json.dumps(obj2), 'utf-8').hex()) + +def pHexPairs(vprs, pretty = False): + """Print each pair in a given iterable using ``pHexPair``""" + for v1, v2 in vprs: pHexPair(v1, v2, pretty) + +def pHexJSONPairs(vobjprs, pretty = False): + """Print each pair in a given iterable using ``pHexJSONPair``""" + for v1, obj2 in vobjprs: pHexJSONPair(v1, obj2, pretty) + +# ------------------------------------------------------------------------------ +# Interactive Shell Interface +# ------------------------------------------------------------------------------ + +def main(argv = None): + if len(argv) > 0 and argv[0] == "shell": + import code + code.InteractiveConsole(globals()).interact(banner = '') + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/src/SAWScript/SolverVersions.hs b/src/SAWScript/SolverVersions.hs index 2e0e87075d..2645803ecb 100644 --- a/src/SAWScript/SolverVersions.hs +++ b/src/SAWScript/SolverVersions.hs @@ -7,6 +7,7 @@ Stability : provisional -} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} module SAWScript.SolverVersions where @@ -14,14 +15,12 @@ import Control.Exception import System.Process (readProcessWithExitCode) import System.Exit (ExitCode(..)) -import qualified Data.Set as Set -import Data.Set (Set) +import qualified Data.Map as Map import qualified Data.SBV.Dynamic as SBV import SAWScript.SolverCache -import qualified GitRev as GitRev -import GitRev (aigHash, w4Hash, sbvVer) +import GitRev -- | Given an 'SBV.Solver' from @SBV@, attempt to query the solver for its -- version and return the result as a string. @@ -40,15 +39,25 @@ getSolverVersion s = Left (_ :: SomeException) -> return Nothing -- | Get the 'SolverBackendVersion' of a 'SolverBackend' -getSolverBackendVersion :: SolverBackend -> IO SolverBackendVersion -getSolverBackendVersion backend = SolverBackendVersion backend <$> - case backend of - Solver s -> getSolverVersion s - W4 _ -> return w4Hash - SBV -> return sbvVer - AIG -> return aigHash - RME -> return (Just GitRev.hash) - --- | Get the 'Set' of 'SolverBackendVersion's of a list of 'SolverBackend's -getSolverBackendVersions :: [SolverBackend] -> IO (Set SolverBackendVersion) -getSolverBackendVersions = fmap Set.fromList . mapM getSolverBackendVersion +getSolverBackendVersion :: SolverBackend -> IO (Maybe String) +getSolverBackendVersion backend = case backend of + What4 -> return what4Hash + SBV -> return sbvVersion + AIG -> return aigHash + RME -> return (Just hash) + -- We use individual cases for the remaining constructors to ensure that if + -- a new backend is added, a warning is generated for this pattern match + ABC -> getSolverVersion SBV.ABC + Boolector -> getSolverVersion SBV.Boolector + Bitwuzla -> getSolverVersion SBV.Bitwuzla + CVC4 -> getSolverVersion SBV.CVC4 + CVC5 -> getSolverVersion SBV.CVC5 + DReal -> getSolverVersion SBV.DReal + MathSAT -> getSolverVersion SBV.MathSAT + Yices -> getSolverVersion SBV.Yices + Z3 -> getSolverVersion SBV.Z3 + +-- | Get the set of 'SolverBackendVersions' of a list of 'SolverBackend's +getSolverBackendVersions :: [SolverBackend] -> IO SolverBackendVersions +getSolverBackendVersions backends = Map.fromList <$> + mapM (\backend -> (backend,) <$> getSolverBackendVersion backend) backends diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index a629511720..4680f939d7 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -49,6 +49,7 @@ import Control.Monad.Trans.Class (MonadTrans(lift)) import Data.IORef import Data.Foldable(foldrM) import Data.List ( intersperse ) +import Data.List.Extra ( dropEnd ) import qualified Data.Map as M import Data.Map ( Map ) import Data.Set ( Set ) @@ -486,7 +487,6 @@ data TopLevelRO = , roProxy :: AIGProxy , roInitWorkDir :: FilePath , roBasicSS :: SAWSimpset - , roSolverCache :: IORef (Maybe SolverCache) , roStackTrace :: [String] -- ^ SAWScript-internal backtrace for use -- when displaying exceptions and such @@ -517,6 +517,7 @@ data TopLevelRW = , rwProofs :: [Value] {- ^ Values, generated anywhere, that represent proofs. -} , rwPPOpts :: PPOpts , rwSharedContext :: SharedContext + , rwSolverCache :: Maybe SolverCache , rwTheoremDB :: TheoremDB -- , rwCrucibleLLVMCtx :: Crucible.LLVMContext @@ -599,7 +600,7 @@ io f = (TopLevel_ (liftIO f)) handleIO e | IOError.isUserError e = do pos <- getPosition - rethrow (SS.TopLevelException pos (init . drop 12 $ show e)) + rethrow (SS.TopLevelException pos (dropEnd 1 . drop 12 $ show e)) | otherwise = rethrow e handleX86Unsupported (X86Unsupported s) = @@ -688,8 +689,8 @@ getSharedContext = TopLevel_ (rwSharedContext <$> get) getJavaCodebase :: TopLevel JSS.Codebase getJavaCodebase = TopLevel_ (asks roJavaCodebase) -getSolverCache :: TopLevel (IORef (Maybe SolverCache)) -getSolverCache = TopLevel_ (asks roSolverCache) +getSolverCache :: TopLevel (Maybe SolverCache) +getSolverCache = TopLevel_ (rwSolverCache <$> get) getTheoremDB :: TopLevel TheoremDB getTheoremDB = TopLevel_ (rwTheoremDB <$> get) @@ -741,22 +742,17 @@ recordProof v = askSolverCache :: SolverCacheOp (Maybe a) -> TopLevel (Maybe a) askSolverCache f = do opts <- getOptions - ref <- getSolverCache - io $ readIORef ref >>= \case - Just cache -> do (ret, cache') <- f opts cache - atomicWriteIORef ref (Just cache') - return ret + getSolverCache >>= \case + Just cache -> io $ f opts cache Nothing -> return Nothing -- | Perform a stateful operation on the 'SolverCache', or do nothing if -- there is no 'SolverCache' -onSolverCache :: SolverCacheOp () -> TopLevel() +onSolverCache :: SolverCacheOp () -> TopLevel () onSolverCache f = do opts <- getOptions - ref <- getSolverCache - io $ readIORef ref >>= \case - Just cache -> do ((), cache') <- f opts cache - atomicWriteIORef ref (Just cache') + getSolverCache >>= \case + Just cache -> io $ f opts cache Nothing -> return () -- | Access the current state of Java Class translation From c8776230f07ea5505e17ba4f1687519f3b50b8b0 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 20 Jun 2023 12:18:25 -0400 Subject: [PATCH 26/51] quick clean up to solver caching code --- saw-core/src/Verifier/SAW/FiniteValue.hs | 1 - saw-remote-api/src/SAWServer.hs | 6 +++--- src/SAWScript/SolverCache.hs | 11 ----------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/saw-core/src/Verifier/SAW/FiniteValue.hs b/saw-core/src/Verifier/SAW/FiniteValue.hs index cbf2e86678..7a0c9c85dc 100644 --- a/saw-core/src/Verifier/SAW/FiniteValue.hs +++ b/saw-core/src/Verifier/SAW/FiniteValue.hs @@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-} -{-# LANGUAGE DeriveGeneric #-} {- | Module : Verifier.SAW.FiniteValue diff --git a/saw-remote-api/src/SAWServer.hs b/saw-remote-api/src/SAWServer.hs index 6f8f9267ee..f5a8f79731 100644 --- a/saw-remote-api/src/SAWServer.hs +++ b/saw-remote-api/src/SAWServer.hs @@ -27,7 +27,6 @@ import qualified Crypto.Hash as Hash --import qualified Crypto.Hash.Conduit as Hash import System.Directory (getCurrentDirectory) import System.IO.Silently (silence) -import Data.IORef import qualified Cryptol.Parser.AST as P import qualified Cryptol.TypeCheck.AST as Cryptol (Schema) @@ -63,6 +62,7 @@ import Verifier.SAW.CryptolEnv (initCryptolEnv, bindTypedTerm) import qualified Cryptol.Utils.Ident as Cryptol import Verifier.SAW.Cryptol.Monadify (defaultMonEnv) import SAWScript.Prover.MRSolver (emptyMREnv) +import SAWScript.SolverCache (emptySolverCache) import qualified Argo --import qualified CryptolServer (validateServerState, ServerState(..)) @@ -206,7 +206,7 @@ initialState readFileFn = halloc <- Crucible.newHandleAllocator jvmTrans <- CJ.mkInitialJVMContext halloc cwd <- getCurrentDirectory - cache <- newIORef Nothing + cache <- emptySolverCache db <- newTheoremDB let ro = TopLevelRO { roJavaCodebase = jcb @@ -220,7 +220,6 @@ initialState readFileFn = #endif , roInitWorkDir = cwd , roBasicSS = ss - , roSolverCache = cache , roStackTrace = [] , roSubshell = fail "SAW server does not support subshells." , roProofSubshell = fail "SAW server does not support subshells." @@ -236,6 +235,7 @@ initialState readFileFn = , rwMRSolverEnv = emptyMREnv , rwPPOpts = defaultPPOpts , rwTheoremDB = db + , rwSolverCache = Just cache , rwSharedContext = sc , rwJVMTrans = jvmTrans , rwPrimsAvail = mempty diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index f89e37ccc6..425604c0a1 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -80,7 +80,6 @@ import GHC.Generics (Generic) import Data.IORef (IORef, newIORef, modifyIORef, readIORef) import Data.Tuple.Extra (first, firstM, both) import Data.List (isPrefixOf, elemIndex, intercalate) -import Data.Hashable (Hashable(..)) import Data.Bits (shiftL, (.|.)) import Data.Maybe (fromMaybe) import Data.Functor ((<&>)) @@ -261,16 +260,6 @@ data SolverCacheKey = instance Eq SolverCacheKey where (SolverCacheKey _ _ bs1) == (SolverCacheKey _ _ bs2) = bs1 == bs2 --- | Truncate a 'SolverCacheKey' (i.e. a SHA256 hash) to an 'Int', used to give --- the type a fast 'Hashable' instance -solverCacheKeyInt :: SolverCacheKey -> Int -solverCacheKeyInt (SolverCacheKey _ _ bs) = - BS.foldl' (\a b -> a `shiftL` 8 .|. fromIntegral b) 0 (BS.take 8 bs) - -instance Hashable SolverCacheKey where - hash = solverCacheKeyInt - hashWithSalt s = hashWithSalt s . solverCacheKeyInt - instance Show SolverCacheKey where show (SolverCacheKey vs opts bs) = encodeHex (BS.take 8 bs) ++ if M.null vs && null opts then "" From d7ae96751d198450434a4dcf0d530a1100f51583 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 20 Jun 2023 12:43:29 -0400 Subject: [PATCH 27/51] replace solver caching CLI option with an env variable --- Setup.hs | 21 ++++++++++----------- saw-remote-api/src/SAWServer.hs | 13 ++++++++++--- saw-script.cabal | 1 - src/SAWScript/Interpreter.hs | 11 +++++++---- src/SAWScript/Options.hs | 7 ------- src/SAWScript/SolverCache.hs | 1 - 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Setup.hs b/Setup.hs index b518bc348c..f5a8837454 100644 --- a/Setup.hs +++ b/Setup.hs @@ -12,17 +12,6 @@ import System.Exit main = defaultMainWithHooks myHooks where myHooks = simpleUserHooks { buildHook = myBuild } -onfailure :: a -> SomeException -> IO a -onfailure on_fail _e = return on_fail - -withExe dir exe_str on_no_exe on_fail m = do - mb <- findExecutable exe_str - - case mb of - Just exe -> withCurrentDirectory dir (m exe) - `catch` onfailure on_fail - Nothing -> return on_no_exe - myBuild pd lbi uh flags = do let dir = autogenPackageModulesDir lbi createDirectoryIfMissing True dir @@ -60,3 +49,13 @@ myBuild pd lbi uh flags = do unless (null $ allBuildInfo pd) $ (buildHook simpleUserHooks) pd lbi uh flags +withExe :: FilePath -> String -> a -> a -> (FilePath -> IO a) -> IO a +withExe dir exe_str on_no_exe on_fail m = do + mb <- findExecutable exe_str + case mb of + Just exe -> withCurrentDirectory dir (m exe) + `catch` onfailure on_fail + Nothing -> return on_no_exe + +onfailure :: a -> SomeException -> IO a +onfailure on_fail _e = return on_fail diff --git a/saw-remote-api/src/SAWServer.hs b/saw-remote-api/src/SAWServer.hs index f5a8f79731..ae9078e08e 100644 --- a/saw-remote-api/src/SAWServer.hs +++ b/saw-remote-api/src/SAWServer.hs @@ -6,6 +6,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE ImplicitParams #-} {-# LANGUAGE KindSignatures #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} module SAWServer @@ -26,6 +27,7 @@ import qualified Data.Text as T import qualified Crypto.Hash as Hash --import qualified Crypto.Hash.Conduit as Hash import System.Directory (getCurrentDirectory) +import System.Environment (lookupEnv) import System.IO.Silently (silence) import qualified Cryptol.Parser.AST as P @@ -62,7 +64,7 @@ import Verifier.SAW.CryptolEnv (initCryptolEnv, bindTypedTerm) import qualified Cryptol.Utils.Ident as Cryptol import Verifier.SAW.Cryptol.Monadify (defaultMonEnv) import SAWScript.Prover.MRSolver (emptyMREnv) -import SAWScript.SolverCache (emptySolverCache) +import SAWScript.SolverCache (emptySolverCache, setSolverCachePath) import qualified Argo --import qualified CryptolServer (validateServerState, ServerState(..)) @@ -206,7 +208,12 @@ initialState readFileFn = halloc <- Crucible.newHandleAllocator jvmTrans <- CJ.mkInitialJVMContext halloc cwd <- getCurrentDirectory - cache <- emptySolverCache + mb_cache <- lookupEnv "SAW_SOLVER_CACHE_PATH" >>= \case + Just p | length p > 0 -> do + cache <- emptySolverCache + setSolverCachePath p opts cache + return $ Just cache + _ -> return Nothing db <- newTheoremDB let ro = TopLevelRO { roJavaCodebase = jcb @@ -235,7 +242,7 @@ initialState readFileFn = , rwMRSolverEnv = emptyMREnv , rwPPOpts = defaultPPOpts , rwTheoremDB = db - , rwSolverCache = Just cache + , rwSolverCache = mb_cache , rwSharedContext = sc , rwJVMTrans = jvmTrans , rwPrimsAvail = mempty diff --git a/saw-script.cabal b/saw-script.cabal index fe64601aae..e11ffde4fe 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -50,7 +50,6 @@ library , filepath , flexdis86 , free - , hashable , haskeline , heapster-saw , hex-text diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index ce1313105a..871bdc9bf3 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -50,6 +50,7 @@ import Data.Set ( Set ) import qualified Data.Text as Text import System.Directory (getCurrentDirectory, setCurrentDirectory, canonicalizePath) import System.FilePath (takeDirectory) +import System.Environment (lookupEnv) import System.Process (readProcess) import qualified SAWScript.AST as SS @@ -455,10 +456,12 @@ buildTopLevelEnv proxy opts = ss <- basic_ss sc jcb <- JCB.loadCodebase (jarList opts) (classPath opts) (javaBinDirs opts) currDir <- getCurrentDirectory - mb_cache <- forM (solverCache opts) $ \p -> do - cache <- emptySolverCache - setSolverCachePath p opts cache - return cache + mb_cache <- lookupEnv "SAW_SOLVER_CACHE_PATH" >>= \case + Just p | length p > 0 -> do + cache <- emptySolverCache + setSolverCachePath p opts cache + return $ Just cache + _ -> return Nothing thmDB <- newTheoremDB Crucible.withHandleAllocator $ \halloc -> do let ro0 = TopLevelRO diff --git a/src/SAWScript/Options.hs b/src/SAWScript/Options.hs index 6c190a977e..ab22d815e0 100644 --- a/src/SAWScript/Options.hs +++ b/src/SAWScript/Options.hs @@ -42,7 +42,6 @@ data Options = Options , printOutFn :: Verbosity -> String -> IO () , summaryFile :: Maybe FilePath , summaryFormat :: SummaryFormat - , solverCache :: Maybe FilePath } deriving (Show) -- | Verbosity is currently a linear setting (vs a mask or tree). Any given @@ -80,7 +79,6 @@ defaultOptions , useColor = True , summaryFile = Nothing , summaryFormat = Pretty - , solverCache = Nothing } printOutWith :: Verbosity -> Verbosity -> String -> IO () @@ -174,11 +172,6 @@ options = ) "either 'json' or 'pretty'") "Specify the format in which the verification summary should be written in ('json' or 'pretty'; defaults to 'json')" - , Option [] ["cache"] - (ReqArg - (\path opts -> return opts { solverCache = Just path }) - "path") - "Enable solver result caching and set the path the cache should be loaded from and saved to" ] -- Try to read verbosity as either a string or number and default to 'Debug'. diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 425604c0a1..8ac19d91b2 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -80,7 +80,6 @@ import GHC.Generics (Generic) import Data.IORef (IORef, newIORef, modifyIORef, readIORef) import Data.Tuple.Extra (first, firstM, both) import Data.List (isPrefixOf, elemIndex, intercalate) -import Data.Bits (shiftL, (.|.)) import Data.Maybe (fromMaybe) import Data.Functor ((<&>)) From 9eae67159cd8234232c19e82f8dec0ff8735e075 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 20 Jun 2023 13:53:37 -0400 Subject: [PATCH 28/51] add docs to solver caching code --- src/SAWScript/Interpreter.hs | 2 +- src/SAWScript/SolverCache.hs | 73 +++++----- src/SAWScript/SolverCache/LMDBOptDatabase.hs | 144 +++++++++++-------- 3 files changed, 123 insertions(+), 96 deletions(-) diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 871bdc9bf3..f109a17d1c 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -40,7 +40,7 @@ import Data.Traversable hiding ( mapM ) #endif import Control.Applicative ( (<|>) ) import qualified Control.Exception as X -import Control.Monad (unless, (>=>), when, forM) +import Control.Monad (unless, (>=>), when) import Control.Monad.IO.Class (liftIO) import qualified Data.ByteString as BS import qualified Data.Map as Map diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 8ac19d91b2..3ff16cbb35 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -12,20 +12,13 @@ on disk. The interface, as used in 'applyProverToGoal', works as follows: along with any relevant 'SolverBackendVersion's (obtained using 'getSolverBackendVersions' from @SAWScript.SolverVersions@), is then hashed using SHA256 ('mkSolverCacheKey'). -2. The 'SolverCache' contains a map from these hashes to previously obtained - results ('solverCacheMap'). If the hash corresponding to the 'SATQuery' and - 'SolverBackendVersion's can be found in the map, then the corresponding - result is used. -3. Otherwise, if the 'SolverCache' was given a path to a directory - ('solverCachePath') and a file whose name is the hash can be found in that - directory, the file's contents are 'read' and used as the result. -4. Otherwise, the 'SATQuery' is dispatched to the requested backend and a - result is obtained. Then: - - This result is added to the 'SolverCache' map using the hash of the - 'SATQuery' and 'SolverBackendVersion's as the key. - - If the 'SolverCache' was given a path to a directory ('solverCachePath'), - then a file whose name is the hash and whose contents are 'show' of the - result is added to the directory. +2. The 'SolverCache' contains an 'LMDBOptDatabase' mapping these hashes to + previously obtained results ('solverCacheDB'). If the hash corresponding to + the 'SATQuery' and 'SolverBackendVersion's can be found in the database + ('lookupInSolverCache'), then the corresponding result is used. +3. Otherwise, the 'SATQuery' is dispatched to the requested backend and a + result is obtained. This result is then added to the database using the + aforementioned hash as the key ('insertInSolverCache'). A interesting detail is how results are represented. For all of the backends which use 'applyProverToGoal', the type of a result is: @@ -117,7 +110,9 @@ import SAWScript.Proof deriving instance Generic FirstOrderType deriving instance Generic FirstOrderValue --- | ... +-- | The options for JSON-serializing 'FirstOrderType's and 'FirstOrderValue's: +-- remove the @FOV@/@FOT@ prefixes and encode the different constructors as +-- two-element arrays. firstOrderJSONOptions :: JSON.Options firstOrderJSONOptions = JSON.defaultOptions { JSON.sumEncoding = JSON.TwoElemArray @@ -137,7 +132,8 @@ instance ToJSON FirstOrderValue where toJSON = JSON.genericToJSON firstOrderJSONOptions toEncoding = JSON.genericToEncoding firstOrderJSONOptions --- | ... +-- | Run the given IO action, but if the given 'timeout' (in ms) is reached +-- or the action encounters any 'SomeException', 'Left' is returned tryWithTimeout :: Int -> IO a -> IO (Either String a) tryWithTimeout t_ms m = try (timeout (t_ms * 1000) m) <&> \case Right (Just a) -> Right a @@ -149,9 +145,7 @@ tryWithTimeout t_ms m = try (timeout (t_ms * 1000) m) <&> \case -- | A datatype representing one of the solver backends available in SAW. Note -- that for 'SBV' and 'W4', multiple backends will be used (e.g. 'SBV' with --- @Solver Z3@ or @W4 W4_AIGER@ with 'AIG' and @Solver ABC@), though not all --- comtinations of backends are valid (e.g. @W4 W4_SMTLib2@ with anything but --- @Solver Z3@) +-- 'Z3' or 'W4' with 'AIG' and 'ABC'). data SolverBackend = What4 | SBV | AIG @@ -178,11 +172,11 @@ instance FromJSONKey SolverBackend where instance ToJSONKey SolverBackend where toJSONKey = JSON.genericToJSONKey JSON.defaultJSONKeyOptions --- | ... +-- | The list of all available 'SolverBackend's allBackends :: [SolverBackend] allBackends = [minBound..] --- | ... +-- | Given an 'SBV.SMTConfig', return the list of corresponding 'SolverBackend's sbvBackends :: SBV.SMTConfig -> [SolverBackend] sbvBackends conf = [SBV, cvtSolver $ SBV.name $ SBV.solver conf] where cvtSolver SBV.ABC = ABC @@ -195,11 +189,13 @@ sbvBackends conf = [SBV, cvtSolver $ SBV.name $ SBV.solver conf] cvtSolver SBV.Yices = Yices cvtSolver SBV.Z3 = Z3 --- | A datatype representing one of the ways the what4 backend can be used in --- SAW - i.e. directly ('W4_Base'), with a tactic ('W4_Tactic'), by converting --- to SMTLib2 then calling ABC ('W4_SMTLib2'), by converting to Verilog then --- calling ABC ('W4_Verilog'), or by converting to AIGER then calling ABC --- ('W4_AIGER') +-- | A datatype representing an option to one of the 'SolverBackend's. +-- Currently, there are only the following options for using 'What4': with a +-- tactic ('W4_Tactic'), by converting to SMTLib2 then calling ABC +-- ('W4_SMTLib2'), by converting to Verilog then calling ABC ('W4_Verilog'), +-- or by converting to AIGER then calling ABC ('W4_AIGER'). Note that certain +-- options may imply that additional 'SolverBackend's were used - see +-- 'optionBackends'. data SolverBackendOption = W4_Tactic String | W4_SMTLib2 | W4_Verilog @@ -212,16 +208,17 @@ instance ToJSON SolverBackendOption where toJSON = JSON.genericToJSON JSON.defaultOptions toEncoding = JSON.genericToEncoding JSON.defaultOptions --- | ... +-- | Given a 'SolverBackendOption', return the list of additional +-- 'SolverBackend's that are used optionBackends :: SolverBackendOption -> [SolverBackend] optionBackends W4_AIGER = [AIG] optionBackends _ = [] --- | ... --- A 'SolverBackend' and its version 'String', if one could be obtained +-- | A map from 'SolverBackend's to their version 'String's, if one could be +-- obtained type SolverBackendVersions = Map SolverBackend (Maybe String) --- | ... +-- | Pretty-print a 'SolverBackend' with its version 'String' showSolverBackendVersion :: SolverBackend -> Maybe String -> [String] -> String showSolverBackendVersion backend (Just v_str) opt_words = if show backend `isPrefixOf` v_str @@ -230,7 +227,8 @@ showSolverBackendVersion backend (Just v_str) opt_words = showSolverBackendVersion backend Nothing opt_words = showSolverBackendVersion backend (Just "[unknown version]") opt_words --- | ... +-- | Pretty-print a set of 'SolverBackendVersions' and 'SolverBackendOption's +-- with the given 'String' separator showBackendVersionsWithOptions :: String -> SolverBackendVersions -> [SolverBackendOption] -> String showBackendVersionsWithOptions sep vs opts = @@ -352,7 +350,7 @@ data SolverCache = , solverCacheTimeout :: Int } --- | ... +-- | A statistic to track in 'solverCacheStats' data SolverCacheStat = Lookups | FailedLookups | Inserts | FailedInserts deriving (Eq, Ord, Bounded, Enum) @@ -363,7 +361,7 @@ emptySolverCache = do stats <- newIORef $ M.fromList ((,0) <$> [minBound..]) return $ SolverCache db stats 1000 --- | A stateful operation on a 'SolverCache', returning a value of the given type +-- | An operation on a 'SolverCache', returning a value of the given type type SolverCacheOp a = Options -> SolverCache -> IO a -- | Lookup a 'SolverCacheKey' in the solver result cache @@ -396,8 +394,8 @@ insertInSolverCache k v opts SolverCache{..} = printOutLn opts Warn ("Solver cache insert failed:\n" ++ err) modifyIORef solverCacheStats $ M.adjust (+1) FailedInserts --- | Set the 'FilePath' of the solver result cache, erroring if it is already --- set, and save all results cached so far +-- | Set the 'FilePath' of the solver result cache and save all results cached +-- so far setSolverCachePath :: FilePath -> SolverCacheOp () setSolverCachePath path opts SolverCache{..} = do pathAbs <- makeAbsolute path @@ -413,7 +411,8 @@ setSolverCachePath path opts SolverCache{..} = do let (s0, s1) = (show sz, if sz == 1 then "" else "s") printOutLn opts Info ("Saved " ++ s0 ++ " cached result" ++ s1 ++ " to disk") --- | ... +-- | Print all entries in the solver result cache whose SHA256 hash keys start +-- with the given string printSolverCacheByHex :: String -> SolverCacheOp () printSolverCacheByHex hex_prefix opts SolverCache{..} = do kvs <- LMDBOpt.filterByHexPrefix hex_prefix solverCacheDB @@ -427,7 +426,7 @@ printSolverCacheByHex hex_prefix opts SolverCache{..} = do printOutLn opts Info $ "- Versions: " ++ vs_str ++ "\n" -- | Remove all entries in the solver result cache which have version(s) that --- do not match the current version(s). +-- do not match the current version(s) cleanSolverCache :: SolverBackendVersions -> SolverCacheOp () cleanSolverCache curr_base_vs opts SolverCache{..} = do let curr_base_vs_obj = M.fromList [("vs", curr_base_vs)] diff --git a/src/SAWScript/SolverCache/LMDBOptDatabase.hs b/src/SAWScript/SolverCache/LMDBOptDatabase.hs index 295fa787ed..d92fd2ed33 100644 --- a/src/SAWScript/SolverCache/LMDBOptDatabase.hs +++ b/src/SAWScript/SolverCache/LMDBOptDatabase.hs @@ -5,7 +5,17 @@ License : BSD3 Maintainer : m-yac Stability : provisional -... +This module implements a 'HashTable'-inspired interface for key-value databases +optionally backed by an LMDB database on disk. + +Instead of using something like the Haskell library @lmdb-simple@, which +requires that the user have the LMDB dynamically-linked C libraries installed +even if an LMDB database is never used at runtime, this module connects to a +Python process on database creation and uses the Python LMDB bindings only when +the user provides a file path at which to open an LMDB database. This means that +the user only needs to have Python installed if they create an 'LMDBOptDatabase' +object at runtime, and only needs to have the Python LMDB bindings, which are +self-contained and can be installed via @pip@, if they provide a file path. -} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-} @@ -36,7 +46,6 @@ import System.IO import Control.Concurrent (forkIO, ThreadId) import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar) -import Control.DeepSeq (deepseq) import Control.Monad ((>=>), forever) import Language.Haskell.TH (runIO) @@ -46,12 +55,12 @@ import qualified Data.Text as T import qualified Text.Hex as Hex import Data.ByteString (ByteString) -import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as LBS import Data.Aeson (FromJSON, ToJSON) import qualified Data.Aeson as JSON --- | ... +-- | The contents of @lmdb_opt_database.py@ lmdb_opt_database__py :: String lmdb_opt_database__py = $(do let fp = "src/SAWScript/SolverCache/lmdb_opt_database.py" @@ -62,29 +71,32 @@ lmdb_opt_database__py = $(do -- Helper functions for encoding/decoding hex strings -------------------------- --- | ... +-- | Remove all characters except hex digits (i.e. `[0-9a-fA-F]`) from the +-- given string sanitizeHex :: String -> String sanitizeHex = filter (`elem` "0123456789abcdefABCDEF") --- | ... +-- | Encode a 'ByteString' as a hex string encodeHex :: ByteString -> String encodeHex bs = T.unpack $ Hex.encodeHex bs --- | ... +-- | Decode a hex string as a 'ByteString' decodeHex :: String -> Either String ByteString decodeHex s = case Hex.decodeHex $ T.pack s of Just bs -> Right bs Nothing -> Left $ "Hex decoding failure on:\n" ++ s --- | ... +-- | Encode an element of a 'ToJSON' type as a hex string encodeJSONHex :: ToJSON a => a -> String -encodeJSONHex = encodeHex . BS.toStrict . JSON.encode +encodeJSONHex = encodeHex . LBS.toStrict . JSON.encode --- | ... +-- | Decode a hex string as an element of a 'FromJSON' type decodeJSONHex :: FromJSON a => String -> Either String a decodeJSONHex = decodeHex >=> JSON.eitherDecodeStrict' --- | ... +-- | Given a list of strings, each of which containing exactly one space +-- character, apply the two given decoding functions to each respective part of +-- each string and return the result as a list of pairs decodePairs :: (String -> Either String a) -> (String -> Either String b) -> [String] -> Either String [(a, b)] @@ -92,19 +104,20 @@ decodePairs f1 f2 = mapM $ \l -> case words l of [s1, s2] -> (,) <$> f1 s1 <*> f2 s2 _ -> Left $ "Expected two strings separated by a space, got: " ++ show l --- | ... +-- | Given a list of at most one string, apply the given decoding function to +-- the string if there is one, otherwise return 'Nothing' decodeMaybe :: (String -> Either String a) -> [String] -> Either String (Maybe a) decodeMaybe f [l] = Just <$> f l decodeMaybe _ [] = Right Nothing decodeMaybe _ ls = Left $ "Expected at most one line, got: " ++ show (unlines ls) --- | ... +-- | Given a list of exactly one string, apply the given decoding function to it decodeSingle :: (String -> Either String a) -> [String] -> Either String a decodeSingle f [l] = f l decodeSingle _ ls = Left $ "Expected one line, got: " ++ show (unlines ls) --- | ... +-- | Given an empty list, return the given element decodeEmpty :: a -> [String] -> Either String a decodeEmpty b [] = Right b decodeEmpty _ ls = Left $ "Expected nothing, got: " ++ show (unlines ls) @@ -112,14 +125,16 @@ decodeEmpty _ ls = Left $ "Expected nothing, got: " ++ show (unlines ls) -- Helper functions for IO ----------------------------------------------------- --- | ... +-- | Apply 'hGetLine' to the given 'Handle' until the given 'String' is +-- received, then return all the prior collected results of 'hGetLine' hGetLinesUntil :: String -> Handle -> IO [String] hGetLinesUntil stop_str h = do l <- hGetLine h if l == stop_str then return [] else (l:) <$> hGetLinesUntil stop_str h --- | ... +-- | Apply 'hGetLine' with a 'timeout' of the given number of microseconds +-- repeatedly until either 'hReady' returns 'False' or the operation times out hGetLinesTimeout :: Int -> Handle -> IO [String] hGetLinesTimeout t_micros h = timeout t_micros (hGetLine h) >>= \case @@ -131,7 +146,12 @@ hGetLinesTimeout t_micros h = -- Internal definition of and operations on LMDBOptDatabase -------------------- --- | ... +-- | A key-value database which is either stored in memory, or if the user has +-- supplied a path (see 'setPath'/'getPath'), as an LMDB database. Keys are +-- represented as 'ByteString's, but values can be of any JSON-serialisable type +-- (see 'ToJSON'/'FromJSON'). This is implemented by connecting to a @python3@ +-- process, so Python must be installed to use this type. To use the LMDB +-- backend, the Python LMDB bindings must also be installed. data LMDBOptDatabase a = LMDBOptDatabase { dbIOVar :: MVar (String, MVar [String]) @@ -140,123 +160,131 @@ data LMDBOptDatabase a = , dbShell :: ProcessHandle } --- | ... -db_internal_timeout :: Int -db_internal_timeout = 100000 +-- | The timeout to use when attempting to get lines from @stderr@ +dbInternalTimeout :: Int +dbInternalTimeout = 100000 --- | ... +-- | Open a new @LMDBOptDatabase@ by connecting to a @python3@ process dbOpen :: IO (LMDBOptDatabase a) dbOpen = withSystemTempFile "lmdb_opt_database.py" $ \fnm fh -> do + -- First, start a python3 process with a temp file containing the contents of + -- lmdb_opt_database.py and the "shell" option for lmdb_opt_database.py + -- TODO: Actually use pip to install this file so we don't have to use a temp + -- file every time we open a database hPutStr fh lmdb_opt_database__py >> hClose fh (mb_i, mb_o, mb_e, p) <- createProcess $ (shell $ "python3 " ++ fnm ++ " shell") {std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe} mb_exit <- getProcessExitCode p case (mb_i, mb_o, mb_e, mb_exit) of - -- If pipes and process were created successfully... + -- If the process was created successfully... (Just i, Just o, Just e, Nothing) -> do hSetBuffering i NoBuffering -- First we create a thread which, when given a string and an MVar to -- which to send output, will send the string to the Python process, - -- gather the output, and send it back over the MVar + -- gather the output, and send it back over the MVar. + -- We make this thread to ensure each new command waits for any currently + -- executing commands to finish before trying to send input to and/or + -- read output from the Python process. i_var <- newEmptyMVar io_tid <- forkIO $ forever $ do (inp_str, outp_var) <- takeMVar i_var hPutStrLn i inp_str outp_ls <- hGetLinesUntil ">>><<<" o putMVar outp_var outp_ls - -- Next we initialize the python process with a one-liner which sets the + -- Finally we initialize the python process with a line which sets the -- REPL's command prompt to ">>><<<\n" (which the process above waits for) let db = LMDBOptDatabase i_var io_tid e p decodeInit l = if l == ">>> ()" then Right () else Left $ "Unexpected: " ++ l u <- dbExec (decodeSingle decodeInit) "import sys; sys.ps1 = '>>><<<\\n'; sys.ps2 = ''; print(())" db - return $ u `deepseq` db - -- Otherwise, either a pipe or the process was not created, so we clean up - -- and fail + return $ u `seq` db + -- Otherwise, either the process or one of its pipes failed to be created, + -- so we clean up anything that was created and fail _ -> do mapM_ hClose mb_i terminateProcess p - mb_e_ls <- mapM (hGetLinesTimeout db_internal_timeout) mb_e + mb_e_ls <- mapM (hGetLinesTimeout dbInternalTimeout) mb_e fail $ "Failed to spawn Python LMDB process" ++ maybe ": Pipe not created" (unlines . (":":)) mb_e_ls --- | ... +-- | Send the given 'String' to the @python3@ process and decode the resulting +-- lines (if any) using the given function dbExec :: ([String] -> Either String b) -> String -> LMDBOptDatabase a -> IO b -dbExec fn inp_str LMDBOptDatabase{..} = do +dbExec decodeFn inp_str LMDBOptDatabase{..} = do outp_var <- newEmptyMVar putMVar dbIOVar (inp_str, outp_var) - mb_outp_ls <- timeout db_internal_timeout (takeMVar outp_var) - case mb_outp_ls of - Just outp_ls -> case outp_ls `deepseq` fn outp_ls of - Right b -> return b - Left read_err -> do - e_ls <- hGetLinesTimeout db_internal_timeout dbErr - fail $ if length e_ls == 0 then read_err - else unlines e_ls - Nothing -> do - e_ls <- hGetLinesTimeout db_internal_timeout dbErr - fail $ unlines e_ls - --- | ... --- dbClose :: LMDBOptDatabase a -> IO () --- dbClose LMDBOptDatabase{..} = do --- hClose dbIn --- killThread dbOutTID --- terminateProcess dbProc + outp_ls <- takeMVar outp_var + case length outp_ls `seq` decodeFn outp_ls of + Right b -> return b + Left read_err -> do + e_ls <- hGetLinesTimeout dbInternalTimeout dbErr + fail $ if length e_ls == 0 then "Decoding Error:\n" ++ read_err + else unlines $ "Python error:" : e_ls -- Exposed interface of LMDBOptDatabase ---------------------------------------- --- | ... +-- | Create a new, empty, 'LMDBOptDatabase'. This will fail if the @python3@ +-- executable cannot be found. new :: IO (LMDBOptDatabase a) new = dbOpen >>= \db -> flip (dbExec (decodeEmpty db)) db "db = LMDBOptDatabase()" --- | ... +-- | Open an LMDB database at the given 'FilePath' with the given maximum size +-- (in MiB), add all current entries in the database to the LMDB database, then +-- set the current implementation of this database to be the LMDB database. This +-- will fail if the Python LMDB bindings cannot be loaded. setPath :: FilePath -> Int -> LMDBOptDatabase a -> IO () setPath path map_size = dbExec (decodeEmpty ()) $ "db.setPath(jsonHex('" ++ encodeJSONHex path ++ "'), " ++ "jsonHex('" ++ encodeJSONHex map_size ++ "'))" --- | ... +-- | Return the location of the directory in which this database is stored, if +-- this database is implemented as an LMDB database getPath :: LMDBOptDatabase a -> IO (Maybe FilePath) getPath = dbExec (decodeSingle decodeJSONHex) "pJSONHex(db.getPath())" --- | ... +-- | Return the number of entries in the database size :: LMDBOptDatabase a -> IO Int size = dbExec (decodeSingle decodeJSONHex) "pJSONHex(len(db))" --- | ... +-- | Return the JSON-serializable value associated to the given 'ByteString' +-- key in the database, if one exists lookup :: FromJSON a => ByteString -> LMDBOptDatabase a -> IO (Maybe a) lookup k = dbExec (decodeMaybe decodeJSONHex) $ "pHex(db.get(hex('" ++ encodeHex k ++ "')))" --- | ... +-- | Insert a 'ByteString' key / JSON-serializable value entry into the database insert :: ToJSON a => ByteString -> a -> LMDBOptDatabase a -> IO () insert k v = dbExec (decodeEmpty ()) $ "db[hex('" ++ encodeHex k ++ "')] = hex('" ++ encodeJSONHex v ++ "')" --- | ... +-- | Delete the entry associated to the given 'ByteString' key in the database delete :: ByteString -> LMDBOptDatabase a -> IO () delete k = dbExec (decodeEmpty ()) $ "del db[hex('" ++ encodeHex k ++ "')]" --- | ... +-- | Return the list of all entries in the database toList :: FromJSON a => LMDBOptDatabase a -> IO [(ByteString, a)] toList = dbExec (decodePairs decodeHex decodeJSONHex) $ "pHexPairs(db.items())" --- | ... +-- | Return all entries in the database whose keys have the given string as a +-- prefix of their hex representation filterByHexPrefix :: FromJSON a => String -> LMDBOptDatabase a -> IO [(ByteString, a)] filterByHexPrefix s = dbExec (decodePairs decodeHex decodeJSONHex) $ "pHexPairs(db.filterByKeyHexPrefix('" ++ sanitizeHex s ++ "'))" --- | ... +-- | Delete all entries @k, v@ in the database for which @v@, when converted to +-- JSON, does not match the given value @ref@, when converted to JSON - in the +-- sense that there is some key in both @v@ and @ref@ which has differing values +-- in each (see @mismatchedJSONObjValues@ in @lmdb_opt_database.py@). Each such +-- @k@, paired with @v@'s list of mismatches, is returned. cleanByJSONObjValues :: (ToJSON b, FromJSON b) => b -> LMDBOptDatabase a -> IO [(ByteString, [(b, b)])] cleanByJSONObjValues ref = dbExec (decodePairs decodeHex decodeJSONHex) $ From c36cb0d5f7e8747e9d067c58710106b9c6265a7d Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 21 Jun 2023 12:47:48 -0400 Subject: [PATCH 29/51] add test for solver result caching --- intTests/test_solver_cache/test.saw | 41 +++++++++++++++++++++++++++++ intTests/test_solver_cache/test.sh | 1 + saw-remote-api/src/SAWServer.hs | 2 +- src/SAWScript/Builtins.hs | 2 +- src/SAWScript/Interpreter.hs | 14 ++++++---- src/SAWScript/SolverCache.hs | 36 +++++++++++++++++++------ src/SAWScript/Value.hs | 22 ++++++---------- 7 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 intTests/test_solver_cache/test.saw create mode 100644 intTests/test_solver_cache/test.sh diff --git a/intTests/test_solver_cache/test.saw b/intTests/test_solver_cache/test.saw new file mode 100644 index 0000000000..e0e9ecaa1f --- /dev/null +++ b/intTests/test_solver_cache/test.saw @@ -0,0 +1,41 @@ +// Test for solver result caching + +enable_experimental; +enable_solver_cache; + +// The solver cache starts out empty +test_solver_cache_stats [0, 0, 0, 0, 0]; + +// The cache should now have one entry and one insertion +prove_print z3 {{ \(x:[64]) -> x == x }}; +test_solver_cache_stats [1, 0, 0, 1, 0]; + +// Testing that cached results do not depend on variable names - thus, the +// cache should now have one more usage, but not a new entry or insertion +prove_print z3 {{ \(new_name:[64]) -> new_name == new_name }}; +test_solver_cache_stats [1, 1, 0, 1, 0]; + +// Testing that cached results depend on the backend used - thus, the cache +// should now have one more entry and one more insertion, but not a new usage +prove_print (w4_unint_z3 []) {{ \(x:[64]) -> x == x }}; +test_solver_cache_stats [2, 1, 0, 2, 0]; + +// Testing that cached results depend on the options passed to the given +// backend - thus, the cache should now have one more entry and one more +// insertion, but not a new usage +prove_print (w4_unint_z3_using "qfnia" []) {{ \(x:[64]) -> x == x }}; +test_solver_cache_stats [3, 1, 0, 3, 0]; + +// Same as the above but for sat results + +fails (prove_print z3 {{ \(x:[64])(y:[64]) -> x == y }}); +test_solver_cache_stats [4, 1, 0, 4, 0]; + +fails (prove_print z3 {{ \(new_name_1:[64])(new_name_2:[64]) -> new_name_1 == new_name_2 }}); +test_solver_cache_stats [4, 2, 0, 4, 0]; + +fails (prove_print w4 {{ \(x:[64])(y:[64]) -> x == y }}); +test_solver_cache_stats [5, 2, 0, 5, 0]; + +fails (prove_print (w4_unint_z3_using "qfnia" []) {{ \(x:[64])(y:[64]) -> x == y }}); +test_solver_cache_stats [6, 2, 0, 6, 0]; diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh new file mode 100644 index 0000000000..0b864017cd --- /dev/null +++ b/intTests/test_solver_cache/test.sh @@ -0,0 +1 @@ +$SAW test.saw diff --git a/saw-remote-api/src/SAWServer.hs b/saw-remote-api/src/SAWServer.hs index ae9078e08e..f2ad295a2e 100644 --- a/saw-remote-api/src/SAWServer.hs +++ b/saw-remote-api/src/SAWServer.hs @@ -211,7 +211,7 @@ initialState readFileFn = mb_cache <- lookupEnv "SAW_SOLVER_CACHE_PATH" >>= \case Just p | length p > 0 -> do cache <- emptySolverCache - setSolverCachePath p opts cache + snd (setSolverCachePath p) opts cache return $ Just cache _ -> return Nothing db <- newTheoremDB diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index 9c610deffb..ac0a527b6a 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -972,7 +972,7 @@ applyProverToGoal backends opts f unintSet g = do vs <- io $ getSolverBackendVersions (backends ++ opt_backends) satq <- io $ sequentToSATQuery sc unintSet (goalSequent g) k <- io $ mkSolverCacheKey sc vs opts satq - (mb, solver_name) <- SV.askSolverCache (lookupInSolverCache k) >>= \case + (mb, solver_name) <- SV.onSolverCache (lookupInSolverCache k) >>= \case -- Use a cached result if one exists (and it's valid w.r.t our query) Just v -> return $ fromSolverCacheValue satq v -- Otherwise try to cache the result of the call diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index f109a17d1c..04705e7a04 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -459,7 +459,7 @@ buildTopLevelEnv proxy opts = mb_cache <- lookupEnv "SAW_SOLVER_CACHE_PATH" >>= \case Just p | length p > 0 -> do cache <- emptySolverCache - setSolverCachePath p opts cache + snd (setSolverCachePath p) opts cache return $ Just cache _ -> return Nothing thmDB <- newTheoremDB @@ -660,9 +660,6 @@ set_solver_cache_path :: FilePath -> TopLevel () set_solver_cache_path path = enable_solver_cache >> onSolverCache (setSolverCachePath path) -print_solver_cache :: String -> TopLevel () -print_solver_cache = onSolverCache . printSolverCacheByHex - clean_solver_cache :: TopLevel () clean_solver_cache = do vs <- io $ getSolverBackendVersions allBackends @@ -1112,7 +1109,7 @@ primitives = Map.fromList ] , prim "print_solver_cache" "String -> TopLevel ()" - (pureVal print_solver_cache) + (pureVal (onSolverCache . printSolverCacheByHex)) Experimental [ "Print all entries in the solver result cache whose SHA256 hash" , " keys start with the given string. Thus, given an empty string," @@ -1127,6 +1124,13 @@ primitives = Map.fromList , " cache have been made so far this session, and how many times" , " cached results have been used so far this session." ] + , prim "test_solver_cache_stats" "[Int] -> TopLevel ()" + (pureVal (onSolverCache . testSolverCacheStats)) + Experimental + [ "Check whether the values of the statistics printed out by" + , " print_solver_cache_stats are equal to those given, failing if" + , " this does not hold. Used for testing." ] + , prim "enable_debug_intrinsics" "TopLevel ()" (pureVal enable_debug_intrinsics) Current diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 3ff16cbb35..66a1e00d70 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -62,6 +62,7 @@ module SAWScript.SolverCache , printSolverCacheByHex , cleanSolverCache , printSolverCacheStats + , testSolverCacheStats ) where import System.Directory (createDirectoryIfMissing, makeAbsolute) @@ -361,12 +362,13 @@ emptySolverCache = do stats <- newIORef $ M.fromList ((,0) <$> [minBound..]) return $ SolverCache db stats 1000 --- | An operation on a 'SolverCache', returning a value of the given type -type SolverCacheOp a = Options -> SolverCache -> IO a +-- | An operation on a 'SolverCache', returning a value of the given type or +-- returning an optional default value in the case of no enabled 'SolverCache' +type SolverCacheOp a = (Maybe a, Options -> SolverCache -> IO a) -- | Lookup a 'SolverCacheKey' in the solver result cache lookupInSolverCache :: SolverCacheKey -> SolverCacheOp (Maybe SolverCacheValue) -lookupInSolverCache k opts SolverCache{..} = +lookupInSolverCache k = (Just Nothing,) $ \opts SolverCache{..} -> tryWithTimeout solverCacheTimeout (LMDBOpt.lookup (solverCacheKeyHash k) solverCacheDB) >>= \case @@ -383,7 +385,7 @@ lookupInSolverCache k opts SolverCache{..} = -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () -insertInSolverCache k v opts SolverCache{..} = +insertInSolverCache k v = (Just (),) $ \opts SolverCache{..} -> printOutLn opts Info ("Caching result: " ++ show k) >> tryWithTimeout solverCacheTimeout (LMDBOpt.insert (solverCacheKeyHash k) v @@ -397,7 +399,7 @@ insertInSolverCache k v opts SolverCache{..} = -- | Set the 'FilePath' of the solver result cache and save all results cached -- so far setSolverCachePath :: FilePath -> SolverCacheOp () -setSolverCachePath path opts SolverCache{..} = do +setSolverCachePath path = (Nothing,) $ \opts SolverCache{..} -> do pathAbs <- makeAbsolute path createDirectoryIfMissing True pathAbs eith_sz <- tryWithTimeout solverCacheTimeout @@ -414,7 +416,7 @@ setSolverCachePath path opts SolverCache{..} = do -- | Print all entries in the solver result cache whose SHA256 hash keys start -- with the given string printSolverCacheByHex :: String -> SolverCacheOp () -printSolverCacheByHex hex_prefix opts SolverCache{..} = do +printSolverCacheByHex hex_prefix = (Nothing,) $ \opts SolverCache{..} -> do kvs <- LMDBOpt.filterByHexPrefix hex_prefix solverCacheDB when (length kvs == 0) $ printOutLn opts Info "No keys found" forM_ kvs $ \(k_hash, SolverCacheValue vs bk_opts nm mb_cexs) -> do @@ -428,7 +430,7 @@ printSolverCacheByHex hex_prefix opts SolverCache{..} = do -- | Remove all entries in the solver result cache which have version(s) that -- do not match the current version(s) cleanSolverCache :: SolverBackendVersions -> SolverCacheOp () -cleanSolverCache curr_base_vs opts SolverCache{..} = do +cleanSolverCache curr_base_vs = (Nothing,) $ \opts SolverCache{..} -> do let curr_base_vs_obj = M.fromList [("vs", curr_base_vs)] fs0 <- LMDBOpt.cleanByJSONObjValues curr_base_vs_obj solverCacheDB let fs1 = concatMap (fmap (both (M.! ("vs" :: String))) . snd) fs0 @@ -445,7 +447,7 @@ cleanSolverCache curr_base_vs opts SolverCache{..} = do -- | Print out statistics about how the solver cache was used printSolverCacheStats :: SolverCacheOp () -printSolverCacheStats opts SolverCache{..} = do +printSolverCacheStats = (Nothing,) $ \opts SolverCache{..} -> do printOutLn opts Info ("== Solver result cache statistics ==") sz <- LMDBOpt.size solverCacheDB loc <- fromMaybe "memory" <$> LMDBOpt.getPath solverCacheDB @@ -461,3 +463,21 @@ printSolverCacheStats opts SolverCache{..} = do ++ " of cached results so far this run (" ++ show ls_f ++ " failed attempt" ++ pl ls_f ++ ")" where pl i = if i == 1 then "" else "s" + +-- | Check whether the values of the statistics printed out by +-- 'printSolverCacheStats' are equal to those given, failing if this does not +-- hold +testSolverCacheStats :: [Integer] -> SolverCacheOp () +testSolverCacheStats ex = (Nothing,) $ \opts SolverCache{..} -> do + sz <- fromIntegral <$> LMDBOpt.size solverCacheDB + test sz 0 "Size of" + stats <- readIORef solverCacheStats + test (stats M.! Lookups) 1 "Number of usages of" + test (stats M.! FailedLookups) 2 "Number of failed usages of" + test (stats M.! Inserts) 3 "Number of insertions into" + test (stats M.! FailedInserts) 4 "Number of failed insertions into" + printOutLn opts Info $ "Solver cache stats matched expected (" ++ + intercalate ", " (show <$> ex) ++ ")" + where test v i str = when (length ex > i && v /= ex !! i) $ fail $ + str ++ " solver cache (" ++ show v ++ ") did not match expected" ++ + " (" ++ show (ex !! i) ++ ")" diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 4680f939d7..7927d76db4 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -737,23 +737,17 @@ recordProof v = do rw <- getTopLevelRW putTopLevelRW rw { rwProofs = toValue v : rwProofs rw } --- | Perform a possibly stateful operation on the 'SolverCache', returning a --- value of type @Maybe a@, or 'Nothing' if there is no 'SolverCache' -askSolverCache :: SolverCacheOp (Maybe a) -> TopLevel (Maybe a) -askSolverCache f = +-- | Perform an operation on the 'SolverCache', returning a default value or +-- failing (depending on the first element of the 'SolverCacheOp') if there +-- is no enabled 'SolverCache' +onSolverCache :: SolverCacheOp a -> TopLevel a +onSolverCache (mb_default, f) = do opts <- getOptions getSolverCache >>= \case Just cache -> io $ f opts cache - Nothing -> return Nothing - --- | Perform a stateful operation on the 'SolverCache', or do nothing if --- there is no 'SolverCache' -onSolverCache :: SolverCacheOp () -> TopLevel () -onSolverCache f = - do opts <- getOptions - getSolverCache >>= \case - Just cache -> io $ f opts cache - Nothing -> return () + Nothing -> case mb_default of + Just a -> return a + Nothing -> fail "Solver result cache not enabled!" -- | Access the current state of Java Class translation getJVMTrans :: TopLevel CJ.JVMContext From f173acb50baa1748a9c827de591cfd74d719a65c Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 21 Jun 2023 14:01:27 -0400 Subject: [PATCH 30/51] expand tests for solver caching --- intTests/test_solver_cache/test.sh | 27 ++++++++++++++++++- .../{test.saw => test_basics.saw} | 2 +- intTests/test_solver_cache/test_clean.saw | 17 ++++++++++++ intTests/test_solver_cache/test_env_var.saw | 10 +++++++ .../test_solver_cache/test_path_first.saw | 12 +++++++++ intTests/test_solver_cache/test_path_ops.saw | 8 ++++++ .../test_solver_cache/test_path_second.saw | 13 +++++++++ 7 files changed, 87 insertions(+), 2 deletions(-) rename intTests/test_solver_cache/{test.saw => test_basics.saw} (96%) create mode 100644 intTests/test_solver_cache/test_clean.saw create mode 100644 intTests/test_solver_cache/test_env_var.saw create mode 100644 intTests/test_solver_cache/test_path_first.saw create mode 100644 intTests/test_solver_cache/test_path_ops.saw create mode 100644 intTests/test_solver_cache/test_path_second.saw diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh index 0b864017cd..178a1db40d 100644 --- a/intTests/test_solver_cache/test.sh +++ b/intTests/test_solver_cache/test.sh @@ -1 +1,26 @@ -$SAW test.saw + +# Testing the basic features of the solver cache +$SAW test_basics.saw + +# Testing setting a path for the solver cache +$SAW test_path_first.saw +$SAW test_path_second.saw + +# Testing setting the solver cache path through an envionment variable +SAW_SOLVER_CACHE_PATH="test.cache" $SAW test_env_var.saw + +# Testing cleaning the solver cache +python3 ../../src/SAWScript/SolverCache/lmdb_opt_database.py shell << END +db = LMDBOptDatabase() +db.setPath('test.cache') +for k,v in db.items(): + v_obj = json.loads(v) + if 'SBV' in v_obj['vs']: + v_obj['vs']['SBV'] = '[OLD VERSION]' + db[k] = bytes(json.dumps(v_obj), 'utf-8') + +END +$SAW test_clean.saw + +# Clean up +rm -rf test.cache diff --git a/intTests/test_solver_cache/test.saw b/intTests/test_solver_cache/test_basics.saw similarity index 96% rename from intTests/test_solver_cache/test.saw rename to intTests/test_solver_cache/test_basics.saw index e0e9ecaa1f..1857bac6d0 100644 --- a/intTests/test_solver_cache/test.saw +++ b/intTests/test_solver_cache/test_basics.saw @@ -1,4 +1,4 @@ -// Test for solver result caching +// Testing the basic features of solver result caching enable_experimental; enable_solver_cache; diff --git a/intTests/test_solver_cache/test_clean.saw b/intTests/test_solver_cache/test_clean.saw new file mode 100644 index 0000000000..16c21f3eb1 --- /dev/null +++ b/intTests/test_solver_cache/test_clean.saw @@ -0,0 +1,17 @@ +// Testing cleaning the solver cache + +enable_experimental; +set_solver_cache_path "test.cache"; + +// The cache still has entries from prior runs +test_solver_cache_stats [6, 0, 0, 0, 0]; + +// After cleaning, all SBV entries should be removed (see test.sh) +clean_solver_cache; +test_solver_cache_stats [4, 0, 0, 0, 0]; + +// After running test_path_ops, we should have all the original entries back, +// as many insertions as there were SBV entries, and as many usages as there +// were in test_path_second less the number of SBV entries +include "test_path_ops.saw"; +test_solver_cache_stats [6, 6, 0, 2, 0]; diff --git a/intTests/test_solver_cache/test_env_var.saw b/intTests/test_solver_cache/test_env_var.saw new file mode 100644 index 0000000000..015ab98251 --- /dev/null +++ b/intTests/test_solver_cache/test_env_var.saw @@ -0,0 +1,10 @@ +// Testing setting the solver cache path through an envionment variable + +enable_experimental; + +// The same as test_path_second.saw + +test_solver_cache_stats [6, 0, 0, 0, 0]; + +include "test_path_ops.saw"; +test_solver_cache_stats [6, 8, 0, 0, 0]; diff --git a/intTests/test_solver_cache/test_path_first.saw b/intTests/test_solver_cache/test_path_first.saw new file mode 100644 index 0000000000..ca01951b61 --- /dev/null +++ b/intTests/test_solver_cache/test_path_first.saw @@ -0,0 +1,12 @@ +// Testing setting a path for the solver cache - the first run + +enable_experimental; +set_solver_cache_path "test.cache"; + +// The solver cache starts out empty +test_solver_cache_stats [0, 0, 0, 0, 0]; + +// After running test_path_ops, we should have as many insertions as we have +// entries in the cache, as well as a couple usages +include "test_path_ops.saw"; +test_solver_cache_stats [6, 2, 0, 6, 0]; diff --git a/intTests/test_solver_cache/test_path_ops.saw b/intTests/test_solver_cache/test_path_ops.saw new file mode 100644 index 0000000000..f7fa6b2930 --- /dev/null +++ b/intTests/test_solver_cache/test_path_ops.saw @@ -0,0 +1,8 @@ +prove_print z3 {{ \(x:[64]) -> x == x }}; +prove_print z3 {{ \(new_name:[64]) -> new_name == new_name }}; +prove_print (w4_unint_z3 []) {{ \(x:[64]) -> x == x }}; +prove_print (w4_unint_z3_using "qfnia" []) {{ \(x:[64]) -> x == x }}; +fails (prove_print z3 {{ \(x:[64])(y:[64]) -> x == y }}); +fails (prove_print z3 {{ \(new_name_1:[64])(new_name_2:[64]) -> new_name_1 == new_name_2 }}); +fails (prove_print w4 {{ \(x:[64])(y:[64]) -> x == y }}); +fails (prove_print (w4_unint_z3_using "qfnia" []) {{ \(x:[64])(y:[64]) -> x == y }}); \ No newline at end of file diff --git a/intTests/test_solver_cache/test_path_second.saw b/intTests/test_solver_cache/test_path_second.saw new file mode 100644 index 0000000000..53fa5ccb7a --- /dev/null +++ b/intTests/test_solver_cache/test_path_second.saw @@ -0,0 +1,13 @@ +// Testing setting a path for the solver cache - the second run + +enable_experimental; +set_solver_cache_path "test.cache"; + +// The cache still has entries from the last run +test_solver_cache_stats [6, 0, 0, 0, 0]; + +// After running test_path_ops, we should have the same number of entries, but +// no insertions and and as many usages as there were insertions plus usages +// the first time +include "test_path_ops.saw"; +test_solver_cache_stats [6, 8, 0, 0, 0]; From bfb1b799b0fa057f733a7e34d9822426f50a9259 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 21 Jun 2023 14:29:30 -0400 Subject: [PATCH 31/51] actually install lmdb on the CI for solver caching tests --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c27e27ba4..d758cdd0e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -432,6 +432,10 @@ jobs: java-package: jdk architecture: x64 + - shell: bash + if: "matrix.suite == 'integration_tests'" + run: pip install lmdb + - name: ${{ matrix.suite }} continue-on-error: ${{ matrix.continue-on-error }} shell: bash From 2354bb2b4e07bbfab77926cad835491be0520f2d Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Thu, 22 Jun 2023 12:10:52 -0400 Subject: [PATCH 32/51] set -e in test_solver_cache --- intTests/test_solver_cache/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh index 178a1db40d..126381b0c8 100644 --- a/intTests/test_solver_cache/test.sh +++ b/intTests/test_solver_cache/test.sh @@ -1,3 +1,4 @@ +set -e # Testing the basic features of the solver cache $SAW test_basics.saw From 16cfe0e38fa3e1d5a76a2464ec91c77dd42c9945 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Thu, 22 Jun 2023 14:51:18 -0400 Subject: [PATCH 33/51] WIP testing saving LMDB caches on CI --- intTests/test_solver_cache/test.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh index 126381b0c8..e627a1d708 100644 --- a/intTests/test_solver_cache/test.sh +++ b/intTests/test_solver_cache/test.sh @@ -5,6 +5,12 @@ $SAW test_basics.saw # Testing setting a path for the solver cache $SAW test_path_first.saw +ls +python3 ../../src/SAWScript/SolverCache/lmdb_opt_database.py shell << END +db = LMDBOptDatabase() +db.setPath('test.cache') +pHexJSONPairs(db.items(), True) +END $SAW test_path_second.saw # Testing setting the solver cache path through an envionment variable From 897f8ff18baefe45064fa57f238a0a1380cdf3b6 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 27 Jun 2023 13:12:40 -0400 Subject: [PATCH 34/51] WIP more CI debugging --- intTests/test_solver_cache/test.sh | 2 ++ intTests/test_solver_cache/test_path_first.saw | 3 +++ intTests/test_solver_cache/test_path_second.saw | 3 +++ 3 files changed, 8 insertions(+) diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh index e627a1d708..52d509f377 100644 --- a/intTests/test_solver_cache/test.sh +++ b/intTests/test_solver_cache/test.sh @@ -9,6 +9,8 @@ ls python3 ../../src/SAWScript/SolverCache/lmdb_opt_database.py shell << END db = LMDBOptDatabase() db.setPath('test.cache') +db._impl +db._impl.begin().stat() pHexJSONPairs(db.items(), True) END $SAW test_path_second.saw diff --git a/intTests/test_solver_cache/test_path_first.saw b/intTests/test_solver_cache/test_path_first.saw index ca01951b61..e9d3285907 100644 --- a/intTests/test_solver_cache/test_path_first.saw +++ b/intTests/test_solver_cache/test_path_first.saw @@ -10,3 +10,6 @@ test_solver_cache_stats [0, 0, 0, 0, 0]; // entries in the cache, as well as a couple usages include "test_path_ops.saw"; test_solver_cache_stats [6, 2, 0, 6, 0]; + +print_solver_cache_stats; +print_solver_cache ""; \ No newline at end of file diff --git a/intTests/test_solver_cache/test_path_second.saw b/intTests/test_solver_cache/test_path_second.saw index 53fa5ccb7a..20a52dbab4 100644 --- a/intTests/test_solver_cache/test_path_second.saw +++ b/intTests/test_solver_cache/test_path_second.saw @@ -3,6 +3,9 @@ enable_experimental; set_solver_cache_path "test.cache"; +print_solver_cache_stats; +print_solver_cache ""; + // The cache still has entries from the last run test_solver_cache_stats [6, 0, 0, 0, 0]; From c040ab50432d1b749a0918a31083df93201fe481 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 27 Jun 2023 14:38:00 -0400 Subject: [PATCH 35/51] remove hex-text dependency, confirm no exns with units in LMDBOptDatabase --- saw-script.cabal | 1 - src/SAWScript/SolverCache/LMDBOptDatabase.hs | 40 ++++++++++---------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/saw-script.cabal b/saw-script.cabal index e11ffde4fe..ec7c3a1a44 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -52,7 +52,6 @@ library , free , haskeline , heapster-saw - , hex-text , hobbits >= 1.3.1 , galois-dwarf >= 0.2.2 , IfElse diff --git a/src/SAWScript/SolverCache/LMDBOptDatabase.hs b/src/SAWScript/SolverCache/LMDBOptDatabase.hs index d92fd2ed33..3186684554 100644 --- a/src/SAWScript/SolverCache/LMDBOptDatabase.hs +++ b/src/SAWScript/SolverCache/LMDBOptDatabase.hs @@ -47,14 +47,16 @@ import System.IO import Control.Concurrent (forkIO, ThreadId) import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar) import Control.Monad ((>=>), forever) +import Data.Char (isHexDigit) +import Text.Printf (printf) +import Text.Read (readEither) +import Numeric (readHex) import Language.Haskell.TH (runIO) import Language.Haskell.TH.Syntax (qAddDependentFile, liftString) -import qualified Data.Text as T -import qualified Text.Hex as Hex - import Data.ByteString (ByteString) +import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import Data.Aeson (FromJSON, ToJSON) @@ -74,17 +76,17 @@ lmdb_opt_database__py = $(do -- | Remove all characters except hex digits (i.e. `[0-9a-fA-F]`) from the -- given string sanitizeHex :: String -> String -sanitizeHex = filter (`elem` "0123456789abcdefABCDEF") +sanitizeHex = filter isHexDigit -- | Encode a 'ByteString' as a hex string encodeHex :: ByteString -> String -encodeHex bs = T.unpack $ Hex.encodeHex bs +encodeHex = concatMap (printf "%02x") . BS.unpack -- | Decode a hex string as a 'ByteString' decodeHex :: String -> Either String ByteString -decodeHex s = case Hex.decodeHex $ T.pack s of - Just bs -> Right bs - Nothing -> Left $ "Hex decoding failure on:\n" ++ s +decodeHex s = BS.pack <$> go s + where go (c0:c1:cs) | [(b,[])] <- readHex [c0,c1] = (b:) <$> go cs + go _ = Left $ "Hex decoding failure on:\n" ++ s -- | Encode an element of a 'ToJSON' type as a hex string encodeJSONHex :: ToJSON a => a -> String @@ -117,11 +119,6 @@ decodeSingle :: (String -> Either String a) -> [String] -> Either String a decodeSingle f [l] = f l decodeSingle _ ls = Left $ "Expected one line, got: " ++ show (unlines ls) --- | Given an empty list, return the given element -decodeEmpty :: a -> [String] -> Either String a -decodeEmpty b [] = Right b -decodeEmpty _ ls = Left $ "Expected nothing, got: " ++ show (unlines ls) - -- Helper functions for IO ----------------------------------------------------- @@ -229,17 +226,18 @@ dbExec decodeFn inp_str LMDBOptDatabase{..} = do -- | Create a new, empty, 'LMDBOptDatabase'. This will fail if the @python3@ -- executable cannot be found. new :: IO (LMDBOptDatabase a) -new = dbOpen >>= \db -> flip (dbExec (decodeEmpty db)) db - "db = LMDBOptDatabase()" +new = do db <- dbOpen + () <- dbExec (decodeSingle readEither) "db = LMDBOptDatabase(); ()" db + return db -- | Open an LMDB database at the given 'FilePath' with the given maximum size -- (in MiB), add all current entries in the database to the LMDB database, then -- set the current implementation of this database to be the LMDB database. This -- will fail if the Python LMDB bindings cannot be loaded. setPath :: FilePath -> Int -> LMDBOptDatabase a -> IO () -setPath path map_size = dbExec (decodeEmpty ()) $ +setPath path map_size = dbExec (decodeSingle readEither) $ "db.setPath(jsonHex('" ++ encodeJSONHex path ++ "'), " ++ - "jsonHex('" ++ encodeJSONHex map_size ++ "'))" + "jsonHex('" ++ encodeJSONHex map_size ++ "')); ()" -- | Return the location of the directory in which this database is stored, if -- this database is implemented as an LMDB database @@ -260,13 +258,13 @@ lookup k = dbExec (decodeMaybe decodeJSONHex) $ -- | Insert a 'ByteString' key / JSON-serializable value entry into the database insert :: ToJSON a => ByteString -> a -> LMDBOptDatabase a -> IO () -insert k v = dbExec (decodeEmpty ()) $ - "db[hex('" ++ encodeHex k ++ "')] = hex('" ++ encodeJSONHex v ++ "')" +insert k v = dbExec (decodeSingle readEither) $ + "db[hex('" ++ encodeHex k ++ "')] = hex('" ++ encodeJSONHex v ++ "'); ()" -- | Delete the entry associated to the given 'ByteString' key in the database delete :: ByteString -> LMDBOptDatabase a -> IO () -delete k = dbExec (decodeEmpty ()) $ - "del db[hex('" ++ encodeHex k ++ "')]" +delete k = dbExec (decodeSingle readEither) $ + "del db[hex('" ++ encodeHex k ++ "')]; ()" -- | Return the list of all entries in the database toList :: FromJSON a => LMDBOptDatabase a -> IO [(ByteString, a)] From c68801d72ff8c6c4c715a6e1960582f5a949a3ef Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 27 Jun 2023 15:16:30 -0400 Subject: [PATCH 36/51] get SBV version through cabal VERSION macro, not freeze file --- Setup.hs | 47 ++++++++++++--------------------- src/SAWScript/SolverVersions.hs | 3 ++- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/Setup.hs b/Setup.hs index f5a8837454..0722b80ccb 100644 --- a/Setup.hs +++ b/Setup.hs @@ -16,46 +16,33 @@ myBuild pd lbi uh flags = do let dir = autogenPackageModulesDir lbi createDirectoryIfMissing True dir - desc <- withExe "." "git" "" "" $ \git -> - init <$> readProcess git ["describe", "--always", "--dirty"] "" - - aig_desc <- withExe "deps/aig" "git" Nothing Nothing $ \git -> do - Just . init <$> readProcess git ["describe", "--always", "--dirty"] "" - - w4_desc <- withExe "deps/what4" "git" Nothing Nothing $ \git -> do - Just . init <$> readProcess git ["describe", "--always", "--dirty"] "" - - sbv_ver <- withExe "." "cabal" Nothing Nothing $ \cabal -> do - ex <- doesFileExist "cabal.project.freeze" - unless ex $ callProcess cabal ["freeze"] - wss <- fmap words <$> lines <$> readFile "cabal.project.freeze" - unless ex $ removeFile "cabal.project.freeze" - case find (\ws -> (ws !! 0) == "any.sbv") wss of - Just ws -> return . Just . init . drop 2 $ ws !! 1 - Nothing -> return Nothing + hasGit <- findExecutable "git" + + let gitfailure :: a -> SomeException -> IO a + gitfailure a _e = return a + + let gitdescribe dir m on_no_exe on_fail = case hasGit of + Just exe -> withCurrentDirectory dir (m <$> + readProcess "git" ["describe", "--always", "--dirty"] "") + `catch` gitfailure on_fail + Nothing -> return on_no_exe + + desc <- gitdescribe "." init "" "" + aig_desc <- gitdescribe "deps/aig" (Just . init) Nothing Nothing + w4_desc <- gitdescribe "deps/what4" (Just . init) Nothing Nothing writeFile (dir "GitRev.hs") $ unlines [ "module GitRev where" + , "-- | String describing the HEAD of saw-script at compile-time" , "hash :: String" , "hash = " ++ show desc + , "-- | String describing the HEAD of the deps/aig submodule at compile-time" , "aigHash :: Maybe String" , "aigHash = " ++ show aig_desc + , "-- | String describing the HEAD of the deps/what4 submodule at compile-time" , "what4Hash :: Maybe String" , "what4Hash = " ++ show w4_desc - , "sbvVersion :: Maybe String" - , "sbvVersion = " ++ show sbv_ver ] unless (null $ allBuildInfo pd) $ (buildHook simpleUserHooks) pd lbi uh flags - -withExe :: FilePath -> String -> a -> a -> (FilePath -> IO a) -> IO a -withExe dir exe_str on_no_exe on_fail m = do - mb <- findExecutable exe_str - case mb of - Just exe -> withCurrentDirectory dir (m exe) - `catch` onfailure on_fail - Nothing -> return on_no_exe - -onfailure :: a -> SomeException -> IO a -onfailure on_fail _e = return on_fail diff --git a/src/SAWScript/SolverVersions.hs b/src/SAWScript/SolverVersions.hs index 2645803ecb..1e58bc8ab2 100644 --- a/src/SAWScript/SolverVersions.hs +++ b/src/SAWScript/SolverVersions.hs @@ -5,6 +5,7 @@ License : BSD3 Maintainer : m-yac Stability : provisional -} +{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} @@ -42,7 +43,7 @@ getSolverVersion s = getSolverBackendVersion :: SolverBackend -> IO (Maybe String) getSolverBackendVersion backend = case backend of What4 -> return what4Hash - SBV -> return sbvVersion + SBV -> return (Just VERSION_sbv) AIG -> return aigHash RME -> return (Just hash) -- We use individual cases for the remaining constructors to ensure that if From 964219c8c9ab126ef241eb710964d3b2a2ed438d Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 27 Jun 2023 15:37:30 -0400 Subject: [PATCH 37/51] fix style issues / typos found by @RyanGlScott --- saw-remote-api/src/SAWServer.hs | 2 +- src/SAWScript/Interpreter.hs | 2 +- src/SAWScript/SolverCache.hs | 13 +++++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/saw-remote-api/src/SAWServer.hs b/saw-remote-api/src/SAWServer.hs index f2ad295a2e..ecafdac09e 100644 --- a/saw-remote-api/src/SAWServer.hs +++ b/saw-remote-api/src/SAWServer.hs @@ -209,7 +209,7 @@ initialState readFileFn = jvmTrans <- CJ.mkInitialJVMContext halloc cwd <- getCurrentDirectory mb_cache <- lookupEnv "SAW_SOLVER_CACHE_PATH" >>= \case - Just p | length p > 0 -> do + Just p | not (null p) -> do cache <- emptySolverCache snd (setSolverCachePath p) opts cache return $ Just cache diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 04705e7a04..3f8d57fd19 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -457,7 +457,7 @@ buildTopLevelEnv proxy opts = jcb <- JCB.loadCodebase (jarList opts) (classPath opts) (javaBinDirs opts) currDir <- getCurrentDirectory mb_cache <- lookupEnv "SAW_SOLVER_CACHE_PATH" >>= \case - Just p | length p > 0 -> do + Just p | not (null p) -> do cache <- emptySolverCache snd (setSolverCachePath p) opts cache return $ Just cache diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 66a1e00d70..2dbf4f3ce6 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -246,7 +246,7 @@ showBackendVersionsWithOptions sep vs opts = -- Solver Cache Keys ----------------------------------------------------------- --- | The key type for 'SolverCache'. Each is a SHA256 hashes of 'SATQuery' and +-- | The key type for 'SolverCache'. Each is a SHA256 hash of a 'SATQuery' and -- a 'Set' of 'SolverBackendVersion's - see 'mkSolverCacheKey' data SolverCacheKey = SolverCacheKey @@ -328,16 +328,21 @@ instance ToJSON SolverCacheValue where toSolverCacheValue :: SolverBackendVersions -> [SolverBackendOption] -> SATQuery -> (Maybe CEX, String) -> Maybe SolverCacheValue toSolverCacheValue vs opts satq (cexs, solver_name) = - fmap (SolverCacheValue vs opts solver_name) - (mapM (mapM (firstM (`elemIndex` ecs))) cexs) + SolverCacheValue vs opts solver_name <$> firstsMaybeM (`elemIndex` ecs) cexs where ecs = M.keys $ satVariables satq + firstsMaybeM :: Monad m => (a -> m b) -> + Maybe [(a, c)] -> m (Maybe [(b, c)]) + firstsMaybeM = mapM . mapM . firstM -- | Convert a 'SolverCacheValue' to something which has the same form as the -- result of a solver call on the given 'SATQuery' fromSolverCacheValue :: SATQuery -> SolverCacheValue -> (Maybe CEX, String) fromSolverCacheValue satq (SolverCacheValue _ _ solver_name cexs) = - (fmap (fmap (first (ecs !!))) cexs, solver_name) + (firstsMaybe (ecs !!) cexs, solver_name) where ecs = M.keys $ satVariables satq + firstsMaybe :: (a -> b) -> Maybe [(a, c)] -> Maybe [(b, c)] + firstsMaybe = fmap . fmap . first + -- The Solver Cache ------------------------------------------------------------ From b3575eab563f9ee497c7e65b08022bbf5c1984f7 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 27 Jun 2023 15:54:24 -0400 Subject: [PATCH 38/51] add forgotten base case to decodeHex --- src/SAWScript/SolverCache/LMDBOptDatabase.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SAWScript/SolverCache/LMDBOptDatabase.hs b/src/SAWScript/SolverCache/LMDBOptDatabase.hs index 3186684554..e957859d5f 100644 --- a/src/SAWScript/SolverCache/LMDBOptDatabase.hs +++ b/src/SAWScript/SolverCache/LMDBOptDatabase.hs @@ -86,7 +86,8 @@ encodeHex = concatMap (printf "%02x") . BS.unpack decodeHex :: String -> Either String ByteString decodeHex s = BS.pack <$> go s where go (c0:c1:cs) | [(b,[])] <- readHex [c0,c1] = (b:) <$> go cs - go _ = Left $ "Hex decoding failure on:\n" ++ s + go [] = return [] + go _ = Left $ "Hex decoding failure on: " ++ s -- | Encode an element of a 'ToJSON' type as a hex string encodeJSONHex :: ToJSON a => a -> String From 6a5ec6725ce0b53f41864694b266779f3a3811bb Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 27 Jun 2023 18:19:59 -0400 Subject: [PATCH 39/51] pip install lmdb in test_solver_cache/test.sh --- intTests/test_solver_cache/test.sh | 11 +++-------- intTests/test_solver_cache/test_path_first.saw | 3 --- intTests/test_solver_cache/test_path_second.saw | 3 --- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh index 52d509f377..b5a10ecf8a 100644 --- a/intTests/test_solver_cache/test.sh +++ b/intTests/test_solver_cache/test.sh @@ -3,16 +3,11 @@ set -e # Testing the basic features of the solver cache $SAW test_basics.saw +# Make sure Python lmdb bindings are installed +pip install lmdb + # Testing setting a path for the solver cache $SAW test_path_first.saw -ls -python3 ../../src/SAWScript/SolverCache/lmdb_opt_database.py shell << END -db = LMDBOptDatabase() -db.setPath('test.cache') -db._impl -db._impl.begin().stat() -pHexJSONPairs(db.items(), True) -END $SAW test_path_second.saw # Testing setting the solver cache path through an envionment variable diff --git a/intTests/test_solver_cache/test_path_first.saw b/intTests/test_solver_cache/test_path_first.saw index e9d3285907..ca01951b61 100644 --- a/intTests/test_solver_cache/test_path_first.saw +++ b/intTests/test_solver_cache/test_path_first.saw @@ -10,6 +10,3 @@ test_solver_cache_stats [0, 0, 0, 0, 0]; // entries in the cache, as well as a couple usages include "test_path_ops.saw"; test_solver_cache_stats [6, 2, 0, 6, 0]; - -print_solver_cache_stats; -print_solver_cache ""; \ No newline at end of file diff --git a/intTests/test_solver_cache/test_path_second.saw b/intTests/test_solver_cache/test_path_second.saw index 20a52dbab4..53fa5ccb7a 100644 --- a/intTests/test_solver_cache/test_path_second.saw +++ b/intTests/test_solver_cache/test_path_second.saw @@ -3,9 +3,6 @@ enable_experimental; set_solver_cache_path "test.cache"; -print_solver_cache_stats; -print_solver_cache ""; - // The cache still has entries from the last run test_solver_cache_stats [6, 0, 0, 0, 0]; From 0e2efb201853daa14571ffd08f82d995b9ac632a Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 28 Jun 2023 14:30:32 -0400 Subject: [PATCH 40/51] improve test_solver_cache_stats args, clean up debugging code, add docs --- intTests/test_solver_cache/test_basics.saw | 18 +++--- intTests/test_solver_cache/test_clean.saw | 6 +- intTests/test_solver_cache/test_env_var.saw | 4 +- .../test_solver_cache/test_path_first.saw | 4 +- .../test_solver_cache/test_path_second.saw | 4 +- src/SAWScript/Interpreter.hs | 54 ++++++++++------- src/SAWScript/SolverCache.hs | 58 +++++++++++-------- .../SolverCache/lmdb_opt_database.py | 28 +++++---- 8 files changed, 100 insertions(+), 76 deletions(-) diff --git a/intTests/test_solver_cache/test_basics.saw b/intTests/test_solver_cache/test_basics.saw index 1857bac6d0..ea6c707f43 100644 --- a/intTests/test_solver_cache/test_basics.saw +++ b/intTests/test_solver_cache/test_basics.saw @@ -4,38 +4,38 @@ enable_experimental; enable_solver_cache; // The solver cache starts out empty -test_solver_cache_stats [0, 0, 0, 0, 0]; +test_solver_cache_stats 0 false 0 0 0 0; // The cache should now have one entry and one insertion prove_print z3 {{ \(x:[64]) -> x == x }}; -test_solver_cache_stats [1, 0, 0, 1, 0]; +test_solver_cache_stats 1 false 0 0 1 0; // Testing that cached results do not depend on variable names - thus, the // cache should now have one more usage, but not a new entry or insertion prove_print z3 {{ \(new_name:[64]) -> new_name == new_name }}; -test_solver_cache_stats [1, 1, 0, 1, 0]; +test_solver_cache_stats 1 false 1 0 1 0; // Testing that cached results depend on the backend used - thus, the cache // should now have one more entry and one more insertion, but not a new usage prove_print (w4_unint_z3 []) {{ \(x:[64]) -> x == x }}; -test_solver_cache_stats [2, 1, 0, 2, 0]; +test_solver_cache_stats 2 false 1 0 2 0; // Testing that cached results depend on the options passed to the given // backend - thus, the cache should now have one more entry and one more // insertion, but not a new usage prove_print (w4_unint_z3_using "qfnia" []) {{ \(x:[64]) -> x == x }}; -test_solver_cache_stats [3, 1, 0, 3, 0]; +test_solver_cache_stats 3 false 1 0 3 0; // Same as the above but for sat results fails (prove_print z3 {{ \(x:[64])(y:[64]) -> x == y }}); -test_solver_cache_stats [4, 1, 0, 4, 0]; +test_solver_cache_stats 4 false 1 0 4 0; fails (prove_print z3 {{ \(new_name_1:[64])(new_name_2:[64]) -> new_name_1 == new_name_2 }}); -test_solver_cache_stats [4, 2, 0, 4, 0]; +test_solver_cache_stats 4 false 2 0 4 0; fails (prove_print w4 {{ \(x:[64])(y:[64]) -> x == y }}); -test_solver_cache_stats [5, 2, 0, 5, 0]; +test_solver_cache_stats 5 false 2 0 5 0; fails (prove_print (w4_unint_z3_using "qfnia" []) {{ \(x:[64])(y:[64]) -> x == y }}); -test_solver_cache_stats [6, 2, 0, 6, 0]; +test_solver_cache_stats 6 false 2 0 6 0; diff --git a/intTests/test_solver_cache/test_clean.saw b/intTests/test_solver_cache/test_clean.saw index 16c21f3eb1..90447536bc 100644 --- a/intTests/test_solver_cache/test_clean.saw +++ b/intTests/test_solver_cache/test_clean.saw @@ -4,14 +4,14 @@ enable_experimental; set_solver_cache_path "test.cache"; // The cache still has entries from prior runs -test_solver_cache_stats [6, 0, 0, 0, 0]; +test_solver_cache_stats 6 true 0 0 0 0; // After cleaning, all SBV entries should be removed (see test.sh) clean_solver_cache; -test_solver_cache_stats [4, 0, 0, 0, 0]; +test_solver_cache_stats 4 true 0 0 0 0; // After running test_path_ops, we should have all the original entries back, // as many insertions as there were SBV entries, and as many usages as there // were in test_path_second less the number of SBV entries include "test_path_ops.saw"; -test_solver_cache_stats [6, 6, 0, 2, 0]; +test_solver_cache_stats 6 true 6 0 2 0; diff --git a/intTests/test_solver_cache/test_env_var.saw b/intTests/test_solver_cache/test_env_var.saw index 015ab98251..8f807bd5da 100644 --- a/intTests/test_solver_cache/test_env_var.saw +++ b/intTests/test_solver_cache/test_env_var.saw @@ -4,7 +4,7 @@ enable_experimental; // The same as test_path_second.saw -test_solver_cache_stats [6, 0, 0, 0, 0]; +test_solver_cache_stats 6 true 0 0 0 0; include "test_path_ops.saw"; -test_solver_cache_stats [6, 8, 0, 0, 0]; +test_solver_cache_stats 6 true 8 0 0 0; diff --git a/intTests/test_solver_cache/test_path_first.saw b/intTests/test_solver_cache/test_path_first.saw index ca01951b61..c5304ca262 100644 --- a/intTests/test_solver_cache/test_path_first.saw +++ b/intTests/test_solver_cache/test_path_first.saw @@ -4,9 +4,9 @@ enable_experimental; set_solver_cache_path "test.cache"; // The solver cache starts out empty -test_solver_cache_stats [0, 0, 0, 0, 0]; +test_solver_cache_stats 0 true 0 0 0 0; // After running test_path_ops, we should have as many insertions as we have // entries in the cache, as well as a couple usages include "test_path_ops.saw"; -test_solver_cache_stats [6, 2, 0, 6, 0]; +test_solver_cache_stats 6 true 2 0 6 0; diff --git a/intTests/test_solver_cache/test_path_second.saw b/intTests/test_solver_cache/test_path_second.saw index 53fa5ccb7a..ff99716333 100644 --- a/intTests/test_solver_cache/test_path_second.saw +++ b/intTests/test_solver_cache/test_path_second.saw @@ -4,10 +4,10 @@ enable_experimental; set_solver_cache_path "test.cache"; // The cache still has entries from the last run -test_solver_cache_stats [6, 0, 0, 0, 0]; +test_solver_cache_stats 6 true 0 0 0 0; // After running test_path_ops, we should have the same number of entries, but // no insertions and and as many usages as there were insertions plus usages // the first time include "test_path_ops.saw"; -test_solver_cache_stats [6, 8, 0, 0, 0]; +test_solver_cache_stats 6 true 8 0 0 0; diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 3f8d57fd19..7fbba8cbe3 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -665,6 +665,11 @@ clean_solver_cache = do vs <- io $ getSolverBackendVersions allBackends onSolverCache (cleanSolverCache vs) +test_solver_cache_stats :: Integer -> Bool -> Integer -> Integer -> Integer -> + Integer -> TopLevel () +test_solver_cache_stats sz p ls ls_f is is_f = + onSolverCache (testSolverCacheStats sz p ls ls_f is is_f) + enable_debug_intrinsics :: TopLevel () enable_debug_intrinsics = do rw <- getTopLevelRW @@ -1088,48 +1093,57 @@ primitives = Map.fromList , prim "enable_solver_cache" "TopLevel ()" (pureVal enable_solver_cache) Experimental - [ "Enable solver result caching. Unless set_solver_cache_path is" - , " later called, the cache will not persist between sessions." + [ "Enable solver result caching, if it is not already enabled. Unless" + , "set_solver_cache_path is later called, the cache will not persist" + , "between sessions. Requires Python 3 to be installed." ] , prim "set_solver_cache_path" "String -> TopLevel ()" (pureVal set_solver_cache_path) Experimental - [ "Enable solver result caching if it is not already enabled, set a" - , " path to use for loading and saving cached results, and save to" - , " that path any results cached so far from the current session." + [ "Enable solver result caching if it is not already enabled, open an" + , "LMDB database at the given path, save to that database all results in" + , "the current cache, then use that database as the cache going forward." + , "Requires Python 3 and the Python lmdb library to be installed." ] , prim "clean_solver_cache" "TopLevel ()" (pureVal clean_solver_cache) Experimental [ "Remove all entries in the solver result cache which were created" - , " using solver backend version(s) which do not match the version(s)" - , " in the current environment." + , "using solver backend versions which do not match the versions" + , "in the current environment." ] , prim "print_solver_cache" "String -> TopLevel ()" (pureVal (onSolverCache . printSolverCacheByHex)) Experimental [ "Print all entries in the solver result cache whose SHA256 hash" - , " keys start with the given string. Thus, given an empty string," - , " all entries in the cache will be printed." + , "keys start with the given string. Providing an empty string results" + , "in all entries in the cache being printed." ] , prim "print_solver_cache_stats" "TopLevel ()" (pureVal (onSolverCache printSolverCacheStats)) Experimental - [ "Print out statistics about how the solver cache was used, namely" - , " how many entries are in the cache, how many insertions into the" - , " cache have been made so far this session, and how many times" - , " cached results have been used so far this session." ] - - , prim "test_solver_cache_stats" "[Int] -> TopLevel ()" - (pureVal (onSolverCache . testSolverCacheStats)) - Experimental - [ "Check whether the values of the statistics printed out by" - , " print_solver_cache_stats are equal to those given, failing if" - , " this does not hold. Used for testing." ] + [ "Print out statistics about how the solver cache has been used, namely" + , "how many entries are in the cache, whether the cache is being stored" + , "in memory or on disk, how many insertions into the cache have been made" + , "so far this session, how many failed insertion attempts have been made" + , "so far this session, how times cached results have been used so far this" + , "session, and with how many failed attempted usages have occurred so far" + , "this session." ] + + , prim "test_solver_cache_stats" "Int -> Bool -> Int -> Int -> Int -> Int -> TopLevel ()" + (pureVal test_solver_cache_stats) + Experimental + [ "Test whether the values of the statistics printed out by" + , "print_solver_cache_stats are equal to those given, failing if" + , "this does not hold. Specifically, the arguments represent how many" + , "entries are in the cache, whether the cache is being stored on disk," + , "how many insertions into the cache have been made, how many failed" + , "insertion attempts have been made, how times cached results have" + , "been used, and how many failed attempted usages have occurred." ] , prim "enable_debug_intrinsics" "TopLevel ()" (pureVal enable_debug_intrinsics) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 2dbf4f3ce6..ea565b25e9 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -74,7 +74,7 @@ import GHC.Generics (Generic) import Data.IORef (IORef, newIORef, modifyIORef, readIORef) import Data.Tuple.Extra (first, firstM, both) import Data.List (isPrefixOf, elemIndex, intercalate) -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, isJust) import Data.Functor ((<&>)) import Data.Map.Strict (Map) @@ -144,20 +144,25 @@ tryWithTimeout t_ms m = try (timeout (t_ms * 1000) m) <&> \case -- Solver Backends ------------------------------------------------------------- --- | A datatype representing one of the solver backends available in SAW. Note --- that for 'SBV' and 'W4', multiple backends will be used (e.g. 'SBV' with +-- | A datatype including all solver backends currently supported by SAW. This +-- type is most often used in a list (@[SolverBackend]@), since at least one +-- other backend is always used along with 'What4' or 'SBV' (e.g. 'SBV' with -- 'Z3' or 'W4' with 'AIG' and 'ABC'). +-- NOTE: This definition includes all backends supported by SBV, even though not +-- all of them are currently supported by SAW (namely, 'Bitwuzla' and 'DReal'). +-- This is to ensure the system for keeping track of solver backend versions +-- is not silently broken if support for these backends is ever added to SAW. data SolverBackend = What4 | SBV | AIG | RME - -- External solvers (copied from SBV.Solver) + -- External solvers supported by SBV (copied from SBV.Solver) | ABC | Boolector - | Bitwuzla + | Bitwuzla -- NOTE: Not currently supported by SAW | CVC4 | CVC5 - | DReal + | DReal -- NOTE: Not currently supported by SAW | MathSAT | Yices | Z3 @@ -347,8 +352,11 @@ fromSolverCacheValue satq (SolverCacheValue _ _ solver_name cexs) = -- The Solver Cache ------------------------------------------------------------ --- | A 'SolverCacheDB' of cached solver results as well as counters for how --- many cache hits and how many new entry creations have occurred +-- | A 'SolverCacheDB' of cached solver results, a timeout in milliseconds to +-- use for all lookups and insertions into the DB, as well as counters for how +-- many lookups, failed lookups, insertions, and failed insertions have been +-- made (see 'SolverCacheStat'). The latter are represented as an 'IORef' to +-- make sure failures are counted correctly. data SolverCache = SolverCache { solverCacheDB :: LMDBOptDatabase SolverCacheValue @@ -378,7 +386,7 @@ lookupInSolverCache k = (Just Nothing,) $ \opts SolverCache{..} -> (LMDBOpt.lookup (solverCacheKeyHash k) solverCacheDB) >>= \case Right (Just v) -> do - printOutLn opts Info ("Using cached result: " ++ show k) + printOutLn opts Debug ("Using cached result: " ++ show k) modifyIORef solverCacheStats $ M.adjust (+1) Lookups return (Just v) Left err -> do @@ -391,7 +399,7 @@ lookupInSolverCache k = (Just Nothing,) $ \opts SolverCache{..} -> -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () insertInSolverCache k v = (Just (),) $ \opts SolverCache{..} -> - printOutLn opts Info ("Caching result: " ++ show k) >> + printOutLn opts Debug ("Caching result: " ++ show k) >> tryWithTimeout solverCacheTimeout (LMDBOpt.insert (solverCacheKeyHash k) v solverCacheDB) >>= \case @@ -472,17 +480,21 @@ printSolverCacheStats = (Nothing,) $ \opts SolverCache{..} -> do -- | Check whether the values of the statistics printed out by -- 'printSolverCacheStats' are equal to those given, failing if this does not -- hold -testSolverCacheStats :: [Integer] -> SolverCacheOp () -testSolverCacheStats ex = (Nothing,) $ \opts SolverCache{..} -> do - sz <- fromIntegral <$> LMDBOpt.size solverCacheDB - test sz 0 "Size of" +testSolverCacheStats :: Integer -> Bool -> Integer -> Integer -> Integer -> + Integer -> SolverCacheOp () +testSolverCacheStats sz p ls ls_f is is_f = (Nothing,) $ \opts SolverCache{..} -> do + sz_actual <- fromIntegral <$> LMDBOpt.size solverCacheDB + test sz sz_actual "Size of solver cache" + p_actual <- isJust <$> LMDBOpt.getPath solverCacheDB + test p p_actual "Whether solver cache saved to disk" stats <- readIORef solverCacheStats - test (stats M.! Lookups) 1 "Number of usages of" - test (stats M.! FailedLookups) 2 "Number of failed usages of" - test (stats M.! Inserts) 3 "Number of insertions into" - test (stats M.! FailedInserts) 4 "Number of failed insertions into" - printOutLn opts Info $ "Solver cache stats matched expected (" ++ - intercalate ", " (show <$> ex) ++ ")" - where test v i str = when (length ex > i && v /= ex !! i) $ fail $ - str ++ " solver cache (" ++ show v ++ ") did not match expected" ++ - " (" ++ show (ex !! i) ++ ")" + test ls (stats M.! Lookups) "Number of usages of solver cache" + test ls_f (stats M.! FailedLookups) "Number of failed usages of solver cache" + test is (stats M.! Inserts) "Number of insertions into solver cache" + test is_f (stats M.! FailedInserts) "Number of failed insertions into solver cache" + printOutLn opts Info $ "Solver cache stats matched expected (" ++ show sz ++ + (if p then " true " else " false ") ++ show ls ++ " " ++ + show ls_f ++ " " ++ show is ++ " " ++ show is_f ++ ")" + where test v v_actual str = when (v /= v_actual) $ fail $ + str ++ " (" ++ show v_actual ++ ")" + ++ " did not match expected (" ++ show v ++ ")" diff --git a/src/SAWScript/SolverCache/lmdb_opt_database.py b/src/SAWScript/SolverCache/lmdb_opt_database.py index 167cdd788b..a401d2c27b 100644 --- a/src/SAWScript/SolverCache/lmdb_opt_database.py +++ b/src/SAWScript/SolverCache/lmdb_opt_database.py @@ -161,33 +161,31 @@ def pHex(v): """Print the given value as a hex string, if it is a ``bytes`` object""" if isinstance(v, bytes): print(v.hex()) -def pJSONHex(obj, pretty = False): +def pJSONHex(obj): """Serialize the given object as JSON to get a ``bytes`` object then print the result as a hex string""" print(bytes(json.dumps(obj), 'utf-8').hex()) -def pHexPair(v1, v2, pretty = False): +def pHexPair(v1, v2): """Print the given pair of values as a hex strings, if both are ``bytes`` - objects. For the second element, if ``pretty`` is ``True``, deserialise it - as JSON and print that instead.""" + objects.""" if isinstance(v1, bytes) and isinstance(v2, bytes): - print(v1.hex(), json.loads(v2) if pretty else v2.hex()) + print(v1.hex(), v2.hex()) -def pHexJSONPair(v1, obj2, pretty = False): - """Print the given pair of values as a hex strings, if the first element is a - ``bytes`` object. For the second element, if ``pretty`` is ``True``, print - it directly, otherwise serialize it as JSON to get a ``bytes`` object and - use that to print its hex string.""" +def pHexJSONPair(v1, obj2): + """Print the given pair of values as a hex strings, if the first element is + a ``bytes`` object. For the second element, serialize it as JSON to get a + ``bytes`` object and use that to print its hex string.""" if isinstance(v1, bytes): - print(v1.hex(), obj2 if pretty else bytes(json.dumps(obj2), 'utf-8').hex()) + print(v1.hex(), bytes(json.dumps(obj2), 'utf-8').hex()) -def pHexPairs(vprs, pretty = False): +def pHexPairs(vprs): """Print each pair in a given iterable using ``pHexPair``""" - for v1, v2 in vprs: pHexPair(v1, v2, pretty) + for v1, v2 in vprs: pHexPair(v1, v2) -def pHexJSONPairs(vobjprs, pretty = False): +def pHexJSONPairs(vobjprs): """Print each pair in a given iterable using ``pHexJSONPair``""" - for v1, obj2 in vobjprs: pHexJSONPair(v1, obj2, pretty) + for v1, obj2 in vobjprs: pHexJSONPair(v1, obj2) # ------------------------------------------------------------------------------ # Interactive Shell Interface From bbdfdf0eb863dc6148e9c12dbdff11d746a1ba9d Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 28 Jun 2023 18:50:35 -0400 Subject: [PATCH 41/51] update the SAW manual with information about solver caching --- doc/manual/manual.md | 92 ++++++++++++++++++++++++++++++++++++++++++ doc/manual/manual.pdf | Bin 475852 -> 482755 bytes 2 files changed, 92 insertions(+) diff --git a/doc/manual/manual.md b/doc/manual/manual.md index beb6047c2c..8c8969b969 100644 --- a/doc/manual/manual.md +++ b/doc/manual/manual.md @@ -108,6 +108,13 @@ SAW also uses several environment variables for configuration: or the `PATH` environment variable is used, as SAW can use this information to determine the location of the core Java libraries' `.jar` file. +`SAW_SOLVER_CACHE_PATH` + + ~ Specify a path at which to keep a cache of solver results obtained during + calls to certain tactics. Note that if this environment variable is set, + Python 3 and the Python LMDB bindings will need to be installed. See the + section **Caching Solver Results** for more detail about this feature. + On Windows, semicolon-delimited lists are used instead of colon-delimited lists. @@ -1243,6 +1250,91 @@ differences in how each library represents certain SMT queries. There are also some experimental features that are only supported with What4 at the moment, such as `enable_lax_loads_and_stores`. +## Caching Solver Results + +SAW has the capability to cache the results of tactics which call out to +automated provers. This can save a considerable amount of time in cases such as +proof development and CI, where the same proof scripts are often run repeatedly +without changes. + +This caching is available for all tactics which call out to automated provers +at runtime: `abc`, `boolector`, `cvc4`, `cvc5`, `mathsat`, `yices`, `z3`, +`rme`, and the family of `unint` tactics described in the previous section. + +When solver caching is enabled and one of the tactics mentioned above is +encountered, if there is already an entry in the cache corresponding to the +call then the cached result is used, otherwise the appropriate solver is +queried, and the result saved to the cache. Entries are indexed by a SHA256 +hash of the exact query to the solver (ignoring variable names), any options +passed to the solver, and the names and full version strings of all the solver +backends involved (e.g. ABC and SBV for the `abc` tactic). This ensures cached +results are only used when they would be identical to the result of actually +running the tactic. + +The simplest way to enable solver caching is to set the environment variable +`SAW_SOLVER_CACHE_PATH`. With this environment variable set, `saw` and +`saw-remote-api` will automatically keep an [LMDB](http://www.lmdb.tech/doc/) +database at the given path containing the solver result cache. Setting this +environment variable globally therefore creates a global, concurrency-safe +solver result cache used by all newly created `saw` or `saw-remote-api` +processes. + +There are also a number of SAW commands for solver caching, all of which +require `enable_experimental`. + +* `set_solver_cache_path` has the same effect as if `SAW_SOLVER_CACHE_PATH` + was set to the given path for the remainder of the current session. + +* `enable_solver_cache` will enable solver caching, but not set a path at + which to save the cache. Thus, unless `SAW_SOLVER_CACHE_PATH` was set or + `set_solver_cache_path` is called, all cached results will be discarded at + the end of the session. This can be useful for testing, or if the user + does not wish to use LMDB. + +* `clean_solver_cache` will remove all entries in the solver result cache + which were created using solver backend versions which do not match the + versions in the current environment. This can be run after an update to + clear out any old, unusable entries from the solver cache. + +There are also the commands `print_solver_cache` and +`print_solver_cache_stats`, which print out information about the entries +in the cache and usage statistics about the cache, respectively. + +Below is an example of using solver caching with `saw -v Debug`. Only the +relevant output is shown, the rest abbreviated with "...". + +~~~~ +sawscript> enable_experimental +sawscript> enable_solver_cache +sawscript> prove_print z3 {{ \(x:[8]) -> x+x == x*2 }} +Caching result: c6e8f2e6ea38dd97 (SBV 9.2, Z3 version 4.8.7 - 64 bit) +... +sawscript> prove_print z3 {{ \(new:[8]) -> new+new == new*2 }} +Using cached result: c6e8f2e6ea38dd97 (SBV 9.2, Z3 version 4.8.7 - 64 bit) +... +sawscript> prove_print (w4_unint_z3_using "qfnia" []) \ + {{ \(x:[8]) -> x+x == x*2 }} +Caching result: 975f9aac6d54cd54 (What4 v1.3-29-g6c462cd using qfnia, Z3 version 4.8.7 - 64 bit) +... +sawscript> print_solver_cache "c6e8f2e6ea38dd97" +SHA: c6e8f2e6ea38dd978e39e3a8a2a1f64d48e5a51412ad688e6e8d1d52285648ab +- Result: unsat +- Solver: "SBV->Z3" +- Versions: SBV 9.2, Z3 version 4.8.7 - 64 bit + +sawscript> print_solver_cache "975f9aac6d54cd54" +SHA: 975f9aac6d54cd54206e5938860fa43f89268a4545f0b01966291f034085c305 +- Result: unsat +- Solver: "W4 ->z3" +- Versions: What4 v1.3-29-g6c462cd using qfnia, Z3 version 4.8.7 - 64 bit +~~~~ + +Note that to use any of the features described above, Python 3 must be +installed. If a path is set for the solver cache (i.e. through +`SAW_SOLVER_CACHE_PATH` or `enable_solver_cache`), then the Python +[`lmdb`](https://lmdb.readthedocs.io/en/release/) library must also be +installed. + ## Other External Provers In addition to the built-in automated provers already discussed, SAW diff --git a/doc/manual/manual.pdf b/doc/manual/manual.pdf index 985611f3ea16e537812bb7ec47f11b9e3ca2c682..af4e36a6af3364c632d30ac30b0d3ddd4b7b2bc4 100644 GIT binary patch delta 269086 zcmZUZQ*fYN)TM)t?R0G0wrzE6+j?W$>DV?qHaqUvw(a@;x%z9S>eQ)qvCi$OwV%EI zhT;!);-io&iAm5iGqA&w&;A=;fn{S!L_q^*V`fVS_UHw;CTbt#m5f+AP1;1!G%mii>ZxO< z`KDj!aX^bKVzgx`E7>*L7K*~0p@?zm2tX}frZTw5j^l9E+hx|;O<84jGB@qTv|{bQHFtW%^l&NsJ|Q@s?oLq$AzgFpQ+pQttEk_~@}>zEbpZOzL0 z+JxERyzO@!akCc}BogQLB;B&GMzb&i5U@~bR;Wqf7CPso+?ile`Tpf@_bfW_6;1IB z>ib?K*E)8om7ZFK5f(gd+RN-X@>V@%d zEZuh{o-eLCz5KJtvBb4`)u`~5ylgv4l!rG#TuEpL@0XlxM2whBT-zZ4i< zbYAn+M3EW%A_W=WQvpS0R%LVpqMz7)V6VpZSq!La6hGJVlg#Z{0!S?3+KG!cv@K6F z4C;Q#r?R*-4wi~Dg_%k+4e3Weus6py1pB)zH23)?;vygTBeVA0^uVaTG}O#2am4+&xC#>fjG^>Y*@VT^a1zH&6)*}uU@ z;IBMP(QV2|@Nx0qYgYUUY<`sjEymK^>1^~(O=S%Zl*}r5!g!LM#*6Va?BK3KW_-?f ztU|w12(dSLtJ0hgsadjpzpt%H^&Bk8xCZHTYa0JBr|BXTr>SfYx}|+F-VtKX zCl~xf17%8{gzy7pO_qk_24zbQh6Mh*Cn3a=C7}dC*^|SePM{n2Fdqo9>`&kdm43ahp7`+95zWl9}<*K)IU4@xx&;S^sSQ=7G@uU@5&o}vGJ$8n=MIncet-Iotn720pp6whx6B%h zPe5+qf}FXr?9$_}ye09p;MGIAjBYNNUBQgdCb7S$nJ?PN(@}2+laYi`Y{lWzHD0Dw zjw40sZd~u0K6jKK7ex?kZH{X`&hB?7UP)6dg3;o9yqy`+@YeVz zo3S`$tA5F|sLRZU8L8Wd0rlFOL4|R&S3$L6R9=7c!AwzHaHnXA@7q_vpiiB1__vHd zY@y++CqY=jGw9XetARzG0vP1(+&!1`NGr0mAtV&1KMpO<#)85zcr0V;)?I0??5;59 z&9;V_@apxk_Q9cEW}YUFB!E##_aqk7EvyI!nv6(l?eK@$%}v=KwwNaWvQ?XJ+^75Z z?|72E4M7wk*-*@8RP67y)}jtDZhTf_$JEOQZd%KWVv)s}5nGS-=J zS!Ca3aWg@+PpmGyjKs;D)lrB9*pTc?bEwWjEyPVN_#$$Kgf$FkX8;tsXE5}Yq`M8~ zV~OMy@gb)LE?+Qj@b1iYrgE2`B8MHW0&3zM@c%;}JeJ%q{4={I^Ieud1{bUwhnvws z!}b2`Gpk$DqI}4nqj<(gP3?A28^mJ|k6`F^RZ}lU?1@U&A7g+of!tAx?Ka=0OyLzbzGY(qG_R_rkb3fO$BKpputOe)#$87&mFi2F-gjgUBXCJ!$9+`prXH z91>0-1VFtWCV)k8^ z0F4el#gkoUE-1)%fNp`&;--2S1yUEoOlMbTCS&6X_WY-> zd$~i`e9tO8{pf+44OtTot-n>JUT(w#ReT=I=X%_ZJtNl{WDB%(5z_iuWixIoskN+c znrHA8z>#qBAjFV;$1nnkfV192={Gsr8Qy zur5<-el#aLhf2M%ZE%VCB{qRHkYYZCrj;yDM_+S!yRcbRV-FWMx;x!~bPb!zY0b|E`l%x)WQkIyZ*DB&(!8SSVE)Pa+fQ8$? zkd+EEEVJ~fX`^HPU}`wK^e3e+vdu5y$F2Iq%5NEHo^a^9MY41}) zy--W8r&{0u7YaV(+J_Aj%y=I;5F@IN;&k=C3I;*a>mz2K|AkF}r9V|U(*+|t(MWY) zI4UsnIy=<&xU9P-oGOhxgveHL*F;8a+d2VN&!k0rNCQdjx#GEB4kD$X4ir!{7-;Db zao&ckdU4N&K?=Bl**47B>ie%l=5y(lQZC5EL_80WV;6_H39OvLTC_&qq$;ufQd=L# z)GAUf4hr;NtP(f)s;S`svzcuYVEsh<>v8Z~|5TyKo0HL1E;3r!BThz6Uekbmne~9c z1`nnw5bl?mHln$3OZoMaI$&$E3Y{MHr@J9XOqlo4W;xXLCW1w=|xZ!6cv?cQk*qymG5jA_D%~@*=pkmb&9_!a-|ucTs!{i*a|e>!`f)G^Ob- z!?DEOStCSILoKz}a+Qhw@47T6A4q<+NX8mrM(&Nx1wJChR;U^?D&Uun2Qq4lL5WQ* zGlH-J%^US%qkoXB@psx92_r3fMcw?r$3|3c>IRW#{zGg=idB30hWG$&b}OzYPa<%^ zR~FjntVSEfVi+zt^IvAH8XwY7X~IwgeDU4m4*egDq!CgsRkdJiRi>EGM}bZ(){E1N z4H7#|Z<#Tcg<4q5G643iYt^QFt#VX_nY3ai8Q+F5eM0MwDkypv=?~qjHh;7lk#b`# z@QpAiap~A#3l{gM>grNs^)21F*OJoXF{kGGjrvE8ba734$iepj76>zu)hQt}SpsYS z=+M9iGaApQZCA*H>4es@R+Hoc^CN?=Gzso~P?CY#H~ zzC6E3g=(FrOVA~G5)6NtN^dO^stRk}_m3O*mhWfg$PSJ;Z`0}hc@Bs*uGHn#p#!n9 z@Cl{iuT2ewL;$f11fFPLDK(aYU4iu2=y_dn1^dtX5iQEX-3}`_pXT0{-ro$}RcFJ^ zrPC;nClLoddv^?6TIs>PF&}O@dq)!!$It1qVvqRY`B6;rB0i(ni@&cs7!+m3 zgu3h>#gF?2{k9OR2i>DhHmc=esO5xJt9J)8$$g3>wi?ikj3=Zk*t1Y`JCY?>EBR_Y zDj%=sl)~F3B4pV-B`gNmIW`j!LPA5-sSo-G>c4Qp#bB<@R4*V`JN0H#(0d|^?G9@q z{bGOQlRHty8lHVa;EB#XCeQJsVRJJ7|Jjq3iH#-wHWic(*x*9^$~9o#o(AQqC4Y58 ziWY5U?7VDQwoS5eTw#D6Ei&zToUA^S{tW=dQeGG1rY!VNj#RTNjdaQAuQ$Z({TJq# zfBgkeZvZB=Cb4PM)E8|cb8)|i<3vVNnv>m=c>(9To4$L_mW*!Oc>hq~Y7Q?gD}L$y zbnoQ#oGc44^T(X@ba&!cEqEwm!xivu$oM?^Tu+&ry49sVe3XsFD)89SCxojLOpEmJ zpi^ZW{pOKxQuy7~!C6vJk^WNNGSjPzKeJnBA4NX(fiBknI^KXh&>3Rm^zlBtUK9{} z^T7_?k|_TYn1I_kq$2(2P6r1Nw(E0>8?+KZZZ6aNa^7Ngu&^a? z!gXl5(Vsa+@0}W1iW|H8Rcyj(x`(=NF7RU)KE)pwOmr=i`<8VGDx^Nc6PoLfCu6Rm z>q$FwgpLI{8oiK0tTcR}m)GZ~-}cnlB+TZso0DorEUYN9O4Iy`-X@StOix$$S8urN zhjs;&n`2Kq*;o4Qa$7{sssmg)TT81N-D!^fxUs9v1K8R`1GMq5mN`Zukhp2*^nSdmG%m=ZAuS)>1=R0ZN$@L0^gdxFJv1&X9EPF539;fCv z`?7wMDI_%v(bYz&8jxGl)Qw+L_`kP*^VF#Sev#(V69*QDJC0+;K(h*U7$1VjCJ|z5 z?`q3XpoP*_Vfo-b=u}A!P$~$?sAX4zc@ObxOGvI^@mfWbD_4G{?2zpg4*W9d%a;Yh zD?Fqng8#_N-rrrB>zzaL7je!!X-Sy}5Lut!hyS#!4n!S@pJi_Scj7G?bV#OIc0a5u ziX9NK$aDROmmM}#?1jP2AI3H<*~jhU_BI_9n={LJpX*jl1E=bN=NZ5`H(%i@2+*!ih6RXeCCK)BDoFc<0M@Y~{Z$h+5 z%n^iL=C%mRw#VN=(y?_Mq14QsGFn&7WTiwc2W)0aig<@*XxkKUW5l zLrNyLh6!I7njlf{d!Rk~RYkMPkD5)|?i zfH4#!3Zh4U4XfrRt?Fo32wL#GbQ`i_SC65e^;zoZtoYQjVFYW4Xa_9MAhAkd2RB`? zjC_tke5=TEaU4J^M8k-ADpJijE8rIpx-J<6I!wR6r{-eG>S;~8v?FCFF|z)GSiNwD z*L=Ciy&M>+KeK#4p6ol!Mu5bsdn_!@22<1%_^8$MZ(3a+DPitb6dYTpE`vvr>{D9M z#)dN(Vm^#zMR$q~#0A7Oc5EfRLnJe@GHW3YYM4%vS`^!`WMwxTiAC?BK@ANH>h9eZ z?-y*|Y+m91T07x@fYe<&6G|CxATu`QOh4&3rqGB|<)Gh~RNZ~V;}_gXZe^C4kd4_w zJoSRb;Ab)gp4&Pq5)Sc^VNA@X7dSg8zir~jMz(EK%5TL*VF62r$Vw_;l?WB3SKciP z^KGwqmRG*Y?&g3cr2hi|TKvH*Yb!OZ+F&D|XZ$aSZnpZoAG5TY8Nr}M;v0H@JE_{W zSNzf5iPea{GE`pLQT(_1UysTP`HTSmr08GaTDPBid@Sw*qV^c2F5|(NxDduyNzE5B6r#l;!ScVzy51zC+ zC&j(jHnU}pxO=2bFupq4x$VD$h+tulQ#e^*c1`I;_R3FK7T?_6FE>{Y2Ycz1Iz0Ue zJOK%kN{$swkHQvkMI1!Q@~Uysw1f=T`V9EStv&S8Q`XS4imrqUS7xAR6vLSDQP+Ci4H|~S6cZT=eBN;DgX8p zfeE~SM0dDUS1UyAxB@w z505#o6$P{PeEqGFR1d>xUaRhsqeBsrU-V(S4Q$f9VSlCPZTEZ}AGc9jD1roS41mKt zh=rUg2#t|QEXAl5&{i=Bt}T?qV(i3Z)B;qmKY?*-lRjGNvUB)NXk;kEf9@f7jl}u& zg-#kF=Y^M^M>`f`w}!t7 zW*bA+u7Mamp%NEgx(*%k@mcZ#9b9bMr@Y7}w*6E$qR?^UN#J1tVh4f!Xh}wsO!5P# zEI@eBNIQ3x@ro3?p5!{eZ-P*;G?*DLjyKp(uiF>~m#foQo!OPw$tX)wTwiS2@9a`)YY(uc2o@cb% zs4uu?6%DIzsv_t-J!8_mN2+H5uP*F12-FB{myjt8>0tuI=pW+g2t&!S=XoeT7U15K zhD(+O7Cb|y&Q!EVv3ud&UwSLuD=IAGyVHc(vpzG$`+BSQBU0ivpwKqKGIO`y0-|X4PjZ9gewb|+4 z%Zn^Q<+@|$ox@n#bx^VRL?FNuWHiIG{1ojpP-b%L2s>`Kn`4nE%*PXx0t}?}2O5gz zAvXbzcZJ2gO^z7SCIOC#iDV|h8~HYhluFL3!~(duM&-6KUoJISA`3Y_TJ31N%7-MQ zxpbSS@K_?~2lYa2ea@(Oym$J{PqyF?_WS{2*x@m#56rgVPS{ZYjOntn1wfu_mD5#< z7|7fD^qLJ+x6b$33jZP?0guaF`sz@M+k>TvR9SYzD(fCowM{2&V0anViJoRml3sP2!qDMapOS3Jq7 z)08WV-I5tvuHlW?__zZ5v8NEos}Dxd3sYG$h+LZ2dDb-)ALc!v11t*OoY2aBfUIEj zz756=R|rbRW=P$}OA3v%4fojm_AKePzhEyTmJX<6B4 zswP&^_Cr9t5ge$LETByI`Q*d&tB=X7-_zZKa;X zMf3`8z9>${eh~S}nKPaq9xmqw?Xh7_ELfdCN^oUZ(_D$tEmW;o-(AQuWl@lnb^cUz zyK$3kzgk{wd~U%%LJKf#+kRg>zn$&f*`x#XEQ#bi+Pa%`F^eP$O}DdEk9+ASgno;* zX|yJCY#V7W#zoJUQ!3(vDxD|x6$@0OrLNKtt(QZ{KGt2J|53H;l|}a);2;~Q{z~7cP40)VR0$NHaZV!7V7z1|2Qo7)Z_!| z%KO`aFKVZjaDv5oG@oQYFa3VKw-!q~d_+yRL2Y08^2QufX@Qu6MbbiSL``{xjFEK& zh7W^U+H4Q(GC0j)O{O-YD$3!{CzSf94IbVI%+?2lE2Gpen|Y!)d;EJ0rL-jv9e>E? zVcow-9PC?z2~S0@hz(qjSV{Tm>oy6fT;9{e^@7Z`=_lC`(B6fGDfKbzd2RL->}J(& zPNR>BsRwtkrs&0WA}o%YX;~B!_P%{>%96c^fv2EOln9lg{dPmkNT3+|VR`K<%OC2H z7}{WAw3qpt2(Bfrs25~;BRR(XX0T6G0>H_%{tHb0I|!+$o5h3Nn;%2%dV&MYRafn* zCxQ)TmOhJQ_3W_`E9^Cc+H*|*t=Ui&E32s|A7tuELY*m=0Edg-5^@LYRNjGA2( zc5Q`wy$UI&B|M<+71W!bOyB`p)7@S5U~rr~gP~Aow?x`u;$Cg54zl$i83VFmPs9bx zP{yWt0!IC{Rlj%TG)6#jB3bWa^#z=qE5$1t#jA6a(vH7DTbOT;|G{uD~0qr$djO2!@r~|04wM|BMBc? zT$Vsys!Zt<-2S?3e*eBwsln_z24Rc3F{nWlIVP}mgcTQ zfKC{izyx0>IV!FawQ1n-A-JCn@*Q@+*p6uSa{Bz&I>NsO1}7_31dJDvULVxJ$oZYP zOKtH@`E|RBrn;preg<|k=9>mLw+GyYa8;x+!@ctAhPa+b<#Pc4DUA@O?{z4#FGUzwL($m_?b5$zl3UE6kC>F{! zv2#C8NFFqnAG<)16Lyy=^Vtf5#3m1h3b)GIbGZ4K;M~x*vYH7wUvS}G3wsFlXJ9d6 z*30%7R*vbjFZ7d}XBlN4UQhF2kFh-jx zG&RAHPF|cnhW@TV*IVm>Cw;3ZF;dZY+KLASHYSi0S@-G`rIdtcgI zY8KG-EBO6bd9Nmvt5s~Is+@N`Cf*jp>jCL4G_@6((!{N=GdyJ)$azM_se}Kapeb8@ zlFzD!jzbAk#R31-9HKw*;lG?J#8-(>DjHK&Jcb-LGcP&i{zLIv=7J4hx^NvY@W~`X zkS(lAz?CHNeM|!CU;(Comy5Y5^5^IaaS#yH?QL(LWplZkFX6_Ly+LsZUKV>Piqof>5b`5bsgK5hb<|106rQ;c0-M{M5?u7 zf^zm7BFa4ogH&)8g>W_^4qAQj)lDxX*dklxHFwH(Shts5iTt=qa!*HgrtiXEmDb;>Q}Rn-OAM?P=ZXO z<=-}^K$pSlbPm^mrSWp5%^-L;C|0`c+0|VG;bitEQjT9`uchTQTlPWWjjbaN{e?Tm?hZi=Pm-HbPuV^1uIxO`K*N%;VS72tXW{e((1vE2ISIY@ z+)@)Q=HUP8E+V=hE|De<3Xq;$069FqYQEJIAlEkcodIik#UFB&R zyE!4+(2^vIR;T|&Pm>l|1i`{(xV=HHY*`=^qbg;#FBuWMtQ>_cfHRZ6ao`4g?ZRvf zv_oaG)yzmOadNv%IlE5vwMW`OhLaR&{?#@R_mVm!wsGMJp+yTH@uj}pOY{r=%*uM-GNK;ONDG;Hbbj>&#m3`hB3<_OgKRqYXnFkjnEEhW(18K z#gO$(39EZT&zknzclj9?HJiT=A1g9{tk{CsP=C``2=_T4wn**8I!9+L5c{*tfDV*` zA`f*WzK4~54{7KWyU4?A&i&l*_e1}n<{ZUt_jkS$e7SOX0%zP6zHE*@+ThtxIXK!E2#HWUvJJlbZ9Q*?Lt5N-MIsmgYy}nH%KyxX;hE#c$E0*T@j=ObkE@!pptlOE2J*%o3?_DN%0oeezz(*MQ#QoQq)HpsH^uv9KQ7 z^f@n3v}73>w*Oiz$m-NUi;kTG-YCMCf3IOxy3+gjHIF`>J&tw|Sg+)GNJX^RUjHUY+%Objh3G7e!nDFSA^ z7+r3Rf)_7pJZ5*=bR)vlS*~e}0*hJMF@nMyFDh~zeS*SVRSOTNLYF{Jvwpkfp|X$y(Oj$-F#pKE@fEhnxEiq zB)pn~RX`Xfa^x5YNGIEu9dY9bym2na!(O^07!B%w(QE(PrGsZq0*wetG2AIKx5^t7 z%5M(MDK?Q&sCsW~e82|}Z}h`rNXeB+E>{a`keEB{XTQccH@>bMNx3+a+uo0NkcnWY~P2)$x>`_?YAwe6q8^C<+t-2RSysq*heEt^yD4 zlBJ_JT&{ZtS^rIF(iUuKIRs28mXSLbw6A(rt)gjvW))x;mCE{vHkjV?C#LcLwE^2W2_6ij~-55^l{m94Xqr7 z&6_4NjHN`8bz9j#bcW!j)G~P+;rlWe`5s>cUj$zUg8v2Ag)1Sc>s7Lp!xqqlR-xCb zw9D-NKcZMBkw&}{y+Y-G3GN(u2|nGrX@}{smeo$4@6p?vBNXjxx=_CrSAIGU+Kv91 z^~vbPyH#V>jBX3SL{b?4%6UwU;Lk&ei-GY*)uNZp*e&4gTbcqKA+`ryt}Vh#KkKsp8MVYui26eHkU2{>d8OdPXvyt@1^jEyfk7HU}7+2nnuA zU;XG25>dTJER<1Wuhy0(a(g&hv{EZGy$le}uBbR=d#rQEfp4fP@l5 z>Z}gKpH718tDKl{onJcnm}0Gx-r_F9 z(sbz~D!X4&6M+GSV-8dT*isY9qAdjy0FfCr2ouf%-I>Z0lZ*la``bPhU4RmiXE0rn zjRg#c%p~5YQ%WTLG2d^}d~{O~Abc`5D&;-BSS?Eaq?-E(g$uh6rCs^c5g;mw`4U^F zz{??ML9}X>ukbN0fEHTrfr%7v{*pS$)A2)lu9F#O1<6SfZiEStDVNc!a@zYcqxKDY zM3L|dDuh03m<3@zjV})I9yC*ySnf!4ZvKOTKTjx|sF!+5TWy#1UnFe@5=~10hj=j- z)Or+puzWhw#g37vR39ntNx`1iTS_w?)c}6dti!3p!_#l<;TM-!6wp(B?x)V8zaOV( z&Mg8RI@t)NsNVO3!{)5j>810&ZMq2+58Jl>^*Z(=t9Vp;1Qd;G)KBzIG;5;@$1$}v za>vT+XO~`LWrDe)sjpeUvcq?Yxmp9`SFHbnU%OkoehxlDJ(HS`yW!F3;QJJP*-P0F zox1ZK1FP&^=D%v&*OkafH#CfM3WgHKnkY4duEHR?quc%uk}kmK|kh%1o_U?juZ@hnWkMpMR;Hk0Y!wPeL^+Qddg= z^Z0_34@rpv^cAQYRL+J@AR3*2t8QKFMiTE)#9>zK`ima5QeF;8URr1}3Ip3u$YRYh zvG_mNPrtxB{Rm(oAQcB9l$-rG*|uPdqzHMfPh5X2hN5XW`Zg7_@An3Qj9FJo<oOeFdU7|z%U*d2&EhSHdGw% z2g!v1fAxe-s*|{hm+_zy%+B5SA}gWZ-?QoU5wRsZIG-}KGLwkz?j!^K@m1oJ^-NE+ zvFf;Q!4=Ey%7T@%_@!jCvb4#U3CH{dD z@Y1oLs^g{38zrm3t z7Rt=&DK5Ua{5l89n@71_I~BZHjNgU}LIevsujUOstXMy7mFFe1SM(6=pgyz;<6j!I zz5qi1i#X-vcQ(pR+)+{tRS@$jC!uusrq&TFB$6I}xpWEJ-n4|*2h|3sQLA0_M_zS3 z^UmlZ#VGv4?_?msa??r4>{(f<k^{qgPlNs%@0 zS!9ceTY<%ilgmL&0;sF}Xfo&x+y%n5Nv4}pUK1!C+g%e6yli9iHtY?*+N6{S9vv(1 z+VGXMRKT;*a&q2rEP}1LPLQ$GacolZLFU2LY2_!Vdge z^VHa@=#Fp?El{nj1E)08Xj~XrBlcVDQGYIy!hls+cMv@^w#$YSi#3-WC7f=tMtKvv z;=J##&eoclHO)yai?x|(i#KHJ#DDpyG@lOLg>-I`vXfl!z42X zB-eNn3ey$Jc0;x$Lc=+^u>Vviy=lPt)%9;sAK5+&bB`uqC{}JT7+=ot&K%gFCCin( zkaVy!j$L?SA`XQkr;jsL1>AD)%qTRxoExtARRwZ}42$DL+lEZw1OW9&JynF8l%hW+ zMSt`b%KbDEz{{7MxTz9HsI_)o(H@p5KOU65G4Rv^#Y%0MiP0%+)t(dtsI{Zl{ z;Lg^UhAVw-&)rsUt9#be+~#gy*HqIIqP5C)m-5!E{Z3J0*qjPo>k(HAW6uh`Rxpsr zCA&J_+8;D*i>^DUdz78hHf{_viqP7lw)2IJU4)hu9$iqxWc8p3Z)rl+azT~Mh;!861`{Da zzD%r!i(HkN>HU z=>?p7IH$8_%KltW0e3N|b@Y4E_LFXQMVDj)iQ2MR|3YDwJr+GT8Fu+_Sx$fMe1q#( z8m>q?h=1_9k}3>GIBDByS4E;D!YQ|0jtQ4?RpY`z!{ArTn(d%KIJ2v^JG@8kul{r=Eve#b(F@^4LF- z+Z`hg>%Gx*@bMFG`=;il{Xu0Ou_fERE7Q+E4z7X&3@FcSXg}e{T)To)gxx1i(myKZ zBsk+r)i`Phz@yUo2D_TJBa+@1J4R#a=WXr;JrUDPESrG`e|uiMk#fL2xT+C zB%|MjW1jxGPnpNHB6b@WL7iFl22{WeRY0PH&9XOC6dU`H=Xh!eBhkv#@j-I36Ws;6 za+EBHZ(AdOtBy`P*`sKw%);-(DKZj@7)=RD0Q^+UpLm(R6Qbq`>p~f@8ejNl#X@HeMGAEl|^O!kn}p&6&9miqnAB) zKw_Q4TjB9`S0SFSbJlBYmI8Yo`u4l{M28>3Lao%ye96>J7g-D7qFgtY5YobyAc{7Mdd^7A;EBlvgHNPPG%Mrl~hb6s8!(U~)+tdsPcPyH*d7X*An?i2v z9fzgYXqmlRu|0PI)T{lkbML^PI}I)UL_nD5BH+28(aPD8G#9NFw!F~8K^Uf0q5U+Y zuh@{F%Dat%y68!@0)fPE{r1<)T6fSz!y6Wc?7Y&gSF0ugh-VkiYTYzZ|A5M5*+0) z{cA3B_Y9Sg{V#{16Y=tNj)iPZ#}1(z5(=v`SI`P^`xSzCU9{%CHNr7yH!Sa2Twpe8^h39iE3-~EypDPNuN{Y096a+0gwfjUz8 zv^DJPu8@^MwYJu2$wIKInR7MuECl8Qngpm~cbi9Je#1v@MGplv#kMdJ4|_mUj8(@G zs#;LAB69>~F8?T@aXw4_5k&ojJmXtnZ$Z>bPJw_`Hz|7?jKMPf&-Q1~m7+r6y1gI1 zb!mv75s=FDu}6WdF+swx^xhnMKM_QLu-wfq&STI#rxn+S^P|H^lY|X(t{gZ*EqJng z$VzV~HjxSj5iLc|pg*O)_)Xr<@v+_c{jbsa+h_`2#rCEo#{;uXgI$#B0%pq{bBY!S;JnZT=d4@#_N;KQI;k*gq z-%5nqP^xYIjH=v?rf-&cCjSH{H%%eOgTC`Nzd?EDPf|I5Slv7NUVwP@MgyuooUhOabo5EqYHLl0=>u& z`shmkt(&jvx(+2cjt z{+_cOqi{MCA;n4gBLP;d}jp;EC3w}k3Y zP1VPtSPfZ8?UX2+HNn@pnGSKw8q6BjUSL}|>%pwT?E?7jYm`^Z&bYLWe4vTXSYST% zuGv|uBwJJXQ*OnPi?-A5R^tDb6cdCUP+)Tvyz2LCk{uyNi}>Dp>@@I=KMH<;gZVRa zJ_!imbl5T=ozA%UFVsJMsnx5{d=+gpy-bCtu=1ZOUPQ@2tV$iJ76Mq`1wzcxxYyB0 z>6lH-F8~FFZZ2aihuLmH1<}2xm*?|93tb>pD1)jM@@jiVIfr<1z~XF(dbitZ^NaR) zq||P0h$w2bEc?>g%HQ@A2J6ge_HH>P(Gyo{m1fgks4<4p-1D2^DLStNF9O;;w%37p zg5Jg7evw1kXP2I#QO-NdpIYcFTl}XDLgGEArvQWk4d|<@fJnxBH1XfbZH@ry;tE2R z&N6-qRFo`J-KUaj+yp-`nu;Es%7LFj>WQw<`Y(J!W5-?~eSW83aL?a^p?TB=^|288 z_ijT5uV?tGMm@Y1`)ltH*aWzmH~a01sr+wiQS!CqLVi>XTY_H5q9FYD9k9p*+4+i= zn}DRgA5(yAYQJ@cL7k_tB=plkb;K(kbA_##T$4%lnqGG)N`IVG1S7Tz&d(XKEVKRxEa?aJr9ApfQM+)~$IC|1%D~MfouWJ}eRA;ZcI* zsDbjX;?dEG^JoYjZco8a!9PD^->*^4$APa~SBmdpvMH=NqVS0T!Yf;-=M1$c38{7WMbSm{M_C<6I(DAUmHk*$n{Jo?!`3-JciIH)KDIZuZQHhO z+txR>ZQFLTvAMBrZtR?W-#R~>s`E_U|G?DLJ=53K-JkZ8B0&X8R0ZJ0avp#Z>HhOe z8WBJD-S1o5Y2Zd98(#TdJ`jx?!^|g@lSX#nH-JXPotC28HGbcvkKNc1)EX0d#tE*b666UDAE+}<84F=dK zx~ClPF65&j`kEQW<9+@1zaZ%O54p$8@gMyLtc>i0|GQuq#I2HJ;_%a_)IeYWMn|R0 zj6-M)U8R-*w%KxRYamkQQM|V3f$C{vgOh0_6PK9~Z?}9H$|!tohf?f<1TdheVo^t2 z;o+zE3;S38=(Bx&Ns3`CTrx|v3z6f(Tr?{h0tV2P$`iTC)NY+bZTlmatvO%Co$m<# zb(auj-I>!b_cs?K)uh>G#L;R1-fo@Q7A0coW}}7fkt@5q+q#XdIfs@Nq2JVv#zC9r zTmLRO+9{@kXdS{#@zG5CVdF zv%A}057aHfj&m6G0Ue2>IH@6`N3rsuk;dfrINE`hR070WWyG#wX z#Wq`Q`$;D=BynafNolf@xBv+4{Wb?B1PsNBSV`gQQM(6rgLJ=je}B_hoJtd9X+!Zc z;y=6~VCkcRC}}>_ouvgRl=8&|Mek|ShKiMxuf_=3IN&n=zU*V2jSOr;QpcdFeD0+4 zh)dJcaQ|XeCOOg8e#}A)GQ{)q>C_NvxWNnGp39a(pYT0y8?M zYV!P389rD&BsV5&yI`V-(zmAhi%FbXC6ZxAsGlM6n7fJ%70tAO&0DM5`ijrANe)__ z^2uhLHQmxLG)40k0M<3l(yHoQ<&H{k!F$vN*>d2aY8b^cL>ywAZ@MlXbz;U&Twcz@UguL=Lcv#*9gCO&`~kz_@&;u zo(rgdL}Aj0x;C?sqRw--`%o-=!!o{XEDnkh5cv0w{O?(TX|aJ@?Ye#nR8S>A#W#q}Cz!lsHZ%WZ8%)0-dL8&#?nXlJ*K zE!ru7S`6k3qYaW%5iB}cW>IYnaIHi|kqEP=vg#GJ+;&+k+=y~mG0(U#qbZq{zy{hR z`{9Djk1B(8x3G?8CkaF1q++K0Nfe_P__rg1xHsc9l>7E$B&d-nT~<*V#81zovE5f- zC#5XIbXC{0D-6ub9-q27h+}A?NhwW_9aRFr$#0{k0SIZu*7J&E7K|BYN0UsBktZ$+ z;JiuG#Fwuxmh_ZA5scD3TxT@{?9E@c7h`UV_8Mw|r-Bv(mwE5WpcdjHYX*7FzcK_% z`V0XRfMZMI)3F&034`5}xoyGPmIxMr04=Bo&DcKn44|*k;RsTx*Ja0cQ`Q`QCma+Y zs6_sp0PRH_!QvDZBZ@hOFZbp$gnA_N=3A%$us(u+U69;RSR?0&%NU)OP0NT$RgMP3 zyd%Gx$&uK0wJZVauy-hdXU#EO1RW^LGs}cwZx%<#=9Xl;#?5=+|214CTViyS?3~y@ zab+nvf);Y~O^PanOusjgLRnXBf3y!^brT-}Imi_d&mDLuQ2`_^C?#jZOK`xlqRAhf zrOH5Ez75}L((_my1I-OYmz_@{eTL>HIFB(=p)<{THL)XgH1{&3Vd$7Pt6=u0_lyL{R1^Z~@ph)coD7D#W~&Iw&L*S*?jZmA9fVn~98w z4@BQtfA2g(RzkG&_^<=qQv;{|Gzze3gBxImZXZPwF8vY`r2zZOI;c^Js!gqL2bSvW z>aMKt&)6rlFC(XDcA+vS4+Z0UQ17y7bC`@bo4Fsm-!M!g6#45RMX$|dUXR>q?ecg-`cG|*{MBl zX~%>=IKm#Nk|u@%d+7NqB(@!S?MV%rO2XlisW_SdN5!nmz0Lp1345cbK>I^%zX$u! zWixwfB`p#Kx_PqDkQ+<@suD$}H>dVPb&4!XdHRckRc4RktJBfu(wdQQd}UHyH;=?` zA0a!5vj>gS$W;oka2#fqM9h)|2HnOp@WINWq(rNVNnq%uuprlGtAy>GGJGQH`vx56 zH^Um}P%mh-(!(LQdD=s;#fG1aw9VqmyCHW(&fUJ&%AF3N9uHwLLx6IkIL)*mlMyvm z2GA?qtk#r2lZT4zB`BgESn_vZc(-GDo!z7t{JF6uyWIwGq;2=DJD1Y0Bd*RO3SL>7 zAsE#_s59wnm@nmuEvJF|{2EqxCwe)Wd@=C*ILZ1r%M)5pj=#-zjsn{w+#3)A5=B-O zkK*jkrBoSo?30-$f)^sv6eQ69%eoTWb&=wFB->ZF?8ug zUWFo7zhWl(ZPNkF9CTcF(gqVeW42g#9_PckgB9YnlDGCjdH?ykL%-#V@U3^;=Xv)h z+=eW4Xap0AW*119oA8MA9d+e9j`y*cmNbPMWl7FyNFK4IAHf0J{AT& z7TyHF4rrv?Ov^^^F5*Q|QqLB`U);2U@hTY-EaRTK&~X5sU_-pRJN#Jdr ziHY2ufKbux@X#6?O!pY4DB}@u7+AC7^>Jo-C}|D!IbC@OM!ZVW^h(>h=!&z?I8n)i zCJ|E?L;Sjye+F!SEG5s1@yaxeQI2>cp9eQSp-+e69c?8vIY?%E$eC+}})T zDYn{v8FqSrsy>F?(DDv_iwUxNvIFjf@sa#a;n07`s@Lt%LD{G6A2A^k0|a2hKis2m z0T(YbWU3jWArt!mkToeheQ#W?HNlk>IQ5&Ca787M@LAqBi=eNENPOZ#lC&=*?+nbR zNxjRZ&JT~cR*ylFi|XzPgFs{iN-$f5BeOsg3P3T+Pv%YVz#^3b$|+AcklZR zbw~<2#o*ZL=gS^vUTHthjw>Bi$qF%BrW@;wWHm}a>Aw=OO-_`5{oqEAQxk%U#Gmf3ps(ITYGw3)he)yt8 zS%VB9NPDr%UgI!sIgFoOT%K`0;p@@+?UC<3&OfgJBiUQCwze#pGqbBKrDi$UJoMgu zS$Xh?zJ)1K@6{mCcz%{EF33+VnGzf>R>a|QI(HSTt<^VOO^1a9f7L{8f9pZ_w!T!o%DwD)!Y)bZLHWW5S)6Htg*- zy|ckq;%+}*Q}sU)PUl$TX)*c~2eQEh^8Mejm~ji%dZ?=^CxV37pC zr6$!BZ`*fHkkfl{H<>854yz02NBBr&H(MP;Nvw-enQfNMPVUN+Tr@ib1J7?_wE~}e z^lrE?#50F|Prvhya_ZlPyG-yUQUmViuIGz*_Nqc;+-=5laU<7F9XF67e=>n9rFR#( zP;X|Zq!Ga98gBc7KcT(db)=}>{2K37*`dS_(he#S+);}}?U`@oj*rnGQ( zYF`WgIe#ktXgGQ)pvR>RNJ-sx%K{P-D9D;$RhHq%bqMB>;~(;jFPFSC#H(pHML{op z$!G}T$#^bAfX)=hG}!h7l3$n_o0`L-St4l$bl9}QH zvlw1^Ci%;plYb}+Y>Iq}exS~9ij11&jEe~NBqU(>0EJ}l26=ngy#LO0oTISSS$li_ zg6p2`sN?m0-IvG}rS@D-%0auR+bBiS1sB}JKnT{a+%bi7m;5Yip1#tqqP7xTN{k$& zuQI0ZjU_z-6%KY;zG|)E(vV8Ojiif!e7qMrzBYCdol#*o`*|D3b7@|D{C3TNp;Yo( zRkmgtFmC@c*%UrhwUyh#Izu@!FXf}r0vfu9G6# zx}BxS5IKWDhyt_asOycqlh$H0rtEdfQQgFNXWv*WUz$7F^pR!Pf&9^c5oG$Rpbzm zBx#+C`(j?%Q%FIzOS=(PHNc&Zm>6R>?u#w9@K0C(>T-?UAqAU1t$nhE%dv%!;39v) zJ_#f%ExX3_rSTfPr`NPdt9${}*5ca)eiaeeI%&3ZW)~Hwq4hP1C8r!=jtMp_;LUSe z{$GK$Sa_JMNlY%7THUFh+FzlwSFcL|)-jCmq_oi=IvyB*YHt>f$?yQv3K2f;yZ$f& z`>P{#nxgjGV2Q_F0vV&p7 zJ(R%!DFVrzq>*SA5c@kk38`&yyyZ7CXCWCmROqdqD>?9lXNoX{zZYgD)ZH>PX8>sW zp&DA7CNjAybUqU4i%Rj5!g>8z2fLO+Ch!Hld2VZlN;=O#0DSF=5-fu= zWk+Gk3=#TIYHD)hY?Ia1Mlh1UCjG;C!zBUc79*o%rhJMUQ7L`_3qXAJcN$0ifKSuK^>${Bq`oehrkaQP6h9K zrY8H+$`X2nAHsz$r5pge{M*P5^npn+NnQs&N9I7oaa z7$7B@Lvh#&p`Yn~bAsB4njj7T%dbnh8>Z@Uhqi5 zFQAULyq>^B;^>EI*<#wIRL3YBYrIw(>jfcb0U*B#Pv2i#ur(la zl@RfRLV#i{x>k=RfWFFR{AEN1H)^>&8tKHDdvFj@%Q6z-J?$wF@#p!DvW|# zRm4>>J8+pIV4?N0%b2YU9AZRY^z^XZhM_$>$mS*ESeXmGgguly;K00WzeQ-70{#c!de#=(uJoCO1}u8nWf@t z^?^Seke_47^ExECH%9*H0UmRl0s)6F-`Ac-yq?X>$D$Zb-RSb=9Vx*QD*Z|J^LgIK z^moF~-1zTdVw{R}GHqljwoUSm(*RS>$6o+tRw3c}`xjqvaqP#+^#O3KA$ZgVyr)kD zmQcm{ml;+K5Brs0S$oE$kKF0vts7djAoobN&x!^#2Ld|A!XJ!koNML7j~2 ziwF>ndS4C)-RLem+sEG+4d*Qp8n8UqBht2_v9jqN`MTv8!jdlP(W*b>A#LbD6@w3F z+4W^nZP}XoIIkAZf=MeuG%vA@<)Togze(8+0eN`zVUoG)UWwo(Eoz_ z*{WreZO@x|*}h)RZr5t09wbgZzj(GsqXA%%dolGrx;MWgeRkJpT|aTh9U87-*;ef7 z`FPL;?Q5G4e6>!4#sauS{#DGmeywTn!TZIxYy12>lwY#x%BkE~9@Q(M^nN^RLm~Hs zw{?7be#})zr0&%@?3}n@tujxDkR2>@huI`0CH$5~_EH#{Y+6?l4%3Q>YAA-(TL!FN zijog-xW38!gkuvtN?6zYu)F1?!I0<8FdQh7cKQfegqf^9wn#rf*6p~)57{r3cu~$6 z5yCgBE8#U>ya!4wnLA+nIzwU5JUkJZqMXdPqI+db9oRf7Wt;O{(Jzh1&p0NEo8p!g zIo4BdlF*eIWF%el-TY<9mXILn9|ojF875a@cNL#2Ykav-M=dhSON#tmrL5puNynb{ zu+uRxXY#^4E%vW6S4s6|tjYVx1%`K1SebxvOlyV20U6Vco6#r-JlduPjjEPdrWNwFVq!(nx@C8=cpnb`7PMCsgrgrt z%x`ZYg+Hz~ysW$S71`WvzykbzT1C^RV;9R=n!(-oUp`y8j;Y za(qxK8RQ5kp&XQyMya%&P@V=QJkus4?jX~h4GO;TX&!K&RSC-Z`~dU`a}J|#67eZt zbh*d3aQ3i14%wr|VTuqv%_T(M{5mvJNOKGlpIOwFmW}@$~*(ibC_x!t#x@E65Cy>lFn3LVBM8#RjxY205H6=QA4{GXDV& zjF^ca>8qol|9(v|j}*L}Q85uf12U~R8kePC3Ifsu-swlogjB(Y{HiqfsR`Rz^02Ky zKdwnt*CFRhTLSJ=E!|&sJqZ}Ke_zM1Du9SO(p->L`(nuI*!EmN`HS$qWPR zq4aP|Q*Dz5cbA>|Vqa9}ut!eA(mrBvj=a|%oj7F+cmRERY~hE3axe1k!JY12+c1hm zX;))4yg1&@IF!HPD2PL4yClD$Ymj?w)4{=Ng((5}82jrA;=HQK-E*sO-~PCs3)CH# z8(+qErf6s3G3x4?$hcf&QbCbGKpZ#J>4~{ORHwW^sjrGpi+RU;0vE>o3rXQ&0WmL)P?L2XTvX3O@?@mG29kpIM zz>m`n17Wxt6n{*D=@z#ZZ~d0j%j(=rHb};pBmf~FVqiuZXACEt2BF_yb-e<)e=h1v zxmXC&iJ_ZmA#adEGew*=*8=?*TzI14`yYzM9;U-2Gb9!e8L@*7?6fV9lTt z&)XaW*f zmB~Bcdf|FDmnHH=!V6v+Yz#~QF_Roz0#Dq`ttL+Ga-y2?o+R8u^N^5_YgpTKrymLA zJB!O`6l6Fh`dE3pA2oDZCX@16eDS`F(gwH#&mH*W^j5ut4Sqo|r+Is()#sl37M~Sf_-_j1iG26bkTMv(WeD? znnKRcyX`8&(iJtJ0}HGX%VVM6*_<11VOX0csFs92B0U8y3xa4$dKvLi1(yDXI01yi zFx{a}f20WJAHrg!GnzpDc;QM5f~M#1EdOGtL>78XKj>!9+R@Kiye(T-=^q}+xE zjfBE1O1>q_Z7oiY!XE^KtDWt8*Z|{Thsdl6Zs!r=Fs+H@F|IpO@*>Gu34F*VQvM=- z`N<|Ng8z9IV1g`(?`PCAx>qKLegqh9ETV!G;|iH;fk)P)n_J>MCc7eZFSswa6>TAQ zRThZZ;cz?srnH@Zjv*T&etMBa2PsP7PV|k5P4rIsV~df_lbvW2=LK$mKzl`9T^f~n z*H=YE*RpN~JB`5RF5=;+p)CPEifT$5)>s}>ZacxuUDX(;A*>5C5r1F{&Io{(PIHk6 z!Xzb{E^l;QK(UZ)-h1v!5Da>0Hi(Dh#f9R;^KFWKvz5Lhr83UCr^x2?;E8oZz#(F8 zcf$fx&BmN!t$2pq*s-4e?1@a}IbzarPykRN1{GvYR^f-dahCz5GOxl~y+I^|6? z5XmGed$MdFjOw20$zvu0gUi4;;QKUuNqTc++>UwO`Q2u?vnf~!%FAIu)kGHCLFvzq z^!RauZX;cYqW|{hYg8^o>C-2aqZ}x`kU+6BVN54oVyv;H4jGd3xB@I|sJ16^of2o= z*Q;bhM@C0rW@_5t&9Cq+^joUa#J?7R=rnsrp(KPQ6GCpot9jM9d58>E@q|(443krw zt|ikRGzhZIn!Qu=!L-zmh#umz+#AE#m{BdImr4B+ydBIR)um7hpm6VEPoc_178Of~ z8~WUV;~-h$fStFfeFoI&ARY#cA@5maQ$*3qq8txS4%By$A8N(Nx&R>nA<>Jtnw<=r zRY!As2rTC;zIjPE)dVOg+}(vty7Fm^>>8X>?-3>Q_TtY_4h@O;G9K~cDbXLj3e!OOa(E$0U^b?Dd{t5X3DJ>C-_?H(JNUjfkDmA;Ca(Gvy4jr?hvCXL))#bI6Yn|4jo5v9TWbrO}IxdSX3R3f8N zw=OuxK#JFLb>s+3k&cqHPpUsYQ94k37&2AAsDmOzEmIMPkS^D~s@8mi)ieboAV___ z(_#5B#UzCzbSJf_u{k=(^GO^psXKv(?b*;=_tuhba07^gZ>W-Wa3-q`k@@RLSK9$( z7_O%lC$YBp#~JMeIluhU8`c@VyEha%po8GAD{u4_^WQGE*z<(%1H{FavFjTMGsTrT z&VQHPAK$Sud`4Uo(mVk*6PTFZEc%z_FdoY68|uTpp#gbaIDI_Shb7kzfn>zUUCe81 ze@y}eH#NY9$S=e)(oaM);xPt}H6B|^g4`w8W!oF?j9em%wxG*d;C0)NLY9qzQBC#5 z;OBT)y|XfgWqjSl^7?O9f8pn^>#Jzd1YAo8m~Qo(dVb#+b(}MIuhCw-7K2Juj!ymF z?)Nupm}W4F!70SYRAQt|+!#JJ%d0fWV1n38yix_#J^MFXRbZ*7SH8TMl73!R1pj$A z9j&rTQpdPI7+ZiR>5+>2zbc3B#^~O@<)Fw7{TIClXcz?q8^^zvfMf z3u8(7C5N!oFea?o!%3X!Y7<_HqNXTsIy8ymX#8WGazm|9vuYi0JCygioQ{d9JEW1* zL(9<3YH|%(2HjHR4fEB#A-^8~{1wsUNr9~h2e|C77#^rc!F#eW?0D{np}|je4zTUV z;dDSdVSfwbBO-yZ#M}*%z;&daF!qz;U4;OFBQqJLN*iP(1ZCa2T;O6Ny)YRPb}bA7 zJ)Akt8sbYPU`>-7iN=^S_FwHqKo#tXjuVN45jy`3ks3L@M+$)B-7E7l*FsOVkyGW7+PX#G786bG&*9IK)4C1zj6y-q4*$za)y7` z80JyK(&WisapIy|A*XP|4eh$26Gb6m$MBP+5hs#~XhoupLyx){QAQ1rOe>Y}&?VNw zjgR7mn1lce+wd_56e4p%(H)J<7Kb(X0z{d-qK~3v5kTq9ZRy=5bsj>)2O?(h5J=cP zri)OeHG}sFMS-b{v5BNq%_ND%$o>d@6(E7&nh~61X0R>w_f>^qS0G*&h6Q$pV@F6q zUVuamV*bMxz>8tAq8qzad3h(;o{L{j~$~0)~1%5Jsg~-k5tOf17iNs z2zh3Ab8~z3@%g-wE{nP2wu9F=HMp&Df1{qB8j4*AGS$A_CR-Pyn&sZ?Bu5=&m`KotfMh`cIJM(n`2S^H|Zs>0lw{gyS<@f zHZM~r=g-$8-BXKpyh&fh1ns}S{qJW+)V$E>A@q&e-`KWtbdBjQZn2-GmOZ8Xl~d;< zYu-C{3G5FQ3+hs{>JMXVFto;yy)oL{(c%+_*z4t!1`qI*ylTRzH;}B8l(7m= z3V5qIri>=6dl*)QM*`GF0M6e`zG_m`p_Qc@UCP6~vn9&IN}er~kzJQ*D$w;^@>xMe z;j=umKQsEy)pA=`WbIkgGo#EVoKHX9jSbs2v|GO$8VP4qO*%t|_5v|}2LAL#esI1@ zF7I>E?tCVDBk~^>hCE(HLKqyK408)t-JZl-uCJy>D_#-6ydFP z9{eW-J#~BbkCg41fFT%&Mv-5OM6_@F5Ux<0j1k9!Vr0uqwrU{0fvc}`l3@?c4OIy= z6oD{hOtVr*4-Hxf8;)gE;b?GPCYA`%h)l$jMvw?@FSzLUZ7VLC@|fRTx<0xY`27Avsx# z8unKPKbKo$y>WmqfqRCF*ByeJR{-cj8<)<1+@@UrF=vK__5aS9sfy!Dl2 zO1_HY1+Gt~iWdj`)KS%Yoj4>$&gRvpDi1IyYFzHl{dl23Bj@!7iQvt)EwC32*&Wcz z%1(y+Y%`V^ch`nhrLN51?=%tMswqkq1W90RscFF3Pc4=zpyv*_axGH5^jz7$i0Dim z>AN2_;-4$BZWh_&5XtMYYLK{MX#8>4KigX${sA5AJ)N`=I<_Tq*RMx;uK9R2qNgUIqGIESn4|&Uw5tgC+ z3c16mE+{;X6NFzYg;qxaPj5(vr>11l1$C2n$_sZ&TNqFPVj06zLtTLIavX_`!v^PY zU)pOrs51xfza8UF)3H5oVOA_S7oW%-U-@u=v%z(;Y&G@;MK+??Xgy1R9s#u@KH zx6%MUBVo(Nubxdvc+hDuPd{$&y2lDS|WOsHw_sA(GJGJVO{ zJeK7HaN^sM6U^(};m(l`a4UWp9qgqyH&s7_zA!DvD)xh!Z9a-{q%nWm+u15E6TNd*24Gy_=k4Xa7V{jwYjmoZ25F{Nygg<-m>o)&hyce2i z;)!Rzf0`UwRn%$?a+N7T!_f*CCxK>X@FJ?DK4ifr`Jw#5wL<6_H>HSMst8k^Z{@M? zMDi4+SZk3ozYpKXLT=SIOhn8hErro#dFP<0w_G+ntPxf}IB137Enzt+05-+Y~u98jwh z0}APX#vw)m)*;fv|B@5v=FFh*&4pbWE0bS7rh4YH&?4N_8v;+I&AN8y_gD&+TqE$; zLhk9k2J?ZuXts6PUS=s>+`8HW(9R5QcclA)X_`?P5GQe{JIhN@o*}oDl>8LPt}0rg z`@q{7)XtD+zcjL~-$DJpHC{nk9w?xjf8ko1HVEBJp_`0 zG6u{~oXw2_3EoV!-;B4{RqQDG?d=D;Z;2K%ciPef3W-xN6ITd z_I_)?WJ%nu;U;1imb)$j@RM;-DzQ~{?(JH<6VH5{tEb|LiePAL8ftd}&bYPI+ zy<*tl#<4*Qs5#o-4jTgiFWTsRBAns_##Qgm^E*=m`EYKTbV0%jELa5GJ;bPZB-buJE2)m(G*md#cHb>&Q~n4wAlb6KU<1L(!G;vev>4ZSz!Thc5GkiJ8V@uztS*E z^EiHDq$YU7IAM>D;e${ZGhvGsf|{ubl~md7!1d0R_8{)M$C9zIWDshOopdzu^u{yHYiU0j5x+{k;{di;ssqdQ8iqS#id zaSZQ^UfmQrK2jIDiFiqZ+GEK@$SrS?sJ(R2Jd9mM;p8Lcbq8hFV)!NP6+Z%L(T_BQ zg9vUWrXmqAlQO2H;VW8j_LIzRez2@iK11T%x-hLjmI&Qz1Q4^DnzbMtF}>B!@8>yA zw7vsI;*|c=dUz(zON|mW1RKOZ>3gwv(uJAEA0Uq5A|QP`izcVwFCODBjytaPZWDQV zM2Moz#(zHXG2pf$rlI#f8=lKa7&L3*h=0AfMM?-Li7y)P)*-wAf%C+H9{YwtQ?_O6 za7yAmP)#Qz*1*%iY7E0k<>Wg%c0?>68nxH%NVBD7tq#kL+D;z%Ty4;&7S*DUAjyqlYO|Fjp)=naL*$R~Izm@t!RzjgW z?1~KlUkI65LP0u(Wl|%OlDajA0cNbG_p| zc<)@{ncM)KscOof7YIsb!rj@?AZ6m%-cIzWU8Gg5ETJKXzMi7$Y>+JTS(i4Chk7wv zVe@=-4A3&yJH`Czm8gp>5+TNA+I8LqAM*wv>Q;ldqYs9I-<5MMJRmWI2~tU?&r=Tf z7o)rFzMFVy3l{f_%AddQOccK8`7EP#<28^UdvcBk0WV#m5uQiUiI6fMz{svkZV4@b zDT%Y=@K(MzxzuISF3Ixj^^Oz})rg7}_kiKJDJ(H1DwG-}nNBSifH>gG^{!K?!UU>n znWy*rDh6e33+lSV|D(0lzq#guhKh7Llic#YjNEDARvke-ug??ZDe#!pB+U!Hb_9gH zQ%*+E5J<8ff^K0Hp^?|NPTrQGB?psEi5i1M@;_=M(`BXud|mEFT$;X!C~WB3`Yn3P zJpcU{@XNc3eg$2{-V#rE|K_0GSN^nH&FW7px}q}YDFJaPt}AINtt)!5m69bfxUc=> zQQh3mpaMWOt8$*DEj^|uylFe9oH?!G+2F7#WYSIkvSG=z5GcV}c$HL}AV`Y>+fdQz z&&D4jcq!EY3|1LT2A-FG1$1NA_n91}R*uurmgxKuQzA-!>v| zTMRHI@@FpFWya#D+w)u5uYe0#>-%NIi-LQ zR1%0-kz-X_^*kRVRoQ=hqw$-y>{$p5t>+^ncRfD+%wSd#N#%Z#>QHel8KoWRb81=U^+q+X z*YGa6Ub0F^Xz#l?;rePU*@;b3Eeu?i>Eio#K2Z-OfYQqqe1^<1;NpXne9m4%F8zRF z7NQ~k|1yAunK79qrw>5cB#f&6Ve6WPRUH~V;=Hi%aYgxKrX)89DPkhKqno1H@V0+t zTX1h)AmYz6&qQFt%+R^|aNg0UqY8WTj>A4v@wHnXOmuYY>O8==D@B2rkC>wT=Aft^ ze7Nu%!4yR}Tfh&kn-!rodXPF99{y;SCGoZ;sdu4Y>nuiD8;mkIs9-0$iHi%5Awa)e z?>nZMV=)YN=(iJpmfn}l0k z@_N~;WY1D|taRJgvH#d7JInu)%ab=tYk;GYBg%{cI_UFle?~_;*`re@OtQJjZ~j%c z2SnVaRSQetrFy9D2uGqC_m(yV7P7}0`vcX6#;z!4oiV85SD48>$xwU2)nu8)S&mdb z2hOnCmBrbniDFEt$%~$F*+V>mE*mw&eOr2xA_D{Ipe;yvqAn0rsQ!!h|3Li`1j`w- zvi=SS1TZRq8<)mJ?Z^+CqY@-!EBUJCRJf|roXXm=ba$+QxS#SMNdVPv*Mwt8N`is1khC>Aq~~-72npTy$NSE^mkV^x0XJwpB{6-+eK+{&OCx z_)o$Ddc6_k_Og+kQ^+)W|hAXMcuJly5pDU5p2mkT0 zCezQpg5>Y9>q6 z+f#GlvEoP}_j-5UT<8S$-Gim^B}asvpG_K8sfyCLBr9@Zw^VKLLAdN2r^;b{9jB`R zn4T3&p<6cBnJMWI9da&$<~VZh&ljn9>TR&L+cfBw;(ewQku97!H6ha;G)h+hZOcG7INKD8P;?r-3uQa$r*( zTBOZY$a{b>I|;!kR7e9VjSHX(93&?JH=3w|;(`?28`VruMB_o@fe9JS{XwQXDSR(m zDIZFADMkaqk?ntW7t#IihbSyj@GR)_QC{s}o4f|GAbhc}4|*II-K-1$`t{aJJiEWR z2FOKKq)|@hNu(1=7hG(?+~ob`Oq2n@K=Zm#$Oc*F77TE#k!edECCSeXzDC=n$^Jw+Lrg*d6#P5)28#tDI+zFP3GG3cx*7);~Ocem0^-e-|v@ewOi*g>Yx#i7d>dG z?ef09*126Bsl4pF_9TUk@svChz>Umhm|~mtUS0jf6)_SQsYKEMP2->I<^Fs?1(r04 zNHPN%Q|<^%U#$bPCeWZc6s&jtBK-vyN7u;GD~&gd^qi-k>Em4*{|Xfkz`jg<_AVs> z82zh+=M}0KOxlWRp%|6`jTV%&P0gcMg$)g~j?DqDDm|?#C7IL30vSHR_)c0)m84-} zNvVx(RWCB5HKpE?yRq^BuH)8j$3qJEj%Ku(Fqk?^3x~6Li)h#x-+$n1Cw;fV^9(Kr@jlim;aE9~qlNLBSXI9~_flOPCUYnlM`~RfZ#` zsp?!?%oG*t(KvThpR^s8;l3e>U0qya7uenm<1@yft_|+Oi;wGl;S)VaPxJy=AM+VC zEqbQS98Pww0pb3d^Q6awSj8Rg!Mj@24=LseNcA0kVoU*TJg;&FFvjb%9FIS#4rfPc zS(+BYm}boBKz;_;4j6HS#~M7!8QY4a$gGnGhc0P=fwYn4?45%ME$tQqywMGvcq5j_ zoPA_e2jPXqPK&AtAYe4b$xos++d!w{w>1W0%vIzS5MAzx_5`c>L1i)F&r#Fs2k12L zABZJkH1B*uIqB)lX z6Q`FhhMw53A>6Rz#q#UN^)yQNVzezR8?`@+n?elp9jV0g-g*06UhDI*?L`Q1sw8`B ztay*yQ~SUZVXClsi{0u4FADMHXNb?MSlW09uO1iXYh5?JY-u1?+mgI&pS(351`*2b zFJt?{dO|Y;#DG+95*R536es9xsD&N`3vH@)m7zZ@bHxo9G%{5(IHq7i7)bnC%Kye? z0jK>wmGGbb{EFf;nmEl+C?4H1u7LPwEJ=n<5zju?BykSNI(OW1DRmb&VJvrXoVA04 zeSZp{3)8AF>P-imQY5D^hfbsN>$@BxES7M4gFBTUlus_ZP;5c}1 z8aYTH5QS&%xiI_#bM9NYPMnT8PecUjYjnL()L=d?Sk)s-u6_q)QV+~DrN}dfW^;TG@xoxg7UH8HaV3 zG!^p#Jn&j{m1Pc__a?a+6b%{#_5L14cRu=!yuHp1l>#)HkURm!Cg2lYrFz^OrDgAN z7kn1G@m4wf)R~6N<*u2@NhyYsb~krsh`RO6O!OKOwDilqoc?Z;8(j0u(|hgeACV6U ziWe-1Ii_Ts#R`*x6G^UUTSgtzEqcjf)=(|EiCA)5LL?&zz$ZOfN;n zpxxB`IJ^jiZ~rhPzP{P59Y~Iyl6ayzy0c4~-}P~V@whfg%uy&Xd)NLSw$33)lc-Ij zWvk1!ZQHhO+y2V7yKHyawq0GeZF~BkiI~mICU+T;o11y>`{Z-Zh2Cbaw7rqX3P7d3 zG%ag>E6_Ra7ycsnAUXBl#>`<+?SVTjtf{rz?C_Aj;R#a}SsCq0i?s2Lj#GlsF1m<= z9m|TFiqFIe0TT&yZ|R(O5fq!yn8kWjS6#&&vb|c4mJUSp)|^kRyjC^C;yBvX4OAJv6jD-}Xq~ z+OWJn)3+OBw3bED*It7I_0QEP9!H_fz)2}Jj&uHUEZ1Z>eTx)=MyYSZBnonUuKb^9 z&cGi~yY4pcju>U*Yy7q9hzueu4a(GOt6$2pIrxyQ$4yQvWa2 zttsaHfeoI|6eAL=$Mmbf@XE^)JFQ+v_^h zaQvBmuN*p#<#BPLj&Ht@j%-_hqgEI1#~E#8BPzzgDD3y=3|Ct^>WmhuttQenUpwBm zY2(fNJ!CRuQIsaepu`1ij-_ue3m=Tpk<*)nWZ<7zBNA_3Epj=n}$ zlCnfIlIIVHqgr~>|J~Lyb20vRCW^$#!S=tlcqSGmuKyT2sQ{XC4!9g}-LtiG40BNg zg_M0`1#)_iimVYXk~W9C9npoMHjpzAjz4|`9tVAK6neyhi@{9UcPL`~%EfJ$`r26P zO=!r?|sR*>@N_`oQ$;X+@FtQ5y?JB&p3fD1;Q z8Dt5CJcXegz+@HMNb=*rI}hTyB9N$X#Um+0@7Zf-Z>c zK_;;5hGbd&aG}S*>!Jl-U^sOFkRW6*({Uw*bC>wLzlh-LBrYv~<@_QI_~6I(+(3!) zltNstQ|$oLdIhZHlZP0Z?$Ui?1Wo&efYwh8O6!oiT3NzVxP`^_^-uEIgVFtxNA!V% z*%I#Sr2r7qLfppq1wCS2$ApQdwvQ1)?IA#^MIDI{?l$(3i@_1PLK(>vdlcspHnU|U z>>&-{9ft3txQChJ^@vv5AhO_P`ea1f{ngfloJP0hV%2FonBzsFak9iuq=RbrHibn< ze&2Sq%pIB>A8+u|pTUaO@zP?i1$=`;k`^jcv;f-t0d7y`UeA|}ZANObar@;mY==)u z*`930-y*vIVoVn|7oX?neP^9RAb?Z-hMhw#EP;M+=Y}7krE}E|_DX;@YccCWkBf^c zOC>?--Y8w=HD2U?kIWjzlDlj7HDD+>Cf>Uj1e|>4aSvQ-?ndJP?$n5BYP0M6Y(=0o z9S}|jc%`}DJ?Pcn+}6#t^Ekpa7Z9M!(%9(Kdy^cf>fNy)%-d;&{p{MdtM#~X1bg$n)R)ejXj>;P5!&ZDIVI$@B)_(%v?_&Z+6M2^vQsu#4$d8-FfY zKvC}*a$uZnjq&c0t&+oeHO6xoNU-z|gsVX=b9Tv0eNP^4CboTOdo|QtQC7!op_YAz zy8K6;Z>Fk2Ln5lg6=rGK#h;4HR_=X?+jjbmEaH#sj8XSe`6X4gm)YzQ2#gP2(j+)* zw)Ch*$hv#hBlNg>_Uq2r{aEk}w-etE3k85_&i^ z&3d63R>3pUcKGnY;{~oV|v)r7kcMg(08y$eRnvN;}?U0!}N6oW)LHMa?v!eUC7 z+AyWMx?NS!M`UL#6vRY^Y`g^I;M8?C6dnZ!TY}ye^RtrN%CLf|zN9XngFN5lH;#(% zO7=`M{)<+%b54>Bc#^savWyY*bf8OxV4Ltzeonu9O|bLtfDX}Tz^shHAHd>9a=qlG z%xEFzvr5i(u6`W^vUklAtPW1B;?DAUs2?->Zgvg%VwC(=Uq=%`O#wgHqYT%Rr*1&J&SRB(=y2>@Fq{X z{}kPx!t4!d$My4k&VVrhySFAocrKU~6UrSyHvO=gVd~xt3}diTmpxr{QP>X1W{8nY zD5rJi?Vzu7;lVtMItO*r*ks74@!oL)+;D{*6J&kpX=q5UHm0|8$;D5YR=Y8ipSP4h z$DQyEg-_U3PlaH;IIXm`&|TQ!%wkk5%;(uKSmeK=gP(z9QSu~8IOsF{LvIhA5No6B z%&IMUx6l3Uc-!?cYZdP(MQ#RI*}4pi`l4?rM`;#2^EP(wOr!j2f9AbVo?Klw<;8eI z`&T%kYh+&kOLr9!rEYeRh8K^_(Ny-rJGb#C`-%~sJR}g)Z75Lc1hRk<#;&} zt7e}^dJ23!4q;Fzj}j{=*e^uW-2hQ1MY!-N7EcAR9h!)NxG2OGLyLIGt-0Ax;@#k% zGC9-GZvu}lzGKn;bfy1To;PfB3Mgm)icce)_Z2>0`kL|k$57g|&I#(39lh9`L77uv z^)D=Xq)o_M>{l0xB3A57xf8ea3dSfw=J4eHPk`w#v&_rsE%MENr>9L z;H;!fE{dVY(<- zHtf$4af_^UJ|Y&Es}e4wPG8CtL*WkXXzcux3`7LHm**< zVYAD#)`AahpymMRb#G25NMt$&3ei(C*LgZXKBUR!F&@OyCZJGxCzV=$5A$HT`iBSz zY1lZz)x5ZF&7g1!G~E& zMnH(Ll8ORAf78ojIU}6GEe)zlw$J)%Zj=_@7N7az&^_!;r8sik)I&n>vutPvu z_-8HQz$UrUgvCpAmMP{9+&sCgXgRE~?r`T--Bm~OBqCpgT6B1=TZP~S15$36Ydg}%34Gt@rY`;SH%2h z2r#nJplm4QgdKRItoU`3Tmk%<^qxi6PP$qvC7&MA{p*@vj2i(_9mqNWvzKV6BC6z> zYcbV^U16xj3&!1cbP~u}2CAJ%ET3;hu6u7e(BZTTWnFa-%NXL@Cc%DiS|~_ZaR51m za}W?W(Z~s0%|cwdd3j#yL##o_FNt>ONd`2G6DBT*+fId&0*vz%hm(4MyXFFYLs1PX z>Zu-Gd0s|~prFtf2V9q|nMYfV3uB}6yipyjIB&w-hjp6r-0<{*)qZBFfhM!^Ocnt@ zYr+OXr?VWK5>%kl7QQ<*8WkDGGq;GwfC_lT6sJ7PEoEhvhmTN=#w={sXlwlVE%Wc} z$ZImL>#wmid>*M4CcA+7`DEH%S25xI3_1o)gXcBGzH*<_vYDbbulnM0s=IGOrAZWq z7(-LD!J+eWxaJG}kj;vtviox7@MLYk$L7RL9Nj!V3Y)gk;xY^)2O&IAWut%r{tEbT z(Q+^Cqag21=x|~K0}-cn$(vX50urRMLi_ZIom`y{>}41iB}8Vh$rHcL%hOOaqkS-A zzGX&oG1S=B@PGaAQ%Ws=OOZ6yywK_^)HKq0K(YNZw~CIlL%eh4kh8``V`4R(k!L`R zlWchzvuqNFO^W$D2ws*DruTLrND1JOCu8jFb;u+Nf}V!&1IMmQfZ;lJCt}@DHDR`< z_EMlAH0YHza$oGhdfa4f;{SUU=rETSS+>C}9`xk^`|R!l=#eTlbTpU+Q?z4r8}b86 zvDP5q&(B-2p>_DMZh}7oqXzOq6ZOgOn-tV!t>hu*nI_+eIWds(2d!!MG{ftU z+e!j&n2E;Ib?r5J5$qFw+$wjLRNo2GKm;t%4;K)Af}L%wgoB1lh-!kb!HNrAq50rK zxN5@n?)u-47u+fk)VcU7_|v$B`uLSyp(IkiS5HqGZOw!7-(sDmo2b)#@$t9O9Fm@4 z$i8UZd)sHu)3;kNAm*jhuSWpNTdu<}Lf6A9-X|E>HCc_-zozsw8eZ>ECV3BcKzPi2 ziTxHJRYYvq18US1uz}H%U-Dx;F(y8YhC&Tbaw@jMJF!NWCz^4}Pc;5^WPgAk}Fb6$UYOq`4 z5teaVZr`-w5R#A=)U*N|OdF~{V^VF><-91t2j&7?F1T9#hzEn=bR}Fvij4%tcw^(J zHR`8X%+GFU?GbD|9z;0daQZ!#q0B&qFq5snUn29MEApS+*HWJNcWvBrU*ULGA(qz( z@u|4Q$N2j?K>ADskAPPLy6y@B>>w!lQus`kL0s>dXOcs*yMO>34i!oA*I#)EDl_5m zUWB;GL)a}jAubuuj(=}rkx$4D-V~hNjqqgMhfMKQUmLEvEIDa`xH|RRc2G1_axXXF ze;m#sO~h_af%i(^Ufm8qbtM;)d99z9f`J)cEt7|>%tvAC)B4if(tZEVCA4m9K>kkK zAk?YpZSQnx;|&DhE%)ks0_DIycbTdBf&pI%FJQ+FGQC0W#s0!Br1@=TB1(R6$KrqyiR4+w%dHlKq)Y|Su%LRh;`NrhM-^GW({2bOl&;=4hQ3Ip%v4G|l}3OK7xn4pnC^M^#-AFsRS!awksON& zDDuIZ*DN0pI0{1Bnk*6LzM_bIApm|ySf@M8w}72dJ_SaY?W#B~nm-VJWrIitgJ3w& z`%5V-UW2&uJj+>2H3i9gb+4Q8F@x?`XroyY(!Xz>jPR>BN=)p&Pa9XCpcn(ld{H(M z9S6!gwa3;SQWw>$uM;QrWgb7jWP05q`CURSmBTWC72JjsI#G^uTHL@%&&IE<-z*ni zE%>@!BozAd9=sI}@-?M{ib=VZ7wsy6M(PQlvSc4QEgE7n;9ZsNA7M-h?GOmdvjzqq zHzjn9)~ODvD)rke!grHRoVN$Se@g}u2D}n0+CcJmhbYMpNOaQ4n*#nYiTA}`526Bo z#seGx_2&XDPPK5ynoF^HL14P-Q2{Xp%^NOUbAWe&GbQGQ!;Z7@*}-}{JzpeNqsslq zKk%kVMGMVYN$9*u^JAhs!X5HCehT`5D><}sLbQm<;I?B_7ar(C5n@iP8IxIB0gl!% zAoxQJ^bPc25Cg2JZ5t)!xD`@it@f`4748y%N8OQl1jSXR>AS)yQ z`#=R^eAB6nw_O0RIs+qLD-6L)$SG^8rttj1Ej$v3b%OTzI1NH_s_brV=q)v0XIYOW z6r}1fVFvtSMYDn>h$glzmSJg>%Rdn%oIGPpIQ_r6$R{f;xGe9t^?uIZFa)hHs&eId z310hR5jmSP%Vlx&8Z7(??F(2+^)X9;5M;O~LUmYKIl-Ie6US#@AWW6a_w{b8uSV|B zo`D{G5BYExZ=}kYV_H^p>rT`fOqf*ry{o$fqTMYG$7`*5EkjRx9}F*j2*BV0qtuGK zvCrSS+%`&)x23dJ{9Yn20cpKo#j6?tN&-4`b>{T+qOek?vMWHww^=W)VD&Xwe;6HX zy)fG#x!^BKGE+8ca^7$?$fSC*+=wXvcu0;_+$hcaxqQytJJnRn(yohp1EjfDVUbf% zxCy#t=?G1p_x3_;!>Pcb_3r@4xECU<6^?&sv#HSb)H~cChqhpMH+{)bx|Yoqk>;u@ zVghRXez23(m$XSPj;;B^NKjUPDperZvyXt(l_|1!M?H9rPbv)W+RA7mUOgQ^7X7lu zA@fD)WpoYlHO!s{A&}vTb*YS0W(XBy3??|uz>)2wQ#)b87pV<4&Y{M4*X)J$?L`p7 zR{|pB7q!)=oJS|lx_6TO%&@&dwDVkL0U4CZB%AgzPu}YzK3Wx6SdlXCpjkj+$8Y3v zO4+0(?GhtfO&NgdNN$auXk#v*2CWWgOpP5TH>XAgqbu--RkGqAB(>P_f>pU68=w6Z zrsHDuCEob*Mi%!I7{6Ye*~|6xF#nOld-P`&*Ht=cR|XsTJ7*~{vx8r@p8_7TKnrE0 z-ttAp+3wnAT?BdfP>ACcdWYy0RbzBcUIKUL91xr(t7b8!;JxSmbD$ZJy$T zTYTZNbH}HiKD@iJ_a5CR2cCe-s0e)Q6wg|4@a`s9w7k$r_Hux9UFt(rYxU~ z4#WvWt}+}viUZL@A1l-24~Xu&`9k@HC`(WB*b2`!9bF)t+_It zgyh?$Sp=lV3dR^k<3fI_cDau@YSQj zVtkkm#)aDgH#!edG^8?Gi!O{1}KpwDCGm7f&%3FB`64MI`Jx*tt@3ba8KZ{o`E{E z5(^bU&Vc!b_(+R;#DSQ=IXtmC{G=w*z!(^cqkb1`ttJ}6@iO#r`er4;f&+`e?f#tdqMlGO{%QD`I1 zI<*e{krzBXb2f`dYPX1M&UiPQbPDm0AO>eXm_XTnJlqTaV!XF-O=mF&J!WFxE9h$L zUf~7Y<|+dBS6lNxN+8qrnf2HflRcibah`*7t!Xj3>1P?1!@TP1ZBF?1o%-*UFcsPH z6cjRKO;6dR?ayJ61_n4V_JJf4hzrB6m4m*DS53c&bc*GK*zX>V6IcZ0@?_&P8_aXP zP8ACaZAK4(jjfiR*N%A8f0prKsJ_71nv?@tFg3k*%9D_5F~c!cR7*;=Y9_o-I-#b5 z?ekNSoV4c2krY?%9Ag*&e(SlxrTdYqE>kuhJz#tiguI#o$=-Gg!n<372#^#kPGgrcMO&5bqDGfGs5=|8 z@*QVj=)6y&8c;`rocqYmR@yzR)=2_9!8G9(>>Ye&M*`RLG~!C*KI>OYl^0U%H&Mds z_fmIQhyTN6J??SU9u{^zXH%_YCE22@yS;EHG$trvBkJPUgMo^%kcZQH5Nh84+?Sq* zTSPnM_DG)S_#)*BWAIG0E9Xefl&HDRSxL1bH^{PRF_Y4QcRXyb_?!R)f!jAQ?qSvj$-u@)aNy)Y)&6D6pjWUrYFtGoKo?9G z*>9++JhLB_uW+f8Kt1F$@y8qj&gPEu%f*7BLt|L8e5|^4TE2{4{9y1OnFcg#uxl+} zj9hEbmjR07UcMnZ(qaW5uy1g%be=fR2}YP!jOEq6-mIV&xhjj-${he^q|2tAhw{wA zZHEXADdCt%hN&>Ap^@+?TWy9n1>h83@(xl*AI_ zfDT7mA_a@X<5ZU-6;DNAq6rl%ED%+*~u9s+=kFb*~JQ8bqIyxR{o zo_e9(THmvqVahg^MBF(Jma$QL*I*3A-`WT#tsfFdl*g@Dc=LRb#IM3Rr$+%r06$VU zCNMDYU|I;+^tucW$ykuhJ(17fh-UDl$^$B3M}rF=_+Z@i&?0LZB%EojpL9ibH_P!M zyih~ji+RZRhGqck%hzAFX8UubYZhP~zKue?BQ%N=+fri~qYA^eh2+ZP5;d;PeW9ioa2P?*HNZe+h7oisj%V zh#uki#^=c?*9P-3Ez_)PLuE)wNs2t{CUb6}P6cRM{Bf)oEODOWZi&7#VPk^zeG#t6 zy=^Zbh+DoT_Y&Ye%W^yqoJ!?68Gi=^J4!OGM}O1j;}Ozz!Taho6R-S(m#yo>8_ZT6@#LJTFU$O!KUW-~>R7gL7zD95l4>M{vC9I`up@C$})c zHoC*to~al{;$gKiQcx0K?=_x=hB6I~2An*ae#v}H^^&oRW~}E;nmel5*g^PHkyc>i zic$;dvs0Zzk8KxOB6cTk90!u6+YE~T+JuwimbbKzQQWL%s(@05%Akp3B z=qO;^#x{L9C54#i0 z?Rt>{HOZ6ANHK zL(TChpg672=*(r)K8$7|K_=%4vJlxw_Is2o+6FLUefK4crpaZk9Lkr<(W4(Y9&kkI z2=JLOUt&e;W?D`9nmOiR@x$itsIMx;Jdw^DP+DwMKOiALaZ;5paU&I< ztKubc1W#i!@p&ii_|uRfcAks%Ee|;2OT8F|Qp4y`-!Hk7aAML8#9Z4~;1J@2 zdydDO$2=Gd#?Cyyl2|Q5df9NVV&SY(_+@f)L zMmnnA$q;o3~r}hh>_A6N|mK= z=pK(A2bb~~LeN0w(BUn!DhTgPKxt1`IfTZBQWv#SJh__jnhG8P-{S%=tk00vWQbq* z{=^5}zh{t-*!~{>P5WC@F}>NwI`Xh5A#-yRtasd{{@K<{eDm*j7Iwk}J00Q;6tjR% zL+0fCAVC(7pF$oMtPi6)m#ToTPz@rq0Uw6u0vl9H+oGn{KpQ;Ioog5paU;n;yh$9J zaNSr+cX(GNB?NIC0B^2cN0F;2?F4t?M9^cc%m6O9L<3C+`76Y~{sUJ}M5&rVLcF8T zN=@;&*CxVA-K8f@ts%>EDA=?J8=2zR=N4UnOHc3^rl%F#{xGcJ`zaZkf7r%S&u z_WN(?FSaKcb>%7S`Y0zH@TNrHM{xlqffE1LEy}svb!S*wKqcMIeuXANB67lo1@aTw z6UJY3R%el~xbt{UZtp7h$Nbd9c2LvD*IJ&V}a$~xJcZ& zY}4s+NY)jR19W0w97rTAGXH$z0O>yDUrg<_FaZb7EV0cm*-Gh18^&%O+WmYW*09&> zHp?YfZchl;01qpdEDv`^hqN7HEgnI-P=d)2#mPuGa2(_+qRTkC_^AqT;1S?VxJQk@ z0|?UKKvE&UbYkMA6s3fjF-TnkWrR9QTZ+?)EATcEpIkWRG?@bGvt!39(J{rer9$hA z)qf2?BE`^F^co+Ru!3Hz5q?6s5@tMDgo71-a>i`N0K#Itu4J*bhZKa|p)4$=$}E=h zNJVzbyW{`1urAyiZ4!I0)S6tr%RG}(|MgF}Y)h)0L$Cysf|V>Liewcm1GAGLA%Ohy zWkeyP3}!e_j6m;!OUhwsyScq?MYs{_rG1Gvue&)gdA>VGC160TM#G1o5v?DT$KZ@E z#FKD629%=f5D%oS`Uri+y#IcHQMh^duEp!~y8%+Y-`NL-QUl0%@AG-f--;|?C_AWN zqQDBk1&=QwTYgns&!tmBtzcMrK;c~lBXg!2@%nR5H?(NtgX!bF7pRhK@`cMokCiuX zuGLJ$DPhtGBtk;lA}U#uioU+jqf*18!u(Sp17raGq%opVl|C8TA^xCq0^Xl6zOQSp zLy3sc`MnW z044Oz{_=_=`{Y`}Nc#Zs^*;x!IQA(|c1&gYvCFT#@Pvd^(J>4U3N~%0QC*=Vwm?|5 z=ygJO~M9WcS zt|YbPo6HH<34vl{Jhy_(Qi6vnX(f~_0Y(QZOmlb$^tR!}I<$-CgDdut$K*LYa1F4m zZ+a^EAd1D}-VPXE!yd~m?@WvonB7_$4>jdg&u@+j8H>vo)K!8p2!p>;Vpf0CJ@5 z4KigOrlY);vGn;YZWI!8TQ* zf+;b0sHoyn@tD|GIz!|v0t;0^0ohOU!_`(k>umYZTtt`#FoMKpRCM z|J@cMXmd(0ConP-ILT>Gz&92M<|QpKp&L?s`Tz!!c+lnsf8Txv3}n361asih(2TVO zSXmr3c)s~QUZCQ#2q~i<^qY4m6{eR*kA)tW0_CNl4ui|})}C(E6Z@j35jBR?*_Y+X z)-l@px(k9?oRc-7Hv-|YCkR@GC{j>-T4q^VF~6XQzpZ08h8E=q;9F`#vO%WP_qPkG zkdUY33@G=yPAO;C9xRR>%}vM41|y8p@N~x^&LQDlx`Wea;sa(H57lLKmp~$U8~-~N z#~4nhlhdFAx_g;eukkR)!V-*06}E7GQzL3qv@4EOpRT`Vqb9<9)?M9GFDhbHplj1@ zDB29A;S%&&?m~1ZAhu!$mTdd6zchA4WxGz_!`ClV%}7?#u;s9Ty{vDUoR2oj!fc$Q zu`7pTm{27srRX?4pvz~v7m2?`uQ8Wk@+KArXe;@b#>sDe1{{6n0T_TzWzZm}P-kArYOC_O{Y=&yHf4YbGdM(P zbUtG&ed^ytu9S?9F^Ksk6hivP?5gKp(>atFbotUp%m!|K@>Sjbz`#a#L~2aDw0L^D zPMOIQ0C_fH8cZGb$LC4mw-dE5YkJ=bzS3*z?;!~bUM>Gc3seh;N@(2xT{JN6h18a9 zJ$HCdXGsZHLaK+pok@_Ct0^1S?a>QFH3FdTB2G+)fitLusWgdUjIO4ZNWB=Iwu1>B zUoS{O>m35?tI_$mDw2}Du8n;bh`QB33ue94fDZlhClm&QE;12rF;64bYB;zUuW+Ay z+5|uTrfL@NXt|ALo(+~93fo*mHRAR0ly9a~Bu76Yx6};S^V%xb$LBP_x|E?$u-C+= zn_(%pVozWM1m$xPC9XEoK0S02x36@4bQ$U*Ja>s^_9^E-3*ujcCn=yQlB{zL9QC3k zz?_08m;~8#YC}DyWel;B&VAEzC}!U>TV?R|-cn!@hy=;6JK!qrT5)Eee#oATRplN9 za863u0KgG-l(Ii2nL)H)6}iMEzR`|@Sxh1UC0nAB@KJsNld$>mE}ZRkngng3QTiWH zAKS(`p^E^|r*gMTO=HaW`iD{nO#}r?05zlxYal_TM7rhQ$R$OSQ<_3}eBSASeW91acpi-*`jhc&{T~%aAdWfn}=QH?KP^ zI4Mm~twfzEj1pS89s<;Z>m!Y6KD0+2cF{JaT3VT@e_(fM%DqNODspzMPd)bt0ea3( z;1+BvVk@f{Ue-XjN(2c(12_H1Ml0+5AqHZsRL3Rui2Cv!a**W$zGy)V&>=3~8)A0{ zkT|C`qXwb`K<>U$OI3~tr2euu(eK5FKGh-^k$z{SBC9n^E7^y@iGw0j7M{M^1X@y1 z0qjUNF|~=r>9I#v5}G-<4CB^w04d9<)4kEe0;2duX3RgP-9zb?5k%SSCJ@!0GqkDu z>4^!Z@EC>I3`lh+IhIXZj=((KFL#}lt3LH~1^6#|zc>LWECgTV<-0?jPKvB2(y*q! zgQjrFDlhZBEIPUBZ5|qSo%$UXSSVAsTYUbk^nCPw0ZEs^$ zu&|xMew^yb;#RI&(rF<*D3F^U2s+y(_WuNVAZ(oJgowbXz>MiiI3QGjE9^D<&9OT@ z1EE(lu7I*(`=qUDG335y@wi~I23V^`#11ZXOS4?SEc4jbjc$CTH()ElP-zjr@;iv zWw5*E+bvW%Ze%L4B41v~oV52kL|xsJJbT?OjV4h8e<22`Wa!E9spr ze4Q%_jk@T7Y2`?P!P1)-!umwB+cObV5wnE-Z3-&Y#yvW~YKxO~cCibUr_#D?RFaw~ z_Bm|U;Wm784hU&VpV9jnzFS3?kF3EXgl*!+Hy4q0Q|R7+fN zM=3KIx;%16cC7Zjao+&YUqj=#WgLvs^O~Vyz!P+U)HY;Iv!FmWWQ3+Z#9Dmf8O3kV ziLo1~ZAC9>I^NQeZnl5xpRL1#MJ#K4%{&kQ62r(jtGx-g8)rxgTMirf@GI5zBY|=h z9*}99@zf-^rqq7D;Z$xI`E8>Z3}!WSM4NQ?*c|hNtr(k7T_CzIeD#7s_8W`t1lbFw zGusy6IjV^qxBT;KBE&UBm5az5s0LadnjVUuJi`Z2l(qt<0jBXKX(CZN5|ZZ2t1Tau zR%|KxKNrP%S5r-Y<%&eUF^rO)HKL@j+5Wx6l=-0yOh!@m>#H4)kEvK>^(_CPe?ux6 zc4W%e08PwS3)N%QG`~)!f*%kl*i%oy{t5U78h>&hq|rgH8PIq)QjG?6+C`6OXCO^}*{6MUO(ImbjsmS}xKKrx1P4K0P3xQgjEHl& zNYu}7&fY!Q-vaby6kv9kS>g-PjI16+i{s$n7D%V|7f}NYrQ1c^LGoLE4GSCkUbrJ9c>`a=A-)%>q>&6OL7OvhhoFahy#sPX@QyMPP&6XcS72Qhp znfUEY*Q5<*XdO{LD(*K65ZZB>d-1L1hxAhrI8wx#eaeyN#QZHf4p}HXi$<7J*W>ckc>wlqNFd~Hny zzv7r*){_PTn~EU@>4DvbQ4>gz@=N;7C9@DV0BO_V+S$cr%cBh}ne+@5kh8K@IV_Xr zU9}dM!VAqcdJnNlcZLGO)NbneCv5o#XEPhR%!>I0uj?`=m=ym;2~C2qaE?p+QuXl_ zxh&aorp2fs1-of$MkAxUbZ4e#>ez;ApE7&aB5rQ*D}+STBRA1!S%E-MepsBcyOHw< z3Z1DV@};P>uRWo&G4jG1u*=OwHRk-N8*!27w2hYEs(<3=4PHraP%qs{ezAy*V9D&5 zi5uBDwQ(1T1_p|Nar<&3kGG~6puvo{&Cm2Mx!WgX$hVaM8mA`lhIH#pSp{N14hE!( zJ%y0e2>C>OV5%uZMc$^WjH&&SOqt9gpDr?Tg}HfS*9nN+Y(7B%gv$UG=LNIOK2?&| zqDs^$a1kEYC0##8>NzM2wHXm(*ncwq3ZDEv#CZ;>>bPQ2v2Z(DP(~rZc#zlYc5WCp zgZYbCwqV6~*u}{Q4tv1f&4Jet#%;G-elpXfk$3M*!!0^V8_eFn&NN#blpOh6@Itx# z$8~>h7vSnOPft}02zOu-IL6p4D$TLY3907~(`EUB)Rp=JMw z!))J+WQ*zNkx&O{>SoqpD~#FUwdR)`bY7S|h*KlyPkvRWMdCnkfb^~` z#t_5FU8Ey8jXXO48idz9(b_1JE=VMHmSrc-%F?U1Sn*l}Y-h=V^=bx`J+qw+sYFN$ zEUk6un6;|7803aztwMvpbTScT+$)A8`jlBRafYjGJk}y{jD>{v@a0jIX*r=O z?dt<>5Y`(_XB7n@<3@5>MgVqKh#YEova7YA;E64I}k(TWqkr7-=O!9>`Bh<8t~Owh*<}!^j!=1Mwt>5b8RG4I9hy<;Ju9^f!PBn5AdF! z!#c7xo#!+jZ4}LJc-RBc*ToHL>SxT_yP0*@tz}vHzIdM*syipC3zCi2vPP{xW%ph_ zvtS+o7K3>enGo=NJ>cS$X|QpsNcQ&t9;j&nf0a+sYXS9DL3>1yK^t227OSn}sv1d~ z$KQ_PC);)P9$QO;?)KW_;+DzFc?B|tP1u|F>{e!7Zq%$u>4Xs@d%T}(f7YL{{*;KK zDRqo{rZ-Jx$t`|3yWNcEr>{@R4H+nq19k=hFH@SQgd+l{-9MO~HkQYVay}ZYmIq?k zB+COl{I737^vReQ1OK!hGI`5{&!QI*DLA_8imR54Y@doC^aVLF(jn4eb2@hhWrcHq zEOa$_hJi#73OhCl-=1a+D$uRDfyiEr$(pWWb9-L2D7P&EOa5S~%!J@p!vr)N`Q?{sLjX*#xn0BDK`; z-PaRGW220-nO15J>@}evY5L*irSG?S+TEo&`mBDQ(`&qiZ(4*FF-qF7Z_8%(m)A&} zB-d7U_PWkD8prLW^PmMoR^!g@iNkc@rmyhL=SK*o&O^BXhLo!h{Q~7^Z%ckz?uwV&N-7@TJH7+k^Y=;B z0~ccsQGaB7)Pp0446MpPaaKw;v!GK{w&-;el~YsF|4T z-?S#uXKTiwjdTyz?N#?ZK#`OcQc<4DMW60CpAzGwO7jcw3G}#fsvPdG>%Cgbz~Cs| zv>V227%QxwxKwmEX=?4ofh%@BdW%$1?_}Gph=A_rh1_9U8I@6AbHR)8sp7Db--O2- z#vAB*z}4nTc`s%vMd!+s#P}}74Ggvt@nuXhUXrsgZ(zUW-;nFgnJl>pn=TyJ#)PG& z(CAk6+$2miEFG^mwKHP2Svqv9$65Ep>WnUZRZ z0|3;*S=2Ts)S^g7lP{)Ol;mc|W*cms?q{12=%N&&Dg(UBfh}Ge>6U_jT!b?suGYLy ziDp($c+D!taxV$}bWnf97=&WIL#YQ@p+sy--~1>N^Jm!% znM*z}eI86@2U1Xhv8rCpHS|z0=o99}+5yg^xpA#yUh^HDMWBI7sDmH39w!>6h?H0T zBrh=#1I`fl4cRGfD;Vu1$q5yj$xZ1DPDv-PFPZZZEbf?E$84Gp;Oj`OX$Fh0a!MF> z`fE6Mu134CV@LLpkIUEJt`Yc^0jRRGB(7;VF@~J$wGWw`gtI|J7bTci$^y|oEdaWA zJsk{tsTmi7<%S8)MJ;q`vzXKP#pWc=b#!5IUXjY409PSW3BIBGR>~e;^P>E zZ|*Kk-16smqtZA_sh)1_qAD1YLjWw=$_R;V!|wjaSdEg-`9)sHL2L!hbH5Uu!E-*- zTO)0G;fDJ?b$Pn9Yfg{)=g4Ifl{k2hgtycj+6bFk>8SS#(v23vVUk2&PNMy){j{3& zX>-u`i_=Ov##>JItA;YoGS`~uvrv1lv7iG6Y=Lkw5f6E($NysMoT4*{)@~i!w(X>2 zvt!$~?R>Fq+qSKaosQA5ZJz9X#yIEV|8J`5a@D%1@z$DiKJyG~{5q>ig&80ne1rVF z#7W-o@uw~J7`Or`Bt037A} zlN8t~lEw&TF4h1Q8nsYJhJtswzBF2zi7o}qgdsd;!o z`+&)W&*`zaZuJ!bVu|qk`n&{>Nn*}8nZ}hqer|K=u+7S1-vPJ`C`&HEC%?1T@vw>! zOoM`oI#Pr>o?iIQ%_@gxWVdRRZX_GVCOUarpN`+&?Jf zcbqvVMmlL&?q6mF6G3j+x$%#Yh`Yo^c3)W(2j~Amy{8XADO6DR(OSRZgHF5lYTO_) z$R%)p(P(rmZ3bjg=P+S+1)Ck??OqN+&=J8(6%D1~o5vl9I8E=F#34e7fMNIoU;S$Z z2gBv2)g3UtSd7DXi@nWu#qBtPX%Z-XQ@T5~*+uvN=SKZB5oD{3%1{qG!!}m}qy+^Yv%bH^@CB0f3N9;Vdsls)G+W)bR0C!(- zF2%tS?GFGs;fPP_cOdKm+OS3ICf}W)x%9~DMN;!jOASxKaAwhX4D^l{_RLF4p~-RO z^18SbckSJEENu<#!Qr&~U9e>ntz>%mvP`Kzzs~}Ged*kuhdeCJ4}2o9EpK5d(CL@# zwWDKS2Q8~AVroNX?CkmZYsqr37kDxBF2`I!Y(Y1EWenas;-G<;6(WYraWNbo@GJzi zc78aYkr1mS`8ATNDw0xKgyW)Pw` zTyFptMw9MS&NYy0SLHK!Afv^-)dHvUu5C^nh2(Xw;F86^auI8xTetp6C1BJ1hiad4 z0Y3?W+(t0y3Id;Xb*gEgNA|E%;2m6NA72>X>0mg)0oEoUq*PweM1}-iejqDV(Fjuz zt7RZ7`;A6{RDVwh8C>j{2Q;F7^u-8-Qy_qjTIO4$~&C7Y2dfP!xCn z8X8@BW!Zz@6ZaLoD+ef-m@*mppC zn29ynKE>MpljuV!_^!9Bzi#EEBPx!DXIFaP00yU>g^nj`19c^CJL;c|U^Eiq4lo(F ztX`VBVQL?e#;J$o+5HS@Nt+2jR5$GDyJD&9ikS85RB!R7&%FC@dKk{0zh45&{%v>H zoe);t)D$U>F&u4CpvyLuV7K?yQH}sYo+|vu%{$u8(RH{w%wueU1*dxn+J9427tWR# zWwqiR8eOr+vLXd^1UT*NcRwv~&D87jx)VZpmGM7=V9&-!T!S9Z z&lPtQ+M%8}mvyckxStgNBMkhhR(um|hX@Mtt=QhN4SXQPkTxKc&*RT!(_glsns^8G zCTHElsvJ;f|Fa2MJ88%_nW5IpkLc&p6N}9@yoB*Aql?Op&s>UK=j@!?tYtsQ1X?D% z9j<2(P%6!BphM5Y^$-LX5H3I%{)S(pcZM?G)kCe%Mwhi3-(?Sp!lP$%mk8Pp=EL-8 zZbKy;XJWSFCAa4FieH3qV&uGkIW&2W)}^JY4SREwOh!znwx2Sj91_+%wk^|SMDVSV z_|3!{J>QYpG=6~@Zg`M|$XHQ~*{_*{kX}0OKXc2q)wNrmzYJ~kF_8d$4wQKlG3O+C zSGw@v;-$i5whIl%Yq>$+tWSvg3zAEUZEt?bbBW_N{Uk{_tom;Lyly|LsP^(00J)LP zChO4x@9#&MT2VG{;y>`dH6J`o16_hB`!k@n>9oB?j6HY4U>B&`=?VASvfH<%mtLZt zJgr9rSq>_1W&hurqbPuVcP8g`j%ML9dE8y3PTQjti`j%V1M5j&Va1OasUf;fhNJD4 z&>RHyIS|OeE?$38N*)7>O5i>2`!HHa+UZ-y^+dY-*J4?$c*s_tG8e2A+Nj5dNSW|p zA;+(pdsxBp2&hvR>u1{}Rz)JxZ{LjxuRjYSUk6{U3l)?b-dW zYi26dUWMT|EH;6}2|veirU?{V;Yx9bIu#Bi2Prz|*B_j1iBm#RuBI{*EHWL{+-w zHu6b7(zt!~He>DJ>Y?1YLC>6~l~?4e%I32F+txV04e7+6NR%#vNksL{TQ6mW()B2x z9p4*_fovjzl<+H6vMd_0;t_dG7{{!A(;lp|S(C%J-V(r5>-(dc=w-c>jT|U@*d$q+ z8O0EG0ba0XYYo#Ef-|<(!`Od)@YCd~Ho2Sk<&4nHOF$vko!)yGn^D(Ct?LsD1&B?? z=Tsa7z)uL41BjGBLH~^@EVx zAxvQW{Q@UEbjk|;k9dN`$@#w(KeDp^*XA~jqZzl+{_5)&LU_ooCK{PMmQSCFOYMfa zHsb=z3Jnrdja`*MFD}l~H1hTA2T3g*Yi?zKRKO&th!%IDYgPK{-06d_EH&ipzM(0q90)f>Lgd7=jHrsANdKP@&lZHnWQBaLdZ$_hT~)j3|d z(Q4y)CUC1PyCa}GgLy;8{u*iC8|UKk<-!mQ^#$$A{Oxet)-;AZAVAp1VvL2Ul%){i z(Hj1*0u|z}2W{Sj%^)>a4H~KDgScYT5(==-%Z_Shc_Tn zj{*4i995|4Y>!pZCV-|NX~q4GzzURzENDoEuWv*@BvITnyskja*QEg(F3N8m)T)!F z`ga#r;fjJ0iSuKwhGm6ztEn=e+fo7GB%L7UI`j!===CvCIYeYpFl2!UVgm41z3HSr zu}hF06wS~yN}SUekLsOrMKMvmJ~)V+fs_O2rf^O4TTx0h-v&!UaLD|yGS|fwlqwZy zPSw!~0;_4W!Q37LnwlZt*K?L1M&`{&{8U>tS-7dB!*!44s^4e!Gdc+TOEQ>9JcgSx z3(vEr^L4U1wFjizDM!5r2>}g6jKR<`&3)o8ZL*&wFX=1gwPh0Do-0Vz?vvjmmHxVM zMp|fE`y)FO3cwzcMuSulnz29KN`Vv{}1FwW1@)e$ze#?{$xIjPuO$%uFfk% zT!V99^5^dpavVE2Gz8os8ZVuwg77h{+>e2?Lvfo0q03y48o<4I=B9n5smhhn>DDMw zla;rxNBiJys@GgB;D0-;(VLmYd1F`6KCaM<@6ToN>%vttQITKJI=@EEJ2ZJeoOBG% z$+Y96>C>|MmR>e-SN=v49M*@KbGEd7fnp5PqEA74?nV5}zT%LJJjT8p z@jXL=l(Pb!p#ofRVF~OzKwOlbcRNaBo~M%%i5MG_<3<7bjX4S8I-f0|;0y9hSroz> zHW;)KJ2mJUmf$%2P3i~6YbyV%XR|x{(&w(t#5BTE9yupRV5ViHHWbK=az|PTA z>?`KCys~jL7xU|*57nn}xiOA=Q*YBk=OX)B?a{WO zD}vo&0_dlc$&B$1z`~A;obCV9ryT>OemMiK*+o>#Pl@m?`kDJ?or>}cE0<>`uJ70f zAGt*7<_%T?2Inko>AJkH@Qdpa_6$(^_3Lb9sjec91&7?bm{<{fEJi^Y&d)hd1`2rQ zL0QtP!I0hw6^1+;n%bWQ_zG0N!3RUdrPudxe5&SSJT+ccO>;G^o8jJwDPDQ-X;AOR8edMOg>npfvywzcE-6-3~7o34Wxt{ zkk7(l^OF5Y0QA&J(TBMju3-@#VtGUQ=xsl%oP`H}6qJ;(hJ%WYu7!W{yi{v>kod-1 zCNz}jgNe-m^>`foCQr(e9}$Fkn!~gJbVDG$C>L7nOLWmfG{8K7IJchA(lj&3wlv$V z8uYVK?oYF^7kT&_eJ(p}aFz8*4e#E7KVr{a(b9oIAZ0Pc)S;)xI{O1ioS{fBS48^Z zWGag-T}po=1jEUoWzpo_-?h|Iq%YOj)SoF_;aa4@Yv16>(jG_uJY^^DB$WzTSU({N zv|g};@~By2l^Au-E0|Yuj8H*}Jw*{Q?ua)`cy@2v_&P#_I4Y$n=|~x0yj#5j#*X{5 zy3>na8BAk|?UgL|B*e#KC-o~KevR08PG0A+t4uo#)A8tk53rsvG>%gxOMTXYEXNDL znT)5`1b^Ytg40i2{*xh&9pYp`OrR88ZDtPX5rfkWG~@a;9$Hn0E{y5f|h7}b~{ZoWt7 zM8hL3%U~YpJGq{9#N*-vEDA+h`hXCY%LuFznb;|_tp~VAy^m{~?7*J@pG)UpmcZZ* z0QQFWFC3+6wiGG~TKk4XCmN{VLlBF;PgcTmG8Mg`Gkp8@8m6G?9-4-y@=;^xB!(au zt3NGEnX;qQ+bYn-4Tfl4(&45FX*JHW3A9?JLV9%XM|zfjSTrijyxGq8L=VJp!dN6r zH{xlLPt-GG2b@~DV7~MKXpo$eR`{Gp? z5}Fp=cKuH0Sr%zHNgiM2B(*wYTu>R~tgfWJdYbq)=uTq8r!omZ!RJGtq8AUe5+5iCH-(AXQztpq1or|}qVkB^|& zx`8A4)8($v4@FMbFeIjxgcC}}7cdZBJ*BqKY?hRAcxp?^OHtc95}Nhta89cB zE8yB%U;W|Qy(5i5^fh%7LB&y~F-fVbUc|eY`Gi$a53gE(uCAnBQ*8Z||N2gX9)@1m ztYpkG?7D0NmUXMNW{GeQd>5m4*#=y5RH^nV9@>5zHy~HQp_j)_Vpm4O&tHARU-d=@ zc`(xP(CrPNU3wrC4(Fbhc0HeA2#X|8?6^C^T5gLE-?T?<6n&&BEz-u^iakl7!VnN7 zM-^Zxo499WZ2M+-9`~@n=}989rpWY0aT5lR(;M9Y3`AK-(71b}Br?>U0kUT`V)91_ zMHxaW>kL^R$_AA3ez8dwWkd~0C*GFk`*T>2Oofk*nl@d_!EW{+-zFb5-_OXX*(*qt zzdrz|#4Z+FIbLbLB!od=iEdC{c;ivlgyg2vu6=IFos5lYj%bw@UZY1Uf~wU+WO5W) zyiN{)Io* zy#3!V0@1C8i6>zVxVEp>xBhCz$?z1G=`>t`A{Xw**EtCtqE_5JL7dB1iuI9s-k>QM zIRnCOGIYg2+cAd3=s?W&A!QrA`?OMjrn-^U#JGoA)d<5yyIF1SwczG{ElhJ?>j*u) z=r7Ok5~w*8g%%&*rz;Uw?LnrIGxiMM63O!nDFvF!wb!*-b493e44dG%l@EJSi7YrZ zBql`(!N_~zB8aF_moSa-AF`Xmy3m3N8Tj6Q5-Mg%_u7Fs3MZFWDVYh9ReKg7>YIC_ z7Is^*7_tMt{TuUd%lOCt)5L85^YovF`(J4{@*p&6u+YG8z#K^dvDAPO4QYE^4y4}e zy4?o8;5`R3PBH_qu_8IxW(%a__`hQVj!PuAB;bv3#oxU<)%fa(9GWB+*+KnlTXFZf z{}eWsy6Z%7hYdjYEMK$OpXf$Yh-_`?{0^O;pD67$yz^oBMY~qIt{6ol~Q_-p* zQ1LxAMz&1~Sd425GVcL)1*U^-b3V8wg3!D*hUhq8+zORn9a{h)5>=F51`KG|=D;hT#wO2m-}zPTm6}AqxjlcLMpa3Xu6|CmvNAc7_6`JCg*> zsI?7TQ{ufq|J@D%0mGEGBw%rP(*Lb9GFIe&&F+OI&+at~?Rb4kRC+~X!}c5mcn($? z*5egC<6^No{oVcwPH?M2SIeW3|4H@pSxwe1why3DI6kqx`KzCK+} z{`>lzH5w@lJZ500BdFP_<)FNtokZEmGG=5C_8rC_3LAF&+#Rr7ad`|bJBI{gEeft92=BJ{*$m=57f`^BPewf+9{G4Ov-{sJGfuA9&k)oK1S?}I&hbN0EorC`b^Jf# z)ZTRR7Yx+$V3Xm^9y9jhxR&!AOuy?NfXXf4~huUkua^V!tdH{>C?ZBl4R0NKJbN~ys~3L;kElG1 zI5|rn2UCOH3Pcw@+id#MBTVpWzsJE>b!*V8fs>G=dXGlGUIeIX_iK*xM?%c!8NC|D zwr8Xocg&qGYq!1Gk~-Ooz1sU>1$Em!qV&PkP%re$p+X4j^TmSndl^>|Eq&LgtR=ng&ZgLswlIj#y#gVgFo-RIh-fl7d<{q@nQ_HMxUL-BaEJ53 zO8F_}5WWzxmKzmAc|%w`x)#Jpt8u{ETYaK2^Uun(sGd7ypgF!4PFdPH6!Rv*t{w$! zI_v)$6|Kn_$YgNNc51MYq?Y2$qhTN`m-1Ij+2s5)4VH1zALDG(s9`jTj+gzUXNr&w ze0R=xq+f7w`msYN>~X(6W*%L^pGp7j?TK2?aa}wY@Nw(1EMdGXWqWpA2~J3~CDjUP@%q zf(HR5>()J@1OMx-9pX1;_}08v+Po()Di$;A|2)=WVP|LlIo6^9{A4#Lkbkn9yQHe3 ztj|(+yl9+|whL$$vs^9m_wit(k%jc^Xch6vjGvD=YilJEnu-TxzYW2l#(Sn_W@dcu zr+<>nsKWzUzaYN<0K=jXkdZ_*rh~t}UEFI9drXu&KUj5r{5XWa+@=HjSk3!}1a{aU z9q$vm^%-Vo^eHs~@nRWA-0j^6!3POaO@{N6rZ@Z_MYQKxYdTQKPd`>G6OVbFS&h|M z50vd`4;-A2I(mzx*_H}|&c>pG>-wsO&peDKF5W0^{rNG(hDb)gT;|b1_nR3rLdI`D z3?9<#J`BnJA7AQ-Vh5MJQPq}Cae_)_^Sip^N-Cdac~U)~`t((MqhA^?F!$-5#C}qC z=O4&%onccK-yg-)tErkrER(WZl#HH$tjvDa(`VH7o@kVIaGxz_n#qDAsVJzQ2o6J%39)~cIF9m^f~4w zLN@FQt3nq6KtsF49pEk}m{;1$?)jtZ52niE7D)y-oEVnlAT{&AXc_;U4iTO@D`>nE z-DgP2?2K&BOvOPvFU>%$$?8?)KR+v7e9b^{SxrhUuJacgP+4w-3Ie&Li;~S`${{XGJarV5`6Y>_Jf!&Us$dn2vtSCCN;OnH9 zQIuyC#@MDt@@?u(o+v>u&z=np2{!2G>Z3g=eAGQ{kS>xJ?BXi3tnG3r76qgu;Pf1= z_t5JAV;M6!JY~sbAP|{@L)jRNizx4Nb8axgVodJ`&oy<#2R=^kMYx38Xg0WufoC4sV&1I7M+|y4(Gm6lXlB+4RmZ4Q?2%POSjsU0nQ*~BG)4-|(SmH$)^rL3%h)vE*!&S&UZ+G)wR~J&9)JGnJq?sLc8A zzq0a-A*WKX#W&jlG3>)TcPK^R0cNLSN!Sh($2ApFlW?EI1ba(^N-;IQkR9Tqn?i1p z*)`{|`}}HMs7~uHqSOYeP9X-vv8q#arRz0M6xWm8?>HR>@t}RAs~BSTwd+QJj_D{` z;Uq$Ee^QDGRDxR>NS2T?oIx5_!x3dyln8^}_KZvk?5dr-bf4dm;~Jt$=E@0JI^`%J zWb(SPBcafE#-KQKf%}*=Kg!iW=#$~6S9fw0w+8(}#*nIU2%kr5N4m&G>xq`G`9JI| z`op#*=lq)HX-16*&`@Zg$Z=u7(`I`7v-0M}EuDRyS331YaK&d1on%+*stN_84h5sP z*LKRMRr1CNgApu8oM36r#rtH|kIj9-Mn3GTA}nttLE?!{we#>=jr_;J=%$K4r~qvf zD|uQ5!oD=Ljc6w*3FUAYbp3@E11!6YR6D9(^7Ga7g1syHb3X7FWsCq|_arG^Lt@}; zH>j@E0|HvQlXBk1rec^}k;^f8pB_gqAWYc2lTgeMuzhuu&`T*26I-MLpG0k2WWB^+ zn$Xy_;0(vWeyiosAjdHth7#T;gRe1fJDLU>GLj##nbrL3ZLu6m8O|m_E@UlGozqn7S&qj zqWOu6jntA_!s*ujbf+|%t;&JYiN~&A#fU(?ttW0DlOp+y1Px~SMx7x6BF~SlGxS_O zX8fkosZ}5BX##kF`P`Z}d>6HE%$&YIbBHC=ZH$u(-{`YBm!tK+)$>76fS>1n?sTEu zVQC&OZpn7ctMxL&Do~c6qPFRKikWv6WQ*H2E8!p5$;!pCC5BUbUdJxe#ENh|V60nA z;d(ZXk(h_r^GA+^bJlT8tDBslXuX1y!W>Bdx->|ld2pM>k78cF>?=-_!1h?E7g1w<$+$jaP-ECt)}x(1#~7iCbn<>8L*xKWja4ixzlG>-}Omdq#@ zCE?3uJR^VvmhuJBJu#0V#7$6` zZuJ+`P!~^h(+DIBcUk+Mo~Wd!*w&jbY28q-zwG12Lq85QdOR4}=9C`qHcgZrirUo# zf4sPw%b3qgu)cX&4Nh*pY|s9N`VJSb?Ew_<2wMPQP8OQt9xRqLYRF8V+~*I4I74D3b?@!xS%+u%7mcSf1j$bK5*Y*1sTyyY`w?RR8jLnli9);RM7tc z&W-_0(Hq;3!A%8kqDZdrt?`m_dmO})D<_zERVuc%_jsMn)c>ACxwF47VYsu*M$^l<7A!fZEM*YjZ*i#3f*NKZuv?1%tLkArAd_3!1OH%uc_s-;YgH`UxzDb({p)MoFjqAn^0RgjN zFXZnYzkC1;xB5xIoC)sW$Xb7l2XJ|<70?D!?jG(ZsQOIY^O}429D1uuE9#pW>>>u- z9BBO8(f{%0LP}edo=HHuyUlUO<8-*8>tuiWVTc5Z3%z)V02;s*QwLL%_QmQh;>{VW0_|KPDll2RTZuq&~RhBnWzI?oVZBT}}m^sMZb~*xqqj=$w_~ z$x;cLfiFG*n5?1E|NkH&ZNn278jFSLe@>{ZOe`$_olwu?_S+M_`2isUl}W8?#S)S_ zM8IZdc|w6U3rYC9ZkX^bH3YB?r(mx;b$UyaTllj&2w_3M5rySO4H|L!==e4K z)rHG#b!a+8DOx37DWjGrBrWTBdY^AJD%{E+P7B5AU#O=qd+$M7zN&q1upb{)Z1LLi zCq16eXMiYsr8cJmX_rPX_rAy&1lyxf$sJpD0dRoz|< zjs!Wd4{$5Xe&|?Zi-Ch)_m_Foc2MT4T9x%32cVWJ*Q>-WV~hqC!=JIyOH+0AM0mTr zZiVYJ-j-+A34!qrD^LTNeJ0H8?Co1)J#5%VFnI@eGaPl?qjFhm&yvIli3r4m+}(SJ zDMk_-D6r$PYav=99UB85DgEGzoTj**=nUL``srwE1Z8pwLiu6nmG<8<_`?@`W&Kda z0Q>)7##wE5?(W}px3TR~7ea!qGdg*1hFupA(f!uPqdVY^daAnc1+FH$DC{Q+4^QuZ zzjRuk`E&u$D9(qNum4h&l@KM;MJJ|%D{f!jS}T;Ih=DA+h&fwyNWuoGKl`=_86Lbh zihL2&vET-)TN@YsFc>X@L4JYkv^)xR0@fSPC1Zte!HNtIS!!#G184)Ux;+n$L8%`} z7t7z}*dfsS><|WzXOvIVsbJ{U>Y2w#GJX@|I?5_37#341)w4H8hSXmsBm-x%ZR^&H z!G{|fE<=<=*Gcf+ScyNm?1ho+qSX)O&Q0fS101jG zd;_we@0t^E4xkw|a+z3n+SuG9OdQYRsFKmt)Cn9g4ET5MxhbQ-%Jys~s=68PYV9rf z4&?7*wRz#&qdG|r18Q3ZSX>pxjVA5oO(|=Ks($%x+)CfdF=HhkAFU@DIsbFapYPur z6dF!|cdhIRqpHM9zH`e1bZ-R-1KQd=TY7A}a5Ve+&r%c%2*N{n^|Nb}K@>G){if2% zSP^s@XZlYg3B9xI-7f5s(7G?afSX~~C2v*=FQ0Q9dLNh=ut#ij;Pt?rO3^6nBt7SA9)h-rp-=fhj?W`D)-k3w zd-(|>Px0~P#dDxzMG2jVnqrR^or!>Upz%=PzQ{b@pCIMot%; z`WA6Ii9)>E_xw~!jWdh@Gcwol&y|Lm^UH)pDAHQ1T14+hn53Z$;eh2u31{{9jd?oL zH%sMrrR7N2IOub*1fxOV6lo2BR-wb@P}~%y8{+BxBCb=@zWP1i04U6FyR&iO#W)Pu z2620zq|^yYE0@$M7tC&2K61~B!W*Rem6X9Gy_r4rD1Fb3l}^v!UUlx<037xZ(WEUX z%RZkr8|wwOVKUfLCQ!l(U~T{9(ZGPGGB)4ixH{| zn~6qVxODA)fr|>n;T^$^0zMDyOqIk+Wyk^H4grnQA}SLB1ehRZqR8zt=`19>K?2vn zlYko43vG7nm8GwRAsKnnYR4|KjHC|Q1$&lFuqWgTlHYZ}E%6rw4}%ql^N$(kC46nk zAO`WN#`PCTWH(@FFom{+zPgtmS9Vq(@x5DM(G_eVgN%U!{TISC@1(zT7s&kaPfFfY zo9<{VZuc-w77$?&xP7C?y)7;4^w7x!EHj!h$HNgiK&Rfs@0vIr%`^Gw>5rGb9HJMA+A? zCfK9&ps9ybH=F|g$|LSu&Ud->aJvx)Z8&b{4j0W7*$*n?;+(_2LmMMa5(SYDF3jZY z*qF6A8-TWSY@;74Q%W0W3+9r@h5xhV70I?$z@bAkDmC&)jUDKJGFBs_(a2{x`?Pme zj2D?8f`u0!Yo4#mbeX+g#3g|wGe7l0B?RL=>K_@3X`Ww>W!P;e4h6pC% zWD%uQR0|K1+9=zy(a{Gyw$bY z>-T+Lop1cqVZC3W!Wv*GL@A$U*HbNfPhF6Ag_ow5)BBKewxCWcn@dl5M4U}gQYX(! z176fb_6RFRL~Z>#k<<~pt!({cGZuxS{0yy7mBF349y_TKT-B@ZjbAHthGE`xc_}?< z)`F<>&{Kk~L=RJ5=%>IfWk#L_zD}z+RwDy#w-4PtY44`Q7WhMVAC!&N9I?E09t%u# zF?jfodVN5-IMbpgDoS3~vO(C5NYk^M>S!Sue-vbny)+I>?dt=d+wJbX3)tIkP6v;WVD(~V9EL&p zIYMVDIXz=DMlO?5=uyv@O*>Uu@HInxhAodgWp%~Pgr89#*=2>TmA-ozvjx>c01bWd zMnmiUtPK(B*5H!mZoI3QRDUK6FMePogb4_d)dIV!d!oNwKPYa*5&fDF!gK=NFG%fu zAcEM9*>_RN`tWnFEW=b|3KWF2KdklHl-0sa8pC0HPROM`<@-O**W2jwz_uG+^N2ctYtOC7NVqys>4* z6Pj|oE5{l_jqSeRC>0zlg@!e}?<5I!2YVxl{(K~3?%2VLD&H>bX*jWp6Jt4&HY1A)vA zwvL>pk=1}`R1>u{D9yKlg5=8FHoKh#i;b>xso-a;_u6P(V#Rhz&k)_tCm0#DS%Bp_ zyMs0@l3qGX4V`uq0`@@RDAR`__@{t4EPHU9`R}!^OSSUGGtfq7-(y`l@6jhqjD!_b zzErA?L96LkK0>1fC~WT>>aJ%h73aEkVEp!r`f_8}L#6%-69OTzKN{XLrhCR%Wu)`^ zs+1Dv2MFMtft90EBssfTAw`d1OdGR^vsoa@={xkppeO)M0GhC4;lHL2m1C?1-Kvpx z>0bP}YQX1%A*QZ(n4C7;JXidzm?VJMDo04_Ztc0S8inMBUWufTC{sw)eMH4H4(xC~ zRL_bRRMmFv5kJy!e?KY3KbIANHYO(N(8)vwrr+;}XDBoEc7{pnehrib5(PHm$8qK6 zIOMzXaT30s14c?w>7X+VFd&V6ts137m1iJZI7a+F-oq0ma2(eWXGi|vS*v{r7QkI? ziwilcse~OP+XXP0Oi4y^%?g9#{#N8QhrL(&+E@nN>7%)NLrmKn6?&GzDV1cWl=Y{B z{+a^NGfNG_#FVaL>Y(8ou+(;5$NF+$%F}*#RMgH&0GiFUN*a^D3MpJyd3e(jn=H5t zp1%Q$$V_F4tm)e*J_WUC{u4LU%tjppisda0ACideQz)WM4t(<|-s~r2k4eC&}y$sg61Af%`_zku7Q!V~n+K9UH&#t{XoR_A`@iua?q$aur7f05TGLI@17!LnF= z3){Y}>~lw{)Q4sfGX!WZfZ=|m+TSE=;eW(R0QQ$zbZdfk6!MNvzr_&0QNqwQIWRZ1 zHG+Q+47iG{;Oja@M5dO{@0ds}u6t@*3jAANbd^_M;zZrgT#y<6BG7{USaK&1ff$mFjexs{H0Tw}Rh|boel# z6vjENYt(%)H^M3Y-Z||cHcwvIE`8yUz3a#;yjROn1S0-QwU6Rx@1Dn4xbFzaNxKkU zd$s$BX}5|PN?b>8na3!Me#qLaiYo5O0%-iffT!pjHekht+t70}W4f9$X#5rAXbzFo zW5SdPN0EgS{jL+FIExicLOMn@hwwgSyGNmm11IK&X?8pK6MIZYjz;<#nyNj~lvtgMCS%?3|SP16_+8iEed+lX7?K{p#+)>Zs+P|+* z|6LZpry+j`$$d#y44a(njWRK?{*9Z}f2H|IUP&CO5vc zc_wvDmtX4aH1(%arQEiWZ>@D+6uyzt^fi6m*^IlI?33`y{Fo7(JUPhJ)efeLpTBQ{hy#-(XbWYp@KE$XI8`8uipjBKwS>oPSxK>pSaiT%u z53ns>*#oA2b%;Ab7}fEb=v$yPq=o7Zg>+gX){gjZ=#Q{^5S4T(^S?X}=3%WQM_Au= ztb!(TC5?>$;Hi2N^*)7~Ro@UKLiD`hXn(HCrNrwz!{jX&&6_JqzF^Pp*?(G0fDuL3 zAxieONg}=m5V1@nBEKZ78)Vb#l;(FV+tTt8NOHBV;;zW&8Nkw0kf1$_&fyzXNDfzUB3qou-25Io`pPcV?g zUWv;AXbt(dwz9tGJwN<=bMA@r-B!bN#Sc?3$h&%-REmc*hrjeGSvKG~a+w1B^#@Jj z0r5OApD8|}?VN0eht9je>vUb?;WSc|Ev_l31aW-`ERSE`Iw0XmD(j9!u`+-yQ{M&M z-j0uwOBa~%pqH*uW$>(8pWvI+0*%I%Pz{nxYp*@cBZIppSp2YxoxPNn)L?rQU9$>U z^}tLbd%N|WXDdRNE^ryl4txXM8Jx?&`~J1J=*3&tgWtvTUEfrb^v3`&2(xcw~n{y{C zF{q&6&9v+3a+n1A9+bA>O)Ror1jqcNzzS`^1g4BD7yuRD0p+$bkO?1K2*N;InrXa8 zLGY`sMu_n-*ufzcGCv*S{gB|0ECXtoZNMZPFzK%po!A5H^Cl1P<8Lir)`bAzFpwG| zKj`oKCs zLabQ7c3f{M%^Pq5CPB-u@c}*Bx@jZzVVQlj-nkknpN&=Zg|mfDhZ;BaV!u~blb;=l zV|x4W*=>hJGYPdn$m*|iSg$655Gx_r&%z38C(+AifaLfc@6bM0(T7IoxgFmY9JPLk zz1$Aex)zf)Wtl3YQrc>=JAYTGBqQvhp&&<`YKh8|d53XoA}71N&i^J9)VReuPP0PF0)3>tpfEc#oHb773#y zl0zI5rR~*|u)MBoy8VV6hmbHvqA0jj#UQO3W(5@`LO+%Ft-E)>ZNRXO55Sl}1U$Ot zM)%Svah>YgswNSWS$IKrQ#r0%9Awos&IX3KH-zNCuN1TSp%sxeHnAAec1&GZx08&Kf3~T;voP48 zUQN;R3Ijy44O*%qV$U$arLgc#KBfiE9d&1hiZR3Q1zQ-Px&YPuZPy3cSbNJAp3F|# zsAYQP>Rl@#_>SuzyhUEwi8eklZ|vKr-JjA&kgay8=)j{v1uxZ#>Q%8HvET<iqG&n3w_ABRzs9aa*2^r)zo>VMt;J zO?##2<(M(0Zt~Di@Wus}e%Adxv=1+UJ*1n*!RSH;7qx8SCLl)vt0L95X^($0m;=0p z^M)*n4FJw`wxW8irpba%zj4`Wp%&F?g*RP$BKS3+3xxU4l7R7Upja9#-`$->C69TY z`t;;)&ELb~<{$;`OjTN{K%LTo1?bcqL=uXB#KyiMTwn!+C@t5~#epP3{a@QVf1mkT z9fZ44j{Ik3Re7+lP!JskYd4o2zSX;3lobuhwmE z-eVA&2!aK|G7is?33FW5xuT!Yz(HYXZa((LIm%8t5N9ZQy)hFEz#xOd@8y_uIZen| zmjQ&}rITi#=D>@|MlBd1cQEE{SzN@Y(h4phDZlk?t2$54vc~acnifT#CJx z(ZV}qwA`o|f;8Huvgb(E%8g@!KbQ)*QOfGS6h?(5E{fMPe)Vi>juvbo=x~im4{Ygu zh8B*UI`ndbb**-1b<~g0-*hB>7$Wz2Xr$J7Fnx-^4XGiACn*`DLNQ_qXdJfR9~72# z9Np%KimKh!?nofsF#j$4x(9HiKv<*|okygJATuzKz*pYP!_D(eCpVqkx10eCjL7k* zRVenS?sF!X*%_MFfHJgh^_zZzNp8UJ_<_%VvA*Ju&y3gw7(YewZzm>G_9b}41q`2_ zZ65RKs^soOEA;-pl|~i4svj@;y5PRLbuS(z35tW{3>fK(C{{Yh<5AVa;IaY1Pt))K zRzlM`JCxHT9Dvp*fKago?F|41G`!;oJ8wYTOW`7dzljvqBV);#FRu_u#iyg=b!ItE zj5NT954@+xBrTc`g5Z=wCCKI*o0LLyF$Ruf%1EUCkdq>oRaSS)r3%!9Hi5~nR=?r| z>7&ibJ~Nk1?mfm27ZoV0=;1c-j^3V+d)B@!icLt%c+`y+wo-Q(1VRr$_i!lz#n>#n znBA`61zKpQM4{(Q={E2Xh8jj)drT7=xZOZ<*MFUNevzK9qJ z7bTi-vz*WX3{Lpr6=c~<7aC*m??E#N^1~_T75SI8-`Oz>UAG?Rq#`v-@|$D4F1EiIkO;9L$%ex0Y=wR zbb&@ky|#oq)6*1qQTnQ!mN51?VUE{7lMo`S1TGBNPpq{DEaTqSIynnW=M>yUoFAqHI;^|!20urUxvC_RM@0$ z%gWXxs>7A$63ubo2yOXUX#yj2qjbARvPxQM(i2f75D_f?A)8Gxd;E8_z+~tCzj7ig zOCs`@-K|S|-FAZu)#pj$L3KNPh0(H#zR(0vO@<^Y{H|Xmbi1%1MHQd8GnDLLb)CS2>+1UZ{ z1^&OHt@{t5T`-@UIUi8K=NzQ0TV2|eL*W-2`}7f{Wso zZZ=(@`T9m!lbf4(EUT!=ZNChg&L)0(+yX|S70AvFmZzH~1Z8t;j3!HUTs|(Fs=%;x zDsk21F19duADGkd&Mj4=5xOsaM7~w#$%%J8g=)4VjGg!MbZc#83+auCcDxXBA+=V#e!poKN8j}$+)=YDbK`O8%)hjD|MNJAXIrWk zE1~3FZqvvUx*--m4OX~AgE8jWL{_1k-^0`CpN!$6X1{_dv`^fn>{3!lJ`=0z13ti^ zpj6-=$nqyPX8LPl+`*pp-1u7t0nC0o1GPiW-ux5e^FwB(u7zq4Rk@0|c1>eI1tP(t zNaZk8O-^vt_>NkD5j~GygPTOv2U^mcq9DrXVs zLe>>lo_wsj)KEV29~SZ!L3T4IL7AC%+f>;*<^j{e0*nS~ul;W``ZW^jhRFG8BV|=z|#?KdUA(8@3PengY4hKx~ z^l}VtY%fhHtSj{h0p5njsf__EGi~-SJf7yEN z8j7}c3*X@(D^Znwmo~e)TK@-sMojxAu;NqmQza!18%2tUOKC0Ei~Pru8!<|O=S$nh z+TT047@~|_DkJ78QgCNF(1J2=h)d!af7kk;WCTPg+{Qmg&vScOyTEdv)5bm!qB#QI z;(QrlzHs$hk6lRoTf0!KGrs6O*;iKmo(eREmB$YHYs3^nsqHCo zmWQ-Q1!N2T^*h;gQNUUduMSbO$<(C7y?JT~becQjQ&TZ7zi3gycd5oY=XyzqKLa^r zsBrn@%v!`pgUUP7Sh@TM)AIR&VB!}Di2R7ylrHyTo%zA=hJcFW$g;xpixl4o!_C44 zxUO=oc86db2f^GnS2LSIcr7m=7|g*GG|YU+b`>LJUV0P0v*!@_~i+Iuvv&nUe_OypHYc3h+7~1 zvBaPb>WbrgKmeUr7O)Ln5%1$onLdHTq_tv0`Zr7`E*R&box;n~AqcZ`@BOJZrwINI z5@$ghleJgDo5|3Ezz;tZ@F1Q?$boq(KX&i_QdgWMm45Z{RnNgh54kihY1R^AyB;o$ zBjVeK@0znhXj5XhH{{$VZJTRXi)XO4Vb1+WiOZvY;($CEPY2Fv5vIqe>Hx@xF*Qy^ zI7y$1J$~8lZ4nFl#g=S?;8MIyE$H_c$5Lc)*gATe8c3|vMKO*Nas?+`!3m_Oo|7?* z4aNka0<2P8Jf#GR#uK1-TNV0PQx><|EkjW2VbljjNbEFu4WL2Gj)c8!MVWh^k8F2YqsIes&=3Si(f(ZI!P!{Hw zmILAERTqV$*}VGMgjl*8bo#m%&ou!<4Ahjx5D?XsyMg6qyWUQD%cX9iS?oV$7n52B zvrY%5Jy2hL@CcpYlKX;`_DU|v0qdwrpsa-%t~o{CVj&yo&A*PB#rlH#bG-(d(lz-Q zA_jl&*VOQSTmCR06Be5gSk>nskI}q z>v*ZXX^mko`Sf7HV8qM-y$)^%jL^npd3!v9b}b_&tHGT^&WAl8mBPpaQ*s*2ATLd* z2k4UQ5y9_*?jD;JhSLkE392VkEH>W&jTrJv<>LWP4zO`d_MQ0p@*cevh> zwfN^e1? zhJbewIHoXc1hVPn6{=Ou5SAmAK&^+4(fY(W56#@qA7YJPYHITD$e7|VBz{n&8A#A;N9~S>RsyL z@$xLOa<2-P4kk$*U;DFbFg8TxWEcuhw{IY4Ew*(%eP`ZF@V+L4!#Y_asPaWNzf)(Ce-#MF!4R9eoGwQNHnl zSiOx<%VdZgwdkL&OfT;b|BzpS6)y=)%lweoI>;4dA_&7ncr-`<|8Fnie-sg zF=+BD&!fjf6l!-OTea2^A%7*!EtHw!d-n{_BF6}4JBZ?jgGxPw3=kN#b-)5qlo?Ai zB86EbX7r{ooVfz#J{%{S`UcBmw(9wLv|KGPFxE__OFT%OA%UEN8Aj!7e*Jo`PI|&M zAJhs-v(q^(eySnOq{6=tnle0E2T3%o8=&V!i{eT5`TL4HcuiVU58iCD%HjC(oH|n> zU-fJ59U1(iN799F!Fj5lVyEl3o=*{kjfi%J2C~IdL4ui6bBp#;kX$}vQbeP`CI$#Z zSgEr#+orVW7*L>}V3@BSw7&02q=*!MuJ*$>OU<8gU%BFlsKCL)P{qHLs*lRo0P%eF z@zLt`PP~$jB0e1L-G7e=80v#+2X-6(E_XE|HwxdZ^c}9*SvVw7a-ryNEhylUT$tK3 z{Mc{n4N?|Q&j}z8X0g{<+pEIYTvB~VtV18QQGM<%qg)>+h)l7!J=DH=n2)_W!W_>44bvW>nv8gWVP~@L%Gc zCn@0g#9ic6;@XXy{FkCS==HKS!J`SZV`I4D|GbVx<`Y&r*5oq4!7-(7S+a1Zy;)V+ zI?}}(G_b%v3)9nz9aGBLCdrs-Td)A#AQAHbgP4f>al|^r8GXB1M%;?;>f&PQ5te!*iaZ+!^0;^=?H7yGN;uA6zP<2-H`NEXVZwI>l45?ebse)rsqzrio&Rw&uRkD5 z!Bh^ai(5#KFn{R%BO+mhNwGlU!_4P#%7xsJm^|Uy5xf+>;-I+5?(oX55sC;;0{hxb z=Ai^>Dp6zfk5dnHKeW7YUm_uLKzh^WrrG@1U*yjKR$W5I4Hn z5#`xcZA{cRobw0=?w}zk_qkNH6~f=Tf2}{nHa?8N*mf#VBq;u%%Jk8+@bk%IHRd@e z#}-KNlMT`GtS3pz9lzb+YR1Z@uPiqpE0(nY+D4+!t2ebRt*v`7rL)m0@TJ(7@|C%3 zoFrVsVah{t-Jc8jo?pr@$~Sv$WUjO@E0JbJ#ziTZ2r46Kzj1XXr{KDe$u#;H&{wpR z67bk#&2N}m*J|7Td;-rR4s6E(MX8*EY$cv?!hqKd(GGoubp;MF=Qm@X>aoG>mW6^@e$V8PkYv1zLqAo@mvlHR! ze0>5}9`s-463nCK)hjD-1i8H=?1=2J{KZV{hyfyV>Z~-nsS;kV@>gD@Q?XzVx{^dt z-lkEa4wb!^uw5SHV&$y_jgQxzY*SDG&V9i;)&lxZT-^ym6}TGN_ZI{I)A{7qQr$P_nn)-pj|E4GrfaSN)RYLF zrN-B=u>|m4;7hgi(Ia|YHdd2f71E~L7vEePXzUFmnveLpp`3J<-!O0tR26+MBFY;k zD%;jQx0ASFI?evQgG*ypJgYzixEfX>ne4u|y`NWi%LdhEsiXYZN?t@oIdm9YunrmT zgBY>GEH&f}YQhn{b-e^I*91i(uSJCryG{@oG^A1hHeWy!mpj`~TQ$-ZpapU)=D zfxV}3qsLK!oGoY}m@85&yStdLx)qsm^O_xQ@S<0KA<>PwJS^$4PbTKV`tbP${WF5_ zCn#1>F75q^%B;#PT<24AH@7&RvGC51<8d9?Y7=hthvoIF_-(Rpouh1k=Jj@{V2n;D zp&(@3`S2wrqKbVg*F)8TYi8bDsU9VqEZe;(WYY|5oc$$FdaOg z)z*8Cn+tY9RRqT}0x!`3$)FgbI9Oo4<1qw2Ed5cwD!Rb{5*JXk3|S zizT{wh!!G5WSU=o6@5cP((8Hh9^yWas9>5TAmf7xo8cu(y`Vy~gbmYO?jLZsR=DqM z62pGnCvDTvOj5ltQ&<7INIP9FGpyU;+!7?X_Oz<;h`8N0UU=d_j2@zW#B7JOo}%Uc zv`T8q-k2}(%{*yRsZ7F=Ad9N2=>$2F$@M2Qe)1FOq~55xiKQO*$uHC3`M`pjf0YG^k~)O zzKQPyo5Tg0dRI>Cn$|qdwr0Zh^Cuw68Mb|M8wiC-Y(oDN5G+Jolpj4`y^Ksb)WegQ z(iHj0+iK9aRDU+^!|pC27@S`)oSmZ3@ zeZy635cs#WpYnYqK{A#bxV6OmRPVeB;lQdmk|3tEfLk7QG`mSQF#BdCw+`ALmgs5M zj;Q?Bh7#vK`~*nWf!Rv~0f^-}MN|^_or3eX>#yGVBlWl`E^`VfYoTELe5(o#b}UMU z*7vd$9x!2jeV6eBLOUpC$ap%itdtAf0>jH^5ZCY}cj&esp?vkyF4Rk~kO~$d<1X06 zRyMum~hGBdLmC40!>&iF4XPOoe~cMTaO~@u5_hE#k1xi5?J5*7py= zPrRH=&RtyisNtGS`x;V3f9)R_HDjRzF(eAAOUH0x+H0LY&5n9cwk`AAB!xJ+NT~M=1+E zAe-}Bviz;nDh?uhK5xTH1>-ou*$}TpOmkDrJS_-^Oaow_oCw(Ord?L~t>aNhH=ZT? z=XlrQTi?WpV}gJrn2^V65T5&4B3eH-c=3#XXkgD>CjevS!#@?I{Z;KiiDg4pGIwnd z;BR*YjP#hUckhn*w_VW0kwBvFIViB-sDfUDf?Ne}UQe=aUk=?E+iGn)Y?z-}FzHtR z++vrHZBQi3%T`S)*Ge;FJ-u9-L)y<4(t&(ic~sNUX+SJ>L0_{n{liwTMl;#izt{OB>4w#kgsy1ZAR&!>vtM_p}jt+*CZ3VDnbb5%M z+?3k2y>=IPt1e|pHEX{^8@BHSQh|2dCX*W#1decv(Siid;Y=4Wg~w_~@kW3A0M*RY z2}FFd#T?#u2_gVapD%LIuRw(-3d91owwy0>TBzxTd(X(>_Ngf~&_+^@Gxu$(fg|0l zj-_MXT{dbtJDq3kDlf0EzQB^L=v4_HBNrrmMH#-v2ipaGUu#V!wRJhjqaG1#*y{3I zb5n>deuyDt4C`-Zpm!%(JE0J$wYd0b@n79LLnqT;z~w)aAj2FoVfc~r%cBFwJG+eO z*~Gk+j5ap`_=O!0VT+sR6&<~g0RF)5m}hV+#GJ8=369c!jKD;DDJT_hh zo)NA&>Tq0=7Uf(_5!4koO;QUmBxmCV4TwI|l(l%hVDGCoSV-w%@&;~EI(Oqb5p6$6 zAeVv_0ok7zgkNutU>ZLZg&DQdDC-PX{K$1>$^`baii*jN!~f8xPE4W{phVLxu&?*K zOTRQ+ZB(mW2rO=I+ErSLb2MIWXFWn7yU+!pyQSQ43_yIhBt#TePAX;~w;P|fzekH@ zQMp$Vk4XKS4XQGUbH&93DG{hkM&ZQQqz=X#y$D}!wk}+01P=TQhw-``)PYQ!LZI6A zTGa)m$llO3ar_W@6be5DEKxza%&haDB5#WmkHQrRp!XFAm=kMmTnwsYIk`{ZvJ&2E z2k{~qFDX(4aT9Al(~@07CKy`iZE20Se7#U#{uF5+?U@6)^Xm%Y^>s0D@~Wij;mIG0 zR)PixG$2>UJSV?(=l&r!v8i(0BR6wKl6T8HdB^WzF}N{zlAB>PNc9)b;ms={3GIsqweRPtJiQ-K+mqgE;>`HHbYmUk!`~82XZf z4u(;DRx~Ebpl9vZu(t5Ydxsx+ey!R^snsJl6P2Z>42Fruc+{zXzN)&Yajq>^lHxFd zf#TL*>*=|isj??dT~e-jJCW=F^(dLTwvNdoW~3Z%`$IKYb#cLY<3oj2j(V*-Q*VW< z-b;G#FhI2x^uQ(N{>R7J-c$$Bxsl|MK4$IY(x9Y0Y^n4rRfCk7TL2)scy(-kFD|&9 z-U*BmaOTuOiPlM%K-SR`!}vT$Z1P-JpW0VMUc4t$cWM6o^y~;USh%__JDicq@OJOO z5D_EV)v^ZKULQ$~NLsf!uMHjUaNSrGE=b+V!(1}vNVm6V!(v;DYzu%?pCvZi-9~(j zP1#G|%8ryR!n50$6z)AZqMucZZNHwna6A-TyfTJ&6aH|pTYnN-u-x4k?CfoK(%u_a zk%l{E+qsg)s!&)KKe(o*D*gHwyGS*I;`pWQ!gb58i@H`^=iyD2HpAN}Nyf0i9+ z+S(|#Zs`%q?x|YN;q+=i(JobZrEI?D55Ob*r+C(=)?IyLn0NuFhJ+?`7GFdqU4iG| zHmaCKFucMN$R76DdWyc3RNkT8x~ha4YdUL(GtYN49GTfZ!}|N=$YhbhJeb{pj$?C3 z5)18M{@0ahnu*p?7cNNzi};8WB7#h)vQfJ-)soP_xqzvmxS~e@BeK#;F^C}5^-_^Y zRzbwOwL)`;{Mne~SyY08Q%@n2{^6zg4B>^H z!cWxuR^Q7?nkMzuti^T+Aqu|$gsro)c4lx4=KKDI#jTbi#LIei5a^#P^5-m`g397k zKB4i}Ad}~&deWQ->3*PWwg!cy@ab-?N#Z7T%YDnIL1Hm3>8f;7yjvUDVz%3 zb`Hk*D7KVwm)eoCLRtx;oYfw%H^IJige_L+P|nI5aCCA=NYKfwFRW^PoX807w?Bow zw5cCM>CA3GeUbyHr#)26kGJh_(uLG=NFwpyZaA9zh zxzL2D(toW=bV+P`NHnc186w?>ouN#!BqEgec+|rI#cxJMspUP{F+!$7rIib{Dd~)K zNbZcaR7c?ES+C3Ggk{8e7;Sa^zl~53p}Zgbb9i;aFG{b38IjBW+MftEhKz^8O4p1G zYZknWX4{sb>V2-7z{i1UiJfx)`ij1x=qX?79m7`m&!?Lj7N4JJ@I02_eoSv}$b-Ry zjxG)Ws8?0PqQb5sH|wbGp6HkM6%RI>>PXRVSIsps2BG&?-?jd-D{kZn1) zTN!t~WZQg&!_pQzWRZqVH4pJL!QCv#Bks!*<+_2f2wt6E2Yi1veDE0r@`WZu_|jim zoh!ug-BwTTR&M$XjDOW|Yo#C;7CCElD;NO(3yfXj0nO;)Gn55}px`21;e@z`ZR6&0 z)LG+(5i-JPHDnYY_6KaqFZ96Njr+zQxoi7eL}I`55LaeqK0}n3u)$N3(PJQ*0 z!{3ebF`2$i&>~l=9>xJ1R2ojp%()Eo61^QHuAI|lw=w~DjSRmG%qOXDO}fHBvEw=> zMkyVfoir{WhKYty2_;^`Rf2rZ*J>w#_qha&{oTfrWIVK1+rlUK?v?Q{^;p+ajupdY^ovpOBG* zLy)TdNCab-siCb;OS@vsU#x8sPV!`VM8SUeQB|>ryHIJ*m311L}$!EqZ@0WS=b3TY(Xce?nJ#e0U2l?yaFI?L50I> zsI5#9J`9f_u+P0^d_p%9Apg74Q?^rL-OA5N$T4B{{l`S?Be^si2*N#mx#9V%Ca0PL znU=pON$@L8`51>2`Clq$fBvOd{9{@nCc>ryH@l_J*1X!wqa`iyV3tf8`?Pol(+agL z?hNzaKltUX(NkjgVq1(DTsnaea{n+E@8K|&++ZaK?aC5Sit)&@mnf9C?s||C{SY4o zAvFH@(~i@}=`Ky5`Wg72v_jy!Y2^b6Iv8_6Nnd&ELq$q5EP|M))561%<51#}lbfUN zSs7TiA+P{N7v9 ztxWhUZdlgE=|)qnO)*6tO0C<@18clW%ESNkXIad!nVAQ;=>cqzk+|+?0@Kdh^kE+K zUt{yw;X-DVuiTomGY;TKzy=-z6pS=+VZm!vX@s6rhydl5s5IIlHNKSBW>nn1D0!Qi zZbK|o$kxIKIF~AO96$=GmWzk6R*XxMmM-PX4-6^YRu#`}FkcP1j}EP22E+SS9@;>w zd#+j-4G;rf1m)K*@YvA8{l&ys&c+(+*ko0}1*LVFib(I#pz47{xajs4|b4PsNy{)rVNn*t&KWPmD(i?L;~s z{Qxu#0opq1<9m(w4a@7DRwoLPrcZ<8DW6po->EQy5?PS}kGcD^*vZesY@i80hh+8o04uGOI_Lb66CjHi)|$f%Lv3JhIr4C$3jOQ? zSM=xr`DO^@X@u?btdiN-exPo0bmO<{NuCc|!+~#7Vnb6yH8-CA=|Qgb4~<-HZjE z5vX!0QUZFj(|-mv&~}C%3->*DKxL6_ZK|$EVn(MnX2d7u{bLEe#ZkhU%Q2F-fHiCwlsl~Z?cDY`6k7zmg##?XD0N|xcdY9D6SFQME!Sk zk=4x?mZh}4PK7B`+B7S71{bBVPlc;SV;$KmQxyqXqWKAka zQZE^DD5O)a(L4Lm#~g`eZn-JVTwFqvbik?*RiGk^^*cU&6{##<=&l)wn`a&)$TXlQ7DI-nwFVdQ285LkM4PBprVB}uaX;QX9aaYN(Wkg9M`xgrb;v>uU z0o~{%1M(@8py};7ViG%mxa7Wiqp4B<>dEB`qp`D1tj&8<+mFvQfGIJua^py9WT!e0Ivni%ibiDY zccTiL=mzqk84BQ)Vhukpt=F7?op1HWNc6O#T*g#rBoSi*mx2TF`%8#G?PR?iXnCUL zeX?YYs_xI3!P86$`-!0fHOmFG$)4ga4rs&h?%x^Jl;hw1%dQ53Lsje=C;Q|-uoVA^ zMVHwkMw2hCz4Kgr^!UycYW{Z)1Y>4S1q}nEMCRgPg=3U8v$b&kNzBatU+QV<&<7|o zfWYmI{=<*&Y*8o@$^ov3-8=43{9<_-cOF;%)2By^JI0h&|Bv{FD007J8<9oGHXoNN zwRINkQ0BKA7)lGi!8i-P!hH1GFa1Vo&0bZLdCQD!8iz583ddki41d`@JG8O#t`A0n zZ%ES7{RbMX6GJ-n>m()S5LO~sCLQG|z~V^Bsvr_WDbe4-c@TnJ8>!r1Nm3F7O8{dC ztDs3cSi5nahmC421yV>284{N-Yeb45n_^U&ROetc%BT+w8dwC*e4z?jX;I3-L`}R2 z9r$H4t})D;=mO^@Tz?7@9u|Pbo_U3xaM32t4yAuH!F&#)x1f^-I|M_7&0*Bc>XH{yfqDZI^(ILU&?MQE6|?t4P=QhKUo8VK zW57khMOA`^MZ^7B#6f`|)%|3u(Efw(`AMH@7uu0#_LOsp;( zKlNkVFqEEe4g4j}S*%2783slzc_uJecLFcr9UJ(z(_V!6g~7jy9Cs!}nDdVyCUD09 zy=o}`&NHj^Y@WwhKW z^759fiBnLJzxl007E-^iFiOh)T{&~pn&~}fjzHbt(Cz|35nxpOHO{~X$1Agc5n zi=w7?6Q}RjM=#UUd3hcJ=L#Jj4cqUZt_r|*_Wj3DtelgJi`mR{Y^?S6w0y?C?Xov= z$8uqwDwM%mSPC{w=`L54(kFGy-YmwSokQOZms78A9Ojt->xvPD_DnGn-XcLA()m~c zch!Iuj=YN^7QIq+|K6@`6PSWQ)4xQKu6k3lCJl2z$|j9suU4V=3iKHi0wP2L1yq|1 z_8~J#7iV?kiCc;UZ^;{RUj8cu?V{)T99KDBJIqThUcKgXoPRrSg7OaG%bi(a1b_2{IM*J`>{24YvSd}mv$ z<>%$gZB;=qub$eR_K04o2q|9+o7Q@59Xeh!o0d-94s|LIo-sK^Em9;@LRZazXKRJy zFMK)&3e2R_p(-fayh#TF#MXMPL2m*M51q0YmJM&fL8iPA;VTJ}aF%D7O0i^wa~uf& zWa4lISr)4`>>#j;K77bbU0ipft{d<+(Yh7nRr$~L(sxO#&Y_)et&ikzx{u^k7dT3&)%d(Y4`!vvv=?1pSwwzBNa~pVC_7ph(&y!I`VVb6(0l}-1Cs)^X%SOZqc;!9SP_w!u5rUM} zA0<4MPRm0)UVY`Cmipl0N({H_hYOxaHbqlEM5-({hl%L9u@C=-v(j%?4V=!oaH%ct zHfo(18_}-YhMc!%<}l~|4&P_tm;bbBS+yZ!3H+4fw=FfjL@kpp;V@u6R7zKEtRdJ& z7Urz6IVN&fp&S42OOonf4T=HI!t~|Ka02@dXxqeYG^2d__87nAmtnRgi${3MWP(|u z@iJ%S`PYnvlEa+nZB=<{ThdAk7zBPkP1}c{7VC7#+xJH7P1tkc3)~^Nj2+n~TI=h= zKFe_!tBlIUV>%>cTxko8Xd_RIXeT81 z73|7Zj;m}5ZESv@Bd;#DJ-%_S6Y$A(2rAFyCeV3&yr{#Ve1MqMeY_kUs`dYHf5cD9 zaFN+7hr3A7P^(mJZsYckFj(wbL!Kf7q^Amf+<4A&k{LSZJ?=|ZE(tNXl?S?iAwMX( z(X%5HsQt1HhIFkWaE=s(s$Se!bERTkq%!%PgZ0CvTqCdSbw+la;Oo!RNCJu&`Br4<703;_@>6xhA~}pQKQ2t?OZbSV+54z0v-6Gx+ez z6mw!J>YpBd=M%mV3KfgJ>?-Sxj^kv_oG{Dm{)xO#$xfc;aYCCZR^W0Apz}O8HCh8g zqR@0#%{Wds?bwu6@)>OvAw6WNRu!a~q0dR!7I6}P-kbZi&XsgRIeip*Kz3PB9P8ZV zff!I4D8mcoR@}zp7mj2dOl0o?s-!9Ebm`T|dmO500vjOWy5jnI^PvAEg_?9p3~7zA z8i~tA<};K%;|3%k5K~hD;W7x~5GP(HGZ4%mr7A@yltG5h-;nS&boY7kAgIM_tnH~G zM ziJdLS!w;fy$fs~1OyyFLZSC$d#7+j&o*)h>P}7X33ypr`2ZhEa%CZ_JK{v zg^jd5$y1lPDNDmv6TlT7%hmEUL4~F(q!`x>rdAP$7QK%GuvLdN)x{rg8`_BOj70P9B%M(5GL@xy=WM;Ye*zhkmYc54;%@r=btUg5STt4F{QW}MDawc};Kdr}|<2gE`dJtTe#~T;DfTHH_r9MMB@*tmC;5QQWE)+kv zoUdXINM6`sUWd|?>>;8#EwsM!$V_k7uG%(I$V#!4kv-VM^bIwY1wu^Z^~R?1HEu}9 zeduMd=Bqs$e?u(f5(FGHwz#(Z*an+uA2)LFb(Xng_@>{vj;v%Hv%3b;5n@*K2a2e# zA4WqXV87KrrSqyXtAcz6%b->g6vYhEr?Vs<5KcN_>&&e}FwFqZ)i1E!d;&`{9!9 zRsKC!)A-lpu=b{4ePv7zv@Z*zQ^U`kcZ0%QAOv;hH@NKZp`UMkE(}ImzG7WaQC~XA zHO%Rcmy^&{^W2bLX<8G)(WAFSrk$O1l-^SL*QVy;5j@#>cT+Byd_DHd0goEUak9>mIzs2-&@Hbg{)+*6jsJaURZ8oM+ z`;sG)JQU@;$%k_q1Tl`BNQtrV$5g}=t-OAmJM>h;OFB8;B{Abl@~GxmJaTE+7igwe z^CM5ahv!h(1n1GGjB}K3lGnSd5hpQ{@yC*p_}P$0yhWuH9lNuB2)_$}9*_E89aIGz2cMqFE~ig8sYgAB0pO#D1x) zvFR=5NH8~l6PQFva!~yP>jTmH>{*nwCsyiuD}o|ai1Dt9dJ7g>RTD6QxAFz=LU5eV zZHHk~(js`#eSTRe*z%-0R1C!K+w;NV2Z*5BlfJAqi$C^|3lDJO2NHI0SfEIqC!Rx_ zc(buSHfce|eAnuDMnp#X3j3wr8H*b!miJ;?{XZkRA?*)3k!&}$(FwyHSO*buw`!8Pl?hhe81z)?Dnjw;tKqovfqUacpqePGoaT{}fQi~PlT zIt_88LV?xndF^NEY$#0|*LT6OC4wW|bu-lNqJF}l|z!W!t;jKIT9gHw-%Y=iWc zlKd8pn(lu`nX&q$s3qPWb;J?qIW(P`AS36|!Ht;5pF-Bs7-1Da3aW39KtbgmK5Ba~ z*HMmmZ`h8*@-k{2t+&$l7!xKJ#XP z5DG^c*}xnm{40w-7YgL|wV&98(}Ov4vW)+Y=wjl2Vi}jha|ArU`zI2px8X%}DXi<* zsQ%)B#{O5)4T@dN+3dAhFJnNOjzD-@^Y1FN^jlLe+wX2nowQkn(7NwZt0T#V^X+XvvL|8GT;=2HR~S< z5_F|_Thv-0y$ahJpZ-j6Z!!r*?X6g~kN-fq#JYD=K`Z~$|G@fx}?cEb%%>z3Clt zgRXmR_kARLf06jLYRkWNT`^&SwGEK3(ZI8kO`SQmC~Jo}WFUs>+iLVc2sVi~3F^>( z0dVp{a*b^Bs6qrG1bah@g(H$^Qd!Z&HtK1U;>ynNY$tsjOsnE)SMj9{%4 z79tk#R2g;336ZM(r9;qVgZ?Q)?i;f7%D-7?q_D}$cNFL6UZY)4hWTRDVjSB?E0RNP z$9A8fIA-ZCleQT6cQM?Lr^B8cZpaCA(J&@t#}*$sz+|p;qwGGD|CTk`n?c8E$9ub} z_V^wditp7J9Y-0yEcwUTk(PG8K`zmIg$;0K`hXXDp#UwEv?P2)S8Ibc;z11%Y96{_Q_ zYAE#=T|E<6VYc3bYFl+vQoax;aqp~&crzE4}N@FRrHb{Gp;xUNZBA_4w$stXlZkC-V%xTD+nG3-GyC7C>RkNiuKT8| zx~i-CTkBo#vr0sgiRckTK%B^U+zoqPkp38B>ECB{9`Rs2XZj<37g&WITD!|*lgw(` zLL4EvDDNt_W~~mQy83v$raZA9c2)IYM5`YQo3o67+#%UDcNT?rItH%n-bvy^j>u*C z_4lJgiicqWG6wtR)j198x3g&PBL|zb@cAS3%opjv(4c|KwIA~F-)zl>u;xS zy}B6hu%jJux!y;TZ?~3l-sWAlEHCJ=a5Sd38J3$yEXE(s2F{WcX)|;0k8}SN-8&e0 zc8`V_ii~dmCaHfl2v0Urvd#6HUrg9>giWlkzCEvH`u7wrTo%Y%Sg$7Q#4g>ABNC=b zM>MBAUd%e}8o4N=Mo|BK(gEL&Af7*aF}(1IEHXZ-6y?>uRIcnGs{-q+l1?F0C!Ea8 zq7A1soB{8K!EaL0zO!??kddR$H8~%;*vru9DYHbL4{Ep3S`>x438WTA zR6dUbkMk3jN{J);H{Fo|xIHBFo5WoLu81wSereiWUUE)5W_;5mk(FewJ*2(0gGk4& zm^BfYc0>B6mG2sX(f;ZY5u;k5XVn-m1&u^O6Xs0cQ4Yun)*oBFy*UcNU%ONZxaD6|rqrHS$&uJ&T`#Qj^uID`&%3r9b4y^CUM z#@1+hwR|@`o@QBtzZ|3rTNvdp%Lg`@L`NI>8oL&%3 zW&6_#Yk(`d)G@Cy#VZ*LY|*W&Y;tx;surR~*SGY4_IKDM#obJHg<$EEm+~q8=A!Po z+VVZ(S6Rwh3ylYV_%#$lDtB=j_uMDP$jMn2k9HQAf1fySZ%H8vx5&Hd-u?IOgMbdn z>3@ZmtlqF7*#}qF+j3v8Sx%maJQ&ysiWR7`6~JR02lCHnN@`J&YQ`}5qpj#?%{*vL zuN^ci_Q2=^l3#gRpLJH}Hn2Jvuvqvqs(IUanx!ysXMrHx*2lZI_DyvS_mQ}up*clQ zg8#sv(X4CFKez6)sfP1;vfb2!n)U52@7gR(l|pBzsA9{Xw=P2}*<%)oDJJP_#(8%P z5CEqG7R(aT@2Ce{tACcD)E|wN5CX9AJIII2eMSE0%|Z;`khl?u`Fv7*c3w~inD~Ao zy>(Uoa=et%Rk`7)4DddOEG5wlg+eZYY8vy?Yj^995D8)Umn{wQdI5hiunsJ@=^itr zkS<~o&VMy1Virb*6qM3GY#e#T^8&fn@c|n5KB3`#8EW2BpgNg#Gg_OjN@%=Rt}fx7 z6stL<@s=MirDrnqxA3jViR6CNxf-c*|D&ccbLfpoZTI07_}g96p%K`5im4ad^AQ{7 zOGO$ht(ixt-AQqwA9IjKohc>}e< zpkhK-@>?B&P``voFx($N$;d(v9rfoH5p)culZ%^$Q0Xp8Oxw>bL}Vm=UCN`CK{=v5 zA|BU5m$bAZ8f$J|d-c6WQd1dCM*3z>WA^&4Q%u=f$&a6v1}56V`ene9zdA=k1fpZ} zQ3M9*qf&XR@Ao?Mb_bC&h-gism;mKEI@j;e)6SuJdCK(Pj!UKXb?{(7uCQ7BhjTEO*V>w^;4 zfcb1ciQj;`fX<(uZ16rfHqCSx)n5$};q;xu*Dy8;2bgvjJB*#Q?T##-r690yV*63D z8jP4D^IOoOuDHWAJ@mce&?|V7Rl^WW7Xx+FkvGgAs57YK1YYeGGivDUiz4teC}Qm) zDY4x1iMG}RqWtF9isP9k)PUo-(J!M?uu#2(@;Es21rVe$4v)U)aCq=wd?DNF=;=}- zQm+Rng^1wXo)h~h2aDAH0VAAF!eegNi+=#4A0weg9w&`~_ApSLbmMZktItErUo+O9Ir&39SUef=bq8&nbEhk%FqxNumQwTJiQSLIScF zlzV(>2Hpbm-Atq%xdRk!YRD0v!>ekht|7lyT5^Ja z2yJI6p)(P#*m}5T+52#ikJT^7T5Rm%tjLmYyA+M~!QZv2BSOVa4=N@8OrN?@EI<%5 zlcDuaXzW6X|TC?6-d>`2`g%){NWCTiuNa>7wGo|kQ!vE+x z+Z@h;r~ELplZi}RDZ0;nJEXefw14#veg?Lyip4^~tdrd*C@`_7`FwiJ+LBm0xxBpZ z?q6MP5=Lh}qmaZR2yL?_cq?NN#J@|b1!54R?V?Yj1&?j-t*+wfHbF+b{QY}uoJ0P( zxSh^onU8IleFgmX+1g^~+-ZKDuHHaI_jT35B~`Zyn~d+rFe^t0haYFT<~BSSfm*NO zTl)zXEXDEcO-bU~h$Ni4bvq_%sP199ky1!LE0$`y8wK;j{V<-(Up9;;?i0PDzti-t z0Lc)W5=ShUOX7&w_HZY}ERV9dSUhy?h_H}Wk-E5VgdZpY(*;}M;;W+1ypZ2KT6YRv3`gtKNKwdUtx5ZNzxKn5e znt4`v2-!781(MEWPS(P5@sU_}YpH>ku4IEa)LDbO_oj8FodinGTRG^TzMs#UE0jGFU?)j5=ebU@|zGC$!f68 zhK7sRm3%mi&&FrPXR_gDjAZiGAJ{sX(Fdz(J7g?Xmyp4q9Kk)2R$EB@j&YHp zcMfDcwhxnvFK^y4ihzX4`SDM3Wwx)?XLcVDT*y_QacxqFu@?rt9}eb`=4;$dPLU^u zFdEk1sZuer2TcB;6+VG6vk0y6v^NOa{<8iXYqyFq8j99aem@yOQmU{RO!oEraVw71 zGiji8JfXg4sZJn;-}TFpS` z>W+Qla}_}x%Uh#~ZT-~k+GtQKWp-u`ygE{F?a4YRBK%dYDRIaqaV_fXVsX@PsK z;QYb$Gs{_QEmCtZPbbUv1YZ;T%My_5c=WiiZn{0|89Vz>?8eVKr?ydeLveNy7#^sP zd#*Me+tq$vBcfl^`|2p?yR?noaSglqN4=|~efY*Q=Z#PV4vp3cWI+tSoMj~=NH&J| zHOcN-;P%SdYXU?}A%9AbURCzXo9ER7$BpA>Hn57H^Lek^xZclf(*WlFdI(Sw;E$r> z_~f|!xnBGAh|w@RMvKVicN6NECtS?hCady0`a3@X&mg@B5o{!M7GyPIV3(0N5aQll z&DU(0QzhX9iq*NAv^t4`bSbtXDPNN*Ah=b40H$KgMBHSve$)3V+~V2f($_&S-qZGL z6{MHLJu==}z-n=QCEuuDAIwyzUTP>mD6r8;F(o}nV7&<3mK27u73HgwR5FNOHLhiD zdP&R!1VsU<&1}+6<;+Y2Q`WM|AvUIn82*)(=_1&`W@5L)MmaH!sB4}TR23&-k|+-E zb$eccwfpA!FZick^tb=I0IdI?7DzUBZl3>f0h<1=3$RjyjSbT)A|B;`%UTOZu7JN< zi-o6^$v+q*;#`I!iSIHwkWjz+x7(c~9A73fcu2Ijc5Umjy85BQ=10X9t=^|o$u5^( zxq5+Sq)afqT7?Pl^h&S9tIp4DJ0JHh&da!cxpdy~bSd@XDkeyOGPC^tbbq<;4kT37 z6Gyvuws#ufRD@7ZmB=%3RTy>xf7BcF--odsSCdz3x5J-&sydCU&~7}cRGCI{s}Hqz z*uDLCGzAntal&wDL&+}og0&9?~R?}}|Y09QSsyL*d}pR0xP?6aa`2-?_y>~$M%g$`6j*z@VgK+vfbA5Po9MjenHpVwO-JEIVjvUN!h5lZ+D zkIn0s&m3${*zzl_37jXMc>ZG!WP})GzJC1T{_rYUnWOkKQD$2W+sr{TeuM?~QoIG*LI&y(H24UgeCtGKxZm+7A zC93PgZy%m(eK@kf>Ny-%x~h~RUW#(O#DhIi5w9YmcnWN!pf;@S>`G_rBR3ND8U*8AyQ0?!Ur{gdx!0zDdbC##zY zeQqDDmqx^nIh%ER=7x6=+FOL0LMK7pM_=7raDQGcRP&7jx2X1`>tWDmw3)b}YUdeq z^+H7wsj1K;l$+^+Ix%@n_Ziccj(Hh^^uO}e(<`$@|yL5AGZlAqOT!)O;|29P4>;4VsqqbD!!qS+n@{+i`TlQMo+t2T>175um ze$(gHK@B*Z(*C>mo6D9D0L^3&l8F0of%~X3f30KUa)<&XPw3!b3e){e;8G?nu?%dO zgz(R!wn(v~9jpE@u9QFN;83C@ebhTf%FHIrOexFFNYt|KHUGA~=1G8O!e$>Yxb!`S zYqwu3tIoi4*g61RuRGwPvi)$PahO8f=7!#hZkavRK8*zPSyv>L>V1|n?xzQJ#&D{% zY?Uz$Ar}XxNT2rxzw^-`)RIM@N}#!8QqOXN^J3kZeMXSU1fy3D=S~%@Dkq9lVdB)w z&pI^vH3nDMq_rw0L`xSE@znn8MNKMvZm#aR%ys>qw@tagf~4PZ z0bEv#t2mEV;cAhBel;Sfh}_`W_>Ae%Y=v$@;9Qs0Mf$7IfSCSGhs$_t8 zhzU3l#cz#FJ4@B;Xn!XY`xo9`c7a$mQr85vB!_@fEy!cktr5={tzQZLNVkAciHvR=* z!s46eEA;Tqe#^B+E^{;Nu&9aq%xkZx4Z2TqYL8XW!UMqnP4C4$YKnF8L(Bc=*oL85 zCS+;)+GWk{b$+qVEp#0U^7bPD%NPbnI>)tWlwdYD;cJx*upALHpc2v>d= zP%(FZCTwOwN>uWn;#PJSG$-FnvJ%+<9|IE^LrsGQMJWm4A;`hM!xT(ljz2avV_=R2 zSHP@bRQuvo1;P7%+fwkVoRBbWM&KtXKPt%^O26!-&X``*Z~1R;JwE6y@3&Q4qhK4= z!jtqtq>;lQ&Z{4^t!i%{!7v#LmKYi%kI)$w2aSeev%Ymt>JYtdSEb(ot&m(3 zdx#BzrajG|x&a4RuNRy*Vvosyn_Jh5uA(M`{X=-G`$TwS^I&)M7o`Kn$jHu~K5%m8 z+0y4n-@F}EXw;jX+uF3VmpV}KpdMVzz@=&D;C>V@mdA>=3!#}hI78#8I1x@OP+{rp zQx{E!A&cXeK_6d*Woed;VPkec)71$l;(hjqU!1@RH*t{^1!z7}f*qy=y|KrYCCaCX zO7xXLlaR7+-jhHR(jpX?YJ?tBgs0)$jg&b(hf-c7X;2CIY$$|mfmxjbn0yY!tQ)1$ zn}q+~m2fXp+4GQVXfJ76<%Dp;K6CHI?|fR6BC?Y#njjpd#g#~MHJV}ooq?;^Em`^u z)(2bI#XZl0Dwrwm0)9&i-kvHt?!~C+9Yk9Xe^@Vv=O%sPLRZk*miC}`i zh%%fU>&n8Y#9IS4RJXx9XmBcr%gi1>E8`x(2ggbVpMY%(n_?scN+#Q`Mi_^g^U&c>*8e zDPP|ho5z+ai6K*z4fZgR_145Z=v`Bc`N1$zTkB5@nFtw47t6zUzD1eGzj|} z{?pOe4>ew(hhCLe>05{}MAIpQkPSwTU%i_Juk_^n&(uq`m4G5L2$&bI=j+nvn~kXm z^8Jp#o&f>ayejwJJLbM#ngKMn-gF(7Mdzp!B$D#N3!IMSHwvG~rjP`fjaZUv!{bP)7Zi~}tTxX>ZNh-N+j*KUAkW+$ zDawrk*t4o+fypZnulQOtVJFGMYGonZnNF$rrt!Xku_?Q5oIsgg047xYvYhLS?fIY8 zZ^t&xiv&sYA#gWBtuF>p0AwwK{tW$#ZYPacC%?zNY{1m{+4b7*u?FS!Ak!n1xw+7` zzs*tdSUKi99c7}{kVp#gP%02^^U5woM_w!d1k(eb^tCo|WsY@QGCbs&T`B2~p0Ox* zSIWJ})Lwpa1=5|k56(3ET^p*FrbZgX6r=ECzvI~R<+oGT!KvgMtUbsZH?LCV;0CCS zdxQgtmoflW&SnOMy>CxUGoXB3S@29Q0d%KE?2@gTle;B{9d7dv+@xdnd~U7JaR3$2 z8O4#}eb|8LE0-X6S#(KRE28&tuk#9nCLNSA{O9xN@~?87hWIt^--E+c!+|BJb`(`m zdcCdrqEFf4Z-LH}{pU;J4MeX(s!8%1%5fCmH2V-35(1B<&h}*^998Ne{u&8 z2-|_^T<=cl!eW1`_wLt(Yw*pYvSwGDZyf^TOG&H&yS2sYwaj`b#)ZAy(%-yZZ-0br zcvCcx&xRckZpM);?H3Nb2sJ)%Llws+M_~;8MWjXV+$r0~HI#BM3lS({LzOzl%ZT7I z;l@b^_c$kWp+v{-dhi*y!VqH0{Fh6c&RyLkgP~LxA?%KcbTTuR?!&pXDkqWHi?Vil3B+aPm&AEeq>gysU zkUTy*dGMZ_E`@tQH-Gt_6Thbs(CE-XeIV2KT0ESa)SXw8(JeY)me{-=FW_0Snyv#R0hG zR_;xPkUFo%39&?7>s1C`LLL(SUE2^MyMLfVFs}4D-f+XX+~Qzg9N>K;@K7Bk|uC!welIHYLsbBD%MIy`FEt87JSq(7tlA%#UJYnKPAV7?rFo z7VrRL&)mFm060<{wDjtEi1C@kw6VQ1 zYoiZH_b%XXrV$kUnCZqDq$~G#BK;9Sp~1YV*l2S5_M1a{##XA%)4Ou_Uo9kudA#d_ z;sr<-{`xxUMoP(n1Xefg2fKXAF>dgQxHWqQ9W0J_M{q3l&8s`8@NF#T-1^i*zVJa$ zcx+fWbH_w@HDFpCYQXs@581zfa;If$;qh}TuHh`qz%y&DbPGQQ+Ag9O_G-B7o1Jhy zom2#jM!nqzNlsy7q4&5OZy)n=_u?|&8UFS>Paa-gJmL2roaLl9T50nI7@g)H!ZBFo z&mXLg(V1W1alexu%@gQ*ovk~O zuE>aTe_5~Zx0j94>|JfdETP!6==KwM1w0d6y_Ev@7cgR9K7}GDiYv(OMwkBVFl1-p zG!vT`*u}FNmZ0$N1jWQC-q4S?s=<{41lEWB#A?68;Ng89N^)YW8Q~oltBvMLwk&yM zv7zp$^8x*^eLjW|(k+-vZN@B!p^8+sxb0>RB#Y~I0ACEWJcMp2d{i1I+^c#9j7Ca zXRC-r=uDXy>gkZjUtC-4OlAWHx>wtGDadsboPfH_VPayCUd_@t9H{sjG<*BJlRxP} z1Z|usp%CAKuN*$m5R)fgv(>s|3YR1)0}50mM^j~^5_#lS)Wj7aen~Fj8-Q-w;h2kJ z_~hOl)LSBtydA>@7!Wv4%E-AsWP97nzCYouqJjX(k`+qBf}!%uab%f(P6{;obw66> zT>y;<=*Cn<7Em>fOkIaCS41qM@;PCJPPU(c*drB6q21`?eNnDL7e)dgLTySmVq$|y z1@5t z-7KCsK6m1y3LY>J8_-;b1cDXXL`#FjO298|F52Io;AzU*q+kr$4e)k}u{1AjTAdjr~zs8{y8kf$hF@)^XQ-{UKd*Oa~U?w2Z$qyck@ zqm=Qm@r3bUS*IRul5Mur62s{eytWg12uSS%J%vm8U zQ|5R{nHPH#yHGuG43nFY`jJ~zW4ri6edPXxS>I4PKEl8S$LRXQ3^{cz{f|7g4U0oV z<5Rb`Rr*^iVNML)3Ckp7ZjRgsYr+)DJ+KcLus)b!F)r?NTxRzDui1z+Ux3`8M;sJy z$_enU6JpdPKu`cL7utT5xBhuODrfG=hOQMnWP-Q(Y+?20+{JKffPbISyx>VxTclLH zyMLx4-}CWS_vkk73i5(m(Oc}-f#k;q$yt_g6l1s}aGR(iUAyhp2Ah$JIr^a2&%}?1 z9Wh-RtwqCqf@P4$xec%o+SJo}0S*S64XK+yqsSVc zgUw*d)YQJ<|G|;J7Ja3TPKe4c#zaVvMTDV;cd3Gky%kTv4&Qc-N=0qWd_8bl1iQMN zJXN=XkKIxj+LUj!ZR5LiW4Z*;A=EXZDw~j>Rj64O8~6hAw?#6u43F=qVSK%$g0em2 zGl>g?-@ozb+rd~Y1CWctL!7HytBsK-#8>Mr+AA$y5=mmL7B@G@vFUa+H>mA7aRdA( z)A_ERj?WIXhhB=MR;gxb<$^p+RC*xGI0GA^{a3t6MGY`bHY4 ze(OwV-1H`$1L*((C@21{%`j8nPhUyU3^CAAdmImoQacj$9525n^8IWwG;JQVVC26M zJX_EYI!0~X5rQ;zQ_TeWK=zFN8Fq^Ir4-?07MmmszDlDC4HQK5d>U504uru$s$Twp zZ0fVOiL=(fHj~M!B2kuO)*HBJBaL{yfDbM^QRw^{k_?9Bx>OP$R#eoVRS+3tviR&* z_3=;86P0ozkFbqhk7=DSS0oe{bpg<3yqQYOkoXks1wWcf7WI$Bx0#khaE&kbdqAr&OdMCzfnv&R3^>#4bb2%dQib9P)+<2o z*mvRC!2?DA#+MQF=Tpv~i5T1Q$y3Za*I4dV=i)IvyshwUjE#BvEyu~H^nyC98RVnK zc^<9p!j~JFREY$78c6A^S$~$K7Bv?+1&T7L`Rw3xE;W;NXa|^WZ0J24QPKb;QPLR{ zR1Pw7pqIk{`9Qi%oEDX8k*w3x?g>J}7~Ai@nJf})I7xe0yw)=CD)aeq%YRi)oBH zQ(Jrf`Zh-*d}8a4C(m?u>A0XGFXVUnW&lUxzBI+}Ojg!1E(OMNu>{eEO0Z|Jqi3}g zHAFR!Ry$r*<9^yh;PcL78c3u^?Do6KBmte8~&x)Kf&)L4l9Qqxd#@KzFIqDcOcNpPS@ z{xt&gR8C#@S4g&#h+;O!JTKcAj?)eg02ky_hkETO$`uSv2ssNYdg*WLi&PduMj(t8 zKDgr_ygDMn3qlpmOGBn6fr!EhNGV$Hv^(_+d-2c%xGytyluZ`hXe*iK*RjEwE711S35^vD~Z@}M*;F2?- z77I#38Pk{$R>s_O@37~YFASY#;@anc9I+jl8F%^*!9zA1nvY}WtgjDc9=yOF&KZt* zFgdz`M)s!@CX6+tr^4?=j_=M0NV)3$@4l{21Rr756!a}wmCuL!mwPRgcR&Jhr1_=3 z9e=So@|qchPu-Kj+uyUOw<(kIr2BXKSd6P&uFM~|??2eTmPB=gh6<*<7Y}$D9NYhC z8>ZTS+NPyq2=(iMcjJCk(-=KR$N+}q14{`jdLkOg`4RgKsRyzMvGs?D36fo8BKa)u zJtJ-J?Hbgwyr?*mbFU4r8bBM`32ipfy_#HI*O6}_SxOTflL_dFvrN*@W5mwzIWU|% zyAL91Sx{ObOeWWT6RkRl&_A^2C_DoepyI&54IA7&tXGa~!zCY3=#vseNMlzMtJ}Nf z8@@Q=x=3w=5GWWVjE+!bk&K|lEMW7O8ZSC2MDm$h$T}M3D>`R307T>Y0-O3sPDDFW z?>_n}WopzGIv&7o!DEUeFZ0XOzMHXLP>bH3{PW-H+lrclWQvFPh&p*R&ub9! z;gI-o4pq*1ALr@}Hnl_}z(EfIj^QWQ=IXGyWkQ*j1f`iwkGRV3Na|@hn z&bZ{W9fV{iOOVYKqZUu|#hJw}@TtN=>7sJ)XrsgpuJG+)^$GoVUp((dc{vIzGUZcB zf3O{eowb1s3gJ|`TpN6hX!qPHYn6uWYPYoH7ugq?f8qK)kol|GuRuYv3}igIkU9dw z5e+T^TRP>-<`+D}%CwfK*chqA{d0_;-{qm(?Y2u7Ai!! zYwnBl5N9$9h)=sz>(Q7|K}jnWi1B@KMeD zdHWS$aMyY@V|A6s-*eT(zMwbMr5o{zr9hwWEkivQljCwlEeIO1f_XOoI6bquT)~Jz z9a$pe!(qL1BOpPZ(;4~9EA6IoO8lZ7f6(S?Qgp-~IQzy<-%8uNTIH(iAVVO{5U%~n zd4LuML`CY+)(E*u1@1B<3L z1s){9DTxGfgps_Np!_WZmQ-8GUy1^mzAreCgoUMy_k%*ZxNg@RYX)idCG@oJHSfjICur$U#lFr6VS4|2)*!iA{Y z^)$#tppqxk@p4>n`UV9!-5+{=(IDV8ROvEiRnQM1*!_;v-^LSM>glkCH%8aFgv4C|JED$QvG45f7zHs>OH?e~yRn3Z>$F+HOF`TLRX ze=rL6+EFVU07}nI(ES*GYd&~M&~QY|7JvWOZBqPSp1$`XHH_ucANygC8HzaTscGNe z(04^?fEDhD;a^Jq_ILu01O5kqn<9!JmLGPcG=TpDcbo{3gJoRxNg4t=n>U{|#Ad6X zgn=U`FU_0(-o$ummu;&L*Ua#}S#Zy|7fEs~ZF!4!3rt*UMEh1mlFduU1<3TzZgT~b zRSMUOM`WWXw8(NA6@}Jvzc7f_h2*7W_V_N#HHEy4oWw$qY*nSGgh&GzbHCkR=+@~9 zrx5)b%za!{krFXEFl1Q@{WY~n%=bQsNnTuE;KETyNppr}Qx-Y95D^Dv#C%9y89%g; znyxJc+Wi5xp!%}24x`dy=ww5#)|2ufL4x zWfu{3S-pDO1zuAtqg#EeZTjxvB?da+7)Ax-mLJx#;Kz~ug0b_x=Ar&zZJ;6C?sWIK zk7<42B4)cQtMcqfpE)YV_VG;D+D!$A;lI@+7OJzEnB_ayjw6prkMJsBXL8E9bRv|=8WX4PBXTu zjY(jp%DU5I_C%L%8Mkb*xCUDbQ;892= zli;h^G(!&!2{#8?_SMGkkuF-pGs1>PN_Mi|uv22OeOGVAZ|FUa+A%?@kEU1$yN_B2 ztN9&i98qop7f}vFOagYo@CK%_J9mpzy9<_TBiL3+R{hN6&5OhrVD1(`Ly^y=Zb6e6e5 z-G`R~DwWdcyDA_WFqIKaq%SILNumhBpm0q_EPk=5#7Xh{?xdL$;Q@u8j@XEzn6SwYfmP2+ZCo z+!ACk=e8i-LGK2U(t^b1TAS>;H4su=?d21q+(Hw^o`4sp`L4ICKRXR)#a{!@(oQeH z%lWGfk3hzsYivP&9;dB|weaoi@3*iy9!!7va?@QMEz!8UPA#JY?Ca`hsJ5cle7x&2 zrkBMUkbVVZccteF>I68AJoV}bt$2ZPl+yV{Z#oEW8Pca9yYn-rthvY^-3H7H=u~e{ zYJ^le%?n&0c8sj%ttfowR8k zQM-PdJN^!itv{a*BAuco01J4W^=*OMs00VK~rXJzX5JOzMsedhw-=k zpYve|W0px`aF~SV4cnt8nvu5QbJx*BNiV$7Yn5~T zCbEBUqt~oVn^(;q)a{1P;ouJdFq2+qJR0x%Z}}QXDzB1uocj9TPikf?@E9+cbM*Ea z2okx@H}h^RkgQhy9))M+(nccIki?SBX0Riq(L3!#Hi=NZ4%gRM>*d07LS5&%hQljo<)NiLQrbS9*| zhbxX=-94CJ+V+ndI3Q9%bK|Bs#a?`J#T_mZ3I1*A!(Z{oiltq`a)hDbx8ol=Y(5Pp zhISV?r<1&lw#i#WY)=;eEa|On-n0h+Y?h8Ig&ppWFF}6zXl+63YxF)$;SW`pb#mA( zJIvi^$+ZEZ6!f_g6q+ivKPR<3FLx`?!LN~dW23nx#3h{*$c9bY@mi`5=><2LkqlDD zdX<05vu4(E@|8q+Yh{{A78{#b_sjmNQ>KR^>FD7yCfk%PN01{1>Sr+-`)4tVnvJ7< z%I&+Ahpou%NytJ|@#K4DR2=rlGHYR&98m37M+IGVmr(oV+8FGcyxvE)JSqDAH5u&n z`jYV?PU{&35Y%+;&_B5uVOGT1+%*3|# za@v*Dc-C;*gA%+;chixRS8qJteB2qe+(n|+bm`Kd#j}XZ7HTZ(`UF3|p!rZF4^o|1 zLD9gNSyC16K{5XOgWA&Ba@>$a1)dH2q+n~Yuc3Q>CiYD2Xkcq-VC%u*d35-v@LdabaeT(*8dKBAM@5eqIH__K z!HQLfh@|A-4pT;ZqjNdO-!)zD;Xc|FU5UAf=HBnl4~H%Q@`wM#Q!n+a_)XoBi`Qz; zmgaqL{8?rUBnbE~ID_4z%wJYi3o&d8YyRiY+ z>(<5MKwbhg(n5#j6E!zU4Q^EjZtUgYxAw&-v2r~rBHZ|*O@wL!C2+7(e6wH+ECTkP z&N6;Z@(4kr#Qc2c92o1~kKzZEf@I_mMH1jz3E|EeS>O#n{VN{ z3;G1q9+8N|3(1-uUi8;$+LkZ}_8vRaCdX>O_S`Qom6+o14Fxxgv;@1O#+a0>te@?+ znGN9+!aWHx=2gs$-D9$(@GFRAOf@959X-1?55jIJ4fgeWY>zaA7DYVA;aqs97PF66 zG31nowysR+a20ZzDD!GmsjnY+2;YbAy#2fgHF!K(*H@cM&E#vILV=O6dGDW zrB&M=lUrO3Trk8cBE?0>XN~g1!+UkM$@W>GQoeYkWZa1w35PMY?!zJKpR6C! zJ~CkQ5e0+Z865`HknNMmNb;90Lge?o%WP;aT^%1VT=ul*w)RME8sS4(qqhy^0bK}b zPEKZ-ZJfK4Y!dyFl36@@A@nIH4E$(-*wwGX_~3O00Xd&)EBqqagvGx>RqzoZH`xb# ztlvNIOC%m+L@HLh^PV7^@NAgMyYN;coI2EgeBm1`|D{3+H-#+(-%n-c35^SxNy(uQ zu=NRMD9RTgH$i2ixDVjejDIrh12W*t%b3XxSX=EDY06!vvZHt*JsF3yb9im9*%!54 zP7-)^uhGG+S0BO4kS^UONuA0rlLm;+T-kiUuw+sQD^>ITzdw5v`O1C7#pZ7zwU6+@ zs*y$Y&cgb`_B@a`LF4_P&31Tv80E#SkG80dPivM_+6RYQpXX+dJ6G}=2c-BvWv_zQ z37R|?t2eXQCkeG8rc1K)`^>~PJQmliGYr}QkIL^it@ z=i5UX`O^8Ucc~rY`C!A8fKy|~`j79YORtiY%vn*8aB?ah4>LMsxuS)$Q-Q`jFGuvUFBEskFX<6K$za6be_eE;l7s!ZO3VShrufeMR*-^%4KA21XdO;v|5Td!P^;{;GnPLHi=*@ z2QDK75m;;^<;7ysP~tcsP!Pbi-{FaUVX*#k3TwnStITWMXG^`u$bE~XAa2Iq=9Ctb zDfmk^L~{@0S=|WX>59Y`mj5T{XpKKS^!8Zw`|F%l`_dGJV8VN@I9;+ zHH9!XuTJkEqfQmRq!nDc5(n22Y0aixxV>!R?Y`%x*H99T<~$n`JxIGi>-Rea z-WHV&dl)VFr#9hIr)*44U}w7J?Xf`Kk&KlNM>Vd29}Q*)m6b9=tcdV!QkJW+5B^q< zj6!$(hFHpFov$@iFH0aVTTp7)eMh~A=#h#s`A>esoSyStkU*HIYyCdNmBnk~;*TCW zM8Li&4Wv*E2Pq({w^Dx%q9C8J-eYpl4VSlJq$JIojteH-Ni3U|(F1lqcdZQDl2PCB-2+cv(~ zwr$%+$LiR&la95s*E)A+U)3+Ddfu9IjCVrMvovvulmDD&`IkK5O3r3DkpdBrSNTJt zLWXe{G`b(?k0#0jiv=-cA$f16bOBT~(L~+^@XdGr2%7kdW%KO{bTAK7VXFVs4v=Xw ze$Kctq)JC*x}MpeO7+W=pE{zMaA*E4MuB}V-6!2XHOJ7xi{q98O~Ge;11wdbiX7g# zEZyvkMgYq-`;J8~w`@15_$J$x1?_r&aAjD@>=azs4jj@lO>J-2u>G%cWnH zw(OfEB&(dggnk2`6w2eYdpGx05x=|09V{Bu};U21_)K93FZdKgCa zsBd~LIJWxNkTZtpepG^)jMQ8)(aR`6HYN8H0}dU*E>%S3v|k36!{`uKBdp!3QzA4P zZHD-%QT}8TWdj`K^@Nn$3BbwXJz_K}agtXJu}{D;{7+o0LodicUcFvCuQbRbJ|-17 zZwUmE=Xs%WQnt>pPs#M zx^QE};uzzoKu|#mTqT}i&nGV+%Z2%By1927{V<%mdbY2uZY!xb>c3XsWnN6CKAkP* zt{}i3FCmxabp3}M;L3m3=9NL#z9DZ2Ng@9ebupR$4|tE6?WbMxe`hn(T0hGzP87cz zed4LfF@dS@!WmC2gQrs>L6E8Zo{XP%$yH_^wZb9f5|{2izbcJlx{4e<`NmZ{Fv~kS zjHr=11n%449s$pnfkQj}2YNVO6%v{R4t3=Wzx5e^W{cdEq6v7$3;L&d_4sY7&+N(=tQ9B6MrczFm5>hSb?cV#Rvl_jk|R&`&nMQ73#J z?H=|?>V-VKsFmWJQ%4=cr)q{+88>T0{}gf(Uw>^=ub~D>p6df@!61MCi><6{piw8e z?NZWHmpTBEXE1CU<%6V~jop`P1hO#SE`?a1#Wi8MHEn`%{uW3Pvf89%-Mm&YkAiF( z4-nhjx5!Qk2$pf*QJU>#3493zO)ilo%{NCJ3^0slGh&4T4poRo4n+NwIqp=Z^!3LOWs0% z-r9<$Qes6LI@0Wj9RCz1x9n!dfUnI)^DloII{qaesxcI>td||z;tZ%b4t^mosA z)^_d2g34~n!Yxydbz{qW6$%wQ??Fpd|NJ?0?^)kJ98yJN835BVUuvD+W< z^wz+`%-1}1327qR*UKiC@%LnVYZP1!9~>TqUDb5yf71gA2P$<{5lxOW&}??9*x5fC zHVa$L^pcjHPW%n802B!nq&NlajRfwXv7rLV(|1*Fi8!c;5gkutAVnke?fKnA-cc6P z@(cLq(}NJ`-De$!n3_>~h3_9WdVw4?SUPj=3S*yO)oNvZ>qlQ0O^4+JT~RJ$q4{{! z+eq*4HIMdK&(_9iT7G#cKyR%*_*n$+AF;w{duoZ`1ulp%6NK%ejiU@!Vp}o4j#$!4 zCb>G%L872C-LBya(r^&mG`Cinco}?-W6}WtNic7S)4(@%xg?49fd>J$VL2L4Fka&1 zDsYa@-#e1NH4FZJLswP&sKI|1DqN!+G60WaiK%GAk_R2v5fv|Fq!eu^lu4TkklRJ> ze{6^JPD(eGWZ!w~NHd1%z-AqVMaD4MBmMU3ge)3^MT#woCP%243> z=~zE{DinQ#*&-}s=bYft#*A@A=+J>#Sx(Hn)vO3>YG$g@#=ui;tyh*g9z*3Dc6aq{ zhVA(q|D-seH@F|soL{)-q}C6ztq(SJpee2|k4rN}DbHS$AaJNQDU1dD#Uj18LW_(4 zG3?N61L2PYFN)h!<4bYtjq48lhz>x)9#8v*U{>*~ZdIrMhC3QtK9TFNC%%e_`>n2= zGPWGDy}PV*Pfh{RU_~t@Lf+n1-{cnYGsl^T5#=t9vw?7+z#FjDx+D`^l=b}0r)YFM zS0z3q)1b{cf;b+%-J<(#(XNRz6Lwf)l_To|HnrLE3vqlJ`r62;V*q0cjZ7(4iPWPM z+P$i0sKTgw}v}V;m5Uz5K%Pe!LLCUb5w97q5~MO^5^8BVTwdyGzCB+??`bAHWxC? zvinoL!X>K@S!E5%+6TpBAefE&nhjHqf`K1e5*aT?gxG@mq~mzM3A;s2>$(!Esa$?V zNpN0K^qK~zaS_%|Y^EZd;h%lYKWMho*ljdIW%M|x;iK7nou(;vH7Def$T@v(G^iQo zGQ*@LCnxl?&KS3IvYCKmx?n~W`pvBI#c$^V&Qff@R$kpSnd#%BML8! zMxv&@A;JMg)HG;XP7B(k+i7`DJT^=tXz-bdd;a;$TNwJf16#htTx|cAxI9?bnAcpo z2MJoC;LdE&Uv}AClL){P2t;a&r(-l&I<(I^k6DamR}SDxWKkFg-0H-*(e3 z;qS6TA%`%VY{m)d_$FTYNUp3%TqmQ86UH1=Z2ufvX6GL`W~K81*g1A>y|x@(m32tS zG@cvx65rtvDR7+m4qiXGo8cExQC!hDdb#yqxN)zFWS$&B2<{)e z?#VHJN_K#xRe?Niu@KY@7SPk1;lUjPgx(T@M|48I<{W0U@Kc^j$+t_>xU+Z4<}qO9 z%YO)YTtP`O5Rf7~tk1oQ6m--3uMmLK0#*btRGlg6Z5P|C;-XYZCj^$jxlTMX?fW6* z$IHjozHF)tBl+qJhlM1NfycvlBeV8I2iKaS(J%yOpnwjE@(v z7fdGUen-0VD2^Ktd)VniBg*X>Y!z?Cz`qsURZ;xEKp=#Gg?i?m^;F)v7H$3}%Hn0MXdPg2#+yg&)r{X!ggYF9Tc%`Ar_Cf-zV^_cDmDo17J;;_9fIzspGVTGz&IW^UWKbiXhL z_D3QD*)_^TpfM?!{V>W$yu9x(BI*WJ?hcEkmF=S7c;5`GFaXwaet~&eTEOi~VB61S zlySLP0GotYO%#f#g$GfzuwJ>QySW9UyP0=&^<~USRWwn(zJ}F^o|WlWCC^W0*6l}T znI62X<@-uXSG}vw#)A@j$I60N{h3tSm$pZ*A=({CZjVosAC78UNS~fRyO$R_7&!#%j5H`v2vlsAW8`IFkT$Sujv)3C?Va(vx zH7jWA3Xbbu@qy6XRwlyeue_;Ak@@^ms0YO-*aNwqMArNi$yb#`0~FKuo7&KT4kvysFj@c+oHG9izX37*YO!I<&fxq5lN zOY@poS%D?O#N?IRAknJl{f5e26@`?4nWPKQb6dJkSD)$zrskE{2}Lb5`&*wT@Og;kZ+x7r!FV- zRNVeN&D2y0M!YM&YMPPErV_DTxkMk9H+kpeLOUM1`tnqJ$D0K37ACgQYsTsV!q#s{ zF)^z8A(cy%_~Dy+A^|fCNdJ&Eux~RfJJXZbhVcODIt!kRi)mXbzJK)$EhF!UUGpXB&gaeo zOT2_>;N{b6x7QQ=kq1;=!}Sk0X!sfyrS5}F zEo!ycAW7J9c~V7Lpnx{+CaJ+Zx!HIWHUt|ge)=ytrueaiH9no3fnaUr^uLDHsm+zHvJV7rPny}hq zKv7a+C!e9Kk6x?ptmXQJleea1;L{~VS=_8D8p*JPxr=(btju2lMi-y8eDV;D-_XGu zW>ldqDZv#%;P3$V*@qtyc~ZX%xS3Kj%}9`Z&{hZM4~*Nd)@ElalKr29L0w5x*aPwDti1r4OV#bGwwXLkW64o0d)cW-5)tl z4uuWw8EjNyZm1*NFVxc=Clp8lCY~(dPZvtm+WK3Rq!9_w`Rlex)_ajf@|rjb!;FOy zKjlc9j@9$odt3X>{p}EyeJ+kXJf46%S33m5iVZSxaN2UFKn& zC=e=HHwYT9tgTGipb<7|REy50z^AlH?cGSM4 zLz@s3@R=yh6?%)d6~B_VoUL?CNo$cCi{?<5Q`_I-fM-H0@dXA` z5~#7>t@T*v#Q8IJ;ZGJ3y35^oWGcJeCE^Q;{sj}so^WwpRkX1F?OdD35w(elwlP;w zU))gwGz*CP_qXC_H6xN8?{tDFAelWNA?yJt{*t%|&!Gn9B8mJwxFc+fa4D@*Vlh21 zy>j$gW08^yT14;w#yuZXPkTGYsdMgR1;{PLEfNG{LzE~1@?;2dNP`h0(TV@&jACtG z8@E(?2S4?jvv!>?e97yp?Vg|0MO4IE_vhF+=I(=}k-n%R3vYx4jEGkPDc%HqzjTQo#-HWmJV6}D0_p;7 zu3;5yh)PAhOUj*_jhMS~rZD8n#4S=)-+*z`NFBpw&%MM*LMTWn zq&X__gVP*1Sneh%nNZsZE4Gw23IMKAluw+7(l!)cnKJ(t$HMVlS%|GI$Arj>MMB3! zbUSn6A*%y!pxhU$lj(e|92})M1^SA9XQx!&#=RIgZiQ|`z~O3|xAbLS3m_#K)gvU9 zqV63lS9XEIRd)&72!${sA$g@ICg%{w*?GpmoVY@x2m4hrS4c-MmA7slUjbsPEIkKi zL8~oaLvqy0r+R~3FTt%{Q{B~Q3|`5Dzh1wtNHci*E-&EQ;~r>^Btz=(qD-$J8*tx)3Y16WhUh_hD26bQcz3Pq2ry;b_QK(Y&sxv2wpDJRj%3a-y|#Ve@9 z0A(KSua-Tasg@Wt=k?x07=S*74T4j)q)^C*ME}Av>Z}1$3`C@s!Zc(`ElbJrus%wv{ zt12;qvSsm0C-2x&S1s7TGz*2Y(%rSQLu7*6sOCH{oV>LiUnmT4xV~h{BWtZ@-C<}L zmlhA-`vu8e+!+7c8wGIwsUVNC{4dGqb3~vasKKu(bkFu{5I4%4>WPo|4dwnCVKXW^b)(%<}E$Olz6yuBo!pdqpFkOGmh>fZiO-$2(Y+j0MiyufT}13kcK zAj}*|^9|I19vtf*AmF|W5HiP5;D9{o8?RI94tYpmiCWx`?zVwwX*h>s%2XnfQhazC z`SF}J`m2SuBNiVF613{*x0(3~Uy~d~TawBBRfs=(M!4iJ%8rQx66B=UxBcfR4GB_@ zzZZX#H;f>A{dzEx04y*2#+S(2A*|t_oJg2wC;J%CxLTu79qx(ttk|smFk>jG5iG z^(po4`!q7yU;1((Nb|FEm<#%K+d5>aQ2Ko!#j)+=j)r)eBH5Nq~BB z@NPqZ5GVVjE-&K!;o`m6(zUv}XH|6u#DR{y-}=QQshv;*DOn?+P~p|#O~b+3+kjsn zO6Qz>CH+oq8Hlc3z@3xTKz49lc0-?^tkXq}YpR@Sl*kToe*ywTT~-Ihk@-Y^r<6M6 z!py##w`Hwx$UgeARj-d6wPy0)VQ{B3pvaQ|ie{2HC*{Xpj>~TNal=%wfjpd=eyZ?5 zNJtMAky(;PO5?i=tGtS}2vh~``6>mM@Jhu*?_5e%1OBcSu}c*Co?FE2kXJyT54V2! znq#(bfL-zVH%*4Bayis(=ikGrLRx``&8>DH4WLk1`mPEv#A-QTtu2VHF&Ps3Ek&k*%=>v6+^$Z-an$ zi#jha^^r-<;wKpHVYIqFg;(AfQ_BAZuo$2$VlGKBTI$;=MEdt|BfG@Qkr6EN7pDnT zaL#iB{bNR;I z8?u~tT{bi+BaM2?76002wC}Rvl|fTX)t8*=MtekGq^nYU+=yuH$b3!l(=Rp^<)|%V z9-v@dYQi3bky%3&rqB))mJm>Cx<$G2@iQ~Swy#lVmNy-jHwi$K^hdZ`wT1V=gU6>D zR6{_GWb<;bfX@>m$NnY;K#fyDHAGblYpZmq;XoRc}WjBzW1>=`@i2-EGZ1f__4tW{>AR0O1yKHEg zGx$22@Iv$!G7uPO*iA5GDy8huFSbYTbM-~S^1h-k49^^ZI3Or5864`b)Q3Xs7B@T5 z81Qrezh|mAAcWMFRTv5y-^`|~%uV)+twq|3$BNd<+e&>KXIr&1r9-l?F8x!Dpx5Y( zGtxzPe_~}*8Ne6WK%uBEt|)#^h4Nbxtm!W}_kBzYfSad-Qs|dWmpC2@cYZ!-`0bqE z?VGb+b5R^%AZr(1+RDA#;=t`tOyuTPhzmc(3sdTh_ootYJyMLDBWjcMDq+5En6HW! z+T-v32X}@ovX|T(OJFXt$OKqEze3l;AH}c$m)NH`!KDaoabJ-Z0o*{&uYvui0PMFk z#f!TWaM%?dlwYDX#Ta#v)gKa@?-JQAyveyC3K?9079>*5-?o%Q%Kmg*^{5Ovlf79^ zl_J-jo539i_ef%Yx{N!n?4p_1+W0oS{U=I;>eubuG(TPb?HiB7(jMdR1oazgl2eA{GeY5w4O-V#=>AwK zB&ptjUB+zVF+Z$CIbrrcNMd4hnnVEk#%K%XiTN@dC3{NiIB1A&8|f0!>=X88U)vzm zv0_`9zyy~y^;umPmQW>QC|5uX2x%i*S9xpQXE;hh{lz)QECj*X>>%}K53ba#fqg@gbGB5?Ytec32n`>PTZOy1PlcBboc`?KdM@t%T&DvFnzhxcsm9TWBK+-@l8K%ojPZ?Tt^G#I zza&Q>rdrK#79aP{d*hUNAl{tIn5 zKrD@m{sU9bw&zsba~Q4w@fvdLx#T?w09$qp_3CYEDB?9<_PCBH9m6xCYi`g<)KlBS zGF;D|_X0UK@jE1=2yYu@#Y!>RSTb(xu<0Vo8fLb@nq3bnKQ%o=0t6&ZS$3>QXY={Z zDoI`w0P10UTvydT*xuhAldXsTNp9s)efw%f%=NMw&WoN7_OKS*EeUC60I$;lUb6CZ zEKoe({RM&!(Y3-H_;DpGT&v|9h8qR85KV@V@GI+J_Nl%YXMK=|6w&bBN)l`0%@c!? zK3lVk$d@(#XfN4l(+ie!!{r#eV)3<>`xUFbEcIUDTZCl^$caiWQ2YC9@7y{XJAdQ zW!NAmFaP3Z5epQZ&`$+&iPyVF02%i%dKD??57zGE=JpoZ2_>CS29F&^1$b0{y#FoL;q>o~5ni3SjM;<9FTD&cv_STvE{u zq3$G>>7l+$+QIb11ed(l4Pg`jF)`?;^`XV0li~l!C$)l+0RqDH!+OZc619q^zoqZE zIK2%5ADDFo4r4~fCGT+ukg@!>e@j_mobYhb)c1{r^}Jy{!MWoH^E-SrgVG(#a?;P` zN#zL852%}ETAe7@2-q81mr0b6M}AObPml{S92msGLe93%3Gs!a*$hI2+g!HcxpJ!i zYI=t`0@<+>!wdk|#MJv<0bg-v<0GM!Wgm!C{=NKfc3Z&y>iyXa00#{h26l`}ET|y; z-JQNWHurSQ7t&84&JY$$_B>fH4^jui2YKgkcyBy#vkkOPhtbK(?9a{uV=dRd@p0N8fPZQ2~+Ng@2f3zdh9aUmsj9G-oj`=cDL)9W)-It z{?+0am-?{2pEBq#P9Xn|l>1nmmU7y~4S`rMm zSj%cyM;xhRNxR$Acxd}Up$PG>?9s2Y&#->1V7!N!pc^tDZvQmPhUD~kWso(*!KfcIwG zK4vX6*_j;Zp*(lBuw*J$PZMB1o*<#S@6^y6=)`K0BhUn_B&ho6vsz2d9q9!%j|<3Q z;&LrA7K)-m?g@Q{=92!zg&FDTG#KZAn?4WpQF-^sLqT-TT=@o!j*&N_lSm9>u)s zsdTsAbG63ye745+rv9>Y!g=&eNiA!&usp^6P}#V-v0K6QI96>fOW=p3@OXUHg4*i| zaBuiJI?k%{54k;4?_V6-5EBc?JN0N$Y^al!nA*DZoCkcq+yltzJ%U9^>VDDaG)>~3 zle;CSm6&GOI})#YvlcKE-xqNPm(cGu`T#t%A5FOq&XBidcB@3XD(V|`VXy{sdcIOQ zC1{b>I#PtsCK;WY0KR`Z*I!6{>aRKK`aW%l$2l`3fACo%7!Jh3JXg-vlBp&A0Ri3R ze!_s=NS=d1{=4f_>-d7LZc153yR+D-GRm&eWeF3-aMdw-sB|uQb+3EF0)9*68 z$S&`G!&ljbE1RcjeaTKhj^T=F`UPFHp_fX!%V*LT9#K~FI__BT?` z?#hH<2hjFf%MjMlF1#5S{zt(Oh0&Ti;yrcKAQ!z0_0|4gXfymp{a%7c7eT9%sji~> z;=GWz{_|04rtSo#V3O8(3!G6`#|0wS>l*f21S|xLpd*`1-La$eyg**kX7p@fQN%Mi z4#$%U8-N=rgxAG`6-#`uLKs>)Tj*$oF?V()_qL6<#5L>gVeN6e> zCl#HXWqs+|IN{#UiZLS8DM*~|8-W>%ZzZFM=Gu0M7MUA1rHHuI^ z%>@Fa9io}F=wpDJRP-=RYPw>8$?vb`2yjw)WG&D{{^Os6-lC-;i1 zM5m@13ZNIUyhwaLdCr>a3P&03RN)Qt7l7fc%gaN1a*+C57kwu0#ceRX?tT%0jFS8z zgRs8kZ?)ozIe7TrP+BuBA?WZvHu8{%kp^bE7}&nE zfY~O$;@MuX71^l2kFMTc(h-XT+^_6ClNCA4^SPSR8Cc_cpX(y!AKEz4{rPfjQ~=we zp1RxoRns6w6gK2gpt*63h&LYDBNp3+vnu+?19=4wne;_wPP&%mT~$GZ`aYoAJb zNybA3)9f#_@4bYLW@*oy+A|h~&+ZcTLFZ`q=x_KWDBWfjB66VLlwSH&f818>nb+)z ze4sJxIX0J8z{2e$%h#*!pRt(m%mCtj1J>5|D~ph!_xQ&!D+n@N8cWOYum#(S^7?mr zdz=Pc8_=@|9T~tWC8Ng5gjpcnbuB-ALW*2AE6jUvr6so4Gp78G!8cbs$LkF*k{3cQ zjEa8vqkd4!2G2K)2dwRrz>3ZI4$R_&iI<|;O#_fUSAlreX+$PSMYN(iSpY>!0@t0V z<(fGv){<~0sLaIaSvpCDr#bEoYpb1`C`Eq7TFxSU{|wvdAg*FrzMfbieIOO`n$^6R z*#rg|UeG4-Rm+uLa)t+S1PnDsFNtfc)OXCLVyyc7Xnq{$IGf*z)YCxE#-Ff6v7?Vf zhJHdOklEHG61b%L;BaX8!+@cBTSgC}^}_7WyZACykc!LzoO^SoGIoFI zU1+%27|Hu&vgWhQ<@GohW~&*9(^a3E26kBZ3%JCNRG|ec_nmc`&?~HYb{VM(`vBwl zPFJAVZ8I0>4GU@GH3?LvdnS)j6DwD1qq$Dfu3us&;lEquU)VI0(UwT{S? zQvkv*D1{-Kfk$$mbbtXHeRkc04<`FactR*6HzlY0g&J)D*K#@a!-8h)Drrs93lIoezV*XzJi9;_do(5O2BpIzd0rAmBYSAo8M^0 z4F=pM1ma^{<<7w>OrY2vYBFyDdk#vmvym*nc**XVPM&1QymT$~pvlXGqQiQlH~VIjV$ZuyB+>PD~U+A*ihuYXNbWby1j` z;U|hC{TZ4Qq6T1Uv=e++uyH)z?S0$oyOxo5CV%t5^`GLCL7;s0BZB_ElZ2O>xTR*} z#Fu95w2(V~^|M7;0#C1F_E+xL1miYSb)i=G-9?o>J>GM{1C`lV#F}9FxQ|udlJNSL z%TZk&Zp4x7Xr|yYS52J?#-a4pRBnq6;cG|G%W@uSUIPf_O^cFGIPb;`c?_Xf{l;z7 zn$pwBZSH56!hw?4iI!>GC~TTB&n208XdKe09h&-MN^hFW&f4E0a^N`eY;dvnm$t+% zBVM^g($11Fl{Sl6bo(SbtJE$9rCdcIP>1tG_H<^(01Ny$sePg!PLjy1avkBXpxouU zvllkCz6gNjKzLSsLc;MXPK7CfoyQbqW+_E#fj3( z)b%EY9*x{ZYxPF>cn+R%p1JeGC#eu1p_HkUYmR;1ZqC>N+JZaI80Q|~YK>Mh%#pX=+* zuE`g%+sZhGEs#%H2~P%?T#|u_PS@p_Usxs8!ust*zVpN*9!*upsK3Vup%Y$U8q2ZC z)%dc8cI#^TTQDFy-RG>j-NSj^4F~mMYjTFG7owGfHY zw{WFRXWQuQd_BHDS3(6LcE=vG$x^);u)^Rz=duq17x#iz6_AinZpB#&S-N8HL^Ss< z8OX4xd2ao6S$?|s`A%Wrwgj$u zpuZ#t3cb8!`n_O>(#|y?rC6IkpdO8S`4g!q2OhHJMoal*z%6-TO9;}+om@0mN2N{@ zFjU&qVm_3p11S?@v&Bw4r#IY@ciks*(}Mm$Q^1eWb@~7Kn*!nBOln>H4QSEYusaY# z@qJc5O#q<__1&Wf#*k>tTEm_K!;N;oj|YRGpT}T9tGG3aYg`5Fbh3#~G?K_RG72P2 zUmLl*s^%y!A($aH89bd!4X~wGXjYpyM0~O;G#wqEx6$gKiQG-j)Au+D7w<)_;Ih4Y z`#yqxbg083wPB6FA0MAb0dDqPQr|3#i`J$;r!=m@B)NV5nX$c(zq@OzAU=dJ{kd4? z+Sa)7_2pOB<5+2UW?hL$+bbFGSXoh3yIwCms^#CW%G?HYXEE$@)TLTqzd9p9-1Ozr zmUI*D=}iMVJe|?QQF>nxC|dHe?m@2>3qQsxHIqwwTlOx^K%2TV0F*etL5*5g?DlvX z(io!N@!L#NKG!Tk6s^JZBO<*hEY^$eZ?#)B0@5hJ4~- zWu?&~i)tz8b!`}|@MLP}VoX{u3lIn8-P&$-8E!J1KW;+ zwFGy|BCsRh$YGJn%cvChS`IL>d3A6TE;cPaX0MZDx2R2NLwDG~@lyLyr%jk{U2j|2 z`1xjr#wI6z1D+8{q!S6&CWqf>Ab4Pe`BkcG^IBjKLgl6f-Wvw)(5S^z^8o9Exkt1P z0ZzS8uBX1^{PsJ39_~#W#~;lv8@-Rd+mG6Q#T$s6cg}E7PdqUQLPZfDR0(5aW_N+W zW$R)6JlF!0{qgOW4sU7lLID)6i88^3DG?FCR4T4bfbogMCjt=pBy-(g9tk-vk5FJ2 z5eB)ZCCXq)zzoooiMh+Jv@dEfQt{yKn`*FLS6aD`K%ttNyeh^A@I9prTV6!|2wOSs zvDwPnB2Kyety7)eb8uSCMrhK=BXi*WmCi08w6|dR9w2kKPxJ!icfY$2+%EMdXfs%N zxdR8K8Q9r{#B2z$B zuR@+Ho90(S>dYSBY*AL$Vft2h9m)4Y8bH0;=H)urxYCFIqrUz?5hG?nzy_-Rg7dru zwuBE_b_OGYL#^Y4$b7F6%ZPQ7gQXC!0@MnM03=DfO(bN9?lOP9Vzr!3tZCic$i`%| zQ=!)BIHXu181Z|-Kg(<3S0MtHAoKx!SI<5}jBD%AO_qJU*mJDljUiG#Kp-%(dB|f1 z9Q;B+Szv*VWbc-QC4L!VO;JNDW5k0JuN2JH0rqPlt+JnUCb)MziVV$dj}$kNF%QkM zfIq3D@-i;@{5<*OIN;n2tMU(_+Q6OM0fP#=BPjc_yJP#?BLajYTKPAlg+i$Ic>u8b zs?C5;n>{vWiF2YEl77|27=_lLCGAz_f!uB&o)Jwecu?kEO>|i~R@Dn**uST>;cL24 zxh15biZL@VIc`2Xs73sO7eWVR{v-!$fPd%^H08z_qJOmxDa7;am4%g13mA=O8xNIhCc`#SlMcfPxU5 zGWrG3Vb`T*aKw_3hS}$g6b!%j{`>x(i7@l-3h62HCGTP(>EuwZV1u``O3dJ1kTpDTE?%Xe!SogCInmvri+0A{&k_tAEu z_Mjr5_J>B0O)12RL@6@`tE?`U0tgPk8O$9%?zeQ6de2$#AB$f>BH;65oLhE03$X zi5oS?#MtXPrFP*DeVzwg^mU_@fbhd4oq0IzJ+)Rbz}3*ed>lG3+d`@s14xq`8&oY8 zkYrNQ!NQs`4T2?1I7wuOxfN}$jOsNCk~w!d>_=46LKM7_K&jw>6L>aPNW4=(P2KVU zAn82OgBWUQoym_I#@z5kqER{`ytrBYsRD*}t7wtM^@%NzCAH=S&>(&xbiTnr0t#5m z>cjI5zgbi=;2tbTe7yW&0YPM-_1zYb1@}F!a-7U_N!{eU8T3S^4VF`5z;YtiFOIob z9P||D?V-I@98=t*v;&$X-a5xuWE=s&8@3&|jMrF-%qD%)be#%R)GDHFi1^0fSY63z+GV;2qfC z5;1kVfh6a6+S7nQEGLHzKQ7y@DsrD#j35LxNO_`64gZLM zR3K+*ZuxW{BhU^D0hnsiVXF|fFO))K8~AaI0bs4|bl$fMb;agehmYGXS=Jn|XESn1 zaB`>-3nKk$SrlQ>x*rrq{44t>^Mbn$oG39WsHDx%Q26Z<6OM}EvjbJ8(=cE!WhQB~ z2FY>vR~n*cis&UMp-THgjEA*IMlCZ5Y#iXlqSBQp8aIy>0G#>|jchzRvqO9H!A#|- z7!n6`9(2fnn#wL;uG<24HnHM2$W=oDp-ZHBRXond<#&nW!MS;Vcl!AToKeFFoo8(G zJfwZymLXRHac$wyqkAP3pKN@Kbh~1?_;(hxbSsG_Bg(?^^RNVot`n* zv;ILG5End`fEC$2Ire&w2;c2?f7ug=)O*87APeeta%m=cp7C5op)fa1b*xsBA54ai zmw!D?X)dH(l{qBUTtsX1X^(f<$*gbx8SSzjX%2b4V+l_y7~0n$S7_Dm0(S2->BG-o zr~%m>BTz2o3N~#tPIwnIK(=ftNjh8?@3jE`6wX-zu!&R)#QRNTV_bYN{ybwkWVoBP z*jX?-%ifkbL1JX86FU_#>FNq(M~*CW{E&1= zSegejfQ4}q2vHUT)momEKLi_#2rnR&CbY^pXB%$*B5xD6G3kN*0BiiM>e)D%Q4@T_ zkPe@*(xsbl+=IV4#)F^rkQ3`WfawnO8lrb(tA8gmO1AIV$HB)0(*^EFF;7>i@O|Q7 zQ6mj6`htaDh3u7F|0q2*k(d-Ng)NGaN=oMebg{UIkA##t3&@B1=Q;?Cs@wLVFIt1a za`6lqaC7tV>rpNS+^ZIE{rP#{jg##YiMhw<@aG4p>bgH0!9#SB8Qek~+Byrl$LwR1 zFYe{B1LxDkF&gHy1hocI+ULp*PK6hshkKs@(&>(I}qv-%JKtjcfCf9FKZ<33=w3MaZ4(VjLnCxiaQo+!MHFg@cQ61C8L0;+ zZ-)S8FrW%Z>vQ12q>U)bVq!(aArBgWwS!sa;zIWMVu`{t>FC+<17br8udkIZ@+Vj$n*Uz z0*w5<NPB4T!SemOmiQeKU`|O4cvyHu zvT#FnTz0gTBrP4y<1O_YJHiUcy$m2PhXYjykA7S#!K0qLD?KwKpRdQvbkeTw@NJz z?es}bG-g)Ad=+D?6i~R@L|=V%IP=Q4j6qx4dXEOEy;f=%K?Jrs(8q^WzdBA1BcB!I^KT%$65oAIqv02L0VoieQ3N zF(lHGIk}bxlx_;Rvk+tY%;za@nNzuM7H_WHx3`<) z7LHBf7z{H;FJe(12@#?cO>-i5g%&e@e6Yp^#@E^C6_c_J@H@RW`y>3FO?i7KooYaK zY(dYTBWpiBXC`KbLP?P>j*dO?Qo#}I3x${$BVD_F!b{t9>Piy^o5>mq0~gm`*pLTG zCIg>Jr$nRj`uF80wtD<+HFqdc~;|yLF_>rl)S&^K|>}%p3C85a%XK43_eU0dYRPE{AprGO>?r87%+NBvZe>ZswcoR~r*DnjG>Ynf*gB`^ z%%W{u$F^?Sw0^M|S z&$e)`mk#=CYEofr3-SoLyv2kw0lq-ELYNvJ6p%IcvkgaiKx&YS3|6oNmjG2Z-4)A$ zY-4)?j(GgL;k=DA(SAmg0P;vU)Q~|86CPTC4>F$u1D$IQso!7(&Mi!-ATexG=bsrY zSMo1p{L7^axtR=fP0UbPx00^_M3EAnpz*bYKhh{%#d*fnngj1;&MH~dH1Mw@L)ZxM2b%dd>{!U zJY}_bX4%^Oo@HA|>FBcGJFnv#9JgRoJgwBi*}Qk(ITU{#(`JtI(6LS4k9K4HT+!@2 zX^1){OGaY5*QHlplH_H_Hi?pa&zZjtlTy{&_>IlF)Vk*pB`SKvS*h=uU`&2wzFe^iMI-WPvej8U zW4_Iu@luAWv<+^s#or)5GaJyNR6f<@^LXW?a?5m1R@sVB&RPB^Ncaf`igf_hfRH_m zCe~!0t}k|8)Y;v!&8ff?ALzVGQ z6jM==C?8Fb$8b~0z>UKx4esxHIB(sRH1<+)!4lmx5%uD(ue+f4FG6$Kn-w~-zLiLF zR!Abk+?>&A?QaNMjQP_y;7zm`X!;h{w#)y<^|z%(g0F5mJiXs1A}zuic*CA~DO^gU&$g(o&NMl}UT!L~^z6eeh=M*k-1yXy;lo<0mM z5><{%0cnM%8VxKPDm>6JH!2MiFpfDg#OjPbv0nxsNc^|WFhh`BiYz`- ze0+ozV)FToAVH>JqkNX}m^5CB6IR8DC*$xOV{U;(wNhO{Y3_RZr62KG(@Gv<@ zdwGY=BIn2Qu|*!RyQa%e&>;B1mp71G2T6G=jLn$%=}g^C5ga7{nzE#YH;|~K%Q-!G z8HzDy(4s*r&W;K`luPCjr-`mU@~T z^I%kVg*6LZbCFXHveVS;pfp{AQy7l_@N!&l94dm}V) z1WO;4Z1E5sK#L90Mu&6vRpnk|n@6oA{#I^JLs7Nb=TG7wgA#3|!RHA*NY1opJG*m&MHV>&*zflWs!hq(xo&8?y_ zyKQlsb3aDq+1KCvb)@ zY(O?PYHgUx)QlNSDr$UH3*pY3R<5BXwV;fZtt4$CjkM`SxADvz{?sXJuMPX zCo7vYRh~vJd`PL^*H?*&=6QQis#e)2fa5nP(04)T5^Qw5gai&Vy-9bP&CD!_*y>K${&Xs$a?D4%h`6u3z_u zqL3yoSf&OHF##XqE~s23J9j9d;d%eJ!#}YJ@Ilg7RNt2DkXguJ+yBNgnFn!EA)pf% z7~y4jn`4lLGby&9vWJG^lhfAl6lY=7I2{o|AuYM*A;YW=AAGw&<-&%o)V`!?^C)MP zHdd|8aWIo0U}9(lVEr-GT1N2TE_Bk{+xR6|yY$P(Y@J)v4)tz2+%1E9i8Dc6X49Ey zt*TmfQ!$+`>ssqfMMv;}nklG%E@IsRnA>(Fa5@RDSL!g8rhBht%0()L@3UlDYFw4p z{&sPqv6-syT!U&9;j1!gHX)V-LR=cfU0Ct(bWR`gkU6;q?$$^9&wBv+ew)2n)wa&z zhPRHEZcsa3T0)?uy{lOKM5g3fTkbgxIv{{gHv=WdhHcvgP)1>4KkFu)s}zp|+HUv7 zTLdW%%(n&<0lKEpxkytqEn|3)6q8}dCcyfG)$7-L{8T-)-B5EZ2Ml{5=y$a{)|^Oi z&-fL{Ae*Ubm%cg(aV1TP_C^OWHr5nsLSz*g(s<_IZd7RI6T}F^!4pH5CV{J)ZF?G2 zD|iA>rusFc*S^|WG*T(7g3*saGGbU3#K2$32295Ne<&bj(5=0X9a9ZBLJbWz06+V~ z>7!m1Yd`+}l-E;W=F}7TJ*riP&@xqd@3Q}!MO(#-e~wg`vGdc-6JvN|{ONHGeb>zU zk^2kycrlYuKXBOb+2oXg*ak98SjhZxKsni;kE4S5Tw1GNOSEsHI|CU3WRUXYj}MW3 zEKkXXW8POVuGWMXNVT78quCx$2EBfXU%3DPk^qjWrn%DsGI|G{6ET>hNtdT!&RkGJ{6zuEmB#~<2mf&~2N2zwwM!l>&Uh|T}B z$KP}Q?%@9xdl*o7YyK&CO=GkQcuot2_@g$(0>*|&l0OV}Q%_ylVDN9f?^#ZY0pi|c zRON)tvU}Qc*?Yn=?eDe)XY_J9+?ELjuOtMPu5w>yX)d`uhd%N^QY@^bqzC;-tjQ?E zW&2G&xW%QcvpCKWtuco)%yq+K27EjE9{khL)2(0F5~aV5@HLrc7fA{Ur1(-~zBfHU zK=s=(z>`fNtM`cTvbO2kGpk2LN@@HB;2MsnIblqmw#Eg$&aEK$k5or(5crlZiZT#K zlxIhO$;Z(#44DlAVSdJ__!b1phySV6hQqCpEcO$*36Mn1-;-T2fc%8M0YMwZ-Hyd$ zI9Oq_oMke#T2|nbhga}E<0z6Kek@vZH=tk=qfzIJHFnh(y}-eb=W~-WusCbGd{|Cd zDU4d|q1V{t;RXsP^=auEZXc~b8L#%leMX=;wBj=vMCSu=(Gi@uIIr0flp`x7e2Y6}v z>ns5Hd&C;&tk}v$yNgRAYBH}$vf-DMUXV_8I6%Wiad>|?jscZN^!IEs?UAN?^VZ!K zT{rLJJr?tU%I~;W|8kZRuDOeS9ogsNOuo;OM7o~6xHUJMMlrPZ2fhn268fhclCGq2 z3s%Eg+MH>|oPC6ybO>q~L{M_s-Fw=DG}enwr`T(~ONd<{&}IP~VS!qXFM z&om)GEB?H`npRpEk7PAv8>?+T?%Xf4*l#n119r)QWzZhAAJ)fPChMm5JGTQLN1tJE zUpG{1{lBs2fJW0l1JqZ@6$Yy5vYS0`i%0OXUwhO$JIX~7z1YvfmSR?|g!3gyJ2*jL zzK!l&?>Gzt2j$fzwUPJ!PN-DNPOn2poGH&q1#N6^GepW5t4U4lNg8=#UTa6~S?Irr zhWGu&7mGs3eH<2jh4nxGC^MH~kc>V@$77hddZzv5I@Bc8>GvYo$5y9CxIR2+= z)tPe8=SJDSq!HQ(9lh2el2&#w2 z*f+kGd$fN+5j{gmdPTqgVREE#AF0u>WCOdnHA{%N6HffN+7R3!3Wov;#C^lR8H@)D zoY>1ON~#zOOd)&{X{eJ$5>!kF>8v>_&zd4p(h$}W6tuW+L>HD^*C4{#0a{@Q91aX~ zwrJ!$r*f=JO^rKDyyjXSEGSDQ3I-`G%a{v9Dy~RMx(>vYDqIa1{GWjh@&0gBSaHn; zTzf71v+)pGuD>*R9m72>;*w6l2-tAln?{0@wn0?}I8v9wbiKIwN^u^B+e%gEVO(~M zseQHL@eEB2R@>FYdn&>koJl?2LiB zrK&Lmh$0k~w-VBVCtQ3yHpWPJh@kl)BE^NoEE+0R+Uk^)j zJdM4!cOjoMXwY=b^7RB|KhW2V45L2N)`+&}58^4>uK$)?yxRqi1of6$Ir`&9wD6Cc zplWkq>&^Qb%IOK9U!rJrn!6JX@tyL)v?fDZu!dqr*Z~kW=PTK_xnnFQ{mb4h8$*@y z&UM`x!sn}=SQ*6hFLgLw8W|>I zY(QR5J1Q6sB-nw7ZuDxJ)V6&X+B=rRz|)!f(KHsoV48D}4~ceitn)FUfdQ{RlRv5! zJWtpq_SC}lbZnl(4@P-|TPvF@3HCypbgw&!L$*}PpF|qQhE0+Lb&=k>l_;k|W6$aa zr3#8z&WOXyCoq=L;5H-V#;{}2X6x8b?=lV-Jm{M^S_i<6db73ByNR&|11Bn`F<9ka zHD+Z8=j_0xITOW`gg4%rg%4HX2!Q`rMgCCJ4Dq9@j_vxT%P)<~f6w7huZw|t)=x~z zMVnuWcfm7qov~&!ibRxQ(ObnT`w{7l?eh=-uSBV$cM zKqs4OXE1@cTU;$xuF^TNugeJ9Y`72b3VDZ~HKa8f-7z)@Q^p57*k=7bK^vnV2X(sX z_n5w;oSPYkbJMz1$V1Zi0oOW4j! zzDy}kAHc<(!&R;C3(mb~I}u_7a4sEi4{q%7+F2Pxx^kXB1j1d+2exCBtw17A*9VZi zU-2d}R(bEGCQ$mm20CK}NKn~ZN5rt;-hJBs4&Rk;E4%jX{PynQX%U@*RUgo){#z~? zq|9LGI4u7g=!WH=1pdFdU%L7UTf=Dof{jg%Arr=V{2SlVkfgz!(FWlQ8151!=x=MR zRtC*(8wGvEUvD+ETLrImnH4A?atS!pTDLPUsxIm$H3bicw@W)JiCvY2=B0$c~XQAM~2$J;Qn6cym;WKv49$o3~k z)HXY$Ho!Fndc&kFfqzct&B-k18(!GQa67#*f#*;3(uQsn2ja?kF!EHaPh z#z^QSB5g3Fw1|_yaeQ5?Q)k9{)98@3<}v}W<);q7Y!cieQs44K^uh@W{{*ATk4*<3 zQz`7eZ-*Z2V5}FHtr3_+zYC9I93XR#d^(rmG`5WqOvAH`0dxS$tn>Y?0KWW)DV04ecFJ5?v@6jsu@Z^9bxb^wVIEb@w)voWyajVG_T}k% zfd%6heM8tQEaB%tDG=Epy(V1x=aCMJokPu__mah}69%!tQCNW)WiKIIs0L}HrxwSv zu92aR(a6bgc|?4HWVcEbM=VI`)T7{R_51wvDJ~ zR|@@%HHeFJ!hMux={0#MIE1u z#=TuzF8()-W=D=h_JM7buiQ~aQEdr4V!Be+gbrUDj%^LH4pRl?a{-voF^W`hI&O?vX9?Txaq&9` z`Qa=J$C4^BE5T_r!3a9iCMliV0bye-5*)buf%_HkP5u7<Ab9%vx+zd z`aADaKQ5`czzEFJ!f&OzD?(x;a0N8m9VEb#na8J+3x%=raOkc6%~+)DZ-2-ggIJsj zUf}GSlt17u%c#bBF^l)<8DHQLu8$smj}4o2aztmqq+CC;tGR7JW2j4NQ{u4+U(yzL zUc0X9EjG$Mho}%f|LpdVj%tqgPm=irR$Kap-70GHsb-dB+y^L-Ej)7k6 zm&s99rUOGJ%?8~!3&ydJ-);&H$e2s5K-{oS0p~1&)DWvEDpDak0Sy>|Vth<=mrvN_ z#VWs;F|`4UL9O0bw=yeTKF9Ndjo-6DZ(v7^ox{=sl!cLOItsYR$%|o0H}p4HMi?Pg z+?3(q#)yomH`|LMRF2u2H_s11=zO6H^#Rqf>U`1RQ)o{Ps%~MIvowt>+!wOKKu1Wc zKKFWXyT3%Kn(jS<#p3*%5!2~@=e(Tt?^q))b5p?g-lAQ@e{?|H*l z6xPoV1bC}@nvNt`?vq{xQV;pPAst3FgAbP%6pH(TL~ zuHcMI;qh}(U&&Fk0pvEtAHdP;ECJ(|?t1BWD(H^&_r%uVr(jt@^z-Yw6YjJ|GOVCB zw}21k9nk%kbQ1Y{Ce!oL!11@q*?ceDP6X0te=&`labIHa zuT-74EO3VoGAme<&b<o_1;dpxqetg5uHUdFLf z;do+d**pC5;0X%|HkX2nkJs< zXMLL5fU=7AX=Xo1MO?!fER5< zb|Q_|7eA^&_XkxMDS3iQ?V(Dh{f&tQBm(eBG0ROX{c+63GPkax8+{TgG<){AOmQJ) z3URf?+@=T}Y_{d_C*sqXJ7`P@_5h{rI6XG1B<8(!n^-o1?GcAgRvK_@kJaOAO*ofz zh^sjqGr?1aiY0oICP-sh-YgTzK$a=ad^;YNzb=hLj_mL+qs;}So4xy^Dh8ScT^Bxe zsoOtI+{ELmW5VA->$k=(aY+a+_rQ>X42puXtWOYUuQrsW9F#)rNk6g#V~-9MjLk61 z_#Tjb9xZt^j%jlnC^8^Xw8n%gDgjlZ73%N{7NR({SWQ1mxHzazIX7)1iWn0wOF237!7fr9$8?=NE$iIUhvrzi zJ#iq{m9=50%1Uae9qpf_+m%RFm#y2=o3CTJgFY!UM*f2HaY)k?#&V}eJj-_$i0Czd zrZ8(5XpkU7c2GU)iW+-@Y%||&FOj1YtsX($>$1fG1vORvE|?701L_LV?4w%2mzY8X z(MhjCL1&o9v!9q%8Lrkub}Sz@iQ-j&G*YlCS+SldkxvnKWEjr#h{!sq%BwQ z8+s7r?Kt~*3xaQ?P|1UD0UYchkl^R?{^!#a-h)kHgwFCmz#(F&-IR`f@`fJATaBBp zT~-<~!0?%2bQxsB-p=(vW|8TFEDP4H&Xn+ea5>affFCpo*>_T8r2rylg*CFu?;pzV z(TMoEvLZV9LA`~yWUc-u`(Sm^)Eh^Cdl(# zO3B5zvWaX6pv|zWktE;X```d7-lXn^454hUo*Pv&%f#?j)8y@?*lh5d0W^2>w-k}Y zYTpB*m?rtkl}H2P##XyvrK4}StCe$Gx9q+c|6NOcnUu$lW>Z85Lvv{nB7UQIQ``d( z0^kO2oCEtkh%G!HEKsvG(W`=Ge|Jx=G3q2 zGkhZfx_!7hhi%lxKyE;nfdK);d%Iup=!cM0@kysg!@or(Y7%9X`2_IsP1wQ&iSoYZ zo-!T1D+9459+&RSK8ek0Ze5YWg5W<9RIM|s{Z{5=62?a+QR!-^HJaiF85V_N6HbnC z$(?A#bEt)Q_5)!s&gY#0xUC}1m2d}8vysNYxu2SxODe|{7KVgFiZH|u&vvLLdtXqg z;)eG!kA-y9axjgTwU*bIyuDF=x7(=WDL58lFhnP3(4J*FvKlyy1C@#29z6mZam|`?kq4g?6f!AsTYs=Ua-S*N8 zw+5nasAXrI^5L14a4c>;SS+t&gDA@DY1~O^*+tJ@;&)-%K8Z34`NvJR7 zGc0Ql-{Pl_yh`})>NW>Qdrr?8lrhe%g}~^&E?zdzA5bish~=D0LY`|6Fn|>VkaK=X z?-HB<-U%ot)wC%e&4{`)L!q7y2Q*Y%GCBsPBT&zmX~*dlI9!{q58e*PRM*k0$B@|QU50_~33nE)@7PTXjf`XP z1J2xgbScCM5hD+~&A)x;h}A!3Pr9nF;0?aYQ)5@nzsN`2J@98G4M?9rGiV2xO)%}P zBGLZ#V0*cv;%<7cF;UYvlQu<6&$X$ee4;gSMZ&`lRzmv8mAMEb-1uT3@WI=dG_qtO zk}nMI?wFCTs;`_GJYvXP?+_m{86jzwFiPHbY0FDK&a#Yw!-xT&oZDmRtfR4X6p|Zh zse<4Py~MRxlq>~94qnK8GUIXX%kSK_$oH34&`aVnv@GOP>mvD;svbg-#rD^{#sf{C zwm|{lha6+ahQ-)mhm%r zQgfvZ?sDld0rCRcMq4SJvKCP)c4D)ou6oS=N>-<;Mw48a^ZaIR;LCFrkG&8xj|5oM z_q})!!AqcUWV+B(E59CZMk~}YQL_uQO*MVp_+~7-2B?@7vhto8I%xFn~%aBm)!QPUpNr_Qj@%pf?ia~fW7ruL4F@Ay0{@4 zCBwC?=LUcJ1gE4xpn7grxu+%)Natnmn*8`cU0l>zkIR9~W`$Wt!!^Z~6PF)<+QQIY z(&BpiG+5LRoKKf4pV>wgtT6Fx$fGjb`iTe5*{Raf+&`Xtax)PX(P+Eko}O`hmWdj{ zmbSAbzqTp+7Wo0I=_8=^=#2c*dn<-XQ-tfh6u^0TNMvC-V9H@LS(%lR>I{@Xpn@;6 zSApj}1jbxm;>UvIx0n};i6E34(X~~^5R*|`0VsmjN~*Kn5#$I^-zCDs>Z7PnFxIfy zZ;W+7bCrXD2_b=pJ=6U!hoCLEVzy{X-Fyi;hmA1hr@o(md5Ng)!QO6xhnm{=hVgK? zf62k&p753_5VIU%JtXyt&d)^BcE-iT1t?ZTz@VA{a#O$c2e;kGC8qyukF#?v0dd2CEMueu2f2lL2SJW8il*hp2cArLOq;wCkO ze7_1=I-lB47)d)3(l@V7lWv)C6-q74Y1r)C-Q*?%Oc!cD)$jPw65KLTb2iSl{V;d> zu3&8=9?d#et;jPq>;+R(8}y|$2bdo1lD@RX>Rn`cW-~Kme{&R}@F*1Fe9Qm6cW>Dc z+yld@nQIn5=oE zMqsIg?bIfI^oVy&4&Bd?!*GVQ1F6?WUikRbQSsOE@F>#cbA?dMWi)Bl-vRv4=5Oqh zM_kSS&K*Iy{zDr?2WR7EPeJ+DMjY$uCv0}2{%6v7B&%d5i9xH%*8_4+DGJweCfYVG zj%JUkYt$^AZXo+qhxWJ6lc_{;eU>UGq)0LbQfeuYEMjP%8{3hiD^IdnTN})p1D&04 zu4T!DC`$LA^m0s-=0L%ze6@t>Im$u+*n`@<+paAV+@(28Dvlr>1jv? zai>Vu9vom}5z(Ai@g<>)LRWtu{nM%xQvMK@h-Wdbp@L2r`Lvb<)mKtll;0?aWx<21 zU=tZYruVuSvN9+!eopo+0a|Q~Jj9-?T#Q!WFAm7mt%#)N$sPRz)Y#*RFGmaWK8e)~ ztwd|qC^JFQa_hrE$6d&YM*~7(7jO>1VF+L&WWA6jHR0_Md8-^fCsh zHb0zBD)`>RSw+zYVEie3(rnLW&M2Km1+B(t0~1u6?+?EYvdbdmbsVY<4PFJ5f!E_~ z>VHdrGy!HSW9mH^Mxr8)=6VLMYkx}38RGnuQ zED7%?O@@YiAPmc65;4T`-anc>Q}N$jP;)-TkRONSNM8^M3|@#F$X~H2)K~EdzPb%w zeFNN4=)p(fXVgWJvAmAD^T+(JF!OIQPLsZ)N$OkieRf@}MCA4*WYNiG!CcwnBxJgb ze$s_1)Y*o=xfy@YYKN-KPT&^hM`}qp`cYfG+*$X$02_!H9WtNky(&KBH|9C;ELplg zm}RiaR=AA$e*(_Vk>Y*J%~qcdBPoJyZeKw@Q)K&&q}}i0BX9%!_Go_|;Mxft-e$of z9EJdq8W6KA%N zb8EW8iq*yjwiRgbVm5U$J1u{C8{Try4MYICJiNt!522(dECo-duqY}offUr+(lh{0=B8H!<2LHWsmae>{9M3M^KZ)xLs$j zp1n`RQLPYrT5{g==i{`A=(Za+?qhEmAe@P#wjt~YHD3j-dzRH@JtTFzj@aX8{lSZC zEO*3i0t+O^E*UE`l^RHB+6LH7iMbBGdW2M($i{m#n6>`HLmI2h>r^TqfMz%3#PrX) z0rfdGwU-{pXLs3Mc(6gU;Ot<;BA1aoDAK_6CF<+!E2OC6uiQ25vHRIJ4X=aw-RJe@ zgn)%j0$w(q0sRfCs$OBPe#89*ynY(S-OV5YHjYrNw1c5(#(T!m8-DkEw2_(w=Yw{A zrE}G2LnxX`Q)MqUv+aqyNciF0Z3{adtxVz} z>`aP3KAk2`MEj1WSbI%UY6Z0=cVgN=puq{fjD*zSSD4fHh&mme8{d`SB z>&kw=lRNcpnvf*IvgFFFJ0y{df6EU%>fXs;N41ncg>P^#F$t)8E687?Ny;UV0ZH%l zp_M%yo9q?$u@(h?$6=QMdiG{e0No9>&r(PZC9@FMk_UND!SGJ+s!uxkVDbL^SrGnO zsk!9=yLlV3EJ%n7!mZ#_^j%drbnj|+hYWG=Y@N{@Atcl}v%UT$pw`l-t;UBoZFK$H)Y&h}Q8*Fb4?x>yUgG*cYnU)Wg{EHlcMq72EX zRoeYF@ZGdrl(rof>{^~kT_n)Q}&3%^9K4Px;kt382I>$tZoR2 z3*4gWr?{Tg4Fe5p&|dO z*`rhpuE1;P0Qto^cgpfskIp&X5TYpwHz(AVNMk1=>bm2HpkTPosC* zVffxCnnrG@Kc3|hTvO`=bG;}vVy6E1DVGZRmeD$?UuJMY^WeP2`RI;%WU+ewQN7OyV#<6(Om z|7*6VzT4)28JqXqBKvoHqtY>+Qf4lf=2Uy zUx*j~z6ckb15r$iBI?<HO##9(}Z|EwR9;e?#IYjQDNJ2_p*;X>axJkEA z@O2dOMD{Utzj|4Z!1oLd0;2CrG(eSkX_xE~qW%U#69q)Ezg;J0As#7w0=KuD1UT*feRbKQ-<8V zJRpDHDDBuy{WQttPALl7R%d{c<~zfE>9{;BtNyOqL~gZF%Xk8 zHye6p75ly%e#l^ZkP=<_No@LyCmM2v(Bck`Oix@mDx}PaI~R)xylY~C9rKb!Gvea$ zM+1u5mX<)uG}CBk!!r=nbQPiPe-H@&SFwb|!@~y0tYG0_=#K>-5QLt3L}0A^u*js_huvpjrhs9k6hzh?EzkK;%U$RiAN4Q*>9nWWhLS1-nio5=xc# zQ%FiDu^_|pWzEaRL#)sbG(~zC>sG}FA7i*F|~^CQ6)lkrkDsFLG^i3869{|$Ad3D}KR zGv{gHW{vYO4dy4{>f#}{mSComtqX}7NDwrQdG}lIrz!_EgN_l(<-mEc*F>#5=;ksr z^+)7N$r3M`Io$4LqK0zaRZ+jkiyTqxwUE(&;b6wz{d1#*2SYknL?=OT-^CRR>i`Ho zWhFVmzW$a^qo%Xk%mVLNOcsi=$1|%V`JiH}>P=f4ZMK@_;5(}`m1PbgnZr1?e({(?@yHMkA_endIlZW>@zjwa&BR!yM}Oy zq+>YMG^G_SnaCEo%&*1pY0@)rM>hUDa3E8vpU{ZhXY!)UU_fm(l~&^<*lFh3S*-1; zzMbbf-h|;Y!_5R{9CFN!WFOv^=Ftfx zQO1E8j`sP`Spjk_$m4ik-mjYl_qjIa_B!8-UR}U$&+g6YkWZJck?>3NZY|KTKK<>b z*7wCJD8c7(322D-`eRu09+7Sw_XfJj zME$91{nsnHJr>Q7DLdvp@4BC2rFZrN=7Y3cgYJoQkYL{3Q7850<@L?|r^|AeSKtqZ zh{9a}0WneEJBvUULAv;@wO`}eYbE!^{JI8v@I1dQlG75a2*+^l2rJwtO47WCYn`Wo63 zN^(kk8@X=}2y%A_4`9Da+8WcXaag_`E8*;YYXo)~3GD2Brwz;#O!I?j#4P;Q{)YJU*hv$kaoV{mVrx+FOK5>M{!fdu` zv;hrkpPQj#4JMeNf|MC8w}ppvAQ6%%HmsehVzif7=Qi`8n8%M>Z%nGEp7_&O!+f)Z z0UR3{%}YM0PD6#B8nciPTw-gskeF%59M=iGWv$*xTFOYF{RSk(Nee~wAkhL^c~)5eFK;yw$N8AzcD0#)smO;wMMNtzd3M~QOuXUE(!*p-^?hsq*j zWOMU{gO%tWc&JL|URg}1-b`Il@$qz5q^OF`%#c)-$NDz1RKEhx$?ullnf@fZG1x=!q1+F;iIa8rVx~zmjgR z(>O-DKUwmfoYm05K4N^xMcONw4ND)%+$w+{u@;?SeJGFa%fMA37apq<8cY2pn>~EN zm|;&E1A|-2Cl7jgHl$0{Bnm zXle_`%fj>jmZwMp|Nll{XJ`431CtJ@o3Ql{F#247Hj{$qWG4}|lmz)N*NBsVN9G_a z4xD13Mry$gUQ3>k(A7NYyVqS+@v(&v8XrOjp})*a3-j_NI@h?>5VAUB4kL9yt~S{z zO(H5E(pK{-|MPxY+oY;vu~ID~j|>sJ{>k=H^7)GYrP&Z)$KkN%d40J#21o^5r+aAT ziPhF_ua=4Em2LK3taf=Uey%TcXunj%klg90ZYb)Ry78#(b|$3S`Q-bTBo@d)yP&srT*oxZErf3erM}1ju1o=#2^t zqj_wN%IdJ}nP8*6t=OvPrh@@?+r3p(_&HrbdMf{APoIo}ZuO8#rn{B4XYWXl z>IV1A9QH*rynBQZ25mj>f6o&7im$2R3L*|&w=EW_q2f~`kdMNkmSJ70ue_=(EqaqV zK579Ml}%1wb7~GvXVtLPo1Qn##$s;?EE_>!ENJ8oO87>J)3Gen7E=boP=`{^s*J-f z`Ih$_6(VyuiqL`WXA802aubM9CY1+v)3m6;#1W62t0D9ej&S6^{5jemPoS>1A6zC%_xWAqA=o6he9dyQ#Io|LHqufAwK#-#~A1sEe_^DG)!!4f43z!;X7+7PCTqlq&1XfinF zRZnX#O%y^{>_rP7H577*12p1$YB`8JnBcu$jxUHv7@CtzpwRZ;y8mHIlXZ@ z0`EnYbxyvgaeqzVuF!^sEAJn5B@+W7o=#-`Rq10H1ON6m$}zb|n!mNsu?Rl>OUeW2 z$>%?Gjp!N|jkpM+d!-l&UYo~9@EqecB#o0(Fy*$1!d#`YOs}b7<y%$?I3z_CnP1uBi3JVV$FC%02xl#eSqaKh+ zRa}#E8cnH}_^BsLZAu+&qFyv2b^ZWK_(^Wg!#j;=vi-8cK!0R*WIt8}I?9-$)fiL)i6 ztOp^DYfnpqIm1169cU)-$I5lWflo&H;0iw&8G)t^g3PuGnSHpyskl7ZSKoH*K@Ime z8fv%k4jCxA;IbW^{ALam=_EBNmdLo^WrEs`#=;7lPow9WB>HHiTJvz{Gqk%037FZwk5=e5_>!+1@DRi`No08d7@#oYGtw5n)J&To;UQ7f85W=fVz=$q+cf{ETI>@SH58xKW_yMzV|1QX{JI9=(4)acD0;5*4v9I1%?1EFlv zk^+}{joUESj5Z)$x#~f#E!R&vB2$6ViCUm}*KE2G^m^FNYhbVS%@A)=uKig~SDH{N@R z{#j|x9U`SG<}OMJryWRpD_Hozk9qYFrm!F>ImKtz;Vz_iua!Y|VWaxWqlxD+|GL47 z3_>z}!T=_eC~!)%>J^$)GH8VKGy=d;_?V>}gv!|j;uTo~Y@?SpP7ask$zXu~iL{7Y zNv`5imy(*dFQ9?4X@_{ZwRd8%ip_)Y2|)OgMR|?ZO<( zMD2>!`df+&y$;_Q4+6GGb5&6BhLgytLVVTZ6p4zsR4+MgO;pPYS`eUJ1`6Ju*1Ss) z_q{=-L4Skr*QVLz?E}?Lv3P*=(P&vlJ08;-p{x|@C~jCK_OuHEh|DojS%1(mI{A|r zqCcv@s-=cd;nrKhM8{fE&aTU@<4c57i8p$;QAM@co zhua7#Mg7dP;W1+~(Rgy!>RRFZ_Y%IQbVWD{87+RU7KAb|HiTB^lRH3LfSpmn@e0@O zluQ)c}EviYhB;)!c!`>V4ivd2!X&JHyXHtw1B+aO*rDeQ4Z_}_kzzq{@xQT@ZmX8E4H z?qRSPK}G$~4xywmD+chaTu`6_Z2q_3r-UV^lim6sW@#|Odv~IUI6AF}YI;#dC?Wc)FwKZxDh+kW~fYVh%40zxj!P zMQA2?ZyVu37ypN?a|+HZ+`4sa+qP}nwr$&A)Nv;rTOHfBjgD>G#>xKwQ|IE;u38uC za$UT&-Z{q{b3FM3!`isbB=~oh!(5y$*Ina(UO4luV9uMiJg3|MKK2;g%EMbZiv@Yi zc|gGutm=RuhkAvrT?g1~GmQ3!b8*0k!0AT!dU+KP%0kcD<@u69pk?}iz)QIj?s*G+V+N3- zlU!t%=7g@?W%=^tHeMHVpJ>RYdY8C2%N^5pECI36vl)`%5n=)BLt9L|?zZZqyT!~I zZN>K8wVg?H7cIrzf{Oalh<59!xdZPz30DbOi|Jx6AGrnqQJopz-E?ca$>jE9hTbN} z8+#}uZ%sVQEw{M54<_u5A{lZ3lisq_5G24+mIq8qrN?g+*)V$qIsg8ta8ZWbXhddj z0ll18W`zhcS*_ikoD$mby?~Hy5AXNyWF=~-G4#ctENHerBC`=bT9r(P@G`wpFqHS} zv57oywX;b8<)~dKL~GggCfo>ZPj+IIf@!Tdy9~tZ6tlcU-#4&AIf%%fLau54Zg{-& z1+S!^!VMfdr$F=mA0`M-19tfq0sFc|domt8>3YL3n;`Vbwl7NUwFPpXqfmk@gIB?> z$!I1qVu;|&M&ZCHr*yMk;x$_P*FS%1XELF983U3=AA>x=Q^=v>95vT8C4$=D%TVmMtC&IY5kYr|5A}h3G1a4M1ctd(K-hpC3Ek_U=HB0>(|vh-^7a_$;*f?o0RGju>x_7agY*Pbn4gG)Pd^bmy!M?{66>TkKR>w5!l02VPOeA5q?cCt_+^zTjk zTfAsf9BB=x$G1j)B15CdJ_G;LAtC>N%@PY|DiPujgN~K`KkFwnsZ;78PypkHBA2?O z2o!3WQ}C6-P4Or+E2hK?Q4TCCbMMM(hFG#0a{F)JnO_Oi-h8YGAZXxGz*14E1Frkr zgr~QdEwF&wFAuU6TrvX|A=Rb4HUEhdO=o5izTCD+zs1!ms-Gd)e( zRMxz%Q|g}5f*Z_}MNp$iWGC*Bi}bmQWA~&6rF&V>-8K3d&N(Z~=qp$s+VRhsXa(N# z$E8FFjKdH$V5&mx!T@k$vbGMotZ)-gk82JIEp0~Cq1P2btkfjrn969df-vbFwp${> zQ|YHj|$s7 zj?{HLtao%X$X+pxV3oetgBIcM2XYOY;ySviA)P@-5OiSas{q);pT9Q=H6)R}uRfbc zYXKAb&mnTNSMxK|no)d`5M=hcPN-L8;lHMH)u7@Sb<8j;Z8*hjQ~*^l^-g!ew_h5=Lxy;~O6WTRO=)dZ+>*+>Yw z+J~``K7OMbK~hGDB1>Ad_pa4-T)ZUdSAXAHqf{z_*@r`7kBDHi7s*mYh0PWrLKhqH z_XmN?2MR2qX(kfi2=SR7YZpY{=*Wt}UqR}79@w**@5eO0PqMObQe=Qq1@ z{K2r=qXKxn>wx{qm#XBca=}%dhg0}``y-qQ-8Q>kplP#ERdB3ZJ6auoZwLPD<6<85+yH70GMkOD|(OVPLR4mzXLt# zWITBRjtoi9_nksU(HW{;Bf*JnnEm&7YQJSn!VFx`5Z=`4xc-=;6q^fLS6yajyM37X zsY)$DOBy`dpXzUz1b!KVEJ^LyP}6|i1{Yg3drS<`;)_M<8JrKw@ox)(m5*DM6E$i# z+yro8bcEsdJX?7!>^QkFy@)+Pb$GdU)GS{ijJ?JPW&~;5&J6T=izarW1Wigm*e2P@ zJhq24zPA2}BnHVgd-JCPJrs3dt(^F4iEIIgBg&_VcAgFsjFwn@EM)%|TEA^h>oo10 z&6=*udYCJHL5$BzVe6*K3qCWfZodnE90Y&|Ds%yQf;+^{te6??cPYK%2pzZ~oMRRj zbG$&K@mQY^!iTaVC0ttLbVnuq1$Hek!{JPrE7aYqn$!&@Eq3%uSn&0Vsqmeo1lDye zzl#?hsz&$h4y+idR2tL@DW1^DE*o;{zO@TE9@~vtDVV#YQ(_%6e6M@rCK`Kf>l`5H zse9)v)3N(|{c)e+xMnf8wR?KqW-a8t`YxbnU(qHYH|70{G-E*AYc6O$DQ*bW{Ce2(P@>356*iN1iTE|)BBqQ_1$o$Z;^tBTT5Ty}HDn?Gm z--ds%Uc{7~fjTdV`*N(s0ar!)h=~9iuF}f_bdbiO6=ricqf@0hjsn zf$A(dD5r!o=HfxL8QAEz8_iCVGA6A0PELE79E!vpDaUunCt6lQaH#dR&F+A$DHm}_ zstJR*`*6DLf6;>vDqKA95Z24qb{Rk~nw%UFrg(B;V9whZe`L;yLHSFfxPbUzV-d>a zM^X+=+GKW)gbriJ!^Tr&R%X*?-=|#2i4zTk4FfHK#uLUJF5(S@>wc&cfrZ7BiT3hMLg^XiN1 zOX^)Mo(AMV)0{?Ngs?)7jD(ZSA7j@ROV!0~*>OPEE{Ten(*T-CXnezTD>(2nWAn%% z_|$|O(BobEM(n&R4kBP1#1EDdFD?5Vt&!e+wl_tHR&QJ;6=cHnJCehT@S5Q8i~Eoo zpIoHG-$QX(w6dO;D3t(8-~0yhG3yyv{TWz;yUqwc#l&Om)6tMcd`)y_S61U2^FCc1 z9u(M$;l(lG$~5(R!1d?h*1JlqwluXFm|dRe%Mn)466DG?(d>RHp*V@mLg2NBzwbOJ zXC}vWGis&xVhfn-QR^KRV0xB92blOYllz6whft8%8cSro)DnPPdPh1tj2256trDm7 zkSy)@qt^r~fm_aC<}bJ+m7k~?HOWRlr4QoloQK4#@|5O6R%C73y6>?-o@hy}IEuIj z%O(5&=4G-;4xYNI6EPb$%BPNw>A+@f9=dt+5Fta~LG8(L_;RX-qFE3rGm|klO3#Th z1jbWGg9Wwn7Xcjj>Cy%Y(!>^P65o4;AT!UU0$3`8Ilo|Q%|*>+&98)#t60M|iv}j+7jL9@9`5A#J11sl zuPiy?)h0qa6A1)$Z)?)cDj}lP zbqw%}{3{M;%%Os1!HgWbBZmPCfWy)>=!P}5)DY>p(Kf<=2^#D~@wuL_r{;N&BQso1 zPm_dUTE#)iLq_JRD!TKWTsyU&PD%pE{Ggksb#iR~%fx~GzeGN=a4`KZ)9g}5_Xq2Y z@iTHDdQ-7M{^v>>Zr2ih>5-WijkeP7@ct9dPpZtU9I572R|WC;llUlVLq!|AE{|_o zhs1QC=XJDkly&_Fr7u$mY({~sk>sZX3tHqD4~4_XOl+ZyS1UV^zD_9)licV%^~A;g zXb^Y>4j5R#8=d0!`Q{Sj2Kd`F5JlIEk@vmQ%u0wmXZ+60VGP0CjkDM8N}p<4GWOKo z`3*gDwkQ|2DYts#Q#{8sQcUf&xUAm2)nq>LAFy-A5y1P0;Qo$Dh7_SYc?ptYhqOGZc z&q2mlNlshD%b>1Aj4r6xU1mfFbWC>anJj8XukvX)zknM~w}-g}J>U1-KEB@Pb_;IX zN1vW}|Fd(b;iYX4n9xW2>?W~I7Oz_XP5g(?Z&lU`vc)nR0L(LW7I zIfX}3g(MaQ8&aAEumjV8yHc@P?~F-))(&)DAy+q2Di=Qeka|F^X})>G_kt{cydjR! zQC}Sveji)X@sXN7E>g@>Eis~p=@-{wX{@i&T1|^%5TVn}&>?G(c)!yOygl@bde*;- zq6W&0OsrkdLpni% z1XzJ5Uu0TT&yC%XbEw$C!>uX`C<=I=d4%XQ46ZfWh>KS+fPZm<6jgG;i^|%o#_;4~(Acf%Fb}_H z^MbYuWr@NuYU|Fljh96a@Sss9&BdzLuBb*{yj`Vp)1vbW`g01K`hB~n8Nd18g+U^u zgHx>rfDqQc5CZNNkiYmVV&osNI#^VWx?Mx#c{u?a3J4J$)n}wp>&sA^1f(WlhoIO% z!0zAOR?!K>`5~+_XH4wDKWuInOptf1alfE@`l6q9KoJa7=?>D*>$?t=V}!j9rB;)m z%slzr(pwy+_)+ugfrRHli>uFE##+nO_xhJ1+eHjDvNbUAYhET4_;x!6oL>jEmB|{1 z24ljb7kwj6-bv&lV&hd%1Em8M38)N50aOjcs@+ScrH?bZSBmjF0wqELG&GIwyBO7H z1g^Q$J4ivuRQs->HN=>=aAr`jZM9-~M+B}&K?oGp?n>2G8+?}7GTyipDjNF{zV*~Z zx{R`BmyP6V0uh}vaTHS|1rTPlhmJ7)$ZOIJ7s?PsxlU)p-eT~w1Z34lG2TdJfa6N8 z&WpCj-t?yk8ly-=B(UI<4}@hlkkGM&GNkjz#iQV{R+pV;U;tUl80ZvNQ4G449C{jC z>7#I2w}w4Rk@T6+{!g-A)8&(ebuZm-Ry^x(Xk1sn;#O?DD%vc`7%vm2xF{^#^;+KthJLZ~2CGfX8EoW^(3b`wCYtHv}!AT>79GjtvRnAV{7wR#>$0 zrIJ}4o)MC17BbT5K7)NACH7Km(tu@!-#>icVd7H$IUC7Rw>*Z+vU4oIU@)WKpF}=B z=ajn}kaDK|!wUN|3oWS6bCB9qh(+z78^2CYVZ{hJ{1i^_xQixoOFwfY0MngjrjJAn+(|>b%CZPjYt4?= zJV;G6f7w8lh!TUQc*{PWmQC@WyXU~;)>L&pqBqGlUq*Muz4l*?9K5%NAnYqrQ% zFnX7$>V(m^o`uNK9#6#1AFVnhu;H;u9C5099dyMseBq1nHlr zs4VNS>Evjh0Wbmanjb)D!G8M!&#jouTp-}mt2dW7IY3e`H84LQl5KO*XLgje zS3?jZr1D+%BrOLBZhV_FUKmqUN$L{-$yq$2`SUAUSrvs2YNmqCQ2yMwKJ)bgu!`WW zNYy5$%>eIZeU*S`(I$lYY(enNus=J#O$P^|yU8%T&l|%zVCyetrl8(3`N8)%%lr?npmx+^16%a)#d!IXWJ=cV2V27c?L+2?~ryDKHgOYLuAzHduTQJUWd- zPA|>sM4-es6m>SM#fG{{c%*qDQ0;f5|2XPXQM)PvuM5u{qNG;j1sgIh^{tK}QU+$3kcFmNen9f{9#9Ka49@n9sR6z4 zv0r^_XfmP$0zj#9eJbju%=bB!$kD#yMowmY1!->aqwO4^S>0FOf0Fu5gU}8(XO$Me0XvsRFNg{R6 z)SQsw&CM_BFK(i1OF@_j$)JTsikZ9`r7&|b^DbR!1p#tvESOg;hB(BCA=5IpYPMbH^ zZ)^j#KIWm?cd&_a(}bu1Xy{UTJ5=$=U?4n6e@PKGsL7G^oIx0>C3@+kjUYiIssHX# zB?gxgtf z*?YgB*$Zz{3hHzLL1LyecIcq{4S}OKXe4Q4E3i-}%2_PBObt93uoN2>)Kpw#aShgz z$<7er$(Wd?jCSl+Mp6u=YU0_>=Afc3RS;ojvhf&F90v(pHE1RV!Q6lu-XDrdr7F$Z!DDO+8B!bS;8=4`*bXRwnF;Yddc*Ab-lg$|g%;U| zmD4uJ$tCoBzVb)LU6owu!>o&dv6<6M5{i}^{|9M3uUJU{Y%|c$SdmXb_p!%8hRjDR zucc1dByT5+ad&9U#L0T#_Q5P8Y;kUA>Rj=ENbfUm#`*4cmT>jkD12H6~XoA3%fsD zwd}+My`wdt2?0LsIGEuB2eC$51E9--gj@1g1wAv{hHw_o=lLOm6V_S^ws74x7y93J zT5+irL$x_2`+g4-H)j_IUoX@6=}|GKT5JTOQ-Cp;&IoFIr}v3NC}qie875sj^2wQ6 z=xcgZ3%})ULd}}6MLJ%*=eT#uPtZ;y1Hw%9n;WRAD^eNE`(e{4?HTW((R@8V%>jIa z?ku8)?^nCmA2(bJr|roz=KN~w1>*a54u2iMb4zZ1=xwAM;Lp+3=68y@zy9@a?A5C| z08p>@aGux$&|=iE#GKbpd$mu&xa)f&Y>*U+tgjhcL%YNs{)U+rG zbr=vS-D764&wMeP@Aiq&&3ihg;dT2gxP3jDW%eY|6*O2j@P@4)oSP(Fx4 zbIiQ)nAz;rf5dAyXyPztUCU{i?5@&!0Ca5zcSj8IC95EZFOaF)+~^L!YFJeZ6wigB z-?q8nrX-FGmBVKShx^)Vl?#ZiVl!OW8~(i-&aW2pqGV9HZC>=;19^bC97K*rB)=Xm z0^6u=#3Wp;ZeNM{0MvjBy+nbh{=usD0DCgph;i8$c3?TI;G~O_+xD*;yjwMi1l&zU zi+?SWNK<7+aR0IG&lbOw_+2cdm87h6dk(>NbZ>IFZTMn+u{<#`KYyQZXbAT+Fgkm< znu^DAU6<+%3P}fcZ?nhP=#NBsGFkl{4ZJL~7HL|^xasoQ8T8IXn-)#!5JK^!B8$th z*PMZK2+?FKgvvqfP*K-xO#7}@2!M~CGBmXt*jtK49Oas`(($y%$8_j7>5 z?#C^pw@2RX;wNZP*Mc)Y&-Sm7JBcfs((sFT(_5R9m0}!fen9yldD3@1DPxqT|Q`XcB`IZjVyw^? z4fy{;+ZS~QLWMr(Xz_E!{_vaH7n1PL*+vI~)>k!n)o%|5Q* zzL*_A(xde=-L3=bnLQ(H4D7nj(O;}(7vgOgH;M=>)P>kZp8DhzZb zebT7m{V3bFZzJ@%p9S3S&|m$tcq>zsAKg7%0N>Byg9g5-rrCz*MLC*y3l2g9pSrBi zgpZPnv*%-1YV)}o*=41%4h#3`#4o+HgD1U}LZ?D{>m^V3I&L?0NmYP{D15A;VfM_f z_EI$a)JL-U#ir+R*8PE{)&2c8!HS>(nB$F@yMNmz9 zKms5ca(Q+P(X-?4Re7<8pJ7kXBPs`7GB(pLM-vGp1*UC{u>RH~S(mD-A*0U-7_X3X z*4$-Hdv|HJfxo(4>HK+%gzJWyJnp21mxmSIcfxx4y~PPGPFLT*W*z)efWDighvyz! zrhG^kVYB%%hyQ%XP&mHy4ZOA8qL}G4K=q1!8jRY{RiGsgWe(;v0VAVqh+L7?ja?6v zvaPw`66GJ3pj8|%C``MO7dyNR4(NM ze5szU&BJ6`E~Oq04Wwj>O!^t7*!&1M7LPkB%~T!_z-`RDlk3c@o<&R;R{qNa@XO7) z-$^FeajZ6H;QdaF^b`It*EOd^<(5&o&joZT?HGoWNETt`s&><~W5^@&5@cOhNjp7} z#x=f3qC084urf+*$=fD0Qaym|sUXkX+CkLjpbTQd+#6wLbqHf%`4BIkSdPd{KOngD zv*G&8&y23zFkLM=m;{GXRp@XGP?f9mg1*?ihF0|sOsw(XzxsyGU~n7hd>KYeyU`*( z+JSjRzg1Wz0to*g_AgY-sdR4C-~PWqW=~TH@D`vfpTu&Su5~j_r8%g0W?kDLb+uv zd9>F5&9tAEIj?JU83wK*t$KH_yR0`t+FUz%t1c=8ck8{C!P9s38o#D#@Ix4eUigYrVxUTX>h>c?o}RwAgT6HO8c0e6I4bFL&2)pc72RX@l4VO_s7H zZu$O5>8qTn70nk*srK4*fJ`4(ui}V}y9F6sR*04)8rd5vRis3$!fJMBxR=GCb%&IN zVNFw8*&lgFa*q_axVJNSfisP`k zkcvvgy`OplJM}P1{;{VvY3nucR;*ej-({Gc*f{dLezaU&Fau z7TLVw*##nv|CF)LnfRYce#u72JC#il)0ljcjri&>T#u!SRMCtcW~RU}2e^ewW$Prq zk`|sVqt^Ko0~!d0itoOsg_7e!m`L9mu zqA?bbQ=cXf?! zaz(^r5ns&B07vQv;Y?W}CfXsMniIx3jDx@HQR+B3G0OM(q+06Mn>K zU`_1eL?-kGn4!!A=@}Ttrz+>(cTSq*9wqu0;8kxd&X!4*Pb!|XOnpI+H1Mza8(k#c zK$<-T0pOk;S|4c~uN~${|J3)fC-;zV6K~2}EG0If&kaBC-wt`Zg*RikjSi5%^iamO z(oGEk(fQ@cyCc*8Rvq|jB_v4l7&~x(8bll(fsh{2acUGBXQksPpEJQGC=i%MURlB^ z)gr|hfAxn$infq_j=-8TeKkBiC4JwY-RHrJ6(9|jWVS#yW`u%;6vZ)^W`5_D%AMpr zQhkjlzD5cz7>n>I%@yB(R=&-Rw*Udt0z_f335k0CtAa_fAd(IOWm}CM?IYZkwhSbN z`R4e#9i^SUPaXwB!^r+G=N8t6=;n>i`p%fLGEk(c#6ix3gEGs_k&vjUfJ`w8fCD zT?4&S+Oy$;>>2N-WaSIl93908jC~Iwl_CGjs1} zn?Tku-5zL4G*K+!Ux+4g`B1=?D*$x+I{No|_}WCdel);nKI3IetdAhTjgoju+riX6 zCn_D`NJ*|l?vOe4*2<0Y(upU^>|dk(sk;4{`7>BAr76WPF!Y`so1V3#nH2U!Z z9~o)?G*R~0e6fDb@%r-|9IlEV+3qIUENtGj$9assamVq_A~yX`@0w1(2PnKuBBZZk z`Nb9$4n2}5AI+h}3J`*4X$DAO%|DlklK9ObOO|f3BD=s-WuY%w@LiJT#t z< zow28G6{Y=DH7{p#udwPp0%2zAVsll|(y7rRHr}_h2oN^!W3hYTKC~PqW6gEKSA1WQ zXXX1Tu{Ku8hyEIP08OE%oNsp5yu$W%Un><*iD|6|FHsYAo`5CYG!Bvq=r~7DM(mZ& z{z8Fm|0bm6rO`|L$Bzoi#K!zT#X1N#^Z({c34!sMknRNSesZb*b;J`BRD66S|!1!JECIz~I6y9gYJKW~AsGFimbBe}!t$ z9rRcHS{9i4rI)lG{ft?rmVVGzDl0ml0MzebXGMUA*<=gERq{8x*PjcF_#XKH@vN6 zT>r}r_t>l2GmsxUysgiuc~iOOGQiq{snPl6LR}=+9n!7W-Su|<3rY@X!!LAQh9=!& zm_dU__P2Y?24O0#<9(rawF~YVua$f~py&&6fw0U_xu--R#>eXwkq@lV>Ee)ulLfryIzUuE2JzVcTCC?4z20RoBi8=^`EI2UGfMier`R?6CPAf+g64we;^rccLXQZG*m=1bz{HN$ zL6mtJo#vbu&yXLV?k>5a!8|v3qj#@Q1X0vm@LPYze0Xfi~4(V`U8Z*iJJ!DK|09g%3f+d*W> zR^I*vRPfl={RDq7N&MSYY+rjLz}@Dc(8;&ZXZ#!b<9p;QfkEs=6O8tA<=!FBU)Ic1 zHPq}pgEjM#&@VT&!BvvDlrlwn)xzl@{qlvE9k#C~Fb)m=(mXi_gbSf8S%WS0Pveap zUTicwrwz$sGsV8!)yZO+vY|zcW?%IXMQel!xqPzUoF^U;^TR8JbUGJA0953aWlqR| zaCSb>4RqkqAmSa1?G)3^CebLmvBVxvQ+*A~#3#|f8yBw9^G>`kTpv%lQ@&%XKc z+B;HQJ^J_k&Z;pR>p|Oe!|=>sF-C|>S1uG@f%*^|)veW;K4Q<_M!DL1+L=)V*A_FA zTNrYA^wqdre|7HP<(Cz!0Ufx(fE?@N<#!*rtJc%BR(&k%j(FZ>xb2p6>+N%>f-~dg z1#|qPl-gS;kRWhUmt`$hXH07h>l|@5-gueQGz*Ia4v=4&1x|H6=hR*s;zS6dE2kb6 z#e4Z-!l=#>as;E0H-QbaZKH*Z{R{bDbvWH=&1MKklhT2e&-cW90H)lvg>@e|m7}Im zPPu%oV&VhjtOb-l{dGL7g-b4wr=ih{2$C*#fhGl3#x$UZp|!?GEHcQ>*2`%7h{_YG zD^7P*2t&*%B`V}XrBHYUg&z*j|w71deR-WwS+$gC}LX2fk z7VU%TjyH^rt&Oe+K=GugQ?|M{(;7x41Fv2BOX4$Xtz-4tddn|LY9}suKaA+n(gR=% z;t5wE)U6aYAG(qWEWs0Qbwk;2@yiOzrPJH$x^#2e*(ypM|B+jxsz5ns%sFNE-Kr&O zf8O%sh<=Dy+LsDP6HFQSZ)s74Q9@F|BD=CWiQEo@s|?T*U~h`r`r10JQZFq+Xhb?V zE;uZk^gTlDwm`6_02ri{hCWnJ^$vK=)5Dnaoj~SO2?cl)D&|p+(G1QJiGE*Az0f zmvJQGhl+eCU>8<35Q75GoUOOc)@CA6Y>yi6U6Gtqv~>VXyecoHWv{ca;OPrF)HOBb zjf`+kysAtFx4tCw1yf11DoaPMikK;O z2vKazP`-(C*1l4J3F|lsYX%h!Y02CqMFP0xyo7}tU^;yzu+ZbXB9MqAm({X)jY8wo z-mKrU*xy8=VH?jP?j{tujn7xY<~X7X0IH&!D;-*!ORn{)Q`UD3JqxdZRAj+A>8YIX znNG$W_~|2DO*nD##^&IUBP`>71P_@}Rf^sUYW5b-buUS1K&L<`&^bMHDxGyTAn*?v zIAhQnAeEKTJU13b1pXKf9Sm!f{V0%@B2Z2sL&-r;!cV$5F)C~RMTPuQ2oUpn!Fu|4 z%q-1rl^NGKC2278^N_C7N4#5x%tn9|sL2)e1 zuidv69=351Ly?oA`KMT_RR?+~Uee(@8^i#xkHA0(`);#A&;4AI zFkeeh+dhX#a!F9ozv&Aa3$p%}Wc@7;ko}BD6&WY>zeA5-kx#~?(Bw(}2#S4;Su?%V z{9K!>VJqWM$X*DzXH)I`I2>5PRD}m1q;ET4rk+~@c?AIlZBwvymbH_2J}sskU!GKT zRJ>W*4zh&Lt5c;1gotZGw=MYShoe*;%p;GB>yI5i=dUf%CJEOOcGxp7kt-hg(98MM z&%!# zgm12}^96AQD$$tJbaqV$8~HVbd+ACsXZo|>RFd$GlpmLyA+_WTR%vfU*@!_|AXp3v z^EhGIRAcUYf*B45iTT@fnMDF13AEw|EJ3LZDO6KsEkgZ8#7%~^%Tz5tsGm!z=o2d7 znTiQ=N(W^vRVzfuQ_lLhC(Q&j^C1wB{-Wgs9SYRqb{B4S7J4#SppLoY`o!Npa>_4JQn<*{Z4-X+pThxe5L@A+?DLP89}Wwk&M zq%1nXsRm<& z$C`P@u;Pu8}-)GdO_Dq-ej{(NKV-;|5HtPgRbv+@+i*BHa~;`(m#aJ zUZ=0cKHWhmvO`VnM*>C8=ajnHAyW+S+)>-}&r<8`jQVN?(HmuH4`*n>bli#>!s*3= zaL=K?vYx}p>QN;3BCE5)sA23r?5UM$u*YWN{4W?8Hm3jKOj84RY+VnUwo#( zFGov7I6a~hHSHDmORXj|Sm%TflXYa0=B;=}j4gnw1sVdL29)9`wI)OqhgMX5HJ3`P z4|L{luRC)7yp1;9#tWnObnb?g6J)+uSGOLel=|R3LJK~svC+2(u+0k(Bj@K-YA*_k z*JONlF~HnUuDhm$X;*vC*t_zg4eXnzfbAY`k?|183`^bMc?@kx zfgXv%(ESDE#<{Xe&#d5OHeq=!bakL(!o@iQeXI;U?buy_h+-h{^G8i*aVhH0UP-i@}p>>4m(ei?Qq|=Q{u-k z^|T2hAznirL(*Z2K1ve!_700XoGU)yZ`mF28k7Q10Al?V^E+y|nG)z+vXLkmkUgPmiJVD6n$<~Ub_JwM&|#V^RMwHb#drJ6o;7D#m}L3ChYa& zx%L2kGmW_4vyGP`eLm#PFn9@0Ch-uXo~w;5ritM^vEoC|KZZ`euuJm9~WS6*h=Sr1go^La~<>K~<|OC9#D^zyqX<-*!V5wD7eA zd~aMe*+AoCcxbh;!M+Tl-0&*12xWPoe=_nd{uGg_K$_D|gaj`sfFwAk&jf>mr>Ig* z7Iw~@mf59|M@2fVFR`DE`s;7PGV&!y?5SeT!(9kBvnm77 zPlOuwYTaes`O*Bl?H$T=^oEsxwhwmfCdV}fC^%r6rqWohkt3mgwUb0fi+zjE*S!dp zy4VvG34MeZmPGbowROi6m8K4rwc4*)*w~nqusF?vLC{K*5G~9az*Va}ji}Bq1&<3; zjP6d1S}8@d!m}Ze19`d5u*Wqifer!aG$<7LNys=Fc~)t#_7${2MyljH~$C%ku5eXLHJ%BIlp#6nT53fHu3#9}!RG(_m za6(EvvNkdn_}RlEUBC5J_1RFY;Q(y3U!l+`@P%~HnfuZs=ak6osk9Qabk92!>opS# ziAUEx225{jA#2LQW=3dVwqJ}HUrYsnP4SCm;O@Ct6KIEo|B)2zWhtU_^Z)h|SCmX- zeUKV=JBq?WvLaSSg)H|nF5m&giKMlOU*ulD#cd;g_9#sNjtKU}fcqvf3~q7cOftij ziPls3*tQg9lVUt%bUa-_wi ztc?BUJf48NAeR|R#76wwKw+<<+{C^DH1O+3#@0 zz{GOT&VcoNF4^Z6K%Gy2{+u>k3K>62Cba>~eb{#;HpHU7O_fy) z1Q}}r$n6ckXL=(zo(=%YYg$~Bn7#=~ejIwCbZFVEOEF=-`C^=s@h8-;zeQf4G8I_D zhuv3a`9|-?vp~Yj`_WM-(;eLFAovOXe-n3@@YiR^WKe?k338D&vs@FaKXlW>k7+%R zfBN3w67_u|zp&%s;vLS&pQ$f?I?=vd;y-~+G}%<;D=BqH4l}^AQC2=ARwI_5)qdKz zwzXBBYh-V1H*GNdmFMqSx^YcK|{J&FdO zw*l6Q3-z3E4Q&8bU<>a3&F7)OL@*)4554M3Q2z>@>(8_ZjN+I@9 z%@bC%*))9r7^=~|4ne7&lK0&jZH`34{SJPkHcm9%$Tc9c8`a@|rifn>^cGRDX|&rx*Snpjz8BmZ_ZWjm5Lr49Al zVB@uFphZ&|YRSDuK21D@`vtz(GABn6er@pJD(ma??J~~rXGbj8umOr^#rncRCC0Vb zaLEl7k`++Y07&xEcKf56OhpujBu1!h%FgoNI1v=R-K%0C zEt#TSW;;e1j((=w(;);#;V=!!0W2gKt}vG^qBDNit3Y$t7F+eYJwZQHhO+g8)0vF&8%Uu&;x?Sp+Xb1-u{^Ue3p^W67{ zV6Hw1kl;DJN0Sq>^Nk>#W22)`S;Zd2?!~~oxa?xvK4s#~UP1$Qu%sGM+`zqiOukT+ z#K<7~g`A+^M|{F;BexI9khEct>Fi&FK18|;0jcaBU#)mIC8CpjG|BHxXb3Mq^MErx zyz5ETT?8E3?W}*`%z{zJIpol8tthbNM%hkbz7pm}S;@g^ z&Guj_(~th$D>HBaieJ z#$*CFR5Dldg$3W~RhNeia&r{|J1dmjNt;(YhV8+8ZFPM2Xi{b5cu2g)HB;EO+)>Oa z03@u`YbHqW9WRT}R4zUcEnoaD#t1%pnz=ayL2p~BT!z4fF|3R5DGgUvRr5&Bf1(yk zZF}F7V{dt}hawPL5V!oIe7G913hoxhe2mLkxto<%TZ-Y!Fzu4fjfw2(32CrET6W;2 zQJ6!94>I^+KtD6`)2`29#CHZxTjv4lt=4E6ahrxVZ^H!~}K{pbIk@DZ4l;mMXs%YIXu3d){ z|FV%gx+)8MQa2oSn-Lr&tzCI1M9}fS*-PJno%Svkhpj6(;0{!vuim$n*K{BL0Z@2l zc;0IaazteV+=Ng%b^h&VMkvU`TtAyijFnsTq>)y=>p*a0G!q}u2ltBmmCvye zI4|+h!~8k*^T`#)Qzp>0_mxEh#z|+jFI{Wx@Ztr~F^s21GU7hX@b(G*WnGoVNdin` zYTQOp$)=G~N&Bb62eaCdW9#IjzaD2J;ditrQ<32`a_{$XtTML-zRKoiPTAUyFh1d#yNUqaASbb#P9H1+`qa!#w8qY>YW@ z(XyJ<4>hz`Xw?rt?Pr-gLMTtTtV5;|XzvkOdz;njt=AL@@@z(%?N9Vh9J2?*$P?A> zLfTGslc}$0dwuLk^EnylfN8WBY&t+YJ-Huq1ZoF_JW<|!$NYX~&WTBXJi&~$hoOQO z?69ATuj+<*y2V}ceA`VwPw<>YLNvK)Q_S`s@9+zk!E7f)lG66(HhtR)pX!g<-e+;? zc62d~oaooKXoy5M<))1d^WUf1OXOlLj#1PnH9M9}kI1vAj1MzvfQ#li2KB8}7}^K1 zmzXG*Jk<>Z4lQF!pzY!bF<7^*I}e9Zn)@tAD3H z=+DTW+h3EoKa>A3#D%NeX#DIV;e@AKUOk};KQHbXmmF~oY*H(n;boO*XNg9k;D_=3 zJsTn9r;zqVp1n*e0I&tK_{k2FD4SU_hiFOD%82}2A=tx)2or=d%@W~FHcDpr%)HS# z^QfD^G7sJsd%OJaBoD`0B{^h-geSX!9;jecrL^uNVr$s$i(<#vOr1@oN--BzNgb+> z57{ToG5=J(D@n0vs5DR_2fFEpJwVArc>=R}z38@GodhZ`0l|5`O!J~J?`SGU6N9Y{ zs?$GH=(bC^x)V!$YAX@X4WgCt)R9?f-ZwDE95KAhQt1B2g)HIm)Dit6M*9Gxq50p!r)GLD~ zdCgeKe}_%3){;@LOmaCp#puv~-zKTXs8%tY6~DR~Y#pUCv$JJ8)7|`K@yXFieKWWK z#FT@ZA|nwx6Tsfw4Lxn%3eTFCVZuv&D{{!LYoLus1VEvi>GoZadf0Cr4T4(F^d~hx z#}Zv6EJX*tg)v{c6YemsabC_I?=#x&ZJ+M2x5yU6D4$i&9wDke|HEUE7b((_>+Jdk zj$+ixf&jE#>!SZ0W{?xMNg9a&-U!ZV&0Ud$iOgpm%zh+Rxc>IO9uUXyF97wuI<)dJ z$Sm$akYXA^B$B1#Z6yewMF0OgN$FSt=Ulyy~Y_~3B3a+!NTtQ$Pq@m8}%{lnPB zhUaFJjNg1Y=YR96>T5jNFpTo>VTb?huJhkpeQxIeAsYTaKtpzJ=Kqp&7intP{gXiY z>KB@yB_On7F6G51>SL!;WavN8tX-2KOY4uuud7N(YK5F{>-8ANQ6{Zthkb(wXHY2{ zB-8jV@FUvwcZd1oUXbq}^KY(Uo0PP$4N_;}54(TKFuw2+vmH$_n{?l=mtIxRx-Xf0 zf(0MVk^emqb=iFlS6)cR09TDvTnpEgkgR#u$OY@QyY|g@yf?GTj*f}0AGE0#be(g3 zAs_ZM{>NW?%7r4i00JO5T(#@;=74>foZ%gk`2OK}WMhI~yP~?aB&kQ-;qLEN5enH8 z_nVuSsL+%^<1 zPGgBuq6PB|O`UXlGA5U|NJZ)9rYB*q9px3lUc3pI#HM`%De$EY6u-uBW#7LO9@2Qd ziCh~d1^ozTs6q)l1H^E|hqG#6T%c&>ozLj zaei1y_aJu_hL&zU1G6oS7{6a49TO5ON3dNcbGGx<^o-o30IDS|UV50a6{jq*5E+xk z1DHsn5<15bpWIWUtc1%0cA|(p0flLkIp@rV>r>Y`|(Jb$o13i`$T2Cw_Bd{f~YjsgPfxEC1W{ z!|&Rjpm*`$CNRBqc#|}l%Kn{4v7bVe$}Ffq@mUkz`EAYYB`L6mP2#sB1r-ns&~&$CC-WdLD=Vq)04gC~wp4g6T~RkCF5+~j_4y#` z1uCioqYH!#ITuV-XxBbr_pQA{9-TmWMsTvdW4^`J(NLCO5$WYHl%ZwYneWDfo#7te z?hEK*nXumqHBp)|RCdn^S$Lld;&J{|%1+Q2y6A7^f87g~2yh0ti(T=lXLbMs4ccc5EF`KublCd0*rg5#43jfO9ASlKRG}qAy ztQ%p~B3fZJR-Rhgo?MU&T|Dlzx6r#p>`HJ)Ta~us0PgI4NkM6-cxi7H*T3vwk8<1a zkH5va4LugGj<)4YavH3D$48MWGD2}t#)}y$)O}(l#^e0X9?=T zR$TJNg1?m-<`Vb8z!<6-zVBGdi5n3!zr#mzcSCSZ3_+S=UxDw%M_%12PMY|G$(OW!0 z+ce1FRKWmJmL1jv8%hsyglo(IdkL^NV0E@EyTUO0W+NC^!ui@lb=G%p)gSluVdYT; zJOp#%>GQ_ifNTe5pla&<*#PY9As)UAgczo!{3P}?%f}s23 zT=l?-|Km}Ie?&m>jO-&`u@|;5z+LjKZ(s@PpIxMSD6_-tuwn@u`T_geYPTQ~0%d9Z zYy?_T6o#7#!uIY%s?fY|gjqBgdQXMF;EUGqH8h#&f})!Lbv#v*$u9_Uwd*S?P^cjj zq|0LPNN;^U#m8qc^heH4VfuAs6EjlrkkQO5^K~1!MmY+Fxeyt|A#jy-wLFBw)Im)+v4hf} z3l$iyZV|EyV(_xRY@5`_fgHruGz7LZYjH>;OHgNiRXx0B44c#p zZZX&(_b*w^zYss2YaRD-Nc3Tic+1yBH4ZFyeUVpU!W}wKq zCnd6kjIzx=E>^QQ(FLh!2RI^z5XL+VX@`~@wPnOli9%nXH@e7B?DM7T!O}e9KpR#< z50zgOJtj_exnWi3aOjA}a^pVY#Ic1948-aJNmHo?8C%F&)t$Np2~TSMQO7VJ57z^f zO9#`g1IJM|{o*loXnD-n$-07GMA3BmWbu*4A5ZyRsG}2Zpj(8Vs@stW zwypo>%!riu14^7ggtb=LNUiCMH@ja!_kvH=RcG8it!Xceejt|kKc1CkF0LTsoS6Jk zd)3YFw487H-t!^W!#1Dc^X*~Ia3g(T^=IJgaz{?*DZ@GoOw+{AzG+@t%Q35cx)8t&Y^DEQRnL7I~oB07j$_vWaB)F<}ev zTV4Ow60lY?(`&1}>O<+KA`qLbS@RPf`(bwU+s=UQo^A^BfvE|dJpJ~cP!PCe(TcUB zovhAzWxU{Soy@)7ceR4;7b4rfLx|jWCiTM%h7(LkyHlj^Zip%)y2s3%*1uO$TH$|t z(_gTzDp{Udd<7kZ^TzdO_Upz=RxFW-d4C8q)dTDp@dYRMCU|y_*8=dcf~!)~1%^)Z zPh0VV!u!t0r4R#HWG#fxr|5kYe$u6rDcwIr6SU^eN|?%zuh0Xh!oS}*c8Y;mM2t8L zexwAQZ07l{c+^}T<(b6|=N?SCk+c2#1PWy zCj&I5hRjtE^fKRE*iK(kqYg6enfK%n1q}RIY2m9AO}v{r~1 zT=O`BCGyB8>2rh}CZ489m0z$okq@E!EX+k$@;I8b9@9|W)|}+&4wUW#k7p<<&@;B_ zevlq%?573&k^vddkPg#z6h@Kl6~o5eCIEOsTWlpi+Pt{LG9wsZQl#@#S06+8(=-K* z@>HX77_75UnDk(q@4rK_+q81(lv~o;IyYwWL{;V;+k{9|G|etQ`K6d6Jab7 zOmHK5|6a*_Aau=7f9auE`^J^=GUm`gB8)-9(#KbPwrI*VSZ={xBORz9BJ>Dh-vY!1 z;0B&<@;p_o)h9z7)T-RdJcG49M9cI#*vz>-fRD}jzFUOHeVUGxQ{(*j1_jf*Q3KsC zmo45e6}&DyY^4^w+AECcW)1oT+TV0NrP&AqU)^!IzGhZ8d74_l8+K#SXrgusa5KwB zULWmAPWPS+uH3zhmmfDh)-C3+7Xo0njTnn-9;p^s#nnu=WC}L@fLKUE%QCdsm3Pk_ z|H`L{TYk07z|<%xK?PB(EKf)8bk~?ydievl%{QCAA-j(1+FJYK3#`8d*g3j^D%Y|p za`8&i`zbS2h(`d4`Y(Sb=2Mkvc9!YhNSYHD`~gTD0SMDsUZih$d|boDvcL%(W9lVL zr=Bu?)n1F*93nA=T(kRF@EIq~mg(v~MAHZh5WqGz-$RB|J1_f;{twOn)BXH?1v6P% zDPN4`LvN|3N3sL0SZC#PjM4J0)_cW6I_goxX7+z0vuPH$jZU-cj;tPE!H zs!;QleEVmMQdtWvyh(Gef`oJqG)ek6o|^{D2SX1&R% zwq_y=6DA22iw4O;FC{?_btFb%jE5K{_90MGo~Ar=)fyB(S=>?my6KVaxW@mjDE^>Q z8bU8~xHpBuu>ZGgp}K84Elsv$e0b9j8Ox?rp)wb2%afu?68n=~y7(6qbj^8Qxg+VO z^jwSBQ(O!75nz(N*Z^ia)Bxr|lB+L!^%uJ6j{tOmPy%F*#W4zBE0!wzq;k0gkg^3` z^6#vBA%;E;?SU(S+Wv^g5crro2b#3j>??+(Tq{2e3L4y1t~H6VN`i|2S%FlT1zfX) zyN9#SqueGM7)`?ABd9DiF_Zk9f@=A-73EKCd5pN$TnS)%Vm?<+R^wRBYi~(4Y2?Ap zt1JPS!G%~*;Tj=)luF;f3xf?cJmED?9$3w?mefoQiq{5%v?AOV3X{o7fhLz_)TyGc zuo~pEP%se*w0hiY%1-XuIZF+!up5l6KvCRt;8+n=X;^Jpf)KZP*6~^271z!gh(^@( z1&8nHivoRW6}-r5n+De0EV*GNDTxM#9-Er+u^rGi()Z>`=wAM?t7xt{SKXmbGWdn2 zDs$O|ZQdvn>1!df)V_T$a`P1fB#3*mxQWId`lGp%(=dkimP2E{uI58D9C#EJM7x%Q z!(6dlegaf9?Gs<)DAkUNj53G1rm$_%8#$5NYhZDyN~_}j-I+D+PkeUaeS@BlIAEd< zz2K=@`*8N2!8N1SVh3PqQKF-v_f4lUte_qpwdLuNEA3lT@&y*(cb#XPKVNSwq+|@f zauMZRxMtio{CbQL^jhWT-p;z<4D&K*5?IeZpRdxg@HXxWI^(^7aTmIIthWI%FMbEt z0vrU#n@#-SUPEj-+CU6$H+n#3mS=o?u15~`x5o>5lusAqIX#=5w-1j+Ih-}RIon%4Qr}Gb zc_GdKB({=6H!cl3{0owXTapGD>5KzV0L}Y{A02lo0~`E92Br2&deYHpYYgT&ZQDes zf#;OT1CFYxZ9faW2gtJyT*XpcNicgp`YnB3DPGj%Dz@nA{0%inVA9xHur>^z%tha% z3~`!&TGFy3d^<^|jYm+eR1HFyf%NS^99+obrY@f_*i>4QUB`i1rE0c=ZxZ^|fUqFT ziPH*B7tUQiUI8}wM{(W!?qXtqT%~@BvyL1#WJd(Ew(;-}@l?hHqq2TKQsn5NyP-Co z9s^q+B-upI$snevRb1k_+hNJGzZCogEo#LlNo%t9nH}3#y_9?>x*4rLrje1_1nq@HXFG6WX5lpqFYkv#OYh&FAC^uRs^ho!1NQ~@UFbK^ zP6%_^js71{c8$-~k*r49c$XY|s|c%vGoucgf~x{sREh5$Aw4!NeCiwj9=i4q1+NS0 zO~qH2aa!S2;v(UpY~>fI{`wQY&myBN*s;fvkE`rJ2F-~|oTIdEn36HT021U;xXuI7 zoRe%Y!`lS~(0x+07oPC?d|@hd!BD<|=hiGet}upYylkn}!OtuC!cA~f5yCn|=^n}Y zK`gY)OO!Ad{3}FDkx$g)GJX@Tr-**;yCAretkwPQQp^I$@kw8=fu>C!9KE24Dod0~ z?j&)mAE>#%G^xynaN!0x0T5$351l&|2j6DF&Kt+I2PHzB?Rx+}TQ4m_FTLVd3CH6Q z`hiJFMo!U7n#OvMLo!jZZoz_Zjhz#NVmwIg4vyFacgN1vEac9^sckhR<7M(ci6yR%vHos!DWUQOW2FgC4&H`YgXOq~=~ zZP+3>okOMMMw*fTbX+vuVu03(m z^z-?5A=?+6J&-+kTU&NDg^7J`O59TZ89qHx{tozueG8n7{&1g&;jTKrRh(GYXO|V9 z%3CgpFPhb^e(8AUX5|d9ceZUyC}Vi}J2bw(Kj>kT!vCG})VDlfz%XHnprn(Hqc}cj zhD2IgaVR}OwMgy__XZHkv!M!zTO4qyHP;n&_)oe!3d{J_!2s8X!i->vk-Qe`i{f;_ zN?wn017EF2@S0tl@!XtcXaH%iwkq9Qu+cI^R-Cy!FKB->i=F5ypXAlcL9nEVB8g0X z|0S3hdP$Ct^5NxFMgm}{V3!G`zvXBfN1-cKBx%uD)lxOi2dItKe=99TvzsU|l*eMM za`pST7ROhj7*HKgv{>TMML9;q=gw6TRWS=~Be!qhYC@COD=w?dY|vRr)h=l+?Qu>8 z$Z##`6db)5RGasn0g|z-lVV7W^}AMoxH=A!$P!sGO)_(I?70a?x=AIX!ME)>IXgGv ztiOCrW8%3lKtT1i0`z);29kakAx^RdZQ^{J$o#KDRKoTJlJYv9I0d0jA*-{o8CuOj z_<8dwzt2H|Kk;Dz>INNi_|5~!2wzCiw^FYcg7M1b@MwpB(CbU8u4x)=6$qP=(KzQ} zF;B?_;!B&4;OgF1J2=9$39?B+85P{<(rHSAZCl9>fIV-%J$)EGvSp_UOiI~b#$YKg zuJ2-G5yF?s)cvg#V>-XTkS#)%wGT&yU{3V|@dNlsLZqWs4XefqroR}Xq#d4wIp)m` zkW83yhwyDboF?=h5j947?OYdx3^2!{qO&n+PNNtocjp5BT!8(miX{Eg)F)11B_y{= z-9+ki0isU6Tay@K*6qcJtEQG`Ye$Qo9t(z^3cHSUO7db#ndRLP15b0XX;-mCijYRN?!3;O29ly4#o<@A?dxUTSKk>Vo*qlN z5ogxesG8Ksm8c8S!yuoS#7)l?Tsy6$I6^!HH8%)7f}Y_Az2!1jC}PI2@_c z=UlEyZDm-9$Y2JqXv1QW#?QwyeN-CGpm5UFe=YpK!8&$ z*j(6nTCoTFNN`DJ_GMthzjpt@M2;2vcd@VbP(S}*j{R=Kt%zm6>rZ) zM06T$1sWDS!|{4h2U5CF9fE!ibo;(EQ#J#|u?keII7t}{@vGbHWrgP2RCBgPjCZlQ zVan<_1hQt>Tcn0IOQsZm#$wIkn?fN4BdB_YrXZEPp{EtmC(Uw#t1wg@#tMHB<7Z&?(dle4&ud zmHLgjlB-kt2iaSe{QiFRXdREd*tgt5TW8s+(xa|7tS{4$`dAfOg9>1;e~D!(8qMq1 zP)?7)+qsL!A-fa&1L{=);7P#fBjRj@sk)BSE@SqbvIdH>7jsfxKqhPnqx^eO%qQnH z{s{%F>(4I)G6o+sg>J$cgGfzgzJkosJcyL?S;EJir%pIC^8S^+KoT}G^TML><lt zp9jlD%}TdWGN3Kf-?NN5IP-{{$V-Q8hUNf{k%|;V@Z_?#Kvr)7MM)!6WQRR9L^Pj+ z=6Z<3UGqg1hNXx<0ni3;(l+l?$QvWOn^F>FVbe&^ekP=1MxDx_Tm-J1ix~7#V01qY z=eoA%;tvWBy`wgp^~Y8TIn={~H6xnmY2}HUs>FHKcysR)_x0SQ!9eNZ3qJMWq~QYR z*&O1tL!CqwrKV$nB`dx0jj0AOmTc3te5JkzTyPx>l|zY(qToTtk@tkQG8ziLa`ZRL$kXge85ge~|tTNH%P>fnml8#!vNe!@TcFHk(iTdA3 z%0$sQ?j~cy5w9s691I5Z1R-Tao{?U`Sg5j-M_Jy$YD+F-PKQ%^#NfV*5jr9V)LHJ& zR4Tg4a)|N&o_VQm@$EQ{~f(u&(8$p7)FiUrP<%#xzFpSdZB1u=`ENV{~lbvMVl_?j{jf(5mrF$qVvD7)zU0uKJ=Sh^+< z>r5GRBq6Sp%I2?v3^$Z+imD>jSA^hL*9OKRkCLX~|-1 z3NQ5_c`>6-&u&lGv1kR+=rTAi{!-$ps)LqZiGdE_Ggr9`&N#B%F5HHpg=mnH82f=N zGGXyxcL>U=54i!TQ%PKl>z)gPN>>sDIDox5BI+QE9YRcaK1o;u0%UDre~cgO5*R-i z1l10zm^ljS8>m=`lK+msRU%00tS&N9(5=T+X=@5d&yQJV1yn*!dl=(XKOBNi^}mvR zWgIv#S-e5&vwjU|l#<*7+AMuStT>1}j(&YecbbPau;Wogl#&?CD+7^M1X5so{l58llAwiP3L*$!TsNc!15D-38WQ?6 zG&CcjlSF2m0WjpEn7=q*W;;1yiyT3c`gs9B2-75OMKqrlj>VT`T$mS^O4a?t!I){F z(;lSE;e|vxKj_K>k7Dm0VGa#9_*H}s)Pbk=r_~{)LgQa%WTdl2ERB&gFkmc?;TFID zQNwWg7nCcW9E(!C-#>edRg2a>oLUSCl4qXU1*v07@c6%71qItXf!fS>Qe(KdUN5&y zh8lk?ND`C1*~iox)>5`B)TYL36Se(8?K`xTYrB=}wNN>w|A1n*V+Mg#I+(L^Gnx(= zvJ)$^Qe)}9@3qo5h$}q3Tm$Fc?!2@kCQw0;d>#P?aVh_fLJ@*fwe*u$@cS8)Kg9`l zU4!0ReL#c-Xod5BoYfH@buEK32PhIVeoj+aja**%?z1C!YhxrRtH#5F{AQ&HxIOEp zFsMF6zC}>}W6Yl_uWRbwHfU)+*UiN}kvwF{GM0P_cl^3-rnYdMu?u8t>!Tpjvql4c zLVJ+vMm*a1cBTtyQPyBCiN{*2qxX%H;RHMzYEh>RRWOT6>n?P(3a_Kvw&IqrQtzKD z;cAGa0nbzSUJg#ujQtc)-_}-+DfsxjZ-XkEpvYOd8K&WJ$OP$0cN<56vs-NLw*fN) z_P30g#ChaZuOX@dQvj8yB=2$~!ZjxU;+l}t{pzRwt>V=Yv~BIP9Ocj-RL+{ZXL3}j zWpMWa)?8f!HdyZp=2#nvTl;FQ-H;J!=7-$FZ7Z-WOW{Y9;xm@0x;Dk9ITt}Y|G%s| zY1X@f$bJU~%@guL4j>LqYww;o89`oEQRpC3pdf!lCK$`t*YRn>@%XFWOorQwRod!{pwS9E?aoW6qqe{wgA*EnSk!88#mS47O2#!82d zi=8IN(h>&Jqceg0V~9m(#DfsUQG%`Mi12;+DZe~B!M7f$+xj$!`_m%_ufJ&{o@SD% z8Ae)%`#zVp{(^rBecnDNn(UomjlWLBH>T<`bXo5mvpJZX z_sQx;pvuN4$hx(4hl2#t+M8#zwU`tZdUN3CtI_U^>&)02=f zY$w%KR`XTIT|<&|J^V&8_M;|I%X%{BElU0-+3J^AV!1DqRg)(M#syl-1}(=P)e?rt zT>nFrXj>?9d9lBG&keDz&Ie?4lt|cr7CJCC=9Jn4aI7!f=l`s8tR$?QoGdA^5s+Bm zTs)j9J8BR#K)cDWOs!=OxIUc#*}oUIUnf3cxZl6w(|0Z|kovZ^w)k5+oW_n@UNbMQ z-@5`phqQTD)~znzF)(oB{L!*<zZ=ogzA z8wXL6qc=mJ@TIS*M;!5iwSf{u7lXKqjvz~Vg0PVT=*18uCg&Zgw#2tNn)^9o+kk7HU;_=RNt z%=Qlt_4Om<+Sx$F=BE@vkFK?P!AyZ9axQ~_=azLBvGfk(#0FXW#CcNlz)8O{=_wZ@B0&oMN;yX_nB}w za23;0S^DMK2==E&AIQN2kpnTy?Fv}SY5W|BdJDkav9lzu9)93d1t>w3(s_B;9od*$ zTU%!{xOuqeH&!!dH#9$n6xS9fL3a#|>_DFYwvlAb&pMy}iJrs6WI}U{GA^TRsy+AO6t3eilA+xsYbWj5jjyci!m$ zAHRq0sE!*ZU^>=KkuT`hgg+$?gz8AOXvhlP^qZmARRPsipC(S(76> z8Vh6VUd|)w+jC3TOs9g^5aPzIpqY_pAXJR1AbTUR;hOlc8|oU~wXVMJ3j(MOF#o{&JfICzOEp- z6?rrRIlln;ablJQ;%iCx&hOSX)}X^L$w)#>Y(_gH@RcC}-E1g3=9dt6H%}$IJ*f2P z(7mIRv)4wWf}C(?dUKRjQ8pmjpJ)K{bIZwRa?VYSyv%)_Xq0s&!xo5ykdZn7#e+W* zW03pyX%_RY$jI_dR+h(`j$|L;x&x~2swU~TJ~l1@xkX=~^v8!_<`lu;q_&>~oN07$ z@#o1&&%gNS7h+K+a~nh0H}$w4Fc=7u`(yYut=Eqz=;Q{S{G z#GSr7mT?K8m7ZXTULiZ1y{ErmwJwf#U^pAA10NbvuACqNJH&H6D15%cM|Ojt`Y0dx z4q!N99}?OiP;;LoNLU~N|Nb12hfe}1-^hK1|Jn^A=qtV9dq2^L|)G{+j;qxwb+MGO)389Yfm7diFe z`c%;5Q2!b7E(uHZ=}XL!`T1bMUVvr@={bE1@SXnLnR+m|&_8%V z4vE=*-{NXU{&K85^a6Je0Tl%iQ-TBnv@S2uZ>5XR#8Ds2pOVgBEC*Yl?{ga+4qy!E zndTkt;TrqgiU$lRB|Y)sbJNr2M$r2O<&5tM&!@D3CLBQ|+G6DAk(C}`;kgFF7|5yn zyuw1Oceu|rddHFGHD%-kL4O@7_r6OQI_>d7{B$vRcaicrI{@|sTOdJToFVc08K*XI zZXi;>Ekk2^MjR+43&lFjB*#74-KJ>ty4kW96ocr6vrQ~jY`>{x{bVQ zV3z_3kY3GRT6W=dIjt-&w6z7745v{M)U@33WimCpIkX)|TJs8WF4ReB1W7a$gcuyBc` zouB=jj2_5B25GUeAWGabaJ9AP^pQ%Sfx^oQFy$vlvU}H23!YRN#to z`7sBg1rAhaa`9Q{{cm2aDD|Gwqk^|Hd1TIWzxS|+2%&qc*EFeD;Alv8k)GYU#z?R+ z$^`dhi}(3ojpXzD=YgZQHo8R;TT0a@q605r3 z94Y7^zvGn4o|&D=XlRM_8yz@Ouj`fPVeH);Ssmx{XJ)9WCOWF!-}p9H0bt06)&4>4 z+`CNg#@|t|MU?a5*6NykopXGB(Cxbj&vv5VKM{e!#Vv6yS;Zb)n-ia8YhimUu;8f- zh0nx2JG{ZWfWBr- zQLeU=i*_(rJF`=vZs+2#pSh%MHfO$`O3!Q)TpxPk)8Nek_J-*)YHChCr8QbK^6pBw zSIU0tdeUqbVUQ<^_Aa=8y}7|je!q(E<-1-6XPl-gq+(nnHq8n;=>o%(D6vEeA)EH! zg8N}gIsi`M&Ibr`RU2sqd-=C1fzvFjw@n#*#5odr5|DjErPcCEe^_DZ)DB0n!y^YL zk=7yzQ{MoY=YR+N`d~Q}y5BQ1SFiiCkxSIymQcm0FwxA#oKA{si$TnrV%x-AhM4S> z(PIB0?BJUAFI(#tF9DHY-rXU3n6s{QKT4?(I@}L;UJCi$oeIx! zB21=@!nk%u$hjb$G2F3)eX0ujf6rEbNK{&vM?J#iS~Nucnw;W|Bwh_3=LQ+;C zt-y-wE^qBe)jf7q@7p?Q-x|kW#y*D$b>+-)2T|dHus%swbOZ)MmVVx+gvq+-84B-d zyZBzQQHh3a2gFGo zcWkcxsCt&(KXNyv(8$_mTWIc~nrz{Jipw?%t+;uSDzNDN{qjp=+2s-uYVb)80Sek@ z0|5_Ctdm(;$O34iS=O=}f{2p|VqbOCrp}8ZVqC4x<4LoM_sm|H%4g56x`&u@`To(@ zZh4zRZ8@r#-~ekG7fZ}mPnxqnT!8CRfkAm&gH%fJZKIb&+yV=S2We#5pncLJuH7vl z?QrnPyD{Wv1iGTG(#e?eOf+k*mRLmFkjnRry2=ySrv&Gj6l@CD#`;u_EJpj2tVf}@XGLmH zl+e5gIYks95mwi@4sC~jM=x6aH^KC^=N1P|i|}p|EuKXdkWJCwohKk94UPotkY{R~ zemJTGwiN?7j$1S8Dz>;UvJ{o9Pa$_V>HcQy5Jsrn4B3c9b`|MCU1)zjfBLW4@Z-sh z-nVOb3P4Kc(S6+gbP&QV+MHlZEOCRLM$EKi|V%q;-yVf+mavun<-+@qJmW3xCu+Nx7t zb&l#RpcD!mF3k0kkYT+weSnZAThW2fA!%x^EMd6pRJO79Zj{zPBq3dVvIc&#)+p+k zvDW_7>Hwxg@7J&#oP~@8$QbPBEs{x~VhGcyL=4DqTWa^GPkB^sk0aWIstMIxk;#6kNSHO@I1B=1(OBaVgu|LeC+S7d3k8 zAi|k%3P`}i9%x`CncSLW?bgvX)8B<)xT>@K&49WMM(V38&kRcsd)<^bWR-1bJ|aGr zS8`R?5E%&{sV2c#R>y@5&R_4nSXg|>hvn+g95aPg*Gp0tlI zUopZO!2a$}8pClvzYwR2w%8G~io4OSWvF*9K^OO}GG+VRt3?OuOcSaICvO){Vbjz3 zayn#nO>6GmZeViCn{rig4 z#=+H``-TTxpDugVNWwIQPWJM!diZWsQlV>8%|af}lHt!J%b4|?pLn!^#ofn$@hhET z%zyDwck%0OT1|}}`m`p017nJ1w&Yj>0(P3fVTPFDEIVYW|4}Y9wi#-cE*Z(}IW{0>uR(m)6W3)PzQ3zQH zrDN?hN|sD4NnV|Hjc{K=y@lm-=cI8rs5wcd6Rw4aWZgZBnHfHV?xW`(imYu5L|#=$ zIg-Ra^790HFTW^T{On&nX>yQqv<>?#IHZ3rB=5h$r$#=MPtUG|si3hK?4M+GFDUw1 z7x~jEw(4btBRwL0Ue!s(m__+*OLd;UhVP-l=H2(%-X$_fUu+fckLO!>qd$T%(FQc* zkQM}5e-&7OO9|+!8HVYFGzrKEAkVENk>g}y3I97#H>}Xrfh|(^K2QP`_MmJy<}-qL z4=Lp%WB50JzRIF$zP{k?G7TgC{RcYEE?pSaT-C?9`I3AbK|opx3eN!uSOkO2q_u}W zo4r24)8d2q5Z7wO(RiJROzmg0M44o|%sRzkL{Yj0E6gUAJC*G?iVVUv@C!OgjJ4B) zT8JJbK<$pdhdQb{7TOKMZ|QI)@d9f}I>KWM8GhtN@jaUS$ux}n*vV}Yhfxia=1Az@ zWS(FwscEKSn{H~v5~P8{2oITwnD!Iq!PrsdiBj631rpt%NUg;|Njr?cVDluL`Lq}l zoGdWzAG|uQ1?s{TD|>nb976>bi`6f^Pj#kNJMbu5Q%U7Kjiu*Bi#14#usXk#o@M8D zl9-dXHcgLjF1<$8fu>dGMlwag6y9WO<^od zOm;z|1n7(XPIeh5J>c0oxR92&KWQBWYZcwb^MW};x^rw=#DoKOq|p#=C= z3gt4>%ww}f0v^@?#6Jm*p&DD%^~CSnT%$R?CCaCd5yntx-p9wdztgc?(x7m5hBDj! zJ{X#2#f!mMD|N7?ME=_c=SFg6Qm3ryp*%+zX##)4McqHXl>2>K zy0c&n2~(Mbgtrs|R==R%szsCTqDiBIZ{ostLIKC&RA5J zZYHDirU4@!R?neSbvkYV?{83mh$dD=;FJ16*tySaKWG_Bic2q!w!9@5R%bj2M<=B43y1pfhzoE998&W=YB4?4Q<% zr%PIf_^s@xz0Lv?e8Bh2*vw_h3t5@uR2*!63m^opU z9{8#KBug3ngSxrl!1#owiDw`lNOt{T2zBQ~%YmOOPPc$4n^{-@Q^kij3e#{(1Op@S zk==s6%G6@5yTWWP^wk5?W?GT67ufq(^T{s^crj%Z`TMqc@VFcb{~n+I9{@T)#lNQ~n+CDwIsyCwZ;)JUE=C%I zq)!YILj~e@l6DG{2|nMqzObIT1sh8)XF{cruUKdcUE=fBWt5 z6OaSFae_Q-BvklR)xG+*N&h!q{OdAB5UwiOkp2Xul-9us+Khg3Qy=^^wp+jXs_3C> z#2SA$DQ8L%qnK?4-0&BHcjDc6=yUfOt)>KoU<-*AQB$xty^*>&&{6+qSDORdxMZSF zkvg9EFfWXOwZFuE+KkgbXZPQB9s)g`<)Ez3{3!KWc*lZP`pEFU-owY4_qTRUnwQZ>h3z8I^ zZ-7hOyZV*`VQSjiY73<5Tf=X!N&|Ld4Ce9A!}H#SKN=3eFlx+*`O4XXpl^^}EBb%o z3B3qJ!W_kX!tF+w;F@N3;=AT#UKI{i@%O6G*>J*3_cF;70;xKI^$dLq8ERt%JOw{laD^n?wVw&ql&?g@AFB&%mPy^?U6ge)u^Niy$tViYiXD1?T zpyn|NbzR5FebO9@)>dy8%S-VljH%)rwJ+!#T*WL`NbuZ4!5!}$j7Z96Wr zWfJUZUFE~g$9?!(EzYya}~e(G3#d|;mvFZ}8by>}K)0rEMgcNKKRm!A%z_p9=e zU#r=BK`MI7BurN41u3xgaEFx7i?s(1Y}M4yyOVm5~87KHG^2A_Tlond`IG5 zBteMp8@~_b_G8QiC)2|sNaknd3|Jk}EHcf+(D4+$%$hgw;{go-DE*+IszFCCk{9rM*E-8{bl||D!5aPJ3yoV_;+e&{FLz9fB4nJbv1Zo|G zzdHu&1Xr8zTH^EA=(lGjOjL6s&0v*@&g>6xpTQ1_PZ^3WV=2T}>n?a2L|2Cs;TNl$ z6#@#TB_H$4qN`6i6R!G$Wi9C|CGn|Ajkybot=iN!?cg1-x)jYkEeLV40?dkN-6NnX zSWiiasYb3q4McxU= zB%t^3=hZRPI^M=1IZS)1ev-GG{T2u3(Ky4V?zDo;IDE0p;^SJ~T_>Vwn_x!bJ3sXX z^O?EQSkh$f-JtFau}6KrF0lJzvfmkC(vP`=|D-6zxMF|iE~w&(60w$i%AlB`B-Ys-8#{(43 zQ<_TP!$Q0ak185g{Oq#IY4cX&d>#~Pq2y>#3P-UGpU(R*?ctZNvvc_+Zhd2%F+?#% zo94}eg5ZD6tlz^LZo9{W6OJ_`;wgL>4YxNrvy(u%fNNzJ8-TH_(nG+%uZ&CAF!b99 zosz|(F_Kp);sNSwiVFFCa2;d919R@-TX7AEsUOB0W>%K_0!G^HpmIeC|6ry(7~pE3 z4aGRn>vv5RMqaJgFo4(6`uCMebJ;r5u0>gZYVm)a+`Ci;k{mmA^z*gPIWO*{D#xYT zQ(jB?wz#KDHC4wC9V9A1wZc7in1t)L8keuPK3{p z?E>sb6s)aLK;=fIlMRXz^sjzob@48{&oCt}hjnr2uuyJ}9L)B;31RzE7E2nqcDd9m zis*mh3jP}FMKfnaSd$lRRHIKjMq#eIgg(Vc1;5OL?yecF|Dps{A$X8rs96md_Nlx) za-E!iMRnx2=uR}q2zr!zA|NPQ;+1DSg7N57xB~w+3xlH170<_jT-R%V@UsjN>mqfh z5?hnxZmdFGH&w2gPX3BeDk@>^cE9RnjJAJ8;lv^IwJTT{+mJxUm^RHcC?u+uF6JtN zy{5;NbheX(r4%V8eNEa~2l?i3%7~K9d!-8Q+qPvnCbXr*c<#HIjz%VRV+b?-xGp-V zVU8beVdM8rj;+Hj9y8-?W>z0PaY7U>-XsSDgUvsjGR=4g0o4N^f9ibfQ*0B zgpm^YY^-uhtzQN9?ICR&{-?`~w$klgG9n=c^Ad9AwQH=%RU+Jf*g`ceBB*^602Ho@w z)4jmOdv6De(&*SJ9)=bk?_#C-B0PV^u_t?~19lz1z%S+NiYBLFkRF~geyN%Oll_wI z2Ln@<>pu9q_Cibt+B7z0z;i?-oL`;YxODXZOYzO|V$v0FRSB%Z5Z66NLat&UJ;#FK zc+PYr)|!aSa%;S&T@LE!5qa!MHqpuOc^pB*vnT1;$i zvbv`h;+j@m=H%iEYoJb!P7O;682PPip!`M8Lf?EJlsEkpbr_b1Gi}?WlmOoCI4gW# zhF)5$x(f-HG?GX!=kN4#AvI49`S``dy8RtX@| zaHt0o&x1-xM+FbkV7Pz4|2Soo3pHlJP-WlY*^FwTSEuY z%;&^1g{;+Zvn#B}v`=rcqs6{9z2GLzUf=@hD~gQIXZG8J?A?EveTr(+NJqVA`3%*; z9N!ZHCQnX>pYz3p8yR@ruIQRs1m53vYQFEeNV~z;6VoGqQq3g77?tTNeYI2kq6y8* zH#K?mUHC2fOEFx^s@ErNnuuJ}bf?NM+MuPL)gaaLSL4N8F<_RigOm6c6b6G@xU6)0 zbvru&CSOw%_Va&jPR>Kw+jUtHt*oUR!O75UG#+Gs(y>YO9Yu@u?67X0_BpK|!8;gR zpysN&3`81 z<^uF~BAKXo-o}%aBBaA>qA&ufyw={bogt*bFy%)PWxIHNhX!@-A-Z>T-8%9N4;j(j zr7sLZB4Ua#b0vO%B+xY;LapEO`-EU9+&C6=KKe~zxu^!bw}=s>L!b$yX061ovVrC~ z3Nm?b_%?r-FPL-r%?`%QkMyyhV44yfAFq%EE8Op72!sV)2x?W)hO|1U!zuEqsNQu0 z(_F8g1Q3{DEa%S%U(M#Y1@b-g!J|UHUA3v^&7Zu7#xcehmExtTpgHaY!?yr49(f_Y z!Z9a4k@^Cklcw`7CWpkYv`G)mb;5yjRtv2Dr^SEHP+ygC2R%&029LWDT(+@on@*5q z)u_$<1bSNzgS8tk(=iOb#^ftBx7EWbRcfhat38Nj#q!d^cr#Zj|He(+Ld1DSfAN#n z{@vED1 z%esHEnt{HRb6v*Qz$nSHct!VYj0vVDyW!iFJKfPq!V5{C%tj|`m1YwAohr8JNVcf< zkv{0z<1**7DVT0XbE)tbaXrxAxu|VvNpgEDh}M<>qsRo899wLWx6R*#bM1AY3Yiu~ zEpByiuJTJH8OIcy{x|`l!se@cUc`Y;p4oq>EV-ur#u(wEjnf_)s_Ij*EXN>tJfXV& zvZumBh7eVgLVI%=Mc4Q4D1jEvtjcYAW)4Y)`k}-&2b_eSqTIDKK zA6H@q^d1A6tD<&<1y~z9(paqAqMN*Ek!g5yXn5_vdIWuyH&YB^0NAl_ZCMDqyA^+E zSqN@QGXY~FdcE@@I)kxI+T!qklC?gz;OyPn>8Y&qM8htQDuJOekFa24TAot!DP7FL}` zF>HZN)p~bJQ?6u-Xk_E&s41wz5PvVqJ_z=TXi}X8mS4!^Jxd+lw!x~1Hllw%mz4QV z13!E?bFNHTwpd>H>ysi_NnPxoWM1oOE<^2zzU$F~kn}TgyhTq~`pyAj zCg)+zAGxj)Y2KB?5Cjs4x{N~z^s zYD!YBWI@2sM~-PshtkMZ7Dq;IT9GAB!km&2!D+YzE^6dY8|rWzY`n|9O~E>oZUnFBR#4+n*Vy{H@uNOF z8wz;5R13K&R?1j*gg*jgHD1$71C>-}D%jkM)pj8^<>TW^#B6WH z9=&Fa8Owp|yVx2-7<>M`cLVu4Q*Ii=*pQ56?_+!1tqEWA0rFKU!lU3>OXLKbMkD7pEp_5~)uPo> z2Ltyn*=<>jN>EMVAiD0tCyvbXYb zq1vK8FY|-F4nrOM`l?S(Ggt*m9@leDF3OPVLc4!3P`L^c9aw=@E|)N*N9aP}+O%^Gn?40mMV&-mQ^=iXy6gZKe~)S}r=Ct`KO7(6$`$~i;#?9| z&Veadxj4{OEY;$R$2Z7aMR{`Ne?BmqN~oV~Y^*dT+g9wTM9?y6!37J?4XpMa1K-D2 zN~nLv*uAHBs$lcPW?NHw%t!#y5%@J}fBzsgB5V$d?vnD_?tsFa#DPlLNJP~x4WBiS zPJy^9yPq>Y@uEE;F-+y=dvh@c4AOxGsQgF)=F97wXd5j*Gk+Y?jc)&+#=a2V^-|2%(4YnPP;X}(d7RCo&xgayoCMkSVA;Up|( zXAHySS})<_UcSKbUUh?E^Lm!CsTZ-CulRl>U3PXK+BNB5*e+|kYB5dSIw?#n%{vzb zJMn{hRte)MgSGewtYApNlxAU{+HY0jV3kMntsz6%{cQK(I(Br_X>&92DO!?z$kl)G zPBi1C%Na4v(sd8M^D~LSVmQ^LTdo!tm6gPS&Pu&U8`177%u*VtOHSViiKM(}b*^hS zi?H)Et3Cpb9PgWJ)gfs$9^YW>50y*=t*hl(L3`!9?YZy2X<0)CXrnkibo%*Y0`Amr zmlTaxE8tT#@-5VAZ#fjC+K^E!DieQkCMnZ2Wl^@iiEqC?^HPWr_*oQss+gRC}S zFHyprK_~T-DJ`%UWg%com{O-czmBO1&T&ID>sRZ%c*MMsixAlX-xo0RRDsiP&jRA+ z^ZS7ahyt2>ihP|TzrKmz<(96E;m|4&GrlYl-ujE5bs2|6c&(7HE5LvJ&S1rSn^IaC zrVJl2QSy9oc)V=-TDl^$*DkG>I7tS@JA(u!wvW^NejtF)=Y(7U)O2IYl%prx=Lv}0SodfXw^^cQpIQxM?Y?h z5_!+7QMDqO=R!Nxlb3%he6L!-aaDC1&dk(pqp1#-?b((E zC})=gwX)c|5@P4Q&TS+bg*g1yrYsx8@^qCqet9UDq61I8dBe%q_!Z%redo|2>#reK zB>qsInvqO8 zHEva-u&8>i9&gY%mrx{3JIgE_7X#Xa_{v%f?2lN-taH5+m4O zxjF2mZ2hYp*%*I%;t#hpP{GI2wrz-`CUdlwyVFG>tm8${@KL#QA(NZ|Wn40hzt$eO zcS$smCdTxF3BkJQ^4@Zz)rN`D#q%Vf>K4j_Z1l@DoT?4tO3iytE88=Wqc|vP9C8K1Y*I7_zzMp@n(@eyE@ve-f4H~hWYuN~V zqym=}Jn+f{zj<<1;gNBN+uQK(K0|jBU&={hq|CE4R{;4oYD<1i&D|n2-mZj12t;1I zOd0#MDJk6SJ#ZNj)ldPWn_s*&oPQ_{tvYV>8*W3xPfsw`vgrTuA3nisgo=yB#~T4z z!(IkHL!EyiY~6hSy2!0NP!$fZPo0QJP($`E)ehyEQTXI_`{S^_X9y=(-_(v}rq(aX zPi%jsh6I1d(6Da$3@bpZ%?3MgR_Z@5pQ2k|s65W9iA=)M?}M`r_YiZR%E*dX9>^Hf zgg$v-AXw_e%SK9SH? zm+~lwJmC4M7Ey^L-)Rf#`+a8A2`!DV-gFe1@iwD7zB-iEXgHY8VbaNd0N7! ze8%ulcL>_-yXVvYHqrlMG!Hj{goM%wnVciO6@(7iJi&f0GP^b9b zn2B;kJW0LB!YMKI&3|-98N7HQQ(~%r8NX<6ZHBNSuriY(=-NCmgIpFs1^WdKT4fIndo?R#oJI zCIL8HaBdi?eV4w|z{Pq6S~|htQnAyM1YvuHUOr}Gn%t-r1Ftkv8+k|Y2=0GyORmEu zG;F*}9^-!hgQGFDkrqx=@f+bKInXjXtM?!5>JBKaw*7y)?f6<zEKv)Z zGsPshqYk-v`z4Zf0pYfXbiK|f!Q;D6h;w*yL`dK<+2x;#jxjM%DGu)Or+N8R2iyq+~W>;$t^Hl1gSpx z3bCpbSd2;9>(77j2kXls5p07f>$eblYu0?w~CqplhnS(BcwFKu4H*#tI4Dr1A3GQb_6ih;LBS?m{I zxY{Jmycio>51Lr<=}PSHU-$Mic+u6-QZ4*`eTy+<+>n2{sV^upR2Ppj9-h(@yf1-0 z?fP7)R=^DZF|SaVh6Y&w{)^M?U@~xrYEG0M(U{WzKOX_8s<1R+7VQr?pAtIxnT}zn zU7y@*h30PCNYf=ws4;lB!+L7wzl1dqH{0ggf1!xFMPW!mva^vGjm4XP`LfhJ@tr$e z{W@Tkq8fjf9j)VgWM^HBQ4%?W;CE!DjJ$~nji~8UXc9>Onh6gDJ%|XF5m| z<~PWB6VBFpT(G@$R71?NCDOS9!k*7ngB|{QY0hc$DA&RRvFq#4ur$~ze1m8A-@ccm z_F|C_2@MKP9r_Gc;-s)G=P1qKjF2xdzbdzV-|2r11fzz;G@F-hLCL1%L>e#--_cxr zcLU9`Q$Pk^Kt)S_ba+kW5L^0Q)slhXR3g}PXUD^Ye0t}PXf`*>=Uek#1Y;`=^KMZ@ zPpp4R4PWFGRHkWEvhVdq?@0YJ3Y-tQ#GzDLa$%y>xD28i5WjFq73r&F)zrSkzkWdU zXpetpZ2P`C!abyYmpUQEvwB!*Tka!pwG|g2=Uh+r-7@aV?-vrS!%I?oeB~$D3idir zMG;U&lzZD4+n!w-+BaN0O}MkZ>~dPb&$5Vk6^P%eePg*OqeWq~bMwUxL*XH4kj;6n z>p`P53K{9PC3;Fr%mW04<^#HW^#T0kU&MbVd2gm|yZgaBdw>rO`bCP)R}#3yR+3a_ zp6UHxiXL5|3=cx9`D>bPs((kYDqMYsr3^g7DbE;|Hv5686-iP*8!l=_;dqg;0w1$&m~#(Ya|Kd7pVki)3bpHFs@#&y zA^x8MJz@yOC?mhx)NT<@LQF7gXq3CJLQ6W&3LijPgNDQH4GchW9~L#BHm(9PWOzLc z_LQPHO-jX)cp~qHk=Pw}XFHi;J^ofceyHrWq8<&dw2|YA8j*Gm;5KBxS~u z?+wS#_6VzSK}go$QsS?(UZbWMw8xU?-p=^d0jrsD=vR{w2n2hr`MLhh+1!6ppf&U8z`iG` z`eIbWqCKLqksD<}GI+Tb`vwIk8Bt^1^QFKxQ5KX!Xo+D6-8d5oiMh5w58K*S>{ds;5lUBP0Ct?_~A(Hk+h6?voNp1kc~UXIT~ zB?Dc^>0=NAWv(GpP~3kwA?{i`_;%j4^LQSH}`s6jIf;Ly|PMbrwl##24nW*3n;{pc-f(z_^0IGoLZ&R zTvHWdu^F=o2vQLYR5yqdr^0@nq_3QgQenI_4PS@(JlSpS`XGOSzTSm8TYxiIi8M&? zMatk%kLa_?Uu4yK0-QTC=`EBbCCN_jMn0#o?}RMRJKNi4i1QxIV=+JffS7!58GoujEY!co&-g-9m23Y_N^JhFWBfCvr`7}XrNpl^Pes`2(XEZQ6 zea~H8UnqO`SK%}*TL5y8EL5miq$84#l|k-nWt0_QSJ%65=nPq zv-}v+JE$Tlz3P{lr}P3?ApSxIyxh}z)S_i8?59AC{VfSa`e17%H;7a{m`mx%7DP)` zGK5U}_Zprw3hOVK-Dc?8BHUq*u$?y}3FaKr>dt@2;}~i9%$W*}*$9x|EU8K>@$JfQ zb#dVyg5Cj{H`A@RxtKEsNK0!SuCROan*n)3k(9$WgBmTIg`!TW`vg$xcy+eRp^k40 z=G2NYD&^=%i8UNJ8ab^bL)xV|uiy3vh(O#nMPbA}(mBhDq36x~0uUnNH=gFzeHiY$ z-+zCHbTnN?K-Ld)=F6Uzz^xRYZ0C|+&k^v?uB$%eGc!~Xn0b1qpJ?ptPofQxTM+Yt zZ@>B6>`#jy<#*toD6N5}zqB$YmyRg1TlFXg$M!kq@_CNcKRC4%?>hR*)gCcx9TY?e z{7~R7fq9YNgB05@bwJdBoF&4UXIRU2?Ad>Oq?_lAaw0f~HbXWhtjn0(N$z5$PBcH0 zjCoPq9EVK#PR;}~lD?Q$F;q;Z+JdkzY;;o+h|i$&tfHcRAJx5pzmv~7F6;7 zik>lC44%bANz{KVSo^_oCQbEw+0TDv)|$_l%sdv|Tj09S5KGnIY#Z$)$c_kaS-C{8 z_B;)w1rs$(#{hpxBsnl+`r)%QL4)Lr;iV_{x~|2Yz{zDoF~8&CIG5`p)yy^PT`hL+ zLojKr0x7( zWH_v=ocJxOq%UI=y?%xIpPbHRJ&P(8vbG+LTQTP$Q7L?ul#IeHu<4K4Yf1>3)cv{E ze34^?ncdSr69*4nXq6^BB9SA+YQ+> z%}v_Gre=?d;GWCI=``4zm*l1sN}!Z$f1X$h^A z9onX3XYO-ct^M)*Pq9ItXH6w&mh9*z2p&ZB*vmM&bMQmIjk@5}%-4T1s$;hukMoRC z;<|;}gTrk6`11i=O-t+h`BOPtNN<1HsZGF;b7lLI0(ASd z;Rjw6X!x|fZP~gBL49z}D5l=9{o_S!&VD_eg3yT2)~T`@Z*vA7FNyu9!#4aAv(#-t zUg3cKY)QwAkMEu%7*?V#PZ{zAMh1bjjo>L4^bHTHlN6n!H>4j4wXpI6-S^o_xr6Q@ zNb%W}>R|}?7a)J^xB4M%Cp+}rFu3wvIs&-#)$!wf-FZdm?*_X}bFQ0azVHl6p_YWV zu2C_X*d>EJV$y&g?z8h?#zgI(Pny4i!qrYEbn~t?3W74q40==9LWuj#moK0RDQ95f z@%1Jy4m3>0;O!~Skcu_J({^1vx}I6+zr~CJUBpLl<@PSG4G?^>%{i|)KDhBuEU z1#R#DQtSzHTCQV2LLpt886DF{!mpVLW@wz*s$2EU1BT;V8G}LWHEUtgDNV=O+#yD`e z+^-h$-yes)sq!f+KbH&~U}8h##tYzPB~MSfCX8U|s4unn^qsvvVwp{SAX6kT#epwq zvy=1ph~WjF5qesd&6!iy=M|8#*U7E%0@szFoB{3H2*Wn;4)9wQgWiE-!j05~=C`4+ zr<{K`6|op1vdTBAFqiR(YZBK&pu+xFc@3u1aRxsX8ov`yiLMbtP(nIHZ5A;pI$FWbePjZ=_xwV9{k8eh8fgp27$$Onh#M*_m;ffg^=QIDhM3H z$*$e`^Vx}x4{2>y2v?FHPsPut9|@97B*+5)V7Qz$=Ms-G(+~*_f|1BjZ&QV)jqk4^ zv@7`G%sfAYgdhiV#amo7*t=C1l;vs_q7uqYbnk4W2^O1RT)<}6e9s6%BkHAhWjj4j#3Bc$DioI z>5Htr+i0(R&yJ9Bv8jAgfXFj{qJz6bK;7%a(pKR=c=RpOb|G`VK|FDGURY`xL}oY> z*-iqcX}ud94&FO1vGzzd#OogCa5vYLqs1jF9rAM@7^l}SL=;bXUB(4 zAl%4tMY+Sn#m)~R)V1Eip?HS0qUWT{|AFYWk1r|VpM7y!)o1lqzfHe@(BZon^X#ds zxUPIHatTA|`TmI`*dBdHNgb_nyC~qaH_$qCjX+(H(yfO6MflU*_gkocMGSvx$!_wY zE-3U-62WmaUC0WPG$OVw67X!g?F*eagK?KK$pp(vCw@Gof#&V_xo{Lz+TixNy+J)p z!CQq;mr$=rUFh8C*7N#=*;s^6R8U~XW`GXB4p|4l{O!SZ>H&N1Oe_@K2>~NnoI-KuUT?5f{ zK8Yms-pFtFFXKqx;Yyp}$ckX8FcM^3PK%+oTMjo^-L+hXQPO02hhfC--bN;9XIX@J z(ih7XUW^c{Ia7Te*$54Fmlbq`?l0k@M>)+$Ek?KQEu3@vk7%ChZkxM za;?DxpBtlhd9TR8F{%=th6hu`?uwWK&vUvMTWfD1YfWy`M@F85TD@`VQa^tC>hSUUzglkG%cmFYkZ2h3sQY9G}bhC~RRZ zoOi~ymFK6V?RV}FpRbbKK0`w+v-!8*h~kW3uw)PBf8ai?CHw@P-0>{TG(3x+;004i zv=>Z5E(PVO`DO)pv{cAzj${=x%3clr>l<~UK&K&gWKMW_zpmNKRjkxdHy0X(^CVyb ziLfqt8;)&cnC*W9Ez(X2Q|S+lH`G4D!gcCzVd+x8VB46Q^Yu8-4P6p6;@g@QyQTg6 zfUh&MRv2fg>1Qh{%2>ng0Xpb-(u=zWP;d5T{&{H_62{ymB<)AiKqI7pus9qb~ZGL zpNWfw=pFDnG@9>W#GU6(R{tY?dSSER7XxF`NT6pTT#gsd8$+am#82feSFcFYIXAJL z4*r9abaXNseoSL2S(n{Nd%${l;y9s2YvW>kEq^Jrr3!he!{6}I=@t0y#@%o;0yC@F zm)C>1jv{{tO7l5u!kH|MxyQ~_6y^TVu)gxO#zrxQb)9!bCWjj=Q1!BT%K3oZ%X^Ar zR)Vqrz(bqvfBYDeh-tzIgm&9ju1 zp)_^1lt3Sg>YB_f^8SAkT3y?{yS8hc$aH^BB?_6F%->7WL$@QObpF@VlOK&@l|?3V z`C$oE7Y1`z+m(ugy5UaS8{+Qyt9~oK<~TB7sBRhC>(TQ9)-9|9T{wBrfQ4;7E@Wd_ zw?ZReZhj&}TEsD#uFkGb;9l}Y*_C`k?ty~*h$?8Pz|JS5rx@9H`3RVtgGJ+cR&al5 z>B+Z1vy0SQH&N{SklVgG3%*61Z$Y!P?pxz0d5&D|dXw1pDFhz7@Q6@FatCqCy<_=p zwt^gtom-}Dx`2${Pcf+u@n^*EL^+LY(*=D-6yc3wgGr%WF8+OMhN)4fN{rSNs^G7O zs5xMx{QhD|s)#~*UtPOa{vXc5t}uVp>vE=iU9>6zOxx|P6UF$3#(Xu?y|I)>wpu#P;@d1CyKA^90HMuLz&&vvZzltzMXEHhMRCD>L^wxX&LV-)-*G2emV z8*qM}99RK%B98LcNQ#(Sg&2JO*Uk!wdmWFA(CIQ{a|kd6$U>6!d6-2z1Ui3YsH|HE z`~%%x%wdJ{Jh{gvuVy$@qZlftTG94n`zqK_f)4OLxtb%cmy>`&_f#CsCY11<(R4!_ zE4~hdc$8I$Lw!sCY$j1MM&wtFq?Gtjy@Cy9j-Qb~8n1#D%fn1`RWe;C5j#7Ox{&&E zKyjS%0!OdtC&~`>oX!LFP9lHeu|d^Af5hc$HwsfALyVVvz)vOG^!#VC%MZaOyxlx!ABLdGGppQ8k8Mg50dQa3A-2$41u8gvFqdZwjum>}82 zvam_)>=+;JTRbGRjH|%1MrqQ>;+Dnp z-$rF-sH*!|DMFtW`Wy4w_>}26Dg1&({247XB-IO=U;b~ z!#;>!-NmOC})cA{j^@E-ptH{Gy@SqGX-xKk%7i14tS2wX<#KI%yg)X4_32?DmVn;TTue zobJ}Pd3nbpfsfvkWBh%`Uk~8>8G2)d4QNFqEt&c=%uc+?Dq?>!4`T}nSKls4<+}!< z^NNMdrcrCdYWVMAqq;tL_D;>(-)NUzm-$YprkJ7iQW=~V*~um-3^r~uLec{ZS8rQ* zos^7YQRi-bxMrf;8;5-cL}#D@R@E5dnGf-IB7}U1$-tuiNCfR?WD&8ooW|ix?m3SZ z;Ff`iY}O}X2RDDqLY8i%pb)4|7kSFw&J1;@3vO@~O1KPKK)%P9Q*qk3txRF*XgsyW zAS8S%9A|Fn|6#s07<1O;#!+ixJO)3{P8gu$j1Qed&+gBSAU{VnrV1}9D}qg*tB%3= z^oc*)kYq|aDm2z9U}Z^voH!8Q0dftpkkmUoJ!B9$P)~on30Rg9+zNz8P>J0v6lr#u zGNF)04n35qDbxH_!7iDFeH`HvGyk5!+TpD7c9z`Ws=Nu&w&M95mW@Ct((&iQFJ+JD zXN7%OTFxb*SgzJPkK>U83{d5j8+`H~ebWM4Jy)|Ni{t+{V73mo;#NNho9SY+yfzP~ z-h58Md`T+0DMW$D&3^RAG_%3_VU1%D^n?H>{sZ2~8arVvyTvXdS`x#8KH1Uy|7%h2 zrqy)ioxQAp8y@({vFw}Z$-qzRNBUpZKy#iS3DtkXPmFyrOTUuS@yfl#j*(UpF@mVc zHCo8hRn*HzL;w`?4X3>7Yg|@uR2v=|-3>vgFCqJs!q`2y)GD5AL%$1KSlF7fW_^)V zM>xqTTgps9J2Zcezjb;`XA5sfD6H76r?Uc51BTUMpg(xHXXd;&d#snt5&*?EP!AUF z1`dC(p3NS@MGUNt7+;yAR9Uu|Lo~klkR%LXVs^7ibW_2)CPCT=0_h;$AR-ep%!I${yyj>(@_ty&fVro!Y+D`%OvE{vt1;y@pv zOnC_v+~ot*MW&CWu?m{O_j2W}9K;$?i#CZyA2jdWzce{Qu@%H!pD^|M^wxQ=?)mPL z)KJ%?PJs2W zvR-_*(1@%_7V@rKdH{`GZI80u-a$!>#LN7&~2exYde-Nj-+svfNEN?JB6 z#u=`E79Y-kgv>mu%SbPhv;Zs-7AgEnj=tr$V$A7`_Ek{Ppu7?`MbqS$sknd4NPXY6 ze!|2#W(tbFmDTy#Uvz}BuwTnbh97>QS9z=lw;QPbkS4VG_WBfHGA6y#LcSJe<~#v! zTbB{fV4y;G?tTuoopw1ZgwH_~)Db~NCR!g6B}wKS}%&lEAtBt zv$CEVJjsq4^&zoFu;8UsH79?ZLxE1)B!Y=m7W!Lf`5n!&FkQTNr@JeuihIAhHg=M0 z3o%N$9pw8-K>e9h#UrgX1N-fG#dDmAo~Nx1Ara$M%UbpsE{@MBHs?%J!!!WVooqf)SY^$`X_kLuniEOm0sVkK zY_cQN2BuAbNlQlNCh7Xp2tBLiJM>tnMs;grlQcRS-4V^Up`qAU@|@r-dn;&AWm(~AS*HD79l!I z8`BOoRQ6gc+vuBbYqNhVA)T7kV4MmJlSDrcx%_nIH{W`EM+p}s(EaS0w*U=dA&PN2 zkwRkO=y8afUf-zyD3ZTr>Dht_>ccbcA^Rs)J)UU&jzB_Lwfm`?e*P74yHkI_9ZtkL z(YW_NJtB6HtFdVRjn?dRm39Z)KF}y!KSd?)xo#}NYJC!+ZZvZ)N()@|Le%TN`3H%PknuP<4m2d=ACfR5XS{TJ2jiyrMa!vbpN-$%L6$B zZ#+9XFR=)RE{I^OZ)V_i1;b_}TV<|+NngQLwtR*r|1*Is#s#O2hB$30r_ArVsPkJ$ z6U?0`o>l38liTrs@0u$#ZJFH`wM4a*O4!VV(}s+tJ6&JSkzw(O2YkSmI)gcCB@3m` zZ__jKu;=l$vg#g;GAtGvYD4?j+uB581d-y%aI+6OSv{gCT*6w%Lg;bo{)YS!Q^-Y- zXCg=T>wVxJf(-fpK6g+z>A606(b=%*MKw_%6URqsVJL^CwP>%`|@FFc=NB)N1}QtbK)$lqD6C$E!9vLoKTwGi`c`dSu8_x$8a z2$Pfiqhh>Ssg?dzFtePs?2&7AR06ZxIsi38Q?(46Ey;SvnAHCMFCmgS+1FZGUkyr= zwpO%J2yxPXQ;OY1vQCaSkaHQTUNj^5yoPIUyO9faYYjT5Q}#RO7Gjn)WU;5T{-!o; zzrTHcijB16 z&+Q4PTp25Z}l&k`%=VxZ-t`8)CaW~OY2xLo&=jE$Xnnx$<>c{@zoNF08PHtutF82xDTAg%flF4$T;Ha3@sAmA%kYq|a zDm2z1*0_c9)X|{dZPF@gSM7<%VNLl{4DGkwB0eKoFs2AbBzz2xQimf?3B&li+u(-H zXlY}lkHFXLxiv-S1KK*2G|5Nlw)J~Rvii;_zN~O*_6`64ckR7gGGUV}4>(Q|9 zS>AV5Q`y~GpIvP{yhOA4)b9it%HlAFiHovYA};D1x03U|JHRqw4#cT=LcD{@JI&qi z4jYRH8{y|@t?7djilnBuJFkr_FYJa&CL_c-YZyxpK0CiUs|}(v2T)UX}%Ln9{w^B`nN#deEAw z?S(qvUv{(HCtQ{8U#~iChh;XkR|oTdc6n!(fIUF{>rz71PGDG<`gB~cLpaCuI>kCs z#Dq8k2o{!jTk&0X^+kAKRJ7tuF+oBcx~T1FGAkCwxrPv&_eDcd}~7VX>(=+i=K1f*bFn zjrcz*DQ~>B{_CsFY(-iVomO9r<%EB5z)QFRA8AxoS8TuAng{{quT&w9zaymuUt&gy zy&YCZr~o)YnyaiIBR-05J!2FSAR#A?54McCc}p#Z%89p6QZaAit-0}k94#B2du4V4 zoopHW#x&rLNT2Dk1CkM9*}3YO$+ zb_Vfu{ji`SS(%)_Bl#WVjrx?s^8iqXx91o0?S_mh{wd*&Mm1Kt{l?AeA#j7xC%y21N@$r&CO zI3Vh{rPO?5F|X&TOng^A2NCawODlvieWT1M`8LVjrBi+g7nexHeKLO3^|f5xxDl*Z z^YXnnpE+07AFj1J)NodLFZROBpTEou1mI)r4A(dCG)7-U?#et zb`fkGEho9%z&pr)QpMMkdvhv4EdL4d)%FqStOe4g&{>}KKvb|UiJQils~)`JV63*- zD!3Gb^`VhkC5#bGhx*Ks-1t zDRmP-_B|BDT?~rOf|}SVrB0^lO!ke)Tz35ix=RTax$eDM`o|gWV9iJMxf%ywTVz>| zL(w4y&6F5__)8Jqy4r5V7IM$l+<3imP3@4BJzG%id-L=XiS&!B zzL5}wQ9@=9Rc~pKZX_1BRSi-WtTg(+ZD>!~2ui|v_3O$-D2W$e$qn65y33YZ>l*7> za(3k5zfxg9cB6i{R%+;wD?{uogL$E^Bla(Uot79Pcb`8+&4T8M5+tQzOZ4v@ z()}^r@A^zG<){$W`S*`R<|s%PqDZ_)W~aj8&h8Z;hp3)kB{Z`wI;>d2yeZ`4=zw#v z9tNe?+1OV|{fmLVYQWKX*b)z$$FW&R%nS!Jej)E%`T+W5686`n{};J3Yt3~U!mVZL zd7~bGmzY_syd^PzCTpclz*?askg@LH;4&yUs?zk0XAmQh-))^GMvNXr`VJ1Q z+%HK%dMS}}&Sh6*vnE7p2OE>~#4vxVpt~6UXMa?Rou!ygUVPbXtX<-t^ycwSg5%m2 zyRsGJaR;GvRHU>cMK-^oXMdS^)f^Qm9yS_aD_`d{|GuF z**4ZUmsR9^c;Zv5V!JpQ2ktj~xZBh~7QnQX!E6%EDn|7Y9jVwjlila2vFa3c|4eh; z4mVc{9M2YpJ_(o4oP9j|n|s!%?e<4#_8&6RN8)GeyeOdpjUgO@Gr*9xeZaT4%clbUa^bFyzTZs_?ILSctkt+MkezsiwJ ziu?pl?ci=90w_5Gg!9Hl2w%lgN1JhSVqFgWl)JRszu`MK{=+@TG3|mi$QPE?JqS#RK?MkRQIZ+9UobOeY&ZlmjRvXJoI1L52M>!pN14`1i!Wllc2+d0(gUgW z;LaV_PaI%@))N%0Su1eaZa*&d*><34o}Q#+9ErRwu{avr5knl8 zsN29<)^jM!sHM04VcXZO74uo=*1_@dHZbF#O5R54?-Z@ulXx7eCepZzkZL*6RQyA!I)9<^G)sFAcf422$cCa|F zb(8jA^0F%JwO1(Vac5C~4jdI#s{pVlx&H8+U}p@5!vdp$aUts!@rBOp5$5<35`x%| zs12?dT5O?kI%@B-B=L7FoR%`B7Y98~jvOFWh`rX;MKV#B;KDBr7#7AQOUGdxZs{4r zM6YicrUanRGsTqErL%^jvuOZWo!!wI;w1)Jfyn@ub=p<@iERmg913RvD1eIo{R?)V zsTx?pi!i3~b)cskie(%LI)y)8W}_Oaugd12Ff`&Q8|;k+_q|*sfZ1SnT*99^fiOyv zz6XfLu*OYeZ0!V(*$zfVhLxnseWpv=7*L_p+!Cr8CHs`j%gLA$f(EdY4KSnXfrY&A zx<+^;GY?Qwv_3+AsM)%_96j25RK8|CTt}+a^q=)G>9W0t>#+gh`)PXE@XdICiKoKc zU{B&R9Y?g@hvkO9C(2A>-8X^Op6W|x1K@~$&^NKVI2{uYlL1t$kUIvYa0-8+%X;PY zKceMtgvCVGJs2+^(>=^_RZXZj7MQbBZ1(&MYIThxd^0$I?Z$2tpkH(;mYz;}ld1t# zuTaVqM`iqd7XKtq!9JG}ZX7?`W|Ir`94R5OTGouMa)fWS27?pmo1q(%zDnf5z8)A_ zCP@dyn{dgk)|0ffXg~PAXuHLtCE|>} zeM?sjFRfmGASxuZ8Zp2?J0ME1m6?)Q{mt6eY2ZmHhtPc6<>lwMX_n#GT)DO@-Usf3 zM&l1?_Yt075VZXWi_^Y0#vl$vmrMzL;LkF-Pw*WpZ~N5aau#>(LzE&pk#%h6U`^KKlgjh$F4ETZ1A zcfN;Dt+Ge4E)o!=nE}+)5%WMPn7ZTfxkv^^uR+tN*mVW(V|tE!-##*c#$V%qz3U^P z9PRF?0TnDb5ehZMdQWy)0rp%U ziY$?^%ilgjL{^XbPM@;vzqGi8n0x5T#)ylyglt2XQ%k=Zi-sHZDgwiAwz?eGWl`xmT^M$`|b(C=!LsPrvm@1W1wnx}2r zA8_7lCSm_hVvIbOl^URfS#rYi3%ta@-GI9xk`w1k7hy|tV|i+&N^RR#5kwnWDzM1@ zBFvL-qU>81`;b=-;4IuV!yH1MIhT`vG-s-PGeK>|4|6k6TQLQoH2;^coUX;ERBPt= zXaEQQ6~*v6>G+EPTO=d$d+nY!!SyJ2(ahjA)T=~zPc`KE>ifyRM?1C{;CCL1YKiO106M?P5`KbRP2Uc9VM8NowsiVW~Z^c>Rrn;X7=4wYO*%Fn51tEH8LP%4G=@yLEta)8}GTM1A z;FG$JC5oAsWg3QYWZ+!dl~4ggKj00M^XZe-ko4BFPHf$wj;8_M1$Pb!#Kap|(}Rx$ z^MRjaoByy+&B>fc-|)fQK<7XaC2)6p-qmZI^1C%Rnfn{fKnD|$H^|F>#%J&aE>EWe z(ymU@mt|Sk`!b_9eigWCx9$Mx{`czs@6guYPvlzkOYYlmF4*6==U7nYjRR#xL_8#=#Y``C! zQE-B9=K>u7FXsRW?Z-KPia_gC^d>jcU3G*W8g!6^z+f++@o&rTx(8Q7njP}iXs`BX z=dJ-)!#l<2 zTeavah`=nq2ZhkrFTKiBv{+MnHmHe;^dLE_=ybNvFITdKbYtz?x`YCe+GMypU>4lK zXCaEu?blmQK?Ua@u2^F>)ciGrK#hz}fY+xwWLm7cvssMCX9{;G+vb%3Mj_7SKOv*g z*(^)CWw(_pRtzzJ6Gzcf3^v*zdd_Fd=8}&B(iaI_obamF$HU{bbQ0>!(P%<%p2jv$ zsvPHhV;SrJA(E~0MIzT3Z$t`y|3t8Vu2H8a0$y7q>FxdToJuYV`j7pvCZzf{YI$WP zBjc|L8#lf^*x_5B7!`j+35WEPm04$q_bqSD9w)I%2Rc=MQSAo$XTCibE-wT1WO46p z57FXoqKEqbFJJ+a+oUcE1Xj@lt$`{%H8l-nBjXu*@dnrl#`(*YMTc$cYnrBLo4zSF z79EzI_AINg*=;IE$GMcAo^=6CN6#PJY0cqyh1p_J!HkP4t%t2e&eJhceDGwD6LZIf zZdabf;<)U8F`&k}g!@e>vU#Tda?V8fnm^zN7FI&?dZH_{mlBn(9>(~K>F0W8WKh-A zJP4C5ulmhgI;N)j6aH0~rdff_aC!BHj3`4@Mfy3f`b@~~zA5H=D@#ghTfp>>Iw6>$ zY~CX4Yf-i0GPf}O5vlnViXAuTnnVC|j7;uXBDC0lk%j*@{vu96GQ2#XHF+Fmy<`IW zj={ONQs?0-vz?ITNXSA7)Us#QwE=9%QDghBqgh>V{fi=%HanCF$fuFoz!|m-^C39w z*5}!!3$WnMkz(81t0YDi5$XLIR$8{(ft@PiyMP$oW6yA7&BXAEBw3C5u9#ay@>Xq~ zNtyzG`vHB;Gu1IJBBtheB z4y#kiBP2XmviP{XM)I>e{Cgz4o~avj>=m7V7@7M-mo40am#+iZFDFwECKhZHV5AiT zdmoq9Y+}4!N+MzQ5ahlaLJh^V7zPIh%A+`?`q2qx`zNidlYR(8BcSoF-(-g$Xv z^$CaISHj&YRJofv#5E9ommnIxZrf|yrEVH{s)CQXtE@tckAGX3Yh$2Z#vpxQ_>!*~ zVA+!e5*OKqrH{c(C`H`vIuCU~ZY<@0r93H?-Hllh@jd^^U=_#r?Z=4zgu)5+rY>Zl zB(I!iu7b;kXeDgG~_^d!|K%FKD z{`+r^&SzeAMK6W|tj0nutNsN58)2f)AkDkVMyv+XaYF%p8-PiosA<2#daBxg!^9=u zcbzx65(V0(nwOC4NrTc0Qk?pC+J#ttN$9Vvd+e+x0K}dU2_LR;PtC#?Etf6$EB(3B z71S8k#Ta&;mq*s;DL6U6?|jCA=3IuFIw~y@a^BV7PC4*ZQM>dB}0#oT8It1#RCSokt1*wvPD|)pWrc#9mOiJ|txt zLRwjKD&X>+S7(RsSe8J|jN#8^>(yOD&_M$}m7AYG+Uj9X75SeO)43DlAo*REVV{Vu ziNyy7E!bvxx~|QqS(c*i4slP_(1Qf&g+Hl2FwYZY8qSC4A+W;dFR9;umNhR(N(7p{ zpIBwdKWLC{Xr{_M;=4Q^pohqmQhDLlhxt87ApDQxJW;^D?uP)Ui$E$Vevhn37sC0= zCHtP}$(&MXj`>vhAZ0Kfg821JE$#Lgopex!(|R$(FKmRsr>z>dsHg5vBFlSPXd%>1w*M6#z!uss{-)TObePd8CpQNOy6}D56|OY z)Hf_Tae}UfpDaQH!5*d)4hYGO@;3TV)$ja~2Nm$B1A|0!{`iW_@}9C$Zx$jUd9#>l zJhQsXBg|w^yaLDR)H(LFzDtUE?77v*EEC=e;G*p6&C2bU1()6zTgw*9p(IsOWfU>BHwH=C+qp2ZGO_Rh1AtIM4;e2Vmi0Vr5}sV_|>cMW6&of$Ts|?`Bhgu_r(t zQ>Hc%Orvk@3$=f1*X1 z0J1<6YkPNRYfAvo&J-ZaB+mp;uy=nqS^{Y7?EuCg3!se|z}^g?2GRnkt4gS-0;E(F z)s<9fncjaVR&{l7uy^`@TEtY<)TI~z;vx!a5&)0}13*e$RqfBK8pzHV zzCeHX|6VbmovX9gziRV;(>AfUbGCGLasJPYAb^>r4d{=0=Ra#^Y4?v!UPM7gQbJXY zQSQCv>=@YNC;3P6 z|BDbQ;>fd z0<(g>%X>S}{QqZ{{TG*{tBs8U&=y4VUlsjtH=wPhjpu(k{+Ev?=#N;M{~yto&XSfM zAX6nv7ZZzriufP1j0^C+??vp)Z9wl!@{dXV&sMT|@7ecr$MVmC1z=?5V*jtP_jWX~ zwgWjk19;f}83Mf*;lK30SN@M$0JDF*lCp-3JpF$+%s*Zdb|&_wmUiX}?W+pp(m-i5WgR6@#z|7tW;m=ZXaRQh{{+RxQxB<*! ze-RIWS^O{J1u#qeFXCncFiZbM>;Pt&zla0CEc+L|Gkp4sxB$#@f6+Ui{9k|c&Y<|e zh=&Ehtn?SX^Qrtr?|iC%(L0~oU-ZtP@fW=_X#Pd-4BG#Tc;Cl>f6;rk#(&Yfr^){! zj&}x=_s#v+o0a8Vp6S0JD}WjFw^o0UqbtznUk>jHnEeaBXJGy>_)cK)m+rk}7M>0k zAiIBgyxT1Q1>du@`WJjp#rl6=@I9A*bH68J`xmmlOaIr*tnXs&{{`QLI{XX13w8XP z*dOEya{i~S|N6YEclsB6m+Jg4_%7AuU+`V3>)$%R`~GwHIGflzf&S(7o`KuH;Clw{ ze<9m@1|I){?-_Xh1OM+1Qu=dy{Btz2{M}Oj?^^u_tGYPZTZ1$$O_y)AvgWuq)M~492o~U2jdUnYcDp7Ty8+$@>u?ONfl*X)vh= zX2~!5S9X!n9?IqwYL{_LXSF4?5d+jmj>LF7zCXmUjlZE?rjqGZwyhRGyO+$ee#LaM zLHAfh#qT6ooa_X5d3(T&iw5?JEYZyv6^3Gu$ig`Ed`H!xPb+^^)6QH>(7r3K=+Qxd zRj%7L*GmvfJKVhde!r{;F>Pe*?5R)MdDV3kEiqe_`urQ|*0}6mxx?OGdRQv?HQGD% zP?c_fF`&l?u`HnH(0&pAsH!_~x`bZ9L8m5V>sg|-!#wsH3>)Pe7qps1RO^v;NIGU6 zs9xMcfGT>aS#y7{PO}yh*{Fx0Gx&SJWFbZscpXKlb+|sXv&Nw0psugqGedgn1MOX= zI4ozrewJQWU^!{SJxe>A$MtU}`?#7`Q{g#yDWu;hkB};0UEPS}$~-GS$crHsTzEqv z5qcfHdcYzdhoDD&`Czi`#lR*JA3h(>bWLqs#9g&5qhEiH*yZ^Dj;ba}UnZ7Fyv_A! z=Selq^_k2mti>^&7umoC$Rm6t1$h8D4m0?-yA$@}j=bvc@hNav?)1~~aEW!-fUNRU zt2W|T?!jTrqIkjkIkeW0(aSr7et29e7OH3Lag=zckvi`8-|FwlGt z7{q9FVS|4O4R$|j=XlJ-IupMxq$m5!9?p>xdwhPt|D<%)U7Ere<@mbLsJJj4hZH{| zA1KP(lKi74a5^pxxLNOU6S%9%a=`hsLh$3N*_3|@Ih=|!SOZ3xiJDnR`;o0yNFiV% z6ue7xKsAkgo>uHAgEyPhJ4n}h%`8eq6S6*sOI&}qN{ll)tBFNN{5b z?Ihr!vA5ge;8&bAQzn1K(5SL$8EqKr^2K;2oGGl>+k<5e01gM|3t=tGmrz z1(%w^cuV7?3wd_vu^gtb?m?C_qDn>(+nIm$oS=!lAakoZBQoy>kNk1CjCiKvR4X~( zBl7GgmFpKf%U@r(P5VH2gnmp9pH8wedoe@t5{b5uQ*FBS`;$IT-^A|dipiZLw}Ga! zR2yT_E7$L@Hj8#Y7wJ#<_w2!&-3Bc*Xci;%=Fq~{E8f^GeiYqAx03yUaE&;IQPJ-p|LUyLTfAomDecMVEJB|J@|#q%fZynl@>rBDn$H zP*3Y=wJ~t0waX#Q=*{`e)}wICN?CIobnf%K-Q=)2>WmgA9H=c9!`yew2seQi6j(uT zpKLQmimbU;u*`d;^1THcYz0>toAG}=@wrQFkvqEVjtmG2W=NCHxT18I#z;-)eNVTz znoRY@bsJY%U|+oM1l`8@ZQhN)u4;VR^qzr@m&%U;RQqjE^l0}c{3A$`m?Q!c8?7A~ zO4P|+y1!@8>zZ0bQ8Ep_RLbT%+K`9w4@uavRT{?tgaDpnvnTUj%CKk52!DU&pD)|6 z=eU+H)B(Evm7JA@64GbE{Gu%xedJzv-th zN9&z@Xqw65mDkOfHHG+?k7Q~ABYnSm?UkNQUKt7I&SzoV;?xc>VUXE1Crq&5vThh= zxn4wa4qcvUuk;=z8KA$|B=>*pB8U^@FH+^`Vh^*R7+>p&cda0nWXSTx%?hyScB1IqJTNfVuWvPSV@0!ABQAA1Ed~Pk{MLS)sB{44}FmjK`Ox;0t`Bg`CJLQ&XoPGN# zP>c+pDYAZu&U*X+_RZmFMV||~Kx(MR`_qPZ!g0gg)9^N}9V>r~t{ql#ud-h6F6087zPi`*RWd5Drph%G1O5ZNHm_|sprJn4 zO5|xrAiTD==SrZu!66a&S(U_XqL1enINodViGylp=4;!ARuzA#&OqQ*4!E_kTiDER z*%sMSJ0I`Q+rI`WX&D_{#>j^KU`p`z6W$gY2oL$ci{UGIpq<=ZCstvT@O7m{bPBU~ z+#*N_KaRQXLB_hQsgSYVsF}DARdgW}OgSN+hAk;dd^6;g4&|koyW%u-5w&yGQFsi+ z-fJhCX=}k+-O_)a2T<{Y`;NIOOMqtf zSKD;I6MOoI#AlpS=b5W-vzcXq%8IRkz=w(${cara`G#MMu@G92jwu`s@P+dszCKVi zw-f>-`dQO11arkGLf$1G*vcJ#P~RXN{3zJS;5r;1;4Oa;rW#q={^+!G1CEd&Qu(~^ z%+F15WyGeJG2^C%viE(f?0KAXxjboa#IN>=h*1QtM5ESD6Sf&Xhv(oKUN%r<%IY9b zB@FXR4;8#PLm}T*sxkLKLLIzm;t0Y7#R-WhLKYG_z%+#V_-Vz6ZAzKj1-Qs>E#{RF zv{h14+|3AX|G0!`wW{mO1ltg22Abq|16l4HidmwoozyL=RkP@(zS7tw^X%ThYO|U} zL0Lu#bj48t#M*7-m`=qr5=G5fwu;%KF)zYcTT*`m1es2hURD=AGSPhAC*bQpRJdy_ zYW6CYG9eL$lV`B!nlOw!W+u*Zhm<2W*WgW>UgNeJo0Ie5xt{7xUbPHFIZ zOTU()7_QX$YpKsN8xtexv#}$6q&D&>sh{=g)<^ixie*5qMlt;+SBmy# zMnivo=vkc)yZ0yQc^7b?y-4p8efH1j^$iOcd$^L4E{F^9-%*7P{A&dW0w;+Kj^wo; zqCY`Cxm)NU%u=l;NX5H|Uk0-bmZa~|B_cZuP(vv1fdpJS2-BrRf z>@XOi(rRrtV8Bqe!+$5a{jfvIQn&0tu8zeKac2P%#rD*iOMNKnmJ7A z7Y&5@at3}##)=I!x~7W$&!U`M=aX`UHmAxL->Tx3o2_WIP2>5m9UJC(&Q224-IssX ze$#9)qX1pHribw+U5kxXZ0F`>C<*|%IG0R{l!*A#9RB_ig&Z6~rNrxTCouhP>pt~0 z15m55-FaM?Uh+mz*2T+Am>8;ok_qJ4$z#z}#&yiFitjvtDjfR?go#HRF`$$^?gV$0;K&Ht?3O-BnpWOg5pp@IG3O zRU6VZqRh=A7n_XVM1_QDG>(4*--_k6$+rDoO$2BPFGRn=X_DgE z3>~h^>~(w=c4%{E)EH+HPuLULT#0_d5mb)LRtC>)dYv>e!KFL2lHd2!s|)>(msHgh zE$?JF60EXOyXC#JQ%mFTq;42B(XGGY3IChqByu z1VI!!9|hC0Ga(#^SzLb^Ha-0*Wk!z&zifJK@+_IIYh(cF+VU9En*X)2`wS`s<2CXc z?wYpfspPxv#|1Cpr~qR{7m6Uv{Mb)OSK)=cbB2g21b$_6MQLHbCkH+D`06xLVaUxcm<%WBRmRV4g)nA3W`j(yr=Bbv(m?J9pWdn=sJ)0=5}ydN3e z=|6 z;IQy-pP1vv?0!ioifhd{$Cgu_lwynRgiE&3^Xp+yBQ&Pifz(;hOZ&0`KAcx|m>66V zPkFJbrP~FgaN~aq#?OS$qT^&lP#K4H1~8*sdIsra2NTfpP_?^Hn_0tbf(Bpj3-pb& zJ`YR_v2uLNd8$?ktRS{;vXvGJ@UjU3lCRXQ^~ zd6S#}c5-XZuJF?d&c@8H$X63cN9! zhftllw2`Pdq;qhiiBme#?nOXOZ__>H!p+X5WcD|VDG9-JJBkni(Su3wuY)rLu}sJ! zul=eQtRsK2bHCw+*^(X=<*Y*o=mBJcIUKBybiR7JS?=iktxsaE4laS-5c zBj;W`Co_1iQ>tO{^2$}rOG)>~dq5GsvA0+>)lPr&TwhWXHC7XnMU=1vGM?VJ*}3qh zNV`#li{7fZ8@QG+^|*l_epgC=tox=H+*WGb`zbXB#{Vu&RQC#sPDo==EK9}DKV4sx zWSxBs?vUTWP4o@P=5;bR8_VF23|+E;}4-v|Cb zpMQTa#Z;y#+;#>0R@3F}w%=I4W9f&IdaCfFXVIL<2g8aCuGxF6?YRb ztU>wIkuiHhzI*HUG|`MH+hj-f5;}i`M2cO-LoWT0DlO@8A9?F|Z ztv@#;{U|u-S7&H90xCy|itPa~K=)++M$?h}nQ6us9v5Dlesz)v zYq?yU3N{*u+4nJSmx*#3fPW-} zxmJ^KA2sX>q#W@@i?y^<;d=lc4n+t~hLt|+DF3>F4Eb4r>L%%{gH{!+-qCV!cCfY- zr9M1ftS>BCCpp~-znI3T0-t}C+sMD`;&mYiX?fJea)>-Jj`s{bhwpR6=dBGbLT7Rr zey<@>w=OmH{4B2!Gl+DY={4O*gT+t!4L2y2H1V}C#9gCCPsv9<31%%y_m@0Pz(g9* z$)qsrk;W@JL-GZC-((T0z~c_#?RSoD`W&kbyDSRD9#*AqOUVSzcM8oppC|GkR3{$a1bnpN%K zb48~6x|J_(pQXtLaL|9Q0bQeuG7PXz_}8=1Z&AdBK9`0);9K->hYt}Z&l*+-a9;q! zUtF{i1*ee>Jz|C!umPro13DL%aA}<=RRqkF6pCDNfD4B_oA1#=qaR1P(kb9lHJ{yO zaGvwG;EdaAsPmG@E!sOO2L%xxyGRM>gPaDr!kV6f`;LhLEbUDfX7k-sXGmxeogab#2?1kKmQ9 zl2{e`S?&%BW$4^4v27YZR|~^obY%$~%ac0CHg?D_;#7Zc#!DX)H?q`oaMFHPL2mcR za(W2dR031B7x)Xhn)QqEGCy1E6}9^IuZKaBgP#uW9QbL$;sms({RM~y3+w|Ak@#SJJ0z+$%|;}n2kTq8>t`KT zhbbFMtlGHIpA-vh=BBs z1?iZiNVLpCQ-$uZd4h!-cybww1U;*~Dglq_YDH>!zzitp2GlVa>c@Zu{sK*0_*t@g{mkG~i^y zvhIJ3Sw6Af2LsBo2;7TtOnx~7F$BQQwdviu=-wmW}$a?dU~qlU4FJaoTOu3mhf5Yc4TV`XXT z0qWRNyMCr9%}gC4^>;|4Y^EVuSYBPHNnui4MnFR!!&$hW&dM?q2fb*B7D+rCR99#- z0~dwFfSH)kz^;@69%X#K0?9X}z_c49?p@j@uD(c`F_d99yK&1=S);&AV_;e)P11kC z!pP}j^BA|dfbjkJ1W?94EJZ?&o&^$S!~m^eL-U7?U2cH@w9${z&ERbryq9bgF7T-J zY+vLVt1i5v_C7mXHoJodY*ApCR!zed?f2mXxyWa5f%?-qr81ed9z8FOYHt7%>o3uA zG;EVgAMBs_?GraV4!>Y8M`_3^FfM-($+DtNvnVjH(V!WrvUPv@JFU3K8nNwAaD)&! z@roTY!`9&%$F&hAp^>N(AjM*pfaeYi)>!a1r}5Z@Fr9P}vubgC!`K3k!(D$tnb9dL z9R8#p5W-caM>xpaO!-DE5xFu#V8&*Bx(WP}(>PG@qjSR?IiWv|Q6iSo<%8qoK8p(M z4aEcx9=7sVl0<8rNSZj}6mQZ$e zb^Oek`=&?_d&%wPr>2?cD}=U2@=WTDrcZZ7?(5$25jTq-x<}lzG)pMMG~XQwrefZ| zc(S*8(+v6;nH(Pmlne#}gBZWQAUQ+f9u#npv_vEELEFE@%~x6Hi#2~gwBTvexQr=b zni{S1S`B}=9Cj+w@B2hgS(jlmiRqZ_G4e(<@`Qyk(m*$ONnfo@dxPhVxA@UHPM&3z zC(K-M>Fl0fE)Ti~a=y4qO>$m4z=^-*tF@fK^iU~nG8&=5YfzP{@_ZCwD@@ekYZ?gl zn;}&zCEY@^%b*C>C)|HrFL*ZZV)cB{L0=He9%UTlMLXAvv}6-VwYMXJl`OON3@wwF+bf4&k_tNA z74sFDdISYLk(8*2Pm^-lVE9?vQFK*xXeb0&D>^Sq(?EW;jV zEaPI4yCuHLmvOh7_OGFA)*1J!S1wi*+2~C+D;h_@29%8;-cyqajxo zpBAaD$s!dGR0L`*VO2SXzjf?X$E17lsy(&w;PAhY4LTB?nmA=;uxQEET^N*rXUxr% z)RKKc62c}-D-6a0YbG>bCqP74EyLwLnk1cj=5z8a=7fLab^{zb2qp5vZq8zSAg=GD z`=U(fcSP~=xY~O5R>pzHs7swpKqEc|3@?j_F&WjZzF$7m5}NUW*KBM>#jd14Xy>d5 z_%OU{%1r0-RI*}EJ!YIS1`f5emB~E#SYgaECah1uh85JrxVQOG7^+7ytkKZ3Ce z>yjn$WA4*qV_2G}B&!_r93t^!hafG9;#yu1wVQty_qxR}L>g9k!K@98?^*BX)dutx zF^*ee!?t~lZ#ORAWL|hP8)EMEqd9}4?7=QaAj_pPpw206N+D_w7_i2Zv0`!&u*t+E zJ2rTHG@$ubqzsb_2!cvL7G5c7xg2ymL$_3#&Lsy{IK{-ynmCwfsdYn-JG`hV9(2!w z!I*!n)a`0Ml$mp?QIkRTxTi?J&WW<7r0=Oyn?w1Wpo<~E>GyVCV{LlkhcERrK%+m# zJ|G7b4RpFmKDYFShn|okkOZ%g`KxJ@S?nZz&zFQgdgu@gMcXm*V4FPZYfx#Z$8x6C z-g>jiu1MMt@^P;FzT<7hQ(#VJqZTpcPIXv{Pi(?a|DXaiCH{N zVn^{O((YwX6@Mg*;0M<)*Qt{B-oBq$8sm7w;6z@Qh+VQLPH%UkG8^#?J_J&*dLMrh z%~MS4;dB$)H7G_c7?ZSkUys*>JMz=yqAvt9(D9`M2Qw+DVG7=gjBD5rz2dzwPCxl- z6)PR*LB+SLndN1d3r$xF7cMADkQ|M%6g%~!%P)vtWqRckMzOfSAmM*-v{uhdda&;< z&fsNmnk;-&;i4RyK@EM~49Nb7-iCifl5&<^%@W8cdwbXnOVRJD*_16N90CcJx?WoA zH66=1M>TJEj@Wpejr0>TS7M>)%}Q>H&Q^b=3F5oCg3vC$JdaoBol`)~y#Tv^$(e=Y z=;!P5bx-#E%Jr^_sp20f%7=}v)ADe+XNtQs#TBYjmn+yayeOS#o7yLzuoQo*h1ABlg6_Oy9v4>dr=gTgFr3$|<3j}fAgFQfnFY8fIlRwlOtd0H$rQBW z@L{iB`&Gl(b7oIRQSe3;fzxkGs7;)`7X9sjnZoc*V)_ICP(ZK0XLaw%ym@qO>Q#-9 zB^mf#Y2(k8v~6muzn@uYOtL3`-I(*3Bp{uCt>A~jw1(h{Vs(4+>TC>;@sX8<d)hE?C1iYMpd7tQ(Wd$e8lmx3LDM7tG$LW#;w!GQny$#7`^ zAdA&$#0AssJ68d{f{??ADR)n#{XVJgWB*i z_Zzrp(sM70E1l~~U(ZEUN8TiT=tfjn-5WyjPuy!m#>9yuQ!fyIT={86<+%SmyZWF6 z+g*OpoUEon+%+|XZ|`o=^eLDI`XuU_QX$hu6N5DB^sD!!M+4SMCNNwld@d!lu zRZj)pACYaNif`lF0T z{z^CvV%vp%^4$4JVaZ0m`X{yOnRR1lMfUO%)A)} zhnu3?7%TICOcl`yM9I!Mo~#w0a8}W3B`-I{3faGFDg}j zNY%0}cGA0D#Gkrux@sI~AaP`zb{+wAEG@AJ4cf1GU`5px<_SBQm>QV%-&Qf98_DBC z7&ye^H_ci=r|LSUn)Be&1aueO^u+kjrrm4gRE^4im-crbFs=jF(ebsA1`|cOFdX7i z9YZ%elXI-Bc~jEt7fT$ev*xoaVU%E9IIL&I&%Qqo-*SBMA|h>335cxPmg<``gWmxA zB3shP+Y@#}#nMa;AEZcS>i}TEQc%7z{b?#1JH5aX!%piFS4(l7ukZf3?)pu4nkPD7 z$#uJbiGUTpAg}M1g$Ar5|13DGj;UrC9Ef6j)qamp(4_-Ady}K_YNFi{TimmLZa-t@ zi3;Qtku0P9#O7)QRttIPDTbaIXjs>|?3J@pi~3{0fQL8EPfooDNUBIK zP&3}x*KO`N$<@TSI8TRhg9j1ZFCE~w5XM@6)@y(QT!qo%bj@;^1!GEUOn)?^-S6eL z1TCBg)h?!7Tp!KbL;zWLpGTXkR-q3)zJC!xu0b^WOe5uq8`@0f3Qd#zBd12#glQg&p|pZz0#Zxqyb!Rk21SqQrqeoaaH^8weL2f*eoSWui}82XIM(wBwvavhsbW;oA( ziRC`5S;k-Enbikkq8O~(VU6noe3tGuJ*JkEzbT6)s zgP7W6;(|N~DZL&_&Xd=fy%>aAD$1X63$X@pnaDahq6{vlWSY@A8i%QVgi5Dan!`m_ z<2Yz`*)Q8#lLA(pE(P4iVO4MSI*{0)U%jY{u@JgWxO#9*8Pr7A$W0tFd%jE zJ5(pj`a4aeTkYsGLrvv8LuAnNP?(G^&Xixn8VRhuTQE++hvh+c**?3z)rjfouxLSS zW2;EvzeWiw#>}t2D}L-vdYa=g`TQH?FS~U8d2TXqPx(SLMH|mS2%Lj|wvdO#k5BUZ zY-o_IE0Z3Q`aZ$NnW1?YNlgZY)Sbdadwe(bBWodS2V{Rd3z93{VBIRj-_6Y`)H>S6oWv%~L@ zwK>6Sbgj}-SA2YpuD(ovMlQm|jshTKB>4NYS9i11PM=LH({2#s)D>8zT!S%^3l$hC z@FD0+%AV*U6QI(hVR`*1S|sUva*$#keyHSnoh(g^u@@jj$NFrDv?;jU9zAi3Wznqd4ThLcX1#Z*$8(^1Xa-1m* zIM|QAHWI_9)@s3MYT0t*R<2}`)3#eDD`#k)G+WZ@gGNsYGqztZ_{H&ueixk*G3UY8 z)mXW?6vzmxlN-5zWh672ZPv*mc}#9pZra}#8WLsC<--gx@{YgqqxPs)ns(?^pC!4m z9qa^|UOjh*tB#F8L=pa~4-?IsT3WWZedYJol_l`WR|7a1qPJ6U_eG6<0@v1et6CXV zgB>5nkI|xAxq$ia zMz+KC{R|W7C4Ok7(!mN23AiL`dii1c3##m7C&{4}0fUmXC7*x`LDHn)-O12U)1(hY-&wD<6b>5_%5!#B5z~}RMtac_d*jkS zXtkW8WktAuftwLH?Sdjk;f}(7Pi$~~!>e{*;_)3*sE<`Am-%CoRqu&XtfA`C6VuuqSvAWWyt3b1GL=v9k_?2=N zk9Qv?#hdE+(SC@r05`6-=3d!}u3DtI>hkmawUj@9QI~riN)gTcx(Y^_E|(QL-2>{e zPMhWGIHCse;gn4c%PDTlJ=?A()Lvl;$&ZW!JSQ?&?&xGg)?Tx;vQw-ql|;7;l|+hf5YC_%I&oXlWK)!$bd3(&pehtID5@00!jPvE z8)H_Vdu7}Ccy^A`VWBni$Kf4RNRCU&(pC+{2R&jT2)aERT#h3KYIn9I;S<`DYiPGo zn`qJ5*MVupsMB-YBXbK@t*Nih{b%Yz3-J+1#4*+y+~yd~N2X-se5XvHL_%Dx<)Za+>Y_nz;y) zCuo;ONjc{h6P!G{L)JVg$H*4+tULs;JHNR@?^KDUo3GD@cnkg~K@|Vpd z4qU24nBY%8<*nU8$EwZsgdWNdGMh$A5lsFYS6~c3{CDY@>*s+JovJqb9g( z7EkrvLJviI^!Lg+AaNgH7gp30%HReCOlE{V!6Ziah0R#3)4wbp6b`1R-)(FG%5s({ z0$qGmq@I&=JFYXSIAN`STvDZ$?sp@?7O+>J>}$I!Y3d3;!`o*Jid)ha+*U@nXX*c7 zT4ssEOoKX>NanvWN3{iqQR7n`#pd|5h>xduyby|!U8XB6VB1fAvcnZw#etjkzE!oa^c?aBl z6eFCP8@TT#x3ay(WS5KQux_R&X)m-(suUF|I~k|A6Xe2}xHETM-*ANrNindyhilRO zSYSV4HY>BQ@ZKkX|0Es7tC9)Cb6tj9U+GB_k=vALlQeyFKwIn_{oN^VxM*eDU~Aog zGJmd9i8-&W;x56OPXt#RXB?1|Awa|Rq%Bz11Gj?J6Eza`O|nOHPV1%R+Alj+kCwJ` z;5X*&qJ^Y*(uTwVOJb1Me#&_?dWqdV3D>%S1-54D@3oeHz@K37cX)nh+{j=O;Lcyw zK*%9wVc6<^9P?LS6ALK4^KOoD+x<&L4cdD}shxN~Y%wIn28yT~NdFjmgNn%iN%3jT zkhN{xdhN{|drG8ZO|fDg=lmF*aRCAlhx5fi!wMuo%|+Y^vdt<3AOc^Lg)DL+aEz zqkmm3wp!)q+JMeoppEA1o8P4~(FmHtFi7BLVIN-@arOO+ZPz-FtBSzJ8wziOUNabj zauV-yY@<%_8)_UK2gZV9$TG`Cxm->1r&o+df#V7kfNJoe zgwpQ0nY2xl0EP@2X^BVz^-e-L+f{hX4yE_$MVJe2zgeM^aZc9nsaU4%A?L9^T4~oc z3CLj5VvX_Dz;3mRG_s8NgOsW!JqT@aA{~K$P+@r5?(S!XpDLZ&&?PZ=hL7Rk7!HjP zqBXv-y=U4vG&l$kyjc_R)#M87aaPBk(;F-iKSA&+!DUrm3~24bXBGJK?C>0 z9-hNL2sg$8S+vB`koPMVq8?bL?OE^qnd%qm8b3=d{olMLvQYK7wW#hu)E(R~{!?Rr z1%61Mq!Zbpv$dQ;0eU;8*iu|`%>jXW-PqSPZC1JFW_1O8n(sPN zB+rt%F~W}~ZK`9A)sY5MSl-TMSL7E3pP%IOf6?^U#Jr{2DIAxYUX-HkNV)?*5&Ny6 zL~tg1bCMBkIIr)S@v|5{_#vDBSjFmp-c|5Ny_sFi@e^B%>U8s(IqQVFs#-up=#_0lAlA7$nu zUbAC0?DU?feTrH#+@QNvNC;gDEIN$D{Z%)dZm3DD)_nDF%8${|)9eGxFAD&FdlNSZ zebEq9!?Me=T1;fye7Hr0VwSNaQHoY}#1YdtXubQ@ymMK!%2SI*_q(eoMpZ z06~E@%V%-YJD*Ss!QmeN%;6x!vD!EKBgSZ9K#URIGd5W{R1bMvYkqlu|Bf@ECR%%Q zv*KN;lzKXeN!S7CEvAc8^jj7ev8F%wW`^;u&BSq4Ffpxrn)mFaM&oc639rf&`24uh zRDOj|#%bf@%GVHd%HDx8BR*@>T7mYt&vl@k*5850Ls(DAKJwYn@N^O90_(`8E26By zvFSoQTi_?73C8o#*pc0Ttl5d2zK{BAB?Cv+8g!MfgulcihM@9LWHO$W;if8oeT&xy z{_0PRl9h(|&4Ea;?;u?uXSSm^(`I%=2%$I#Wp@0uf&$g#4DUP6c@4lZz!`%Tc?nBn zOcaaY7S8L%%NV4DFV33J^)u%x=yY(Z2&2{gC@OU*pr-KJOF}?@x?B%=c`_*=kru;I zQIVKOT*&{RC(9SXb~oz?_7>^;X`SGQ7pz4PaS2m*?@uXg>L)`+$j*Bw>#;N|*sUYD zw5IzF6I7oH$?s{`0m1qG%R*S9#*N-nRu>xRrhqGKUjSii!&f0ElE`|b_k*`y37 z1H6ZF*#=)CsEEAXue~SW=J*?>dgOI%xG+Z{Rg1aNwUN8ZMyPu?uh+w5+bb|P+;NJM z)erLCG?NE8nsr*hnR5vai-q z!jM$#J6+R!KD%+2ehUD;{*(wNtSXo;$r9ELP3M;qeIaC|>?my7^%POC%nG0iDTzS( z@ha&K-lhDjv0H)zzO2h$H8274VOTb14eUI;D0IX<->8}PO*tMom4p9E*DoemIr;?- zU{`gsPaNBS@)f_hKF^;!7+;kw$bjSF>iWwW{im`1&ZisA@Q(B3Uou?9+CflgSS$X& zf%v=8`!^0UEJeb07-I#{5b>8!rY$^_1+dqGHTp3U<#S}T01w1l=#gXlJQeyOe z8F(bwSGx|!U2`kCM0WXjO%GLohp6R06N8re)%y;A&P9^ZHie*19yq^#*%;$#WhmsN z3WMVtff{Zq7%$L5f;IC%V`(O>F*vk33M=e|rf)0pb;FXjejrtbK3FhChx02u*wLIH zJXvfaUqZv37t92%P7Co7NC^ol7HuRlR>>fjwHnb?{s)aaa8HzGltbS~XMw@5@h4r8 z61N(EFNH@D6WaLj(UY$nkosq#UA5FbhXvz4YPacwI#i!7B70jRkr0@wi#z#2qeAa? z4cafg9JT!^=Ln-GdGVbzLI!_{*T@>h?2*QYLj zHFUiY!sT9w3-;+!mZRCM+Ak3NoO1mwi4li(dWA+KC9MnUtN~{vQw5ji-k1jrPHe`+ zOHzl7OGBaMGtFsIK_Pq*1Y(vn`WWl{k6k9hytf;p5ps1{F-&k|;RUyA!NyZF>p58Q z`5d-(h%MF%UaG)mT6|pflL$BZQ@Fikijjo~$z9X1R(DHpt-3}7aCPmZ6SD%7z;nOsEF2c_B&z^1ZWpBB}tI#N_ejS!q1 z@C8D({GYN5V0(uLV5u+Vnw{F*ib=MErBC)@UW?w`OBeJAH_QF^%d|2gTPAdW8dbw3 ztS$PW1%|kY!iUG*Bq)DN6GzaP8+OzYeb#DV2V{E-Ty3vE#n*)_sPW;NSQRO)@YFYF z6iwcmq{A)FhB^Fv`C$Y0WcWQU7+AHLbTJ+`MMkYKC_m`sb$Uixd`+QtFGF_`Rd-M* zuRP3$gaGo8(sSEF96}$YO;t>z7-Y$OCo#*T~(o4u_xH zJ)5iv6do7W$dF84%0iS`O+x1~qD~`dKVFQO_@vauU?xirzr>;sc+ip*6;nqlrN7d# z2**#flzVU0f^*9n$O=kJF=C)!5T6``kt?19SN2dzstXVd2p8QRo5^v1h7I77P*--g zp$!0j)DrZi>GpY%D#X=et}>kJ?IC$#HI>9O*XhUXHyIU;?r^dt?I-egS4@IQb#bqg zz$ife9I875=~jcD&Lj1h73B%0;N1t#NR9qpGl%0|%q4f` zu%reBQ+}WBzR|%Y_V_#cZaL`w*fYz0IpPQOY!|5sjXuR)xNb#%4wJ>Ra#zNYaI6Va z`T5egU7#uNYPt~qKJkwu3?$7c4igVoIa+&$$D11=6!l@)B8$wKWjxI`&|eT=Rn;y^ zYUxkR%I8$qK*f}7i@9QAfuSE7OJQGuTziYqhTsL(J42-cM$K4@EIl15GvKryl8O;= zbiVTSuLS*QT>v|O7`TEw%k_wd?pGImc(JUYo*8cW|FmsR+m!$a0IQ8#+qP}nwr$&- z+Ecqz+qP}nwzd1VFZ&tENf^ru7l{Mzs%ub+$6E*&@OtnYMNhA$Dcj>aopA|MC-U$C zg_7SGzX;xt+G+|JZ>u#~+x|u}zo)Ur;o^gTw0Nm~8|%+pqS&7}co%Y? zxW4Yefawc^1V8!~XnsmhcmH70+}Vk70H@4&UK!Jl&cKLOa!N$p@@#*+ZctR`G;|)m zCpl$A0>`XvTPS8QPZ4grrmh+)f5m2ref7D(ZP$IrNB4Q82pplogEe83oC2Z&@5I{3 z6`@+;($0r})-3jT+yF*|Wf&ku^zEM}h^s(KrhOi4$vEhKdtQ#4_~w<_j5Mo(o1Qw) z;+R;@#g)BtF)VSVOZR@TRDR53*7(ck_#ThqSi>?o|MMvTh}+4r2vV$y)n<)0XDEs{ zdsQ@@Fo^be7j0LmME&c=j|h6#uwdF+iOZ_D90~w`eS2iBakv2S=I>B-4A!;oE6jz6 z^z5J0bHfP~W;KXBb`cX!&xh`qBV8Q&FGF2fQ^XYoi->K`b5??wgWso!4wQXg9Sr*W z5AXGD;VKJ6Am521Hd;+lPdoAAcbR91q@tdp-R>|ESO@gq)|q?ORW%3!5t$Y76IbQq zbwUq+i_8P#$s@RVsaWnvdatFhmr27mdZ}VOBSxK&I+{vX}X8Z`P52R2Hyx ztzVO41c&0&_4}E1xa9i=#}6DeKMdjGPSb}Q6!eEU5Cxo$mo*=GA~oC&$;j)RY0>F% zKsfOPo>gCasi;_EC@`Y`5@opB&jk9T`cZANV~KQUTz453w1@JRJb0V1EOpb1MfU2u zuMrZP$~2&_)Hs3+QLm8>!YDR>RorMVbOaE6i3XC$o4`-*$XFm}?4+`U=;o&};NO@J ztKtoP?J#8KF6(8{Gkanzb|BES$94gPyj88ExMAn_c+V%dT+e)r*o2>By9$5;o@3^V zK4ojO@JVAqF4*|XSohxyy&gM5AvL# z#$oXrYV@Fbw#2|%G1pvpZ?UUXsllxxtPfP6`1s3PiR}0hU_k-pZZ3JE047y@d%WKQ zv}|I(23S!2FSIl^9T;lickH;`jw2yKCv*E_6?s`3a?P3#UR{_go~=^ihBwJ}8))&_ zyJlkSWcOnVIS8JkoI1*XtVCm-Qg4<+kdR1}7sVRJ>aK~&h>`#~rNB=%SFbq&ggt2m z9KebK4lD~a&IjOpT0`Xo?~&FSn251{KFwDJfyV64Kz zZbezaSjf@YOau+{{E|aOoPS+8+(z1ZbAGUmv&Y6wP(ht3aWgJ|boUxb%hAk-hk8!B z;>SqPbL-cJ z;WmdFeQh^6Nw$x4kpOD_&F}KSI>+h=fO4QamiDTk7XdqZFdx4V5u=}@P^G)WDil|< z`ZKFF78|VYQ)iNY%-sbooywxSa?@rNmPcD+CI7UY4lGGhBngIXJ=K@$u9h4E@fCln<$UO_TxoBAS26bVUGqDm7{+*iPkQ3Y zLT(>Klh8%WMuTHD|A73F)gIlo++DPL5@0lJ_MwG>^A>=nnAOQ!tMT@eYr;R;PA%4S z<(beGhTF-xqS5@Fyv_?U-r=hKzQ#R@rg*a#{(>oO?d-81nDNbroPPNFn zCZW6wEn0kk+CP{w-9L+?7a#b6T%;{W-EDPP0|n`DYneR?YhB+XAkp7go;TRgJ$k?3 zCauM=S%72UP5R?B%n`z#K@~(6vJ@zam{}hIF_`*U7oiNq4M-U$<%3JoOQ5Vj+$$HK z{&!)JSe@YFY<6NN{D-kjQhKF3rZ8@GNbAt;y8*m^ZRT&o5qeo*){umwP!hnz0Ut6&dh`DE zAeukvz_;i!IVFLPZw`36=QU8$QQ8a|^Xl!%+tMB>7SoL950IOT?nD-e->8RXA*6R8 z9L8F+^Z~ckVQ(ko4kGlX%P3A=B410f0(}ZZ8Z9zC5M{oVd7&Jxz`xuqF@dbhf-E7U z8+)@za=PV=)lV1|J=Gi#q$b0Ev0zuAym?7{ofR4cUp;1=f9Ph;wL01d2r@XBT@~)F z4WwYabVBi@Ax}&l9%TyNlQXH}8?#p=HefbH49`c5*!lqd5XKcdIh_aSE1lEY%MFk1Zy&xyf2|4U~i$51j3<&eDC~Ai%fp$uQ!hTyAuu zZOP$4r!A^RPUrI5s+BY&{KQ0=@f z2)xFR;6I(8M1@_bbMaq)>wR!qWfAa@ykpb^5icvsmum{L$W<@-5*^TmJKd+E+USer z_9W3>uT{?#h`NWHIZ3sj72+E#)2-C)G;cDq`@TJ0S?M%4-0I)L;P~3uBdb7F7g;&b zU66Xo!u3`;@Z`&R+;;6gOx^Ee$*4s<=&ghMb0HJoVwWk2oH+%5@CWV0L;iA^EqDAy z>XaEZr(^*t4eyUw5B8c|>+~2yv&W*?hciW%#Xivb+DE+o)@AhejbMYPQa@^ARUZ(q z2z>}`emeH~{b2+*A+^;7(K--)JqVMB>c=#NGpF%%B`4hJPlf(3)jD1hMK|D0gXbRDYUhk;iI*PLK3Sv6W zz21EZrBmsL+}PYDyRaEBA_XYQ0^+|o4tyI)k{m9=90>%~J-n-V+wfG=#&+(DL8s8D z!5TE=Tw`+GOW+XhHp|uC^O)p`pMnD$4D<9F0jv3uyiS<1m7^DeZ@46PecIYFEpT63$N98%w^(4 zZC{N@6kAOZlw_7tjFbh{87_dC#jn6(DRefo7p&JCQL_=S0@kK6dZ_fCWYBIR^|tP5 z7w2%tpDp!&e6xR#%TTUJ9QW_fRxh2Bngieb^wFCE&MFHLK0D)Dwmd0v&IUo2c@RN zQQ!AYXDY*6R-Ml#*+IvhMI-p(maB3Gs^gcVUX#46atmvo*pBqvQF#-8Rn?474WqvT z(-Rke4ZUzyx!FZ^(wwX^|HBJiM9GnHwYL)`LQEg%lp-r>;=(J8axl3WJSSQF!NpqC z^>@`=U@b(vH|`E5lg>)Y2rS+xHeT>uVdrwQ9dkPZh@V)FxUczM!@iFB<6;q@`Sto=Lw!;k|4{3# zlVt~z)jQRgxds4b%IeRg4zc#+34^y^8q!neKBUYTkVXX?epc8_INAZ&=)U0&3VXg>9bW0qRHyO2niCr>+m6vu{06oS=r!JuP!w=(&E z1eH&YwV*_%=fnj95 zW!&F>5e+-)#Q^aG0$@M-z4m%ftrpy~yr9r(OW|Mj2k&x6m&UWJnXpKOtxcFfC1hm9 z-bJt38>FNA3HNRqo52)Y!HjuUc#0u^iELEq??Hc2Q6e$30a}bd&r7k@I7m;`bl);3 zSMb0)qt2tJrIUF3QH-nAYf}y6AW=yqC&U5Jj0t0xHx4DEP9{5 znGI9X_VDcl7UT11`BL%sEX0tpN3-~;qJVTuQQ!mJL0`o8&1^-}gCoo51c#=7s9S=% zLTS+~JUm#5xv{&(x7ludpDqPFD!bJs86$1me%>++6OKF+*&*2o)F;X~b&L|(1pWoiUaaW;R+c-akLhqN=SIi0_ciHU^Yc@sg@ z3%86|BzP(`Oht+LjIs(}2$>9j0X2A%R$F&m3y#!`>c%s=KY(AVfaJeGWi;NNEq1Fc z9at6Va|$T#SX!jHfNansJT0HtE2t^#kVU9JM zc9&~188_kePzaF{k*Aj_?VoN^(Ex_bU?n6{<^K5CFEhC}6?$oT1Cv5S>7s2CJ@(FJ z!BdWl17T2m=>lT#LDZ;09;x_vDRrYhh*Y$GgukhOcGB5}eH6R;`b7n)A9->W6uuhWOgEoT-pYhb_r&2yxRUPVG<9cru_OT7Ke`4<^)C z$ZGzaY~$MqXLw%iS5N{49OSv7wi1aUt!~Z%CVh$|_{8CyuDj%aLcuuZqX`T&pNwE0 z0G}dJ+P#4#ux+c%3MMnrU;Y3W^UDMz6c_mLp>)+ToQg4ideRk4tgbM$RKn_!%n9;q zy|g_d;898nr!P|HL-48PK006sU;4^)CwN@wu~Y=VE+XqjEhxWeil^7XAmz1^lc^K! zZ=eF!$3cz$Q*)bt@_V8JdU}4XII4>TmEp(Rx(i0vkT;sKmP@rSPO@uVOEN#VVl_ug3!HKa5j`nfr`Q6CLQbf^}IrX%Bd#I8z&lUsM8xDDTbkv1gK?KiLA-MU)1) zH*^%=uzD&65-F;AE_27@qfrm!@YYPs=KjdO zYc)J_lAYv--hCnH!5vm?xslpQT)gd$K*oHQD3vM}+mMd4QwpkJ@`aj8SA)CAUeRY+ z)oZD5OB%s{P)>)r%P8c~J%0#)go^H@x6;Qu6-KmoRSg~oGNqx7rl)_`L@F^J6ZYXX z4+O%$o03+xxfOb$P+G|PqvYwc2;Ux@KatD{yylcsvzW;7Z|sCd5#H@?pjSr|4~o{y z6Inp7`&U0-{Tt`3OL28$TjV{i{EjIGYhSSXB2`j<$VrzJ$gC&7F38}*7i5IliV~2Y~0c6`M;QeSEnxzwt>dWjXFdh>Po(RC&u-XyS-`Qtbw_< zuB|Dvfh!{;?2K*Uccla(l!f}KfdNd^ykFAgE*|Dw@^;gNG)%|ngf3_6Cwnr{SmL7- zHyoOO5e~{RF#^UEZ-a493-?ROUNzM8^i49zT1sty8DhqOA|ZJ+eo>jW6N6@} z?9{}+a-sL6^&EGo+R8>2ku*7?d1N>B{G1Y!rEslrj>||1fXEe5ffc0jv;uKk28DJ3lVW2S z|2*(bQT?%5Y&t;}T6`rjc6@rftTn%x&9f!j^0n}CXR^&D$|;c9BYa;98-=ZZG!ic^ ztQOvXn2^r$Fl)g=s3kP+nu4(;lvTX5mORNp2O6+wcEX z*a~GdRjvlMZ6eI`0$MMs)jE>cYlDxzGLWRtIe9Ww_SaeZ>>cV>QjM#BMwskH<6BOT zh`1K(^LU@=U7D?o=yH}7^(7F05c!@480oHYhv<`Ooj0&vMNjPWxg@3hGDK`8wNTsB?+!<;WGMT4M(ho{s%$e6t0lmw zTas;UUF*^m-u(t+e{`ZAO^!Rnj7Zf*L&ImkbY(u;;Wtv69D|^1N+17!0jA55$d*Mp zM>C4Q9vP=CVsqd|MtvAAGvZhb8EaCQ>#E9TufbUS?bh?o`!kcW4IjckUeCC zUf2t9Pv((NZP3`Gx&$15Q`56_$#JGCxhWYJ5Mzd3ujJ-9*CsG1bB`ZgyK|U{7QF@; zq8Xz#y7?VQd84giv_1c^_7g&Ak+%=6GpOVn_snnUAF8L>=9#Xe(6t?}`?@O5na!BoUrh#}8Pk3n?Z&dJFXla>$56%OG`l|G6E}n@e z{v$Z=DP4FG=EXnXNG$d#rmhUl(CNs~h{)SoQ&cu?5@bH2w5Zunr<%>c+ zefn5?LO7bGa|=~xnNU|1t+G~@Ar14R=Togz;f^xg?BcW76T`&b!~)JdCj z*9Nsaqm)y!{+v5RKfp%9%is5G2HuLWm#~sah4eND7hVM@bQCMgZ25^TRW0a*%=j63 z%XWOFQAcsY^&yyX1z3Fzvh0oHTKn>os#a5?)WW?$hB2pqiEtV2O~HYvs)8X)GJJTw zX~%iNrt}Vxa9*)>d2``yu<357*GCTA{ztaNhcf&W&(Qh9ZR|fT?nW?p*VhiDv;9?0 z&PuZPPg5xjG8s7riQ4<64M@&&^n9KNDoQzhDM+C5GKL(jH+Ttkym$hWnB*Q~4`z&@I{1+jitxgvLc_|2VaVu=b=%I;KK7xumIV82j>0zI#Sn9#G} zv2EPFR#-hd0hlK(#G>3d`%&}=f_&aSU6Y7ouIG`K^Chw?rVdsef~psx}sXG0SL1Kd*92!+1NS{YrL;f!QI+ z_7s}Dq)a)}szDi;y4*B#$b91nx?>~axRN5uW-DMOv8-|IF7KCxR2qx!DH1Ms z6O3hl8c342+u`rmRMnL(JZy`CsbD@@h^+{j70fW^{H0|RmA^8OUmw}(7H=}TFE^b)GP#+RF_g{yM*98bA1j958lpX-qEbloC!x`k~QQqH=F zR7T=Cl;a?SsI%f$vH{N^k?o&%u3U72UJO)!{BMEt?Wo?q6|lCFiPSxjCIL> zZhZYL6*mZaVS%(WjLss5)H=Ui@e6gAER8xpob&XW_Cf@~%f3_PBTYov z8}hxYe08V-Zay#!oys^B(5}8!HBks(&n{e#wLQgL*$2*A%ud$;3l|t=X7yGZ^WpA6 zN9Y$G0^zW{jK9-}nl$^{HmK^Jx6^LI_rDc(^EwU$&F7&G4UqwX?P(5ZOKjSI6#S?! zZaCUZr+|Jy9DBuuShwmKI>}Qf);aX?DEg=TzN(yGw)kwL377MG%EI$+Rp?9_>?e~v z&NPNr1OZ;?RPl8vQINdq1_g;_7b25dicfR`ztzc*c2d*d}2R`Dwj2e@N4S))UG^&CO4^ILl5+S9c3Zw<`)Rp z(e-hym0NJNN;1qIzv@SbdwNoJN7K*_iT=wcS&A0CuifR}Awa2c^dERU;oE%V8| zqNAz+idaTB^jNe|avJg`^Y0?~*MtCQ^$m(lFgh^~n=5-B#hS z2L`^*v9CteJH-EwslFIc^K+?WP-6GB8v+LVik+Fop5YFzPqPLh_8@h9&&KYV0_lK$ zL2aBHQBa!@BlU%UI5{=9KR8t7UDn|H3qE{miKGjVB%cD*mD>!=QoRagtRNul4D{ym zIW1b5#)&?7` zYCq^dM451ZOuyX9M?Lf=B>MiqtR%fvRC1ot;XWf7Cwrwk%lSR^pIj?>axDWGTT+Xe zC1lYx!nS;%wA2q8aeL9_ACW`1glhh_XF_6)w>N3iw!>V(VLD%6Zq95v*4g_*IT`(wh}4%I zk?He8(ferv1t{*Q2&szhZ}N_vEN%Kj)ZCWDZpbCnJ>XT$h(P#_r9+FqG5TluJ8UdUYPuU`D$_F0(g=f3;{V9)5rq!q_E)Nty&l) z?}ur%FE7gzwIm4vcE{JWbAZ1@wPL&@wL`;@JNR3-MDGG0Bw|JZF3*kGXWwuZ;ZjH{u+B z*M7Z<@o=Paqp~jKm*a`0fTZ8nF{zW>L-F^Xn8@y(Q=`=30&W z%NF$6h4%&Ka|!lCFg0N_mQ7;{s<>f_8GJSi5}R65U2AG2SldwXWWFavA=hnus8u31 zfA^fY{z95uHyLp8l~tS23Zh5Pb~QjqVyr(h&lnd+AOF??iWV4h3qUn{ZEw1j{2F%na4%6E4HNq zf!kc}k)BgMNP=hFsfHAXvlgUY%&dgvf5g!PO8YB9RdufFpFR+oFYHCZtw5Xcdr{3P ze{?qw=w$w&|9}!QIKPoJnoS+`7AjE|m^^-g(4h!jy`e`v62;e*SVkwu@cLqclxwSo zo<#^v#NPXlKXIP6Z~*4hIXhLd_bs$>onV6=$c5m28?hEyM83gY5ra5q=IwOYf7z*= z0SOMrX%2JZx}#Q%uI586^(a@q;AX4^j*9Llv$f9BPy}z?>8YcCdxuM z@O8vs<(_oY8vDpwbz)=O8Far>eGRhLbMT60ag+Q2(~Mfpoqq~V8eQ+WwT)kEKTk^A zs1~O0I}$xR)_c{&qPw%sFNJlffBCkOEd;la^<=7|SBdR~X=o?VER7gS!To4w0k@GX z;0fpVf%G>36RbK16uJHIB+D0VyiFhd1HOliMek2}#DfH`M%~DTNGh_WSvsCAASXxg z-b$HADH@MI?@P@o-20r{nH{rpZqo#rP}<|Rs(W63RG~Iq!&5XA!j=<>f3f}8F4SV8 zr(7Oy=C@tS0|HEDPOGC$5@7EN3LT=O6g@kw2m*C||9rk#b8IPq^LR=JE6wl1*H^&D zkPNda7k(C<2m>Q)Rx@5*pzbU82{bzYRK3aa6wmU;YXg!79Xkr*v$b$kSX$Zrj9q!d z*|lY+%xNgKoOxJoUTuCMe{s;g5(ZD>8 zq1%RcD!RD`zqxk$N52eHj`qiPKvH~kI}zd&Zq15|JS_q@qWG2~akb*HtJECWYHITauqb!1RlnxeJ-1wTd#z1RT?<(Z;XiLe`Uxw8IX7;x5t95 zRou62y8k?^BVf>ERDniM7m8wMFSlNopzh7mmF{kQ5;LcZzM9!Dw z%HX?eV61ShC}?Y$b9pdYXcM+M9MV z^8J$X&b#1W;`;S=G0ZI#5i~s`T+2v#m1{jGBC8^$@J_R79ixC-Gx5K7hoyhx#A6r zw5QNuevXNPI5uqFMqZf{PQY$|HCdI!po3A%GcVpqo&oCHc>fx1v{d>}V}MCiw1^Le zfS=?}6}D*le@qKavHZCx3QCSoT^wqMpysfu9}3%K%y@86JrUSYn~T zab(5N1G4=ob&A-u^YU14;7j5RP5{M zz~FH{L)|o6aMWt)SxPKQJjeu1fB{1C{z&N`gp)-%Y#3qt$w~F_SVl^& z->g=yKU!?sCKJIy{n1iEV(Z4bwPHa;XzB19fAMN3At*GE4L>|baE_#jJcMmQP+vR!N`AIv~kTemds9j;jx`Y1CJ z)z>ZSV;ock>;lPwvy%PU8@#ccN9-6Ewfjaz6_(_oeprGawzf>QeS zx$8F7UoB`VS3)V&d3Utdwi#Rjz191@-HLx`94`WQBkm2f1jAC|?%q2U*svspzJW7) z-x8iVJHuv&gedWj*Y)w~D8>O!f9s3~)E%Gfd2|WJQ`5cQCx)@QU~E)UTPh+*dk_vn zzQERT;XyY=!7|8vnKc9?2S4FX;>ry$;Siub!A_&fG9UB?9NWGqKEho3j12%ybUKrm zCmqAm@?|W$F$}$JT$s25!YjZx8E;<0ObZc(wLvPTFa+WU_((Cer2D7rf0jL*x%koX zKS)1|SiySTFKt@%xit>>^tUVz#;t@uv7SiR+EN~bHo=OOUM8)Zm1{rEI*UoW0Wcae zjDL(b^Mc2S|AAm!uT<6GXr_Y5OnF@;Lz4Nfguo_c$)qXeFMe+_cV;5*YrI1XT%WVD z2S5p!O{}D0ZRyDJSZ^T2f7!d6QM1zpl)_J=@E1ToKk=V@_TMmC6N%pw3qkMF{{BMw zQ~-%%M^W8et8ao`|?(C%qJilABj)af5YCY36NPEYnG;78_0&O+>e}|#0n(^cP9{sy# z$8f=P4%_ZV_e`k)@PI)3JkSv+0`PRfATdjk`dpX8KZ|<{6aw zlKm;l9H6b#26l!mQS`g>-H|QGOyUz^8=51|GCe%gygofN7P*%E?2kQGu}_Wpbc|p3 zR;7Ic#dKgcg!As{t8j;NBo@CWVD2GE89zcPleASS#SV{#e@iXp$hPX|GsyK>=U7>F zlNoxN!&`ye*L-(t$zVE<49pNC@I;Jz)n@kRu8sJnopz(+K3Tb#!JPcx>Fn6L)cn`N zB%T^?dr(LboWhOiV4&ugVd_{jGXohblH{f7gw=~@dy>C^GB%IMhO!@d)ev^{xV4rl zuwRz#f*Z{bf5O1O-(jzxFVfghsOo!J62#@IY^z?Gokd55d^fKozeBmvZ`E45?07~D zu4B9i^E+OCJT~TQ^)x8Ywz+NlYLtx zuYc`nJ)fDnhx<362~PJ0?D$gnb0s6uv!^wen8_`Oe;bLm8|5=CdASm;+t-)mSK#@n z$opO^7AA1zy4{x1!!28GLeqI?907LkcsCXsVb$+p_;zjq|%ulZYEQ&#^TfA=4M^7lYa;`??Xt0_Hn4AuStC+|e)^`-0gj<*kX?*0VHCAUEjAldKg$Tu2jw_Vx+{yV%< zZS}EQqJc=9A<)|PxZ?LP1P#9as{YdYh}lS6*istLy^Zb{wf!x3Q+cu0P8IyId)WsQ zL?6Oivv+^pThsm%u+lY%x9 zP>BNGH_j%yUtP@lA8^8l#*@)K69O|bm$2po6caNrGB64+Ol59obZ9alGchqTF_)2x z1r!A{GB-Chm*H0gD1W(SRMp-3HB3n8NX*C?$P3_dwg4!YID=Ec5r53gJctwkNgxR52zHtR zj6DEyKxZRW4|^aBfZFIEplIjh%wTNf1a<>K=9VBJ4LC*A&fde((%i!NcMUcMhToNb zXNxcbWQtiA7Wr3oPXD}`Byl=2?+eJHWo%s|K!RlD#`+E zjVwXVK#&p01RUsWsY!f?>lckfh)4wVL0cMsqz~Ahhe(#wj=pUP$u)MT{xQZ%+ zEO_uh403j0bwG^H?#};2|E?!2CJP=#E^YuTD=UB*Jfz|vQ&BrxTQF-U#NYCXS%P(P zwsZ7g`hVuy8f51N^7?=6W|kmRv){^_y4W+RfqyI=T!7MI|BeQm5dYZBfzAMC0MG#d zbT_eJ`rYoIY58qt`E3Tv;NxX)XAdwlvT*|XSegOBKM=i~j9h^LXGa&HkJsOh|3-)` zTmVx`6KC+CgAWVhKh>o{W_AFcf0@Bd{?GOA5upC(pwfU(si_^v#sgpqG(%*Pw{r#$ z1b_AaKj-X!dP%t0*vK2%0;&I3(*KP!vbD7F_^-(SiO>N4=1MJZ=V)tW^FKaICkab; zpsAvzvx&t&D*nqX?Q8@dePNKf4G_F1|CrQ%pC%jd*n+Pf%ilK_fPsaR>wkRUi8QeW z0iB!xoIL-yfM6B=hc8(A-)sR)VltAVAAhCj{-2TgCrlh;u^g6y2ZDFAyHXCHu>og?D!&E(_&FbV%Q{R?pdm_+^{ zE&!A0AH)r2@(1w%n8g2sxR?P<5`Pd2fJyQXVg)cs{XuL1Ch0$j9l#{>2Z6bK{C|VM zTx9C!T-5#`FcA;PQod0Bi*JAt!fisN3$H2+P$jRbA zRu)$F-)6_Z3}EiYjz%WnJKW6qFMl(~zs>(TcK@l$@-MSB(D}dOdD#AA|DOb~q$dAC z9AJtjb~fNq{?EeLe_L#A|16b-8LWfpUl809_@@F`K;VCM;QZ}(a51v^kA)3P20W=Y zMz()d0n1_brwTa2%+mF*vcKJSE`RC53=T2>(**1{|NVdf{v875{zqDNuzz9}9`+VM z&|eW?o8?~+Oy24*2;O4rzaUtgKa1c5t6=l{T>tTdrTl9}EZ~4YtvJB7K;Zk}j|kw_ zAQxNX-6H0) zvQoTyzKNeeg_;yWm^Lq~%?TA_*QQDzwv2q`eDRURg@>}#8Px*P<>viMe~{7~$YkYe z6|qmIx5Ty(0n~;LMS0rV@1s~pU(qg;NOenFmUG^?6-=`7;RV_$!`u+8AsJZv&URF7y5w9{&BG3_l%|Wa0ix;8YufCH8EpIol^-&d zPw@+k(+G=oA~N$ZA)Ru=Ep4n*#tmiXzZpO?4S1Cjt0dG@E-KY46B^#B5Fu-e`sqU) zGz1F4pXgeV*niTOboyHzTC?en^M?*`py^RpDz_&Z__3!AbSY)Ee+{wn?kMAA7k$kS z#LKD_#v%CW%r(iPlPwy_Ko&YW=U>&IOx>>dG%cSQ_)se;(Nscggy$VV;I#eK-l*YI zZ85wpCmf~mVJi~)8XmhmvQd`N!Ca=+uM5VJ7!v3i#D7vj-9+Mc#{&@d)$#&CwVWR_ zQ#t&qDywio+R}wg0$hz@uMYhNow?h9<>R3Yg+Bc?1rlKNit#>RKPG7>ai`6r`g;63f zVd@JUmo(J|%4}A`;xhzG#uqpC1o!z0ft*I zm++7CM$f8wR9nM&_#?K!p8=m`utzBM)w>w3n9NE)@Dy42G$IfwU&bL|G2;V918qNL~97N*=Zyd(bM;4P#o zs-v*tIhMnx&sI1;@tK5bT63~iB?8;qq?9!G3oF#b)ly$nF5;CpD#^ZCHAbE6b0N2q za#?huReRhRWe;Ae-_>TC;rHI>P*FP5`G3$JwEWKCCU=2N3vHDtyEn{J<$R$q&Sw5{ zR%HZ2Kl4gPp`W8XFi}CB%f8wZBdN4_Rd((HeT4(J9C1IF?g8@{XlEklK_<@iD6hem z#6JiTxgXAOf&5W=x&+f^?+xS}bwcAvgZzTE3_rpeZNbH#-k_tYFgtU`jW4)_L&qFDKJhA07O$p_b$GHNMck^0F87<-uCnA@_S27fxi-;*O#TO3&n`A8T z;@K2))t?KTY{$qntds@VY=X&3hkwHYR6T#@v= zQR3R;(%;MzP;`6|*_RMMK+-ou@QR>WEU}iX2zr z?Q%YbihYn+7A$hNzW2N(p*2SfOHW_y#h!07a~np`A$^Z`9^C&-6or4HL*538p|+>z zc)^=EMSg_t5aCHK{jmRh<$vs!cFDSXbpvxK|HXJXCFxn5|EiJAr2|DHD4+s}xcIs)kSCuu_qB^RjnB7fdh%m|_S)jcnD z=_bw=i^Z6X>~U`sK{+zPv%Ed0;WCh#ixu!@i-g!=pV*f)){me_McFULkMqg;-&{I# zDWp&?49}U|w0yW(1E@LK_97~k4zL>&p#L~Rp1Ebij`u=kr^l?APGU!si8E0%(2B;9 z-Q<|&obq95J4vyk!hgQn5!s3`op#F46@Isiw>UCNU$k;MiSG~3vO#mcwBq&nqp9p@jickRhbFf^6ocLJAtMy z+rQ#uUivVQc4(!MW)yRsD6nx=s;a7#UaZT?%^i4f&ns8P=Czi{ZlXBruXTNrJY5R)nIIw;4dLfb5*;QFwT#lk4yi?PxrY%>NyA|cTIT74_3!-p2?d6q@xS6_L+5n^4oQw=mydp?}(XyA$S^OZP1(8fpAFr7w$EOL6|qQo2&+rgE&~3z)2*KYZE3 zLguyTt+_Z%wc8`Ooj4Hrv0p3eyBl%$W}Iixs&938A}?{Tg|2h)Cr_OqTv`#aF30w@ zhVjwtlz)lE>g<_NfLQV;Q`n?N1>wG#EqY~x68pt3L3QvcP(`ell^tBOb@-EPA#cC! zEBmi=BaCx$GfynLK#gvIAeu|y<_R;TJnM=u*Uw_z-Y_1BOBVqXQFki+DJN9XMx=&q z1bmT&4{y<8e{Ie!;j^?>H*ZyZ_pOK&(hYQB<9~yh{$3-KPbtl%BTd-x#Tt6;NXd(- z*SPMcqtB&F`&B_Pb%d9xAZ^e|1Dij5alD6?GFG64Ms`x4ua&7lAcZPx z>w6n7wfb|`JDsagyi{V`R(W}PR^oZBNZjG~?qS4c3p@yU)e#@JaP%&#fUit2MURV+ zKYwo@*Z4!Grl>s%ZlsQX>7>B*7eXcVkY#DSd#iEc{=$YO&3@R3JQd7P6?_r#)+}nn{ z{2Bq$cAjM5`?pgxH33g5&7S=Bt6apLmw&_8qWQq}Dm_f%DUBBE1mP?OkrJEl5k8-s z&>FbR7<8kzHD^hncr+<4mdcixUj?}%9#tHvJe}lwjV6ZYjFh@SYVoaoM`5 z9ASw0iFqCA=#ehx@pl`}Lzs3k9*EqzZBPnZoR+2qMGE>HA*V+;X#G9>s@~dDPJa#V zv`rCJtY6gUG;4qBn8X_rd#hV*o zLKH>T#Z>P|j}C4A8W-0`V5W zTu(*cT&pvFT6P`a$y?1lFVk!kE`M9()kpeZh-XBWAJy!Izgy)?pi^y{$B7NB3iRl18tZV zGG{FeG~yx>?FgMl*}_3A{+K~CyIPT~FC=fTVqXCB{VMsF7hw0Y`^6BN>VGaD{$~=t zwI>4t%7f46IZdrXMnE=QMh5XhzeV;{)P>AffknBG#vL9t6{1kO31yw*>yq{IWZWqn+FH z{NM*q;i23&t@0-l9^}AADtSHZ=er9($i3x$sa2)=YLS*K_ry{{i_*F zqi2XJC>Nc#+4Yww%v4Q8c~lT=yx(=GA`Gu4y>tc}lx|_Iw**Y~Co+gA_?87$DZk+{mNFgJPpE<_bEQX)X0ft4B2m)X^{SSGSJ-DkUhIQf%P5~p82A~nDTPvww>;Y zAzGMr>|NlP!y&Rch<^_4CnQaIA-qiieYfKkux_wo(DSx=JB5v5=<)=8*zilpeuW*Z zVH8=(govxhW>%lmQ?uvU1h2qT>oJOA2EUj1fC76mukMIaLI<>9 z^DOV2QX3@qp~yt&rc9FRzH?!d4q=Z*ufQH4a# z{ju;M%>GVXL`{X1#`;D2W%xzfO=dgACHC4UAfl481Wa>6OI?yonxnB@|fUR#Jk`afFviZe{Y z3HKm<*U{MmWJN-&{nRlkt-{9imCH+BPdIYwTha~$w6LV6Sn0cYGplFDeg#o8{*ovN z+-k~yc*FGuF#EICytlxHEqdnhmlcBGx7~c#kCL4_?|%;UH7J|#Jp#vXJ^a(%IJk&+ za&IKaS+qVaWonV|Vux6umnFdlXGMgKwo@XMR}s2IMTQCKBaS&Trz%dkj+GK(s-SCH zr7dIF#wyncD;XRRy=N=1`bgm5$vu`4Hsr98nkU1;F_%>Fl|u@CxNw4ByelGHsNt>^ zffoO!>3_?Q`(zP}(EeK&n3+V<K3fP`I%ouBjRVdO6+@!3uem>veV;IqiP6Mu4YnX3iW?Zi+&GJxXvo)qz=_UiZ_ zU6()lesP$x5{-3w9^pAidlhye4c6>rPDkuMnQ>^!yJOLz%o4!Oj6^DrwB=-T%5*nW zA=CM2`b`l}x0H#ZZ8`2f@-<69^Amzj(qk=hAd534jiIR%5fZH12-f(*<6MaP^Q|bO z#(xm<0Hly_UTl9H<7C*P{CGc>nNvd^c}f1)e#qU2gV|LVMI?>CXgu&CyjqsCSx z?vhO@lKITW{QX|HU_Yu|b0RbTFL4IAlz&%6^?AnaXWkLrQF#d4=X8pI*%He~qe`=qI#@ zi9qN@fV<|?UvFkG3#5VfxBezVwhWOrvK+XKz(~@z)k9AE0I^}sgf)~;fUu(P)eI7G z*PDM{5BQbkNlirgOX=hZHJy(8?A?0<$mO4DDs913MO>S{&q-$W{t;;^Hh&Mlq7Cu8 zikD}_s~!AfqAhuite3+Y<0NmZ%RMPNpjP*rl)EKregsy3DVw5HWmTxTu)Zw1uqWtq z@X6MpQ-4DkV5U-PJm0{(aTz-uY88m1fD%8DodGON5!WNC(4{EuhTV=xa#TUpsVV%t z(4l*GoLdvP;l=^9*7MIrn12@|rv7~%;hc)yZP$a0ltmcM%U6Wxa(=@ldnti-}rrD+2d z7mu9`qMttCCUX{ksq?yWwj{#aaj5Bbd`{|b+$63R!wXR1TX67T+<&Ngx6ZZm5%fdG zQAun*0WZm!#ZOnQ^o+GW;6?SknK$@+t+UJABix3*1frYG^Gk2N@I10SX88h$DTu2! zXj1_C$@~oDoy_s}v*0bQ%kVYdo|Dn&;+yiw$h7R>+WKNo7;1V$~Y?d z;k?vnV{J5XZIGd&sQu8}8R2r#!x%7o26GssZ7lpsNe_yY6=J|x3?g;XR4T`6bQO{8`}>{?EBtv42VZ^Y3!_smbqAE>xH@E%TZ- zw>#dHYOYNZ{vfIq+dCeiZvN8hNB~G2OrND#1+-uyn4-HD&5JcbNlOuI1#8^IQ#_$C z0^x+)NQ!xSlA+r$NGC3P19Pe6#|!wa3u_^#t{G1neoV?9s$P~oPz$}?thi!?^v$f_ zOlVe1RDUmVZVLK=?k&*LB$L#&(5A{yE}GtSuZ%x}rp4SUKwU>8z9^HRhRWZ~?#u&g z*eL~dBfE-+KM#3Urr2eXm?$XCKeMHj*~8Jt`*9E>bzp>CC@ z){?0yk68N$M@~vbm71=CH4=i4jDYje(l&K`Cw~r8ywBdqT;(@oSf*6WGC8cb!JePpY&+a3m5eWHmuB74Qk845qXo~Rd=e-c?RJPd%PTcnRLu}XR*PjJE!K4M)K@>% z%(fe$ZG`J*)$JCsb>&EmNMTfP_nEY z>?|=_BI|C+M}H4+f#6675=v>l`xPjghJw}n>*J~N3o0RXGDPO|UfMcv-?zN!2K$i( zdWRyh1%eSOU`MvKHhz}2-y~j3^^5e)ihqeaY%ig(O8->JJDE8kq1cP-(v_vX3C?b)Qt0x>!2~C* z#yA9P4(!lcye#O(^T7$n4!g+IKa`_9EMkA!S4qyxbp3kCPYCyzX0+1G_|vzC8lw|v zGv0D)9vd>EX*WcZEWTT^6eWIfcxP;&zkbfoFniAeWFgPPnX=q^G=d;op>)7@ zE;kGtb6Xg(mtpcLwS22E=YRZQF&n-71^kj!Ul>IY0-lx2(;hBy;C&Zv33K%H4@sgx zZ7`ro&G1C#?bUVW_`pDYxi81K`+;{GHJ|v^!*_p(^5)Kmv<=&=P|_tR2)ov~(3zWx zGK^Qe+7hAQhyvLxw9RLaQD2`dDMk|$u!$0=tD~p!G363QT=4N+kcG2WR%a8L4WnJ?5#)y)VrGxt>T%o7#~}&^oNSR*$JcLjS*ZbM zOTRWfHAanSXe)pLwS`94)8Wr5FNax*V`(l*NkQ&9PSELQhjR3l1(od%5h6B#dg}0T zb`J~x=yxmypT`}<=)cgBR9#%0ei33O!K4vhnxOe29e+{6`Yp=A@oL+F=jCEjfGHmF zqSk1$Vxt??X$1kLJZe4M-CQ3d>$9>oWL)FyM>yn=?x*vhaNt!(YTC&i>=DLT3S5;f zo4MtEW*k2^JE1{k>`ohT?f%1PGuQa0-G*!Qnjo+c@D>Y2k64H^9L9ZqofJPu3+}g9)uqw)p!zRm z2}Dn4EbE@BkT@~}>?4XA&eLqg=s(QRGnE_;%yE<)PJjRWJkhJ#=0p+#_lY$Gx+TTOjIfz{ zS1MVHSGA}XuUIkQ6W`P#t>jZ^@75U3s0h^wO>^-W&EnT}XHjjtv3I_*F!;4)!xp38 z_TS>qsMH{B>Q=u~&f}S6JH!91_wHSm$|;c*1R5@3X;An5V0FDk>!(#3`X>+c9{et?H~)X$?f;p zAN#ZkiXCy%QgUsLkW{`|;Y0Ha?J^giVzKBctaUO$g{$E2lu6|qM|USZC?0j$!IUtf zBzVGP1^4)KzZHgy3Bb6ONPq2vqB8qb>3;()APr4=fRxH&V2`&W0Xl{_ciyleVz-=K3^5QDZ# z@SwW-s(=K8YrBp}1uiru1-?~DDE+1(H}KzLr&3Llao+9LUV8)QHQ-Y%R%9%Z#JOM% zoZ&Y$j`Dg12+w|?btvixx!Id;+r2uL8ZDXO!9aVX;ifBn`ILVIpRPk|x_=yt7e)GB z1(Q*FTI-bydY)BQmatos+bMZtq)NU{gNem-oaZD^Jj-TS5o zlVeGEF7Ux7dX_8gCk!aHFF@yBvoGZp!Gb>SG!|gx;GeUQ!MQ0zf*9)^^M^nCLDKyt zl}g!Drkr3?}kN`l;=I$e=-Cm-cDo@m*no%f?l2l5HrbM@n`?K?Th zgHGZQ*t4K!^HG-Gi?8XT52c9VocA16+OfqFa>xQsnuXcOO65N8XLs3?erN0rP-IU= z_BShLosv`P3mgsKqI9*gY2OYttebx8!AYrZi^K6kO8_*z-hHL@vVTXx(rxC6mjrZw zj<+uEClz{h`c9wf%ID|#c;eeI)UB{0n_S6-g^2>o{s9e>*t+M$2(j0 z?1K6-=f0ErDIb+OBcb6q*!u1OW6EZ^=D2}&0jq_DAdXX!maalxPd(F>PTXaeMin*cQlZ zlj7nK(AufrmVv@tG5qlhzkb$jGi&U?aE6M__SnkiNuQn>6rMWnzP+~wJht{GrVA%h z*95K$W;5|rUb(2N*h(<6Gv6b=!DF$W9NgyXm)7(m-KtNa+d3 `PW$iX;fb{9=q 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 VUGfC0Yic9Z)xq-eCN>!GzW^O8%gO)% delta 262070 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

w77TO;z@1n(Ze#`3svh5SYD}`X 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Ηh4yX)1wT@UqU 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%0Abu{iNqA*QO!xjS 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 QiQkeHr{LqeXP`mxHy0 zxQPRvIyP%7Q2zZMS#n~Nkb%3rv=BN23g2s+xM@J|p$iXp`njtNE6 z#oj-An6il4#?E8Y)OK+d`^O84-j;iR!;d(s_myqe0EP=Xe?H_W5sKS4Yh5}9I zh|&lv!jMz~E6LDghR(f@byi&X0hh|tm#KM>-ng7=pA+xK=hREgQ{`GlMUvmvS#3L< znf9`2|MnHCh?f@AOWXFDgk(JODP!IlJX8P69feIbexXGNRR7J8yzOB_(n6ue35Yzi20?oAA0w^x2(Y(s79z%rQ! z;eGN!LXv!G8k7nL6Y&$F;2Ti9`+ilWb)A)keLwV#o>mLP{FuG~LH-5I4|g*E1j3Wb zGP{FA!}nz|)qn)>Ub1j7MNfc0J=*b2E}fH__;Cmk%c6Y)PiTKk6%^?1pA9%wj634QQ4QTp{aAWcQq*(M)0K#Y-EZ0F+v_XFjfYYjFqE{Yuh0$?QyO3w9`~G%q_d~+|izfF0eEXwJw#e;RmzAp( zhJ(VGFcW)(dlKBLK(S#=}Q|bZ)i`? zqM1-iv6toq7~RR-Kf%0bSGg1WqcX{juroS@n!g8+yU}NpkVvxJ9@_Qx>JTQ?^V^kP zjOseOC>E(be={gb`hPb3Kfv74@sZ`#r-rL8AKQOrYR|YZMF;tR3L1|;?;hVSOord( zKl0`{CNfq%oi3X!-IfH19R>w$buT-A55YF0k`>9lWcfC-@^1PJh#lDU%`DwYMm#t@ z^H-@`fwcKZ0i+jK;JdPXm6bDtfk&IA_7(O^&K!(dsec~cAtiD|6Nt+n$n1kIrO zJt8=cCAw)oTEG_(lsOuk8*x~LhL9^(OrJD0h{_C(p_(8Bsjw?T&=RXF_sQoq?S_O!*!PaFY+H2(vxw<6SL|C7a{^Q zHYIQv(7@+bW4qXjg#aN2uvcJk3UuUmQ0tVsH^Eh(Gwh&2R1_w8O8Y>#y(@y6DjW3nn@}{0k z;X!{l@ZVNtLD}!;S>)winV(l0M-{3p_C$icK~D<-lMW+lZ@Dpbby0o_q$;58wpl#M zd%c>Piap&qPhm<~CWI$aZ12%?80~h@q4G1OW{VKIh$MCK4zU=UWVzez`4!$dkcA8Jw+|`%68H8v?x= zIWKQkbo1){o4rS8sKjnIq|bX)RPn=E0C(aX9>n?CS6z%m_9#n!6~a7_C2&|Y?|^^# zUE^vUV0n+smcQ8Kers`khh!eJWhlFTV3S;e+gWcac0-?i%_$Kj>Y+0JUM29ftx3nX zeTnW*UODSTcOZ-%-$;t+!J&Gu_%GSQ7UjhaQlFe9<^5ad8U~9^IpFLN0`# zg?oDzPs#!HyslONWBhF`e{^?#P$p_OdvTXgUR3S5HON=<$lc-+u{i3O`*K>NgDUE6 zr;D~(8F#7Qi=*EfGu;nv0TqcO$hSdG4o3!kKh~2G=qKB}bw&Z%RHhHr)xUpV@CkMF ze5S2&NKe67l8B>%zX#l%7cno^ErrC=TMqdEpr>JUG_^lZ5{GxLA2*irgKk#07J&No z7QpYNf3k0Tq|3bOa}f~oe_Wwq)!kL8uVJq7(fPbb@-KI-EyTjs;KnjLa{H%=K^=Z` z!&`8;9npXccst@5uY{XnO)q~N?r$ypzrrpLo<_#y&%3<4nJ=>F z7}EWGcBpT>(!SXYEyv__Yb3K8D}HIJU7m>aD36~B{o9VZ-IG zwj*jwe3p2F?lzF<^gu40@-D^(4I10CD0&E{>rSi>?{S=&vl@BRS77=9FGFa6uPWlRxa;8;CH#a?{qPCeMuIcUj#zaY_W5UTSUqa!jxvs6Ihc?EG@0=@zdU5VNX_f`jlCZ1{cO$ zyT`G6z3Xej@J{zkFGd*9+Laq7LLMJ(?S@kGN$vdHWSL_Gm_`VEnh1>DnTw44FwJN# z2J`@A10HP#W}bgtTd-7}CMT(^$0@83`oiIO7{D@wy8I}NC6u8ZV3rIX=)mSxVY!6e5rrdhiu zBMB^OcQpxSY80NmV&^A{y&T)eMXe%x_EPQPfXL^cCnJAv!$@crh3`^Dd4p@6xO4Pj zw`XltzA`JW>l>`I}Jhq&s1j@Nw0R}5d8+j(g zU~m}?;UW8?YiqhI`Q5$lVEs%E4pec)9aCV94FMwH#CMe1nJ|>nU&fN&hgM9%9ub!@ z?wN%6mK1+o*0HDB5R~c8u2@%HiCLH>e#8^qOyLE7KtMx5T2_dfp&CXNMK!Kyd4@D= zxWG^n>{OGmDAP6rK!fTy;|hj9nES@$L?En+H*|XLg8&a*z#%cxTNoY}Ud^h{ya`Ln zvMBSS$3D;b(8R#?*Ie2`l}Ql94m7Fqvm_yGw#t78urOFFx$gwg;_cC|g>05E8Xt zrE9J+4M9#S`YF}2eu7J4|CZV!FDn$OuhP`@vJyEdu|oGfCDtPwabOz+kkhZ7i!2EyHK5}CBkrjEz#$f{K$qkADWb!}jqyta0Xew;~s*80%?aVKy$>&emG#9$+IUR&%{| zOP(|}*V5G}5^A4Z9=u2BgADDJEowrAQwnOp)HxgRJQQTLFgx7TisSpkq*273?SoBm5nAfP9x`G_uEg{7`fZ#D-qN3;4=d}SK{7X%$Qu+ za$w!o>56yuLT_Deb`={dNyFmEdpS&QaARW-h*U^${m!SPcaO!=#}*}{JYo6l%=zFI zCZ}Q_Ts8cp_2XZ`Yz5imy~k*?Yg4F(5~v@m+4AJilfX|RYzQwe=p)z|&GG7>-Ys!c~K*TAP^j_puTN;N%3GZc7>;~DpUC)#hzKEo4 z<*kW*jRc@2aVL#n>i-2CL(%n@!Ttjilb13S12Zr*mqEAzDSyRUZExH-4*s5BG2hMs zI<6)8bq|X}wlBNwwbyKtYjD^u3df^ly!Fehp_^7qdzg!*9;jeCrCC&U4M8WyVu@J($gllp9 z`^DR%fQ|6{H3+qlP56P38@t3==7(W4yQo3LzRMeP-G6p9EDImFUN=Qwwjc)G*DZWE z@fNx=G>|t7x>$8B>TrwV>0xL^Vs?G0-3wQCg?8Q3Js6_)kLRJvyw@dsr@B8*mG=xE zW?spY7TOeDNpHG^$bxWT_#*|_vHTRMu%MUmzO8GJ(4aMDBUgyi*iSP_DqD-+9!HUv zmsNgMv48Es2y{`jjp@5J*uia{wLboNHa^Po{IyDE zBJ@=lOWcuQ26Oy0l;kIGn%njZeJxHr+*T=J_w9?@J2`Bm@Z(U-ViowY5~SIMHBl_# z<7J(9V2^vcufbSg%4Ob^1_h4=s|(M{1|RjH0DoujpieH_!H1NM?W_YU}fd8ECA562gzrF}Bp`dP9Qc zBv+^N#au%efQZ#P@AVv| zph4tE(&3GZ;8#>`Rl7MFERKuX?tR(X{!UO+prsEvnC}vf(7!uBzj?hLB9PbT!R*ni1%zN8S`q8n^VGwi-azX>5hr;=l*3O!a&cdJfA zxCyU&LKhNcYUYA;S>^p;@Oe??#^A`0_}^aAq9Q4sF-Z z4>Lv>gR|^1#{I|z|58ghHslCSiwQ({PbklFP932ibk<*iZ_fMsm7W(%U0BX@xDT9? zFEd*O+3a!*SNQCn?ZU>e>S+7t%fRKF4F(sl^`fMl0#98&{H|Sov)S9mr7^uvKgioD zrwkECz);g!7z0I%(GA4p={5HGWx&h}bxlqy3gofPn6tMdGh9ij3^ z-kGOkB*<(9_P+v*9hoqJ>uhjOAH+N)70S?;p>l6g`rRFkiMQOUO@H2wiWk=95KvMA zstu{8?s~P;(zp2>XzqMbc4mKQ27dH^0i^sfOKeERQ?YABpJLR4EYPg$!4{^hF%4g} zM*j%o-;%6!zeV+KaWi2ps)bESa7CI`%y+7(O_+c^l8dMghw1O$Sx)%zJ)5npEofAE z-*(>uVuK}aA6nv2D}O+Rn~Ln4tdMx@FppY6Kjgk(nB7c;XbLFAl8pV`Ex=w>8Fn-% zM3`k81Ui<{aZlp;w`JEhj`I(h0z9uP(cjF&4g!Yt%^I5mZL^o7Zc+9ZXa709`0(!Z zhnS~|gyI)IQzP;jX*^+9I~qc$LuO+ZTW8%Y5PYEENP4u3Hz*zytz+)fwZME5j2 zgs9{Y68ndbmE|cqo17f4gJ5(BhbTY!N!$Ddm zun8)e2FLly<>xo=Pd}Vpe12*VQ{{&$`K$7ii-ir7_+ARVUu%|n3R{<--@g3k=eH+k zPXL14!UGBPM1<*1K*%CS){Io4E2}O)+S8~C72u4Ru77_h`5DP6W56pZr|S&e2oMKg z)OLjOvLT?bqH$J)>!R&==HO>U@nEDXuR1_yBu4XCW!}Hqp67DgDjOPDFlJ*di)^fM z2TM+oNXD@rW!%JSbb%GVg+}cDTyMHfkDE<^x;Qjh={pc(Y{4k-LkNz^k^_c-MC5_N z6Ov)eFn>+L=`x3c#<)4FV%SJET}RtJe2_qcNdjd%8hQd5dGU-cbMEPlTF}_}%bQ{5 zuCyVe&<{fCI*txW%LYrw{9z|-!A*{OXfO@5gS()`=&h{|igJFqlc$j%;z@-zf7`JK zDhT{I`mPc}PD_X}(*Y=BX!lz^La0VNn?O;k&z^0gWRBNPbW}vQz1SZ8)2wyjQ(@2n?lo_ zdsw&=;Zfxx$DSPyR67(@_hjADO)M6}~ zoIU{eu2Gj#qb?=4z+JRoQ{L<2G=HMZ4G;C#yk1rIP)0mO%#=)hnNCH*L%sgMGeoDB ziV{Bz#U9-jrCC)r*WH_mF1`NRuWjud;IA5>N;8ahxdVbp-~=Fm;B#D;SIbQl+^oTm zYoZXnHBacBuUAZ`jowPc*R4B<-2CnW1maGCJ6-}FzX__X!w{yQyXw5ekAH@G!dlfo z@aQW{`~cV$zxu6>jhIOBk4HesG{wGans}wXZtOLvlfVt+fh6$$euw~b;CCWZ zwiZqi2N%O{1vaN^JH*14bgae)ebg}g7UcmMl!zbJl$#RvzSI@N7NG8G-4BhdvdX(@ zi8bugWT&V7l~)&|-}HcwVSft&NJ~*4{q}n>Tfi`7!FYCOC)KksM$fNi=SROD1%49Y zLip!r`kfDOfE4t>K)^#cAUpt`%%DC=`soc|Y&x2i`O-#{og)K@C*}~@Kj&3jTA&N1 z_ogYPlx_OyS*WBhBQ-kw$h`bU(@JWm?Lqbb0=1Sz-U?-IWOHq)16zAOU7(pFx6b(vuzFb{7q`tsV=pC9fXs z=>+-TuU^#4-5t+d4u^vQiK3`tu~?5^6&e{7eWjEN-a}hm{qpV)uObnGzkl&fHsx`xZ5A38uhuc#ZNg0c ztW`9B`x;i3PQFq)Ok)%TjXs&%zACHk_8;-p+G-no$+w?wx&{{e3qP%Y&-Wjiy1u{J zH08154rKcUc45YG;wW_4xSL59g^7X}2jJrS3ZxK4!J%$DZnYrPBgn`F#isl@ip?hP z%DRHy_8LcIvI8)5gA(1<<)*;#E`SZ8{+l;{KXT*Y3duBW-}3YOUEUdPmePfUYni${ z@Wge~U_I@6ejD#G4IwWr==j0ER2$v z1ySDZ@oP|~5=8>jbVYM$xk(FySNDCjL1Eq(A6@dTq`*aFj$1qpxQn_X<$coX+wn{BPB9pi=jPgzVsqhfpwYnM!a>B54Rxx$hbXEXl=60;f4tp)gF?5r zd9}T5yBb-)!0MGzp&p2ZJYSrylmby$O@7RnBs30Ovd+NiiXVHMZ+7f%lN7w{{sFWK z+DY)&qV4ycaK7{~E*Zsboeks6u(-*&YVg6Za2xE%mZ2e%vcV=l=65Ao6stPE#BGif zc*jC37T>^5fj=T<=$Q-@2jpOXpBw2s?aEQ)IM04Bqx)_?P2P9)A@7jrmV1uSY--fm z2Qr`r)zX+0)Us?ny^0$x87YtBp4{7+aucCsI?Q6Rv<-NIa$7X{;CAvu-LsL1wTsW` z%7fT9JEjySZb^C3t@MN4AqnX{0_W?A+u9b)+`c{@is>AnP)z#m_t)Hia~i$a;8!pr z3yR(azqCCZ@t~2jma*>0WUGHKG3Fz zXFg1YiBT(SRcMUkiDZ`&!x{vii$a^gzsPxWEwoCYyO*6vY;a#UGK3Tc8lzgl1FIDa zohI<3m|+}VGa;{5Tkb}G(XpbSxAJUsJQn4F-U4>}%O_lem#9fxs6d>v)#i7biyXoz zGn1JxOWd5y^-J?Q7?>?SxU1`Zu>ptliL+ZkO*YY&&yIQo-(*fu%jRKYE}2tjxQMC^6E~sC;kDW>;_H2WD807;Uvxe>CpRH+zXX5(Uu#b10KE zC}B4ScLoU(wM0yRi2%wWFeaYAWV)gx@DK%997qVpUWT-BzD+Hz3L?e!TDTdfX^JU> z-~{(gk#8S8gHPaa8>N6i1&0eq41kTqJRkYBnIQsvtQ&wsk~Uy2v1JKq3GVYx$y0Mz z#eMS9GF?EmBnOQM1L1`*(Q;qk@`5q^;Yv?sTP)_tkKpBhk4=eSg5L06I^Ii7a`d_| z0Zhi3&^tGV!7N#Oruh@j7SLlm#Wcv00Rfq6EIkDzJXgcTv2dR#JC45COUq5zm=A^{9@OE8r6mLICR5j(IL z!9lG+;2gexVN3;BrNz|vTA+3{Sw?;I$t*t~^S1TCo*A1+8ka{5V7&H;)%ljll>Vg$ zkpLumMx15jvU5F+P?s$*?PJPw3<=(Oy z6vU6;@*5Oo5q=1n?CUpgxmy;ONfHk|42NXu=VLT~q?{Dg=WJhwOr|T3Z3J|ZIUV*| zxT%zFs{w|?&Qz^W=CZjJ$C=oQj1(o$IKNxqRAbXH282S>^{w~sr(H36_(w?qNt*p$ zhDbn=c(C;@6}*jnST91?Xuz?U4FtGN*TWvm3{4M8R8^wnlTTR=;h7U<2yCz#m1+-x z=^Gt?wB_NrFIt)Kgz_1x3hWOPoUka> zd(7-5yNITis4aXWIJFbaQ)f%ec!}!FCn=PF&rA4LL*gH)#YAm>x)4_nylQQLj+xUz zi+*8h6H^6qIrX|J4s}rf;x<_qcBJd!aVB@ae+l5DQ+7&Tzy7J~xKWsyKO$nZ@msl`RwMF=e_`wH;Z7 ze8g$yc}-nMWaLuB)=V=?rDcrjiFFMTl{NZ zHc$E7II&@zx<%cw_;}=+s?<~eohY3HBI&H&`0V+Kv6}RO1R8(N@dp7leZv^ zU2#ixeOW9RkT=W-`Ii9!#E>6Ly;3TaQyj?EFX$?$9PV+gA6VYMgo{)fYEZG+9RHI@ zHgIoZNWj7C&M5Oyn1Io?pcAq)*iBr|{GtuK{07Ed61pfQJeEXKwj4Ba0%i_5CJoI1 zW#{kN_Y7_ip+ejGl=xZwkiDLN+1sb-hbZw>MJ`1OMg*Whqm#%HvQ0fy@Nvvd0Fzr7 zs5>)I7u<0*(*;#64aQ6+VprjYz~m&lxp@KG$qu@+db9J=cJr8IWAIVrf7-KyAHTWa zVzm#^eYHow_q-}AodPCH=3jlbDo?o4CL$0rBYe8@C2h!9MOfK0QNYuG$+9ptW=`j` zJ;k(M8tG7}1>w@4X61+z_MEa??~6?~?#9+1h52j*Rz!-l!0~=pq9jZ_d>PXVZG&yu zZt`a9ixi|NIvJ}NMI~>zJa5N0gV_OA$$a{x{V4{}n9Tz*ACgyYv&|zdNFqd`HEjeJ zH0~EXz_;>h%h9xusaW%Wp>ZnPoqSXe-#q1ev@;-fK{bd4|F^xyW?$sh=Qs^FIt@e6 zQ>Ql6{s_!F4TJ9TthAQIA42CX;h^Y7mUY7R_Ta zM>S{;4JulIjsfJga*v--!O99c6rx+YA{Fu zHLArTukmj=7_vc5!}%ae5GR!*SA0Z8SmbUR{8j3t(p6UAZx8&J8%Fk0koTvd1G18n zTr5nAf|&E;h?!F`zPg7=UH-wRf*^vj9gjJlop7LaJvbkK`Rm1|6D$dtU={~seQL5w zL{rWOwKv6n{QHQKNE&h456{KN9BWqmK%oro-bX=mR#b=Q5pIDnWXws5InP1`8%9w) zXAARE{@c|9-@hvE`VW`l#zFT?8m37$CryAX3I5D~Eip>O3XP`N7hDt@U^1?{?~Z*p z7f&nyxv8#y&q%aqTBWj-{-P#z*gHIzlCx+zVE6xj(-APIk~Em!5`3Yq6oAU`DKTQX z!BMESvHVF7xXcnnvv57pm3uaHg{{gUqEY)uS6H0}8UOxjon{(+u%iEit?{p1{BXy& zJ>Lp?bvr!Z?IYig^h){~zLk7COh2ypq2pWb$&y$Og z_|25=hHpDR{y)2zG$Y}rILal+B3vKnT2TKjwU2b=F(vg^)CNi^-2MT#D2S0>6#SDO z{>cwNtJu&po=3nEbWD2++WsZ5k_JD)H48Fo|3Yp0*W)^iRIrAdAufSg(gYj)mT&Ns zS{?jHkF!{}P84SvMElTUdlf!NGMy*BxDPiHeq}(cffB)7p7k zX+PoG({)c*)HoC4n$k?k{v}>%;R*|MS#VEnJ?Wp0$!SbHjCY9MhO47%$`gn%V-?)s z1_W>D`XleBqcw+K3@Gbsmd1Y#+XU-+pw$(BbZzL`4r|w>2pT1jMJ_P3F(GYIHfa+# z8SYaGATuHAhMBk{j!L?A%&Px8>uL?)ig5g7Cqf$Oud-eEXJQ(p|KI7kA!n4*-lq6B zP4c;)qVrarQ2-(SmT!wUxV+I8((}22otNxF+4*k#jks!w!;;z+wb2%$jP#q_tBa(6 zy_;Ca77Zr4#9%ZV4BK9IW-#u|V6roVac2gTy<{+&3?`czjQd=JQO_8RA|uEs&<^{| zZQ>?f8Vp5t7iRIoJB>D!u?^{o4dEdS#OjxgXn-?cznlDjAIvJYVG>#VhyG8$M5`@y z`~NJQXwU&rFy@8()y=EFyo$oqA|bqIearvp2i%h(`Y}{x2ap?wnHh3&X7Y<~;%qX? zh1w1O7Sf150NljExB0#<>A&Zo58qdtIpg!GS4O9h;#g1R2RiK}qzjakdlJgQ+W#Ay z!0uTJWo~41baG{3Z3<;>WN%_>3N7`EQuJ+K12hQ?TB0l>6seGu9rw`he)Cw;yNZ$~&GoK@f#p7iv$Hd^ zznP&dW>+&Ydvqe96*D=@)GX6koC`TyZckpF#8O$wi(Z$O##h|LCr`F=_C)2oG;*6Hp>lDJ{O5NJDp1@qo>f9^B3n| zod~A{kS+6MTc{+DVP>IiY?72^gFS2tyJLGpEzc~e?SG(6qKf)EY6lQNCDCW|MV48d z%QK|PQ;-nbYidiHzo7n(TBfOJzM=krEOI)*d?6Ew+e;xL1FJc`n@4+U8)~buk3@q< z^b56moWW&&uMgjY7Oaq272N|WBcfMuqAXfNYjGX=74-{V2C6KT(KC4D(FMivCHgu}zq-QqkXOPVx4?(%@GTc7f7Lqe5vPfNm6?8rmhb8){K= zk~F7Kb5<%cqagDNQZK7oL^ZVo9!uk~P(?YZEPtamft4&pU4#8$Atx*FpvkACz0GJo=nvE0NITAY+Ivr5GLKC3|plztFs0DGAP|u8>jY1(6Fg2vw0IdW$ygP&XiP1m5mf*_(ik8o z6elLqu|+Zwi|jz=kH8qXtUJ279bfF@Ab@Br5PhHcXqk0Z$#Uw!uhwBTaV@CfC6kOt8BN zxVRk>o-Bfq^j?@)t6=w4u#tU}wFwkCk*orqD{ouPJ4_qdK&1588JyIYld)~)jDM}z z*mxI{B>M4v(KVwLOQCeaoPzoT-@?$D9RD-o(|SCwy&bzd{(uX310=nY>gaQ7e?kz? z2_FSrPmDD`PiP8p?cQ5AxA=BW4g*-&QQOl@Mg5lAhGu#cBpt&DUxd%m_mKy}R8D)w zBXlEZ-)F5%(qw>x|4Xc7=#xo~X@6EMMf#V7J}c3Nm~!OQX4Il`Q+H(*M` zCFH=r{Go`K{lUpBC#}XyX36wZlItb28R^#&K#dx|VrYG5GOIb6Rqv0+%0pw3zk$Y< z`Wd6KJw#(lmBF{H_E2Ctjh~Lb=h6OVK?Om3SvYO8z`g#wpeDNsii@WX`Hs;@w&f>S0lh#QUM$7Jma>RiNH=^i7U$Y1ccN zx+{y-EmN%EYC#RT@B4CfpQq?s9U(l9`u%Rl_q`h&gKbM60b%i~gqNGLtCl` zTNlgHQG>(>rqC9&23aV3x6>Nhp}VXGvfcO{5;oehF?Jm~m4h7X4g*9^Jd5H@Uod-~ zDwoxBq0O9XWPi4(FJ64`A=3IENdcr~mI7GY*s7cj{9H-#u8)`SPNwmkL|r-`xG1VC zcNOQ01QO>@`EfRMBrx_DpYih9)cEB*g#6l!9o#r5@ zL&gL1rq`WUIr^@hwolqF5mTj}*KFfqR z1+_TvB!7!F1u*K@Rqs|=G8HY`?Y3xEi@ItY&93M`{=r=U7vX6hFY>{pO9Y^IcQRC; zG0`dM=+>>)<)pNOP&%4bD=30oroqw)mYokGCgCAteT6v$p&t^p?~J5^B0<3Z-rv^u zJ~VJbyb|Vs&rMZ38SWAfaKf(K)o}k;ymV7hl79j7&I`ooAS(9`>L!MAi`NBK98M|E zL4|Wrp1vvp7#$SD5@#%K8y^ry1EvA>j>1=3}{zvQ9HYxkH5~)9iiW6Xt?g$ zZSebz4znJFKZlv+>k%KFblN$L_7nvJ3M%mY;>kCs=(!KhCcb+YkzfP|gNXA^FkLug zB!4v39|fk%X8mUY)0ASh76S4LXSbU}0(M-+t*Ej){uKIl}09tNn7sCFg3x zF{c>WnzuW}Vb-)zh7QQNQ{r+bxH%bP9Dl@@cP30;`J;@8lhj4BFHx1KSjeK@3|lzK zO2gh={)eN<>2bh00=?M2oV&8eE%3XP{a_Ekc~_UOc`*J$3dL0qiIh=d5(Y09K3Cbu;51x@G z6%~uhjMxS>_rB{e2++?exPqt?zE<$U9n3j@famo*fz-0A+I`PN>;gR{m8o+uWHr?b z4bv5Uzu!7$&aIWclWG*g!Pm4?U9Cp>poo>*%nE-K!9EPD-hT&te_tv)$_(K9e~|g3)C_@RNf-7nrs{q>Kq0IC za&qsRsu_+-e>@z2{F#oF)Mms08K(x4Orc_9rv$w(gUS|5N0&xTNgzy8R(GE#gIEV+ z2%)Z4?l%CzE1sR|W=xg9=NyScKB%j|IbVE@-_nryTJfZhl9-zIT#Z3$R)2BChvWg` z8YX{GkDPCg;k|{^D+hCvK=_+VxdVnSuP5a=kpA z-4M}sW>Pd$Jv%%3;Y7q)f{gI-i2v^_O=lK#mKxL+qIg`Olv3fav)It#WxG8W;cza~@b+A0VS)cT zOK-htiu$gvdV2MDeAK$nz|}V8j^6w;PU6cq=R%0^cW8_Azpj2ci&+V;->EbZ zBFd9QqHq^7jh%ECDvNYtNV<1bQ}sK4lF+2#uU!j`IBd5)b=+3Nt{n%udHZ#+9Y}|F z1~yUO*sjM#U%vwd%zB8UJe!ZeWHGFl1y_4m@3P(FHf0hXa4FoITS}U+1eFvGwRxxX zX4&o!wH@q+Zbw!qHk-a38}2{UMQL|!y#b+KVHeEZ-5WC45|z1YV72PYXnFB}VTY>^ zbhByc-KHI=(b+F!)v-8zb=_2d#!8nUt<2#VK?A`8bH?EDC$wP{ejJLSYMbcje6l5W zgp5RLf(wyFnat^M{*)p<8k45$F{#3Z-P=Xqk9+$Obb|XxK+=EE1rD{{UY1>T;C{r- zJ-p0oJam>a5s}D5V00eEAgkMdU)5}?vKUa0(tvt*xVSWiz>bY_>_BxQY*6p~@VZ45 z;ea0CRd1+wI(59E?QD43_QV@*r&W(fQGBXxv^s~}M2XG@LIY*C^-p)ERT{zy$lEP2nUbCKqwKV$@e)bC*2YN zmqX_qG`27P`2#>9qF8!RfHyXA79^R86omX^P$&q(1PY886r!QA@LFle4+$68>fwn$ zD!9diPeUF#LBq}5o_GVGk%ZoNECI|;D@nnseZ-t2uvCkn>6}P^1}@Fglk4i{+>ryi z?OCI)JxQvTfz|mG?j|u61iG^AxbJp{6W;L)yLn7Ut)26j(Ae0r+8obBTJVFb{#gD~ zmXH_`Ngp^-*^hf9M%8y95u$qex!l?E^9qh1n&Bs5G0h?abSwd)sC)$NAdZ1{vV@fk z4F(#t+UEGt;+)}s*1>b!<9~)&#{;)??TTj8?x|fhKt&L`K#z1BL-ZpAE+q6P0gfT_ zVY460(%KC(rE2RLlch#y`>`Gf?kzvG4DrzsA9_~t1Vw#|;yTWI*PjY%UR!w*sXSQ= zbJdOZnOau@E#Iu?abDRVB|w+DmXK-XE9Rmj)55agoh*!h*@Qb=AGTx7%yIN)mxc$J zcn$Zpr8_-Cq>?E~w6S!uNCeu$o+Ah5O(r; z-tz^qj#9yYs-Yu*IJm*JOi6fydy-rOvbVUcqOX7mQkN%AUK37|r#qAvF&uEZHx-)h z&mcDvtU;MTXGxT0teAfA%qE?fP3oCVIx(9(I9KK%@%1TK&_EN-MDg$>Ib*rq+3wWp39yKC!se6K z$bdzEP9r+I8R?`JdB{}50R3Vp8H$M^!BcP?sNwOXG8F9IOtU}LkxQ@vDUNcd4+9CY zEh?`C7njsTHl`#`a~eO(4vfF($rao$l0c#try3hkk|nh0f56{V2j^H!8oqa$KUww2 zeGx!JMvFiT1$-v=$){U9U2-6I-#g#X^UcnGd8^xoZ$ZOO@maX_;8f_EYijI@*|+f{ z9tdHm=B{dgsH@TeTBXdZE_4!yZ+Ov)SKAlOU+Sb@HQE-am+Zu9W}{0Y=132y0n!9{%pVC30obR_e<{nRf@7+j~0i9WIUr&1c1b zjJ==S7k6&kx+d{BR%ckHfz5A@m#7HaECO1?vaaOz2a+;(>VV+lSaEuOPxQI4lCu_>A02bQ z9e60@$DSEd(xHaAslq&gS7c7`Dhhl1Y}e?{Q(<0h$Ifi7XkDKHhZGQx?3k&8OdnIq zY+R~xbOc43KiPSsjZVxZ7K>!id&7k^7?+^S!&Kz(f69Vc(Dkk58_bR+h}98bxH2-ysV0KslB>Y3*fNYBNVvSuBw8S5 z4SLt)0x-B-e^i74y17k(UqUbp;h1nFv#Y1k6;5+wS@HOsk8%r21ms>YXToN~UaJ_y ziqh%UyeRKWlEiF$k5J2S!ZB1(6u(HEL$1R3)v6WCV-?DO=tZ$cgB}CsYLN6c?EX%7 z!vy@ofeuCd(#k~pWpYqWYaW&zhND2+1jGg2E^r}q+s?BN0F)G>c2>biNm!D%du5H? zYE50){E(*dE8r!GiAi=6BGh$H;g8mK{7pz`ema&=e}mY#HLe+cdv!; zxS1N^m{*=KM3SQ6Eh3HqKw`s!b@?d)rJ*~{8llv=xeRVc(8QP| zZaKRvFES@FRf8}Hj$}W#UR=*1DDf6A4~a815~yBj`%wbfXJes2C5d|BB{wrRbFu06 z)|DXwF4mp=h8>W8aRXU>j!{Zy3Z1m+ov+QV9*&C1twD>H2Bl{`WNb_N#F5=j$< znBdXeqUD2y)4C3VQ~snNeA9@>lLa>JF+U+HoMLtdF{Qv zI$+H1c0URbQmRF=MXl&Gc$F4W6TphJx)H6~8we18afF&HF(mXK7+ge6;Lmpv#E%A- zc?#&m(Q9dg1R*KWmt{r?GKtUW&Y-z)1TK6#O|=XRSH|u%@^WdqnWLd?7|)h^61#u{ zatr6^m2!CX8acXY@i%;tt0YX%7c_Y(Ml_O(9>6rDxipWGsoJ-IFN>E&lb;F>74~pi zG-sZDSY(tUaOK#wXF}bMpQo<8}LcK(T0=3}HLoWgagoH~~F~4lp0Qy6vsuO}j zM^6LgfHH~!3O{}L%k`HRtNTaMY96wL2$ZjY0|LOv07NHXzxiRcrx~$X@NRA3c*EY@ zF`1kLNpELK9-HXO#DPxiu%ka%Ni-ujkTitJL3`t%23KQ6+X3m{-$&P6D)}aoYZpWB;8+93F4xnH1Ne%NPPTF|(Ds6^4qyQi16)wDn%RlaBjYlM@=t{v2@k<$zX3zb9gv1Te$(D$G8F@UmeM{KY?qvAsBl>daTjYSR zJ^s7@Zngg=u7S;P?)jM$1EPcZD%}&w3{$0drhCQV6FI5S=#A0Uw;J12N;pJFWY>m~nT=MaU9wEWV4>?q{j zHa30eQaLD9xRR_Z1xlQC&f;znP?e#19&bfFZ|{{?mD~Nc(l?su)d=%Klfq;Sc-2wV zzc_kQQn5WIq?>*9X{$Pk@wgl?7Pm}n>b2}FUO}@7gF10R>GjzC1nH7PihxVo^|iQ0 zP-@FQ{W=FZ5Js7b0G8xS`+`y=^AF5daw6$w5v(-AjF^j`sAOeDl;8=85oAwp*QAa05Lm28mJC`Eh+blI#GfkEg^BdvYo_T` zTlmjrZSCQvxICwjmW|wn>ki3asu+LwPg~AgDQK^6Vm!?+7I+ZD?{aBrX?*kQDV|#M zjK$@ZCPJcx0T~(_l;m*TWk$Eqad@yjpeyyXF=M(twpP>LRZ=FqSHIkvs2mI;TTgg; zA)K{WKhd!CRTTWLA}gDwsyjBlc9S`vx=sInfl8rQy(xnQx*1d>81H{|d|lS|$^_SE zt09T)q%0#N9Xk#$*@R9GfQ?z9ml<#ew_pk1yWIdw00zzW`9Mnzx)j;){@&vYo+?td zWXrjqkNb;X$h_}3T+SyhfPAHMqQ+AIT`XzBnJJL1dbpacyBC{sa+@A+aidnh7L^)t zcv{k8o=q);58(3h2V@50PLZu4T{{F2lv|aXyDg^XZSSzXVc=YzCSW_U)FTX-ZW+1rB>NPxvi-^|Dap>-rW+DC z8h_%Vz8xS%vWd@!P*Gm5vi>Gs|H3!q&D;AJVkw9kU=zvfF_3%wt+2mX#7F%(&pKuy86^9mW-tPE5cYap=a_dRIK_#u9jH|j;IVsc z0p2*GvAXaMk@Hg%?VX~rgw#Fp4 zd%-qCAZS-Pv5-;>U#wa@wsD=IlR1FX9xJKc(px84HjFteEC9mn;k$SDK@jMK0ApGU z{v!Bgx$(>O>!{RYT^#Wl4IvBecKv~s#*0Z`Ru6IhNCm=HXp|bC#PGR)bwhL!W5O9v zBo!@Xg7$>+fYYek-h)B-a_2cwK5DMPCJ>U{nR5+LW3NT)#)`kRooHtz(xm<6KmC_L zej#=%E_C~5f=E^(xv|Wd@HA$a04v<^j~G74xnIPZY9QAjuB7i#^zuP0cqN8MPQTcK zRJnO{;gtKQU($F9L-ju^-C8>u);novu=QKEHi0%IHDzhuO~mcEEuJ@)Ed;jlN;gI7 z>!M;YZ)CtWGxf?B9v|6cz3j33w$MjGFO{8HAVbY9=(fTKx){M0bnzl_grcy z_R}mN4y_38UDRThsOh&($hcnCH@IV(ef@Rhm66ymtu{EPRSA3g+((ZOo|3JPcmelP~igu*9mw6dq}2;INH$6dewHj`-N zZP)R$ag!=FRc|X-ddt7HzRhDrFIqLe^;FnVO8T(58maSYUo3G?0Ka6T(VaU&ohM72 z>NOUh{jRV*F`@a#$%{O}TMOE<18mZ(j-vX0?!(LY z@mL>cUDtVS5|D_u-XsPWxHnM62>u8TcH_VMILp3& zKX#|@sJH8~Wqe~or&<4Vk6AgfMV2BbQ!}kpFGZLA`hIN&?l50O1N3X{MO9n73BJq~ zb;HW!4@;ve)pT=Y8YpN1 zYqpRfB2FusJLX#qSR+e22>#U$ee}>Z7!Pp%uSAM^11vaIC=$4{<8qzbMoBB!e?ba! zNJ*}aGM0Lp1vs>=J<-YTS~=z3XQ7m}*M8Hg@%D~+)wB zXp~Jl95-rlb$sY_Z=XIRhmgCP-u5mKx2Wqme0lq_3eeU6f;&_Z`vz(SpF5E`#a1?m z7L?>*ML<4wCCsdq&%!OwHO8?(8G%jIrj&;+guLdiL2M3!;9|6-4mMztx{;t8;&amh z4K6iG+QcbL<6+bwq~#9|$RlHhLku7U;?-Tio5c-7VnnVs%)WpYJ#kx`F^2xBp=h!1(kHR>?^LsI~i?uGM~VaT>uP1;T$> z;=>CnC6~~V+E30pJfXxfDL*NSMy5UF04q;p-Ec5~iwEhDkU8_TsDW_DEW=isZVFZ# zf&%_VKzUyeYlElHz)|dauj>GlY*g4N&vJ*e+hjF6lugOyRv6Je5(o@_*r|8;f?Wm1*{JfFh{}gMT9GC&} z7B}TZ8yjL_mO)Jm3lOOpeD2U^9~yrJGi9%qc2?6>kiSXjfkxT;0tVuK1la`k)j$W-S)ki&!Tmo72Pi=K&=(z&^7wu_xHuYOjkExE7>t)!p7OPNl|Om zQt?Bw4k0VA5CHG$-L?I@r0{-rFDRDJg7~`H4i$ zwe{!6t1C!<>E^clcuq3Y$D<2PNQ7Wt(+1G-@j`4!)V|ApYvA~Z?am~BMeJS~?wYwk zyt_Le9@k!MSC|IyU171?Z^lL2mbvz;?n>RkzqpT0CnPLvZw>TCyXK>c` z68}#H%LnzB2{G3ny_E02mR%@XT1a;88IejJX__t(w5r8`eTs-`nF0+lfMSsq1W&(vWR&Ito#bl0p`VKs-C1|{;~b0inuCs26LAS*KZUo z>G=VJ#;2616rtgKsQtjMQ!{WPbFC2GkF{Bc<3`@nwq!hx#v}`F5ibvOMZ71R1@cq7-*q)ri&}g3awnJonSUVM&c#J1BPbf<)8Nwb zPFpejbt5Yfq{W)tC6gDwlIVzlsSh`pF+u!~j7A zf>4KyA%YNq$~7;Gz*JNOoro*bb>jZbNSV-_i-m3{D**|GFdNos|FG;uqWz({2$_p4 zJf^T5O9;^0K&x0Z)WJS%DC`st6ak71>vi!S@m){xmbDcFgs1Qeq-mxkxXM1yMwk-7 zr(tngWnWILfQdj^^-_Im20aae2Yo%o38-22$7&^hIbl9pN5f!+Ard@<&vQU7w|2x; z*^MAQV)=u^8Gm!=WEiwm-Pou`;rn=wT{*Ju&$=;eJcy>qIj8l%Djbro(zWg>bXCA& zhKWJR<(WFyODXpE?Dm!%2rTgU@(?oMre;)F&`s!W6WPNH_1dB8*>+nEA?EX@wJsKJ z^R`t~DlmX*kE^j6CoB!TBlmtM^Rb^~m#1h{%6yL`%Alq0Ie{vqmkDvqV^zFTCnyfi zyZhe)H-H5fbPkSmsRbUk%v!U1jWD6t>ea)_U5}3bK^41R5`1Zyy-uf!E&yu z5i@#$v_uyiQmiAG7~iyO)LMx=Z`3qKf*+%bh~&%qj46@OOR;e0vGqsx)*%m`(El>j zjggVZ0O>t^_?&2b7RwxYdw{4>gw`;WxG|K}>RP}+V%*)*-;OnmbFS$7ANBH}P{a1# z3pSb{)gf1S&es|3vc;qJ`yG%O;LiS%LfgS_o+sG|<%kI^1*2)|QVx8L+6fe2$?m#e zort|bf?E#an>?@~Ri&@cbsHP2m;uU493L3VK!vY}l%Vb=PP*Xdd|-XHLN_6r)Oj1r|3KpLfdC(>KM?}s+tH8>6nI*I9nZ)cNl$9QRw3= zQrx!Z^^`*Dc1(_?s`~EDGQ(S#RV}8L5%+5hCbT&J28k0nt=;~4EYV^S)0Um&rmjakmU~wC0DUn5t!ou+#a_$(N(aZ(PRaAS)bc=6V`8x|ZC9Z#o zO~t*CO0fXJJ<(PgTz+V{d{9+RusM@HP`Cu}uEgP?} zsVUHKB3{l*&nHeJNhi+kPC6ImAURG~4z~8yaM)j%_zg=y*V!`Xa+tYSi}|I6Yps`Y zCN?F^#LMtoM*xXrgw4f(_9tu}9^C+(*VX1yNUs=|d$#n8@li``s$R$DluP-RjjR!a z)GVPie8>kQv$)YBMx=i|HWwExe*hi}Xe^*`tc3#uR$t-QbnNF~;E^)%9qbxqdPA%7P`lhkQf z_SyyUQNreu8M>pvOV%8B%t5 z^tG;o2ZVpu8Ju?AwhEP>?e5lvIzqq-x&$-qk>orRq3+>Vv8kw0L{*2A{eprh_niAt zh&@?T}Sep3CAmy z5gPJbc)Ez2{yEMue8Jwq#DjYn>tu(VQZUCKrnkZ}Zw(>%?B(x*SCs5J&!X{I)~6pWo^ z0|=pQ*i6w>hYnVwj<;%1FRrkKPmU1pMu1+&Sbi?58I2r<8m7m$D%{TUeOa20{F0NJ zTAFG(arDlQ@@&M^^AxR5mG8$VX8+o7+*1pR|KZyA0zRLEri264m5VEaGMzDoBGYNA zRFxwG4Vox&{2e&r14kwbM3&8&TUR^a3CJ2+ng@yoAs$ysnPnwrbgD1(t>kj2!-<4w zB1kv{PHH`>K8oV2aB%VGEMuq$9tKA|10ou%N0?9jq3&uvD@kBd2LrGGl9Rv_-bsN+aJc%&$ugiZrUU&+P z`E=;f#P>U!)1p(^gwCVso8CFNx8+j=@^x6ZKLA0~2xgCogpV^ud zot6uT!}p%h^mWpwg=2OEo8Um+0%Ylz{drFRtmXM9E%b8N`W&*P^OgRRImRh4E@8IJ zIDoh?rhUUc96CgDi{vC2e58r0Z6!N%22^iOj)Oe;~QsK0g90Msg;%ix6Vn-aNu<0%*7zaxk)l5-DxLzO^dzP2mfkLBBO>=?O z6L=!w_MT30a8L+`6;yT}i!zU0%L!xNpHyAR-2~BLj-TVzykZuwtGf3Jj(=s&BAN>e2bjM3?wXls@-a7*1LIiJwvW6he3&- zv~&NBeB7HqFmV!(;BIjb6q)64v=y58)glamA| zi$#aWt+KUT+g^-j4B!sr^#WcSm1k|!lauf;)@?u9(w{fF@OI}nhMm24nOFl#`(dhZ zXGZAmjrH?ZhL`V?85gNe8U$Fh%(FJhZBHY&>x?bEKA#`WjV18kI>luRLs$Ja%jSQr zCqQ|si;(D6<3fdjep*wHiu-L?MHSOTIxZ|0<*T1=lm)I#vV2OBs8i9gm>WLN5_gyyDO9ywLYeL>>0yI3i0G}ztRbuWxu1F*8*DqkyG)g_--w4bw^fIP) z<}Mb5jI8Wz|7)!sYe?JUu>b14RKHutpFD=+^g$K#=ebxGh=Op0DQ5MFKNh`OUBzC+ zmizJT)8vdbq1O8&x+RP_=+r@A9=glJp+aes4Ly?e>kf+CoM$-RoTsP&_5PoWqqyOq zqQSUhN-~Q@A5DR!e;|Up>X8%HT>UrzCC)P<;p8EP0__Z-%b@(2CPyE^NCL^CAwOqw zB4?HtiX|5x>|#F(MXZlf8muNN4Ti>pGJuxXpdPN@y3EH!wvhxXq680(FOV@LhLcG( zEK6>1G#sbbg9HvL24%cb0j@SLV`HEs+=dMLmp!gC$e-!~<;LH94Hg{bgT|cufSz*I zBFqV+{WJh@G$Al?1g9LN$gnc_%YwKf3s8VtKvLww$dY=1dd|n@d5#|@#0rkZv zUxv$LF@{*cah`!a9m${!n^IPzhz2Fq7EPY}vuqhiEw%?)i?Wv}(pZLrP)c0z4L6*@ ziu=R`eeQJ@qYnTV-b78h;KMBhL=xh8paI`BRsQCh*L*X}XSs9-D?}ZR95srtY;0n- z)XM+_`Foo0WS)Ez05WA-%MXsK{b%2gH#YW{te(k7gNVY0bt-BV)HUPP`q^cQKtRqulfp+o5 zF@*96JZPU@5KsSmXrQTT)JdOq8=N_1w(2js6;OS;y!X z?8Q2Mg=>b00`*v>2e620g(^fROm;*N|U4S*i3iXhSn*K6hh(tjwuXyid!cZ%&r7MA$<)sx+iXG*_~6^6}#2jPai!Y6(Iq_O+~F+N}N7d`JldbV zW4|rebMIelW?9T@hGbfEB?vgncnt`b6NQ{LLsnRFt_m2minW6W`*tlL^7<`*lZ1L2 zO-NeQ&G^Y%)JweE1-`0K=aBFS;PK>)_7&Z@bsX3M0(_7y7kb~W29uHWx#3PZScRTs3zbjyT@dD>VsH|rZv zaavh4wHx**)3|VqNXhCEq977`>V~}9tDLNHX&lMWlg~$LAgJ@F9q|D0?TwnlK6q@N z+U2oKTRx7`l|^v>>LH02`37lZD~4F7L9nmJjyK@tahju!eCw#A$BdLE4QFaPfuB?D zJHg)7f8DPARx}$NJ9#z+h>m9mh|UEe$FQpsFE3UgZZ7eDXf@((Otse>a4-}KL1GRZ zjN24PpOL-#58nTJ7>5I#D7Xp%HZH>o7B9%8 zmKjI@|4r$uo7<*kGkheh(t%2tX+yFQUh2A7DOa`g>Ij$jK;@669;m1y-TmhAk{5z) z@r;;IjrsN{0WBxy@k0bN?RL%3`GPBl>gs;8=9!Tp^`>3uWxIG*E@OU0#32)}+>dSB zx-C=Ck1VfUnaMSBxl}2eKI5@shDviC-Y%jbd!6kmfrm29Ihp<^bk6WUq4OLq z+qkV(q#wUNqtAkJ^o|tKNH6Iu5K9zp#_arnx`{ASs59N28ZRwNYAHVbpr6-Shfw4a z?JhZo{>XzV2M%1mM>yAs6T2iEJss#bSvDi(aoGfP$CU7yAD8FbDvfa^1!DylSwARs zp3e_bHhmZM-GMy&KLPE!gtzwW>VUaijFr;JDxH(8tWDWUlkAigmh&0IN8=}X?KbyK z-OSPj2Nn&5h07Z^>Ow>6s53+ADTzaQ`|`EZ8asSj+louX_2rJ2Pxehb9@#E_rMbLB z8qe={H7KNSAk&8L_mgAQK{1aP+~iDG>4Qp`t3-9xYL(Uw&VWe$<(>`1832jYOp&iU z*JW-BUH78rQ|a0@J{qUeP_H%Ovw}M~`Q-vgmP3Xqep*VgX{P&!i`_IU zR-N6sF8RbGx(Y`1J$7ZHC5;CC09+)UoRcDD)6(JwBDDINx$F zW~p~*QGlw_FsJ+8?d!MXqpxi_?`R3Hw_c@zyeKk8<@%Kfmiceb&1*Mxl|A~m&87H4 zs89*CwEAo4XEDf5LUu^Ul3QhN&6-cV9SX&eo+X+OIDAcPvO9@Fe!yXri>$&chDSLI zvYf$zUxP4ReUOCcQ$$sf{^?5Xy?Q2gtS!SU9a@8oR6P(bkA0xIozhpx*gy}5w~57u z)IMyoe65X+r^9g(^;+##=fif$@r?=k)JpVUUEJAD3$ruoA& zIp5N~e2vq@4ik)^RY05e+rrFv9T1Ux%VRD5B+aZ-OLpl`j8&x6h^1;xutugHJAOyx zS;A$1-am;~!WrrOMc^6HbxC2Odz%YvNO7nfCyY~J7l&6cig`GRwGU7uMb@B0t4iAE zSVI-m1Qy>DKggXA`6oHdxJP_MbAs7WR5q%BuKW!0cT6+kSiaIQnb#-ff9T?GTz;~xn`GTv|z$4ZTL4B!*E=p?z=M%VREEg zPMj2!S&XMRy)>yPW))(oJ~$_wV{l}Muqec2!#0T>z{^s6pT%672WOv<>&$<5^&8=) zEm$Av`@lMq7$AIxA}N;MiX!FfQ0-zILZB^g3=X_rm$s)iDm8h$@VDL5^B{1fk3cxNuz*h{#N@#$r4DiNQ(5{SjP_#EM#9# zdAh&Er(smS8u3J4CTRp+p)%I#h7=?VH)IU|Vo@>2Js<<+5o>_s_ta$ag>UGz?;cVU z(L|5wvx+3c8^t3j@g#as`J-~Seg;t7d?gNUFqLBgnInE0hrCRCZ;t_H3W(MeVQ8VM zMgmP(%qK4}Bql+&)hH3tDU2dQJtZ}Cl-*grn)F?HI;JWgw%|mbrk61?Bux?7q(%s( zvM8kR6CfH>Wkf?w^yR)eWf^0Xjg?6x7HB={Hy&(-eGjru0jL_>%Et~kGIs^dX~dHs zB3!Hkifs5Ydls-}K+95aMV;LZoIexgVY@khpb~t<42kUJ#i*gtC`BX41Ek0SgryI> zf<;DHZ>_v*k+%lg+Yu{#)wjc(AE>kGLV+8PJ-{Y3Ui@^S!`&wlgq$5umoiW=6C7Cf zu90@CO7op_IhAj@mN-d@=)_bewhZbj;?{NzCDxii`r?HL!3`^)Lr&nwKlRe~o6TUX zKDbLS&!`X9l!4vAmu$)Z@A$AcZn8f+BIa#BO=lUEd;}>2tI?nK)9%S!?QvZ&4$IT6 zD?q=3M#aj2fh=jT?>wLafpQ;$ms8eHAr~k={5Zc$@m1y+-i#VjPibtfzh_@{7a?@5 zMAFa!@%wCgGCIz;2cfrYmz4txa+1`<TKsPZMWWDNt&9 zBV&IW1Xju1FC67VWq|2 zr{(v+U8{e)%l{>_ZoW`yqf&mYb-FJ1m8W53{W7Yx&EHrZn+xg3MDN^Wk^7}z1jq|T zo~rXwUXPk~0Jfq7!&m$lIG&jF+Sg{~CMcmMar2 z4nT84!VtQvV57C;$eDtfzVUf60A#b-(dkt>rr%zS+kgK+N)O^4-!Qk!e|S5DT{C~@ zTeD%xgwLDylB3P4|8B=FxSnc+rKBjsiq* zOI8#K1Kd2AotLbQVCY0cBt^Jan|8$IJIoF=kty}q>fp4qq0iN99O{4=Ls~Fc*674H znDtj|KO1k0rB}TowYiE1fWO1mB>GTdOoEH7k~`&iK^-sFi5(#+4)&CSkgT2CpM8&( zVsu42*QYdgQc(`s41-HH9rFssP17i$k6b25e<_ ztc?Q4;axm$C671wm?MTy0YvDZ#vGT?W`+QD_mIdSOq7cp5Y!liGT^|Zs5Q0L&|eiC zsZ5A-Q`}#;)UJ{UK>w^<@+ktv`q_0HH6bpB6+YyZfqd z-Z1~;0KW7LBYGrm4}$@W(0%4LvW+tz=WClDY{YY`hGR%zs3(6|=99U+m1_AUvNLEA z*$eJ))Qw=bttm?YcNx^-Q`7RAOnnF1D^ylvYqe9up3VXX@aEsJ8F(Ibz5_T(<YgNXD;&{KyPC-qt_&T*}eUXe%vfQ?Kj&dy*BWM~qzR*Fh|KEOrp4B*CYf`B$Ck{q#F7S{83KgZ4|hc_QBQS&Y# zo%d;I2cwddnKY7;8A2~BA5PvvZlYnxz|I0)K@mLxcxNXX^bDQ(%%Np-@?-=awm;un zPeO%pu4bibEpilrEOj$g9vo##4LSbkNeHMx3>%?ePWYwcz-o?&4x%>OohN!TRN?&k z(mLNaTir0_^?VBPWM+EHB{aZ$>USp=w-pY|-{f{qKjvlF7N@*$1ooLqmW%L*F)u2` z57F@k5Jhjl2+h*_=SR?JlYSc!FB^SCN_LTxs}W)9iyB0uFQWHue;8^{hI3zhh=2#G zAZ^@F=ZSc+M`f2|%L{GHHsdmG2MQpurIQRTfWv+;>2V-|@80^0OgTTBF{Vfd+zBry z9VV4?$iGFx@_PIr0Q(qR#gsw2O^oX;52+slvZ9deV=v}!P5bEs)3x~`I$A3R#2MQ^ z%1`-gDF0fLy@Aa%i^b($)~w6F8IB5>wLz+#=fGmFpKX5COU_JJd6Lii)ZIqAADyz! zM_--~DYSMnLh{}AIM{4X<>!|DOB}ZuR1xsO^fqzSl}fnr;=v3_gI7;6O!PGE=nN|X zK5FmbHr~LK=&6Vi_V8b2Qn-IOTI_#4U6;Z3^mar40E2n*Qzo0d(c|;~WZNJp$hwce zpA5Ki*{Z~1DK>ffK5wJ#6ciHrbms$LCY<+XRb$nEw^1q(|E~7YDf_zwbfW>$Q>mf0 zMB$zx5ilW%-7_z%-X#r6R#dn7ohVKtUbaK23DhsYtNs(f0P`uGSlro;QUCH6m_wv@ z|Noy|*ch1!83_Nw<>BG^fASa$1M~l7^VDc*+HZ*=`@Pm-8W0E#U`P7aOG2!J*vRBYLOk_o?cxbXOar(tufXX8EeDy{jfHSoKA!)#XCJ5!4J* zB~*!!8KOt5oZMZ-nHR)YKa%s zMN`t+Y!?=nU*F2z=J<8;YWCOEvT!ortx?5q^|GOf0$=CR&HHqF{m>avzHUd@&yz=6 zqL&n4TePU(V&IY9|K)~lo=4aj0KPhCA)2X71Wcf5(rV;WLN$jg$H1%lgEI!4cO)6r z57{Wh&?&2rWS5uT<2|ni1tOFpY`Z6G5+fa__CLT_rWvl&c4)Ydv79gGqh4(8h>0{Y zP{t&u=HI!1={%`snL~zv9UGEQ{jT$_uTB%K$paE3zncjfwsKq<*7HCZz{jdx##?5s z#vuLl=24FB)_coRMVxs9@U>IiAG*3$PteG=5oy8{>4Z{p8D>eA)DhPt_O^Pp(Sg$G z&DDD}u6lU()>^~6Wl(s|!MnAEef7m^TLksc!^hQ7bL*O>J<*Mg(n;=aKlJs77n&H* zBjBft$=mpUK&_W;qet5Xz`CqDN(#g}L|2jjZJISrBLhfLj_$KcM{P@TfdD&U|Gcqi zD>GCaroMU+DPI|hS^o{r;$mIqZ%HXB5WWyRV1Am(M6H^cEJu9G zr5n1JaF*l-{yml@R4SRp0Lr=|)$NAP0-8>TeO2^cs90GP_^)7nKt|40lv0brzi(oU z9V@9L7hPJ7zCR<&vRk$h#)vK}dMYfLY6B>)zn*T$Pi;q?mEGu28Ye;* z%)`NViS|q#h2R`c0M|C3LO_>PE@ zInnB6jHHyz3s~WhmUk<(GwXkJcha`soeXzbP>$K1A0kM1+RNGQ@~_%fm$VpJn=?A} zD~!XJ6OQKn=ZT6n88{9mIbwwN5650zqrgTYqB^0)bgzfuNXANc*xw6E@VkyN33XL> z7B!6jp23Dm0rHpr)RJ^#mhHq72>i}KFeN`(&OYlIyDX=KSFJqlg6o7ADVV<;U3&T@ zI61Bm>CwAVp=c+i1mmcbK_=NCkix*I39B%g3Fm^wV^r0-yL-2knXAh_y%@aQPuuJ+ zxk6e1tiK;?+#URpbY5a&7z~75&P7R21M{QH{VR1O7eJK`>d{ol2scz-iLi>qsriM; zj$|hS+xP>+-Vzv_dkdPqECg}uPbq+)co7d8?;{|d8c*`i|B(c6xQXhvh&cIO6Pj+W*TmlqH_U}X8foQg~l|MUgLlwqx@SV2uxZ&J9yl?qejxN%Xs6^Gv zl4OCLZG~kC2e%2a@a=4tjzjW1i=*o%Hh%YOcyGGB%rgc4*ohal(aBy_3~e#QGu$-> z3kGv%V-IhZ(n2)4F}qvyge4j{ydfA%4W<{>0j{ajCOjtOuO-beg|@GANLj!s+6bDR zKQjI)(I7-qv7QR`>PS3MB zHM2o{2Sm8~ON)>=1V5GcA7pk8{kp_vfe==hJYj%x4zDeDq<`MC9o!K((J&L`tB|Ft z08jC(zhYj=DTM@U=|bU-x1(Ow^C8tec2O)?{G$$u;_@}V8!RrYV6@O+FmNT6^LO&q z%b;M-{ed_vPxkI?TN;`kBCvsjbBka2{-H#pm^NNxwjXjRMhiG|TvP*Eb#1QhJIss~ zg6AkGVk=&^uY#&sqL&HDrfKRXxpwv70Sq7f>poKWqo+4B!;{+T2j1;MuqftCrYu6Y3X;M;OZG4>XMN-Q(mdUcLC=aiIDxO45NGc9rre zP}g;zrt+Z*7d7TlQYm6MsEF2h0_p>Sh54^#x4Zly;`oWsTpxi+NP>^-br+W5we)6F zN?Q4msV_^7J1@)xCB?j*DxwsDS)<&-pEiP5G&I7S>u%rsbv?$?(&&uF2IkJ9_XloL zjhWj?PhJ&vEX-=LNM1Zi0Qd77Jcd61sKQx>}qEQ}2;i8L1fVI*Yv-*rEb(nS|k*?V_05?zVj|%KVrt^bDK0~g&TF-qspaZbX>KRbVaZTZ2G~J^& zP*!q>=r)(Tblp^)_KfajKrpbv2a!^0bm(J?+mJ%e*rUJuX!=DU*KovYM#1PWhZ-m& zZW+Fi=a5P9JUXl9R8U!#1>t@p33mpi#&RqsSzF=>@tEGoPi7fW0_-OZ;ta|_f^`xr z;$cmffDk8G-3DI6;6MX$`K@cCX3GeOJsu(C!UOaAPHiLY%+dyj46wTKPdJ<}{{fFe z#)1vpPMiI0p&+`cC#A91Uq@C!4sE=}xnNL$V8<;nr9>Qzr$}0SD#ds~ywgn*gZ{ls z@He_?v+)JGPV`sV0TvzEzxT6$8_EsPV^06uSd`W_Oe$)eG&`GCGE&F~y2(hl$by8A zevgasVK7h|kDn>;maDA$39~AcO&+~S54aMvT!p54S%|yRhe(=~;A37#*R}LLBR=oH zE|A~o`gv*+ISX>CWT_$tfjjGC~0R0r&7fT_S=^= z%lp&I%4+f^Z7m5w|6%<$GiOiJQxbiHsJhGW9SN|7|AeWRe~t!tS;X1bV`7$MN0dVE zku}=}%4pPv2MeDaR*OE*p1F`Mfe|v0pbWAmn|q{Lbj=i@S>^CVmhd~jdC{p`GToWU zjBOf23VzDd0m}G^sSW$H#P5B=p0ypVjut>uLriQWBa+sNAM!qqDDK&8-+cpL0XsD% z!oeVxDXvpw=$NzIUVSDV$&B6X9-j9P@6J|<<8$AUh{ECc*4a~B)zR=GGGZ$JXoM(x zsM9Ec6Fd9s>p0pi;Nfq7|DG7;l727mWH6c+VCv^w11i0?w^`VBTi<7EHxW?1owcxu zRV_lN69&;tD&WK5CK+!y^bf}%{?u`A#DD~fv%Y$g6FD~{3Z!k{O$h0$x*2b#7Lm>i zry1`>LWQ^&6j}xfI|fEg!Sl=4|laEiwnrISHsCrutFH= zuEXB{(74u21}5dI9`?;RNbUVM@XH=X#NN5W0Kj{7F9(>I=GCtvY5P8g19f$6qa)V2*Qu)ahk=LMVhfx}@y;*)4+)?Mib=3>9g%0)~ zO03B}z#o^QIhq#@gp6A7W;#xSWItR6JLH)WSSL*W{&v_IO7Ql=Zlt)a2L_kuT*qVZ zvtD_WRB>el{Tc?1Cyv2Yvl-}I*|tk~t|Clf=#CbPk#tIZg@~H~_Xz1sxFB(P3fP=- zn-P@|#0dYpE zVRn&!*>PDW2dl2_9*W#4$#<(2oZG*8W;u15d&=WAkrSp;$%4j<;%Otxk` zVrK4(T={ut)z%BHDNfG=!UFX0&eW!2y4ufbMD%KU|2oL|EN)?RT)}NT{^;sxAHKH8 ze*Go_k520dGA~9@#zcDH_`T4;O5fFa~woWA#YNbURCzfi|4O9t}Dl{EMSEo z`{Pchajl=(x&h4X?*TwbNHBti>z(cVc(wZb0kdIdlopB2_d3KOSGb6^O;)8m>N`In z&mg@B5nKdxCS)~IK$oF75bV}n&DU&@T_xcNiru-AxH5r?d?B_hDPNNzAh?;22&Q7g zMBHS(cHQ?^xW%K%xvzt8tf%ew3P>-9TST0dfaSv4a-Lzo9vGlqFEx}G7|>{_n4A_U zuvQ3eLkh##iu&0}Dj7(x8rw2EwJ7Eef~tVrW;$W3a%!rMC2LV-9}`_jjPRG1={!i^ zdVHtDS~($=sB4ZDR24UVf+!aM@7A0GYxnil7x;&66zqTD1X$VsFDH<&;)?*z&du{5 zt;|2a#Q)&}mTPcuV0uNwBmHhzYvIWi2v%yb@ijB}2LnZ%N^vC#oF@k2>sP+I-8jMs zWFmrwM0;yjH!rHI?<=f-R$S8RemEBIaOsx)$k&LF38Gi4Fb1Cf(kt=);ODlPi+vO4 zW!$=0JnMM6ka}?u6Qn<$UV3}FyV!G!2de6cque^%JN0oZg8!Ap%QJCR7<2-UKN|Gj zhH)HLl2&TB!k)aVI*qE(uidNum38J+A82i}d--i^2q=EwhH2yVv}wQH&}SI`VG{~f ze%Z}vQW<;0U9E4$Pj$9>|7<3Hj+L5$X( zY$R9MC!S6Vkswg?%fyv%&d?~MS8U| zwVyHPc7o-DlP64CMa&aNBhKG>XK7E@?N-&YM0UOV?jdlk4M*f#K8L|cSCuftNl}iq zTqWqf@Fh-4rgAruVlP4X?`EB`^pqDtC!$e9RVkXMA6jy8LMF*3L*KN(14X{tJXBbz zn$T-2hpaK()dY9tPLbfOE7J;TY>&h~ja3vty05{QR^OAt5RrsPp(mA6?H+YS=X#9K z?4y4B3U#GMK|grU{n{x0>f(=)YC%KEG}9urSc$k|lj?wO;yGBKP@oZf7Ng+h2G^&i z`!k)7l1g}-KD8)FiEr%(fTGhBDR#q^hI3ExZ8bEmMTFrbtdpj2Kk|ejYdw$>&t@RF zk&P2ha4A%<-dFbmc($+VpLjbJ=ux*lUfGE6bA4yMFeG-!-l*F(Gq{D&+We*=bR5`y z_}RS)|M=HjHP0|$lWH%j9tLAti-{Ymc8)PeH$)_XnhIS)xtYEW5R=Dpn>K0bn3Ew) z`+7TQ)}m(4(M&+IISIB#k-Era#%G6-PAdw@Cr2_iGe=|Hp_^rM{pe-lI$*r|=OlTn z`?s`@+*FYZO=Y&sP2}=w*==cWKfAl~|LcWVPM=c;HDG^2`|sO#HcQ?gG=o7%BKF-G z{=LfVZyghteWWCCOosqdkmhR)pFClKt#8dFL~s_lNs1HYQ1!^TT=t}mONpBJUhfbg zGZQ~OsVp}wQOmm9TyAs46OX`z!##Z=~!veDq|8%Ekqk}e?Psr}W9mRRuI zT-|e#L8MY46Gg)d1f0g*x(H{;)0u6>3z9@Gnj=$a*n`w~GV6H%(k*vIQo0w6=jzfW zrc^cxnIgMHw#&B^W=wAcpM#2@(C}3BAh-2|@R=2(AhksDXeqv1ph9OFlbF z*QooeS8FhjukMNjdxohfeu$zMF^>sGdT-(AU2dtoe*?fM*u>^9!mbf3Kg-JYg^>W{ z4}U*2!6wfeV#JKkA~eHlnd?Z1;3&o+I5!4Zd{TXc?qTgVU25bqHbM^yo48LscMIE~ z`y?lKSq07A0fKURPwo*D?Bk!BZofv?4NNm2OVU;^YHqIbinMQ_>rkn8TfHarsWqdL zxnFup(7`QNe?YENwx%n~SFbh(mk^d66v^}Re{$8A!z~&9; z8_O7I=r<@zNeB-?4t@<&Fo8Mz+|Y=IITBn3vxHIYi&Yf_?<=>V;8i&$VcG~Mh*y44 zk~ffk*-e=?Ij`UJ+uD4%*In9ctGGhNF|0)(>4Qimhe4YAvER0$wRH%?WFS~rG=NQ{TtP3>lY6R8|*u#0g;Jy;OPxxQoxSV$t zHtFvjAXwfdAQ+hixnaC0?K4J1boTUtlQYkhJU{s4Zlgh?U2osirk=jkfrj=14( zGe3P}1&+Ci3#BMP^N{0hu_Wk?+%GLqKa5qPE(Mx|lznoa1e%Z+pukkab(z9F3}&yT z%;-6kawA9si^*p~AY}7R>lDD`vngg=DV1I&{B|#edzng~hg?E>NmDDwh2!^_d(X@B zXi*Evjx%Wjag`R9BgoZgiZB2Mt|HeY=~Fmw9ARg-Tyv@*rr3YQp_&MGR8g@nhK0x1 zmSYp7L`u9SShxjN0+L3r)b)p=7 z0`5?;%!bbZIU#43`9OIp*B?;pJ9Uu~7$@ZJ2~$MM133ITuP8?XQZaxs#vrfrwy~j) z_;^Dvxg3s{Wrg{P5a>X5os=<{--e0$dR<^0%c8q4p;+Z0S{;i>ymt00;f^sHSZl)d zbtvQU+MF1cQE8@C!4$D9Sb+?+L%PIY9OwB?kH=9PE^)83`8Zvpn~@3UT|fWl4dyFL zRT-Fb@o~2!4mQL9a5^A!uVYXiG=$t%)%`fC0Vkx2LSqUCg}22Wr){U0P(5%UCit8q zsNbk{?Rs2330XJ3H*SE$7Bs9BW0kIP?8{8($7vluy4!?iCtCQ7SjO`#M z@`D9$Cz+J*L7qYkhZuc|ICQak0$Ec83;act;rK{L7Dgq`${L`$3ED=7S2cK=lw zdk-$30HSD38ANH#%~AR1?tS6PEq~-h9NoUyS%Yk`4$G{tQ^DFGkr1renYjN4bg|qo ziF_mwf9l_lm!8c`FZhIJE{n~Dv-S3Ro0Y$nk}9q5g3BoNK}SLu9bQeU*1Z`c z$IL{xH8j9{s#<`mR&z2{z1a;P^Tj`5`R8j)8G_q?Gp>oyU zcHa+3_aU}<1QqT!TNwX9@9Hli^T(P5;=mF-9ghA~oT&OMk7m`81ym&rel{{asPll84b$ogF`{VGc+<9%A`FLvh)7W^?byyq* zcZY^2!`{z<%nJBf?d(q} zb&U58+fo2fzY?{6tfNo(X8@xHi(cDbJ)T_?Aeg=E`<+WIW^uo9;20{yA}u*mnd`Cn z&N9)uGY)SAp^>!_bybW4EDB=J{%|8Ypsx;Zkoq==P!0XolokA;Bl}Na_$IJKcY>7c zO>IKtqvc8qwIjEhduzJ^1MX()VKR?0eRHTNH-ZDqsFDRFEkpbz(4+}HP8wD#4d%{p zOd&9d^9hJa-f`swN_G9QpyHO~TwZL>9#>$GteY1I6X$~AuZ3D)^q~OAT135R`WKx} z8nI4(_d8kt$+Od|)$-8><+VVQL)6*XkhZVpNO|mRv+a&jQ7cGfg*Ye`2-i7f=fXoz z76^dpo=^Hpi?}k|sx1it^3=A3bX(U*l)EeWPGoX7FR23gR@@tRiv6|?O;bZX6=IT6 z_@Uon^!WnzgmrK-=^A?%^4it2L^-GdD*X;|U*e?{fRnSHMrH5Y71Qu9TT>Q1m5T@6 zt`WOntLEfx$!3S&c!ZyD$ePQk^*-{a>I5P=lD!TZkbLCg1uqIOC~HM@-|w{l!k|kB zCJ#S;9A11W$Eu58;eG8Nq!ZIM+G^ z#+RH>19oGB-D{C~UxWvDv8lIlwbuRs+3>2UFP{ZBAl!^AS<){Ya2{f`?}{dlLypQA z@|#GL-lcC zSVmNl^R41+pUkKvO{wb5xC4Lb=^)3GJUlqM^PZV3hPgvGfBKvezoin=XwyNxqtN%7 z-=7)RomJGdlBqA#)xExoiqfb%V^;hi81$z-psiut(tS~(i08BTaptUQJFst&r2Em; z_5%8;y9|}QKC$$6ceV%I&U5P(`Qw#Yx-}UtJ3!ra+mPy+JX?-d4vwe zywu}-YkGKXitJi%lI=u@TDmEva#R1-k-wieU00F!xxD@em0diA%zsw|Gjy2HlsM;u zBDCy>f28Lsl(l0MlyRb&JDRORMnO9fu2l3&3aG4P>tEow1a+=oBJ$ct{ zY5T{&^t5V!xwFh(c0NPuCY?Id{UB!PK}_c!4>uA*JMuVQlLf5EH}6p;hHhRx%I^L7 z5`y<`Uzl-uG^3eg6Oo$!Dc$>zR6vJ~CUb|}ZkJxz+W1)C#7{21Z%q4^NsJ7-vP^$759zURhP+^N7JS39Eoemre!g+UUlonr0#ny2!n7} z-U;b}qk=(fWy2&-W|agPG69wh1QC0erI%C!};!`mf|D#)KQ%P9z-iAitnm%nxFt8Pk=gn3b$8<_Pyj^7Dvhij?qD%IhXu90i7i$W<9zc#hc_ zQ=I0HmPljkgy)sCca`+N@u@YXyq5Up%f2r*y3@Z}sRxlD-26NT=XxrxKUy`vbYeMO zm;h0qV{Tpt_8lnpTY7cf#rTY)+t~gxYhesX_s$b+q!Q-;obJXQq$~4tB>m}6q0YRa z*l2w7TF#+0Z6j6Z;Z?cwuS}A|EY4+K@f@U!U~P?bJ-K*a0=t{`on1cp2ruYZ+=@M& z4i49=BPfRY`mYgQlPAVKmz20`6B)g!oz-!Evw~u+Ldtr(1lwfO) zCl^0A?%VgDoMoifnyGX7n4MZKya{z( zm79i@Za3N7SqThV&Kz#0j0p@Lr%A-ZC>%AYHry0#ZWN-47u<4EmK1eC*aDcf?Ova zaIDK1CME{y)hLO@g^H^|x3kMVeoPA_Y~w@?fq)IVw0}oON}BkarPdu?uqa9CpRXc0 zk|Gk+)aKB2>ejGW7TmY0p}+Y|l@ z8VG z^QS^7q#I+bFVaQm+)w~Ss7=XQOl&YQ-z{dO($ZOohu3kqfP`Jg0-yVvc+9l$G2%^l zHtFA^;wFC^vCO7Am;x7_HjeWwbO%TMZWa$*?_2Q^1$P*Tb!e^wLct0xqQybtf64z^ zT(spL;Hk=5q+kqLWgYY2wQyuUhgi^%&FkvCOJH*&SLC2&e;6x>OEd9^!FCa2mE%z@B*|P&@SzE zAWu*;<ttxxv-Yr%h0@7v@hsk51WAS4_vW`96s9QNp_%x#L-voCUkigmL zq@fHk%Mzde{%QTUgc<~OR9aIMhHQ**@Z1jBcQ`eLofHWi=&EE+&BBh4z8xaubWtg7 zJepK$G(X-B_NUaq)6KZCBG;e=9ZJn~L^>}3FKSh_1(ov_s)}{GfEQjI0{W?xo=|Di zWy$Xoro8j&;eV8hT1 z8ZyS;cs93ub?Rcc(I>b|Z=Uxcsx4G1+Sxl*k?(nbt$T2ta{+n5tLQEA?LhWrgXAoY zKa4im7Pv`Jk*?ixZG%fs!5X>O?Puc0#|fV*iPEIuKE~G1{ zMqzyT5$sLnD4rR3=*^Qs#Fj>=ARVqv?C&{0MabrngghAv+#srP89cNXKEN@H$zX0- zH5vC4#WAqNMI)IYRo;=)qVEzO#P20?zfmjVkf&nMt9!Z1;s=X^3^Z^_r^)BFxD0G_ zG->G{eBZ6&!`{%<1YY(Bn+>R&KO)H*pMy+cO4Zap5&k7Ae=hh)9Uc>vosWu;poj=V z5ARR~7I`V2fE~Q<7?z0InEJTmwg`50IeMsW1|7L3GqfpRYuUtg=|p!4U_ks(kF0D$ zc~+rjS!m!3$lDUh$TT>*rH1kGlnTspm(L*17k-1~(X)jy2bLfggoij+Hdh)Wj)^bV zTC|p1JS7svSk14mQDV|;X|B=QvttMNkEilnJRF|wX%9RVODt1NQ_BQ-n5c9?mT(8w zMf)##6AQ!HGH%5PcBeSWUKdi| zCNkM1SqM}bRcN3fqUKVu>$M^D_fvH924qv7y^NhS0awh4szOngBi3v9C`0u)UH^A3 zTT$q|8j^H|rMeUnZ&oz4UsVw4qp}3-m-TT^(BqYIA`ftlT@R_9Fqb40=XL(jro0(S z%#Z{W?fE~OiWl?_#pLF%zDN1P1P*M?7mu`r)gSZL{jh73=lueprnZ=tg7J(l_Ig08 zFpV9SfwUsZEKGQlt2BCaL{f#Kan?&f_sD1d+1?#h@7jkE^w(4N<9M{q*u)7|ol6Y& zic`_3F8*fN7UudK{iegjQ(Ar<_B6`j!yJ$1R>8})Oo~K2Jq@IE=8PXpVvCxyoB~BD z)Ld538JC*z8nivk77p|-t|+NLvMA{^DjEkFK;FxtkFqaaDo%?=wLsSCVfzH3ZiM4| z*Gv`xHk_z6EM9BU`E2y_-HmaTX(HpQ^?W*E%y6)St-ee#8Sx;nnVtMms<0nL7kWHf zTYvJ*oU?v!RD4MTUqtIo)M!3gb*sMcvTr|T?NhSofO0{!vg_K&N`gq~fklF|z?F&d}1DySmAi2piwL<;gYKSv<$xI>N>M0deBD_S@ zM^j<^dKND4brF&TVLhUYqgUL!$|Q5yrwsoPmgs5teN9EAcrvC5H&(d|!AI*ut3%61 z>+)@ou*O{Mx0+Lxqzfa2UBhcUF$!(rSP)!@bzVeEZjhRzL1HD91{Nvy5US<%EAO=;r*=Hv22 z_#RqKLEn;D`FyZvtm>Mi|*b`Y_wZ)}JEA$hHv)X)Q zN1nN42~AXV2B0g>GC@Cw88gji&v54CHi)cgPHBlakyHmOT6G+*cVNd+a0<*r#e#tw zG`P82Eg#y1N#3K-I`y@wh#;1o}H)Fk^CcPW^ z$3G+7vYNeQvb)!x9~d?EIlJl(Z4_<-8tWVK-@|_3V}&9D*!H;zo|dJw0U1wM4O2Bf z&~4Km$Q#;C15GDA?)UfSgb4|ce*;kthQ$Av6o8)?34-)%0ogS&gvGKYsYAL9?XTzA zj}@gTL4jM}xM6a`Ie48b1FcH2Ho>`Ojfy|oK}e=E1=(CMYwzq?13be!q$ks% znWZ+)S;)p9n%Q2GrR(T4L)7LUH=d+Up7JerE)X z@I{c&{Q4;I%4)IF+M>4=my{R-AJNF0vs(rRcdS;@S5|oZJXVbD@_R#^yOAzg^7Z&$ z(|_b(aa=5`1wtcLFwf*2rDZgiDHt;RK#>T3w_odA_m7w7bV51xOueq06hCh#7_`2e z5dC9!3c#|{x6<~mRJrKb%MeO4glTaX;Yhsu_KZ{mh=uR|}m zbIauY$}-Ufx%#TBs^jM}0y)u@o_C=)TA?E0BKsI+;H$6+4DTNA9`bfTk%}X_ljz&D zk(CSxIjG5&bohAfzdJ>3RyvD7DxN+dWQT25JF3jysd@>19XZp>7tZ(i6pGa(~l;6P2t+`}&xsDQYA1wmf(PziGw=FN*ad4e~&{yqGlqTYZ>&<{J& zUBo0lTBimGm0ouEn_KBTbHR`iD(W27(fXE>dNH+l)%E>YXmvk?V)9gDI5uPMVF011 z$SX7?S`G9}r#wehpyU-<4G?_{JwpT*m9uSMWJZMQCjJL5R~H7)1VN6N*+(e$#KE!>F48izw7ozfq>UgrOB99 zLEi`C^gB$!j>S9I(_s&9D2dm3ko<&a7|TDmP>$gq%niCDEfNhc>XVimAZZ51tD^5g zGj(9BPRR+C#D<$|mN5haYT4a1Kx7#^-ENbDTKTGo zH+^o5sHjODA>vvSu+;|Wi#Lm~K`gr5XWbMh2nX2R(iyC$lBG%rQb4j{=!8=CgECLk zA3%SAM9<`mVlb?h+ez9rE7$;hv|1SkirV(2m8H7Bc;(j+D0ILY*VSJP>Xv>f&8qtk zrFIrH=b(7*^^lL7mUWskJ)l2+y{Gx@kAVH{s1^1HrROH>eh9lU8@wQFI3#9^yZdsT z5dWvA@4Zh6W%=;Kx!+}mBF=nj+VeB;Symcgg+FBYr&7N;8i(gV_=yK3iz15Ug&rym z5d6d&BSPX}8B=|dhJeoE&0`I=-s~q~;KN8S*56iL}LVSzuuke)aeK(6a5~{d00`A5;5L4U|9_LJ-I;4_cn+{UR0m& z%uz>4bBb+U8Zk2;9t&p3d_Y|pH#DD;rX>a1{SG#-`m*v}?Rcwwn^R^{%Ksgy?94~< zBAEGL)6lTkbkI-qosWDlF1?L>1+*x?ns9TOSkvnU6t-y z+?DoRcuGg0A|gJykz}(SmG*w0<{oqfxBm=|?+FY>``5zwH7P=~ABd>Io6Lq>L>#Q5 zz%uROxp;i66`1l{D^kJuFZm%IJ<88(Y}$u=_qlPbr>>}>+pJ(1V?+S%pI+}4GN)L( zla2AsY&V;9Cjk#QPTn{VbVHKYTf*|R4UfF2Ub*Q4uc?*MslL%Nd2{y^108S(rGjzI z3vHSA<;Z%$-2Ps3U%$UP&=6*OvUAkOv^H=az15Xjd+&kw(qD|P&)ur3IX<-%b8UU+ z@9Nrcj7&O{=cX}_|1{9ycYc-8;p^L>WO3*(ASlVpzlsA?^M=juexB%KNVaR>*clwy zKY&>I&WktV=lXrx?-F4rEdL0oiVWh8zWG=fAD__?W7}+@s`HQtrd9moS)uEFY_Xm7 z6ZAz(6Xq-XE~~A8Zs~eUN->s)BgoIeCs>ae0F&B-(!!=j&<0 z`S1+-O0~l|L5?;!Ofj=0}D!p%+37& z4M8lNoLv9OOr-_n|C5%}^`EpHg0Vx#P_ciG5N??)9Lrf^g}}y8WNv7&gRAww*?;m>rx;t+MkTOPWZmd7ecWki{cpcta`7NORVXCvMydK} zRE$Tv!7v`P&I(r#hWoifZd(~@(CldF%13V>SWgDi!vUh2ru30AOfl5zVJ+g;ln_!Z z!juwH@!vJw5}n=PA`;OT;<;(f@hPN|NC;G{o1uq>gqs5_`fB5LNf)dT7~#SqBs*EJ z*(tHvzW-4oD=lIjaEGxg2WHYc!b>_!HmeOfMKlK- z#2e+6?#n8H?zhtBGY!cI!(o&q1!Ns6iAv<7@sKP<6o*TI;#kF7GE()48o9_S>_A~f zLb}5#hMpzuwG zEWR;l#EEfxZltP#0hF{av{D!rmFSb|MMut(G^~AUg@Sbw^aYxbi^ZkaL$ zbymxp zLV+ayGxdt3*2U@}JV@5n^^Df5+dJDfUS6TBDT0DG{no3$R@>9+@YqZ(LcmGRx5tu? ziP;;&IYN5uuJv`7P0$G{wb=l^aID@*ykZnEr?xoR_Zzruccw4C zoHQ2)3v}+T6N@N+ySn;ms?DfXZ?C%asU@)nRj+WwAzo_e)~mOa5ZO6Yu} zHtYpA4d|0m-1wQ3SDob#Z~W&3w5zu!)PpM>=LF7?I{vKWE-QTJ5v@D&;0FoMndH5ZCFYLO@gk2P^bmNd&gNVc7AL3l)(du5~KfAdtBOUxZ?1yn>+JM+ul(F2Sf^JPV6M7*o$|Lxczwo z;XnI6!XJ-A8qnJ2MZ53MX5p}0 z(BbCr66i~S-WIsFO7G1Sc3*W-Cx_Fr&D@QiRO>HFL7yW*p`lXyYeLiGVyE&9{0fCP zCW>1^T+%6?Y}mLRzoqJcUT}jMSwCg8SNWGbYep?6UvZ?DW`?O`k&&@gzwBe3GCdSY zM-P_~*@kQxq8xGk41mShKZ9A=Y!u~PX4kDeY)NiMLKc#OFW)PpV!t<%nfX4;e3F4vHSFfvjs;GwLSN6b$_<-pjwIenFyY zppDsSMhccS8aYis=TqAD^U(e~i+FPsa#l)CTk-3=*VTvs>!wf#!fVEKm}JU!!O7v( zwsOIy8SZOy#MHQl&Fm`>*v>k5AA;G$x{pg_8Kp#5Q(vT9wrJR`6Pb61;Gd{`Dy3(fw!5x#_Sji3U6y^hv?h z;#@)Zc=v)@U7;=1BHLZceVZfBr5H07i-VI{y#;{#rhhzuAeGgd5)QFPN=E}*Ljzk6 zF3*GgKPJE>e+SolOs+8{m3&0>kb#pbM-i+@b%;nx{`DYvq&F&ubL>sS^ z#e*&Dl}B(b$fB$A{;ZphFZ&fqh~wd7{?QicI$yui>&U?fL%%zdrs&;cM!f-9!-6W0 zdUwdi(0W=2$Fxi}3eL8;i+dc*hV0rJT(?^Xn*(K09Y_rsmQT>wAl1K79k{lWL)hFC zqr}efpa^&6i!v6f36Q|WPWH)!%QyGmeL7A5HNhhUjT-&+6x1Oj;AT= zG|QSF*BXHsJ*v4OFZQa7WeE$_{5sdda~t@f20S1Wi5HMH-M{Fq*0e2R4eUO2rcR93 ze(t(mTqrTc-5CgO6lw}~M~*TnSz0~YZZR7m#D{qhrq8LE8o5PhN)c2L%b2K3XgPRv zZS03$Q|j;O_1GM$3oVFvjKMqePA+5}tzg;)N{(wol{j)YQn%Z;AAgSIfiJDtHnY#U z0z<6Ap!%Jh5hedy^BQ#xO5!Mo+EHL&0hL;9b3|@_IdIMpqlg?EE}uEVkAUFW*(Td( zjz;<7o}7LwYM8Cq8z~&h)Vc?cq<6e_K>NUe!$%YZdTV&#UqiM>A|uIPx&V>a_a?Kh zv3Pm3&v4Pxp3~YRwP8pAWrfi;l-mU$raC&BX0&ncOt4AxOG;+)=myg#A2aZy17erI z3*v&-7zE_Jt1Sr%W#bpV0;>?hgRisp`B=Zd6BJ84M2l3cbmu-nHsM<{m385-ggbVq z{rn`*U;3g#4Ksl&0N+bt<_U=no=(oD5U}wMVkpcLAU8&1qqy_u)QEdB=t~C>%1W8Z z^;uhO7ih{{CbJ@WAw3v}v$A<@uGkl}oR8yqb*?bLtyUhuOOY>JCrBMjFA@icPF>i% z!LVhLzg4Q{`F($OFZ7XnkB!OOL~j4X2d73B**gR02iJ2?-UN;RlQzr#;eLb{uRhAW zHZHYUPH7Juer=AMIrdD+b4&{0|B$^5S|e=oSg78}WS{t^89r5+Q}u=T21pC-C*XepH(OmVoC-x}#h^BGt{O}B1EVhHwTuFy{B_rO3NA%J3a z{aBZgc0CE13KF%1*S|1ti94ZZ@gTC^IX~MPQqPmlW4%r37|R12rgQ`vJJx=FKUw@M zNy(fU2?-wyE!PCl&g$-fL(Y)tD}^-f6rl{t7rx8paI2@w8xiQfhiqYF#Cv-eJAPFr!<9I zjszw`yD8$U9m1si3KcC&0VjYvu|ZD?+5V?r;;G%Z!cbi}W$R^$Z-kpX8o#_h$>_ui zLM5;HPNY58fu=2h!VDYmT7A|guY|b8(?c1t9slPW=*~@%!Lg`$-1v-sggW~1n1OTH zposIKxlYo7UfS+O->(oS6`0OVWw!!CWZ}+5qg)cCSV}BMUL@V*pvsJPn5XctSvy*m zTM9+cVp-`o0$Yh0>db<+8w$41H1O8k;tbC15vz7TU0!&gn=o8Y!x;PUEZk*g7y=Hg zVP_$K_%Bfulg@N&>fHHNV5)7l$DJ z9d*oMWNlmq7R)tU3OT{s8JggsuVOcd;4B6%!UYjoY$D{vqEk_0IUrCG!L{BHh<#wN zzc_`}PBP=#cDyrc{suF2|M$lISJ+ zW8>Dz_t*Nxyi(@YKWGuQZS+3`GNvpWz!E-0lb1eWjVR{wnciG4pM2vTy?f^?dnxVL{(}zy+x~w>ql|w)zEq zO+*I$U*=#|&i^t8gLC}9LeWX>e>*X5H2-S@l8NyV;fe74DK8wurxRiku!-ERw0{wm zE3AC#`2(m$uAM#p6`F1;>7kzy zdR3=iu3g##8XQA0lHG`VJ7+DJ#~WYhW4@2}_j{zZqMqI~D!*Nl|2TzDR1L5*Z&XPP z=W~->eQwdLq6f>I835G~(1H&!Mgp)+AH>lV*uGB1|pc_U5B{%jgGZO+szI$w|On0*dy##?L7Ri(4 zS|Ila9>qXpv^@&XnTHgfP0nEWt#U4S&byOE6^qQ)7nxL+76MCZ5Z_uIB9bQkPwe=< z1?l)uJ=bHu^sD9Ty(}+l)=ii7x1H_Xks{PZpKh6vZf0iYIH)4eF{@payMrp)Zw6g z$eo0ZeaVHX4+WO=lYgzgiW=;t!Y(Lo{6)0VPlZ3=zcmYsXN&x!elBhLXYzUi|9gN~bXul>W| z>^XGz*&rb4bcj#C*Fi6lI$W!9qszAWnHlvbDtx@D;32U8`zBV39vK@Vs<}E4SzeZx zhZ3ZjeHt6i8bzLysx=03pXWO#iWtF}nBS(lnG;e*nV}I*$6s4en^g?weS)fw%H9*nzcuI|d9g>e*%cJb$Kz2F) z;v!Yc>m=ZZB4>`j=i`2R%Vf9r^PTkCes9RjN0R_MSL?*}X9M}3ekR5D056W$dXbgz z{=q-+D_X9-uliu&AjNKK5{bVJwHlqvw)YMP&BIpGz2)R5;{_3wKq5hcl_x-bP#_&O z*VUl;dM-J=q;2h$to2_kb{2B72>2Z8v%SyjnYCRry*E0lo&tg2*a?Hv;Hg3s*_F+%As021%dCvGkt(T8wrcu;)$NBg%&eBjUxb|D znYIC7aTayIH3`jJFG!<(5y3!h*$zkJOcuF$@?4^GcMs)m%tO9jF@Gq(R}sF6=C4u> z7(zy|{VHq0Q3M~=m5|D3rjn@3|DG}tsIY_D``8NYlaOjA!@2$1mSO_ehRgm39u>=U zm+Z^C9lBry9wnwAngXe8JQO7LyRSxba4{4hplkE!rBv_*VTZJYn{`Y;_iKbFLYE%G z+G=d-wQ5;RODkQS?iV8U=304)(-BOrQD;ZbM%ZqE$-kiv$utVz;h$QR4h?lNs2p~tWT^L3YI(M*NZ!1p>G?jhvS=&dHbFUwXfys5B* zBI_)9Ux z5`Hmz!l<9IQzIA6Dt^LjeU>4d87wN5WI0NgN@(l4iUq^74@yIp$==w;5U)8S_v^ml z!zBptkJsu=aFSS=1aCM{F)(>I`eJ_yoI1xvE@xdbgd5 z&z`3rbm0LvSbCwEkt|{L{r)0rm31&^?bfVpmE5|mDGzb1mRHjQB>(+NT$DJPd!MnN z%>o?~g?Jy%yYx9Rc#tw)3_}Ts=N~F;!sS7QTk?3SRk~pJB`>c+TYaZ|41%!rShZ!z zQZn?%NFe9uijbVw78oGH1L`k_{=hYlx!^T(~S`V&()cbiduNZY$@GB=wkZ;pyl+6Z=a1d;U>vax5y=a zZ>S$VT#P;Fj^gylbF09qel@GGVujFIMddSQ?rcP(*Hk?DClnx*MEZV8fW$gA=gJ=; zR#sl6Us^s%q+m3CmMrL2;ML3hThT&C+;a)FB=>ffz5pP#o9OclVZKP^#3F8OcVm*p zEviRAGc8Rt7q`s0tzG7aYa53}e3F5MZaXL+uM|z_^T#o(qy`o?Rn;jo1A`=G5Cg#c zv=EXmF4Coxg7j}rphM&@(tQ%Y3@O;8vY;_S$QMK%};KI!1$~L;Gy- zn8jFj&d~mzF(Qfoqtd^j{z%R{YA**3QCHBfE3|jed$%CpqoB;g8-Zrup)q= z>P%7ZxY%A77o|!%A+Q9_b>fj}KMWy1T|Kq-Wm9Ds$yZ-GEG7}Q{UzvEn6=G(uZ%2E zzXiyeo#HXhXw0CBgP>d5t$uf8e7=IcVlqkhJJMZ5aomE~!%iO=QSQ`Wt9UC0E>v_^ zMe+Xvfe-=~di50Jmykq3OqJ>P9t=~t8vvUuod3OWii**kX5dQ!rorOZ_;I2fZoi`~saQ1o)AF|gE`l?^*7-Ye4cSJ8qP-b&o-I>5G8T?3(gN* zV8ePRtjR1!(cIExRyutMMeez8CrdF7v_CEtnDv`iK_+c>*jnzCy}4M5vBNW1$wOZY2*bd?ZxYy;52DYHeP675El-D&`I4h)Dn8MQG_nbPrE{0dOHh$*}>W&XkOT@SOm(|kRrb3$8dSA{2cN9HErvFbK$(+tIQX1hyi0<^8O2LOvH?#8R2 z%-6&Wy*tU)!}ddYraiTxgZSNj?VA~8swa>q{zuB2cDI8(?RbZvW>G01^vlSTKe^vw zBuxI0b2^E^&#kV9By`Ak&a_ik6M8Ccf1YP*sstlG6yG$>NM=)s*sfiokII|8b8?{_ zk6e9us=eb)0(c7(+vqi8bpc@;x1^XDRsE34B})A8O+Ar-nMI_($QsypnU$Rzepk0+ zlK8KR7+p2t(+RYbpHL>cE1y1yXaAbI%tHKx7f%*Q{uMRT_=}IEqk$=sOYLud6D2Kq z!5i1M8^$Ujepu0a^E5l?ufVFdO+7i`5^og*p6=>zp|I0~j;n{D`ha-3FEow2DZ~~t zE@W8`!pc2^MdE7;r99XvsW6<_qP|5(gD3nSz&?zfu)@Ei>BSe)=bPQD?>+*et_7Ui zXg>fZSLIP~c5Naclh8HreCN*E)+Z+%@zcA9f=ezNRkEKb)i*=^bN-3sETV0k=O+B8 zjSUbOg1l-H6*lAZ1IT@ZmIcFbh=tTGCdauFUux}oCl0_S-&i%|hJ;~vqE(~ey1Q-V z$$w|If2f&kbf650GxK z;K{g{wx#0xSI^Kg@{ZUwUy<&9?Jly!OPD79s7;!sv80^11H6yl8PhHZ0X~nB6mO`$ z6Xy5(J;9%OK-D!|e{q9`Z(vdCKFQRg)`|_1gdJBVRg?t^NK>QWjzCC49(=-rP!#v+ zIL%1!Dg2yt0%<#CSyaH|aytYd`Lzb;W0mm?AIv7bXPG{ojeEbGPv3ge~({F5EBH)d$%5*dgt^-rchx0Q>yiPE1uL5 zod=$){Gt3lM3jF0Sslj{^m3sIt33e}B_($98M^xDwd&4VZd^KfYf1(_Us06B&8ni2 z3`>~1sHe-yEC?{V_^juXhiLqU4&F4Q3T;UVt_T8$2e{8Z{%a{u>URM*Q);Fe36c-m z>fro=ar@QU>`Y~Rw#|;Y2g_}q?!2ze%B#e|%-C+A33BgSKl3=YS(@W7$bV3ttMd#6 z$*O(OP+IH8GH45%TfE`u56>lX#W&L5TYoLQ<9H)SW9>kEusk1cSUUby7D&J7E}^G~ zCgim44oLDjz?lS%iTcYBnjsy?HXSk|b8yd;1vKD0adBqxwdhm=^ybtNINVd}CTlBt zEnq81Z(zH3%TNyrh8{Z!(*5t{%SzhHnxpn$4s@kBd98x+Z!fCfC{+WC-%x6J>u56{ zNqqw5yoAJ8oEe;Rn3ZwuNPZsc(3ngl8 z{T)iuhy>{TP1_{vgUAwjO&o<`#^Q*da->bi+QsaHt^L-)PKe3@7e}(cUWy#FlcHd? zJ%CMhSs|i|ho;C%M$m<+?9GHZjpQ<_hXdyGnp&0dfESR{X4HM6#3JP7nnovqvO^eE`J* ziHqov)RHqZFq=U(xUEl*+rf zR|CiG&>aXkTut+qzU&(Tq$Hzygv3(R{S)QNE>O7YE@2y?5N0GKuk^&^9Ktv|&ls3f zS7`KLze?r`>FDM1)~%CkKx~zz=fEszwdGq#j#~LtZ?NkXxV3AlyBdwb8+q{e+xInT z25;ZhC476_Bh9g7NF833>5Xb=3QQgyjTc&X>c1nbP~zJoSX0D^vsT0u2)_&pMUSh! zRr-!VvI~v5sRMT@C(-FDuFrJE8>qwpWghLXmVKb9mKZeWjou>|fIfu{iRy4)RANNU zn=|Y{(w>&%} zV86NxeGTGjP5s+txOtKHby5f;`#uVpkH9Kz*zzxys%ec+b4T~P1ASF$k4gz0)2^7A z)u~vk-G<&!jYY{mKus#ZuZWTD#b7O`0;8 zINq+4vU0xL9ye{TC|vc~Odn>=wa3*}l^8+UviP-=cWk+<7VK}Dg+f{B-un3wGQnL` za~>E@-ukXD6b3k4Uoz#fwN|t4Ff@!yi-+&SqU0WKjQ`!O0yzIvkVjemw`BAMBG3@j z;P(`|XZsC^8)Z)Q#AkeR@Jg^?xdJ2Ut*9PbAxy!Ga8EAjZ+alRSki2OG+3VMXkpyVLrD`1e$+j;Y@_0f>@B z#>|+aP@jxgV3w0*BJgTS?~d}jx}3gtDNT+HRSm(%*KM7w2oJ58D(b3WRg92TzpqTo zYUKLsIx7wMQ;~WC=*pPcYulJo?|w)lll`SH7lJfDJBPWbU$>(}mI|fc2T~l{PVQ)k zrzw(c$uu5GvcjFCQv*CnD**2{1PF1mPwMg_J{T_Ek1buVt9wyZXFwe2$os8dOp@9O zHIR}u0tyvg9o{q?ti1#H1)_Ap$yd_v)Ruwh+6CM>Sq)?d*JU^K^~E|})VQw7nMR51 z5cek_K-6VzP#l?0 zt%zNs(D%Y3ZkN0Q`eL~C)7Kocg#+xG&%bFhRF%u2ZYTc%P8HG$JZx^Q`*;9_!qT6u z3SSK_3q6IMVbe~xQ0K0{6+o)HwHjr`G}uNND5dV>db`mPn?gh9{N7su_ z+Mou*o^%d0qZ5c_M>vF*^M_>7&U|y55}Y|z@*=g{_XF(A5~a#0*l%&onuyx(aviJS zC`1jR9gb|BEr`vuoP7rbyj#?HWx0<`Y8F4ia37=9^*Oxq)|gU$0l;E_vV^%T#b~K- zrx5Ai!;S0`FGohO#9y2yRKYpV4fK~)^)4O-LPY?)UZ_DN`s6rN@hBrwyp0HpN{&;} z-or*e?U3BVOk;UFaCG9vt*urg)N{D&LHHdJL5iF>(Gib-AkqtS1^ld+3AlbfJeC)A zLm7gzSHfBE-O7q=0zgfzbG{Zvh!DQ)++`9IOy$m;Dq$~)e7UCg(uwh1}U~e=?`4NpgO4t`o5WGv0XC^*ib)T^Ve+Scfo!%E;!LEVW zIwWq(^Iv2nri0;awe7)vx6Xe=)#VR1gZYIi;In2q!5XESxf>>;9zdzdiwsUK28a@32^bJ=WWPx-gDX1q>MD`EmvHy&uHIc!z+WPn5r*1(~b6szD!r8 z_P7<%+Lig9;-_C~D#}q?!8}C4y3&L_3?s9KC`_RpC@dkM)O3q-<>O~&hHYP`&Ma>_ zDQ^;hCh3oGw`vRTg9ndKHK>Mw8p-D6UIm{gM2`JU41gM^f@+AW7S>kz!c$*nXKWK+ z%0i`wqlUsw54Rx|WR5Iym0&@xlF`&(Vh&&#c0%A(OfDv6hTux)zN>X@s;|rupGup; z&lYZ0y1sOSy!0X{|DAkktIT)m>U$nt(ijoWXD5p4ZC`vO8s+A`xc11rt1z^xtBxq# zvLDhS3@Eu+dA6zV2h^HWh^*!heWXQ=Ua0Ml79oW}80hgj)Fv1hEP)k1YLuyAx-V)1 z>jbM60SN%|)#k&CzguuQ_FMvPxklpW0yQzD;%|iH&E4gz(Cd>ytI{jx5c5`k(kKsoe&p(iWjETIqyGJ z;6|hvH%HVK>2<<<-7sGjEwsnN!6$cyEwY!~97|v>vd9EjKEFcO;~&Ma0GHV3IKkxz zZE;_b76IHq&hLSP=K$>YG{wvNQ*hW-9+Y3AHN_Zpkky|OTOShHF1*RPAqp8>fEFZD z&EK|^M9ThjT=l37Ig`CvPL(1zom;^jhYv_%f4Yo2uI-|k*W36uz5ORjgX%Zz+%&&j z{vH^laEe;H5AnGPnqj}CG1%Fg2ywRTE@C5Z6 zYLZiid5}x^1LOM6*xXn|*DAP{)dGWdaji*41ZqU06bujGM8O9+EJ`2B6dicsI-RUPbd~3 zgJ5>vo(5WGntydw4;Zo1+KyGoi4yBgma158N=CaRBxGO;bXm6$4Y$_29NL;uYbHZ& zxAJ0$8;+MX;F`6o5P!{J0ptv)t7cXpe|)?-ZHSxMjqG$%rc9qTn`>8W-S>K@u~Uts z5k>ghDMHu57&0717lz&N%Kuooo;VeDvU-ZnyTV(Qri9pjGysmMth9^=f^fw~m zLH*r0(MYU|6%EVnL;M@saEMqM7ySpOo^9W$xaTNb0pcy>&U4v&@?Q$?80yvA)KJ80 zyy9^aQ96caMAzJ)lc=Y*jb*rzJ?{l_V&Zp1MiJgN%8Hd@vbk*B*kRK}lr_w3fi=4k zRDNc9jsyruoU-g#mCok#n^ls$Apq3F__(gAeX@OgI3`;U{gvF#qx$jHikRzVHJlec z8|+~%xL+31%mCh?1H5MC=~$q6e)tOn9ieN5Iq>63R=8HnHw-rlY$KWsA>min!5mP1 zGtT-T4=JMIy_Y1`#G5AuC4IGK7m=@M{Lx;v(WVzH=Z4EMcE$3)^75nMjI1r8=`}*f zKJ;#@Bw97sM<2@#vy@ARnZg@xD0Z|zmo`C)!=~hsC$)u91jOPgH!vAAJ;(Cj$r(p* zj`QWnEMN^P?CQgw#wM2U_}7_+QMY$KeiAcD%4Q2FVfFiTZac3@7930!6LY4O87YQc z&h-ps#AJkh`JT`bPG?|EuV>gGC$IeCW)TY%ozPDOa*5Y_KmZx{FnSXy=nvNJ)7$-!@|9yr?$uR%z6I@V7M^2hcTBb^`sqSDaq6nx3Vt zr}d%5qLbnO%qO*i zkpTk2^}~A1$r81SroW@_xIDWH0w0)l1rB3I#wG7@2avJ+wtr7qWt{ME(bV^ih4s8; zJ;k}_2lG36GK10`%W~4s z{`%v^3;+iW7Y25MN-U@#{nMSkH#YZt!WYs{AkGjLOZGBZFAq`&!{n9R6zOqn6_ARM zJH{XD@K}09AH#9n*;&`B*MA(vi0bp6#mWP7nl05zMnGhCIC6tEm!eKPb@(>VJwOYYQX7m2WCw+ zCmlO!80mSf(OsY3eOeL>w^+++SVtVGV_Cb~(|Bm-QK1NNLH799`Bzv!RxsYk+$Lv#b{N3j?+ zlmKpFcq@wk6rv4spn&&h+dgM4G})OP=%GCKw6J6<*3J@OKA$0>yYJP|8|cJpk|WRr ztR$%V=(Ac&%^m3lHBSo2Vd8QvG8T)XLLLZxhUn~RLyM1kSpBLogoFA2 zaFC=;A81e(*8lGF{Kx5=w8@U*m#1IQrl3k1+U{{=Sk<^rcA4BHxq|vZEZM<{#%3gw z0D~ys+~?Od53g6GXm(}OszZQ6G-tv3{@Ph!;MxYGrmqkEid#KQlUOAo>W31p>el{# zJ%P!L@z?Reiob(;>;60{NaL%h_Z|4VLmro?Eo1KEV|uwvA0RqTbDyLTT9vgeWqF>( ziH*^-UhDVZXA^Z{*P|%qx&3<-^SY58rz%t+tLZ=$ulLjtkuHu4EIxI z^Y+$m71!fLwY4mPACki3=}il2zbC-G;rsX`tI9v*?p(cpX>3zWEFka9qeZcyPF7-S z`^s}3@b&sY4xsl47A2|sMWfR+iF-lrmYh~%nqluqyyneXz)<{9#2H*dzu)Ks@X&rT z66va_Z`6gs8qDeWPUV!KMOyDj5k8+}bZP?l{^{I!CGn}h;i&8TvLPPl z%#i#?&l16?<05}F$XGnjDuG35!oIyGpdgMkF)euH0p<@?cb-O9E!R)YI{?vy6fyF~ zOJVhomt1xN2yu@O0$r>YJ|ZNB7`;_aWu;OP1v7 zf7J9v+_qPp8xfQMK@n%Cw_8oWtMDSbyoXI+Wf!h&o~DguI{`U{Yo_T}bj^ldD(Nnt zNnd!t*7CG%!}du^-;M=6#o*XNq@LZi3Bhg~;H{P+tfO6cD=_?zf*}f{HFd;C>Xtz+ zdKc=O{ol}L_{;kJ1dlF)RwYwiMfIh5A#eSc(k-vqfy1&5%OfXGSe*~EuNdnVN=cc}a8qT43 zer+5h56=5zyBb6x|6U-rW>x!`@`X<-IyuY6@{MuAgP#>+M5tFn9K0u<)lNWf%kOI$ zQQG>>VJxm~90G!RAvg60TtQh8)8 z&_w>zpTpjw<$pT2gUt;bUS;e2GCM@4rWp#LSFyZEd_H;3nwtto8SPZzP4ibnz^u#b zV|#Lt`dk-%Chz54Fum?U5rK@7{1Jn&zU6PV;)*$V_}@@kGwXxe%JGLLv%uB%j}*I_ zaUhJ$W%eQH@IE&3kVlaQX1W;IzO#VYCcom@Ua(c!sD&q2Z!hVHr2+0Y_MXX#9On64 zP3a7*@%^t2k@8P%oap|1xi%_WKv7TKUH+PB5F-j3awyQ;IL68fWEUYzcu`MjD__`J zE@*Q#rW$d0g`H<$Qo7^)h{ugjCA}o$k%DRVH`>pB!e+CyXHM-oi^5lT3HzXPw0raq zd=ivyGYb(p&~HjFeX2ihYxd0R_C!9=81@`n%d24Fc9P{ARSz#%On7GEfW851Yx~tD z$j}G;6PQ&587__G6?oW!9YuNld%b;5gRV{JS%i)Z;FOY4V`aiDknXycf65_6E}K>6 zeYnyR+nX6ve#hY3>)n%$hF8f;As0qPKm1WYs1<{kTgF4y_DNvHW_$-`al*ta(d?!H z$e!y!JnJ+f6Qm+qQJpMB040Iz?(<5`92ILxI1^N6;`A(?q{8zY_olVg?roGJzhW(C zk-mS1?Q{@Vu`FLttdKsC3VF?1Ud(I)gA6ZdllYqDYA-p%qc{SF8l#uQ4OZ$0W>Ya% zeSS1Qj&q#N??mcppcmsWSfbd`Cn7^XArr`KYZ3`uQhjhZH2mQqK)o%a2hm1h_Sb!U znJP#{W`?qto*Sc~G$qc1Ia3+Czw{n7Tx^WwLo!+OdFIMSoC~wn48+-*PfY_mEc_*0 zVn?dbqLusZ22JQS);zn6RE2$j@qDK%Q0$JG3-qRiwDGzGD$@g#$Eb;wtF_TwCu!F& zu~YJx;|ZDI{I?|^KsFJYNjL(%30sH%k}6ne=d?lR5%8?$HO6eIz^~l#!c~ z)BRG7Hh^oToceK5IP4@y!-ymCOk9R7+!wqnn}M(V7#tUpGpl@L_-$j#(KkasRI3=w z7_Hzv5CMcA1Mb)DIaL`9A9&^l0qIGfXxHqf^IK6MR(!j|mu_ka{Zx*S{c*opZV6vO z!`eq60TAWQzk!K4CF|9rz9*aCXvPf&+$IF#V_fCV!75Ck*dA&!?*aP`O0lz%EWdc{ zc9*`kb8-6J#N){ed^Vm{C#k9jhgW~W?8@0X2UAAg2WOUV&S0|@p~+H-=7>^X;7K{E zgk`XBlt4~Q6ha}WtrcqlahG*bn494ziX;6QniHbN1ZcDq{8X@UJl*gA*y_8Mk#;8k z@WAz-;gdn2eDx!O{=S!lmz%hwX5++{X6&?(J9+c7MOg+R`=aQ zl|4JzcfkXdIZ(u!VEKHARo<5H`jN{~T^nx1k?d%u;4)WDoeIXG^wm^uiw)sxN6^c1 z9%^0}0_07Ll217A#SD22p;!IJZPc35)5&e_XP3f(lGllrY1=GpnlaBMnR#p+(x@Gp z`eRCOn#<1G-yw47IPqd|xxYYLVwVxGTq0>_NtjBT#VopWnw?c@mx5BRA`qyuplURwc+fAO|37&0yq?&6`zoBypB_0N?_+PMVVPn5gmzz zswx>3MgCDdb?WVJL54nSpQFV(%u26o*fIF3)T`>Z*4P{z^KAT=tCb_F{8W0wj_w^! z-86Ct{53h zg2K~bM(q++H|DHNK zvb#C6Zk+JqPG_fNz0b{>}JofIem%*+m1K;5?APo?zUtB$!p?92r@>8a>@A zP|czWea~QsYEY}NnbPV}q3Rw93VGYo!~;OBcerDke*#+=o<}KVnM_y?o?#550N`i? z5uRaG+FRVZN@Wm#k$M z^{eQ}EGti1G$a7Oarb?J%X{1}RA!gk=M1M7@oW-FqCI@n)QopO7oZ3^9vv1} zsZSN2*^q4?SsPkpYDB$l8}x6oz1cPSGImE9$FK$RIV<7W0Fz5HFwyCz9P=Biq*_?N zoyd2dc*LWr>IC)o7$J1RD@QB48Kpt|qOE8dnJZDF|&faL!0XlGkM=+l=eSf@gnf3dlPC zK(j&Egqir`pmX93LQE^!tDqJlQTh(9wCQ{Yy`8Ve_usUrAjIC-Q#M(uHv?7}+}B+8 zVc^n!(3%1g63U%8OCd{F?7fKQ{uKim7B$bEzb?x^SwG(y4E)YSA>cY44qdNILRI3^ z5E`<9tDZP}LgWXen3HeJ(&NK2TC_;WEuRG&&47JMj4{X(mgUag+)mjn81f}qgrYo^~Tb|~#!15%2$`6KG_sFy#HigMr) zTW++JPX^qw2eyQu9L?!vb9GeeBmqOEO)chQi8_!nF*aN5#7la^J$ctdGB+*gKS&Dr zDY`B{?PmR7ip0vo@xNmzn|6m{D84W1X9*y5p}za{z!(yZS?kzSV7Srl5Ak3S^z#@j zXcc!xagA$$-A*>qiAECHMn-{z=^G<=SJfQlWdt+CCWGe-sR6e13e9TshKMg#g{I?^ zi#A&QbCLVWdHNnF;o|+MRa~|YZ{H`-&kl81q&BSakCT&&s9V5+OX|C2anbtp*ObO} zm?XE)Uo*Ck@eg-x6~xC7razY}Tss=KzP|kGdK{|_FRZH(Y5OJP9jmLVYBw8&$F=+i zRhc`0?kt8qj=EIqn>S}9h}*tg+LCU`oI7;s;0!2$+)&uDEQsL)VrDk$z zZ_ECb8E8{?1|gp|s@*_{F2zO?Y-WNEZ$X|;j7Gyo)Kudz-W|D7GWY{5XAJwIS2 zkAVc;;|D;dPvSjJc)pP@TTJaqsU^5q7J;21Xya?FgyE*2Mh=TqCfD=Pt5wMIn7Y|N zR3>wy-Oj+isl-d|N=#H@IS7I&>T_5TdVOs8Y3d=v6q-7>!xQfKz~VT-b|4%4^7>qveW(g5n!HZRw~ z#+5$uANBPIiWo5q0ya?f7o6uUuqAxdvNISF9BLgWMCN;oSV63l94v)+6QEX5L;^_K zZ6P5;beH+-6|3cRVomGjMm8p!oe8y0$05ZE!H7Qy{#jWMzYYS)=Eb+?_Yl<3L9U~r;c%xvh4zOPj zX_ft&Gr_&*QDkUtd!o3FjCpL9{R2oHm6vhJ=jX{M#{uVNSd)JY)due54j5F}8$mgc z-5WdD84(~H(aOIaEfhkv&jWzfS8WA++3d41OI#4mko2oA#VE7}Eo-ka59D?O@r-C% z!GkjQYNE@^v8rAg!!DfFhOg^J<(80!D#pyf^HfjuDSi3Z+(1e7op2N0{*5Uta0@toCT@LcHj&I$E z3Em=dk%QP^=Tw$%7eoA%0SZEJ#^@J7hh3MN!4XSB8fKp}QZW3{`}gN(Cc@0SE2O8) zm(cTg>?M~e`n3DdNxnt7&=9aVjJOzJSx=J)y+QbUf2kx(z=F92#?B~C>N&Wne6HZ# zF5j(TbaGfPUDBzO0+{8V-D9^6pMN{X6jl?DOCb_Lgqo^nAg3CCe_BL@xKFvyS-RrQ zJwN-}-(r8Kn-1xtzofcZ=voa**@KFJ+8-K0Hl+|N5~a)(tg^aXN)RvrXE1jZee9i) zp4{fUJ`VIU(%NVJxa)TfIbnfA_|J+)| z09Qi;^LgaJYzwJkObSSHY*4jWM3PBK2McS)GzgY3;UtkA=2o=1HmcVsNaoz*upd!L z3sLY!0;PfjPT<*ECGk!HHFe7afTZ(84`QgLbtXS;8gs)FiAL#&@Zx6mrwSO_t)WF0 z*C)0>meiUTK!f;&(D?=f2`FH#s1MIK{AN+ffP1tY@$vG94FZsX)^}S#7CiL0%5gH! zC3TbYX3!IvHds!L0n3S4zdGh(anMs-w1@UqaZGWK(hg{nc8lW_z9Z`yX?GTvY* zGJn*=E2B3)vhH4*jEzBpv`*Z0$2-lM0QM5)y&`XQz(z)t|n7t690 zmq*@5LZpwNI^fd;+EH1DfJd_z#r5!BHA$&*PVAR@-%BNwNH-zg3KlzhmuQxGvF4~K zL?IUM0-b#Dq~Ar(Bjt%QHT)w2Qh}VMx$V<^fwZ!g@vk17&I|52aH7PhpprI2 zL*cheOgJir&kj_XPQ!q`mYJl{8YIU(Tx*D)E25X6gen~fF&@<-8MVwLuyKGFi%M6b zXxu(k-~j4FG_vvN%#Q5M2Q!tUVn`g&dC(yPYAU;Yx$X+w*~E(9A=eBAgszb0Rq;3* zS3V?82IuDe-Rb8Wa7GOybY8H{^N(yb(# zj3^7sH-62keJ7v-8yn*dcY4O$%=!m$KwR=%tpa5C<=E>zB7Aq+{bf%fQXdQ>fh?%o z$)%a(dB$@Ug~Hr4)v;Ph{sA+5y!;z!N^>FQs>~s&<|107&-=W?PG)@vFKAcwNOQ;= z9m{xH!O*@2xk77xm$3V1NuPfHLJi377=dyr*RW}$al(6`0kUPwNz&oEcy9&xXK>C6 zTL7e5Al@G$8{^``@s}CXA;Z0_rOtxUS@!P%0u&C7hE#t{^ zIuKWV6BG7KnKAKS(_!x74qvXzvbeGC-YgX(#%6Cw2h(>ALC4ba$NHPMaU0W{p z4H6?$o!F^}N!L~(J91>1usr z@sW@+X94+8|6B)wQFYrs^d)OBST3F+18#0Uem%;ifCts$?LYticjILHL}KnSI{f(o zs=Ds4M(_|_WCphohqlf_?lJq=gtGhq z+kLN5fPP+(0Eo;cz+KO-AbB>H86Y84Rcb()t2;FljT2vY1#Aama(l8sK1-xwM#l zu~ee)LOQxWcqCo{?L<$J=kZJb^A{g9xkpaK7`mAJn|RnUsKLG2PaJ4Pl@{A-Ev5KC z^sJ1++BnPMoJM`o`TOsjBJzBHivS~kFM0P^AJ|s_Z*clH*TO)|5O9px`=9jvZw|{8 z25AosK3KkAHxj=?0?a9i9|4O`NEU9Wjw_C~lBA`hdAy~5W5-xwok_!9LTvYprBk|K zM|_WAGG15qFiW{mxjrm5m=x`#_4$9F>Tyu#HP@4p8;$GAbP}A7a*2r{as|wDm%7H= zWOulyki=%zS=!RaTj*D0dzb%>rA?>!KIm#4Op2|g*~#|I7Dp-6%Jud@HncJRgS=KTfb+? zhtr0^w3QdkrG2?CNWipY>Y3k6`+lCW%!DkP@W8p6_A!!e+l2KaVt5D~O%{y8NtHx% zX8QMkuIpktZi3aMBU?j(lc&48yRC5x$EI)$h8d$*u_%v(2vLfrIg$H9iy1#YSmOfY zn{4!oN!bSY-Cmo65&q7my#3QoH6S~-pqH$DVkp;0X4`Ld?sN zuDw3th9%no}os`&gAtPu;ZV`R>D+ zH)O#O=Qe<%6G%yTS;cinyO`t61Ef}Ij8Q7C;9Uf+L{hlmg(#PW;&`;M8~^3LN=Nz| z7+M#>&cDPpi-TaRHdwdJy3lj0iGFP09p~>snSvRM;3f)*=ZRe0QFAnIOwa) z&@#Smby>r;-?O!+Z=KyViYj*BsU(r=)v!W!?rBTkZz^lEGuU;e!%&&|!^}ruO-ROj z7CpaO*S%lz%OWe|mkLcBUiJdbRASFIf3}AP>RWO`L3A_X2&uH`xFa4eZ>UU&5)K4_ zDf+7wOMXCXkevjYuNWH-Su)ib!;WNQdjW=U?5F;`l`Y<8S``och(B1DP6!f!eHNi#X9UJ2L@qx*WJ2R#_bXfUFL>7pv`l*2uND?nhX`vH9?l@rJ6WkE08hgp?_4>T4#D0Inm>ksLnp4Wb zh7N2d*f)VBq!X^U351H245<8Bgc;Z>8{<(w7|W<;zfan(`&etVCDNMyCuWOtib5tP zXVv3awoSOU!wgs2u9*rr&Ej=4M?q+FQ1`3rnc>a+w_-kODoGwDl}D%mvxm$%P4x#m<-h- zA6ZeE7T=s}#5!o87DhbTmk2I^h#r=_N;tiAZEnx3HK^pzvd;&X{W}Z?Ut=t_*uvSI zXWuylPwlVGEXSc^%N+eS1Ke!E%p7sBS~)X%LhHAsH!h;YW&2i<;ym~1zjhO1Rol1? zO_~+~$K=zWR4)yDFgUTY=+AU??pZi1J{ zs9|R%-fMhOdEt3dMG9mMh<_5z&tmEGEbk1K(iFulu>(#22KX3T0v9H8D<+=D$|sbW zrLr+emIt%V@H~UVj?weVq^IEV>d#U&}8T zBOAvfUR|{{7qkG=B+H&mknwdb1fny)MZ!!>=pEMn1~EsOJpb6f3l;&4-eX#KdEVLo z_7vaJ`|`Z2{k1*kS#VE}!P_2_8HH>Egh#Ia$96m;(O{Ou+w=}_vU_@Y4(sWCQ*T0F zwh2J3L35 zU0_tKP?nLKy`FmQhksGk)s|J^!1tt?(@OqISEon8ttx-_U?oItrz5-~gDw3$Q@PZr z6+X?J(h*hADqv+76W$dp&Uf=7!NpJ%KKOCtJYgSZhGqkZ2oN;aCGScsFhUF+1pFSA z6|Js8=CP)v))j&Gyu)morN4Y^ngi&r?(*T&34HYC^5@V%klzYn zHXwXHQ+AOB1kH@H$hx$L2NRTAc^}ggASd7J(Q|35 zo9Jph!Jz^4$uF2viW0&|K!3yUzxqP{emGQtuhXAz{h)EcJ(qKY~96}C` zC1ou-maEl(hMRd{(;mWZHq3Byt8mo1jC0nR=fU4ryJcD9m@PQsQW%Gqx!!P6MRwig zYe)$|i8t$Sx~2k7BQ6y>%6^LlX{(t$6#s&#W6G}q22Y_Q>k!>7B#@Q--KZ~ap$JKv zJiuiEgFXOiC{iOxnZ0fvx1OKnKg}I7AQ>I8HcVk;%m5-5F}A7(cV|K^Ro|SPU&_Q> zoHCw5-1sWqpKK(g+;RO*QD?0!oC_jy>9_~r4!asOO8^h|ob|ZwJo6%f*~xW&vf^<9 zCjB;MhpkV^cy9Q1QpVjo7gb*nwisW=@t1=p#Vgc*~?+ zX$w$VMle3l{BYWOuHmrtus}*p3CGdM$RtjdrjiOBlI!>OmitBZvOOqPBk$$MIu8u+ zUif{YFE33+ItbOxi|8}zhl^tHP>E>G18w%(je~p`j24>r3nS37rE>z(NXP=~(Wp8y zfYzdZ@{WSH)fyPtr}$|HZ=Elyct(~)4UvPyDOHkBshTJ20hgYSUyS3v)4*t#hoyY-0o zbOKbX#BMT0^FhssomdRlYssj@pfaWH{o+JrGg;=P8d)#QTj7uKxKIKRVMzo>LHXnJ zIc?Bm`otQjOCR;W4g&DUZRTcW>pH6o&N@n}PR&?JF`kVeOYP&DiG(dJ>uEnp=wrdiVoj6I=EQ%9BHW7+s9Hc)` zxo*A3N6}r~1v$%XK(`l+c2~W9&4CE}j7Nq9yosV_>AM{tThy>{@6RCG#+poZkfbbK z3g_JWjRMtNoDhB}XngR}1YmWubx(z21&0^HNV}T&+FLz?N-T+qFY*aMLI}+W@85x_ z^UI+Bo(x<9)xz`GK3Rt~SXXDmHn%^NHsVdT=JRi^w3ZA5n-=f^HGL>TCgP@M**;6i^*D(3DQS}1!oJGO&~Gg^<_881?in&%D3=E<6><1je-e4mT| zo7wMn{HgB3hsT2ow+GbDkG#$b-}H|`{+ac41^t)np+VrR`6l5sj#A6uI4l(4{;@3L zHPA&6)z{TbK6Pe>!oBr;U_2?Z74{sZD8p}(+*6mz+~b#M`>@JCqm|O&FpJZ9Bf>Lt zmijhMamwaA^pXY;V_+mCJZgtyOhmvh+iY^fEG}i7#jpjbjoO`|uj?Mu;ab!7;GPEm z7hCTXq)E7K?UrrZwr$(CZTln?HwxfY28-rKx6X0a+(6nb(j|!Di`SZavoXoI8 z8NX~#@OxcYg7F?Jk6FXg}4gsKk#VGj}1j>d#RBFOtSBMw; z3Ec*WBj@i+|JDQlhP(wr8N=R*#i2X=&0sOdU}Cu{$0G|X=Y7srBu4mLwC=7)#vnqa z$`fnksw;eng&WV~CZ%V7-gfo0n)16aYPpwIZHtQ&Aehvzp{>7z_}Bl$jH6&?u)&g} z#5aV3(gQ#cZ@%kc0B6XLH^UQ22jfn`ALIMcmjb7Ue8OMsGlQ)q9XSHF?okxMlz)Sm zdk&q5c^}TOqiE{LBRGwWbj};#rS7k_WY60x(l~F)QZC$6ToO@}c|(*9yQ1)haHhou z94?H-ZRj`-P#o3Wx6ZUfnCZ*ga9eiWdW`p2&Ic%dVqgDjVoJE-EcSI|osTpAIZqPm ze)Zy1-)b5|(>NIXDMU*cn083Imc%Yt3u|d}rW$wl5p>eRtDzG@%4K!$Z4Xl0C_0nD z$+=SRKI?Yjs9g8eGfrdEna>JOPp~;x2LrBncz-vkG&dT}YRWcJ*?Qi6TxGJ`VF93E)4I*dLFhCvt}unFHYRS4g~dMaPNB0rXM&Yt17OExc_HTsakqw12W=Vab6;5 zb7zMxQp!k0Vsc;Hz!UvOGiu*l_f0sw{|}Q$6kP7}i10hK|HW6CnG_2*l~}{;4{)ESjTCE{7HDEiG>Eyt1gk>dfmDNxfK=sMfvrOG zax^ldv8lDhm5AS#;?)lq#FdPpLXbT?#(!`%+@t*qifHLd(kll1kCG#e`iTuj#2Z*e zt(ZbYov`93REA-Wkl5sqz#bd^=`kKmFd}brNU0)BP=&Bb#Gy{+Nf0p|#B*lITm7-j9ca_S{BiO8H(+4WW6B+7g%rQCUa`MzR%9I@mIrHY?P0iBLk|3RUB9U`0quA0@;EFW9&^EcB7EU_lGR1oBIX zSyU9t)YT~|s~^@ps$%7Uk~8b?%V)+(+uKMn_eps2Hh+NkyT$YIq-CST#=#7dhOC6m zc*CsA@qV+GXk+qebf>spn%oHkLv8<-{%xH>4}bqUnZQtf-rZ|XwSCdi*(>3TV9$E~ z9cLv0g~rwLjz_+N!qUe|QR_)0FS*)e>zFDTVr{_pfmNzXy&w}{OQ23je{z=Z@~i_W zz1Hyi>ncAdrZ}DOM}#;n2f*LGso%4Hy^Kxg@00zTk(2P{V(cyh(7zK3+wZ91WZ%7K z_j)&A2T5Q3{50FW^I07;+YQ=}R~E4O^)mk6-i>%cr$*H=$I}~>{X|2M{O`4<|A8y^BspSv}6U>2EXJH;s0 z32~2TfTEB42w$jrVWtz&z0zWLX{4Kqu?BiS>!_eR{AUq@ccWENr?lxu)7-Tf0i4a& zkEJo$52ZQx`VeU*$2y*;apGWa8Fz;cCMVNNgI%*5t77}Cq?-C0^+i?J43r+eLt z9kHa6{~}N`(r*&StBds3u0%Q$7=KmOE0vSSaE2dQJ%zG}2DKg~HG&?GHr>E{`IK_F zK++z5vU+M{62Cm+bqmwnBt@pd4DD9-NpH zwQ~S{nsTlOJi%_pLz{8(Rseyg>l093XS{KYW!`70F@&zKp4NB)0z~%qF(EXlcfY2; z!%yYM?>)PAUOV^jw1_VLsxQb?|81B5{lVq>KXKtqtZYR8yIlawC-eFR$bOlsq@oqiqZLa4+S+vO6rK|T#m31)V5Ln@Jn|v`o-s42(ULptqzS=SWA)e6QP+GY%mcf=SP=_IumZtkmix~!w+&HhNiwQ>47dm2UAEHcnvnrWvqt?$=z znmfwh&tlxo3veCkK^Ee`pJ+qNl9z*_kxD7aA~~2ERoUv0*aTJQ>kE^x06d&6n2}g4 zG`z8nV|Re0gf@?_)nUrcv82NOBFoQl<(xZcS!SHbjSKRI1G=%OshaNo%AR^M4Xm)Ct6 z9kDm-aj2>iE~g&!pQv8y=mu$DaBig8W67#}?6bRc&|2W(M${87;W6G09HCnHz*2{+ zn2SL7_GcbW*KO#G$R-y-fc-V!pa)})YR=L?21VpAw!!nK0`O)SQ!0H_$}32q z>6H0<*|tQt#8N2k&N1;+ig8Ri+xm;@`3}Uv$d{}84H|?~_ycagu!NTjsX%Cx_y&LF zU!*!Lb{;u{)=L_@P7uf%OYS$+7;6dsQZ-N;Eu|=qRgDy7oLb=+%(K#j=;tpG>zJ02 zzw82g+{J~52sesQHvj=I{=$J*h!cOv)IT7bBs=grwxy7-7(>_yr<})0$K!!M^oY3+ z@xQ?>$CV(DF4$Fgka@b;nNkeCbf9luxx4&wHPYk&$6oQs?LB3xJ`kHkky17LjY&!@+8O4gFy6Jh#hVz zm&Rcn+xTdT&`{b1O5vZS9ekqU*eoNaCCatzn~(~3`hj!gvY8EttIELZZa^F$nJ-3H zNw>iPu?-e2-^@iMefyoGmXuZQgpia+j9`iZZx=4Jt{`i;U>RaGi9FByRC`Akz2hl( zb4qYJA)GSi2!P_AttCJ{+ZaN@*@O{#odtBe$K_%U;?sE+mIXy(R)W)5f&pZtby7O1 z1Kj3#Bq(6-3;S=t59R0Qx8UEuIscA=mv@Iuf}gVA->;822pBtSMl2x>M8-z2x;?=u zY5OrbRowHm8RdgnyB#HU10Yc0k1%_UR9#mU^Og}O0DtEL%I6go7bw0tYS`^mcX@D3 zIF5j3+rtEC60`VJQh_jLE;gODKN-v91MN?_<6z6v!AtDjQ?iGgWf|2NZ>I4+y%S4Z zg7wiOpRr+6PL8N_=;Ry6wl#MRD0Fp6Z3a37hn~F7vDV|l2Og^ z{z+2bfNBfhuseB8p1da*mu9s4KPJFTLDyK8Y3xut&)&T>Uw!63QG3-6A5l~Ttam+1 z-{iSNawONQa_C?%Z*+2Q`n>(?TrPJ`r^e94SFRQIEJ5oYv*jxj#g}l_aJOv0PuVLe zf#o)JFb&WlGuiOY9}{l=bRfS_Tpe%K`>vuJ0OP=y2W2wkmFa+xDbpeMt%3>6laJei zLlVYPOCUFlGy4lBeoC-4BqfQE-GBzPKoK4WnyVK~(qg4fMs!X4<)BvY>pQ96-99G^ z{Ea_zK_4K;^j#y8eB_0ZELw8dh{?-gNw>7O7zSt|Rh;DEphocYskb}JLKKeK>bI{? z0LXlS3e`d7@#=ixkuyk7Hi{lWm-95WYwS0Y!azrG%YOHIP}@HQsp{^%{KcZYTM;wq zeiz*A^`97{F7wlX&%UBP{eMbg_hF`~YolG{9g8_p4VWhikcfi9E=YHJv=eXU=m~gM zv@@dLs1$B|eSt#+5!){GI#7Ve8wlM6AbRA^LER)PKC~?=D4KwiGA!Z|;Paqyru!8* zBf5qEEMU^D3p>siIz63y=*oXj4+_;q%m1Sfy||$Nj>P=^1qW+cPt}nG&3Sr?cHDJ97&#r0O@mXGXXdkOkK7EvhGZ|LxR_5W=L(qjYYpJ9V!eSbUWn0`_P+rJlEY%OVD>_$FmV>Gl z5K(4u=iBa*)S#FmI6o1eXt+HD#HtQg2pq;I+0IqCqRKgAlX?7F)>Uv+ZveVW@waE| za~6YgOLx8UJL7l9_;YHb_lv)*Ao}%v!wGvvEg70$latQ}{T|@nDVaq2naS{aJb3bB ze7?{Jvm1f%Jy1;LX4GFD-wV->HdVH0+lB)`^2d+->6H6|VnikM+m3hv2$l~LwtCek zQl)qHo9y+NE`qz6X4$K|g`~781$CiSMH}S%0ftcWxFzbmr2)G%;90@yH13r^OlU03 z7nz=_W`GwEn(%MB94Ov0^I+%&@iLZxEKPWI=*mdL)oQb<39~CI^m}uKahZ$mFd$Pq zm_xGF8hD{sLtyvRZ(|r#a|bh5Hy3kb`~OT4jwUuRTx=PIO2B9!%uE^gIRCmuR&h4s zx5pm*`%QLdpgpW!24#idCgN|_8&Gh&8wtsT3%IuNb?{tfM62Bs0gC#w#&;*mxsbS^ z4R@E9mtDR^tIsq#{{ChAxV0-ZtJNzd#srn>or0eybZR`Rd>s`_iMk~C`2o$E)&1_4 zf&=sL_CVVfz;{p6&5HdpPwE7K(Sm@eIG}(^Li( zl{Bx`GWLFni-aov7b#N7v=mQABUhtC?oFVBThH(scrshZfQ%n#R||l-Y$LE0YP7mE ztOh<9QeGzK3M#dOD4Fp$BIFYbz$Hd6H@5J{G9Ay{zK(A6NvKfo-RCgD2A3+t))aA@ z#&@vZk-?pePh;$$GRE7tFKx%_wN@rF>#N(sunugGIC8R7gJF5Do>*_fx~hX;%VC=h zp4OKy(GfRB7|-%%ngk>>O|$3QaxwjJX(VuDg?$@qE-2mVI~Y^aQ`hUh^r=hT`DN@T z8dn_?{s~;aJ${8vgm<+Mf)J!v6qIFk3O{$fsVL!~5MoFCmBk->d?aUNieAR^gy{2Z z!KHRWo!dZ`0gj|GE>KZvUnN|j3cF-3j8%)#^s9t}jpB@b%LY*M@eft3@Mk~!E}sba zHVy?ysK^Ip&%zjww%r8ls_X#YV{{>+7#}3rJGaW}%1sl<}cb&N~w^DEkEr(TQ`7^O(dL&kTI3(>>j3*50f7 zMysxgAfH__ggBskR0hSbp98t!v?Eiy9=w$HMJ}4Y97Q z4NX~ETutTp;56N~M54NE!;aQ$1H&EoMUg(T6UN6OOPY;sJv|+G8 zj0Dj^`M5i3`~|$tY_GjUhDNx06nVef1`8P2MDeF!D*RuNRDkLL*%G$I1T2U~avcIP z!z`Zl)U--}ttPT#^{7c0rvj*vj9I~w`BZ^q-v7Q}*kl35u$HwD#9o6t(VQ8GS84mzwFPQ8qU3hNk+)RTA!JM*65hIQdfT3y@eIK7Un=TNzR49|ODq(JHp`~MB zjlQFOSgnj-CP{6V{rbu2qS2GW;hy&23bGFg&l!no6#7CZLR4BbRQ!yF$JLe= zO0wrSX$RGXjIR{!wc2txF0^M)_4j8>V49IZ8x#7}S?M!Uq~WIQUs5OXupiQ8Y76j%+tg-Cc>y1%K#4ayI`+5QweyKf#Nr zlm5OIYJlI|Zs)Ib^bL2lbZ+aBJ`mx(Z>cYn@Yq#vis+zgE-iw`Z4_;adjf#jyMdZz z(DWe8|W!tV#7 z?3f+Il7kdo_zsw0DARvQ-F{OWv?IuyoneXSCFpaq>(=$_e*lQw zzFeKdHfv+RHzCVF0QQ9Ydw=6mkH9J7lg^Gu7KO!X5~UP*_;7KJS;F`U@_y!DG9A4u z12M&(SME)}2~BJ6ToJ;8V87v%tum|qe$PuKOpHz;)6`I^HN_9nEepgZoStBlI#G${ zPzrDz1VW)*EI8X^w+b~^!W=@(MFNcGf2p&tD4md*>*Ej0LlHW>+9Dh8|Aj~u)qj+F zE~KH9foi;}wYWj&?u+uf+d-a4!7>+tA~-#V^eoeo?pW&lya~P|-lnYQXGM%CPa@0| zAPl#jk47_5iIu#snPwYvlq{x*8b%{A5P2fh?~iEs#26+?9Yj0-J{~PM@&>pNj>Xt) zyKlvyHWN?xXju%Q`S4qbm5D8>Q!Ew?MpR;Vrp!?Np4D547a|v|8&M|9xw*JDSU5jhmEa0r}rGf2y4!qZ|qSUC!6a57=tQeHK!7v>&64b-Vy-DzObTm1&Rmj z1dx$v+LDc?N8X(!Q%#3(q@1c&^`{_JG@nHnw z7Dl8d-MErf)5^gQ_)?f|h4roNLWIM^9# zFL#vROYbuxXqsTqB#Y_2F>#blv_h;%c-qBCNI$(c6QYNiSOx?>c^eT&mP|(Sgu&dO z(9=})motJ!44dg3;esc_CC%YS$=a-Jd&$OGl+m#n(BYDDdaRsxG?tEmb0Vx%;GLtE zxE2eOq@c;b3b;>YJkS62yRa$p?bHZ*OI(GN27hT?CcReHfh#iK`MaR@MAfg!H*i>h zgL2gTHvj7FdkWwq*6!XPJ}2Id|FwUkE%NdjM(m=BK(M!WVxOa%-`-EYOMPCo$9QR| z)LZBy{2X)%`e^m{vxBEhAO4}4Jb(jinC@)@Nf7tc^7H5PERtygDkPO@;#`*4OktC= zTymU`w1B$NMnbEsMVNw>&~&A{9(|ya*{Q10I2Y<7zZt;!_FBbdC&0)h1`_r2C>n(K z7AP2*F7VRItHYhq3UNZv>;h?BOFo$s3bP zeCjaMqdERYc$zee6e4W>Rk-Vt+rHh21;#5e#SPByMUia3zcI(p>tjh1H%z6VzrOw2 z;7^<2lmrM=&CM$JR7U{ny6Rh(ofxc(i#qRhIh5L}Fzsl#A-i_s@Z(Ke8s1M@-e{iz zi893ccDeSMYgEDr6U_!cF0-khd}5!QE-lS{@Z^!1ji`u5*%S5jjN`FLR13DSnIr1l zA@5)2wO>mg1+GV>=at<5t)DcFzrjrgSdaxr6a>hm95s`aSt=;cLhuF3`7(MHcs_!m z&F3Y4Es6h#crh9CL%0##SY`|}7_=1t!)g3Zb+$bQ9|h>TM0i+!74`GS>Noq1GY_h- zvEebmC2+B3y8mJ0w*i&U7EY;KC_!bj79{`H_46+;5w$e}U+YMY|2F~}{E0;3hR5ki607n(- zt^HGUn%0i$U3%s_Ro)QNjgIaLrG#jNR%^gf*axgxY$hR;BPobt;Zc3Qw>-&@Yv82| zsRQ_t)RQ6o3z}5v775p(l!EN~&CWedZUCutf%Y@qjxP=VZDSQ@qimZmGpC;l<~G8y ztPACeJQMvs5EYdnUurXZljA+&x3*ZF%Ph}qMn=pZwjv}hxgx9|**}l&Et?{o5jK;T z;S@KT9Kw0qH4JkBTrnb0hQLS|hEM$o$!2N5$|x~nz=1~JEWG9WndwF(&7(DZE0X_I z`uMS9?sXYdKYcd+S>g_aK5JRQlQT#8&ef4IgsImGftagk;;cV|xS`EIm?h8H>i>rz z$&q5Lj`|-|ValQmHQ+=?H({#_`9G@Tv9yAzI2yGwPcP5~xiC!2xp3QrD2g4rwn4LG zx}Nk)9m=16PlgitjX8>(kRtIIaEX;fl8E60PE1F(?mY2kO-&FhHdI#p`IZ%9f++2O zUd?fJszW)a^0g9%*C=y7KreFhUc08?Q2v$9fE%o1-=2XXfFDr#q)l*YojX^j$y#~H zH2FAOv^Krbyog{t~F_Ry*jQvMW{h+{sXri6+g`Ldn_(O*(pl;0?WVa|muXB`36Q+Q^eg_6hgF*=0ml0dKn!=n;%ve1#I8Z zoV@T8ApQ(CX|8uGXN*Rzf?92?fdQh;*U+zn0+eU>=$ed;cB2LXi^Ij%dRJ1V>!IwxnLiSimK1NXFn1s^a)@sHGc_y< zZO_%(RDx24Ya!@7-RkVYdj@`S7kARsLriuYQ(8>tBH$eX+&n%wWR|=zv`daE$N1+e({*5MKh&Tx{ftu1A^UY zJnG7-se*B{VyLS3*&rV`CYW<-pY6{pAwdOl-pm*8|2e21rmJ=e0i}qzoa*k-BDJxB zZ3SxF=uKUWPOE>t_3t?62P5peJ-kI1hmq0~R)VKenB)~#fC}nuXqy{(eYBMBS!GU) zbEU5=qr1foJuG)3fwLQOVg}~i0J`kznk&x} zb9<~VT$sRFFt$)4k*f$EWN9F}V)b=)6%rKj*Y4_em;)?Z`ZvM6?h88e0)WCMJ}>L8 zfPn^OWv?(-zmb6gZa+1no@Sr`YexuX>Y>myqkW_3O}|GT>PU6Giy_9RNLx%R|81l(}WwxwNPyq z;r`=k=04+;T7FIO-Iz8YNKkxlnN&H{P4ZU93j2y3B=!;E*ANY{0iLGe4Mo4j3eNRK}s|12W$sE{?_+j2lFs#$3@{3kJNW4F97MQ;|E6EmxVX;#rfm@XN)N^`&(wuhR7 z+@`~vRl^g3V}q~E%8vqxi{y{b#`A`89(kiPLLXus(OsGQg?cg*PF^)=wWJ7lLikiT;_AxUEGmVU3<@#jRxB;yTnt?Q(_p&i|fvzw_g;3aV(566Sf!Wp7nfBkJA zJV36ZpZGZ4=m5NQh^0LqTnppx6X_Re%06Lu-eA92cUSE`9S?7jAl%=Y-q z^A*ay&x)vr+t!;)?8Rjg_jiXd#<-Qe{nS11^sI>!mk)+%LFz`az_5SS+;J)z$W7#~ zSMKklnjU6!z~V0>&$x>5KyWWXXYhn4;MZ~iAORn&0!@Rzdfo*+FJt$bVYuE%>IQDe zhOaUSuBmnWxnAUIG1CuzisgKYLJ73uM7U61YbzV9h@T~(e)?ml}6LKhMCV+`)moqYCJNHP@Mqej}fPX>nA z3|8!rz&^Z318LH_gCTv=Bl03TH%8W1S>NX_Ln#!-bu1CX%hy-B@zA{uGCQx!s@z~T z5j9CIfU)QX<$K|aTfNRIsLv}#@ah;Xn4wx}?pqKCdVW8XnoXa#k+0GSgI>-u7tG&5 zgTn`xVLIzkLOQBe6`cg8L5jA%#qS<>JF~4+!fDaDRs9RM+qG-=k6C3OGndHwp1n_E zin+F)1c2^>A+(XW9iZ-RLagpxiE0j94{~uFeC&H=? zjN_!xo2R>2I44Vn`L@c^u`Zw|S|o2@?9FTRr#z`Fk@U>EifSgfL{CJ<%yjsbS@Y*= zJgXEh7Cw6+Zuc+ zGYn&)4X*%TlQp=u|2^Vz{*TFt5|N7qhEdMk!P3o&h?$N1zc@M?fP4ZjCtUBR=J`57 z5i<29tQOdWBq#hrQH&jeH6}OLT#Hk8BdBki)Q>;mc+SApjKzA96fo{mY(CZHLsXHT zjmKab1|GTnr1Ek)zNCHe5sB5Pc$HO3jP%wu8Pn#W7|U2wN;YOGMg~_m4wD9qj)(d^ z{Nid=ZO7Oq#ah!LfQds%sJtKrEH7H2`kYOYtf%5F3(7$w*mVXTU!r_~Ok6UF2@#qn zYe705>^JRTQ>2HHc2#`v37R_^UAYQInlU%4#DOd~DBNC!(dkr!9?AgRbl3L319+W@ zzZ$Kcxt0T*=0mAQ10C0R&|^_BF9LKi2{%Q9GAUM==y@REpV%Xb$7-;axj+pwXOxGo zw=fA)7Z1L@0yUj%RY=f41gCDqebD+iT{)y4bOKi{1ImTDE^O67GoP8MJ1SF3l6cw7 z=JqHRHJt0NjQlfEWsmnQEzS<|_fPo@ zayqm1+&vS(c&R9RBC|S@2O_qr-lVnBdb?Q$wyQc*QR)bsF^p}yliL`c^EXBApae0c z@-X}hct8Q_;-Zsi8wpPjnOpR~CsA@R7D5K^8FW%}z|fG(zKx#l8p0uzj^g9xxh1rBw}0Q#80m(u?Y!1$y&$ltQ*ZE%dY1{Ah?%l2p`*dp?2);G%)dKYE(?8y7eczmd5`3Ol z^w9xt_}j2+y&vzGkS?+`eHI=$IagO}uZQa=dObv;h9l~a@H7+Hw~$T7sxMU=o$si2 z7*xY1tmu#28-DVY-q}y+Pm(eX+NaJz{CW4sU6fl_H@A=9F00*MfrfMug?sjRgarMc zOnlvV>7sX5evRjI?K#G9Pu!tG6GWu9x$1z(cK?dpL@5MBsr~iic0_kM)pkT>&S85( zUq*kQgkEPXc+PH3!QOZM(Q;J*KtwOHz_(lDRvm-yIf{cIzvs^M-{F0MB&WoW(Z}|H zAa{rG0M_fI?Qw0jh0mK=ht<3B68652MnJcLz;1xk5ynJGIh^P!<~078ND?WfBp%?( z*mUt)A8!U?)%cs`Rs%9@fdiL=a~-^t!Y-^2cdJ3%Pb`?-nDsrl#%xIDDOSvSXzaQ< z2?C>@G7cKnN3~HoUC7Z0*Btr}N#48+vGlz?iND;YSeN*s@$gun`v#X(=+~?Sg2)iG z(UB02sXfPr6g&ZCD|}39J$NQ`PBOr!MmgJ0h4QJM}tKZM?nipU>Hcb()s zE!~oQ4EoBZ&7Jui#_TC=FoP_op6ZuBEJsw~+#~Y_*_d$8sUs`~db4GtHE>w_{451? zFy15uxYSs=4J^0=k$_mSe(iJ>y`9(wr>O_o0&d(!V^TfkiZ5Bp<96YYh1iG zyv*f?IzHqK`&FP!PXd`=*$$v+qI`Tp)V%CEMv$XBH}00fs?cmVToxH4otrNhtU&X` zMNu;U&SWzEVd9F6i=+Kpg3|f%gK(LojN3y8CbQ~2j;jvnwv6O$MvqvuJGwg_Cps-7M zWPy)z;KCp1z}~%5bpTTM)hAw z?e;%5>1+M@YzmH(tyt7b68OJbBz8P5sl%)|P_n@qi6u8!4Ox7ASF@y_K6hpL=N5cO zTre%Tfif=*^sBe%T%%Tf@al|twA4YF+GM9Rv8a4-8};k_@5dQUzp{RHI)}yw^8J}YJo9ABu9{>U$o5q z$59>Xlisi{ogcsF)n=hkpcZlj`y8gFzNo-3s^`|ItPYFbNfzq6itTz%01Z@+?MFq0 zpVK9{r{W)G9(=xo^r8Pmxf)n7o)v{lgmYrNxoSv49J+tuKDwsB033RBRhO;CIn-gN z?Z@$1Jc~wuV+a!jppD2%YzorEQAHVfH0hmitERP=CJMkU_o0N3=?l2T**D^PYB&fz z8RNWPO)LpXV!dhm{E>D>`^4r#*mRI=LqwfOBthTePZgF9#QJ)cuszb3cBPvoOjN)M z4ZyiLksu_;k62Km1{hj5>yhP;IUqvnAIm>Yh!qh9z7%a~5(NiI&vZJH%_fVU{px1N z3fNtHmsfo#=xX(o9cJ2rTV+J85S9{v;1#?hL^NHy54=2B&>RN3O$mQlRS~q^KWk>AD4Ni`cn%T3+FSiqBw|}d|{|h8azhC%lkP#^{jlJ9^33$2%Ip9`Wy>W3n79+1x@Hq2;VKee*jqBndg0PI_BGTPGu&f00H%BomadZyJW`5> z*SNuma8zwzBsNuutRoFh#pTJqx;EoaD%guC$UR28B*3WrtF|<OImcs6W=o{KS5i*cCB8)ZsO2uy5%2Bl7SG`!6+yOX;1?Cftd+o(G6wUBP;1a z81|WL3$QN*)_8U@?%#5xo9oK>W?A7uS~{F}01GK39g61CIN54hP?ndCi&(W=CBezM z)A%JH(p+V_*KnVU1-Ep8Hri5E$M}Gt$-gY+Lv<7({^r+(Z$BdpkfsGXLz0=HuYZ(6QFkzsL()h6vr%4?e?Ke-~)Jx0C5Z*~Zs)Jmv*B+B~GQkTt zi7D{stQ?yskYM)E!PJ-BWpu@1=I>5m0Po}m&+P?K!oUSZMU=ahUNoQ+b?F%;QH76k zC2&gBUC%b`z&j%C3nri%qMp7TBA#jFoDEmnTGOW_9dbuX03@QN~wE$2g z9!3cVfpQkUczNako9LCz)1wtx011?TA~pPWlB;OcmALxd8*rd>+7V7}?Y&5>Jfkz7 zW>&&GH+mF`kWi2X#DUlrrKF1+16E#YyCB;tLA$(_?zTK#pTkea6Q2#jd=*5z{uE-W z08jMe9rY3)JY!5$)&nY97jF_>^j8%~wZt$o%tkAS@OVqg`AykPe2HL+ z?69jb72`CH_@Du`YYivT6?!E^BdNOm*Ft#j(GFZn(E#IIc+B{0G>(jwsz&(1qnNJ= zO%aw{MvI@TIldH>HNNEqU~1RK-qxVt(x z00!I)Ru0bJ7TyRBbI)O7w$DcRh!{MzB^Bo(xinx~HLHI3?AGU3_sP05jToIX_qa+CaqdUQQu@~q0kZ+gRtwRSnr6tKuL=WLrQP$(He#}>4EUi{}6`d#p zBx8NX>ZI~w9PP1-4={cL@T!7v5r?;>MP33x5sER+$7XoYWg)?sHf|dU{-fm>7pKcz z?^NO&XQ36$RqL)NVAjpY9)nwXZ1>k{aRGAyP_P86Iv~iQNn!WE0X7fKUAw|;^)7?H z-VpHfY|gF#`FA8Dl=6;&u)IR&Gm-yc(of*ISBzqzjje>%DsSVH(tg5Y5wmfjP?&8( ztG?`=K*LQ{_u)tlrV7W}kXCBC?- zRSSf&(!Y6g_1hrOGHY1itx5^^s-3hDEyd%Ciu&oecE^Od1MepZ zS21sY7fLsGnof+TVba$`SiT6U`yH&WY^ofxHf zPAkDK8}TmPtRThr1FS?2BC5ZHYmUDU9`9h2^Iap~3Zw(4(P-7P?J z)OcA)xQro(AY%fotK7{uK0h%?JyqHqIM2-AC@o@sX{b*DBw&*D;vY2*4rI6$HwRwy zJEo@=np)(WHs8_@Li&YC(ul)2L} zY@7YT5iX1aYG9}oEX>w_!TZN2hcI!(ao6c30{Lk)hwv>57m|n3(M1`!cs&K15r*^8 z24lKa))2wv8D_&71g2O4jrETzd;#$vd5Qz^{C>Wi_dvFkx+FXcDzH+?17`MaA3?P^ zfZ0gq!?DQi14ejA@u|5~TD{KrAP$Kd*o=gZp&>&|k?jbF> z_yuvJNh9xaZiS!nY;RUEmUGTsw!gofRpk-Ngi1IJUxv$F^b1<&aY5Ro!BT&`e)OFMG(6NL*$ zMn_!e@4B(;djoF+Rxzf1vrc~R=b}XFKba1-d(oyl(i%`t?T-JWjYgAw2f~;m|NkAQ z+!@G$z-XW>T>rBizWKKtK59e%SKVOzr_`nKG!lhc<^p`ZWJf$2&59}IT9gCJ%G|rA zjv=0Gp4|S&cRnnc+MACR0R#;^8dxeiW7zeGoABZRvmF-j@K;3Sg7J6=3>Fn9>CGic z9aB&r3VS3OGLHftUe<2>WGzkatF!YB<8OE&dZI4(pT3?xK!=}YvN%aHRV2n*j(7z~ z;gQ+)AD`a0!OuF{%dHa{IMMpO({-g?^=56EdP9h@ZbOL143ouEzAO8@^aTCIhZRJw zm4&x6#cA{v?>u|&Q%S>5O~(usnLE&ylgII^euFm+=#0C-N>Il>slyPRA~Ix|i&1xQ zyt*73dK;ojK#8VWH%t8^4(Ii9pVehNWT0sUC@rX)*{DNKyJJWD#tK{Zpyukd2e~za~r^=?+U3%k7W^l84nh0t%iR|CR%}y!YL^c0D*Bhq6SQDs9iXmn5?aXE-T#h z%k!o~a(kyyUFcnP5GyqaIi@litRPHQx9zS-@N8Co8LiK{p-;j(p^zXC)Iv3@3YDC^ zx2{VV?;ku_65E~gg?e@ke!M&QP08Xk-c zX?w0hkw8%FwLavx)R-{wzNUL>y9FrACikit0E~+x=XX<*0L1k@j~nf-n{LVVHe&)QB06mt*wVyuH6l6LuYnP&>B!l>^8TO@J@A5=S%3~h(4wL zCo)eB4kCgsm+PZKbJjC_PbR>_U|7ZLI$8lvo>&FW#f>a)9Dje_FN1T+10xa|9Q(*F zz+d}u|M7@{7u|4`67TkvP1zWhZ#4nxd^Qq--mWohq^~$sBS^|fQDjMru7S~Rrn_EK4j=pt3xWUPaUwtLa7>_S{Gc^WjKZJ zk3``d=+1?$QvU=Bzz~59OfkuN;L?2(G!vNnPuGysApfCfJxf%)n8-xX1k5hCo2Nk# z5lJvn0}M%Fa-yYya0%iG+ml<*N9ruGJC&4`4kbxCN15M%l<>Qfulq1mut07m0NOCu znK;>@HmopDBwAjU0I<{|R{9y+mn?OU{s?;B!+8D%92JsP=sSyyqBGjCNrDsKys-Fu zVZUok!VKKR5YgJW_6rT^;SXXIhyLXcFtx7FHOBy^el;Llf3VsudEJ^LyT;Gh` z2^U|xa7GN#?u$k08C(d;vABx>VCCahh%mV}I>m5%U8uPecAQz6Tg4uxI=R_A zZIiDS#@=KEGlI13Vg`DDKodJxf+nRO>6GkYo;*aF+T8j^5`$!0xKFG`4@Di`tRnvV zn`{M$Big5xc9{+njFwn@GUO->ZOHal#~kgF&8Du)R=6vDajegJNym=LKdF`(R(HsS zKLLUVDs%;UnmfeKtc)2gu7X~1oDSR&&M}vZIZ2?!cyiDO;Y(SO5-zi4uDgc*8oL3Q z;bcDC73%R_P3j(#7CUA=Jos+iRQOR+0_(1U-^B|LRikfVA6AT1Didm*6i?{>op| zaiW2+VSpvjc*3~-MZAHK9bu_Wr>*n>&Jv~6#2{8cvCN+oz-`xdqL1j`LRcY3M#4$vkFjfu zW$NO;*l|GDE{RH*(*T-CXnezTD>(2nWAn(t_|$|O(BobEM(n&R_99>##1EDdFD?5V zt&v`Rzix^Vt=_myD#?WDcO-`w;WfeG7xy7GKDkJV0pCM$S+ueqmnc<~KKTvgW7adU z`ZKTwcbyS@iiyYAr=!7(_?qa;z?)Ez*cwY@z04B1?2dGH7%i4CS|v{DAz9k@N3sb}0=JyO%wKRpsyIq?;vnJ{B$w>>o0rKZIcVytPQ+~3D4#kyrURR` zdFbZNU4#sM2el{1{>!ljie^Ei+)T#YC_N_%UAKof0UsCE54Kc`{X+0`?@40&kpBfaU(=SosB%i~};P;}!S~8?g5A?pru_4d)l^ zUvp7&S@SEQymnV>dG6@l_u4902WPv!GF^3A41v7HUjvNLo01ivj zpc~fIQbVNYM%xJgC2+74#rt|*rJkDSJ&w$9IXw-AX%z=44;h)Oy7{^R{?MT@eq0BlHx(P?e=elqwk^?@ z?wNVfXe<5p??27{q{__7k!qfG)exUQg^{8*RJ5_{^7y88NK6NMo<|!;S=WD1`Z9&U zW)#R8Nq!Dtfr}jDA#fO(i7k}zYUKyg*C{1ok{i9J9=O;a4Fa#g{sRkmqf`9e-&}&+ zegK;WqUd@t^1fG^SqX8cjNh3#jKP?@adz5W=~GQh#va-`zoBQ&7UjY=-m({zsn#?EulMkJ71n~NyKs1$Mq;}pFM+~hlvr?PCux?DOR^9`$p-Mw`z2DH% z*6+DAHINcrvLz@K(#A^3ZQHk|RPY6i0c(S)`&QbWXlrWVbCB`XlG7IPGN>yNqYLVF zml@Ii9g`h4IC-l)i zyGec}i`OlHCjLWaK9VSkpAw>F`K^+?Nv|-JYCp2P=$D41oWdiiLK2ID4Jl2t1AuA3 zU8&rxcfuq;YX>^7l&c#llMA1INIjs|G~c}8dqI{z-Vn#=sILhNzmF~L_()A37b)SX zkr>g#^o{GVG}hN>t)ay+h|uX~=#Vu?yx(aC-X032p7raZsD&~k6KfZAmrjr%`A4Ud z>@XTH_EOSG(o)g-j)T~H6~P@w3ZU$hoaYiC@*s43|Bx~z0aoD27nv5-b7eQ=94fJQ z_w50~2XW9h2&ziv?FC`aYl5q_a6&mc-;xfMAQB-%u$c=R!dJ0Q7uOl17TmmgjsmA- zMM=+27^^H!gh1c)*lDfazp76g*Z2#)4oW;~&F3d16=C6GYUb(By{6+;0*E!`($7I% z?~BCbelOra+Ffh5vSI}Na)Z_>wkuP;LCIyD@a?0z1z*0ZcU6pY7pbScQ}3j=j}jNO zO$+tfAH{HZ_r0`QfYc2lo-&d;N$}BeS3Kc)c3tOP10~RMpQ6(2VsjNL~4NopMy<zFK9bcmMR>hw(d;Zcv|!T z4;p3CT&!yCifZJ=+g3R>EjqoRKc}#%-?w|1@tYSc3=$z7oN6@)0kHOk5OBAE{KQ`o zBmaQa!J=~1?HU@#Lm#h5NBYU`X`>;o!8#N@NoZ_k1g)j$M3gUC(1mS96DPy;kQs=Z zC4g!y7eU(BYT;%I>uMjddF*d8dMXQ^cx3HBYIKWN>_TUb-+;^Zm?w9VT%}dFMTDre zyvpeNh+l@S!P>Me0G1X~7Bm*z7n--m?X@E5Y*!I%39RY!5_nTyIsTq!7^OY-nD^*Z>7<8iloJA0Dh<$#TzjE*J| zdH_E>Sle_a%Sc3G$8v#HLWt<7J|m4FtG>! zu(@6^LEf>(g+llAML+F;A{eOB9i*SvcO59l2zwq%ttLU4dGNWWx7bhdqvqEG3D1L; z)SNkwwN|L_^)ExVix_HTYhdEnzDy|a?RE?}y$)(ClQj+v#)L;N`b3<(lgLHH#;c$P zNCzkqP#KO=0UCx?yO&PO9%po~6ytXUN`?GsXd2yiF>1~TTym#(kOGmZ_FY11i7{{C z%%EV~{)*)t5x5`)B2d)0Db-kQ@L6KZc;Qm0XzWM$)Ke4bGRm4=Hj=9eM0C!?QB08( zLYU1SI>7WJuSqjpC_@nCI-U)CiNVVfkkuH)cp;S^1FE<>FWMS=)1M+}j3N<{z=BRb z5SH0MLdFuxk!1>@vOh0ab0{%TCwq}X|p6_JWU*vhpY7$GVKHz&chSjU7?BAAM7*(2pQVG z4Rc8HY9|DAbCz$VbR8yN@jII!>5_x-{Q|@j+%9-{LE9}oKw4grE zL26ec7Po_Lgr1zjiV<@7DxBVN7fB99rJvE8 z%+&C0)LhD9Nno8?7!Hv2M-}s6U!h#{V^2i+S7{u>> zHr)x8*g`4J)kB$%@Rd0^+|&=#MsU zr})p^a^P`mtGgc2n`E0Wqr2i>`>#e0;+dNWc^{%RTVyL4y-HPe!suI1qVSsXe?ZZ} z2JuQ~@ni`krnJuDhZ1N_2Tl<2i3?HHfUqK?xa@m^^v_dr8o^;{(CPji zwI0>z_1@jKN(*#EiUwb-^h47GZT^_aGv=`>LJ5mc#=m z3e*ZaqmxrOcMo+7pO4FN;lSFMm@0)&5JTaT^5yf4?nmL@#G=pif#;fZax~8{0RMQ+ z4-JCwz=ptJIdOtA&3!D#V&i2mOTVFzRejg zj47%l^$CFFEFRJP`4z3annDLPQ^95^e{Ni#`Fa6ZMQ~T7dK1%TfcLV#TEL@t6GDBq zFz9C3j~(BpgM-k`WEkH2jp2L?P=J{!sJBdh@IBDhQL;pM+LhS)Fin&@($5?B=@^Ba z;X6c*jtB0Q*BtH%4G2hr0^?B%NClM|C8oX&65j)lPUDc%OS3u=DD??JosD;GMjaL) z3`!5PvO%=KX%6;AoW!Hiyd3xuNs)-g*s$$*=6n`X<@^ZiRRopt-L3)vp5=c*`KtM~ zCY0RvbrUAOyx*`xem8R}od>Q@>z+k@j-0Y_ev>LB=IPC{^jloL$2&-@NA5QfSccz{ z_drdM&^D~MUG_E*J-}28oM=x3r;1^ReRrjze?M2noof%jE{{A{6(4I~sMbniw~qTr z&B$!%2Ko+WE*;X=2%&ERqHnuI&X%ql(jObrJ!7swo2~fUw(n*~+=@fwY`LErtfsl{@;S7q0xkW(@Y5S;!m~)In#Ve=+1)fk> z#gJno3uAGSW71BlLH%YIMevUWlt`kgmGwS{aM32@je%st#YlzY@S2BY7)6u#LK6f< zFo+LIa0dn`M#+_g=uyE!eUwV6$Sg(##q=473!&;VU?qggSVkq9Irag}fQ$kUVz$+Z zxC=Sdh}wGP(4`pA*N_+03J)vRJFVx}{;=%D)z z0i!o)Bxz$Suuvz;SuDCt4LlgI6dM-QR9s|n4c3y$P7vYAn3$%Fw(M3$QVeBk;@M8- zprX#z5MgGr@fcDZ2%+*QW$rrAA?BqLlxC~t|9Qg##V(RHXeI{1+<+P0AA(7xD$Ux# zV{8f;{5Qnjq4u1xy$}F16YO*JhS~GIOXCF#EwT?Qr)`jvOX%@@<%f*BD!I^ySr-9g zGpCs(6fHOY57K&Gv5ElLW}u(3GM|F(V~>LjnU7XpOP#Pu-c}r8R+oOZKj+WMxze&A z*38J_?$DTtqxHb;gIPw{;@r^Gx#D$+Wcd6T8&?Km`;a`;JR%t^uKMj;!-Pz{^peK`#wzEoLwAzy-ee$ zN5!0Ku@Q()jR9afBdF~h-zN^ClqK(Fm~?H)CujabU(=&n_%3e~YSxA=((&Rw$Gua2 zf_54i5N5L9+(1=dk;-7+51U45&v*@u=Iilk4&WPfXAw1gzuLb3MB!REf1NyI&abv! zAii(s@Yex6w&doA-bT6sejHtGzNeV`>tFxIUOk)r>j8QX=ZQT4Ek+GX%z6E^SGyF9 zyS^vF21%jF`r5HIv`f8tkB0kT49?0`dyXkaO^ebH`vH-%J!TfW%onryZtob~yr*Lt zUf0jU+t-s>W)Fhg*nKeb;Ny;~MC=pt4ou$+<%1|Rhs+!Inay7PN4$1}CJtlPwVam8 z?rN=vF2H6`cf=51vI=te0-37KjqdQPhE(C z4_1vk*ptyljPt&*Jb*R6Lf;x>RRia5}JCn;phRej~{kTQ+cF4P3`~)rPT5#s)*?yIB zCvoLd8om*4dTVpCQj9~*4=6tnQ2MSXWsEZVu8@bl`DXZNdUF7ybw5d0xw36IKGd~(|+M%%&SOc{Jm${P}f;bAvS9sl=n08I1LY?HLsYy3#&r)bM_k?c28z`rOY# z?sw>~{#m@0Daw!T9xi~-=kP%T-&E6VL-e8?O}qsMp@DZ@)@Q;;Y314Tu?w~NT&?V~ z(pZOu+jQcWUfRKv-b#^U5xw=2hg%)DtGc8rz+DtR*3d9}W>%Wn}>(+-dTNQPXV9Ygf!D7dO9 zarZUs34BE5pi9PP+T~~>p`^h4RV%E&^+?vG>SD;~Jp#rnC2kGup~*HzL^Po!~)FP7*|8ZWAfQd{z} z35itqCwnT)Gq<)EwK*tKJzuBt1wJgiw+{e zp;Q$*90OG6>b#&YHm{*ozXKC%Jov4?p)(lVMmk-F5z}t8h>v!lpd=r9k^Q1vIlO)^ z057p?s7;+&Fo6C`@bZ;J{_-1$_xar7+aF);eos{3SC@|X^5uGgK~gIrpZVTs!BWvA z&Yssf%I^vOHHzqhlHumo99K}z?K^7)UhLjC@+N?$>01c5tR;`u`oEd>({iVEjV{B0 zRiss~?sez&Mo61$M=#Yyg`jS|w{m#;u713iQ4K1t6*$ehgk?qMuR$f5Q?-G;2xzUh z_+<;PiaSr?&y5xvuB*nF^o!3`KKAAAS`KuAsW@#Ao50C3w!|%;AL)FRGqtk$Vky;5 zn+}la?c!Myv2nK`gUbrhl0+kWL#2w8h*ea>?gaO;7`SeqvM{V^`b+jl&XL?B1up6B z3>vxaVnEbKjIzxw51C}YP{&}EojHavIe)LOOgR)oRa5wFyoW|T!4wN0->Rl&%kcoJ zw>W|ya@~raA?oPjoi2D8DB5v7I&wImOVuX z5{IhKQ4~6yzsBN(=EVIK)(65A{GIDuq-63w((RuhT_jt^w1OO^uXnIb#fmGu+i!p+ zgwA}=jk2kTe|%fywwCYv!hju;jv3C&O4k>W5LP>w3$7N|3 zAN$N5c13YO>iZaSswWQ>5{w8EKLUUJLPF zgVgs-=$Ru^FVqA zhViM&x%ZuuCb>t6eua278;i5$k`pP3UvO&-=GSUasNI7_OrOvCk1$ zbEdC`r>CUv`>}gJc(MYdp_0rN$i|FNu#loS2Gh*%JX5)oyhduS@x<3i!3ARx9;LbB z8_+7Yx$zbtU|N7E3^pNA&qFJj6bmEiAW*i|*wH@1U1-ZeQkZX!uiH_&VEYEkq=HRs ztL7K9o*sd7=p>bIo)0L%dn&qVIV*3rtPlCc(Mqj$sqrF;mox!J1P~1WFn<;K6s^w; z!<6?BDv8+qk`pj~V0qBJd5MpgkXR#BguN)8^bu+oT|;y%9Hv1^n4I5{5LtRbvmewQ zOAA0yyS1PTop?$m_DGW=PH%&%@9yet<~=~?{XNUD?GhbcEPp$DC92*Ir`-tTKT2B+&dQ~(I`ljqJG~f)q8q`X z88M-?Ogh{g8~p8!yn|vQ)63y_Wt?@*MB~=#Ej*Zt)nk#s>Du~NxTK2z1R|S2F>r|u z)~el97cjC!U==)^@|OBV8jPSV9>$5VGDLeeT$nxM)s#%#Ac@mD!LEiSn7dAQA#FG-|Ad^*0(=OB z$y{0ET_Tj;SkA$6bZ66>a1h}RH8w{V(C_adzE{V@LwsiL^=uQs8m8L=O^GIoB^-)q z5|D+21h`TXPifnm+T}#0BOEEomC7A5r`}q* zQeHapM4A0-v^!O|J2QU<>!mcM2n9p$*|F(aOPWbxPxOLma+##*3ZEN*7^s_Um0Sr^ z5&dJZ`nPa`#Ww|yI4wU!3mrCYkVwN2PO#!iP5vJs<+I!EuP){?z2Cp<&!Et78(GHT zw6={>G_8(JIa_QC_GN}|ix)=FwUA36@u$s$EL=;k60 z<#hUUk4{X^ONUz)Vb}X>G#90g+t86>i1-x%h1hKk_d(+-A7FvR6o~0CJ;K+B#HkpenQXDyM=1 z;Bv6Z6BZYV0rC@h7W|ih0NEsw{Ri`Nc=VWHw=g?VgIdGn=dX3fp1M_(_EXiooXx$W z>h}nQnW>A-RYgn3MvK^ZpUz@H*u1yJ?uFaXa+HiU*9l+AePy1N&!@!NSQQ`oYrp|C zg`RT0*YHDgP==Ydv_0+OYEkEa|3kkW@g&IeIc;uXOeo3T*o~AuTVBUTWVL zC@Lru8}omS>xtDYG=NK-AE@~7vlkQ?T)3tEu|L9$6rDY$$?aokhz8w3e`V;hz*MMS(t7kW zX1QAWL0_4y=zIcD>xTs`rlA|=cE1CTPM>?FRZowezFzm;D}Y4NrF3KO%p(7{j9to1 zxIs0hLjnN7&8I`px;oKG)p&%gwj;AxZn*ev>{G>DI#Ay5wu*7RfE(_ySG8v#KX!Oq zpHcIsYR!3owFgt9^UImKSgt#`Td%w8?K~7p4rs$SWL<_P-C~$QgGctaTg(PwDy_qP zk#>zU?i#O^JfOb#3vq$4+)%lvR3OIN^A(W~tkLn}kd|8BRnK0CUh?HRl zlh%Bm5N9^%nFNqbcq;++O6b_&FeF7)$vsrw}2N*{NHTHC#N~FLvrO2X~5W zSI*XDEusXI`KKnZhzyzxkzTYo1@&8;rbsXuQDsLY8t--xS+bS4e*qOdwsk+j4@?sO zb`{&l4q$Y*IVg1UE%X`xhW_{-`AT3Ad(i}={am$o$Wy?Yd8&q*ooBFSUK;Y{sy4Vv z5|>i0NUvHn9jITi@Up}9)da?&!C#gqXOD0plqGAhrT%HWvBQgvX6v{iSz@Nxce^@S zB2zxJh|%n$9;|4MFd>&u_M7v>Jz{=%rHD@F;%DOpd1aXs(m$M?4|D?^cr=iB$6`Ch zbhAk`if$~i$HP=#!!q$nG~mXWt86_8q#t`zY5%N%oB!D-UtW7himONezTZhTW@9~Y zn{F7M`76c5Q1vhIlIU549kIk(C<1w z!&}a4C zS5WvtwoWKZG{Bv^w;+OuH5aL}2}FAtoo?mHPR@;zdMCtK_GHmMsP1^d*!;551^gJb z#htP>y_wc9Dj9g~(q9svQEMHm*VbFUQBpf`LHl7ukCyI$UmzZEg+kp*Ve=s?nZOb} z;Z`@4{T88CP|lrRR@Y^l(@s`V>iCb`8r6l$Ib%*KyYE&lQTy|jCr9){ywW~YIGSL} zzy)Q+5k?6~g^TRU>LhYI3@$Q2fTg`DYU^w3v?{%{2%!<_ptzvq=%8f8KQ;(Tu_zF^ z8%o~+d?Y-~okZjsC|h_9_laqr0((NtBervpM-Y>zq@I{l4wPGaz`1p5+eq>b%2qt0 zAuNh#Vr0UAJOSN5L1Z#Vai0C}qET*QP=*#UH^p(1?VeM})SkwXh#xBQfRJ5S)c_0% zJae|*x?eUEiDG-yc<+kjoT9A*VB*zzDJ^@QMTJja$RRGNDQ{$ibK=!i;=t0yvF+h9 zuzM=gk%mMJ#sqaH3Od)8=IKQ5RKN8lp)Z(9qg7cta#h4kv4e?XV}|lgoU-$TR|1OMzbgZXNOD;%o7X5bKJCoYQOGpftbTS3j<;<@f62@U8J2n9N)hfbxlt_B4DAp>U&0<5L7GMeYc!ic~h z!=Zy*FxH>3~h55Dn)}q5U4q_;BGBm#w zOSPH+cg0IOTqgrS4E7Ni2w~rCHVC@lU>0=<9xx%|$4WMxm7;t3@dhalY{$uKh}?mo zsRGKJ%X^Z_(75W<$3D}3VMP?8dZAN_Pzu+XDY0` z*%aj2bwU5Q@&`p>qjJN;Z-0ZjV&do_#)Q%ZUb^DHI+=dULJ#CGA9g|O_z>g{`( zJ3IDej6Ve)3vO#cAxVGenmbRlR}dx`6Dp)HD=B9QuA|du9mHwLm_*?|DH{? z^W$(}1ydCOkC49YbeVc?3FH|F6u3>n)>+<8-ubkca(sDG-BI~w`D>6Rd|sU@-9K1d z6S{4|TR$A7>R=vuTwH(b@Hu~Ni8e{Nj|V>D{<&$(T+}xsGKV+?387S6Ce&8ga9ITOrRp89G;{LgSYLLr7D+ zIH0yz*@B*YD?wvpBlYWuD?o|HoTjsD zLfFW+Dcn<6iaFDd{ice9Z=~Y5!VIY;XRum(Bg#e$$^yY+P?*ON%ccf%-vi8WFi_0T zrppWv2}z(8KVS(;Wk{i#Dr*tqCn9b#tX-~Z`9b|$Mn#`c3C~nQkW)4&YpGf(LY{Kg z$31B#pqUSWfK-5%6L=`_7q4?B1slRt_a^`^&36-f_?-rN(%g-yGcf0fkXq$rvwho) z4i$}+Gt*>&)eT?TprH=QS3-K)*ZA(WrrZX&`90Ae!McO|Yn&TZEW^6tk4ABur9j>$ zll@J;4nNwuelSecZuE&2I%uY&Vb}JuU+ZICZ~J2o9!Lj2*sa7>S=KCLM|1VKlfiH- zXiIGcq*B5w2K4L-hUbLxx0W0xHc*Xj&Ua9q*Nl=$L*YbLrk{&}YpO(%QI?#^zYTYQ zAHRL`z^!+?jKh4xghP8ux;^ixXaA$L-zMhgt~9wj!{aoa5UzRah?`04jqGWL7iK5G-T_~V4p}``>S=&}Tq-U5`v|2S>PEn9O5`wX zc{lWOG!k4~4O~zE7*-x@X24w{ZFG2FDe#`(<|QP=AY4`p1VPH8J)CL~wupl&JMWeL z^W$4{#&Bh#x$XCjzwQM^K?t`>a>s(%ZVl^)bkNnu$qc%>I46hggvuZK0bNJHqO<#7 zD!NfGEv*-Hz2r?c%Y@{V-HJaogg5B=9w(3DylnGB7$E&a80~fXTI|yugd#iC)V?H8 zRuH{WmiBOl7EFh&s3DwQEC{z8`YY=>jI16-a!;~4 zD~wvk?!%tHQjRwmA4!zBwRq2`;DndHik@WY83}3QeWRG+t5Y^&>1iTSjTWN_+$mvM97w+ zl5^po%EUXh!+U^5lG4cqvHEwrRl9eu5%AfoylzS957woNX*}4YVV>bz$O6qw^Karm z!h+8pi|B&g{~GI4eVjqiLD-o78$!Lv^ULL+Iez<*_9(Nw0zV2!I7m*Vd`)-cspNu} zS7q7_y+Z{}+B)s z)59E-q)oJOn1qZX#jHcw2J!71Xx6RX0JRRr_h`bM8MjN|jjiBkLQYIg!&QK9YPeU$ z{N~gq0|$>>y|4B|$=WCB%lnG!bLu-+Z1Aa)X&>0~=VmcXiL704xNJ5!xPMrNWY=ZX zlas9k!5gXjq}1wrO}O-<`jN|`s_BedY78XJ9!0NcFqFmirQmG+{bc%610H=3C%LCn4MECEH) z5ChEp-6zGvC3>}~#H_nAsdS(SL zvl**(4rB9EZ$lRI5*3iX%Rv>m36el7t~-56_xHG_BK%4DfK1=&Eo>z2rwB}iDfNZl z5jG;eYJWB>tN?f2#3rCr3fRb}h#y4@b=YZ|Y=`@{of1EWsi#d43Go{07?KWC^ih() zr*~M~{#@|^f6MlO*8ot)%(BoQ>AtL}R|cRSFZVmR!a;g*O(zpdY8MSi?4@lV2M#m; z1!)e<`6Nj#FDmN#dNcZ1kL5IcVzrYap6`VwxDv6DxidT~6SsFrQssY$|+^8_nS>!iA2*#bb>ZO2GnW(>Ghv ztF$GoudqQxBCSuP7J|L32&!6BC5bIO0`4ze^0phipoOm`;B(`m$p#u9!$Yf$4fbUa z<%(C8MJUSy{Zox^@u!$n1=5^$A~9fh7pUaxyAp0UX?; zI}AJbZ_-2@2#fqEQTfTvH=1VN;(k79wzXg@$cdmPnN@&{ej?PcXX`HO&X2_3ZD(Ju zqc^Plvs|!aJ2|c~K*0giG?m73jT{LT+D;M~E%q%sU-u$Z=4?k$Ec6j4&>=F z!yeb91PU0U)1Xk~Cn4i#s98L6>7hI!R@3QlVZLu7=N@6+2kiPM3u9%(uP3=XhE^m?!VW@8b6oIZ!@}@EV8Cj|}1ytH$$;_o% z5h*QVKGb-}9AjojMI=~=_W-`IgH`~Y9$tTR76PDbs5#Z5;e?cUWNl7I8e)@vpf5s$9B518Kmg{&iHU zfx=!*x%V>vc>ftVaRGSGK+5LDfIt7R!UedxNp}y8mP$4d*lDls#W-cfp49)!sBF=0Slx^z{23hV)E{+*g!n1(1WVgc=0~526;C=t;}vn6EX&CamVTZ1up?ol*&zYVZfUa04UYXG#V0$OnI zZ$1wNCV~hVexOxfg8EnJTz{68NKuVgUDfhF$o;nGpB;KJRwi@vQNT5sKnrIk{BTTd zLAD{Sf{cN*tl@zL5YA6|XECKpcP>l>U*^gCF08BLR*JBXYM-#8&8Fe|$54&#bqLDz zl)UcNXmcbQ?sxDTwQ-{9MgZ55-Kc&(Z_N)~w>n$&hiG+Ujjw{BBD}%^tW09zW8a3c zN(dA!Vy{VD2_#c4lQBkKc@Co6*2Ky(8~L}RDcg~}DQ&3V1{<%{11*}$P)lyL@@e8J z+%NDYmN_|s@N0tyS6N@DZumMH<4^MrBDQo9)ibyT0Rkevxh%PedpJ7|m1tSWr7VP|! zPdO$Tj1y9ZHvZ#U)zPMUEe9m^h`G#j#+g-gGe|_SSHLA|(puj#iV9y&A;Y%J5VY4a zsg^x|3Zq-fAgWkHDibhr6Dub&tsxX%Lwj=e&{P6{;j8&Ng!S6Aek5 zgj&q*8u!7|UI|F$^aSW8cq$Q`6`)A+HKD-0spbPNcyVqgHTK}JXtuHsK$!%8pXQQ6 zzNhWsVG4u90*q03DUUDW-#34QEH%n@3iFmSHOfj3P6Jmm&$PGl&-&YdeGvf5qjt!0 zxDSG3g}S6lTxmCqu8f_AdrrkGiLvu_n8J@RV2(V~S(}jv+)>Ef%oP>#(P=CV8|URI zgmhLac@a0SbPPL!1la2b>{F-7$Z-?-j_ahdZh9eE0mu+A(ry{SL3ezuBhol|fpi0K zyBMN*9jX8QgA??#m&&6LnIFTriki}PXVI{V;W!YrUToXvOZjupgE!Om(o%C0pz@Qp(Jum!3ley5_RRVt)jO37_g1RBg6e!Yn&@88|*2B)Tw`Pm=&!c z4|V%$Au(2A-IE6yO8ZaqTX#XOK=ju>C7G&K`5n3N)i!UUcf!>dpU@%)bb}l>>QM~{ z<%%71#PVQDhZAu5iv8_H(#+cwd*Q{ylyfl!32d{d$*JKO_TOLmsIwBd%ODQ~&>TO= zT|g$?MItLM88eRK7nnE_6m_j-z12&AsffQ4} z0QLPe2LZREJ%xew0N%joqwPB9pc ziPImWCsSJp$=Gkjx)|+%nX7|wIWGLmc|Xi;^~lPQ3ll4=L-kZkbAwXzr0O`!)Dcd8 z#%UWq4M+0`&(hnhS#P_lh?j3S+U$5{c;=il7)hF>`4HZAZjeHCL(}W;KwQ8<51^e! zdBday=w+nzqmMxB0FfriTkTjq&ir#>G#XE|Wa*)=VpVQhU$^o& z*IOhNYjOTfbymA$!}yFii_Gve1JHESxkRJ7mx@IBBJ>p#<&>wm1H+1})R08L!D+LJoa zAqGZxy5-#mvgrHjkzvsp+t@Cx(iK)#iDs5y6asb_H^`?EOnwUCP~_Fu98d`|Z<#RJ zVgB20mc%Jm(xNK5AWsPTxFOmcu3V>7c!QOk2{tQ#^qB z5`6r#y%29?5Q;JyuMx3TOs@s8Q%uIrrZT0ttLo$qjpwJFGp4u$4ZkX43~CB(q?mzj zx?yi%(g^O593EfVEq51zD!{ei9B-CYaim`?1%tWq<~qgs_7tl9BDTTABCqCh^lO7? zRRUE^cDmmk)G2!$&yo~s&?#wG7gWXS(7FN1{%J?-Z(`GD3GtKB!)TxVdSqE06W5u( z6QM$8m((Yd4K1@|z89@Tnk##7m}MgL(V7TXvL~tTK}*#TXSai=dw}btzVhA#l7Q$= zRb*FJc4(=p>IN3#musY zRXIAW6lJkfPF({{0stNuvYGbK4WWna-q|>`^}=XU=X)&4O~OWWfHIQl+6#Y&VU^>0 z_Vkd!et+wHhpk1nFi!cRX7&VLBjBf6MWs^$&R0MaSPw9#n7nudg=ZusSvVUJlndGcCLBqb~;y?Xf|i8h~rO02s_ht>0bXu!4=rF-NC zISq7!)J4?O-YyB+Pb0)^M^oGeE#K|pyT(QLHKTu+;IkFt!4pB3!;gaUMmz>^*G|K> zc3%$9o@0rbw_SZ`-}uD&u&nCnnCMcWNxP!${MQ%$~0n#k-UIk4IN%Df44_yK^uMo$yIlX8>iIR zFl%Gj;9uA1?%WLc^689!LeT+6#Yj}Od!L`V5eVWzbH;8-g092noz8GGaNoVCXuNN9 zi(ChOb#zwC9Kfn2dtPuA%M^+v<}iy5_M33ok-fQ$B}s`E&e1n@(i+N`U*97XXINRB zMZR}bREGKTB%%{q^bMrKmNk&6jbY1v^5Gv-`+kVrnk0v+MA26xMP2~Ju*8S6YoXjA zXyoP7`WI{74q4O#+5b{e3E8(I3mc&M`PlZNo!{n^l=!f8QniY2W?8wX8Otx%5yM)J zDm$KJ4&xl73WA>btp|-u>S%kg@#>%g3V_$_)Is9|F_IsF9x6?2JbMOaTN=?k-eQ~+ zld48A-6nIl3N#H(J*5B?i@H2?Q02=m*<#@`=8Z>CF$ATw&LjSL=cd_-*GFsw(fI-j z)8_wNvl?=u!7cGKHwmh^G|>2kW=NufVi|;(CU(p&^73J8TkGOHZ{$+<>p=+M*~(7m z-r~~U8g2JRM_68l#|cA`#U{D}UXHkY`pttzVIvjC z3Xv-_Bdg-FB!2SSTRKXTVGJR0UxGsq(!8KEDBtjYP?Y1Fx&MjJ*+zkFw2U&M(rKXX zZplgEMqE->(%1p0hx^)7;Iwr8zB6|dr#-JP0MaT{*BBU`$EVM|Vys5F^^bgP?H%&& z1XN^(r8qhlSl^rsWd{}$Uk^i=*tA^)tUo!J?DOuu0WXvb2QE_)q?QGI0Ton^z zEphxBy^hUT{r-cga|#bLSi5#?+qTVyjh!~OZTpRF+qT`S+jIY+u$sK>G?}Sl^!^V0^ z1(ah^zqI;mG{sgJE`Ng}q(n3G3i+m2WQZwX1Myphvxw`0(!`qBM5I|NHwNk{^~6@zuY)(F^O9U1Z6(f z=F@C{GvBwj&{51Y4etL1^q_%Q43S{Mw^5KJntZ`bgmLASLM8-3>&z1pi2uZHCO1g+ zF<|bjlYo9(*Y%%3@O1hDG5UeAcSiYcfgt4hG+#S-9{6Z{kD(R2xoDe8&NESLqFzNU+WP9oQpzBiNWwNMc6rbOc!48k1~%1L+`B$ z6nfPjxrHV(TU1mJyiKHPF;#&eSHHcX0)-kjMfy_;9_?$uulV#LhW^CWEyA#Ytg6Rn zuBP{3uZokX7;q8vtVtk?PwLiZ0pOH{5*(EEn@R4WJ#j%j`d#8H*h2*evmx^E>ltmU zC9L<~8(zaj(O5*?{TTcHW8+kbBD#2|2n0mYIyuX3{r5-%6)Re()6G~YPUeKA8DuQW z>O#Y2zHy#HaXv&AaTr{6Lp?9y2u(-}PW+HG=wcOyn@5zaf*8E4id~Dw1dxZgmVv;Y zVIvM{Yz68fpr()4s{9Z0$Pvm-Tyw(Y5yvhyi(3jd#G@jswGZ*r*^aM3OkjddiR>Z1 z?VkXleCrrMeGk3}zo4|q`4#xqU+FWJvtuxfPDJe~V?;>m_{T^>Qk=E!yMNDv<7=U+XoTtdHU5Y>=MUad!V z$+H`~Jb1aU?V3tF5t&_Z7KcO`yDwX;NTOouNP|%n?v4mD_Y&*KF=^@}ooFDPHzkoHY@BQ1b-k9mg)T%zH^><^j49*3T2JU`(xK^$Ae z&`_))m^7Vgh^dXNUBkIYi155V5Oo~$>0~2FxqK+&M*ePX2lxh7FnAVYJ0Kp{fL6eA zo2n<|LljGIK$aM7^7&l&3w3PL19Y3vTWu#A!LI%Hyg8AQKv0=8h=}$YJE;vr=~mAh z=waxEhT5#Bw+-F3@ejnZz^BWK?4?y?oO9D%^*6o3ZmWfsZ@r&lz3dBFe$oy7d5& z@gL@=?)HZC5A@TRkIXIT}7SYsuP6{8e|^yzo{4Pyb{^W+Fu}j^FUM`)jMP1vTTrQ?k0S{HJCRDVQ*rJ!+UJTeU(W z=KCSS+z4=BBo>`NnBv(#-3r3P3av@a6d5@$JZ~ooi5$6{l|u|-k+l)NT%h+;1W1=p zrS<&yo}@j0S;ky>c7q-~9r^9AQ@0q1Wz?wS&}Ul6`Bs7ds#o2g(*pB^k^JMSescx) z6yzhd-%tq|C0&d1I;z&TPXZs#@k&o&(Y>5MFq0jh}{T%O-gcece~H9|_$GGhcft)~UI(UdJ7q zNJKDbS^N1*FPF@?hbk?3>ZF4eM1`Lq9NK_{Al%@KE#Bvv^~O|)<9gLQnHRA3$5@#@ zN85RiNAU4^{}0Q^gfFwvN*bIW)KD;en|06wa=GFIQlT3nBi8DnYkeY!9yXxQpaU(p z(^}0S@U>kh8|&r`Qy1w)d=Y;wn@!a(03H_E=-bmn$(g?Mq1F4>iOREY(CYPureUvs!g?I{(Xs84-vs|doaIntw zMbnvMrMjzM<4jiW8wRa5 zZp2J}J>dfs5~3b237_bbIDNqx{k21&uripXYr?HJ@||C8N)>H%@TRT#3arw3&?K23 z>D;~o8&c#!Pr9gf&SPd5IjOMEmp_U>Oup*E!Ls38-mP9i*T0~{HHM-8*C&;RS_;Mi z&cXFxo769j$ygO`lmvdRqQ z*_-x|#HrG*+P5vQT&Hyb>XO9cYH0}l?2*1S3Znsc*B1GR z*jDEy)g%t*2lR=rDCk-Xd~&DKEt&b2@fWz39Kh%lN2wvqOt>M;qa=5K?wShv_a8y% zg5dr{?@ED4k?v#Ngx%AdgR~O4#SN6nmU75gLMKC&mizI_l~vbY&cepNV!*k z7#1~os@`f5W0i%J{dt<&-Pu1dBFX(JbwP+T=EvPO7n4!g3P?4Hp{FF-HzKMVhH$CGuPaRv&v6a8gaN<}K z)o5C8TY(UFcsKCd+?UqR8-9ko}QGyuN$t5@+Mt8W?F@UZ4bl%*vb8hLGLCB}C_ z|CN5QNI~}rgk3{(%e(0bcb35~HdCF?E$;9|k;q&Rlcn+Rf0bLP8YDqHl*LUp@iG|8 zpPGR&afytMEnzn?xA>t+-%lJc)qq~~R;z!!{K(>-Rd2HgFtsVs(a`&6G8k7;Pft4v z^vRWuY$*AIOCNq+WnI18ZZ4)}4ZU#_VW3GnRXTysVE7`6y*QG&7~XC6g3PYW`uW|C9v|&Y z6!j`SIK3<$-d#-|PBZ27Ext)wez@EjrZIg7UY|B<=v7Lgr@NvYNK}ix)q%YVvG$ zYh$t6hV*nJ*_eTId zPdOty{9_iS&T3}L=|y`S<`rGXWVoUCwCN*`nwi}ID}xuvi!NN%aza@sM?E@&F&- z#}g}GckfRt=WDfzyNAJtqK9ATf1#Zb=5w0^KcVcKUuvV-jC1k+a2~E9tP#$RIcf>5 z32sv*e{_ZQ+P3j)Z2o@yb#x+hThwSKzP5tX4yT$B4G(1}zeu$oNc=H}jIwCYkw8A7 zx(69D|6Sq=rG3+kj1hp5AdkUy8I0wcVuu;oDJp{Qm!iA&hBx4kP^Axr@(;eUVeNH; zF}mbqPp=JqSuGT4ft!vJ(Iv|CN;L>!rDIv9gt-=2C0dStrkRilm~^{94DdVv!JT5O z9q^Q55ll@?`F9PpZ1LjghfLO3p;Ysvh+F?a%~#Q)G9Si;8w9vOOysHo?U_DkZ-_uV0mhxXd|Et_L<0LtAI^rvu(_+C_Y!V{UN??RliZu_07-88|3Kj*pX1i@CkHm z{`h#*$0mi}pY}GeI%dQ$WsRbwmrbBJJ8p$UT3&T5KS#Ap?ThpU5Gr$_iiq1BacQ(R z6mIokHk<3xOrdH%uS!FI?I-ZM1KWxGycK8w>9D>g(^sh3 zDoj?KrLrL8Xe@_==%$e5&Bsxwtb`(kO#bLKlo)zhj-T@J^+HAhV65Vh31+zC?3h5I zFIOaK(_GV5GbsdUj5pksmSZ_g6&NeyG1j;T{Ms{`y z;}o)FHcZp(JY5GK!qFa5iCFL*2QIGe%>K{8u2T_ErIUqev6U;1?lIswG|W zLWk&rN--*7=ORgE18;(YaJR7a<@hX}))4%H#dN^ekl=1&1c16p&l0)!2r|kaRzh9w z^GYyLy%HJg_#1j-S@eEDkAPVwMcw<>j_-L*Ln{}gf2-oB_ykg2VFWt zX{cj6)e&&uD|BFpU_iF&Hib#6*k=lr^5Om_MiwRVN10}zonldiGrC5V~n)pvk2#cg&~qD3+^z!-KX=U{u83+XrH~?qOc+6cuZ_A zChbKGBjv$-(C#&uN=-EBKMsBJG*(h-ht%I_-Cw|WXaDUf3^AL|Qp7bgtIPG%C2ud; z?Bf7TtZHK0De~UkJ3VXX`jfcvXfL2pe6h_+UWR{zOF28%1^jLMx<|YV%nV^8e=0~3 zdU@Us>BNuw#yXu%;sN5!M>&`5T8 zN%Q`3D5os>r-Mo9c1X?k0_0l&Z5K{2(uyS!yn_gRt%z#rAE_-vux{5#iB5K}QDd%~ z-hyuf7^a%JnsiyTnjPeO55gYs9w&)^VW2hdjW*{{ zm@$9V;7JQf(TL)xQfv42r{SNG86=SbOSu_qBOFT$o!F%xV<|{ExmX$TX?8+@Q5=;Q z&t?q-)vU>xOI^#wWJ&fZYC#mkvY|8*soL*Ku0?%yM3~6$$o3G!hhB&rF7sUwpA{IR zj+yB(2znW-P|kSzaB%)OvlS|LKixaHZPAhvC0%?Vg~6M@`T@%uji%081vRq4Q@>JQ zETYL|HUj^Pq9eR%sYLVVQ?P&!2}4fHdV@N)#g^67s|U`OJQmg_YstjgfTYXvZ66OG z0?XzT#wQe_Luo;bSFyQuP=5rKcOM|gC6?+SE|?3(9Yg4Vh^=GL*QOC_h12SA`%UOLan=+Cc;n07*uKnDF5eJai0^goy?vLdAu^1T&^^ z@K2Nehx~bLmhxXd>xPDf? zq7XI+SVCLpzh6K@tJ|3?1ybl1>j%G5$mUDAW3J}wmhU3_%91}ktetM)k(c^cTI%So zI9Gc$^hNY%8_}GpL2FV09F4E>%q3$51DeX2QFwdzi8y5ULc5?oRREp@i~%Cfc7&SS z1l1!krV{;!Y+Q6)FY>Nf3eAJvwOC>|P3WGzdnBz;Y=*fai$W~}h z;0&omK@?9eXB%Yg4p5XdMn!f!R7XVfJ8o@+I61IbQe|9@+6{s>gp;=Ym`2_lJ=l_x zAd8qmf(|ex6*KNu2IVGj<66RChykPjaWemFXFl<`_{2A6tJPq9jgV6#B2+7?b%9Qv zsHH}nPn|FSA^Aw(LmCW}0lw%h*+yJFzgjIEqn#;@=xZ9tO3T7Am`T#lK+;IS$`GNCmRrkT^~WeRHiFkP)AD{DeWaH?M*-4(a+;nJwx;dbytZ+}>Amb# z+4YPIf~cD*;u7Y(0@NPbKCI-p)Sz5QX0`0pF9g=>Fy^ z%ggy+N_qZ}3e7T8g9|1>1-jmih&8cPIRoRdeAMyx-GkUG`?5~Y?Q#Uyb_%va1Pu{Z zo({^B4?$Q0EEyDq4&{BEpB-sGuiE1V2HK ztn&0tF;!zPMg0!7>0bv56mCw2v99x&%^e*h51@!x6{Yh z^8v_WofAaibWzED>)9AsU}(P|h0GG^1pbp~y@*CC(>kT4U}ME;#PsP(xUC~2H^X@> zJibNlm0hV~R5P~isX)Hn=jO_KqzSB^>ZTldfB>hPQ5;7zK`{a>Eg`IkIgBMb<#X)^ zc{ZiDl+h5Zs8!7=IC>?yCEpft@c)cwY5_5hpWiemP$K2RV12eaT!bj*n&y$!3h$es z52_CRTUI9?irvjRK(JXHfAqiC>LNgb80aXPsA4_m@f+t`D9W*a6} z0p0|*E)6oK=r_Nu%QJ>!8#^}r12r6xVR%Lc-`ocH1wx`?l8iBkh=BELM3T`~90+_8 zEY=k&C7?$2Cw& zJY8+b$|pJ45q$P0pV0+JmdBOHD7+XAQWE1Rm{les5$qm8S?w`D2z5GzduhXaaY*S# zf&d3_us}o|Vzozzi!3CGXhMLjFCK^sfL#U?0E3{~Llv_?L45}mD^m*G3$#uKNuSe0 zCJMRpx+!l@1L^%S$D)8rsO11-k{*CV(50h32h8Lp(KNE^T6wI{C4tVtFv2^WE)mTwM2Q9Xidd2>K; zZV5X%rHl+k@e>Ce?O!0iKrcul{!$Q!bbyaP16Ys`N081!4|2CfL=y)dMN~P7;es*{ zZA~Brb~xZ)h$jhJ45lE8@GqqesmTyid8>|uAp;G~Sol1d1!oWpxg>5M=O3p|PQ)@# zh@^2r5D>;R%~<_jNC(I2Pck9GhfAgA`RQoFJlO33QsMYYB3&4A*ocEzk&OmR@8htYp-Eif`SljK^7Z7S z8#RRrf#mlJGE7MOeHxAsnyzh-x{5!*l=>x3aNrj5(e4K#B1k7v@Z++9`1IEbC`*tc zG1J!!mG$VKYyTq-1YaGDBxSWkco26silDp89ty+S6XZJtS4ek_gPND7UU*srHPj;-qa;!w2P)aXZV;VXXpXXhL>;w1N_A+b^gpdP{4O=JWLmWXITh>3kak4^u zYGTkqra?h=Up1lqqi_Jze|^mCxYWV9tC;J~xpgeRx8J)X6hQPmEP&IPG>WhZmIHQt z-Zx_`H*aNjo4l#UhZ;ZcHFUVw@>e;Ocgih)iX&YNftjYkb^?pF$8>F0 z`Q_3}*;LGTMi)T8%(oN{t#>0i{vV@N)sJi!UZ2>)K0JjO{lJ&!K1`zFO|OO%Jt;6do?JnwJ2FAN z3&}ZCB@M`E= z4WdNE;fz&1oDme?=E45(aWwIGud_p@GL~6lTH^i9(dyei-){2|+NPr0?HN~Or^yHk zy*u^=@2!;A>Yd3MIa#b`FBQy}LJIn_^uev_ohd=;ulWFUmjl9vUVH33SS!x|=z_%m zX(il@t#BvJ*iM@zJl>p{z~IU9vi=nncd5DBrR!?1#kstUf%N1;ApaC**&X#LOmUiI zXErMGPgcndG3sqDnVw~=eRAaY$1YH4x7ZI(zWMt}Wx>#i0@H(0aEx%lRELzW)fgHtXiOY0F?!zfh6#3Wgdj^0R!U`A)_td2Ib z!eU=OoaBY+h3TMVdi9I3D%RjGrKlBG=aH7quazU+I{jVr+!s6jZ=;-Kgq{Ah?~i8` z|MGH~yM5i%{(L&xVR%&r&nAVrk9K@Gv-tHUWsTTN{i>+@=e1o&l5{(vCK>-xm#l3w zmG>Sa|2NfIMJ&0}pV_*_8w2ASt!exuwxVTu^)9Ne0F(J5lxwz6$6Tql}=1xEfn_8d4 zit9^LpnFEf_MpJCM+>wT*q2pwQp>y~KcRfU7u=1xGzgnnsIWEk9x*j*7C>7gsP(dc zPwM`T+z=bxQu4*%rD_vBw=QDad_5c9cDfKM?nSpUVzhKTtJog$u(p8j-uE^2XX{0jS!gr)lOC+1B5 zgl_b6SzVf5!Lay1jzZ2@7uu7g#rn6@qVWmtSKRiB&*qD97pEjgJiPV}od;DuV5iq2b^u~wJ z&&-$~MIR87GkGAqng(=QaD0u3z1H}0*&b%b*zvo9Pc=v zmu%>xoE8bCFOy+2Jd8HJK?U{qTHWAsfE8z@hL^lA0cwvVLGYiE6> zt1tS)co7ptL&uX?AyapdN7r?#y`UiHN|TaNq&&u#vq7*e2cm?_i*{^v<$+*M1W0zs zkgPCS>P?3_jOUqJ3AeS0x$26ISPtrD+km_J0Ag_ni+`|m3Ugml(SupZAT2kS1FEHy z(u=CqwOIiJ00LKSJtWM!M9dPg?%-VA^NH6LwOS|9pL3)~9VQ@mt%2F)he0%NJ&o#d zfYzwfk$PW$^U9 zgMNv`j#BNJ=-3AxuGvf&a}3Pry7JhLG~OGD3N5vcMneEm7&jfXJv*-ZIdsD8}72;6Lg0$__WvLPxdVPwa351cztOg-1%qlNN#}dYE>%E?@9pKz}RdckYh!>rOCOd-Dt79+%RH zpZTO6wwM0iN-yk_+@Jd5GvKX34n~gvvZ<#pP$@}5e#H_8DU`qJ!{5s>GK4z9TS zzC7S0?r-9Ug>HXBvo6vV(lKrkTjqqE0lkqalz1YAuq_Ab&;gjTE`W=;`w@a%%~o2$ zLH>PO@FK_heM<%(ah`;M1mws_X|1w45LQGwy~|1LzC+A)nAu(#E&eyc9E7rBxP07Dy)Q_%Ju?@dXH?BNS5LI3XoAXRXz-cgS`R7AggsiK+k;tKrtN#r<)%S>Q*8pz3Ax7MRexWliY)uwUsW_$T>l_K4L!>tKtcO$BH+P^b+af71C|}ME86zM z5D79N9BWQGGzBq4Ol!3TycyPs-nom@g&esx4-nI?-#+_0t?trjti}|R9AT{z;)&TC zN%J;Fif~=4FevZpkje?DH~UD$EwONTkw#|>JEtrYIz56iPKKU+o5Oxap)2YsosTQe z#8tj@-%y77E6d}SC3p{sHt!Nk$d=e|E|U{W=B%r}_8*B12nuu`GGDXO1DlR=v3g zg9iFmhWjuKHx1VPEU3Fsr2dA=?1;>Ww=H=iHrb}uQ{ppuB{vOC(b34!S`v&Eg*+}J zceOooXN1TO}W_IZs?mi(JFu@#k?Z2gIK<^$(+6fH%~43ByY<`M_U%8sroC%-m|2Dq72-fhi`>qLQBt(LbHzYhG75GovyW)SOjK zSe4(m)fO1)_#c~WKm1=DT%$t_#Mbb3z27670})J!Hldk@wIR?3YQTbA%Rt}EG0d)| zNkD*6c^)N+JZDoYc=uqvh+;QK_GrC_UlB>Dj2JVVEHDumx;CK=>dGB2djXuD zK?RqJH7kSdh23%uI-{ZaJ#ZRaojS zc$Pjn!rHE)`-2xlpKNT`YO+Rv<1uIqd%z4*`1ixNtb92oeenUkY#Yh&jhSGrZ1Ewi2c=j%C zq?Mg7IwzreMURPsP_D3^ut!R;R4OJ_l!lv*H_4RICo6rieo_2^5;|*%_{TCYEak6wcmocE|psky%cn7>tdwm*bR5Ky8($ z6rPdn?-eC-cR!rJlB-j?6*Z5Qc_K)Y_?xa8fr;fjA3M_BMe9hI$|NLwJm$q*d9i+U1rinI^1zv7Whd?FbqoQ9{8?4j|x&7q3$Coh_Gpn%%t7u)KObERx# z=L9)SW|#7}!?XHBWjj8XD!<*WIEP?ZYcVT2X6U}N`=cSLYzHy9YN)2JScl1#LYdj% z%ayDWWQc86$%;@0&Ym*)h5|?3>8m+mr%VqkN`cnEjAlGN(h5ZCik}X;i_Gvr-?HPg zS17X-M28QUo8HCfKLF()<_r1wf5%YDo4>I#&ade>NkRGZOZ8-Y1AUvc>^K}B>{v|P zLO(=_f~lWvqrmDCO7W>)*-vQ>M_&`~utU!|c`&G9!w{X2#!QT)rfVKjJ%TE_2a2t5 zv9Hzf8kGB&{X3Z^zsH9-oDAKHA{1~f1;Jw1cHV975tIO72SC)eY>-TE9k;955N|dm z@4*HJe{K#^eOOl~(I)T*Z(*Sjfst6FY7ayxv75l$8LRv_K>a6K+So4Y)}|xVGny8j zp?EOa?V}OYy)zvre!e*UBBE?|aS==nKi(Kj(*+R>jKpVd8~Pe^n~mNoi-qt%7nlz7 zs+@!1;qSUH0YJouIjbblzr%~y^<<77nBD+skV*Wkofm8!PK<{mYU#AMj)?GLk>5{`RzNnW?x|C*K5R z2aGo#b>j`f^oYlC*!Lti|w|3#H z3Zys@?lid0JpS1mJPgn#-UTrQ1So-Czf}A8dP^IzQpo zltKg>z(-&z|E%Z!mC(sEr~GBEo&~uIdzILRT3ZUP7jn?sgJZ)ilZCR znzb{N{n*(LY(;;~X<_s%+-`qrv8{lGPsGT!_m|!3dQAm4wA$FPf-gST9<;BM0e3S- zIKG`7yxR#8!b9p~v6>2hnOG0Zw~yDQ2VVDKf5C_i5DfbaTuG$wWtzDibRC=MVIryC zK2^@GI}FG^v5ljH)p~<(R@n>hlxSNjY5tSi7L$$om@o6uOjp;SAdGeAZ`;{qnOw63 zPo1F0|Fzoj{xSTRnZE&X!z6bKb1zr^kP4sFb+P1S*IWgzKcc^_+2w;~_ zU3rZ)J`dHU7c}@rnRsnQ%&FJoYB1bz~O!OPTr7k$WDEqU~APEBT$f6NN&syjOFyFd`NqGpdZDSJy5U2#Cr z!hA}gGt8hab+h&TFg%s9p>#x5Le&-s# zJ_dPc%zZVxnsHvrR5xiPfgHc=t^tl{feb_vv&;fN-EL^+-OE7K4rAg+*); zzJM-1E;xpg5xl|^egqPw_z0lq!8+6B{b$1vo%O)U$@jWTJ3`xjn2f~UlxR)R50zy6OM(f~i?X=CDQclI0Y}z0t zV&)RrA+H}PQV}uQg%z7u2m8zYQkBiK}=#Ws09ora=xc!fl*Fd`r(e|n1flgoTbH`fBFjMHfM4hV>Mo;WL1V$>dOQCbp+VBOhpGO}sBst9 zU?Hv}=&P7!xE(M$W-333Kx@bdy%`xD1a}WE+-35#P`%4m6SRmxiIcw&jmos7{++`i zBeZob`sVK?V<^`vaWE;ULb7t@y~F6jHVTe0^D|RsZ&0)wA&w4D?ftTG!tze-(1!@a zs^GVU5}afMV&2Rf%hi5bU6b<*rJb*FHmew`c-HZBU?8wJUA;SqXV*0vgdWRDaX{;X zgV@H++ms~S;csn1*MZP@0NnX{ zU)vEeqJnq?-%Ks_4NPL=D&I*Q_RFC38_u3p9^x?1+=aV5ri5P2DvST>!s}CtjPp9* zLan%_zZGzsv(=>nl}NzvdVkk4%gcOSOgCDx@l#LsrM12N^&WJe`Z0rZ`p*jc2<$iK zYcFeRXj*i=d&-*kI7p%E#mGjQ2$9m4Duv+L_$#nWDU9kGW#J=(|RSlSc0j?U$BO6Kd@x++TA*q=p%vC1GrDT3vqUHF^wc_Ta_Y3ehI_c zu3*7oDahWbpzB)Pny*3#RZ@?5gK|wooX3jpZ%#ScfVk`Lwo?U;F&M98Z!!`Zt%DTv zZ-KX`D(zrjT<}4KvzW7^{yMAHz)ceZ>IIo=8Jc#|Z@X$yU2fId4291~B|198E4SWr z(`cYoe1C}azJ*=`XER=SB%N!TgNC9qmFbu+@K@<*ic4!!a0v!2rLX*kCiKg(!$f%E zw08;I^=7LnFI98NkjHgbMyv#tt1%4wM>`W=xnV4^o#Ek=d~D4iBN6+djhPy;fTJh| z(+&kRc+kw7OG-71U39ieXke3c+C6J+i9Zm%7@UCiVpHuTQ=<>XMcUjZ5=jkaQt817 z)g;43y7jkJm|BmGe<$ljX=6M>-Y)^By`EK1^W=y}G5YsG9&DanB#+DL)NaR_;n=df zBHhDNCZX$_dRA@C{{AVr<|<2%XE>>Pf$JooXk3g zSZ`!|lN)-GQM`?cOw{Vn9=#b(+;RXymKE!r97>i9k*cUsn?eSql-C!;PD`V@8U1o6 zGQFEl??VwI>9^2vIro$75*fp=mn-WRmbwVBLhzaqSdc4W#6FL`9%yAY@0YKlU-XQ$ z(~-LECwJ+kMf)WHm7za?--Ng=-*P0JyN|f!P@QI@5-#ggG=+TB|68#1t^pX|EyT}y zL;Ksihwy$#4C!v*ddhu>-0*F=$Zdmh0F#tLpnLil^JEzwEH?bmfop#J#^cOdnTh+j z;pC5{!tpyR3jI3v+0|SR!b&8^Wu1)w2CKS;!rQ26Hl@mXB2=lJ&loOQlJtOBAe9L`IP zP5!(1tmv)}aq|BFM?kp0H~kcK7?y`KZQG-i0N(95D|}yuURtZV4uV&*5g~M`K-~Kf z7J!L!9^$YqC}ui>EO~h|1a>P&yj?9gC+O4r+kyjD2_VvNs0R|ygGxw81rO3-f4IQ^ zIAxRzHDqTwW%@2R7t5bg1P+RRmC^Totf zo_l&8Zm8vuJyf#R-Tz_jdEgKZ?|Nn{MPA32gQ?3~LkH2!=fpCFtkrO{E3C(~Pj9lL z#lAMZ;3myp-~#C@ij2=^_S=K(f8CjVifYqHN4;nH4AsFL-xC5RPfmxQ^TmW48F<~U z=$css-rsg=zVEq6yTR8J(<6UU%_PDYmFX&dwNw0}3C+tlHF@-1_$~TNF3*C%b7 zh+NZjr^+wdprxMGAl36%l$vIq{C~X zFaoK(*50z6A*8`D5zdgkUJ#I2Lq1`b}ZEs0O^Zh!Lbipb4aAt;DXff#x|1GI?+Ke>Rvem~;8f4#v!n z^s%2{ni3ozuaE>Q-0x%vgaus)YE{vOv^uE6De|eP-gN`hT(6%55SU;r=g$aV&E~iT z@;&syqe8x2wW;RKpS*|0F~%2_;-#seIqn3*w*WI9c_F^SF(*Ee`U0Pmrt>Z)hs3Y6 zNe|6+!hv&E3#|U9f5pyFUzKqOJxs&~kGm0Ewy|xSPLO2PsLlKYdRq>IwHq(fF$})O zQk{S#~^q-p}PLEr@}*q5LJ^xdvh5@*Z1xy zffmlJ%58dPKBjzZ-?#$i;R++uypb-FF^FJx2kRhO1bvk^Qw(AN*s*VISqQqje-&t12yRL<0b?S1z4IYDgRxE8 z;_!cxwLZ4s?A_Ywt@Z?~K>$&S)vs832=<}en=A1 zyJ&*Pf1ivh|NhpgO@7Q@%9B=2|4PN)RR`6>DHD8cGWzyp(72!>toGG;yR-HyMY=KSHdUs1xu4Id7WaH+j zDX78_e=o{D2=}!K#Qhf1*B@l=)5rKYTcIu1s0BSYG(+lOkA2 zUF@D@Uh8QtL+yyZ>(PRc^fPh2UqR}{)!o<^DY>GyV2*C?aOUzoj`O_?=N%!t!y124*Um+EuLZt@*2UVA%Mb+M#GNMLAu51J4|;X> zBu^?I@A=F80}9zUCmiAS7WE=+f|%N>C(9s8w@(PxctldAAQaIz%qEEfB}L%y^svPZ z+1cEJZ8cY%5+=7z{e#siyD~(3hGYi&f7)GfgP3x?I&KmB%@<_HZ+5LBP*Pj%iGX(#Ta7 zM@DX1ktI*UoRSd1X}AO~YUEEF>Tn!vyvX(XrVg!@xB+t3N54H1zJqz+C6Xa;`D1_H z{VerxO!S0Vpgw7uF}w!7*!;PWfA35Gd6-e`3M2mX|KlkK`i;1PdmeRp2_l8hhi9IX zVmlIZ&hvC1<@*MrD4o`cnhjQN1h42;P~%b8*!sHhqdq$u3V6I!3%My)%2;-UKLTVm zUeihgl~iWsBWK2MFrqu^Ofex|qSaCd1NSf4ZCQ*;P)*?=y6)7D zC64fjP=BYhnx+;^2`hzn5;5aNi&oKloRqY6VWTm#ImrE)A>`-we-$Tr4xZT4N}B1i zx2P?Bm4SP)jre&BXB=H|R(K9S`K@O^PfyeQ%JD$IYBwaT&^6^VP@@9;7;M;kk7_Tco=#>z93SAy7672)ToP8!fhkzIIM7ur)#8iCH^^K? zd2;1{J}{d~sGn?XtTZLtR_v%m&@ySk1q;s&to9xQ-^W) zNC43h_%&&N{~$FYYz~U_NQmz4!+zEO=-cnc1M1C+3F9b(wfG3EU`WA~ zW?`S&Z&l)8l}GcfAw${yZ1>ERKs{$u+K|sV7!`DJMTXNHt*V9%FBu0 zzqbVujzMiP(wTF2fsZ*T2D?rZ@fNId8?@6Ie`TqEn~X*9&P@ri4{@|L^xL#7l|N_I zVJEP7tGYZ`jhplhfl;G|Wk%J@TllG{MP?8^vQ-X$<%!OcUxOFeOB~nNw=DKk3F>CJ zz-F2(5)6HrieG;Qgt7&xmS3UcQBhY&#wy>hHqADJAPGJ5sPyy3{-aO;B3zS@Csb@k zf1OeK={Kj7clGD8?#(XjF;$wPL*esI`o%V!im`iq}+8HYu9t&p!Pf580CV8wi!Qd$|N3?DF2@_cc4ylnbfx+1gJ zF0Ge1Ne0C`g9IkFkJJ2qAb`*3gj@jDMTk6r3#aVi$W>dC#j+wIZ44LOa!yf0rwK zuUf!yRdpK9%+zh8sScLofT(QB8qLYtF?(x6Kpm>>*_H(;XO{!Dve>&4V&}ciZ6q3n zIQ-V8EE~k~bd@)Lc_^2n15dqq!^zk972%qF=g=YRuOU|?{!pHpkxV+{%sfkFj}k{_ zzyfjqV^9eT@%-%YJq6au(acQae|4H&Z8{s=3L#VE8yRuXE(dQscW+ZdIeOsCum)v9RPK7i{%T z=w+&{fS3+!FcvfXBsV86bX}Ba2SnP-#zw13FI(mkBiLWLIqaou{i_|>e;9h=54SW> z!N=0JZHS{LbF`Mb(?uby<3-T$QMq#=lbiu%Tr!Nm)*iTbNi>iq#`J;-!Mf@4-g2YW zhKbR|^CY0^7SoY-jf}bPvt<=2GXnda2&x~iao3O!T zH&8x#LTfiAqhz1gSx{!af1jw+OvHZiu8gJ)8nK*f*$8~30+$s$@X7?gd2&_Zk#UFH z+wku`Lw6Ei%1L6R%(FCC0Qoj*OMXqw-6AyJu7pGgL|(j18T+&;DctNma2XNRPywTx zU%WM(e<%&DI&SnEZbQRQPcYW9=>PE_KEZ5+ii^d^8v$9vUIsoxf1M$0-F*ML$gMk2 z6%MaYorp+KL-sD!4&|9q_~do_OU`^qFZ06JkF|#Ov2LdgR>6z5Obf($ck7V$QaawK6zjuSn9;fOeAmqlP5;n z22O;Z8g&6G--6tBO94{>o^}sqtn!rb7*59Ol*5S{3dDnXTEeG%#_&*g2-@ts>~PO-XN5@o z86%$Kyw6{ZA6aDR3XO@-3n*&2$h11RplI+*U4=Uzk*i$zU$po&kWyCr}*BOiE>0dNxjFyDKYfTe{@F~ zyo$c)cCFH`w+&*5y0DYm=SMdZv9FXCQ#C6V8eDGV^O&*fZo7GAnqAe-;rr7tUAPDcG~LR$v0G+bUa+h{P% zFF}suUGaiQoQx1`MYpXd9IQhyrUIOL7S(Mz(A{KKRpfyt0XST6ZWyY4m%h`$#d-x= zI>F#lvD1?TVS9yMK4xN?+^7`;uQXB{c}MUFf9`NguEQlXY`jYzCvme+*dglinR#&A)|FQw4ZVhnSGbO=MAIbT32g{X3$F!;DhH|UuQx@ zzAS8o0&IY$m|x0MukNqA6cg5}x%nbli)3fX1krJi{G){6;R{u(et^Lovl&K`<|!RE ze;rM@$u1UReX=Ro=_taDV#7ZC8rOr)B6@(J&oYG3jA-D2k!n-yyM$=cv}!7E?Q2aTB-xn5IIS_hepK+{=jj>5UaF|+1rsTQ zqcOCR7EV<08{s86&@wu!_aE%)4k)d*fBm}c_*z|JMz*TW=JmPy5!s+^?d|?v_>f@4 zHRSdWOi3|u^xCeG7F(iAnL?krL!ax*iNPI|_3$MqhkBh{fQ&dMjgi@)d{HOLyxlE@&AiRQ&6e`gDT zhK3lIDQ-5`DVzF&sqi{Q=!VO?BdRGGcxsjjIy1&BQ45+g#U!|+4!L;yC6aXk;kJi# zz0N4XA1U0+NvS3m1+G3qM%83QNQYPj%J~x&N@1|a-f`Sn$3YkoEWMmEEJ)UDzI z&aXD3t{NIylb)_GZC=3H1U5w~V}qPBz#X89fwxCl>=$3S+9b`q7#mv;nppAaO6>1n z_x3Y*(bdsXE&P6ci!o%}e~`JUFDNop7mqR?p3)P%FM&Pn`dq12zzqK}uTYqV23Y_8 zi_`63GH{1#PLv+en9~109|5SUury&7?GHJh5<2;rj$x-=pWJJO=5E_a(b%SXI+d@ z5;=q5cVwlEyom{osOeMWaq2NmZtw@{K|4u+8?3G}tP9gJ<{OzL%u-Vv!FC4GK;j`V3d%q_8dL zD9zxEkS{R5Dz|;#f9VYbqlUyZo0o1u$)@B)8ZZvu(Oi6Y1I@BiKn7nxMN58kcunOH zTl!wrl7Zn=BG`0i$HRntdgqU5HaE)WTk~B6V=E2wZc#)}tba-kU*r^2rfF2N@AXFS zNc}PjoDaFgp;TIOVWQNy45Atkzi>$v>8oVb)V{>Oen9kSe~)Ht`@TBDJ*0h?Iw8fg zdRS>&?jvxu6&E1qTu=7hGVaRn7ZR<*OHzA$o?RN+H(WeT zxU;_Oa$3O8vWR#Uh~KJxW4S1!MPama^TiHB;UQ>{&3UftL8CMZ8R@nqdP+;o0|bTU z1G;DX#`@uYWfDaA&MT*W>61c=xl2m7&>HS}d9$ldf4??T?YnpDV ze@Cz?Tz!Y-22b7SKWuxa`Y8c&kH|KS#%4Mv2jX&Z$&m@&%+)f)6`=DYD8$)E2n`Lp zyXxn$C>HyB4lMC%$xeuurN?7Dm@;I)fb#lcD<>*@f3LUbSO){83_Qar&lr|A`+=zy zNm4%>E^0>Mc#*LJAG2+ka}Qi|1yVep)(;d4wd-T5+>*>8{+|IoVhF`3Bfr|zZV^sG zOfYL`l)JA&OFGXAA3$1zhQsX*3_x)o7B!$Yt^zV-cs&gEl%hCIO2v_QBJYNg-Cn{+ zKl?$qe}h(wi?e5d`P6Kt85GLS&Jm+(C`H0Ek_km5WyX^44ad*+2&-{G=gj>sv&$e2 z_&TOBk>5M~)vNRz=C=lWj*i7_Yb5m#UzgY?NLz~BBjYD&Q~g#pe?(2eNohsdkx!q| zW8}-fm^6neRz=P{gem!hY7?0U$PA9sc)T;R>+NOrGU~wCS!F)`gM=TTMYN?Gyf%+NUGmY`tw0wCR{=vf6 ztW0^;TtsO)#to_HSCbJ41beOdx&F=Bf80`_HS_4ez9*^rVpPMTJ)*IZ8)ZQQDfcnrNA~(7L-D0iD654o#6crZ?WBIQm{tlxlm1d!TlQ9nwJ-ErL>cIjH7IY z|BClO#3LztS}xUH!D5Q7@qy^k8!@*Pd86c>yzO9Kj?Y3R16|1JV-Nymt|3!Uf801B z?pizecHXt~cpkoQDHgsCq|s^tX)_UaL4Or)FiF-0`n4E3`_lQLSAq!z1k4$_qPgBT z_j+86u$<++vPx>F3_bV;WA@|=D8!I>*`c2Jr{v$9TBXxmQx#&d8M6rpQV|SPH;5Cb z!hW5kubhrjVZ1aAUx)cT*=_Cme;|Rr-i10_fHPQ$G)VA8%HU9s=(EaSWYu~CoI5h< zEtDiB$xiP^KButnge=cH+uLS{^B&A&Befhp{_PL&`*>U?LHCn=Xd7^{mVJsQZJYy7 zG0u^kE2NrP5Zy4ISozF>6pb4X+haeBidbQu1VywR!pCMIttIn>(KxhKfBkm>ZG`CC z(RA88QP;=gX&TnXoeln&A%=CE$V~&}Ts&=wXT^_Q%lUFtK|_%!LpJso1e*xr?&ndR zB{z@{(Xea7C3vDMr93VhFGwz|@J$Mnhd4<5lde(;XXFM|_yG>sCG(m7la~+g^ca&piG%z}S&s|+#D0}x;;WRB<0CJBk zRH#^_D#HeraOr5p#VvNArZ{5__*kB1!hSc$LcGzKQZ&!LN^=SRe|&PJm92N7|KRWy z>YQqssl|vOOaBhyUM>cX*~d*QI;K0Br&JG7?g_jSNq1qh{20X(_P^a5BQ z{z3-4+|zp0qGc=Wr$CJTEeS>XU~45eh*Uk8OXo1wzX6V`? z++mNfoi`&1<{Z=Nf6mC`7-{&-nF@{B2$0__sY)yH?aFU;ap4|<-T|36)2+9;m@@`Q zOKTmjuzT~H0eM1^l*2ZI8ZDfKqE4y%1W@XDb+*f)j&BR*)QT}G<>*L>H5@n^Ijtl^ z+NC+K-}VTIK-@M(VZ=SsIm?Qn=gs^A5F+9?p61nk81B2@e}9K`G+jnO)(>;$%bu3N ztrVYZ=aOH~5%ABht3Kp2GgJ|nd3vXxXzc7yq79K-5c7g>zxmwkPm3Srci^5Vt%0V$ zv@#}_jwrHQ^(Y3%_BrPAd5+aTIJFe-I{M1h9x-bj6hsL8P~a|sd6D0P6x%O#K-7So zCBm6!Sj%?of7yJbo9B#jA~=UOLpCO?%b46r?qa1*G(VG!c~RUPhfMiS&IB`(zL-`q zR+yVzPF=puQV3fq_duzXp%dMo{5?#|Q_5KtC2Ww|fR3M7PuW6?SuWDgJT?01M}tDE z+<~(|h*iAGP|Euc5sE5MqiI4v6KG)rkpA;K{m)j%e`mxkrjj(cT6&Kcw3h34Q;MfI z)VocCBSYOa-;^n8y%|XnIO)+ke5Zi*?Mxg7>U?RJwAHPS5k4-EMR4(NyjtFmAxkRw`JPo7;6E#c60Dnm&IWS}T z;j=VBgXD|hr6>2guEm|e$z?(@zvJOJm+KEq3oiFncbgO4!UESgJ%<>H1j< zpSa-c*LLW*4%R9HM1|q|_CvMRq~`*dN|5XFe_ukR?fhP3IIOFj_${iWFJlwEeues< zoX%xEiz*eewjPaJG3OysDSVccjKVFj>5tiKN(h?N{khhBkz<9K-P1o42M=9nl_oqQ zkvN+Yp&uBeQ*=+#7h{9m?N>=w_p%Stp)05)QE`|zn%wYIUcA^`2q8h%#ZAvQl3n|; ze+(N7_Tz7%;7h&76{J7PaZlLE(k_L;T`qEQHCYPV4cRlzPm;btAmaVXr#Dx?{UxpUm^m?i8y^jmu?mD`l3a%IF z#G&TpCwVEQD-rPu!rVYfV-Bwk9>57qcF*9Ke&K07&oL{B&x~+*Cn31!J(1p4f4Ehf zCAPCP6UQNX6Y{nDCY%W*md+w4G&v479k&3^aa!pUUa?)i^R8t%UC6`}#M_C-E?Cj0 zpT5xY;h~=}jy!x(qq^*LoV_$494$<$KLE=rPAe_)%GFY$8OG>CZakfF=9Nmb+|bo;d72VN9t__V!k*}4fqeQ?ew zrrxmq<3()Fem$Ln(1_61sj?bxa|RwSiT$U;HvAK_)NMgt;eh>YNym(j@17$VR-!IX z8S(^127$DV;3*gM4G*f56rH0tq#p^ju<`=k_t{FhgYF?n@!6E>VF>pZe<19)`XOy6 zJM`T!xbj^(0=V?m@#B5nc}3{&2D?miuA64Q@C-_!mV~#iQ8Aj>C4)R-(tscCv-4oa zMD3qXn!kd=)lMgL^R6`tf-=esdQ;g#i2Kc#FQ5r2XJF#-^(HP3G)%_e?J3TXiZ#O1 zc3nKWo>}O>#f$;udwzkRe|WV{(Htr7TB}=&?z}68H;*O-ZSVh5>`n9(jo2?4h`AI!|~a)W#xw zOJZg_gN+4zM$UHc%E=wz3BW8I*JiaFGBFz0Va5DRM@1DI)5`|Nf2F@s_$W*_lzN3j z{T^FHTbc{hyeBI-MIF=broXJ4gToO`Ch0s|Eobcr+_Rbcr9l+As|k8d2wN9z9nT#! zUz=~M^66GrX2Q17MbIE3mh(!+|FF5NvY;!>N5MzuhqDHCqqE>7QEtyP)NMs9u79#fx|>qQIB>Y!uNLy(ABVlE@+m7nmkbNfP^^#5M~nJZ@0%+!pNIx2_{QDaf=4>6|fEYxUm20s-VzY|Z1t`S2} zLOMik7BMM0TEWhJWCFYQ{6c*Fwgkq%j;g`dwVc0SALl}hJZvG^X`Dxqn5uHrR;ASd zFI>>X4}bf&f8q{?8PsV8fyH&24^c|@mb~4Ckm7DC2pq!6uHE_b*@=!1X>C>rSCSu3 z#m}c736e}C$O8XhxSTcT5|1&{5D5)}k;qVQQ-!9D@2?@WEBN8eJU@hlAO~~BTU<2Q zyHyvIBHB@Sl$&{uWEjW~e?@$7yGA*Zr*+}fXuEFSa4lfo zqv&>Z)+3R^Xs9h!8CN(IBLO4Isof2R`z%$CQU~bApXkBqi>$rdXs>+Fj*xM&seDp^ z$TNSUgS$gO-Rs2CR^dN*^exhMA#=V#JaKkjSZW$XW;hesP6DQBy&D`3-b;)y;qY$A zD}wd~e>8-9sYzxgs4>h#L?1CP1^4Oi-YW!G>y1=r$A?WI+{kf7xx>T7&JQBgwcf&^ zc!sp1=cLU4f#|i5FDc=leQ{dVXZ2RUO}~K9;ky{~?5V4`u6!+W2}9`l{)r>l9(_ki z9j$V^DB!d=&^mODKwXg1t%m+Z_|x6@Td03Ue++8LZt|fnDD+Vh!ErQQ$O@A*BDO6O z@NBy63!ONFahEd51j|Y%emtdt=I!{oa1>P9;P$z_K|M^tTZK@UP_IZ`=-lYm^ZJC@ zScFehP+-PpfE*Yq`JGCNJ?iH0T4tqOoZDUT!-O`23H@Q>7rm?qg;ux3-2y&crQ=D*hHKBe|bv~suu^cp2en)m7>o|pyDQ2N}aBI&%r+Y zvZEh=0%3MAa?Qj0w$xGZYHcXTsfff;ZrHT533V@r7itZ1t-%DJ8>4r5ugJhLUJXzO za#2ub2%`Q*lcKq^)HO++HKm@ud8=_;K5Dm}H{BMG zMt{+TF;&tleJLWCD!|EmDT3-)=xD1`3>jH~} zp)mdV@F&UW4=Hh>LvwvmUdL-rNg@@y3-1R|+|q~|X{Ea0BZbwpQIZ2Q0{SC=13w&| zQ}Gn~4&V=~nMgTacXT?By#3`bfA6@3>|;zEpUe0tY+)^&cgD7r=clCYckU3Muaew8 zLqjaH`M2MQ;*4OhWDn+l;6AM-`~;oc@hr?VJd2;;1ye}07feDf1?8#vW(9b(RLEG7W^?9zr=#M|Nh=`#P8`?T>SuDWzkf(gk^l6&|L3Zcwlh z0Fs_z^aVo>Mw!7~IdZc0e+9sm$QV9}r}~AU!75UAHZ+N!iHn8k9q>9dn(tx6o##$g z|08{RVYA>D17p%ipl2dnju+1xL!^SlPvtIGuSn84H?f@#{)3ZrbTS)$Ok*iom)%Hv zz%rhzEyyPe4yvsuJ)5Ub$bsJeO8Q+M5~qQ|Y>CQ&1NX%?y5VzoR%_e@$Q_t@bCujGWQSGXhE(Gi8$^-qvC<2O9syouL=;G@c z!yD$lx+pka1NQCVj!Cl2OxdN9oRKwZN9h5rUMi%`vy_#gG2m5PJ9;ZEBd z;_msYek;D_I5J?UZW-I_(enb3g>61AWMf&kLL*>qej-F##4(w!&aO`2 zUh+lRm3%_(fr9*qDrl&{&L^X%7}8i+wH9r#rTHCd^OX(v6M%*D9Zp`P^#{8Y#rdRjy%zMV*HRH z`57`sf{;GXcC7T2MuKiEGgN0K*j7)rqN)*N6#OPJ-+|#9aDJW~SOIn-j`G(?ikMr4 z7<~QL&I*Zp9gmFA=`v(<2rvc6LX!1)m_<7Te>!BStXl~D1KnNBVTJNMxyL52W;j%% z7%HY((e`8eD%enh4)8v?nj@~4lYl|@R2SlpOHTruYwlK!%TElGF>MTJ3ElNkos~!ah&o3N3ZB7$`19M&I9#M ze#)8f2YqpR;U2$>{JHdMrCHGs{2?eLZ21-8}r)u zl<7I;>gZ|2$mp?fIscP_cdmqwBqWC$vTk3QWAIA|+fNjW+WISj->ul`M*2$1ZzMq6 zm32b#M*36z7d;{aavT%km|!dE*;CciZT%f*90=j(Uw4(mK8Rl3#ivtembRjpf5#X> zWfVXTw0nl)`#*dB-DxkKU7G{C&q? z58(S5dSitRXhkF~nff!#PQ1w~e_}EZV+#pa-!4hzy9S~2iiORlQES6$`0rt(x;}XJ zPR-iiXqR1=`A(>&n4$Gj8Jri{$tEZaHf}OP(gO=uZ(Desl#F9h=WcztW}@30hkXV_ zXP^OA)fnQL5Ak;*gnWp}z@q+01np;J5wW$L#^FouIgb|LmVtHe>clQmTsh= z5U5TUdCK0-40WdqZg3S!xC~lAzQ>nSaoV`8OkwG0JhjCjBz!9zXKv~LVZJpObJpd? zQEOs620zbE7@*^f51m8L?$3=NKSwpD3NI-uf=!>Rj=}i!i9g$rWJ)?JG}b9#Wl4XW zI1t|fat*SO)H^&qWDq$}e^0y#Se6mo3WP^ciQOy|X?B@1p^!!nJ(Q^_)BIJzE}4aW z9N`l)|DM6x;jHp@mfYZ~ya~~^;`tnwjX)^U@#n%XWsm4*g?(6B&LyE(uGTw`i%-zF6KJzY=e@Io3H`|3MlHw+> zz=?>(b#lFfdfI?JSF0-0IHV>!Xd``i9Nh-Q2M1jc7e)PyR zv%&gdjbjk>ga9Z01K!9QJ7F!m#V#UR62pQ%+0p#}Yf)XPUi02K2Lr@ZQG zTvl&X8y*_n4MC_cA^ViV*gd$^DxPdZzYAMf*qX9teUVg0ILRqn%1l8!G=Gl2b$Ux@ z3vWj#tk|umvjS2BhSg!9KX|xj=Dat1te4CZ0L3;?4;JnQe-5vn%^t!<46KeAUzwv+ zS+VgC7>I7dSwf&q=W$p3U(%d0_j5J{mFP@&cCfv|x&; zM40yPNdUtq;sQwbQ5lUxagl4tMt!cgSCHgdiAQ0It{iw-&aqc^Tdy)n1!3!WZ=ZX( zV&0Wk+q%Cxo8Q*Q0Nmfx5j>aP|KYCLv)MNT+61;1f4S|UuKC*aPHvXzbs+90ZXxc7 zbPf>SWmio)ViDtx$)8uPS{4DO!sJaWXPLJyjHRIBKp&w@c?lKVs$}CbaqX`V?R?CcV=_z7}TYJOOW8ml4lkph9=ZUA%XvyDO=Rd%wCic9LrgF-pR9T{fS5-B|>v8@(|u z$ejG^v#LcKf5yfgnwUC+<0ZO)E-obDyi9zW7o$cDGQTr>zYk5#v?M zTJ{+(j?XDJ=S)+>Gyu_^Y(7y~WzEECf0kpK6G`I%{eVAgvLn<6rcHoJOGf4<>H5+L zJ*(wA^jN4yb!%gjG&&mH5zV%tq1b{sh&_&G%MY->s`81}{Cui;Mh!Rk-#cm6%UHN) zK<7Ccu*@rC+FpRx;Nt~gGCmL9Tf#*kPHoK~D>3C3Av#MN(+)LM_F603=$mhAf3qtg zoto5OoC*w+L_ZI?{B-6w-+FvU2^S>L{p^{y01aXxig7xTLSo_Qafq8<->Cm6lD}o? z*@6k`!!zz7`zKXBo@o7!Ktfry`>C6L{uOb%Q-8o6PQ*IVxc5IjB6g6gv1tE|*6ehZ zb_d%&&?sC#MJ4aKZY;uTeG;K=e>AO-DDb2Q{<7Pl&*JdIFf|TLmjw?T@~lQ1KUbCZ zh3r3~-jfqaYM;-n6G<)kH@e3BucZdL-c1EhI}~YkRA6O@GAk~x9h8@WPM%xUAU1+r zVkZ?vwK;H!cT{%0eCLc#Xqky#J4nb6DY5znMP4GDn0gYAQ^`@_w)|Iwf52gn{Ha8z zD!+m6h1eU1iX`c6ZWdTSte+$YS*8uj;ZE4^Z_F{2D0mETKz9iQd8vIU{Z?IQzlps! zB1%N4AB(%?S0!jMFSm0+)uNp&pf5Qvz#j*N{dp3*=|ed-TvIE$e$SqT-`4|2w!BS& z{x?C{G*W;A7K3uw(d?z8e=Of6<4TnVRzTs1I83y?d-VdGN{`fXKpX$-$h}H^@Q6=+ zeR< zu7OEk!Bw_=h9>_rfh@)ar;dg=Z7HYB@4Be-TSybkohY7F>3@^kfAQ~{D>QAH-4?Y( zwUtWP%!Jd1jHNqWU(S(X@rVa}z?M3LIcg;frO$8EGxD(K@wKw*9*i<978+_p``Fvs zL}CPy;>d8b4?0;rq9|O#TE{}@aq9kt{1H>gMUZDANA~M|;2weu`Tst5PðK6%mE zu;@iKQ6Lk?M``4jfA#W1pXbO+`UFHXH1e&8o=QF=4GNlST_y>XMT_gi=U*>8pJ61q zbXHRA`U=S3S*|CqlS#58-o3RD^KANB7DxB|DJJz%{R{`(g4vHJgre=utFvqQTO(jZWdTV(3T{@|Qz z89YvIW)v>>3Ef(qbZ3&ua--m=nANCf0NapcN;)bu)*;roh4a+WpxBUvz}2u37)431KVBTos#_`BQShRtYcU{mP^VBIx!3NecW&P_i3WN-gd z^heZAf0l}iDX2=N&N~&EaR!hbu3j?a@lfm0uKnI`^S(R4GGPwHsdz%XgUUP2-R}+?iw7Iw=V-0zj!&z31glL;)HHeT zbl}e88au+$Tvgq18u+1IU|4_0(l{JS9a*qke}TR#*_}SvW@t!$(;L&GhbTzf(qMHv zWCI6TM=g_#gr^)4N>(M-1bXhC)g3A#0@pB&6OT!@(Ik5U&^RPWl-H-tc6@Sfd-8bL z(QdhNvVw{Q{l(IaA8%fk1#ph1dLe)-SSeN>AT(3ho$MibII#I-gI06V3mUvt7U3T?Fcwkh#9@oU{ zYN91J22j7fw!m{laXJWa0%jlIdXe$%Q)U&jrie23M*=SchG$rzX~#o5%l0!$S=Qd$ zBevF3=6x|iLL9oN?PxM97RI@T5S-(If4*)Hg;C~*dfBMaz6K1lLt=cz>SWY=fD+E& zmH^r>3rB8J(~J4Kns|Ws?p^?-QLpV)0_lUS;G@jpzuqHGVkRQ!=D-!OE!k&k{p1|^ zx|wO*Wu|iX3@=W5cJlDmmT^ElYMhHUaxlSXQQ-sPgkQ6dvcXk!bSJzL;`X#6fBo@Y z<0kctBHO#0b*ZrgnG<)iYR6%*sW{tk$U%Y|@1u?QKPoA2ytV%8tIcdhS`(dCUyS91 ze{jG{xB(w&R8?1OzuTG!0p+h$A&$Qzr3GJNMv1*0R!67+I6<1LtREvjif%n)6cQjI zCyo!cjJbJBEr!a8w@*?rZ{w}GfAJhG8=ZS)b^@Jj8T`gH;EqVA#bRxUd}A@M=c!D5S3d_4?}tk( zgfV@i%qaOb$=#(>eh3$rNW^_Ie$@4~T-~@4tXK2$y*HmZSJolV27$x8C;$n7JKDwg zPoSI7@bDU3X*`Bm&a_VSy91$wh0R>h;Z93lkk}0e*ZZVqc6v#6Ghu4Alny%(oXeJLb zn0!5*srNzWS3)Ej)E}-(wv(j}B>5%tsM=sAx}bIuY#c2ox!u4!f5=kB*OPm5DnKm% z3GvnT5$LQ1(xuQ@p7lUfur7(4#+a)fyy0N1w%97T6od7lkyL&)Er69BA(TAk8aP@6L0%?bJX#I4vo46F~Mo6vSN&iq3+X*eRt>rs+)f zjmTVf{RX;A2^G2Sy;}Om8SY@sNA{ zy>dtno5QI@eW)D?wX^?It7PnOmQWmT< z`oC>xPuU1c!g=-U%0(!N7hlN@-B7yAmRsu@>sfMkzd zf2yFn82)E}REnLYm`+}N*=(#`;-B>9@lJx{+7`RA736UTp>$NFv?E0}zoBP;nRwM4 z6+$u&e?N?T0PR~B-Zh<}hk*_y`lTtcqnd46V|pl|sUv4ZycTPb0bXl~&Se643o&^; z!&r)9Q>qGCZr(!0E({F8?0ng_bhF66_@Iit=Do+v3U7$7VO67u5w5^`5Da|J1mw+E zLDa^)CNRK_^?g2&-?`EIQ$XTI2i}-H+;C; z)Ib)%w3WeZ63r?`^${JZ*f^8j=cuvj6m|bhbKMR%R|*`@7KT1Ny<>PL&GQBtYh&BV z#j_x{g0ALgFArfYh7YO1Ga>MFnjr?dU|?vzb1 z_Z)$M8%K$3FRi@w+CTGx6<7Kf8pOcCR7t*ZKa>YlMgxu_2ah07fW5-=)mu#o#9aTvov z70E1T7ZZKNkCcwa^`2c`&d+}S`+C9Qp36eOvFjkGW{{QgYm`lO%-D;U&6NPMOFMi$ zP;~r9P7W(?f%;Y!1Baac7EPSrOkTa0v~|gCCyMj_}V+ zOTJg?yEd)Us)HcIyP8Gd9ZeH|C>I*KShuyp;eiiiVWszkSeJ_yylj{Wd# zSgyh|#x1za;lW>2z0rGb1^CFOjYfR{dyIm<{$U0FI%8Qz%ITGLpBl4igJaiaZOck^ z2E`~N69^i?0Zd=Z0ZEr$1sd^dV@ojtB{gF?kowGs7J-gX(d(vvC&ZAs zzQEpT$6q5A$G$!qR~7rjYW}(-VKMG{7SQ}??NQ5BTC&6M#RTZ zuK=l~YWuv&HnM~d>jx_gbS62+xDB1UKskCrMGabsFGZ-v*0KBT#A9<^Pk!r3mz~D) z2Md~CAr7BdJ)&!AQJh5?xYNgofQ>msAvbXmgJYdzxL$5E7Cy+@dsT(?CsCG!+)b0`@eiczdA}qCN z%&%B>4O{8={03?{4#j)Q#Q11BY7kUyG6GZKSzu`AO}m5_IV+NqN}~Wkq;$2t6Q6(W zms+<4zn*0I;zQme(`kKI_<0@Z?z^?m%FUz~AhM)y!$@(Dl|Zs_EBr95=jWK5$tGvu zQtp|o1^7;c{|skcI~M^OQZlGoWdbkMA164#?{S~QN5O(a$B;p>C4Ov1qIv!aXLb2f zPR#TMRlBE;A2#l>WWKBbuFD|@^n61%>a<)1pX72-^%|uybV>6MKg=(3#a;m*iUYpj zx<&GjM|^6MI-RAMDwhz)Ca7SvldRu($=xysz1@V^I+RjzOK36oY)d@ulf{HscImWn z%1=J%P4z=B3aQ}DB3VwORZGc@jaGs$-Bulg1r&QR-F~u~2#i$#17aHTMqK8 zwW%3NTK^{3maKQAslsP`*AEYmTddP|SM=JNX}6%Sf+hBmte#PF4UrrF!9>q@a}E(B ziUH+d{dT5V+7xdHXdT}d_FWoU4M2Jko13_kDf=B|lZ_%6y)$wU4C`mp#N|1Y*Pqry z5qZkj{29W7K{d4iU=pQ;`SOkNm9NLy*M%FMG(_L2T1I9$#mc@5#tkw>ho<1F;r)Nn~U&PMp7EU^R>;K!fp_+FResCOr&} zH0PwRwbIyyBcM}Nt-?1N(!)uZgK?8lIZW1scNjUyS|D5ncry!UMqsnyT`dXEu0zjk zT65XI^9PEhhYXGF648HImyXYB=-oTuSH$LM(b5%c)p>S@D^}G@)-j+W7iEAhETG&A zQKz@>-?m9XNz@3>m#nx$UYq*F`ECoa1PmGO1G>)1e&b(Wm4af>aiPGm5Bey2XoEiK z5gkPPW_A7mbP^qKmQA_*VaBp=%0s@;z)$PK!)~jNFO8y0tR3GL5Eavie^bb-zwBvj z4Nd=bGBy;EBGSMZ1F3(4w4R`c3t^nMmER2ITt*@2pGa`{(|Ij1o?zF>x=EC-9^S%x zg|X2)RBCeP(~o7r=O_Gprz3d(`Z(3OUd_`on@*7wU^Eaf?)n_(Wxayqf*^5~<+HSe zR=XK3F8=Zc`u-iOGEQOv>CeoyO-ktlz4>+i^lD~_{Th+emMx|6mx4(QA21a+KUmwP z@9szEz~9Y)4w59aV_7U?SyxkUn?Ex4>l!Fxcsew-BcG(-nKuhAbTQu(G!b{`HrV^| zMRK`;fMhnyaz9pK`#~gERwZ3#$b7ahU`=-QV2PUT(Tf!b^cTk94bSOIG>9%a+1;

`(_`x33I~}ySP7-bwsaI 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 F{|iCRFeU&1 diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 46ddfd14e8..3065e1172c 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -1086,10 +1086,12 @@ primitives = Map.fromList , prim "set_solver_cache_path" "String -> TopLevel ()" (pureVal set_solver_cache_path) Current - [ "Enable solver result caching if it is not already enabled, open an" - , "LMDB database at the given path, save to that database all results in" - , "the current cache, then use that database as the cache going forward." - , "Requires Python 3 and the Python lmdb library to be installed." + [ "Create a solver result cache at the given path, add to that cache all results" + , "in the currently used solver result cache, if there is one, then use the newly" + , "created cache as the solver result cache going forward. Note that if the" + , "SAW_SOLVER_CACHE_PATH environment variable was set at startup but solver" + , "caching has yet to actually be used, then the value of the environment" + , "variable is ignored." ] , prim "clean_solver_cache" "TopLevel ()" @@ -1104,20 +1106,19 @@ primitives = Map.fromList (pureVal (onSolverCache . printSolverCacheByHex)) Current [ "Print all entries in the solver result cache whose SHA256 hash" - , "keys start with the given string. Providing an empty string results" - , "in all entries in the cache being printed." + , "keys start with the given hex string. Providing an empty string" + , "results in all entries in the cache being printed." ] , prim "print_solver_cache_stats" "TopLevel ()" (pureVal (onSolverCache printSolverCacheStats)) Current [ "Print out statistics about how the solver cache has been used, namely" - , "how many entries are in the cache, whether the cache is being stored" - , "in memory or on disk, how many insertions into the cache have been made" - , "so far this session, how many failed insertion attempts have been made" - , "so far this session, how times cached results have been used so far this" - , "session, and with how many failed attempted usages have occurred so far" - , "this session." ] + , "how many entries are in the cache, how many insertions into the cache have" + , "been made so far this session, how many failed insertion attempts have" + , "been made so far this session, how times cached results have been used so" + , "far this session, and with how many failed attempted usages have occurred so" + , "far this session." ] , prim "test_solver_cache_stats" "Int -> Int -> Int -> Int -> Int -> TopLevel ()" (pureVal test_solver_cache_stats) @@ -1125,10 +1126,9 @@ primitives = Map.fromList [ "Test whether the values of the statistics printed out by" , "print_solver_cache_stats are equal to those given, failing if" , "this does not hold. Specifically, the arguments represent how many" - , "entries are in the cache, whether the cache is being stored on disk," - , "how many insertions into the cache have been made, how many failed" - , "insertion attempts have been made, how times cached results have" - , "been used, and how many failed attempted usages have occurred." ] + , "entries are in the cache, how many insertions into the cache have been made," + , "how many failed insertion attempts have been made, how times cached results" + , "have been used, and how many failed attempted usages have occurred." ] , prim "enable_debug_intrinsics" "TopLevel ()" (pureVal enable_debug_intrinsics) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 3bbb4ff437..6f7efc04f6 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -5,18 +5,25 @@ License : BSD3 Maintainer : m-yac Stability : provisional -This file defines an interface for caching SMT/SAT solver results in memory and -on disk. The interface, as used in 'applyProverToGoal', works as follows: +This file defines an interface for caching SMT/SAT solver results using an LMDB +database. The interface, as used in 'applyProverToGoal', works as follows: 1. An 'SMTQuery' is converted into a string using 'scWriteExternal', and along with any relevant 'SolverBackendVersion's (obtained using 'getSolverBackendVersions' from @SAWScript.SolverVersions@), is then hashed using SHA256 ('mkSolverCacheKey'). -2. The 'SolverCache' contains an 'LMDBOptDatabase' mapping these hashes to - previously obtained results ('solverCacheDB'). If the hash corresponding to - the 'SATQuery' and 'SolverBackendVersion's can be found in the database - ('lookupInSolverCache'), then the corresponding result is used. -3. Otherwise, the 'SATQuery' is dispatched to the requested backend and a +2. The core of the 'SolverCache' is an LMDB database mapping these hashes to + previously obtained results ('solverCacheEnv', 'solverCacheDB'). If this is + the first time solver caching is being used and the `SAW_SOLVER_CACHE_PATH` + environment variable was set at startup, then open an LMDB database at the + specified path and use this database for all subsequent uses of the solver + cache. Note that this only occurs if there have been no prior calls to the + `set_solver_cache_path` command, which immediately opens an LMDB database at + specified path when called. +3. If the hash corresponding to the 'SATQuery' and 'SolverBackendVersion's + can be found in the database ('lookupInSolverCache'), then the corresponding + result is used. +4. Otherwise, the 'SATQuery' is dispatched to the requested backend and a result is obtained. This result is then added to the database using the aforementioned hash as the key ('insertInSolverCache'). @@ -272,8 +279,9 @@ showBackendVersionsWithOptions sep vs opts = -- Solver Cache Keys ----------------------------------------------------------- --- | The key type for 'SolverCache'. Each is a SHA256 hash of a 'SATQuery' and --- a 'Set' of 'SolverBackendVersion's - see 'mkSolverCacheKey' +-- | The key type for 'SolverCache'. Each is a SHA256 hash of a 'SATQuery' (see +-- 'mkSolverCacheKey') along with optional solver version information used only +-- for pretty-printing. data SolverCacheKey = SolverCacheKey { solverCacheKeyVersions :: SolverBackendVersions @@ -289,7 +297,7 @@ instance Show SolverCacheKey where if M.null vs && null opts then "" else " (" ++ showBackendVersionsWithOptions ", " vs opts ++ ")" --- | ... +-- | Make a 'SolverCacheKey' with no version information solverCacheKeyFromHash :: ByteString -> SolverCacheKey solverCacheKeyFromHash = SolverCacheKey M.empty [] @@ -329,10 +337,10 @@ mkSolverCacheKey sc vs opts satq = do -- Solver Cache Values --------------------------------------------------------- --- | The value type for 'SolverCache': a 'Set' of 'SolverBackendVersion's, a --- 'String' representing the solver used, and an optional list of --- counterexamples, represented as pairs of indexes into the list of --- 'satVariables' of an associated 'SATQuery' +-- | The value type for 'SolverCache': solver version information, the timestamp +-- of when the entry was last used, a 'String' representing the solver used, and +-- an optional list of counterexamples, represented as pairs of indexes into the +-- list of 'satVariables' of an associated 'SATQuery' data SolverCacheValue = SolverCacheValue { solverCacheValueVersions :: SolverBackendVersions @@ -359,6 +367,9 @@ instance ToJSON SolverCacheValue where "vs" .= vs <> (if null opts then mempty else "opts" .= opts) <> "nm" .= nm <> maybe mempty (\cexs -> "cexs" .= cexs) mb_cexs <> "t" .= t +-- NOTE: We go through JSON because the `aeson` library gives us much nicer and +-- more customizable encodings than the `serialise` library, and because there +-- is a bijection between JSON and CBOR so we can freely pass between the two instance Serialise SolverCacheValue where encode = encodeValue . toJSON decode = do @@ -393,11 +404,13 @@ fromSolverCacheValue satq (SolverCacheValue _ _ solver_name cexs _) = -- The Solver Cache ------------------------------------------------------------ --- | A 'SolverCacheDB' of cached solver results, a timeout in milliseconds to --- use for all lookups and insertions into the DB, as well as counters for how --- many lookups, failed lookups, insertions, and failed insertions have been --- made (see 'SolverCacheStat'). The latter are represented as an 'IORef' to --- make sure failures are counted correctly. +-- | A 'SolverCache' consists of a 'FilePath' to an LMDB database (which may or +-- may not exist yet), an optional LMDB database at that path (represented as an +-- 'LMDB.Environment' and 'LMDB.Database' once it is created), counters for how +-- many lookups, failed lookups, insertions, and failed insertions have been made +-- (see 'SolverCacheStat'), a map size for the LMDB database, and a timeout to +-- use for database lookups and inserts. Note that the counters are stored in an +-- 'IORef' to make sure failures are counted correctly. data SolverCache = SolverCache { solverCachePath :: FilePath @@ -412,7 +425,9 @@ data SolverCache = data SolverCacheStat = Lookups | FailedLookups | Inserts | FailedInserts deriving (Eq, Ord, Bounded, Enum) --- | ... +-- | Create a 'SolverCache' with the given 'FilePath', but do not yet open an +-- LMDB database at that path (i.e. `solverCacheEnv` and `solverCacheDB` are +-- both set to 'Nothing') lazyOpenSolverCache :: FilePath -> IO SolverCache lazyOpenSolverCache path = do stats <- newIORef $ M.fromList ((,0) <$> [minBound..]) @@ -423,13 +438,17 @@ lazyOpenSolverCache path = do solverCacheMapSize = 4 {- GiB -} * 1073741824, solverCacheTimeout = 1 {- sec -} * 1000000 } --- | ... +-- | Create a 'SolverCache' with the given 'FilePath' and open an LMDB database +-- at that path (i.e. `solverCacheEnv` and `solverCacheDB` are both 'Just') openSolverCache :: FilePath -> IO SolverCache openSolverCache path = do (_, _, cache') <- forceSolverCacheOpened =<< lazyOpenSolverCache path return cache' --- | ... +-- | Ensure that the given 'SolverCache' has opened an LMDB database at its set +-- 'FilePath' - returning either the newly created or previously created +-- 'LMDB.Environment' and 'LMDB.Database', as well as an 'SolverCache' +-- containing both of them forceSolverCacheOpened :: SolverCache -> IO (LMDB.Environment LMDB.ReadWrite, LMDB.Database SolverCacheKey SolverCacheValue, @@ -445,7 +464,11 @@ forceSolverCacheOpened cache@SolverCache{..} = do let cache' = cache { solverCacheEnv = Just env, solverCacheDB = Just db } return (env, db, cache') --- | ... +-- | Try to call 'forceSolverCacheOpened' then try to perform the given +-- 'LMDB.Transaction' on the LMDB database associated to the given +-- 'SolverCache', returning 'Left' if an error or timeout occurred at any point +-- and 'Right' otherwise, as well as the updated 'SolverCache' returned by +-- 'forceSolverCacheOpened' tryTransaction :: (LMDB.Mode tmode, LMDB.SubMode LMDB.ReadWrite tmode) => SolverCache -> (LMDB.Database SolverCacheKey SolverCacheValue -> @@ -460,7 +483,8 @@ tryTransaction cache@SolverCache{..} t = return (Left $ "Failed to open LMDB database: " ++ err, cache) -- | An operation on a 'SolverCache', returning a value of the given type or --- returning an optional default value in the case of no enabled 'SolverCache' +-- an optional default value in the case of no enabled 'SolverCache', as well as +-- an updated 'SolverCache' type SolverCacheOp a = (Maybe a, Options -> SolverCache -> IO (a, SolverCache)) -- | Lookup a 'SolverCacheKey' in the solver result cache From b29790c7cf604f8fdca673e0db843b073d538aa0 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 25 Jul 2023 19:09:39 -0400 Subject: [PATCH 46/51] update solver cache tests, fix but in clean_solver_cache --- intTests/test_solver_cache/test.sh | 37 +++++++++---------- intTests/test_solver_cache/test_basics.saw | 21 +++++------ intTests/test_solver_cache/test_clean.saw | 13 +++---- intTests/test_solver_cache/test_env_var.saw | 9 +---- .../{test_path_ops.saw => test_ops.saw} | 0 .../test_solver_cache/test_path_and_reuse.saw | 12 ++++++ .../test_solver_cache/test_path_first.saw | 12 ------ .../test_solver_cache/test_path_second.saw | 13 ------- src/SAWScript/SolverCache.hs | 4 +- 9 files changed, 47 insertions(+), 74 deletions(-) rename intTests/test_solver_cache/{test_path_ops.saw => test_ops.saw} (100%) create mode 100644 intTests/test_solver_cache/test_path_and_reuse.saw delete mode 100644 intTests/test_solver_cache/test_path_first.saw delete mode 100644 intTests/test_solver_cache/test_path_second.saw diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh index b5a10ecf8a..9baf8ac9bd 100644 --- a/intTests/test_solver_cache/test.sh +++ b/intTests/test_solver_cache/test.sh @@ -1,30 +1,27 @@ set -e # Testing the basic features of the solver cache -$SAW test_basics.saw +SAW_SOLVER_CACHE_PATH="test_solver_cache.cache" $SAW test_basics.saw -# Make sure Python lmdb bindings are installed -pip install lmdb - -# Testing setting a path for the solver cache -$SAW test_path_first.saw -$SAW test_path_second.saw - -# Testing setting the solver cache path through an envionment variable -SAW_SOLVER_CACHE_PATH="test.cache" $SAW test_env_var.saw +# Testing the `set_solver_cache_path` command as well as re-using a cache file +$SAW test_path_and_reuse.saw # Testing cleaning the solver cache -python3 ../../src/SAWScript/SolverCache/lmdb_opt_database.py shell << END -db = LMDBOptDatabase() -db.setPath('test.cache') -for k,v in db.items(): - v_obj = json.loads(v) - if 'SBV' in v_obj['vs']: - v_obj['vs']['SBV'] = '[OLD VERSION]' - db[k] = bytes(json.dumps(v_obj), 'utf-8') +python3 -i ../../saw-remote-api/python/saw_client/solver_cache.py << END +cache = SolverCache("test_solver_cache.cache") +for k,v in cache.items(): + if 'SBV' in v.solver_versions: + v.solver_versions['SBV'] = '[OLD VERSION]' + cache[k] = v END $SAW test_clean.saw -# Clean up -rm -rf test.cache +# Testing that the envionment variable only creates the cache file when needed +rm -rf test_solver_cache.cache +SAW_SOLVER_CACHE_PATH="test_solver_cache.cache" $SAW test_env_var.saw +if [ -d "test_solver_cache.cache" ]; then + echo "FAILURE: Cache file created from SAW_SOLVER_CACHE_PATH when not used"; exit 1 +else + echo "SUCCESS: Cache file not created from SAW_SOLVER_CACHE_PATH when not used" +fi diff --git a/intTests/test_solver_cache/test_basics.saw b/intTests/test_solver_cache/test_basics.saw index ea6c707f43..436d54b759 100644 --- a/intTests/test_solver_cache/test_basics.saw +++ b/intTests/test_solver_cache/test_basics.saw @@ -1,41 +1,38 @@ // Testing the basic features of solver result caching -enable_experimental; -enable_solver_cache; - // The solver cache starts out empty -test_solver_cache_stats 0 false 0 0 0 0; +test_solver_cache_stats 0 0 0 0 0; // The cache should now have one entry and one insertion prove_print z3 {{ \(x:[64]) -> x == x }}; -test_solver_cache_stats 1 false 0 0 1 0; +test_solver_cache_stats 1 0 0 1 0; // Testing that cached results do not depend on variable names - thus, the // cache should now have one more usage, but not a new entry or insertion prove_print z3 {{ \(new_name:[64]) -> new_name == new_name }}; -test_solver_cache_stats 1 false 1 0 1 0; +test_solver_cache_stats 1 1 0 1 0; // Testing that cached results depend on the backend used - thus, the cache // should now have one more entry and one more insertion, but not a new usage prove_print (w4_unint_z3 []) {{ \(x:[64]) -> x == x }}; -test_solver_cache_stats 2 false 1 0 2 0; +test_solver_cache_stats 2 1 0 2 0; // Testing that cached results depend on the options passed to the given // backend - thus, the cache should now have one more entry and one more // insertion, but not a new usage prove_print (w4_unint_z3_using "qfnia" []) {{ \(x:[64]) -> x == x }}; -test_solver_cache_stats 3 false 1 0 3 0; +test_solver_cache_stats 3 1 0 3 0; // Same as the above but for sat results fails (prove_print z3 {{ \(x:[64])(y:[64]) -> x == y }}); -test_solver_cache_stats 4 false 1 0 4 0; +test_solver_cache_stats 4 1 0 4 0; fails (prove_print z3 {{ \(new_name_1:[64])(new_name_2:[64]) -> new_name_1 == new_name_2 }}); -test_solver_cache_stats 4 false 2 0 4 0; +test_solver_cache_stats 4 2 0 4 0; fails (prove_print w4 {{ \(x:[64])(y:[64]) -> x == y }}); -test_solver_cache_stats 5 false 2 0 5 0; +test_solver_cache_stats 5 2 0 5 0; fails (prove_print (w4_unint_z3_using "qfnia" []) {{ \(x:[64])(y:[64]) -> x == y }}); -test_solver_cache_stats 6 false 2 0 6 0; +test_solver_cache_stats 6 2 0 6 0; diff --git a/intTests/test_solver_cache/test_clean.saw b/intTests/test_solver_cache/test_clean.saw index 90447536bc..ff2a1bf47e 100644 --- a/intTests/test_solver_cache/test_clean.saw +++ b/intTests/test_solver_cache/test_clean.saw @@ -1,17 +1,16 @@ // Testing cleaning the solver cache -enable_experimental; -set_solver_cache_path "test.cache"; +set_solver_cache_path "test_solver_cache.cache"; // The cache still has entries from prior runs -test_solver_cache_stats 6 true 0 0 0 0; +test_solver_cache_stats 6 0 0 0 0; // After cleaning, all SBV entries should be removed (see test.sh) clean_solver_cache; -test_solver_cache_stats 4 true 0 0 0 0; +test_solver_cache_stats 4 0 0 0 0; -// After running test_path_ops, we should have all the original entries back, +// After running test_ops, we should have all the original entries back, // as many insertions as there were SBV entries, and as many usages as there // were in test_path_second less the number of SBV entries -include "test_path_ops.saw"; -test_solver_cache_stats 6 true 6 0 2 0; +include "test_ops.saw"; +test_solver_cache_stats 6 6 0 2 0; diff --git a/intTests/test_solver_cache/test_env_var.saw b/intTests/test_solver_cache/test_env_var.saw index 8f807bd5da..7b7a4f4e85 100644 --- a/intTests/test_solver_cache/test_env_var.saw +++ b/intTests/test_solver_cache/test_env_var.saw @@ -1,10 +1,3 @@ // Testing setting the solver cache path through an envionment variable -enable_experimental; - -// The same as test_path_second.saw - -test_solver_cache_stats 6 true 0 0 0 0; - -include "test_path_ops.saw"; -test_solver_cache_stats 6 true 8 0 0 0; +prove_print assume_valid {{ \(x:[64]) -> x == x }}; diff --git a/intTests/test_solver_cache/test_path_ops.saw b/intTests/test_solver_cache/test_ops.saw similarity index 100% rename from intTests/test_solver_cache/test_path_ops.saw rename to intTests/test_solver_cache/test_ops.saw diff --git a/intTests/test_solver_cache/test_path_and_reuse.saw b/intTests/test_solver_cache/test_path_and_reuse.saw new file mode 100644 index 0000000000..5efce48a0a --- /dev/null +++ b/intTests/test_solver_cache/test_path_and_reuse.saw @@ -0,0 +1,12 @@ +// Testing the `set_solver_cache_path` command as well as re-using a cache file + +set_solver_cache_path "test_solver_cache.cache"; + +// The cache still has entries from the last run +test_solver_cache_stats 6 0 0 0 0; + +// After running test_path_ops, we should have the same number of entries, but +// no insertions and and as many usages as there were insertions plus usages +// the first time +include "test_ops.saw"; +test_solver_cache_stats 6 8 0 0 0; diff --git a/intTests/test_solver_cache/test_path_first.saw b/intTests/test_solver_cache/test_path_first.saw deleted file mode 100644 index c5304ca262..0000000000 --- a/intTests/test_solver_cache/test_path_first.saw +++ /dev/null @@ -1,12 +0,0 @@ -// Testing setting a path for the solver cache - the first run - -enable_experimental; -set_solver_cache_path "test.cache"; - -// The solver cache starts out empty -test_solver_cache_stats 0 true 0 0 0 0; - -// After running test_path_ops, we should have as many insertions as we have -// entries in the cache, as well as a couple usages -include "test_path_ops.saw"; -test_solver_cache_stats 6 true 2 0 6 0; diff --git a/intTests/test_solver_cache/test_path_second.saw b/intTests/test_solver_cache/test_path_second.saw deleted file mode 100644 index ff99716333..0000000000 --- a/intTests/test_solver_cache/test_path_second.saw +++ /dev/null @@ -1,13 +0,0 @@ -// Testing setting a path for the solver cache - the second run - -enable_experimental; -set_solver_cache_path "test.cache"; - -// The cache still has entries from the last run -test_solver_cache_stats 6 true 0 0 0 0; - -// After running test_path_ops, we should have the same number of entries, but -// no insertions and and as many usages as there were insertions plus usages -// the first time -include "test_path_ops.saw"; -test_solver_cache_stats 6 true 8 0 0 0; diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 6f7efc04f6..4bf3313104 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -574,8 +574,8 @@ cleanSolverCache curr_base_vs = (Nothing,) $ \opts cache -> do then Just (base_ver, v_ver) else Nothing) known_curr_base_vs vs flt k v (ks, mvs) = let mvs' = mismatched_vs (solverCacheValueVersions v) - in if M.null mvs then (ks, mvs) - else (k:ks, M.union mvs mvs') + in if M.null mvs' then (ks, mvs) + else (k:ks, M.union mvs mvs') (env, db, cache') <- forceSolverCacheOpened cache (ks, mvs) <- LMDB.readOnlyTransaction env $ LMDB.foldrWithKey flt ([], M.empty) db forM_ ks $ \k -> LMDB.transaction env $ LMDB.delete k db From 5f2f2f9e1b66b32b549fd6f04969a69412da6cd4 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 25 Jul 2023 19:36:10 -0400 Subject: [PATCH 47/51] update solver cache testing on CI --- .github/workflows/ci.yml | 4 ---- intTests/test_solver_cache/test.sh | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80f77ac0b6..57f7d3454d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -421,10 +421,6 @@ jobs: java-package: jdk architecture: x64 - - shell: bash - if: "matrix.suite == 'integration_tests'" - run: pip install lmdb - - name: ${{ matrix.suite }} continue-on-error: ${{ matrix.continue-on-error }} shell: bash diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh index 9baf8ac9bd..b8e20f0e29 100644 --- a/intTests/test_solver_cache/test.sh +++ b/intTests/test_solver_cache/test.sh @@ -7,6 +7,7 @@ SAW_SOLVER_CACHE_PATH="test_solver_cache.cache" $SAW test_basics.saw $SAW test_path_and_reuse.saw # Testing cleaning the solver cache +pip install cbor2 python-dateutil lmdb python3 -i ../../saw-remote-api/python/saw_client/solver_cache.py << END cache = SolverCache("test_solver_cache.cache") for k,v in cache.items(): From c28e3405fefeb7465a85e68d90cce95d614548d3 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 25 Jul 2023 19:41:13 -0400 Subject: [PATCH 48/51] improve layout of docstrings for solver_cache_stats commands --- src/SAWScript/Interpreter.hs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index f1a6c458f2..1e25925687 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -1113,22 +1113,24 @@ primitives = Map.fromList , prim "print_solver_cache_stats" "TopLevel ()" (pureVal (onSolverCache printSolverCacheStats)) Current - [ "Print out statistics about how the solver cache has been used, namely" - , "how many entries are in the cache, how many insertions into the cache have" - , "been made so far this session, how many failed insertion attempts have" - , "been made so far this session, how times cached results have been used so" - , "far this session, and with how many failed attempted usages have occurred so" - , "far this session." ] + [ "Print out statistics about how the solver cache has been used, namely:" + , "1. How many entries are in the cache (and where the cache is stored)" + , "2. How many insertions into the cache have been made so far this session" + , "3. How many failed insertion attempts have been made so far this session" + , "4. How times cached results have been used so far this session" + , "5. How many failed attempted usages have occurred so far this session." ] , prim "test_solver_cache_stats" "Int -> Int -> Int -> Int -> Int -> TopLevel ()" (pureVal test_solver_cache_stats) Current [ "Test whether the values of the statistics printed out by" - , "print_solver_cache_stats are equal to those given, failing if" - , "this does not hold. Specifically, the arguments represent how many" - , "entries are in the cache, how many insertions into the cache have been made," - , "how many failed insertion attempts have been made, how times cached results" - , "have been used, and how many failed attempted usages have occurred." ] + , "print_solver_cache_stats are equal to those given, failing if this does not" + , "hold. Specifically, the arguments represent:" + , "1. How many entries are in the cache" + , "2. How many insertions into the cache have been made so far this session" + , "3. How many failed insertion attempts have been made so far this session" + , "4. How times cached results have been used so far this session" + , "5. How many failed attempted usages have occurred so far this session" ] , prim "enable_debug_intrinsics" "TopLevel ()" (pureVal enable_debug_intrinsics) From bc446cf0ce29ad095e44d702ea6264e409337095 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 26 Jul 2023 13:19:18 -0400 Subject: [PATCH 49/51] make SolverCacheOp a datatype for clarity --- src/SAWScript/SolverCache.hs | 36 +++++++++++++++++++++++++----------- src/SAWScript/Value.hs | 6 +++--- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 4bf3313104..09b1448162 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -64,6 +64,8 @@ module SAWScript.SolverCache , lazyOpenSolverCache , openSolverCache , SolverCacheOp + , solverCacheOp + , solverCacheOpDefault , lookupInSolverCache , insertInSolverCache , setSolverCachePath @@ -482,14 +484,26 @@ tryTransaction cache@SolverCache{..} t = Left err -> return (Left $ "Failed to open LMDB database: " ++ err, cache) --- | An operation on a 'SolverCache', returning a value of the given type or --- an optional default value in the case of no enabled 'SolverCache', as well as --- an updated 'SolverCache' -type SolverCacheOp a = (Maybe a, Options -> SolverCache -> IO (a, SolverCache)) +-- | An operation on a 'SolverCache', returning a value of the given type as +-- well as an updated 'SolverCache' ('solverCacheOp'). Additionally, in the case +-- of no enabled solver cache, the operation could either fail or return a +-- default value ('solverCacheOpDefault'). +data SolverCacheOp a = SCOpOrFail (Options -> SolverCache -> IO (a, SolverCache)) + | SCOpOrDefault a (Options -> SolverCache -> IO (a, SolverCache)) + +-- | Get the operation associated to a 'SolverCacheOp' +solverCacheOp :: SolverCacheOp a -> Options -> SolverCache -> IO (a, SolverCache) +solverCacheOp (SCOpOrFail f) = f +solverCacheOp (SCOpOrDefault _ f) = f + +-- | Get the default value associated to a 'SolverCacheOp', if any +solverCacheOpDefault :: SolverCacheOp a -> Maybe a +solverCacheOpDefault (SCOpOrFail _) = Nothing +solverCacheOpDefault (SCOpOrDefault a _) = Just a -- | Lookup a 'SolverCacheKey' in the solver result cache lookupInSolverCache :: SolverCacheKey -> SolverCacheOp (Maybe SolverCacheValue) -lookupInSolverCache k = (Just Nothing,) $ \opts cache@SolverCache{..} -> +lookupInSolverCache k = SCOpOrDefault Nothing $ \opts cache@SolverCache{..} -> getCurrentTime >>= \now -> let upd _ v = Just v { solverCacheValueLastUsed = now } in tryTransaction cache (LMDB.updateLookupWithKey upd k) >>= \case @@ -506,7 +520,7 @@ lookupInSolverCache k = (Just Nothing,) $ \opts cache@SolverCache{..} -> -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () -insertInSolverCache k v = (Just (),) $ \opts cache@SolverCache{..} -> +insertInSolverCache k v = SCOpOrDefault () $ \opts cache@SolverCache{..} -> printOutLn opts Debug ("Caching result: " ++ show k) >> tryTransaction cache (LMDB.insert k v) >>= \case (Right (), cache') -> do @@ -520,7 +534,7 @@ insertInSolverCache k v = (Just (),) $ \opts cache@SolverCache{..} -> -- | Set the 'FilePath' of the solver result cache and save all results cached -- so far setSolverCachePath :: FilePath -> SolverCacheOp () -setSolverCachePath path = (Nothing,) $ \opts cache@SolverCache{..} -> +setSolverCachePath path = SCOpOrFail $ \opts cache@SolverCache{..} -> if path == solverCachePath then do (_, _, cache') <- forceSolverCacheOpened cache @@ -542,7 +556,7 @@ setSolverCachePath path = (Nothing,) $ \opts cache@SolverCache{..} -> -- | Print all entries in the solver result cache whose SHA256 hash keys start -- with the given string printSolverCacheByHex :: String -> SolverCacheOp () -printSolverCacheByHex hex_pref = (Nothing,) $ \opts cache -> do +printSolverCacheByHex hex_pref = SCOpOrFail $ \opts cache -> do (env, db, cache') <- forceSolverCacheOpened cache let flt k v kvs = if hex_pref `isPrefixOf` encodeHex (solverCacheKeyHash k) then (k,v):kvs else kvs @@ -567,7 +581,7 @@ printSolverCacheByHex hex_pref = (Nothing,) $ \opts cache -> do -- | Remove all entries in the solver result cache which have version(s) that -- do not match the current version(s) or are marked as stale cleanSolverCache :: SolverBackendVersions -> SolverCacheOp () -cleanSolverCache curr_base_vs = (Nothing,) $ \opts cache -> do +cleanSolverCache curr_base_vs = SCOpOrFail $ \opts cache -> do let known_curr_base_vs = M.filter isJust curr_base_vs mismatched_vs vs = M.mapMaybe id $ M.intersectionWith (\base_ver v_ver -> if base_ver /= v_ver @@ -592,7 +606,7 @@ cleanSolverCache curr_base_vs = (Nothing,) $ \opts cache -> do -- | Print out statistics about how the solver cache was used printSolverCacheStats :: SolverCacheOp () -printSolverCacheStats = (Nothing,) $ \opts cache@SolverCache{..} -> do +printSolverCacheStats = SCOpOrFail $ \opts cache@SolverCache{..} -> do (env, db, cache') <- forceSolverCacheOpened cache printOutLn opts Info ("== Solver result cache statistics ==") sz <- LMDB.readOnlyTransaction env $ LMDB.size db @@ -615,7 +629,7 @@ printSolverCacheStats = (Nothing,) $ \opts cache@SolverCache{..} -> do -- hold testSolverCacheStats :: Integer -> Integer -> Integer -> Integer -> Integer -> SolverCacheOp () -testSolverCacheStats sz ls ls_f is is_f = (Nothing,) $ \opts cache@SolverCache{..} -> do +testSolverCacheStats sz ls ls_f is is_f = SCOpOrFail $ \opts cache@SolverCache{..} -> do (env, db, cache') <- forceSolverCacheOpened cache sz_actual <- fromIntegral <$> LMDB.readOnlyTransaction env (LMDB.size db) test sz sz_actual "Size of solver cache" diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 7e1e6e43f4..ee00c5e46a 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -748,14 +748,14 @@ recordProof v = -- failing (depending on the first element of the 'SolverCacheOp') if there -- is no enabled 'SolverCache' onSolverCache :: SolverCacheOp a -> TopLevel a -onSolverCache (mb_default, f) = +onSolverCache cacheOp = do opts <- getOptions rw <- getTopLevelRW case rwSolverCache rw of - Just cache -> do (a, cache') <- io $ f opts cache + Just cache -> do (a, cache') <- io $ solverCacheOp cacheOp opts cache putTopLevelRW rw { rwSolverCache = Just cache' } return a - Nothing -> case mb_default of + Nothing -> case solverCacheOpDefault cacheOp of Just a -> return a Nothing -> fail "Solver result cache not enabled!" From 9c5c3949c92d5fb6115bf1760ac2cf4eff3c4bf8 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 26 Jul 2023 13:57:09 -0400 Subject: [PATCH 50/51] resolve some comments from @RyanGlScott --- doc/manual/manual.md | 2 +- doc/manual/manual.pdf | Bin 489215 -> 489203 bytes intTests/test_solver_cache/test.sh | 22 +- saw-remote-api/python/poetry.lock | 313 ++++++++++++------ saw-remote-api/python/pyproject.toml | 3 + .../python/saw_client/solver_cache.py | 11 +- src/SAWScript/SolverCache.hs | 1 - 7 files changed, 230 insertions(+), 122 deletions(-) diff --git a/doc/manual/manual.md b/doc/manual/manual.md index 5947af82f6..8b6c06eab6 100644 --- a/doc/manual/manual.md +++ b/doc/manual/manual.md @@ -1297,7 +1297,7 @@ There are also a number of SAW commands related to solver caching. clear out any old, unusable entries from the solver cache. * `print_solver_cache` prints to the console all entries in the cache whose - SHA256 hashe keys start with the given hex string. Providing an empty string + SHA256 hash keys start with the given hex string. Providing an empty string results in all entries in the cache being printed. * `print_solver_cache_stats` prints to the console statistics including the diff --git a/doc/manual/manual.pdf b/doc/manual/manual.pdf index 1dabdbfdfe1c15f6da6d0470bb1e26e1b7fd6339..cd2df09cb7a8382955773776151297cb050783a7 100644 GIT binary patch delta 6286 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 QoMaFgay~u{eNFQJ0U=ElCIA2c delta 6254 zcmai$1yoeux5p)i?$Du2l#m!=KtiNDg%OYzL277lfJ;bsC?OIeB`w_ygCHS@BcasL z-Q5rWzu*7=fB*O1T5o;U{+_${J!h}8?%8+WyT8eFy~z|*szjq03`)-$$_MM}o2|({bBOL@#P< zh5O_*_YR*p@hAfB=drD8c^43?Z5-h4>5u#&ESk?GE^Vd}pc%0G?#M$@UyTd(sXIz5 z%d5XwC9Ie)X}_2+nFv|F^z?Y*Qb*rRQUt{CmQmA6dLXAXTsIlSFr>pnp2-Z4j%|b|-=bnx7 znY064UcLcY?zAI_5yAAq)!*p&{18+6`nHl36`_Gu;v?mGLq=7O#FR)E$q8`k3#SgM zL-tmiJ5|yW=(I>-{`^akr2{HlveK!6qK_iQ*dc@a!r$Cz!B12HC9X2Aybn~T-9%v8 zr!->li2|04#&=US`-jXLakP-RViFDPrg!4ySX+!0Q3E;1k%#rs^heJWZdI6%G@s?% zxswb=)_7|bM;8E#DNv~A@icSOv(|=nC)?Rq&9TvteP>y>cn%frx4gm)?pZ(i!<$IQ zj8+Jw!VGmeYguN15)p@_MuyB%vdzl zlRX}G^(OOA%sKzs6dPpSw?VrGigb+FHajnkbaXBBegJ<;j*qRwo9v$mtPRKq3} z;>t@q`vKp(+E*jx<`Dl<+Mrcf>p+J{M9MaLVmvSgS@BB?`mY{P^L+M;Xg2Qo9&i#E^CC&cd~8xM9HVQ>*Dbz zma^lDyZ2i^so5k_)$3)lLki(hlxhTU!zd>yCaFxROMQ9Nw9sMC<&y3_^H)5~2;Z(m ze5dU6!(OH38rH+`y~|6xl|@-ve9bjdkL#-?BY(+1VwsN}C9Y!=*?IVRf&AqKGIsf~ z{mc1A23P`7eo&^L=D?-ez#9WV&QmgFNR48HghyDH*M`T}pN9Um4uFgu2eCr5i= zgQY?E7RlJHxHmg_?v%43fuxnAVaV4nl25xI%ITY=`}DJZAR^cy*iOfo4NH-*=l)8y zLb~Q#T+&Tr-B#8#>e7&`mmeqQ8f`YRsRu4H8iMo_f;Ga_G?^(b#3v%SBnr%GirSm` zX0KyDfu`(#SWB*@yvCEp1IR-j6GPP(UVo5Sfk8*sVEo)Q?jcZ4TrO%rKO~YunKed% z;69tr*QPdbrDY!c(AhEHCiK4w_Ts(>1-I8nu6ty9xxU+G>Ip;M{DKd6jB+% z6BQG=+bPkpP zf2gP*hONEPo~>r5U*UeVL>#=)Ij)|2>p4%TTE1bG!S$zN&#<4^SS<8}jueC*2{wmL zg+_3~Z*_WT!wqyx8E{8+Nu@*KR9DsWZf0xdoo5uLX9Sc!Qbo&8;iV--GCEAPgs~8+ zQcPp9w~yn1(&>wa!gZ=frMR*W5g#MT6c0|BRXw-NPcS-alqC+=N4HIG&AHeSeWmJn zYavfjQJntq7T#Mb+@iURTb?V>M_B-6ET1w5r2Ly)DnwJ=^`; z*fg_~r8N=VJ#s}4TX1^M+gZaeWx>&z57%?tGm~6hJKvL84wmtaTV~GHd7h6@PP2EG zaA0e*zfLj{HZ}%8>JG96!B!ueV#I_^4Jh9^t3j8J7`)?)M-y>3bEnn`3f(T&mOZvF|BBRcvsp z2Gs_+n3jE5ISN^tZ=*1QqzzSAn@4@B1Nh#;43oX3^GbNWDsYSIn^#F=$HWWJy8q)N zdw=@?A3##}aN176dBzQTK-KhX_B+enb?tYd`yC|Fw@Q3o(0LVN>bd46qjn_w)Lp|K zJkx%JEpE-OU8-+v zO(%<9S2zw*MzA34l_4*T*6pkffBpP}hs(w8+)IG&ZN1GC;0vE0^#_-XP_|wf1~lR8 z)m=$`%$;MzvjfRR7}11h5l+o# zM*vxbiU%6d2hs`zAuQ+PEdmuS8tE8xI-%z3Trx9mt#??<@tKX8?V_i^+kDK9^-7G= zE5>!w5ybVkQSca~FpIfZgrk>*R>6gp{Xk52k6TM~L;W~xf_gH3yYA!Ttsa-yZNH#L z>Ed0Q{lgnm+9*er-!13%CjeX4kJXaHGa&etCfr-K@Pbq}Eix!S7dCRHgI_tn9YZs= za%ZYlZgK1>3`v;Bs^1N3W#BqJ=uOJYUF{R67A$~;yk2h0XZTr#bcBjNq^_4}d zSkNY-^(?+Pb*P_qu{OwU@{y-jFT>iexIAlc+>EKrMRV`B+WVa-7z^VqEqOL?PT&of z`l9so#6s<~yjoQ-Et|`;kf%9vt`p#vJ#QBCt9j@N+JlX|@1w8nj>~X++t<6qD+RJL zzx>>^lEtXs&$Ij*O)gZHWqNLJ31{PD-e)P^_tPs78xp-L!KX{%;Xgd_)+<<==6Hf% z?Z~q_(*Y06UG*}m3;yL!6<>+pWMw`p>?u^lV8MiSV{6r|=4fl!5r?r};H#S~6 z_#Ni}7R@QSLU^7q){SA`7|xC1-WcAE;n(L0gW@3gqCyfvV#2B7wjidzuj`F%K}y*2 zazYYf3i1kyqJoMN!lDo%d8n|6k|G2u1QimM7lO*muuA=ZQ&Q*cKoY>y2u>5Tz+I`Q z&sq((&)Zx^rbf=&wh`V(ZQci)h>1ijT^e)7dQLxzH&036a+!r#<}?bF+%(Dtw-+(d zw@G$O34)>8WIj3xRRPz&r_e$kUdW!`Ok~VgFbRGL3mM3L%RIOr{v3K+gX{-p>qn4o zaDFApTNDwDu*5>NGzh4zBpXHHj}yGJfH($~Rg%`B$i@kqEyOp1S_MhZEo2!3??4H{ zF@l*IA&n^L(!9Ok$?z~sP#y}oUWB(*A($5hEgCMV^ShQB}fWk#!mG3|AuAo&ss zJ1sc0@*e5v)zQW$rz^LMO)D7oHfEmh74I*Tf@z||>XO6)Zorb6FGI1KZWP6TLL!sx zudOeihQnY9aSWZ>pQY27*+K zumYFSEZ8)h0Z#3VbZWryo@Jg42klTO?GP}dsC|frFcK|+sXIhwCG(jja`A?qOJlATuo6Zzum^k zNGTOoxHBmO?f}C;4{x4LcrqMp<=Gy{LmD?7;eCL781-Ei6162uujyE#Z4ciSkS-@D zkt=P5A*zn7HoyFiuBCSn7&R-sWKXaY7@pqR+61=#=)Yu4GA6`+hxnRCz`p97)PBbg z&~VxfNDd!wYUQ7vnEMaunRncscHVH>HIuo#5;>{i&iXmAy}3Uv^OrcG&FU_Npk&+_ zPL;+~H-Sa7zSb1P{bRc3|K{kCZBVv1BrmBaI(I+N)BOs^m#)WjnP)qSVk#;e^yvaf|feHUSDEa0&fCEPZPZ>!=W!f_CHM## z0KEl)Q;;Doq1ei^w*kg*YB-e^(jJQQVwN#8oET2|0QtND`{tw-u*ES25IEo5rpQT% zOZV_hP&1r-3x(rDz~O@%KT9aJc#rJjj#N1P4wV_w!3W1>mZ>G2NG4D_WHo+A!VgQ* zj`2yd1wr-E_44D3n0Q-Z(zZ*4(1_eTsz6t@`j*G{m7GX&V^$#%E4!}m5xJD|9AQc% zvA4m00v_L0auOe*o>xndww=50CQ05zR&ppUt8qAQ?)-9cwR0SIA+S3tKpwZzX|w)& zVb^yU`cIY&AG%aNC(xbSf2ZYghS`-DBHIJ*lc~^Y+PE^L3Oa#k0bioOG7rn;g(!hy z$-qn^l6H^xHo{TI0$(U2a!*&1{h0>LJT7)dI_@EHHG-pai+u%I=sIqWK(X?+DOiI) z^m!$tVTT$Fuf&qHRC9GR->258QYQaPJ1v^DlCZ9WY#e7o-}rcDm%@lyzj@_|QreUL zsGw%~dafP~RFWT|u+oA;-~^_X8cacXaFV%Vzas7TxY--}g|j4?q3&?z2Z8D##p;+d zi!~zCPzN}ZbBAUFUi~cDWcUlXK)y9V54yUgv~xSF^Px}Hk9;65yd}_Rr#n;y=9B#+ zr1-=MkA=0g8*hewr#D3U z@LRSRR>oiy95=302uiq>ZmaciHfbh?TN8@|{pquYW=kg97 zg*hTO<0Rwc6+7unPxP3_ShIyQc+ihKpgZ8(|3K5PmBN#&8@dp`W*W$h;{7FSy^mnBA3^yW| zjCNzp6~P~r`pgx?$VqMPZsu;bZ#bihVrZvynPrp0cKVrlvn7o&jZ|3EnElCua%#3K zNb!LtIAF17j*DOaX=+&ZbWh4K+I^zOba*Ehr>IA6n)%g$0Rc%J! zR_kKTwwj=4qWNE>XvfJQDQvwjvSnho!B zj^Y5u8B{DL(VlP6+Q$k^3>?v9rFXM;d;17zSX-KR@}glIV;Dq_D}fL3wuXEkqDVvj zCum1;8oJ>BdPq}#<)-hXEO;e^0lU?s`G?;I(=H7~HeVOB|CSK6|CD4XzbU29R=Zn` zSFjuH6C7HQtYr52nIm{;?&NoV0kzV!#(JFvqrmF>dS1=i3F87Upw^x-ThwVjTF$o~ zw`N_lHScYiSKjKf($Bhgnyr_Ho|}f~p_q?j-SoSopAMhdGh|D@Nu-)M$>`{;<8eOD zXoDnKHxH$RjvAG2Vxh&A)*Q(*^hVWcq#e)tR0^Fg5P>s>rJJs3dZo`!+8I%~v~a}l zgY?9diRywr`yr&S9hzH-9`m7i!1&tCUf`XKa1(YCi~Z*}e!Qo{@*KVONi2j7|8V^O z;S{%$sswn9-MeBhcb;?IJxF4i5{t4 zb2vjZ|LO3P)b}1BM!;A3+RlQ;+}13RO@Y<@(Pmy_CFxI@4D!b;nnB&l(T}F{xN%f=GE2s~R57i;>0KWm@iU{Y8}=eq^4dTGfbsJKbu#U8e4S<&B6L zE$n4zr^+L>-y3rIVrQMvjA0P^ow9T_2XxuFXNZ3l`i=d9^%WoiT1L05x7DRHPS9GD z-$ArnyTBaPnP)twoI5zf z`^q;&ceLTn?_~(1GxC!8HrMxurvDS(4qxF4?82`t_?b-ml5nd5h7Jj#^B1ajWvH_w zhq_+OL3YzmH2hL6yg=%IKiPYayvEs%RohAJ S^8&FFia?3DxD>UOi2e&TNgcod diff --git a/intTests/test_solver_cache/test.sh b/intTests/test_solver_cache/test.sh index b8e20f0e29..6364ddfc0f 100644 --- a/intTests/test_solver_cache/test.sh +++ b/intTests/test_solver_cache/test.sh @@ -6,14 +6,20 @@ SAW_SOLVER_CACHE_PATH="test_solver_cache.cache" $SAW test_basics.saw # Testing the `set_solver_cache_path` command as well as re-using a cache file $SAW test_path_and_reuse.saw -# Testing cleaning the solver cache -pip install cbor2 python-dateutil lmdb -python3 -i ../../saw-remote-api/python/saw_client/solver_cache.py << END -cache = SolverCache("test_solver_cache.cache") -for k,v in cache.items(): - if 'SBV' in v.solver_versions: - v.solver_versions['SBV'] = '[OLD VERSION]' - cache[k] = v +# Testing the `clean_solver_cache` command by manually altering the version +# string of all SBV entries in the database, then running `clean_solver_cache` +pip install cbor2 lmdb +python3 -m lmdb -e test_solver_cache.cache shell << END +import cbor2 +with ENV.begin(write=True) as txn: + for k_enc, v_enc in txn.cursor(): + k, v = cbor2.loads(k_enc), cbor2.loads(v_enc) + if 'vs' in v and 'SBV' in v['vs']: + v['vs']['SBV'] = '[OLD VERSION]' + txn.put(k_enc, cbor2.dumps(v)) + print(f'Altered {k.hex()} {v}') + else: + print(f'Keeping {k.hex()} {v}') END $SAW test_clean.saw diff --git a/saw-remote-api/python/poetry.lock b/saw-remote-api/python/poetry.lock index a73e6b7303..0e385103c9 100644 --- a/saw-remote-api/python/poetry.lock +++ b/saw-remote-api/python/poetry.lock @@ -25,6 +25,55 @@ files = [ {file = "BitVector-3.5.0.tar.gz", hash = "sha256:cac2fbccf11e325115827ed7be03e5fd62615227b0bbf3fa5a18a842a221839c"}, ] +[[package]] +name = "cbor2" +version = "5.4.6" +description = "CBOR (de)serializer with extensive tag support" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cbor2-5.4.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:309fffbb7f561d67f02095d4b9657b73c9220558701c997e9bfcfbca2696e927"}, + {file = "cbor2-5.4.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ff95b33e5482313a74648ca3620c9328e9f30ecfa034df040b828e476597d352"}, + {file = "cbor2-5.4.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9eb582fce972f0fa429d8159b7891ff8deccb7affc4995090afc61ce0d328a"}, + {file = "cbor2-5.4.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3950be57a1698086cf26d8710b4e5a637b65133c5b1f9eec23967d4089d8cfed"}, + {file = "cbor2-5.4.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78304df140b9e13b93bcbb2aecee64c9aaa9f1cadbd45f043b5e7b93cc2f21a2"}, + {file = "cbor2-5.4.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e73ca40dd3c7210ff776acff9869ddc9ff67bae7c425b58e5715dcf55275163f"}, + {file = "cbor2-5.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:0b956f19e93ba3180c336282cd1b6665631f2d3a196a9c19b29a833bf979e7a4"}, + {file = "cbor2-5.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c12c0ab78f5bc290b08a79152a8621822415836a86f8f4b50dadba371736fda"}, + {file = "cbor2-5.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3545b16f9f0d5f34d4c99052829c3726020a07be34c99c250d0df87418f02954"}, + {file = "cbor2-5.4.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24144822f8d2b0156f4cda9427f071f969c18683ffed39663dc86bc0a75ae4dd"}, + {file = "cbor2-5.4.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1835536e76ea16e88c934aac5e369ba9f93d495b01e5fa2d93f0b4986b89146d"}, + {file = "cbor2-5.4.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:39452c799453f5bf33281ffc0752c620b8bfa0b7c13070b87d370257a1311976"}, + {file = "cbor2-5.4.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3316f09a77af85e7772ecfdd693b0f450678a60b1aee641bac319289757e3fa0"}, + {file = "cbor2-5.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:456cdff668a50a52fdb8aa6d0742511e43ed46d6a5b463dba80a5a720fa0d320"}, + {file = "cbor2-5.4.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9394ca49ecdf0957924e45d09a4026482d184a465a047f60c4044eb464c43de9"}, + {file = "cbor2-5.4.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56dfa030cd3d67e5b6701d3067923f2f61536a8ffb1b45be14775d1e866b59ae"}, + {file = "cbor2-5.4.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5094562dfe3e5583202b93ef7ca5082c2ba5571accb2c4412d27b7d0ba8a563"}, + {file = "cbor2-5.4.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:94f844d0e232aca061a86dd6ff191e47ba0389ddd34acb784ad9a41594dc99a4"}, + {file = "cbor2-5.4.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7bbd3470eb685325398023e335be896b74f61b014896604ed45049a7b7b6d8ac"}, + {file = "cbor2-5.4.6-cp37-cp37m-win_amd64.whl", hash = "sha256:0bd12c54a48949d11f5ffc2fa27f5df1b4754111f5207453e5fae3512ebb3cab"}, + {file = "cbor2-5.4.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2984a488f350aee1d54fa9cb8c6a3c1f1f5b268abbc91161e47185de4d829f3"}, + {file = "cbor2-5.4.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c285a2cb2c04004bfead93df89d92a0cef1874ad337d0cb5ea53c2c31e97bfdb"}, + {file = "cbor2-5.4.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6709d97695205cd08255363b54afa035306d5302b7b5e38308c8ff5a47e60f2a"}, + {file = "cbor2-5.4.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96087fa5336ebfc94465c0768cd5de0fcf9af3840d2cf0ce32f5767855f1a293"}, + {file = "cbor2-5.4.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0d2b926b024d3a1549b819bc82fdc387062bbd977b0299dd5fa5e0ea3267b98b"}, + {file = "cbor2-5.4.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6e1b5aee920b6a2f737aa12e2b54de3826b09f885a7ce402db84216343368140"}, + {file = "cbor2-5.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:79e048e623846d60d735bb350263e8fdd36cb6195d7f1a2b57eacd573d9c0b33"}, + {file = "cbor2-5.4.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80ac8ba450c7a41c5afe5f7e503d3092442ed75393e1de162b0bf0d97edf7c7f"}, + {file = "cbor2-5.4.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ce1a2c272ba8523a55ea2f1d66e3464e89fa0e37c9a3d786a919fe64e68dbd7"}, + {file = "cbor2-5.4.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1618d16e310f7ffed141762b0ff5d8bb6b53ad449406115cc465bf04213cefcf"}, + {file = "cbor2-5.4.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bbbdb2e3ef274865dc3f279aae109b5d94f4654aea3c72c479fb37e4a1e7ed7"}, + {file = "cbor2-5.4.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f9c702bee2954fffdfa3de95a5af1a6b1c5f155e39490353d5654d83bb05bb9"}, + {file = "cbor2-5.4.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b9f3924da0e460a93b3674c7e71020dd6c9e9f17400a34e52a88c0af2dcd2aa"}, + {file = "cbor2-5.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:d54bd840b4fe34f097b8665fc0692c7dd175349e53976be6c5de4433b970daa4"}, + {file = "cbor2-5.4.6-py3-none-any.whl", hash = "sha256:181ac494091d1f9c5bb373cd85514ce1eb967a8cf3ec298e8dfa8878aa823956"}, + {file = "cbor2-5.4.6.tar.gz", hash = "sha256:b893500db0fe033e570c3adc956af6eefc57e280026bd2d86fd53da9f1e594d7"}, +] + +[package.extras] +doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["pytest", "pytest-cov"] + [[package]] name = "certifi" version = "2023.7.22" @@ -38,99 +87,86 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.0.1" +version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = "*" +python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.0.1.tar.gz", hash = "sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-win32.whl", hash = "sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b"}, - {file = "charset_normalizer-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-win32.whl", hash = "sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3"}, - {file = "charset_normalizer-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-win32.whl", hash = "sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41"}, - {file = "charset_normalizer-3.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154"}, - {file = "charset_normalizer-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-win32.whl", hash = "sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e"}, - {file = "charset_normalizer-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-win32.whl", hash = "sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8"}, - {file = "charset_normalizer-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59"}, - {file = "charset_normalizer-3.0.1-py3-none-any.whl", hash = "sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24"}, + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] [[package]] @@ -161,6 +197,45 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "lmdb" +version = "1.4.1" +description = "Universal Python binding for the LMDB 'Lightning' Database" +optional = false +python-versions = "*" +files = [ + {file = "lmdb-1.4.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:dcdbe27f75da9b8f58815c6ac9a1f8fa2d7a8d42abc22abb664e089002d5ffa4"}, + {file = "lmdb-1.4.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:032ce6f490caedbec642fc0a79114475e8520d1bf1e1465c6a12b8e5fe39022f"}, + {file = "lmdb-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13c5c8504d419039d6617cee24941e420d648a5b15c4b21e6491821400e5750f"}, + {file = "lmdb-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f8018a947608c4be0dc885c90f477a600be1b71285059a9c68280d36b3fb29b"}, + {file = "lmdb-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:360ac42a8772f571fdd01156e0466d6be52eea1140556a138281b7c887916ae2"}, + {file = "lmdb-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f683f3d9a1771f21a7788a9be98fae9f3ce13cb8d549d6074d0402f284572458"}, + {file = "lmdb-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73332a830c72d76d57744cd2b29eca2c258bc406273ca4ee07dc9e48ae84d712"}, + {file = "lmdb-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c178c5134e942256a830b0bca7bb052d3d7c645b4b8759d720ab49ec36b3aae"}, + {file = "lmdb-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:12047c239ab6ccbbc9db99277aabcfe1c15b1cfc9ea33b92ab30ddd6f0823a10"}, + {file = "lmdb-1.4.1-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:91930a2a7eb9acc4d687f9067d6f9ec83c9673bbee55823badbbee2f9a3e9970"}, + {file = "lmdb-1.4.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:1b106eb7a23b6a224bc7dfe2bd5a34c84973dda039965ae99106e10d22833dd9"}, + {file = "lmdb-1.4.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d7779ccfacd5f4c62f28485dd2427b54d19dd7016000e6237816a3750287a82"}, + {file = "lmdb-1.4.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c1f1eff7ae8d8d534309f05e274fd646dd1d4abf5157c59db59a54a55463371"}, + {file = "lmdb-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b6354df94d241e8c0158f716902224109a5f3f7ed9a24447a25f968427f61d77"}, + {file = "lmdb-1.4.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:64cf7470edfc45ff0369956e40a0784b5225097569299b91f893bd50fa336f52"}, + {file = "lmdb-1.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3c15d344731507fcfddb911a86d325e867c5574751af28591e82ecf21aad1e5"}, + {file = "lmdb-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:342550b86bb6275bfb89dbde9e48385da51d57124433bd464cd7681d0702f566"}, + {file = "lmdb-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:3a99a3859427fbc273ae1e932b3e7da946089757e74a05a24a19f5c4a1aba933"}, + {file = "lmdb-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f71da9bd33fd17c9cdbe2bd4ce87f4b36b8f044927df4220bec4b03f209c78a2"}, + {file = "lmdb-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e9ff50ad20d890bc63524230237a61b6eb3be96ad6a6ac475e8ba1a1f2c751f"}, + {file = "lmdb-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81abf9475a62b7ced1ac0352967106b7ed1ac5d1c1a0d23ed24abe55a28f9884"}, + {file = "lmdb-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:c9fa31743b447a3fbbbdaefc858de1c761568d855155dec54d5ad490f88856b6"}, + {file = "lmdb-1.4.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:26ef8fa7bd34a64f78f5e16fa9bcce0fe2ad682dd26ef078f95a8847dacb1171"}, + {file = "lmdb-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f5dc8a335f7925fd667d62a5e43bed3aa35959b32b233fe0112a6ef02e07877"}, + {file = "lmdb-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ba5d78b0ff130b38a56b7161ceb7e27ba4364d827d2bbb251c24b06c28c64cd"}, + {file = "lmdb-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3b84f6a349ed1bd3fa4e6c3c6b711d0389cc8d9206733cb92feffaf102998e0c"}, + {file = "lmdb-1.4.1-pp27-pypy_73-macosx_10_7_x86_64.whl", hash = "sha256:a428e6b0e298290b91b7d0ce409f595c2c9027d7f2076c39ba006290b90d14cc"}, + {file = "lmdb-1.4.1-pp27-pypy_73-win_amd64.whl", hash = "sha256:885d3f3bf51b9167d368e37b1f1277eabf595dceefd69a489bd81c1ffd3d8ffd"}, + {file = "lmdb-1.4.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4bd8e49d5209c652b2caa18a3a4c30524025d7868d34b7bb249c42f7997da240"}, + {file = "lmdb-1.4.1.tar.gz", hash = "sha256:1f4c76af24e907593487c904ef5eba1993beb38ed385af82adb25a858f2d658d"}, +] + [[package]] name = "mypy" version = "0.991" @@ -213,15 +288,29 @@ reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + [[package]] name = "requests" version = "2.31.0" @@ -243,6 +332,17 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -256,32 +356,33 @@ files = [ [[package]] name = "typing-extensions" -version = "4.4.0" +version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, - {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, ] [[package]] name = "urllib3" -version = "1.26.14" +version = "2.0.4" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7" files = [ - {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"}, - {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"}, + {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"}, + {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "e71c605ccdce9858ca305335b89ecee016b5e93f9cad94d1ffd81c4335a58b61" +content-hash = "10ff740f6f3d75ad166908c0eb46e73ed65a13af67879ff507c95089ed103e0c" diff --git a/saw-remote-api/python/pyproject.toml b/saw-remote-api/python/pyproject.toml index 2a425480c8..7677b432bd 100644 --- a/saw-remote-api/python/pyproject.toml +++ b/saw-remote-api/python/pyproject.toml @@ -16,6 +16,9 @@ requests = "^2.25.1" BitVector = "^3.4.9" cryptol = "3.0.0" # { path = "../../deps/cryptol/cryptol-remote-api/python/", develop = true } argo-client = "0.0.11" +lmdb = "^1.4.1" +cbor2 = "^5.4.6" +python-dateutil = "^2.8.2" [tool.poetry.dev-dependencies] mypy = "^0.991" diff --git a/saw-remote-api/python/saw_client/solver_cache.py b/saw-remote-api/python/saw_client/solver_cache.py index ed6797b692..12f66e61fe 100644 --- a/saw-remote-api/python/saw_client/solver_cache.py +++ b/saw-remote-api/python/saw_client/solver_cache.py @@ -1,11 +1,10 @@ -import cbor2 +import cbor2 # type: ignore from collections.abc import MutableMapping from dataclasses import dataclass -from datetime import datetime +from datetime import datetime, timezone import dateutil.parser -import lmdb +import lmdb # type: ignore import os -import pytz from typing import Dict, List, Optional, Tuple, Union from typing_extensions import Literal @@ -65,7 +64,7 @@ def solver_cache_entry_encoder(encoder, entry): if entry.counterexamples is not None: obj['cexs'] = entry.counterexamples obj['nm'] = entry.solver_name if len(entry.solver_options) > 0: obj['opts'] = entry.solver_options - obj['t'] = entry.timestamp.astimezone(pytz.utc).isoformat('T').replace('+00:00', 'Z') + obj['t'] = entry.timestamp.astimezone(timezone.utc).isoformat('T').replace('+00:00', 'Z') obj['vs'] = entry.solver_versions return encoder.encode(obj) @@ -74,7 +73,7 @@ class SolverCache(MutableMapping): as a `MutableMapping` from a 256-bit `bytes` keys to `SolverCacheEntry` values, with LMDB as the backend.""" - def __init__(self, path : str = None): + def __init__(self, path : Optional[str] = None): """Immediately open a solver cache at the given path, or if no path is given, at the path specified by the current value of the `SAW_SOLVER_CACHE_PATH` environment variable. If neither the former is diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 09b1448162..92d208be32 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -576,7 +576,6 @@ printSolverCacheByHex hex_pref = SCOpOrFail $ \opts cache -> do printOutLn opts Info $ "- Versions: " ++ vs_str printOutLn opts Info $ "- Last used: " ++ show t ++ "\n" return ((), cache') - where -- | Remove all entries in the solver result cache which have version(s) that -- do not match the current version(s) or are marked as stale From a960c3665db04562d30b9160682bcef359cb5fff Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Wed, 26 Jul 2023 15:11:54 -0400 Subject: [PATCH 51/51] move JSON instances and add associated docs to FiniteValue.hs --- saw-core/saw-core.cabal | 1 + saw-core/src/Verifier/SAW/FiniteValue.hs | 39 ++++++++++++++++++++++-- src/SAWScript/SolverCache.hs | 29 +----------------- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/saw-core/saw-core.cabal b/saw-core/saw-core.cabal index 9e6d9d77d9..f5be965b28 100644 --- a/saw-core/saw-core.cabal +++ b/saw-core/saw-core.cabal @@ -25,6 +25,7 @@ library build-depends: base >= 4.8, + aeson >= 2.0 && < 2.2, array, bytestring, containers, diff --git a/saw-core/src/Verifier/SAW/FiniteValue.hs b/saw-core/src/Verifier/SAW/FiniteValue.hs index 7a0c9c85dc..3f7db224a0 100644 --- a/saw-core/src/Verifier/SAW/FiniteValue.hs +++ b/saw-core/src/Verifier/SAW/FiniteValue.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveGeneric #-} {- | Module : Verifier.SAW.FiniteValue @@ -15,6 +16,7 @@ import Control.Applicative import Data.Traversable #endif +import GHC.Generics (Generic) import Control.Monad (mzero) import Control.Monad.Trans (lift) import Control.Monad.Trans.Maybe @@ -27,6 +29,9 @@ import Numeric.Natural (Natural) import Prettyprinter hiding (Doc) +import Data.Aeson ( FromJSON(..), ToJSON(..) ) +import qualified Data.Aeson as JSON + import qualified Verifier.SAW.Recognizer as R import Verifier.SAW.SharedTerm import Verifier.SAW.TypedAST @@ -50,6 +55,9 @@ data FiniteValue deriving Eq -- | First-order types that can be encoded in an SMT solver. +-- NB: The JSON encoding of this type, used for saw-script solver result caching, +-- assumes constructor names and argument orders will not change (though the +-- order and number of constructors may change) - see 'firstOrderJSONOptions' data FirstOrderType = FOTBit | FOTInt @@ -58,9 +66,12 @@ data FirstOrderType | FOTArray FirstOrderType FirstOrderType | FOTTuple [FirstOrderType] | FOTRec (Map FieldName FirstOrderType) - deriving (Eq, Show) + deriving (Eq, Show, Generic) -- | Values inhabiting those first-order types. +-- NB: The JSON encoding of this type, used for saw-script solver result caching, +-- assumes constructor names and argument orders will not change (though the +-- order and number of constructors may change) - see 'firstOrderJSONOptions' data FirstOrderValue = FOVBit Bool | FOVInt Integer @@ -70,7 +81,7 @@ data FirstOrderValue | FOVArray FirstOrderType FirstOrderType | FOVTuple [FirstOrderValue] | FOVRec (Map FieldName FirstOrderValue) - deriving Eq + deriving (Eq, Generic) toFirstOrderType :: FiniteType -> FirstOrderType toFirstOrderType ft = @@ -136,6 +147,30 @@ ppFirstOrderValue opts = loop FOVRec xs -> braces (sep (punctuate comma (map ppField (Map.toList xs)))) where ppField (f,x) = pretty f <+> pretty '=' <+> loop x +-- | The options for JSON-serializing 'FirstOrderType's and 'FirstOrderValue's: +-- remove the @FOT@/@FOV@ prefixes and encode the different constructors as +-- two-element arrays. Thus, this encoding assumes constructor names and +-- argument orders will not change (though the order and number of constructors +-- may change). +firstOrderJSONOptions :: JSON.Options +firstOrderJSONOptions = + JSON.defaultOptions { JSON.sumEncoding = JSON.TwoElemArray + , JSON.constructorTagModifier = dropFO } + where dropFO ('F':'O':tv:cs) | tv `elem` ['T', 'V'] = cs + dropFO cs = cs + +instance FromJSON FirstOrderType where + parseJSON = JSON.genericParseJSON firstOrderJSONOptions +instance FromJSON FirstOrderValue where + parseJSON = JSON.genericParseJSON firstOrderJSONOptions + +instance ToJSON FirstOrderType where + toJSON = JSON.genericToJSON firstOrderJSONOptions + toEncoding = JSON.genericToEncoding firstOrderJSONOptions +instance ToJSON FirstOrderValue where + toJSON = JSON.genericToJSON firstOrderJSONOptions + toEncoding = JSON.genericToEncoding firstOrderJSONOptions + -- | Smart constructor fvVec :: FiniteType -> [FiniteValue] -> FiniteValue diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 92d208be32..bf1310b450 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -46,8 +46,6 @@ were obtained by the backends in the first place). {-# LANGUAGE TupleSections #-} {-# LANGUAGE ViewPatterns #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - module SAWScript.SolverCache ( SolverBackend(..) , allBackends @@ -121,32 +119,7 @@ import SAWScript.Options import SAWScript.Proof --- Orphan Instances and Helper Functions --------------------------------------- - -deriving instance Generic FirstOrderType -deriving instance Generic FirstOrderValue - --- | The options for JSON-serializing 'FirstOrderType's and 'FirstOrderValue's: --- remove the @FOV@/@FOT@ prefixes and encode the different constructors as --- two-element arrays. -firstOrderJSONOptions :: JSON.Options -firstOrderJSONOptions = - JSON.defaultOptions { JSON.sumEncoding = JSON.TwoElemArray - , JSON.constructorTagModifier = dropFO } - where dropFO ('F':'O':tv:cs) | tv `elem` ['T', 'V'] = cs - dropFO cs = cs - -instance FromJSON FirstOrderType where - parseJSON = JSON.genericParseJSON firstOrderJSONOptions -instance FromJSON FirstOrderValue where - parseJSON = JSON.genericParseJSON firstOrderJSONOptions - -instance ToJSON FirstOrderType where - toJSON = JSON.genericToJSON firstOrderJSONOptions - toEncoding = JSON.genericToEncoding firstOrderJSONOptions -instance ToJSON FirstOrderValue where - toJSON = JSON.genericToJSON firstOrderJSONOptions - toEncoding = JSON.genericToEncoding firstOrderJSONOptions +-- Helper Functions ------------------------------------------------------------ -- | Run the given IO action, but if the given 'timeout' (in microseconds) is -- reached or the action encounters any 'SomeException', 'Left' is returned

|zxk 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 From 7721cfb83450e31feaf15c1533dccf848d481ae3 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 18 Jul 2023 10:50:30 -0400 Subject: [PATCH 42/51] fix/improve getSolverVersion for yices and others --- src/SAWScript/SolverCache.hs | 6 ++---- src/SAWScript/SolverVersions.hs | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index ea565b25e9..9cbc5ad30c 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -73,7 +73,7 @@ import System.Timeout (timeout) import GHC.Generics (Generic) import Data.IORef (IORef, newIORef, modifyIORef, readIORef) import Data.Tuple.Extra (first, firstM, both) -import Data.List (isPrefixOf, elemIndex, intercalate) +import Data.List (elemIndex, intercalate) import Data.Maybe (fromMaybe, isJust) import Data.Functor ((<&>)) @@ -227,9 +227,7 @@ type SolverBackendVersions = Map SolverBackend (Maybe String) -- | Pretty-print a 'SolverBackend' with its version 'String' showSolverBackendVersion :: SolverBackend -> Maybe String -> [String] -> String showSolverBackendVersion backend (Just v_str) opt_words = - if show backend `isPrefixOf` v_str - then unwords $ v_str : opt_words - else unwords $ show backend : v_str : opt_words + unwords $ show backend : v_str : opt_words showSolverBackendVersion backend Nothing opt_words = showSolverBackendVersion backend (Just "[unknown version]") opt_words diff --git a/src/SAWScript/SolverVersions.hs b/src/SAWScript/SolverVersions.hs index 1e58bc8ab2..cb674dabb2 100644 --- a/src/SAWScript/SolverVersions.hs +++ b/src/SAWScript/SolverVersions.hs @@ -12,7 +12,7 @@ Stability : provisional module SAWScript.SolverVersions where -import Control.Exception +import Control.Exception (SomeException, try) import System.Process (readProcessWithExitCode) import System.Exit (ExitCode(..)) @@ -29,15 +29,24 @@ import GitRev getSolverVersion :: SBV.Solver -> IO (Maybe String) getSolverVersion s = let s' = SBV.solver $ SBV.defaultSolverConfig s - args = case SBV.name s' of - -- n.b. abc will return a non-zero exit code if asked - -- for command usage. - SBV.ABC -> ["s", "-q", "version;quit"] - _ -> ["--version"] + (args, pref) = case SBV.name s' of + -- n.b. abc will return a non-zero exit code if asked for command usage. + SBV.ABC -> (["s", "-q", "version;quit"], "UC Berkeley, ABC ") + SBV.Boolector -> (["--version"] , "") + SBV.Bitwuzla -> (["--version"] , "") + SBV.CVC4 -> (["--version"] , "This is CVC4 version ") + SBV.CVC5 -> (["--version"] , "This is CVC5 version ") + SBV.DReal -> (["--version"] , "dReal v") + SBV.MathSAT -> (["-version"] , "MathSAT5 version ") + SBV.Yices -> (["--version"] , "Yices ") + SBV.Z3 -> (["--version"] , "Z3 version ") in try (readProcessWithExitCode (SBV.executable s') args "") >>= \case - Right (ExitSuccess,o,_) | [l] <- lines o -> return $ Just l + Right (ExitSuccess,o,_) | (l:_) <- lines o -> + return $ Just $ dropPrefix pref l Right _ -> return Nothing Left (_ :: SomeException) -> return Nothing + where dropPrefix (x:xs) (y:ys) | x == y = dropPrefix xs ys + dropPrefix _ ys = ys -- | Get the 'SolverBackendVersion' of a 'SolverBackend' getSolverBackendVersion :: SolverBackend -> IO (Maybe String) From fa146c8b9747f176975c43fa4c31d792f9e38b3b Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Fri, 21 Jul 2023 15:10:40 -0400 Subject: [PATCH 43/51] solver caching: use lmdb-simple, make env var lazy, save timestamps --- .gitmodules | 3 + cabal.project | 2 + deps/lmdb | 1 + saw-remote-api/src/SAWServer.hs | 9 +- saw-script.cabal | 3 + src/SAWScript/Builtins.hs | 3 +- src/SAWScript/Interpreter.hs | 49 +-- src/SAWScript/SolverCache.hs | 281 +++++++++++------ src/SAWScript/SolverCache/LMDBOptDatabase.hs | 291 ------------------ .../SolverCache/lmdb_opt_database.py | 200 ------------ src/SAWScript/SolverVersions.hs | 2 +- src/SAWScript/Value.hs | 12 +- 12 files changed, 231 insertions(+), 625 deletions(-) create mode 160000 deps/lmdb delete mode 100644 src/SAWScript/SolverCache/LMDBOptDatabase.hs delete mode 100644 src/SAWScript/SolverCache/lmdb_opt_database.py diff --git a/.gitmodules b/.gitmodules index 926212c99c..90b2fb191d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,3 +46,6 @@ [submodule "deps/language-sally"] path = deps/language-sally url = https://github.com/GaloisInc/language-sally +[submodule "deps/lmdb"] + path = deps/lmdb + url = https://github.com/GaloisInc/lmdb.git diff --git a/cabal.project b/cabal.project index 7ed7119a69..4b3dc8753f 100644 --- a/cabal.project +++ b/cabal.project @@ -32,6 +32,8 @@ packages: deps/parameterized-utils deps/flexdis86 deps/flexdis86/binary-symbols + deps/lmdb/lmdb + deps/lmdb/lmdb-simple deps/macaw/base deps/macaw/symbolic deps/macaw/x86 diff --git a/deps/lmdb b/deps/lmdb new file mode 160000 index 0000000000..d454c81385 --- /dev/null +++ b/deps/lmdb @@ -0,0 +1 @@ +Subproject commit d454c8138587aa762bc19a8cb1b101f3379f8f3c diff --git a/saw-remote-api/src/SAWServer.hs b/saw-remote-api/src/SAWServer.hs index ecafdac09e..0d00b24681 100644 --- a/saw-remote-api/src/SAWServer.hs +++ b/saw-remote-api/src/SAWServer.hs @@ -64,7 +64,7 @@ import Verifier.SAW.CryptolEnv (initCryptolEnv, bindTypedTerm) import qualified Cryptol.Utils.Ident as Cryptol import Verifier.SAW.Cryptol.Monadify (defaultMonEnv) import SAWScript.Prover.MRSolver (emptyMREnv) -import SAWScript.SolverCache (emptySolverCache, setSolverCachePath) +import SAWScript.SolverCache (lazyOpenSolverCache) import qualified Argo --import qualified CryptolServer (validateServerState, ServerState(..)) @@ -209,11 +209,8 @@ initialState readFileFn = jvmTrans <- CJ.mkInitialJVMContext halloc cwd <- getCurrentDirectory mb_cache <- lookupEnv "SAW_SOLVER_CACHE_PATH" >>= \case - Just p | not (null p) -> do - cache <- emptySolverCache - snd (setSolverCachePath p) opts cache - return $ Just cache - _ -> return Nothing + Just path | not (null path) -> Just <$> lazyOpenSolverCache path + _ -> return Nothing db <- newTheoremDB let ro = TopLevelRO { roJavaCodebase = jcb diff --git a/saw-script.cabal b/saw-script.cabal index ec7c3a1a44..a5e9fb845d 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -31,6 +31,7 @@ library , bimap , bytestring , bv-sized >= 1.0 && < 1.1 + , cborg-json , containers , constraints >= 0.6 , cryptohash-sha256 >= 0.11.102.1 @@ -60,6 +61,7 @@ library , lens , llvm-pretty >= 0.8 , llvm-pretty-bc-parser >= 0.1.3.1 + , lmdb-simple , macaw-base , macaw-x86 , macaw-symbolic @@ -83,6 +85,7 @@ library , saw-core-sbv , saw-core-what4 , sbv >= 9.1 && < 9.3 + , serialise , split , temporary , template-haskell diff --git a/src/SAWScript/Builtins.hs b/src/SAWScript/Builtins.hs index ac0a527b6a..560fe1337d 100644 --- a/src/SAWScript/Builtins.hs +++ b/src/SAWScript/Builtins.hs @@ -976,8 +976,7 @@ applyProverToGoal backends opts f unintSet g = do -- Use a cached result if one exists (and it's valid w.r.t our query) Just v -> return $ fromSolverCacheValue satq v -- Otherwise try to cache the result of the call - _ -> f satq >>= \res -> - case toSolverCacheValue vs opts satq res of + _ -> f satq >>= \res -> io (toSolverCacheValue vs opts satq res) >>= \case Just v -> SV.onSolverCache (insertInSolverCache k v) >> return res Nothing -> return res diff --git a/src/SAWScript/Interpreter.hs b/src/SAWScript/Interpreter.hs index 7fbba8cbe3..46ddfd14e8 100644 --- a/src/SAWScript/Interpreter.hs +++ b/src/SAWScript/Interpreter.hs @@ -457,11 +457,8 @@ buildTopLevelEnv proxy opts = jcb <- JCB.loadCodebase (jarList opts) (classPath opts) (javaBinDirs opts) currDir <- getCurrentDirectory mb_cache <- lookupEnv "SAW_SOLVER_CACHE_PATH" >>= \case - Just p | not (null p) -> do - cache <- emptySolverCache - snd (setSolverCachePath p) opts cache - return $ Just cache - _ -> return Nothing + Just path | not (null path) -> Just <$> lazyOpenSolverCache path + _ -> return Nothing thmDB <- newTheoremDB Crucible.withHandleAllocator $ \halloc -> do let ro0 = TopLevelRO @@ -648,27 +645,23 @@ disable_lax_loads_and_stores = do rw <- getTopLevelRW putTopLevelRW rw { rwLaxLoadsAndStores = False } -enable_solver_cache :: TopLevel () -enable_solver_cache = do - rw <- getTopLevelRW - getSolverCache >>= \case - Just _ -> return () - Nothing -> do emptyCache <- io $ emptySolverCache - putTopLevelRW rw { rwSolverCache = Just emptyCache } - set_solver_cache_path :: FilePath -> TopLevel () -set_solver_cache_path path = - enable_solver_cache >> onSolverCache (setSolverCachePath path) +set_solver_cache_path path = do + rw <- getTopLevelRW + case rwSolverCache rw of + Just _ -> onSolverCache (setSolverCachePath path) + Nothing -> do cache <- io $ openSolverCache path + putTopLevelRW rw { rwSolverCache = Just cache } clean_solver_cache :: TopLevel () clean_solver_cache = do vs <- io $ getSolverBackendVersions allBackends onSolverCache (cleanSolverCache vs) -test_solver_cache_stats :: Integer -> Bool -> Integer -> Integer -> Integer -> +test_solver_cache_stats :: Integer -> Integer -> Integer -> Integer -> Integer -> TopLevel () -test_solver_cache_stats sz p ls ls_f is is_f = - onSolverCache (testSolverCacheStats sz p ls ls_f is is_f) +test_solver_cache_stats sz ls ls_f is is_f = + onSolverCache (testSolverCacheStats sz ls ls_f is is_f) enable_debug_intrinsics :: TopLevel () enable_debug_intrinsics = do @@ -1090,17 +1083,9 @@ primitives = Map.fromList , "currently are 'z3' and 'yices'." ] - , prim "enable_solver_cache" "TopLevel ()" - (pureVal enable_solver_cache) - Experimental - [ "Enable solver result caching, if it is not already enabled. Unless" - , "set_solver_cache_path is later called, the cache will not persist" - , "between sessions. Requires Python 3 to be installed." - ] - , prim "set_solver_cache_path" "String -> TopLevel ()" (pureVal set_solver_cache_path) - Experimental + Current [ "Enable solver result caching if it is not already enabled, open an" , "LMDB database at the given path, save to that database all results in" , "the current cache, then use that database as the cache going forward." @@ -1109,7 +1094,7 @@ primitives = Map.fromList , prim "clean_solver_cache" "TopLevel ()" (pureVal clean_solver_cache) - Experimental + Current [ "Remove all entries in the solver result cache which were created" , "using solver backend versions which do not match the versions" , "in the current environment." @@ -1117,7 +1102,7 @@ primitives = Map.fromList , prim "print_solver_cache" "String -> TopLevel ()" (pureVal (onSolverCache . printSolverCacheByHex)) - Experimental + Current [ "Print all entries in the solver result cache whose SHA256 hash" , "keys start with the given string. Providing an empty string results" , "in all entries in the cache being printed." @@ -1125,7 +1110,7 @@ primitives = Map.fromList , prim "print_solver_cache_stats" "TopLevel ()" (pureVal (onSolverCache printSolverCacheStats)) - Experimental + Current [ "Print out statistics about how the solver cache has been used, namely" , "how many entries are in the cache, whether the cache is being stored" , "in memory or on disk, how many insertions into the cache have been made" @@ -1134,9 +1119,9 @@ primitives = Map.fromList , "session, and with how many failed attempted usages have occurred so far" , "this session." ] - , prim "test_solver_cache_stats" "Int -> Bool -> Int -> Int -> Int -> Int -> TopLevel ()" + , prim "test_solver_cache_stats" "Int -> Int -> Int -> Int -> Int -> TopLevel ()" (pureVal test_solver_cache_stats) - Experimental + Current [ "Test whether the values of the statistics printed out by" , "print_solver_cache_stats are equal to those given, failing if" , "this does not hold. Specifically, the arguments represent how many" diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 9cbc5ad30c..0cb3f29135 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -54,7 +54,8 @@ module SAWScript.SolverCache , toSolverCacheValue , fromSolverCacheValue , SolverCache(..) - , emptySolverCache + , lazyOpenSolverCache + , openSolverCache , SolverCacheOp , lookupInSolverCache , insertInSolverCache @@ -65,23 +66,26 @@ module SAWScript.SolverCache , testSolverCacheStats ) where -import System.Directory (createDirectoryIfMissing, makeAbsolute) +import System.Directory (createDirectoryIfMissing) import Control.Exception (try, SomeException) import Control.Monad (when, forM_) import System.Timeout (timeout) import GHC.Generics (Generic) import Data.IORef (IORef, newIORef, modifyIORef, readIORef) -import Data.Tuple.Extra (first, firstM, both) -import Data.List (elemIndex, intercalate) +import Data.Time.Clock (UTCTime, getCurrentTime) +import Data.Tuple.Extra (first, firstM) +import Data.List (elemIndex, intercalate, isPrefixOf) import Data.Maybe (fromMaybe, isJust) import Data.Functor ((<&>)) +import Numeric (readHex) import Data.Map.Strict (Map) import qualified Data.Map.Strict as M import qualified Data.Text as T -import Data.Text.Encoding +import Data.Text.Encoding (encodeUtf8) +import Text.Printf (printf) import Data.ByteString (ByteString) import qualified Data.ByteString as BS @@ -89,8 +93,13 @@ import qualified Data.ByteString as BS import qualified Crypto.Hash.SHA256 as SHA256 import Data.Aeson ( FromJSON(..), ToJSON(..), FromJSONKey(..), ToJSONKey(..) - , (.:), (.:?), (.=) ) + , (.:), (.:?), (.=), fromJSON ) import qualified Data.Aeson as JSON +import Codec.CBOR.JSON (encodeValue, decodeValue) +import Codec.Serialise (Serialise(..)) + +import qualified Database.LMDB.Simple as LMDB +import qualified Database.LMDB.Simple.Extra as LMDB import qualified Data.SBV.Dynamic as SBV @@ -99,9 +108,6 @@ import Verifier.SAW.SATQuery import Verifier.SAW.ExternalFormat import Verifier.SAW.SharedTerm -import SAWScript.SolverCache.LMDBOptDatabase (encodeHex, LMDBOptDatabase) -import qualified SAWScript.SolverCache.LMDBOptDatabase as LMDBOpt - import SAWScript.Options import SAWScript.Proof @@ -133,14 +139,28 @@ instance ToJSON FirstOrderValue where toJSON = JSON.genericToJSON firstOrderJSONOptions toEncoding = JSON.genericToEncoding firstOrderJSONOptions --- | Run the given IO action, but if the given 'timeout' (in ms) is reached --- or the action encounters any 'SomeException', 'Left' is returned +-- | Run the given IO action, but if the given 'timeout' (in microseconds) is +-- reached or the action encounters any 'SomeException', 'Left' is returned tryWithTimeout :: Int -> IO a -> IO (Either String a) -tryWithTimeout t_ms m = try (timeout (t_ms * 1000) m) <&> \case +tryWithTimeout t_us m = try (timeout t_us m) <&> \case Right (Just a) -> Right a - Right Nothing -> Left $ "Operation timed out (" ++ show t_ms ++ "ms)" + Right Nothing -> let t_str | t_us `mod` 1000000 == 0 = show (t_us `div` 1000000) ++ "s" + | t_us `mod` 1000 == 0 = show (t_us `div` 1000) ++ "ms" + | otherwise = show t_us ++ "us" + in Left $ "Operation timed out (" ++ t_str ++ ")" Left (exn :: SomeException) -> Left $ show exn +-- | Encode a 'ByteString' as a hex string +encodeHex :: ByteString -> String +encodeHex = concatMap (printf "%02x") . BS.unpack + +-- | Decode a hex string as a 'ByteString' +decodeHex :: String -> Either String ByteString +decodeHex s = BS.pack <$> go s + where go (c0:c1:cs) | [(b,[])] <- readHex [c0,c1] = (b:) <$> go cs + go [] = return [] + go _ = Left $ "Hex decoding failure on: " ++ s + -- Solver Backends ------------------------------------------------------------- @@ -266,6 +286,14 @@ instance Show SolverCacheKey where if M.null vs && null opts then "" else " (" ++ showBackendVersionsWithOptions ", " vs opts ++ ")" +-- | ... +solverCacheKeyFromHash :: ByteString -> SolverCacheKey +solverCacheKeyFromHash = SolverCacheKey M.empty [] + +instance Serialise SolverCacheKey where + encode = encode . solverCacheKeyHash + decode = solverCacheKeyFromHash <$> decode + -- | Hash using SHA256 a 'String' representation of a 'SATQuery' and a 'Set' of -- 'SolverBackendVersion's to get a 'SolverCacheKey'. In particular, this -- 'String' representation contains all the 'SolverBackendVersion's, the @@ -308,6 +336,7 @@ data SolverCacheValue = , solverCacheValueOptions :: [SolverBackendOption] , solverCacheValueSolverName :: String , solverCacheValueCEXs :: Maybe [(Int, FirstOrderValue)] + , solverCacheValueLastUsed :: UTCTime } deriving Eq instance FromJSON SolverCacheValue where @@ -316,22 +345,34 @@ instance FromJSON SolverCacheValue where opts <- v .:? "opts" nm <- v .: "nm" mb_cexs <- v .:? "cexs" - return $ SolverCacheValue vs (fromMaybe [] opts) nm mb_cexs + t <- v .: "t" + return $ SolverCacheValue vs (fromMaybe [] opts) nm mb_cexs t instance ToJSON SolverCacheValue where - toJSON (SolverCacheValue vs opts nm mb_cexs) = JSON.object $ + toJSON (SolverCacheValue vs opts nm mb_cexs t) = JSON.object $ ["vs" .= vs] ++ (if null opts then [] else ["opts" .= opts]) ++ - ["nm" .= nm] ++ maybe [] (\cexs -> ["cexs" .= cexs]) mb_cexs - toEncoding (SolverCacheValue vs opts nm mb_cexs) = JSON.pairs $ + ["nm" .= nm] ++ maybe [] (\cexs -> ["cexs" .= cexs]) mb_cexs ++ ["t" .= t] + toEncoding (SolverCacheValue vs opts nm mb_cexs t) = JSON.pairs $ "vs" .= vs <> (if null opts then mempty else "opts" .= opts) <> - "nm" .= nm <> maybe mempty (\cexs -> "cexs" .= cexs) mb_cexs + "nm" .= nm <> maybe mempty (\cexs -> "cexs" .= cexs) mb_cexs <> "t" .= t + +instance Serialise SolverCacheValue where + encode = encodeValue . toJSON + decode = do + v <- decodeValue False + case fromJSON v of + JSON.Success x -> return x + JSON.Error e -> fail e -- | Convert the result of a solver call on the given 'SATQuery' to a -- 'SolverCacheValue' toSolverCacheValue :: SolverBackendVersions -> [SolverBackendOption] -> - SATQuery -> (Maybe CEX, String) -> Maybe SolverCacheValue -toSolverCacheValue vs opts satq (cexs, solver_name) = - SolverCacheValue vs opts solver_name <$> firstsMaybeM (`elemIndex` ecs) cexs + SATQuery -> (Maybe CEX, String) -> + IO (Maybe SolverCacheValue) +toSolverCacheValue vs opts satq (cexs, solver_name) = do + getCurrentTime <&> \t -> case firstsMaybeM (`elemIndex` ecs) cexs of + Just cexs' -> Just $ SolverCacheValue vs opts solver_name cexs' t + Nothing -> Nothing where ecs = M.keys $ satVariables satq firstsMaybeM :: Monad m => (a -> m b) -> Maybe [(a, c)] -> m (Maybe [(b, c)]) @@ -340,12 +381,11 @@ toSolverCacheValue vs opts satq (cexs, solver_name) = -- | Convert a 'SolverCacheValue' to something which has the same form as the -- result of a solver call on the given 'SATQuery' fromSolverCacheValue :: SATQuery -> SolverCacheValue -> (Maybe CEX, String) -fromSolverCacheValue satq (SolverCacheValue _ _ solver_name cexs) = +fromSolverCacheValue satq (SolverCacheValue _ _ solver_name cexs _) = (firstsMaybe (ecs !!) cexs, solver_name) where ecs = M.keys $ satVariables satq firstsMaybe :: (a -> b) -> Maybe [(a, c)] -> Maybe [(b, c)] firstsMaybe = fmap . fmap . first - -- The Solver Cache ------------------------------------------------------------ @@ -357,8 +397,11 @@ fromSolverCacheValue satq (SolverCacheValue _ _ solver_name cexs) = -- make sure failures are counted correctly. data SolverCache = SolverCache - { solverCacheDB :: LMDBOptDatabase SolverCacheValue + { solverCachePath :: FilePath + , solverCacheEnv :: Maybe (LMDB.Environment LMDB.ReadWrite) + , solverCacheDB :: Maybe (LMDB.Database SolverCacheKey SolverCacheValue) , solverCacheStats :: IORef (Map SolverCacheStat Integer) + , solverCacheMapSize :: Int , solverCacheTimeout :: Int } @@ -366,104 +409,168 @@ data SolverCache = data SolverCacheStat = Lookups | FailedLookups | Inserts | FailedInserts deriving (Eq, Ord, Bounded, Enum) --- | An empty 'SolverCache' with no associated 'FilePath' -emptySolverCache :: IO SolverCache -emptySolverCache = do - db <- LMDBOpt.new +-- | ... +lazyOpenSolverCache :: FilePath -> IO SolverCache +lazyOpenSolverCache path = do stats <- newIORef $ M.fromList ((,0) <$> [minBound..]) - return $ SolverCache db stats 1000 + return SolverCache { solverCachePath = path, + solverCacheEnv = Nothing, + solverCacheDB = Nothing, + solverCacheStats = stats, + solverCacheMapSize = 4 {- GiB -} * 1073741824, + solverCacheTimeout = 1 {- sec -} * 1000000 } + +-- | ... +openSolverCache :: FilePath -> IO SolverCache +openSolverCache path = do + (_, _, cache') <- forceSolverCacheOpened =<< lazyOpenSolverCache path + return cache' + +-- | ... +forceSolverCacheOpened :: SolverCache -> + IO (LMDB.Environment LMDB.ReadWrite, + LMDB.Database SolverCacheKey SolverCacheValue, + SolverCache) +forceSolverCacheOpened cache@SolverCache{ solverCacheEnv = Just env + , solverCacheDB = Just db } = + return (env, db, cache) +forceSolverCacheOpened cache@SolverCache{..} = do + createDirectoryIfMissing True solverCachePath + let limits = LMDB.defaultLimits { LMDB.mapSize = solverCacheMapSize } + env <- LMDB.openEnvironment solverCachePath limits + db <- LMDB.readOnlyTransaction env (LMDB.getDatabase Nothing) + let cache' = cache { solverCacheEnv = Just env, solverCacheDB = Just db } + return (env, db, cache') + +-- | ... +tryTransaction :: (LMDB.Mode tmode, LMDB.SubMode LMDB.ReadWrite tmode) => + SolverCache -> + (LMDB.Database SolverCacheKey SolverCacheValue -> + LMDB.Transaction tmode a) -> + IO (Either String a, SolverCache) +tryTransaction cache@SolverCache{..} t = + tryWithTimeout solverCacheTimeout (forceSolverCacheOpened cache) >>= \case + Right (env, db, cache') -> + (,cache') <$> tryWithTimeout solverCacheTimeout + (LMDB.transaction env (t db)) + Left err -> + return (Left $ "Failed to open LMDB database: " ++ err, cache) -- | An operation on a 'SolverCache', returning a value of the given type or -- returning an optional default value in the case of no enabled 'SolverCache' -type SolverCacheOp a = (Maybe a, Options -> SolverCache -> IO a) +type SolverCacheOp a = (Maybe a, Options -> SolverCache -> IO (a, SolverCache)) -- | Lookup a 'SolverCacheKey' in the solver result cache lookupInSolverCache :: SolverCacheKey -> SolverCacheOp (Maybe SolverCacheValue) -lookupInSolverCache k = (Just Nothing,) $ \opts SolverCache{..} -> - tryWithTimeout solverCacheTimeout - (LMDBOpt.lookup (solverCacheKeyHash k) - solverCacheDB) >>= \case - Right (Just v) -> do +lookupInSolverCache k = (Just Nothing,) $ \opts cache@SolverCache{..} -> + getCurrentTime >>= \now -> + let upd _ v = Just v { solverCacheValueLastUsed = now } in + tryTransaction cache (LMDB.updateLookupWithKey upd k) >>= \case + (Right (Just v), cache') -> do printOutLn opts Debug ("Using cached result: " ++ show k) modifyIORef solverCacheStats $ M.adjust (+1) Lookups - return (Just v) - Left err -> do + return (Just v, cache') + (Left err, cache') -> do printOutLn opts Warn ("Solver cache lookup failed:\n" ++ err) modifyIORef solverCacheStats $ M.adjust (+1) FailedLookups - return Nothing - Right Nothing -> do - return Nothing + return (Nothing, cache') + (Right Nothing, cache') -> do + return (Nothing, cache') -- | Add a 'SolverCacheValue' to the solver result cache insertInSolverCache :: SolverCacheKey -> SolverCacheValue -> SolverCacheOp () -insertInSolverCache k v = (Just (),) $ \opts SolverCache{..} -> +insertInSolverCache k v = (Just (),) $ \opts cache@SolverCache{..} -> printOutLn opts Debug ("Caching result: " ++ show k) >> - tryWithTimeout solverCacheTimeout - (LMDBOpt.insert (solverCacheKeyHash k) v - solverCacheDB) >>= \case - Right () -> do + tryTransaction cache (LMDB.insert k v) >>= \case + (Right (), cache') -> do modifyIORef solverCacheStats $ M.adjust (+1) Inserts - Left err -> do + return ((), cache') + (Left err, cache') -> do printOutLn opts Warn ("Solver cache insert failed:\n" ++ err) modifyIORef solverCacheStats $ M.adjust (+1) FailedInserts + return ((), cache') -- | Set the 'FilePath' of the solver result cache and save all results cached -- so far setSolverCachePath :: FilePath -> SolverCacheOp () -setSolverCachePath path = (Nothing,) $ \opts SolverCache{..} -> do - pathAbs <- makeAbsolute path - createDirectoryIfMissing True pathAbs - eith_sz <- tryWithTimeout solverCacheTimeout - (LMDBOpt.size solverCacheDB) - eith_db <- tryWithTimeout solverCacheTimeout - (LMDBOpt.setPath pathAbs 4096 solverCacheDB) - case (,) <$> eith_sz <*> eith_db of - Left err -> fail $ "Could not set solver cache path:\n" ++ err - Right (sz, ()) | sz == 0 -> return () - Right (sz, ()) -> do - let (s0, s1) = (show sz, if sz == 1 then "" else "s") - printOutLn opts Info ("Saved " ++ s0 ++ " cached result" ++ s1 ++ " to disk") +setSolverCachePath path = (Nothing,) $ \opts cache@SolverCache{..} -> + if path == solverCachePath + then do + (_, _, cache') <- forceSolverCacheOpened cache + return ((), cache') + else do + (new_env, new_db, cache') <- + forceSolverCacheOpened cache { solverCachePath = path + , solverCacheEnv = Nothing + , solverCacheDB = Nothing } + case (solverCacheEnv, solverCacheDB) of + (Just old_env, Just old_db) -> do + kvs <- LMDB.readOnlyTransaction old_env $ LMDB.toList old_db + forM_ kvs $ \(k,v) -> LMDB.transaction new_env $ LMDB.insert k v new_db + printOutLn opts Info ("Saved " ++ show (length kvs) ++ " cached result" ++ + (if length kvs == 1 then "" else "s") ++ " to disk") + return ((), cache') + _ -> return ((), cache') -- | Print all entries in the solver result cache whose SHA256 hash keys start -- with the given string printSolverCacheByHex :: String -> SolverCacheOp () -printSolverCacheByHex hex_prefix = (Nothing,) $ \opts SolverCache{..} -> do - kvs <- LMDBOpt.filterByHexPrefix hex_prefix solverCacheDB +printSolverCacheByHex hex_pref = (Nothing,) $ \opts cache -> do + (env, db, cache') <- forceSolverCacheOpened cache + let flt k v kvs = if hex_pref `isPrefixOf` encodeHex (solverCacheKeyHash k) + then (k,v):kvs else kvs + kvs <- case decodeHex hex_pref of + Right (solverCacheKeyFromHash -> k) -> do + LMDB.readOnlyTransaction env (LMDB.lookup k db) >>= \case + Just v -> return [(k,v)] + Nothing -> LMDB.readOnlyTransaction env $ LMDB.foldrWithKey flt [] db + Left _ -> LMDB.readOnlyTransaction env $ LMDB.foldrWithKey flt [] db when (length kvs == 0) $ printOutLn opts Info "No keys found" - forM_ kvs $ \(k_hash, SolverCacheValue vs bk_opts nm mb_cexs) -> do + forM_ kvs $ \(k, SolverCacheValue vs bk_opts nm mb_cexs t) -> do let vs_str = showBackendVersionsWithOptions ", " vs bk_opts res_str = maybe "unsat" (("sat " ++) . show) mb_cexs - printOutLn opts Info $ "SHA: " ++ encodeHex k_hash + printOutLn opts Info $ "SHA: " ++ encodeHex (solverCacheKeyHash k) printOutLn opts Info $ "- Result: " ++ res_str printOutLn opts Info $ "- Solver: " ++ show nm - printOutLn opts Info $ "- Versions: " ++ vs_str ++ "\n" + printOutLn opts Info $ "- Versions: " ++ vs_str + printOutLn opts Info $ "- Last used: " ++ show t ++ "\n" + return ((), cache') + where -- | Remove all entries in the solver result cache which have version(s) that --- do not match the current version(s) +-- do not match the current version(s) or are marked as stale cleanSolverCache :: SolverBackendVersions -> SolverCacheOp () -cleanSolverCache curr_base_vs = (Nothing,) $ \opts SolverCache{..} -> do - let curr_base_vs_obj = M.fromList [("vs", curr_base_vs)] - fs0 <- LMDBOpt.cleanByJSONObjValues curr_base_vs_obj solverCacheDB - let fs1 = concatMap (fmap (both (M.! ("vs" :: String))) . snd) fs0 - fs2 = M.unions $ fmap (uncurry $ M.intersectionWith (,)) fs1 - s0 = if length fs0 == 1 then "" else "s" - s1 = if M.size fs2 == 0 then "" else ":" +cleanSolverCache curr_base_vs = (Nothing,) $ \opts cache -> do + let known_curr_base_vs = M.filter isJust curr_base_vs + mismatched_vs vs = M.mapMaybe id $ M.intersectionWith + (\base_ver v_ver -> if base_ver /= v_ver + then Just (base_ver, v_ver) else Nothing) + known_curr_base_vs vs + flt k v (ks, mvs) = let mvs' = mismatched_vs (solverCacheValueVersions v) + in if M.null mvs then (ks, mvs) + else (k:ks, M.union mvs mvs') + (env, db, cache') <- forceSolverCacheOpened cache + (ks, mvs) <- LMDB.readOnlyTransaction env $ LMDB.foldrWithKey flt ([], M.empty) db + forM_ ks $ \k -> LMDB.transaction env $ LMDB.delete k db + let s0 = if length ks == 1 then "" else "s" + s1 = if M.size mvs == 0 then "" else ":" printOutLn opts Info $ - "Removed " ++ show (length fs0) ++ + "Removed " ++ show (length ks) ++ " cached result" ++ s0 ++ " with mismatched version" ++ s0 ++ s1 - forM_ (M.toList fs2) $ \(backend, (v1, v2)) -> + forM_ (M.toList mvs) $ \(backend, (v1, v2)) -> printOutLn opts Info $ "- " ++ showSolverBackendVersion backend v1 [] ++ " (Current: " ++ showSolverBackendVersion backend v2 [] ++ ")" + return ((), cache') -- | Print out statistics about how the solver cache was used printSolverCacheStats :: SolverCacheOp () -printSolverCacheStats = (Nothing,) $ \opts SolverCache{..} -> do +printSolverCacheStats = (Nothing,) $ \opts cache@SolverCache{..} -> do + (env, db, cache') <- forceSolverCacheOpened cache printOutLn opts Info ("== Solver result cache statistics ==") - sz <- LMDBOpt.size solverCacheDB - loc <- fromMaybe "memory" <$> LMDBOpt.getPath solverCacheDB + sz <- LMDB.readOnlyTransaction env $ LMDB.size db printOutLn opts Info ("- " ++ show sz ++ " result" ++ pl sz - ++ " cached in " ++ loc) + ++ " cached in " ++ solverCachePath) stats <- readIORef solverCacheStats let (ls, ls_f) = (stats M.! Lookups, stats M.! FailedLookups) (is, is_f) = (stats M.! Inserts, stats M.! FailedInserts) @@ -473,26 +580,26 @@ printSolverCacheStats = (Nothing,) $ \opts SolverCache{..} -> do printOutLn opts Info $ "- " ++ show ls ++ " usage" ++ pl ls ++ " of cached results so far this run (" ++ show ls_f ++ " failed attempt" ++ pl ls_f ++ ")" + return ((), cache') where pl i = if i == 1 then "" else "s" -- | Check whether the values of the statistics printed out by -- 'printSolverCacheStats' are equal to those given, failing if this does not -- hold -testSolverCacheStats :: Integer -> Bool -> Integer -> Integer -> Integer -> - Integer -> SolverCacheOp () -testSolverCacheStats sz p ls ls_f is is_f = (Nothing,) $ \opts SolverCache{..} -> do - sz_actual <- fromIntegral <$> LMDBOpt.size solverCacheDB +testSolverCacheStats :: Integer -> Integer -> Integer -> Integer -> Integer -> + SolverCacheOp () +testSolverCacheStats sz ls ls_f is is_f = (Nothing,) $ \opts cache@SolverCache{..} -> do + (env, db, cache') <- forceSolverCacheOpened cache + sz_actual <- fromIntegral <$> LMDB.readOnlyTransaction env (LMDB.size db) test sz sz_actual "Size of solver cache" - p_actual <- isJust <$> LMDBOpt.getPath solverCacheDB - test p p_actual "Whether solver cache saved to disk" stats <- readIORef solverCacheStats test ls (stats M.! Lookups) "Number of usages of solver cache" test ls_f (stats M.! FailedLookups) "Number of failed usages of solver cache" test is (stats M.! Inserts) "Number of insertions into solver cache" test is_f (stats M.! FailedInserts) "Number of failed insertions into solver cache" - printOutLn opts Info $ "Solver cache stats matched expected (" ++ show sz ++ - (if p then " true " else " false ") ++ show ls ++ " " ++ - show ls_f ++ " " ++ show is ++ " " ++ show is_f ++ ")" + printOutLn opts Info $ "Solver cache stats matched expected (" ++ show sz ++ " " ++ + show ls ++ " " ++ show ls_f ++ " " ++ show is ++ " " ++ show is_f ++ ")" + return ((), cache') where test v v_actual str = when (v /= v_actual) $ fail $ str ++ " (" ++ show v_actual ++ ")" ++ " did not match expected (" ++ show v ++ ")" diff --git a/src/SAWScript/SolverCache/LMDBOptDatabase.hs b/src/SAWScript/SolverCache/LMDBOptDatabase.hs deleted file mode 100644 index e957859d5f..0000000000 --- a/src/SAWScript/SolverCache/LMDBOptDatabase.hs +++ /dev/null @@ -1,291 +0,0 @@ -{- | -Module : SAWScript.SolverCache -Description : Optionally LMDB-backed databases -License : BSD3 -Maintainer : m-yac -Stability : provisional - -This module implements a 'HashTable'-inspired interface for key-value databases -optionally backed by an LMDB database on disk. - -Instead of using something like the Haskell library @lmdb-simple@, which -requires that the user have the LMDB dynamically-linked C libraries installed -even if an LMDB database is never used at runtime, this module connects to a -Python process on database creation and uses the Python LMDB bindings only when -the user provides a file path at which to open an LMDB database. This means that -the user only needs to have Python installed if they create an 'LMDBOptDatabase' -object at runtime, and only needs to have the Python LMDB bindings, which are -self-contained and can be installed via @pip@, if they provide a file path. --} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE ViewPatterns #-} - -module SAWScript.SolverCache.LMDBOptDatabase - ( encodeHex - , decodeHex - , LMDBOptDatabase - , new - , setPath - , getPath - , size - , lookup - , insert - , delete - , toList - , filterByHexPrefix - , cleanByJSONObjValues - ) where - -import Prelude hiding (lookup) -import System.Timeout (timeout) -import System.IO.Temp (withSystemTempFile) -import System.Process -import System.IO - -import Control.Concurrent (forkIO, ThreadId) -import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar) -import Control.Monad ((>=>), forever) -import Data.Char (isHexDigit) -import Text.Printf (printf) -import Text.Read (readEither) -import Numeric (readHex) - -import Language.Haskell.TH (runIO) -import Language.Haskell.TH.Syntax (qAddDependentFile, liftString) - -import Data.ByteString (ByteString) -import qualified Data.ByteString as BS -import qualified Data.ByteString.Lazy as LBS - -import Data.Aeson (FromJSON, ToJSON) -import qualified Data.Aeson as JSON - --- | The contents of @lmdb_opt_database.py@ -lmdb_opt_database__py :: String -lmdb_opt_database__py = $(do - let fp = "src/SAWScript/SolverCache/lmdb_opt_database.py" - qAddDependentFile fp - s <- runIO $ readFile fp - liftString s) - - --- Helper functions for encoding/decoding hex strings -------------------------- - --- | Remove all characters except hex digits (i.e. `[0-9a-fA-F]`) from the --- given string -sanitizeHex :: String -> String -sanitizeHex = filter isHexDigit - --- | Encode a 'ByteString' as a hex string -encodeHex :: ByteString -> String -encodeHex = concatMap (printf "%02x") . BS.unpack - --- | Decode a hex string as a 'ByteString' -decodeHex :: String -> Either String ByteString -decodeHex s = BS.pack <$> go s - where go (c0:c1:cs) | [(b,[])] <- readHex [c0,c1] = (b:) <$> go cs - go [] = return [] - go _ = Left $ "Hex decoding failure on: " ++ s - --- | Encode an element of a 'ToJSON' type as a hex string -encodeJSONHex :: ToJSON a => a -> String -encodeJSONHex = encodeHex . LBS.toStrict . JSON.encode - --- | Decode a hex string as an element of a 'FromJSON' type -decodeJSONHex :: FromJSON a => String -> Either String a -decodeJSONHex = decodeHex >=> JSON.eitherDecodeStrict' - --- | Given a list of strings, each of which containing exactly one space --- character, apply the two given decoding functions to each respective part of --- each string and return the result as a list of pairs -decodePairs :: (String -> Either String a) -> - (String -> Either String b) -> - [String] -> Either String [(a, b)] -decodePairs f1 f2 = mapM $ \l -> case words l of - [s1, s2] -> (,) <$> f1 s1 <*> f2 s2 - _ -> Left $ "Expected two strings separated by a space, got: " ++ show l - --- | Given a list of at most one string, apply the given decoding function to --- the string if there is one, otherwise return 'Nothing' -decodeMaybe :: (String -> Either String a) -> [String] -> - Either String (Maybe a) -decodeMaybe f [l] = Just <$> f l -decodeMaybe _ [] = Right Nothing -decodeMaybe _ ls = Left $ "Expected at most one line, got: " ++ show (unlines ls) - --- | Given a list of exactly one string, apply the given decoding function to it -decodeSingle :: (String -> Either String a) -> [String] -> Either String a -decodeSingle f [l] = f l -decodeSingle _ ls = Left $ "Expected one line, got: " ++ show (unlines ls) - - --- Helper functions for IO ----------------------------------------------------- - --- | Apply 'hGetLine' to the given 'Handle' until the given 'String' is --- received, then return all the prior collected results of 'hGetLine' -hGetLinesUntil :: String -> Handle -> IO [String] -hGetLinesUntil stop_str h = do - l <- hGetLine h - if l == stop_str then return [] - else (l:) <$> hGetLinesUntil stop_str h - --- | Apply 'hGetLine' with a 'timeout' of the given number of microseconds --- repeatedly until either 'hReady' returns 'False' or the operation times out -hGetLinesTimeout :: Int -> Handle -> IO [String] -hGetLinesTimeout t_micros h = - timeout t_micros (hGetLine h) >>= \case - Just l -> hReady h >>= \case - True -> (l:) <$> hGetLinesTimeout t_micros h - False -> return [l] - Nothing -> return [] - - --- Internal definition of and operations on LMDBOptDatabase -------------------- - --- | A key-value database which is either stored in memory, or if the user has --- supplied a path (see 'setPath'/'getPath'), as an LMDB database. Keys are --- represented as 'ByteString's, but values can be of any JSON-serialisable type --- (see 'ToJSON'/'FromJSON'). This is implemented by connecting to a @python3@ --- process, so Python must be installed to use this type. To use the LMDB --- backend, the Python LMDB bindings must also be installed. -data LMDBOptDatabase a = - LMDBOptDatabase - { dbIOVar :: MVar (String, MVar [String]) - , dbIOTID :: ThreadId - , dbErr :: Handle - , dbShell :: ProcessHandle - } - --- | The timeout to use when attempting to get lines from @stderr@ -dbInternalTimeout :: Int -dbInternalTimeout = 100000 - --- | Open a new @LMDBOptDatabase@ by connecting to a @python3@ process -dbOpen :: IO (LMDBOptDatabase a) -dbOpen = withSystemTempFile "lmdb_opt_database.py" $ \fnm fh -> do - -- First, start a python3 process with a temp file containing the contents of - -- lmdb_opt_database.py and the "shell" option for lmdb_opt_database.py - -- TODO: Actually use pip to install this file so we don't have to use a temp - -- file every time we open a database - hPutStr fh lmdb_opt_database__py >> hClose fh - (mb_i, mb_o, mb_e, p) <- createProcess $ - (shell $ "python3 " ++ fnm ++ " shell") - {std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe} - mb_exit <- getProcessExitCode p - case (mb_i, mb_o, mb_e, mb_exit) of - -- If the process was created successfully... - (Just i, Just o, Just e, Nothing) -> do - hSetBuffering i NoBuffering - -- First we create a thread which, when given a string and an MVar to - -- which to send output, will send the string to the Python process, - -- gather the output, and send it back over the MVar. - -- We make this thread to ensure each new command waits for any currently - -- executing commands to finish before trying to send input to and/or - -- read output from the Python process. - i_var <- newEmptyMVar - io_tid <- forkIO $ forever $ do - (inp_str, outp_var) <- takeMVar i_var - hPutStrLn i inp_str - outp_ls <- hGetLinesUntil ">>><<<" o - putMVar outp_var outp_ls - -- Finally we initialize the python process with a line which sets the - -- REPL's command prompt to ">>><<<\n" (which the process above waits for) - let db = LMDBOptDatabase i_var io_tid e p - decodeInit l = if l == ">>> ()" then Right () - else Left $ "Unexpected: " ++ l - u <- dbExec (decodeSingle decodeInit) - "import sys; sys.ps1 = '>>><<<\\n'; sys.ps2 = ''; print(())" db - return $ u `seq` db - -- Otherwise, either the process or one of its pipes failed to be created, - -- so we clean up anything that was created and fail - _ -> do - mapM_ hClose mb_i - terminateProcess p - mb_e_ls <- mapM (hGetLinesTimeout dbInternalTimeout) mb_e - fail $ "Failed to spawn Python LMDB process" - ++ maybe ": Pipe not created" (unlines . (":":)) mb_e_ls - --- | Send the given 'String' to the @python3@ process and decode the resulting --- lines (if any) using the given function -dbExec :: ([String] -> Either String b) -> String -> LMDBOptDatabase a -> IO b -dbExec decodeFn inp_str LMDBOptDatabase{..} = do - outp_var <- newEmptyMVar - putMVar dbIOVar (inp_str, outp_var) - outp_ls <- takeMVar outp_var - case length outp_ls `seq` decodeFn outp_ls of - Right b -> return b - Left read_err -> do - e_ls <- hGetLinesTimeout dbInternalTimeout dbErr - fail $ if length e_ls == 0 then "Decoding Error:\n" ++ read_err - else unlines $ "Python error:" : e_ls - - --- Exposed interface of LMDBOptDatabase ---------------------------------------- - --- | Create a new, empty, 'LMDBOptDatabase'. This will fail if the @python3@ --- executable cannot be found. -new :: IO (LMDBOptDatabase a) -new = do db <- dbOpen - () <- dbExec (decodeSingle readEither) "db = LMDBOptDatabase(); ()" db - return db - --- | Open an LMDB database at the given 'FilePath' with the given maximum size --- (in MiB), add all current entries in the database to the LMDB database, then --- set the current implementation of this database to be the LMDB database. This --- will fail if the Python LMDB bindings cannot be loaded. -setPath :: FilePath -> Int -> LMDBOptDatabase a -> IO () -setPath path map_size = dbExec (decodeSingle readEither) $ - "db.setPath(jsonHex('" ++ encodeJSONHex path ++ "'), " ++ - "jsonHex('" ++ encodeJSONHex map_size ++ "')); ()" - --- | Return the location of the directory in which this database is stored, if --- this database is implemented as an LMDB database -getPath :: LMDBOptDatabase a -> IO (Maybe FilePath) -getPath = dbExec (decodeSingle decodeJSONHex) - "pJSONHex(db.getPath())" - --- | Return the number of entries in the database -size :: LMDBOptDatabase a -> IO Int -size = dbExec (decodeSingle decodeJSONHex) - "pJSONHex(len(db))" - --- | Return the JSON-serializable value associated to the given 'ByteString' --- key in the database, if one exists -lookup :: FromJSON a => ByteString -> LMDBOptDatabase a -> IO (Maybe a) -lookup k = dbExec (decodeMaybe decodeJSONHex) $ - "pHex(db.get(hex('" ++ encodeHex k ++ "')))" - --- | Insert a 'ByteString' key / JSON-serializable value entry into the database -insert :: ToJSON a => ByteString -> a -> LMDBOptDatabase a -> IO () -insert k v = dbExec (decodeSingle readEither) $ - "db[hex('" ++ encodeHex k ++ "')] = hex('" ++ encodeJSONHex v ++ "'); ()" - --- | Delete the entry associated to the given 'ByteString' key in the database -delete :: ByteString -> LMDBOptDatabase a -> IO () -delete k = dbExec (decodeSingle readEither) $ - "del db[hex('" ++ encodeHex k ++ "')]; ()" - --- | Return the list of all entries in the database -toList :: FromJSON a => LMDBOptDatabase a -> IO [(ByteString, a)] -toList = dbExec (decodePairs decodeHex decodeJSONHex) $ - "pHexPairs(db.items())" - --- | Return all entries in the database whose keys have the given string as a --- prefix of their hex representation -filterByHexPrefix :: FromJSON a => String -> LMDBOptDatabase a -> - IO [(ByteString, a)] -filterByHexPrefix s = dbExec (decodePairs decodeHex decodeJSONHex) $ - "pHexPairs(db.filterByKeyHexPrefix('" ++ sanitizeHex s ++ "'))" - --- | Delete all entries @k, v@ in the database for which @v@, when converted to --- JSON, does not match the given value @ref@, when converted to JSON - in the --- sense that there is some key in both @v@ and @ref@ which has differing values --- in each (see @mismatchedJSONObjValues@ in @lmdb_opt_database.py@). Each such --- @k@, paired with @v@'s list of mismatches, is returned. -cleanByJSONObjValues :: (ToJSON b, FromJSON b) => b -> LMDBOptDatabase a -> - IO [(ByteString, [(b, b)])] -cleanByJSONObjValues ref = dbExec (decodePairs decodeHex decodeJSONHex) $ - "pHexJSONPairs(db.filterByMismatchedJSONObjValues(" ++ - "jsonHex('" ++ encodeJSONHex ref ++ "'), delete = True))" diff --git a/src/SAWScript/SolverCache/lmdb_opt_database.py b/src/SAWScript/SolverCache/lmdb_opt_database.py deleted file mode 100644 index a401d2c27b..0000000000 --- a/src/SAWScript/SolverCache/lmdb_opt_database.py +++ /dev/null @@ -1,200 +0,0 @@ -from collections.abc import MutableMapping -import json -import sys - -def mismatchedJSONObjValues(obj1, obj2): - """A generator which, given two JSON-serializable objects, yields a sequence - of pairs of JSON-serailizable objects which indicate which object values - are mismatched between the two objects. This is done recursively on - sub-objects and keys which are present in one object but not the other - are ignored. - Example - ------- - >>> list(mismatchedJSONObjValues({'a': 4, 'b': {'c': 5}, 'd': 6 }, - {'a': 3, 'b': {'c': 2, 'z': 1}, 'd': 6})) - [ ({'a': 4}, {'a': 3}), ({'b': {'c': 5}}, {'b': {'c': 2}}) ] - """ - if isinstance(obj1, dict) and isinstance(obj2, dict): - for k in obj1.keys() & obj2.keys(): - for ms1, ms2 in mismatchedJSONObjValues(obj1[k], obj2[k]): - yield {k: ms1}, {k: ms2} - elif obj1 != obj2: - yield obj1, obj2 - -# ------------------------------------------------------------------------------ -# A Database optionally backed by LMDB -# ------------------------------------------------------------------------------ - -class LMDBOptDatabase(MutableMapping): - """A key-value database which is either implemented as a Python ``dict``, or - if the user has supplied a path (see ``setPath``/``getPath``), as an LMDB - database. Keys and values are represented as ``bytes``.""" - - def __init__(self): - """Create an empty database with no set path, implemented as a ``dict``""" - self._path = None - self._impl = {} - - def setPath(self, path, map_size = 10): - """Open an LMDB database at the given ``path`` with the given ``map_size``, - add all current entries in the database to the LMDB database, then - set the current implementation of this database to be the LMDB database. - Parameters - ---------- - path : str - Location of the directory to store the database - map_size : str, optional - Maximum size the database may grow to, in MiB. If at any point the - database grows larger, an exception will be raised and ``setPath`` must - be called again with a larger ``map_size``. Default is 10(MiB). - Raises - ------ - ImportError - If the Python ``lmdb`` library cannot be loaded - """ - import lmdb - # NOTE: If the LMDB database to load is the same as the currently loaded - # database, we must make sure to close the old ``lmdb.Environment`` first - if self._path == path: self._impl.close() - new_impl = lmdb.Environment(path, map_size = map_size * 1048576) - if self._path != path: - with new_impl.begin(write = True) as txn: - for k,v in self.items(): - txn.put(k, v, overwrite=True) - self._path = path - self._impl = new_impl - - def getPath(self): - """Return the location of the directory in which this database is stored, - if this database is implemented as an LMDB database. Return ``None`` - otherwise.""" - return self._path - - # Implementation of the methods of the MutableMapping base class - - def __len__(self): - if self._path is None: return len(self._impl) - with self._impl.begin() as txn: return txn.stat()['entries'] - - def __getitem__(self, k): - if self._path is None: return self._impl[k] - with self._impl.begin() as txn: - v = txn.get(k) - if v is None: raise KeyError - return v - - def __setitem__(self, k, v): - if self._path is None: self._impl[k] = v; return - with self._impl.begin(write = True) as txn: txn.put(k, v, overwrite=True) - - def __delitem__(self, k): - if self._path is None: del self._impl[k]; return - with self._impl.begin(write = True) as txn: txn.delete(k) - - def _iter(self, fn, keys, values): - if self._path is None: yield from fn(self._impl); return - with self._impl.begin() as txn: - with txn.cursor() as curs: - yield from curs.iternext(keys, values) - - def __iter__(self): yield from self._iter(iter, True, False) - def keys (self): yield from self._iter(dict.keys, True, False) - def values (self): yield from self._iter(dict.values, False, True ) - def items (self): yield from self._iter(dict.items, True, True ) - - # Methods for performing special filters on keys or values - - def filterByKeyHexPrefix(self, p_hex): - """A generator which yields all key-value pairs whose keys have the given - string as a prefix of their hex representation. If there is a key whose - hex representation exactly matches the given string, it and its value - are returned immediately, without whole database being searched.""" - try: - p = bytes.fromhex(p_hex) - yield (p, self[p]) - except Exception: - for k,v in self.items(): - if k.hex().startswith(p_hex): - yield (k, v) - - def filterByMismatchedJSONObjValues(self, obj_ref, delete = False): - """This generator finds all entries ``k, v`` in the database for which - ``v``, when deserialized as JSON, does not match ``obj_ref`` - in the - sense that there is some key in both ``v`` and ``obj_ref`` which has - differing values in each (see ``mismatchedJSONObjValues``). Each such - ``k``, paired with ``v``'s list of mismatches (the list returned by - ``mismatchedJSONObjValues``), is then yielded. If ``delete`` is ``True``, - each entry is deleted from the database before being yielded.""" - # NOTE: This function could just as well be defined as the body of - # ``onKVs`` below with ``self.items()`` for ``kvs`` and ``del self[k]`` - # for `deleteFn`. However, that would result in creating an additional - # LMDB transaction for every deleted element. Instead, we have a special - # case for LMDB-backed databases which uses only a single tranaction. - def onKVs(kvs, deleteFn): - for k,v in kvs: - try: - ms = list(mismatchedJSONObjValues(json.loads(v), obj_ref)) - if len(ms) > 0: - if delete: deleteFn(k) - yield (k, ms) - except Exception: - pass - if self._path is None: - yield from onKVs(self._impl.items(), self._impl.__delitem__); return - with self._impl.begin(write = True) as txn: - with txn.cursor() as curs: - yield from onKVs(curs, txn.delete) - -# ------------------------------------------------------------------------------ -# Helper functions for using LMDBOptDatabase objects with hex strings -# ------------------------------------------------------------------------------ - -def hex(hex_str): - """Convert a hex string to ``bytes``""" - return bytes.fromhex(hex_str) - -def jsonHex(obj_hex_str): - """Convert a hex string to ``bytes`` then deserialize a the result as JSON""" - return json.loads(hex(obj_hex_str)) - -def pHex(v): - """Print the given value as a hex string, if it is a ``bytes`` object""" - if isinstance(v, bytes): print(v.hex()) - -def pJSONHex(obj): - """Serialize the given object as JSON to get a ``bytes`` object then print - the result as a hex string""" - print(bytes(json.dumps(obj), 'utf-8').hex()) - -def pHexPair(v1, v2): - """Print the given pair of values as a hex strings, if both are ``bytes`` - objects.""" - if isinstance(v1, bytes) and isinstance(v2, bytes): - print(v1.hex(), v2.hex()) - -def pHexJSONPair(v1, obj2): - """Print the given pair of values as a hex strings, if the first element is - a ``bytes`` object. For the second element, serialize it as JSON to get a - ``bytes`` object and use that to print its hex string.""" - if isinstance(v1, bytes): - print(v1.hex(), bytes(json.dumps(obj2), 'utf-8').hex()) - -def pHexPairs(vprs): - """Print each pair in a given iterable using ``pHexPair``""" - for v1, v2 in vprs: pHexPair(v1, v2) - -def pHexJSONPairs(vobjprs): - """Print each pair in a given iterable using ``pHexJSONPair``""" - for v1, obj2 in vobjprs: pHexJSONPair(v1, obj2) - -# ------------------------------------------------------------------------------ -# Interactive Shell Interface -# ------------------------------------------------------------------------------ - -def main(argv = None): - if len(argv) > 0 and argv[0] == "shell": - import code - code.InteractiveConsole(globals()).interact(banner = '') - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/src/SAWScript/SolverVersions.hs b/src/SAWScript/SolverVersions.hs index cb674dabb2..e2d577a373 100644 --- a/src/SAWScript/SolverVersions.hs +++ b/src/SAWScript/SolverVersions.hs @@ -35,7 +35,7 @@ getSolverVersion s = SBV.Boolector -> (["--version"] , "") SBV.Bitwuzla -> (["--version"] , "") SBV.CVC4 -> (["--version"] , "This is CVC4 version ") - SBV.CVC5 -> (["--version"] , "This is CVC5 version ") + SBV.CVC5 -> (["--version"] , "This is cvc5 version ") SBV.DReal -> (["--version"] , "dReal v") SBV.MathSAT -> (["-version"] , "MathSAT5 version ") SBV.Yices -> (["--version"] , "Yices ") diff --git a/src/SAWScript/Value.hs b/src/SAWScript/Value.hs index 7927d76db4..be8a03da95 100644 --- a/src/SAWScript/Value.hs +++ b/src/SAWScript/Value.hs @@ -517,7 +517,7 @@ data TopLevelRW = , rwProofs :: [Value] {- ^ Values, generated anywhere, that represent proofs. -} , rwPPOpts :: PPOpts , rwSharedContext :: SharedContext - , rwSolverCache :: Maybe SolverCache + , rwSolverCache :: Maybe SolverCache , rwTheoremDB :: TheoremDB -- , rwCrucibleLLVMCtx :: Crucible.LLVMContext @@ -689,9 +689,6 @@ getSharedContext = TopLevel_ (rwSharedContext <$> get) getJavaCodebase :: TopLevel JSS.Codebase getJavaCodebase = TopLevel_ (asks roJavaCodebase) -getSolverCache :: TopLevel (Maybe SolverCache) -getSolverCache = TopLevel_ (rwSolverCache <$> get) - getTheoremDB :: TopLevel TheoremDB getTheoremDB = TopLevel_ (rwTheoremDB <$> get) @@ -743,8 +740,11 @@ recordProof v = onSolverCache :: SolverCacheOp a -> TopLevel a onSolverCache (mb_default, f) = do opts <- getOptions - getSolverCache >>= \case - Just cache -> io $ f opts cache + rw <- getTopLevelRW + case rwSolverCache rw of + Just cache -> do (a, cache') <- io $ f opts cache + putTopLevelRW rw { rwSolverCache = Just cache' } + return a Nothing -> case mb_default of Just a -> return a Nothing -> fail "Solver result cache not enabled!" From c426c710e455b0da862d815b19964f21b1f43e06 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 25 Jul 2023 14:42:49 -0400 Subject: [PATCH 44/51] move python interface for SAW solver caches to saw-remote-api --- saw-remote-api/python/CHANGELOG.md | 5 + .../python/saw_client/solver_cache.py | 124 ++++++++++++++++++ saw-script.cabal | 2 - src/SAWScript/SolverCache.hs | 3 + 4 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 saw-remote-api/python/saw_client/solver_cache.py diff --git a/saw-remote-api/python/CHANGELOG.md b/saw-remote-api/python/CHANGELOG.md index 4246522d1f..b636450a0f 100644 --- a/saw-remote-api/python/CHANGELOG.md +++ b/saw-remote-api/python/CHANGELOG.md @@ -1,5 +1,10 @@ # Revision history for saw-client +## 1.0.1 -- YYYY-MM-DD + +* Add `solver_cache.py` implementing an interface for interacting with SAW + solver cache databases. + ## 0.9.1 -- YYYY-MM-DD * Add a `set_option` command to `saw_client` that allows enabling and disabling diff --git a/saw-remote-api/python/saw_client/solver_cache.py b/saw-remote-api/python/saw_client/solver_cache.py new file mode 100644 index 0000000000..ed6797b692 --- /dev/null +++ b/saw-remote-api/python/saw_client/solver_cache.py @@ -0,0 +1,124 @@ +import cbor2 +from collections.abc import MutableMapping +from dataclasses import dataclass +from datetime import datetime +import dateutil.parser +import lmdb +import os +import pytz +from typing import Dict, List, Optional, Tuple, Union +from typing_extensions import Literal + +# Adapted from src/SAWScript/SolverCache.hs +SolverBackend = Union[ + Literal["What4"], + Literal["SBV"], + Literal["AIG"], + Literal["RME"], + # External solvers supported by SBV (adapted from SBV.Solver) + Literal["ABC"], + Literal["Boolector"], + Literal["Bitwuzla"], # NOTE: Not currently supported by SAW + Literal["CVC4"], + Literal["CVC5"], + Literal["DReal"], # NOTE: Not currently supported by SAW + Literal["MathSAT"], + Literal["Yices"], + Literal["Z3"]] + +@dataclass +class SolverCacheEntry: + counterexamples : Optional[List[List]] + solver_name : str + solver_versions : Dict[SolverBackend, str] + solver_options : List[List] + timestamp : datetime + + def __post_init__(self): + if self.counterexamples is not None: + if not isinstance(self.counterexamples, list) or \ + any(not isinstance(cex, list) for cex in self.counterexamples): + raise ValueError('`counterexamples` must be a list of lists') + if not isinstance(self.solver_name, str): + raise ValueError('`solver_name` must be a string') + if not isinstance(self.solver_versions, dict) or \ + any(not isinstance(k, str) or not isinstance(v, str) for k,v in self.solver_versions.items()): + raise ValueError('`solver_versions` must be a `dict` with `str` keys and values') + if self.solver_options is not None: + if not isinstance(self.solver_options, list) or \ + any(not isinstance(opt, list) for opt in self.solver_options): + raise ValueError('`solver_options` must be a list of lists') + if not isinstance(self.timestamp, datetime): + raise ValueError('`timestamp` must be a `datetime` object') + +def load_solver_cache_entry(enc): + obj = cbor2.loads(enc) + opts = obj['opts'] if 'opts' in obj else [] + t = dateutil.parser.isoparse(obj['t']) + return SolverCacheEntry(obj.get('cexs'), obj['nm'], obj['vs'], opts, t) + +def solver_cache_entry_encoder(encoder, entry): + if not isinstance(entry, SolverCacheEntry): + raise Exception('cannot serialize type %s' % entry.__class__) + entry.__post_init__() # re-validate everything just to be sure + obj = {} # this dict should be created in alphabetical order to match the Haskell + if entry.counterexamples is not None: obj['cexs'] = entry.counterexamples + obj['nm'] = entry.solver_name + if len(entry.solver_options) > 0: obj['opts'] = entry.solver_options + obj['t'] = entry.timestamp.astimezone(pytz.utc).isoformat('T').replace('+00:00', 'Z') + obj['vs'] = entry.solver_versions + return encoder.encode(obj) + +class SolverCache(MutableMapping): + """An interface for interacting with a SAW solver cache. This is implemented + as a `MutableMapping` from a 256-bit `bytes` keys to `SolverCacheEntry` + values, with LMDB as the backend.""" + + def __init__(self, path : str = None): + """Immediately open a solver cache at the given path, or if no path is + given, at the path specified by the current value of the + `SAW_SOLVER_CACHE_PATH` environment variable. If neither the former is + given nor the latter is set, a `ValueError` is raised.""" + if path is None: + if 'SAW_SOLVER_CACHE_PATH' not in os.environ or os.environ['SAW_SOLVER_CACHE_PATH'] == '': + raise ValueError("no path given to SolverCache and SAW_SOLVER_CACHE_PATH not set") + path = os.environ['SAW_SOLVER_CACHE_PATH'] + self.env = lmdb.Environment(path, map_size = 4 * 1073741824) # 4 GiB + self.path = path + + def __len__(self): + with self.env.begin() as txn: + return txn.stat()['entries'] + + def __getitem__(self, k : bytes): + if not isinstance(k, bytes) or len(k) != 32: + raise ValueError("solver cache key must be a 256-bit `bytes` value") + with self.env.begin() as txn: + v = txn.get(cbor2.dumps(k)) + if v is None: raise KeyError + return load_solver_cache_entry(v) + + def __setitem__(self, k : bytes, v : SolverCacheEntry): + if not isinstance(k, bytes) or len(k) != 32: + raise ValueError("solver cache key must be a 256-bit `bytes` value") + if not isinstance(v, SolverCacheEntry): + raise ValueError("solver cache value must be a `SolverCacheEntry` object") + with self.env.begin(write = True) as txn: + txn.put(cbor2.dumps(k), cbor2.dumps(v, default=solver_cache_entry_encoder), overwrite=True) + + def __delitem__(self, k : bytes): + if not isinstance(k, bytes) or len(k) != 32: + raise ValueError("solver cache key must be a 256-bit `bytes` value") + with self.env.begin(write = True) as txn: + txn.delete(cbor2.dumps(k)) + + def _iter(self, keys, values, fn): + with self.env.begin() as txn: + with txn.cursor() as curs: + for v in curs.iternext(keys, values): + yield fn(v) + + def __iter__(self): yield from self._iter(True, False, cbor2.loads) + def keys (self): yield from self._iter(True, False, cbor2.loads) + def values (self): yield from self._iter(False, True, load_solver_cache_entry) + def items (self): yield from self._iter(True, True, lambda kv: (cbor2.loads(kv[0]), load_solver_cache_entry(kv[1]))) diff --git a/saw-script.cabal b/saw-script.cabal index a5e9fb845d..6ef3261dba 100644 --- a/saw-script.cabal +++ b/saw-script.cabal @@ -10,7 +10,6 @@ License-file: LICENSE extra-source-files: src/SAWScript/Parser.y src/SAWScript/Lexer.x - src/SAWScript/SolverCache/lmdb_opt_database.py custom-setup setup-depends: @@ -131,7 +130,6 @@ library SAWScript.SBVParser SAWScript.SBVModel SAWScript.SolverCache - SAWScript.SolverCache.LMDBOptDatabase SAWScript.SolverVersions SAWScript.Token SAWScript.TopLevel diff --git a/src/SAWScript/SolverCache.hs b/src/SAWScript/SolverCache.hs index 0cb3f29135..3bbb4ff437 100644 --- a/src/SAWScript/SolverCache.hs +++ b/src/SAWScript/SolverCache.hs @@ -230,9 +230,12 @@ data SolverBackendOption = W4_Tactic String instance FromJSON SolverBackendOption where parseJSON = JSON.genericParseJSON JSON.defaultOptions + { JSON.sumEncoding = JSON.TwoElemArray } instance ToJSON SolverBackendOption where toJSON = JSON.genericToJSON JSON.defaultOptions + { JSON.sumEncoding = JSON.TwoElemArray } toEncoding = JSON.genericToEncoding JSON.defaultOptions + { JSON.sumEncoding = JSON.TwoElemArray } -- | Given a 'SolverBackendOption', return the list of additional -- 'SolverBackend's that are used From aea7c089513044bd7556fadbb77efb44a7cad641 Mon Sep 17 00:00:00 2001 From: Matthew Yacavone Date: Tue, 25 Jul 2023 18:26:51 -0400 Subject: [PATCH 45/51] update docs for solver caching --- doc/manual/manual.md | 91 +++++++++++++++++++---------------- doc/manual/manual.pdf | Bin 482755 -> 484114 bytes src/SAWScript/Interpreter.hs | 32 ++++++------ src/SAWScript/SolverCache.hs | 72 ++++++++++++++++++--------- 4 files changed, 114 insertions(+), 81 deletions(-) diff --git a/doc/manual/manual.md b/doc/manual/manual.md index 8c8969b969..aea5048ec0 100644 --- a/doc/manual/manual.md +++ b/doc/manual/manual.md @@ -111,9 +111,9 @@ SAW also uses several environment variables for configuration: `SAW_SOLVER_CACHE_PATH` ~ Specify a path at which to keep a cache of solver results obtained during - calls to certain tactics. Note that if this environment variable is set, - Python 3 and the Python LMDB bindings will need to be installed. See the - section **Caching Solver Results** for more detail about this feature. + calls to certain tactics. A cache is not created at this path until it is + needed. See the section **Caching Solver Results** for more detail about this + feature. On Windows, semicolon-delimited lists are used instead of colon-delimited lists. @@ -1277,63 +1277,72 @@ The simplest way to enable solver caching is to set the environment variable database at the given path containing the solver result cache. Setting this environment variable globally therefore creates a global, concurrency-safe solver result cache used by all newly created `saw` or `saw-remote-api` -processes. +processes. Note that when this environment variable is set, SAW does not create +a cache at the specified path until it is actually needed. -There are also a number of SAW commands for solver caching, all of which -require `enable_experimental`. +There are also a number of SAW commands related to solver caching. -* `set_solver_cache_path` has the same effect as if `SAW_SOLVER_CACHE_PATH` - was set to the given path for the remainder of the current session. - -* `enable_solver_cache` will enable solver caching, but not set a path at - which to save the cache. Thus, unless `SAW_SOLVER_CACHE_PATH` was set or - `set_solver_cache_path` is called, all cached results will be discarded at - the end of the session. This can be useful for testing, or if the user - does not wish to use LMDB. +* `set_solver_cache_path` is like setting `SAW_SOLVER_CACHE_PATH` for the + remainder of the current session, but opens an LMDB database at the specified + path immediately. If a cache is already in use in the current session + (i.e. through a prior call to `set_solver_cache_path` or through + `SAW_SOLVER_CACHE_PATH` being set and the cache being used at least once) + then all entries in the cache already in use will be copied to the new cache + being opened. * `clean_solver_cache` will remove all entries in the solver result cache which were created using solver backend versions which do not match the versions in the current environment. This can be run after an update to clear out any old, unusable entries from the solver cache. -There are also the commands `print_solver_cache` and -`print_solver_cache_stats`, which print out information about the entries -in the cache and usage statistics about the cache, respectively. +* `print_solver_cache` prints to the console all entries in the cache whose + SHA256 hashe keys start with the given hex string. Providing an empty string + results in all entries in the cache being printed. + +* `print_solver_cache_stats` prints to the console statistics including the + size of the solver cache, where on disk it is stored, and some counts of how + often it has been used during the current session. + +For performing more complicated database operations on the set of cached +results, the file `solver_cache.py` is provided with the Python bindings of the +SAW Remote API. This file implements a general-purpose Python interface for +interacting with the LMDB databases kept by SAW for solver caching. Below is an example of using solver caching with `saw -v Debug`. Only the relevant output is shown, the rest abbreviated with "...". ~~~~ -sawscript> enable_experimental -sawscript> enable_solver_cache +sawscript> set_solver_cache_path "example.cache" sawscript> prove_print z3 {{ \(x:[8]) -> x+x == x*2 }} -Caching result: c6e8f2e6ea38dd97 (SBV 9.2, Z3 version 4.8.7 - 64 bit) +[22:13:00.832] Caching result: d1f5a76e7a0b7c01 (SBV 9.2, Z3 4.8.7 - 64 bit) ... sawscript> prove_print z3 {{ \(new:[8]) -> new+new == new*2 }} -Using cached result: c6e8f2e6ea38dd97 (SBV 9.2, Z3 version 4.8.7 - 64 bit) +[22:13:04.122] Using cached result: d1f5a76e7a0b7c01 (SBV 9.2, Z3 4.8.7 - 64 bit) ... sawscript> prove_print (w4_unint_z3_using "qfnia" []) \ - {{ \(x:[8]) -> x+x == x*2 }} -Caching result: 975f9aac6d54cd54 (What4 v1.3-29-g6c462cd using qfnia, Z3 version 4.8.7 - 64 bit) + {{ \(x:[8]) -> x+x == x*2 }} +[22:13:09.484] Caching result: 4ee451f8429c2dfe (What4 v1.3-29-g6c462cd using qfnia, Z3 4.8.7 - 64 bit) ... -sawscript> print_solver_cache "c6e8f2e6ea38dd97" -SHA: c6e8f2e6ea38dd978e39e3a8a2a1f64d48e5a51412ad688e6e8d1d52285648ab -- Result: unsat -- Solver: "SBV->Z3" -- Versions: SBV 9.2, Z3 version 4.8.7 - 64 bit - -sawscript> print_solver_cache "975f9aac6d54cd54" -SHA: 975f9aac6d54cd54206e5938860fa43f89268a4545f0b01966291f034085c305 -- Result: unsat -- Solver: "W4 ->z3" -- Versions: What4 v1.3-29-g6c462cd using qfnia, Z3 version 4.8.7 - 64 bit -~~~~ - -Note that to use any of the features described above, Python 3 must be -installed. If a path is set for the solver cache (i.e. through -`SAW_SOLVER_CACHE_PATH` or `enable_solver_cache`), then the Python -[`lmdb`](https://lmdb.readthedocs.io/en/release/) library must also be -installed. +sawscript> print_solver_cache "d1f5a76e7a0b7c01" +[22:13:13.250] SHA: d1f5a76e7a0b7c01bdfe7d0e1be82b4f233a805ae85a287d45933ed12a54d3eb +[22:13:13.250] - Result: unsat +[22:13:13.250] - Solver: "SBV->Z3" +[22:13:13.250] - Versions: SBV 9.2, Z3 4.8.7 - 64 bit +[22:13:13.250] - Last used: 2023-07-25 22:13:04.120351 UTC + +sawscript> print_solver_cache "4ee451f8429c2dfe" +[22:13:16.727] SHA: 4ee451f8429c2dfefecb6216162bd33cf053f8e66a3b41833193529449ef5752 +[22:13:16.727] - Result: unsat +[22:13:16.727] - Solver: "W4 ->z3" +[22:13:16.727] - Versions: What4 v1.3-29-g6c462cd using qfnia, Z3 4.8.7 - 64 bit +[22:13:16.727] - Last used: 2023-07-25 22:13:09.484464 UTC + +sawscript> print_solver_cache_stats +[22:13:20.585] == Solver result cache statistics == +[22:13:20.585] - 2 results cached in example.cache +[22:13:20.585] - 2 insertions into the cache so far this run (0 failed attempts) +[22:13:20.585] - 1 usage of cached results so far this run (0 failed attempts) +~~~~ ## Other External Provers diff --git a/doc/manual/manual.pdf b/doc/manual/manual.pdf index af4e36a6af3364c632d30ac30b0d3ddd4b7b2bc4..5946dbcb917659a315202863bf4021cd45ba7191 100644 GIT binary patch delta 154719 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