-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree.


-- | This module shadows names of some units previously defined in
-- 'Duckling.Distance.Types'.
-- Therefore, the units from that module must be used qualified by module.
module Duckling.DistanceUnits.Types
  ( ContextualDistance (..)
  , toSystemUnit
  , toRawUnit
  ) where

import Data.Semigroup
import Data.Tuple.Extra (both)
import Prelude

import qualified Duckling.Distance.Types as TDist


-- | Supports deferred resolution of ambiguous units.
-- Note that this sum type cannot be simply replaced by
-- "Maybe (ContextualDistance Double DeferrableUnit)"
-- (with "Nothing" representing "Nonrelatable").
-- See "NOTE A" below.
data ContextualDistance
  = Nonrelatable
    -- ^ If two different ambiguous units were to be composed, then
    -- it would not be possible to decide which to resolve to. As of today,
    -- this won't happen since for now "M" is the lone ambiguous unit.
  | ContextualDistance Double DeferrableUnit

-- | This represents Units that have not yet been established as unrelatable.
-- The "Definite" constructor is purposely listed in front of "Ambiguous",
-- so that it will be preferred when taking choosing the "min" between
-- an "Ambiguous" and "Definite" unit.
data DeferrableUnit
  = Definite SystemUnit
  | Ambiguous AmbiguousUnit
  deriving (DeferrableUnit -> DeferrableUnit -> Bool
(DeferrableUnit -> DeferrableUnit -> Bool)
-> (DeferrableUnit -> DeferrableUnit -> Bool) -> Eq DeferrableUnit
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DeferrableUnit -> DeferrableUnit -> Bool
$c/= :: DeferrableUnit -> DeferrableUnit -> Bool
== :: DeferrableUnit -> DeferrableUnit -> Bool
$c== :: DeferrableUnit -> DeferrableUnit -> Bool
Eq, Eq DeferrableUnit
Eq DeferrableUnit
-> (DeferrableUnit -> DeferrableUnit -> Ordering)
-> (DeferrableUnit -> DeferrableUnit -> Bool)
-> (DeferrableUnit -> DeferrableUnit -> Bool)
-> (DeferrableUnit -> DeferrableUnit -> Bool)
-> (DeferrableUnit -> DeferrableUnit -> Bool)
-> (DeferrableUnit -> DeferrableUnit -> DeferrableUnit)
-> (DeferrableUnit -> DeferrableUnit -> DeferrableUnit)
-> Ord DeferrableUnit
DeferrableUnit -> DeferrableUnit -> Bool
DeferrableUnit -> DeferrableUnit -> Ordering
DeferrableUnit -> DeferrableUnit -> DeferrableUnit
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: DeferrableUnit -> DeferrableUnit -> DeferrableUnit
$cmin :: DeferrableUnit -> DeferrableUnit -> DeferrableUnit
max :: DeferrableUnit -> DeferrableUnit -> DeferrableUnit
$cmax :: DeferrableUnit -> DeferrableUnit -> DeferrableUnit
>= :: DeferrableUnit -> DeferrableUnit -> Bool
$c>= :: DeferrableUnit -> DeferrableUnit -> Bool
> :: DeferrableUnit -> DeferrableUnit -> Bool
$c> :: DeferrableUnit -> DeferrableUnit -> Bool
<= :: DeferrableUnit -> DeferrableUnit -> Bool
$c<= :: DeferrableUnit -> DeferrableUnit -> Bool
< :: DeferrableUnit -> DeferrableUnit -> Bool
$c< :: DeferrableUnit -> DeferrableUnit -> Bool
compare :: DeferrableUnit -> DeferrableUnit -> Ordering
$ccompare :: DeferrableUnit -> DeferrableUnit -> Ordering
$cp1Ord :: Eq DeferrableUnit
Ord)

