{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE Safe #-}

{- |
Module      :  Data.Number.RealCyclotomic
Copyright   :  (c) Scott N. Walck 2012-2017
License     :  GPL-3 (see LICENSE)
Maintainer  :  Scott N. Walck <walck@lvc.edu>
Stability   :  experimental

The real cyclotomic numbers are a subset of the real numbers with
the following properties:

     1.  The real cyclotomic numbers are represented exactly, enabling exact
     computations and equality comparisons.

     2.  The real cyclotomic numbers contain the rationals.
     As a consequence, the real cyclotomic numbers are a dense subset of the
     real numbers.

     3.  The real cyclotomic numbers contain the square roots of all nonnegative rational numbers.

     4.  The real cyclotomic numbers form a field:  they are closed under addition, subtraction,
     multiplication, and division.

     5.  The real cyclotomic numbers contain the sine and cosine of all rational
     multiples of pi (equivalently, the sine and cosine of any rational number
     of degrees or any rational number of revolutions).

     Floating point numbers do not do well with equality comparison:

>(sqrt 2 + sqrt 3)^2 == 5 + 2 * sqrt 6
> -> False

     "Data.Number.RealCyclotomic" represents these numbers exactly, allowing equality comparison:

>(sqrtRat 2 + sqrtRat 3)^2 == 5 + 2 * sqrtRat 6
> -> True

     'RealCyclotomic's can be exported as inexact real numbers using the 'toReal' function:

>sqrtRat 2
> -> e(8) - e(8)^3
>toReal $ sqrtRat 2
> -> 1.414213562373095

This module is based on the module 'Data.Complex.Cyclotomic'.
Usually you would only import one of the modules 'Data.Number.RealCyclotomic'
or 'Data.Complex.Cyclotomic', depending on whether you wanted only
real numbers (this module) or complex numbers (the other).
Functions such as @sqrtRat@, @sinDeg@, @cosDeg@ are defined
in both modules, with different type signatures, so their
names will conflict if both modules are imported.
-}

module Data.Number.RealCyclotomic
    ( RealCyclotomic
    , sqrtRat
    , sinDeg
    , cosDeg
    , sinRev
    , cosRev
    , isRat
    , toRat
    , toReal
    , magSq
    , goldenRatio
    , heron
    )
    where

import qualified Data.Complex.Cyclotomic as Cyc
import Data.Complex
    ( realPart
    )

-- | A real cyclotomic number.
newtype RealCyclotomic = RealCyclotomic Cyc.Cyclotomic
    deriving (RealCyclotomic -> RealCyclotomic -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: RealCyclotomic -> RealCyclotomic -> Bool
$c/= :: RealCyclotomic -> RealCyclotomic -> Bool
== :: RealCyclotomic -> RealCyclotomic -> Bool
$c== :: RealCyclotomic -> RealCyclotomic -> Bool
Eq)

-- | @abs@ and @signum@ are undefined.
instance Num RealCyclotomic where
    RealCyclotomic Cyclotomic
x + :: RealCyclotomic -> RealCyclotomic -> RealCyclotomic
+ RealCyclotomic Cyclotomic
y = Cyclotomic -> RealCyclotomic
RealCyclotomic (Cyclotomic
x forall a. Num a => a -> a -> a
+ Cyclotomic
y)
    RealCyclotomic Cyclotomic
x - :: RealCyclotomic -> RealCyclotomic -> RealCyclotomic
- RealCyclotomic Cyclotomic
y = Cyclotomic -> RealCyclotomic
RealCyclotomic (Cyclotomic
x forall a. Num a => a -> a -> a
- Cyclotomic
y)
    RealCyclotomic Cyclotomic
x * :: RealCyclotomic -> RealCyclotomic -> RealCyclotomic
* RealCyclotomic Cyclotomic
y = Cyclotomic -> RealCyclotomic
RealCyclotomic (Cyclotomic
x forall a. Num a => a -> a -> a
* Cyclotomic
y)
    negate :: RealCyclotomic -> RealCyclotomic
negate (RealCyclotomic Cyclotomic
x) = Cyclotomic -> RealCyclotomic
RealCyclotomic (forall a. Num a => a -> a
negate Cyclotomic
x)
    fromInteger :: Integer -> RealCyclotomic
fromInteger Integer
n = Cyclotomic -> RealCyclotomic
RealCyclotomic (forall a. Num a => Integer -> a
fromInteger Integer
n)
    abs :: RealCyclotomic -> RealCyclotomic
abs    = forall a. HasCallStack => a
undefined
    signum :: RealCyclotomic -> RealCyclotomic
signum = forall a. HasCallStack => a
undefined

instance Fractional RealCyclotomic where
    recip :: RealCyclotomic -> RealCyclotomic
recip (RealCyclotomic Cyclotomic
x) = Cyclotomic -> RealCyclotomic
RealCyclotomic (forall a. Fractional a => a -> a
recip Cyclotomic
x)
    fromRational :: Rational -> RealCyclotomic
fromRational Rational
r = Cyclotomic -> RealCyclotomic
RealCyclotomic (forall a. Fractional a => Rational -> a
fromRational Rational
r)

instance Show RealCyclotomic where
    show :: RealCyclotomic -> String
show (RealCyclotomic Cyclotomic
x) = forall a. Show a => a -> String
show Cyclotomic
x

-- I need to do Ord first.
-- A Real instance would make realToFrac work.
-- instance Real RealCyclotomic where
--     toRational c = toRational (toReal c)

-- | The square root of a 'Rational' number.
sqrtRat :: Rational -> RealCyclotomic
sqrtRat :: Rational -> RealCyclotomic
sqrtRat Rational
r
    | Rational
r forall a. Ord a => a -> a -> Bool
>= Rational
0  = Cyclotomic -> RealCyclotomic
RealCyclotomic (Rational -> Cyclotomic
Cyc.sqrtRat Rational
r)
    | Bool
otherwise  = forall a. HasCallStack => String -> a
error String
"sqrtRational needs a nonnegative argument"

-- | Sine function with argument in degrees.
sinDeg :: Rational -> RealCyclotomic
sinDeg :: Rational -> RealCyclotomic
sinDeg Rational
r = Cyclotomic -> RealCyclotomic
RealCyclotomic (Rational -> Cyclotomic
Cyc.sinDeg Rational
r)

-- | Cosine function with argument in degrees.
cosDeg :: Rational -> RealCyclotomic
cosDeg :: Rational -> RealCyclotomic
cosDeg Rational
r = Cyclotomic -> RealCyclotomic
RealCyclotomic (Rational -> Cyclotomic
Cyc.cosDeg Rational
r)

-- | Sine function with argument in revolutions.
sinRev :: Rational -> RealCyclotomic
sinRev :: Rational -> RealCyclotomic
sinRev Rational
r = Cyclotomic -> RealCyclotomic
RealCyclotomic (Rational -> Cyclotomic
Cyc.sinRev Rational
r)

-- | Cosine function with argument in revolutions.
cosRev :: Rational -> RealCyclotomic
cosRev :: Rational -> RealCyclotomic
cosRev Rational
r = Cyclotomic -> RealCyclotomic
RealCyclotomic (Rational -> Cyclotomic
Cyc.cosRev Rational
r)

-- | Is the cyclotomic a rational?
isRat :: RealCyclotomic -> Bool
isRat :: RealCyclotomic -> Bool
isRat (RealCyclotomic Cyclotomic
r) = Cyclotomic -> Bool
Cyc.isRat Cyclotomic
r

-- | Return an exact rational number if possible.
toRat :: RealCyclotomic -> Maybe Rational
toRat :: RealCyclotomic -> Maybe Rational
toRat (RealCyclotomic Cyclotomic
r) = Cyclotomic -> Maybe Rational
Cyc.toRat Cyclotomic
r

-- | Export as an inexact real number.
toReal :: RealFloat a => RealCyclotomic -> a
toReal :: forall a. RealFloat a => RealCyclotomic -> a
toReal (RealCyclotomic Cyclotomic
r) = forall a. Complex a -> a
realPart (forall a. RealFloat a => Cyclotomic -> Complex a
Cyc.toComplex Cyclotomic
r)

-- | The golden ratio, @(1 + √5)/2@.
goldenRatio :: RealCyclotomic
goldenRatio :: RealCyclotomic
goldenRatio = (RealCyclotomic
1 forall a. Num a => a -> a -> a
+ Rational -> RealCyclotomic
sqrtRat Rational
5) forall a. Fractional a => a -> a -> a
/ RealCyclotomic
2

-- | Heron's formula for the area of a triangle with
--   side lengths a, b, c.
heron :: Rational        -- ^ a
      -> Rational        -- ^ b
      -> Rational        -- ^ c
      -> RealCyclotomic  -- ^ area of triangle
heron :: Rational -> Rational -> Rational -> RealCyclotomic
heron Rational
a Rational
b Rational
c
    = Rational -> RealCyclotomic
sqrtRat (Rational
s forall a. Num a => a -> a -> a
* (Rational
sforall a. Num a => a -> a -> a
-Rational
a) forall a. Num a => a -> a -> a
* (Rational
sforall a. Num a => a -> a -> a
-Rational
b) forall a. Num a => a -> a -> a
* (Rational
sforall a. Num a => a -> a -> a
-Rational
c))
      where
        s :: Rational
s = (Rational
a forall a. Num a => a -> a -> a
+ Rational
b forall a. Num a => a -> a -> a
+ Rational
c) forall a. Fractional a => a -> a -> a
/ Rational
2

-- | The square of the magnitude of a real cyclotomic.
magSq :: RealCyclotomic -> RealCyclotomic
magSq :: RealCyclotomic -> RealCyclotomic
magSq (RealCyclotomic Cyclotomic
c) = Cyclotomic -> RealCyclotomic
RealCyclotomic (Cyclotomic
c forall a. Num a => a -> a -> a
* Cyclotomic -> Cyclotomic
Cyc.conj Cyclotomic
c)