{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
module Numeric.Optics
( base
, integral
, binary
, octal
, decimal
, hex
, adding
, subtracting
, multiplying
, dividing
, exponentiating
, negated
, pattern Integral
) where
import Data.Char (chr, ord, isAsciiLower, isAsciiUpper, isDigit)
import Data.Maybe (fromMaybe)
import GHC.Stack
import Numeric (readInt, showIntAtBase)
import Data.Tuple.Optics
import Optics.AffineFold
import Optics.Iso
import Optics.Optic
import Optics.Prism
import Optics.Review
import Optics.Setter
integral :: (Integral a, Integral b) => Prism Integer Integer a b
integral = prism toInteger $ \i -> let a = fromInteger i in
if toInteger a == i
then Right a
else Left i
{-# INLINE integral #-}
pattern Integral :: forall a. Integral a => a -> Integer
pattern Integral a <- (preview integral -> Just a) where
Integral a = review integral a
base :: (HasCallStack, Integral a) => Int -> Prism' String a
base b
| b < 2 || b > 36 = error ("base: Invalid base " ++ show b)
| otherwise = prism intShow intRead
where
intShow n = showSigned' (showIntAtBase (toInteger b) intToDigit') (toInteger n) ""
intRead s =
case readSigned' (readInt (fromIntegral b) (isDigit' b) digitToInt') s of
[(n,"")] -> Right n
_ -> Left s
{-# INLINE base #-}
intToDigit' :: HasCallStack => Int -> Char
intToDigit' i
| i >= 0 && i < 10 = chr (ord '0' + i)
| i >= 10 && i < 36 = chr (ord 'a' + i - 10)
| otherwise = error ("intToDigit': Invalid int " ++ show i)
{-# INLINE intToDigit' #-}
digitToInt' :: HasCallStack => Char -> Int
digitToInt' c = fromMaybe (error ("digitToInt': Invalid digit " ++ show c))
(digitToIntMay c)
{-# INLINE digitToInt' #-}
digitToIntMay :: Char -> Maybe Int
digitToIntMay c
| isDigit c = Just (ord c - ord '0')
| isAsciiLower c = Just (ord c - ord 'a' + 10)
| isAsciiUpper c = Just (ord c - ord 'A' + 10)
| otherwise = Nothing
{-# INLINE digitToIntMay #-}
isDigit' :: Int -> Char -> Bool
isDigit' b c = case digitToIntMay c of
Just i -> i < b
_ -> False
{-# INLINE isDigit' #-}
showSigned' :: Real a => (a -> ShowS) -> a -> ShowS
showSigned' f n
| n < 0 = showChar '-' . f (negate n)
| otherwise = f n
{-# INLINE showSigned' #-}
readSigned' :: Real a => ReadS a -> ReadS a
readSigned' f ('-':xs) = f xs <&> over _1 negate
readSigned' f xs = f xs
{-# INLINE readSigned' #-}
binary :: Integral a => Prism' String a
binary = base 2
{-# INLINE binary #-}
octal :: Integral a => Prism' String a
octal = base 8
{-# INLINE octal #-}
decimal :: Integral a => Prism' String a
decimal = base 10
{-# INLINE decimal #-}
hex :: Integral a => Prism' String a
hex = base 16
{-# INLINE hex #-}
adding :: Num a => a -> Iso' a a
adding n = iso (+n) (subtract n)
{-# INLINE adding #-}
subtracting :: Num a => a -> Iso' a a
subtracting n = iso (subtract n) (+n)
{-# INLINE subtracting #-}
multiplying :: (Fractional a, Eq a) => a -> Iso' a a
multiplying 0 = error "Numeric.Optics.multiplying: factor 0"
multiplying n = iso (*n) (/n)
{-# INLINE multiplying #-}
dividing :: (Fractional a, Eq a) => a -> Iso' a a
dividing 0 = error "Numeric.Optics.dividing: divisor 0"
dividing n = iso (/n) (*n)
{-# INLINE dividing #-}
exponentiating :: (Floating a, Eq a) => a -> Iso' a a
exponentiating 0 = error "Numeric.Optics.exponentiating: exponent 0"
exponentiating n = iso (**n) (**recip n)
{-# INLINE exponentiating #-}
negated :: Num a => Iso' a a
negated = iso negate negate
{-# INLINE negated #-}