```{-# LANGUAGE CPP #-}

-- | A gradient is represented by an IntMap from gradient indices
-- to values. Elements with no associated values in the gradient
-- are assumed to have a 0 value assigned. Such elements are
-- parameters, only nonzero elements are taken into account.
--
-- Each value associated with a gradient position is a pair of
-- positive and negative components. They are stored separately
-- to ensure high accuracy of computation results.
-- Besides, both positive and negative components are stored
-- in a logarithmic domain.

, empty
, fromList
, fromLogList
, toList
, parUnions
) where

import Data.List (foldl')
import qualified Data.IntMap as M
#if MIN_VERSION_containers(0,4,2)
import Control.Applicative ((<\$>), (<*>))
import Control.Monad.Par.Scheds.Direct (Par, runPar, spawn, get)
#else
#endif

import Numeric.SGD.LogSigned

-- | Gradient with nonzero values stored in a logarithmic domain.
-- Since values equal to zero have no impact on the update phase
-- of the SGD method, it is more efficient to not to store those

{-# INLINE insertWith #-}
insertWith :: (a -> a -> a) -> M.Key -> a -> M.IntMap a -> M.IntMap a
#if MIN_VERSION_containers(0,4,1)
insertWith = M.insertWith'
#else
insertWith f k x m = case M.lookup k m of
Just y  ->
let x' = f x y
in  x' `seq` M.insert k x' m
Nothing -> x `seq` M.insert k x m
#endif

-- | Add normal-domain double to the gradient at the given position.

-- | Add log-domain, singed number to the gradient at the given position.

-- | Construct gradient from a list of (index, value) pairs.
-- positions.
{-# INLINE fromList #-}
fromList :: [(Int, Double)] -> Grad
fromList =
in  foldl' ins empty

-- | Construct gradient from a list of (index, signed, log-domain number)
-- pairs.  All values from the list are added at respective gradient
-- positions.
{-# INLINE fromLogList #-}
fromLogList :: [(Int, LogSigned)] -> Grad
fromLogList =
in  foldl' ins empty

-- | Collect gradient components with values in normal domain.
{-# INLINE toList #-}
toList :: Grad -> [(Int, Double)]
toList =
let unLog (i, x) = (i, toNorm x)
in  map unLog . M.assocs

-- | Empty gradient, i.e. with all elements set to 0.
{-# INLINE empty #-}
empty = M.empty

-- | Perform parallel unions operation on gradient list.
-- Experimental version.
parUnions [] = error "parUnions: empty list"
#if MIN_VERSION_containers(0,4,2)
parUnions xs = runPar (parUnionsP xs)

-- | Parallel unoins in the Par monad.
parUnionsP [x] = return x
parUnionsP zs  = do
let (xs, ys) = split zs
xsP <- spawn (parUnionsP xs)
ysP <- spawn (parUnionsP ys)
M.unionWith (+) <\$> get xsP <*> get ysP
where
split []        = ([], [])
split (x:[])    = ([x], [])
split (x:y:rest)  =
let (xs, ys) = split rest
in  (x:xs, y:ys)
#else
parUnions xs = M.unions xs
#endif
```