{-|
Module      : Gargantext.Core.Methods.Distances
Description : 
Copyright   : (c) CNRS, 2017-Present
License     : AGPL + CECILL v3
Maintainer  : team@gargantext.org
Stability   : experimental
Portability : POSIX

Motivation and definition of the @Conditional@ distance.
-}

{-# LANGUAGE BangPatterns      #-}
{-# LANGUAGE Strict            #-}
module Gargantext.Core.Methods.Distances.Conditional
  where

import Control.DeepSeq (NFData)
import Control.Parallel.Strategies (parList, rdeepseq, using)
import Data.Hashable (Hashable)
import Data.List (unzip)
import Data.Maybe (catMaybes)
import Gargantext.Prelude
import Gargantext.Core.Viz.Graph.Utils (getMax)
import qualified Data.HashMap.Strict as Map
import qualified Data.Set            as Set


type HashMap = Map.HashMap
------------------------------------------------------------------------
-- First version as first implementation
-- - qualitatively verified
-- - parallized as main optimization
conditional :: (Ord a, Hashable a, NFData a)
            => HashMap (a,a) Int
            -> HashMap (a,a) Double
conditional m' = Map.fromList $ ((catMaybes results') `using` parList rdeepseq)
  where
    results' = [ let
                  ij = (/) <$> Map.lookup (i,j) m <*> Map.lookup (i,i) m
                  ji = (/) <$> Map.lookup (j,i) m <*> Map.lookup (j,j) m
                  in getMax (i,j) ij ji

               | i <- keys
               , j <- keys
               , i < j
               ]
    -- Converting from Int to Double
    m       = Map.map fromIntegral m'

    -- Get the matrix coordinates, removing duplicates
    keys    = Set.toList $ Set.fromList (x <> y)
    (x,y)   = unzip $ Map.keys m