{-# LANGUAGE OverloadedStrings #-}

-- | This module provides utilities for manipulating numbers in Lojban.
module Language.Lojban.Numbers
( numberToLojban
, lojbanToNumber
) where

import Text.Read (readMaybe)
import Control.Applicative ((<$>), (<*>))
import qualified Data.Text as T

-- | Converts a decimal digit character into the corresponding Lojban cmavo.
digitToLojban :: Char -> T.Text
digitToLojban :: Char -> Text
digitToLojban Char
'0' = Text
"no"
digitToLojban Char
'1' = Text
"pa"
digitToLojban Char
'2' = Text
"re"
digitToLojban Char
'3' = Text
"ci"
digitToLojban Char
'4' = Text
"vo"
digitToLojban Char
'5' = Text
"mu"
digitToLojban Char
'6' = Text
"xa"
digitToLojban Char
'7' = Text
"ze"
digitToLojban Char
'8' = Text
"bi"
digitToLojban Char
'9' = Text
"so"

-- | Converts Lojban cmavo into the corresponding decimal digit character.
lojbanToDigit :: T.Text -> Maybe Char
lojbanToDigit :: Text -> Maybe Char
lojbanToDigit Text
"no" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'0'
lojbanToDigit Text
"pa" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'1'
lojbanToDigit Text
"re" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'2'
lojbanToDigit Text
"ci" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'3'
lojbanToDigit Text
"vo" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'4'
lojbanToDigit Text
"mu" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'5'
lojbanToDigit Text
"xa" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'6'
lojbanToDigit Text
"ze" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'7'
lojbanToDigit Text
"bi" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'8'
lojbanToDigit Text
"so" = Char -> Maybe Char
forall a. a -> Maybe a
Just Char
'9'
lojbanToDigit Text
_ = Maybe Char
forall a. Maybe a
Nothing

-- | Converts an integer into Lojban text.
numberToLojban :: Integer -> T.Text
numberToLojban :: Integer -> Text
numberToLojban = [Text] -> Text
T.concat ([Text] -> Text) -> (Integer -> [Text]) -> Integer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Text) -> [Char] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Char -> Text
digitToLojban ([Char] -> [Text]) -> (Integer -> [Char]) -> Integer -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> [Char]
forall a. Show a => a -> [Char]
show

-- | Converts Lojban text into an integer.
--
-- Supports the separator "ki'o", e.g. "pa ki'o ci" (1003).
lojbanToNumber :: T.Text -> Maybe Integer
lojbanToNumber :: Text -> Maybe Integer
lojbanToNumber Text
t =
    case [Maybe Text] -> Maybe [Text]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
forall (m :: * -> *) a. Monad m => [m a] -> m [a]
sequence [Maybe Text]
subnumbers of
        Just [Text]
subnumbers' -> [Char] -> Maybe Integer
forall a. Read a => [Char] -> Maybe a
readMaybe ([Char] -> Maybe Integer) -> [Char] -> Maybe Integer
forall a b. (a -> b) -> a -> b
$ Text -> [Char]
T.unpack (Text -> [Char]) -> Text -> [Char]
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat [Text]
subnumbers'
        Maybe [Text]
Nothing -> Maybe Integer
forall a. Maybe a
Nothing
    where subnumbers :: [Maybe Text]
subnumbers = (Maybe Text -> Maybe Text) -> [Maybe Text] -> [Maybe Text]
forall a b. (a -> b) -> [a] -> [b]
map ((Text -> Text) -> Maybe Text -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Text -> Text) -> Maybe Text -> Maybe Text)
-> (Text -> Text) -> Maybe Text -> Maybe Text
forall a b. (a -> b) -> a -> b
$ Int -> Char -> Text -> Text
T.justifyRight Int
3 Char
'0') ([Maybe Text] -> [Maybe Text]) -> [Maybe Text] -> [Maybe Text]
forall a b. (a -> b) -> a -> b
$ (Text -> Maybe Text) -> [Text] -> [Maybe Text]
forall a b. (a -> b) -> [a] -> [b]
map Text -> Maybe Text
simpleLojbanToNumberText ([Text] -> [Maybe Text]) -> [Text] -> [Maybe Text]
forall a b. (a -> b) -> a -> b
$ HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
"ki'o" Text
t

simpleLojbanToNumber :: T.Text -> Maybe Integer
simpleLojbanToNumber :: Text -> Maybe Integer
simpleLojbanToNumber Text
t =
    case Maybe Text
x of
        Just Text
x' -> [Char] -> Maybe Integer
forall a. Read a => [Char] -> Maybe a
readMaybe ([Char] -> Maybe Integer) -> [Char] -> Maybe Integer
forall a b. (a -> b) -> a -> b
$ Text -> [Char]
T.unpack Text
x'
        Maybe Text
Nothing -> Maybe Integer
forall a. Maybe a
Nothing
    where x :: Maybe Text
x = Text -> Maybe Text
simpleLojbanToNumberText Text
t

simpleLojbanToNumberText :: T.Text -> Maybe T.Text
simpleLojbanToNumberText :: Text -> Maybe Text
simpleLojbanToNumberText Text
"" = Text -> Maybe Text
forall a. a -> Maybe a
Just Text
""
simpleLojbanToNumberText Text
text =
    Char -> Text -> Text
T.cons (Char -> Text -> Text) -> Maybe Char -> Maybe (Text -> Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Maybe Char
lojbanToDigit Text
prefix Maybe (Text -> Text) -> Maybe Text -> Maybe Text
forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Maybe Text
simpleLojbanToNumberText Text
suffix
    where (Text
prefix, Text
suffix) = Int -> Text -> (Text, Text)
T.splitAt Int
2 Text
text