-- | These measurement-system-specific units exist to disambiguate
-- the \"M\" of the 'Unit' type and to maintain the measurement-system context
-- for resolving to the appropriate precision.
--
-- It's also just handy to have separate programmatically accessible lists
-- of units for each type of measurement system.
data SystemUnit
  = Metric MetricUnit
  | Imperial ImperialUnit
  deriving (SystemUnit -> SystemUnit -> Bool
(SystemUnit -> SystemUnit -> Bool)
-> (SystemUnit -> SystemUnit -> Bool) -> Eq SystemUnit
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SystemUnit -> SystemUnit -> Bool
$c/= :: SystemUnit -> SystemUnit -> Bool
== :: SystemUnit -> SystemUnit -> Bool
$c== :: SystemUnit -> SystemUnit -> Bool
Eq, Eq SystemUnit
Eq SystemUnit
-> (SystemUnit -> SystemUnit -> Ordering)
-> (SystemUnit -> SystemUnit -> Bool)
-> (SystemUnit -> SystemUnit -> Bool)
-> (SystemUnit -> SystemUnit -> Bool)
-> (SystemUnit -> SystemUnit -> Bool)
-> (SystemUnit -> SystemUnit -> SystemUnit)
-> (SystemUnit -> SystemUnit -> SystemUnit)
-> Ord SystemUnit
SystemUnit -> SystemUnit -> Bool
SystemUnit -> SystemUnit -> Ordering
SystemUnit -> SystemUnit -> SystemUnit
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: SystemUnit -> SystemUnit -> SystemUnit
$cmin :: SystemUnit -> SystemUnit -> SystemUnit
max :: SystemUnit -> SystemUnit -> SystemUnit
$cmax :: SystemUnit -> SystemUnit -> SystemUnit
>= :: SystemUnit -> SystemUnit -> Bool
$c>= :: SystemUnit -> SystemUnit -> Bool
> :: SystemUnit -> SystemUnit -> Bool
$c> :: SystemUnit -> SystemUnit -> Bool
<= :: SystemUnit -> SystemUnit -> Bool
$c<= :: SystemUnit -> SystemUnit -> Bool
< :: SystemUnit -> SystemUnit -> Bool
$c< :: SystemUnit -> SystemUnit -> Bool
compare :: SystemUnit -> SystemUnit -> Ordering
$ccompare :: SystemUnit -> SystemUnit -> Ordering
$cp1Ord :: Eq SystemUnit
Ord)

data ImperialUnit
  = Inch
  | Foot
  | Yard
  | Mile
  deriving (ImperialUnit -> ImperialUnit -> Bool
(ImperialUnit -> ImperialUnit -> Bool)
-> (ImperialUnit -> ImperialUnit -> Bool) -> Eq ImperialUnit
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ImperialUnit -> ImperialUnit -> Bool
$c/= :: ImperialUnit -> ImperialUnit -> Bool
== :: ImperialUnit -> ImperialUnit -> Bool
$c== :: ImperialUnit -> ImperialUnit -> Bool
Eq, Eq ImperialUnit
Eq ImperialUnit
-> (ImperialUnit -> ImperialUnit -> Ordering)
-> (ImperialUnit -> ImperialUnit -> Bool)
-> (ImperialUnit -> ImperialUnit -> Bool)
-> (ImperialUnit -> ImperialUnit -> Bool)
-> (ImperialUnit -> ImperialUnit -> Bool)
-> (ImperialUnit -> ImperialUnit -> ImperialUnit)
-> (ImperialUnit -> ImperialUnit -> ImperialUnit)
-> Ord ImperialUnit
ImperialUnit -> ImperialUnit -> Bool
ImperialUnit -> ImperialUnit -> Ordering
ImperialUnit -> ImperialUnit -> ImperialUnit
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: ImperialUnit -> ImperialUnit -> ImperialUnit
$cmin :: ImperialUnit -> ImperialUnit -> ImperialUnit
max :: ImperialUnit -> ImperialUnit -> ImperialUnit
$cmax :: ImperialUnit -> ImperialUnit -> ImperialUnit
>= :: ImperialUnit -> ImperialUnit -> Bool
$c>= :: ImperialUnit -> ImperialUnit -> Bool
> :: ImperialUnit -> ImperialUnit -> Bool
$c> :: ImperialUnit -> ImperialUnit -> Bool
<= :: ImperialUnit -> ImperialUnit -> Bool
$c<= :: ImperialUnit -> ImperialUnit -> Bool
< :: ImperialUnit -> ImperialUnit -> Bool
$c< :: ImperialUnit -> ImperialUnit -> Bool
compare :: ImperialUnit -> ImperialUnit -> Ordering
$ccompare :: ImperialUnit -> ImperialUnit -> Ordering
$cp1Ord :: Eq ImperialUnit
Ord)

