-- | Compute the mem and disk covariance.
compDetailedCV :: [Node.Node] -> [Double]
compDetailedCV all_nodes =
  let (offline, nodes) = partition Node.offline all_nodes
      mem_l = map Node.pMem nodes
      dsk_l = map Node.pDsk nodes
      -- metric: memory covariance
      mem_cv = stdDev mem_l
      -- metric: disk covariance
      dsk_cv = stdDev dsk_l
      -- metric: count of instances living on N1 failing nodes
      n1_score = fromIntegral . sum . map (\n -> length (Node.sList n) +
                                                 length (Node.pList n)) .
                 filter Node.failN1 $ nodes :: Double
      res_l = map Node.pRem nodes
      -- metric: reserved memory covariance
      res_cv = stdDev res_l
      -- offline instances metrics
      offline_ipri = sum . map (length . Node.pList) $ offline
      offline_isec = sum . map (length . Node.sList) $ offline
      -- metric: count of instances on offline nodes
      off_score = fromIntegral (offline_ipri + offline_isec)::Double
      -- metric: count of primary instances on offline nodes (this
      -- helps with evacuation/failover of primary instances on
      -- 2-node clusters with one node offline)
      off_pri_score = fromIntegral offline_ipri::Double
      cpu_l = map Node.pCpu nodes
      -- metric: covariance of vcpu/pcpu ratio
      cpu_cv = stdDev cpu_l
      -- metrics: covariance of cpu, memory, disk and network load
      (c_load, m_load, d_load, n_load) =
        unzip4 $ map (\n ->
                      let DynUtil c1 m1 d1 n1 = Node.utilLoad n
                          DynUtil c2 m2 d2 n2 = Node.utilPool n
                      in (c1/c2, m1/m2, d1/d2, n1/n2)) nodes
      -- metric: conflicting instance count
      pri_tags_inst = sum $ map Node.conflictingPrimaries nodes
      pri_tags_score = fromIntegral pri_tags_inst::Double
      -- metric: spindles %
      spindles_cv = map (\n -> Node.instSpindles n / Node.hiSpindles n) nodes
  in [ mem_cv, dsk_cv, n1_score, res_cv, off_score, off_pri_score, cpu_cv
     , stdDev c_load, stdDev m_load , stdDev d_load, stdDev n_load
     , pri_tags_score, stdDev spindles_cv ]
compDetailedCV :: [Node.Node] -> [Double]
compDetailedCV all_nodes =
  let (offline, nodes) = partition Node.offline all_nodes
      mem_l = map Node.pMem nodes
      dsk_l = map Node.pDsk nodes
      -- metric: memory covariance
      mem_cv = stdDev mem_l
      -- metric: disk covariance
      dsk_cv = stdDev dsk_l
      -- metric: count of instances living on N1 failing nodes
      n1_score = fromIntegral . sum . map (\n -> length (Node.sList n) +
                                                 length (Node.pList n)) .
                 filter Node.failN1 $ nodes :: Double
      res_l = map Node.pRem nodes
      -- metric: reserved memory covariance
      res_cv = stdDev res_l
      -- offline instances metrics
      offline_ipri = sum . map (length . Node.pList) $ offline
      offline_isec = sum . map (length . Node.sList) $ offline
      -- metric: count of instances on offline nodes
      off_score = fromIntegral (offline_ipri + offline_isec)::Double
      -- metric: count of primary instances on offline nodes (this
      -- helps with evacuation/failover of primary instances on
      -- 2-node clusters with one node offline)
      off_pri_score = fromIntegral offline_ipri::Double
      cpu_l = map Node.pCpu nodes
      -- metric: covariance of vcpu/pcpu ratio
      cpu_cv = stdDev cpu_l
      -- metrics: covariance of cpu, memory, disk and network load
      (c_load, m_load, d_load, n_load) =
        unzip4 $ map (\n ->
                      let DynUtil c1 m1 d1 n1 = Node.utilLoad n
                          DynUtil c2 m2 d2 n2 = Node.utilPool n
                      in (c1/c2, m1/m2, d1/d2, n1/n2)) nodes
      -- metric: conflicting instance count
      pri_tags_inst = sum $ map Node.conflictingPrimaries nodes
      pri_tags_score = fromIntegral pri_tags_inst::Double
      -- metric: spindles %
      spindles_cv = map (\n -> Node.instSpindles n / Node.hiSpindles n) nodes
  in [ mem_cv, dsk_cv, n1_score, res_cv, off_score, off_pri_score, cpu_cv
     , stdDev c_load, stdDev m_load , stdDev d_load, stdDev n_load
     , pri_tags_score, stdDev spindles_cv ]
compDetailedCV all_nodes =
  let (offline, nodes) = partition Node.offline all_nodes
      mem_l = map Node.pMem nodes
      dsk_l = map Node.pDsk nodes
      -- metric: memory covariance
      mem_cv = stdDev mem_l
      -- metric: disk covariance
      dsk_cv = stdDev dsk_l
      -- metric: count of instances living on N1 failing nodes
      n1_score = fromIntegral . sum . map (\n -> length (Node.sList n) +
                                                 length (Node.pList n)) .
                 filter Node.failN1 $ nodes :: Double
      res_l = map Node.pRem nodes
      -- metric: reserved memory covariance
      res_cv = stdDev res_l
      -- offline instances metrics
      offline_ipri = sum . map (length . Node.pList) $ offline
      offline_isec = sum . map (length . Node.sList) $ offline
      -- metric: count of instances on offline nodes
      off_score = fromIntegral (offline_ipri + offline_isec)::Double
      -- metric: count of primary instances on offline nodes (this
      -- helps with evacuation/failover of primary instances on
      -- 2-node clusters with one node offline)
      off_pri_score = fromIntegral offline_ipri::Double
      cpu_l = map Node.pCpu nodes
      -- metric: covariance of vcpu/pcpu ratio
      cpu_cv = stdDev cpu_l
      -- metrics: covariance of cpu, memory, disk and network load
      (c_load, m_load, d_load, n_load) =
        unzip4 $ map (\n ->
                      let DynUtil c1 m1 d1 n1 = Node.utilLoad n
                          DynUtil c2 m2 d2 n2 = Node.utilPool n
                      in (c1/c2, m1/m2, d1/d2, n1/n2)) nodes
      -- metric: conflicting instance count
      pri_tags_inst = sum $ map Node.conflictingPrimaries nodes
      pri_tags_score = fromIntegral pri_tags_inst::Double
      -- metric: spindles %
      spindles_cv = map (\n -> Node.instSpindles n / Node.hiSpindles n) nodes
  in [ mem_cv, dsk_cv, n1_score, res_cv, off_score, off_pri_score, cpu_cv
     , stdDev c_load, stdDev m_load , stdDev d_load, stdDev n_load
     , pri_tags_score, stdDev spindles_cv ]