oeis_utils/
lib.rs

1use lazy_static::lazy_static;
2use num_bigint::BigInt;
3use regex::Regex;
4use std::fs::File;
5use std::io::BufRead;
6use std::io::BufReader;
7use std::path::PathBuf;
8use std::str::FromStr;
9
10/// Represents the whole OEIS database, which is sequence of [`Series`]
11pub struct OEISDatabase {
12    series: Vec<Series>,
13}
14
15impl OEISDatabase {
16    pub fn series(&self) -> &Vec<Series> {
17        &self.series
18    }
19
20    pub fn from_path(path: &PathBuf) -> Result<Self, std::io::Error> {
21        let f = File::open(path);
22        match f {
23            Ok(f) => {
24                let reader = BufReader::new(f);
25                let series = reader
26                    .lines()
27                    .map(|s| s.unwrap())
28                    .skip_while(|s| s.starts_with('#'))
29                    .map(|s| Series::from_str(&s).unwrap())
30                    .collect();
31
32                Ok(Self { series })
33            }
34            Err(e) => Err(e),
35        }
36    }
37}
38
39#[derive(Debug, Clone, Hash, PartialEq)]
40pub enum NumberValue {
41    InRange(i128),
42    OutOfRange(BigInt),
43}
44
45/// Represents one series in the OEIS database. The `id` represents the number in "A000055".
46#[derive(Debug, Clone, Hash)]
47pub struct Series {
48    id: usize,
49    values: Vec<NumberValue>,
50}
51
52impl Series {
53    pub fn id(&self) -> usize {
54        self.id
55    }
56    pub fn values(&self) -> &Vec<NumberValue> {
57        &self.values
58    }
59}
60
61lazy_static! {
62    static ref RE: Regex = Regex::new(r#"A(?P<Id>\d{6}) (?P<vals>[,\-?\d*,]+),"#).unwrap();
63}
64
65impl FromStr for Series {
66    type Err = ();
67
68    fn from_str(s: &str) -> Result<Self, Self::Err> {
69        let caps = RE.captures(s);
70        match caps {
71            Some(m) => Ok(Self {
72                id: m.name("Id").unwrap().as_str().parse().unwrap(),
73                values: m
74                    .name("vals")
75                    .unwrap()
76                    .as_str()
77                    .split(',')
78                    .filter(|s| !s.is_empty())
79                    // .map(|s| BigInt::parse_bytes(s.as_bytes(), 10).unwrap())
80                    .map(|s| match s.parse::<i128>() {
81                        Ok(n) => NumberValue::InRange(n),
82                        Err(_) => {
83                            NumberValue::OutOfRange(BigInt::parse_bytes(s.as_bytes(), 10).unwrap())
84                        }
85                    })
86                    .collect(),
87            }),
88            None => Err(()),
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    #[test]
97    fn a344199() {
98        let text = "A344199 ,18,36,60,252,708,834,900,2178,7722,7980,";
99        let s = Series::from_str(text).unwrap();
100        assert_eq!(s.id(), 344199);
101        assert_eq!(
102            *s.values(),
103            vec![18, 36, 60, 252, 708, 834, 900, 2178, 7722, 7980]
104                .iter()
105                .map(|e| NumberValue::InRange(*e))
106                .collect::<Vec<NumberValue>>()
107        );
108    }
109
110    #[test]
111    fn a000001() {
112        let text = "A000001 ,0,1,1,1,2,1,2,1,5,2,2,1,5,1,2,1,14,1,5,1,5,2,2,1,15,2,2,5,4,1,4,1,51,1,2,1,14,1,2,2,14,1,6,1,4,2,2,1,52,2,5,1,5,1,15,2,13,2,2,1,13,1,2,4,267,1,4,1,5,1,4,1,50,1,2,3,4,1,6,1,52,15,2,1,15,1,2,1,12,1,10,1,4,2,";
113        let s = Series::from_str(text).unwrap();
114        assert_eq!(s.id(), 1);
115        assert_eq!(
116            *s.values(),
117            vec![
118                0, 1, 1, 1, 2, 1, 2, 1, 5, 2, 2, 1, 5, 1, 2, 1, 14, 1, 5, 1, 5, 2, 2, 1, 15, 2, 2,
119                5, 4, 1, 4, 1, 51, 1, 2, 1, 14, 1, 2, 2, 14, 1, 6, 1, 4, 2, 2, 1, 52, 2, 5, 1, 5,
120                1, 15, 2, 13, 2, 2, 1, 13, 1, 2, 4, 267, 1, 4, 1, 5, 1, 4, 1, 50, 1, 2, 3, 4, 1, 6,
121                1, 52, 15, 2, 1, 15, 1, 2, 1, 12, 1, 10, 1, 4, 2
122            ]
123            .iter()
124            .map(|e| NumberValue::InRange(*e))
125            .collect::<Vec<NumberValue>>()
126        );
127    }
128}