-- | This module exists just to facilitate testing.
-- /Nothing here is part of the OEIS API./

module Math.OEIS.Internal where

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

import Control.Arrow (second, (***))
import Data.Char     (isSpace, toUpper, toLower)
import Data.List     (intercalate, isPrefixOf, foldl')
import Network.URI   (parseURI, URI)

import qualified Network.HTTP.Client       as HTTP
import qualified Network.HTTP.Client.TLS   as HTTP
import qualified Network.HTTP.Types        as HTTP
import qualified Data.ByteString.Lazy.UTF8 as U

import Math.OEIS.Types

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

baseSearchURI :: String
baseSearchURI :: String
baseSearchURI = String
"https://oeis.org/search?fmt=text&q="

idSearchURI :: String -> String
idSearchURI :: String -> String
idSearchURI String
n = String
baseSearchURI String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"id:" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
n

seqSearchURI :: SequenceData -> String
seqSearchURI :: SequenceData -> String
seqSearchURI SequenceData
xs = String
baseSearchURI String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," ((Integer -> String) -> SequenceData -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Integer -> String
forall a. Show a => a -> String
show SequenceData
xs)

getOEIS :: (a -> String) -> a -> IO [OEISSequence]
getOEIS :: forall a. (a -> String) -> a -> IO [OEISSequence]
getOEIS a -> String
toURI a
key =
    case String -> Maybe URI
parseURI (a -> String
toURI a
key) of
      Maybe URI
Nothing  -> [OEISSequence] -> IO [OEISSequence]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return []
      Just URI
uri -> do
          Maybe String
mbody <- URI -> IO (Maybe String)
get URI
uri
          [OEISSequence] -> IO [OEISSequence]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([OEISSequence] -> IO [OEISSequence])
-> [OEISSequence] -> IO [OEISSequence]
forall a b. (a -> b) -> a -> b
$ [OEISSequence]
-> (String -> [OEISSequence]) -> Maybe String -> [OEISSequence]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] String -> [OEISSequence]
parseOEIS Maybe String
mbody

get :: URI -> IO (Maybe String)
get :: URI -> IO (Maybe String)
get URI
uri = do
    Manager
httpman <- ManagerSettings -> IO Manager
HTTP.newManager ManagerSettings
HTTP.tlsManagerSettings
    let request :: Request
request = URI -> Request
HTTP.requestFromURI_ URI
uri
    Response ByteString
response <- Request -> Manager -> IO (Response ByteString)
HTTP.httpLbs Request
request Manager
httpman
    Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ case Status -> Bool
HTTP.statusIsSuccessful (Response ByteString -> Status
forall body. Response body -> Status
HTTP.responseStatus Response ByteString
response) of
        Bool
False -> Maybe String
forall a. Maybe a
Nothing
        Bool
True -> String -> Maybe String
forall a. a -> Maybe a
Just (ByteString -> String
U.toString (Response ByteString -> ByteString
forall body. Response body -> body
HTTP.responseBody Response ByteString
response))

readKeyword :: String -> Keyword
readKeyword :: String -> Keyword
readKeyword = String -> Keyword
forall a. Read a => String -> a
read (String -> Keyword) -> (String -> String) -> String -> Keyword
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
capitalize

capitalize :: String -> String
capitalize :: String -> String
capitalize String
""     = String
""
capitalize (Char
c:String
cs) = Char -> Char
toUpper Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower String
cs

emptyOEIS :: OEISSequence
emptyOEIS :: OEISSequence
emptyOEIS = [String]
-> SequenceData
-> SequenceData
-> String
-> [String]
-> [String]
-> [String]
-> [String]
-> String
-> Int
-> Int
-> [(Language, String)]
-> [String]
-> [String]
-> [Keyword]
-> [String]
-> OEISSequence
OEIS [] [] [] String
"" [] [] [] [] String
"" Int
0 Int
0 [] [] [] [] []

addElement :: (Char, String) -> OEISSequence -> OEISSequence
addElement :: (Char, String) -> OEISSequence -> OEISSequence
addElement (Char
'I', String
x) OEISSequence
c = OEISSequence
c { catalogNums = words x }
addElement (Char
t, String
x)   OEISSequence
c | Char
t Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
"STU" = OEISSequence
c { sequenceData = nums ++ sequenceData c }
    where nums :: SequenceData
