module MasterPlan.Data ( ProjectExpr
, ProjectKey
, Project(..)
, ProjectProperties(..)
, ProjAttribute(..)
, Trust(..)
, Cost(..)
, Progress(..)
, defaultProjectProps
, defaultCost
, defaultTrust
, defaultProgress
, defaultAtomic
, properties
, cost
, progress
, trust
, simplify
, subprojects
, prioritize ) where
import Data.Generics
import Data.List.NonEmpty (NonEmpty ((:|)))
import qualified Data.List.NonEmpty as NE
import Data.Void (Void)
type ProjectKey = String
newtype Trust = Trust { getTrust :: Float }
deriving (Show, Eq, Data, Typeable, Ord, Num, Real, RealFrac, Fractional)
newtype Cost = Cost { getCost :: Float }
deriving (Show, Eq, Data, Typeable, Ord, Num, Real, RealFrac, Fractional)
newtype Progress = Progress { getProgress :: Float }
deriving (Show, Eq, Data, Typeable, Ord, Num, Real, RealFrac, Fractional)
data Project e = Sum ProjectProperties (NE.NonEmpty (Project e))
| Product ProjectProperties (NE.NonEmpty (Project e))
| Sequence ProjectProperties (NE.NonEmpty (Project e))
| Atomic ProjectProperties Cost Trust Progress
| Annotated e
deriving (Eq, Show, Data, Typeable)
type ProjectExpr = Project Void
data ProjectProperties = ProjectProperties { title :: Maybe String
, description :: Maybe String
, url :: Maybe String
, owner :: Maybe String
} deriving (Eq, Show, Data, Typeable)
data ProjAttribute = PTitle | PDescription | PUrl | POwner | PCost | PTrust | PProgress
deriving (Eq, Enum, Bounded)
instance Show ProjAttribute where
show PTitle = "title"
show PDescription = "description"
show PUrl = "url"
show POwner = "owner"
show PCost = "cost"
show PTrust = "trust"
show PProgress = "progress"
defaultProjectProps ∷ ProjectProperties
defaultProjectProps = ProjectProperties { title = Nothing
, description = Nothing
, url = Nothing
, owner = Nothing }
defaultCost ∷ Cost
defaultCost = 0
defaultTrust ∷ Trust
defaultTrust = 1
defaultProgress ∷ Progress
defaultProgress = 0
defaultAtomic :: Project a
defaultAtomic = Atomic defaultProjectProps defaultCost defaultTrust defaultProgress
cost ∷ ProjectExpr → Cost
cost (Atomic _ (Cost c) _ (Progress p)) = Cost $ c * (1 p)
cost (Sequence _ ps) = costConjunction ps
cost (Product _ ps) = costConjunction ps
cost (Sum _ ps) =
Cost $ sum $ map (\x -> (1 snd x) * fst x) $ zip costs accTrusts
where
costs = NE.toList $ (getCost . cost) <$> ps
accTrusts = NE.toList $ NE.scanl (\a b -> a + b*(1a)) 0 $ (getTrust . trust) <$> ps
cost (Annotated _) = undefined
costConjunction ∷ NE.NonEmpty ProjectExpr → Cost
costConjunction ps =
Cost $ sum $ zipWith (*) costs accTrusts
where
costs = NE.toList $ (getCost . cost) <$> ps
accTrusts = NE.toList $ product <$> NE.inits ((getTrust . trust) <$> ps)
trust ∷ ProjectExpr → Trust
trust (Atomic _ _ (Trust t) (Progress p)) = Trust $ p + t * (1p)
trust (Sequence _ ps) = trustConjunction ps
trust (Product _ ps) = trustConjunction ps
trust (Sum _ ps) =
Trust $ foldl (\a b -> a + b*(1a)) 0 $ (getTrust . trust) <$> ps
trust (Annotated _) = undefined
trustConjunction ∷ NE.NonEmpty ProjectExpr → Trust
trustConjunction ps = Trust $ product $ (getTrust . trust) <$> ps
subprojects :: Project a -> [Project a]
subprojects (Sequence _ ps) = NE.toList ps
subprojects (Product _ ps) = NE.toList ps
subprojects (Sum _ ps) = NE.toList ps
subprojects _ = []
properties :: Project a -> Maybe ProjectProperties
properties (Annotated _) = Nothing
properties (Product props _) = Just props
properties (Sequence props _) = Just props
properties (Sum props _) = Just props
properties (Atomic props _ _ _) = Just props
progress ∷ ProjectExpr → Progress
progress (Atomic _ _ _ p) = p
progress (Sequence _ ps) = progressConjunction ps
progress (Product _ ps) = progressConjunction ps
progress (Sum _ ps) = maximum $ progress <$> ps
progress (Annotated _) = undefined
progressConjunction ∷ NE.NonEmpty ProjectExpr → Progress
progressConjunction ps = sum (progress <$> ps) / fromIntegral (length ps)
simplify ∷ Project a → Project a
simplify (Sum _ (p :| [])) = simplify p
simplify (Product _ (p :| [])) = simplify p
simplify (Sequence _ (p :| [])) = simplify p
simplify (Sum r ps) =
Sum r $ (reduce . simplify) =<< ps
where
reduce (Sum _ ps') = reduce =<< ps'
reduce p = [simplify p]
simplify (Product r ps) =
Product r $ (reduce . simplify) =<< ps
where
reduce (Product _ ps') = reduce =<< ps'
reduce p = [simplify p]
simplify (Sequence r ps) =
Sequence r $ (reduce . simplify) =<< ps
where
reduce (Sequence _ ps') = reduce =<< ps'
reduce p = [simplify p]
simplify p = p
prioritize ∷ ProjectExpr → ProjectExpr
prioritize (Sum r ps) =
let f p = getCost (cost p) / getTrust (trust p)
in Sum r $ NE.sortWith (nanToInf . f) $ prioritize <$> ps
prioritize (Product r ps) =
let f p = getCost (cost p) / (1 getTrust (trust p))
in Product r $ NE.sortWith (nanToInf . f) $ prioritize <$> ps
prioritize (Sequence r ps) =
Sequence r $ prioritize <$> ps
prioritize p = p
nanToInf :: RealFloat a => a -> a
nanToInf x = if isNaN x then 1/0 else x