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
13 {-# OPTIONS_GHC -fno-warn-orphans #-}
14 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
16 module Gargantext.Prelude
17 ( module Gargantext.Prelude
20 , module GHC.Err.Located
33 import Control.Monad.Base (MonadBase(..))
34 import GHC.Exts (sortWith)
35 import GHC.Err.Located (undefined)
36 import GHC.Real (round)
37 import Data.Map (Map, lookup)
38 import Data.Maybe (isJust, fromJust, maybe)
39 import Data.Monoid (Monoid, mempty)
40 import Data.Semigroup (Semigroup, (<>))
41 import Data.Text (Text)
42 import Data.Typeable (Typeable)
43 import Protolude ( Bool(True, False), Int, Int64, Double, Integer
44 , Fractional, Num, Maybe(Just,Nothing)
45 , Enum, Bounded, Float
48 , pure, (>>=), (=<<), (<*>), (<$>), (<&>), (>>)
50 , Ord, Integral, Foldable, RealFrac, Monad, filter
51 , reverse, map, mapM, zip, drop, take, zipWith
52 , sum, fromIntegral, length, fmap, foldl, foldl'
53 , takeWhile, sqrt, identity
54 , abs, min, max, maximum, minimum, return, snd, truncate
55 , (+), (*), (/), (-), (.), ($), (&), (**), (^), (<), (>), log
56 , Eq, (==), (>=), (<=), (<>), (/=), xor
57 , (&&), (||), not, any, all
60 , elem, die, mod, div, const, either
61 , curry, uncurry, repeat
70 import Prelude (Enum, Bounded, minBound, maxBound, putStrLn)
71 -- TODO import functions optimized in Utils.Count
72 -- import Protolude hiding (head, last, all, any, sum, product, length)
73 -- import Gargantext.Utils.Count
74 import qualified Data.List as L hiding (head, sum)
75 import qualified Control.Monad as M
76 import qualified Data.Map as M
77 import Data.Map.Strict (insertWith)
78 import qualified Data.Vector as V
79 import Safe (headMay, lastMay, initMay, tailMay)
80 import Text.Show (Show(), show)
81 import Text.Read (Read())
82 import Data.String.Conversions (cs)
85 printDebug :: (Show a, MonadBase IO m) => [Char] -> a -> m ()
86 printDebug msg x = liftBase . putStrLn $ msg <> " " <> show x
87 -- printDebug _ _ = pure ()
90 -- | splitEvery n == chunkAlong n n
91 splitEvery :: Int -> [a] -> [[a]]
94 let (h,t) = L.splitAt n xs
100 -- | Function to split a range into chunks
101 -- if step == grain then linearity (splitEvery)
102 -- elif step < grain then overlapping
103 -- else dotted with holes
104 -- TODO FIX BUG if Steps*Grain /= length l
105 -- chunkAlong 10 10 [1..15] == [1..10]
106 -- BUG: what about the rest of (divMod 15 10)?
107 -- TODO: chunkAlongNoRest or chunkAlongWithRest
108 -- default behavior: NoRest
110 chunkAlong :: Eq a => Grain -> Step -> [a] -> [[a]]
111 chunkAlong a b l = case a >= length l of
113 False -> chunkAlong' a b l
115 chunkAlong' :: Eq a => Grain -> Step -> [a] -> [[a]]
116 chunkAlong' a b l = case a > 0 && b > 0 of
117 True -> chunkAlong'' a b l
118 False -> panic "ChunkAlong: Parameters should be > 0 and Grain > Step"
120 chunkAlong'' :: Eq a => Int -> Int -> [a] -> [[a]]
121 chunkAlong'' a b l = filter (/= []) $ only (while dropAlong)
124 while = takeWhile (\x -> length x >= a)
125 dropAlong = L.scanl (\x _y -> drop b x) l ([1..] :: [Integer])
127 -- | Optimized version (Vector)
128 chunkAlongV :: Int -> Int -> V.Vector a -> V.Vector (V.Vector a)
129 chunkAlongV a b l = only (while dropAlong)
131 only = V.map (V.take a)
132 while = V.takeWhile (\x -> V.length x >= a)
133 dropAlong = V.scanl (\x _y -> V.drop b x) l (V.fromList [1..])
135 -- | TODO Inverse of chunk ? unchunkAlong ?
136 -- unchunkAlong :: Int -> Int -> [[a]] -> [a]
137 -- unchunkAlong = undefined
140 -- splitAlong [2,3,4] ("helloworld" :: [Char]) == ["he", "llo", "worl", "d"]
141 splitAlong :: [Int] -> [Char] -> [[Char]]
142 splitAlong _ [] = [] -- No list? done
143 splitAlong [] xs = [xs] -- No place to split at? Return the remainder
144 splitAlong (x:xs) ys = take x ys : splitAlong xs (drop x ys)
145 -- take until our split spot, recurse with next split spot and list remainder
147 takeWhileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
148 takeWhileM _ [] = return []
149 takeWhileM p (a:as) = do
153 vs <- takeWhileM p as
158 -- To select the right algorithme according to the type:
159 -- https://github.com/mikeizbicki/ifcxt
161 sumSimple :: Num a => [a] -> a
162 sumSimple = L.foldl' (+) 0
164 -- | https://en.wikipedia.org/wiki/Kahan_summation_algorithm
165 sumKahan :: Num a => [a] -> a
166 sumKahan = snd . L.foldl' go (0,0)
168 go (c,t) i = ((t'-t)-y,t')
173 -- | compute part of the dict
174 count2map :: (Ord k, Foldable t) => t k -> Map k Double
175 count2map xs = M.map (/ (fromIntegral (length xs))) (count2map' xs)
177 -- | insert in a dict
178 count2map' :: (Ord k, Foldable t) => t k -> Map k Double
179 count2map' xs = L.foldl' (\x y -> insertWith (+) y 1 x) M.empty xs
181 trunc :: (RealFrac a, Integral c, Integral b) => b -> a -> c
182 trunc n = truncate . (* 10^n)
184 trunc' :: Int -> Double -> Double
185 trunc' n x = fromIntegral $ truncate $ (x * 10^n)
187 ------------------------------------------------------------------------
188 bool2num :: Num a => Bool -> a
192 bool2double :: Bool -> Double
193 bool2double = bool2num
195 bool2int :: Bool -> Int
197 ------------------------------------------------------------------------
199 -- Normalizing && scaling data
200 scale :: [Double] -> [Double]
203 scaleMinMax :: [Double] -> [Double]
204 scaleMinMax xs = map (\x -> (x - mi / (ma - mi + 1) )) xs'
210 scaleNormalize :: [Double] -> [Double]
211 scaleNormalize xs = map (\x -> (x - v / (m + 1))) xs'
217 normalize :: [Double] -> [Double]
218 normalize as = normalizeWith identity as
220 normalizeWith :: Fractional b => (a -> b) -> [a] -> [b]
221 normalizeWith extract bs = map (\x -> x/(sum bs')) bs'
225 -- Zip functions to add
226 zipFst :: ([b] -> [a]) -> [b] -> [(a, b)]
227 zipFst f xs = zip (f xs) xs
229 zipSnd :: ([a] -> [b]) -> [a] -> [(a, b)]
230 zipSnd f xs = zip xs (f xs)
233 maximumWith :: (Ord a1, Foldable t) => (a2 -> a1) -> t a2 -> a2
234 maximumWith f = L.maximumBy (compare `on` f)
236 -- | To get all combinations of a list with no
237 -- repetition and apply a function to the resulting list of pairs
238 listToCombi :: forall a b. (a -> b) -> [a] -> [(b,b)]
239 listToCombi f l = [ (f x, f y) | (x:rest) <- L.tails l, y <- rest ]
241 ------------------------------------------------------------------------
242 -- Empty List Sugar Error Handling
243 -- TODO add Garg Monad Errors
245 listSafe1 :: Text -> ([a] -> Maybe a)
247 listSafe1 s f e xs = maybe (panic $ h <> e) identity (f xs)
249 h = "[ERR][Gargantext] Empty list for " <> s <> " in "
251 head' :: Text -> [a] -> a
252 head' = listSafe1 "head" headMay
254 last' :: Text -> [a] -> a
255 last' = listSafe1 "last" lastMay
257 ------------------------------------------------------------------------
259 listSafeN :: Text -> ([a] -> Maybe [a])
260 -> Text -> [a] -> [a]
261 listSafeN s f e xs = maybe (panic $ h <> e) identity (f xs)
263 h = "[ERR][Gargantext] Empty list for " <> s <> " in "
265 tail' :: Text -> [a] -> [a]
266 tail' = listSafeN "tail" tailMay
268 init' :: Text -> [a] -> [a]
269 init' = listSafeN "init" initMay
271 ------------------------------------------------------------------------
272 --- Some Statistics sugar functions
273 -- Exponential Average
274 eavg :: [Double] -> Double
275 eavg (x:xs) = a*x + (1-a)*(eavg xs)
280 mean :: Fractional a => [a] -> a
281 mean xs = sum xs / fromIntegral (length xs)
283 sumMaybe :: Num a => [Maybe a] -> Maybe a
284 sumMaybe = fmap sum . M.sequence
286 variance :: Floating a => [a] -> a
287 variance xs = sum ys / (fromIntegral (length xs) - 1)
290 ys = map (\x -> (x - m) ** 2) xs
292 deviation :: Floating a => [a] -> a
293 deviation = sqrt . variance
295 movingAverage :: (Eq b, Fractional b) => Int -> [b] -> [b]
296 movingAverage steps xs = map mean $ chunkAlong steps 1 xs
298 -----------------------------------------------------------------------
299 -----------------------------------------------------------------------
300 --- Map in Map = Map2
301 -- To avoid Map (a,a) b
302 type Map2 a b = Map a (Map a b)
313 -----------------------------------------------------------------------
314 foldM' :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
315 foldM' _ z [] = return z
316 foldM' f z (x:xs) = do
318 z' `seq` foldM' f z' xs
320 -----------------------------------------------------------------------
321 -- | Instance for basic numerals
322 -- See the difference between Double and (Int Or Integer)
323 instance Monoid Double where
326 instance Semigroup Double where
330 instance Monoid Int where
333 instance Semigroup Int where
336 instance Monoid Integer where
339 instance Semigroup Integer where