packages feed

union-angle-0.1.0.0: src/Data/Angle.hs

{-# LANGUAGE BlockArguments, LambdaCase #-}
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Data.Angle (Angle, pattern Radian, radian, pattern Degree, degree) where

import Control.Arrow (second)
import Text.Read (Lexeme(..), readPrec, step, parens, prec, lexP, (+++))

---------------------------------------------------------------------------

-- * DATA TYPE AND PATTERN
-- * INSTANCE DEFINITION

---------------------------------------------------------------------------
-- DATA TYPE AND PATTERN
---------------------------------------------------------------------------

data Angle f = Radian_ f | Degree_ f

-- ^ >>> Radian pi
-- Radian 3.141592653589793
-- >>> degree it
-- 180.0
--
-- >>> Degree 180
-- Degree 180.0
--
-- >>> Radian pi + Degree 180
-- Radian 6.283185307179586

{-# COMPLETE Radian #-}

pattern Radian :: Floating f => f -> Angle f
pattern Radian r <- (radian -> r) where Radian = Radian_

radian :: Floating f => Angle f -> f
radian = \case Radian_ r -> r; Degree_ d -> d * pi / 180

{-# COMPLETE Degree #-}

pattern Degree :: Floating f => f -> Angle f
pattern Degree d <- (degree -> d) where Degree = Degree_

degree :: Floating f => Angle f -> f
degree = \case Radian_ r -> r * 180 / pi; Degree_ d -> d

---------------------------------------------------------------------------
-- INSTANCE DEFINITION
---------------------------------------------------------------------------

instance Show f => Show (Angle f) where
	showsPrec d = \case
		Radian_ x -> showParen (d > 10)
			$ ("Radian " ++) . showsPrec 11 x
		Degree_ x -> showParen (d > 10)
			$ ("Degree " ++) . showsPrec 11 x

instance (Read f, Floating f) => Read (Angle f) where
	readPrec = parens $
		prec 10 do Ident "Radian" <- lexP; Radian_ <$> step readPrec
		+++
		prec 10 do Ident "Degree" <- lexP; Degree_ <$> step readPrec

instance (Eq f, Floating f) => Eq (Angle f) where
	Degree_ x == Degree_ y = x == y; Radian x == Radian y = x == y

instance (Ord f, Floating f) => Ord (Angle f) where
	Degree_ x <= Degree_ y = x <= y; Radian x <= Radian y = x <= y

instance Floating f => Num (Angle f) where
	Degree_ x + Degree_ y = Degree_ $ x + y
	Radian x + Radian y = Radian_ $ x + y
	Degree_ x * Degree_ y = Degree_ $ x * y * pi / 180
	Radian x * Radian y = Radian_ $ x * y
	negate = \case Radian_ x -> Radian_ $ - x; Degree_ x -> Degree_ $ - x
	abs = \case Radian_ x -> Radian_ $ abs x; Degree_ x -> Degree_ $ abs x
	signum = \case
		Radian_ x -> Radian_ $ signum x; Degree_ x -> Radian_ $ signum x
	fromInteger = Radian_ . fromInteger

instance Floating f => Fractional (Angle f) where
	recip = \case
		Radian_ x -> Radian_ $ recip x
		Degree_ x -> Degree_ $ recip x * (180 / pi) ^ (2 :: Int)
	fromRational = Radian_ . fromRational

instance (Floating f, Real f) => Real (Angle f) where
	toRational = toRational . radian

instance Floating f => Floating (Angle f) where
	pi = Radian_ pi
	exp = applyAngle exp
	log = applyAngle log
	sin = applyAngle sin
	cos = applyAngle cos
	asin = applyAngle asin
	acos = applyAngle acos
	atan = applyAngle atan
	sinh = applyAngle sinh
	cosh = applyAngle cosh
	asinh = applyAngle asinh
	acosh = applyAngle acosh
	atanh = applyAngle atanh

applyAngle :: Floating f => (f -> f) -> Angle f -> Angle f
applyAngle f = \case
	Radian_ x -> Radian_ $ f x
	Degree_ x -> Degree_ $ f (x * pi / 180) * 180 / pi

instance (Floating f, RealFrac f) => RealFrac (Angle f) where
	properFraction = \case
		Radian_ x -> Radian `second` properFraction x
		Degree_ x -> (Degree_ . (/ pi) . (* 180))
			`second` properFraction (x * pi / 180)