{-# LANGUAGE Safe #-}

{-|
Module      : Data.Char.Number.Duodecimal
Description : A module used to render duodecimal numbers.
Maintainer  : hapytexeu+gh@gmail.com
Stability   : experimental
Portability : POSIX

The <https://www.unicode.org/charts/PDF/U2150.pdf 2150 unicode block> contains two characters for duodecimal numbers: numbers with base 12.

In order to represent digits for 10 and 11, unicode has two codepoints: @TURNED DIGIT TWO@, and @TURNED DIGIT THREE@. This module makes it
more convenient to convert an 'Integral' number to these digits, as well as converting a number to its duodecimal representation.
-}

module Data.Char.Number.Duodecimal (
    -- * Characters used for duodecimal numbers
    char10, char11
    -- * Convert values to digits
  , duodecimalDigit, duodecimalDigit'
    -- * Convert value to a sequence of digits
  , duodecimalNumber, duodecimalNumber'
  ) where

import Data.Bits((.|.))
import Data.Char(chr)
import Data.Char.Core(PlusStyle, positionalNumberSystem)
import Data.Default(def)
import Data.Text(Text)

-- | The character used to denote 10: @ↀ@.
char10 :: Char  -- ^ A character used in duodecimal numbers to denote 10.
char10 :: Char
char10 = Char
'\x2180'

-- | The character used to denote 11: @ↁ@.
char11 :: Char  -- ^ A character used in duodecimal numbers to denote 11.
char11 :: Char
char11 = Char
'\x2181'

_duodecimalDigit :: Int -> Char
_duodecimalDigit :: Int -> Char
_duodecimalDigit Int
n
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
10 = Int -> Char
chr (Int
0x30 Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. Int
n)
    | Bool
otherwise = Int -> Char
chr (Int
0x2180 Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. Int
n)

-- | Convert the given 'Integral' number to its unicode character. In case the
-- value is less than @0@, or greater than @11@, the behavior is unspecified.
duodecimalDigit' :: Integral i
  => i  -- ^ The given number to convert.
  -> Char  -- ^ A unicode character that represents this digit.
duodecimalDigit' :: i -> Char
duodecimalDigit' = Int -> Char
_duodecimalDigit (Int -> Char) -> (i -> Int) -> i -> Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | Convert the given 'Integral' number to its unicode character wrapped in a
-- 'Just' data constructor. In case the value is less than @0@ or greater than
-- @11@, 'Nothing' is returned.
duodecimalDigit :: Integral i => i -> Maybe Char
duodecimalDigit :: i -> Maybe Char
duodecimalDigit i
n
    | i
n i -> i -> Bool
forall a. Ord a => a -> a -> Bool
>= i
0 Bool -> Bool -> Bool
&& i
n i -> i -> Bool
forall a. Ord a => a -> a -> Bool
< i
12 = Char -> Maybe Char
forall a. a -> Maybe a
Just (i -> Char
forall i. Integral i => i -> Char
duodecimalDigit' i
n)
    | Bool
otherwise = Maybe Char
forall a. Maybe a
Nothing

-- | Convert the given 'Integral' number to a 'Text' object that contains
-- a sequence of duodecimal digits that represent that number. The given
-- 'PlusStyle' specifies if the number is prefixed with @+@ if it is positive.
duodecimalNumber :: Integral i
  => PlusStyle  -- ^ The given 'PlusStyle' to use.
  -> i  -- ^ The given number to convert.
  -> Text  -- ^ A string of unicode characters representing the value in duodecimal notation.
duodecimalNumber :: PlusStyle -> i -> Text
duodecimalNumber = i -> (Int -> Char) -> Char -> Char -> PlusStyle -> i -> Text
forall i.
Integral i =>
i -> (Int -> Char) -> Char -> Char -> PlusStyle -> i -> Text
positionalNumberSystem i
12 Int -> Char
_duodecimalDigit Char
'+' Char
'-'

-- | Convert the given 'Integral' number to a 'Text' object that contains
-- sequence of duodecimal digits that represent that number.
duodecimalNumber' :: Integral i
  => i  -- ^ The given number to convert.
  -> Text  -- ^ A string of unicode characters representing the value in duodecimal notation.
duodecimalNumber' :: i -> Text
duodecimalNumber' = PlusStyle -> i -> Text
forall i. Integral i => PlusStyle -> i -> Text
duodecimalNumber PlusStyle
forall a. Default a => a
def