-- | This package is used to create and manipulate physical quantities, which
-- are a numerical value associated with a unit of measurement.
--
-- In this package, values with units are represented with the Quantity type.
-- Included is an expression parser and a huge list of predefined quantities
-- with which to parse strings into a Quantity datatype. Once created, a
-- quantity can be converted to different units or queried for its
-- dimensionality. A user can also operate on quantities arithmetically, and
-- doing so uses automatic unit conversion and simplification.


module Data.Quantities
       (
         -- * Constructors
         -- $constructors
         fromString
       , unitsFromString
       , Definitions
       , Quantity
       , magnitude
       , units
       , CompoundUnit
         -- * Conversion
         -- $conversion
       , convert
       , convertBase
       , dimensionality
         -- * Quantity arithmetic
         -- $arithmetic
       , addQuants
       , subtractQuants
       , multiplyQuants
       , divideQuants
       , exptQuants
         -- * Custom definitions
         -- $custom-defs
       , fromString'
       , readDefinitions
       , defaultDefString
         -- * Error type
       , QuantityError(..)
       , QuantityComputation
       ) where


import Data.Quantities.Constructors (fromString, fromString', unitsFromString)
import Data.Quantities.Convert (convert, convertBase, addQuants, subtractQuants,
                                dimensionality)
import Data.Quantities.Data
import Data.Quantities.Definitions (readDefinitions)
import Data.Quantities.DefaultUnits (defaultDefString)


-- $setup
-- >>> import Control.Applicative


-- $constructors
--
-- Currently, one constructor is supported to create quantities: 'fromString'.
-- There is an included expression parser that can parse values and strings
-- corresponding to builtin units. To view defined unit types, look at the
-- /source code/ for 'defaultDefString'.

-- $conversion
--
-- These functions are used to convert quantities from one unit type to
-- another.


-- $arithmetic
--
-- Once created, quantities can be manipulated using the included arithmetic
-- functions.
--
-- >>> let (Right x) = fromString "m/s"
-- >>> let (Right y) = fromString "mile/hr"
-- >>> x `multiplyQuants` y
-- 1.0 meter mile / hour / second
-- >>> x `divideQuants` y
-- 1.0 hour meter / mile / second
-- >>> x `addQuants` y
-- Right 1.4470399999999999 meter / second
-- >>> x `subtractQuants` y
-- Right 0.55296 meter / second
-- >>> x `exptQuants` 1.5
-- 1.0 meter ** 1.5 / second ** 1.5
--
-- The functions 'multiplyQuants', 'divideQuants', and 'exptQuants' change
-- units, and the units of the result are reduced to simplest terms.
--
-- >>> x `divideQuants` x
-- 1.0
-- >>> fmap (multiplyQuants x) $ fromString "s"
-- Right 1.0 meter
-- >>> x `exptQuants` 0
-- 1.0


-- $custom-defs
--
-- You don't have to use the default definitions provided by
-- 'defaultDefString'. Here is an example of adding a new unit called
-- @metric_foot@.
--
-- >>> let myDefString = defaultDefString ++ "\n" ++ "metric_foot = 300mm"
-- >>> let (Right d') = readDefinitions myDefString
-- >>> let myFromString = fromString' d'
-- >>> myFromString "metric_foot"
-- Right 1.0 metric_foot
-- >>> convertBase <$> myFromString "metric_foot"
-- Right 0.3 meter
--
-- It is usually much easier to copy the source code for 'defaultDefString' and
-- add your definitions in the appropriate spot (for example, put @metric_foot@
-- next to the other unit definitions). Then, use 'fromString'' to create your
-- Quantity constructor.
--
-- NOTE: It is very important not to perform conversions on two quantities from
-- different Definitions. Most of the error checking for undefined units is
-- done when a unit is created, and not when performing conversions. We try to
-- catch when different definitions are used.
--
-- >>> let (Right m)  = fromString "m"
-- >>> let (Right ft) = myFromString "ft"
-- >>> convert m (units ft)
-- Left (DifferentDefinitionsError meter foot)