packages feed

formatn (empty) → 0.0.1

raw patch · 5 files changed

+328/−0 lines, 5 filesdep +attoparsecdep +basedep +containerssetup-changed

Dependencies added: attoparsec, base, containers, doctest, foldl, formatn, generic-lens, numhask, scientific, tdigest, text, transformers

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Tony Day (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Tony Day nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ formatn.cabal view
@@ -0,0 +1,64 @@+cabal-version:  2.4+name:           formatn+version:        0.0.1+synopsis:       Number text formatting.+description:    This package provides support for common number formatting patterns.+    .+    == Usage+    .+    > import Data.FormatN+    .+    See doctests for functionality and range.++category:       Development+homepage:       https://github.com/tonyday567/formatn#readme+bug-reports:    https://github.com/tonyday567/formatn/issues+author:         Tony Day+maintainer:     tonyday567@gmail.com+copyright:      2016 Tony Day+license:        BSD-3-Clause+license-file:   LICENSE+build-type:     Simple++source-repository head+  type: git+  location: https://github.com/tonyday567/formatn++library+  hs-source-dirs:+    src+  default-extensions:+  ghc-options:+    -Wall+    -Wcompat+    -Wincomplete-record-updates+    -Wincomplete-uni-patterns+    -Wredundant-constraints+  build-depends:+    attoparsec >= 0.13 && < 0.14,+    base >=4.7 && <5,+    containers >= 0.6,+    foldl >= 1.4,+    generic-lens >= 1.2 && < 3.0,+    numhask >= 0.7 && < 0.8,+    scientific >= 0.3,+    tdigest >= 0.2,+    text >= 1.2,+    transformers >= 0.5+  exposed-modules:+    Data.FormatN+  other-modules:+  default-language: Haskell2010++test-suite test+  type: exitcode-stdio-1.0+  main-is: test.hs+  hs-source-dirs:+      test+  default-extensions:+  build-depends:+    base >=4.7 && <5,+    doctest >= 0.16 && < 0.18,+    formatn,+    numhask >= 0.7 && < 0.8,+  default-language: Haskell2010
+ src/Data/FormatN.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RebindableSyntax #-}+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_GHC -Wall #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+{-# OPTIONS_GHC -fno-warn-type-defaults #-}++-- | Formatting of 'Double's.+module Data.FormatN+  ( FormatN (..),+    defaultFormatN,+    fromFormatN,+    toFormatN,+    fixed,+    decimal,+    prec,+    comma,+    expt,+    dollar,+    formatN,+    precision,+    formatNs,+  )+where++import Data.Generics.Labels ()+import Data.List (nub)+import Data.Scientific+import qualified Data.Text as Text+import NumHask.Prelude++-- | Wrapper for the various formatting options.+--+-- >>> defaultFormatN+-- FormatComma 2+data FormatN+  = FormatFixed Int+  | FormatDecimal Int+  | FormatComma Int+  | FormatExpt Int+  | FormatPrec Int+  | FormatDollar Int+  | FormatPercent Int+  | FormatNone+  deriving (Eq, Show, Generic)++-- | The official format+defaultFormatN :: FormatN+defaultFormatN = FormatComma 2++-- | make text+formatN :: FormatN -> Double -> Text+formatN (FormatFixed n) x = fixed n x+formatN (FormatDecimal n) x = decimal n x+formatN (FormatPrec n) x = prec n x+formatN (FormatComma n) x = comma n x+formatN (FormatExpt n) x = expt n x+formatN (FormatDollar n) x = dollar n x+formatN (FormatPercent n) x = percent n x+formatN FormatNone x = pack (show x)++-- | type textifier+fromFormatN :: (IsString s) => FormatN -> s+fromFormatN (FormatFixed _) = "Fixed"+fromFormatN (FormatDecimal _) = "Decimal"+fromFormatN (FormatComma _) = "Comma"+fromFormatN (FormatExpt _) = "Expt"+fromFormatN (FormatPrec _) = "Prec"+fromFormatN (FormatDollar _) = "Dollar"+fromFormatN (FormatPercent _) = "Percent"+fromFormatN FormatNone = "None"++-- | type readifier+toFormatN :: (Eq s, IsString s) => s -> Int -> FormatN+toFormatN "Fixed" n = FormatFixed n+toFormatN "Decimal" n = FormatDecimal n+toFormatN "Comma" n = FormatComma n+toFormatN "Expt" n = FormatExpt n+toFormatN "Prec" n = FormatPrec n+toFormatN "Dollar" n = FormatDollar n+toFormatN "Percent" n = FormatPercent n+toFormatN "None" _ = FormatNone+toFormatN _ _ = FormatNone++-- | to x decimal places+--+-- >>> fixed 2 1+-- "1.00"+--+-- >>> fixed 2 0.001+-- "0.00"+fixed :: Int -> Double -> Text+fixed x n = pack $ formatScientific Fixed (Just x) (fromFloatDigits n)++-- | scientific exponential+--+-- >>> expt 2 1234+-- "1.23e3"+expt :: Int -> Double -> Text+expt x n = pack $ formatScientific Exponent (Just x) (fromFloatDigits n)++-- | round to n significant figures+--+-- >>> roundSig 2 1234+-- 1230.0+--+-- >>> roundSig 2 0.001234+-- 1.23e-3+roundSig :: Int -> Double -> Scientific+roundSig n x = scientific r' (e - length ds0)+  where+    (ds, e) = toDecimalDigits $ fromFloatDigits x+    (ds0, ds1) = splitAt (n + 1) ds+    r =+      (fromIntegral $ foldl' (\x a -> x * 10 + a) 0 ds0 :: Double)+        + fromIntegral (foldl' (\x a -> x * 10 + a) 0 ds1) / (10.0 ^ (length ds1 :: Int))+    r' = round r :: Integer++-- | format numbers between 0.001 and 1,000,000 using digit and comma notation and exponential outside this range, with x significant figures.+--+-- >>> prec 1 0.00234+-- "0.0023"+--+-- >>> prec 1 0.000023+-- "2.3e-5"+--+-- >>> prec 1 123+-- "120"+--+-- >>> prec 1 123456+-- "120000"+--+-- >>> prec 1 1234567+-- "1.2e6"+prec :: Int -> Double -> Text+prec n x+  | x < 0 = "-" <> prec n (- x)+  | x == 0 = "0"+  | x < 0.001 = expt n x+  | x > 1e6 = expt n x+  | otherwise = decimal n (toRealFloat x')+  where+    x' = maybe fromFloatDigits roundSig (Just n) x++-- | round to n significant figures and always use decimal notation+--+-- >>> decimal 2 0.000001234+-- "0.00000123"+--+-- >>> decimal 2 1234567+-- "1230000"+decimal :: Int -> Double -> Text+decimal n x = x''+  where+    x' = pack $ formatScientific Fixed Nothing $ maybe fromFloatDigits roundSig (Just n) x+    x'' = (\x -> bool x' (fst x) (snd x == ".0")) $ Text.breakOn "." x'++-- | add commas format for numbers above 1,000 but below 1 million, otherwise use prec.+--+-- >>> comma 2 1234+-- "1,230"+comma :: Int -> Double -> Text+comma n x+  | x < 0 = "-" <> comma n (- x)+  | x < 1000 || x > 1e6 = prec n x+  | otherwise = addcomma (prec n x)+  where+    addcomma x = uncurry (<>) . first (Text.reverse . Text.intercalate "," . Text.chunksOf 3 . Text.reverse) $ Text.breakOn "." x++-- | dollars and cents, always decimal notation+--+-- >>> dollar 2 1234+-- "$1,230"+--+-- >>> dollar 2 0.01234+-- "$0.0123"+dollar :: Int -> Double -> Text+dollar n x+  | x < 0 = "-" <> dollar n (- x)+  | otherwise = "$" <> comma n x++-- | fixed percent, always decimal notation+--+-- >>> percent 2 0.001234+-- "0.123%"+percent :: Int -> Double -> Text+percent n x = (<> "%") $ decimal n (100 * x)++-- | Provide formatted text for a list of numbers so that they are just distinguished.  'precision commas 2 ticks' means use as much precision as is needed for them to be distinguished, but with at least 2 significant figures.+precision :: (Int -> Double -> Text) -> Int -> [Double] -> [Text]+precision f n0 xs =+  precLoop f n0 xs+  where+    precLoop f' n xs' =+      let s = f' n <$> xs'+       in if s == nub s || n > 4+            then s+            else precLoop f' (n + 1) xs'++-- | Consistently format a list of doubles.+formatNs :: FormatN -> [Double] -> [Text]+formatNs (FormatFixed n) xs = precision fixed n xs+formatNs (FormatDecimal n) xs = precision decimal n xs+formatNs (FormatPrec n) xs = precision prec n xs+formatNs (FormatComma n) xs = precision comma n xs+formatNs (FormatExpt n) xs = precision expt n xs+formatNs (FormatDollar n) xs = precision dollar n xs+formatNs (FormatPercent n) xs = precision percent n xs+formatNs FormatNone xs = pack . show <$> xs
+ test/test.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE RebindableSyntax #-}+{-# OPTIONS_GHC -Wall #-}+{-# OPTIONS_GHC -fno-warn-unused-imports #-}++module Main where++import NumHask.Prelude+import Test.DocTest++main :: IO ()+main =+  doctest+  [ "src/Data/FormatN.hs"+  ]