2 Module : Gargantext.Prelude
3 Description : Specific Prelude of the project
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
12 {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
13 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
16 module Gargantext.Prelude
17 ( module Gargantext.Prelude
20 , module GHC.Err.Located
32 import Control.Monad.Base (MonadBase(..))
33 import GHC.Exts (sortWith)
34 import GHC.Err.Located (undefined)
35 import GHC.Real (round)
36 import Data.Map (Map, lookup)
37 import Data.Maybe (isJust, fromJust, maybe)
38 import Data.Text (Text)
39 import Protolude ( Bool(True, False), Int, Int64, Double, Integer
40 , Fractional, Num, Maybe(Just,Nothing)
41 , Enum, Bounded, Float
43 , pure, (>>=), (=<<), (<*>), (<$>), (<&>), (>>)
45 , Ord, Integral, Foldable, RealFrac, Monad, filter
46 , reverse, map, mapM, zip, drop, take, zipWith
47 , sum, fromIntegral, length, fmap, foldl, foldl'
48 , takeWhile, sqrt, identity
49 , abs, min, max, maximum, minimum, return, snd, truncate
50 , (+), (*), (/), (-), (.), ($), (&), (**), (^), (<), (>), log
51 , Eq, (==), (>=), (<=), (<>), (/=)
52 , (&&), (||), not, any, all
55 , elem, die, mod, div, const, either
56 , curry, uncurry, repeat
65 import Prelude (Enum, Bounded, minBound, maxBound, putStrLn)
66 -- TODO import functions optimized in Utils.Count
67 -- import Protolude hiding (head, last, all, any, sum, product, length)
68 -- import Gargantext.Utils.Count
69 import qualified Data.List as L hiding (head, sum)
70 import qualified Control.Monad as M
71 import qualified Data.Map as M
72 import Data.Map.Strict (insertWith)
73 import qualified Data.Vector as V
74 import Safe (headMay, lastMay, initMay, tailMay)
75 import Text.Show (Show(), show)
76 import Text.Read (Read())
77 import Data.String.Conversions (cs)
80 printDebug :: (Show a, MonadBase IO m) => [Char] -> a -> m ()
81 printDebug msg x = liftBase . putStrLn $ msg <> " " <> show x
82 -- printDebug _ _ = pure ()
85 -- | splitEvery n == chunkAlong n n
86 splitEvery :: Int -> [a] -> [[a]]
89 let (h,t) = L.splitAt n xs
95 -- | Function to split a range into chunks
96 -- if step == grain then linearity (splitEvery)
97 -- elif step < grain then overlapping
98 -- else dotted with holes
99 -- TODO FIX BUG if Steps*Grain /= length l
100 -- chunkAlong 10 10 [1..15] == [1..10]
101 -- BUG: what about the rest of (divMod 15 10)?
102 -- TODO: chunkAlongNoRest or chunkAlongWithRest
103 -- default behavior: NoRest
105 chunkAlong :: Eq a => Grain -> Step -> [a] -> [[a]]
106 chunkAlong a b l = case a >= length l of
108 False -> chunkAlong' a b l
110 chunkAlong' :: Eq a => Grain -> Step -> [a] -> [[a]]
111 chunkAlong' a b l = case a > 0 && b > 0 of
112 True -> chunkAlong'' a b l
113 False -> panic "ChunkAlong: Parameters should be > 0 and Grain > Step"
115 chunkAlong'' :: Eq a => Int -> Int -> [a] -> [[a]]
116 chunkAlong'' a b l = filter (/= []) $ only (while dropAlong)
119 while = takeWhile (\x -> length x >= a)
120 dropAlong = L.scanl (\x _y -> drop b x) l ([1..] :: [Integer])
122 -- | Optimized version (Vector)
123 chunkAlongV :: Int -> Int -> V.Vector a -> V.Vector (V.Vector a)
124 chunkAlongV a b l = only (while dropAlong)
126 only = V.map (V.take a)
127 while = V.takeWhile (\x -> V.length x >= a)
128 dropAlong = V.scanl (\x _y -> V.drop b x) l (V.fromList [1..])
130 -- | TODO Inverse of chunk ? unchunkAlong ?
131 -- unchunkAlong :: Int -> Int -> [[a]] -> [a]
132 -- unchunkAlong = undefined
135 -- splitAlong [2,3,4] ("helloworld" :: [Char]) == ["he", "llo", "worl", "d"]
136 splitAlong :: [Int] -> [Char] -> [[Char]]
137 splitAlong _ [] = [] -- No list? done
138 splitAlong [] xs = [xs] -- No place to split at? Return the remainder
139 splitAlong (x:xs) ys = take x ys : splitAlong xs (drop x ys)
140 -- take until our split spot, recurse with next split spot and list remainder
142 takeWhileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
143 takeWhileM _ [] = return []
144 takeWhileM p (a:as) = do
148 vs <- takeWhileM p as
153 -- To select the right algorithme according to the type:
154 -- https://github.com/mikeizbicki/ifcxt
156 sumSimple :: Num a => [a] -> a
157 sumSimple = L.foldl' (+) 0
159 -- | https://en.wikipedia.org/wiki/Kahan_summation_algorithm
160 sumKahan :: Num a => [a] -> a
161 sumKahan = snd . L.foldl' go (0,0)
163 go (c,t) i = ((t'-t)-y,t')
168 -- | compute part of the dict
169 count2map :: (Ord k, Foldable t) => t k -> Map k Double
170 count2map xs = M.map (/ (fromIntegral (length xs))) (count2map' xs)
172 -- | insert in a dict
173 count2map' :: (Ord k, Foldable t) => t k -> Map k Double
174 count2map' xs = L.foldl' (\x y -> insertWith (+) y 1 x) M.empty xs
176 trunc :: (RealFrac a, Integral c, Integral b) => b -> a -> c
177 trunc n = truncate . (* 10^n)
179 trunc' :: Int -> Double -> Double
180 trunc' n x = fromIntegral $ truncate $ (x * 10^n)
182 ------------------------------------------------------------------------
183 bool2num :: Num a => Bool -> a
187 bool2double :: Bool -> Double
188 bool2double = bool2num
190 bool2int :: Bool -> Int
192 ------------------------------------------------------------------------
194 -- Normalizing && scaling data
195 scale :: [Double] -> [Double]
198 scaleMinMax :: [Double] -> [Double]
199 scaleMinMax xs = map (\x -> (x - mi / (ma - mi + 1) )) xs'
205 scaleNormalize :: [Double] -> [Double]
206 scaleNormalize xs = map (\x -> (x - v / (m + 1))) xs'
212 normalize :: [Double] -> [Double]
213 normalize as = normalizeWith identity as
215 normalizeWith :: Fractional b => (a -> b) -> [a] -> [b]
216 normalizeWith extract bs = map (\x -> x/(sum bs')) bs'
220 -- Zip functions to add
221 zipFst :: ([b] -> [a]) -> [b] -> [(a, b)]
222 zipFst f xs = zip (f xs) xs
224 zipSnd :: ([a] -> [b]) -> [a] -> [(a, b)]
225 zipSnd f xs = zip xs (f xs)
228 maximumWith :: (Ord a1, Foldable t) => (a2 -> a1) -> t a2 -> a2
229 maximumWith f = L.maximumBy (compare `on` f)
231 -- | To get all combinations of a list with no
232 -- repetition and apply a function to the resulting list of pairs
233 listToCombi :: forall a b. (a -> b) -> [a] -> [(b,b)]
234 listToCombi f l = [ (f x, f y) | (x:rest) <- L.tails l, y <- rest ]
236 ------------------------------------------------------------------------
237 -- Empty List Sugar Error Handling
238 -- TODO add Garg Monad Errors
240 listSafe1 :: Text -> ([a] -> Maybe a)
242 listSafe1 s f e xs = maybe (panic $ h <> e) identity (f xs)
244 h = "[ERR][Gargantext] Empty list for " <> s <> " in "
246 head' :: Text -> [a] -> a
247 head' = listSafe1 "head" headMay
249 last' :: Text -> [a] -> a
250 last' = listSafe1 "last" lastMay
252 ------------------------------------------------------------------------
254 listSafeN :: Text -> ([a] -> Maybe [a])
255 -> Text -> [a] -> [a]
256 listSafeN s f e xs = maybe (panic $ h <> e) identity (f xs)
258 h = "[ERR][Gargantext] Empty list for " <> s <> " in "
260 tail' :: Text -> [a] -> [a]
261 tail' = listSafeN "tail" tailMay
263 init' :: Text -> [a] -> [a]
264 init' = listSafeN "init" initMay
266 ------------------------------------------------------------------------
267 --- Some Statistics sugar functions
268 -- Exponential Average
269 eavg :: [Double] -> Double
270 eavg (x:xs) = a*x + (1-a)*(eavg xs)
275 mean :: Fractional a => [a] -> a
276 mean xs = sum xs / fromIntegral (length xs)
278 sumMaybe :: Num a => [Maybe a] -> Maybe a
279 sumMaybe = fmap sum . M.sequence
281 variance :: Floating a => [a] -> a
282 variance xs = sum ys / (fromIntegral (length xs) - 1)
285 ys = map (\x -> (x - m) ** 2) xs
287 deviation :: Floating a => [a] -> a
288 deviation = sqrt . variance
290 movingAverage :: (Eq b, Fractional b) => Int -> [b] -> [b]
291 movingAverage steps xs = map mean $ chunkAlong steps 1 xs
293 ma :: [Double] -> [Double]
296 -----------------------------------------------------------------------
297 -----------------------------------------------------------------------
298 --- Map in Map = Map2
299 -- To avoid Map (a,a) b
300 type Map2 a b = Map a (Map a b)
311 -----------------------------------------------
313 foldM' :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
314 foldM' _ z [] = return z
315 foldM' f z (x:xs) = do
317 z' `seq` foldM' f z' xs