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 #-}
15 module Gargantext.Prelude
16 ( module Gargantext.Prelude
19 , 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 Data.Typeable (Typeable)
40 import Protolude ( Bool(True, False), Int, Int64, Double, Integer
41 , Fractional, Num, Maybe(Just,Nothing)
42 , Enum, Bounded, Float
44 , pure, (>>=), (=<<), (<*>), (<$>), (<&>), (>>)
46 , Ord, Integral, Foldable, RealFrac, Monad, filter
47 , reverse, map, mapM, zip, drop, take, zipWith
48 , sum, fromIntegral, length, fmap, foldl, foldl'
49 , takeWhile, sqrt, identity
50 , abs, min, max, maximum, minimum, return, snd, truncate
51 , (+), (*), (/), (-), (.), ($), (&), (**), (^), (<), (>), log
52 , Eq, (==), (>=), (<=), (<>), (/=)
53 , (&&), (||), not, any, all
56 , elem, die, mod, div, const, either
57 , curry, uncurry, repeat
66 import Prelude (Enum, Bounded, minBound, maxBound, putStrLn)
67 -- TODO import functions optimized in Utils.Count
68 -- import Protolude hiding (head, last, all, any, sum, product, length)
69 -- import Gargantext.Utils.Count
70 import qualified Data.List as L hiding (head, sum)
71 import qualified Control.Monad as M
72 import qualified Data.Map as M
73 import Data.Map.Strict (insertWith)
74 import qualified Data.Vector as V
75 import Safe (headMay, lastMay, initMay, tailMay)
76 import Text.Show (Show(), show)
77 import Text.Read (Read())
78 import Data.String.Conversions (cs)
81 printDebug :: (Show a, MonadBase IO m) => [Char] -> a -> m ()
82 printDebug msg x = liftBase . putStrLn $ msg <> " " <> show x
83 -- printDebug _ _ = pure ()
86 -- | splitEvery n == chunkAlong n n
87 splitEvery :: Int -> [a] -> [[a]]
90 let (h,t) = L.splitAt n xs
96 -- | Function to split a range into chunks
97 -- if step == grain then linearity (splitEvery)
98 -- elif step < grain then overlapping
99 -- else dotted with holes
100 -- TODO FIX BUG if Steps*Grain /= length l
101 -- chunkAlong 10 10 [1..15] == [1..10]
102 -- BUG: what about the rest of (divMod 15 10)?
103 -- TODO: chunkAlongNoRest or chunkAlongWithRest
104 -- default behavior: NoRest
106 chunkAlong :: Eq a => Grain -> Step -> [a] -> [[a]]
107 chunkAlong a b l = case a >= length l of
109 False -> chunkAlong' a b l
111 chunkAlong' :: Eq a => Grain -> Step -> [a] -> [[a]]
112 chunkAlong' a b l = case a > 0 && b > 0 of
113 True -> chunkAlong'' a b l
114 False -> panic "ChunkAlong: Parameters should be > 0 and Grain > Step"
116 chunkAlong'' :: Eq a => Int -> Int -> [a] -> [[a]]
117 chunkAlong'' a b l = filter (/= []) $ only (while dropAlong)
120 while = takeWhile (\x -> length x >= a)
121 dropAlong = L.scanl (\x _y -> drop b x) l ([1..] :: [Integer])
123 -- | Optimized version (Vector)
124 chunkAlongV :: Int -> Int -> V.Vector a -> V.Vector (V.Vector a)
125 chunkAlongV a b l = only (while dropAlong)
127 only = V.map (V.take a)
128 while = V.takeWhile (\x -> V.length x >= a)
129 dropAlong = V.scanl (\x _y -> V.drop b x) l (V.fromList [1..])
131 -- | TODO Inverse of chunk ? unchunkAlong ?
132 -- unchunkAlong :: Int -> Int -> [[a]] -> [a]
133 -- unchunkAlong = undefined
136 -- splitAlong [2,3,4] ("helloworld" :: [Char]) == ["he", "llo", "worl", "d"]
137 splitAlong :: [Int] -> [Char] -> [[Char]]
138 splitAlong _ [] = [] -- No list? done
139 splitAlong [] xs = [xs] -- No place to split at? Return the remainder
140 splitAlong (x:xs) ys = take x ys : splitAlong xs (drop x ys)
141 -- take until our split spot, recurse with next split spot and list remainder
143 takeWhileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
144 takeWhileM _ [] = return []
145 takeWhileM p (a:as) = do
149 vs <- takeWhileM p as
154 -- To select the right algorithme according to the type:
155 -- https://github.com/mikeizbicki/ifcxt
157 sumSimple :: Num a => [a] -> a
158 sumSimple = L.foldl' (+) 0
160 -- | https://en.wikipedia.org/wiki/Kahan_summation_algorithm
161 sumKahan :: Num a => [a] -> a
162 sumKahan = snd . L.foldl' go (0,0)
164 go (c,t) i = ((t'-t)-y,t')
169 -- | compute part of the dict
170 count2map :: (Ord k, Foldable t) => t k -> Map k Double
171 count2map xs = M.map (/ (fromIntegral (length xs))) (count2map' xs)
173 -- | insert in a dict
174 count2map' :: (Ord k, Foldable t) => t k -> Map k Double
175 count2map' xs = L.foldl' (\x y -> insertWith (+) y 1 x) M.empty xs
177 trunc :: (RealFrac a, Integral c, Integral b) => b -> a -> c
178 trunc n = truncate . (* 10^n)
180 trunc' :: Int -> Double -> Double
181 trunc' n x = fromIntegral $ truncate $ (x * 10^n)
183 ------------------------------------------------------------------------
184 bool2num :: Num a => Bool -> a
188 bool2double :: Bool -> Double
189 bool2double = bool2num
191 bool2int :: Bool -> Int
193 ------------------------------------------------------------------------
195 -- Normalizing && scaling data
196 scale :: [Double] -> [Double]
199 scaleMinMax :: [Double] -> [Double]
200 scaleMinMax xs = map (\x -> (x - mi / (ma - mi + 1) )) xs'
206 scaleNormalize :: [Double] -> [Double]
207 scaleNormalize xs = map (\x -> (x - v / (m + 1))) xs'
213 normalize :: [Double] -> [Double]
214 normalize as = normalizeWith identity as
216 normalizeWith :: Fractional b => (a -> b) -> [a] -> [b]
217 normalizeWith extract bs = map (\x -> x/(sum bs')) bs'
221 -- Zip functions to add
222 zipFst :: ([b] -> [a]) -> [b] -> [(a, b)]
223 zipFst f xs = zip (f xs) xs
225 zipSnd :: ([a] -> [b]) -> [a] -> [(a, b)]
226 zipSnd f xs = zip xs (f xs)
229 maximumWith :: (Ord a1, Foldable t) => (a2 -> a1) -> t a2 -> a2
230 maximumWith f = L.maximumBy (compare `on` f)
232 -- | To get all combinations of a list with no
233 -- repetition and apply a function to the resulting list of pairs
234 listToCombi :: forall a b. (a -> b) -> [a] -> [(b,b)]
235 listToCombi f l = [ (f x, f y) | (x:rest) <- L.tails l, y <- rest ]
237 ------------------------------------------------------------------------
238 -- Empty List Sugar Error Handling
239 -- TODO add Garg Monad Errors
241 listSafe1 :: Text -> ([a] -> Maybe a)
243 listSafe1 s f e xs = maybe (panic $ h <> e) identity (f xs)
245 h = "[ERR][Gargantext] Empty list for " <> s <> " in "
247 head' :: Text -> [a] -> a
248 head' = listSafe1 "head" headMay
250 last' :: Text -> [a] -> a
251 last' = listSafe1 "last" lastMay
253 ------------------------------------------------------------------------
255 listSafeN :: Text -> ([a] -> Maybe [a])
256 -> Text -> [a] -> [a]
257 listSafeN s f e xs = maybe (panic $ h <> e) identity (f xs)
259 h = "[ERR][Gargantext] Empty list for " <> s <> " in "
261 tail' :: Text -> [a] -> [a]
262 tail' = listSafeN "tail" tailMay
264 init' :: Text -> [a] -> [a]
265 init' = listSafeN "init" initMay
267 ------------------------------------------------------------------------
268 --- Some Statistics sugar functions
269 -- Exponential Average
270 eavg :: [Double] -> Double
271 eavg (x:xs) = a*x + (1-a)*(eavg xs)
276 mean :: Fractional a => [a] -> a
277 mean xs = sum xs / fromIntegral (length xs)
279 sumMaybe :: Num a => [Maybe a] -> Maybe a
280 sumMaybe = fmap sum . M.sequence
282 variance :: Floating a => [a] -> a
283 variance xs = sum ys / (fromIntegral (length xs) - 1)
286 ys = map (\x -> (x - m) ** 2) xs
288 deviation :: Floating a => [a] -> a
289 deviation = sqrt . variance
291 movingAverage :: (Eq b, Fractional b) => Int -> [b] -> [b]
292 movingAverage steps xs = map mean $ chunkAlong steps 1 xs
294 -----------------------------------------------------------------------
295 -----------------------------------------------------------------------
296 --- Map in Map = Map2
297 -- To avoid Map (a,a) b
298 type Map2 a b = Map a (Map a b)
309 -----------------------------------------------
311 foldM' :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
312 foldM' _ z [] = return z
313 foldM' f z (x:xs) = do
315 z' `seq` foldM' f z' xs