module Data.Number.ER.Real.Approx.Tests.Reporting
where
import qualified Data.Number.ER.Real.Approx as RA
import Data.Number.ER.Misc
import qualified Data.List as List
import Text.Regex
import System.IO
unsafeERTestReport ::
(Show tId, Show sId, RA.ERIntApprox ira) =>
String ->
(tId, sId, ira, ira) ->
a -> a
unsafeERTestReport reportFileName (testId, subId, almostPreciseVal, approxVal) =
unsafeReport reportFileName $
stdRepLine (testId, subId) (overestimation, detail)
where
overestimation = fst $ getOverestimation almostPreciseVal approxVal
detail = (almostPreciseVal, approxVal)
stdRepLine (testId, subId) (overestimation, detail) =
"case=" ++ show testId
++ ";pt=" ++ show subId
++ ";ovest=" ++ show overestimation
++ ";detail=" ++ show detail
getOverestimation ::
(RA.ERIntApprox ira) =>
ira -> ira -> (Double, (ira, ira))
getOverestimation model res =
((abs $ wMod wRes) / (1 + (max 0 (wMod))), (model, res))
where
wMod = hMod lMod
wRes = hRes lRes
(lMod, hMod) = RA.doubleBounds model
(lRes, hRes) = RA.doubleBounds res
produceSummary :: String -> IO ()
produceSummary filepath =
do
casesInfo <- parseReport filepath
writeFile summaryFilepath $ formatSummary casesInfo
return ()
where
summaryFilepath = filepath ++ "-summary"
formatSummary casesInfo =
"all " ++ show casesCount ++ " cases:"
++ "\n approx. average time per case: " ++ show timeInSeconds ++ " seconds"
++ "\n approx. average per case average overestimation: " ++ show avgOverestimation
++ "\n approx. average per case maximum overestimation: " ++ show maxOverestimation
++ "\n\n removing the worst 5% of the cases (for each measure separately):"
++ "\n approx. average time per case: " ++ show timeInSeconds95 ++ " seconds"
++ "\n approx. average per case average overestimation: " ++ show avgOverestimation95
++ "\n approx. average per case maximum overestimation: " ++ show maxOverestimation95
++ "\n\n considering only the worst 50% but not the worst 5% of the cases (for each measure separately):"
++ "\n approx. average time per case: " ++ show timeInSeconds45 ++ " seconds"
++ "\n approx. average per case average overestimation: " ++ show avgOverestimation45
++ "\n approx. average per case maximum overestimation: " ++ show maxOverestimation45
++ "\n\n considering only the best 50% of the cases (for each measure separately):"
++ "\n approx. average time per case: " ++ show timeInSeconds50 ++ " seconds"
++ "\n approx. average per case average overestimation: " ++ show avgOverestimation50
++ "\n approx. average per case maximum overestimation: " ++ show maxOverestimation50
++ "\n\n" ++ (unlines $ map formatSummaryCase casesInfo)
where
(allTimes, (allAvgOvers, allMaxOvers)) =
mapSnd unzip $ unzip $ snd $ unzip casesInfo
timeInSeconds = (sum allTimes) / casesCountF
avgOverestimation = (sum allAvgOvers) / casesCountF
maxOverestimation = (sum allMaxOvers) / casesCountF
casesCount = length casesInfo
casesCountF :: Double
casesCountF = fromInteger $ toInteger casesCount
timeInSeconds95 = (sum allTimes95) / casesCount95F
avgOverestimation95 = (sum allAvgOvers95) / casesCount95F
maxOverestimation95 = (sum allMaxOvers95) / casesCount95F
allTimes95 = drop fivePerCent $ reverse $ List.sort allTimes
allAvgOvers95 = drop fivePerCent $ reverse $ List.sort allAvgOvers
allMaxOvers95 = drop fivePerCent $ reverse $ List.sort allMaxOvers
casesCount95F = fromInteger $ toInteger $ casesCount fivePerCent
fivePerCent = max 1 $ (5 * casesCount) `div` 100
timeInSeconds50 = (sum allTimes50) / casesCount50F
avgOverestimation50 = (sum allAvgOvers50) / casesCount50F
maxOverestimation50 = (sum allMaxOvers50) / casesCount50F
allTimes50 = drop fiftyPerCent $ reverse $ List.sort allTimes
allAvgOvers50 = drop fiftyPerCent $ reverse $ List.sort allAvgOvers
allMaxOvers50 = drop fiftyPerCent $ reverse $ List.sort allMaxOvers
casesCount50F = fromInteger $ toInteger $ casesCount fiftyPerCent
fiftyPerCent = casesCount `div` 2
timeInSeconds45 = (sum allTimes45) / casesCount45F
avgOverestimation45 = (sum allAvgOvers45) / casesCount45F
maxOverestimation45 = (sum allMaxOvers45) / casesCount45F
allTimes45 = drop fivePerCent $ reverse $ drop fiftyPerCent $ List.sort allTimes
allAvgOvers45 = drop fivePerCent $ reverse $ drop fiftyPerCent $ List.sort allAvgOvers
allMaxOvers45 = drop fivePerCent $ reverse $ drop fiftyPerCent $ List.sort allMaxOvers
casesCount45F = fromInteger $ toInteger $ casesCount fiftyPerCent fivePerCent
formatSummaryCase (caseId, (timeInSeconds, (avgOverestimation, maxOverestimation))) =
"case " ++ caseId ++ ":"
++ "\n approximate time = " ++ show timeInSeconds ++ " seconds"
++ "\n average sampled overestimation = " ++ show avgOverestimation
++ "\n maximal sampled overestimation = " ++ show maxOverestimation
parseReport :: String -> IO [(String, (Double, (Double, Double)))]
parseReport filepath =
withFile filepath ReadMode readFirstAndOtherLines
where
readFirstAndOtherLines h =
do
startLine <- hGetLine h
firstLine <- hGetLine h
readCases (firstLine, (getTime firstLine) (getTime startLine)) h
readCases (currentLine, caseCompTime) h =
do
(caseOverestimations, maybeNextLineAndTime) <- readCase [] 0 currentLine
let caseInfo = (caseId, (caseCompTime, avgAndMax caseOverestimations))
case maybeNextLineAndTime of
Nothing -> return [caseInfo]
Just (nextLine, nextCaseTime) ->
do
otherCases <- readCases (nextLine, nextCaseTime) h
return $ caseInfo : otherCases
where
avgAndMax ns =
(sum ns / (fromInteger $ toInteger $ length ns), foldl1 max ns)
caseId = getCaseId currentLine
readCase overestimationsSoFar currentTimeStep currentLine
| currentCaseId /= caseId =
return (overestimationsSoFar, Just (currentLine, currentTimeStep))
| otherwise =
do
finished <- hIsEOF h
case finished of
True -> return (currentOverestimations, Nothing)
False ->
do
nextLine <- hGetLine h
let nextTimeStep = (getTime nextLine) (getTime currentLine)
readCase currentOverestimations nextTimeStep nextLine
where
currentCaseId = getCaseId currentLine
currentOverestimations =
currentOverestimation : overestimationsSoFar
currentOverestimation = getOverestimation currentLine
getTime :: String -> Double
getTime line =
case reads line of
[(time,'s':_)] -> time
getCaseId :: String -> String
getCaseId line =
case matchRegex idRegex line of
Just [caseId] -> caseId
where
idRegex = mkRegex "case=([^;]*);"
getOverestimation :: String -> Double
getOverestimation line =
case matchRegex ovestRegex line of
Just [ovestS] -> read ovestS
where
ovestRegex = mkRegex "ovest=([^;]*);"