data MetricUnit
  = Millimetre
  | Centimetre
  | Metre
  | Kilometre
  deriving (MetricUnit -> MetricUnit -> Bool
(MetricUnit -> MetricUnit -> Bool)
-> (MetricUnit -> MetricUnit -> Bool) -> Eq MetricUnit
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: MetricUnit -> MetricUnit -> Bool
$c/= :: MetricUnit -> MetricUnit -> Bool
== :: MetricUnit -> MetricUnit -> Bool
$c== :: MetricUnit -> MetricUnit -> Bool
Eq, Eq MetricUnit
Eq MetricUnit
-> (MetricUnit -> MetricUnit -> Ordering)
-> (MetricUnit -> MetricUnit -> Bool)
-> (MetricUnit -> MetricUnit -> Bool)
-> (MetricUnit -> MetricUnit -> Bool)
-> (MetricUnit -> MetricUnit -> Bool)
-> (MetricUnit -> MetricUnit -> MetricUnit)
-> (MetricUnit -> MetricUnit -> MetricUnit)
-> Ord MetricUnit
MetricUnit -> MetricUnit -> Bool
MetricUnit -> MetricUnit -> Ordering
MetricUnit -> MetricUnit -> MetricUnit
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: MetricUnit -> MetricUnit -> MetricUnit
$cmin :: MetricUnit -> MetricUnit -> MetricUnit
max :: MetricUnit -> MetricUnit -> MetricUnit
$cmax :: MetricUnit -> MetricUnit -> MetricUnit
>= :: MetricUnit -> MetricUnit -> Bool
$c>= :: MetricUnit -> MetricUnit -> Bool
> :: MetricUnit -> MetricUnit -> Bool
$c> :: MetricUnit -> MetricUnit -> Bool
<= :: MetricUnit -> MetricUnit -> Bool
$c<= :: MetricUnit -> MetricUnit -> Bool
< :: MetricUnit -> MetricUnit -> Bool
$c< :: MetricUnit -> MetricUnit -> Bool
compare :: MetricUnit -> MetricUnit -> Ordering
$ccompare :: MetricUnit -> MetricUnit -> Ordering
$cp1Ord :: Eq MetricUnit
Ord)

-- | Currently there is only one actually ambiguous unit, but this design allows
-- for expansion.
data AmbiguousUnit
  = M -- ^ "Miles" or "Metres"
  deriving (AmbiguousUnit -> AmbiguousUnit -> Bool
(AmbiguousUnit -> AmbiguousUnit -> Bool)
-> (AmbiguousUnit -> AmbiguousUnit -> Bool) -> Eq AmbiguousUnit
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: AmbiguousUnit -> AmbiguousUnit -> Bool
$c/= :: AmbiguousUnit -> AmbiguousUnit -> Bool
== :: AmbiguousUnit -> AmbiguousUnit -> Bool
$c== :: AmbiguousUnit -> AmbiguousUnit -> Bool
Eq, Eq AmbiguousUnit
Eq AmbiguousUnit
-> (AmbiguousUnit -> AmbiguousUnit -> Ordering)
-> (AmbiguousUnit -> AmbiguousUnit -> Bool)
-> (AmbiguousUnit -> AmbiguousUnit -> Bool)
-> (AmbiguousUnit -> AmbiguousUnit -> Bool)
-> (AmbiguousUnit -> AmbiguousUnit -> Bool)
-> (AmbiguousUnit -> AmbiguousUnit -> AmbiguousUnit)
-> (AmbiguousUnit -> AmbiguousUnit -> AmbiguousUnit)
-> Ord AmbiguousUnit
AmbiguousUnit -> AmbiguousUnit -> Bool
AmbiguousUnit -> AmbiguousUnit -> Ordering
AmbiguousUnit -> AmbiguousUnit -> AmbiguousUnit
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: AmbiguousUnit -> AmbiguousUnit -> AmbiguousUnit
$cmin :: AmbiguousUnit -> AmbiguousUnit -> AmbiguousUnit
max :: AmbiguousUnit -> AmbiguousUnit -> AmbiguousUnit
$cmax :: AmbiguousUnit -> AmbiguousUnit -> AmbiguousUnit
>= :: AmbiguousUnit -> AmbiguousUnit -> Bool
$c>= :: AmbiguousUnit -> AmbiguousUnit -> Bool
> :: AmbiguousUnit -> AmbiguousUnit -> Bool
$c> :: AmbiguousUnit -> AmbiguousUnit -> Bool
<= :: AmbiguousUnit -> AmbiguousUnit -> Bool
$c<= :: AmbiguousUnit -> AmbiguousUnit -> Bool
< :: AmbiguousUnit -> AmbiguousUnit -> Bool
$c< :: AmbiguousUnit -> AmbiguousUnit -> Bool
compare :: AmbiguousUnit -> AmbiguousUnit -> Ordering
$ccompare :: AmbiguousUnit -> AmbiguousUnit -> Ordering
$cp1Ord :: Eq AmbiguousUnit
Ord)

