{-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}

-- | For measuring length, the distance between two points.
--
-- @
--     Data.Measure.Length\> 27 ..> miles <.. metres
--     43452.288
-- @
--
-- @
--     Data.Measure.Length\> 14.6 ..> kilometres <.. nauticalMiles
--     7.883369330453564
-- @
--
-- @
--     Data.Measure.Length\> 14.6 ..> parsecs <.. kilometres
--     4.5050896172e14
-- @
module Data.Measure.Length(LengthMeasure,
                           nm,
                           yoctometres,
                           zeptometres,
                           attometres,
                           femtometres,
                           fermis,
                           picometres,
                           nanometres,
                           micrometres,
                           millimetres,
                           centimetres,
                           decimetres,
                           metres,
                           kilometres,
                           au,
                           lightSeconds,
                           lightMinutes,
                           lightHours,
                           lightDays,
                           lightYears,
                           parsecs,
                           ells,
                           inches,
                           thou,
                           feet,
                           yards,
                           quarters,
                           miles,
                           nauticalMiles,
                           nauticalMilesAdmiralty,
                           fathoms,
                           cables,
                           chains,
                           furlongs,
                           Length,
                           nm',
                           length') where

import Data.Measure.RelativeDouble
import Data.Measure.ConvertDouble

-- | A ratio of measurement of length to nanometres.
newtype LengthMeasure = Nanometre {
  nm :: Double -- ^ The ratio of measurement of length in nanometres.
} deriving (Eq, Ord, Enum, Num, Fractional, Floating)

instance Show LengthMeasure where
  show = show . nm

-- | A measurement of length, 10 ^ -24 metres.
yoctometres :: LengthMeasure
yoctometres = zeptometres ./. 1000

-- | A measurement of length, 10 ^ -21 metres.
zeptometres :: LengthMeasure
zeptometres = attometres ./. 1000

-- | A measurement of length, 10 ^ -18 metres.
attometres :: LengthMeasure
attometres = femtometres ./. 1000

-- | A measurement of length, 10 ^ -15 metres.
femtometres :: LengthMeasure
femtometres = picometres ./. 1000

-- | A measurement of length, 10 ^ -15 metres.
fermis :: LengthMeasure
fermis = femtometres

-- | A measurement of length, 10 ^ -12 metres.
picometres :: LengthMeasure
picometres = nanometres ./. 1000

-- | A measurement of length, 10 ^ -9 metres.
nanometres :: LengthMeasure
nanometres = Nanometre 1

-- | A measurement of length, 10 ^ -6 metres.
micrometres :: LengthMeasure
micrometres = nanometres .*. 1000

-- | A measurement of length, 10 ^ -3 metres.
millimetres :: LengthMeasure
millimetres = micrometres .*. 1000

-- | A measurement of length, 10 ^ -2 metres.
centimetres :: LengthMeasure
centimetres = millimetres .*. 10

-- | A measurement of length, 10 ^ -1 metres.
decimetres :: LengthMeasure
decimetres = centimetres .*. 10

-- | A measurement of length.
metres :: LengthMeasure
metres = millimetres .*. 1000

-- | A measurement of length, 10 ^ 3 metres.
kilometres :: LengthMeasure
kilometres = metres .*. 1000

-- | A measurement of length, Astronomical Units.
au :: LengthMeasure
au = metres .*. 149597871464

-- | A measurement of length, the distance light travels in a vaccuum in one second.
lightSeconds :: LengthMeasure
lightSeconds = metres .*. 299792458

-- | A measurement of length, the distance light travels in a vaccuum in one minute.
lightMinutes :: LengthMeasure
lightMinutes = lightSeconds .*. 60

-- | A measurement of length, the distance light travels in a vaccuum in one hour.
lightHours :: LengthMeasure
lightHours = lightMinutes .*. 60

-- | A measurement of length, the distance light travels in a vaccuum in one day.
lightDays :: LengthMeasure
lightDays = lightHours .*. 24

-- | A measurement of length, the distance light travels in a vaccuum in one (Julian) year.
lightYears :: LengthMeasure
lightYears = metres .*. 9460730472580800

-- | A measurement of length, parallax of one arcsecond.
parsecs :: LengthMeasure
parsecs = kilometres .*. 30856778200000

-- | A measurement of length, approximating the length of a man's arm.
ells :: LengthMeasure
ells = metres .*. 1.143

-- | A measurement of length, 25.4 millimetres.
inches :: LengthMeasure
inches = millimetres .*. 25.4

-- | A measurement of length, 0.0254 millimetres.
thou :: LengthMeasure
thou = inches ./. 1000

-- | A measurement of length, 12 inches.
feet :: LengthMeasure
feet = inches .*. 12

-- | A measurement of length, 3 feet.
yards :: LengthMeasure
yards = feet .*. 3

-- | A measurement of length, a quarter of a yard.
quarters :: LengthMeasure
quarters = yards ./. 4

-- | A measurement of length, 1760 yards.
miles :: LengthMeasure
miles = yards .*. 1760

-- | A measurement of length, 1852 metres.
nauticalMiles :: LengthMeasure
nauticalMiles = metres .*. 1852

-- | A measurement of length, 6080 feet.
nauticalMilesAdmiralty :: LengthMeasure
nauticalMilesAdmiralty = feet .*. 6080

-- | A measurement of length, 1828.8 millimetres.
fathoms :: LengthMeasure
fathoms = micrometres .*. 1828800

-- | A measurement of length, 100 fathoms.
cables :: LengthMeasure
cables = fathoms .*. 100

-- | A measurement of length, 66 feet.
chains :: LengthMeasure
chains = feet .*. 66

-- | A measurement of length, 10 chains.
furlongs :: LengthMeasure
furlongs = chains .*. 10

-- | A measurement of length in nanometres.
newtype Length = Length {
  nm' :: Double -- ^ The number of nanometres.
} deriving (Eq, Ord, Enum, Num, Fractional, Floating)

-- | Construct a @Length@ from a floating-point value.
length' :: Double -> Length
length' = Length

instance Show Length where
  show (Length d) = show d ++ "nm"

instance RelativeDouble LengthMeasure where
  Nanometre n .*. r = Nanometre (n * r)
  Nanometre n ./. r = Nanometre (n / r)

instance ConvertDouble LengthMeasure Length where
  x ..> n = Length (x * nm n)
  n <.. x = nm' n / nm x