packages feed

hmt-0.18: Music/Theory/Show.hs

-- | Show functions.
module Music.Theory.Show where

import Data.Char {- base -}
import Data.List {- base -}
import Data.Ratio {- base -}
import Numeric {- base -}

import qualified Music.Theory.List as T {- hmt -}
import qualified Music.Theory.Math as T {- hmt -}
import qualified Music.Theory.Math.Convert as T {- hmt -}

-- * DIFF

-- | Show positive and negative values always with sign, maybe show zero, maybe right justify.
--
-- > map (num_diff_str_opt (True,2)) [-2,-1,0,1,2] == ["-2","-1"," 0","+1","+2"]
num_diff_str_opt :: (Ord a, Num a, Show a) => (Bool,Int) -> a -> String
num_diff_str_opt (wr_0,k) n =
  let r = case compare n 0 of
            LT -> '-' : show (abs n)
            EQ -> if wr_0 then "0" else ""
            GT -> '+' : show n
  in if k > 0 then T.pad_left ' ' k r else r

-- | Show /only/ positive and negative values, always with sign.
--
-- > map num_diff_str [-2,-1,0,1,2] == ["-2","-1","","+1","+2"]
-- > map show [-2,-1,0,1,2] == ["-2","-1","0","1","2"]
num_diff_str :: (Num a, Ord a, Show a) => a -> String
num_diff_str = num_diff_str_opt (False,0)

-- * RATIONAL

-- | Pretty printer for 'Rational' using @/@ and eliding denominators of @1@.
--
-- > map rational_pp [1,3/2,5/4,2] == ["1","3/2","5/4","2"]
rational_pp :: (Show a,Integral a) => Ratio a -> String
rational_pp r =
    let n = numerator r
        d = denominator r
    in if d == 1
       then show n
       else concat [show n,"/",show d]

-- | Pretty print ratio as @:@ separated integers, if /nil/ is True elide unit denominator.
--
-- > map (ratio_pp_opt True) [1,3/2,2] == ["1","3:2","2"]
ratio_pp_opt :: Bool -> Rational -> String
ratio_pp_opt nil r =
  let f :: (Integer,Integer) -> String
      f (n,d) = concat [show n,":",show d]
  in case T.rational_nd r of
       (n,1) -> if nil then show n else f (n,1)
       x -> f x

-- | Pretty print ratio as @:@ separated integers.
--
-- > map ratio_pp [1,3/2,2] == ["1:1","3:2","2:1"]
ratio_pp :: Rational -> String
ratio_pp = ratio_pp_opt False

-- | Show rational to /n/ decimal places.
--
-- > let r = approxRational pi 1e-100
-- > r == 884279719003555 / 281474976710656
-- > show_rational_decimal 12 r == "3.141592653590"
-- > show_rational_decimal 3 (-100) == "-100.000"
show_rational_decimal :: Int -> Rational -> String
show_rational_decimal n = double_pp n . fromRational

-- * REAL

-- | Show /r/ as float to /k/ places.
--
-- > real_pp 4 (1/3 :: Rational) == "0.3333"
-- > map (real_pp 4) [1,1.1,1.12,1.123,1.1234,1/0,sqrt (-1)]
real_pp :: Real t => Int -> t -> String
real_pp k = realfloat_pp k . T.real_to_double

-- | Variant that writes `∞` for Infinity.
--
-- > putStrLn $ unwords $ map (real_pp_unicode 4) [1/0,-1/0]
real_pp_unicode :: Real t => Int -> t -> [Char]
real_pp_unicode k r =
  case real_pp k r of
    "Infinity" -> "∞"
    "-Infinity" -> "-∞"
    s -> s

-- | Prints /n/ as integral or to at most /k/ decimal places. Does not print -0.
--
-- > real_pp_trunc 4 (1/3 :: Rational) == "0.3333"
-- > map (real_pp_trunc 4) [1,1.1,1.12,1.123,1.1234] == ["1","1.1","1.12","1.123","1.1234"]
-- > map (real_pp_trunc 4) [1.00009,1.00001] == ["1.0001","1"]
-- > map (real_pp_trunc 2) [59.999,60.001,-0.00,-0.001]
real_pp_trunc :: Real t => Int -> t -> String
real_pp_trunc k n =
  case break (== '.') (real_pp k n) of
    (i,[]) -> i
    (i,j) -> case dropWhileEnd (== '0') j of
               "." -> if i == "-0" then "0" else i
               z -> i ++ z

-- | Variant of 'showFFloat'.  The 'Show' instance for floats resorts
-- to exponential notation very readily.
--
-- > [show 0.01,realfloat_pp 2 0.01] == ["1.0e-2","0.01"]
-- > map (realfloat_pp 4) [1,1.1,1.12,1.123,1.1234,1/0,sqrt (-1)]
realfloat_pp :: RealFloat a => Int -> a -> String
realfloat_pp k n = showFFloat (Just k) n ""

-- | Type specialised 'realfloat_pp'.
float_pp :: Int -> Float -> String
float_pp = realfloat_pp

-- | Type specialised 'realfloat_pp'.
--
-- > double_pp 4 0
double_pp :: Int -> Double -> String
double_pp = realfloat_pp

-- * BIN

-- | Read binary integer.
--
-- > unwords (map (show_bin Nothing) [0 .. 7]) == "0 1 10 11 100 101 110 111"
-- > unwords (map (show_bin (Just 3)) [0 .. 7]) == "000 001 010 011 100 101 110 111"
show_bin :: (Integral i,Show i) => Maybe Int -> i -> String
show_bin k n = (maybe id (\x -> T.pad_left '0' x) k) (showIntAtBase 2 intToDigit n "")