-- | Represents a value with an unambiguous unit
data UnitValuePair = UnitValuePair SystemUnit Double

sumScaledUnits :: SystemUnit -> (UnitValuePair, UnitValuePair) -> Double
sumScaledUnits :: SystemUnit -> (UnitValuePair, UnitValuePair) -> Double
sumScaledUnits SystemUnit
preferredUnit = (Double -> Double -> Double) -> (Double, Double) -> Double
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Double -> Double -> Double
forall a. Num a => a -> a -> a
(+) ((Double, Double) -> Double)
-> ((UnitValuePair, UnitValuePair) -> (Double, Double))
-> (UnitValuePair, UnitValuePair)
-> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UnitValuePair -> Double)
-> (UnitValuePair, UnitValuePair) -> (Double, Double)
forall a b. (a -> b) -> (a, a) -> (b, b)
both (SystemUnit -> UnitValuePair -> Double
scaleUnits SystemUnit
preferredUnit)

-- | Determine the definite meaning of an "Ambiguous" unit using the context of
-- another "Definite" unit
reconcileAmbiguousWithDefinite ::
     Double
  -> AmbiguousUnit
  -> Double
  -> SystemUnit
  -> ContextualDistance
reconcileAmbiguousWithDefinite :: Double
-> AmbiguousUnit -> Double -> SystemUnit -> ContextualDistance
reconcileAmbiguousWithDefinite Double
av AmbiguousUnit
au Double
dv SystemUnit
du =
  Double -> DeferrableUnit -> ContextualDistance
ContextualDistance Double
combinedValue (DeferrableUnit -> ContextualDistance)
-> DeferrableUnit -> ContextualDistance
forall a b. (a -> b) -> a -> b
$ SystemUnit -> DeferrableUnit
Definite SystemUnit
preferredUnit
  where
    resolvedAmbiguousUnit :: SystemUnit
resolvedAmbiguousUnit = AmbiguousUnit -> SystemUnit -> SystemUnit
resolveUnit AmbiguousUnit
au SystemUnit
du
    preferredUnit :: SystemUnit
preferredUnit = SystemUnit
du SystemUnit -> SystemUnit -> SystemUnit
forall a. Ord a => a -> a -> a
`min` SystemUnit
resolvedAmbiguousUnit
    combinedValue :: Double
combinedValue = SystemUnit -> (UnitValuePair, UnitValuePair) -> Double
sumScaledUnits SystemUnit
preferredUnit
      (SystemUnit -> Double -> UnitValuePair
UnitValuePair SystemUnit
resolvedAmbiguousUnit Double
av, SystemUnit -> Double -> UnitValuePair
UnitValuePair SystemUnit
du Double
dv)

-- | When both Metric and Imperial units are given, resolve to Metric.
-- Otherwise, preserve the original measurement system and use the smaller unit.
-- For the purpose of this resolution, all Metric units are considered "smaller"
-- than Imperial units.
instance Semigroup ContextualDistance where

  ContextualDistance
_ <> :: ContextualDistance -> ContextualDistance -> ContextualDistance
<> ContextualDistance
Nonrelatable = ContextualDistance
Nonrelatable
  ContextualDistance
Nonrelatable <> ContextualDistance
_ = ContextualDistance
Nonrelatable

  (ContextualDistance Double
v1 u :: DeferrableUnit
u@(Ambiguous AmbiguousUnit
u1))
    <> (ContextualDistance Double
v2 (Ambiguous AmbiguousUnit
u2))
      | AmbiguousUnit
u1 AmbiguousUnit -> AmbiguousUnit -> Bool
forall a. Eq a => a -> a -> Bool
== AmbiguousUnit
u2  = Double -> DeferrableUnit -> ContextualDistance
ContextualDistance (Double
v1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
v2) DeferrableUnit
u
      | Bool
otherwise = ContextualDistance
Nonrelatable
      -- NOTE A: Needing to return "Nonrelatable" in this edge case is why
      -- the two-member "ContextualDistance" sum type cannot be simply
      -- represented as (Maybe (ContextualDistance Double DeferrableUnit)).

  (ContextualDistance Double
av (Ambiguous AmbiguousUnit
au))
    <> (ContextualDistance Double
dv (Definite SystemUnit
du)) =
      Double
