-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathhops.hs
152 lines (126 loc) · 4.52 KB
/
hops.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
-- |
-- Copyright : Anders Claesson
-- Maintainer : Anders Claesson <anders.claesson@gmail.com>
-- License : BSD-3
--
module Main (main) where
import GHC.TypeLits
import Data.Proxy
import Data.Maybe
import Data.Ratio
import qualified Data.Map as M
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as BL
import Data.Aeson (decodeStrict', encode)
import Control.Parallel.Strategies
import System.Directory
import System.IO
import HOPS.Entry
import HOPS.OEIS
import HOPS.Options
import HOPS.Config
import HOPS.DB
import HOPS.GF
versionString :: String
versionString = "0.8.5"
seqsURL :: String
seqsURL = "https://oeis.org/stripped.gz"
data Input (n :: Nat)
= RunPrgs (Env n) [Expr] [Core] [Entry]
| TagSeqs Int [Sequence]
| UpdateDBs FilePath
| Empty
data Output
= Entries [Entry]
| NOP
lines' :: BL.ByteString -> [B.ByteString]
lines' = filter (not . B.null) . map BL.toStrict . BL.lines
readStdin :: IO [B.ByteString]
readStdin = lines' <$> BL.getContents
decodeErr :: B.ByteString -> Entry
decodeErr = fromMaybe (error "error decoding JSON") . decodeStrict'
readEntries :: IO [Entry]
readEntries = map decodeErr <$> readStdin
readPrgs :: Options -> IO ([Expr], [Core])
readPrgs opts = do
prgs <- concatMap (expand . parseExprErr) <$>
if script opts == ""
then return (map B.pack (program opts))
else lines' <$> BL.readFile (script opts)
return (prgs, core <$> prgs)
mkEntry :: (ANum, Sequence) -> Entry
mkEntry (ANum a, s) = Entry (aNumExpr a) s []
readDB :: Config -> IO [Entry]
readDB = fmap (map mkEntry . parseStripped . unDB) . readSeqDB
readSeqs :: IO [Sequence]
readSeqs = map (parseIntegerSeq . B.filter (/=' ')) <$> readStdin
readInput :: KnownNat n => Options -> Config -> IO (Input n)
readInput opts cfg
| version opts = return Empty
| update opts = return $ UpdateDBs (hopsDir cfg)
| isJust (tagSeqs opts) = TagSeqs (fromJust (tagSeqs opts)) <$> readSeqs
| otherwise = do
(prgs, cprgs) <- readPrgs opts
inp <- if forAll opts
then readDB cfg
else if "stdin" `elem` (vars =<< cprgs)
then readEntries
else return []
db <- if null (anums =<< cprgs)
then return emptyANumDB
else readANumDB cfg
return $ RunPrgs (Env db M.empty) prgs cprgs inp
printOutput :: Output -> IO ()
printOutput NOP = return ()
printOutput (Entries es) = mapM_ (BL.putStrLn . encode) es
stdEnv :: KnownNat n => Proxy n -> Env n -> Sequence -> Env n
stdEnv n (Env a v) s = Env a $ M.insert "stdin" (series n (map Val s)) v
evalMany :: KnownNat n => Env n -> [Core] -> [Sequence]
evalMany env = map (rationalPrefix . evalCore env)
runPrgs :: KnownNat n => [Env n] -> [Core] -> [Sequence]
runPrgs envs progs =
concat ( [ evalMany env progs
| env <- envs
] `using` parBuffer 256 rdeepseq )
hops :: KnownNat n => Options -> Proxy n -> Input n -> IO Output
hops opts n inp =
case inp of
UpdateDBs hopsdir -> do
createDirectoryIfMissing False hopsdir
putStrLn "# You have download a file manually:"
putStrLn $ "cd " ++ hopsdir
putStrLn $ "wget " ++ seqsURL
putStrLn "gunzip stripped.gz"
putStrLn "cd -"
hFlush stdout
return NOP
TagSeqs i0 ts ->
return $ Entries [ Entry (tagExpr i) t [] | (i, t) <- zip [i0 .. ] ts ]
Empty -> putStrLn ("hops " ++ versionString) >> return NOP
RunPrgs env prgs cprgs entries ->
return $ Entries
[ Entry p f t
| (p, f, t) <- zip3 ps results (trails ++ repeat [])
, not (int opts) || all (\r -> denominator r == 1) f
, minPrec opts == 0 || minPrec opts <= length f
]
where
(qs, seqs, trails) = unzip3 [ (q,s,t) | Entry q s t <- entries ]
results = runPrgs envs cprgs
(ps, envs) = if null qs
then (prgs, [env])
else ((<>) <$> qs <*> prgs, map (stdEnv n env) seqs)
-- | Main function and entry point for hops.
main :: IO ()
main = do
c <- getConfig
t <- getOptions
let d = fromIntegral (prec t)
case someNatVal d of
Nothing -> error $ show d ++ " not a valid prec"
Just (SomeNat (_ :: Proxy n)) ->
readInput t c >>= hops t (Proxy :: Proxy n) >>= printOutput