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 {-# LANGUAGE FlexibleContexts #-}
16 {-# LANGUAGE NoImplicitPrelude #-}
17 {-# LANGUAGE OverloadedStrings #-}
18 {-# LANGUAGE RankNTypes #-}
20 module Gargantext.Prelude
21 ( module Gargantext.Prelude
24 , module GHC.Err.Located
36 import Control.Monad.Base (MonadBase(..))
37 import GHC.Exts (sortWith)
38 import GHC.Err.Located (undefined)
39 import GHC.Real (round)
40 import Data.Maybe (isJust, fromJust, maybe)
41 import Data.Text (Text)
42 import Protolude ( Bool(True, False), Int, Int64, Double, Integer
43 , Fractional, Num, Maybe(Just,Nothing)
44 , Enum, Bounded, Float
46 , 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
58 , elem, die, mod, div, const, either
59 , curry, uncurry, repeat
67 import Prelude (Enum, Bounded, minBound, maxBound, putStrLn)
68 -- TODO import functions optimized in Utils.Count
69 -- import Protolude hiding (head, last, all, any, sum, product, length)
70 -- import Gargantext.Utils.Count
71 import qualified Data.List as L hiding (head, sum)
72 import qualified Control.Monad as M
75 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 ma :: [Double] -> [Double]
302 -----------------------------------------------------------------------
306 fib n = fib (n-1) + fib (n-2)