-> AmbiguousUnit -> Double -> SystemUnit -> ContextualDistance
reconcileAmbiguousWithDefinite Double
av AmbiguousUnit
au Double
dv SystemUnit
du

  (ContextualDistance Double
dv (Definite SystemUnit
du))
    <> (ContextualDistance Double
av (Ambiguous AmbiguousUnit
au)) =
      Double
-> AmbiguousUnit -> Double -> SystemUnit -> ContextualDistance
reconcileAmbiguousWithDefinite Double
av AmbiguousUnit
au Double
dv SystemUnit
du

  (ContextualDistance Double
v1 (Definite SystemUnit
u1))
    <> (ContextualDistance Double
v2 (Definite SystemUnit
u2)) =
      Double -> DeferrableUnit -> ContextualDistance
ContextualDistance Double
v (DeferrableUnit -> ContextualDistance)
-> DeferrableUnit -> ContextualDistance
forall a b. (a -> b) -> a -> b
$ SystemUnit -> DeferrableUnit
Definite SystemUnit
u
      where
        v :: Double
v = SystemUnit -> (UnitValuePair, UnitValuePair) -> Double
sumScaledUnits SystemUnit
u (SystemUnit -> Double -> UnitValuePair
UnitValuePair SystemUnit
u1 Double
v1, SystemUnit -> Double -> UnitValuePair
UnitValuePair SystemUnit
u2 Double
v2)
        u :: SystemUnit
u = SystemUnit
u1 SystemUnit -> SystemUnit -> SystemUnit
forall a. Ord a => a -> a -> a
`min` SystemUnit
u2

resolveUnit :: AmbiguousUnit -> SystemUnit -> SystemUnit
resolveUnit :: AmbiguousUnit -> SystemUnit -> SystemUnit
resolveUnit AmbiguousUnit
M (Metric MetricUnit
_) = MetricUnit -> SystemUnit
Metric MetricUnit
Metre
resolveUnit AmbiguousUnit
M (Imperial ImperialUnit
_) = ImperialUnit -> SystemUnit
Imperial ImperialUnit
Mile


-- | Disambiguation of original Unit type
toSystemUnit :: TDist.Unit -> DeferrableUnit
toSystemUnit :: Unit -> DeferrableUnit
toSystemUnit Unit
TDist.M          = AmbiguousUnit -> DeferrableUnit
Ambiguous AmbiguousUnit
M
toSystemUnit Unit
TDist.Millimetre = SystemUnit -> DeferrableUnit
Definite (SystemUnit -> DeferrableUnit) -> SystemUnit -> DeferrableUnit
forall a b. (a -> b) -> a -> b
$ MetricUnit -> SystemUnit
Metric MetricUnit
Millimetre
toSystemUnit Unit
TDist.Centimetre = SystemUnit -> DeferrableUnit
Definite (SystemUnit -> DeferrableUnit) -> SystemUnit -> DeferrableUnit
forall a b. (a -> b) -> a -> b
$ MetricUnit -> SystemUnit
Metric MetricUnit
Centimetre
toSystemUnit Unit
TDist.Metre      = SystemUnit -> DeferrableUnit
Definite (SystemUnit -> DeferrableUnit) -> SystemUnit -> DeferrableUnit
forall a b. (a -> b) -> a -> b
$ MetricUnit -> SystemUnit
Metric MetricUnit
Metre
toSystemUnit Unit
TDist.Kilometre  = SystemUnit -> DeferrableUnit
Definite (SystemUnit -> DeferrableUnit) -> SystemUnit -> DeferrableUnit
forall a b. (a -> b) -> a -> b
$ MetricUnit -> SystemUnit
Metric MetricUnit
Kilometre
toSystemUnit Unit
TDist.Inch       = SystemUnit -> DeferrableUnit
Definite (SystemUnit -> DeferrableUnit) -> SystemUnit -> DeferrableUnit
forall a b. (a -> b) -> a -> b
$ ImperialUnit -> SystemUnit
Imperial ImperialUnit
Inch
toSystemUnit Unit
TDist.Foot       = SystemUnit -> DeferrableUnit
Definite (SystemUnit -> DeferrableUnit) -> SystemUnit -> DeferrableUnit
forall a b. (a -> b) -> a -> b
$ ImperialUnit -> SystemUnit
Imperial ImperialUnit
Foot
toSystemUnit Unit
TDist.Yard       = SystemUnit -> DeferrableUnit
Definite (SystemUnit -> DeferrableUnit) -> SystemUnit -> DeferrableUnit
forall a b. (a -> b) -> a -> b
$ ImperialUnit -> SystemUnit
Imperial ImperialUnit
Yard
toSystemUnit Unit
TDist.Mile       = SystemUnit -> DeferrableUnit
Definite (SystemUnit -> DeferrableUnit) -> SystemUnit -> DeferrableUnit
forall a b. (a -> b) -> a -> b
$ ImperialUnit -> SystemUnit
Imperial ImperialUnit
Mile

