{-# LANGUAGE ImplicitParams, Safe #-}

module Data.BellmanFord where

import Data.Maybe
import Control.Monad
import Control.CUtils.DataParallel
import Control.CUtils.Conc
import Data.List.Extras.Argmax
import Data.List
import Data.Array

keys :: [(a, b)] -> [a]
keys = map fst

mapWithKey :: (t -> u -> v) -> Array Int (t, u) -> Array Int (t, v)
mapWithKey f ar = let
		?seq = True
		?pool = BoxedThreadPool NoPool in
	arr_concF(\(_, i)->let (a, b) = ar!i in ((,) $! a) $! f a b) ((), snd(bounds ar)+1)

-- | Edge relaxation.
relaxEdge nodeWeights edgeWeights x (k, weight) = argmin snd ((k, weight) : map (\y -> (y, maybe (1/0) (\z -> snd (fromJust (lookup y nodeWeightsLs)) + z) (lookup (y, x) edgeWeights))) (keys nodeWeightsLs)) where
	nodeWeightsLs = elems nodeWeights

-- | The Bellman-Ford shortest path algorithm.
bellmanFord :: (Eq a) => [((a, a), Double)] -> a -> [(a, (a, Double))]
bellmanFord gr x = elems$foldl' (\weights _ -> mapWithKey (relaxEdge weights gr) weights) weights [1..length gr+1] where
	ks = nub (map fst (keys gr) ++ map snd (keys gr))
	weights = newArray$(x, (x, 0)) : map (\x -> (x, (x, 1/0))) (delete x ks)

retrievePath :: (Eq a) => [(a, (a, Double))] -> a -> a -> [a]
retrievePath mp a a2 | a == a2 = [a]
retrievePath mp a a2 = retrievePath mp a (fst (fromJust (lookup a2 mp))) ++ [a2]

-- | Cycle finding
cycles :: (Eq a) => [((a, a), ())] -> a -> Maybe a
cycles gr x = do
	(y, z) <- lookup x weights
	guard (round z < 0)
	return y where
	gr' = mapWithKey (\_ _ -> -1)$newArray gr
	weights = bellmanFord(elems gr') x
