packages feed

sloane 1.6 → 1.7

raw patch · 6 files changed

+263/−81 lines, 6 filesdep +directorydep +filepathdep +networkdep −url

Dependencies added: directory, filepath, network, stringsearch, time, zlib

Dependencies removed: url

Files

+ CHANGELOG.md view
@@ -0,0 +1,9 @@+# Changelog++## sloane 1.7 - April 20, 2014++* Enable filtering against a local cache of sequences.++## sloane 1.6 - March 18, 2014++* Properly handle multiple search terms.
README.md view
@@ -1,12 +1,12 @@ ----title: SLOANE(1) Sloane User Manual | Version 1.6-date: March 18, 2014+title: SLOANE(1) Sloane User Manual | Version 1.7+date: April 20, 2014 ---  # NAME  sloane - a command line interface to Sloane's-On-Line Encyclopedia of Integer Sequences (OEIS) <http://oeis.org>+On-Line Encyclopedia of Integer Sequences <http://oeis.org>  # SYNOPSIS @@ -16,8 +16,32 @@  The sloane command searches Sloane's On-Line Encyclopedia of Integer Sequences (OEIS). The search terms are typically the leading term of a-sequence, but can be a sequence id (its A-number) or even arbitrary words.+sequence. For example, +    sloane 1,1,2,5,15,52,203,877,4140++returns entry A000110 (Bell numbers), and four more entries. One can+also search by sequence id (A-number), or even search for arbitrary+words. See the **EXAMPLES** section.++If no search terms are specified the standard input is read+line-by-line. In this mode the search is done locally against a+downloaded list of known sequences. If the sequence is in the OEIS, then+it is returned to the standard output; if not, it is ignored. This way+sloane can quickly filter out the sequences from the input that are in+the OEIS. Assuming that *FILE* contains one sequence per line,++    sloane <FILE++returns the subset of the sequences in *FILE* that are in the OEIS. To+also lookup the names of those sequences in the OEIS one could, for+instance, run++    sloane <FILE | xargs -L1 --verbose sloane++Sloane crops long lines to fit the widths of the terminal. If this is+unwanted, pipe the output through cat.+ # OPTIONS  -k --keys=KEYS@@ -27,10 +51,13 @@ :   Print all fields  -u --url-:   Only print urls of found entries+:   Print urls of found entries (but nothing else) +--update+:   Update the local sequence cache+ -n --limit=INT-:   Retrieve at most this many entries (default: 5)+:   Fetch at most this many entries (default: 5)  -? --help :   Display a short help message@@ -42,38 +69,43 @@  The most common search is for entries matching a sequence of consecutive terms: -~~~~~-sloane 1,3,19,183,2371,38703-~~~~~+    sloane 1,3,19,183,2371,38703  At the time of writing this particular query would return -~~~~~-S A006531 1,1,3,19,183,2371,38703,763099,17648823,468603091,14050842303,-N A006531 Semiorders on n elements.-~~~~~+    S A006531 1,1,3,19,183,2371,38703,763099,17648823,468603091,14050842303,+    N A006531 Semiorders on n elements.  As this illustrates, the default is to return just the sequence (S) and the name (N) fields. To override the default one can use the keys option. For instance, the following search shows the sequence, name, comments, and formula fields of the sequence whose A-number is A006531: -~~~~~-sloane -kSNCF id:A006531-~~~~~+    sloane --keys=SNCF id:A006531  The next example returns at most 3 results of a free text search: -~~~~~-sloane -n3 "(2+2)-free posets"-~~~~~+    sloane -n3 "(2+2)-free posets"  To view the full entries of these 3 results in a browser (e.g., firefox) one can use the url option: -~~~~~-firefox `sloane --url -n3 "(2+2)-free posets"`-~~~~~+    firefox `sloane --url -n3 "(2+2)-free posets"`++In the final example we use sloane to use the local cache to filter out+sequences from the standard input that are in the OEIS:++    sloane <<END+    1,2,3,6,11,23,47,106,235           # Comma separated integers+    1 2 444 90 120                     # Space separated integers+    '(3 9 27 88 123)                   # S-expression+    [2,3,5,7,11,13,17,19,23,29,31,37]  # Haskell list+    [1; 1; 2; 3; 5; 8; 13; 21]         # O'Caml list+    {1, -1, 2, -6, 24, -120, 720}      # Mathematica list+    END++The '#' character and any text after it is ignored by sloane. Note also+that sloane is quite liberal in the formatting of sequences it accepts.  # KEYS 
− changelog.md
@@ -1,4 +0,0 @@-sloane 1.6 - March 18, 2014-===========================--* Properly handle multiple search terms.
sloane.1 view
@@ -1,8 +1,8 @@-.TH "SLOANE" "1" "March 18, 2014" "Sloane User Manual" "Version 1.6"+.TH "SLOANE" "1" "March 18, 2014" "Sloane User Manual" "Version 1.7" .SH NAME .PP sloane \- a command line interface to Sloane\[aq]s On\-Line Encyclopedia-of Integer Sequences (OEIS) <http://oeis.org>+of Integer Sequences <http://oeis.org> .SH SYNOPSIS .PP sloane [\f[I]options\f[]] \f[I]search\-terms\f[]@@ -10,8 +10,49 @@ .PP The sloane command searches Sloane\[aq]s On\-Line Encyclopedia of Integer Sequences (OEIS).-The search terms are typically the leading term of a sequence, but can-be a sequence id (its A\-number) or even arbitrary words.+The search terms are typically the leading term of a sequence.+For example,+.IP+.nf+\f[C]+sloane\ 1,1,2,5,15,52,203,877,4140+\f[]+.fi+.PP+returns entry A000110 (Bell numbers), and four more entries.+One can also search by sequence id (A\-number), or even search for+arbitrary words.+See the \f[B]EXAMPLES\f[] section.+.PP+If no search terms are specified the standard input is read+line\-by\-line.+In this mode the search is done locally against a downloaded list of+known sequences.+If the sequence is in the OEIS, then it is returned to the standard+output; if not, it is ignored.+This way sloane can quickly filter out the sequences from the input that+are in the OEIS.+Assuming that \f[I]FILE\f[] contains one sequence per line,+.IP+.nf+\f[C]+sloane\ <FILE+\f[]+.fi+.PP+returns the subset of the sequences in \f[I]FILE\f[] that are in the+OEIS.+To also lookup the names of those sequences in the OEIS one could, for+instance, run+.IP+.nf+\f[C]+sloane\ <FILE\ |\ xargs\ \-L1\ \-\-verbose\ sloane+\f[]+.fi+.PP+Sloane crops long lines to fit the widths of the terminal.+If this is unwanted, pipe the output through cat. .SH OPTIONS .TP .B \-k \-\-keys=KEYS@@ -25,12 +66,17 @@ .RE .TP .B \-u \-\-url-Only print urls of found entries+Print urls of found entries (but nothing else) .RS .RE .TP+.B \-\-update+Update the local sequence cache+.RS+.RE+.TP .B \-n \-\-limit=INT-Retrieve at most this many entries (default: 5)+Fetch at most this many entries (default: 5) .RS .RE .TP@@ -71,7 +117,7 @@ .IP .nf \f[C]-sloane\ \-kSNCF\ id:A006531+sloane\ \-\-keys=SNCF\ id:A006531 \f[] .fi .PP@@ -91,6 +137,26 @@ firefox\ `sloane\ \-\-url\ \-n3\ "(2+2)\-free\ posets"` \f[] .fi+.PP+In the final example we use sloane to use the local cache to filter out+sequences from the standard input that are in the OEIS:+.IP+.nf+\f[C]+sloane\ <<END+1,2,3,6,11,23,47,106,235\ \ \ \ \ \ \ \ \ \ \ #\ Comma\ separated\ integers+1\ 2\ 444\ 90\ 120\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ #\ Space\ separated\ integers+\[aq](3\ 9\ 27\ 88\ 123)\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ #\ S\-expression+[2,3,5,7,11,13,17,19,23,29,31,37]\ \ #\ Haskell\ list+[1;\ 1;\ 2;\ 3;\ 5;\ 8;\ 13;\ 21]\ \ \ \ \ \ \ \ \ #\ O\[aq]Caml\ list+{1,\ \-1,\ 2,\ \-6,\ 24,\ \-120,\ 720}\ \ \ \ \ \ #\ Mathematica\ list+END+\f[]+.fi+.PP+The \[aq]#\[aq] character and any text after it is ignored by sloane.+Note also that sloane is quite liberal in the formatting of sequences it+accepts. .SH KEYS .PP These are the keys used by OEIS <http://oeis.org/eishelp2.html>.
sloane.cabal view
@@ -1,5 +1,5 @@ Name:                sloane-Version:             1.6+Version:             1.7 Synopsis:            A command line interface to Sloane's On-Line Encyclopedia of Integer Sequences Description:         A command line interface to Sloane's On-Line Encyclopedia of Integer Sequences.                      For usage see <http://github.com/akc/sloane>.@@ -10,7 +10,7 @@ Maintainer:          anders.claesson@gmail.com Category:            Math Build-type:          Custom-Extra-Source-Files:  README.md sloane.1 changelog.md+Extra-Source-Files:  README.md sloane.1 CHANGELOG.md  Cabal-version:       >=1.6 @@ -20,5 +20,7 @@  Executable sloane   Main-is:             sloane.hs-  Build-depends:       base >=3 && <5, cmdargs >=0.9, HTTP >=4000.0.9, url >=2,-                       bytestring >=0.9, ansi-terminal >=0.6, terminal-size >=0.2+  Build-depends:       base >=3 && <5, cmdargs >=0.9, HTTP >=4000.0.9, network >=2.4,+                       bytestring >=0.9, ansi-terminal >=0.6, terminal-size >=0.2,+                       filepath >=1.3, directory >=1.2, stringsearch >=0.3,+                       zlib >=0.5, time >=1.4
sloane.hs view
@@ -1,45 +1,73 @@ {-# LANGUAGE DeriveDataTypeable #-}  -- |--- Copyright   : Anders Claesson 2012, 2013, 2014+-- Copyright   : Anders Claesson 2012-2014 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com> -- License     : BSD-3 ---import Prelude hiding (all, putStrLn, putStr)-import Data.ByteString.Char8 (putStrLn, putStr, pack, empty)+import Prelude hiding (all)+import qualified Data.ByteString.Char8       as B+import qualified Data.ByteString.Lazy.Char8  as BL+import qualified Data.ByteString.Lazy.Search as Search+import qualified Codec.Compression.GZip      as GZip+import System.IO (stdin, stderr)+import System.Directory+import System.FilePath ((</>)) import System.Console.ANSI import System.Console.CmdArgs import System.Console.Terminal.Size (Window(..), size)-import Data.Maybe (fromJust)-import Control.Monad (unless, guard)-import Network.HTTP (simpleHTTP, getRequest, getResponseBody)-import Network.URL (importURL, exportURL, add_param)+import Data.Time (getCurrentTime, diffUTCTime)+import Data.Maybe (maybe, fromJust)+import Control.Monad (when, unless)+import Network.HTTP+import Network.URI (parseURI)  type OEISEntries = [String] type ANumbers = [String] type Query = String type Keys = String -oeisHost = "http://oeis.org"-oeisURL  = fromJust . importURL $ oeisHost ++ "/search?fmt=text"-oeisKeys = "ISTUVWXNDHFYAOEeptoKC"+oeisHost  = "http://oeis.org/"+oeisURL   = oeisHost ++ "search?fmt=text"+oeisKeys  = "ISTUVWXNDHFYAOEeptoKC" -data Sloane = Sloane { keys  :: String-                     , all   :: Bool-                     , url   :: Bool-                     , limit :: Int-                     , terms :: [String]+cacheDir  = ".sloane"+cacheFile = "stripped.gz"+cacheURL  = oeisHost ++ cacheFile++msgDownloadingCache = unlines+    [ "Downloading " ++ cacheURL+    , "This may take a minute or two ..."+    ]+msgCacheIsUpToDate = unlines+    [ "The sequence cache is now up-to-date"+    ]+msgNoCache = unlines+    [ "No sequence cache found. You need to run \"sloane --update\""+    ]+msgOldCache = unlines+    [ "The sequence cache is more than two weeks old"+    , "You may want to run \"sloane --update\""+    ]++data Sloane = Sloane { all    :: Bool+                     , keys   :: String+                     , limit  :: Int+                     , update :: Bool+                     , url    :: Bool+                     , terms  :: [String]                      }               deriving (Data, Typeable)  sloane = cmdArgsMode $ Sloane-  { keys = "SN"  &= typ "KEYS" &= help "Keys of fields to print (default: SN)"-  , all  = False &= name "a"   &= help "Print all fields"-  , url  = False &= name "u"   &= help "Print urls of found entries"-  , limit = 5 &= name "n" &= help "Retrieve at most this many entries (default: 5)"+  { all = False &= name "a" &= help "Print all fields"+  , keys = "SN" &= typ "KEYS" &= help "Keys of fields to print (default: SN)"+  , limit = 5 &= name "n" &= help "Fetch at most this many entries (default: 5)"+  , update = False &= help "Update the local sequence cache"+  , url = False &= name "u" &= help "Print urls of found entries"   , terms = def &= args &= typ "SEARCH-TERMS"   }-  &= versionArg [summary "sloane 1.6"]+  &= versionArg [summary "sloane 1.7"]   &= summary "Search Sloane's On-Line Encyclopedia of Integer Sequences"  select :: Keys -> OEISEntries -> OEISEntries@@ -49,32 +77,44 @@ aNumbers es = [ words ids !! 1 | ids@(_:_) <- select "I" es ]  urls :: OEISEntries -> String-urls = unlines . map ((oeisHost ++ "/") ++ ) . aNumbers+urls = unlines . map (oeisHost ++ ) . aNumbers +get :: HStream b => String -> IO b+get uri = simpleHTTP (defaultGETRequest_ uri') >>= getResponseBody+  where+    uri' = fromJust $ parseURI uri++updateCache :: FilePath -> IO ()+updateCache home = do+    let dir = home </> cacheDir+    createDirectoryIfMissing False dir+    put msgDownloadingCache+    cache <- get cacheURL+    B.writeFile (dir </> cacheFile) cache+    put msgCacheIsUpToDate+ searchOEIS :: Int -> Query -> IO OEISEntries-searchOEIS n s =-    trim `fmap` (simpleHTTP (getRequest url) >>= getResponseBody)+searchOEIS n s = trim `fmap` get uri   where     trim = map (drop 1) . reverse . drop 2 . reverse . drop 5 . lines-    url = exportURL $ oeisURL `add_param` ("n", show n) `add_param` ("q", s)+    uri = oeisURL ++ "&" ++ urlEncodeVars [("n", show n), ("q", s)] +cropStr :: (Int -> String -> String) -> Int -> String -> String+cropStr f maxLen s = if maxLen < length s then f maxLen s else s+ cropSeq :: Int -> String -> String-cropSeq maxLen = reverse . dropWhile (/= ',') . reverse . take maxLen+cropSeq = cropStr $ \maxLen ->+              reverse . dropWhile (/= ',') . reverse . take maxLen  cropLine :: Int -> String -> String-cropLine maxLen s-    | maxLen >= length s = s-    | otherwise          = take (maxLen-2) s ++ ".."+cropLine = cropStr $ \maxLen s -> take (maxLen-2) s ++ ".."  getWidth :: IO Int-getWidth = f `fmap` size-  where-    f Nothing    = maxBound-    f (Just win) = width win+getWidth = maybe maxBound width `fmap` size -put = putStr . pack-putLn = putStrLn . pack-newline = putStrLn empty+put = B.putStr . B.pack+putErr = B.hPutStr stderr . B.pack+newline = B.putStrLn B.empty  putEntries :: Int -> OEISEntries -> IO () putEntries width = mapM_ $ \line ->@@ -89,15 +129,52 @@             let crop = if key == "S" then cropSeq else cropLine             put $ ' ' : crop width (unwords rest) ++ "\n" +isInfixOf :: B.ByteString -> BL.ByteString -> Bool+isInfixOf q = not . null . Search.indices q++readCache :: FilePath -> IO BL.ByteString+readCache home = do+    let name = home </> cacheDir </> cacheFile+    updated <- doesFileExist name+    if updated+        then do+            c <- getCurrentTime+            m <- getModificationTime name+            let week = 60*60*24*7+            let expired = c `diffUTCTime` m > 2*week+            when expired $ putErr msgOldCache+            GZip.decompress `fmap` BL.readFile name+        else+            error msgNoCache++seqs :: B.ByteString -> [B.ByteString]+seqs = filter (not . B.null) . map mkSeq . B.lines+  where+    mkSeq = normalize . dropComment+    dropComment = B.takeWhile (/= '#')+    normalize   = B.intercalate (B.pack ",") . B.words . clean . B.map tr+    tr c  = if c `elem` ";," then ' ' else c+    clean = B.filter (\c -> B.elem c (B.pack " 0123456789-"))++filterSeqs :: FilePath -> IO ()+filterSeqs home = do+    cache <- readCache home+    B.getContents >>= mapM_ B.putStrLn . filter (`isInfixOf` cache) . seqs+ main = do+    home <- getHomeDirectory     args <- cmdArgsRun sloane-    ncols <- getWidth-    let pick = if all args then id else select (keys args)     let query = unwords $ terms args-    hits <- searchOEIS (limit args) query-    unless (null hits) $ do-        newline-        if url args-            then put (urls hits)-            else putEntries (ncols - 10) (pick hits)-        newline+    case (update args, null query) of+        (True, _) -> updateCache home+        (_, True) -> filterSeqs home+        _         -> do+            ncols <- getWidth+            hits  <- searchOEIS (limit args) query+            let pick = if all args then id else select (keys args)+            unless (null hits) $ do+                newline+                if url args+                    then put (urls hits)+                    else putEntries (ncols - 10) (pick hits)+                newline