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
10 Here is a longer description of this module, containing some
11 commentary with @some markup@.
14 {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
15 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
17 {-# LANGUAGE NoImplicitPrelude #-}
18 {-# LANGUAGE OverloadedStrings #-}
19 {-# LANGUAGE RankNTypes #-}
21 module Gargantext.Prelude
22 ( module Gargantext.Prelude
25 , module GHC.Err.Located
35 import GHC.Exts (sortWith)
36 import GHC.Err.Located (undefined)
37 import GHC.Real (round)
38 import Control.Monad.IO.Class (MonadIO)
39 import Data.Maybe (isJust, fromJust, maybe)
40 import Data.Text (Text)
41 import Protolude ( Bool(True, False), Int, Int64, Double, Integer
42 , Fractional, Num, Maybe(Just,Nothing)
43 , Enum, Bounded, Float
45 , pure, (>>=), (=<<), (<*>), (<$>)
48 , Ord, Integral, Foldable, RealFrac, Monad, filter
49 , reverse, map, mapM, zip, drop, take, zipWith
50 , sum, fromIntegral, length, fmap, foldl, foldl'
51 , takeWhile, sqrt, identity
52 , abs, min, max, maximum, minimum, return, snd, truncate
53 , (+), (*), (/), (-), (.), ($), (&), (**), (^), (<), (>), log
54 , Eq, (==), (>=), (<=), (<>), (/=)
55 , (&&), (||), not, any, all
57 , elem, die, mod, div, const, either
58 , curry, uncurry, repeat
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
73 import qualified Data.Map as M
75 import Data.Map.Strict (insertWith)
76 import qualified Data.Vector as V
77 import Safe (headMay, lastMay, initMay, tailMay)
78 import Text.Show (Show(), show)
79 import Text.Read (Read())
80 import Data.String.Conversions (cs)
83 printDebug :: (Show a, MonadIO m) => [Char] -> a -> m ()
84 printDebug msg x = putStrLn $ msg <> " " <> show x
85 -- printDebug _ _ = pure ()
88 map2 :: (t -> b) -> [[t]] -> [[b]]
89 map2 fun = map (map fun)
92 -- Some Statistics sugar functions
93 -- Exponential Average
94 eavg :: [Double] -> Double
95 eavg (x:xs) = a*x + (1-a)*(eavg xs)
100 mean :: Fractional a => [a] -> a
101 mean xs = sum xs / fromIntegral (length xs)
104 sumMaybe :: Num a => [Maybe a] -> Maybe a
105 sumMaybe = fmap sum . M.sequence
107 variance :: Floating a => [a] -> a
108 variance xs = sum ys / (fromIntegral (length xs) - 1)
111 ys = map (\x -> (x - m) ** 2) xs
114 deviation :: Floating a => [a] -> a
115 deviation = sqrt . variance
117 movingAverage :: (Eq b, Fractional b) => Int -> [b] -> [b]
118 movingAverage steps xs = map mean $ chunkAlong steps 1 xs
120 ma :: [Double] -> [Double]
123 -- | splitEvery n == chunkAlong n n
124 splitEvery :: Int -> [a] -> [[a]]
127 let (h,t) = L.splitAt n xs
128 in h : splitEvery n t
133 -- | Function to split a range into chunks
134 -- if step == grain then linearity (splitEvery)
135 -- elif step < grain then overlapping
136 -- else dotted with holes
137 -- TODO FIX BUG if Steps*Grain /= length l
138 -- chunkAlong 10 10 [1..15] == [1..10]
139 -- BUG: what about the rest of (divMod 15 10)?
140 -- TODO: chunkAlongNoRest or chunkAlongWithRest
141 -- default behavior: NoRest
143 chunkAlong :: Eq a => Grain -> Step -> [a] -> [[a]]
144 chunkAlong a b l = case a >= length l of
146 False -> chunkAlong' a b l
148 chunkAlong' :: Eq a => Grain -> Step -> [a] -> [[a]]
149 chunkAlong' a b l = case a > 0 && b > 0 of
150 True -> chunkAlong'' a b l
151 False -> panic "ChunkAlong: Parameters should be > 0 and Grain > Step"
153 chunkAlong'' :: Eq a => Int -> Int -> [a] -> [[a]]
154 chunkAlong'' a b l = filter (/= []) $ only (while dropAlong)
157 while = takeWhile (\x -> length x >= a)
158 dropAlong = L.scanl (\x _y -> drop b x) l ([1..] :: [Integer])
160 -- | Optimized version (Vector)
161 chunkAlongV :: Int -> Int -> V.Vector a -> V.Vector (V.Vector a)
162 chunkAlongV a b l = only (while dropAlong)
164 only = V.map (V.take a)
165 while = V.takeWhile (\x -> V.length x >= a)
166 dropAlong = V.scanl (\x _y -> V.drop b x) l (V.fromList [1..])
168 -- | TODO Inverse of chunk ? unchunkAlong ?
169 -- unchunkAlong :: Int -> Int -> [[a]] -> [a]
170 -- unchunkAlong = undefined
173 -- splitAlong [2,3,4] ("helloworld" :: [Char]) == ["he", "llo", "worl", "d"]
174 splitAlong :: [Int] -> [Char] -> [[Char]]
175 splitAlong _ [] = [] -- No list? done
176 splitAlong [] xs = [xs] -- No place to split at? Return the remainder
177 splitAlong (x:xs) ys = take x ys : splitAlong xs (drop x ys) -- take until our split spot, recurse with next split spot and list remainder
179 takeWhileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
180 takeWhileM _ [] = return []
181 takeWhileM p (a:as) = do
185 vs <- takeWhileM p as
190 -- To select the right algorithme according to the type:
191 -- https://github.com/mikeizbicki/ifcxt
193 sumSimple :: Num a => [a] -> a
194 sumSimple = L.foldl' (+) 0
196 -- | https://en.wikipedia.org/wiki/Kahan_summation_algorithm
197 sumKahan :: Num a => [a] -> a
198 sumKahan = snd . L.foldl' go (0,0)
200 go (c,t) i = ((t'-t)-y,t')
205 -- | compute part of the dict
206 count2map :: (Ord k, Foldable t) => t k -> Map k Double
207 count2map xs = M.map (/ (fromIntegral (length xs))) (count2map' xs)
209 -- | insert in a dict
210 count2map' :: (Ord k, Foldable t) => t k -> Map k Double
211 count2map' xs = L.foldl' (\x y -> insertWith (+) y 1 x) M.empty xs
214 trunc :: (RealFrac a, Integral c, Integral b) => b -> a -> c
215 trunc n = truncate . (* 10^n)
217 trunc' :: Int -> Double -> Double
218 trunc' n x = fromIntegral $ truncate $ (x * 10^n)
221 ------------------------------------------------------------------------
222 bool2num :: Num a => Bool -> a
226 bool2double :: Bool -> Double
227 bool2double = bool2num
229 bool2int :: Bool -> Int
231 ------------------------------------------------------------------------
233 -- Normalizing && scaling data
234 scale :: [Double] -> [Double]
237 scaleMinMax :: [Double] -> [Double]
238 scaleMinMax xs = map (\x -> (x - mi / (ma - mi + 1) )) xs'
244 scaleNormalize :: [Double] -> [Double]
245 scaleNormalize xs = map (\x -> (x - v / (m + 1))) xs'
251 normalize :: [Double] -> [Double]
252 normalize as = normalizeWith identity as
254 normalizeWith :: Fractional b => (a -> b) -> [a] -> [b]
255 normalizeWith extract bs = map (\x -> x/(sum bs')) bs'
259 -- Zip functions to add
260 zipFst :: ([b] -> [a]) -> [b] -> [(a, b)]
261 zipFst f xs = zip (f xs) xs
263 zipSnd :: ([a] -> [b]) -> [a] -> [(a, b)]
264 zipSnd f xs = zip xs (f xs)
267 maximumWith :: (Ord a1, Foldable t) => (a2 -> a1) -> t a2 -> a2
268 maximumWith f = L.maximumBy (compare `on` f)
271 -- | To get all combinations of a list with no repetition and apply a function to the resulting list of pairs
272 listToCombi :: forall a b. (a -> b) -> [a] -> [(b,b)]
273 listToCombi f l = [ (f x, f y) | (x:rest) <- L.tails l, y <- rest ]
275 ------------------------------------------------------------------------
276 -- Empty List Sugar Error Handling
277 -- TODO add Garg Monad Errors
279 listSafe1 :: Text -> ([a] -> Maybe a)
281 listSafe1 s f e xs = maybe (panic $ h <> e) identity (f xs)
283 h = "[ERR][Gargantext] Empty list for " <> s <> " in "
285 head' :: Text -> [a] -> a
286 head' = listSafe1 "head" headMay
288 last' :: Text -> [a] -> a
289 last' = listSafe1 "last" lastMay
291 ------------------------------------------------------------------------
293 listSafeN :: Text -> ([a] -> Maybe [a])
294 -> Text -> [a] -> [a]
295 listSafeN s f e xs = maybe (panic $ h <> e) identity (f xs)
297 h = "[ERR][Gargantext] Empty list for " <> s <> " in "
299 tail' :: Text -> [a] -> [a]
300 tail' = listSafeN "tail" tailMay
302 init' :: Text -> [a] -> [a]
303 init' = listSafeN "init" initMay