-- | Reconversion to original Unit type
toRawUnit :: DeferrableUnit -> TDist.Unit
toRawUnit :: DeferrableUnit -> Unit
toRawUnit (Ambiguous AmbiguousUnit
M) = Unit
TDist.M
toRawUnit (Definite (Metric MetricUnit
Millimetre)) = Unit
TDist.Millimetre
toRawUnit (Definite (Metric MetricUnit
Centimetre)) = Unit
TDist.Centimetre
toRawUnit (Definite (Metric MetricUnit
Metre)) = Unit
TDist.Metre
toRawUnit (Definite (Metric MetricUnit
Kilometre)) = Unit
TDist.Kilometre
toRawUnit (Definite (Imperial ImperialUnit
Inch)) = Unit
TDist.Inch
toRawUnit (Definite (Imperial ImperialUnit
Foot)) = Unit
TDist.Foot
toRawUnit (Definite (Imperial ImperialUnit
Yard)) = Unit
TDist.Yard
toRawUnit (Definite (Imperial ImperialUnit
Mile)) = Unit
TDist.Mile

-- | Convert a distance to the given units.
-- This only works if the unit is unambiguous.
scaleUnits ::
     SystemUnit -- ^ target unit
  -> UnitValuePair -- ^ Original unit and value
  -> Double
scaleUnits :: SystemUnit -> UnitValuePair -> Double
scaleUnits SystemUnit
targetUnit (UnitValuePair SystemUnit
startingUnit Double
v)
  | SystemUnit
startingUnit SystemUnit -> SystemUnit -> Bool
forall a. Eq a => a -> a -> Bool
== SystemUnit
targetUnit = Double
v
  | Bool
otherwise = Double -> SystemUnit -> Double
inSIUnits Double
v SystemUnit
startingUnit Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double -> SystemUnit -> Double
inSIUnits Double
1 SystemUnit
targetUnit

-- | This is used when distances
-- must be normalized across measurement systems.
-- The Metric metre is the Standard International unit of distance.
inSIUnits :: Double -> SystemUnit -> Double
inSIUnits :: Double -> SystemUnit -> Double
inSIUnits Double
val (Metric MetricUnit
u) = MetricUnit -> Double -> Double
forall a. Fractional a => MetricUnit -> a -> a
inMetres MetricUnit
u Double
val
inSIUnits Double
val (Imperial ImperialUnit
u) = ImperialUnit -> Double -> Double
forall a. Num a => ImperialUnit -> a -> a
inInches ImperialUnit
u Double
val Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
metersPerInch

-- | This conversion factor is exact.
metersPerInch :: Double
metersPerInch :: Double
metersPerInch = Double
0.0254

inMetres :: Fractional a => MetricUnit -> a -> a
inMetres :: MetricUnit -> a -> a
inMetres MetricUnit
Millimetre a
n = a
n a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
1000
inMetres MetricUnit
Centimetre a
n = a
n a -> a -> a
forall a. Fractional a => a -> a -> a
/ a
100
inMetres MetricUnit
Metre      a
n = a
n
inMetres MetricUnit
Kilometre  a
n = a
n a -> a -> a
forall a. Num a => a -> a -> a
* a
1000

inInches :: Num a => ImperialUnit -> a -> a
inInches :: ImperialUnit -> a -> a
inInches ImperialUnit
Inch a
n = a
n
inInches ImperialUnit
Foot a
n = a
12   a -> a -> a
forall a. Num a => a -> a -> a
* a
n
inInches ImperialUnit
Yard a
n = a
3    a -> a -> a
forall a. Num a => a -> a -> a
* ImperialUnit -> a -> a
forall a. Num a => ImperialUnit -> a -> a
inInches ImperialUnit
Foot a
n
inInches ImperialUnit
Mile a
n = a
5280 a -> a -> a
forall a. Num a => a -> a -> a
* ImperialUnit -> a -> a
forall a. Num a => ImperialUnit -> a -> a
inInches ImperialUnit
Foot a
n