nums = (String -> Integer) -> [String] -> SequenceData
forall a b. (a -> b) -> [a] -> [b]
map String -> Integer
forall a. Read a => String -> a
read ([String] -> SequenceData) -> [String] -> SequenceData
forall a b. (a -> b) -> a -> b
$ String -> [String]
csvItems String
x
addElement (Char
t, String
x)   OEISSequence
c | Char
t Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
"VWX" = OEISSequence
c { signedData = nums ++ signedData c }
    where nums :: SequenceData
nums = (String -> Integer) -> [String] -> SequenceData
forall a b. (a -> b) -> [a] -> [b]
map String -> Integer
forall a. Read a => String -> a
read ([String] -> SequenceData) -> [String] -> SequenceData
forall a b. (a -> b) -> a -> b
$ String -> [String]
csvItems String
x
addElement (Char
'N', String
x) OEISSequence
c = OEISSequence
c { description = x                  }
addElement (Char
'D', String
x) OEISSequence
c = OEISSequence
c { references  = x : references c }
addElement (Char
'H', String
x) OEISSequence
c = OEISSequence
c { links       = x : links c      }
addElement (Char
'F', String
x) OEISSequence
c = OEISSequence
c { formulas    = x : formulas c   }
addElement (Char
'Y', String
x) OEISSequence
c = OEISSequence
c { xrefs       = x : xrefs c      }
addElement (Char
'A', String
x) OEISSequence
c = OEISSequence
c { author      = x                  }
addElement (Char
'O', String
x) OEISSequence
c = OEISSequence
c { offset      = read o
                          , firstGT1    = read f }
  where (String
o,String
f) = (String -> String) -> (String, String) -> (String, String)
forall b c d. (b -> c) -> (d, b) -> (d, c)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second String -> String
forall a. HasCallStack => [a] -> [a]
tail ((String, String) -> (String, String))
-> (String -> (String, String)) -> String -> (String, String)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
',') (String -> (String, String)) -> String -> (String, String)
forall a b. (a -> b) -> a -> b
$ String
x
addElement (Char
'p', String
x) OEISSequence
c = OEISSequence
c { programs    = (Maple, x) :
                                            programs c     }
addElement (Char
't', String
x) OEISSequence
c = OEISSequence
c { programs    = (Mathematica, x) :
                                            programs c     }
addElement (Char
'o', String
x) OEISSequence
c = OEISSequence
c { programs    = (Other, x) :
                                            programs c     }
addElement (Char
'E', String
x) OEISSequence
c = OEISSequence
c { extensions  = x : extensions c }
addElement (Char
'e', String
x) OEISSequence
c = OEISSequence
c { examples    = x : examples c   }
addElement (Char
'K', String
x) OEISSequence
c = OEISSequence
c { keywords    = parseKeywords x    }
addElement (Char
'C', String
x) OEISSequence
c = OEISSequence
c { comments    = x : comments c   }
addElement (Char, String)
_ OEISSequence
c = OEISSequence
c

