{-# 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.Class (def)
import Data.Text (Text)

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

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

_duodecimalDigit :: Int -> Char
_duodecimalDigit :: Int -> Char
_duodecimalDigit Int
n
  | Int
n forall a. Ord a => a -> a -> Bool
< Int
10 = Int -> Char
chr (Int
0x30 forall a. Bits a => a -> a -> a
.|. Int
n)
  | Bool
otherwise = Int -> Char
chr (Int
0x2180 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 =>
  -- | The given number to convert.
  i ->
  -- | A unicode character that represents this digit.
  Char
duodecimalDigit' :: forall i. Integral i => i -> Char
duodecimalDigit' = Int -> Char
_duodecimalDigit forall b c a. (b -> c) -> (a -> b) -> a -> c
. 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 :: forall i. Integral i => i -> Maybe Char
duodecimalDigit i
n
  | i
n forall a. Ord a => a -> a -> Bool
>= i
0 Bool -> Bool -> Bool
&& i
n forall a. Ord a => a -> a -> Bool
< i
12 = forall a. a -> Maybe a
Just (forall i. Integral i => i -> Char
duodecimalDigit' i
n)
  | Bool
otherwise = 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 =>
  -- | The given 'PlusStyle' to use.
  PlusStyle ->
  -- | The given number to convert.
  i ->
  -- | A string of unicode characters representing the value in duodecimal notation.
  Text
duodecimalNumber :: forall i. Integral i => PlusStyle -> i -> Text
duodecimalNumber = 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 =>
  -- | The given number to convert.
  i ->
  -- | A string of unicode characters representing the value in duodecimal notation.
  Text
duodecimalNumber' :: forall i. Integral i => i -> Text
duodecimalNumber' = forall i. Integral i => PlusStyle -> i -> Text
duodecimalNumber forall a. Default a => a
def