{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# OPTIONS_GHC -Wall #-}

-- | Metric classes
module NumHask.Algebra.Metric
  ( Signed (..),
    Norm (..),
    distance,
    Direction (..),
    Polar (..),
    polar,
    coord,
    Epsilon (..),
    (~=),
  )
where

import Data.Bool (bool)
import Data.Int (Int16, Int32, Int64, Int8)
import Data.Word (Word16, Word32, Word64, Word8)
import GHC.Generics (Generic)
import GHC.Natural (Natural (..))
import NumHask.Algebra.Additive (Additive (zero), Subtractive (..), (-))
import NumHask.Algebra.Module (MultiplicativeAction ((.*)))
import NumHask.Algebra.Multiplicative (Multiplicative (one))
import Prelude hiding
  ( Bounded (..),
    Integral (..),
    negate,
    (*),
    (-),
  )
import qualified Prelude as P

-- $setup
--
-- >>> :set -XRebindableSyntax
-- >>> import NumHask.Prelude

-- | 'signum' from base is not an operator name in numhask and is replaced by 'sign'.  Compare with 'Norm' where there is a change in codomain.
--
-- prop> \a -> abs a * sign a ~= a
--
-- abs zero == zero, so any value for sign zero is ok.  We choose lawful neutral:
--
-- >>> sign zero == zero
-- True
--
-- >>> abs (-1)
-- 1
--
-- >>> sign (-1)
-- -1
class
  (Additive a, Multiplicative a) =>
  Signed a
  where
  sign :: a -> a
  abs :: a -> a

instance Signed Double where
  sign :: Double -> Double
sign Double
a =
    case forall a. Ord a => a -> a -> Ordering
compare Double
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Double -> Double
abs = forall a. Num a => a -> a
P.abs

instance Signed Float where
  sign :: Float -> Float
sign Float
a =
    case forall a. Ord a => a -> a -> Ordering
compare Float
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Float -> Float
abs = forall a. Num a => a -> a
P.abs

instance Signed Int where
  sign :: Int -> Int
sign Int
a =
    case forall a. Ord a => a -> a -> Ordering
compare Int
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Int -> Int
abs = forall a. Num a => a -> a
P.abs

instance Signed Integer where
  sign :: Integer -> Integer
sign Integer
a =
    case forall a. Ord a => a -> a -> Ordering
compare Integer
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Integer -> Integer
abs = forall a. Num a => a -> a
P.abs

instance Signed Natural where
  sign :: Natural -> Natural
sign Natural
a =
    case forall a. Ord a => a -> a -> Ordering
compare Natural
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Natural -> Natural
abs = forall a. a -> a
id

instance Signed Int8 where
  sign :: Int8 -> Int8
sign Int8
a =
    case forall a. Ord a => a -> a -> Ordering
compare Int8
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Int8 -> Int8
abs = forall a. Num a => a -> a
P.abs

instance Signed Int16 where
  sign :: Int16 -> Int16
sign Int16
a =
    case forall a. Ord a => a -> a -> Ordering
compare Int16
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Int16 -> Int16
abs = forall a. Num a => a -> a
P.abs

instance Signed Int32 where
  sign :: Int32 -> Int32
sign Int32
a =
    case forall a. Ord a => a -> a -> Ordering
compare Int32
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Int32 -> Int32
abs = forall a. Num a => a -> a
P.abs

instance Signed Int64 where
  sign :: Int64 -> Int64
sign Int64
a =
    case forall a. Ord a => a -> a -> Ordering
compare Int64
a forall a. Additive a => a
zero of
      Ordering
EQ -> forall a. Additive a => a
zero
      Ordering
GT -> forall a. Multiplicative a => a
one
      Ordering
LT -> forall a. Subtractive a => a -> a
negate forall a. Multiplicative a => a
one
  abs :: Int64 -> Int64
abs = forall a. Num a => a -> a
P.abs

instance Signed Word where
  sign :: Word -> Word
sign Word
a = forall a. a -> a -> Bool -> a
bool forall a. Multiplicative a => a
one forall a. Additive a => a
zero (Word
a forall a. Eq a => a -> a -> Bool
== forall a. Additive a => a
zero)
  abs :: Word -> Word
abs = forall a. Num a => a -> a
P.abs

instance Signed Word8 where
  sign :: Word8 -> Word8
sign Word8
a = forall a. a -> a -> Bool -> a
bool forall a. Multiplicative a => a
one forall a. Additive a => a
zero (Word8
a forall a. Eq a => a -> a -> Bool
== forall a. Additive a => a
zero)
  abs :: Word8 -> Word8
abs = forall a. Num a => a -> a
P.abs

instance Signed Word16 where
  sign :: Word16 -> Word16
sign Word16
a = forall a. a -> a -> Bool -> a
bool forall a. Multiplicative a => a
one forall a. Additive a => a
zero (Word16
a forall a. Eq a => a -> a -> Bool
== forall a. Additive a => a
zero)
  abs :: Word16 -> Word16
abs = forall a. Num a => a -> a
P.abs

instance Signed Word32 where
  sign :: Word32 -> Word32
sign Word32
a = forall a. a -> a -> Bool -> a
bool forall a. Multiplicative a => a
one forall a. Additive a => a
zero (Word32
a forall a. Eq a => a -> a -> Bool
== forall a. Additive a => a
zero)
  abs :: Word32 -> Word32
abs = forall a. Num a => a -> a
P.abs

instance Signed Word64 where
  sign :: Word64 -> Word64
sign Word64
a = forall a. a -> a -> Bool -> a
bool forall a. Multiplicative a => a
one forall a. Additive a => a
zero (Word64
a forall a. Eq a => a -> a -> Bool
== forall a. Additive a => a
zero)
  abs :: Word64 -> Word64
abs = forall a. Num a => a -> a
P.abs

-- | Norm is a slight generalisation of Signed. The class has the same shape but allows the codomain to be different to the domain.
--
-- > \a -> norm a >= zero
-- > \a -> norm zero == zero
-- > \a -> a == norm a .* basis a
-- > \a -> norm (basis a) == one
--
-- >>> norm (-0.5 :: Double) :: Double
-- 0.5
--
-- >>> basis (-0.5 :: Double) :: Double
-- -1.0
class (Additive a, Multiplicative b, Additive b) => Norm a b | a -> b where
  -- | or length, or ||v||
  norm :: a -> b

  -- | or direction, or v-hat
  basis :: a -> a

instance Norm Double Double where
  norm :: Double -> Double
norm = forall a. Num a => a -> a
P.abs
  basis :: Double -> Double
basis = forall a. Num a => a -> a
P.signum

instance Norm Float Float where
  norm :: Float -> Float
norm = forall a. Num a => a -> a
P.abs
  basis :: Float -> Float
basis = forall a. Num a => a -> a
P.signum

instance Norm Int Int where
  norm :: Int -> Int
norm = forall a. Num a => a -> a
P.abs
  basis :: Int -> Int
basis = forall a. Num a => a -> a
P.signum

instance Norm Integer Integer where
  norm :: Integer -> Integer
norm = forall a. Num a => a -> a
P.abs
  basis :: Integer -> Integer
basis = forall a. Num a => a -> a
P.signum

instance Norm Natural Natural where
  norm :: Natural -> Natural
norm = forall a. Num a => a -> a
P.abs
  basis :: Natural -> Natural
basis = forall a. Num a => a -> a
P.signum

instance Norm Int8 Int8 where
  norm :: Int8 -> Int8
norm = forall a. Num a => a -> a
P.abs
  basis :: Int8 -> Int8
basis = forall a. Num a => a -> a
P.signum

instance Norm Int16 Int16 where
  norm :: Int16 -> Int16
norm = forall a. Num a => a -> a
P.abs
  basis :: Int16 -> Int16
basis = forall a. Num a => a -> a
P.signum

instance Norm Int32 Int32 where
  norm :: Int32 -> Int32
norm = forall a. Num a => a -> a
P.abs
  basis :: Int32 -> Int32
basis = forall a. Num a => a -> a
P.signum

instance Norm Int64 Int64 where
  norm :: Int64 -> Int64
norm = forall a. Num a => a -> a
P.abs
  basis :: Int64 -> Int64
basis = forall a. Num a => a -> a
P.signum

instance Norm Word Word where
  norm :: Word -> Word
norm = forall a. Num a => a -> a
P.abs
  basis :: Word -> Word
basis = forall a. Num a => a -> a
P.signum

instance Norm Word8 Word8 where
  norm :: Word8 -> Word8
norm = forall a. Num a => a -> a
P.abs
  basis :: Word8 -> Word8
basis = forall a. Num a => a -> a
P.signum

instance Norm Word16 Word16 where
  norm :: Word16 -> Word16
norm = forall a. Num a => a -> a
P.abs
  basis :: Word16 -> Word16
basis = forall a. Num a => a -> a
P.signum

instance Norm Word32 Word32 where
  norm :: Word32 -> Word32
norm = forall a. Num a => a -> a
P.abs
  basis :: Word32 -> Word32
basis = forall a. Num a => a -> a
P.signum

instance Norm Word64 Word64 where
  norm :: Word64 -> Word64
norm = forall a. Num a => a -> a
P.abs
  basis :: Word64 -> Word64
basis = forall a. Num a => a -> a
P.signum

-- | Distance, which combines the Subtractive notion of difference, with Norm.
--
-- > distance a b >= zero
-- > distance a a == zero
-- > distance a b .* basis (a - b) == a - b
distance :: (Norm a b, Subtractive a) => a -> a -> b
distance :: forall a b. (Norm a b, Subtractive a) => a -> a -> b
distance a
a a
b = forall a b. Norm a b => a -> b
norm (a
a forall a. Subtractive a => a -> a -> a
- a
b)

-- | Convert between a "co-ordinated" or "higher-kinded" number and representations of an angle. Typically thought of as polar co-ordinate conversion.
--
-- See [Polar coordinate system](https://en.wikipedia.org/wiki/Polar_coordinate_system)
--
-- > ray . angle == basis
-- > norm (ray x) == one
class (Additive coord, Multiplicative coord, Additive dir, Multiplicative dir) => Direction coord dir | coord -> dir where
  angle :: coord -> dir
  ray :: dir -> coord

-- | Something that has a magnitude and a direction.
data Polar mag dir = Polar {forall mag dir. Polar mag dir -> mag
magnitude :: !mag, forall mag dir. Polar mag dir -> dir
direction :: !dir}
  deriving (Polar mag dir -> Polar mag dir -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall mag dir.
(Eq mag, Eq dir) =>
Polar mag dir -> Polar mag dir -> Bool
/= :: Polar mag dir -> Polar mag dir -> Bool
$c/= :: forall mag dir.
(Eq mag, Eq dir) =>
Polar mag dir -> Polar mag dir -> Bool
== :: Polar mag dir -> Polar mag dir -> Bool
$c== :: forall mag dir.
(Eq mag, Eq dir) =>
Polar mag dir -> Polar mag dir -> Bool
Eq, Int -> Polar mag dir -> ShowS
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall mag dir.
(Show mag, Show dir) =>
Int -> Polar mag dir -> ShowS
forall mag dir. (Show mag, Show dir) => [Polar mag dir] -> ShowS
forall mag dir. (Show mag, Show dir) => Polar mag dir -> String
showList :: [Polar mag dir] -> ShowS
$cshowList :: forall mag dir. (Show mag, Show dir) => [Polar mag dir] -> ShowS
show :: Polar mag dir -> String
$cshow :: forall mag dir. (Show mag, Show dir) => Polar mag dir -> String
showsPrec :: Int -> Polar mag dir -> ShowS
$cshowsPrec :: forall mag dir.
(Show mag, Show dir) =>
Int -> Polar mag dir -> ShowS
Show, forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall mag dir x. Rep (Polar mag dir) x -> Polar mag dir
forall mag dir x. Polar mag dir -> Rep (Polar mag dir) x
$cto :: forall mag dir x. Rep (Polar mag dir) x -> Polar mag dir
$cfrom :: forall mag dir x. Polar mag dir -> Rep (Polar mag dir) x
Generic)

-- | Convert from a number to a Polar.
polar :: (Norm coord mag, Direction coord dir) => coord -> Polar mag dir
polar :: forall coord mag dir.
(Norm coord mag, Direction coord dir) =>
coord -> Polar mag dir
polar coord
z = forall mag dir. mag -> dir -> Polar mag dir
Polar (forall a b. Norm a b => a -> b
norm coord
z) (forall coord dir. Direction coord dir => coord -> dir
angle coord
z)

-- | Convert from a Polar to a (coordinated aka higher-kinded) number.
coord :: (MultiplicativeAction coord mag, Direction coord dir) => Polar mag dir -> coord
coord :: forall coord mag dir.
(MultiplicativeAction coord mag, Direction coord dir) =>
Polar mag dir -> coord
coord (Polar mag
m dir
d) = mag
m forall m a. MultiplicativeAction m a => a -> m -> m
.* forall coord dir. Direction coord dir => dir -> coord
ray dir
d

-- | A small number, especially useful for approximate equality.
class
  (Eq a, Additive a) =>
  Epsilon a
  where
  epsilon :: a
  epsilon = forall a. Additive a => a
zero

  -- | are we near enough?
  --
  -- >>> nearZero (epsilon :: Double)
  -- True
  nearZero :: a -> Bool
  default nearZero :: (Ord a, Subtractive a) => a -> Bool
  nearZero a
a = forall a. Epsilon a => a
epsilon forall a. Ord a => a -> a -> Bool
>= a
a Bool -> Bool -> Bool
&& forall a. Epsilon a => a
epsilon forall a. Ord a => a -> a -> Bool
>= forall a. Subtractive a => a -> a
negate a
a

  -- | Approximate equality
  --
  -- >>> aboutEqual zero (epsilon :: Double)
  -- True
  aboutEqual :: a -> a -> Bool
  default aboutEqual :: (Subtractive a) => a -> a -> Bool
  aboutEqual a
a a
b = forall a. Epsilon a => a -> Bool
nearZero forall a b. (a -> b) -> a -> b
$ a
a forall a. Subtractive a => a -> a -> a
- a
b

infixl 4 ~=

-- | About equal operator.
--
-- >>> (1.0 + epsilon) ~= (1.0 :: Double)
-- True
(~=) :: (Epsilon a) => a -> a -> Bool
~= :: forall a. Epsilon a => a -> a -> Bool
(~=) = forall a. Epsilon a => a -> a -> Bool
aboutEqual

-- | 1e-14
instance Epsilon Double where
  epsilon :: Double
epsilon = Double
1e-14

-- | 1e-6
instance Epsilon Float where
  epsilon :: Float
epsilon = Float
1e-6

-- | 0
instance Epsilon Int

instance Epsilon Integer

instance Epsilon Int8

instance Epsilon Int16

instance Epsilon Int32

instance Epsilon Int64

instance Epsilon Word

instance Epsilon Word8

instance Epsilon Word16

instance Epsilon Word32

instance Epsilon Word64