packages feed

dataframe 0.3.1.2 → 0.3.2.0

raw patch · 51 files changed

+4189/−993 lines, 51 filesdep ~basedep ~containersdep ~textPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base, containers, text

API changes (from Hackage documentation)

- DataFrame: defaultOptions :: ReadOptions
- DataFrame.Display.Web.ChartJs: minifiedChartJs :: Text
- DataFrame.Functions: standardDeviation :: (Columnable a, Real a, Unbox a) => Expr a -> Expr Double
- DataFrame.IO.CSV: defaultOptions :: ReadOptions
- DataFrame.IO.Parquet.Page: safeHead :: [a] -> Maybe a
- DataFrame.Internal.Schema: data Schema
+ DataFrame: [dateFormat] :: ReadOptions -> String
+ DataFrame: defaultReadOptions :: ReadOptions
+ DataFrame: fromAny :: Columnable a => Any -> Maybe a
+ DataFrame: fromRows :: [Text] -> [[Any]] -> DataFrame
+ DataFrame: hasElemType :: Columnable a => Column -> Bool
+ DataFrame: readCsvWithOpts :: ReadOptions -> FilePath -> IO DataFrame
+ DataFrame: toAny :: Columnable a => a -> Any
+ DataFrame: toRowList :: DataFrame -> [Row]
+ DataFrame: type Row = Vector Any
+ DataFrame.Display.Web.Plot: chartJsScript :: Text
+ DataFrame.Functions: BeamConfig :: Int -> Int -> (Vector Double -> Vector Double -> Maybe Double) -> BeamConfig
+ DataFrame.Functions: [beamLength] :: BeamConfig -> Int
+ DataFrame.Functions: [rankingFunction] :: BeamConfig -> Vector Double -> Vector Double -> Maybe Double
+ DataFrame.Functions: [searchDepth] :: BeamConfig -> Int
+ DataFrame.Functions: data BeamConfig
+ DataFrame.Functions: eq :: (Columnable a, Eq a) => Expr a -> Expr a -> Expr Bool
+ DataFrame.Functions: fitRegression :: Text -> Int -> Int -> DataFrame -> Either String (Expr Double)
+ DataFrame.Functions: geq :: (Columnable a, Ord a, Eq a) => Expr a -> Expr a -> Expr Bool
+ DataFrame.Functions: gt :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr Bool
+ DataFrame.Functions: leq :: (Columnable a, Ord a, Eq a) => Expr a -> Expr a -> Expr Bool
+ DataFrame.Functions: lt :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr Bool
+ DataFrame.Functions: max :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr a
+ DataFrame.Functions: min :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr a
+ DataFrame.Functions: pow :: (Columnable a, Num a) => Int -> Expr a -> Expr a
+ DataFrame.Functions: relu :: (Columnable a, Num a) => Expr a -> Expr a
+ DataFrame.Functions: stddev :: (Columnable a, Real a, Unbox a) => Expr a -> Expr Double
+ DataFrame.IO.CSV: [dateFormat] :: ReadOptions -> String
+ DataFrame.IO.CSV: defaultReadOptions :: ReadOptions
+ DataFrame.IO.CSV: readCsvWithOpts :: ReadOptions -> FilePath -> IO DataFrame
+ DataFrame.IO.Parquet.Thrift: readTimestampType :: Bool -> TimeUnit -> ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType
+ DataFrame.Internal.Column: concatColumnsEither :: Column -> Column -> Column
+ DataFrame.Internal.Column: hasElemType :: Columnable a => Column -> Bool
+ DataFrame.Internal.Expression: instance (GHC.Classes.Eq a, GHC.Show.Show a) => GHC.Classes.Eq (DataFrame.Internal.Expression.Expr a)
+ DataFrame.Internal.Expression: replaceExpr :: (Columnable a, Columnable b, Columnable c) => Expr a -> Expr b -> Expr c -> Expr c
+ DataFrame.Internal.Row: (!?) :: [a] -> Int -> Maybe a
+ DataFrame.Internal.Row: fromAny :: Columnable a => Any -> Maybe a
+ DataFrame.Internal.Row: mkColumnFromRow :: Int -> [[Any]] -> Column
+ DataFrame.Internal.Schema: newtype Schema
+ DataFrame.Internal.Statistics: binIndex :: Double -> Double -> Int -> Double -> Int
+ DataFrame.Internal.Statistics: bincount :: Int -> Vector Int -> Vector Int
+ DataFrame.Internal.Statistics: jointBincount :: Int -> Vector Int -> Vector Int -> Vector Int
+ DataFrame.Internal.Statistics: meanSquaredError :: Vector Double -> Vector Double -> Maybe Double
+ DataFrame.Internal.Statistics: mutualInformationBinned :: Int -> Vector Double -> Vector Double -> Maybe Double
+ DataFrame.Operations.Core: fromRows :: [Text] -> [[Any]] -> DataFrame
- DataFrame: ReadOptions :: Bool -> Bool -> Bool -> Int -> ReadOptions
+ DataFrame: ReadOptions :: Bool -> Bool -> Bool -> Int -> String -> ReadOptions
- DataFrame.Functions: beamSearch :: DataFrame -> Int -> TypedColumn Double -> [Expr Double] -> Int -> Maybe (Expr Double)
+ DataFrame.Functions: beamSearch :: DataFrame -> BeamConfig -> TypedColumn Double -> [Expr Double] -> Maybe (Expr Double)
- DataFrame.Functions: deduplicate :: DataFrame -> [Expr Double] -> [Expr Double]
+ DataFrame.Functions: deduplicate :: DataFrame -> [Expr Double] -> [(Expr Double, TypedColumn Double)]
- DataFrame.Functions: pickTopN :: DataFrame -> TypedColumn Double -> Int -> [Expr Double] -> [Expr Double]
+ DataFrame.Functions: pickTopN :: DataFrame -> TypedColumn Double -> BeamConfig -> [(Expr Double, TypedColumn a)] -> [Expr Double]
- DataFrame.IO.CSV: ReadOptions :: Bool -> Bool -> Bool -> Int -> ReadOptions
+ DataFrame.IO.CSV: ReadOptions :: Bool -> Bool -> Bool -> Int -> String -> ReadOptions
- DataFrame.IO.Parquet.Thrift: readDecimalType :: LogicalType -> ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType
+ DataFrame.IO.Parquet.Thrift: readDecimalType :: Int32 -> Int32 -> ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType
- DataFrame.IO.Parquet.Thrift: readIntType :: LogicalType -> ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType
+ DataFrame.IO.Parquet.Thrift: readIntType :: Int8 -> Bool -> ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType
- DataFrame.IO.Parquet.Thrift: readTimeType :: LogicalType -> ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType
+ DataFrame.IO.Parquet.Thrift: readTimeType :: Bool -> TimeUnit -> ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType
- DataFrame.Internal.Expression: interpretAggregation :: Columnable a => GroupedDataFrame -> Expr a -> TypedColumn a
+ DataFrame.Internal.Expression: interpretAggregation :: Columnable a => GroupedDataFrame -> Expr a -> Either Column (TypedColumn a)
- DataFrame.Internal.Row: [Value] :: forall a. Columnable' a => a -> Any
+ DataFrame.Internal.Row: [Value] :: forall a. Columnable a => a -> Any
- DataFrame.Internal.Row: showValue :: Columnable' a => a -> Text
+ DataFrame.Internal.Row: showValue :: Columnable a => a -> Text
- DataFrame.Internal.Row: toAny :: Columnable' a => a -> Any
+ DataFrame.Internal.Row: toAny :: Columnable a => a -> Any
- DataFrame.Internal.Row: toRowList :: [Text] -> DataFrame -> [Row]
+ DataFrame.Internal.Row: toRowList :: DataFrame -> [Row]
- DataFrame.Operations.Typing: parseDefault :: Bool -> Column -> Column
+ DataFrame.Operations.Typing: parseDefault :: Bool -> String -> Column -> Column
- DataFrame.Operations.Typing: parseDefaults :: Bool -> DataFrame -> DataFrame
+ DataFrame.Operations.Typing: parseDefaults :: Bool -> String -> DataFrame -> DataFrame

Files

CHANGELOG.md view
@@ -1,5 +1,19 @@ # Revision history for dataframe +## 0.3.2.0+* Fix dataframe semigroup instance. Appending two rows of the same name but different types now gives a row of `Either a b` (work by @jhrcek).+* Fix left expansion of semigroup instance (work by @jhrcek). +* Added `hasElemType` function that can be used with `selectBy` to filter columns by type. E.g. `selectBy [byProperty (hasElemType @Int)] df.`+* Added basic support for program synthesis for feature generation (`synthesizeFeatureExpr`) and symbolic regression (`fitRegression`).+* Web plotting doesn't embed entire script anymore.+* Added `relu`, `min`, and `max` functions for expressions.+* Add `fromRows` function to build a dataframe from rows. Also add `toAny` function that converts a value to a dynamic-like Columnable value.+* `isNumeric` function now recognises `Integer` types.+* Added `readCsvWithOpts` function that allows read specification.+* Expose option to specify data formats when parsing CSV.+* Added setup script for Hasktorch example.++ ## 0.3.1.2 * Update granite version, again, for stackage. 
README.md view
@@ -124,6 +124,5 @@  ## Future work * Apache arrow compatability-* Integration with common data formats (currently only supports CSV)-* Support windowed plotting (currently only supports ASCII plots)+* Integration with more data formats (SQLite, Postgres, json lines, xlsx). * Host the whole library + Jupyter lab on Azure with auth and isolation.
app/Main.hs view
@@ -1,27 +1,37 @@+{-# LANGUAGE NumericUnderscores #-} -- Useful Haskell extensions. -- Allow string literal to be interpreted as any other string type.- {-# LANGUAGE OverloadedStrings #-} +import Data.Time+import qualified Data.Vector.Unboxed as VU import qualified DataFrame as D -- import for general functionality.-import qualified DataFrame.Functions as F-import DataFrame ((|>))--import Data.Function-import qualified Data.List as L-import Data.Maybe-import System.Environment+import System.Random.Stateful  main :: IO () main = do-    (d:b:_) <- fmap (map read) getArgs-    df <- D.readCsv "./data/housing.csv"-    let (Right e) = F.synthesizeFeatureExpr "median_house_value" d b (df |> D.exclude ["ocean_proximity", "total_bedrooms", "median_income"])--    print e--    mapM_ print (L.sortBy (flip compare `on` snd) (correlations (df  |> D.derive "n" e)))--correlations d = map (`correlationWithHouseValue` d) (D.columnNames d)-    where-        correlationWithHouseValue columnName d = (columnName, fromMaybe 0 (D.correlation columnName "median_house_value" d))+    let n = 100_000_000+    g <- newIOGenM =<< newStdGen+    let range = (-20.0 :: Double, 20.0 :: Double)+    startGeneration <- getCurrentTime+    ns <- VU.replicateM n (uniformRM range g)+    xs <- VU.replicateM n (uniformRM range g)+    ys <- VU.replicateM n (uniformRM range g)+    let df = D.fromUnnamedColumns (map D.fromUnboxedVector [ns, xs, ys])+    endGeneration <- getCurrentTime+    let generationTime = diffUTCTime endGeneration startGeneration+    putStrLn $ "Data generation Time: " ++ show generationTime+    startCalculation <- getCurrentTime+    print $ D.mean "0" df+    print $ D.variance "1" df+    print $ D.correlation "1" "2" df+    endCalculation <- getCurrentTime+    let calculationTime = diffUTCTime endCalculation startCalculation+    putStrLn $ "Calculation Time: " ++ show calculationTime+    startFilter <- getCurrentTime+    print $ D.filter "0" (>= (19.9 :: Double)) df D.|> D.take 10+    endFilter <- getCurrentTime+    let filterTime = diffUTCTime endFilter startFilter+    putStrLn $ "Filter Time: " ++ show filterTime+    let totalTime = diffUTCTime endFilter startGeneration+    putStrLn $ "Total Time: " ++ show totalTime
benchmark/Main.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} @@ -17,18 +16,26 @@  polars :: IO () polars = do-    output <- readProcess "./benchmark/dataframe_benchmark/bin/python3" ["./benchmark/polars/polars_benchmark.py"] ""+    output <-+        readProcess+            "./benchmark/dataframe_benchmark/bin/python3"+            ["./benchmark/polars/polars_benchmark.py"]+            ""     putStrLn output  pandas :: IO () pandas = do-    output <- readProcess "./benchmark/dataframe_benchmark/bin/python3" ["./benchmark/pandas/pandas_benchmark.py"] ""+    output <-+        readProcess+            "./benchmark/dataframe_benchmark/bin/python3"+            ["./benchmark/pandas/pandas_benchmark.py"]+            ""     putStrLn output  explorer :: IO () explorer = do-    _ <- readProcess "./benchmark/dataframe_benchmark/bin/mix" ["deps.get"] ""-    output <- readProcess "./benchmark/dataframe_benchmark/bin/mix" ["run", "./benchmark/explorer/explorer_benchmark.exs"] ""+    output <-+        readProcess "mix" ["run", "./benchmark/explorer/explorer_benchmark.exs"] ""     putStrLn output  groupByHaskell :: IO ()@@ -38,30 +45,46 @@         df             |> D.groupBy ["ocean_proximity"]             |> D.aggregate-                [ F.minimum (F.col @Double "median_house_value") `F.as` "minimum_median_house_value"-                , F.maximum (F.col @Double "median_house_value") `F.as` "maximum_median_house_value"+                [ F.minimum (F.col @Double "median_house_value")+                    `F.as` "minimum_median_house_value"+                , F.maximum (F.col @Double "median_house_value")+                    `F.as` "maximum_median_house_value"                 ]  groupByPolars :: IO () groupByPolars = do-    output <- readProcess "./benchmark/dataframe_benchmark/bin/python3" ["./benchmark/polars/group_by.py"] ""+    output <-+        readProcess+            "./benchmark/dataframe_benchmark/bin/python3"+            ["./benchmark/polars/group_by.py"]+            ""     putStrLn output  groupByPandas :: IO () groupByPandas = do-    output <- readProcess "./benchmark/dataframe_benchmark/bin/python3" ["./benchmark/pandas/group_by.py"] ""+    output <-+        readProcess+            "./benchmark/dataframe_benchmark/bin/python3"+            ["./benchmark/pandas/group_by.py"]+            ""     putStrLn output  groupByExplorer :: IO () groupByExplorer = do-    output <- readProcess "./benchmark/dataframe_benchmark/bin/mix" ["run", "./benchmark/explorer/group_by.exs"] ""+    output <-+        readProcess+            "./benchmark/dataframe_benchmark/bin/mix"+            ["run", "./benchmark/explorer/group_by.exs"]+            ""     putStrLn output  parseFile :: String -> IO () parseFile path = void $ D.readCsv path  parseCovidEffectsCSV :: IO ()-parseCovidEffectsCSV = parseFile "./data/effects-of-covid-19-on-trade-at-15-december-2021-provisional.csv"+parseCovidEffectsCSV =+    parseFile+        "./data/effects-of-covid-19-on-trade-at-15-december-2021-provisional.csv"  parseHousingCSV :: IO () parseHousingCSV = parseFile "./data/housing.csv"@@ -77,25 +100,27 @@  main :: IO () main = do-  output <- readProcess "cabal" ["build", "-O2"] ""-  putStrLn output-  defaultMain-    [ bgroup-        "stats"-        [ bench "simpleStatsHaskell" $ nfIO haskell-        , bench "simpleStatsPandas" $ nfIO pandas-        , bench "simpleStatsPolars" $ nfIO polars-        , bench "groupByHaskell" $ nfIO groupByHaskell-        , bench "groupByPolars" $ nfIO groupByPolars-        , bench "groupByPandas" $ nfIO groupByPandas-        , bench "groupByExplorer" $ nfIO groupByExplorer-        ],-      bgroup-        "Parsing"-        [ bench "effects-of-covid-19-on-trade-at-15-december-2021-provisional.csv (9.0 MB)" $ nfIO parseCovidEffectsCSV-        , bench "housing.csv (1.4 MB)" $ nfIO parseHousingCSV-        , bench "starwars.csv (10 KB)" $ nfIO parseStarWarsCSV-        , bench "chipotle.tsv (356 KB)" $ nfIO parseChipotleTSV-        , bench "measurements.txt (135 KB)" $ nfIO parseMeasurementsTXT+    output <- readProcess "cabal" ["build", "-O2"] ""+    putStrLn output+    defaultMain+        [ bgroup+            "stats"+            [ bench "simpleStatsHaskell" $ nfIO haskell+            , bench "simpleStatsPandas" $ nfIO pandas+            , bench "simpleStatsPolars" $ nfIO polars+            , bench "groupByHaskell" $ nfIO groupByHaskell+            , bench "groupByPolars" $ nfIO groupByPolars+            , bench "groupByPandas" $ nfIO groupByPandas+            , bench "groupByExplorer" $ nfIO groupByExplorer+            ]+        , bgroup+            "Parsing"+            [ bench+                "effects-of-covid-19-on-trade-at-15-december-2021-provisional.csv (9.0 MB)"+                $ nfIO parseCovidEffectsCSV+            , bench "housing.csv (1.4 MB)" $ nfIO parseHousingCSV+            , bench "starwars.csv (10 KB)" $ nfIO parseStarWarsCSV+            , bench "chipotle.tsv (356 KB)" $ nfIO parseChipotleTSV+            , bench "measurements.txt (135 KB)" $ nfIO parseMeasurementsTXT+            ]         ]-    ]
dataframe.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               dataframe-version:            0.3.1.2+version:            0.3.2.0  synopsis: A fast, safe, and intuitive DataFrame library. @@ -14,7 +14,7 @@  copyright: (c) 2024-2025 Michael Chavinda category: Data-tested-with: GHC ==9.8.3 || ==9.6.6 || == 9.4.8 || ==9.10.1 || ==9.12.1 || ==9.12.2+tested-with: GHC ==9.4.8 || ==9.6.7 || ==9.8.4 || ==9.10.3 || ==9.12.2 extra-doc-files: CHANGELOG.md README.md  source-repository head@@ -23,7 +23,12 @@  common warnings     -- TODO add more warnings, eventually -Wall-    ghc-options: -Wunused-packages -Wunused-imports+    ghc-options:+        -Wincomplete-patterns+        -Wincomplete-uni-patterns+        -Wunused-imports+        -Wunused-packages+        -Wunused-local-binds  library     import: warnings@@ -32,7 +37,6 @@                     DataFrame.Lazy,                     DataFrame.Functions,                     DataFrame.Display.Web.Plot,-                    DataFrame.Display.Web.ChartJs,                     DataFrame.Internal.Types,                     DataFrame.Internal.Expression,                     DataFrame.Internal.Parsing,@@ -68,12 +72,12 @@                     DataFrame.IO.Parquet.Types,                     DataFrame.Lazy.IO.CSV,                     DataFrame.Lazy.Internal.DataFrame-    build-depends:    base > 4 && <5,+    build-depends:    base >= 4 && <5,                       array ^>= 0.5,                       attoparsec >= 0.12 && < 0.15,                       bytestring >= 0.11 && < 0.13,                       bytestring-lexing >= 0.5 && < 0.6,-                      containers >= 0.6.7 && < 0.8,+                      containers >= 0.6.7 && < 0.9,                       directory >= 1.3.0.0 && < 2,                       granite ^>= 0.3,                       hashable >= 1.2 && < 2,@@ -92,11 +96,10 @@ executable dataframe     import: warnings     main-is: Main.hs-    build-depends:    base > 4 && < 5,+    build-depends:    base >= 4 && < 5,                       dataframe ^>= 0.3,                       random >= 1 && < 2,                       time >= 1.12 && < 2,-                      text,                       vector ^>= 0.13,     hs-source-dirs:   app     default-language: Haskell2010@@ -108,7 +111,7 @@     type: exitcode-stdio-1.0     main-is: Main.hs     hs-source-dirs: benchmark-    build-depends: base > 4 && < 5,+    build-depends: base >= 4 && < 5,                    criterion >= 1 && < 2,                    process >= 1.6 && < 2,                    dataframe ^>= 0.3@@ -125,16 +128,19 @@     main-is: Main.hs     other-modules: Assertions,                    Functions,+                   Operations.Aggregations,                    Operations.Apply,+                   Operations.Core,                    Operations.Derive,                    Operations.Filter,                    Operations.GroupBy,                    Operations.InsertColumn,+                   Operations.Merge,                    Operations.Sort,                    Operations.Statistics,                    Operations.Take,                    Parquet-    build-depends:  base > 4 && < 5,+    build-depends:  base >= 4 && < 5,                     dataframe ^>= 0.3,                     HUnit ^>= 1.6,                     random >= 1 && < 2,
src/DataFrame.hs view
@@ -171,6 +171,7 @@     -- * Core data structures     module Dataframe,     module Column,+    module Row,     module Expression,      -- * Core dataframe operations@@ -202,13 +203,23 @@  import DataFrame.Display.Terminal.Plot as Plot import DataFrame.Errors as Errors-import DataFrame.IO.CSV as CSV (ReadOptions (..), defaultOptions, readCsv, readSeparated, readTsv, writeCsv, writeSeparated)+import DataFrame.IO.CSV as CSV (+    ReadOptions (..),+    defaultReadOptions,+    readCsv,+    readCsvWithOpts,+    readSeparated,+    readTsv,+    writeCsv,+    writeSeparated,+ ) import DataFrame.IO.Parquet as Parquet (readParquet) import DataFrame.Internal.Column as Column (     Column,     fromList,     fromUnboxedVector,     fromVector,+    hasElemType,     hasMissing,     isNumeric,     toList,@@ -223,8 +234,18 @@     toMatrix,  ) import DataFrame.Internal.Expression as Expression (Expr)-import DataFrame.Operations.Aggregation as Aggregation (aggregate, distinct, groupBy)-import DataFrame.Operations.Core as Core hiding (ColumnInfo (..), nulls, partiallyParsed, renameSafe)+import DataFrame.Internal.Row as Row (Row, fromAny, toAny, toRowList)+import DataFrame.Operations.Aggregation as Aggregation (+    aggregate,+    distinct,+    groupBy,+ )+import DataFrame.Operations.Core as Core hiding (+    ColumnInfo (..),+    nulls,+    partiallyParsed,+    renameSafe,+ ) import DataFrame.Operations.Join as Join import DataFrame.Operations.Merge as Merge import DataFrame.Operations.Sorting as Sorting
src/DataFrame/Display/Terminal/Plot.hs view
@@ -54,7 +54,8 @@ plotHistogram :: (HasCallStack) => T.Text -> DataFrame -> IO () plotHistogram colName = plotHistogramWith colName (defaultPlotConfig Histogram) -plotHistogramWith :: (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO ()+plotHistogramWith ::+    (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO () plotHistogramWith colName config df = do     let values = extractNumericColumn colName df         (minVal, maxVal) = if null values then (0, 1) else (minimum values, maximum values)@@ -63,17 +64,20 @@ plotScatter :: (HasCallStack) => T.Text -> T.Text -> DataFrame -> IO () plotScatter xCol yCol = plotScatterWith xCol yCol (defaultPlotConfig Scatter) -plotScatterWith :: (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO ()+plotScatterWith ::+    (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO () plotScatterWith xCol yCol config df = do     let xVals = extractNumericColumn xCol df         yVals = extractNumericColumn yCol df         points = zip xVals yVals     T.putStrLn $ scatter [(xCol <> " vs " <> yCol, points)] (plotSettings config) -plotScatterBy :: (HasCallStack) => T.Text -> T.Text -> T.Text -> DataFrame -> IO ()+plotScatterBy ::+    (HasCallStack) => T.Text -> T.Text -> T.Text -> DataFrame -> IO () plotScatterBy xCol yCol grouping = plotScatterByWith xCol yCol grouping (defaultPlotConfig Scatter) -plotScatterByWith :: (HasCallStack) => T.Text -> T.Text -> T.Text -> PlotConfig -> DataFrame -> IO ()+plotScatterByWith ::+    (HasCallStack) => T.Text -> T.Text -> T.Text -> PlotConfig -> DataFrame -> IO () plotScatterByWith xCol yCol grouping config df = do     let vals = extractStringColumn grouping df     let df' = insertColumn grouping (D.fromList vals) df@@ -88,7 +92,8 @@ plotLines :: (HasCallStack) => T.Text -> [T.Text] -> DataFrame -> IO () plotLines xAxis colNames = plotLinesWith xAxis colNames (defaultPlotConfig Line) -plotLinesWith :: (HasCallStack) => T.Text -> [T.Text] -> PlotConfig -> DataFrame -> IO ()+plotLinesWith ::+    (HasCallStack) => T.Text -> [T.Text] -> PlotConfig -> DataFrame -> IO () plotLinesWith xAxis colNames config df = do     seriesData <- forM colNames $ \col -> do         let values = extractNumericColumn col df@@ -99,7 +104,8 @@ plotBoxPlots :: (HasCallStack) => [T.Text] -> DataFrame -> IO () plotBoxPlots colNames = plotBoxPlotsWith colNames (defaultPlotConfig BoxPlot) -plotBoxPlotsWith :: (HasCallStack) => [T.Text] -> PlotConfig -> DataFrame -> IO ()+plotBoxPlotsWith ::+    (HasCallStack) => [T.Text] -> PlotConfig -> DataFrame -> IO () plotBoxPlotsWith colNames config df = do     boxData <- forM colNames $ \col -> do         let values = extractNumericColumn col df@@ -109,7 +115,8 @@ plotStackedBars :: (HasCallStack) => T.Text -> [T.Text] -> DataFrame -> IO () plotStackedBars categoryCol valueColumns = plotStackedBarsWith categoryCol valueColumns (defaultPlotConfig StackedBar) -plotStackedBarsWith :: (HasCallStack) => T.Text -> [T.Text] -> PlotConfig -> DataFrame -> IO ()+plotStackedBarsWith ::+    (HasCallStack) => T.Text -> [T.Text] -> PlotConfig -> DataFrame -> IO () plotStackedBarsWith categoryCol valueColumns config df = do     let categories = extractStringColumn categoryCol df         uniqueCategories = L.nub categories@@ -175,7 +182,8 @@ plotBars :: (HasCallStack) => T.Text -> DataFrame -> IO () plotBars colName = plotBarsWith colName Nothing (defaultPlotConfig Bar) -plotBarsWith :: (HasCallStack) => T.Text -> Maybe T.Text -> PlotConfig -> DataFrame -> IO ()+plotBarsWith ::+    (HasCallStack) => T.Text -> Maybe T.Text -> PlotConfig -> DataFrame -> IO () plotBarsWith colName groupByCol config df =     case groupByCol of         Nothing -> plotSingleBars colName config df@@ -203,7 +211,8 @@ plotBarsTopN :: (HasCallStack) => Int -> T.Text -> DataFrame -> IO () plotBarsTopN n colName = plotBarsTopNWith n colName (defaultPlotConfig Bar) -plotBarsTopNWith :: (HasCallStack) => Int -> T.Text -> PlotConfig -> DataFrame -> IO ()+plotBarsTopNWith ::+    (HasCallStack) => Int -> T.Text -> PlotConfig -> DataFrame -> IO () plotBarsTopNWith n colName config df = do     let barData = getCategoricalCounts colName df     case barData of@@ -217,10 +226,12 @@                 grouped = groupWithOther n paired             T.putStrLn $ bars grouped (plotSettings config) -plotGroupedBarsWith :: (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO ()+plotGroupedBarsWith ::+    (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO () plotGroupedBarsWith = plotGroupedBarsWithN 10 -plotGroupedBarsWithN :: (HasCallStack) => Int -> T.Text -> T.Text -> PlotConfig -> DataFrame -> IO ()+plotGroupedBarsWithN ::+    (HasCallStack) => Int -> T.Text -> T.Text -> PlotConfig -> DataFrame -> IO () plotGroupedBarsWithN n groupCol valCol config df = do     let colIsNumeric = isNumericColumnCheck valCol df @@ -228,9 +239,9 @@         then do             let groups = extractStringColumn groupCol df                 values = extractNumericColumn valCol df-                grouped = M.toList $ M.fromListWith (+) (zip groups values)-                finalGroups = groupWithOther n grouped-            T.putStrLn $ bars finalGroups (plotSettings config)+                m = M.fromListWith (+) (zip groups values)+                grouped = map (\v -> (v, m M.! v)) groups+            T.putStrLn $ bars grouped (plotSettings config)         else do             let groups = extractStringColumn groupCol df                 vals = extractStringColumn valCol df@@ -246,7 +257,8 @@ plotValueCounts :: (HasCallStack) => T.Text -> DataFrame -> IO () plotValueCounts colName = plotValueCountsWith colName 10 (defaultPlotConfig Bar) -plotValueCountsWith :: (HasCallStack) => T.Text -> Int -> PlotConfig -> DataFrame -> IO ()+plotValueCountsWith ::+    (HasCallStack) => T.Text -> Int -> PlotConfig -> DataFrame -> IO () plotValueCountsWith colName maxBars config df = do     let counts = getCategoricalCounts colName df     case counts of@@ -287,7 +299,10 @@             let numUnique = length c                 config =                     (defaultPlotConfig Bar)-                        { plotSettings = (plotSettings (defaultPlotConfig Bar)){plotTitle = colName <> " (" <> T.pack (show numUnique) <> " unique values)"}+                        { plotSettings =+                            (plotSettings (defaultPlotConfig Bar))+                                { plotTitle = colName <> " (" <> T.pack (show numUnique) <> " unique values)"+                                }                         }             if numUnique <= 12                 then T.putStrLn $ bars c (plotSettings config)@@ -298,11 +313,7 @@                             T.putStrLn $ bars grouped (plotSettings config)                         else do                             let grouped = groupWithOther 10 c-                                otherCount = numUnique - 10-                            T.putStrLn $-                                bars-                                    grouped-                                    (plotSettings config)+                            T.putStrLn $ bars grouped (plotSettings config)         Nothing -> plotBars colName df  plotCategoricalSummary :: (HasCallStack) => DataFrame -> IO ()@@ -313,11 +324,13 @@         case counts of             Just c -> when (length c > 1) $ do                 let numUnique = length c-                putStrLn $ "\n=== " ++ T.unpack col ++ " (" ++ show numUnique ++ " unique values) ==="+                putStrLn $+                    "\n=== " ++ T.unpack col ++ " (" ++ show numUnique ++ " unique values) ==="                 if numUnique > 15 then plotBarsTopN 10 col df else plotBars col df             Nothing -> return () -getCategoricalCounts :: (HasCallStack) => T.Text -> DataFrame -> Maybe [(T.Text, Double)]+getCategoricalCounts ::+    (HasCallStack) => T.Text -> DataFrame -> Maybe [(T.Text, Double)] getCategoricalCounts colName df =     case M.lookup colName (columnIndices df) of         Nothing -> error $ "Column " ++ T.unpack colName ++ " not found"@@ -377,7 +390,8 @@                     Just Refl -> V.toList $ V.map realToFrac vec                     Nothing -> error $ "Column is not numeric (type: " ++ show (typeRep @a) ++ ")" -unboxedVectorToDoubles :: forall a. (Typeable a, VU.Unbox a, Show a) => VU.Vector a -> [Double]+unboxedVectorToDoubles ::+    forall a. (Typeable a, VU.Unbox a, Show a) => VU.Vector a -> [Double] unboxedVectorToDoubles vec =     case testEquality (typeRep @a) (typeRep @Double) of         Just Refl -> VU.toList vec@@ -401,7 +415,8 @@ plotPie :: (HasCallStack) => T.Text -> Maybe T.Text -> DataFrame -> IO () plotPie valCol labelCol = plotPieWith valCol labelCol (defaultPlotConfig Pie) -plotPieWith :: (HasCallStack) => T.Text -> Maybe T.Text -> PlotConfig -> DataFrame -> IO ()+plotPieWith ::+    (HasCallStack) => T.Text -> Maybe T.Text -> PlotConfig -> DataFrame -> IO () plotPieWith valCol labelCol config df = do     let categoricalData = getCategoricalCounts valCol df     case categoricalData of@@ -446,7 +461,8 @@ plotPieWithPercentages :: (HasCallStack) => T.Text -> DataFrame -> IO () plotPieWithPercentages colName = plotPieWithPercentagesConfig colName (defaultPlotConfig Pie) -plotPieWithPercentagesConfig :: (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO ()+plotPieWithPercentagesConfig ::+    (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO () plotPieWithPercentagesConfig colName config df = do     let counts = getCategoricalCounts colName df     case counts of@@ -472,7 +488,8 @@ plotPieTopN :: (HasCallStack) => Int -> T.Text -> DataFrame -> IO () plotPieTopN n colName = plotPieTopNWith n colName (defaultPlotConfig Pie) -plotPieTopNWith :: (HasCallStack) => Int -> T.Text -> PlotConfig -> DataFrame -> IO ()+plotPieTopNWith ::+    (HasCallStack) => Int -> T.Text -> PlotConfig -> DataFrame -> IO () plotPieTopNWith n colName config df = do     let counts = getCategoricalCounts colName df     case counts of@@ -491,12 +508,12 @@     let counts = getCategoricalCounts colName df     case counts of         Just c -> do-            let numUnique = length c-                total = sum (map snd c)+            let total = sum (map snd c)                 significant = filter (\(_, v) -> v / total >= 0.01) c                 config =                     (defaultPlotConfig Pie)-                        { plotSettings = (plotSettings (defaultPlotConfig Pie)){plotTitle = colName <> " Distribution"}+                        { plotSettings =+                            (plotSettings (defaultPlotConfig Pie)){plotTitle = colName <> " Distribution"}                         }             if length significant <= 6                 then T.putStrLn $ pie significant (plotSettings config)@@ -513,7 +530,8 @@ plotPieGrouped :: (HasCallStack) => T.Text -> T.Text -> DataFrame -> IO () plotPieGrouped groupCol valCol = plotPieGroupedWith groupCol valCol (defaultPlotConfig Pie) -plotPieGroupedWith :: (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO ()+plotPieGroupedWith ::+    (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO () plotPieGroupedWith groupCol valCol config df = do     let colIsNumeric = isNumericColumnCheck valCol df @@ -566,7 +584,8 @@ plotMarketShare :: (HasCallStack) => T.Text -> DataFrame -> IO () plotMarketShare colName = plotMarketShareWith colName (defaultPlotConfig Pie) -plotMarketShareWith :: (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO ()+plotMarketShareWith ::+    (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO () plotMarketShareWith colName config df = do     let counts = getCategoricalCounts colName df     case counts of
src/DataFrame/Display/Terminal/PrettyPrint.hs view
@@ -28,7 +28,10 @@ fillRight c n s = T.replicate (n - T.length s) (T.singleton c) `T.append` s  fillCenter :: Char -> Int -> T.Text -> T.Text-fillCenter c n s = T.replicate l (T.singleton c) `T.append` s `T.append` T.replicate r (T.singleton c)+fillCenter c n s =+    T.replicate l (T.singleton c)+        `T.append` s+        `T.append` T.replicate r (T.singleton c)   where     x = n - T.length s     l = x `div` 2@@ -46,14 +49,31 @@  showTable :: Bool -> [T.Text] -> [T.Text] -> [[T.Text]] -> T.Text showTable properMarkdown header types rows =-    let consolidatedHeader = if properMarkdown then zipWith (\h t -> h <> "<br>" <> t) header types else header+    let consolidatedHeader =+            if properMarkdown+                then zipWith (\h t -> h <> "<br>" <> t) header types+                else header         cs = map (\h -> ColDesc center h left) consolidatedHeader-        widths = [maximum $ map T.length col | col <- transpose $ consolidatedHeader : types : rows]+        widths =+            [ maximum $ map T.length col+            | col <- transpose $ consolidatedHeader : types : rows+            ]         border = T.intercalate "---" [T.replicate width (T.singleton '-') | width <- widths]         separator = T.intercalate "-|-" [T.replicate width (T.singleton '-') | width <- widths]-        fillCols fill cols = T.intercalate " | " [fill c width col | (c, width, col) <- zip3 cs widths cols]+        fillCols fill cols =+            T.intercalate " | " [fill c width col | (c, width, col) <- zip3 cs widths cols]         lines =             if properMarkdown-                then border : fillCols colTitleFill consolidatedHeader : separator : map (fillCols colValueFill) rows-                else border : fillCols colTitleFill consolidatedHeader : separator : fillCols colTitleFill types : separator : map (fillCols colValueFill) rows+                then+                    border+                        : fillCols colTitleFill consolidatedHeader+                        : separator+                        : map (fillCols colValueFill) rows+                else+                    border+                        : fillCols colTitleFill consolidatedHeader+                        : separator+                        : fillCols colTitleFill types+                        : separator+                        : map (fillCols colValueFill) rows      in T.unlines lines
− src/DataFrame/Display/Web/ChartJs.hs
@@ -1,8 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module DataFrame.Display.Web.ChartJs where--import qualified Data.Text as T--minifiedChartJs :: T.Text-minifiedChartJs = "!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=e(function(){try{return require(\"moment\")}catch(t){}}()):\"function\"==typeof define&&define.amd?define([\"require\"],(function(t){return e(function(){try{return t(\"moment\")}catch(t){}}())})):(t=t||self).Chart=e(t.moment)}(this,(function(t){\"use strict\";t=t&&t.hasOwnProperty(\"default\")?t.default:t;var e={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},n=function(t,e){return t(e={exports:{}},e.exports),e.exports}((function(t){var n={};for(var i in e)e.hasOwnProperty(i)&&(n[e[i]]=i);var a=t.exports={rgb:{channels:3,labels:\"rgb\"},hsl:{channels:3,labels:\"hsl\"},hsv:{channels:3,labels:\"hsv\"},hwb:{channels:3,labels:\"hwb\"},cmyk:{channels:4,labels:\"cmyk\"},xyz:{channels:3,labels:\"xyz\"},lab:{channels:3,labels:\"lab\"},lch:{channels:3,labels:\"lch\"},hex:{channels:1,labels:[\"hex\"]},keyword:{channels:1,labels:[\"keyword\"]},ansi16:{channels:1,labels:[\"ansi16\"]},ansi256:{channels:1,labels:[\"ansi256\"]},hcg:{channels:3,labels:[\"h\",\"c\",\"g\"]},apple:{channels:3,labels:[\"r16\",\"g16\",\"b16\"]},gray:{channels:1,labels:[\"gray\"]}};for(var r in a)if(a.hasOwnProperty(r)){if(!(\"channels\"in a[r]))throw new Error(\"missing channels property: \"+r);if(!(\"labels\"in a[r]))throw new Error(\"missing channel labels property: \"+r);if(a[r].labels.length!==a[r].channels)throw new Error(\"channel and label counts mismatch: \"+r);var o=a[r].channels,l=a[r].labels;delete a[r].channels,delete a[r].labels,Object.defineProperty(a[r],\"channels\",{value:o}),Object.defineProperty(a[r],\"labels\",{value:l})}a.rgb.hsl=function(t){var e,n,i=t[0]/255,a=t[1]/255,r=t[2]/255,o=Math.min(i,a,r),l=Math.max(i,a,r),s=l-o;return l===o?e=0:i===l?e=(a-r)/s:a===l?e=2+(r-i)/s:r===l&&(e=4+(i-a)/s),(e=Math.min(60*e,360))<0&&(e+=360),n=(o+l)/2,[e,100*(l===o?0:n<=.5?s/(l+o):s/(2-l-o)),100*n]},a.rgb.hsv=function(t){var e,n,i,a,r,o=t[0]/255,l=t[1]/255,s=t[2]/255,u=Math.max(o,l,s),d=u-Math.min(o,l,s),c=function(t){return(u-t)/6/d+.5};return 0===d?a=r=0:(r=d/u,e=c(o),n=c(l),i=c(s),o===u?a=i-n:l===u?a=1/3+e-i:s===u&&(a=2/3+n-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=n[t];if(i)return i;var a,r,o,l=1/0;for(var s in e)if(e.hasOwnProperty(s)){var u=e[s],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d<l&&(l=d,a=s)}return a},a.keyword.rgb=function(t){return e[t]},a.rgb.xyz=function(t){var e=t[0]/255,n=t[1]/255,i=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,l=t[1]/100,s=t[2]/100;if(0===l)return[r=255*s,r,r];e=2*s-(n=s<.5?s*(1+l):s+l-s*l),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),l=255*i*(1-n*r),s=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,s,o];case 1:return[l,i,o];case 2:return[o,i,s];case 3:return[o,l,i];case 4:return[s,o,i];case 5:return[i,o,l]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,l=Math.max(o,.01);return i=(2-r)*o,n=r*l,[a,100*(n=(n/=(e=(2-r)*l)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,l,s=t[0]/360,u=t[1]/100,d=t[2]/100,c=u+d;switch(c>1&&(u/=c,d/=c),i=6*s-(e=Math.floor(6*s)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,l=u;break;case 1:r=a,o=n,l=u;break;case 2:r=u,o=n,l=a;break;case 3:r=u,o=a,l=n;break;case 4:r=a,o=u,l=n;break;case 5:r=n,o=u,l=a}return[255*r,255*o,255*l]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),l=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=l>.008856?l:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return\"000000\".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split(\"\").map((function(t){return t+t})).join(\"\"));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),l=r-o;return e=l<=0?0:r===n?(i-a)/l%6:r===i?2+(a-n)/l:4+(n-i)/l+4,e/=6,[360*(e%=1),100*l,100*(l<1?o/(1-l):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,l=o%1,s=1-l;switch(Math.floor(o)){case 0:r[0]=1,r[1]=l,r[2]=0;break;case 1:r[0]=s,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=l;break;case 3:r[0]=0,r[1]=s,r[2]=1;break;case 4:r[0]=l,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=s}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return\"000000\".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));n.rgb,n.hsl,n.hsv,n.hwb,n.cmyk,n.xyz,n.lab,n.lch,n.hex,n.keyword,n.ansi16,n.ansi256,n.hcg,n.apple,n.gray;function i(t){var e=function(){for(var t={},e=Object.keys(n),i=e.length,a=0;a<i;a++)t[e[a]]={distance:-1,parent:null};return t}(),i=[t];for(e[t].distance=0;i.length;)for(var a=i.pop(),r=Object.keys(n[a]),o=r.length,l=0;l<o;l++){var s=r[l],u=e[s];-1===u.distance&&(u.distance=e[a].distance+1,u.parent=a,i.unshift(s))}return e}function a(t,e){return function(n){return e(t(n))}}function r(t,e){for(var i=[e[t].parent,t],r=n[e[t].parent][t],o=e[t].parent;e[o].parent;)i.unshift(e[o].parent),r=a(n[e[o].parent][o],r),o=e[o].parent;return r.conversion=i,r}var o={};Object.keys(n).forEach((function(t){o[t]={},Object.defineProperty(o[t],\"channels\",{value:n[t].channels}),Object.defineProperty(o[t],\"labels\",{value:n[t].labels});var e=function(t){for(var e=i(t),n={},a=Object.keys(e),o=a.length,l=0;l<o;l++){var s=a[l];null!==e[s].parent&&(n[s]=r(s,e))}return n}(t);Object.keys(e).forEach((function(n){var i=e[n];o[t][n]=function(t){var e=function(e){if(null==e)return e;arguments.length>1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if(\"object\"==typeof n)for(var i=n.length,a=0;a<i;a++)n[a]=Math.round(n[a]);return n};return\"conversion\"in t&&(e.conversion=t.conversion),e}(i),o[t][n].raw=function(t){var e=function(e){return null==e?e:(arguments.length>1&&(e=Array.prototype.slice.call(arguments)),t(e))};return\"conversion\"in t&&(e.conversion=t.conversion),e}(i)}))}));var l=o,s={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},u={getRgba:d,getHsla:c,getRgb:function(t){var e=d(t);return e&&e.slice(0,3)},getHsl:function(t){var e=c(t);return e&&e.slice(0,3)},getHwb:h,getAlpha:function(t){var e=d(t);if(e)return e[3];if(e=c(t))return e[3];if(e=h(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return\"#\"+v(t[0])+v(t[1])+v(t[2])+(e>=0&&e<1?v(Math.round(255*e)):\"\")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return f(t,e);return\"rgb(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\")\"},rgbaString:f,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return\"rgb(\"+n+\"%, \"+i+\"%, \"+a+\"%)\"},percentaString:g,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return\"hsl(\"+t[0]+\", \"+t[1]+\"%, \"+t[2]+\"%)\"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return\"hwb(\"+t[0]+\", \"+t[1]+\"%, \"+t[2]+\"%\"+(void 0!==e&&1!==e?\", \"+e:\"\")+\")\"},keyword:function(t){return b[t.slice(0,3)]}};function d(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a=\"\";if(i){a=(i=i[1])[3];for(var r=0;r<e.length;r++)e[r]=parseInt(i[r]+i[r],16);a&&(n=Math.round(parseInt(a+a,16)/255*100)/100)}else if(i=t.match(/^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i)){a=i[2],i=i[1];for(r=0;r<e.length;r++)e[r]=parseInt(i.slice(2*r,2*r+2),16);a&&(n=Math.round(parseInt(a,16)/255*100)/100)}else if(i=t.match(/^rgba?\\(\\s*([+-]?\\d+)\\s*,\\s*([+-]?\\d+)\\s*,\\s*([+-]?\\d+)\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/i)){for(r=0;r<e.length;r++)e[r]=parseInt(i[r+1]);n=parseFloat(i[4])}else if(i=t.match(/^rgba?\\(\\s*([+-]?[\\d\\.]+)\\%\\s*,\\s*([+-]?[\\d\\.]+)\\%\\s*,\\s*([+-]?[\\d\\.]+)\\%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/i)){for(r=0;r<e.length;r++)e[r]=Math.round(2.55*parseFloat(i[r+1]));n=parseFloat(i[4])}else if(i=t.match(/(\\w+)/)){if(\"transparent\"==i[1])return[0,0,0,0];if(!(e=s[i[1]]))return}for(r=0;r<e.length;r++)e[r]=m(e[r],0,255);return n=n||0==n?m(n,0,1):1,e[3]=n,e}}function c(t){if(t){var e=t.match(/^hsla?\\(\\s*([+-]?\\d+)(?:deg)?\\s*,\\s*([+-]?[\\d\\.]+)%\\s*,\\s*([+-]?[\\d\\.]+)%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)/);if(e){var n=parseFloat(e[4]);return[m(parseInt(e[1]),0,360),m(parseFloat(e[2]),0,100),m(parseFloat(e[3]),0,100),m(isNaN(n)?1:n,0,1)]}}}function h(t){if(t){var e=t.match(/^hwb\\(\\s*([+-]?\\d+)(?:deg)?\\s*,\\s*([+-]?[\\d\\.]+)%\\s*,\\s*([+-]?[\\d\\.]+)%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)/);if(e){var n=parseFloat(e[4]);return[m(parseInt(e[1]),0,360),m(parseFloat(e[2]),0,100),m(parseFloat(e[3]),0,100),m(isNaN(n)?1:n,0,1)]}}}function f(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),\"rgba(\"+t[0]+\", \"+t[1]+\", \"+t[2]+\", \"+e+\")\"}function g(t,e){return\"rgba(\"+Math.round(t[0]/255*100)+\"%, \"+Math.round(t[1]/255*100)+\"%, \"+Math.round(t[2]/255*100)+\"%, \"+(e||t[3]||1)+\")\"}function p(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),\"hsla(\"+t[0]+\", \"+t[1]+\"%, \"+t[2]+\"%, \"+e+\")\"}function m(t,e,n){return Math.min(Math.max(e,t),n)}function v(t){var e=t.toString(16).toUpperCase();return e.length<2?\"0\"+e:e}var b={};for(var x in s)b[s[x]]=x;var y=function(t){return t instanceof y?t:this instanceof y?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void(\"string\"==typeof t?(e=u.getRgba(t))?this.setValues(\"rgb\",e):(e=u.getHsla(t))?this.setValues(\"hsl\",e):(e=u.getHwb(t))&&this.setValues(\"hwb\",e):\"object\"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues(\"rgb\",e):void 0!==e.l||void 0!==e.lightness?this.setValues(\"hsl\",e):void 0!==e.v||void 0!==e.value?this.setValues(\"hsv\",e):void 0!==e.w||void 0!==e.whiteness?this.setValues(\"hwb\",e):void 0===e.c&&void 0===e.cyan||this.setValues(\"cmyk\",e)))):new y(t);var e};y.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace(\"rgb\",arguments)},hsl:function(){return this.setSpace(\"hsl\",arguments)},hsv:function(){return this.setSpace(\"hsv\",arguments)},hwb:function(){return this.setSpace(\"hwb\",arguments)},cmyk:function(){return this.setSpace(\"cmyk\",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues(\"alpha\",t),this)},red:function(t){return this.setChannel(\"rgb\",0,t)},green:function(t){return this.setChannel(\"rgb\",1,t)},blue:function(t){return this.setChannel(\"rgb\",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel(\"hsl\",0,t)},saturation:function(t){return this.setChannel(\"hsl\",1,t)},lightness:function(t){return this.setChannel(\"hsl\",2,t)},saturationv:function(t){return this.setChannel(\"hsv\",1,t)},whiteness:function(t){return this.setChannel(\"hwb\",1,t)},blackness:function(t){return this.setChannel(\"hwb\",2,t)},value:function(t){return this.setChannel(\"hsv\",2,t)},cyan:function(t){return this.setChannel(\"cmyk\",0,t)},magenta:function(t){return this.setChannel(\"cmyk\",1,t)},yellow:function(t){return this.setChannel(\"cmyk\",2,t)},black:function(t){return this.setChannel(\"cmyk\",3,t)},hexString:function(){return u.hexString(this.values.rgb)},rgbString:function(){return u.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return u.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return u.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return u.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return u.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return u.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return u.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],n=0;n<t.length;n++){var i=t[n]/255;e[n]=i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),n=t.luminosity();return e>n?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?\"AAA\":e>=4.5?\"AA\":\"\"},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues(\"rgb\",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues(\"hsl\",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues(\"hsl\",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues(\"hsl\",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues(\"hsl\",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues(\"hwb\",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues(\"hwb\",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues(\"rgb\",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues(\"alpha\",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues(\"alpha\",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues(\"hsl\",e),this},mix:function(t,e){var n=this,i=t,a=void 0===e?.5:e,r=2*a-1,o=n.alpha()-i.alpha(),l=((r*o==-1?r:(r+o)/(1+r*o))+1)/2,s=1-l;return this.rgb(l*n.red()+s*i.red(),l*n.green()+s*i.green(),l*n.blue()+s*i.blue()).alpha(n.alpha()*a+i.alpha()*(1-a))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new y,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],\"[object Array]\"===(e={}.toString.call(t))?a[r]=t.slice(0):\"[object Number]\"===e?a[r]=t:console.error(\"unexpected color value:\",t));return n}},y.prototype.spaces={rgb:[\"red\",\"green\",\"blue\"],hsl:[\"hue\",\"saturation\",\"lightness\"],hsv:[\"hue\",\"saturation\",\"value\"],hwb:[\"hue\",\"whiteness\",\"blackness\"],cmyk:[\"cyan\",\"magenta\",\"yellow\",\"black\"]},y.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},y.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i<t.length;i++)n[t.charAt(i)]=e[t][i];return 1!==e.alpha&&(n.a=e.alpha),n},y.prototype.setValues=function(t,e){var n,i,a=this.values,r=this.spaces,o=this.maxes,s=1;if(this.valid=!0,\"alpha\"===t)s=e;else if(e.length)a[t]=e.slice(0,t.length),s=e[t.length];else if(void 0!==e[t.charAt(0)]){for(n=0;n<t.length;n++)a[t][n]=e[t.charAt(n)];s=e.a}else if(void 0!==e[r[t][0]]){var u=r[t];for(n=0;n<t.length;n++)a[t][n]=e[u[n]];s=e.alpha}if(a.alpha=Math.max(0,Math.min(1,void 0===s?a.alpha:s)),\"alpha\"===t)return!1;for(n=0;n<t.length;n++)i=Math.max(0,Math.min(o[t][n],a[t][n])),a[t][n]=Math.round(i);for(var d in r)d!==t&&(a[d]=l[t][d](a[t]));return!0},y.prototype.setSpace=function(t,e){var n=e[0];return void 0===n?this.getValues(t):(\"number\"==typeof n&&(n=Array.prototype.slice.call(e)),this.setValues(t,n),this)},y.prototype.setChannel=function(t,e,n){var i=this.values[t];return void 0===n?i[e]:(n===i[e]||(i[e]=n,this.setValues(t,i)),this)},\"undefined\"!=typeof window&&(window.Color=y);var _=y;function k(t){return-1===[\"__proto__\",\"prototype\",\"constructor\"].indexOf(t)}var w,M={noop:function(){},uid:(w=0,function(){return w++}),isNullOrUndef:function(t){return null==t},isArray:function(t){if(Array.isArray&&Array.isArray(t))return!0;var e=Object.prototype.toString.call(t);return\"[object\"===e.substr(0,7)&&\"Array]\"===e.substr(-6)},isObject:function(t){return null!==t&&\"[object Object]\"===Object.prototype.toString.call(t)},isFinite:function(t){return(\"number\"==typeof t||t instanceof Number)&&isFinite(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return M.valueOrDefault(M.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&\"function\"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,i){var a,r,o;if(M.isArray(t))if(r=t.length,i)for(a=r-1;a>=0;a--)e.call(n,t[a],a);else for(a=0;a<r;a++)e.call(n,t[a],a);else if(M.isObject(t))for(r=(o=Object.keys(t)).length,a=0;a<r;a++)e.call(n,t[o[a]],o[a])},arrayEquals:function(t,e){var n,i,a,r;if(!t||!e||t.length!==e.length)return!1;for(n=0,i=t.length;n<i;++n)if(a=t[n],r=e[n],a instanceof Array&&r instanceof Array){if(!M.arrayEquals(a,r))return!1}else if(a!==r)return!1;return!0},clone:function(t){if(M.isArray(t))return t.map(M.clone);if(M.isObject(t)){for(var e=Object.create(t),n=Object.keys(t),i=n.length,a=0;a<i;++a)e[n[a]]=M.clone(t[n[a]]);return e}return t},_merger:function(t,e,n,i){if(k(t)){var a=e[t],r=n[t];M.isObject(a)&&M.isObject(r)?M.merge(a,r,i):e[t]=M.clone(r)}},_mergerIf:function(t,e,n){if(k(t)){var i=e[t],a=n[t];M.isObject(i)&&M.isObject(a)?M.mergeIf(i,a):e.hasOwnProperty(t)||(e[t]=M.clone(a))}},merge:function(t,e,n){var i,a,r,o,l,s=M.isArray(e)?e:[e],u=s.length;if(!M.isObject(t))return t;for(i=(n=n||{}).merger||M._merger,a=0;a<u;++a)if(e=s[a],M.isObject(e))for(l=0,o=(r=Object.keys(e)).length;l<o;++l)i(r[l],t,e,n);return t},mergeIf:function(t,e){return M.merge(t,e,{merger:M._mergerIf})},extend:Object.assign||function(t){return M.merge(t,[].slice.call(arguments,1),{merger:function(t,e,n){e[t]=n[t]}})},inherits:function(t){var e=this,n=t&&t.hasOwnProperty(\"constructor\")?t.constructor:function(){return e.apply(this,arguments)},i=function(){this.constructor=n};return i.prototype=e.prototype,n.prototype=new i,n.extend=M.inherits,t&&M.extend(n.prototype,t),n.__super__=e.prototype,n},_deprecated:function(t,e,n,i){void 0!==e&&console.warn(t+': \"'+n+'\" is deprecated. Please use \"'+i+'\" instead')}},S=M;M.callCallback=M.callback,M.indexOf=function(t,e,n){return Array.prototype.indexOf.call(t,e,n)},M.getValueOrDefault=M.valueOrDefault,M.getValueAtIndexOrDefault=M.valueAtIndexOrDefault;var C={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-C.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*C.easeInBounce(2*t):.5*C.easeOutBounce(2*t-1)+.5}},P={effects:C};S.easingEffects=C;var A=Math.PI,D=A/180,T=2*A,I=A/2,F=A/4,O=2*A/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),l=e+o,s=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,s),l<u&&s<d?(t.arc(l,s,o,-A,-I),t.arc(u,s,o,-I,0),t.arc(u,d,o,0,I),t.arc(l,d,o,I,A)):l<u?(t.moveTo(l,n),t.arc(u,s,o,-I,I),t.arc(l,s,o,I,A+I)):s<d?(t.arc(l,s,o,-A,0),t.arc(l,d,o,0,A)):t.arc(l,s,o,-A,A),t.closePath(),t.moveTo(e,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a,r){var o,l,s,u,d,c=(r||0)*D;if(e&&\"object\"==typeof e&&(\"[object HTMLImageElement]\"===(o=e.toString())||\"[object HTMLCanvasElement]\"===o))return t.save(),t.translate(i,a),t.rotate(c),t.drawImage(e,-e.width/2,-e.height/2,e.width,e.height),void t.restore();if(!(isNaN(n)||n<=0)){switch(t.beginPath(),e){default:t.arc(i,a,n,0,T),t.closePath();break;case\"triangle\":t.moveTo(i+Math.sin(c)*n,a-Math.cos(c)*n),c+=O,t.lineTo(i+Math.sin(c)*n,a-Math.cos(c)*n),c+=O,t.lineTo(i+Math.sin(c)*n,a-Math.cos(c)*n),t.closePath();break;case\"rectRounded\":u=n-(d=.516*n),l=Math.cos(c+F)*u,s=Math.sin(c+F)*u,t.arc(i-l,a-s,d,c-A,c-I),t.arc(i+s,a-l,d,c-I,c),t.arc(i+l,a+s,d,c,c+I),t.arc(i-s,a+l,d,c+I,c+A),t.closePath();break;case\"rect\":if(!r){u=Math.SQRT1_2*n,t.rect(i-u,a-u,2*u,2*u);break}c+=F;case\"rectRot\":l=Math.cos(c)*n,s=Math.sin(c)*n,t.moveTo(i-l,a-s),t.lineTo(i+s,a-l),t.lineTo(i+l,a+s),t.lineTo(i-s,a+l),t.closePath();break;case\"crossRot\":c+=F;case\"cross\":l=Math.cos(c)*n,s=Math.sin(c)*n,t.moveTo(i-l,a-s),t.lineTo(i+l,a+s),t.moveTo(i+s,a-l),t.lineTo(i-s,a+l);break;case\"star\":l=Math.cos(c)*n,s=Math.sin(c)*n,t.moveTo(i-l,a-s),t.lineTo(i+l,a+s),t.moveTo(i+s,a-l),t.lineTo(i-s,a+l),c+=F,l=Math.cos(c)*n,s=Math.sin(c)*n,t.moveTo(i-l,a-s),t.lineTo(i+l,a+s),t.moveTo(i+s,a-l),t.lineTo(i-s,a+l);break;case\"line\":l=Math.cos(c)*n,s=Math.sin(c)*n,t.moveTo(i-l,a-s),t.lineTo(i+l,a+s);break;case\"dash\":t.moveTo(i,a),t.lineTo(i+Math.cos(c)*n,a+Math.sin(c)*n)}t.fill(),t.stroke()}},_isPointInArea:function(t,e){var n=1e-6;return t.x>e.left-n&&t.x<e.right+n&&t.y>e.top-n&&t.y<e.bottom+n},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){var a=n.steppedLine;if(a){if(\"middle\"===a){var r=(e.x+n.x)/2;t.lineTo(r,i?n.y:e.y),t.lineTo(r,i?e.y:n.y)}else\"after\"===a&&!i||\"after\"!==a&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y);t.lineTo(n.x,n.y)}else n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}},R=L;S.clear=L.clear,S.drawRoundedRectangle=function(t){t.beginPath(),L.roundedRect.apply(L,arguments)};var z={_set:function(t,e){return S.merge(this[t]||(this[t]={}),e)}};z._set(\"global\",{defaultColor:\"rgba(0,0,0,0.1)\",defaultFontColor:\"#666\",defaultFontFamily:\"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",defaultFontSize:12,defaultFontStyle:\"normal\",defaultLineHeight:1.2,showLines:!0});var N=z,B=S.valueOrDefault;var E={toLineHeight:function(t,e){var n=(\"\"+t).match(/^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/);if(!n||\"normal\"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case\"px\":return t;case\"%\":t/=100}return e*t},toPadding:function(t){var e,n,i,a;return S.isObject(t)?(e=+t.top||0,n=+t.right||0,i=+t.bottom||0,a=+t.left||0):e=n=i=a=+t||0,{top:e,right:n,bottom:i,left:a,height:e+i,width:a+n}},_parseFont:function(t){var e=N.global,n=B(t.fontSize,e.defaultFontSize),i={family:B(t.fontFamily,e.defaultFontFamily),lineHeight:S.options.toLineHeight(B(t.lineHeight,e.defaultLineHeight),n),size:n,style:B(t.fontStyle,e.defaultFontStyle),weight:null,string:\"\"};return i.string=function(t){return!t||S.isNullOrUndef(t.size)||S.isNullOrUndef(t.family)?null:(t.style?t.style+\" \":\"\")+(t.weight?t.weight+\" \":\"\")+t.size+\"px \"+t.family}(i),i},resolve:function(t,e,n,i){var a,r,o,l=!0;for(a=0,r=t.length;a<r;++a)if(void 0!==(o=t[a])&&(void 0!==e&&\"function\"==typeof o&&(o=o(e),l=!1),void 0!==n&&S.isArray(o)&&(o=o[n],l=!1),void 0!==o))return i&&!l&&(i.cacheable=!1),o}},W={_factorize:function(t){var e,n=[],i=Math.sqrt(t);for(e=1;e<i;e++)t%e==0&&(n.push(e),n.push(t/e));return i===(0|i)&&n.push(i),n.sort((function(t,e){return t-e})).pop(),n},log10:Math.log10||function(t){var e=Math.log(t)*Math.LOG10E,n=Math.round(e);return t===Math.pow(10,n)?n:e}},V=W;S.log10=W.log10;var H={getRtlAdapter:function(t,e,n){return t?function(t,e){return{x:function(n){return t+t+e-n},setWidth:function(t){e=t},textAlign:function(t){return\"center\"===t?t:\"right\"===t?\"left\":\"right\"},xPlus:function(t,e){return t-e},leftForLtr:function(t,e){return t-e}}}(e,n):{x:function(t){return t},setWidth:function(t){},textAlign:function(t){return t},xPlus:function(t,e){return t+e},leftForLtr:function(t,e){return t}}},overrideTextDirection:function(t,e){var n,i;\"ltr\"!==e&&\"rtl\"!==e||(i=[(n=t.canvas.style).getPropertyValue(\"direction\"),n.getPropertyPriority(\"direction\")],n.setProperty(\"direction\",e,\"important\"),t.prevTextDirection=i)},restoreTextDirection:function(t){var e=t.prevTextDirection;void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty(\"direction\",e[0],e[1]))}},j=S,q=P,U=R,Y=E,G=V,X=H;j.easing=q,j.canvas=U,j.options=Y,j.math=G,j.rtl=X;var K=function(t){j.extend(this,t),this.initialize.apply(this,arguments)};j.extend(K.prototype,{_type:void 0,initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=j.extend({},t._model)),t._start={},t},transition:function(t){var e=this,n=e._model,i=e._start,a=e._view;return n&&1!==t?(a||(a=e._view={}),i||(i=e._start={}),function(t,e,n,i){var a,r,o,l,s,u,d,c,h,f=Object.keys(n);for(a=0,r=f.length;a<r;++a)if(u=n[o=f[a]],e.hasOwnProperty(o)||(e[o]=u),(l=e[o])!==u&&\"_\"!==o[0]){if(t.hasOwnProperty(o)||(t[o]=l),(d=typeof u)==typeof(s=t[o]))if(\"string\"===d){if((c=_(s)).valid&&(h=_(u)).valid){e[o]=h.mix(c,i).rgbString();continue}}else if(j.isFinite(s)&&j.isFinite(u)){e[o]=s+(u-s)*i;continue}e[o]=u}}(i,a,n,t),e):(e._view=j.extend({},n),e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return j.isNumber(this._model.x)&&j.isNumber(this._model.y)}}),K.extend=j.inherits;var Z=K,$=Z.extend({chart:null,currentStep:0,numSteps:60,easing:\"\",render:null,onAnimationProgress:null,onAnimationComplete:null}),J=$;Object.defineProperty($.prototype,\"animationObject\",{get:function(){return this}}),Object.defineProperty($.prototype,\"chartInstance\",{get:function(){return this.chart},set:function(t){this.chart=t}}),N._set(\"global\",{animation:{duration:1e3,easing:\"easeOutQuart\",onProgress:j.noop,onComplete:j.noop}});var Q={animations:[],request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,e.startTime=Date.now(),e.duration=n,i||(t.animating=!0),a=0,r=o.length;a<r;++a)if(o[a].chart===t)return void(o[a]=e);o.push(e),1===o.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=j.findIndex(this.animations,(function(e){return e.chart===t}));-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=j.requestAnimFrame.call(window,(function(){t.request=null,t.startDigest()})))},startDigest:function(){var t=this;t.advance(),t.animations.length>0&&t.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r<a.length;)e=(t=a[r]).chart,n=t.numSteps,i=Math.floor((Date.now()-t.startTime)/t.duration*n)+1,t.currentStep=Math.min(i,n),j.callback(t.render,[e,t],e),j.callback(t.onAnimationProgress,[t],e),t.currentStep>=n?(j.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},tt=j.options.resolve,et=[\"push\",\"pop\",\"shift\",\"splice\",\"unshift\"];function nt(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(et.forEach((function(e){delete t[e]})),delete t._chartjs)}}var it=function(t,e){this.initialize(t,e)};j.extend(it.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:[\"backgroundColor\",\"borderCapStyle\",\"borderColor\",\"borderDash\",\"borderDashOffset\",\"borderJoinStyle\",\"borderWidth\"],_dataElementOptions:[\"backgroundColor\",\"borderColor\",\"borderWidth\",\"pointStyle\"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this,e=t.getMeta(),n=t.chart,i=n.scales,a=t.getDataset(),r=n.options.scales;null!==e.xAxisID&&e.xAxisID in i&&!a.xAxisID||(e.xAxisID=a.xAxisID||r.xAxes[0].id),null!==e.yAxisID&&e.yAxisID in i&&!a.yAxisID||(e.yAxisID=a.yAxisID||r.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&nt(this._data,this)},createMetaDataset:function(){var t=this,e=t.datasetElementType;return e&&new e({_chart:t.chart,_datasetIndex:t.index})},createMetaData:function(t){var e=this,n=e.dataElementType;return n&&new n({_chart:e.chart,_datasetIndex:e.index,_index:t})},addElements:function(){var t,e,n=this,i=n.getMeta(),a=n.getDataset().data||[],r=i.data;for(t=0,e=a.length;t<e;++t)r[t]=r[t]||n.createMetaData(t);i.dataset=i.dataset||n.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,e,n=this,i=n.getDataset(),a=i.data||(i.data=[]);n._data!==a&&(n._data&&nt(n._data,n),a&&Object.isExtensible(a)&&(e=n,(t=a)._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,\"_chartjs\",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),et.forEach((function(e){var n=\"onData\"+e.charAt(0).toUpperCase()+e.slice(1),i=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),a=i.apply(this,e);return j.each(t._chartjs.listeners,(function(t){\"function\"==typeof t[n]&&t[n].apply(t,e)})),a}})})))),n._data=a),n.resyncElements()},_configure:function(){var t=this;t._config=j.merge(Object.create(null),[t.chart.options.datasets[t._type],t.getDataset()],{merger:function(t,e,n){\"_meta\"!==t&&\"data\"!==t&&j._merger(t,e,n)}})},_update:function(t){var e=this;e._configure(),e._cachedDataOpts=null,e.update(t)},update:j.noop,transition:function(t){for(var e=this.getMeta(),n=e.data||[],i=n.length,a=0;a<i;++a)n[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],n=e.length,i=0;for(t.dataset&&t.dataset.draw();i<n;++i)e[i].draw()},getStyle:function(t){var e,n=this,i=n.getMeta(),a=i.dataset;return n._configure(),a&&void 0===t?e=n._resolveDatasetElementOptions(a||{}):(t=t||0,e=n._resolveDataElementOptions(i.data[t]||{},t)),!1!==e.fill&&null!==e.fill||(e.backgroundColor=e.borderColor),e},_resolveDatasetElementOptions:function(t,e){var n,i,a,r,o=this,l=o.chart,s=o._config,u=t.custom||{},d=l.options.elements[o.datasetElementType.prototype._type]||{},c=o._datasetElementOptions,h={},f={chart:l,dataset:o.getDataset(),datasetIndex:o.index,hover:e};for(n=0,i=c.length;n<i;++n)a=c[n],r=e?\"hover\"+a.charAt(0).toUpperCase()+a.slice(1):a,h[a]=tt([u[r],s[r],d[r]],f);return h},_resolveDataElementOptions:function(t,e){var n=this,i=t&&t.custom,a=n._cachedDataOpts;if(a&&!i)return a;var r,o,l,s,u=n.chart,d=n._config,c=u.options.elements[n.dataElementType.prototype._type]||{},h=n._dataElementOptions,f={},g={chart:u,dataIndex:e,dataset:n.getDataset(),datasetIndex:n.index},p={cacheable:!i};if(i=i||{},j.isArray(h))for(o=0,l=h.length;o<l;++o)f[s=h[o]]=tt([i[s],d[s],c[s]],g,e,p);else for(o=0,l=(r=Object.keys(h)).length;o<l;++o)f[s=r[o]]=tt([i[s],d[h[s]],d[s],c[s]],g,e,p);return p.cacheable&&(n._cachedDataOpts=Object.freeze(f)),f},removeHoverStyle:function(t){j.merge(t._model,t.$previousStyle||{}),delete t.$previousStyle},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t._index,i=t.custom||{},a=t._model,r=j.getHoverColor;t.$previousStyle={backgroundColor:a.backgroundColor,borderColor:a.borderColor,borderWidth:a.borderWidth},a.backgroundColor=tt([i.hoverBackgroundColor,e.hoverBackgroundColor,r(a.backgroundColor)],void 0,n),a.borderColor=tt([i.hoverBorderColor,e.hoverBorderColor,r(a.borderColor)],void 0,n),a.borderWidth=tt([i.hoverBorderWidth,e.hoverBorderWidth,a.borderWidth],void 0,n)},_removeDatasetHoverStyle:function(){var t=this.getMeta().dataset;t&&this.removeHoverStyle(t)},_setDatasetHoverStyle:function(){var t,e,n,i,a,r,o=this.getMeta().dataset,l={};if(o){for(r=o._model,a=this._resolveDatasetElementOptions(o,!0),t=0,e=(i=Object.keys(a)).length;t<e;++t)l[n=i[t]]=r[n],r[n]=a[n];o.$previousStyle=l}},resyncElements:function(){var t=this,e=t.getMeta(),n=t.getDataset().data,i=e.data.length,a=n.length;a<i?e.data.splice(a,i-a):a>i&&t.insertElements(i,a-i)},insertElements:function(t,e){for(var n=0;n<e;++n)this.addElementAndReset(t+n)},onDataPush:function(){var t=arguments.length;this.insertElements(this.getDataset().data.length-t,t)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),it.extend=j.inherits;var at=it,rt=2*Math.PI;function ot(t,e){var n=e.startAngle,i=e.endAngle,a=e.pixelMargin,r=a/e.outerRadius,o=e.x,l=e.y;t.beginPath(),t.arc(o,l,e.outerRadius,n-r,i+r),e.innerRadius>a?(r=a/e.innerRadius,t.arc(o,l,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,l,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function lt(t,e,n){var i=\"inner\"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin=\"round\"):(t.lineWidth=e.borderWidth,t.lineJoin=\"bevel\"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+rt,ot(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=rt,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+rt,n.startAngle,!0),a=0;a<n.fullCircles;++a)t.stroke();for(t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.startAngle+rt),a=0;a<n.fullCircles;++a)t.stroke()}(t,e,n,i),i&&ot(t,n),t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.endAngle),t.arc(n.x,n.y,n.innerRadius,n.endAngle,n.startAngle,!0),t.closePath(),t.stroke()}N._set(\"global\",{elements:{arc:{backgroundColor:N.global.defaultColor,borderColor:\"#fff\",borderWidth:2,borderAlign:\"center\"}}});var st=Z.extend({_type:\"arc\",inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var n=this._view;if(n){for(var i=j.getAngleFromPoint(n,{x:t,y:e}),a=i.angle,r=i.distance,o=n.startAngle,l=n.endAngle;l<o;)l+=rt;for(;a>l;)a-=rt;for(;a<o;)a+=rt;var s=a>=o&&a<=l,u=r>=n.innerRadius&&r<=n.outerRadius;return s&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i=\"inner\"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/rt)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+rt,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;t<a.fullCircles;++t)e.fill();a.endAngle=a.startAngle+n.circumference%rt}e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),e.fill(),n.borderWidth&&lt(e,n,a),e.restore()}}),ut=j.valueOrDefault,dt=N.global.defaultColor;N._set(\"global\",{elements:{line:{tension:.4,backgroundColor:dt,borderWidth:3,borderColor:dt,borderCapStyle:\"butt\",borderDash:[],borderDashOffset:0,borderJoinStyle:\"miter\",capBezierPoints:!0,fill:!0}}});var ct=Z.extend({_type:\"line\",draw:function(){var t,e,n,i=this,a=i._view,r=i._chart.ctx,o=a.spanGaps,l=i._children.slice(),s=N.global,u=s.elements.line,d=-1,c=i._loop;if(l.length){if(i._loop){for(t=0;t<l.length;++t)if(e=j.previousItem(l,t),!l[t]._view.skip&&e._view.skip){l=l.slice(t).concat(l.slice(0,t)),c=o;break}c&&l.push(l[0])}for(r.save(),r.lineCap=a.borderCapStyle||u.borderCapStyle,r.setLineDash&&r.setLineDash(a.borderDash||u.borderDash),r.lineDashOffset=ut(a.borderDashOffset,u.borderDashOffset),r.lineJoin=a.borderJoinStyle||u.borderJoinStyle,r.lineWidth=ut(a.borderWidth,u.borderWidth),r.strokeStyle=a.borderColor||s.defaultColor,r.beginPath(),(n=l[0]._view).skip||(r.moveTo(n.x,n.y),d=0),t=1;t<l.length;++t)n=l[t]._view,e=-1===d?j.previousItem(l,t):l[d],n.skip||(d!==t-1&&!o||-1===d?r.moveTo(n.x,n.y):j.canvas.lineTo(r,e._view,n),d=t);c&&r.closePath(),r.stroke(),r.restore()}}}),ht=j.valueOrDefault,ft=N.global.defaultColor;function gt(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}N._set(\"global\",{elements:{point:{radius:3,pointStyle:\"circle\",backgroundColor:ft,borderColor:ft,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}});var pt=Z.extend({_type:\"point\",inRange:function(t,e){var n=this._view;return!!n&&Math.pow(t-n.x,2)+Math.pow(e-n.y,2)<Math.pow(n.hitRadius+n.radius,2)},inLabelRange:gt,inXRange:gt,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,n=this._chart.ctx,i=e.pointStyle,a=e.rotation,r=e.radius,o=e.x,l=e.y,s=N.global,u=s.defaultColor;e.skip||(void 0===t||j.canvas._isPointInArea(e,t))&&(n.strokeStyle=e.borderColor||u,n.lineWidth=ht(e.borderWidth,s.elements.point.borderWidth),n.fillStyle=e.backgroundColor||u,j.canvas.drawPoint(n,i,r,o,l,a))}}),mt=N.global.defaultColor;function vt(t){return t&&void 0!==t.width}function bt(t){var e,n,i,a,r;return vt(t)?(r=t.width/2,e=t.x-r,n=t.x+r,i=Math.min(t.y,t.base),a=Math.max(t.y,t.base)):(r=t.height/2,e=Math.min(t.x,t.base),n=Math.max(t.x,t.base),i=t.y-r,a=t.y+r),{left:e,top:i,right:n,bottom:a}}function xt(t,e,n){return t===e?n:t===n?e:t}function yt(t,e,n){var i,a,r,o,l=t.borderWidth,s=function(t){var e=t.borderSkipped,n={};return e?(t.horizontal?t.base>t.x&&(e=xt(e,\"left\",\"right\")):t.base<t.y&&(e=xt(e,\"bottom\",\"top\")),n[e]=!0,n):n}(t);return j.isObject(l)?(i=+l.top||0,a=+l.right||0,r=+l.bottom||0,o=+l.left||0):i=a=r=o=+l||0,{t:s.top||i<0?0:i>n?n:i,r:s.right||a<0?0:a>e?e:a,b:s.bottom||r<0?0:r>n?n:r,l:s.left||o<0?0:o>e?e:o}}function _t(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&bt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}N._set(\"global\",{elements:{rectangle:{backgroundColor:mt,borderColor:mt,borderSkipped:\"bottom\",borderWidth:0}}});var kt=Z.extend({_type:\"rectangle\",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=bt(t),n=e.right-e.left,i=e.bottom-e.top,a=yt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill(\"evenodd\"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return _t(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return vt(n)?_t(n,t,null):_t(n,null,e)},inXRange:function(t){return _t(this._view,t,null)},inYRange:function(t){return _t(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return vt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return vt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),wt={},Mt=st,St=ct,Ct=pt,Pt=kt;wt.Arc=Mt,wt.Line=St,wt.Point=Ct,wt.Rectangle=Pt;var At=j._deprecated,Dt=j.valueOrDefault;function Tt(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,l=e.pixels[t],s=j.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a<r;++a)o=Math.min(o,Math.abs(e[a]-e[a-1]));for(a=0,r=t.getTicks().length;a<r;++a)i=t.getPixelForTick(a),o=a>0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return j.isNullOrUndef(r)?(i=s*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:l-i/2}}N._set(\"bar\",{hover:{mode:\"label\"},scales:{xAxes:[{type:\"category\",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:\"linear\"}]}}),N._set(\"global\",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var It=at.extend({dataElementType:wt.Rectangle,_dataElementOptions:[\"backgroundColor\",\"borderColor\",\"borderSkipped\",\"borderWidth\",\"barPercentage\",\"barThickness\",\"categoryPercentage\",\"maxBarThickness\",\"minBarLength\"],initialize:function(){var t,e,n=this;at.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,At(\"bar chart\",e.barPercentage,\"scales.[x/y]Axes.barPercentage\",\"dataset.barPercentage\"),At(\"bar chart\",e.barThickness,\"scales.[x/y]Axes.barThickness\",\"dataset.barThickness\"),At(\"bar chart\",e.categoryPercentage,\"scales.[x/y]Axes.categoryPercentage\",\"dataset.categoryPercentage\"),At(\"bar chart\",n._getValueScale().options.minBarLength,\"scales.[x/y]Axes.minBarLength\",\"dataset.minBarLength\"),At(\"bar chart\",e.maxBarThickness,\"scales.[x/y]Axes.maxBarThickness\",\"dataset.maxBarThickness\")},update:function(t){var e,n,i=this,a=i.getMeta().data;for(i._ruler=i.getRuler(),e=0,n=a.length;e<n;++e)i.updateElement(a[e],e,t)},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=i.getDataset(),o=i._resolveDataElementOptions(t,e);t._xScale=i.getScaleForId(a.xAxisID),t._yScale=i.getScaleForId(a.yAxisID),t._datasetIndex=i.index,t._index=e,t._model={backgroundColor:o.backgroundColor,borderColor:o.borderColor,borderSkipped:o.borderSkipped,borderWidth:o.borderWidth,datasetLabel:r.label,label:i.chart.data.labels[e]},j.isArray(r.data[e])&&(t._model.borderSkipped=null),i._updateElementGeometry(t,e,n,o),t.pivot()},_updateElementGeometry:function(t,e,n,i){var a=this,r=t._model,o=a._getValueScale(),l=o.getBasePixel(),s=o.isHorizontal(),u=a._ruler||a.getRuler(),d=a.calculateBarValuePixels(a.index,e,i),c=a.calculateBarIndexPixels(a.index,e,u,i);r.horizontal=s,r.base=n?l:d.base,r.x=s?n?l:d.head:c.center,r.y=s?c.center:n?l:d.head,r.height=s?c.size:void 0,r.width=s?void 0:c.size},_getStacks:function(t){var e,n,i=this._getIndexScale(),a=i._getMatchingVisibleMetas(this._type),r=i.options.stacked,o=a.length,l=[];for(e=0;e<o&&(n=a[e],(!1===r||-1===l.indexOf(n.stack)||void 0===r&&void 0===n.stack)&&l.push(n.stack),n.index!==t);++e);return l},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var n=this._getStacks(t),i=void 0!==e?n.indexOf(e):-1;return-1===i?n.length-1:i},getRuler:function(){var t,e,n=this,i=n._getIndexScale(),a=[];for(t=0,e=n.getMeta().data.length;t<e;++t)a.push(i.getPixelForValue(null,t,n.index));return{pixels:a,start:i._startPixel,end:i._endPixel,stackCount:n.getStackCount(),scale:i}},calculateBarValuePixels:function(t,e,n){var i,a,r,o,l,s,u,d=this,c=d.chart,h=d._getValueScale(),f=h.isHorizontal(),g=c.data.datasets,p=h._getMatchingVisibleMetas(d._type),m=h._parseValue(g[t].data[e]),v=n.minBarLength,b=h.options.stacked,x=d.getMeta().stack,y=void 0===m.start?0:m.max>=0&&m.min>=0?m.min:m.max,_=void 0===m.start?m.end:m.max>=0&&m.min>=0?m.max-m.min:m.min-m.max,k=p.length;if(b||void 0===b&&void 0!==x)for(i=0;i<k&&(a=p[i]).index!==t;++i)a.stack===x&&(r=void 0===(u=h._parseValue(g[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(m.min<0&&r<0||m.max>=0&&r>0)&&(y+=r));return o=h.getPixelForValue(y),s=(l=h.getPixelForValue(y+_))-o,void 0!==v&&Math.abs(s)<v&&(s=v,l=_>=0&&!f||_<0&&f?o-v:o+v),{size:s,base:o,head:l,center:l+s/2}},calculateBarIndexPixels:function(t,e,n,i){var a=\"flex\"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,l=t<a.length-1?a[t+1]:null,s=n.categoryPercentage;return null===o&&(o=r-(null===l?e.end-e.start:l-r)),null===l&&(l=r+r-o),i=r-(r-Math.min(o,l))/2*s,{chunk:Math.abs(l-o)/2*s/e.stackCount,ratio:n.barPercentage,start:i}}(e,n,i):Tt(e,n,i),r=this.getStackIndex(t,this.getMeta().stack),o=a.start+a.chunk*r+a.chunk/2,l=Math.min(Dt(i.maxBarThickness,1/0),a.chunk*a.ratio);return{base:o-l/2,head:o+l/2,center:o,size:l}},draw:function(){var t=this,e=t.chart,n=t._getValueScale(),i=t.getMeta().data,a=t.getDataset(),r=i.length,o=0;for(j.canvas.clipArea(e.ctx,e.chartArea);o<r;++o){var l=n._parseValue(a.data[o]);isNaN(l.min)||isNaN(l.max)||i[o].draw()}j.canvas.unclipArea(e.ctx)},_resolveDataElementOptions:function(){var t=this,e=j.extend({},at.prototype._resolveDataElementOptions.apply(t,arguments)),n=t._getIndexScale().options,i=t._getValueScale().options;return e.barPercentage=Dt(n.barPercentage,e.barPercentage),e.barThickness=Dt(n.barThickness,e.barThickness),e.categoryPercentage=Dt(n.categoryPercentage,e.categoryPercentage),e.maxBarThickness=Dt(n.maxBarThickness,e.maxBarThickness),e.minBarLength=Dt(i.minBarLength,e.minBarLength),e}}),Ft=j.valueOrDefault,Ot=j.options.resolve;N._set(\"bubble\",{hover:{mode:\"single\"},scales:{xAxes:[{type:\"linear\",position:\"bottom\",id:\"x-axis-0\"}],yAxes:[{type:\"linear\",position:\"left\",id:\"y-axis-0\"}]},tooltips:{callbacks:{title:function(){return\"\"},label:function(t,e){var n=e.datasets[t.datasetIndex].label||\"\",i=e.datasets[t.datasetIndex].data[t.index];return n+\": (\"+t.xLabel+\", \"+t.yLabel+\", \"+i.r+\")\"}}}});var Lt=at.extend({dataElementType:wt.Point,_dataElementOptions:[\"backgroundColor\",\"borderColor\",\"borderWidth\",\"hoverBackgroundColor\",\"hoverBorderColor\",\"hoverBorderWidth\",\"hoverRadius\",\"hitRadius\",\"pointStyle\",\"rotation\"],update:function(t){var e=this,n=e.getMeta().data;j.each(n,(function(n,i){e.updateElement(n,i,t)}))},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=t.custom||{},o=i.getScaleForId(a.xAxisID),l=i.getScaleForId(a.yAxisID),s=i._resolveDataElementOptions(t,e),u=i.getDataset().data[e],d=i.index,c=n?o.getPixelForDecimal(.5):o.getPixelForValue(\"object\"==typeof u?u:NaN,e,d),h=n?l.getBasePixel():l.getPixelForValue(u,e,d);t._xScale=o,t._yScale=l,t._options=s,t._datasetIndex=d,t._index=e,t._model={backgroundColor:s.backgroundColor,borderColor:s.borderColor,borderWidth:s.borderWidth,hitRadius:s.hitRadius,pointStyle:s.pointStyle,rotation:s.rotation,radius:n?0:s.radius,skip:r.skip||isNaN(c)||isNaN(h),x:c,y:h},t.pivot()},setHoverStyle:function(t){var e=t._model,n=t._options,i=j.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Ft(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Ft(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Ft(n.hoverBorderWidth,n.borderWidth),e.radius=n.radius+n.hoverRadius},_resolveDataElementOptions:function(t,e){var n=this,i=n.chart,a=n.getDataset(),r=t.custom||{},o=a.data[e]||{},l=at.prototype._resolveDataElementOptions.apply(n,arguments),s={chart:i,dataIndex:e,dataset:a,datasetIndex:n.index};return n._cachedDataOpts===l&&(l=j.extend({},l)),l.radius=Ot([r.radius,o.r,n._config.radius,i.options.elements.point.radius],s,e),l}}),Rt=j.valueOrDefault,zt=Math.PI,Nt=2*zt,Bt=zt/2;N._set(\"doughnut\",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:\"single\"},legendCallback:function(t){var e,n,i,a=document.createElement(\"ul\"),r=t.data,o=r.datasets,l=r.labels;if(a.setAttribute(\"class\",t.id+\"-legend\"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement(\"li\"))).appendChild(document.createElement(\"span\")).style.backgroundColor=o[0].backgroundColor[e],l[e]&&i.appendChild(document.createTextNode(l[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r]&&(a.data[r].hidden=!a.data[r].hidden);o.update()}},cutoutPercentage:50,rotation:-Bt,circumference:Nt,tooltips:{callbacks:{title:function(){return\"\"},label:function(t,e){var n=e.labels[t.index],i=\": \"+e.datasets[t.datasetIndex].data[t.index];return j.isArray(n)?(n=n.slice())[0]+=i:n+=i,n}}}});var Et=at.extend({dataElementType:wt.Arc,linkScales:j.noop,_dataElementOptions:[\"backgroundColor\",\"borderColor\",\"borderWidth\",\"borderAlign\",\"hoverBackgroundColor\",\"hoverBorderColor\",\"hoverBorderWidth\"],getRingIndex:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&++e;return e},update:function(t){var e,n,i,a,r=this,o=r.chart,l=o.chartArea,s=o.options,u=1,d=1,c=0,h=0,f=r.getMeta(),g=f.data,p=s.cutoutPercentage/100||0,m=s.circumference,v=r._getRingWeight(r.index);if(m<Nt){var b=s.rotation%Nt,x=(b+=b>=zt?-Nt:b<-zt?Nt:0)+m,y=Math.cos(b),_=Math.sin(b),k=Math.cos(x),w=Math.sin(x),M=b<=0&&x>=0||x>=Nt,S=b<=Bt&&x>=Bt||x>=Nt+Bt,C=b<=-Bt&&x>=-Bt||x>=zt+Bt,P=b===-zt||x>=zt?-1:Math.min(y,y*p,k,k*p),A=C?-1:Math.min(_,_*p,w,w*p),D=M?1:Math.max(y,y*p,k,k*p),T=S?1:Math.max(_,_*p,w,w*p);u=(D-P)/2,d=(T-A)/2,c=-(D+P)/2,h=-(T+A)/2}for(i=0,a=g.length;i<a;++i)g[i]._options=r._resolveDataElementOptions(g[i],i);for(o.borderWidth=r.getMaxBorderWidth(),e=(l.right-l.left-o.borderWidth)/u,n=(l.bottom-l.top-o.borderWidth)/d,o.outerRadius=Math.max(Math.min(e,n)/2,0),o.innerRadius=Math.max(o.outerRadius*p,0),o.radiusLength=(o.outerRadius-o.innerRadius)/(r._getVisibleDatasetWeightTotal()||1),o.offsetX=c*o.outerRadius,o.offsetY=h*o.outerRadius,f.total=r.calculateTotal(),r.outerRadius=o.outerRadius-o.radiusLength*r._getRingWeightOffset(r.index),r.innerRadius=Math.max(r.outerRadius-o.radiusLength*v,0),i=0,a=g.length;i<a;++i)r.updateElement(g[i],i,t)},updateElement:function(t,e,n){var i=this,a=i.chart,r=a.chartArea,o=a.options,l=o.animation,s=(r.left+r.right)/2,u=(r.top+r.bottom)/2,d=o.rotation,c=o.rotation,h=i.getDataset(),f=n&&l.animateRotate||t.hidden?0:i.calculateCircumference(h.data[e])*(o.circumference/Nt),g=n&&l.animateScale?0:i.innerRadius,p=n&&l.animateScale?0:i.outerRadius,m=t._options||{};j.extend(t,{_datasetIndex:i.index,_index:e,_model:{backgroundColor:m.backgroundColor,borderColor:m.borderColor,borderWidth:m.borderWidth,borderAlign:m.borderAlign,x:s+a.offsetX,y:u+a.offsetY,startAngle:d,endAngle:c,circumference:f,outerRadius:p,innerRadius:g,label:j.valueAtIndexOrDefault(h.label,e,a.data.labels[e])}});var v=t._model;n&&l.animateRotate||(v.startAngle=0===e?o.rotation:i.getMeta().data[e-1]._model.endAngle,v.endAngle=v.startAngle+v.circumference),t.pivot()},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return j.each(n.data,(function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))})),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?Nt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,l,s,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e<n;++e)if(d.isDatasetVisible(e)){t=(i=d.getDatasetMeta(e)).data,e!==this.index&&(r=i.controller);break}if(!t)return 0;for(e=0,n=t.length;e<n;++e)a=t[e],r?(r._configure(),o=r._resolveDataElementOptions(a,e)):o=a._options,\"inner\"!==o.borderAlign&&(l=o.borderWidth,u=(s=o.hoverBorderWidth)>(u=l>u?l:u)?s:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=j.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Rt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Rt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Rt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&(e+=this._getRingWeight(n));return e},_getRingWeight:function(t){return Math.max(Rt(this.chart.data.datasets[t].weight,1),0)},_getVisibleDatasetWeightTotal:function(){return this._getRingWeightOffset(this.chart.data.datasets.length)}});N._set(\"horizontalBar\",{hover:{mode:\"index\",axis:\"y\"},scales:{xAxes:[{type:\"linear\",position:\"bottom\"}],yAxes:[{type:\"category\",position:\"left\",offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:\"left\"}},tooltips:{mode:\"index\",axis:\"y\"}}),N._set(\"global\",{datasets:{horizontalBar:{categoryPercentage:.8,barPercentage:.9}}});var Wt=It.extend({_getValueScaleId:function(){return this.getMeta().xAxisID},_getIndexScaleId:function(){return this.getMeta().yAxisID}}),Vt=j.valueOrDefault,Ht=j.options.resolve,jt=j.canvas._isPointInArea;function qt(t,e){var n=t&&t.options.ticks||{},i=n.reverse,a=void 0===n.min?e:0,r=void 0===n.max?e:0;return{start:i?r:a,end:i?a:r}}N._set(\"line\",{showLines:!0,spanGaps:!1,hover:{mode:\"label\"},scales:{xAxes:[{type:\"category\",id:\"x-axis-0\"}],yAxes:[{type:\"linear\",id:\"y-axis-0\"}]}});var Ut=at.extend({datasetElementType:wt.Line,dataElementType:wt.Point,_datasetElementOptions:[\"backgroundColor\",\"borderCapStyle\",\"borderColor\",\"borderDash\",\"borderDashOffset\",\"borderJoinStyle\",\"borderWidth\",\"cubicInterpolationMode\",\"fill\"],_dataElementOptions:{backgroundColor:\"pointBackgroundColor\",borderColor:\"pointBorderColor\",borderWidth:\"pointBorderWidth\",hitRadius:\"pointHitRadius\",hoverBackgroundColor:\"pointHoverBackgroundColor\",hoverBorderColor:\"pointHoverBorderColor\",hoverBorderWidth:\"pointHoverBorderWidth\",hoverRadius:\"pointHoverRadius\",pointStyle:\"pointStyle\",radius:\"pointRadius\",rotation:\"pointRotation\"},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],l=i.chart.options,s=i._config,u=i._showLine=Vt(s.showLine,l.showLines);for(i._xScale=i.getScaleForId(a.xAxisID),i._yScale=i.getScaleForId(a.yAxisID),u&&(void 0!==s.tension&&void 0===s.lineTension&&(s.lineTension=s.tension),r._scale=i._yScale,r._datasetIndex=i.index,r._children=o,r._model=i._resolveDatasetElementOptions(r),r.pivot()),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(u&&0!==r._model.tension&&i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i,a,r=this,o=r.getMeta(),l=t.custom||{},s=r.getDataset(),u=r.index,d=s.data[e],c=r._xScale,h=r._yScale,f=o.dataset._model,g=r._resolveDataElementOptions(t,e);i=c.getPixelForValue(\"object\"==typeof d?d:NaN,e,u),a=n?h.getBasePixel():r.calculatePointY(d,e,u),t._xScale=c,t._yScale=h,t._options=g,t._datasetIndex=u,t._index=e,t._model={x:i,y:a,skip:l.skip||isNaN(i)||isNaN(a),radius:g.radius,pointStyle:g.pointStyle,rotation:g.rotation,backgroundColor:g.backgroundColor,borderColor:g.borderColor,borderWidth:g.borderWidth,tension:Vt(l.tension,f?f.tension:0),steppedLine:!!f&&f.steppedLine,hitRadius:g.hitRadius}},_resolveDatasetElementOptions:function(t){var e,n,i,a,r,o,l,s,u,d,c,h=this,f=h._config,g=t.custom||{},p=h.chart.options,m=p.elements.line,v=at.prototype._resolveDatasetElementOptions.apply(h,arguments);return v.spanGaps=Vt(f.spanGaps,p.spanGaps),v.tension=Vt(f.lineTension,m.tension),v.steppedLine=Ht([g.steppedLine,f.steppedLine,m.stepped]),v.clip=(e=Vt(f.clip,(o=h._xScale,l=h._yScale,s=v.borderWidth,d=qt(o,u=s/2),{top:(c=qt(l,u)).end,right:d.end,bottom:c.start,left:d.start})),j.isObject(e)?(n=e.top,i=e.right,a=e.bottom,r=e.left):n=i=a=r=e,{top:n,right:i,bottom:a,left:r}),v},calculatePointY:function(t,e,n){var i,a,r,o,l,s,u,d=this.chart,c=this._yScale,h=0,f=0;if(c.options.stacked){for(l=+c.getRightValue(t),u=(s=d._getSortedVisibleDatasetMetas()).length,i=0;i<u&&(r=s[i]).index!==n;++i)a=d.data.datasets[r.index],\"line\"===r.type&&r.yAxisID===c.id&&((o=+c.getRightValue(a.data[e]))<0?f+=o||0:h+=o||0);return l<0?c.getPixelForValue(f+l):c.getPixelForValue(h+l)}return c.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,n,i,a=this.chart,r=this.getMeta(),o=r.dataset._model,l=a.chartArea,s=r.data||[];function u(t,e,n){return Math.max(Math.min(t,n),e)}if(o.spanGaps&&(s=s.filter((function(t){return!t._model.skip}))),\"monotone\"===o.cubicInterpolationMode)j.splineCurveMonotone(s);else for(t=0,e=s.length;t<e;++t)n=s[t]._model,i=j.splineCurve(j.previousItem(s,t)._model,n,j.nextItem(s,t)._model,o.tension),n.controlPointPreviousX=i.previous.x,n.controlPointPreviousY=i.previous.y,n.controlPointNextX=i.next.x,n.controlPointNextY=i.next.y;if(a.options.elements.line.capBezierPoints)for(t=0,e=s.length;t<e;++t)n=s[t]._model,jt(n,l)&&(t>0&&jt(s[t-1]._model,l)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,l.left,l.right),n.controlPointPreviousY=u(n.controlPointPreviousY,l.top,l.bottom)),t<s.length-1&&jt(s[t+1]._model,l)&&(n.controlPointNextX=u(n.controlPointNextX,l.left,l.right),n.controlPointNextY=u(n.controlPointNextY,l.top,l.bottom)))},draw:function(){var t,e=this,n=e.chart,i=e.getMeta(),a=i.data||[],r=n.chartArea,o=n.canvas,l=0,s=a.length;for(e._showLine&&(t=i.dataset._model.clip,j.canvas.clipArea(n.ctx,{left:!1===t.left?0:r.left-t.left,right:!1===t.right?o.width:r.right+t.right,top:!1===t.top?0:r.top-t.top,bottom:!1===t.bottom?o.height:r.bottom+t.bottom}),i.dataset.draw(),j.canvas.unclipArea(n.ctx));l<s;++l)a[l].draw(r)},setHoverStyle:function(t){var e=t._model,n=t._options,i=j.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Vt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Vt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Vt(n.hoverBorderWidth,n.borderWidth),e.radius=Vt(n.hoverRadius,n.radius)}}),Yt=j.options.resolve;N._set(\"polarArea\",{scale:{type:\"radialLinear\",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e,n,i,a=document.createElement(\"ul\"),r=t.data,o=r.datasets,l=r.labels;if(a.setAttribute(\"class\",t.id+\"-legend\"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement(\"li\"))).appendChild(document.createElement(\"span\")).style.backgroundColor=o[0].backgroundColor[e],l[e]&&i.appendChild(document.createTextNode(l[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r].hidden=!a.data[r].hidden;o.update()}},tooltips:{callbacks:{title:function(){return\"\"},label:function(t,e){return e.labels[t.index]+\": \"+t.yLabel}}}});var Gt=at.extend({dataElementType:wt.Arc,linkScales:j.noop,_dataElementOptions:[\"backgroundColor\",\"borderColor\",\"borderWidth\",\"borderAlign\",\"hoverBackgroundColor\",\"hoverBorderColor\",\"hoverBorderWidth\"],_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i,a=this,r=a.getDataset(),o=a.getMeta(),l=a.chart.options.startAngle||0,s=a._starts=[],u=a._angles=[],d=o.data;for(a._updateRadius(),o.count=a.countVisibleElements(),e=0,n=r.data.length;e<n;e++)s[e]=l,i=a._computeAngle(e),u[e]=i,l+=i;for(e=0,n=d.length;e<n;++e)d[e]._options=a._resolveDataElementOptions(d[e],e),a.updateElement(d[e],e,t)},_updateRadius:function(){var t=this,e=t.chart,n=e.chartArea,i=e.options,a=Math.min(n.right-n.left,n.bottom-n.top);e.outerRadius=Math.max(a/2,0),e.innerRadius=Math.max(i.cutoutPercentage?e.outerRadius/100*i.cutoutPercentage:1,0),e.radiusLength=(e.outerRadius-e.innerRadius)/e.getVisibleDatasetCount(),t.outerRadius=e.outerRadius-e.radiusLength*t.index,t.innerRadius=t.outerRadius-e.radiusLength},updateElement:function(t,e,n){var i=this,a=i.chart,r=i.getDataset(),o=a.options,l=o.animation,s=a.scale,u=a.data.labels,d=s.xCenter,c=s.yCenter,h=o.startAngle,f=t.hidden?0:s.getDistanceFromCenterForValue(r.data[e]),g=i._starts[e],p=g+(t.hidden?0:i._angles[e]),m=l.animateScale?0:s.getDistanceFromCenterForValue(r.data[e]),v=t._options||{};j.extend(t,{_datasetIndex:i.index,_index:e,_scale:s,_model:{backgroundColor:v.backgroundColor,borderColor:v.borderColor,borderWidth:v.borderWidth,borderAlign:v.borderAlign,x:d,y:c,innerRadius:0,outerRadius:n?m:f,startAngle:n&&l.animateRotate?h:g,endAngle:n&&l.animateRotate?h:p,label:j.valueAtIndexOrDefault(u,e,u[e])}}),t.pivot()},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),n=0;return j.each(e.data,(function(e,i){isNaN(t.data[i])||e.hidden||n++})),n},setHoverStyle:function(t){var e=t._model,n=t._options,i=j.getHoverColor,a=j.valueOrDefault;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=a(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=a(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=a(n.hoverBorderWidth,n.borderWidth)},_computeAngle:function(t){var e=this,n=this.getMeta().count,i=e.getDataset(),a=e.getMeta();if(isNaN(i.data[t])||a.data[t].hidden)return 0;var r={chart:e.chart,dataIndex:t,dataset:i,datasetIndex:e.index};return Yt([e.chart.options.elements.arc.angle,2*Math.PI/n],r,t)}});N._set(\"pie\",j.clone(N.doughnut)),N._set(\"pie\",{cutoutPercentage:0});var Xt=Et,Kt=j.valueOrDefault;N._set(\"radar\",{spanGaps:!1,scale:{type:\"radialLinear\"},elements:{line:{fill:\"start\",tension:0}}});var Zt=at.extend({datasetElementType:wt.Line,dataElementType:wt.Point,linkScales:j.noop,_datasetElementOptions:[\"backgroundColor\",\"borderWidth\",\"borderColor\",\"borderCapStyle\",\"borderDash\",\"borderDashOffset\",\"borderJoinStyle\",\"fill\"],_dataElementOptions:{backgroundColor:\"pointBackgroundColor\",borderColor:\"pointBorderColor\",borderWidth:\"pointBorderWidth\",hitRadius:\"pointHitRadius\",hoverBackgroundColor:\"pointHoverBackgroundColor\",hoverBorderColor:\"pointHoverBorderColor\",hoverBorderWidth:\"pointHoverBorderWidth\",hoverRadius:\"pointHoverRadius\",pointStyle:\"pointStyle\",radius:\"pointRadius\",rotation:\"pointRotation\"},_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],l=i.chart.scale,s=i._config;for(void 0!==s.tension&&void 0===s.lineTension&&(s.lineTension=s.tension),r._scale=l,r._datasetIndex=i.index,r._children=o,r._loop=!0,r._model=i._resolveDatasetElementOptions(r),r.pivot(),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i=this,a=t.custom||{},r=i.getDataset(),o=i.chart.scale,l=o.getPointPositionForValue(e,r.data[e]),s=i._resolveDataElementOptions(t,e),u=i.getMeta().dataset._model,d=n?o.xCenter:l.x,c=n?o.yCenter:l.y;t._scale=o,t._options=s,t._datasetIndex=i.index,t._index=e,t._model={x:d,y:c,skip:a.skip||isNaN(d)||isNaN(c),radius:s.radius,pointStyle:s.pointStyle,rotation:s.rotation,backgroundColor:s.backgroundColor,borderColor:s.borderColor,borderWidth:s.borderWidth,tension:Kt(a.tension,u?u.tension:0),hitRadius:s.hitRadius}},_resolveDatasetElementOptions:function(){var t=this,e=t._config,n=t.chart.options,i=at.prototype._resolveDatasetElementOptions.apply(t,arguments);return i.spanGaps=Kt(e.spanGaps,n.spanGaps),i.tension=Kt(e.lineTension,n.elements.line.tension),i},updateBezierControlPoints:function(){var t,e,n,i,a=this.getMeta(),r=this.chart.chartArea,o=a.data||[];function l(t,e,n){return Math.max(Math.min(t,n),e)}for(a.dataset._model.spanGaps&&(o=o.filter((function(t){return!t._model.skip}))),t=0,e=o.length;t<e;++t)n=o[t]._model,i=j.splineCurve(j.previousItem(o,t,!0)._model,n,j.nextItem(o,t,!0)._model,n.tension),n.controlPointPreviousX=l(i.previous.x,r.left,r.right),n.controlPointPreviousY=l(i.previous.y,r.top,r.bottom),n.controlPointNextX=l(i.next.x,r.left,r.right),n.controlPointNextY=l(i.next.y,r.top,r.bottom)},setHoverStyle:function(t){var e=t._model,n=t._options,i=j.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Kt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Kt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Kt(n.hoverBorderWidth,n.borderWidth),e.radius=Kt(n.hoverRadius,n.radius)}});N._set(\"scatter\",{hover:{mode:\"single\"},scales:{xAxes:[{id:\"x-axis-1\",type:\"linear\",position:\"bottom\"}],yAxes:[{id:\"y-axis-1\",type:\"linear\",position:\"left\"}]},tooltips:{callbacks:{title:function(){return\"\"},label:function(t){return\"(\"+t.xLabel+\", \"+t.yLabel+\")\"}}}}),N._set(\"global\",{datasets:{scatter:{showLine:!1}}});var $t={bar:It,bubble:Lt,doughnut:Et,horizontalBar:Wt,line:Ut,polarArea:Gt,pie:Xt,radar:Zt,scatter:Ut};function Jt(t,e){return t.native?{x:t.x,y:t.y}:j.getRelativePosition(t,e)}function Qt(t,e){var n,i,a,r,o,l,s=t._getSortedVisibleDatasetMetas();for(i=0,r=s.length;i<r;++i)for(a=0,o=(n=s[i].data).length;a<o;++a)(l=n[a])._view.skip||e(l)}function te(t,e){var n=[];return Qt(t,(function(t){t.inRange(e.x,e.y)&&n.push(t)})),n}function ee(t,e,n,i){var a=Number.POSITIVE_INFINITY,r=[];return Qt(t,(function(t){if(!n||t.inRange(e.x,e.y)){var o=t.getCenterPoint(),l=i(e,o);l<a?(r=[t],a=l):l===a&&r.push(t)}})),r}function ne(t){var e=-1!==t.indexOf(\"x\"),n=-1!==t.indexOf(\"y\");return function(t,i){var a=e?Math.abs(t.x-i.x):0,r=n?Math.abs(t.y-i.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(r,2))}}function ie(t,e,n){var i=Jt(e,t);n.axis=n.axis||\"x\";var a=ne(n.axis),r=n.intersect?te(t,i):ee(t,i,!1,a),o=[];return r.length?(t._getSortedVisibleDatasetMetas().forEach((function(t){var e=t.data[r[0]._index];e&&!e._view.skip&&o.push(e)})),o):[]}var ae={modes:{single:function(t,e){var n=Jt(e,t),i=[];return Qt(t,(function(t){if(t.inRange(n.x,n.y))return i.push(t),i})),i.slice(0,1)},label:ie,index:ie,dataset:function(t,e,n){var i=Jt(e,t);n.axis=n.axis||\"xy\";var a=ne(n.axis),r=n.intersect?te(t,i):ee(t,i,!1,a);return r.length>0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},\"x-axis\":function(t,e){return ie(t,e,{intersect:!1})},point:function(t,e){return te(t,Jt(e,t))},nearest:function(t,e,n){var i=Jt(e,t);n.axis=n.axis||\"xy\";var a=ne(n.axis);return ee(t,i,n.intersect,a)},x:function(t,e,n){var i=Jt(e,t),a=[],r=!1;return Qt(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Jt(e,t),a=[],r=!1;return Qt(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},re=j.extend;function oe(t,e){return j.where(t,(function(t){return t.pos===e}))}function le(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function se(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function ue(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var l=r.getPadding();o.top=Math.max(o.top,l.top),o.left=Math.max(o.left,l.left),o.bottom=Math.max(o.bottom,l.bottom),o.right=Math.max(o.right,l.right)}if(i=e.outerWidth-se(o,t,\"left\",\"right\"),a=e.outerHeight-se(o,t,\"top\",\"bottom\"),i!==t.w||a!==t.h){t.w=i,t.h=a;var s=n.horizontal?[i,t.w]:[a,t.h];return!(s[0]===s[1]||isNaN(s[0])&&isNaN(s[1]))}}function de(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?[\"left\",\"right\"]:[\"top\",\"bottom\"])}function ce(t,e,n){var i,a,r,o,l,s,u=[];for(i=0,a=t.length;i<a;++i)(o=(r=t[i]).box).update(r.width||e.w,r.height||e.h,de(r.horizontal,e)),ue(e,n,r)&&(s=!0,u.length&&(l=!0)),o.fullWidth||u.push(r);return l&&ce(u,e,n)||s}function he(t,e,n){var i,a,r,o,l=n.padding,s=e.x,u=e.y;for(i=0,a=t.length;i<a;++i)o=(r=t[i]).box,r.horizontal?(o.left=o.fullWidth?l.left:e.left,o.right=o.fullWidth?n.outerWidth-l.right:e.left+e.w,o.top=u,o.bottom=u+o.height,o.width=o.right-o.left,u=o.bottom):(o.left=s,o.right=s+o.width,o.top=e.top,o.bottom=e.top+e.h,o.height=o.bottom-o.top,s=o.right);e.x=s,e.y=u}N._set(\"global\",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var fe,ge={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||\"top\",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw:function(){e.draw.apply(e,arguments)}}]},t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=[\"fullWidth\",\"position\",\"weight\"],r=a.length,o=0;o<r;++o)i=a[o],n.hasOwnProperty(i)&&(e[i]=n[i])},update:function(t,e,n){if(t){var i=t.options.layout||{},a=j.options.toPadding(i.padding),r=e-a.width,o=n-a.height,l=function(t){var e=function(t){var e,n,i,a=[];for(e=0,n=(t||[]).length;e<n;++e)i=t[e],a.push({index:e,box:i,pos:i.position,horizontal:i.isHorizontal(),weight:i.weight});return a}(t),n=le(oe(e,\"left\"),!0),i=le(oe(e,\"right\")),a=le(oe(e,\"top\"),!0),r=le(oe(e,\"bottom\"));return{leftAndTop:n.concat(a),rightAndBottom:i.concat(r),chartArea:oe(e,\"chartArea\"),vertical:n.concat(i),horizontal:a.concat(r)}}(t.boxes),s=l.vertical,u=l.horizontal,d=Object.freeze({outerWidth:e,outerHeight:n,padding:a,availableWidth:r,vBoxMaxWidth:r/2/s.length,hBoxMaxHeight:o/2}),c=re({maxPadding:re({},a),w:r,h:o,x:a.left,y:a.top},a);!function(t,e){var n,i,a;for(n=0,i=t.length;n<i;++n)(a=t[n]).width=a.horizontal?a.box.fullWidth&&e.availableWidth:e.vBoxMaxWidth,a.height=a.horizontal&&e.hBoxMaxHeight}(s.concat(u),d),ce(s,c,d),ce(u,c,d)&&ce(s,c,d),function(t){var e=t.maxPadding;function n(n){var i=Math.max(e[n]-t[n],0);return t[n]+=i,i}t.y+=n(\"top\"),t.x+=n(\"left\"),n(\"right\"),n(\"bottom\")}(c),he(l.leftAndTop,c,d),c.x+=c.w,c.y+=c.h,he(l.rightAndBottom,c,d),t.chartArea={left:c.left,top:c.top,right:c.left+c.w,bottom:c.top+c.h},j.each(l.chartArea,(function(e){var n=e.box;re(n,t.chartArea),n.update(c.w,c.h)}))}}},pe=(fe=Object.freeze({__proto__:null,default:\"/*\\r\\n * DOM element rendering detection\\r\\n * https://davidwalsh.name/detect-node-insertion\\r\\n */\\r\\n@keyframes chartjs-render-animation {\\r\\n\\tfrom { opacity: 0.99; }\\r\\n\\tto { opacity: 1; }\\r\\n}\\r\\n\\r\\n.chartjs-render-monitor {\\r\\n\\tanimation: chartjs-render-animation 0.001s;\\r\\n}\\r\\n\\r\\n/*\\r\\n * DOM element resizing detection\\r\\n * https://github.com/marcj/css-element-queries\\r\\n */\\r\\n.chartjs-size-monitor,\\r\\n.chartjs-size-monitor-expand,\\r\\n.chartjs-size-monitor-shrink {\\r\\n\\tposition: absolute;\\r\\n\\tdirection: ltr;\\r\\n\\tleft: 0;\\r\\n\\ttop: 0;\\r\\n\\tright: 0;\\r\\n\\tbottom: 0;\\r\\n\\toverflow: hidden;\\r\\n\\tpointer-events: none;\\r\\n\\tvisibility: hidden;\\r\\n\\tz-index: -1;\\r\\n}\\r\\n\\r\\n.chartjs-size-monitor-expand > div {\\r\\n\\tposition: absolute;\\r\\n\\twidth: 1000000px;\\r\\n\\theight: 1000000px;\\r\\n\\tleft: 0;\\r\\n\\ttop: 0;\\r\\n}\\r\\n\\r\\n.chartjs-size-monitor-shrink > div {\\r\\n\\tposition: absolute;\\r\\n\\twidth: 200%;\\r\\n\\theight: 200%;\\r\\n\\tleft: 0;\\r\\n\\ttop: 0;\\r\\n}\\r\\n\"}))&&fe.default||fe,me=\"$chartjs\",ve=\"chartjs-\",be=ve+\"size-monitor\",xe=ve+\"render-monitor\",ye=ve+\"render-animation\",_e=[\"animationstart\",\"webkitAnimationStart\"],ke={touchstart:\"mousedown\",touchmove:\"mousemove\",touchend:\"mouseup\",pointerenter:\"mouseenter\",pointerdown:\"mousedown\",pointermove:\"mousemove\",pointerup:\"mouseup\",pointerleave:\"mouseout\",pointerout:\"mouseout\"};function we(t,e){var n=j.getStyle(t,e),i=n&&n.match(/^(\\d+)(\\.\\d+)?px$/);return i?Number(i[1]):void 0}var Me=function(){var t=!1;try{var e=Object.defineProperty({},\"passive\",{get:function(){t=!0}});window.addEventListener(\"e\",null,e)}catch(t){}return t}(),Se=!!Me&&{passive:!0};function Ce(t,e,n){t.addEventListener(e,n,Se)}function Pe(t,e,n){t.removeEventListener(e,n,Se)}function Ae(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function De(t){var e=document.createElement(\"div\");return e.className=t||\"\",e}function Te(t,e,n){var i,a,r,o,l=t[me]||(t[me]={}),s=l.resizer=function(t){var e=1e6,n=De(be),i=De(be+\"-expand\"),a=De(be+\"-shrink\");i.appendChild(De()),a.appendChild(De()),n.appendChild(i),n.appendChild(a),n._reset=function(){i.scrollLeft=e,i.scrollTop=e,a.scrollLeft=e,a.scrollTop=e};var r=function(){n._reset(),t()};return Ce(i,\"scroll\",r.bind(i,\"expand\")),Ce(a,\"scroll\",r.bind(a,\"shrink\")),n}((i=function(){if(l.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Ae(\"resize\",n)),i&&i.clientWidth<a&&n.canvas&&e(Ae(\"resize\",n))}},r=!1,o=[],function(){o=Array.prototype.slice.call(arguments),a=a||this,r||(r=!0,j.requestAnimFrame.call(window,(function(){r=!1,i.apply(a,o)})))}));!function(t,e){var n=t[me]||(t[me]={}),i=n.renderProxy=function(t){t.animationName===ye&&e()};j.each(_e,(function(e){Ce(t,e,i)})),n.reflow=!!t.offsetParent,t.classList.add(xe)}(t,(function(){if(l.resizer){var e=t.parentNode;e&&e!==s.parentNode&&e.insertBefore(s,e.firstChild),s._reset()}}))}function Ie(t){var e=t[me]||{},n=e.resizer;delete e.resizer,function(t){var e=t[me]||{},n=e.renderProxy;n&&(j.each(_e,(function(e){Pe(t,e,n)})),delete e.renderProxy),t.classList.remove(xe)}(t),n&&n.parentNode&&n.parentNode.removeChild(n)}var Fe={disableCSSInjection:!1,_enabled:\"undefined\"!=typeof window&&\"undefined\"!=typeof document,_ensureLoaded:function(t){if(!this.disableCSSInjection){var e=t.getRootNode?t.getRootNode():document;!function(t,e){var n=t[me]||(t[me]={});if(!n.containsStyles){n.containsStyles=!0,e=\"/* Chart.js */\\n\"+e;var i=document.createElement(\"style\");i.setAttribute(\"type\",\"text/css\"),i.appendChild(document.createTextNode(e)),t.appendChild(i)}}(e.host?e:document.head,pe)}},acquireContext:function(t,e){\"string\"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext(\"2d\");return n&&n.canvas===t?(this._ensureLoaded(t),function(t,e){var n=t.style,i=t.getAttribute(\"height\"),a=t.getAttribute(\"width\");if(t[me]={initial:{height:i,width:a,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||\"block\",null===a||\"\"===a){var r=we(t,\"width\");void 0!==r&&(t.width=r)}if(null===i||\"\"===i)if(\"\"===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var o=we(t,\"height\");void 0!==r&&(t.height=o)}}(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[me]){var n=e[me].initial;[\"height\",\"width\"].forEach((function(t){var i=n[t];j.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)})),j.each(n.style||{},(function(t,n){e.style[n]=t})),e.width=e.width,delete e[me]}},addEventListener:function(t,e,n){var i=t.canvas;if(\"resize\"!==e){var a=n[me]||(n[me]={}),r=(a.proxies||(a.proxies={}))[t.id+\"_\"+e]=function(e){n(function(t,e){var n=ke[t.type]||t.type,i=j.getRelativePosition(t,e);return Ae(n,e,i.x,i.y,t)}(e,t))};Ce(i,e,r)}else Te(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if(\"resize\"!==e){var a=((n[me]||{}).proxies||{})[t.id+\"_\"+e];a&&Pe(i,e,a)}else Ie(i)}};j.addEvent=Ce,j.removeEvent=Pe;var Oe=Fe._enabled?Fe:{acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext(\"2d\")||null}},Le=j.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},Oe);N._set(\"global\",{plugins:{}});var Re={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach((function(t){var n=e.indexOf(t);-1!==n&&e.splice(n,1)})),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,n){var i,a,r,o,l,s=this.descriptors(t),u=s.length;for(i=0;i<u;++i)if(\"function\"==typeof(l=(r=(a=s[i]).plugin)[e])&&((o=[t].concat(n||[])).push(a.options),!1===l.apply(r,o)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var n=[],i=[],a=t&&t.config||{},r=a.options&&a.options.plugins||{};return this._plugins.concat(a.plugins||[]).forEach((function(t){if(-1===n.indexOf(t)){var e=t.id,a=r[e];!1!==a&&(!0===a&&(a=j.clone(N.global.plugins[e])),n.push(t),i.push({plugin:t,options:a||{}}))}})),e.descriptors=i,e.id=this._cacheId,i},_invalidate:function(t){delete t.$plugins}},ze={constructors:{},defaults:{},registerScaleType:function(t,e,n){this.constructors[t]=e,this.defaults[t]=j.clone(n)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?j.merge(Object.create(null),[N.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){var n=this;n.defaults.hasOwnProperty(t)&&(n.defaults[t]=j.extend(n.defaults[t],e))},addScalesToLayout:function(t){j.each(t.scales,(function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,ge.addBox(t,e)}))}},Ne=j.valueOrDefault,Be=j.rtl.getRtlAdapter;N._set(\"global\",{tooltips:{enabled:!0,custom:null,mode:\"nearest\",position:\"average\",intersect:!0,backgroundColor:\"rgba(0,0,0,0.8)\",titleFontStyle:\"bold\",titleSpacing:2,titleMarginBottom:6,titleFontColor:\"#fff\",titleAlign:\"left\",bodySpacing:2,bodyFontColor:\"#fff\",bodyAlign:\"left\",footerFontStyle:\"bold\",footerSpacing:2,footerMarginTop:6,footerFontColor:\"#fff\",footerAlign:\"left\",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:\"#fff\",displayColors:!0,borderColor:\"rgba(0,0,0,0)\",borderWidth:0,callbacks:{beforeTitle:j.noop,title:function(t,e){var n=\"\",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index<a&&(n=i[r.index])}return n},afterTitle:j.noop,beforeBody:j.noop,beforeLabel:j.noop,label:function(t,e){var n=e.datasets[t.datasetIndex].label||\"\";return n&&(n+=\": \"),j.isNullOrUndef(t.value)?n+=t.yLabel:n+=t.value,n},labelColor:function(t,e){var n=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:n.borderColor,backgroundColor:n.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:j.noop,afterBody:j.noop,beforeFooter:j.noop,footer:j.noop,afterFooter:j.noop}}});var Ee={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;e<n;++e){var o=t[e];if(o&&o.hasValue()){var l=o.tooltipPosition();i+=l.x,a+=l.y,++r}}return{x:i/r,y:a/r}},nearest:function(t,e){var n,i,a,r=e.x,o=e.y,l=Number.POSITIVE_INFINITY;for(n=0,i=t.length;n<i;++n){var s=t[n];if(s&&s.hasValue()){var u=s.getCenterPoint(),d=j.distanceBetweenPoints(e,u);d<l&&(l=d,a=s)}}if(a){var c=a.tooltipPosition();r=c.x,o=c.y}return{x:r,y:o}}};function We(t,e){return e&&(j.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Ve(t){return(\"string\"==typeof t||t instanceof String)&&t.indexOf(\"\\n\")>-1?t.split(\"\\n\"):t}function He(t){var e=N.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:Ne(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:Ne(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:Ne(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:Ne(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:Ne(t.titleFontStyle,e.defaultFontStyle),titleFontSize:Ne(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:Ne(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:Ne(t.footerFontStyle,e.defaultFontStyle),footerFontSize:Ne(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function je(t,e){return\"center\"===e?t.x+t.width/2:\"right\"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function qe(t){return We([],Ve(t))}var Ue=Z.extend({initialize:function(){this._model=He(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options.callbacks,n=e.beforeTitle.apply(t,arguments),i=e.title.apply(t,arguments),a=e.afterTitle.apply(t,arguments),r=[];return r=We(r,Ve(n)),r=We(r,Ve(i)),r=We(r,Ve(a))},getBeforeBody:function(){return qe(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return j.each(t,(function(t){var r={before:[],lines:[],after:[]};We(r.before,Ve(i.beforeLabel.call(n,t,e))),We(r.lines,i.label.call(n,t,e)),We(r.after,Ve(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return qe(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=We(r,Ve(n)),r=We(r,Ve(i)),r=We(r,Ve(a))},update:function(t){var e,n,i,a,r,o,l,s,u,d,c=this,h=c._options,f=c._model,g=c._model=He(h),p=c._active,m=c._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},x={width:f.width,height:f.height},y={x:f.caretX,y:f.caretY};if(p.length){g.opacity=1;var _=[],k=[];y=Ee[h.position].call(c,p,c._eventPosition);var w=[];for(e=0,n=p.length;e<n;++e)w.push((i=p[e],a=void 0,r=void 0,o=void 0,l=void 0,s=void 0,u=void 0,d=void 0,a=i._xScale,r=i._yScale||i._scale,o=i._index,l=i._datasetIndex,s=i._chart.getDatasetMeta(l).controller,u=s._getIndexScale(),d=s._getValueScale(),{xLabel:a?a.getLabelForIndex(o,l):\"\",yLabel:r?r.getLabelForIndex(o,l):\"\",label:u?\"\"+u.getLabelForIndex(o,l):\"\",value:d?\"\"+d.getLabelForIndex(o,l):\"\",index:o,datasetIndex:l,x:i._model.x,y:i._model.y}));h.filter&&(w=w.filter((function(t){return h.filter(t,m)}))),h.itemSort&&(w=w.sort((function(t,e){return h.itemSort(t,e,m)}))),j.each(w,(function(t){_.push(h.callbacks.labelColor.call(c,t,c._chart)),k.push(h.callbacks.labelTextColor.call(c,t,c._chart))})),g.title=c.getTitle(w,m),g.beforeBody=c.getBeforeBody(w,m),g.body=c.getBody(w,m),g.afterBody=c.getAfterBody(w,m),g.footer=c.getFooter(w,m),g.x=y.x,g.y=y.y,g.caretPadding=h.caretPadding,g.labelColors=_,g.labelTextColors=k,g.dataPoints=w,x=function(t,e){var n=t._chart.ctx,i=2*e.yPadding,a=0,r=e.body,o=r.reduce((function(t,e){return t+e.before.length+e.lines.length+e.after.length}),0);o+=e.beforeBody.length+e.afterBody.length;var l=e.title.length,s=e.footer.length,u=e.titleFontSize,d=e.bodyFontSize,c=e.footerFontSize;i+=l*u,i+=l?(l-1)*e.titleSpacing:0,i+=l?e.titleMarginBottom:0,i+=o*d,i+=o?(o-1)*e.bodySpacing:0,i+=s?e.footerMarginTop:0,i+=s*c,i+=s?(s-1)*e.footerSpacing:0;var h=0,f=function(t){a=Math.max(a,n.measureText(t).width+h)};return n.font=j.fontString(u,e._titleFontStyle,e._titleFontFamily),j.each(e.title,f),n.font=j.fontString(d,e._bodyFontStyle,e._bodyFontFamily),j.each(e.beforeBody.concat(e.afterBody),f),h=e.displayColors?d+2:0,j.each(r,(function(t){j.each(t.before,f),j.each(t.lines,f),j.each(t.after,f)})),h=0,n.font=j.fontString(c,e._footerFontStyle,e._footerFontFamily),j.each(e.footer,f),{width:a+=2*e.xPadding,height:i}}(this,g),v=function(t,e){var n,i,a,r,o,l=t._model,s=t._chart,u=t._chart.chartArea,d=\"center\",c=\"center\";l.y<e.height?c=\"top\":l.y>s.height-e.height&&(c=\"bottom\");var h=(u.left+u.right)/2,f=(u.top+u.bottom)/2;\"center\"===c?(n=function(t){return t<=h},i=function(t){return t>h}):(n=function(t){return t<=e.width/2},i=function(t){return t>=s.width-e.width/2}),a=function(t){return t+e.width+l.caretSize+l.caretPadding>s.width},r=function(t){return t-e.width-l.caretSize-l.caretPadding<0},o=function(t){return t<=f?\"top\":\"bottom\"},n(l.x)?(d=\"left\",a(l.x)&&(d=\"center\",c=o(l.y))):i(l.x)&&(d=\"right\",r(l.x)&&(d=\"center\",c=o(l.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:c}}(this,x),b=function(t,e,n,i){var a=t.x,r=t.y,o=t.caretSize,l=t.caretPadding,s=t.cornerRadius,u=n.xAlign,d=n.yAlign,c=o+l,h=s+l;return\"right\"===u?a-=e.width:\"center\"===u&&((a-=e.width/2)+e.width>i.width&&(a=i.width-e.width),a<0&&(a=0)),\"top\"===d?r+=c:r-=\"bottom\"===d?e.height+c:e.height/2,\"center\"===d?\"left\"===u?a+=c:\"right\"===u&&(a-=c):\"left\"===u?a-=h:\"right\"===u&&(a+=h),{x:a,y:r}}(g,x,v,c._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=x.width,g.height=x.height,g.caretX=y.x,g.caretY=y.y,c._model=g,t&&h.custom&&h.custom.call(c,g),c},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,l,s,u=n.caretSize,d=n.cornerRadius,c=n.xAlign,h=n.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if(\"center\"===h)l=g+m/2,\"left\"===c?(a=(i=f)-u,r=i,o=l+u,s=l-u):(a=(i=f+p)+u,r=i,o=l-u,s=l+u);else if(\"left\"===c?(i=(a=f+d+u)-u,r=a+u):\"right\"===c?(i=(a=f+p-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),\"top\"===h)l=(o=g)-u,s=o;else{l=(o=g+m)+u,s=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:l,y3:s}},drawTitle:function(t,e,n){var i,a,r,o=e.title,l=o.length;if(l){var s=Be(e.rtl,e.x,e.width);for(t.x=je(e,e._titleAlign),n.textAlign=s.textAlign(e._titleAlign),n.textBaseline=\"middle\",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=j.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r<l;++r)n.fillText(o[r],s.x(t.x),t.y+i/2),t.y+=i+a,r+1===l&&(t.y+=e.titleMarginBottom-a)}},drawBody:function(t,e,n){var i,a,r,o,l,s,u,d,c=e.bodyFontSize,h=e.bodySpacing,f=e._bodyAlign,g=e.body,p=e.displayColors,m=0,v=p?je(e,\"left\"):0,b=Be(e.rtl,e.x,e.width),x=function(e){n.fillText(e,b.x(t.x+m),t.y+c/2),t.y+=c+h},y=b.textAlign(f);for(n.textAlign=f,n.textBaseline=\"middle\",n.font=j.fontString(c,e._bodyFontStyle,e._bodyFontFamily),t.x=je(e,y),n.fillStyle=e.bodyFontColor,j.each(e.beforeBody,x),m=p&&\"right\"!==y?\"center\"===f?c/2+1:c+2:0,l=0,u=g.length;l<u;++l){for(i=g[l],a=e.labelTextColors[l],r=e.labelColors[l],n.fillStyle=a,j.each(i.before,x),s=0,d=(o=i.lines).length;s<d;++s){if(p){var _=b.x(v);n.fillStyle=e.legendColorBackground,n.fillRect(b.leftForLtr(_,c),t.y,c,c),n.lineWidth=1,n.strokeStyle=r.borderColor,n.strokeRect(b.leftForLtr(_,c),t.y,c,c),n.fillStyle=r.backgroundColor,n.fillRect(b.leftForLtr(b.xPlus(_,1),c-2),t.y+1,c-2,c-2),n.fillStyle=a}x(o[s])}j.each(i.after,x)}m=0,j.each(e.afterBody,x),t.y-=h},drawFooter:function(t,e,n){var i,a,r=e.footer,o=r.length;if(o){var l=Be(e.rtl,e.x,e.width);for(t.x=je(e,e._footerAlign),t.y+=e.footerMarginTop,n.textAlign=l.textAlign(e._footerAlign),n.textBaseline=\"middle\",i=e.footerFontSize,n.fillStyle=e.footerFontColor,n.font=j.fontString(i,e._footerFontStyle,e._footerFontFamily),a=0;a<o;++a)n.fillText(r[a],l.x(t.x),t.y+i/2),t.y+=i+e.footerSpacing}},drawBackground:function(t,e,n,i){n.fillStyle=e.backgroundColor,n.strokeStyle=e.borderColor,n.lineWidth=e.borderWidth;var a=e.xAlign,r=e.yAlign,o=t.x,l=t.y,s=i.width,u=i.height,d=e.cornerRadius;n.beginPath(),n.moveTo(o+d,l),\"top\"===r&&this.drawCaret(t,i),n.lineTo(o+s-d,l),n.quadraticCurveTo(o+s,l,o+s,l+d),\"center\"===r&&\"right\"===a&&this.drawCaret(t,i),n.lineTo(o+s,l+u-d),n.quadraticCurveTo(o+s,l+u,o+s-d,l+u),\"bottom\"===r&&this.drawCaret(t,i),n.lineTo(o+d,l+u),n.quadraticCurveTo(o,l+u,o,l+u-d),\"center\"===r&&\"left\"===a&&this.drawCaret(t,i),n.lineTo(o,l+d),n.quadraticCurveTo(o,l,o+d,l),n.closePath(),n.fill(),e.borderWidth>0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,j.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),j.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],\"mouseout\"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!j.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),Ye=Ee,Ge=Ue;Ge.positioners=Ye;var Xe=j.valueOrDefault;function Ke(){return j.merge(Object.create(null),[].slice.call(arguments),{merger:function(t,e,n,i){if(\"xAxes\"===t||\"yAxes\"===t){var a,r,o,l=n[t].length;for(e[t]||(e[t]=[]),a=0;a<l;++a)o=n[t][a],r=Xe(o.type,\"xAxes\"===t?\"category\":\"linear\"),a>=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?j.merge(e[t][a],[ze.getScaleDefaults(r),o]):j.merge(e[t][a],o)}else j._merger(t,e,n,i)}})}function Ze(){return j.merge(Object.create(null),[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||Object.create(null),r=n[t];\"scales\"===t?e[t]=Ke(a,r):\"scale\"===t?e[t]=j.merge(a,[ze.getScaleDefaults(r.type),r]):j._merger(t,e,n,i)}})}function $e(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(j.findIndex(t,a)>=0);return i}function Je(t){return\"top\"===t||\"bottom\"===t}function Qe(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}N._set(\"global\",{elements:{},events:[\"mousemove\",\"mouseout\",\"click\",\"touchstart\",\"touchmove\"],hover:{onHover:null,mode:\"nearest\",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var tn=function(t,e){return this.construct(t,e),this};j.extend(tn.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||Object.create(null)).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=Ze(N.global,N[t.type],t.options||{}),t}(e);var i=Le.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=j.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,tn.instances[n.id]=n,Object.defineProperty(n,\"data\",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error(\"Failed to create chart: can't acquire context from the given item\")},initialize:function(){var t=this;return Re.notify(t,\"beforeInit\"),j.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Re.notify(t,\"afterInit\"),t},clear:function(){return j.canvas.clear(this),this},stop:function(){return Q.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(j.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:j.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+\"px\",i.style.height=o+\"px\",j.retinaScale(e,n.devicePixelRatio),!t)){var l={width:r,height:o};Re.notify(e,\"resize\",[l]),n.onResize&&n.onResize(e,l),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;j.each(e.xAxes,(function(t,n){t.id||(t.id=$e(e.xAxes,\"x-axis-\",n))})),j.each(e.yAxes,(function(t,n){t.id||(t.id=$e(e.yAxes,\"y-axis-\",n))})),n&&(n.id=n.id||\"scale\")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:\"category\",dposition:\"bottom\"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:\"linear\",dposition:\"left\"}})))),e.scale&&i.push({options:e.scale,dtype:\"radialLinear\",isDefault:!0,dposition:\"chartArea\"}),j.each(i,(function(e){var i=e.options,r=i.id,o=Xe(i.type,e.dtype);Je(i.position)!==Je(e.dposition)&&(i.position=e.dposition),a[r]=!0;var l=null;if(r in n&&n[r].type===o)(l=n[r]).options=i,l.ctx=t.ctx,l.chart=t;else{var s=ze.getScaleConstructor(o);if(!s)return;l=new s({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[l.id]=l}l.mergeTicksOptions(),e.isDefault&&(t.scale=l)})),j.each(a,(function(t,e){t||delete n[e]})),t.scales=n,ze.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t<e;t++){var r=a[t],o=n.getDatasetMeta(t),l=r.type||n.config.type;if(o.type&&o.type!==l&&(n.destroyDatasetMeta(t),o=n.getDatasetMeta(t)),o.type=l,o.order=r.order||0,o.index=t,o.controller)o.controller.updateIndex(t),o.controller.linkScales();else{var s=$t[o.type];if(void 0===s)throw new Error('\"'+o.type+'\" is not a chart type.');o.controller=new s(n,t),i.push(o.controller)}}return i},resetElements:function(){var t=this;j.each(t.data.datasets,(function(e,n){t.getDatasetMeta(n).controller.reset()}),t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,n,i,a,r=this;if(t&&\"object\"==typeof t||(t={duration:t,lazy:arguments[1]}),a=(i=r).options,j.each(i.scales,(function(t){ge.removeBox(i,t)})),a=Ze(N.global,N[i.config.type],a),i.options=i.config.options=a,i.ensureScalesHaveIDs(),i.buildOrUpdateScales(),i.tooltip._options=a.tooltips,i.tooltip.initialize(),Re._invalidate(r),!1!==Re.notify(r,\"beforeUpdate\")){r.tooltip._data=r.data;var o=r.buildOrUpdateControllers();for(e=0,n=r.data.datasets.length;e<n;e++)r.getDatasetMeta(e).controller.buildOrUpdateElements();r.updateLayout(),r.options.animation&&r.options.animation.duration&&j.each(o,(function(t){t.reset()})),r.updateDatasets(),r.tooltip.initialize(),r.lastActive=[],Re.notify(r,\"afterUpdate\"),r._layers.sort(Qe(\"z\",\"_idx\")),r._bufferedRender?r._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:r.render(t)}},updateLayout:function(){var t=this;!1!==Re.notify(t,\"beforeLayout\")&&(ge.update(this,this.width,this.height),t._layers=[],j.each(t.boxes,(function(e){e._configure&&e._configure(),t._layers.push.apply(t._layers,e._layers())}),t),t._layers.forEach((function(t,e){t._idx=e})),Re.notify(t,\"afterScaleUpdate\"),Re.notify(t,\"afterLayout\"))},updateDatasets:function(){var t=this;if(!1!==Re.notify(t,\"beforeDatasetsUpdate\")){for(var e=0,n=t.data.datasets.length;e<n;++e)t.updateDataset(e);Re.notify(t,\"afterDatasetsUpdate\")}},updateDataset:function(t){var e=this,n=e.getDatasetMeta(t),i={meta:n,index:t};!1!==Re.notify(e,\"beforeDatasetUpdate\",[i])&&(n.controller._update(),Re.notify(e,\"afterDatasetUpdate\",[i]))},render:function(t){var e=this;t&&\"object\"==typeof t||(t={duration:t,lazy:arguments[1]});var n=e.options.animation,i=Xe(t.duration,n&&n.duration),a=t.lazy;if(!1!==Re.notify(e,\"beforeRender\")){var r=function(t){Re.notify(e,\"afterRender\"),j.callback(n&&n.onComplete,[t],e)};if(n&&i){var o=new J({numSteps:i/16.66,easing:t.easing||n.easing,render:function(t,e){var n=j.easing.effects[e.easing],i=e.currentStep,a=i/e.numSteps;t.draw(n(a),a,i)},onAnimationProgress:n.onProgress,onAnimationComplete:r});Q.addAnimation(e,o,i,a)}else e.draw(),r(new J({numSteps:0,chart:e}));return e}},draw:function(t){var e,n,i=this;if(i.clear(),j.isNullOrUndef(t)&&(t=1),i.transition(t),!(i.width<=0||i.height<=0)&&!1!==Re.notify(i,\"beforeDraw\",[t])){for(n=i._layers,e=0;e<n.length&&n[e].z<=0;++e)n[e].draw(i.chartArea);for(i.drawDatasets(t);e<n.length;++e)n[e].draw(i.chartArea);i._drawTooltip(t),Re.notify(i,\"afterDraw\",[t])}},transition:function(t){for(var e=this,n=0,i=(e.data.datasets||[]).length;n<i;++n)e.isDatasetVisible(n)&&e.getDatasetMeta(n).controller.transition(t);e.tooltip.transition(t)},_getSortedDatasetMetas:function(t){var e,n,i=this,a=[];for(e=0,n=(i.data.datasets||[]).length;e<n;++e)t&&!i.isDatasetVisible(e)||a.push(i.getDatasetMeta(e));return a.sort(Qe(\"order\",\"index\")),a},_getSortedVisibleDatasetMetas:function(){return this._getSortedDatasetMetas(!0)},drawDatasets:function(t){var e,n,i=this;if(!1!==Re.notify(i,\"beforeDatasetsDraw\",[t])){for(n=(e=i._getSortedVisibleDatasetMetas()).length-1;n>=0;--n)i.drawDataset(e[n],t);Re.notify(i,\"afterDatasetsDraw\",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Re.notify(this,\"beforeDatasetDraw\",[n])&&(t.controller.draw(e),Re.notify(this,\"afterDatasetDraw\",[n]))},_drawTooltip:function(t){var e=this,n=e.tooltip,i={tooltip:n,easingValue:t};!1!==Re.notify(e,\"beforeTooltipDraw\",[i])&&(n.draw(),Re.notify(e,\"afterTooltipDraw\",[i]))},getElementAtEvent:function(t){return ae.modes.single(this,t)},getElementsAtEvent:function(t){return ae.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return ae.modes[\"x-axis\"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=ae.modes[e];return\"function\"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return ae.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this,n=e.data.datasets[t];n._meta||(n._meta={});var i=n._meta[e.id];return i||(i=n._meta[e.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:n.order||0,index:t}),i},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e<n;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return\"boolean\"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,n=this.data.datasets[t],i=n._meta&&n._meta[e];i&&(i.controller.destroy(),delete n._meta[e])},destroy:function(){var t,e,n=this,i=n.canvas;for(n.stop(),t=0,e=n.data.datasets.length;t<e;++t)n.destroyDatasetMeta(t);i&&(n.unbindEvents(),j.canvas.clear(n),Le.releaseContext(n.ctx),n.canvas=null,n.ctx=null),Re.notify(n,\"destroy\"),delete tn.instances[n.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var t=this;t.tooltip=new Ge({_chart:t,_chartInstance:t,_data:t.data,_options:t.options.tooltips},t)},bindEvents:function(){var t=this,e=t._listeners={},n=function(){t.eventHandler.apply(t,arguments)};j.each(t.options.events,(function(i){Le.addEventListener(t,i,n),e[i]=n})),t.options.responsive&&(n=function(){t.resize()},Le.addEventListener(t,\"resize\",n),e.resize=n)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,j.each(e,(function(e,n){Le.removeEventListener(t,n,e)})))},updateHoverStyle:function(t,e,n){var i,a,r,o=n?\"set\":\"remove\";for(a=0,r=t.length;a<r;++a)(i=t[a])&&this.getDatasetMeta(i._datasetIndex).controller[o+\"HoverStyle\"](i);\"dataset\"===e&&this.getDatasetMeta(t[0]._datasetIndex).controller[\"_\"+o+\"DatasetHoverStyle\"]()},eventHandler:function(t){var e=this,n=e.tooltip;if(!1!==Re.notify(e,\"beforeEvent\",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var i=e.handleEvent(t);n&&(i=n._start?n.handleEvent(t):i|n.handleEvent(t)),Re.notify(e,\"afterEvent\",[t]);var a=e._bufferedRequest;return a?e.render(a):i&&!e.animating&&(e.stop(),e.render({duration:e.options.hover.animationDuration,lazy:!0})),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,n=this,i=n.options||{},a=i.hover;return n.lastActive=n.lastActive||[],\"mouseout\"===t.type?n.active=[]:n.active=n.getElementsAtEventForMode(t,a.mode,a),j.callback(i.onHover||i.hover.onHover,[t.native,n.active],n),\"mouseup\"!==t.type&&\"click\"!==t.type||i.onClick&&i.onClick.call(n,t.native,n.active),n.lastActive.length&&n.updateHoverStyle(n.lastActive,a.mode,!1),n.active.length&&a.mode&&n.updateHoverStyle(n.active,a.mode,!0),e=!j.arrayEquals(n.active,n.lastActive),n.lastActive=n.active,e}}),tn.instances={};var en=tn;tn.Controller=tn,tn.types={},j.configMerge=Ze,j.scaleMerge=Ke;function nn(){throw new Error(\"This method is not implemented: either no adapter can be found or an incomplete integration was provided.\")}function an(t){this.options=t||{}}j.extend(an.prototype,{formats:nn,parse:nn,format:nn,add:nn,diff:nn,startOf:nn,endOf:nn,_create:function(t){return t}}),an.override=function(t){j.extend(an.prototype,t)};var rn={_date:an},on={formatters:{values:function(t){return j.isArray(t)?t:\"\"+t},linear:function(t,e,n){var i=n.length>3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=j.log10(Math.abs(i)),r=\"\";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=j.log10(Math.abs(t)),l=Math.floor(o)-Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toExponential(l)}else{var s=-1*Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toFixed(s)}else r=\"0\";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(j.log10(t)));return 0===t?\"0\":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():\"\"}}},ln=j.isArray,sn=j.isNullOrUndef,un=j.valueOrDefault,dn=j.valueAtIndexOrDefault;function cn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),l=t._startPixel,s=t._endPixel,u=1e-6;if(!(n&&(i=1===a?Math.max(o-l,s-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=r<e?i:-i)<l-u||o>s+u)))return o}function hn(t,e,n,i){var a,r,o,l,s,u,d,c,h,f,g,p,m,v=n.length,b=[],x=[],y=[],_=0,k=0;for(a=0;a<v;++a){if(l=n[a].label,s=n[a].major?e.major:e.minor,t.font=u=s.string,d=i[u]=i[u]||{data:{},gc:[]},c=s.lineHeight,h=f=0,sn(l)||ln(l)){if(ln(l))for(r=0,o=l.length;r<o;++r)g=l[r],sn(g)||ln(g)||(h=j.measureText(t,d.data,d.gc,h,g),f+=c)}else h=j.measureText(t,d.data,d.gc,h,l),f=c;b.push(h),x.push(f),y.push(c/2),_=Math.max(h,_),k=Math.max(f,k)}function w(t){return{width:b[t]||0,height:x[t]||0,offset:y[t]||0}}return function(t,e){j.each(t,(function(t){var n,i=t.gc,a=i.length/2;if(a>e){for(n=0;n<a;++n)delete t.data[i[n]];i.splice(0,a)}}))}(i,v),p=b.indexOf(_),m=x.indexOf(k),{first:w(0),last:w(v-1),widest:w(p),highest:w(m)}}function fn(t){return t.drawTicks?t.tickMarkLength:0}function gn(t){var e,n;return t.display?(e=j.options._parseFont(t),n=j.options.toPadding(t.padding),e.lineHeight+n.height):0}function pn(t,e){return j.extend(j.options._parseFont({fontFamily:un(e.fontFamily,t.fontFamily),fontSize:un(e.fontSize,t.fontSize),fontStyle:un(e.fontStyle,t.fontStyle),lineHeight:un(e.lineHeight,t.lineHeight)}),{color:j.options.resolve([e.fontColor,t.fontColor,N.global.defaultFontColor])})}function mn(t){var e=pn(t,t.minor);return{minor:e,major:t.major.enabled?pn(t,t.major):e}}function vn(t){var e,n,i,a=[];for(n=0,i=t.length;n<i;++n)void 0!==(e=t[n])._index&&a.push(e);return a}function bn(t,e,n,i){var a,r,o,l,s=un(n,0),u=Math.min(un(i,t.length),t.length),d=0;for(e=Math.ceil(e),i&&(e=(a=i-n)/Math.floor(a/e)),l=s;l<0;)d++,l=Math.round(s+d*e);for(r=Math.max(s,0);r<u;r++)o=t[r],r===l?(o._index=r,d++,l=Math.round(s+d*e)):delete o.label}N._set(\"scale\",{display:!0,position:\"left\",offset:!1,gridLines:{display:!0,color:\"rgba(0,0,0,0.1)\",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:\"rgba(0,0,0,0.25)\",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:\"\",padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:on.formatters.values,minor:{},major:{}}});var xn=Z.extend({zeroLineIndex:0,getPadding:function(){var t=this;return{left:t.paddingLeft||0,top:t.paddingTop||0,right:t.paddingRight||0,bottom:t.paddingBottom||0}},getTicks:function(){return this._ticks},_getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]},mergeTicksOptions:function(){},beforeUpdate:function(){j.callback(this.options.beforeUpdate,[this])},update:function(t,e,n){var i,a,r,o,l,s=this,u=s.options.ticks,d=u.sampleSize;if(s.beforeUpdate(),s.maxWidth=t,s.maxHeight=e,s.margins=j.extend({left:0,right:0,top:0,bottom:0},n),s._ticks=null,s.ticks=null,s._labelSizes=null,s._maxLabelLines=0,s.longestLabelWidth=0,s.longestTextCache=s.longestTextCache||{},s._gridLineItems=null,s._labelItems=null,s.beforeSetDimensions(),s.setDimensions(),s.afterSetDimensions(),s.beforeDataLimits(),s.determineDataLimits(),s.afterDataLimits(),s.beforeBuildTicks(),o=s.buildTicks()||[],(!(o=s.afterBuildTicks(o)||o)||!o.length)&&s.ticks)for(o=[],i=0,a=s.ticks.length;i<a;++i)o.push({value:s.ticks[i],major:!1});return s._ticks=o,l=d<o.length,r=s._convertTicksToLabels(l?function(t,e){for(var n=[],i=t.length/e,a=0,r=t.length;a<r;a+=i)n.push(t[Math.floor(a)]);return n}(o,d):o),s._configure(),s.beforeCalculateTickRotation(),s.calculateTickRotation(),s.afterCalculateTickRotation(),s.beforeFit(),s.fit(),s.afterFit(),s._ticksToDraw=u.display&&(u.autoSkip||\"auto\"===u.source)?s._autoSkip(o):o,l&&(r=s._convertTicksToLabels(s._ticksToDraw)),s.ticks=r,s.afterUpdate(),s.minSize},_configure:function(){var t,e,n=this,i=n.options.ticks.reverse;n.isHorizontal()?(t=n.left,e=n.right):(t=n.top,e=n.bottom,i=!i),n._startPixel=t,n._endPixel=e,n._reversePixels=i,n._length=e-t},afterUpdate:function(){j.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){j.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){j.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){j.callback(this.options.beforeDataLimits,[this])},determineDataLimits:j.noop,afterDataLimits:function(){j.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){j.callback(this.options.beforeBuildTicks,[this])},buildTicks:j.noop,afterBuildTicks:function(t){var e=this;return ln(t)&&t.length?j.callback(e.options.afterBuildTicks,[e,t]):(e.ticks=j.callback(e.options.afterBuildTicks,[e,e.ticks])||e.ticks,t)},beforeTickToLabelConversion:function(){j.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this,e=t.options.ticks;t.ticks=t.ticks.map(e.userCallback||e.callback,this)},afterTickToLabelConversion:function(){j.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){j.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t,e,n,i,a,r,o,l=this,s=l.options,u=s.ticks,d=l.getTicks().length,c=u.minRotation||0,h=u.maxRotation,f=c;!l._isVisible()||!u.display||c>=h||d<=1||!l.isHorizontal()?l.labelRotation=c:(e=(t=l._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(l.maxWidth,l.chart.width-e),e+6>(a=s.offset?l.maxWidth/d:i/(d-1))&&(a=i/(d-(s.offset?.5:1)),r=l.maxHeight-fn(s.gridLines)-u.padding-gn(s.scaleLabel),o=Math.sqrt(e*e+n*n),f=j.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(c,Math.min(h,f))),l.labelRotation=f)},afterCalculateTickRotation:function(){j.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){j.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,l=t._isVisible(),s=\"bottom\"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:l&&(e.width=fn(o)+gn(r)),u?l&&(e.height=fn(o)+gn(r)):e.height=t.maxHeight,a.display&&l){var d=mn(a),c=t._getLabelSizes(),h=c.first,f=c.last,g=c.widest,p=c.highest,m=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,x=j.toRadians(t.labelRotation),y=Math.cos(x),_=Math.sin(x),k=_*g.width+y*(p.height-(b?p.offset:0))+(b?0:m);e.height=Math.min(t.maxHeight,e.height+k+v);var w,M,S=t.getPixelForTick(0)-t.left,C=t.right-t.getPixelForTick(t.getTicks().length-1);b?(w=s?y*h.width+_*h.offset:_*(h.height-h.offset),M=s?_*(f.height-f.offset):y*f.width+_*f.offset):(w=h.width/2,M=f.width/2),t.paddingLeft=Math.max((w-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-C)*t.width/(t.width-C),0)+3}else{var P=a.mirror?0:g.width+v+m;e.width=Math.min(t.maxWidth,e.width+P),t.paddingTop=h.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){j.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return\"top\"===t||\"bottom\"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(sn(t))return NaN;if((\"number\"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;n<i;++n)t[n].label=e[n];return e},_getLabelSizes:function(){var t=this,e=t._labelSizes;return e||(t._labelSizes=e=hn(t.ctx,mn(t.options.ticks),t.getTicks(),t.longestTextCache),t.longestLabelWidth=e.widest.width),e},_parseValue:function(t){var e,n,i,a;return ln(t)?(e=+this.getRightValue(t[0]),n=+this.getRightValue(t[1]),i=Math.min(e,n),a=Math.max(e,n)):(e=void 0,n=t=+this.getRightValue(t),i=t,a=t),{min:i,max:a,start:e,end:n}},_getScaleLabel:function(t){var e=this._parseValue(t);return void 0!==e.start?\"[\"+e.start+\", \"+e.end+\"]\":+this.getRightValue(t)},getLabelForIndex:j.noop,getPixelForValue:j.noop,getValueForPixel:j.noop,getPixelForTick:function(t){var e=this,n=e.options.offset,i=e._ticks.length,a=1/Math.max(i-(n?0:1),1);return t<0||t>i-1?null:e.getPixelForDecimal(t*a+(n?a/2:0))},getPixelForDecimal:function(t){var e=this;return e._reversePixels&&(t=1-t),e._startPixel+t*e._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this,e=t.min,n=t.max;return t.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0},_autoSkip:function(t){var e,n,i,a,r=this,o=r.options.ticks,l=r._length,s=o.maxTicksLimit||l/r._tickSize()+1,u=o.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;e<n;e++)t[e].major&&i.push(e);return i}(t):[],d=u.length,c=u[0],h=u[d-1];if(d>s)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;i<t.length;i++)a=t[i],i===o?(a._index=i,o=e[++r*n]):delete a.label}(t,u,d/s),vn(t);if(i=function(t,e,n,i){var a,r,o,l,s=function(t){var e,n,i=t.length;if(i<2)return!1;for(n=t[0],e=1;e<i;++e)if(t[e]-t[e-1]!==n)return!1;return n}(t),u=(e.length-1)/i;if(!s)return Math.max(u,1);for(o=0,l=(a=j.math._factorize(s)).length-1;o<l;o++)if((r=a[o])>u)return r;return Math.max(u,1)}(u,t,0,s),d>0){for(e=0,n=d-1;e<n;e++)bn(t,i,u[e],u[e+1]);return a=d>1?(h-c)/(d-1):null,bn(t,i,j.isNullOrUndef(a)?0:c-a,c),bn(t,i,h,j.isNullOrUndef(a)?t.length:h+a),vn(t)}return bn(t,i),vn(t)},_tickSize:function(){var t=this,e=t.options.ticks,n=j.toRadians(t.labelRotation),i=Math.abs(Math.cos(n)),a=Math.abs(Math.sin(n)),r=t._getLabelSizes(),o=e.autoSkipPadding||0,l=r?r.widest.width+o:0,s=r?r.highest.height+o:0;return t.isHorizontal()?s*i>l*a?l/i:s/a:s*a<l*i?s/i:l/a},_isVisible:function(){var t,e,n,i=this,a=i.chart,r=i.options.display;if(\"auto\"!==r)return!!r;for(t=0,e=a.data.datasets.length;t<e;++t)if(a.isDatasetVisible(t)&&((n=a.getDatasetMeta(t)).xAxisID===i.id||n.yAxisID===i.id))return!0;return!1},_computeGridLineItems:function(t){var e,n,i,a,r,o,l,s,u,d,c,h,f,g,p,m,v,b=this,x=b.chart,y=b.options,_=y.gridLines,k=y.position,w=_.offsetGridLines,M=b.isHorizontal(),S=b._ticksToDraw,C=S.length+(w?1:0),P=fn(_),A=[],D=_.drawBorder?dn(_.lineWidth,0,0):0,T=D/2,I=j._alignPixel,F=function(t){return I(x,t,D)};for(\"top\"===k?(e=F(b.bottom),l=b.bottom-P,u=e-T,c=F(t.top)+T,f=t.bottom):\"bottom\"===k?(e=F(b.top),c=t.top,f=F(t.bottom)-T,l=e+T,u=b.top+P):\"left\"===k?(e=F(b.right),o=b.right-P,s=e-T,d=F(t.left)+T,h=t.right):(e=F(b.left),d=t.left,h=F(t.right)-T,o=e+T,s=b.left+P),n=0;n<C;++n)i=S[n]||{},sn(i.label)&&n<S.length||(n===b.zeroLineIndex&&y.offset===w?(g=_.zeroLineWidth,p=_.zeroLineColor,m=_.zeroLineBorderDash||[],v=_.zeroLineBorderDashOffset||0):(g=dn(_.lineWidth,n,1),p=dn(_.color,n,\"rgba(0,0,0,0.1)\"),m=_.borderDash||[],v=_.borderDashOffset||0),void 0!==(a=cn(b,i._index||n,w))&&(r=I(x,a,g),M?o=s=d=h=r:l=u=c=f=r,A.push({tx1:o,ty1:l,tx2:s,ty2:u,x1:d,y1:c,x2:h,y2:f,width:g,color:p,borderDash:m,borderDashOffset:v})));return A.ticksLength=C,A.borderValue=e,A},_computeLabelItems:function(){var t,e,n,i,a,r,o,l,s,u,d,c,h=this,f=h.options,g=f.ticks,p=f.position,m=g.mirror,v=h.isHorizontal(),b=h._ticksToDraw,x=mn(g),y=g.padding,_=fn(f.gridLines),k=-j.toRadians(h.labelRotation),w=[];for(\"top\"===p?(r=h.bottom-_-y,o=k?\"left\":\"center\"):\"bottom\"===p?(r=h.top+_+y,o=k?\"right\":\"center\"):\"left\"===p?(a=h.right-(m?0:_)-y,o=m?\"left\":\"right\"):(a=h.left+(m?0:_)+y,o=m?\"right\":\"left\"),t=0,e=b.length;t<e;++t)i=(n=b[t]).label,sn(i)||(l=h.getPixelForTick(n._index||t)+g.labelOffset,u=(s=n.major?x.major:x.minor).lineHeight,d=ln(i)?i.length:1,v?(a=l,c=\"top\"===p?((k?1:.5)-d)*u:(k?0:.5)*u):(r=l,c=(1-d)*u/2),w.push({x:a,y:r,rotation:k,label:i,font:s,textOffset:c,textAlign:o}));return w},_drawGrid:function(t){var e=this,n=e.options.gridLines;if(n.display){var i,a,r,o,l,s=e.ctx,u=e.chart,d=j._alignPixel,c=n.drawBorder?dn(n.lineWidth,0,0):0,h=e._gridLineItems||(e._gridLineItems=e._computeGridLineItems(t));for(r=0,o=h.length;r<o;++r)i=(l=h[r]).width,a=l.color,i&&a&&(s.save(),s.lineWidth=i,s.strokeStyle=a,s.setLineDash&&(s.setLineDash(l.borderDash),s.lineDashOffset=l.borderDashOffset),s.beginPath(),n.drawTicks&&(s.moveTo(l.tx1,l.ty1),s.lineTo(l.tx2,l.ty2)),n.drawOnChartArea&&(s.moveTo(l.x1,l.y1),s.lineTo(l.x2,l.y2)),s.stroke(),s.restore());if(c){var f,g,p,m,v=c,b=dn(n.lineWidth,h.ticksLength-1,1),x=h.borderValue;e.isHorizontal()?(f=d(u,e.left,v)-v/2,g=d(u,e.right,b)+b/2,p=m=x):(p=d(u,e.top,v)-v/2,m=d(u,e.bottom,b)+b/2,f=g=x),s.lineWidth=c,s.strokeStyle=dn(n.color,0),s.beginPath(),s.moveTo(f,p),s.lineTo(g,m),s.stroke()}}},_drawLabels:function(){var t=this;if(t.options.ticks.display){var e,n,i,a,r,o,l,s,u=t.ctx,d=t._labelItems||(t._labelItems=t._computeLabelItems());for(e=0,i=d.length;e<i;++e){if(o=(r=d[e]).font,u.save(),u.translate(r.x,r.y),u.rotate(r.rotation),u.font=o.string,u.fillStyle=o.color,u.textBaseline=\"middle\",u.textAlign=r.textAlign,l=r.label,s=r.textOffset,ln(l))for(n=0,a=l.length;n<a;++n)u.fillText(\"\"+l[n],0,s),s+=o.lineHeight;else u.fillText(l,0,s);u.restore()}}},_drawTitle:function(){var t=this,e=t.ctx,n=t.options,i=n.scaleLabel;if(i.display){var a,r,o=un(i.fontColor,N.global.defaultFontColor),l=j.options._parseFont(i),s=j.options.toPadding(i.padding),u=l.lineHeight/2,d=n.position,c=0;if(t.isHorizontal())a=t.left+t.width/2,r=\"bottom\"===d?t.bottom-u-s.bottom:t.top+u+s.top;else{var h=\"left\"===d;a=h?t.left+u+s.top:t.right-u-s.top,r=t.top+t.height/2,c=h?-.5*Math.PI:.5*Math.PI}e.save(),e.translate(a,r),e.rotate(c),e.textAlign=\"center\",e.textBaseline=\"middle\",e.fillStyle=o,e.font=l.string,e.fillText(i.labelString,0,0),e.restore()}},draw:function(t){var e=this;e._isVisible()&&(e._drawGrid(t),e._drawTitle(),e._drawLabels())},_layers:function(){var t=this,e=t.options,n=e.ticks&&e.ticks.z||0,i=e.gridLines&&e.gridLines.z||0;return t._isVisible()&&n!==i&&t.draw===t._draw?[{z:i,draw:function(){t._drawGrid.apply(t,arguments),t._drawTitle.apply(t,arguments)}},{z:n,draw:function(){t._drawLabels.apply(t,arguments)}}]:[{z:n,draw:function(){t.draw.apply(t,arguments)}}]},_getMatchingVisibleMetas:function(t){var e=this,n=e.isHorizontal();return e.chart._getSortedVisibleDatasetMetas().filter((function(i){return(!t||i.type===t)&&(n?i.xAxisID===e.id:i.yAxisID===e.id)}))}});xn.prototype._draw=xn.prototype.draw;var yn=xn,_n=j.isNullOrUndef,kn=yn.extend({determineDataLimits:function(){var t,e=this,n=e._getLabels(),i=e.options.ticks,a=i.min,r=i.max,o=0,l=n.length-1;void 0!==a&&(t=n.indexOf(a))>=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(l=t),e.minIndex=o,e.maxIndex=l,e.min=n[o],e.max=n[l]},buildTicks:function(){var t=this,e=t._getLabels(),n=t.minIndex,i=t.maxIndex;t.ticks=0===n&&i===e.length-1?e:e.slice(n,i+1)},getLabelForIndex:function(t,e){var n=this,i=n.chart;return i.getDatasetMeta(e).controller._getValueScaleId()===n.id?n.getRightValue(i.data.datasets[e].data[t]):n._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;yn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return _n(e)||_n(n)||(t=o.chart.data.datasets[n].data[e]),_n(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=j.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=this,n=Math.round(e._startValue+e.getDecimalForPixel(t)*e._valueRange);return Math.min(Math.max(n,0),e.ticks.length-1)},getBasePixel:function(){return this.bottom}}),wn={position:\"bottom\"};kn._defaults=wn;var Mn=j.noop,Sn=j.isNullOrUndef;var Cn=yn.extend({getRightValue:function(t){return\"string\"==typeof t?+t:yn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=j.sign(t.min),i=j.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this,n=e.options.ticks,i=n.stepSize,a=n.maxTicksLimit;return i?t=Math.ceil(e.max/i)-Math.floor(e.min/i)+1:(t=e._computeTickLimit(),a=a||11),a&&(t=Math.min(a,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:Mn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:j.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],l=t.stepSize,s=l||1,u=t.maxTicks-1,d=t.min,c=t.max,h=t.precision,f=e.min,g=e.max,p=j.niceNum((g-f)/u/s)*s;if(p<1e-14&&Sn(d)&&Sn(c))return[f,g];(r=Math.ceil(g/p)-Math.floor(f/p))>u&&(p=j.niceNum(r*p/u/s)*s),l||Sn(h)?n=Math.pow(10,j._decimalPlaces(p)):(n=Math.pow(10,h),p=Math.ceil(p*n)/n),i=Math.floor(f/p)*p,a=Math.ceil(g/p)*p,l&&(!Sn(d)&&j.almostWhole(d/p,p/1e3)&&(i=d),!Sn(c)&&j.almostWhole(c/p,p/1e3)&&(a=c)),r=(a-i)/p,r=j.almostEquals(r,Math.round(r),p/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Sn(d)?i:d);for(var m=1;m<r;++m)o.push(Math.round((i+m*p)*n)/n);return o.push(Sn(c)?a:c),o}(i,t);t.handleDirectionalChanges(),t.max=j.max(a),t.min=j.min(a),e.reverse?(a.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),yn.prototype.convertTicksToLabels.call(t)},_configure:function(){var t,e=this,n=e.getTicks(),i=e.min,a=e.max;yn.prototype._configure.call(e),e.options.offset&&n.length&&(i-=t=(a-i)/Math.max(n.length-1,1)/2,a+=t),e._startValue=i,e._endValue=a,e._valueRange=a-i}}),Pn={position:\"left\",ticks:{callback:on.formatters.linear}};function An(t,e,n,i){var a,r,o=t.options,l=function(t,e,n){var i=[n.type,void 0===e&&void 0===n.stack?n.index:\"\",n.stack].join(\".\");return void 0===t[i]&&(t[i]={pos:[],neg:[]}),t[i]}(e,o.stacked,n),s=l.pos,u=l.neg,d=i.length;for(a=0;a<d;++a)r=t._parseValue(i[a]),isNaN(r.min)||isNaN(r.max)||n.data[a].hidden||(s[a]=s[a]||0,u[a]=u[a]||0,o.relativePoints?s[a]=100:r.min<0||r.max<0?u[a]+=r.min:s[a]+=r.max)}function Dn(t,e,n){var i,a,r=n.length;for(i=0;i<r;++i)a=t._parseValue(n[i]),isNaN(a.min)||isNaN(a.max)||e.data[i].hidden||(t.min=Math.min(t.min,a.min),t.max=Math.max(t.max,a.max))}var Tn=Cn.extend({determineDataLimits:function(){var t,e,n,i,a=this,r=a.options,o=a.chart.data.datasets,l=a._getMatchingVisibleMetas(),s=r.stacked,u={},d=l.length;if(a.min=Number.POSITIVE_INFINITY,a.max=Number.NEGATIVE_INFINITY,void 0===s)for(t=0;!s&&t<d;++t)s=void 0!==(e=l[t]).stack;for(t=0;t<d;++t)n=o[(e=l[t]).index].data,s?An(a,u,e,n):Dn(a,e,n);j.each(u,(function(t){i=t.pos.concat(t.neg),a.min=Math.min(a.min,j.min(i)),a.max=Math.max(a.max,j.max(i))})),a.min=j.isFinite(a.min)&&!isNaN(a.min)?a.min:0,a.max=j.isFinite(a.max)&&!isNaN(a.max)?a.max:1,a.handleTickRangeOptions()},_computeTickLimit:function(){var t,e=this;return e.isHorizontal()?Math.ceil(e.width/40):(t=j.options._parseFont(e.options.ticks),Math.ceil(e.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){var e=this;return e.getPixelForDecimal((+e.getRightValue(t)-e._startValue)/e._valueRange)},getValueForPixel:function(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange},getPixelForTick:function(t){var e=this.ticksAsNumbers;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])}}),In=Pn;Tn._defaults=In;var Fn=j.valueOrDefault,On=j.math.log10;var Ln={position:\"left\",ticks:{callback:on.formatters.logarithmic}};function Rn(t,e){return j.isFinite(t)&&t>=0?t:e}var zn=yn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,l=o.options,s=o.chart,u=s.data.datasets,d=o.isHorizontal();function c(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var h=l.stacked;if(void 0===h)for(t=0;t<u.length;t++)if(e=s.getDatasetMeta(t),s.isDatasetVisible(t)&&c(e)&&void 0!==e.stack){h=!0;break}if(l.stacked||h){var f={};for(t=0;t<u.length;t++){var g=[(e=s.getDatasetMeta(t)).type,void 0===l.stacked&&void 0===e.stack?t:\"\",e.stack].join(\".\");if(s.isDatasetVisible(t)&&c(e))for(void 0===f[g]&&(f[g]=[]),a=0,r=(i=u[t].data).length;a<r;a++){var p=f[g];n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(p[a]=p[a]||0,p[a]+=n.max)}}j.each(f,(function(t){if(t.length>0){var e=j.min(t),n=j.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t<u.length;t++)if(e=s.getDatasetMeta(t),s.isDatasetVisible(t)&&c(e))for(a=0,r=(i=u[t].data).length;a<r;a++)n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(o.min=Math.min(n.min,o.min),o.max=Math.max(n.max,o.max),0!==n.min&&(o.minNotZero=Math.min(n.min,o.minNotZero)));o.min=j.isFinite(o.min)?o.min:null,o.max=j.isFinite(o.max)?o.max:null,o.minNotZero=j.isFinite(o.minNotZero)?o.minNotZero:null,this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;t.min=Rn(e.min,t.min),t.max=Rn(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(On(t.min))-1),t.max=Math.pow(10,Math.floor(On(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(On(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(On(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(On(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:Rn(e.min),max:Rn(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=Fn(t.min,Math.pow(10,Math.floor(On(e.min)))),o=Math.floor(On(e.max)),l=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(On(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(On(r)),i=Math.floor(r/Math.pow(10,n)));var s=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10==++i&&(i=1,s=++n>=0?1:s),r=Math.round(i*Math.pow(10,n)*s)/s}while(n<o||n===o&&i<l);var u=Fn(t.max,r);return a.push(u),a}(i,t);t.max=j.max(a),t.min=j.min(a),e.reverse?(n=!n,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),n&&a.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),yn.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){var e=this.tickValues;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(On(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;yn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=Fn(t.options.ticks.fontSize,N.global.defaultFontSize)/t._length),t._startValue=On(e),t._valueOffset=n,t._valueRange=(On(t.max)-On(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(On(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),Nn=Ln;zn._defaults=Nn;var Bn=j.valueOrDefault,En=j.valueAtIndexOrDefault,Wn=j.options.resolve,Vn={display:!0,animate:!0,position:\"chartArea\",angleLines:{display:!0,color:\"rgba(0,0,0,0.1)\",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:\"rgba(255,255,255,0.75)\",backdropPaddingY:2,backdropPaddingX:2,callback:on.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Hn(t){var e=t.ticks;return e.display&&t.display?Bn(e.fontSize,N.global.defaultFontSize)+2*e.backdropPaddingY:0}function jn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:t<i||t>a?{start:e-n,end:e}:{start:e,end:e+n}}function qn(t){return 0===t||180===t?\"center\":t<180?\"left\":\"right\"}function Un(t,e,n,i){var a,r,o=n.y+i/2;if(j.isArray(e))for(a=0,r=e.length;a<r;++a)t.fillText(e[a],n.x,o),o+=i;else t.fillText(e,n.x,o)}function Yn(t,e,n){90===t||270===t?n.y-=e.h/2:(t>270||t<90)&&(n.y-=e.h)}function Gn(t){return j.isNumber(t)?t:0}var Xn=Cn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Hn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;j.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);j.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Hn(this.options))},convertTicksToLabels:function(){var t=this;Cn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=j.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:\"\"}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this,e=t.options;e.display&&e.pointLabels.display?function(t){var e,n,i,a=j.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var l,s,u,d=t.chart.data.labels.length;for(e=0;e<d;e++){i=t.getPointPosition(e,t.drawingArea+5),l=t.ctx,s=a.lineHeight,u=t.pointLabels[e],n=j.isArray(u)?{w:j.longestText(l,l.font,u),h:u.length*s}:{w:l.measureText(u).width,h:s},t._pointLabelSizes[e]=n;var c=t.getIndexAngle(e),h=j.toDegrees(c)%360,f=jn(h,i.x,n.w,0,180),g=jn(h,i.y,n.h,90,270);f.start<r.l&&(r.l=f.start,o.l=c),f.end>r.r&&(r.r=f.end,o.r=c),g.start<r.t&&(r.t=g.start,o.t=c),g.end>r.b&&(r.b=g.end,o.b=c)}t.setReductions(t.drawingArea,r,o)}(t):t.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),l=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=Gn(a),r=Gn(r),o=Gn(o),l=Gn(l),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+l)/2)),i.setCenterPoint(a,r,o,l)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,l=n+a.drawingArea,s=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((l+s)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(j.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this,i=n.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(i)*e+n.xCenter,y:Math.sin(i)*e+n.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this,n=e.min,i=e.max;return e.getPointPositionForValue(t||0,e.beginAtZero?0:n<0&&i<0?i:n>0&&i>0?n:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,l=r.angleLines,s=Bn(l.lineWidth,o.lineWidth),u=Bn(l.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Hn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=j.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline=\"middle\";for(var l=t.chart.data.labels.length-1;l>=0;l--){var s=0===l?a/2:0,u=t.getPointPosition(l,r+s+5),d=En(i.fontColor,l,N.global.defaultFontColor);e.fillStyle=d;var c=t.getIndexAngle(l),h=j.toDegrees(c);e.textAlign=qn(h),Yn(h,t._pointLabelSizes[l],u),Un(e,t.pointLabels[l],u,o.lineHeight)}e.restore()}(i),o.display&&j.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,l=t.chart.data.labels.length,s=En(e.color,i-1),u=En(e.lineWidth,i-1);if((o||l)&&s&&u){if(r.save(),r.strokeStyle=s,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d<l;d++)a=t.getPointPosition(d,n),r.lineTo(a.x,a.y)}r.closePath(),r.stroke(),r.restore()}}(i,o,e,n))})),l.display&&s&&u){for(a.save(),a.lineWidth=s,a.strokeStyle=u,a.setLineDash&&(a.setLineDash(Wn([l.borderDash,o.borderDash,[]])),a.lineDashOffset=Wn([l.borderDashOffset,o.borderDashOffset,0])),t=i.chart.data.labels.length-1;t>=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=j.options._parseFont(n),l=Bn(n.fontColor,N.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign=\"center\",e.textBaseline=\"middle\",j.each(t.ticks,(function(r,s){(0!==s||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[s]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=l,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:j.noop}),Kn=Vn;Xn._defaults=Kn;var Zn=j._deprecated,$n=j.options.resolve,Jn=j.valueOrDefault,Qn=Number.MIN_SAFE_INTEGER||-9007199254740991,ti=Number.MAX_SAFE_INTEGER||9007199254740991,ei={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ni=Object.keys(ei);function ii(t,e){return t-e}function ai(t){return j.valueOrDefault(t.time.min,t.ticks.min)}function ri(t){return j.valueOrDefault(t.time.max,t.ticks.max)}function oi(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,l=t.length-1;o>=0&&o<=l;){if(a=t[(i=o+l>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]<n)o=i+1;else{if(!(a[e]>n))return{lo:a,hi:r};l=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],l=o[e]-r[e],s=l?(n-r[e])/l:0,u=(o[i]-r[i])*s;return r[i]+u}function li(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return\"function\"==typeof a&&(o=a(o)),j.isFinite(o)||(o=\"string\"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||\"function\"!=typeof r||(o=r(e),j.isFinite(o)||(o=n.parse(o))),o)}function si(t,e){if(j.isNullOrUndef(e))return null;var n=t.options.time,i=li(t,t.getRightValue(e));return null===i||n.round&&(i=+t._adapter.startOf(i,n.round)),i}function ui(t,e,n,i){var a,r,o,l=ni.length;for(a=ni.indexOf(t);a<l-1;++a)if(o=(r=ei[ni[a]]).steps?r.steps:ti,r.common&&Math.ceil((n-e)/(o*r.size))<=i)return ni[a];return ni[l-1]}function di(t,e,n){var i,a,r=[],o={},l=e.length;for(i=0;i<l;++i)o[a=e[i]]=i,r.push({value:a,major:!1});return 0!==l&&n?function(t,e,n,i){var a,r,o=t._adapter,l=+o.startOf(e[0].value,i),s=e[e.length-1].value;for(a=l;a<=s;a=+o.add(a,1,i))(r=n[a])>=0&&(e[r].major=!0);return e}(t,r,o,n):r}var ci=yn.extend({initialize:function(){this.mergeTicksOptions(),yn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new rn._date(e.adapters.date);return Zn(\"time scale\",n.format,\"time.format\",\"time.parser\"),Zn(\"time scale\",n.min,\"time.min\",\"ticks.min\"),Zn(\"time scale\",n.max,\"time.max\",\"ticks.max\"),j.mergeIf(n.displayFormats,i.formats()),yn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),yn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,l=this,s=l.chart,u=l._adapter,d=l.options,c=d.time.unit||\"day\",h=ti,f=Qn,g=[],p=[],m=[],v=l._getLabels();for(t=0,n=v.length;t<n;++t)m.push(si(l,v[t]));for(t=0,n=(s.data.datasets||[]).length;t<n;++t)if(s.isDatasetVisible(t))if(a=s.data.datasets[t].data,j.isObject(a[0]))for(p[t]=[],e=0,i=a.length;e<i;++e)r=si(l,a[e]),g.push(r),p[t][e]=r;else p[t]=m.slice(0),o||(g=g.concat(m),o=!0);else p[t]=[];m.length&&(h=Math.min(h,m[0]),f=Math.max(f,m[m.length-1])),g.length&&(g=n>1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e<n;++e)a[i=t[e]]||(a[i]=!0,r.push(i));return r}(g).sort(ii):g.sort(ii),h=Math.min(h,g[0]),f=Math.max(f,g[g.length-1])),h=si(l,ai(d))||h,f=si(l,ri(d))||f,h=h===ti?+u.startOf(Date.now(),c):h,f=f===Qn?+u.endOf(Date.now(),c)+1:f,l.min=Math.min(h,f),l.max=Math.max(h+1,f),l._table=[],l._timestamps={data:g,datasets:p,labels:m}},buildTicks:function(){var t,e,n,i=this,a=i.min,r=i.max,o=i.options,l=o.ticks,s=o.time,u=i._timestamps,d=[],c=i.getLabelCapacity(a),h=l.source,f=o.distribution;for(u=\"data\"===h||\"auto\"===h&&\"series\"===f?u.data:\"labels\"===h?u.labels:function(t,e,n,i){var a,r=t._adapter,o=t.options,l=o.time,s=l.unit||ui(l.minUnit,e,n,i),u=$n([l.stepSize,l.unitStepSize,1]),d=\"week\"===s&&l.isoWeekday,c=e,h=[];if(d&&(c=+r.startOf(c,\"isoWeek\",d)),c=+r.startOf(c,d?\"day\":s),r.diff(n,e,s)>1e5*u)throw e+\" and \"+n+\" are too far apart with stepSize of \"+u+\" \"+s;for(a=c;a<n;a=+r.add(a,u,s))h.push(a);return a!==n&&\"ticks\"!==o.bounds||h.push(a),h}(i,a,r,c),\"ticks\"===o.bounds&&u.length&&(a=u[0],r=u[u.length-1]),a=si(i,ai(o))||a,r=si(i,ri(o))||r,t=0,e=u.length;t<e;++t)(n=u[t])>=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=s.unit||(l.autoSkip?ui(s.minUnit,i.min,i.max,c):function(t,e,n,i,a){var r,o;for(r=ni.length-1;r>=ni.indexOf(n);r--)if(o=ni[r],ei[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ni[n?ni.indexOf(n):0]}(i,d.length,s.minUnit,i.min,i.max)),i._majorUnit=l.major.enabled&&\"year\"!==i._unit?function(t){for(var e=ni.indexOf(t)+1,n=ni.length;e<n;++e)if(ei[ni[e]].common)return ni[e]}(i._unit):void 0,i._table=function(t,e,n,i){if(\"linear\"===i||!t.length)return[{time:e,pos:0},{time:n,pos:1}];var a,r,o,l,s,u=[],d=[e];for(a=0,r=t.length;a<r;++a)(l=t[a])>e&&l<n&&d.push(l);for(d.push(n),a=0,r=d.length;a<r;++a)s=d[a+1],o=d[a-1],l=d[a],void 0!==o&&void 0!==s&&Math.round((s+o)/2)===l||u.push({time:l,pos:a/(r-1)});return u}(i._timestamps.data,a,r,f),i._offsets=function(t,e,n,i,a){var r,o,l=0,s=0;return a.offset&&e.length&&(r=oi(t,\"time\",e[0],\"pos\"),l=1===e.length?1-r:(oi(t,\"time\",e[1],\"pos\")-r)/2,o=oi(t,\"time\",e[e.length-1],\"pos\"),s=1===e.length?o:(o-oi(t,\"time\",e[e.length-2],\"pos\"))/2),{start:l,end:s,factor:1/(l+1+s)}}(i._table,d,0,0,o),l.reverse&&d.reverse(),di(i,d,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n._adapter,a=n.chart.data,r=n.options.time,o=a.labels&&t<a.labels.length?a.labels[t]:\"\",l=a.datasets[e].data[t];return j.isObject(l)&&(o=n.getRightValue(l)),r.tooltipFormat?i.format(li(n,o),r.tooltipFormat):\"string\"==typeof o?o:i.format(li(n,o),r.displayFormats.datetime)},tickFormatFunction:function(t,e,n,i){var a=this,r=a._adapter,o=a.options,l=o.time.displayFormats,s=l[a._unit],u=a._majorUnit,d=l[u],c=n[e],h=o.ticks,f=u&&d&&c&&c.major,g=r.format(t,i||(f?d:s)),p=f?h.major:h.minor,m=$n([p.callback,p.userCallback,h.callback,h.userCallback]);return m?m(g,e,n):g},convertTicksToLabels:function(t){var e,n,i=[];for(e=0,n=t.length;e<n;++e)i.push(this.tickFormatFunction(t[e].value,e,t));return i},getPixelForOffset:function(t){var e=this,n=e._offsets,i=oi(e._table,\"time\",t,\"pos\");return e.getPixelForDecimal((n.start+i)*n.factor)},getPixelForValue:function(t,e,n){var i=this,a=null;if(void 0!==e&&void 0!==n&&(a=i._timestamps.datasets[n][e]),null===a&&(a=si(i,t)),null!==a)return i.getPixelForOffset(a)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this,n=e._offsets,i=e.getDecimalForPixel(t)/n.factor-n.end,a=oi(e._table,\"pos\",i,\"time\");return e._adapter._create(a)},_getLabelSize:function(t){var e=this,n=e.options.ticks,i=e.ctx.measureText(t).width,a=j.toRadians(e.isHorizontal()?n.maxRotation:n.minRotation),r=Math.cos(a),o=Math.sin(a),l=Jn(n.fontSize,N.global.defaultFontSize);return{w:i*r+l*o,h:i*o+l*r}},getLabelWidth:function(t){return this._getLabelSize(t).w},getLabelCapacity:function(t){var e=this,n=e.options.time,i=n.displayFormats,a=i[n.unit]||i.millisecond,r=e.tickFormatFunction(t,0,di(e,[t],e._majorUnit),a),o=e._getLabelSize(r),l=Math.floor(e.isHorizontal()?e.width/o.w:e.height/o.h);return e.options.offset&&l--,l>0?l:1}}),hi={position:\"bottom\",distribution:\"linear\",bounds:\"data\",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:\"millisecond\",displayFormats:{}},ticks:{autoSkip:!1,source:\"auto\",major:{enabled:!1}}};ci._defaults=hi;var fi={category:kn,linear:Tn,logarithmic:zn,radialLinear:Xn,time:ci},gi={datetime:\"MMM D, YYYY, h:mm:ss a\",millisecond:\"h:mm:ss.SSS a\",second:\"h:mm:ss a\",minute:\"h:mm a\",hour:\"hA\",day:\"MMM D\",week:\"ll\",month:\"MMM YYYY\",quarter:\"[Q]Q - YYYY\",year:\"YYYY\"};rn._date.override(\"function\"==typeof t?{_id:\"moment\",formats:function(){return gi},parse:function(e,n){return\"string\"==typeof e&&\"string\"==typeof n?e=t(e,n):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,n){return t(e).format(n)},add:function(e,n,i){return t(e).add(n,i).valueOf()},diff:function(e,n,i){return t(e).diff(t(n),i)},startOf:function(e,n,i){return e=t(e),\"isoWeek\"===n?e.isoWeekday(i).valueOf():e.startOf(n).valueOf()},endOf:function(e,n){return t(e).endOf(n).valueOf()},_create:function(e){return t(e)}}:{}),N._set(\"global\",{plugins:{filler:{propagate:!0}}});var pi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e<r&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,n=e?e.x:null,i=e?e.y:null;return j.isArray(e)?function(t,n){return e[n]}:function(t){return{x:null===n?t.x:n,y:null===i?t.y:i}}}};function mi(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return\"origin\";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return\"-\"!==r[0]&&\"+\"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case\"bottom\":return\"start\";case\"top\":return\"end\";case\"zero\":return\"origin\";case\"origin\":case\"start\":case\"end\":return r;default:return!1}}function vi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,l=o.options,s=o.chart.data.labels.length,u=t.fill,d=[];if(!s)return null;for(e=l.ticks.reverse?o.max:o.min,n=l.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a<s;++a)r=\"start\"===u||\"end\"===u?o.getPointPositionForValue(a,\"start\"===u?e:n):o.getBasePosition(a),l.gridLines.circular&&(r.cx=i.x,r.cy=i.y,r.angle=o.getIndexAngle(a)-Math.PI/2),d.push(r);return d}(t):function(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if(\"start\"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:\"end\"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePixel&&(r=i.getBasePixel()),null!=r){if(void 0!==r.x&&void 0!==r.y)return r;if(j.isFinite(r))return{x:(e=i.isHorizontal())?r:null,y:e?null:r}}return null}(t)}function bi(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function xi(t){var e=t.fill,n=\"dataset\";return!1===e?null:(isFinite(e)||(n=\"boundary\"),pi[n](t))}function yi(t){return t&&!t.skip}function _i(t,e,n,i,a){var r,o,l,s;if(i&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r<i;++r)j.canvas.lineTo(t,e[r-1],e[r]);if(void 0===n[0].angle)for(t.lineTo(n[a-1].x,n[a-1].y),r=a-1;r>0;--r)j.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,l=n[0].cy,s=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-l,2)),r=a-1;r>0;--r)t.arc(o,l,s,n[r].angle,n[r-1].angle,!0)}}function ki(t,e,n,i,a,r){var o,l,s,u,d,c,h,f,g=e.length,p=i.spanGaps,m=[],v=[],b=0,x=0;for(t.beginPath(),o=0,l=g;o<l;++o)d=n(u=e[s=o%g]._view,s,i),c=yi(u),h=yi(d),r&&void 0===f&&c&&(l=g+(f=o+1)),c&&h?(b=m.push(u),x=v.push(d)):b&&x&&(p?(c&&m.push(u),h&&v.push(d)):(_i(t,m,v,b,x),b=x=0,m=[],v=[]));_i(t,m,v,b,x),t.closePath(),t.fillStyle=a,t.fill()}var wi={id:\"filler\",afterDatasetsUpdate:function(t,e){var n,i,a,r,o=(t.data.datasets||[]).length,l=e.propagate,s=[];for(i=0;i<o;++i)r=null,(a=(n=t.getDatasetMeta(i)).dataset)&&a._model&&a instanceof wt.Line&&(r={visible:t.isDatasetVisible(i),fill:mi(a,i,o),chart:t,el:a}),n.$filler=r,s.push(r);for(i=0;i<o;++i)(r=s[i])&&(r.fill=bi(s,i,l),r.boundary=vi(r),r.mapper=xi(r))},beforeDatasetsDraw:function(t){var e,n,i,a,r,o,l,s=t._getSortedVisibleDatasetMetas(),u=t.ctx;for(n=s.length-1;n>=0;--n)(e=s[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,l=a.backgroundColor||N.global.defaultColor,o&&l&&r.length&&(j.canvas.clipArea(u,t.chartArea),ki(u,r,o,a,l,i._loop),j.canvas.unclipArea(u)))}},Mi=j.rtl.getRtlAdapter,Si=j.noop,Ci=j.valueOrDefault;function Pi(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}N._set(\"global\",{legend:{display:!0,position:\"top\",align:\"center\",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement(\"ul\"),r=t.data.datasets;for(a.setAttribute(\"class\",t.id+\"-legend\"),e=0,n=r.length;e<n;e++)(i=a.appendChild(document.createElement(\"li\"))).appendChild(document.createElement(\"span\")).style.backgroundColor=r[e].backgroundColor,r[e].label&&i.appendChild(document.createTextNode(r[e].label));return a.outerHTML}});var Ai=Z.extend({initialize:function(t){var e=this;j.extend(e,t),e.legendHitBoxes=[],e._hoveredItem=null,e.doughnutMode=!1},beforeUpdate:Si,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Si,beforeSetDimensions:Si,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Si,beforeBuildLabels:Si,buildLabels:function(){var t=this,e=t.options.labels||{},n=j.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter((function(n){return e.filter(n,t.chart.data)}))),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:Si,beforeFit:Si,fit:function(){var t=this,e=t.options,n=e.labels,i=e.display,a=t.ctx,r=j.options._parseFont(n),o=r.size,l=t.legendHitBoxes=[],s=t.minSize,u=t.isHorizontal();if(u?(s.width=t.maxWidth,s.height=i?10:0):(s.width=i?10:0,s.height=t.maxHeight),i){if(a.font=r.string,u){var d=t.lineWidths=[0],c=0;a.textAlign=\"left\",a.textBaseline=\"middle\",j.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;(0===e||d[d.length-1]+i+2*n.padding>s.width)&&(c+=o+n.padding,d[d.length-(e>0?0:1)]=0),l[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),s.height+=c}else{var h=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],p=n.padding,m=0,v=0;j.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*h>s.height&&(p+=m+n.padding,f.push(m),g.push(v),m=0,v=0),m=Math.max(m,i),v+=o+h,l[e]={left:0,top:0,width:i,height:o}})),p+=m,f.push(m),g.push(v),s.width+=p}t.width=s.width,t.height=s.height}else t.width=s.width=t.height=s.height=0},afterFit:Si,isHorizontal:function(){return\"top\"===this.options.position||\"bottom\"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=N.global,a=i.defaultColor,r=i.elements.line,o=t.height,l=t.columnHeights,s=t.width,u=t.lineWidths;if(e.display){var d,c=Mi(e.rtl,t.left,t.minSize.width),h=t.ctx,f=Ci(n.fontColor,i.defaultFontColor),g=j.options._parseFont(n),p=g.size;h.textAlign=c.textAlign(\"left\"),h.textBaseline=\"middle\",h.lineWidth=.5,h.strokeStyle=f,h.fillStyle=f,h.font=g.string;var m=Pi(n,p),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case\"start\":return n.padding;case\"end\":return t-i;default:return(t-i+n.padding)/2}},x=t.isHorizontal();d=x?{x:t.left+b(s,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,l[0]),line:0},j.rtl.overrideTextDirection(t.ctx,e.textDirection);var y=p+n.padding;j.each(t.legendItems,(function(e,i){var f=h.measureText(e.text).width,g=m+p/2+f,_=d.x,k=d.y;c.setWidth(t.minSize.width),x?i>0&&_+g+n.padding>t.left+t.minSize.width&&(k=d.y+=y,d.line++,_=d.x=t.left+b(s,u[d.line])):i>0&&k+y>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,k=d.y=t.top+b(o,l[d.line]));var w=c.x(_);!function(t,e,i){if(!(isNaN(m)||m<=0)){h.save();var o=Ci(i.lineWidth,r.borderWidth);if(h.fillStyle=Ci(i.fillStyle,a),h.lineCap=Ci(i.lineCap,r.borderCapStyle),h.lineDashOffset=Ci(i.lineDashOffset,r.borderDashOffset),h.lineJoin=Ci(i.lineJoin,r.borderJoinStyle),h.lineWidth=o,h.strokeStyle=Ci(i.strokeStyle,a),h.setLineDash&&h.setLineDash(Ci(i.lineDash,r.borderDash)),n&&n.usePointStyle){var l=m*Math.SQRT2/2,s=c.xPlus(t,m/2),u=e+p/2;j.canvas.drawPoint(h,i.pointStyle,l,s,u,i.rotation)}else h.fillRect(c.leftForLtr(t,m),e,m,p),0!==o&&h.strokeRect(c.leftForLtr(t,m),e,m,p);h.restore()}}(w,k,e),v[i].left=c.leftForLtr(w,v[i].width),v[i].top=k,function(t,e,n,i){var a=p/2,r=c.xPlus(t,m+a),o=e+a;h.fillText(n.text,r,o),n.hidden&&(h.beginPath(),h.lineWidth=2,h.moveTo(r,o),h.lineTo(c.xPlus(r,i),o),h.stroke())}(w,k,e,f),x?d.x+=g+n.padding:d.y+=y})),j.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n<a.length;++n)if(t>=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a=\"mouseup\"===t.type?\"click\":t.type;if(\"mousemove\"===a){if(!i.onHover&&!i.onLeave)return}else{if(\"click\"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),\"click\"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Di(t,e){var n=new Ai({ctx:t.ctx,options:e,chart:t});ge.configure(t,n,e),ge.addBox(t,n),t.legend=n}var Ti={id:\"legend\",_element:Ai,beforeInit:function(t){var e=t.options.legend;e&&Di(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(j.mergeIf(e,N.global.legend),n?(ge.configure(t,n,e),n.options=e):Di(t,e)):n&&(ge.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Ii=j.noop;N._set(\"global\",{title:{display:!1,fontStyle:\"bold\",fullWidth:!0,padding:10,position:\"top\",text:\"\",weight:2e3}});var Fi=Z.extend({initialize:function(t){j.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Ii,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Ii,beforeSetDimensions:Ii,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Ii,beforeBuildLabels:Ii,buildLabels:Ii,afterBuildLabels:Ii,beforeFit:Ii,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(j.isArray(n.text)?n.text.length:1)*j.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Ii,isHorizontal:function(){var t=this.options.position;return\"top\"===t||\"bottom\"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=j.options._parseFont(n),l=o.lineHeight,s=l/2+n.padding,u=0,d=t.top,c=t.left,h=t.bottom,f=t.right;e.fillStyle=j.valueOrDefault(n.fontColor,N.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=c+(f-c)/2,r=d+s,i=f-c):(a=\"left\"===n.position?c+s:f-s,r=d+(h-d)/2,i=h-d,u=Math.PI*(\"left\"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign=\"center\",e.textBaseline=\"middle\";var g=n.text;if(j.isArray(g))for(var p=0,m=0;m<g.length;++m)e.fillText(g[m],0,p,i),p+=l;else e.fillText(g,0,0,i);e.restore()}}});function Oi(t,e){var n=new Fi({ctx:t.ctx,options:e,chart:t});ge.configure(t,n,e),ge.addBox(t,n),t.titleBlock=n}var Li={},Ri=wi,zi=Ti,Ni={id:\"title\",_element:Fi,beforeInit:function(t){var e=t.options.title;e&&Oi(t,e)},beforeUpdate:function(t){var e=t.options.title,n=t.titleBlock;e?(j.mergeIf(e,N.global.title),n?(ge.configure(t,n,e),n.options=e):Oi(t,e)):n&&(ge.removeBox(t,n),delete t.titleBlock)}};for(var Bi in Li.filler=Ri,Li.legend=zi,Li.title=Ni,en.helpers=j,function(){function t(t,e,n){var i;return\"string\"==typeof t?(i=parseInt(t,10),-1!==t.indexOf(\"%\")&&(i=i/100*e.parentNode[n])):i=t,i}function e(t){return null!=t&&\"none\"!==t}function n(n,i,a){var r=document.defaultView,o=j._getParentNode(n),l=r.getComputedStyle(n)[i],s=r.getComputedStyle(o)[i],u=e(l),d=e(s),c=Number.POSITIVE_INFINITY;return u||d?Math.min(u?t(l,n,a):c,d?t(s,o,a):c):\"none\"}j.where=function(t,e){if(j.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return j.each(t,(function(t){e(t)&&n.push(t)})),n},j.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i<a;++i)if(e.call(n,t[i],i,t))return i;return-1},j.findNextWhere=function(t,e,n){j.isNullOrUndef(n)&&(n=-1);for(var i=n+1;i<t.length;i++){var a=t[i];if(e(a))return a}},j.findPreviousWhere=function(t,e,n){j.isNullOrUndef(n)&&(n=t.length);for(var i=n-1;i>=0;i--){var a=t[i];if(e(a))return a}},j.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},j.almostEquals=function(t,e,n){return Math.abs(t-e)<n},j.almostWhole=function(t,e){var n=Math.round(t);return n-e<=t&&n+e>=t},j.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},j.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},j.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},j.toRadians=function(t){return t*(Math.PI/180)},j.toDegrees=function(t){return t*(180/Math.PI)},j._decimalPlaces=function(t){if(j.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},j.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},j.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},j.aliasPixel=function(t){return t%2==0?0:.5},j._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},j.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,l=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),s=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=l/(l+s),d=s/(l+s),c=i*(u=isNaN(u)?0:u),h=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-c*(o.x-a.x),y:r.y-c*(o.y-a.y)},next:{x:r.x+h*(o.x-a.x),y:r.y+h*(o.y-a.y)}}},j.EPSILON=Number.EPSILON||1e-14,j.splineCurveMonotone=function(t){var e,n,i,a,r,o,l,s,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),c=d.length;for(e=0;e<c;++e)if(!(i=d[e]).model.skip){if(n=e>0?d[e-1]:null,(a=e<c-1?d[e+1]:null)&&!a.model.skip){var h=a.model.x-i.model.x;i.deltaK=0!==h?(a.model.y-i.model.y)/h:0}!n||n.model.skip?i.mK=i.deltaK:!a||a.model.skip?i.mK=n.deltaK:this.sign(n.deltaK)!==this.sign(i.deltaK)?i.mK=0:i.mK=(n.deltaK+i.deltaK)/2}for(e=0;e<c-1;++e)i=d[e],a=d[e+1],i.model.skip||a.model.skip||(j.almostEquals(i.deltaK,0,this.EPSILON)?i.mK=a.mK=0:(r=i.mK/i.deltaK,o=a.mK/i.deltaK,(s=Math.pow(r,2)+Math.pow(o,2))<=9||(l=3/Math.sqrt(s),i.mK=r*l*i.deltaK,a.mK=o*l*i.deltaK)));for(e=0;e<c;++e)(i=d[e]).model.skip||(n=e>0?d[e-1]:null,a=e<c-1?d[e+1]:null,n&&!n.model.skip&&(u=(i.model.x-n.model.x)/3,i.model.controlPointPreviousX=i.model.x-u,i.model.controlPointPreviousY=i.model.y-u*i.mK),a&&!a.model.skip&&(u=(a.model.x-i.model.x)/3,i.model.controlPointNextX=i.model.x+u,i.model.controlPointNextY=i.model.y+u*i.mK))},j.nextItem=function(t,e,n){return n?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},j.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},j.niceNum=function(t,e){var n=Math.floor(j.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},j.requestAnimFrame=\"undefined\"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},j.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),l=a.touches;l&&l.length>0?(n=l[0].clientX,i=l[0].clientY):(n=a.clientX,i=a.clientY);var s=parseFloat(j.getStyle(r,\"padding-left\")),u=parseFloat(j.getStyle(r,\"padding-top\")),d=parseFloat(j.getStyle(r,\"padding-right\")),c=parseFloat(j.getStyle(r,\"padding-bottom\")),h=o.right-o.left-s-d,f=o.bottom-o.top-u-c;return{x:n=Math.round((n-o.left-s)/h*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},j.getConstraintWidth=function(t){return n(t,\"max-width\",\"clientWidth\")},j.getConstraintHeight=function(t){return n(t,\"max-height\",\"clientHeight\")},j._calculatePadding=function(t,e,n){return(e=j.getStyle(t,e)).indexOf(\"%\")>-1?n*parseInt(e,10)/100:parseInt(e,10)},j._getParentNode=function(t){var e=t.parentNode;return e&&\"[object ShadowRoot]\"===e.toString()&&(e=e.host),e},j.getMaximumWidth=function(t){var e=j._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-j._calculatePadding(e,\"padding-left\",n)-j._calculatePadding(e,\"padding-right\",n),a=j.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},j.getMaximumHeight=function(t){var e=j._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-j._calculatePadding(e,\"padding-top\",n)-j._calculatePadding(e,\"padding-bottom\",n),a=j.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},j.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},j.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||\"undefined\"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+\"px\",i.style.width=r+\"px\")}},j.fontString=function(t,e,n){return e+\" \"+t+\"px \"+n},j.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,l,s,u,d,c=0,h=n.length;for(o=0;o<h;o++)if(null!=(u=n[o])&&!0!==j.isArray(u))c=j.measureText(t,a,r,c,u);else if(j.isArray(u))for(l=0,s=u.length;l<s;l++)null==(d=u[l])||j.isArray(d)||(c=j.measureText(t,a,r,c,d));var f=r.length/2;if(f>n.length){for(o=0;o<f;o++)delete a[r[o]];r.splice(0,f)}return c},j.measureText=function(t,e,n,i,a){var r=e[a];return r||(r=e[a]=t.measureText(a).width,n.push(a)),r>i&&(i=r),i},j.numberOfLabelLines=function(t){var e=1;return j.each(t,(function(t){j.isArray(t)&&t.length>e&&(e=t.length)})),e},j.color=_?function(t){return t instanceof CanvasGradient&&(t=N.global.defaultColor),_(t)}:function(t){return console.error(\"Color.js not found!\"),t},j.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:j.color(t).saturate(.5).darken(.1).rgbString()}}(),en._adapters=rn,en.Animation=J,en.animationService=Q,en.controllers=$t,en.DatasetController=at,en.defaults=N,en.Element=Z,en.elements=wt,en.Interaction=ae,en.layouts=ge,en.platform=Le,en.plugins=Re,en.Scale=yn,en.scaleService=ze,en.Ticks=on,en.Tooltip=Ge,en.helpers.each(fi,(function(t,e){en.scaleService.registerScaleType(e,t,t._defaults)})),Li)Li.hasOwnProperty(Bi)&&en.plugins.register(Li[Bi]);en.platform.initialize();var Ei=en;return\"undefined\"!=typeof window&&(window.Chart=en),en.Chart=en,en.Legend=Li.legend._element,en.Title=Li.title._element,en.pluginService=en.plugins,en.PluginBase=en.Element.extend({}),en.canvasHelpers=en.helpers.canvas,en.layoutService=en.layouts,en.LinearScaleBase=Cn,en.helpers.each([\"Bar\",\"Bubble\",\"Doughnut\",\"Line\",\"PolarArea\",\"Radar\",\"Scatter\"],(function(t){en[t]=function(e,n){return new en(e,en.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Ei}));"
src/DataFrame/Display/Web/Plot.hs view
@@ -12,7 +12,6 @@ import Data.Char import qualified Data.List as L import qualified Data.Map as M-import Data.Maybe (fromMaybe) import qualified Data.Text as T import qualified Data.Text.IO as T import Data.Type.Equality (TestEquality (testEquality), type (:~:) (Refl))@@ -24,7 +23,6 @@ import System.Random (newStdGen, randomRs) import Type.Reflection (typeRep) -import DataFrame.Display.Web.ChartJs import DataFrame.Internal.Column (Column (..), isNumeric) import qualified DataFrame.Internal.Column as D import DataFrame.Internal.DataFrame (DataFrame (..), getColumn)@@ -73,11 +71,18 @@         , plotFile = Nothing         } +chartJsScript :: T.Text+chartJsScript =+    "<script src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js\"></script>\n"+ generateChartId :: IO T.Text generateChartId = do     gen <- newStdGen-    let randomWords = filter (\c -> c `elem` ([49 .. 57] ++ [65 .. 90] ++ [97 .. 122])) (take 64 (randomRs (49, 126) gen :: [Int]))-    return $ "chart_" <> (T.pack $ (map chr randomWords))+    let randomWords =+            filter+                (\c -> c `elem` ([49 .. 57] ++ [65 .. 90] ++ [97 .. 122]))+                (take 64 (randomRs (49, 126) gen :: [Int]))+    return $ "chart_" <> T.pack (map chr randomWords)  wrapInHTML :: T.Text -> T.Text -> Int -> Int -> T.Text wrapInHTML chartId content width height =@@ -89,9 +94,7 @@         , "px;height:"         , T.pack (show height)         , "px\"></canvas>\n"-        , "<script>"-        , minifiedChartJs-        , "</script>\n"+        , "<script src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js\"></script>\n"         , "<script>\n"         , content         , "\n</script>\n"@@ -100,7 +103,8 @@ plotHistogram :: (HasCallStack) => T.Text -> DataFrame -> IO HtmlPlot plotHistogram colName = plotHistogramWith colName (defaultPlotConfig Histogram) -plotHistogramWith :: (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotHistogramWith ::+    (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotHistogramWith colName config df = do     chartId <- generateChartId     let values = extractNumericColumn colName df@@ -110,10 +114,14 @@         bins = [minVal + fromIntegral i * binWidth | i <- [0 .. numBins - 1]]         counts = calculateHistogram values bins binWidth -        labels = T.intercalate "," ["\"" <> T.pack (show (round b :: Int)) <> "\"" | b <- bins]+        labels =+            T.intercalate "," ["\"" <> T.pack (show (round b :: Int)) <> "\"" | b <- bins]         dataPoints = T.intercalate "," [T.pack (show c) | c <- counts] -        chartTitle = if T.null (plotTitle config) then "Histogram of " <> colName else plotTitle config+        chartTitle =+            if T.null (plotTitle config)+                then "Histogram of " <> colName+                else plotTitle config          jsCode =             T.concat@@ -148,7 +156,9 @@                 , "});"                 ] -    return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+    return $+        HtmlPlot $+            wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)  calculateHistogram :: [Double] -> [Double] -> Double -> [Int] calculateHistogram values bins binWidth =@@ -158,15 +168,21 @@ plotScatter :: (HasCallStack) => T.Text -> T.Text -> DataFrame -> IO HtmlPlot plotScatter xCol yCol = plotScatterWith xCol yCol (defaultPlotConfig Scatter) -plotScatterWith :: (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotScatterWith ::+    (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotScatterWith xCol yCol config df = do     chartId <- generateChartId     let xVals = extractNumericColumn xCol df         yVals = extractNumericColumn yCol df         points = zip xVals yVals -        dataPoints = T.intercalate "," ["{x:" <> T.pack (show x) <> ", y:" <> T.pack (show y) <> "}" | (x, y) <- points]-        chartTitle = if T.null (plotTitle config) then xCol <> " vs " <> yCol else plotTitle config+        dataPoints =+            T.intercalate+                ","+                [ "{x:" <> T.pack (show x) <> ", y:" <> T.pack (show y) <> "}" | (x, y) <- points+                ]+        chartTitle =+            if T.null (plotTitle config) then xCol <> " vs " <> yCol else plotTitle config          jsCode =             T.concat@@ -202,12 +218,17 @@                 , "});"                 ] -    return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+    return $+        HtmlPlot $+            wrapInHTML chartId jsCode (plotWidth config) (plotHeight config) -plotScatterBy :: (HasCallStack) => T.Text -> T.Text -> T.Text -> DataFrame -> IO HtmlPlot+plotScatterBy ::+    (HasCallStack) => T.Text -> T.Text -> T.Text -> DataFrame -> IO HtmlPlot plotScatterBy xCol yCol grouping = plotScatterByWith xCol yCol grouping (defaultPlotConfig Scatter) -plotScatterByWith :: (HasCallStack) => T.Text -> T.Text -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotScatterByWith ::+    (HasCallStack) =>+    T.Text -> T.Text -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotScatterByWith xCol yCol grouping config df = do     chartId <- generateChartId     let vals = extractStringColumn grouping df@@ -229,7 +250,11 @@             xVals = extractNumericColumn xCol filtered             yVals = extractNumericColumn yCol filtered             points = zip xVals yVals-            dataPoints = T.intercalate "," ["{x:" <> T.pack (show x) <> ", y:" <> T.pack (show y) <> "}" | (x, y) <- points]+            dataPoints =+                T.intercalate+                    ","+                    [ "{x:" <> T.pack (show x) <> ", y:" <> T.pack (show y) <> "}" | (x, y) <- points+                    ]         return $             T.concat                 [ "    {\n"@@ -247,7 +272,10 @@                 ]      let datasetsStr = T.intercalate ",\n" datasets-        chartTitle = if T.null (plotTitle config) then xCol <> " vs " <> yCol <> " by " <> grouping else plotTitle config+        chartTitle =+            if T.null (plotTitle config)+                then xCol <> " vs " <> yCol <> " by " <> grouping+                else plotTitle config          jsCode =             T.concat@@ -276,12 +304,15 @@                 , "});"                 ] -    return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+    return $+        HtmlPlot $+            wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)  plotLines :: (HasCallStack) => T.Text -> [T.Text] -> DataFrame -> IO HtmlPlot plotLines xAxis colNames = plotLinesWith xAxis colNames (defaultPlotConfig Line) -plotLinesWith :: (HasCallStack) => T.Text -> [T.Text] -> PlotConfig -> DataFrame -> IO HtmlPlot+plotLinesWith ::+    (HasCallStack) => T.Text -> [T.Text] -> PlotConfig -> DataFrame -> IO HtmlPlot plotLinesWith xAxis colNames config df = do     chartId <- generateChartId     let xValues = extractNumericColumn xAxis df@@ -347,18 +378,23 @@                 , "});"                 ] -    return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+    return $+        HtmlPlot $+            wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)  plotBars :: (HasCallStack) => T.Text -> DataFrame -> IO HtmlPlot plotBars colName = plotBarsWith colName Nothing (defaultPlotConfig Bar) -plotBarsWith :: (HasCallStack) => T.Text -> Maybe T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotBarsWith ::+    (HasCallStack) =>+    T.Text -> Maybe T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotBarsWith colName groupByCol config df =     case groupByCol of         Nothing -> plotSingleBars colName config df         Just grpCol -> plotGroupedBarsWith grpCol colName config df -plotSingleBars :: (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotSingleBars ::+    (HasCallStack) => T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotSingleBars colName config df = do     chartId <- generateChartId     let barData = getCategoricalCounts colName df@@ -399,7 +435,9 @@                         , "  }\n"                         , "});"                         ]-            return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+            return $+                HtmlPlot $+                    wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)         Nothing -> do             let values = extractNumericColumn colName df                 labels' =@@ -441,12 +479,16 @@                         , "  }\n"                         , "});"                         ]-            return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+            return $+                HtmlPlot $+                    wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)  plotPie :: (HasCallStack) => T.Text -> Maybe T.Text -> DataFrame -> IO HtmlPlot plotPie valCol labelCol = plotPieWith valCol labelCol (defaultPlotConfig Pie) -plotPieWith :: (HasCallStack) => T.Text -> Maybe T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotPieWith ::+    (HasCallStack) =>+    T.Text -> Maybe T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotPieWith valCol labelCol config df = do     chartId <- generateChartId     let categoricalData = getCategoricalCounts valCol df@@ -484,7 +526,9 @@                         , "  }\n"                         , "});"                         ]-            return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+            return $+                HtmlPlot $+                    wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)         Nothing -> do             let values = extractNumericColumn valCol df                 labels' = case labelCol of@@ -526,7 +570,9 @@                         , "  }\n"                         , "});"                         ]-            return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+            return $+                HtmlPlot $+                    wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)  pieColors :: [T.Text] pieColors =@@ -542,10 +588,12 @@     , "rgb(238, 130, 238)"     ] -plotStackedBars :: (HasCallStack) => T.Text -> [T.Text] -> DataFrame -> IO HtmlPlot+plotStackedBars ::+    (HasCallStack) => T.Text -> [T.Text] -> DataFrame -> IO HtmlPlot plotStackedBars categoryCol valueColumns = plotStackedBarsWith categoryCol valueColumns (defaultPlotConfig StackedBar) -plotStackedBarsWith :: (HasCallStack) => T.Text -> [T.Text] -> PlotConfig -> DataFrame -> IO HtmlPlot+plotStackedBarsWith ::+    (HasCallStack) => T.Text -> [T.Text] -> PlotConfig -> DataFrame -> IO HtmlPlot plotStackedBarsWith categoryCol valueColumns config df = do     chartId <- generateChartId     let categories = extractStringColumn categoryCol df@@ -613,12 +661,15 @@                 , "});"                 ] -    return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+    return $+        HtmlPlot $+            wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)  plotBoxPlots :: (HasCallStack) => [T.Text] -> DataFrame -> IO HtmlPlot plotBoxPlots colNames = plotBoxPlotsWith colNames (defaultPlotConfig BoxPlot) -plotBoxPlotsWith :: (HasCallStack) => [T.Text] -> PlotConfig -> DataFrame -> IO HtmlPlot+plotBoxPlotsWith ::+    (HasCallStack) => [T.Text] -> PlotConfig -> DataFrame -> IO HtmlPlot plotBoxPlotsWith colNames config df = do     chartId <- generateChartId     boxData <- forM colNames $ \col -> do@@ -634,8 +685,6 @@      let labels = T.intercalate "," ["\"" <> col <> "\"" | (col, _, _, _, _, _) <- boxData]         medians = T.intercalate "," [T.pack (show med) | (_, _, _, med, _, _) <- boxData]-        mins = T.intercalate "," [T.pack (show minV) | (_, minV, _, _, _, _) <- boxData]-        maxs = T.intercalate "," [T.pack (show maxV) | (_, _, _, _, _, maxV) <- boxData]         chartTitle = if T.null (plotTitle config) then "Box Plot" else plotTitle config          jsCode =@@ -669,12 +718,17 @@                 , "});"                 ] -    return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+    return $+        HtmlPlot $+            wrapInHTML chartId jsCode (plotWidth config) (plotHeight config) -plotGroupedBarsWith :: (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotGroupedBarsWith ::+    (HasCallStack) => T.Text -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotGroupedBarsWith = plotGroupedBarsWithN 10 -plotGroupedBarsWithN :: (HasCallStack) => Int -> T.Text -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotGroupedBarsWithN ::+    (HasCallStack) =>+    Int -> T.Text -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotGroupedBarsWithN n groupCol valCol config df = do     chartId <- generateChartId     let colIsNumeric = isNumericColumnCheck valCol df@@ -683,11 +737,14 @@         then do             let groups = extractStringColumn groupCol df                 values = extractNumericColumn valCol df-                grouped = M.toList $ M.fromListWith (+) (zip groups values)-                finalGroups = groupWithOther n grouped-                labels = T.intercalate "," ["\"" <> label <> "\"" | (label, _) <- finalGroups]-                dataPoints = T.intercalate "," [T.pack (show val) | (_, val) <- finalGroups]-                chartTitle = if T.null (plotTitle config) then groupCol <> " by " <> valCol else plotTitle config+                m = M.fromListWith (+) (zip groups values)+                grouped = map (\v -> (v, m M.! v)) groups+                labels = T.intercalate "," ["\"" <> label <> "\"" | (label, _) <- grouped]+                dataPoints = T.intercalate "," [T.pack (show val) | (_, val) <- grouped]+                chartTitle =+                    if T.null (plotTitle config)+                        then groupCol <> " by " <> valCol+                        else plotTitle config                  jsCode =                     T.concat@@ -721,7 +778,9 @@                         , "  }\n"                         , "});"                         ]-            return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+            return $+                HtmlPlot $+                    wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)         else do             let groups = extractStringColumn groupCol df                 vals = extractStringColumn valCol df@@ -734,7 +793,10 @@                 finalCounts = groupWithOther n [(k, fromIntegral v) | (k, v) <- counts]                 labels = T.intercalate "," ["\"" <> label <> "\"" | (label, _) <- finalCounts]                 dataPoints = T.intercalate "," [T.pack (show val) | (_, val) <- finalCounts]-                chartTitle = if T.null (plotTitle config) then groupCol <> " by " <> valCol else plotTitle config+                chartTitle =+                    if T.null (plotTitle config)+                        then groupCol <> " by " <> valCol+                        else plotTitle config                  jsCode =                     T.concat@@ -766,7 +828,9 @@                         , "  }\n"                         , "});"                         ]-            return $ HtmlPlot $ wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)+            return $+                HtmlPlot $+                    wrapInHTML chartId jsCode (plotWidth config) (plotHeight config)  -- TODO: Move these helpers to a common module. @@ -789,7 +853,7 @@                     UnboxedColumn vec -> V.toList $ VG.map (T.pack . show) (VG.convert vec)                     OptionalColumn (vec :: V.Vector (Maybe a)) -> case testEquality (typeRep @a) (typeRep @T.Text) of                         Nothing -> V.toList $ V.map (T.pack . show) vec-                        Just Refl -> V.toList $ V.map (fromMaybe "Nothing" . fmap ("Just " <>)) vec+                        Just Refl -> V.toList $ V.map (maybe "Nothing" ("Just " <>)) vec  extractNumericColumn :: (HasCallStack) => T.Text -> DataFrame -> [Double] extractNumericColumn colName df =@@ -814,7 +878,8 @@                     Just Refl -> V.toList $ V.map realToFrac vec                     Nothing -> error $ "Column is not numeric (type: " ++ show (typeRep @a) ++ ")" -unboxedVectorToDoubles :: forall a. (Typeable a, VU.Unbox a, Show a) => VU.Vector a -> [Double]+unboxedVectorToDoubles ::+    forall a. (Typeable a, VU.Unbox a, Show a) => VU.Vector a -> [Double] unboxedVectorToDoubles vec =     case testEquality (typeRep @a) (typeRep @Double) of         Just Refl -> VU.toList vec@@ -824,7 +889,8 @@                 Just Refl -> VU.toList $ VU.map realToFrac vec                 Nothing -> error $ "Column is not numeric (type: " ++ show (typeRep @a) ++ ")" -getCategoricalCounts :: (HasCallStack) => T.Text -> DataFrame -> Maybe [(T.Text, Double)]+getCategoricalCounts ::+    (HasCallStack) => T.Text -> DataFrame -> Maybe [(T.Text, Double)] getCategoricalCounts colName df =     case M.lookup colName (columnIndices df) of         Nothing -> error $ "Column " ++ T.unpack colName ++ " not found"@@ -843,7 +909,9 @@                         let counts = countValues vec                          in case testEquality (typeRep @a) (typeRep @T.Text) of                                 Nothing -> Just [((T.pack . show) k, fromIntegral v) | (k, v) <- counts]-                                Just Refl -> Just [(fromMaybe "Nothing" (fmap ("Just " <>) k), fromIntegral v) | (k, v) <- counts]+                                Just Refl ->+                                    Just+                                        [(maybe "Nothing" ("Just " <>) k, fromIntegral v) | (k, v) <- counts]   where     countValues :: (Ord a, Show a) => V.Vector a -> [(a, Int)]     countValues vec = M.toList $ V.foldr' (\x acc -> M.insertWith (+) x 1 acc) M.empty vec@@ -888,7 +956,8 @@ plotBarsTopN :: (HasCallStack) => Int -> T.Text -> DataFrame -> IO HtmlPlot plotBarsTopN n colName = plotBarsTopNWith n colName (defaultPlotConfig Bar) -plotBarsTopNWith :: (HasCallStack) => Int -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot+plotBarsTopNWith ::+    (HasCallStack) => Int -> T.Text -> PlotConfig -> DataFrame -> IO HtmlPlot plotBarsTopNWith n colName config df = do     let config' = config{plotTitle = plotTitle config <> " (Top " <> T.pack (show n) <> ")"}     plotBarsWith colName Nothing config' df@@ -896,7 +965,8 @@ plotValueCounts :: (HasCallStack) => T.Text -> DataFrame -> IO HtmlPlot plotValueCounts colName = plotValueCountsWith colName 10 (defaultPlotConfig Bar) -plotValueCountsWith :: (HasCallStack) => T.Text -> Int -> PlotConfig -> DataFrame -> IO HtmlPlot+plotValueCountsWith ::+    (HasCallStack) => T.Text -> Int -> PlotConfig -> DataFrame -> IO HtmlPlot plotValueCountsWith colName maxBars config df = do     let config' = config{plotTitle = "Value counts for " <> colName}     plotBarsTopNWith maxBars colName config' df@@ -905,10 +975,9 @@ plotAllHistograms df = do     let numericCols = filter (isNumericColumn df) (columnNames df)     xs <- forM numericCols $ \col -> do-        T.putStrLn $ "<!-- Histogram for " <> col <> " -->"         plotHistogram col df-    let allPlots = L.foldl' (\acc (HtmlPlot contents) -> acc <> "\n" <> (T.replace minifiedChartJs "" contents)) "" xs-    return (HtmlPlot (T.concat ["\n<script>\n", minifiedChartJs, "\n</script>\n", allPlots]))+    let allPlots = L.foldl' (\acc (HtmlPlot contents) -> acc <> "\n" <> contents) "" xs+    return (HtmlPlot allPlots)  plotCategoricalSummary :: (HasCallStack) => DataFrame -> IO HtmlPlot plotCategoricalSummary df = do@@ -917,11 +986,12 @@         let counts = getCategoricalCounts col df         case counts of             Just c -> do-                if (length c > 1)+                if length c > 1                     then                         ( do                             let numUnique = length c-                            putStrLn $ "\n<!-- " ++ T.unpack col ++ " (" ++ show numUnique ++ " unique values) -->"+                            putStrLn $+                                "\n<!-- " ++ T.unpack col ++ " (" ++ show numUnique ++ " unique values) -->"                             if numUnique > 15 then plotBarsTopN 10 col df else plotBars col df                         )                     else return (HtmlPlot "")@@ -940,7 +1010,10 @@     case counts of         Just c -> do             let numUnique = length c-                config = (defaultPlotConfig Bar){plotTitle = colName <> " (" <> T.pack (show numUnique) <> " unique values)"}+                config =+                    (defaultPlotConfig Bar)+                        { plotTitle = colName <> " (" <> T.pack (show numUnique) <> " unique values)"+                        }             if numUnique <= 12                 then plotBarsWith colName Nothing config df                 else plotBarsTopNWith 10 colName config df@@ -951,7 +1024,7 @@     plotId <- generateChartId     home <- getHomeDirectory     let operatingSystem = os-    let path = "plot-" <> (T.unpack plotId) <> ".html"+    let path = "plot-" <> T.unpack plotId <> ".html"      let fullPath =             if operatingSystem == "mingw32"
src/DataFrame/Errors.hs view
@@ -38,9 +38,15 @@     show :: DataFrameException -> String     show (TypeMismatchException context) =         let-            errorString = typeMismatchError (either id show (userType context)) (either id show (expectedType context))+            errorString =+                typeMismatchError+                    (either id show (userType context))+                    (either id show (expectedType context))          in-            addCallPointInfo (errorColumnName context) (callingFunctionName context) errorString+            addCallPointInfo+                (errorColumnName context)+                (callingFunctionName context)+                errorString     show (ColumnNotFoundException columnName callPoint availableColumns) = columnNotFound columnName callPoint availableColumns     show (EmptyDataSetException callPoint) = emptyDataSetError callPoint     show (WrongQuantileNumberException q) = wrongQuantileNumberError q
src/DataFrame/Functions.hs view
@@ -16,29 +16,40 @@  import DataFrame.Internal.Column import DataFrame.Internal.DataFrame (DataFrame (..), unsafeGetColumn)-import DataFrame.Internal.Expression (Expr (..), UExpr (..), eSize, interpret)+import DataFrame.Internal.Expression (+    Expr (..),+    UExpr (..),+    eSize,+    interpret,+    replaceExpr,+ ) import DataFrame.Internal.Statistics-import DataFrame.Operations.Subset (exclude)+import qualified DataFrame.Operations.Statistics as Stats+import DataFrame.Operations.Subset (exclude, select)  import Control.Monad import qualified Data.Char as Char import Data.Function import qualified Data.List as L import qualified Data.Map as M+import Data.Maybe (fromMaybe, listToMaybe) import qualified Data.Set as S import qualified Data.Text as T+import Data.Type.Equality import qualified Data.Vector.Generic as VG import qualified Data.Vector.Unboxed as VU-import Debug.Trace ( traceShow )+import qualified DataFrame.Operations.Transformations as D+import Debug.Trace (trace, traceShow) import Language.Haskell.TH import qualified Language.Haskell.TH.Syntax as TH import Type.Reflection (typeRep)-import Prelude hiding (sum, minimum, maximum)-import Data.Type.Equality+import Prelude hiding (maximum, minimum, sum)  name :: (Show a) => Expr a -> T.Text name (Col n) = n-name other = error $ "You must call `name` on a column reference. Not the expression: " ++ show other+name other =+    error $+        "You must call `name` on a column reference. Not the expression: " ++ show other  col :: (Columnable a) => T.Text -> Expr a col = Col@@ -55,24 +66,41 @@ lift :: (Columnable a, Columnable b) => (a -> b) -> Expr a -> Expr b lift = UnaryOp "udf" -lift2 :: (Columnable c, Columnable b, Columnable a) => (c -> b -> a) -> Expr c -> Expr b -> Expr a+lift2 ::+    (Columnable c, Columnable b, Columnable a) =>+    (c -> b -> a) -> Expr c -> Expr b -> Expr a lift2 = BinaryOp "udf"  (==) :: (Columnable a, Eq a) => Expr a -> Expr a -> Expr Bool (==) = BinaryOp "eq" (Prelude.==) +eq :: (Columnable a, Eq a) => Expr a -> Expr a -> Expr Bool+eq = BinaryOp "eq" (Prelude.==)+ (<) :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr Bool (<) = BinaryOp "lt" (Prelude.<) +lt :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr Bool+lt = BinaryOp "lt" (Prelude.<)+ (>) :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr Bool (>) = BinaryOp "gt" (Prelude.>) +gt :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr Bool+gt = BinaryOp "gt" (Prelude.>)+ (<=) :: (Columnable a, Ord a, Eq a) => Expr a -> Expr a -> Expr Bool (<=) = BinaryOp "leq" (Prelude.<=) +leq :: (Columnable a, Ord a, Eq a) => Expr a -> Expr a -> Expr Bool+leq = BinaryOp "leq" (Prelude.<=)+ (>=) :: (Columnable a, Ord a, Eq a) => Expr a -> Expr a -> Expr Bool (>=) = BinaryOp "geq" (Prelude.>=) +geq :: (Columnable a, Ord a, Eq a) => Expr a -> Expr a -> Expr Bool+geq = BinaryOp "geq" (Prelude.>=)+ and :: Expr Bool -> Expr Bool -> Expr Bool and = BinaryOp "and" (&&) @@ -86,10 +114,10 @@ count expr = AggFold expr "foldUdf" 0 (\acc _ -> acc + 1)  minimum :: (Columnable a, Ord a) => Expr a -> Expr a-minimum expr = AggReduce expr "minimum" min+minimum expr = AggReduce expr "minimum" Prelude.min  maximum :: (Columnable a, Ord a) => Expr a -> Expr a-maximum expr = AggReduce expr "maximum" max+maximum expr = AggReduce expr "maximum" Prelude.max  sum :: forall a. (Columnable a, Num a, VU.Unbox a) => Expr a -> Expr a sum expr = AggNumericVector expr "sum" VG.sum@@ -101,81 +129,176 @@ median expr = AggNumericVector expr "mean" median'  percentile :: Int -> Expr Double -> Expr Double-percentile n expr = AggNumericVector expr (T.pack $ "percentile " ++ show n) ((VU.! 0) . quantiles' (VU.fromList [n]) 100)+percentile n expr =+    AggNumericVector+        expr+        (T.pack $ "percentile " ++ show n)+        ((VU.! 0) . quantiles' (VU.fromList [n]) 100) -standardDeviation :: (Columnable a, Real a, VU.Unbox a) => Expr a -> Expr Double-standardDeviation expr = AggNumericVector expr "stddev" (sqrt . variance')+stddev :: (Columnable a, Real a, VU.Unbox a) => Expr a -> Expr Double+stddev expr = AggNumericVector expr "stddev" (sqrt . variance')  zScore :: Expr Double -> Expr Double-zScore c = (c - mean c) / standardDeviation c+zScore c = (c - mean c) / stddev c -reduce :: forall a b. (Columnable a, Columnable b) => Expr b -> a -> (a -> b -> a) -> Expr a+pow :: (Columnable a, Num a) => Int -> Expr a -> Expr a+pow i = UnaryOp ("pow " <> T.pack (show i)) (^ i)++relu :: (Columnable a, Num a) => Expr a -> Expr a+relu = UnaryOp "relu" (Prelude.max 0)++min :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr a+min = BinaryOp "min" Prelude.min++max :: (Columnable a, Ord a) => Expr a -> Expr a -> Expr a+max = BinaryOp "max" Prelude.max++reduce ::+    forall a b.+    (Columnable a, Columnable b) => Expr b -> a -> (a -> b -> a) -> Expr a reduce expr = AggFold expr "foldUdf"  generatePrograms :: [Expr Double] -> [Expr Double] -> [Expr Double]-generatePrograms vars existingPrograms =-    existingPrograms ++-    [ transform p-    | p <- existingPrograms-    , transform <- [zScore, abs, log . (+ Lit 1), exp]-    ] ++-    [ p + q-    | (i, p) <- zip [0..] existingPrograms-    , (j, q) <- zip [0..] existingPrograms-    , i Prelude.>= j-    ] ++-    [ ifThenElse (p DataFrame.Functions.>= percentile n p) p q-    | p <- existingPrograms-    , q <- existingPrograms ++ [lit 1, lit 0, lit (-1)]-    , n <- [1, 25, 50, 75, 99]-    ] ++-    [ p - q-    | p <- existingPrograms-    , q <- existingPrograms-    ] ++-    [ p * q-    | (i, p) <- zip [0..] existingPrograms-    , (j, q) <- zip [0..] existingPrograms-    , i Prelude.>= j-    ] ++-    [ p / q-    | p <- existingPrograms-    , q <- existingPrograms-    ] ++-    [ t p-    | v <- vars-    , p <- existingPrograms-    , t <- transformsWithVar v-    ]-  where-    transformsWithVar v =-        [ (v +)-        , (* v)-        , (/ v)-        , (v /)-        , \v' -> v' - v-        , (v -)-        ]+generatePrograms vars [] =+    vars+        ++ [ transform p+           | p <- vars+           , transform <-+                [ zScore+                , abs+                , sqrt+                , log . (+ Lit 1)+                , exp+                , mean+                , median+                , stddev+                , sin+                , cos+                , relu+                , signum+                ]+           ]+        ++ [ pow i p+           | p <- vars+           , i <- [2 .. 6]+           ]+        ++ [ p + q+           | (i, p) <- zip [0 ..] vars+           , (j, q) <- zip [0 ..] vars+           , i Prelude.> j+           ]+        ++ [ DataFrame.Functions.min p q+           | (i, p) <- zip [0 ..] vars+           , (j, q) <- zip [0 ..] vars+           , i Prelude.> j+           ]+        ++ [ DataFrame.Functions.max p q+           | (i, p) <- zip [0 ..] vars+           , (j, q) <- zip [0 ..] vars+           , i Prelude.>= j+           ]+        ++ [ p - q+           | (i, p) <- zip [0 ..] vars+           , (j, q) <- zip [0 ..] vars+           , i /= j+           ]+        ++ [ p * q+           | (i, p) <- zip [0 ..] vars+           , (j, q) <- zip [0 ..] vars+           , i Prelude.>= j+           ]+        ++ [ p / q+           | (i, p) <- zip [0 ..] vars+           , (j, q) <- zip [0 ..] vars+           , i /= j+           ]+generatePrograms vars ps =+    let+        existingPrograms = vars ++ ps+     in+        existingPrograms+            ++ [ transform p+               | p <- existingPrograms+               , transform <-+                    [ zScore+                    , sqrt+                    , abs+                    , log . (+ Lit 1)+                    , exp+                    , mean+                    , median+                    , stddev+                    , sin+                    , cos+                    , relu+                    , signum+                    ]+               ]+            ++ [ pow i p+               | p <- existingPrograms+               , i <- [2 .. 6]+               ]+            ++ [ p + q+               | (i, p) <- zip [0 ..] existingPrograms+               , (j, q) <- zip [0 ..] existingPrograms+               , i Prelude.>= j+               ]+            ++ [ DataFrame.Functions.min p q+               | (i, p) <- zip [0 ..] existingPrograms+               , (j, q) <- zip [0 ..] existingPrograms+               , i Prelude.> j+               ]+            ++ [ DataFrame.Functions.max p q+               | (i, p) <- zip [0 ..] existingPrograms+               , (j, q) <- zip [0 ..] existingPrograms+               , i Prelude.> j+               ]+            ++ [ ifThenElse (p DataFrame.Functions.>= percentile n p) p q+               | (i, p) <- zip [0 ..] existingPrograms+               , (j, q) <- zip [0 ..] existingPrograms+               , i /= j+               , n <- [1, 25, 50, 75, 99]+               ]+            ++ [ ifThenElse (p DataFrame.Functions.>= percentile n p) p q+               | p <- existingPrograms+               , q <- [lit 1, lit 0, lit (-1)]+               , n <- [1, 25, 50, 75, 99]+               ]+            ++ [ p - q+               | (i, p) <- zip [0 ..] existingPrograms+               , (j, q) <- zip [0 ..] existingPrograms+               , i /= j+               ]+            ++ [ p * q+               | (i, p) <- zip [0 ..] existingPrograms+               , (j, q) <- zip [0 ..] existingPrograms+               , i Prelude.>= j+               ]+            ++ [ p / q+               | (i, p) <- zip [0 ..] existingPrograms+               , (j, q) <- zip [0 ..] existingPrograms+               , i /= j+               ]  -- | Deduplicate programs pick the least smallest one by size.-deduplicate :: DataFrame-            -> [Expr Double]-            -> [Expr Double]+deduplicate ::+    DataFrame ->+    [Expr Double] ->+    [(Expr Double, TypedColumn Double)] deduplicate df = go S.empty . L.sortBy (\e1 e2 -> compare (eSize e1) (eSize e2))   where     go _ [] = []     go seen (x : xs)         | hasInvalid = go seen xs         | S.member res seen = go seen xs-        | otherwise = x : go (S.insert res seen) xs-            where-                res = interpret df x-                infinity = read @Double "Infinity"-                hasInvalid = case res of-                    (TColumn (UnboxedColumn (col :: VU.Vector a))) -> case testEquality (typeRep @Double) (typeRep @a) of-                                    Just Refl -> VU.any (\n -> isNaN n || isInfinite n) col-                                    Nothing -> False-                    _ -> False+        | otherwise = (x, res) : go (S.insert res seen) xs+      where+        res = interpret df x+        hasInvalid = case res of+            (TColumn (UnboxedColumn (col :: VU.Vector a))) -> case testEquality (typeRep @Double) (typeRep @a) of+                Just Refl -> VU.any (\n -> isNaN n || isInfinite n) col+                Nothing -> False+            _ -> False  -- | Checks if two programs generate the same outputs given all the same inputs. equivalent :: DataFrame -> Expr Double -> Expr Double -> Bool@@ -190,61 +313,140 @@     Int ->     DataFrame ->     Either String (Expr Double)-synthesizeFeatureExpr target d b df = let+synthesizeFeatureExpr target d b df =+    let         df' = exclude [target] df-        names = (map fst . L.sortBy (compare `on` snd) . M.toList . columnIndices) df'-        variables = map col names-    in case beamSearch df' b (interpret df (Col target)) variables d of+     in+        case beamSearch+            df'+            (BeamConfig d b (\l r -> (^ 2) <$> correlation' l r))+            (interpret df (Col target))+            [] of             Nothing -> Left "No programs found"             Just p -> Right p +fitRegression ::+    -- | Target expression+    T.Text ->+    -- | Depth of search (Roughly, how many terms in the final expression)+    Int ->+    -- | Beam size - the number of candidate expressions to consider at a time.+    Int ->+    DataFrame ->+    Either String (Expr Double)+fitRegression target d b df =+    let+        df' = exclude [target] df+        targetMean = fromMaybe 0 $ Stats.mean target df+     in+        case beamSearch+            df'+            ( BeamConfig+                d+                b+                ( \l r ->+                    mutualInformationBinned+                        (Prelude.max 10 (ceiling (sqrt (fromIntegral (VU.length l)))))+                        l+                        r+                )+            )+            (interpret df (Col target))+            [] of+            Nothing -> Left "No programs found"+            Just p ->+                trace (show p) $+                    let+                     in case beamSearch+                            ( D.derive "_generated_regression_feature_" p df+                                & select ["_generated_regression_feature_"]+                            )+                            (BeamConfig d b (\l r -> fmap negate (meanSquaredError l r)))+                            (interpret df (Col target))+                            [Col "_generated_regression_feature_", lit targetMean, lit 10] of+                            Nothing -> Left "Could not find coefficients"+                            Just p' -> Right (replaceExpr p (Col @Double "_generated_regression_feature_") p')++data BeamConfig = BeamConfig+    { searchDepth :: Int+    , beamLength :: Int+    , rankingFunction :: VU.Vector Double -> VU.Vector Double -> Maybe Double+    }+ beamSearch ::     DataFrame ->-    -- | Beam size-    Int ->+    -- | Parameters of the beam search.+    BeamConfig ->     -- | Examples     TypedColumn Double ->     -- | Programs     [Expr Double] ->-    -- | Search depth-    Int ->     Maybe (Expr Double)-beamSearch df b outputs programs d-    | d Prelude.== 0 = case ps of-        []    -> Nothing-        (x:_) -> Just x+beamSearch df cfg outputs programs+    | searchDepth cfg Prelude.== 0 = case ps of+        [] -> Nothing+        (x : _) -> Just x     | otherwise =-        case findFirst ps of-            Just p -> Just p-            Nothing -> beamSearch df b outputs (generatePrograms (map col names) ps) (d - 1)+        beamSearch+            df+            (cfg{searchDepth = searchDepth cfg - 1})+            outputs+            (generatePrograms (map col names) ps)   where-    ps = pickTopN df outputs b $ deduplicate df programs+    ps = pickTopN df outputs cfg $ deduplicate df programs     names = (map fst . L.sortBy (compare `on` snd) . M.toList . columnIndices) df-    findFirst [] = Nothing-    findFirst (p : ps')-        | satisfiesExamples df outputs p = Just p-        | otherwise = findFirst ps' -pickTopN :: DataFrame-         -> TypedColumn Double-         -> Int-         -> [Expr Double]-         -> [Expr Double]-pickTopN df (TColumn col) n ps = let+pickTopN ::+    DataFrame ->+    TypedColumn Double ->+    BeamConfig ->+    [(Expr Double, TypedColumn a)] ->+    [Expr Double]+pickTopN _ _ _ [] = []+pickTopN df (TColumn col) cfg ps =+    let         l = VU.convert (toVector @Double col)-        ordered = take n (L.sortBy (\e e' -> let-                c1 = abs <$> correlation' l (asDoubleVector e')-                c2 = abs <$> correlation' l (asDoubleVector e)-            in if maybe False isInfinite c1 || maybe False isInfinite c2 then LT else compare c1 c2) ps)-        asDoubleVector e = let+        ordered =+            Prelude.take+                (beamLength cfg)+                ( map fst $+                    L.sortBy+                        ( \(_, c2) (_, c1) ->+                            if maybe False isInfinite c1+                                || maybe False isInfinite c2+                                || maybe False isNaN c1+                                || maybe False isNaN c2+                                then LT+                                else compare c1 c2+                        )+                        (map (\(e, res) -> (e, rankingFunction cfg l (asDoubleVector res))) ps)+                )+        asDoubleVector c =+            let+                (TColumn col') = c+             in+                VU.convert (toVector @Double col')+        interpretDoubleVector e =+            let                 (TColumn col') = interpret df e-            in VU.convert (toVector @Double col')-    in ordered+             in+                VU.convert (toVector @Double col')+     in+        trace+            ( "Best loss: "+                ++ show+                    (rankingFunction cfg l <$> (interpretDoubleVector <$> (listToMaybe ordered)))+                ++ " "+                ++ (if null ordered then "empty" else show (listToMaybe ordered))+            )+            ordered  satisfiesExamples :: DataFrame -> TypedColumn Double -> Expr Double -> Bool-satisfiesExamples df col expr = let+satisfiesExamples df col expr =+    let         result = interpret df expr-    in result Prelude.== col+     in+        result Prelude.== col  -- See Section 2.4 of the Haskell Report https://www.haskell.org/definition/haskell2010.pdf isReservedId :: T.Text -> Bool
src/DataFrame/IO/CSV.hs view
@@ -37,6 +37,7 @@ import DataFrame.Internal.Column (Column (..), columnLength) import DataFrame.Internal.DataFrame (DataFrame (..)) import DataFrame.Internal.Parsing+import DataFrame.Operations.Typing import System.IO import Type.Reflection import Prelude hiding (concat, takeWhile)@@ -68,15 +69,28 @@     -- ^ Whether to partially parse values into `Maybe`/`Either`. (default: True)     , chunkSize :: Int     -- ^ Default chunk size (in bytes) for csv reader. (default: 512'000)+    , dateFormat :: String+    {- ^ Format of date fields as recognized by the Data.Time.Format module.++    __Examples:__++    @+    > parseTimeM True defaultTimeLocale "%Y/%-m/%-d" "2010/3/04" :: Maybe Day+    Just 2010-03-04+    > parseTimeM True defaultTimeLocale "%d/%-m/%-Y" "04/3/2010" :: Maybe Day+    Just 2010-03-04+    @+    -}     } -defaultOptions :: ReadOptions-defaultOptions =+defaultReadOptions :: ReadOptions+defaultReadOptions =     ReadOptions         { hasHeader = True         , inferTypes = True         , safeRead = True         , chunkSize = 512_000+        , dateFormat = "%Y-%m-%d"         }  newGrowingVector :: Int -> IO (GrowingVector a)@@ -108,7 +122,8 @@     VM.unsafeWrite vec' size val     writeIORef sizeRef $! size + 1 -appendGrowingUnboxedVector :: (VUM.Unbox a) => GrowingUnboxedVector a -> a -> IO ()+appendGrowingUnboxedVector ::+    (VUM.Unbox a) => GrowingUnboxedVector a -> a -> IO () appendGrowingUnboxedVector (GrowingUnboxedVector vecRef sizeRef capRef) !val = do     size <- readIORef sizeRef     cap <- readIORef capRef@@ -133,7 +148,8 @@     size <- readIORef sizeRef     V.freeze (VM.slice 0 size vec) -freezeGrowingUnboxedVector :: (VUM.Unbox a) => GrowingUnboxedVector a -> IO (VU.Vector a)+freezeGrowingUnboxedVector ::+    (VUM.Unbox a) => GrowingUnboxedVector a -> IO (VU.Vector a) freezeGrowingUnboxedVector (GrowingUnboxedVector vecRef sizeRef _) = do     vec <- readIORef vecRef     size <- readIORef sizeRef@@ -148,8 +164,19 @@ @ -} readCsv :: FilePath -> IO DataFrame-readCsv = readSeparated ',' defaultOptions+readCsv = readSeparated ',' defaultReadOptions +{- | Read CSV file from path and load it into a dataframe.++==== __Example__+@+ghci> D.readCsvWithOpts ".\/data\/taxi.csv" (D.defaultReadOptions { dateFormat = "%d/%-m/%-Y" })++@+-}+readCsvWithOpts :: ReadOptions -> FilePath -> IO DataFrame+readCsvWithOpts opts = readSeparated ',' opts+ {- | Read TSV (tab separated) file from path and load it into a dataframe.  ==== __Example__@@ -159,13 +186,13 @@ @ -} readTsv :: FilePath -> IO DataFrame-readTsv = readSeparated '\t' defaultOptions+readTsv = readSeparated '\t' defaultReadOptions  {- | Read text file with specified delimiter into a dataframe.  ==== __Example__ @-ghci> D.readSeparated ';' D.defaultOptions ".\/data\/taxi.txt"+ghci> D.readSeparated ';' D.defaultReadOptions ".\/data\/taxi.txt"  @ -}@@ -193,13 +220,16 @@     frozenCols <- V.fromList <$> mapM freezeGrowingColumn growingCols      let numRows = maybe 0 columnLength (frozenCols V.!? 0)-+        df =+            DataFrame+                { columns = frozenCols+                , columnIndices = M.fromList (zip columnNames [0 ..])+                , dataframeDimensions = (numRows, V.length frozenCols)+                }     return $-        DataFrame-            { columns = frozenCols-            , columnIndices = M.fromList (zip columnNames [0 ..])-            , dataframeDimensions = (numRows, V.length frozenCols)-            }+        if inferTypes opts+            then parseDefaults (safeRead opts) (dateFormat opts) df+            else df  initializeColumns :: [BS.ByteString] -> ReadOptions -> IO [GrowingColumn] initializeColumns row opts = mapM initColumn row@@ -360,7 +390,7 @@     DataFrame ->     IO () writeSeparated c filepath df = withFile filepath WriteMode $ \handle -> do-    let (rows, columns) = dataframeDimensions df+    let (rows, _) = dataframeDimensions df     let headers = map fst (L.sortBy (compare `on` snd) (M.toList (columnIndices df)))     TIO.hPutStrLn handle (T.intercalate ", " headers)     forM_ [0 .. (rows - 1)] $ \i -> do
src/DataFrame/IO/Parquet.hs view
@@ -52,7 +52,8 @@                     && elementType s == STRING                     && typeLength s > 0 =                     Just (typeLength s)-                | otherwise = findTypeLength ss targetPath (if numChildren s > 0 then depth + 1 else depth)+                | otherwise =+                    findTypeLength ss targetPath (if numChildren s > 0 then depth + 1 else depth)              pathToElement _ _ _ = [] @@ -73,7 +74,8 @@                         else colDataPageOffset             let colLength = columnTotalCompressedSize metadata -            let columnBytes = map (BSO.index contents . fromIntegral) [colStart .. (colStart + colLength - 1)]+            let columnBytes =+                    map (BSO.index contents . fromIntegral) [colStart .. (colStart + colLength - 1)]              pages <- readAllPages (columnCodec metadata) columnBytes @@ -82,12 +84,18 @@                         then getTypeLength colPath                         else Nothing -            let primaryEncoding = fromMaybe EPLAIN (fmap fst (L.uncons (columnEncodings metadata)))+            let primaryEncoding = maybe EPLAIN fst (L.uncons (columnEncodings metadata))              let schemaTail = drop 1 (schema fileMetadata)             let colPath = columnPathInSchema (columnMetaData colChunk)             let (maxDef, maxRep) = levelsForPath schemaTail colPath-            column <- processColumnPages (maxDef, maxRep) pages (columnType metadata) primaryEncoding maybeTypeLength+            column <-+                processColumnPages+                    (maxDef, maxRep)+                    pages+                    (columnType metadata)+                    primaryEncoding+                    maybeTypeLength              modifyIORef colMap (M.insert colName column) @@ -99,6 +107,7 @@      pure $ DI.fromNamedColumns orderedColumns +readMetadataFromPath :: FilePath -> IO FileMetadata readMetadataFromPath path = do     contents <- BSO.readFile path     let (size, magicString) = contents `seq` readMetadataSizeFromFooter contents@@ -109,7 +118,10 @@ readMetadataSizeFromFooter contents =     let         footerOffSet = BSO.length contents - 8-        sizeBytes = map (fromIntegral @Word8 @Int32 . BSO.index contents) [footerOffSet .. footerOffSet + 3]+        sizeBytes =+            map+                (fromIntegral @Word8 @Int32 . BSO.index contents)+                [footerOffSet .. footerOffSet + 3]         size = fromIntegral $ L.foldl' (.|.) 0 $ zipWith shift sizeBytes [0, 8, 16, 24]         magicStringBytes = map (BSO.index contents) [footerOffSet + 4 .. footerOffSet + 7]         magicString = BSO.pack magicStringBytes@@ -132,7 +144,13 @@                 childResults = extractLeafPaths children idx newPath              in childResults ++ extractLeafPaths remaining (idx + length childResults) path -processColumnPages :: (Int, Int) -> [Page] -> ParquetType -> ParquetEncoding -> Maybe Int32 -> IO DI.Column+processColumnPages ::+    (Int, Int) ->+    [Page] ->+    ParquetType ->+    ParquetEncoding ->+    Maybe Int32 ->+    IO DI.Column processColumnPages (maxDef, maxRep) pages pType _ maybeTypeLength = do     let dictPages = filter isDictionaryPage pages     let dataPages = filter isDataPage pages@@ -198,7 +216,13 @@                 let n = fromIntegral dataPageHeaderV2NumValues                 let bs0 = pageBytes page                 let (defLvls, _repLvls, afterLvls) =-                        readLevelsV2 n maxDef maxRep definitionLevelByteLength repetitionLevelByteLength bs0+                        readLevelsV2+                            n+                            maxDef+                            maxRep+                            definitionLevelByteLength+                            repetitionLevelByteLength+                            bs0                 let nPresent =                         if dataPageHeaderV2NumNulls > 0                             then fromIntegral (dataPageHeaderV2NumValues - dataPageHeaderV2NumNulls)@@ -240,7 +264,12 @@                     ERLE_DICTIONARY -> decodeDictV1 dictValsM maxDef defLvls nPresent afterLvls                     EPLAIN_DICTIONARY -> decodeDictV1 dictValsM maxDef defLvls nPresent afterLvls                     other -> error ("Unsupported v2 encoding: " ++ show other)-+            -- Cannot happen as these are filtered out by isDataPage above+            DictionaryPageHeader{} -> error "processColumnPages: impossible DictionaryPageHeader"+            INDEX_PAGE_HEADER -> error "processColumnPages: impossible INDEX_PAGE_HEADER"+            PAGE_TYPE_HEADER_UNKNOWN -> error "processColumnPages: impossible PAGE_TYPE_HEADER_UNKNOWN"     case cols of         [] -> pure $ DI.fromList ([] :: [Maybe Int])-        (c : cs) -> pure $ L.foldl' (\l r -> fromMaybe (error "concat failed") (DI.concatColumns l r)) c cs+        (c : cs) ->+            pure $+                L.foldl' (\l r -> fromMaybe (error "concat failed") (DI.concatColumns l r)) c cs
src/DataFrame/IO/Parquet/Binary.hs view
@@ -12,11 +12,19 @@  littleEndianWord32 :: [Word8] -> Word32 littleEndianWord32 bytes-    | length bytes >= 4 = foldr (.|.) 0 (zipWith (\b i -> fromIntegral b `shiftL` i) (take 4 bytes) [0, 8, 16, 24])+    | length bytes >= 4 =+        foldr+            (.|.)+            0+            (zipWith (\b i -> fromIntegral b `shiftL` i) (take 4 bytes) [0, 8, 16, 24])     | otherwise = littleEndianWord32 (take 4 $ bytes ++ repeat 0)  littleEndianWord64 :: [Word8] -> Word64-littleEndianWord64 bytes = foldr (.|.) 0 (zipWith (\b i -> fromIntegral b `shiftL` i) (take 8 bytes) [0, 8 ..])+littleEndianWord64 bytes =+    foldr+        (.|.)+        0+        (zipWith (\b i -> fromIntegral b `shiftL` i) (take 8 bytes) [0, 8 ..])  littleEndianInt32 :: [Word8] -> Int32 littleEndianInt32 = fromIntegral . littleEndianWord32@@ -30,10 +38,20 @@ readUVarInt :: [Word8] -> (Word64, [Word8]) readUVarInt xs = loop xs 0 0 0   where-    loop bs x _ 10 = (x, bs)-    loop (b : bs) x s i-        | b < 0x80 = (x .|. (fromIntegral b) `shiftL` s, bs)-        | otherwise = loop bs (x .|. fromIntegral ((b .&. 0x7f) `shiftL` s)) (s + 7) (i + 1)+    {-+    Each input byte contributes:+    - lower 7 payload bits+    - The high bit (0x80) is the continuation flag: 1 = more bytes follow, 0 = last byte+    Why the magic number 10: For a 64‑bit integer we need at most ceil(64 / 7) = 10 bytes+    -}+    loop :: [Word8] -> Word64 -> Int -> Int -> (Word64, [Word8])+    loop bs result _ 10 = (result, bs)+    loop (b : bs) result shift i+        | b < 0x80 = (result .|. (fromIntegral b `shiftL` shift), bs)+        | otherwise =+            let payloadBits = fromIntegral (b .&. 0x7f) :: Word64+             in loop bs (result .|. (payloadBits `shiftL` shift)) (shift + 7) (i + 1)+    loop [] _ _ _ = error "readUVarInt: not enough input bytes"  readVarIntFromBytes :: (Integral a) => [Word8] -> (a, [Word8]) readVarIntFromBytes bs = (fromIntegral n, rem)
src/DataFrame/IO/Parquet/Dictionary.hs view
@@ -44,21 +44,21 @@  readPageWord64 :: [Word8] -> [Double] readPageWord64 [] = []-readPageWord64 xs = castWord64ToDouble (littleEndianWord64 (take 8 xs)) : readPageWord64 (drop 8 xs)+readPageWord64 xs =+    castWord64ToDouble (littleEndianWord64 (take 8 xs)) : readPageWord64 (drop 8 xs)  readPageBytes :: [Word8] -> [T.Text] readPageBytes [] = [] readPageBytes xs =     let lenBytes = fromIntegral (littleEndianInt32 $ take 4 xs)         totalBytesRead = lenBytes + 4-     in T.pack (map (chr . fromIntegral) $ take lenBytes (drop 4 xs)) : readPageBytes (drop totalBytesRead xs)+     in T.pack (map (chr . fromIntegral) $ take lenBytes (drop 4 xs))+            : readPageBytes (drop totalBytesRead xs)  readPageBool :: [Word8] -> [Bool] readPageBool [] = [] readPageBool bs =-    let totalBits = length bs * 8-        bits = concatMap (\b -> map (\i -> (b `shiftR` i) .&. 1 == 1) [0 .. 7]) bs-     in bits+    concatMap (\b -> map (\i -> (b `shiftR` i) .&. 1 == 1) [0 .. 7]) bs  readPageInt64 :: [Word8] -> [Int64] readPageInt64 [] = []@@ -66,7 +66,8 @@  readPageFloat :: [Word8] -> [Float] readPageFloat [] = []-readPageFloat xs = castWord32ToFloat (littleEndianWord32 (take 4 xs)) : readPageFloat (drop 4 xs)+readPageFloat xs =+    castWord32ToFloat (littleEndianWord32 (take 4 xs)) : readPageFloat (drop 4 xs)  readNInt96Times :: Int -> [Word8] -> ([UTCTime], [Word8]) readNInt96Times 0 bs = ([], bs)@@ -80,7 +81,7 @@ readPageInt96Times :: [Word8] -> [UTCTime] readPageInt96Times [] = [] readPageInt96Times bs =-    let (times, rest) = readNInt96Times (length bs `div` 12) bs+    let (times, _) = readNInt96Times (length bs `div` 12) bs      in times  readPageFixedBytes :: [Word8] -> Int -> [T.Text]@@ -99,7 +100,10 @@              in do                     when (length idxs /= nPresent) $                         error $-                            "dict index count mismatch: got " ++ show (length idxs) ++ ", expected " ++ show nPresent+                            "dict index count mismatch: got "+                                ++ show (length idxs)+                                ++ ", expected "+                                ++ show nPresent                     case dictVals of                         DBool ds -> do                             let values = [ds !! i | i <- idxs]
src/DataFrame/IO/Parquet/Levels.hs view
@@ -36,7 +36,8 @@                      in (defVals, afterDefData)      in (map fromIntegral defLvlsU32, map fromIntegral repLvlsU32, afterDef) -readLevelsV2 :: Int -> Int -> Int -> Int32 -> Int32 -> [Word8] -> ([Int], [Int], [Word8])+readLevelsV2 ::+    Int -> Int -> Int -> Int32 -> Int32 -> [Word8] -> ([Int], [Int], [Word8]) readLevelsV2 n maxDef maxRep defLen repLen bs =     let (repBytes, afterRepBytes) = splitAt (fromIntegral repLen) bs         (defBytes, afterDefBytes) = splitAt (fromIntegral defLen) afterRepBytes
src/DataFrame/IO/Parquet/Page.hs view
@@ -7,7 +7,7 @@ import Data.Bits import qualified Data.ByteString as BSO import Data.Int-import Data.Maybe+import Data.Maybe (fromMaybe, listToMaybe) import Data.Word import DataFrame.IO.Parquet.Binary import DataFrame.IO.Parquet.Thrift@@ -43,7 +43,10 @@             Right res -> pure res         UNCOMPRESSED -> pure (BSO.pack compressed)         other -> error ("Unsupported compression type: " ++ show other)-    pure $ (Just $ Page hdr (BSO.unpack fullData), drop (fromIntegral $ compressedPageSize hdr) rem)+    pure+        ( Just $ Page hdr (BSO.unpack fullData)+        , drop (fromIntegral $ compressedPageSize hdr) rem+        )  readPageHeader :: PageHeader -> [Word8] -> Int16 -> (PageHeader, [Word8]) readPageHeader hdr [] _ = (hdr, [])@@ -63,7 +66,10 @@                     let                         (uncompressedPageSize, rem') = readInt32FromBytes rem                      in-                        readPageHeader (hdr{uncompressedPageSize = uncompressedPageSize}) rem' identifier+                        readPageHeader+                            (hdr{uncompressedPageSize = uncompressedPageSize})+                            rem'+                            identifier                 3 ->                     let                         (compressedPageSize, rem') = readInt32FromBytes rem@@ -92,8 +98,11 @@                         readPageHeader (hdr{pageTypeHeader = dataPageHeaderV2}) rem' identifier                 n -> error $ "Unknown page header field" ++ show n -readPageTypeHeader :: PageTypeHeader -> [Word8] -> Int16 -> (PageTypeHeader, [Word8])+readPageTypeHeader ::+    PageTypeHeader -> [Word8] -> Int16 -> (PageTypeHeader, [Word8]) readPageTypeHeader hdr [] _ = (hdr, [])+readPageTypeHeader INDEX_PAGE_HEADER _ _ = error "readPageTypeHeader: unsupported INDEX_PAGE_HEADER"+readPageTypeHeader PAGE_TYPE_HEADER_UNKNOWN _ _ = error "readPageTypeHeader: unsupported PAGE_TYPE_HEADER_UNKNOWN" readPageTypeHeader hdr@(DictionaryPageHeader{..}) xs lastFieldId =     let         fieldContents = readField' xs lastFieldId@@ -105,18 +114,28 @@                     let                         (numValues, rem') = readInt32FromBytes rem                      in-                        readPageTypeHeader (hdr{dictionaryPageHeaderNumValues = numValues}) rem' identifier+                        readPageTypeHeader+                            (hdr{dictionaryPageHeaderNumValues = numValues})+                            rem'+                            identifier                 2 ->                     let                         (enc, rem') = readInt32FromBytes rem                      in-                        readPageTypeHeader (hdr{dictionaryPageHeaderEncoding = parquetEncodingFromInt enc}) rem' identifier+                        readPageTypeHeader+                            (hdr{dictionaryPageHeaderEncoding = parquetEncodingFromInt enc})+                            rem'+                            identifier                 3 ->                     let-                        (isSorted : rem') = rem+                        isSorted = fromMaybe (error "readPageTypeHeader: not enough bytes") (listToMaybe rem)                      in-                        readPageTypeHeader (hdr{dictionaryPageIsSorted = isSorted == compactBooleanTrue}) rem' identifier-                n -> error $ show n+                        readPageTypeHeader+                            (hdr{dictionaryPageIsSorted = isSorted == compactBooleanTrue})+                            (drop 1 rem)+                            identifier+                n ->+                    error $ "readPageTypeHeader: unsupported identifier " ++ show n readPageTypeHeader hdr@(DataPageHeader{..}) xs lastFieldId =     let         fieldContents = readField' xs lastFieldId@@ -133,17 +152,26 @@                     let                         (enc, rem') = readInt32FromBytes rem                      in-                        readPageTypeHeader (hdr{dataPageHeaderEncoding = parquetEncodingFromInt enc}) rem' identifier+                        readPageTypeHeader+                            (hdr{dataPageHeaderEncoding = parquetEncodingFromInt enc})+                            rem'+                            identifier                 3 ->                     let                         (enc, rem') = readInt32FromBytes rem                      in-                        readPageTypeHeader (hdr{definitionLevelEncoding = parquetEncodingFromInt enc}) rem' identifier+                        readPageTypeHeader+                            (hdr{definitionLevelEncoding = parquetEncodingFromInt enc})+                            rem'+                            identifier                 4 ->                     let                         (enc, rem') = readInt32FromBytes rem                      in-                        readPageTypeHeader (hdr{repetitionLevelEncoding = parquetEncodingFromInt enc}) rem' identifier+                        readPageTypeHeader+                            (hdr{repetitionLevelEncoding = parquetEncodingFromInt enc})+                            rem'+                            identifier                 5 ->                     let                         (stats, rem') = readStatisticsFromBytes emptyColumnStatistics rem 0@@ -176,7 +204,10 @@                     let                         (enc, rem') = readInt32FromBytes rem                      in-                        readPageTypeHeader (hdr{dataPageHeaderV2Encoding = parquetEncodingFromInt enc}) rem' identifier+                        readPageTypeHeader+                            (hdr{dataPageHeaderV2Encoding = parquetEncodingFromInt enc})+                            rem'+                            identifier                 5 ->                     let                         (n, rem') = readInt32FromBytes rem@@ -189,27 +220,34 @@                         readPageTypeHeader (hdr{repetitionLevelByteLength = n}) rem' identifier                 7 ->                     let-                        isCompressed = fromMaybe True $ fmap ((== compactBooleanTrue) . (.&. 0x0f)) (safeHead xs)+                        (isCompressed, rem') = case rem of+                            b : bytes -> ((b .&. 0x0f) == compactBooleanTrue, bytes)+                            [] -> (True, [])                      in-                        readPageTypeHeader (hdr{dataPageHeaderV2IsCompressed = dataPageHeaderV2IsCompressed}) rem identifier+                        readPageTypeHeader+                            (hdr{dataPageHeaderV2IsCompressed = isCompressed})+                            rem'+                            identifier                 8 ->                     let                         (stats, rem') = readStatisticsFromBytes emptyColumnStatistics rem 0                      in-                        readPageTypeHeader (hdr{dataPageHeaderV2Statistics = stats}) rem' identifier+                        readPageTypeHeader+                            (hdr{dataPageHeaderV2Statistics = stats})+                            rem'+                            identifier                 n -> error $ show n -safeHead :: [a] -> Maybe a-safeHead [] = Nothing-safeHead (x : _) = Just x- readField' :: [Word8] -> Int16 -> Maybe ([Word8], TType, Int16) readField' [] _ = Nothing readField' (x : xs) lastFieldId     | x .&. 0x0f == 0 = Nothing     | otherwise =         let modifier = fromIntegral ((x .&. 0xf0) `shiftR` 4) :: Int16-            (identifier, rem) = if modifier == 0 then readIntFromBytes @Int16 xs else (lastFieldId + modifier, xs)+            (identifier, rem) =+                if modifier == 0+                    then readIntFromBytes @Int16 xs+                    else (lastFieldId + modifier, xs)             elemType = toTType (x .&. 0x0f)          in Just (rem, elemType, identifier) @@ -282,7 +320,8 @@         (xs, rest) = splitFixed (k - 1) len bs'      in (body : xs, rest) -readStatisticsFromBytes :: ColumnStatistics -> [Word8] -> Int16 -> (ColumnStatistics, [Word8])+readStatisticsFromBytes ::+    ColumnStatistics -> [Word8] -> Int16 -> (ColumnStatistics, [Word8]) readStatisticsFromBytes cs xs lastFieldId =     let         fieldContents = readField' xs lastFieldId@@ -321,13 +360,21 @@                      in                         readStatisticsFromBytes (cs{columnMinValue = minInBytes}) rem' identifier                 7 ->-                    let-                        (isMaxValueExact : rem') = rem-                     in-                        readStatisticsFromBytes (cs{isColumnMaxValueExact = isMaxValueExact == compactBooleanTrue}) rem' identifier+                    case rem of+                        [] ->+                            error "readStatisticsFromBytes: not enough bytes"+                        (isMaxValueExact : rem') ->+                            readStatisticsFromBytes+                                (cs{isColumnMaxValueExact = isMaxValueExact == compactBooleanTrue})+                                rem'+                                identifier                 8 ->-                    let-                        (isMinValueExact : rem') = rem-                     in-                        readStatisticsFromBytes (cs{isColumnMinValueExact = isMinValueExact == compactBooleanTrue}) rem' identifier+                    case rem of+                        [] ->+                            error "readStatisticsFromBytes: not enough bytes"+                        (isMinValueExact : rem') ->+                            readStatisticsFromBytes+                                (cs{isColumnMinValueExact = isMinValueExact == compactBooleanTrue})+                                rem'+                                identifier                 n -> error $ show n
src/DataFrame/IO/Parquet/Thrift.hs view
@@ -125,13 +125,43 @@     deriving (Show, Eq)  defaultSchemaElement :: SchemaElement-defaultSchemaElement = SchemaElement "" STOP 0 0 (-1) UNKNOWN_REPETITION_TYPE 0 0 0 LOGICAL_TYPE_UNKNOWN+defaultSchemaElement =+    SchemaElement+        ""+        STOP+        0+        0+        (-1)+        UNKNOWN_REPETITION_TYPE+        0+        0+        0+        LOGICAL_TYPE_UNKNOWN  emptyColumnMetadata :: ColumnMetaData-emptyColumnMetadata = ColumnMetaData PARQUET_TYPE_UNKNOWN [] [] COMPRESSION_CODEC_UNKNOWN 0 0 0 [] 0 0 0 emptyColumnStatistics [] 0 0 emptySizeStatistics emptyGeospatialStatistics+emptyColumnMetadata =+    ColumnMetaData+        PARQUET_TYPE_UNKNOWN+        []+        []+        COMPRESSION_CODEC_UNKNOWN+        0+        0+        0+        []+        0+        0+        0+        emptyColumnStatistics+        []+        0+        0+        emptySizeStatistics+        emptyGeospatialStatistics  emptyColumnChunk :: ColumnChunk-emptyColumnChunk = ColumnChunk "" 0 emptyColumnMetadata 0 0 0 0 COLUMN_CRYPTO_METADATA_UNKNOWN []+emptyColumnChunk =+    ColumnChunk "" 0 emptyColumnMetadata 0 0 0 0 COLUMN_CRYPTO_METADATA_UNKNOWN []  emptyKeyValue :: KeyValue emptyKeyValue = KeyValue{key = "", value = ""}@@ -139,7 +169,14 @@ emptyRowGroup :: RowGroup emptyRowGroup = RowGroup [] 0 0 [] 0 0 0 -compactBooleanTrue, compactI32, compactI64, compactDouble, compactBinary, compactList, compactStruct :: Word8+compactBooleanTrue+    , compactI32+    , compactI64+    , compactDouble+    , compactBinary+    , compactList+    , compactStruct ::+        Word8 compactBooleanTrue = 0x01 compactI32 = 0x05 compactI64 = 0x06@@ -162,7 +199,8 @@                 , (compactStruct, STRUCT)                 ] -readField :: BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO (Maybe (TType, Int16))+readField ::+    BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO (Maybe (TType, Int16)) readField buf pos lastFieldId fieldStack = do     t <- readAndAdvance pos buf     if t .&. 0x0f == 0@@ -194,10 +232,10 @@ skipFieldData :: TType -> BS.ByteString -> IORef Int -> IO () skipFieldData fieldType buf pos = case fieldType of     BOOL -> return ()-    I32 -> readIntFromBuffer @Int32 buf pos >> pure ()-    I64 -> readIntFromBuffer @Int64 buf pos >> pure ()-    DOUBLE -> readIntFromBuffer @Int64 buf pos >> pure ()-    STRING -> readByteString buf pos >> pure ()+    I32 -> void (readIntFromBuffer @Int32 buf pos)+    I64 -> void (readIntFromBuffer @Int64 buf pos)+    DOUBLE -> void (readIntFromBuffer @Int64 buf pos)+    STRING -> void (readByteString buf pos)     LIST -> skipList buf pos     STRUCT -> skipToStructEnd buf pos     _ -> return ()@@ -212,14 +250,23 @@ readMetadata :: BS.ByteString -> Int -> IO FileMetadata readMetadata contents size = do     let metadataStartPos = BS.length contents - footerSize - size-    let metadataBytes = BS.pack $ map (BS.index contents) [metadataStartPos .. (metadataStartPos + size - 1)]+    let metadataBytes =+            BS.pack $+                map (BS.index contents) [metadataStartPos .. (metadataStartPos + size - 1)]     let lastFieldId = 0     let fieldStack = []     bufferPos <- newIORef (0 :: Int)-    metadata <- readFileMetaData defaultMetadata metadataBytes bufferPos lastFieldId fieldStack+    metadata <-+        readFileMetaData defaultMetadata metadataBytes bufferPos lastFieldId fieldStack     return metadata -readFileMetaData :: FileMetadata -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO FileMetadata+readFileMetaData ::+    FileMetadata ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO FileMetadata readFileMetaData metadata metaDataBuf bufferPos lastFieldId fieldStack = do     fieldContents <- readField metaDataBuf bufferPos lastFieldId fieldStack     case fieldContents of@@ -227,20 +274,37 @@         Just (elemType, identifier) -> case identifier of             1 -> do                 version <- readIntFromBuffer @Int32 metaDataBuf bufferPos-                readFileMetaData (metadata{version = version}) metaDataBuf bufferPos identifier fieldStack+                readFileMetaData+                    (metadata{version = version})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             2 -> do                 sizeAndType <- readAndAdvance bufferPos metaDataBuf                 listSize <-                     if (sizeAndType `shiftR` 4) .&. 0x0f == 15                         then readVarIntFromBuffer @Int metaDataBuf bufferPos                         else return $ fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f)--                let elemType = toTType sizeAndType-                schemaElements <- replicateM listSize (readSchemaElement defaultSchemaElement metaDataBuf bufferPos 0 [])-                readFileMetaData (metadata{schema = schemaElements}) metaDataBuf bufferPos identifier fieldStack+                let _elemType = toTType sizeAndType+                schemaElements <-+                    replicateM+                        listSize+                        (readSchemaElement defaultSchemaElement metaDataBuf bufferPos 0 [])+                readFileMetaData+                    (metadata{schema = schemaElements})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             3 -> do                 numRows <- readIntFromBuffer @Int64 metaDataBuf bufferPos-                readFileMetaData (metadata{numRows = fromIntegral numRows}) metaDataBuf bufferPos identifier fieldStack+                readFileMetaData+                    (metadata{numRows = fromIntegral numRows})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             4 -> do                 sizeAndType <- readAndAdvance bufferPos metaDataBuf                 listSize <-@@ -248,9 +312,16 @@                         then readVarIntFromBuffer @Int metaDataBuf bufferPos                         else return $ fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) -                let elemType = toTType sizeAndType-                rowGroups <- replicateM listSize (readRowGroup emptyRowGroup metaDataBuf bufferPos 0 [])-                readFileMetaData (metadata{rowGroups = rowGroups}) metaDataBuf bufferPos identifier fieldStack+                -- TODO actually check elemType agrees (also for all the other underscored _elemType in this module)+                let _elemType = toTType sizeAndType+                rowGroups <-+                    replicateM listSize (readRowGroup emptyRowGroup metaDataBuf bufferPos 0 [])+                readFileMetaData+                    (metadata{rowGroups = rowGroups})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             5 -> do                 sizeAndType <- readAndAdvance bufferPos metaDataBuf                 listSize <-@@ -258,12 +329,23 @@                         then readVarIntFromBuffer @Int metaDataBuf bufferPos                         else return $ fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) -                let elemType = toTType sizeAndType-                keyValueMetadata <- replicateM listSize (readKeyValue emptyKeyValue metaDataBuf bufferPos 0 [])-                readFileMetaData (metadata{keyValueMetadata = keyValueMetadata}) metaDataBuf bufferPos identifier fieldStack+                let _elemType = toTType sizeAndType+                keyValueMetadata <-+                    replicateM listSize (readKeyValue emptyKeyValue metaDataBuf bufferPos 0 [])+                readFileMetaData+                    (metadata{keyValueMetadata = keyValueMetadata})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             6 -> do                 createdBy <- readString metaDataBuf bufferPos-                readFileMetaData (metadata{createdBy = Just createdBy}) metaDataBuf bufferPos identifier fieldStack+                readFileMetaData+                    (metadata{createdBy = Just createdBy})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             7 -> do                 sizeAndType <- readAndAdvance bufferPos metaDataBuf                 listSize <-@@ -271,18 +353,39 @@                         then readVarIntFromBuffer @Int metaDataBuf bufferPos                         else return $ fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) -                let elemType = toTType sizeAndType+                let _elemType = toTType sizeAndType                 columnOrders <- replicateM listSize (readColumnOrder metaDataBuf bufferPos 0 [])-                readFileMetaData (metadata{columnOrders = columnOrders}) metaDataBuf bufferPos identifier fieldStack+                readFileMetaData+                    (metadata{columnOrders = columnOrders})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             8 -> do                 encryptionAlgorithm <- readEncryptionAlgorithm metaDataBuf bufferPos 0 []-                readFileMetaData (metadata{encryptionAlgorithm = encryptionAlgorithm}) metaDataBuf bufferPos identifier fieldStack+                readFileMetaData+                    (metadata{encryptionAlgorithm = encryptionAlgorithm})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             9 -> do                 footerSigningKeyMetadata <- readByteString metaDataBuf bufferPos-                readFileMetaData (metadata{footerSigningKeyMetadata = footerSigningKeyMetadata}) metaDataBuf bufferPos identifier fieldStack+                readFileMetaData+                    (metadata{footerSigningKeyMetadata = footerSigningKeyMetadata})+                    metaDataBuf+                    bufferPos+                    identifier+                    fieldStack             n -> return $ error $ "UNIMPLEMENTED " ++ show n -readSchemaElement :: SchemaElement -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO SchemaElement+readSchemaElement ::+    SchemaElement ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO SchemaElement readSchemaElement schemaElement buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -291,43 +394,89 @@         Just (elemType, identifier) -> case identifier of             1 -> do                 schemaElemType <- toIntegralType <$> readInt32FromBuffer buf pos-                readSchemaElement (schemaElement{elementType = schemaElemType}) buf pos identifier fieldStack+                readSchemaElement+                    (schemaElement{elementType = schemaElemType})+                    buf+                    pos+                    identifier+                    fieldStack             2 -> do                 typeLength <- readInt32FromBuffer buf pos-                readSchemaElement (schemaElement{typeLength = typeLength}) buf pos identifier fieldStack+                readSchemaElement+                    (schemaElement{typeLength = typeLength})+                    buf+                    pos+                    identifier+                    fieldStack             3 -> do                 fieldRepetitionType <- readInt32FromBuffer buf pos-                readSchemaElement (schemaElement{repetitionType = repetitionTypeFromInt fieldRepetitionType}) buf pos identifier fieldStack+                readSchemaElement+                    (schemaElement{repetitionType = repetitionTypeFromInt fieldRepetitionType})+                    buf+                    pos+                    identifier+                    fieldStack             4 -> do                 nameSize <- readVarIntFromBuffer @Int buf pos                 if nameSize <= 0                     then readSchemaElement schemaElement buf pos identifier fieldStack                     else do                         contents <- replicateM nameSize (readAndAdvance pos buf)-                        readSchemaElement (schemaElement{elementName = T.pack (map (chr . fromIntegral) contents)}) buf pos identifier fieldStack+                        readSchemaElement+                            (schemaElement{elementName = T.pack (map (chr . fromIntegral) contents)})+                            buf+                            pos+                            identifier+                            fieldStack             5 -> do                 numChildren <- readInt32FromBuffer buf pos-                readSchemaElement (schemaElement{numChildren = numChildren}) buf pos identifier fieldStack+                readSchemaElement+                    (schemaElement{numChildren = numChildren})+                    buf+                    pos+                    identifier+                    fieldStack             6 -> do                 convertedType <- readInt32FromBuffer buf pos-                readSchemaElement (schemaElement{convertedType = convertedType}) buf pos identifier fieldStack+                readSchemaElement+                    (schemaElement{convertedType = convertedType})+                    buf+                    pos+                    identifier+                    fieldStack             7 -> do                 scale <- readInt32FromBuffer buf pos                 readSchemaElement (schemaElement{scale = scale}) buf pos identifier fieldStack             8 -> do                 precision <- readInt32FromBuffer buf pos-                readSchemaElement (schemaElement{precision = precision}) buf pos identifier fieldStack+                readSchemaElement+                    (schemaElement{precision = precision})+                    buf+                    pos+                    identifier+                    fieldStack             9 -> do                 fieldId <- readInt32FromBuffer buf pos-                readSchemaElement (schemaElement{fieldId = fieldId}) buf pos identifier fieldStack+                readSchemaElement+                    (schemaElement{fieldId = fieldId})+                    buf+                    pos+                    identifier+                    fieldStack             10 -> do                 logicalType <- readLogicalType buf pos 0 []-                readSchemaElement (schemaElement{logicalType = logicalType}) buf pos identifier fieldStack+                readSchemaElement+                    (schemaElement{logicalType = logicalType})+                    buf+                    pos+                    identifier+                    fieldStack             _ -> do                 skipFieldData elemType buf pos                 readSchemaElement schemaElement buf pos identifier fieldStack -readRowGroup :: RowGroup -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO RowGroup+readRowGroup ::+    RowGroup -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO RowGroup readRowGroup r buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -336,9 +485,9 @@             1 -> do                 sizeAndType <- readAndAdvance pos buf                 let sizeOnly = fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) :: Int-                -- type of the contents of the list.-                let elemType = toTType sizeAndType-                columnChunks <- replicateM sizeOnly (readColumnChunk emptyColumnChunk buf pos 0 [])+                let _elemType = toTType sizeAndType+                columnChunks <-+                    replicateM sizeOnly (readColumnChunk emptyColumnChunk buf pos 0 [])                 readRowGroup (r{rowGroupColumns = columnChunks}) buf pos identifier fieldStack             2 -> do                 totalBytes <- readIntFromBuffer @Int64 buf pos@@ -352,13 +501,19 @@                 readRowGroup (r{fileOffset = offset}) buf pos identifier fieldStack             6 -> do                 compressedSize <- readIntFromBuffer @Int64 buf pos-                readRowGroup (r{totalCompressedSize = compressedSize}) buf pos identifier fieldStack+                readRowGroup+                    (r{totalCompressedSize = compressedSize})+                    buf+                    pos+                    identifier+                    fieldStack             7 -> do                 ordinal <- readIntFromBuffer @Int16 buf pos                 readRowGroup (r{ordinal = ordinal}) buf pos identifier fieldStack             _ -> error $ "Unknown row group field: " ++ show identifier -readColumnChunk :: ColumnChunk -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ColumnChunk+readColumnChunk ::+    ColumnChunk -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ColumnChunk readColumnChunk c buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -366,29 +521,71 @@         Just (elemType, identifier) -> case identifier of             1 -> do                 stringSize <- readVarIntFromBuffer @Int buf pos-                contents <- map (chr . fromIntegral) <$> replicateM stringSize (readAndAdvance pos buf)-                readColumnChunk (c{columnChunkFilePath = contents}) buf pos identifier fieldStack+                contents <-+                    map (chr . fromIntegral) <$> replicateM stringSize (readAndAdvance pos buf)+                readColumnChunk+                    (c{columnChunkFilePath = contents})+                    buf+                    pos+                    identifier+                    fieldStack             2 -> do                 columnChunkMetadataFileOffset <- readIntFromBuffer @Int64 buf pos-                readColumnChunk (c{columnChunkMetadataFileOffset = columnChunkMetadataFileOffset}) buf pos identifier fieldStack+                readColumnChunk+                    (c{columnChunkMetadataFileOffset = columnChunkMetadataFileOffset})+                    buf+                    pos+                    identifier+                    fieldStack             3 -> do                 columnMetadata <- readColumnMetadata emptyColumnMetadata buf pos 0 []-                readColumnChunk (c{columnMetaData = columnMetadata}) buf pos identifier fieldStack+                readColumnChunk+                    (c{columnMetaData = columnMetadata})+                    buf+                    pos+                    identifier+                    fieldStack             4 -> do                 columnOffsetIndexOffset <- readIntFromBuffer @Int64 buf pos-                readColumnChunk (c{columnChunkOffsetIndexOffset = columnOffsetIndexOffset}) buf pos identifier fieldStack+                readColumnChunk+                    (c{columnChunkOffsetIndexOffset = columnOffsetIndexOffset})+                    buf+                    pos+                    identifier+                    fieldStack             5 -> do                 columnOffsetIndexLength <- readInt32FromBuffer buf pos-                readColumnChunk (c{columnChunkOffsetIndexLength = columnOffsetIndexLength}) buf pos identifier fieldStack+                readColumnChunk+                    (c{columnChunkOffsetIndexLength = columnOffsetIndexLength})+                    buf+                    pos+                    identifier+                    fieldStack             6 -> do                 columnChunkColumnIndexOffset <- readIntFromBuffer @Int64 buf pos-                readColumnChunk (c{columnChunkColumnIndexOffset = columnChunkColumnIndexOffset}) buf pos identifier fieldStack+                readColumnChunk+                    (c{columnChunkColumnIndexOffset = columnChunkColumnIndexOffset})+                    buf+                    pos+                    identifier+                    fieldStack             7 -> do                 columnChunkColumnIndexLength <- readInt32FromBuffer buf pos-                readColumnChunk (c{columnChunkColumnIndexLength = columnChunkColumnIndexLength}) buf pos identifier fieldStack+                readColumnChunk+                    (c{columnChunkColumnIndexLength = columnChunkColumnIndexLength})+                    buf+                    pos+                    identifier+                    fieldStack             _ -> return c -readColumnMetadata :: ColumnMetaData -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ColumnMetaData+readColumnMetadata ::+    ColumnMetaData ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO ColumnMetaData readColumnMetadata cm buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -400,15 +597,25 @@             2 -> do                 sizeAndType <- readAndAdvance pos buf                 let sizeOnly = fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) :: Int-                let elemType = toTType sizeAndType+                let _elemType = toTType sizeAndType                 encodings <- replicateM sizeOnly (readParquetEncoding buf pos 0 [])-                readColumnMetadata (cm{columnEncodings = encodings}) buf pos identifier fieldStack+                readColumnMetadata+                    (cm{columnEncodings = encodings})+                    buf+                    pos+                    identifier+                    fieldStack             3 -> do                 sizeAndType <- readAndAdvance pos buf                 let sizeOnly = fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) :: Int-                let elemType = toTType sizeAndType+                let _elemType = toTType sizeAndType                 paths <- replicateM sizeOnly (readString buf pos)-                readColumnMetadata (cm{columnPathInSchema = paths}) buf pos identifier fieldStack+                readColumnMetadata+                    (cm{columnPathInSchema = paths})+                    buf+                    pos+                    identifier+                    fieldStack             4 -> do                 cType <- compressionCodecFromInt <$> readInt32FromBuffer buf pos                 readColumnMetadata (cm{columnCodec = cType}) buf pos identifier []@@ -417,70 +624,140 @@                 readColumnMetadata (cm{columnNumValues = numValues}) buf pos identifier []             6 -> do                 columnTotalUncompressedSize <- readIntFromBuffer @Int64 buf pos-                readColumnMetadata (cm{columnTotalUncompressedSize = columnTotalUncompressedSize}) buf pos identifier []+                readColumnMetadata+                    (cm{columnTotalUncompressedSize = columnTotalUncompressedSize})+                    buf+                    pos+                    identifier+                    []             7 -> do                 columnTotalCompressedSize <- readIntFromBuffer @Int64 buf pos-                readColumnMetadata (cm{columnTotalCompressedSize = columnTotalCompressedSize}) buf pos identifier []+                readColumnMetadata+                    (cm{columnTotalCompressedSize = columnTotalCompressedSize})+                    buf+                    pos+                    identifier+                    []             8 -> do                 sizeAndType <- readAndAdvance pos buf                 let sizeOnly = fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) :: Int-                let elemType = toTType sizeAndType-                columnKeyValueMetadata <- replicateM sizeOnly (readKeyValue emptyKeyValue buf pos 0 [])-                readColumnMetadata (cm{columnKeyValueMetadata = columnKeyValueMetadata}) buf pos identifier fieldStack+                let _elemType = toTType sizeAndType+                columnKeyValueMetadata <-+                    replicateM sizeOnly (readKeyValue emptyKeyValue buf pos 0 [])+                readColumnMetadata+                    (cm{columnKeyValueMetadata = columnKeyValueMetadata})+                    buf+                    pos+                    identifier+                    fieldStack             9 -> do                 columnDataPageOffset <- readIntFromBuffer @Int64 buf pos-                readColumnMetadata (cm{columnDataPageOffset = columnDataPageOffset}) buf pos identifier []+                readColumnMetadata+                    (cm{columnDataPageOffset = columnDataPageOffset})+                    buf+                    pos+                    identifier+                    []             10 -> do                 columnIndexPageOffset <- readIntFromBuffer @Int64 buf pos-                readColumnMetadata (cm{columnIndexPageOffset = columnIndexPageOffset}) buf pos identifier []+                readColumnMetadata+                    (cm{columnIndexPageOffset = columnIndexPageOffset})+                    buf+                    pos+                    identifier+                    []             11 -> do                 columnDictionaryPageOffset <- readIntFromBuffer @Int64 buf pos-                readColumnMetadata (cm{columnDictionaryPageOffset = columnDictionaryPageOffset}) buf pos identifier []+                readColumnMetadata+                    (cm{columnDictionaryPageOffset = columnDictionaryPageOffset})+                    buf+                    pos+                    identifier+                    []             12 -> do                 stats <- readStatistics emptyColumnStatistics buf pos 0 []                 readColumnMetadata (cm{columnStatistics = stats}) buf pos identifier fieldStack             13 -> do                 sizeAndType <- readAndAdvance pos buf                 let sizeOnly = fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) :: Int-                let elemType = toTType sizeAndType-                pageEncodingStats <- replicateM sizeOnly (readPageEncodingStats emptyPageEncodingStats buf pos 0 [])-                readColumnMetadata (cm{columnEncodingStats = pageEncodingStats}) buf pos identifier fieldStack+                let _elemType = toTType sizeAndType+                pageEncodingStats <-+                    replicateM sizeOnly (readPageEncodingStats emptyPageEncodingStats buf pos 0 [])+                readColumnMetadata+                    (cm{columnEncodingStats = pageEncodingStats})+                    buf+                    pos+                    identifier+                    fieldStack             14 -> do                 bloomFilterOffset <- readIntFromBuffer @Int64 buf pos-                readColumnMetadata (cm{bloomFilterOffset = bloomFilterOffset}) buf pos identifier []+                readColumnMetadata+                    (cm{bloomFilterOffset = bloomFilterOffset})+                    buf+                    pos+                    identifier+                    []             15 -> do                 bloomFilterLength <- readInt32FromBuffer buf pos-                readColumnMetadata (cm{bloomFilterLength = bloomFilterLength}) buf pos identifier []+                readColumnMetadata+                    (cm{bloomFilterLength = bloomFilterLength})+                    buf+                    pos+                    identifier+                    []             16 -> do                 stats <- readSizeStatistics emptySizeStatistics buf pos 0 []-                readColumnMetadata (cm{columnSizeStatistics = stats}) buf pos identifier fieldStack+                readColumnMetadata+                    (cm{columnSizeStatistics = stats})+                    buf+                    pos+                    identifier+                    fieldStack             17 -> return $ error "UNIMPLEMENTED"             _ -> return cm -readEncryptionAlgorithm :: BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO EncryptionAlgorithm+readEncryptionAlgorithm ::+    BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO EncryptionAlgorithm readEncryptionAlgorithm buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of         Nothing -> return ENCRYPTION_ALGORITHM_UNKNOWN         Just (elemType, identifier) -> case identifier of             1 -> do-                readAesGcmV1 (AesGcmV1{aadPrefix = [], aadFileUnique = [], supplyAadPrefix = False}) buf pos 0 []+                readAesGcmV1+                    (AesGcmV1{aadPrefix = [], aadFileUnique = [], supplyAadPrefix = False})+                    buf+                    pos+                    0+                    []             2 -> do-                readAesGcmCtrV1 (AesGcmCtrV1{aadPrefix = [], aadFileUnique = [], supplyAadPrefix = False}) buf pos 0 []+                readAesGcmCtrV1+                    (AesGcmCtrV1{aadPrefix = [], aadFileUnique = [], supplyAadPrefix = False})+                    buf+                    pos+                    0+                    []             n -> return ENCRYPTION_ALGORITHM_UNKNOWN -readColumnOrder :: BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ColumnOrder+readColumnOrder ::+    BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ColumnOrder readColumnOrder buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of         Nothing -> return COLUMN_ORDER_UNKNOWN         Just (elemType, identifier) -> case identifier of             1 -> do-                _ <- replicateM_ 2 (readTypeOrder buf pos 0 [])+                replicateM_ 2 (readTypeOrder buf pos 0 [])                 return TYPE_ORDER             _ -> return COLUMN_ORDER_UNKNOWN -readAesGcmCtrV1 :: EncryptionAlgorithm -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO EncryptionAlgorithm+readAesGcmCtrV1 ::+    EncryptionAlgorithm ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO EncryptionAlgorithm readAesGcmCtrV1 v@(AesGcmCtrV1 aadPrefix aadFileUnique supplyAadPrefix) buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -491,13 +768,31 @@                 readAesGcmCtrV1 (v{aadPrefix = aadPrefix}) buf pos lastFieldId fieldStack             2 -> do                 aadFileUnique <- readByteString buf pos-                readAesGcmCtrV1 (v{aadFileUnique = aadFileUnique}) buf pos lastFieldId fieldStack+                readAesGcmCtrV1+                    (v{aadFileUnique = aadFileUnique})+                    buf+                    pos+                    lastFieldId+                    fieldStack             3 -> do                 supplyAadPrefix <- readAndAdvance pos buf-                readAesGcmCtrV1 (v{supplyAadPrefix = supplyAadPrefix == compactBooleanTrue}) buf pos lastFieldId fieldStack+                readAesGcmCtrV1+                    (v{supplyAadPrefix = supplyAadPrefix == compactBooleanTrue})+                    buf+                    pos+                    lastFieldId+                    fieldStack             _ -> return ENCRYPTION_ALGORITHM_UNKNOWN+readAesGcmCtrV1 _ _ _ _ _ =+    error "readAesGcmCtrV1 called with non AesGcmCtrV1" -readAesGcmV1 :: EncryptionAlgorithm -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO EncryptionAlgorithm+readAesGcmV1 ::+    EncryptionAlgorithm ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO EncryptionAlgorithm readAesGcmV1 v@(AesGcmV1 aadPrefix aadFileUnique supplyAadPrefix) buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -511,10 +806,18 @@                 readAesGcmV1 (v{aadFileUnique = aadFileUnique}) buf pos lastFieldId fieldStack             3 -> do                 supplyAadPrefix <- readAndAdvance pos buf-                readAesGcmV1 (v{supplyAadPrefix = supplyAadPrefix == compactBooleanTrue}) buf pos lastFieldId fieldStack+                readAesGcmV1+                    (v{supplyAadPrefix = supplyAadPrefix == compactBooleanTrue})+                    buf+                    pos+                    lastFieldId+                    fieldStack             _ -> return ENCRYPTION_ALGORITHM_UNKNOWN+readAesGcmV1 _ _ _ _ _ =+    error "readAesGcmV1 called with non AesGcmV1" -readTypeOrder :: BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ColumnOrder+readTypeOrder ::+    BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ColumnOrder readTypeOrder buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -524,7 +827,8 @@                 then return TYPE_ORDER                 else readTypeOrder buf pos identifier fieldStack -readKeyValue :: KeyValue -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO KeyValue+readKeyValue ::+    KeyValue -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO KeyValue readKeyValue kv buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -538,7 +842,13 @@                 readKeyValue (kv{value = v}) buf pos identifier fieldStack             _ -> return kv -readPageEncodingStats :: PageEncodingStats -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO PageEncodingStats+readPageEncodingStats ::+    PageEncodingStats ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO PageEncodingStats readPageEncodingStats pes buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -552,13 +862,25 @@                 readPageEncodingStats (pes{pageEncoding = pEnc}) buf pos identifier []             3 -> do                 encodedCount <- readInt32FromBuffer buf pos-                readPageEncodingStats (pes{pagesWithEncoding = encodedCount}) buf pos identifier []+                readPageEncodingStats+                    (pes{pagesWithEncoding = encodedCount})+                    buf+                    pos+                    identifier+                    []             _ -> pure pes -readParquetEncoding :: BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ParquetEncoding+readParquetEncoding ::+    BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ParquetEncoding readParquetEncoding buf pos lastFieldId fieldStack = parquetEncodingFromInt <$> readInt32FromBuffer buf pos -readStatistics :: ColumnStatistics -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO ColumnStatistics+readStatistics ::+    ColumnStatistics ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO ColumnStatistics readStatistics cs buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -575,7 +897,12 @@                 readStatistics (cs{columnNullCount = nullCount}) buf pos identifier fieldStack             4 -> do                 distinctCount <- readIntFromBuffer @Int64 buf pos-                readStatistics (cs{columnDistictCount = distinctCount}) buf pos identifier fieldStack+                readStatistics+                    (cs{columnDistictCount = distinctCount})+                    buf+                    pos+                    identifier+                    fieldStack             5 -> do                 maxInBytes <- readByteString buf pos                 readStatistics (cs{columnMaxValue = maxInBytes}) buf pos identifier fieldStack@@ -584,13 +911,29 @@                 readStatistics (cs{columnMinValue = minInBytes}) buf pos identifier fieldStack             7 -> do                 isMaxValueExact <- readAndAdvance pos buf-                readStatistics (cs{isColumnMaxValueExact = isMaxValueExact == compactBooleanTrue}) buf pos identifier fieldStack+                readStatistics+                    (cs{isColumnMaxValueExact = isMaxValueExact == compactBooleanTrue})+                    buf+                    pos+                    identifier+                    fieldStack             8 -> do                 isMinValueExact <- readAndAdvance pos buf-                readStatistics (cs{isColumnMinValueExact = isMinValueExact == compactBooleanTrue}) buf pos identifier fieldStack+                readStatistics+                    (cs{isColumnMinValueExact = isMinValueExact == compactBooleanTrue})+                    buf+                    pos+                    identifier+                    fieldStack             _ -> pure cs -readSizeStatistics :: SizeStatistics -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO SizeStatistics+readSizeStatistics ::+    SizeStatistics ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO SizeStatistics readSizeStatistics ss buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of@@ -598,19 +941,36 @@         Just (elemType, identifier) -> case identifier of             1 -> do                 unencodedByteArrayDataTypes <- readIntFromBuffer @Int64 buf pos-                readSizeStatistics (ss{unencodedByteArrayDataTypes = unencodedByteArrayDataTypes}) buf pos identifier fieldStack+                readSizeStatistics+                    (ss{unencodedByteArrayDataTypes = unencodedByteArrayDataTypes})+                    buf+                    pos+                    identifier+                    fieldStack             2 -> do                 sizeAndType <- readAndAdvance pos buf                 let sizeOnly = fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) :: Int-                let elemType = toTType sizeAndType-                repetitionLevelHistogram <- replicateM sizeOnly (readIntFromBuffer @Int64 buf pos)-                readSizeStatistics (ss{repetitionLevelHistogram = repetitionLevelHistogram}) buf pos identifier fieldStack+                let _elemType = toTType sizeAndType+                repetitionLevelHistogram <-+                    replicateM sizeOnly (readIntFromBuffer @Int64 buf pos)+                readSizeStatistics+                    (ss{repetitionLevelHistogram = repetitionLevelHistogram})+                    buf+                    pos+                    identifier+                    fieldStack             3 -> do                 sizeAndType <- readAndAdvance pos buf                 let sizeOnly = fromIntegral ((sizeAndType `shiftR` 4) .&. 0x0f) :: Int-                let elemType = toTType sizeAndType-                definitionLevelHistogram <- replicateM sizeOnly (readIntFromBuffer @Int64 buf pos)-                readSizeStatistics (ss{definitionLevelHistogram = definitionLevelHistogram}) buf pos identifier fieldStack+                let _elemType = toTType sizeAndType+                definitionLevelHistogram <-+                    replicateM sizeOnly (readIntFromBuffer @Int64 buf pos)+                readSizeStatistics+                    (ss{definitionLevelHistogram = definitionLevelHistogram})+                    buf+                    pos+                    identifier+                    fieldStack             _ -> pure ss  footerSize :: Int@@ -624,7 +984,8 @@     | n == 6 = STRING     | otherwise = STRING -readLogicalType :: BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType+readLogicalType ::+    BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType readLogicalType buf pos lastFieldId fieldStack = do     t <- readAndAdvance pos buf     if t .&. 0x0f == 0@@ -635,7 +996,7 @@                 if modifier == 0                     then readIntFromBuffer @Int16 buf pos                     else return (lastFieldId + modifier)-            let elemType = toTType (t .&. 0x0f)+            let _elemType = toTType (t .&. 0x0f)             case identifier of                 1 -> do                     replicateM_ 2 (readField buf pos 0 [])@@ -650,18 +1011,18 @@                     replicateM_ 2 (readField buf pos 0 [])                     return ENUM_TYPE                 5 -> do-                    readDecimalType (DecimalType{decimalTypeScale = 0, decimalTypePrecision = 0}) buf pos 0 []+                    readDecimalType 0 0 buf pos 0 []                 6 -> do                     replicateM_ 2 (readField buf pos 0 [])                     return DATE_TYPE                 7 -> do-                    readTimeType (TimeType{isAdjustedToUTC = False, unit = MILLISECONDS}) buf pos 0 []+                    readTimeType False MILLISECONDS buf pos 0 []                 8 -> do-                    readTimeType (TimestampType{isAdjustedToUTC = False, unit = MILLISECONDS}) buf pos 0 []+                    readTimestampType False MILLISECONDS buf pos 0 []                 -- Apparently reserved for interval types                 9 -> return LOGICAL_TYPE_UNKNOWN                 10 -> do-                    intType <- readIntType (IntType{intIsSigned = False, bitWidth = 0}) buf pos 0 []+                    intType <- readIntType 0 False buf pos 0 []                     _ <- readField buf pos 0 []                     pure intType                 11 -> do@@ -687,11 +1048,18 @@                     return GeographyType{crs = "", algorithm = SPHERICAL}                 _ -> return LOGICAL_TYPE_UNKNOWN -readIntType :: LogicalType -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType-readIntType v@(IntType bitWidth intIsSigned) buf pos lastFieldId fieldStack = do+readIntType ::+    Int8 ->+    Bool ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO LogicalType+readIntType bitWidth intIsSigned buf pos lastFieldId fieldStack = do     t <- readAndAdvance pos buf     if t .&. 0x0f == 0-        then return v+        then return (IntType bitWidth intIsSigned)         else do             let modifier = fromIntegral ((t .&. 0xf0) `shiftR` 4) :: Int16             identifier <-@@ -701,53 +1069,76 @@              case identifier of                 1 -> do-                    bitWidthValue <- readAndAdvance pos buf-                    readIntType (v{bitWidth = fromIntegral bitWidthValue}) buf pos identifier fieldStack+                    bitWidth' <- readAndAdvance pos buf+                    readIntType (fromIntegral bitWidth') intIsSigned buf pos identifier fieldStack                 2 -> do-                    let isSigned = (t .&. 0x0f) == compactBooleanTrue-                    readIntType (v{intIsSigned = isSigned}) buf pos identifier fieldStack+                    let intIsSigned' = (t .&. 0x0f) == compactBooleanTrue+                    readIntType bitWidth intIsSigned' buf pos identifier fieldStack                 _ -> error $ "UNKNOWN field ID for IntType: " ++ show identifier -readDecimalType :: LogicalType -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType-readDecimalType v@(DecimalType p s) buf pos lastFieldId fieldStack = do+readDecimalType ::+    Int32 ->+    Int32 ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO LogicalType+readDecimalType precision scale buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of-        Nothing -> return v+        Nothing -> return (DecimalType precision scale)         Just (elemType, identifier) -> case identifier of             1 -> do-                s' <- readInt32FromBuffer buf pos-                readDecimalType (v{decimalTypeScale = s'}) buf pos lastFieldId fieldStack+                scale' <- readInt32FromBuffer buf pos+                readDecimalType precision scale' buf pos lastFieldId fieldStack             2 -> do-                p' <- readInt32FromBuffer buf pos-                readDecimalType (v{decimalTypePrecision = p'}) buf pos lastFieldId fieldStack+                precision' <- readInt32FromBuffer buf pos+                readDecimalType precision' scale buf pos lastFieldId fieldStack             _ -> error $ "UNKNOWN field ID for DecimalType" ++ show identifier -readTimeType :: LogicalType -> BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO LogicalType-readTimeType v@(TimeType _ _) buf pos lastFieldId fieldStack = do+readTimeType ::+    Bool ->+    TimeUnit ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO LogicalType+readTimeType isAdjustedToUTC unit buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of-        Nothing -> return v+        Nothing -> return (TimeType isAdjustedToUTC unit)         Just (elemType, identifier) -> case identifier of             1 -> do                 -- TODO: Check for empty-                isAdjustedToUTC <- readAndAdvance pos buf-                readTimeType (v{isAdjustedToUTC = isAdjustedToUTC == compactBooleanTrue}) buf pos lastFieldId fieldStack+                isAdjustedToUTC' <- (== compactBooleanTrue) <$> readAndAdvance pos buf+                readTimeType isAdjustedToUTC' unit buf pos lastFieldId fieldStack             2 -> do-                u <- readUnit buf pos 0 []-                readTimeType (v{unit = u}) buf pos lastFieldId fieldStack+                unit' <- readUnit buf pos 0 []+                readTimeType isAdjustedToUTC unit' buf pos lastFieldId fieldStack             _ -> error $ "UNKNOWN field ID for TimeType" ++ show identifier-readTimeType v@(TimestampType _ _) buf pos lastFieldId fieldStack = do++readTimestampType ::+    Bool ->+    TimeUnit ->+    BS.ByteString ->+    IORef Int ->+    Int16 ->+    [Int16] ->+    IO LogicalType+readTimestampType isAdjustedToUTC unit buf pos lastFieldId fieldStack = do     fieldContents <- readField buf pos lastFieldId fieldStack     case fieldContents of-        Nothing -> return v+        Nothing -> return (TimestampType isAdjustedToUTC unit)         Just (elemType, identifier) -> case identifier of             1 -> do                 -- TODO: Check for empty-                isAdjustedToUTC <- readAndAdvance pos buf-                readTimeType (v{isAdjustedToUTC = isAdjustedToUTC == compactBooleanTrue}) buf pos lastFieldId fieldStack+                isAdjustedToUTC' <- (== compactBooleanTrue) <$> readAndAdvance pos buf+                readTimestampType isAdjustedToUTC' unit buf pos lastFieldId fieldStack             2 -> do-                u <- readUnit buf pos 0 []-                readTimeType (v{unit = u}) buf pos lastFieldId fieldStack+                unit' <- readUnit buf pos 0 []+                readTimestampType isAdjustedToUTC unit' buf pos lastFieldId fieldStack             _ -> error $ "UNKNOWN field ID for TimestampType" ++ show identifier  readUnit :: BS.ByteString -> IORef Int -> Int16 -> [Int16] -> IO TimeUnit
src/DataFrame/IO/Parquet/Time.hs view
@@ -25,9 +25,9 @@  julianDayToDay :: Integer -> Day julianDayToDay julianDay =-    let a = julianDay + 32044-        b = (4 * a + 3) `div` 146097-        c = a - (146097 * b) `div` 4+    let a = julianDay + 32_044+        b = (4 * a + 3) `div` 146_097+        c = a - (146_097 * b) `div` 4         d = (4 * c + 3) `div` 1461         e = c - (1461 * d) `div` 4         m = (5 * e + 2) `div` 153@@ -49,7 +49,13 @@ dayToJulianDay :: Day -> Integer dayToJulianDay day =     let (year, month, dayOfMonth) = toGregorian day-        a = fromIntegral $ (14 - (fromIntegral month)) `div` 12+        a = fromIntegral $ (14 - fromIntegral month) `div` 12         y = fromIntegral $ year + 4800 - a-        m = fromIntegral $ month + 12 * (fromIntegral a) - 3-     in fromIntegral dayOfMonth + (153 * m + 2) `div` 5 + 365 * y + y `div` 4 - y `div` 100 + y `div` 400 - 32045+        m = fromIntegral $ month + 12 * fromIntegral a - 3+     in fromIntegral dayOfMonth+            + (153 * m + 2) `div` 5+            + 365 * y+            + y `div` 4+            - y `div` 100+            + y `div` 400+            - 32_045
src/DataFrame/IO/Parquet/Types.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE OverloadedStrings #-}- module DataFrame.IO.Parquet.Types where  import Data.Int@@ -234,10 +232,26 @@     deriving (Show, Eq)  emptyDictionaryPageHeader = DictionaryPageHeader 0 PARQUET_ENCODING_UNKNOWN False-emptyDataPageHeader = DataPageHeader 0 PARQUET_ENCODING_UNKNOWN PARQUET_ENCODING_UNKNOWN PARQUET_ENCODING_UNKNOWN emptyColumnStatistics-emptyDataPageHeaderV2 = DataPageHeaderV2 0 0 0 PARQUET_ENCODING_UNKNOWN 0 0 {- default for v2 is compressed -} True emptyColumnStatistics+emptyDataPageHeader =+    DataPageHeader+        0+        PARQUET_ENCODING_UNKNOWN+        PARQUET_ENCODING_UNKNOWN+        PARQUET_ENCODING_UNKNOWN+        emptyColumnStatistics+emptyDataPageHeaderV2 =+    DataPageHeaderV2+        0+        0+        0+        PARQUET_ENCODING_UNKNOWN+        0+        0 {- default for v2 is compressed -}+        True+        emptyColumnStatistics -data RepetitionType = REQUIRED | OPTIONAL | REPEATED | UNKNOWN_REPETITION_TYPE deriving (Eq, Show)+data RepetitionType = REQUIRED | OPTIONAL | REPEATED | UNKNOWN_REPETITION_TYPE+    deriving (Eq, Show)  data LogicalType     = STRING_TYPE
src/DataFrame/Internal/Column.hs view
@@ -59,7 +59,7 @@     (==) (TColumn a) (TColumn b) = a == b  instance (Ord a) => Ord (TypedColumn a) where-    (compare) (TColumn a) (TColumn b) = compare a b+    compare (TColumn a) (TColumn b) = compare a b  -- | Gets the underlying value from a TypedColumn. unwrapTypedColumn :: TypedColumn a -> Column@@ -75,8 +75,23 @@ isNumeric (UnboxedColumn (vec :: VU.Vector a)) = case sNumeric @a of     STrue -> True     _ -> False+isNumeric (BoxedColumn (vec :: VB.Vector a)) = case testEquality (typeRep @a) (typeRep @Integer) of+    Nothing -> False+    Just Refl -> True isNumeric _ = False +-- | Checks if a column is of a given type values.+hasElemType :: forall a. (Columnable a) => Column -> Bool+hasElemType (BoxedColumn (column :: VB.Vector b)) = fromMaybe False $ do+    Refl <- testEquality (typeRep @a) (typeRep @b)+    pure True+hasElemType (UnboxedColumn (column :: VU.Vector b)) = fromMaybe False $ do+    Refl <- testEquality (typeRep @a) (typeRep @b)+    pure True+hasElemType (OptionalColumn (column :: VB.Vector b)) = fromMaybe False $ do+    Refl <- testEquality (typeRep @a) (typeRep @b)+    pure True+ -- | An internal/debugging function to get the column type of a column. columnVersionString :: Column -> String columnVersionString column = case column of@@ -94,7 +109,7 @@     OptionalColumn (column :: VB.Vector a) -> show (typeRep @a)  instance (Show a) => Show (TypedColumn a) where-    show :: Show a => TypedColumn a -> String+    show :: (Show a) => TypedColumn a -> String     show (TColumn col) = show col  instance Show Column where@@ -165,7 +180,7 @@     (Columnable a) =>     ColumnifyRep 'RBoxed a     where-    toColumnRep :: Columnable a => VB.Vector a -> Column+    toColumnRep :: (Columnable a) => VB.Vector a -> Column     toColumnRep = BoxedColumn  instance@@ -200,7 +215,8 @@ [1,2,3,4] @ -}-fromUnboxedVector :: forall a. (Columnable a, VU.Unbox a) => VU.Vector a -> Column+fromUnboxedVector ::+    forall a. (Columnable a, VU.Unbox a) => VU.Vector a -> Column fromUnboxedVector = UnboxedColumn  {- | O(n) Convert a list to a column. Automatically picks the best representation of a vector to store the underlying data in.@@ -321,17 +337,23 @@ sortedIndexes :: Bool -> Column -> VU.Vector Int sortedIndexes asc (BoxedColumn column) = runST $ do     withIndexes <- VG.thaw $ VG.indexed column-    VA.sortBy (\(a, b) (a', b') -> (if asc then compare else flip compare) b b') withIndexes+    VA.sortBy+        (\(a, b) (a', b') -> (if asc then compare else flip compare) b b')+        withIndexes     sorted <- VG.unsafeFreeze withIndexes     return $ VU.generate (VG.length column) (\i -> fst (sorted VG.! i)) sortedIndexes asc (UnboxedColumn column) = runST $ do     withIndexes <- VG.thaw $ VG.indexed column-    VA.sortBy (\(a, b) (a', b') -> (if asc then compare else flip compare) b b') withIndexes+    VA.sortBy+        (\(a, b) (a', b') -> (if asc then compare else flip compare) b b')+        withIndexes     sorted <- VG.unsafeFreeze withIndexes     return $ VU.generate (VG.length column) (\i -> fst (sorted VG.! i)) sortedIndexes asc (OptionalColumn column) = runST $ do     withIndexes <- VG.thaw $ VG.indexed column-    VA.sortBy (\(a, b) (a', b') -> (if asc then compare else flip compare) b b') withIndexes+    VA.sortBy+        (\(a, b) (a', b') -> (if asc then compare else flip compare) b b')+        withIndexes     sorted <- VG.unsafeFreeze withIndexes     return $ VU.generate (VG.length column) (\i -> fst (sorted VG.! i)) {-# INLINE sortedIndexes #-}@@ -359,7 +381,8 @@         | otherwise -> Nothing  -- | Filter column with index.-ifilterColumn :: forall a. (Columnable a) => (Int -> a -> Bool) -> Column -> Maybe Column+ifilterColumn ::+    forall a. (Columnable a) => (Int -> a -> Bool) -> Column -> Maybe Column ifilterColumn f c@(BoxedColumn (column :: VB.Vector b)) = do     Refl <- testEquality (typeRep @a) (typeRep @b)     return $ BoxedColumn $ VG.ifilter f column@@ -369,7 +392,9 @@ ifilterColumn _ _ = Nothing  -- | Fold (right) column with index.-ifoldrColumn :: forall a b. (Columnable a, Columnable b) => (Int -> a -> b -> b) -> b -> Column -> Maybe b+ifoldrColumn ::+    forall a b.+    (Columnable a, Columnable b) => (Int -> a -> b -> b) -> b -> Column -> Maybe b ifoldrColumn f acc c@(BoxedColumn (column :: VB.Vector d)) = do     Refl <- testEquality (typeRep @a) (typeRep @d)     return $ VG.ifoldr f acc column@@ -381,7 +406,9 @@     return $ VG.ifoldr f acc column  -- | Fold (left) column with index.-ifoldlColumn :: forall a b. (Columnable a, Columnable b) => (b -> Int -> a -> b) -> b -> Column -> Maybe b+ifoldlColumn ::+    forall a b.+    (Columnable a, Columnable b) => (b -> Int -> a -> b) -> b -> Column -> Maybe b ifoldlColumn f acc c@(BoxedColumn (column :: VB.Vector d)) = do     Refl <- testEquality (typeRep @a) (typeRep @d)     return $ VG.ifoldl' f acc column@@ -423,9 +450,19 @@ -- | An internal, column version of zip. zipColumns :: Column -> Column -> Column zipColumns (BoxedColumn column) (BoxedColumn other) = BoxedColumn (VG.zip column other)-zipColumns (BoxedColumn column) (UnboxedColumn other) = BoxedColumn (VB.generate (min (VG.length column) (VG.length other)) (\i -> (column VG.! i, other VG.! i)))+zipColumns (BoxedColumn column) (UnboxedColumn other) =+    BoxedColumn+        ( VB.generate+            (min (VG.length column) (VG.length other))+            (\i -> (column VG.! i, other VG.! i))+        ) zipColumns (BoxedColumn column) (OptionalColumn optcolumn) = BoxedColumn (VG.zip (VB.convert column) optcolumn)-zipColumns (UnboxedColumn column) (BoxedColumn other) = BoxedColumn (VB.generate (min (VG.length column) (VG.length other)) (\i -> (column VG.! i, other VG.! i)))+zipColumns (UnboxedColumn column) (BoxedColumn other) =+    BoxedColumn+        ( VB.generate+            (min (VG.length column) (VG.length other))+            (\i -> (column VG.! i, other VG.! i))+        ) zipColumns (UnboxedColumn column) (UnboxedColumn other) = UnboxedColumn (VG.zip column other) zipColumns (UnboxedColumn column) (OptionalColumn optcolumn) = BoxedColumn (VG.zip (VB.convert column) optcolumn) zipColumns (OptionalColumn optcolumn) (BoxedColumn column) = BoxedColumn (VG.zip optcolumn (VB.convert column))@@ -434,7 +471,10 @@ {-# INLINE zipColumns #-}  -- | An internal, column version of zipWith.-zipWithColumns :: forall a b c. (Columnable a, Columnable b, Columnable c) => (a -> b -> c) -> Column -> Column -> Maybe Column+zipWithColumns ::+    forall a b c.+    (Columnable a, Columnable b, Columnable c) =>+    (a -> b -> c) -> Column -> Column -> Maybe Column zipWithColumns f (UnboxedColumn (column :: VU.Vector d)) (UnboxedColumn (other :: VU.Vector e)) = case testEquality (typeRep @a) (typeRep @d) of     Just Refl -> case testEquality (typeRep @b) (typeRep @e) of         Just Refl -> pure $ case sUnbox @c of@@ -476,37 +516,73 @@ freezeColumn' :: [(Int, T.Text)] -> MutableColumn -> IO Column freezeColumn' nulls (MBoxedColumn col)     | null nulls = BoxedColumn <$> VB.unsafeFreeze col-    | all (isNullish . snd) nulls = OptionalColumn . VB.imap (\i v -> if i `elem` map fst nulls then Nothing else Just v) <$> VB.unsafeFreeze col-    | otherwise = BoxedColumn . VB.imap (\i v -> if i `elem` map fst nulls then Left (fromMaybe (error "") (lookup i nulls)) else Right v) <$> VB.unsafeFreeze col+    | all (isNullish . snd) nulls =+        OptionalColumn+            . VB.imap (\i v -> if i `elem` map fst nulls then Nothing else Just v)+            <$> VB.unsafeFreeze col+    | otherwise =+        BoxedColumn+            . VB.imap+                ( \i v ->+                    if i `elem` map fst nulls+                        then Left (fromMaybe (error "") (lookup i nulls))+                        else Right v+                )+            <$> VB.unsafeFreeze col freezeColumn' nulls (MUnboxedColumn col)     | null nulls = UnboxedColumn <$> VU.unsafeFreeze col-    | all (isNullish . snd) nulls = VU.unsafeFreeze col >>= \c -> return $ OptionalColumn $ VB.generate (VU.length c) (\i -> if i `elem` map fst nulls then Nothing else Just (c VU.! i))-    | otherwise = VU.unsafeFreeze col >>= \c -> return $ BoxedColumn $ VB.generate (VU.length c) (\i -> if i `elem` map fst nulls then Left (fromMaybe (error "") (lookup i nulls)) else Right (c VU.! i))+    | all (isNullish . snd) nulls =+        VU.unsafeFreeze col >>= \c ->+            return $+                OptionalColumn $+                    VB.generate+                        (VU.length c)+                        (\i -> if i `elem` map fst nulls then Nothing else Just (c VU.! i))+    | otherwise =+        VU.unsafeFreeze col >>= \c ->+            return $+                BoxedColumn $+                    VB.generate+                        (VU.length c)+                        ( \i ->+                            if i `elem` map fst nulls+                                then Left (fromMaybe (error "") (lookup i nulls))+                                else Right (c VU.! i)+                        ) {-# INLINE freezeColumn' #-}  -- | Fills the end of a column, up to n, with Nothing. Does nothing if column has length greater than n. expandColumn :: Int -> Column -> Column expandColumn n (OptionalColumn col) = OptionalColumn $ col <> VB.replicate (n - VG.length col) Nothing expandColumn n column@(BoxedColumn col)-    | n > VG.length col = OptionalColumn $ VB.map Just col <> VB.replicate (n - VG.length col) Nothing+    | n > VG.length col =+        OptionalColumn $ VB.map Just col <> VB.replicate (n - VG.length col) Nothing     | otherwise = column expandColumn n column@(UnboxedColumn col)-    | n > VG.length col = OptionalColumn $ VB.map Just (VU.convert col) <> VB.replicate (n - VG.length col) Nothing+    | n > VG.length col =+        OptionalColumn $+            VB.map Just (VU.convert col) <> VB.replicate (n - VG.length col) Nothing     | otherwise = column  -- | Fills the beginning of a column, up to n, with Nothing. Does nothing if column has length greater than n. leftExpandColumn :: Int -> Column -> Column leftExpandColumn n column@(OptionalColumn col)-    | n > VG.length col = OptionalColumn $ VG.replicate (n - VG.length col) Nothing <> col+    | n > VG.length col =+        OptionalColumn $ VG.replicate (n - VG.length col) Nothing <> col     | otherwise = column leftExpandColumn n column@(BoxedColumn col)-    | n > VG.length col = OptionalColumn $ VG.replicate (n - VG.length col) Nothing <> VG.map Just col+    | n > VG.length col =+        OptionalColumn $ VG.replicate (n - VG.length col) Nothing <> VG.map Just col     | otherwise = column leftExpandColumn n column@(UnboxedColumn col)-    | n > VG.length col = OptionalColumn $ VG.replicate (n - VG.length col) Nothing <> VG.map Just (VU.convert col)+    | n > VG.length col =+        OptionalColumn $+            VG.replicate (n - VG.length col) Nothing <> VG.map Just (VU.convert col)     | otherwise = column --- | Concatenates two columns.+{- | Concatenates two columns.+Returns Nothing if the columns are of different types.+-} concatColumns :: Column -> Column -> Maybe Column concatColumns (OptionalColumn left) (OptionalColumn right) = case testEquality (typeOf left) (typeOf right) of     Nothing -> Nothing@@ -519,6 +595,47 @@     Just Refl -> Just (UnboxedColumn $ left <> right) concatColumns _ _ = Nothing +{- | Concatenates two columns.++Works similar to 'concatColumns', but unlike that function, it will also combine columns of different types+by wrapping the values in an Either.++E.g. combining Column containing [1,2] with Column containing ["a","b"]+will result in a Column containing [Left 1, Left 2, Right "a", Right "b"].+-}+concatColumnsEither :: Column -> Column -> Column+concatColumnsEither (OptionalColumn left) (OptionalColumn right) = case testEquality (typeOf left) (typeOf right) of+    Nothing ->+        OptionalColumn $ fmap (fmap Left) left <> fmap (fmap Right) right+    Just Refl ->+        OptionalColumn $ left <> right+concatColumnsEither (BoxedColumn left) (BoxedColumn right) = case testEquality (typeOf left) (typeOf right) of+    Nothing ->+        BoxedColumn $ fmap Left left <> fmap Right right+    Just Refl ->+        BoxedColumn $ left <> right+concatColumnsEither (UnboxedColumn left) (UnboxedColumn right) = case testEquality (typeOf left) (typeOf right) of+    Nothing ->+        BoxedColumn $ fmap Left (VG.convert left) <> fmap Right (VG.convert right)+    Just Refl ->+        UnboxedColumn $ left <> right+concatColumnsEither (BoxedColumn left) (UnboxedColumn right) =+    BoxedColumn $ fmap Left left <> fmap Right (VG.convert right)+concatColumnsEither (UnboxedColumn left) (BoxedColumn right) =+    BoxedColumn $ fmap Left (VG.convert left) <> fmap Right right+concatColumnsEither (OptionalColumn left) (BoxedColumn right) =+    OptionalColumn $+        fmap (fmap Left) left <> fmap (Just . Right) (VG.convert right)+concatColumnsEither (BoxedColumn left) (OptionalColumn right) =+    OptionalColumn $+        fmap (Just . Left) (VG.convert left) <> fmap (fmap Right) right+concatColumnsEither (OptionalColumn left) (UnboxedColumn right) =+    OptionalColumn $+        fmap (fmap Left) left <> fmap (Just . Right) (VG.convert right)+concatColumnsEither (UnboxedColumn left) (OptionalColumn right) =+    OptionalColumn $+        fmap (Just . Left) (VG.convert left) <> fmap (fmap Right) right+ {- | O(n) Converts a column to a boxed vector. Throws an exception if the wrong type is specified.  __Examples:__@@ -554,7 +671,9 @@     Right val -> VB.toList val  -- | A safe version of toVector that returns an Either type.-toVectorSafe :: forall a v. (VG.Vector v a, Columnable a) => Column -> Either DataFrameException (v a)+toVectorSafe ::+    forall a v.+    (VG.Vector v a, Columnable a) => Column -> Either DataFrameException (v a) toVectorSafe column@(OptionalColumn (col :: VB.Vector b)) =     case testEquality (typeRep @a) (typeRep @b) of         Just Refl -> Right $ VG.convert col
src/DataFrame/Internal/DataFrame.hs view
@@ -18,8 +18,8 @@ import Data.Function (on) import Data.List (sortBy, transpose, (\\)) import Data.Type.Equality (TestEquality (testEquality), type (:~:) (Refl))-import DataFrame.Errors (DataFrameException(..)) import DataFrame.Display.Terminal.PrettyPrint+import DataFrame.Errors (DataFrameException (..)) import DataFrame.Internal.Column import Text.Printf import Type.Reflection (typeRep)@@ -32,6 +32,7 @@     , columnIndices :: M.Map T.Text Int     -- ^ Keeps the column names in the order they were inserted in.     , dataframeDimensions :: (Int, Int)+    -- ^ (rows, columns)     }  {- | A record that contains information about how and what@@ -50,7 +51,7 @@         printf             "{ keyColumns: %s groupedColumns: %s }"             (show cols)-            (show ((M.keys (columnIndices df)) \\ cols))+            (show (M.keys (columnIndices df) \\ cols))  instance Eq GroupedDataFrame where     (==) (Grouped df cols indices os) (Grouped df' cols' indices' os') = (df == df') && (cols == cols')@@ -59,7 +60,11 @@     (==) :: DataFrame -> DataFrame -> Bool     a == b =         M.keys (columnIndices a) == M.keys (columnIndices b)-            && foldr (\(name, index) acc -> acc && (columns a V.!? index == (columns b V.!? (columnIndices b M.! name)))) True (M.toList $ columnIndices a)+            && foldr+                ( \(name, index) acc -> acc && (columns a V.!? index == (columns b V.!? (columnIndices b M.! name)))+                )+                True+                (M.toList $ columnIndices a)  instance Show DataFrame where     show :: DataFrame -> String@@ -129,7 +134,14 @@     let         m = V.map (toVector @Double) (columns df)      in-        V.generate (fst (dataframeDimensions df)) (\i -> foldl (\acc j -> acc `VU.snoc` (realToFrac ((m V.! j) V.! i))) VU.empty [0 .. (V.length m - 1)])+        V.generate+            (fst (dataframeDimensions df))+            ( \i ->+                foldl+                    (\acc j -> acc `VU.snoc` realToFrac ((m V.! j) V.! i))+                    VU.empty+                    [0 .. (V.length m - 1)]+            )  {- | Get a specific column as a vector. 
src/DataFrame/Internal/Expression.hs view
@@ -25,16 +25,17 @@ import DataFrame.Internal.Column import DataFrame.Internal.DataFrame import DataFrame.Internal.Types-import Type.Reflection (typeRep, Typeable)+import Type.Reflection (Typeable, typeOf, typeRep)  data Expr a where     Col :: (Columnable a) => T.Text -> Expr a     Lit :: (Columnable a) => a -> Expr a-    If :: (Columnable a)-        => Expr Bool-        -> Expr a-        -> Expr a-        -> Expr a+    If ::+        (Columnable a) =>+        Expr Bool ->+        Expr a ->+        Expr a ->+        Expr a     UnaryOp ::         ( Columnable a         , Columnable b@@ -57,7 +58,8 @@         ( VG.Vector v b         , Typeable v         , Columnable a-        , Columnable b) =>+        , Columnable b+        ) =>         Expr b ->         T.Text -> -- Operation name         (v b -> a) ->@@ -103,29 +105,53 @@         (TColumn left) = interpret @a df l         (TColumn right) = interpret @a df r      in-        TColumn $ fromMaybe (error "zipWithColumns returned nothing") $ zipWithColumns (\(c :: Bool) (l' :: a, r' :: a)-> if c then l' else r') conditions (zipColumns left right)+        TColumn $+            fromMaybe (error "zipWithColumns returned nothing") $+                zipWithColumns+                    (\(c :: Bool) (l' :: a, r' :: a) -> if c then l' else r')+                    conditions+                    (zipColumns left right) interpret df (UnaryOp _ (f :: c -> d) value) =     let         (TColumn value') = interpret @c df value      in         -- TODO: Handle this gracefully.         TColumn $ fromMaybe (error "mapColumn returned nothing") (mapColumn f value')+interpret df (BinaryOp _ (f :: c -> d -> e) (Lit left) (Lit right)) =+    TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) (f left right) interpret df (BinaryOp _ (f :: c -> d -> e) (Lit left) right) =     let         (TColumn right') = interpret @d df right      in-        TColumn $ fromMaybe (error "mapColumn returned nothing") (mapColumn (f left) right')+        TColumn $+            fromMaybe (error "mapColumn returned nothing") (mapColumn (f left) right') interpret df (BinaryOp _ (f :: c -> d -> e) left (Lit right)) =     let         (TColumn left') = interpret @c df left      in-        TColumn $ fromMaybe (error "mapColumn returned nothing") (mapColumn (`f` right) left')+        TColumn $+            fromMaybe (error "mapColumn returned nothing") (mapColumn (`f` right) left') interpret df (BinaryOp _ (f :: c -> d -> e) left right) =     let         (TColumn left') = interpret @c df left         (TColumn right') = interpret @d df right      in-        TColumn $ fromMaybe (error "mapColumn returned nothing") (zipWithColumns f left' right')+        TColumn $+            fromMaybe (error "mapColumn returned nothing") (zipWithColumns f left' right')+interpret df (AggVector expr op (f :: v b -> c)) =+    let+        (TColumn column) = interpret @b df expr+     in+        case column of+            (BoxedColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of+                Just Refl -> interpret @c df (Lit (f col))+                Nothing -> error "Type mismatch"+            (OptionalColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of+                Just Refl -> interpret @c df (Lit (f col))+                Nothing -> error "Type mismatch"+            (UnboxedColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of+                Just Refl -> interpret @c df (Lit (f col))+                Nothing -> error "Type mismatch" interpret df (AggReduce expr op (f :: forall a. (Columnable a) => a -> a -> a)) =     let         (TColumn column) = interpret @a df expr@@ -143,276 +169,212 @@             (UnboxedColumn (v :: VU.Vector d)) -> case testEquality (typeRep @d) (typeRep @b) of                 Just Refl -> TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) (f v)                 Nothing -> error "Invalid operation"+            (BoxedColumn (v :: V.Vector d)) -> case testEquality (typeRep @d) (typeRep @Integer) of+                Just Refl ->+                    TColumn $+                        fromVector $+                            V.replicate+                                (fst $ dataframeDimensions df)+                                (f (VU.convert $ V.map fromInteger v))+                Nothing -> error "Invalid operation"             _ -> error "Invalid operation" interpret df (AggFold expr op start (f :: (a -> b -> a))) =     let         (TColumn column) = interpret @b df expr      in-        case headColumn @a column of+        case ifoldlColumn (\acc _ v -> f acc v) start column of             Nothing -> error "Invalid operation"-            Just h -> case ifoldlColumn (\acc _ v -> f acc v) h column of-                Nothing -> error "Invalid operation"-                Just value -> TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) value-interpret _ expr = error ("Invalid operation for dataframe: " ++ show expr)+            Just value -> TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) value -interpretAggregation :: forall a. (Columnable a) => GroupedDataFrame -> Expr a -> TypedColumn a-interpretAggregation gdf (Lit value) = TColumn $ fromVector $ V.replicate (VG.length (offsets gdf) - 1) value+interpretAggregation ::+    forall a.+    (Columnable a) => GroupedDataFrame -> Expr a -> Either Column (TypedColumn a)+interpretAggregation gdf (Lit value) =+    Right $ TColumn $ fromVector $ V.replicate (VG.length (offsets gdf) - 1) value interpretAggregation gdf@(Grouped df names indices os) (Col name) = case getColumn name df of     Nothing -> throw $ ColumnNotFoundException name "" (M.keys $ columnIndices df)-    Just col -> TColumn $ atIndicesStable (VG.map (indices `VG.unsafeIndex`) (VG.init os)) col+    Just (BoxedColumn col) ->+        Left $+            fromVector $+                V.generate+                    (VU.length os - 1)+                    ( \i ->+                        ( V.generate+                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))+                            ( \j ->+                                col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))+                            )+                        )+                    )+    Just (OptionalColumn col) ->+        Left $+            fromVector $+                V.generate+                    (VU.length os - 1)+                    ( \i ->+                        ( V.generate+                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))+                            ( \j ->+                                col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))+                            )+                        )+                    )+    Just (UnboxedColumn col) ->+        Left $+            fromVector $+                V.generate+                    (VU.length os - 1)+                    ( \i ->+                        ( VU.generate+                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))+                            ( \j ->+                                col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))+                            )+                        )+                    ) interpretAggregation gdf (UnaryOp _ (f :: c -> d) expr) =-    let-        (TColumn value) = interpretAggregation @c gdf expr-     in-        case mapColumn f value of+    case interpretAggregation @c gdf expr of+        Left unaggregated -> case unaggregated of+            BoxedColumn (col :: V.Vector b) -> case testEquality (typeRep @b) (typeRep @(V.Vector c)) of+                Just Refl -> Left $ fromVector $ V.map (V.map f) col+                Nothing -> case testEquality (typeRep @b) (typeRep @(VU.Vector c)) of+                    Nothing -> error "Type mismatch"+                    Just Refl -> case (sUnbox @c, sUnbox @a) of+                        (STrue, STrue) -> Left $ fromVector $ V.map (VU.map f) col+                        (_, _) -> error ""+            _ -> error "Very unlikely: unaggregated column is boxed"+        Right (TColumn aggregated) -> case mapColumn f aggregated of             Nothing -> error "Type error in interpretation"-            Just col -> TColumn col-interpretAggregation gdf (If cond l r) =-    let-        (TColumn conditions) = interpretAggregation @Bool gdf cond-        (TColumn left) = interpretAggregation @a gdf l-        (TColumn right) = interpretAggregation @a gdf r-     in-        TColumn $ fromMaybe (error "zipWithColumns returned nothing") $ zipWithColumns (\(c :: Bool) (l' :: a, r' :: a)-> if c then l' else r') conditions (zipColumns left right)+            Just col -> Right $ TColumn col interpretAggregation gdf (BinaryOp _ (f :: c -> d -> e) left right) =-    let-        (TColumn left') = interpretAggregation @c gdf left-        (TColumn right') = interpretAggregation @d gdf right-     in-        case zipWithColumns f left' right' of+    case (interpretAggregation @c gdf left, interpretAggregation @d gdf right) of+        (Right (TColumn left'), Right (TColumn right')) -> case zipWithColumns f left' right' of             Nothing -> error "Type error in binary operation"-            Just col -> TColumn col+            Just col -> Right $ TColumn col+        (Left left', Left right') -> case (left', right') of+            (BoxedColumn (l :: V.Vector m), BoxedColumn (r :: V.Vector n)) -> case testEquality (typeRep @m) (typeRep @(VU.Vector c)) of+                Just Refl -> case testEquality (typeRep @n) (typeRep @(VU.Vector d)) of+                    Just Refl -> case (sUnbox @c, sUnbox @d, sUnbox @e) of+                        (STrue, STrue, STrue) -> Left $ fromVector $ V.zipWith (\l' r' -> VU.zipWith f l' r') l r+                        (_, _, _) -> error "Weird error"+                    Nothing -> case testEquality (typeRep @n) (typeRep @(V.Vector d)) of+                        Just Refl -> case sUnbox @c of+                            STrue -> Left $ fromVector $ V.zipWith (\l' r' -> V.zipWith f (V.convert l') r') l r+                            SFalse -> error "Very unlikely: boxed value in unboxed column."+                        Nothing -> error "Not a vector of any type???"+                Nothing -> error "Not a vector of any type???"+            _ -> error "Very unlikely: aggregation shouldn't be in a boxed column"+        (_, _) -> error "Cannot apply binary operation to unaggregated and aggregated column"+interpretAggregation gdf (If cond l r) =+    case ( interpretAggregation @Bool gdf cond+         , interpretAggregation @a gdf l+         , interpretAggregation @a gdf r+         ) of+        (Right (TColumn conditions), Right (TColumn left), Right (TColumn right)) ->+            Right $+                TColumn $+                    fromMaybe (error "zipWithColumns returned nothing") $+                        zipWithColumns+                            (\(c :: Bool) (l' :: a, r' :: a) -> if c then l' else r')+                            conditions+                            (zipColumns left right)+        ( Left conditions+            , Left left@(BoxedColumn left')+            , Left right@(BoxedColumn right')+            ) ->+                Left $+                    fromMaybe (error "zipWithColumns returned nothing") $+                        zipWithColumns+                            ( \(c :: VU.Vector Bool) (l' :: V.Vector a, r' :: V.Vector a) ->+                                V.zipWith+                                    (\c' (l'', r'') -> if c' then l'' else r'')+                                    (V.convert c)+                                    (V.zip l' r')+                            )+                            conditions+                            (zipColumns left right)+        ( Left conditions+            , Left left@(UnboxedColumn left')+            , Left right@(UnboxedColumn right')+            ) -> case sUnbox @a of+                STrue ->+                    Left $+                        fromMaybe (error "zipWithColumns returned nothing") $+                            zipWithColumns+                                ( \(c :: VU.Vector Bool) (l' :: VU.Vector a, r' :: VU.Vector a) -> VU.zipWith (\c' (l'', r'') -> if c' then l'' else r'') c (VU.zip l' r')+                                )+                                conditions+                                (zipColumns left right)+                SFalse -> error "Very unlikely: Unboxed type in boxed column"+        (_, _, _) -> error "Some columns in the conditional are unaggregated" interpretAggregation gdf@(Grouped df names indices os) (AggVector expr op (f :: v b -> c)) =-    case unwrapTypedColumn (interpretAggregation @b gdf expr) of-        BoxedColumn (col :: V.Vector d) -> case testEquality (typeRep @b) (typeRep @d) of+    case interpretAggregation @b gdf expr of+        Left (BoxedColumn (col :: V.Vector d)) -> case testEquality (typeRep @(v b)) (typeRep @d) of             Nothing -> error "Type mismatch"             Just Refl -> case testEquality (typeRep @v) (typeRep @V.Vector) of                 Nothing -> error "Container mismatch"-                Just Refl -> TColumn $-                    fromVector $-                        V.generate-                            (VG.length os - 1)-                            ( \i ->-                                f-                                    ( V.generate-                                        (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                                        (\j -> col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i))))-                                    )-                            )-        UnboxedColumn (col :: VU.Vector d) -> case testEquality (typeRep @b) (typeRep @d) of-            Just Refl -> case testEquality (typeRep @v) (typeRep @VU.Vector) of-                Nothing -> error "Container mismatch"-                Just Refl ->-                    case sUnbox @c of-                        SFalse ->+                Just Refl -> Right $ TColumn $ fromVector $ V.map f col+        Left _ -> error "Very unlikely: aggregated into unboxed column???"+        Right (TColumn (BoxedColumn col)) -> case testEquality (typeRep @(v b)) (typeOf col) of+            Just Refl -> interpretAggregation @c gdf (Lit (f col))+            Nothing -> error "Type mismatch"+        Right (TColumn (OptionalColumn col)) -> case testEquality (typeRep @(v b)) (typeOf col) of+            Just Refl -> interpretAggregation @c gdf (Lit (f col))+            Nothing -> error "Type mismatch"+        Right (TColumn (UnboxedColumn col)) -> case testEquality (typeRep @(v b)) (typeOf col) of+            Just Refl -> interpretAggregation @c gdf (Lit (f col))+            Nothing -> error "Type mismatch"+interpretAggregation gdf@(Grouped df names indices os) (AggReduce expr op (f :: forall a. (Columnable a) => a -> a -> a)) =+    case interpretAggregation @a gdf expr of+        Left (BoxedColumn (col :: V.Vector d)) -> case testEquality (typeRep @(V.Vector a)) (typeRep @d) of+            Nothing -> case testEquality (typeRep @(VU.Vector a)) (typeRep @d) of+                Nothing -> error "Type mismatch"+                Just Refl -> case sUnbox @a of+                    STrue ->+                        Right $                             TColumn $                                 fromVector $-                                    V.generate-                                        (VG.length os - 1)-                                        ( \i ->-                                            f-                                                ( VU.generate-                                                    (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                                                    (\j -> col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i))))-                                                )-                                        )-                        STrue ->-                            TColumn $-                                fromUnboxedVector $-                                    VU.generate-                                        (VG.length os - 1)-                                        ( \i ->-                                            f-                                                ( VU.generate-                                                    (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                                                    (\j -> col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i))))-                                                )-                                        )-        OptionalColumn (col :: V.Vector d) -> case testEquality (typeRep @b) (typeRep @d) of-            Nothing -> error "Type mismatch"-            Just Refl -> case testEquality (typeRep @v) (typeRep @V.Vector) of-                Nothing -> error "Container mismatch"-                Just Refl -> TColumn $-                    fromVector $-                        V.generate-                            (VG.length os - 1)-                            ( \i ->-                                f-                                    ( V.generate-                                        (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                                        (\j -> col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i))))-                                    )-                            )-interpretAggregation gdf@(Grouped df names indices os) (AggReduce expr op (f :: forall a. (Columnable a) => a -> a -> a)) = case unwrapTypedColumn (interpretAggregation @a gdf expr) of-    BoxedColumn col -> TColumn $-        fromVector $-            VG.generate (VG.length os - 1) $ \g ->-                let !start = os `VG.unsafeIndex` g-                    !end = os `VG.unsafeIndex` (g + 1)-                 in go (col `VG.unsafeIndex` (indices `VG.unsafeIndex` start)) (start + 1) end-      where-        {-# INLINE go #-}-        go !acc j e-            | j == e = acc-            | otherwise =-                let !x = col `VG.unsafeIndex` (indices `VG.unsafeIndex` j)-                 in go (f acc x) (j + 1) e-    UnboxedColumn col -> case sUnbox @a of-        SFalse -> TColumn $-            fromVector $-                VG.generate (VG.length os - 1) $ \g ->-                    let !start = os `VG.unsafeIndex` g-                        !end = os `VG.unsafeIndex` (g + 1)-                     in go (col `VG.unsafeIndex` (indices `VG.unsafeIndex` start)) (start + 1) end-          where-            {-# INLINE go #-}-            go !acc j e-                | j == e = acc-                | otherwise =-                    let !x = col `VG.unsafeIndex` (indices `VG.unsafeIndex` j)-                     in go (f acc x) (j + 1) e-        STrue -> TColumn $-            fromUnboxedVector $-                VG.generate (VG.length os - 1) $ \g ->-                    let !start = os `VG.unsafeIndex` g-                        !end = os `VG.unsafeIndex` (g + 1)-                     in go (col `VG.unsafeIndex` (indices `VG.unsafeIndex` start)) (start + 1) end-          where-            {-# INLINE go #-}-            go !acc j e-                | j == e = acc-                | otherwise =-                    let !x = col `VG.unsafeIndex` (indices `VG.unsafeIndex` j)-                     in go (f acc x) (j + 1) e-    OptionalColumn col -> TColumn $-        fromVector $-            VG.generate (VG.length os - 1) $ \g ->-                let !start = os `VG.unsafeIndex` g-                    !end = os `VG.unsafeIndex` (g + 1)-                 in go (col `VG.unsafeIndex` (indices `VG.unsafeIndex` start)) (start + 1) end-      where-        {-# INLINE go #-}-        go !acc j e-            | j == e = acc-            | otherwise =-                let !x = col `VG.unsafeIndex` (indices `VG.unsafeIndex` j)-                 in go (f acc x) (j + 1) e-interpretAggregation gdf@(Grouped df names indices os) (AggFold expr op s (f :: (a -> b -> a))) = case unwrapTypedColumn (interpretAggregation @b gdf expr) of-    BoxedColumn (col :: V.Vector c) -> case testEquality (typeRep @b) (typeRep @c) of-        Nothing -> error "Type mismatch"-        Just Refl -> TColumn $-            fromVector $-                VG.generate (VG.length os - 1) $ \g ->-                    let !start = os `VG.unsafeIndex` g-                        !end = os `VG.unsafeIndex` (g + 1)-                     in go s start end-          where-            {-# INLINE go #-}-            go !acc j e-                | j == e = acc-                | otherwise =-                    let !x = col `VG.unsafeIndex` (indices `VG.unsafeIndex` j)-                     in go (f acc x) (j + 1) e-    UnboxedColumn (col :: VU.Vector c) -> case testEquality (typeRep @b) (typeRep @c) of-        Nothing -> error "Type mismatch"-        Just Refl -> case sUnbox @a of-            SFalse -> TColumn $-                fromVector $-                    VG.generate (VG.length os - 1) $ \g ->-                        let !start = os `VG.unsafeIndex` g-                            !end = os `VG.unsafeIndex` (g + 1)-                         in go s start end-              where-                {-# INLINE go #-}-                go !acc j e-                    | j == e = acc-                    | otherwise =-                        let !x = col `VG.unsafeIndex` (indices `VG.unsafeIndex` j)-                         in go (f acc x) (j + 1) e-            STrue -> TColumn $-                fromUnboxedVector $-                    VG.generate (VG.length os - 1) $ \g ->-                        let !start = os `VG.unsafeIndex` g-                            !end = os `VG.unsafeIndex` (g + 1)-                         in go s start end-          where-            {-# INLINE go #-}-            go !acc j e-                | j == e = acc-                | otherwise =-                    let !x = col `VG.unsafeIndex` (indices `VG.unsafeIndex` j)-                     in go (f acc x) (j + 1) e-    OptionalColumn (col :: V.Vector c) -> case testEquality (typeRep @b) (typeRep @c) of-        Nothing -> error "Type mismatch"-        Just Refl -> TColumn $-            fromVector $-                VG.generate (VG.length os - 1) $ \g ->-                    let !start = os `VG.unsafeIndex` g-                        !end = os `VG.unsafeIndex` (g + 1)-                     in go s start end-          where-            {-# INLINE go #-}-            go !acc j e-                | j == e = acc-                | otherwise =-                    let !x = col `VG.unsafeIndex` (indices `VG.unsafeIndex` j)-                     in go (f acc x) (j + 1) e-interpretAggregation gdf@(Grouped df names indices os) (AggNumericVector expr op (f :: VU.Vector b -> c)) = case unwrapTypedColumn (interpretAggregation @b gdf expr) of-    UnboxedColumn (col :: VU.Vector d) -> case testEquality (typeRep @b) (typeRep @d) of-        Nothing -> case testEquality (typeRep @d) (typeRep @Int) of-            Just Refl -> case sUnbox @c of-                SFalse ->-                    TColumn $-                        fromVector $-                            V.generate-                                (VG.length os - 1)-                                ( \i ->-                                    f-                                        ( VU.generate-                                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                                            (\j -> fromIntegral (col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))))-                                        )-                                )-                STrue ->-                    TColumn $-                        fromUnboxedVector $-                            VU.generate-                                (VG.length os - 1)-                                ( \i ->-                                    f-                                        ( VU.generate-                                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                                            (\j -> fromIntegral (col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))))-                                        )-                                )-        Just Refl -> case sNumeric @d of-            SFalse -> error $ "Cannot apply numeric aggregation to non-numeric column: " ++ show expr-            STrue -> case sUnbox @c of-                SFalse ->+                                    V.map (\v -> VU.foldl' f (VG.head v) (VG.drop 1 v)) col+                    SFalse -> error "Very unlikely case: unboxed vector contains a boxed type"+            Just Refl ->+                Right $                     TColumn $                         fromVector $-                            V.generate-                                (VG.length os - 1)-                                ( \i ->-                                    f-                                        ( VU.generate-                                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                                            (\j -> col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i))))-                                        )-                                )-                STrue ->-                    TColumn $-                        fromUnboxedVector $-                            VU.generate-                                (VG.length os - 1)-                                ( \i ->-                                    f-                                        ( VU.generate-                                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                                            (\j -> col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i))))-                                        )-                                )-    _ -> error $ "Cannot apply numeric aggregation to non-numeric: " ++ show expr+                            V.map (\v -> VG.foldl' f (VG.head v) (VG.drop 1 v)) col+        Left _ -> error "Impossible to aggregate into unaggregated column"+        Right (TColumn column) -> case headColumn @a column of+            Nothing -> error "Invalid operation"+            Just h -> case ifoldlColumn (\acc _ v -> f acc v) h column of+                Nothing -> error "Invalid operation"+                Just value -> interpretAggregation @a gdf (Lit value)+interpretAggregation gdf@(Grouped df names indices os) (AggFold expr op s (f :: (a -> b -> a))) =+    case interpretAggregation @b gdf expr of+        Left (BoxedColumn (col :: V.Vector d)) -> case testEquality (typeRep @(V.Vector b)) (typeRep @d) of+            Just Refl -> Right $ TColumn $ fromVector $ V.map (\v -> V.foldl' f s v) col+            Nothing -> case testEquality (typeRep @(VU.Vector b)) (typeRep @d) of+                Just Refl -> case sUnbox @b of+                    STrue -> Right $ TColumn $ fromVector $ V.map (\v -> VU.foldl' f s v) col+                    SFalse -> error "Very unlikely: boxed type in unboxed column"+                Nothing -> undefined+        Left _ -> error "Impossible to aggregate into unaggregated column"+        Right (TColumn column) -> case ifoldlColumn (\acc _ v -> f acc v) s column of+            Nothing -> error "Invalid operation"+            Just value -> interpretAggregation @a gdf (Lit value)+interpretAggregation gdf@(Grouped df names indices os) (AggNumericVector expr op (f :: VU.Vector b -> c)) =+    case interpretAggregation @b gdf expr of+        Left (BoxedColumn (col :: V.Vector d)) -> case testEquality (typeRep @(VU.Vector b)) (typeRep @d) of+            Nothing -> case testEquality (typeRep @(VU.Vector Int)) (typeRep @d) of+                Nothing -> error $ "Type mismatch - non numeric " ++ (show (typeRep @d))+                Just Refl -> Right $ TColumn $ fromVector $ V.map f (VG.map (VG.map fromIntegral) col)+            Just Refl -> Right $ TColumn $ fromVector $ V.map f col+        Left _ -> error "Impossible to aggregate into unaggregated column"+        Right (TColumn (UnboxedColumn col)) -> case testEquality (typeRep @(VU.Vector b)) (typeOf col) of+            Just Refl -> interpretAggregation @c gdf (Lit (f col))+            Nothing -> error "Type mismatch"+        Right (TColumn (BoxedColumn col)) -> case testEquality (typeRep @(V.Vector Integer)) (typeOf col) of+            Just Refl -> interpretAggregation @c gdf (Lit (f (VU.convert $ V.map fromInteger col)))+            Nothing -> error "Type mismatch"+        Right _ -> error "Cannot apply numeric aggregation to non-numeric column."  instance (Num a, Columnable a) => Num (Expr a) where     (+) :: Expr a -> Expr a -> Expr a@@ -483,22 +445,48 @@ instance (Show a) => Show (Expr a) where     show :: forall a. (Show a) => Expr a -> String     show (Col name) = "(col @" ++ show (typeRep @a) ++ " " ++ show name ++ ")"-    show (Lit value) = show value+    show (Lit value) = "(lit (" ++ show value ++ "))"     show (If cond l r) = "(ifThenElse " ++ show cond ++ " " ++ show l ++ " " ++ show r ++ ")"     show (UnaryOp name f value) = "(" ++ T.unpack name ++ " " ++ show value ++ ")"     show (BinaryOp name f a b) = "(" ++ T.unpack name ++ " " ++ show a ++ " " ++ show b ++ ")"     show (AggNumericVector expr op _) = "(" ++ T.unpack op ++ " " ++ show expr ++ ")"     show (AggVector expr op _) = "(" ++ T.unpack op ++ " " ++ show expr ++ ")"     show (AggReduce expr op _) = "(" ++ T.unpack op ++ " " ++ show expr ++ ")"-    show (AggFold expr op _ _ )   = "(" ++ T.unpack op ++ " " ++ show expr ++ ")"+    show (AggFold expr op _ _) = "(" ++ T.unpack op ++ " " ++ show expr ++ ")" +instance (Eq a, Show a) => Eq (Expr a) where+    (==) :: (Eq a, Show a) => Expr a -> Expr a -> Bool+    (==) l r = show l == show r++replaceExpr ::+    forall a b c.+    (Columnable a, Columnable b, Columnable c) =>+    Expr a -> Expr b -> Expr c -> Expr c+replaceExpr new old expr = case testEquality (typeRep @b) (typeRep @c) of+    Just Refl -> case testEquality (typeRep @a) (typeRep @c) of+        Just Refl -> if old == expr then new else replace'+        Nothing -> expr+    Nothing -> replace'+  where+    replace' = case expr of+        (Col _) -> expr+        (Lit _) -> expr+        (If cond l r) ->+            If (replaceExpr new old cond) (replaceExpr new old l) (replaceExpr new old r)+        (UnaryOp name f value) -> UnaryOp name f (replaceExpr new old value)+        (BinaryOp name f l r) -> BinaryOp name f (replaceExpr new old l) (replaceExpr new old r)+        (AggNumericVector expr op f) -> AggNumericVector (replaceExpr new old expr) op f+        (AggVector expr op f) -> AggVector (replaceExpr new old expr) op f+        (AggReduce expr op f) -> AggReduce (replaceExpr new old expr) op f+        (AggFold expr op acc f) -> AggFold (replaceExpr new old expr) op acc f+ eSize :: Expr a -> Int-eSize (Col _)  = 1-eSize (Lit _)  = 1+eSize (Col _) = 1+eSize (Lit _) = 1 eSize (If c l r) = 1 + eSize c + eSize l + eSize r eSize (UnaryOp _ _ e) = 1 + eSize e eSize (BinaryOp _ _ l r) = 1 + eSize l + eSize r eSize (AggNumericVector expr op _) = eSize expr + 1 eSize (AggVector expr op _) = eSize expr + 1 eSize (AggReduce expr op _) = eSize expr + 1-eSize (AggFold expr op _ _ )   = eSize expr + 1+eSize (AggFold expr op _ _) = eSize expr + 1
src/DataFrame/Internal/Parsing.hs view
@@ -13,7 +13,7 @@ import Text.Read (readMaybe)  isNullish :: T.Text -> Bool-isNullish s = s `S.member` S.fromList ["Nothing", "NULL", "", " ", "nan"]+isNullish s = s `S.member` S.fromList ["Nothing", "NULL", "", " ", "nan", "null"]  readValue :: (HasCallStack, Read a) => T.Text -> a readValue s = case readMaybe (T.unpack s) of
src/DataFrame/Internal/Row.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE OverloadedStrings #-}@@ -26,13 +27,19 @@ import DataFrame.Errors (DataFrameException (..)) import DataFrame.Internal.Column import DataFrame.Internal.DataFrame-import DataFrame.Internal.Types import Text.ParserCombinators.ReadPrec (ReadPrec)-import Text.Read (Lexeme (Ident), lexP, parens, readListPrec, readListPrecDefault, readPrec)+import Text.Read (+    Lexeme (Ident),+    lexP,+    parens,+    readListPrec,+    readListPrecDefault,+    readPrec,+ ) import Type.Reflection (typeOf, typeRep)  data Any where-    Value :: (Columnable' a) => a -> Any+    Value :: (Columnable a) => a -> Any  instance Eq Any where     (==) :: Any -> Any -> Bool@@ -50,7 +57,7 @@     show :: Any -> String     show (Value a) = T.unpack (showValue a) -showValue :: forall a. (Columnable' a) => a -> T.Text+showValue :: forall a. (Columnable a) => a -> T.Text showValue v = case testEquality (typeRep @a) (typeRep @T.Text) of     Just Refl -> v     Nothing -> case testEquality (typeRep @a) (typeRep @String) of@@ -66,15 +73,41 @@         Ident "Value" <- lexP         readPrec -toAny :: forall a. (Columnable' a) => a -> Any+-- | Wraps a value into an \Any\ type. This helps up represent rows as heterogenous lists.+toAny :: forall a. (Columnable a) => a -> Any toAny = Value +-- | Unwraps a value from an \Any\ type.+fromAny :: forall a. (Columnable a) => Any -> Maybe a+fromAny (Value (v :: b)) = do+    Refl <- testEquality (typeRep @a) (typeRep @b)+    pure v+ type Row = V.Vector Any -toRowList :: [T.Text] -> DataFrame -> [Row]-toRowList names df =+(!?) :: [a] -> Int -> Maybe a+(!?) [] _ = Nothing+(!?) (x : _) 0 = Just x+(!?) (x : xs) n = (!?) xs (n - 1)++mkColumnFromRow :: Int -> [[Any]] -> Column+mkColumnFromRow i rows = case rows of+    [] -> fromList ([] :: [T.Text])+    (row : _) -> case row !? i of+        Nothing -> fromList ([] :: [T.Text])+        Just (Value (v :: a)) -> fromList $ reverse $ L.foldl' addToList [v] (drop 1 rows)+          where+            addToList acc r = case r !? i of+                Nothing -> acc+                Just (Value (v' :: b)) -> case testEquality (typeRep @a) (typeRep @b) of+                    Nothing -> acc+                    Just Refl -> v' : acc++toRowList :: DataFrame -> [Row]+toRowList df =     let-        nameSet = S.fromList names+        nameSet =+            S.fromList (map fst (L.sortBy (compare `on` snd) $ M.toList (columnIndices df)))      in         map (mkRowRep df nameSet) [0 .. (fst (dataframeDimensions df) - 1)] @@ -89,7 +122,12 @@ mkRowFromArgs names df i = V.map get (V.fromList names)   where     get name = case getColumn name df of-        Nothing -> throw $ ColumnNotFoundException name "[INTERNAL] mkRowFromArgs" (M.keys $ columnIndices df)+        Nothing ->+            throw $+                ColumnNotFoundException+                    name+                    "[INTERNAL] mkRowFromArgs"+                    (M.keys $ columnIndices df)         Just (BoxedColumn column) -> toAny (column V.! i)         Just (UnboxedColumn column) -> toAny (column VU.! i)         Just (OptionalColumn column) -> toAny (column V.! i)@@ -116,6 +154,8 @@         Just (UnboxedColumn c) -> case c VU.!? i of             Just e -> toAny e             Nothing -> throwError name+        Nothing ->+            throw $ ColumnNotFoundException name "mkRowRep" (M.keys $ columnIndices df)  sortedIndexes' :: Bool -> V.Vector Row -> VU.Vector Int sortedIndexes' asc rows = runST $ do
src/DataFrame/Internal/Schema.hs view
@@ -29,7 +29,7 @@ schemaType :: forall a. (Columnable a) => SchemaType schemaType = SType (P.Proxy @a) -data Schema = Schema+newtype Schema = Schema     { elements :: M.Map T.Text SchemaType     }     deriving (Show, Eq)
src/DataFrame/Internal/Statistics.hs view
@@ -34,7 +34,7 @@ -- accumulator: count, mean, m2 data VarAcc = VarAcc !Int !Double !Double deriving (Show) -varianceStep :: Real a => VarAcc -> a -> VarAcc+varianceStep :: (Real a) => VarAcc -> a -> VarAcc varianceStep (VarAcc !n !mean !m2) !x =     let !n' = n + 1         !delta = realToFrac x - mean@@ -111,18 +111,20 @@         let !n = VU.length samp         mutableSamp <- VU.thaw samp         VA.sort mutableSamp-        VU.mapM (\i -> do-            let !p = fromIntegral i / fromIntegral q-                !position = p * fromIntegral (n - 1)-                !index = floor position-                !f = position - fromIntegral index-            x <- VUM.read mutableSamp index-            if f == 0-                then return x-                else do-                    y <- VUM.read mutableSamp (index + 1)-                    return $ (1 - f) * x + f * y-            ) qs+        VU.mapM+            ( \i -> do+                let !p = fromIntegral i / fromIntegral q+                    !position = p * fromIntegral (n - 1)+                    !index = floor position+                    !f = position - fromIntegral index+                x <- VUM.read mutableSamp index+                if f == 0+                    then return x+                    else do+                        y <- VUM.read mutableSamp (index + 1)+                        return $ (1 - f) * x + f * y+            )+            qs {-# INLINE quantiles' #-}  interQuartileRange' :: VU.Vector Double -> Double@@ -130,3 +132,75 @@     let quartiles = quantiles' (VU.fromList [1, 3]) 4 samp      in quartiles VU.! 1 - quartiles VU.! 0 {-# INLINE interQuartileRange' #-}++meanSquaredError :: VU.Vector Double -> VU.Vector Double -> Maybe Double+meanSquaredError target prediction =+    let+        squareDiff = VU.ifoldl' (\sq i e -> (e - target VU.! i) ^ 2 + sq) 0 prediction+     in+        Just $ squareDiff / fromIntegral (max (VU.length target) (VU.length prediction))+{-# INLINE meanSquaredError #-}++mutualInformationBinned ::+    Int -> VU.Vector Double -> VU.Vector Double -> Maybe Double+mutualInformationBinned k xs ys+    | VU.length xs /= VU.length ys = Nothing+    | VU.null xs = Nothing+    | k < 2 = Nothing+    | rx <= 0 || ry <= 0 = Just 0+    | otherwise =+        let bx = VU.map (binIndex xmin xmax k) xs+            by = VU.map (binIndex ymin ymax k) ys+            n = fromIntegral (VU.length xs) :: Double+            mx = bincount k bx+            my = bincount k by+            mxy = jointBincount k bx by+         in Just $+                sum+                    [ let !cxy = fromIntegral c+                          !pxy = cxy / n+                          !px = fromIntegral (mx VU.! i) / n+                          !py = fromIntegral (my VU.! j) / n+                       in if c == 0 then 0 else pxy * logBase 2 (pxy / (px * py))+                    | i <- [0 .. k - 1]+                    , j <- [0 .. k - 1]+                    , let !c = mxy VU.! (i * k + j)+                    ]+  where+    (xmin, xmax) = (VU.minimum xs, VU.maximum xs)+    (ymin, ymax) = (VU.minimum ys, VU.maximum ys)+    rx = xmax - xmin+    ry = ymax - ymin++binIndex :: Double -> Double -> Int -> Double -> Int+binIndex lo hi k x+    | hi == lo = 0+    | otherwise =+        let !t = (x - lo) / (hi - lo)+            !ix = floor (fromIntegral k * t) :: Int+         in max 0 (min (k - 1) ix)+{-# INLINE binIndex #-}++bincount :: Int -> VU.Vector Int -> VU.Vector Int+bincount k bs = VU.create $ do+    mv <- VU.thaw (VU.replicate k 0)+    VU.forM_ bs $ \b -> do+        let i = if b < 0 then 0 else if b >= k then k - 1 else b+        x <- VUM.read mv i+        VUM.write mv i (x + 1)+    pure mv+{-# INLINE bincount #-}++jointBincount :: Int -> VU.Vector Int -> VU.Vector Int -> VU.Vector Int+jointBincount k bx by = VU.create $ do+    mv <- VU.thaw (VU.replicate (k * k) 0)+    VU.forM_ (VU.zip bx by) $ \(i, j) -> do+        let ii = clamp i 0 (k - 1)+            jj = clamp j 0 (k - 1)+            ix = ii * k + jj+        x <- VUM.read mv ix+        VUM.write mv ix (x + 1)+    pure mv+  where+    clamp z a b = max a (min b z)+{-# INLINE jointBincount #-}
src/DataFrame/Lazy/IO/CSV.hs view
@@ -27,7 +27,13 @@ import Data.IORef import Data.Maybe import Data.Type.Equality (TestEquality (testEquality))-import DataFrame.Internal.Column (Column (..), MutableColumn (..), columnLength, freezeColumn', writeColumn)+import DataFrame.Internal.Column (+    Column (..),+    MutableColumn (..),+    columnLength,+    freezeColumn',+    writeColumn,+ ) import DataFrame.Internal.DataFrame (DataFrame (..)) import DataFrame.Internal.Parsing import System.IO@@ -50,7 +56,17 @@ and we convert any rows with nullish objects into Maybe (safeRead). -} defaultOptions :: ReadOptions-defaultOptions = ReadOptions{hasHeader = True, inferTypes = True, safeRead = True, rowRange = Nothing, seekPos = Nothing, totalRows = Nothing, leftOver = "", rowsRead = 0}+defaultOptions =+    ReadOptions+        { hasHeader = True+        , inferTypes = True+        , safeRead = True+        , rowRange = Nothing+        , seekPos = Nothing+        , totalRows = Nothing+        , leftOver = ""+        , rowsRead = 0+        }  {- | Reads a CSV file from the given path. Note this file stores intermediate temporary files@@ -67,12 +83,14 @@ readTsv path = fst <$> readSeparated '\t' defaultOptions path  -- | Reads a character separated file into a dataframe using mutable vectors.-readSeparated :: Char -> ReadOptions -> FilePath -> IO (DataFrame, (Integer, T.Text, Int))+readSeparated ::+    Char -> ReadOptions -> FilePath -> IO (DataFrame, (Integer, T.Text, Int)) readSeparated c opts path = do     totalRows <- case totalRows opts of-        Nothing -> countRows c path >>= \total -> if hasHeader opts then return (total - 1) else return total+        Nothing ->+            countRows c path >>= \total -> if hasHeader opts then return (total - 1) else return total         Just n -> if hasHeader opts then return (n - 1) else return n-    let (begin, len) = case rowRange opts of+    let (_, len) = case rowRange opts of             Nothing -> (0, totalRows)             Just (start, len) -> (start, min len (totalRows - rowsRead opts))     withFile path ReadMode $ \handle -> do@@ -85,7 +103,8 @@         unless (hasHeader opts) $ hSeek handle AbsoluteSeek 0          currPos <- hTell handle-        when (isJust $ seekPos opts) $ hSeek handle AbsoluteSeek (fromMaybe currPos (seekPos opts))+        when (isJust $ seekPos opts) $+            hSeek handle AbsoluteSeek (fromMaybe currPos (seekPos opts))          -- Initialize mutable vectors for each column         let numColumns = length columnNames@@ -104,7 +123,8 @@         getInitialDataVectors numRows mutableCols dataRow          -- Read rows into the mutable vectors-        (unconsumed, r) <- fillColumns numRows c mutableCols nullIndices remainder handle+        (unconsumed, r) <-+            fillColumns numRows c mutableCols nullIndices remainder handle          -- Freeze the mutable vectors into immutable ones         nulls' <- V.unsafeFreeze nullIndices@@ -125,9 +145,18 @@ getInitialDataVectors n mCol xs = do     forM_ (zip [0 ..] xs) $ \(i, x) -> do         col <- case inferValueType x of-            "Int" -> MUnboxedColumn <$> ((VUM.unsafeNew n :: IO (VUM.IOVector Int)) >>= \c -> VUM.unsafeWrite c 0 (fromMaybe 0 $ readInt x) >> return c)-            "Double" -> MUnboxedColumn <$> ((VUM.unsafeNew n :: IO (VUM.IOVector Double)) >>= \c -> VUM.unsafeWrite c 0 (fromMaybe 0 $ readDouble x) >> return c)-            _ -> MBoxedColumn <$> ((VM.unsafeNew n :: IO (VM.IOVector T.Text)) >>= \c -> VM.unsafeWrite c 0 x >> return c)+            "Int" ->+                MUnboxedColumn+                    <$> ( (VUM.unsafeNew n :: IO (VUM.IOVector Int)) >>= \c -> VUM.unsafeWrite c 0 (fromMaybe 0 $ readInt x) >> return c+                        )+            "Double" ->+                MUnboxedColumn+                    <$> ( (VUM.unsafeNew n :: IO (VUM.IOVector Double)) >>= \c -> VUM.unsafeWrite c 0 (fromMaybe 0 $ readDouble x) >> return c+                        )+            _ ->+                MBoxedColumn+                    <$> ( (VM.unsafeNew n :: IO (VM.IOVector T.Text)) >>= \c -> VM.unsafeWrite c 0 x >> return c+                        )         VM.unsafeWrite mCol i col {-# INLINE getInitialDataVectors #-} @@ -161,7 +190,14 @@             return (row, unconsumed)  -- | Reads rows from the handle and stores values in mutable vectors.-fillColumns :: Int -> Char -> VM.IOVector MutableColumn -> VM.IOVector [(Int, T.Text)] -> T.Text -> Handle -> IO (T.Text, Int)+fillColumns ::+    Int ->+    Char ->+    VM.IOVector MutableColumn ->+    VM.IOVector [(Int, T.Text)] ->+    T.Text ->+    Handle ->+    IO (T.Text, Int) fillColumns n c mutableCols nullIndices unused handle = do     input <- newIORef unused     rowsRead <- newIORef (0 :: Int)@@ -191,7 +227,13 @@ {-# INLINE fillColumns #-}  -- | Writes a value into the appropriate column, resizing the vector if necessary.-writeValue :: VM.IOVector MutableColumn -> VM.IOVector [(Int, T.Text)] -> Int -> Int -> T.Text -> IO ()+writeValue ::+    VM.IOVector MutableColumn ->+    VM.IOVector [(Int, T.Text)] ->+    Int ->+    Int ->+    T.Text ->+    IO () writeValue mutableCols nullIndices count colIndex value = do     col <- VM.unsafeRead mutableCols colIndex     res <- writeColumn count value col@@ -200,7 +242,12 @@ {-# INLINE writeValue #-}  -- | Freezes a mutable vector into an immutable one, trimming it to the actual row count.-freezeColumn :: VM.IOVector MutableColumn -> V.Vector [(Int, T.Text)] -> ReadOptions -> Int -> IO Column+freezeColumn ::+    VM.IOVector MutableColumn ->+    V.Vector [(Int, T.Text)] ->+    ReadOptions ->+    Int ->+    IO Column freezeColumn mutableCols nulls opts colIndex = do     col <- VM.unsafeRead mutableCols colIndex     freezeColumn' (nulls V.! colIndex) col@@ -291,7 +338,7 @@     DataFrame ->     IO () writeSeparated c filepath df = withFile filepath WriteMode $ \handle -> do-    let (rows, columns) = dataframeDimensions df+    let (rows, _) = dataframeDimensions df     let headers = map fst (L.sortBy (compare `on` snd) (M.toList (columnIndices df)))     TIO.hPutStrLn handle (T.intercalate ", " headers)     forM_ [0 .. (rows - 1)] $ \i -> do
src/DataFrame/Lazy/Internal/DataFrame.hs view
@@ -53,7 +53,16 @@     (df', _) <-         foldM             ( \(accDf, (pos, unused, r)) (start, end) -> do-                mapM_ putStr ["Scanning: ", show start, " to ", show end, " rows out of ", show totalRows, "\n"]+                mapM_+                    putStr+                    [ "Scanning: "+                    , show start+                    , " to "+                    , show end+                    , " rows out of "+                    , show totalRows+                    , "\n"+                    ]                  (sdf, (pos', unconsumed, rowsRead)) <-                     D.readSeparated@@ -87,7 +96,8 @@ addOperation :: LazyOperation -> LazyDataFrame -> LazyDataFrame addOperation op df = df{operations = operations df ++ [op]} -derive :: (C.Columnable a) => T.Text -> E.Expr a -> LazyDataFrame -> LazyDataFrame+derive ::+    (C.Columnable a) => T.Text -> E.Expr a -> LazyDataFrame -> LazyDataFrame derive name expr = addOperation (Derive name expr)  select :: (C.Columnable a) => [T.Text] -> LazyDataFrame -> LazyDataFrame
src/DataFrame/Operations/Aggregation.hs view
@@ -18,6 +18,7 @@  import Control.Exception (throw) import Control.Monad.ST (runST)+import Data.Either (fromRight) import Data.Hashable import Data.Type.Equality (TestEquality (..), type (:~:) (Refl)) import DataFrame.Errors@@ -43,8 +44,18 @@     DataFrame ->     GroupedDataFrame groupBy names df-    | any (`notElem` columnNames df) names = throw $ ColumnNotFoundException (T.pack $ show $ names L.\\ columnNames df) "groupBy" (columnNames df)-    | otherwise = Grouped df names (VG.map fst valueIndices) (VU.fromList (reverse (changingPoints valueIndices)))+    | any (`notElem` columnNames df) names =+        throw $+            ColumnNotFoundException+                (T.pack $ show $ names L.\\ columnNames df)+                "groupBy"+                (columnNames df)+    | otherwise =+        Grouped+            df+            names+            (VG.map fst valueIndices)+            (VU.fromList (reverse (changingPoints valueIndices)))   where     indicesToGroup = M.elems $ M.filterWithKey (\k _ -> k `elem` names) (columnIndices df)     rowRepresentations = VU.generate (fst (dimensions df)) (mkRowRep indicesToGroup df)@@ -86,7 +97,8 @@             Just Refl -> hash value             Nothing -> hash (show value) -mkGroupedColumns :: VU.Vector Int -> DataFrame -> DataFrame -> T.Text -> DataFrame+mkGroupedColumns ::+    VU.Vector Int -> DataFrame -> DataFrame -> T.Text -> DataFrame mkGroupedColumns indices df acc name =     case (V.!) (columns df) (columnIndices df M.! name) of         BoxedColumn column ->@@ -105,11 +117,14 @@ aggregate :: [(T.Text, UExpr)] -> GroupedDataFrame -> DataFrame aggregate aggs gdf@(Grouped df groupingColumns valueIndices offsets) =     let-        df' = selectIndices (VG.map (valueIndices VG.!) (VG.init offsets)) (select groupingColumns df)-        groupedColumns = columnNames df L.\\ groupingColumns+        df' =+            selectIndices+                (VG.map (valueIndices VG.!) (VG.init offsets))+                (select groupingColumns df)+         f (name, Wrap (expr :: Expr a)) d =             let-                value = interpretAggregation @a gdf expr+                value = fromRight (error "Not fully aggregated") (interpretAggregation @a gdf expr)              in                 insertColumn name (unwrapTypedColumn value) d      in
src/DataFrame/Operations/Core.hs view
@@ -23,9 +23,18 @@ import Data.Maybe import Data.Type.Equality (TestEquality (..)) import DataFrame.Errors-import DataFrame.Internal.Column (Column (..), Columnable, columnLength, columnTypeString, expandColumn, fromList, fromVector)+import DataFrame.Internal.Column (+    Column (..),+    Columnable,+    columnLength,+    columnTypeString,+    expandColumn,+    fromList,+    fromVector,+ ) import DataFrame.Internal.DataFrame (DataFrame (..), empty, getColumn) import DataFrame.Internal.Parsing (isNullish)+import DataFrame.Internal.Row (Any, mkColumnFromRow) import Type.Reflection import Prelude hiding (null) @@ -174,8 +183,16 @@         n = max (columnLength column) r      in         case M.lookup name (columnIndices d) of-            Just i -> DataFrame (V.map (expandColumn n) (columns d V.// [(i, column)])) (columnIndices d) (n, c)-            Nothing -> DataFrame (V.map (expandColumn n) (columns d `V.snoc` column)) (M.insert name c (columnIndices d)) (n, c + 1)+            Just i ->+                DataFrame+                    (V.map (expandColumn n) (columns d V.// [(i, column)]))+                    (columnIndices d)+                    (n, c)+            Nothing ->+                DataFrame+                    (V.map (expandColumn n) (columns d `V.snoc` column))+                    (M.insert name c (columnIndices d))+                    (n, c + 1)  {- | /O(n)/ Clones a column and places it under a new name in the dataframe. @@ -206,9 +223,13 @@ @ -} cloneColumn :: T.Text -> T.Text -> DataFrame -> DataFrame-cloneColumn original new df = fromMaybe (throw $ ColumnNotFoundException original "cloneColumn" (M.keys $ columnIndices df)) $ do-    column <- getColumn original df-    return $ insertColumn new column df+cloneColumn original new df = fromMaybe+    ( throw $+        ColumnNotFoundException original "cloneColumn" (M.keys $ columnIndices df)+    )+    $ do+        column <- getColumn original df+        return $ insertColumn new column df  {- | /O(n)/ Renames a single column. @@ -288,14 +309,17 @@ @ -} renameMany :: [(T.Text, T.Text)] -> DataFrame -> DataFrame-renameMany replacements df = fold (uncurry rename) replacements df+renameMany = fold (uncurry rename) -renameSafe :: T.Text -> T.Text -> DataFrame -> Either DataFrameException DataFrame-renameSafe orig new df = fromMaybe (Left $ ColumnNotFoundException orig "rename" (M.keys $ columnIndices df)) $ do-    columnIndex <- M.lookup orig (columnIndices df)-    let origRemoved = M.delete orig (columnIndices df)-    let newAdded = M.insert new columnIndex origRemoved-    return (Right df{columnIndices = newAdded})+renameSafe ::+    T.Text -> T.Text -> DataFrame -> Either DataFrameException DataFrame+renameSafe orig new df = fromMaybe+    (Left $ ColumnNotFoundException orig "rename" (M.keys $ columnIndices df))+    $ do+        columnIndex <- M.lookup orig (columnIndices df)+        let origRemoved = M.delete orig (columnIndices df)+        let newAdded = M.insert new columnIndex origRemoved+        return (Right df{columnIndices = newAdded})  data ColumnInfo = ColumnInfo     { nameOfColumn :: !T.Text@@ -336,7 +360,9 @@         & insertColumn "# Unique Values" (fromList (map uniqueValues infos))         & insertColumn "Type" (fromList (map typeOfColumn infos))   where-    infos = L.sortBy (compare `on` nonNullValues) (V.ifoldl' go [] (columns df)) :: [ColumnInfo]+    infos =+        L.sortBy (compare `on` nonNullValues) (V.ifoldl' go [] (columns df)) ::+            [ColumnInfo]     indexMap = M.fromList (map (\(a, b) -> (b, a)) $ M.toList (columnIndices df))     columnName i = M.lookup i indexMap     go acc i col@(OptionalColumn (c :: V.Vector a)) =@@ -347,7 +373,17 @@             columnType = T.pack $ show $ typeRep @a             unique = S.size $ VG.foldr S.insert S.empty c          in-            if isNothing cname then acc else ColumnInfo (fromMaybe "" cname) (columnLength col - countNulls) countNulls countPartial unique columnType : acc+            if isNothing cname+                then acc+                else+                    ColumnInfo+                        (fromMaybe "" cname)+                        (columnLength col - countNulls)+                        countNulls+                        countPartial+                        unique+                        columnType+                        : acc     go acc i col@(BoxedColumn (c :: V.Vector a)) =         let             cname = columnName i@@ -355,7 +391,17 @@             columnType = T.pack $ show $ typeRep @a             unique = S.size $ VG.foldr S.insert S.empty c          in-            if isNothing cname then acc else ColumnInfo (fromMaybe "" cname) (columnLength col) 0 countPartial unique columnType : acc+            if isNothing cname+                then acc+                else+                    ColumnInfo+                        (fromMaybe "" cname)+                        (columnLength col)+                        0+                        countPartial+                        unique+                        columnType+                        : acc     go acc i col@(UnboxedColumn c) =         let             cname = columnName i@@ -364,7 +410,10 @@          in             -- Unboxed columns cannot have nulls since Maybe             -- is not an instance of Unbox a-            if isNothing cname then acc else ColumnInfo (fromMaybe "" cname) (columnLength col) 0 0 unique columnType : acc+            if isNothing cname+                then acc+                else+                    ColumnInfo (fromMaybe "" cname) (columnLength col) 0 0 unique columnType : acc  nulls :: Column -> Int nulls (OptionalColumn xs) = VG.length $ VG.filter isNothing xs@@ -418,7 +467,7 @@ fromNamedColumns = L.foldl' (\df (name, column) -> insertColumn name column df) empty  {- | Create a dataframe from a list of columns. The column names are "0", "1"... etc.-Useful for quick exploration but you should probably alwyas rename the columns after+Useful for quick exploration but you should probably always rename the columns after or drop the ones you don't want.  ==== __Example__@@ -448,6 +497,32 @@ fromUnnamedColumns :: [Column] -> DataFrame fromUnnamedColumns = fromNamedColumns . zip (map (T.pack . show) [0 ..]) +{- | Create a dataframe from a list of column names and rows.++==== __Example__+@+ghci> df = D.fromRows ["A", "B"] [[D.toAny 1, D.toAny 11], [D.toAny 2, D.toAny 12], [D.toAny 3, D.toAny 13]]++ghci> df++-----------------+index |  A  |  B+------|-----|----+ Int  | Int | Int+------|-----|----+0     | 1   | 11+1     | 2   | 12+2     | 3   | 13++@+-}+fromRows :: [T.Text] -> [[Any]] -> DataFrame+fromRows names rows =+    L.foldl'+        (\df i -> insertColumn (names !! i) (mkColumnFromRow i rows) df)+        empty+        [0 .. length names - 1]+ {- | O (k * n) Counts the occurences of each value in a given column.  ==== __Example__@@ -462,7 +537,9 @@ -} valueCounts :: forall a. (Columnable a) => T.Text -> DataFrame -> [(a, Int)] valueCounts columnName df = case getColumn columnName df of-    Nothing -> throw $ ColumnNotFoundException columnName "valueCounts" (M.keys $ columnIndices df)+    Nothing ->+        throw $+            ColumnNotFoundException columnName "valueCounts" (M.keys $ columnIndices df)     Just (BoxedColumn (column' :: V.Vector c)) ->         let             column = V.foldl' (\m v -> MS.insertWith (+) v (1 :: Int) m) M.empty column'@@ -497,7 +574,8 @@                 Just Refl -> M.toAscList column     Just (UnboxedColumn (column' :: VU.Vector c)) ->         let-            column = V.foldl' (\m v -> MS.insertWith (+) v (1 :: Int) m) M.empty (V.convert column')+            column =+                V.foldl' (\m v -> MS.insertWith (+) v (1 :: Int) m) M.empty (V.convert column')          in             case (typeRep @a) `testEquality` (typeRep @c) of                 Nothing ->
src/DataFrame/Operations/Join.hs view
@@ -42,23 +42,53 @@         leftIndicesToGroup = M.elems $ M.filterWithKey (\k _ -> k `elem` cs) (D.columnIndices left)         leftRowRepresentations = VU.generate (fst (D.dimensions left)) (D.mkRowRep leftIndicesToGroup left)         -- key -> [index0, index1]-        leftKeyCountsAndIndices = VU.foldr (\(i, v) acc -> M.insertWith (++) v [i] acc) M.empty (VU.indexed leftRowRepresentations)+        leftKeyCountsAndIndices =+            VU.foldr+                (\(i, v) acc -> M.insertWith (++) v [i] acc)+                M.empty+                (VU.indexed leftRowRepresentations)         -- key -> [index0, index1]         rightIndicesToGroup = M.elems $ M.filterWithKey (\k _ -> k `elem` cs) (D.columnIndices right)         rightRowRepresentations = VU.generate (fst (D.dimensions right)) (D.mkRowRep rightIndicesToGroup right)-        rightKeyCountsAndIndices = VU.foldr (\(i, v) acc -> M.insertWith (++) v [i] acc) M.empty (VU.indexed rightRowRepresentations)+        rightKeyCountsAndIndices =+            VU.foldr+                (\(i, v) acc -> M.insertWith (++) v [i] acc)+                M.empty+                (VU.indexed rightRowRepresentations)         -- key -> [(left_indexes0, right_indexes1)]-        mergedKeyCountsAndIndices = M.foldrWithKey (\k v m -> if k `M.member` rightKeyCountsAndIndices then M.insert k (VU.fromList v, VU.fromList (rightKeyCountsAndIndices M.! k)) m else m) M.empty leftKeyCountsAndIndices+        mergedKeyCountsAndIndices =+            M.foldrWithKey+                ( \k v m ->+                    if k `M.member` rightKeyCountsAndIndices+                        then M.insert k (VU.fromList v, VU.fromList (rightKeyCountsAndIndices M.! k)) m+                        else m+                )+                M.empty+                leftKeyCountsAndIndices         -- [(ints, ints)]         leftAndRightIndicies = M.elems mergedKeyCountsAndIndices         -- [(ints, ints)] (expanded to n * m)-        expandedIndices = map (\(l, r) -> (mconcat (replicate (VU.length r) l), mconcat (replicate (VU.length l) r))) leftAndRightIndicies+        expandedIndices =+            map+                ( \(l, r) -> (mconcat (replicate (VU.length r) l), mconcat (replicate (VU.length l) r))+                )+                leftAndRightIndicies         expandedLeftIndicies = mconcat (map fst expandedIndices)         expandedRightIndicies = mconcat (map snd expandedIndices)         -- df-        expandedLeft = left{columns = VB.map (D.atIndicesStable expandedLeftIndicies) (D.columns left), dataframeDimensions = (VU.length expandedLeftIndicies, snd (D.dataframeDimensions left))}+        expandedLeft =+            left+                { columns = VB.map (D.atIndicesStable expandedLeftIndicies) (D.columns left)+                , dataframeDimensions =+                    (VU.length expandedLeftIndicies, snd (D.dataframeDimensions left))+                }         -- df-        expandedRight = right{columns = VB.map (D.atIndicesStable expandedRightIndicies) (D.columns right), dataframeDimensions = (VU.length expandedRightIndicies, snd (D.dataframeDimensions right))}+        expandedRight =+            right+                { columns = VB.map (D.atIndicesStable expandedRightIndicies) (D.columns right)+                , dataframeDimensions =+                    (VU.length expandedRightIndicies, snd (D.dataframeDimensions right))+                }         -- [string]         leftColumns = D.columnNames left         rightColumns = D.columnNames right@@ -66,4 +96,15 @@         insertIfPresent _ Nothing df = df         insertIfPresent name (Just c) df = D.insertColumn name c df      in-        D.fold (\name df -> if name `elem` cs then df else (if name `elem` leftColumns then insertIfPresent ("Right_" <> name) (D.getColumn name expandedRight) df else insertIfPresent name (D.getColumn name expandedRight) df)) rightColumns initDf+        D.fold+            ( \name df ->+                if name `elem` cs+                    then df+                    else+                        ( if name `elem` leftColumns+                            then insertIfPresent ("Right_" <> name) (D.getColumn name expandedRight) df+                            else insertIfPresent name (D.getColumn name expandedRight) df+                        )+            )+            rightColumns+            initDf
src/DataFrame/Operations/Merge.hs view
@@ -10,13 +10,13 @@  import Data.Maybe +{- | Vertically merge two dataframes using shared columns.+Columns that exist in only one dataframe are padded with Nothing.+-} instance Semigroup D.DataFrame where-    -- \| Vertically merge two dataframes using shared columns.     (<>) :: D.DataFrame -> D.DataFrame -> D.DataFrame     (<>) a b =         let-            columnsInBOnly = filter (\c -> c `notElem` D.columnNames b) (D.columnNames b)-            columnsInA = D.columnNames a             addColumns a' b' df name                 | fst (D.dimensions a') == 0 && fst (D.dimensions b') == 0 = df                 | fst (D.dimensions a') == 0 = fromMaybe df $ do@@ -27,21 +27,28 @@                     pure $ D.insertColumn name col df                 | otherwise =                     let-                        numColumnsA = (fst $ D.dimensions a')-                        numColumnsB = (fst $ D.dimensions b')-                        numColumns = max numColumnsA numColumnsB+                        numRowsA = fst $ D.dimensions a'+                        numRowsB = fst $ D.dimensions b'+                        sumRows = numRowsA + numRowsB+                         optA = D.getColumn name a'                         optB = D.getColumn name b'                      in                         case optB of                             Nothing -> case optA of-                                Nothing -> D.insertColumn name (D.fromList ([] :: [T.Text])) df-                                Just a'' -> D.insertColumn name (D.expandColumn numColumnsB a'') df+                                Nothing ->+                                    -- N.B. this case should never happen, because we're dealing with columns coming from+                                    -- union of column names of both dataframes. Nothing + Nothing would mean column+                                    -- wasn't in either dataframe, which shouldn't happen+                                    D.insertColumn name (D.fromList ([] :: [T.Text])) df+                                Just a'' ->+                                    D.insertColumn name (D.expandColumn sumRows a'') df                             Just b'' -> case optA of-                                Nothing -> D.insertColumn name (D.leftExpandColumn numColumnsA b'') df-                                Just a'' -> fromMaybe df $ do-                                    concatedColumns <- D.concatColumns a'' b''-                                    pure $ D.insertColumn name concatedColumns df+                                Nothing ->+                                    D.insertColumn name (D.leftExpandColumn sumRows b'') df+                                Just a'' ->+                                    let concatedColumns = D.concatColumnsEither a'' b''+                                     in D.insertColumn name concatedColumns df          in             L.foldl' (addColumns a b) D.empty (D.columnNames a `L.union` D.columnNames b) @@ -50,4 +57,8 @@  -- | Add two dataframes side by side/horizontally. (|||) :: D.DataFrame -> D.DataFrame -> D.DataFrame-(|||) a b = D.fold (\name acc -> D.insertColumn name (D.unsafeGetColumn name b) acc) (D.columnNames b) a+(|||) a b =+    D.fold+        (\name acc -> D.insertColumn name (D.unsafeGetColumn name b) acc)+        (D.columnNames b)+        a
src/DataFrame/Operations/Sorting.hs view
@@ -26,7 +26,12 @@     DataFrame ->     DataFrame sortBy order names df-    | any (`notElem` columnNames df) names = throw $ ColumnNotFoundException (T.pack $ show $ names L.\\ columnNames df) "sortBy" (columnNames df)+    | any (`notElem` columnNames df) names =+        throw $+            ColumnNotFoundException+                (T.pack $ show $ names L.\\ columnNames df)+                "sortBy"+                (columnNames df)     | otherwise =         let             indexes = sortedIndexes' (order == Ascending) (toRowVector names df)
src/DataFrame/Operations/Statistics.hs view
@@ -56,12 +56,24 @@         counts :: forall a. (Columnable a) => [(a, Int)]         counts = valueCounts name df         calculatePercentage cs k = toAny $ toPct2dp (fromIntegral k / fromIntegral (P.sum $ map snd cs))-        initDf = empty & insertVector "Statistic" (V.fromList ["Count" :: T.Text, "Percentage (%)"])+        initDf =+            empty+                & insertVector "Statistic" (V.fromList ["Count" :: T.Text, "Percentage (%)"])         freqs :: forall v a. (VG.Vector v a, Columnable a) => v a -> DataFrame-        freqs col = L.foldl' (\d (col, k) -> insertVector (showValue @a col) (V.fromList [toAny k, calculatePercentage (counts @a) k]) d) initDf counts+        freqs col =+            L.foldl'+                ( \d (col, k) ->+                    insertVector+                        (showValue @a col)+                        (V.fromList [toAny k, calculatePercentage (counts @a) k])+                        d+                )+                initDf+                counts      in         case getColumn name df of-            Nothing -> throw $ ColumnNotFoundException name "frequencies" (M.keys $ columnIndices df)+            Nothing ->+                throw $ ColumnNotFoundException name "frequencies" (M.keys $ columnIndices df)             Just ((BoxedColumn (column :: V.Vector a))) -> freqs column             Just ((OptionalColumn (column :: V.Vector a))) -> freqs column             Just ((UnboxedColumn (column :: VU.Vector a))) -> freqs column@@ -106,19 +118,29 @@             SFalse -> case sFloating @a of                 STrue -> Just (VU.map realToFrac f)                 SFalse -> Nothing-    Nothing -> throw $ ColumnNotFoundException name "applyStatistic" (M.keys $ columnIndices df)+    Nothing ->+        throw $+            ColumnNotFoundException name "applyStatistic" (M.keys $ columnIndices df)     _ -> Nothing {-# INLINE _getColumnAsDouble #-}  -- | Calculates the sum of a given column as a standalone value.-sum :: forall a. (Columnable a, Num a, VU.Unbox a) => T.Text -> DataFrame -> Maybe a+sum ::+    forall a. (Columnable a, Num a, VU.Unbox a) => T.Text -> DataFrame -> Maybe a sum name df = case getColumn name df of     Nothing -> throw $ ColumnNotFoundException name "sum" (M.keys $ columnIndices df)     Just ((UnboxedColumn (column :: VU.Vector a'))) -> case testEquality (typeRep @a') (typeRep @a) of         Just Refl -> Just $ VG.sum column         Nothing -> Nothing+    Just ((BoxedColumn (column :: V.Vector a'))) -> case testEquality (typeRep @a') (typeRep @a) of+        Just Refl -> Just $ VG.sum column+        Nothing -> Nothing+    Just ((OptionalColumn (column :: V.Vector (Maybe a')))) -> case testEquality (typeRep @a') (typeRep @a) of+        Just Refl -> Just $ VG.sum (VG.map (fromMaybe 0) column)+        Nothing -> Nothing -applyStatistic :: (VU.Vector Double -> Double) -> T.Text -> DataFrame -> Maybe Double+applyStatistic ::+    (VU.Vector Double -> Double) -> T.Text -> DataFrame -> Maybe Double applyStatistic f name df = apply =<< _getColumnAsDouble name (filterJust name df)   where     apply col =@@ -128,14 +150,46 @@             if isNaN res then Nothing else pure res {-# INLINE applyStatistic #-} -applyStatistics :: (VU.Vector Double -> VU.Vector Double) -> T.Text -> DataFrame -> Maybe (VU.Vector Double)+applyStatistics ::+    (VU.Vector Double -> VU.Vector Double) ->+    T.Text ->+    DataFrame ->+    Maybe (VU.Vector Double) applyStatistics f name df = fmap f (_getColumnAsDouble name (filterJust name df))  -- | Descriptive statistics of the numeric columns. summarize :: DataFrame -> DataFrame-summarize df = fold columnStats (columnNames df) (fromNamedColumns [("Statistic", fromList ["Count" :: T.Text, "Mean", "Minimum", "25%", "Median", "75%", "Max", "StdDev", "IQR", "Skewness"])])+summarize df =+    fold+        columnStats+        (columnNames df)+        ( fromNamedColumns+            [+                ( "Statistic"+                , fromList+                    [ "Count" :: T.Text+                    , "Mean"+                    , "Minimum"+                    , "25%"+                    , "Median"+                    , "75%"+                    , "Max"+                    , "StdDev"+                    , "IQR"+                    , "Skewness"+                    ]+                )+            ]+        )   where-    columnStats name d = if all isJust (stats name) then insertUnboxedVector name (VU.fromList (map (roundTo 2 . fromMaybe 0) $ stats name)) d else d+    columnStats name d =+        if all isJust (stats name)+            then+                insertUnboxedVector+                    name+                    (VU.fromList (map (roundTo 2 . fromMaybe 0) $ stats name))+                    d+            else d     stats name =         let             count = fromIntegral . numElements <$> getColumn name df
src/DataFrame/Operations/Subset.hs view
@@ -41,28 +41,41 @@  -- | O(k * n) Take the last n rows of a DataFrame. takeLast :: Int -> DataFrame -> DataFrame-takeLast n d = d{columns = V.map (takeLastColumn n') (columns d), dataframeDimensions = (n', c)}+takeLast n d =+    d+        { columns = V.map (takeLastColumn n') (columns d)+        , dataframeDimensions = (n', c)+        }   where     (r, c) = dataframeDimensions d     n' = clip n 0 r  -- | O(k * n) Drop the first n rows of a DataFrame. drop :: Int -> DataFrame -> DataFrame-drop n d = d{columns = V.map (sliceColumn n' (max (r - n') 0)) (columns d), dataframeDimensions = (max (r - n') 0, c)}+drop n d =+    d+        { columns = V.map (sliceColumn n' (max (r - n') 0)) (columns d)+        , dataframeDimensions = (max (r - n') 0, c)+        }   where     (r, c) = dataframeDimensions d     n' = clip n 0 r  -- | O(k * n) Drop the last n rows of a DataFrame. dropLast :: Int -> DataFrame -> DataFrame-dropLast n d = d{columns = V.map (sliceColumn 0 n') (columns d), dataframeDimensions = (n', c)}+dropLast n d =+    d{columns = V.map (sliceColumn 0 n') (columns d), dataframeDimensions = (n', c)}   where     (r, c) = dataframeDimensions d     n' = clip (r - n) 0 r  -- | O(k * n) Take a range of rows of a DataFrame. range :: (Int, Int) -> DataFrame -> DataFrame-range (start, end) d = d{columns = V.map (sliceColumn (clip start 0 r) n') (columns d), dataframeDimensions = (n', c)}+range (start, end) d =+    d+        { columns = V.map (sliceColumn (clip start 0 r) n') (columns d)+        , dataframeDimensions = (n', c)+        }   where     (r, c) = dataframeDimensions d     n' = clip (end - start) 0 r@@ -85,12 +98,17 @@     DataFrame ->     DataFrame filter filterColumnName condition df = case getColumn filterColumnName df of-    Nothing -> throw $ ColumnNotFoundException filterColumnName "filter" (M.keys $ columnIndices df)+    Nothing ->+        throw $+            ColumnNotFoundException filterColumnName "filter" (M.keys $ columnIndices df)     Just (BoxedColumn (column :: V.Vector b)) -> filterByVector filterColumnName column condition df     Just (OptionalColumn (column :: V.Vector b)) -> filterByVector filterColumnName column condition df     Just (UnboxedColumn (column :: VU.Vector b)) -> filterByVector filterColumnName column condition df -filterByVector :: forall a b v. (VG.Vector v b, Columnable a, Columnable b) => T.Text -> v b -> (a -> Bool) -> DataFrame -> DataFrame+filterByVector ::+    forall a b v.+    (VG.Vector v b, Columnable a, Columnable b) =>+    T.Text -> v b -> (a -> Bool) -> DataFrame -> DataFrame filterByVector filterColumnName column condition df = case testEquality (typeRep @a) (typeRep @b) of     Nothing ->         throw $@@ -106,7 +124,10 @@         let             ixs = indexes condition column          in-            df{columns = V.map (atIndicesStable ixs) (columns df), dataframeDimensions = (VG.length ixs, snd (dataframeDimensions df))}+            df+                { columns = V.map (atIndicesStable ixs) (columns df)+                , dataframeDimensions = (VG.length ixs, snd (dataframeDimensions df))+                }  indexes :: (VG.Vector v a) => (a -> Bool) -> v a -> VU.Vector Int indexes condition cols = runST $ do@@ -143,10 +164,17 @@ filterWhere expr df =     let         (TColumn col) = interpret @Bool df expr-        (Just indexes) = findIndices (== True) col+        indexes = case findIndices (== True) col of+            Just ixs -> ixs+            Nothing ->+                -- Shouldn't happen as we are filtering by Bool column+                error "filterWhere: type mismatch"         c' = snd $ dataframeDimensions df      in-        df{columns = V.map (atIndicesStable indexes) (columns df), dataframeDimensions = (VU.length indexes, c')}+        df+            { columns = V.map (atIndicesStable indexes) (columns df)+            , dataframeDimensions = (VU.length indexes, c')+            }  {- | O(k) removes all rows with `Nothing` in a given column from the dataframe. @@ -154,7 +182,8 @@ -} filterJust :: T.Text -> DataFrame -> DataFrame filterJust name df = case getColumn name df of-    Nothing -> throw $ ColumnNotFoundException name "filterJust" (M.keys $ columnIndices df)+    Nothing ->+        throw $ ColumnNotFoundException name "filterJust" (M.keys $ columnIndices df)     Just column@(OptionalColumn (col :: V.Vector (Maybe a))) -> filter @(Maybe a) name isJust df & apply @(Maybe a) fromJust name     Just column -> df @@ -164,9 +193,10 @@ -} filterNothing :: T.Text -> DataFrame -> DataFrame filterNothing name df = case getColumn name df of-    Nothing -> throw $ ColumnNotFoundException name "filterNothing" (M.keys $ columnIndices df)+    Nothing ->+        throw $ ColumnNotFoundException name "filterNothing" (M.keys $ columnIndices df)     Just (OptionalColumn (col :: V.Vector (Maybe a))) -> filter @(Maybe a) name isNothing df-    _                                                 -> df+    _ -> df  {- | O(n * k) removes all rows with `Nothing` from the dataframe. @@ -193,7 +223,12 @@     DataFrame select cs df     | L.null cs = empty-    | any (`notElem` columnNames df) cs = throw $ ColumnNotFoundException (T.pack $ show $ cs L.\\ columnNames df) "select" (columnNames df)+    | any (`notElem` columnNames df) cs =+        throw $+            ColumnNotFoundException+                (T.pack $ show $ cs L.\\ columnNames df)+                "select"+                (columnNames df)     | otherwise = L.foldl' addKeyValue empty cs   where     addKeyValue d k = fromMaybe df $ do@@ -207,37 +242,42 @@     | ColumnIndexRange (Int, Int)     | ColumnName T.Text --- | Criteria for selecting a column by name.------ > selectBy [byName "Age"] df------ equivalent to:------ > select ["Age"] df+{- | Criteria for selecting a column by name.++> selectBy [byName "Age"] df++equivalent to:++> select ["Age"] df+-} byName :: T.Text -> SelectionCriteria byName = ColumnName --- | Criteria for selecting columns whose property satisfies given predicate.------ > selectBy [byProperty isNumeric] df+{- | Criteria for selecting columns whose property satisfies given predicate.++> selectBy [byProperty isNumeric] df+-} byProperty :: (Column -> Bool) -> SelectionCriteria byProperty = ColumnProperty --- | Criteria for selecting columns whose name satisfies given predicate.------ > selectBy [byNameProperty (T.isPrefixOf "weight")] df+{- | Criteria for selecting columns whose name satisfies given predicate.++> selectBy [byNameProperty (T.isPrefixOf "weight")] df+-} byNameProperty :: (T.Text -> Bool) -> SelectionCriteria byNameProperty = ColumnNameProperty --- | Criteria for selecting columns whose names are in the given lexicographic range (inclusive).------ > selectBy [byNameRange ("a", "c")] df+{- | Criteria for selecting columns whose names are in the given lexicographic range (inclusive).++> selectBy [byNameRange ("a", "c")] df+-} byNameRange :: (T.Text, T.Text) -> SelectionCriteria byNameRange = ColumnTextRange --- | Criteria for selecting columns whose indices are in the given (inclusive) range.------ > selectBy [byIndexRange (0, 5)] df+{- | Criteria for selecting columns whose indices are in the given (inclusive) range.++> selectBy [byIndexRange (0, 5)] df+-} byIndexRange :: (Int, Int) -> SelectionCriteria byIndexRange = ColumnIndexRange @@ -248,9 +288,14 @@     columnsWithProperties = L.foldl' columnWithProperty [] xs     columnWithProperty acc (ColumnName name) = acc ++ [name]     columnWithProperty acc (ColumnNameProperty f) = acc ++ L.filter f (columnNames df)-    columnWithProperty acc (ColumnTextRange (from, to)) = acc ++ reverse (Prelude.dropWhile (to /=) $ reverse $ dropWhile (from /=) (columnNames df))+    columnWithProperty acc (ColumnTextRange (from, to)) =+        acc+            ++ reverse+                (Prelude.dropWhile (to /=) $ reverse $ dropWhile (from /=) (columnNames df))     columnWithProperty acc (ColumnIndexRange (from, to)) = acc ++ Prelude.take (to - from + 1) (Prelude.drop from (columnNames df))-    columnWithProperty acc (ColumnProperty f) = acc ++ map fst (L.filter (\(k, v) -> v `elem` ixs) (M.toAscList (columnIndices df)))+    columnWithProperty acc (ColumnProperty f) =+        acc+            ++ map fst (L.filter (\(k, v) -> v `elem` ixs) (M.toAscList (columnIndices df)))       where         ixs = V.ifoldl' (\acc i c -> if f c then i : acc else acc) [] (columns df) 
src/DataFrame/Operations/Transformations.hs view
@@ -14,7 +14,15 @@ import Control.Exception (throw) import Data.Maybe import DataFrame.Errors (DataFrameException (..), TypeErrorContext (..))-import DataFrame.Internal.Column (Column (..), Columnable, columnTypeString, ifoldrColumn, imapColumn, mapColumn, unwrapTypedColumn)+import DataFrame.Internal.Column (+    Column (..),+    Columnable,+    columnTypeString,+    ifoldrColumn,+    imapColumn,+    mapColumn,+    unwrapTypedColumn,+ ) import DataFrame.Internal.DataFrame (DataFrame (..), getColumn) import DataFrame.Internal.Expression import DataFrame.Operations.Core@@ -112,15 +120,28 @@ applyWhere ::     forall a b.     (Columnable a, Columnable b) =>-    (a -> Bool) -> -- ^ Filter condition-    T.Text -> -- ^ Criterion Column-    (b -> b) -> -- ^ function to apply-    T.Text -> -- ^ Column name-    DataFrame -> -- ^ DataFrame to apply operation to+    -- | Filter condition+    (a -> Bool) ->+    -- | Criterion Column+    T.Text ->+    -- | function to apply+    (b -> b) ->+    -- | Column name+    T.Text ->+    -- | DataFrame to apply operation to+    DataFrame ->     DataFrame applyWhere condition filterColumnName f columnName df = case getColumn filterColumnName df of-    Nothing -> throw $ ColumnNotFoundException filterColumnName "applyWhere" (M.keys $ columnIndices df)-    Just column -> case ifoldrColumn (\i val acc -> if condition val then V.cons i acc else acc) V.empty column of+    Nothing ->+        throw $+            ColumnNotFoundException+                filterColumnName+                "applyWhere"+                (M.keys $ columnIndices df)+    Just column -> case ifoldrColumn+        (\i val acc -> if condition val then V.cons i acc else acc)+        V.empty+        column of         Nothing ->             throw $                 TypeMismatchException@@ -150,7 +171,9 @@     DataFrame ->     DataFrame applyAtIndex i f columnName df = case getColumn columnName df of-    Nothing -> throw $ ColumnNotFoundException columnName "applyAtIndex" (M.keys $ columnIndices df)+    Nothing ->+        throw $+            ColumnNotFoundException columnName "applyAtIndex" (M.keys $ columnIndices df)     Just column -> case imapColumn (\index value -> if index == i then f value else value) column of         Nothing ->             throw $@@ -173,7 +196,8 @@     DataFrame ->     DataFrame impute columnName value df = case getColumn columnName df of-    Nothing -> throw $ ColumnNotFoundException columnName "impute" (M.keys $ columnIndices df)+    Nothing ->+        throw $ ColumnNotFoundException columnName "impute" (M.keys $ columnIndices df)     Just (OptionalColumn _) -> case safeApply (fromMaybe value) columnName df of         Left (TypeMismatchException context) -> throw $ TypeMismatchException (context{callingFunctionName = Just "impute"})         Left exception -> throw exception
src/DataFrame/Operations/Typing.hs view
@@ -19,14 +19,26 @@ import DataFrame.Internal.Parsing import Type.Reflection (typeRep) -parseDefaults :: Bool -> DataFrame -> DataFrame-parseDefaults safeRead df = df{columns = V.map (parseDefault safeRead) (columns df)}+parseDefaults :: Bool -> String -> DataFrame -> DataFrame+parseDefaults safeRead dateFormat df = df{columns = V.map (parseDefault safeRead dateFormat) (columns df)} -parseDefault :: Bool -> Column -> Column-parseDefault safeRead (BoxedColumn (c :: V.Vector a)) =+parseDefault :: Bool -> String -> Column -> Column+parseDefault safeRead dateFormat (BoxedColumn (c :: V.Vector a)) =     let-        parseTimeOpt s = parseTimeM {- Accept leading/trailing whitespace -} True defaultTimeLocale "%Y-%m-%d" (T.unpack s) :: Maybe Day-        unsafeParseTime s = parseTimeOrError {- Accept leading/trailing whitespace -} True defaultTimeLocale "%Y-%m-%d" (T.unpack s) :: Day+        parseTimeOpt s =+            parseTimeM {- Accept leading/trailing whitespace -}+                True+                defaultTimeLocale+                dateFormat+                (T.unpack s) ::+                Maybe Day+        unsafeParseTime s =+            parseTimeOrError {- Accept leading/trailing whitespace -}+                True+                defaultTimeLocale+                dateFormat+                (T.unpack s) ::+                Day      in         case (typeRep @a) `testEquality` (typeRep @T.Text) of             Nothing -> case (typeRep @a) `testEquality` (typeRep @String) of@@ -34,7 +46,8 @@                     let                         emptyToNothing v = if isNullish (T.pack v) then Nothing else Just v                         safeVector = V.map emptyToNothing c-                        hasNulls = V.foldl' (\acc v -> if isNothing v then acc || True else acc) False safeVector+                        hasNulls =+                            V.foldl' (\acc v -> if isNothing v then acc || True else acc) False safeVector                      in                         if safeRead && hasNulls then BoxedColumn safeVector else BoxedColumn c                 Nothing -> BoxedColumn c@@ -45,12 +58,16 @@                         Just _ ->                             let safeVector = V.map ((=<<) readInt . emptyToNothing) c                                 hasNulls = V.elem Nothing safeVector-                             in if safeRead && hasNulls then BoxedColumn safeVector else UnboxedColumn (VU.generate (V.length c) (fromMaybe 0 . (safeVector V.!)))+                             in if safeRead && hasNulls+                                    then BoxedColumn safeVector+                                    else UnboxedColumn (VU.generate (V.length c) (fromMaybe 0 . (safeVector V.!)))                         Nothing -> case readDouble example of                             Just _ ->                                 let safeVector = V.map ((=<<) readDouble . emptyToNothing) c                                     hasNulls = V.elem Nothing safeVector-                                 in if safeRead && hasNulls then BoxedColumn safeVector else UnboxedColumn (VU.generate (V.length c) (fromMaybe 0 . (safeVector V.!)))+                                 in if safeRead && hasNulls+                                        then BoxedColumn safeVector+                                        else UnboxedColumn (VU.generate (V.length c) (fromMaybe 0 . (safeVector V.!)))                             Nothing -> case parseTimeOpt example of                                 Just d ->                                     let@@ -80,4 +97,4 @@                                         hasNulls = V.any isNullish c                                      in                                         if safeRead && hasNulls then BoxedColumn safeVector else BoxedColumn c-parseDefault safeRead column = column+parseDefault _ _ column = column
tests/Functions.hs view
@@ -1,9 +1,26 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-}  module Functions where -import DataFrame.Functions (sanitize)+import DataFrame.Functions (+    col,+    generatePrograms,+    geq,+    ifThenElse,+    lit,+    max,+    mean,+    min,+    percentile,+    pow,+    relu,+    sanitize,+    stddev,+ )+import DataFrame.Internal.Expression (add, divide, mult) import Test.HUnit+import Prelude hiding (max, min)  -- Test cases for the sanitize function sanitizeIdentifiers :: Test@@ -56,5 +73,745 @@                 (sanitize "***")         ] +generateProgramsCalledWithNoExistingPrograms :: Test+generateProgramsCalledWithNoExistingPrograms =+    TestCase+        ( assertEqual+            "generatePrograms called with no existing programs"+            [ col @Double "x"+            , divide+                (add (col @Double "x") (negate (mean (col @Double "x"))))+                (stddev (col @Double "x"))+            , abs (col @Double "x")+            , exp (mult (log (col @Double "x")) (lit 0.5))+            , log (add (col @Double "x") (lit 1.0))+            , exp (col @Double "x")+            , mean (col @Double "x")+            , mean (col @Double "x")+            , stddev (col @Double "x")+            , sin (col @Double "x")+            , cos (col @Double "x")+            , relu (col @Double "x")+            , signum (col @Double "x")+            , pow 2 (col @Double "x")+            , pow 3 (col @Double "x")+            , pow 4 (col @Double "x")+            , pow 5 (col @Double "x")+            , pow 6 (col @Double "x")+            , max (col @Double "x") (col @Double "x")+            , mult (col @Double "x") (col @Double "x")+            ]+            (generatePrograms [col "x"] [])+        )++generateProgramsCalledWithSingleExistingPrograms :: Test+generateProgramsCalledWithSingleExistingPrograms =+    TestCase+        ( assertEqual+            "generatePrograms called with single existing programs"+            [ col @Double "x"+            , col @Double "y"+            , divide+                (add (col @Double "x") (negate (mean (col @Double "x"))))+                (stddev (col @Double "x"))+            , exp (mult (log (col @Double "x")) (lit 0.5))+            , abs (col @Double "x")+            , log (add (col @Double "x") (lit 1.0))+            , exp (col @Double "x")+            , mean (col @Double "x")+            , mean (col @Double "x")+            , stddev (col @Double "x")+            , sin (col @Double "x")+            , cos (col @Double "x")+            , relu (col @Double "x")+            , signum (col @Double "x")+            , divide+                (add (col @Double "y") (negate (mean (col @Double "y"))))+                (stddev (col @Double "y"))+            , exp (mult (log (col @Double "y")) (lit 0.5))+            , abs (col @Double "y")+            , log (add (col @Double "y") (lit 1.0))+            , exp (col @Double "y")+            , mean (col @Double "y")+            , mean (col @Double "y")+            , stddev (col @Double "y")+            , sin (col @Double "y")+            , cos (col @Double "y")+            , relu (col @Double "y")+            , signum (col @Double "y")+            , pow 2 (col @Double "x")+            , pow 3 (col @Double "x")+            , pow 4 (col @Double "x")+            , pow 5 (col @Double "x")+            , pow 6 (col @Double "x")+            , pow 2 (col @Double "y")+            , pow 3 (col @Double "y")+            , pow 4 (col @Double "y")+            , pow 5 (col @Double "y")+            , pow 6 (col @Double "y")+            , add (col @Double "x") (col @Double "x")+            , add (col @Double "y") (col @Double "x")+            , add (col @Double "y") (col @Double "y")+            , min (col @Double "y") (col @Double "x")+            , max (col @Double "y") (col @Double "x")+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , add (col @Double "x") (negate (col @Double "y"))+            , add (col @Double "y") (negate (col @Double "x"))+            , mult (col @Double "x") (col @Double "x")+            , mult (col @Double "y") (col @Double "x")+            , mult (col @Double "y") (col @Double "y")+            , divide (col @Double "x") (col @Double "y")+            , divide (col @Double "y") (col @Double "x")+            ]+            (generatePrograms [col "x"] [col "y"])+        )++generateProgramsCalledWithCompoundExistingPrograms :: Test+generateProgramsCalledWithCompoundExistingPrograms =+    TestCase+        ( assertEqual+            "generatePrograms called with compound existing programs"+            [ col @Double "x"+            , col @Double "y"+            , add (lit 1.0) (col @Double "z")+            , divide+                (add (col @Double "x") (negate (mean (col @Double "x"))))+                (stddev (col @Double "x"))+            , exp (mult (log (col @Double "x")) (lit 0.5))+            , abs (col @Double "x")+            , log (add (col @Double "x") (lit 1.0))+            , exp (col @Double "x")+            , mean (col @Double "x")+            , mean (col @Double "x")+            , stddev (col @Double "x")+            , sin (col @Double "x")+            , cos (col @Double "x")+            , relu (col @Double "x")+            , signum (col @Double "x")+            , divide+                (add (col @Double "y") (negate (mean (col @Double "y"))))+                (stddev (col @Double "y"))+            , exp (mult (log (col @Double "y")) (lit 0.5))+            , abs (col @Double "y")+            , log (add (col @Double "y") (lit 1.0))+            , exp (col @Double "y")+            , mean (col @Double "y")+            , mean (col @Double "y")+            , stddev (col @Double "y")+            , sin (col @Double "y")+            , cos (col @Double "y")+            , relu (col @Double "y")+            , signum (col @Double "y")+            , divide+                ( add+                    (add (lit 1.0) (col @Double "z"))+                    (negate (mean (add (lit 1.0) (col @Double "z"))))+                )+                (stddev (add (lit 1.0) (col @Double "z")))+            , exp (mult (log (add (lit 1.0) (col @Double "z"))) (lit 0.5))+            , abs (add (lit 1.0) (col @Double "z"))+            , log (add (add (lit 1.0) (col @Double "z")) (lit 1.0))+            , exp (add (lit 1.0) (col @Double "z"))+            , mean (add (lit 1.0) (col @Double "z"))+            , mean (add (lit 1.0) (col @Double "z"))+            , stddev (add (lit 1.0) (col @Double "z"))+            , sin (add (lit 1.0) (col @Double "z"))+            , cos (add (lit 1.0) (col @Double "z"))+            , relu (add (lit 1.0) (col @Double "z"))+            , signum (add (lit 1.0) (col @Double "z"))+            , pow 2 (col @Double "x")+            , pow 3 (col @Double "x")+            , pow 4 (col @Double "x")+            , pow 5 (col @Double "x")+            , pow 6 (col @Double "x")+            , pow 2 (col @Double "y")+            , pow 3 (col @Double "y")+            , pow 4 (col @Double "y")+            , pow 5 (col @Double "y")+            , pow 6 (col @Double "y")+            , pow 2 (add (lit 1.0) (col @Double "z"))+            , pow 3 (add (lit 1.0) (col @Double "z"))+            , pow 4 (add (lit 1.0) (col @Double "z"))+            , pow 5 (add (lit 1.0) (col @Double "z"))+            , pow 6 (add (lit 1.0) (col @Double "z"))+            , add (col @Double "x") (col @Double "x")+            , add (col @Double "y") (col @Double "x")+            , add (col @Double "y") (col @Double "y")+            , add (add (lit 1.0) (col @Double "z")) (col @Double "x")+            , add (add (lit 1.0) (col @Double "z")) (col @Double "y")+            , add (add (lit 1.0) (col @Double "z")) (add (lit 1.0) (col @Double "z"))+            , min (col @Double "y") (col @Double "x")+            , min (add (lit 1.0) (col @Double "z")) (col @Double "x")+            , min (add (lit 1.0) (col @Double "z")) (col @Double "y")+            , max (col @Double "y") (col @Double "x")+            , max (add (lit 1.0) (col @Double "z")) (col @Double "x")+            , max (add (lit 1.0) (col @Double "z")) (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (col @Double "x")+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (add (lit 1.0) (col @Double "z"))+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 1 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "x")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 25 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "x")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 50 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "x")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 75 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "x")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 99 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "x")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 1 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "y")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 25 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "y")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 50 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "y")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 75 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "y")+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 99 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (col @Double "y")+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "x") (percentile 1 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "x") (percentile 25 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "x") (percentile 50 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "x") (percentile 75 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "x") (percentile 99 (col @Double "x")))+                (col @Double "x")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (lit 1.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (lit 0.0)+            , ifThenElse+                (geq (col @Double "y") (percentile 1 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 25 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 50 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 75 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                (geq (col @Double "y") (percentile 99 (col @Double "y")))+                (col @Double "y")+                (lit (-1.0))+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 1 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 1.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 25 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 1.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 50 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 1.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 75 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 1.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 99 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 1.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 1 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 0.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 25 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 0.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 50 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 0.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 75 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 0.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 99 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit 0.0)+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 1 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit (-1.0))+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 25 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit (-1.0))+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 50 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit (-1.0))+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 75 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit (-1.0))+            , ifThenElse+                ( geq+                    (add (lit 1.0) (col @Double "z"))+                    (percentile 99 (add (lit 1.0) (col @Double "z")))+                )+                (add (lit 1.0) (col @Double "z"))+                (lit (-1.0))+            , add (col @Double "x") (negate (col @Double "y"))+            , add (col @Double "x") (negate (add (lit 1.0) (col @Double "z")))+            , add (col @Double "y") (negate (col @Double "x"))+            , add (col @Double "y") (negate (add (lit 1.0) (col @Double "z")))+            , add (add (lit 1.0) (col @Double "z")) (negate (col @Double "x"))+            , add (add (lit 1.0) (col @Double "z")) (negate (col @Double "y"))+            , mult (col @Double "x") (col @Double "x")+            , mult (col @Double "y") (col @Double "x")+            , mult (col @Double "y") (col @Double "y")+            , mult (add (lit 1.0) (col @Double "z")) (col @Double "x")+            , mult (add (lit 1.0) (col @Double "z")) (col @Double "y")+            , mult (add (lit 1.0) (col @Double "z")) (add (lit 1.0) (col @Double "z"))+            , divide (col @Double "x") (col @Double "y")+            , divide (col @Double "x") (add (lit 1.0) (col @Double "z"))+            , divide (col @Double "y") (col @Double "x")+            , divide (col @Double "y") (add (lit 1.0) (col @Double "z"))+            , divide (add (lit 1.0) (col @Double "z")) (col @Double "x")+            , divide (add (lit 1.0) (col @Double "z")) (col @Double "y")+            ]+            (generatePrograms [col "x"] [col "y", lit 1 + col "z"])+        )+ tests :: [Test]-tests = pure $ TestLabel "sanitizeIdentifiers" sanitizeIdentifiers+tests =+    [ TestLabel "sanitizeIdentifiers" sanitizeIdentifiers+    , TestLabel+        "generateProgramsCalledWithNoExistingPrograms"+        generateProgramsCalledWithNoExistingPrograms+    , TestLabel+        "generateProgramsCalledWithSingleExistingPrograms"+        generateProgramsCalledWithSingleExistingPrograms+    , TestLabel+        "generateProgramsCalledWithCompoundExistingPrograms"+        generateProgramsCalledWithCompoundExistingPrograms+    ]
tests/Main.hs view
@@ -14,11 +14,14 @@ import Test.HUnit  import qualified Functions+import qualified Operations.Aggregations import qualified Operations.Apply+import qualified Operations.Core import qualified Operations.Derive import qualified Operations.Filter import qualified Operations.GroupBy import qualified Operations.InsertColumn+import qualified Operations.Merge import qualified Operations.Sort import qualified Operations.Statistics import qualified Operations.Take@@ -48,26 +51,54 @@ parseDate :: Test parseDate =     let-        expected = DI.BoxedColumn (V.fromList [fromGregorian 2020 02 14, fromGregorian 2021 02 14, fromGregorian 2022 02 14])-        actual = D.parseDefault True (DI.fromVector (V.fromList ["2020-02-14" :: T.Text, "2021-02-14", "2022-02-14"]))+        expected =+            DI.BoxedColumn+                ( V.fromList+                    [fromGregorian 2020 02 14, fromGregorian 2021 02 14, fromGregorian 2022 02 14]+                )+        actual =+            D.parseDefault+                True+                "%Y-%m-%d"+                (DI.fromVector (V.fromList ["2020-02-14" :: T.Text, "2021-02-14", "2022-02-14"]))      in         TestCase (assertEqual "Correctly parses gregorian date" expected actual)  incompleteDataParseEither :: Test incompleteDataParseEither =     let-        expected = DI.BoxedColumn (V.fromList [Right $ fromGregorian 2020 02 14, Left ("2021-02-" :: T.Text), Right $ fromGregorian 2022 02 14])-        actual = D.parseDefault True (DI.fromVector (V.fromList ["2020-02-14" :: T.Text, "2021-02-", "2022-02-14"]))+        expected =+            DI.BoxedColumn+                ( V.fromList+                    [ Right $ fromGregorian 2020 02 14+                    , Left ("2021-02-" :: T.Text)+                    , Right $ fromGregorian 2022 02 14+                    ]+                )+        actual =+            D.parseDefault+                True+                "%Y-%m-%d"+                (DI.fromVector (V.fromList ["2020-02-14" :: T.Text, "2021-02-", "2022-02-14"]))      in         TestCase (assertEqual "Parses Either for gregorian date" expected actual)  incompleteDataParseMaybe :: Test incompleteDataParseMaybe =     let-        expected = DI.BoxedColumn (V.fromList [Just $ fromGregorian 2020 02 14, Nothing, Just $ fromGregorian 2022 02 14])-        actual = D.parseDefault True (DI.fromVector (V.fromList ["2020-02-14" :: T.Text, "", "2022-02-14"]))+        expected =+            DI.BoxedColumn+                ( V.fromList+                    [Just $ fromGregorian 2020 02 14, Nothing, Just $ fromGregorian 2022 02 14]+                )+        actual =+            D.parseDefault+                True+                "%Y-%m-%d"+                (DI.fromVector (V.fromList ["2020-02-14" :: T.Text, "", "2022-02-14"]))      in-        TestCase (assertEqual "Parses Maybe for gregorian date with null/empty" expected actual)+        TestCase+            (assertEqual "Parses Maybe for gregorian date with null/empty" expected actual)  parseTests :: [Test] parseTests =@@ -80,11 +111,14 @@ tests =     TestList $         dimensionsTest+            ++ Operations.Aggregations.tests             ++ Operations.Apply.tests+            ++ Operations.Core.tests             ++ Operations.Derive.tests             ++ Operations.Filter.tests             ++ Operations.GroupBy.tests             ++ Operations.InsertColumn.tests+            ++ Operations.Merge.tests             ++ Operations.Sort.tests             ++ Operations.Statistics.tests             ++ Operations.Take.tests@@ -95,4 +129,6 @@ main :: IO () main = do     result <- runTestTT tests-    if failures result > 0 || errors result > 0 then Exit.exitFailure else Exit.exitSuccess+    if failures result > 0 || errors result > 0+        then Exit.exitFailure+        else Exit.exitSuccess
+ tests/Operations/Aggregations.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeApplications #-}++module Operations.Aggregations where++import qualified Data.Text as T+import qualified DataFrame as D+import qualified DataFrame.Functions as F+import qualified DataFrame.Internal.Column as DI++import Data.Function+import Test.HUnit++values :: [(T.Text, DI.Column)]+values =+    [ ("test1", DI.fromList ([1, 1, 1, 2, 2, 2, 3, 3, 3, 1, 1, 1] :: [Int]))+    , ("test2", DI.fromList ([12, 11 .. 1] :: [Int]))+    , ("test3", DI.fromList ([1 .. 12] :: [Int]))+    , ("test4", DI.fromList ['a' .. 'l'])+    , ("test4", DI.fromList (map show ['a' .. 'l']))+    , ("test6", DI.fromList ([1 .. 12] :: [Integer]))+    ]++testData :: D.DataFrame+testData = D.fromNamedColumns values++foldAggregation :: Test+foldAggregation =+    TestCase+        ( assertEqual+            "Counting elements after grouping gives correct numbers"+            ( D.fromNamedColumns+                [ ("test1", DI.fromList [(1 :: Int), 2, 3])+                , ("test2", DI.fromList [(6 :: Int), 3, 3])+                ]+            )+            ( testData+                & D.groupBy ["test1"]+                & D.aggregate [F.count (F.col @Int "test2") `F.as` "test2"]+            )+        )++numericAggregation :: Test+numericAggregation =+    TestCase+        ( assertEqual+            "Mean works for ints"+            ( D.fromNamedColumns+                [ ("test1", DI.fromList [(1 :: Int), 2, 3])+                , ("test2", DI.fromList [(6.5 :: Double), 8.0, 5.0])+                ]+            )+            ( testData+                & D.groupBy ["test1"]+                & D.aggregate [F.mean (F.col @Int "test2") `F.as` "test2"]+            )+        )++numericAggregationOfUnaggregatedUnaryOp :: Test+numericAggregationOfUnaggregatedUnaryOp =+    TestCase+        ( assertEqual+            "Mean works for ints"+            ( D.fromNamedColumns+                [ ("test1", DI.fromList [(1 :: Int), 2, 3])+                , ("test2", DI.fromList [(6.5 :: Double), 8.0, 5.0])+                ]+            )+            ( testData+                & D.groupBy ["test1"]+                & D.aggregate+                    [ F.mean (F.lift (fromIntegral @Int @Double) (F.col @Int "test2")) `F.as` "test2"+                    ]+            )+        )++numericAggregationOfUnaggregatedBinaryOp :: Test+numericAggregationOfUnaggregatedBinaryOp =+    TestCase+        ( assertEqual+            "Mean works for ints"+            ( D.fromNamedColumns+                [ ("test1", DI.fromList [(1 :: Int), 2, 3])+                , ("test2", DI.fromList [(13 :: Double), 16, 10])+                ]+            )+            ( testData+                & D.groupBy ["test1"]+                & D.aggregate [F.mean (F.col @Int "test2" + (F.col @Int "test2")) `F.as` "test2"]+            )+        )++reduceAggregationOfUnaggregatedUnaryOp :: Test+reduceAggregationOfUnaggregatedUnaryOp =+    TestCase+        ( assertEqual+            "Mean works for ints"+            ( D.fromNamedColumns+                [ ("test1", DI.fromList [(1 :: Int), 2, 3])+                , ("test2", DI.fromList [(12 :: Double), 9, 6])+                ]+            )+            ( testData+                & D.groupBy ["test1"]+                & D.aggregate+                    [ F.maximum (F.lift (fromIntegral @Int @Double) (F.col @Int "test2"))+                        `F.as` "test2"+                    ]+            )+        )++reduceAggregationOfUnaggregatedBinaryOp :: Test+reduceAggregationOfUnaggregatedBinaryOp =+    TestCase+        ( assertEqual+            "Mean works for ints"+            ( D.fromNamedColumns+                [ ("test1", DI.fromList [(1 :: Int), 2, 3])+                , ("test2", DI.fromList [(24 :: Int), 18, 12])+                ]+            )+            ( testData+                & D.groupBy ["test1"]+                & D.aggregate+                    [F.maximum (F.col @Int "test2" + (F.col @Int "test2")) `F.as` "test2"]+            )+        )++tests :: [Test]+tests =+    [ TestLabel "foldAggregation" foldAggregation+    , TestLabel "numericAggregation" numericAggregation+    , TestLabel+        "numericAggregationOfUnaggregatedUnaryOp"+        numericAggregationOfUnaggregatedUnaryOp+    , TestLabel+        "numericAggregationOfUnaggregatedBinaryOp"+        numericAggregationOfUnaggregatedBinaryOp+    , TestLabel+        "reduceAggregationOfUnaggregatedUnaryOp"+        reduceAggregationOfUnaggregatedUnaryOp+    , TestLabel+        "reduceAggregationOfUnaggregatedBinaryOp"+        reduceAggregationOfUnaggregatedBinaryOp+    ]
tests/Operations/Apply.hs view
@@ -92,7 +92,9 @@     TestCase         ( assertEqual             "Applies function to many fields"-            (D.fromNamedColumns (map (,D.fromList $ replicate 26 (1 :: Integer)) ["test4", "test6", "test8"]))+            ( D.fromNamedColumns+                (map (,D.fromList $ replicate 26 (1 :: Integer)) ["test4", "test6", "test8"])+            )             ( D.select ["test4", "test6", "test8"] $                 D.applyMany @Char                     (const (1 :: Integer))@@ -106,7 +108,9 @@     TestCase         ( assertEqual             "Unboxes fields when necessary"-            (D.fromNamedColumns (map (,D.fromList $ replicate 26 (1 :: Int)) ["test4", "test6", "test8"]))+            ( D.fromNamedColumns+                (map (,D.fromList $ replicate 26 (1 :: Int)) ["test4", "test6", "test8"])+            )             ( D.select ["test4", "test6", "test8"] $                 D.applyMany @Char                     (const (1 :: Int))@@ -135,7 +139,10 @@         ( assertExpectException             "[Error Case]"             (DE.typeMismatchError (show $ typeRep @Char) (show $ typeRep @[Char]))-            (print $ DI.getColumn "test2" $ D.applyMany @Char (const (1 :: Int)) ["test2"] testData)+            ( print $+                DI.getColumn "test2" $+                    D.applyMany @Char (const (1 :: Int)) ["test2"] testData+            )         )  applyWhereWrongConditionType :: Test@@ -144,7 +151,9 @@         ( assertExpectException             "[Error Case]"             (DE.typeMismatchError (show $ typeRep @Integer) (show $ typeRep @Int))-            (print $ D.applyWhere (even @Integer) "test1" ((+ 1) :: Int -> Int) "test5" testData)+            ( print $+                D.applyWhere (even @Integer) "test1" ((+ 1) :: Int -> Int) "test5" testData+            )         )  applyWhereWrongTargetType :: Test@@ -153,7 +162,9 @@         ( assertExpectException             "[Error Case]"             (DE.typeMismatchError (show $ typeRep @Float) (show $ typeRep @Int))-            (print $ D.applyWhere (even @Int) "test1" ((+ 1) :: Float -> Float) "test5" testData)+            ( print $+                D.applyWhere (even @Int) "test1" ((+ 1) :: Float -> Float) "test5" testData+            )         )  applyWhereConditionColumnNotFound :: Test@@ -179,8 +190,13 @@     TestCase         ( assertEqual             "applyWhere works as intended"-            (Just $ DI.UnboxedColumn (VU.fromList (zipWith ($) (cycle [id, (+ 1)]) [(1 :: Int) .. 26])))-            (D.getColumn "test5" $ D.applyWhere (even @Int) "test1" ((+ 1) :: Int -> Int) "test5" testData)+            ( Just $+                DI.UnboxedColumn+                    (VU.fromList (zipWith ($) (cycle [id, (+ 1)]) [(1 :: Int) .. 26]))+            )+            ( D.getColumn "test5" $+                D.applyWhere (even @Int) "test1" ((+ 1) :: Int -> Int) "test5" testData+            )         )  tests :: [Test]
+ tests/Operations/Core.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-}++module Operations.Core where++import qualified DataFrame as D+import qualified DataFrame.Internal.Column as DI++import Test.HUnit++testData :: D.DataFrame+testData =+    D.fromNamedColumns+        [ ("A", DI.fromList ([1 .. 3] :: [Int]))+        , ("B", DI.fromList ['a' .. 'c'])+        ]++createsDataFrameFromRows :: Test+createsDataFrameFromRows =+    TestCase+        ( assertEqual+            "dataframe created from rows"+            testData+            ( D.fromRows+                ["A", "B"]+                [ [D.toAny (1 :: Int), D.toAny ('a' :: Char)]+                , [D.toAny (2 :: Int), D.toAny ('b' :: Char)]+                , [D.toAny (3 :: Int), D.toAny ('c' :: Char)]+                ]+            )+        )++tests :: [Test]+tests = [TestLabel "createsDataFrameFromRows" createsDataFrameFromRows]
tests/Operations/Derive.hs view
@@ -28,11 +28,18 @@     TestCase         ( assertEqual             "derive works with column expression"-            (Just $ DI.BoxedColumn (V.fromList (zipWith (\n c -> show n ++ [c]) [1 .. 26] ['a' .. 'z'])))+            ( Just $+                DI.BoxedColumn+                    (V.fromList (zipWith (\n c -> show n ++ [c]) [1 .. 26] ['a' .. 'z']))+            )             ( DI.getColumn "test4" $                 D.derive                     "test4"-                    (F.lift2 (++) (F.lift show (F.col @Int "test1")) (F.lift (: ([] :: [Char])) (F.col @Char "test3")))+                    ( F.lift2+                        (++)+                        (F.lift show (F.col @Int "test1"))+                        (F.lift (: ([] :: [Char])) (F.col @Char "test3"))+                    )                     testData             )         )
tests/Operations/Filter.hs view
@@ -86,7 +86,12 @@         ( assertEqual             "Filters out Nothing and unwraps Maybe"             (D.fromNamedColumns [("test", D.fromList $ replicate 5 (1 :: Int))])-            (D.filterJust "test" (D.fromNamedColumns [("test", D.fromList $ take 10 $ cycle [Just (1 :: Int), Nothing])]))+            ( D.filterJust+                "test"+                ( D.fromNamedColumns+                    [("test", D.fromList $ take 10 $ cycle [Just (1 :: Int), Nothing])]+                )+            )         )  tests :: [Test]
tests/Operations/InsertColumn.hs view
@@ -32,7 +32,9 @@         ( assertEqual             "Two columns should be equal"             (Just $ DI.BoxedColumn (V.fromList ["Thuba" :: T.Text, "Zodwa", "Themba"]))-            (DI.getColumn "new" $ D.insertVector "new" (V.fromList ["Thuba" :: T.Text, "Zodwa", "Themba"]) D.empty)+            ( DI.getColumn "new" $+                D.insertVector "new" (V.fromList ["Thuba" :: T.Text, "Zodwa", "Themba"]) D.empty+            )         )  addBoxedColumn' :: Test@@ -41,7 +43,12 @@         ( assertEqual             "Two columns should be equal"             (Just $ DI.fromList ["Thuba" :: T.Text, "Zodwa", "Themba"])-            (DI.getColumn "new" $ D.insertColumn "new" (DI.fromList ["Thuba" :: T.Text, "Zodwa", "Themba"]) D.empty)+            ( DI.getColumn "new" $+                D.insertColumn+                    "new"+                    (DI.fromList ["Thuba" :: T.Text, "Zodwa", "Themba"])+                    D.empty+            )         )  -- Adding an boxed vector with an unboxable type (Int/Double) to an empty dataframe creates a new column boxed containing the vector elements.@@ -69,8 +76,14 @@     TestCase         ( assertEqual             "Missing values should be replaced with Nothing"-            (Just $ DI.OptionalColumn (V.fromList [Just "a" :: Maybe T.Text, Just "b", Just "c", Nothing, Nothing]))-            (DI.getColumn "newer" $ D.insertVector "newer" (V.fromList ["a" :: T.Text, "b", "c"]) $ D.insertVector "new" (V.fromList ["a" :: T.Text, "b", "c", "d", "e"]) D.empty)+            ( Just $+                DI.OptionalColumn+                    (V.fromList [Just "a" :: Maybe T.Text, Just "b", Just "c", Nothing, Nothing])+            )+            ( DI.getColumn "newer" $+                D.insertVector "newer" (V.fromList ["a" :: T.Text, "b", "c"]) $+                    D.insertVector "new" (V.fromList ["a" :: T.Text, "b", "c", "d", "e"]) D.empty+            )         )  addSmallerColumnUnboxed :: Test@@ -78,8 +91,14 @@     TestCase         ( assertEqual             "Missing values should be replaced with Nothing"-            (Just $ DI.OptionalColumn (V.fromList [Just 1 :: Maybe Int, Just 2, Just 3, Nothing, Nothing]))-            (DI.getColumn "newer" $ D.insertVector "newer" (V.fromList [1 :: Int, 2, 3]) $ D.insertVector "new" (V.fromList [1 :: Int, 2, 3, 4, 5]) D.empty)+            ( Just $+                DI.OptionalColumn+                    (V.fromList [Just 1 :: Maybe Int, Just 2, Just 3, Nothing, Nothing])+            )+            ( DI.getColumn "newer" $+                D.insertVector "newer" (V.fromList [1 :: Int, 2, 3]) $+                    D.insertVector "new" (V.fromList [1 :: Int, 2, 3, 4, 5]) D.empty+            )         )  insertColumnWithDefaultFillsWithDefault :: Test@@ -88,7 +107,10 @@         ( assertEqual             "Missing values should be replaced with Nothing"             (Just $ DI.UnboxedColumn (VU.fromList [1 :: Int, 2, 3, 0, 0]))-            (DI.getColumn "newer" $ D.insertVectorWithDefault 0 "newer" (V.fromList [1 :: Int, 2, 3]) $ D.insertVector "new" (V.fromList [1 :: Int, 2, 3, 4, 5]) D.empty)+            ( DI.getColumn "newer" $+                D.insertVectorWithDefault 0 "newer" (V.fromList [1 :: Int, 2, 3]) $+                    D.insertVector "new" (V.fromList [1 :: Int, 2, 3, 4, 5]) D.empty+            )         )  insertColumnWithDefaultFillsLargerNoop :: Test@@ -97,7 +119,10 @@         ( assertEqual             "Lists should be the same size"             (Just $ DI.UnboxedColumn (VU.fromList [(6 :: Int) .. 10]))-            (DI.getColumn "newer" $ D.insertVectorWithDefault 0 "newer" (V.fromList [(6 :: Int) .. 10]) $ D.insertVector "new" (V.fromList [1 :: Int, 2, 3, 4, 5]) D.empty)+            ( DI.getColumn "newer" $+                D.insertVectorWithDefault 0 "newer" (V.fromList [(6 :: Int) .. 10]) $+                    D.insertVector "new" (V.fromList [1 :: Int, 2, 3, 4, 5]) D.empty+            )         )  addLargerColumnBoxed :: Test@@ -106,7 +131,10 @@         ( assertEqual             "Smaller lists should grow and contain optionals"             ( D.fromNamedColumns-                [ ("new", D.fromList [Just "a" :: Maybe T.Text, Just "b", Just "c", Nothing, Nothing])+                [+                    ( "new"+                    , D.fromList [Just "a" :: Maybe T.Text, Just "b", Just "c", Nothing, Nothing]+                    )                 , ("newer", D.fromList ["a" :: T.Text, "b", "c", "d", "e"])                 ]             )@@ -156,13 +184,19 @@ tests :: [Test] tests =     [ TestLabel "dimensionsChangeAfterAdd" dimensionsChangeAfterAdd-    , TestLabel "dimensionsNotChangedAfterDuplicate" dimensionsNotChangedAfterDuplicate+    , TestLabel+        "dimensionsNotChangedAfterDuplicate"+        dimensionsNotChangedAfterDuplicate     , TestLabel "addBoxedColunmToEmpty" addBoxedColumn     , TestLabel "addBoxedColumnAutoUnboxes" addBoxedColumn     , TestLabel "addSmallerColumnBoxed" addSmallerColumnBoxed     , TestLabel "addSmallerColumnUnboxed" addSmallerColumnUnboxed     , TestLabel "addLargerColumnBoxed" addLargerColumnBoxed     , TestLabel "addLargerColumnUnboxed" addLargerColumnUnboxed-    , TestLabel "insertColumnWithDefaultFillsWithDefault" insertColumnWithDefaultFillsWithDefault-    , TestLabel "insertColumnWithDefaultFillsLargerNoop" insertColumnWithDefaultFillsLargerNoop+    , TestLabel+        "insertColumnWithDefaultFillsWithDefault"+        insertColumnWithDefaultFillsWithDefault+    , TestLabel+        "insertColumnWithDefaultFillsLargerNoop"+        insertColumnWithDefaultFillsLargerNoop     ]
+ tests/Operations/Merge.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE OverloadedStrings #-}++module Operations.Merge where++import qualified DataFrame as D+import qualified DataFrame.Internal.Column as DI+import DataFrame.Operations.Merge ((|||))+import Test.HUnit++df1 :: D.DataFrame+df1 =+    D.fromNamedColumns+        [ ("A", DI.fromList [1 :: Int, 2])+        , ("B", DI.fromList ['a', 'b'])+        ]++df2 :: D.DataFrame+df2 =+    D.fromNamedColumns+        [ ("A", DI.fromList [3 :: Int])+        , ("B", DI.fromList ['c'])+        ]++dfOnlyA :: D.DataFrame+dfOnlyA = D.fromNamedColumns [("A", DI.fromList [1 :: Int, 2])]++dfOnlyB :: D.DataFrame+dfOnlyB = D.fromNamedColumns [("B", DI.fromList ['c', 'd'])]++dfSide1 :: D.DataFrame+dfSide1 =+    D.fromNamedColumns+        [("L", DI.fromList [10 :: Int, 20])]++dfSide2 :: D.DataFrame+dfSide2 =+    D.fromNamedColumns+        [("R", DI.fromList ["x" :: String, "y"])]++mergeVerticalWAI :: Test+mergeVerticalWAI =+    TestCase+        ( assertEqual+            "Vertical merge concatenates matching columns"+            ( D.fromNamedColumns+                [ ("A", DI.fromList [1 :: Int, 2, 3])+                , ("B", DI.fromList ['a', 'b', 'c'])+                ]+            )+            (df1 <> df2)+        )++mergeVerticalDisjointSchemas :: Test+mergeVerticalDisjointSchemas =+    TestCase+        ( assertEqual+            "Disjoint schemas produce optional padding"+            ( D.fromNamedColumns+                [+                    ( "A"+                    , DI.fromList+                        [ Just (1 :: Int)+                        , Just 2+                        , Nothing+                        , Nothing+                        ]+                    )+                ,+                    ( "B"+                    , DI.fromList+                        [ Nothing+                        , Nothing+                        , Just 'c'+                        , Just 'd'+                        ]+                    )+                ]+            )+            (dfOnlyA <> dfOnlyB)+        )++mergeVerticalWithEmptyLeft :: Test+mergeVerticalWithEmptyLeft =+    TestCase+        ( assertEqual+            "Empty <> df = df"+            df1+            (D.empty <> df1)+        )++mergeVerticalWithEmptyRight :: Test+mergeVerticalWithEmptyRight =+    TestCase+        ( assertEqual+            "df <> Empty = df"+            df1+            (df1 <> D.empty)+        )++mergeHorizontalWAI :: Test+mergeHorizontalWAI =+    TestCase+        ( assertEqual+            "Horizontal merge combines columns side by side"+            ( D.fromNamedColumns+                [ ("L", DI.fromList [10 :: Int, 20])+                , ("R", DI.fromList ["x" :: String, "y"])+                ]+            )+            (dfSide1 ||| dfSide2)+        )++mergeHorizontalWithEmptyRight :: Test+mergeHorizontalWithEmptyRight =+    TestCase+        ( assertEqual+            "df ||| empty = df"+            dfSide1+            (dfSide1 ||| D.empty)+        )++mergeHorizontalWithEmptyLeft :: Test+mergeHorizontalWithEmptyLeft =+    TestCase+        ( assertEqual+            "empty ||| df = df"+            dfSide2+            (D.empty ||| dfSide2)+        )++mergeVerticalDifferentTypesSameColumnName :: Test+mergeVerticalDifferentTypesSameColumnName =+    let+        dfIntA = D.fromNamedColumns [("A", DI.fromList [1 :: Int, 2])]++        dfTextA = D.fromNamedColumns [("A", DI.fromList ["x" :: String, "y"])]+     in+        TestCase+            ( assertEqual+                "Merging columns with same name but different types wraps the values in Either"+                ( D.fromNamedColumns+                    [ ("A", DI.fromList [Left (1 :: Int), Left 2, Right ("x" :: String), Right "y"])+                    ]+                )+                (dfIntA <> dfTextA)+            )++tests :: [Test]+tests =+    [ TestLabel "mergeVerticalWAI" mergeVerticalWAI+    , TestLabel "mergeVerticalDisjointSchemas" mergeVerticalDisjointSchemas+    , TestLabel "mergeVerticalWithEmptyLeft" mergeVerticalWithEmptyLeft+    , TestLabel "mergeVerticalWithEmptyRight" mergeVerticalWithEmptyRight+    , TestLabel "mergeHorizontalWAI" mergeHorizontalWAI+    , TestLabel "mergeHorizontalWithEmptyRight" mergeHorizontalWithEmptyRight+    , TestLabel "mergeHorizontalWithEmptyLeft" mergeHorizontalWithEmptyLeft+    , TestLabel+        "mergeVerticalDifferentTypesSameColumnName"+        mergeVerticalDifferentTypesSameColumnName+    ]
tests/Operations/Statistics.hs view
@@ -60,7 +60,9 @@     TestCase         ( assertBool             "Skewness of a simple data set"-            (abs (D.skewness' (VU.fromList [25, 28, 26, 30, 40, 50, 40]) - 0.566_731_633_676) < 1e-12)+            ( abs (D.skewness' (VU.fromList [25, 28, 26, 30, 40, 50, 40]) - 0.566_731_633_676)+                < 1e-12+            )         )  skewnessOfEmptyDataSet :: Test@@ -77,7 +79,11 @@     TestCase         ( assertEqual             "2-quantile of an odd length data set"-            (D.quantiles' (VU.fromList [0, 1, 2]) 2 (VU.fromList [179.94, 231.94, 839.06, 534.23, 248.94]))+            ( D.quantiles'+                (VU.fromList [0, 1, 2])+                2+                (VU.fromList [179.94, 231.94, 839.06, 534.23, 248.94])+            )             (VU.fromList [179.94, 248.94, 839.06])         ) @@ -86,7 +92,11 @@     TestCase         ( assertEqual             "2-quantile of an even length data set"-            (D.quantiles' (VU.fromList [0, 1, 2]) 2 (VU.fromList [179.94, 231.94, 839.06, 534.23, 248.94, 276.37]))+            ( D.quantiles'+                (VU.fromList [0, 1, 2])+                2+                (VU.fromList [179.94, 231.94, 839.06, 534.23, 248.94, 276.37])+            )             (VU.fromList [179.94, 262.655, 839.06])         ) @@ -95,7 +105,11 @@     TestCase         ( assertEqual             "Quartiles of an odd length data set"-            (D.quantiles' (VU.fromList [0, 1, 2, 3, 4]) 4 (VU.fromList [3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20]))+            ( D.quantiles'+                (VU.fromList [0, 1, 2, 3, 4])+                4+                (VU.fromList [3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20])+            )             (VU.fromList [3, 7.5, 9, 14, 20])         ) @@ -104,7 +118,11 @@     TestCase         ( assertEqual             "Quartiles of an even length data set"-            (D.quantiles' (VU.fromList [0, 1, 2, 3, 4]) 4 (VU.fromList [3, 6, 7, 8, 8, 10, 13, 15, 16, 20]))+            ( D.quantiles'+                (VU.fromList [0, 1, 2, 3, 4])+                4+                (VU.fromList [3, 6, 7, 8, 8, 10, 13, 15, 16, 20])+            )             (VU.fromList [3, 7.25, 9, 14.5, 20])         ) @@ -171,8 +189,12 @@     , TestLabel "quartilesOfOddLengthDataSet" quartilesOfOddLengthDataSet     , TestLabel "quartilesOfEvenLengthDataSet" quartilesOfEvenLengthDataSet     , TestLabel "deciles" deciles-    , TestLabel "interQuartileRangeOfOddLengthDataSet" interQuartileRangeOfOddLengthDataSet-    , TestLabel "interQuartileRangeOfEvenLengthDataSet" interQuartileRangeOfEvenLengthDataSet+    , TestLabel+        "interQuartileRangeOfOddLengthDataSet"+        interQuartileRangeOfOddLengthDataSet+    , TestLabel+        "interQuartileRangeOfEvenLengthDataSet"+        interQuartileRangeOfEvenLengthDataSet     , TestLabel "wrongQuantileNumber" wrongQuantileNumber     , TestLabel "wrongQuantileIndex" wrongQuantileIndex     ]
tests/Operations/Take.hs view
@@ -16,34 +16,62 @@         ]  takeWAI :: Test-takeWAI = TestCase (assertEqual "Gets first 10 numbers" (Just $ D.fromList [(1 :: Int) .. 10]) (D.getColumn "test1" $ D.take 10 testData))+takeWAI =+    TestCase+        ( assertEqual+            "Gets first 10 numbers"+            (Just $ D.fromList [(1 :: Int) .. 10])+            (D.getColumn "test1" $ D.take 10 testData)+        )  takeLastWAI :: Test-takeLastWAI = TestCase (assertEqual "Gets first 10 numbers" (Just $ D.fromList [(17 :: Int) .. 26]) (D.getColumn "test1" $ D.takeLast 10 testData))+takeLastWAI =+    TestCase+        ( assertEqual+            "Gets first 10 numbers"+            (Just $ D.fromList [(17 :: Int) .. 26])+            (D.getColumn "test1" $ D.takeLast 10 testData)+        )  lengthEqualsTakeParam :: Test-lengthEqualsTakeParam = TestCase (assertEqual "should be (5, 2)" (5, 2) (D.dimensions $ D.take 5 testData))+lengthEqualsTakeParam =+    TestCase+        (assertEqual "should be (5, 2)" (5, 2) (D.dimensions $ D.take 5 testData))  lengthGreaterThanTakeParam :: Test-lengthGreaterThanTakeParam = TestCase (assertEqual "should be (26, 2)" (26, 2) (D.dimensions $ D.take 30 testData))+lengthGreaterThanTakeParam =+    TestCase+        (assertEqual "should be (26, 2)" (26, 2) (D.dimensions $ D.take 30 testData))  emptyIsZero :: Test-emptyIsZero = TestCase (assertEqual "should be (0, 0)" (0, 0) (D.dimensions $ D.take 5 D.empty))+emptyIsZero =+    TestCase+        (assertEqual "should be (0, 0)" (0, 0) (D.dimensions $ D.take 5 D.empty))  negativeIsZero :: Test-negativeIsZero = TestCase (assertEqual "should be (0, 2)" (0, 2) (D.dimensions $ D.take (-1) testData))+negativeIsZero =+    TestCase+        (assertEqual "should be (0, 2)" (0, 2) (D.dimensions $ D.take (-1) testData))  lengthEqualsTakeLastParam :: Test-lengthEqualsTakeLastParam = TestCase (assertEqual "should be (5, 2)" (5, 2) (D.dimensions $ D.takeLast 5 testData))+lengthEqualsTakeLastParam =+    TestCase+        (assertEqual "should be (5, 2)" (5, 2) (D.dimensions $ D.takeLast 5 testData))  lengthGreaterThanTakeLastParam :: Test-lengthGreaterThanTakeLastParam = TestCase (assertEqual "should be (26, 2)" (26, 2) (D.dimensions $ D.takeLast 30 testData))+lengthGreaterThanTakeLastParam =+    TestCase+        (assertEqual "should be (26, 2)" (26, 2) (D.dimensions $ D.takeLast 30 testData))  emptyIsZeroTakeLast :: Test-emptyIsZeroTakeLast = TestCase (assertEqual "should be (0, 0)" (0, 0) (D.dimensions $ D.takeLast 5 D.empty))+emptyIsZeroTakeLast =+    TestCase+        (assertEqual "should be (0, 0)" (0, 0) (D.dimensions $ D.takeLast 5 D.empty))  negativeIsZeroTakeLast :: Test-negativeIsZeroTakeLast = TestCase (assertEqual "should be (0, 2)" (0, 2) (D.dimensions $ D.takeLast (-1) testData))+negativeIsZeroTakeLast =+    TestCase+        (assertEqual "should be (0, 2)" (0, 2) (D.dimensions $ D.takeLast (-1) testData))  tests :: [Test] tests =
tests/Parquet.hs view
@@ -21,7 +21,19 @@         , ("bigint_col", D.fromList [0 :: Int64, 10, 0, 10, 0, 10, 0, 10])         , ("float_col", D.fromList [0 :: Float, 1.1, 0, 1.1, 0, 1.1, 0, 1.1])         , ("double_col", D.fromList [0 :: Double, 10.1, 0, 10.1, 0, 10.1, 0, 10.1])-        , ("date_string_col", D.fromList ["03/01/09" :: Text, "03/01/09", "04/01/09", "04/01/09", "02/01/09", "02/01/09", "01/01/09", "01/01/09"])+        ,+            ( "date_string_col"+            , D.fromList+                [ "03/01/09" :: Text+                , "03/01/09"+                , "04/01/09"+                , "04/01/09"+                , "02/01/09"+                , "02/01/09"+                , "01/01/09"+                , "01/01/09"+                ]+            )         , ("string_col", D.fromList (take 8 (cycle ["0" :: Text, "1"])))         ,             ( "timestamp_col"@@ -68,18 +80,450 @@ mtCarsDataset :: D.DataFrame mtCarsDataset =     D.fromNamedColumns-        [ ("model", D.fromList ["Mazda RX4" :: Text, "Mazda RX4 Wag", "Datsun 710", "Hornet 4 Drive", "Hornet Sportabout", "Valiant", "Duster 360", "Merc 240D", "Merc 230", "Merc 280", "Merc 280C", "Merc 450SE", "Merc 450SL", "Merc 450SLC", "Cadillac Fleetwood", "Lincoln Continental", "Chrysler Imperial", "Fiat 128", "Honda Civic", "Toyota Corolla", "Toyota Corona", "Dodge Challenger", "AMC Javelin", "Camaro Z28", "Pontiac Firebird", "Fiat X1-9", "Porsche 914-2", "Lotus Europa", "Ford Pantera L", "Ferrari Dino", "Maserati Bora", "Volvo 142E"])-        , ("mpg", D.fromList [21.0 :: Double, 21.0, 22.8, 21.4, 18.7, 18.1, 14.3, 24.4, 22.8, 19.2, 17.8, 16.4, 17.3, 15.2, 10.4, 10.4, 14.7, 32.4, 30.4, 33.9, 21.5, 15.5, 15.2, 13.3, 19.2, 27.3, 26.0, 30.4, 15.8, 19.7, 15.0, 21.4])-        , ("cyl", D.fromList [6 :: Int32, 6, 4, 6, 8, 6, 8, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 8, 8, 8, 8, 4, 4, 4, 8, 6, 8, 4])-        , ("disp", D.fromList [160.0 :: Double, 160.0, 108.0, 258.0, 360.0, 225.0, 360.0, 146.7, 140.8, 167.6, 167.6, 275.8, 275.8, 275.8, 472.0, 460.0, 440.0, 78.7, 75.7, 71.1, 120.1, 318.0, 304.0, 350.0, 400.0, 79.0, 120.3, 95.1, 351.0, 145.0, 301.0, 121.0])-        , ("hp", D.fromList [110 :: Int32, 110, 93, 110, 175, 105, 245, 62, 95, 123, 123, 180, 180, 180, 205, 215, 230, 66, 52, 65, 97, 150, 150, 245, 175, 66, 91, 113, 264, 175, 335, 109])-        , ("drat", D.fromList [3.9 :: Double, 3.9, 3.85, 3.08, 3.15, 2.76, 3.21, 3.69, 3.92, 3.92, 3.92, 3.07, 3.07, 3.07, 2.93, 3.0, 3.23, 4.08, 4.93, 4.22, 3.7, 2.76, 3.15, 3.73, 3.08, 4.08, 4.43, 3.77, 4.22, 3.62, 3.54, 4.11])-        , ("wt", D.fromList [2.62 :: Double, 2.875, 2.32, 3.215, 3.44, 3.46, 3.57, 3.19, 3.15, 3.44, 3.44, 4.07, 3.73, 3.78, 5.25, 5.424, 5.345, 2.2, 1.615, 1.835, 2.465, 3.52, 3.435, 3.84, 3.845, 1.935, 2.14, 1.513, 3.17, 2.77, 3.57, 2.78])-        , ("qsec", D.fromList [16.46 :: Double, 17.02, 18.61, 19.44, 17.02, 20.22, 15.84, 20.0, 22.9, 18.3, 18.9, 17.4, 17.6, 18.0, 17.98, 17.82, 17.42, 19.47, 18.52, 19.9, 20.01, 16.87, 17.3, 15.41, 17.05, 18.9, 16.7, 16.9, 14.5, 15.5, 14.6, 18.6])-        , ("vs", D.fromList [0 :: Int32, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1])-        , ("am", D.fromList [1 :: Int32, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1])-        , ("gear", D.fromList [4 :: Int32, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 4])-        , ("carb", D.fromList [4 :: Int32, 4, 1, 1, 2, 1, 4, 2, 2, 4, 4, 3, 3, 3, 4, 4, 4, 1, 2, 1, 1, 2, 2, 4, 2, 1, 2, 2, 4, 6, 8, 2])+        [+            ( "model"+            , D.fromList+                [ "Mazda RX4" :: Text+                , "Mazda RX4 Wag"+                , "Datsun 710"+                , "Hornet 4 Drive"+                , "Hornet Sportabout"+                , "Valiant"+                , "Duster 360"+                , "Merc 240D"+                , "Merc 230"+                , "Merc 280"+                , "Merc 280C"+                , "Merc 450SE"+                , "Merc 450SL"+                , "Merc 450SLC"+                , "Cadillac Fleetwood"+                , "Lincoln Continental"+                , "Chrysler Imperial"+                , "Fiat 128"+                , "Honda Civic"+                , "Toyota Corolla"+                , "Toyota Corona"+                , "Dodge Challenger"+                , "AMC Javelin"+                , "Camaro Z28"+                , "Pontiac Firebird"+                , "Fiat X1-9"+                , "Porsche 914-2"+                , "Lotus Europa"+                , "Ford Pantera L"+                , "Ferrari Dino"+                , "Maserati Bora"+                , "Volvo 142E"+                ]+            )+        ,+            ( "mpg"+            , D.fromList+                [ 21.0 :: Double+                , 21.0+                , 22.8+                , 21.4+                , 18.7+                , 18.1+                , 14.3+                , 24.4+                , 22.8+                , 19.2+                , 17.8+                , 16.4+                , 17.3+                , 15.2+                , 10.4+                , 10.4+                , 14.7+                , 32.4+                , 30.4+                , 33.9+                , 21.5+                , 15.5+                , 15.2+                , 13.3+                , 19.2+                , 27.3+                , 26.0+                , 30.4+                , 15.8+                , 19.7+                , 15.0+                , 21.4+                ]+            )+        ,+            ( "cyl"+            , D.fromList+                [ 6 :: Int32+                , 6+                , 4+                , 6+                , 8+                , 6+                , 8+                , 4+                , 4+                , 6+                , 6+                , 8+                , 8+                , 8+                , 8+                , 8+                , 8+                , 4+                , 4+                , 4+                , 4+                , 8+                , 8+                , 8+                , 8+                , 4+                , 4+                , 4+                , 8+                , 6+                , 8+                , 4+                ]+            )+        ,+            ( "disp"+            , D.fromList+                [ 160.0 :: Double+                , 160.0+                , 108.0+                , 258.0+                , 360.0+                , 225.0+                , 360.0+                , 146.7+                , 140.8+                , 167.6+                , 167.6+                , 275.8+                , 275.8+                , 275.8+                , 472.0+                , 460.0+                , 440.0+                , 78.7+                , 75.7+                , 71.1+                , 120.1+                , 318.0+                , 304.0+                , 350.0+                , 400.0+                , 79.0+                , 120.3+                , 95.1+                , 351.0+                , 145.0+                , 301.0+                , 121.0+                ]+            )+        ,+            ( "hp"+            , D.fromList+                [ 110 :: Int32+                , 110+                , 93+                , 110+                , 175+                , 105+                , 245+                , 62+                , 95+                , 123+                , 123+                , 180+                , 180+                , 180+                , 205+                , 215+                , 230+                , 66+                , 52+                , 65+                , 97+                , 150+                , 150+                , 245+                , 175+                , 66+                , 91+                , 113+                , 264+                , 175+                , 335+                , 109+                ]+            )+        ,+            ( "drat"+            , D.fromList+                [ 3.9 :: Double+                , 3.9+                , 3.85+                , 3.08+                , 3.15+                , 2.76+                , 3.21+                , 3.69+                , 3.92+                , 3.92+                , 3.92+                , 3.07+                , 3.07+                , 3.07+                , 2.93+                , 3.0+                , 3.23+                , 4.08+                , 4.93+                , 4.22+                , 3.7+                , 2.76+                , 3.15+                , 3.73+                , 3.08+                , 4.08+                , 4.43+                , 3.77+                , 4.22+                , 3.62+                , 3.54+                , 4.11+                ]+            )+        ,+            ( "wt"+            , D.fromList+                [ 2.62 :: Double+                , 2.875+                , 2.32+                , 3.215+                , 3.44+                , 3.46+                , 3.57+                , 3.19+                , 3.15+                , 3.44+                , 3.44+                , 4.07+                , 3.73+                , 3.78+                , 5.25+                , 5.424+                , 5.345+                , 2.2+                , 1.615+                , 1.835+                , 2.465+                , 3.52+                , 3.435+                , 3.84+                , 3.845+                , 1.935+                , 2.14+                , 1.513+                , 3.17+                , 2.77+                , 3.57+                , 2.78+                ]+            )+        ,+            ( "qsec"+            , D.fromList+                [ 16.46 :: Double+                , 17.02+                , 18.61+                , 19.44+                , 17.02+                , 20.22+                , 15.84+                , 20.0+                , 22.9+                , 18.3+                , 18.9+                , 17.4+                , 17.6+                , 18.0+                , 17.98+                , 17.82+                , 17.42+                , 19.47+                , 18.52+                , 19.9+                , 20.01+                , 16.87+                , 17.3+                , 15.41+                , 17.05+                , 18.9+                , 16.7+                , 16.9+                , 14.5+                , 15.5+                , 14.6+                , 18.6+                ]+            )+        ,+            ( "vs"+            , D.fromList+                [ 0 :: Int32+                , 0+                , 1+                , 1+                , 0+                , 1+                , 0+                , 1+                , 1+                , 1+                , 1+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 1+                , 1+                , 1+                , 1+                , 0+                , 0+                , 0+                , 0+                , 1+                , 0+                , 1+                , 0+                , 0+                , 0+                , 1+                ]+            )+        ,+            ( "am"+            , D.fromList+                [ 1 :: Int32+                , 1+                , 1+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 0+                , 1+                , 1+                , 1+                , 0+                , 0+                , 0+                , 0+                , 0+                , 1+                , 1+                , 1+                , 1+                , 1+                , 1+                , 1+                ]+            )+        ,+            ( "gear"+            , D.fromList+                [ 4 :: Int32+                , 4+                , 4+                , 3+                , 3+                , 3+                , 3+                , 4+                , 4+                , 4+                , 4+                , 3+                , 3+                , 3+                , 3+                , 3+                , 3+                , 4+                , 4+                , 4+                , 3+                , 3+                , 3+                , 3+                , 3+                , 4+                , 5+                , 5+                , 5+                , 5+                , 5+                , 4+                ]+            )+        ,+            ( "carb"+            , D.fromList+                [ 4 :: Int32+                , 4+                , 1+                , 1+                , 2+                , 1+                , 4+                , 2+                , 2+                , 4+                , 4+                , 3+                , 3+                , 3+                , 4+                , 4+                , 4+                , 1+                , 2+                , 1+                , 1+                , 2+                , 2+                , 4+                , 2+                , 1+                , 2+                , 2+                , 4+                , 6+                , 8+                , 2+                ]+            )         ]  mtCars :: Test