    -- | 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
              mbody <- URI -> IO (Maybe String)
    get URI
    uri
              return $ maybe [] parseOEIS mbody

    get :: URI -> IO (Maybe String)
    get :: URI -> IO (Maybe String)
    get URI
    uri = do
        httpman <- ManagerSettings -> IO Manager
    HTTP.newManager ManagerSettings
    HTTP.tlsManagerSettings
        let request = URI -> Request
    HTTP.requestFromURI_ URI
    uri
        response <- HTTP.httpLbs request httpman
        return $ case HTTP.statusIsSuccessful (HTTP.responseStatus 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