content: {-# LANGUAGE PolyKinds, DataKinds, TypeOperators, FlexibleInstances,
               ScopedTypeVariables, FlexibleContexts, ConstraintKinds, CPP,
               UndecidableInstances #-}
  
  #if __GLASGOW_HASKELL__ < 709
  {-# LANGUAGE OverlappingInstances #-}
  #endif
  
  #if __GLASGOW_HASKELL__ >= 900
  {-# OPTIONS_GHC -Wno-star-is-type #-}
  #endif
  
  {-# OPTIONS_GHC -fno-warn-orphans #-}
  
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  Data.Metrology.Show
  -- Copyright   :  (C) 2013 Richard Eisenberg
  -- License     :  BSD-style (see LICENSE)
  -- Maintainer  :  Richard Eisenberg (rae@cs.brynmawr.edu)
  -- Stability   :  experimental
  -- Portability :  non-portable
  --
  -- This module defines 'Show' instance for quantities. The show instance
  -- prints out the number stored internally with its correct units. To print
  -- out quantities with specific units use the function `showIn`.
  -----------------------------------------------------------------------------
  
  module Data.Metrology.Show () where
  
  import Data.Proxy (Proxy(..))
  import Data.List
  import Data.Singletons (sing, SingI)
  
  import Data.Metrology.Factor
  import Data.Metrology.Qu
  import Data.Metrology.Z
  import Data.Metrology.LCSU
  
  class ShowUnitFactor (dims :: [Factor *]) where
    showDims :: Bool   -- take absolute value of exponents?
             -> Proxy dims -> ([String], [String])
  
  instance ShowUnitFactor '[] where
    showDims _ _ = ([], [])
  
  instance (ShowUnitFactor rest, Show unit, SingI z)
           => ShowUnitFactor (F unit z ': rest) where
    showDims take_abs _ =
      let (nums, denoms) = showDims take_abs (Proxy :: Proxy rest)
          baseStr        = show (undefined :: unit)
          power          = szToInt (sing :: Sing z)
          abs_power      = if take_abs then abs power else power
          str            = if abs_power == 1
                           then baseStr
                           else baseStr ++ "^" ++ (show abs_power) in
      case compare power 0 of
        LT -> (nums, str : denoms)
        EQ -> (nums, denoms)
        GT -> (str : nums, denoms)
  
  showFactor :: ShowUnitFactor dimspec => Proxy dimspec -> String
  showFactor p
    = let (nums, denoms) = mapPair (build_string . sort) $ showDims True p in
      case (length nums, length denoms) of
        (0, 0) -> ""
        (_, 0) -> " " ++ nums
        (0, _) -> " " ++ build_string (snd (showDims False p))
        (_, _) -> " " ++ nums ++ "/" ++ denoms
    where
      mapPair :: (a -> b) -> (a, a) -> (b, b)
      mapPair f (x, y) = (f x, f y)
  
      build_string :: [String] -> String
      build_string [] = ""
      build_string [s] = s
      build_string s = "(" ++ build_string_helper s ++ ")"
  
      build_string_helper :: [String] -> String
      build_string_helper [] = ""
      build_string_helper [s] = s
      build_string_helper (h:t) = h ++ " * " ++ build_string_helper t
  
  instance
  #if __GLASGOW_HASKELL__ >= 709
      {-# OVERLAPPABLE #-}
  #endif
      (ShowUnitFactor (LookupList dims lcsu), Show n)
      => Show (Qu dims lcsu n) where
    show (Qu d) = show d ++
                  (showFactor (Proxy :: Proxy (LookupList dims lcsu)))
  