parseOEIS :: String -> [OEISSequence]
parseOEIS :: String -> [OEISSequence]
parseOEIS String
x = if String
"No results." String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` ([String]
ls[String] -> Int -> String
forall a. HasCallStack => [a] -> Int -> a
!!Int
3)
                then []
                else [(Char, String)] -> [OEISSequence]
go ([(Char, String)] -> [OEISSequence])
-> ([String] -> [(Char, String)]) -> [String] -> [OEISSequence]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Char, String) -> Bool) -> [(Char, String)] -> [(Char, String)]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile ((Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'I') (Char -> Bool)
-> ((Char, String) -> Char) -> (Char, String) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char, String) -> Char
forall a b. (a, b) -> a
fst) ([(Char, String)] -> [(Char, String)])
-> ([String] -> [(Char, String)]) -> [String] -> [(Char, String)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> [(Char, String)]
parseRawOEIS ([String] -> [OEISSequence]) -> [String] -> [OEISSequence]
forall a b. (a -> b) -> a -> b
$ [String]
ls'
    where ls :: [String]
ls = String -> [String]
lines String
x
          ls' :: [String]
ls' = [String] -> [String]
forall a. HasCallStack => [a] -> [a]
init ([String] -> [String])
-> ([String] -> [String]) -> [String] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> [String] -> [String]
forall a. Int -> [a] -> [a]
drop Int
5 ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ [String]
ls
          go :: [(Char, String)] -> [OEISSequence]
go [] = []
          go ((Char, String)
i:[(Char, String)]
xs) = (OEISSequence -> (Char, String) -> OEISSequence)
-> OEISSequence -> [(Char, String)] -> OEISSequence
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (((Char, String) -> OEISSequence -> OEISSequence)
-> OEISSequence -> (Char, String) -> OEISSequence
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Char, String) -> OEISSequence -> OEISSequence
addElement) OEISSequence
emptyOEIS ([(Char, String)] -> [(Char, String)]
forall a. [a] -> [a]
reverse ((Char, String)
i(Char, String) -> [(Char, String)] -> [(Char, String)]
forall a. a -> [a] -> [a]
:[(Char, String)]
ys)) OEISSequence -> [OEISSequence] -> [OEISSequence]
forall a. a -> [a] -> [a]
: [(Char, String)] -> [OEISSequence]
go [(Char, String)]
zs
              where ([(Char, String)]
ys, [(Char, String)]
zs) = ((Char, String) -> Bool)
-> [(Char, String)] -> ([(Char, String)], [(Char, String)])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break ((Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'I') (Char -> Bool)
-> ((Char, String) -> Char) -> (Char, String) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char, String) -> Char
forall a b. (a, b) -> a
fst) [(Char, String)]
xs

parseRawOEIS :: [String] -> [(Char, String)]
parseRawOEIS :: [String] -> [(Char, String)]
parseRawOEIS = (String -> (Char, String)) -> [String] -> [(Char, String)]
forall a b. (a -> b) -> [a] -> [b]
map String -> (Char, String)
parseItem ([String] -> [(Char, String)])
-> ([String] -> [String]) -> [String] -> [(Char, String)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> [String]
combineConts

parseKeywords :: String -> [Keyword]
parseKeywords :: String -> [Keyword]
parseKeywords = (String -> Keyword) -> [String] -> [Keyword]
forall a b. (a -> b) -> [a] -> [b]
map String -> Keyword
readKeyword ([String] -> [Keyword])
-> (String -> [String]) -> String -> [Keyword]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
csvItems

csvItems :: String -> [String]
csvItems :: String -> [String]
csvItems String
"" = []
csvItems String
x = String
item String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
others
    where (String
item, String
rest) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
',') String
x
          others :: [String]
others = String -> [String]
csvItems (String -> [String]) -> String -> [String]
forall a b. (a -> b) -> a -> b
$ Char -> String -> String
del Char
',' String
rest

del :: Char -> String -> String
del :: Char -> String -> String
del Char
_ String
""     = String
""
del Char
c (Char
x:String
xs) | Char
cChar -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==Char
x      = String
xs
             | Bool
otherwise = Char
xChar -> String -> String
forall a. a -> [a] -> [a]
:String
xs

parseItem :: String -> (Char, String)
parseItem :: String -> (Char, String)
parseItem String
s = (Char
c, String
str)
    where ( Char
'%':Char
c:String
_ , String
rest) = String -> (String, String)
splitWord String
s
          (String
_, String
str )    = if Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'I' then (String
"", String
rest)
                                     else String -> (String, String)
splitWord String
rest

combineConts :: [String] -> [String]
combineConts :: [String] -> [String]
combineConts (s :: String
s@(Char
'%':Char
_:String
_) : [String]
ss) =
  (String -> [String] -> [String]) -> (String, [String]) -> [String]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (:) ((String, [String]) -> [String])
-> ([String] -> (String, [String])) -> [String] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> [String] -> String
joinConts String
s ([String] -> String)
-> ([String] -> [String])
-> ([String], [String])
-> (String, [String])
forall b c b' c'. (b -> c) -> (b' -> c') -> (b, b') -> (c, c')
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** [String] -> [String]
combineConts) (([String], [String]) -> (String, [String]))
-> ([String] -> ([String], [String]))
-> [String]
-> (String, [String])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Bool) -> [String] -> ([String], [String])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break String -> Bool
isItem ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ [String]
ss
combineConts [String]
ss = [String]
ss

splitWord :: String -> (String, String)
splitWord :: String -> (String, String)
splitWord = (String -> String) -> (String, String) -> (String, String)
forall b c d. (b -> c) -> (d, b) -> (d, c)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second String -> String
trimLeft ((String, String) -> (String, String))
-> (String -> (String, String)) -> String -> (String, String)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isSpace

isItem :: String -> Bool
isItem :: String -> Bool
isItem String
x = Bool -> Bool
not (String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
x) Bool -> Bool -> Bool
&& Char
'%' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Char
forall a. HasCallStack => [a] -> a
head String
x

joinConts :: String -> [String] -> String
joinConts :: String -> [String] -> String
joinConts String
s [String]
conts = String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String -> String) -> [String] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap String -> String
trimLeft [String]
conts

trimLeft :: String -> String
trimLeft :: String -> String
trimLeft = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isSpace