1 /*
2    This file is part of the BOLT-LMM linear mixed model software package
3    developed by Po-Ru Loh.  Copyright (C) 2014-2019 Harvard University.
4 
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <vector>
20 #include <string>
21 #include <cstdlib>
22 #include <cstdio>
23 #include <iostream>
24 #include <sstream>
25 #include <sys/types.h>	// uint
26 
27 #include "StringUtils.hpp"
28 
29 namespace StringUtils {
30   using std::vector;
31   using std::string;
32   using std::cout;
33   using std::cerr;
34   using std::endl;
35 
stoi(const string & s)36   int stoi(const string &s) {
37     int i;
38     if (sscanf(s.c_str(), "%d", &i) == 0) {
39       cerr << "ERROR: Could not parse integer from string: " << s << endl;
40       exit(1);
41     }
42     return i;
43   }
stod(const string & s)44   double stod(const string &s) {
45     double d;
46     sscanf(s.c_str(), "%lf", &d);
47     return d;
48   }
itos(int i)49   string itos(int i) {
50     std::ostringstream oss;
51     oss << i;
52     return oss.str();
53   }
findDelimiters(const string & s,const string & c)54   string findDelimiters(const string &s, const string &c) {
55     string delims;
56     for (uint p = 0; p < s.length(); p++)
57       if (c.find(s[p], 0) != string::npos)
58 	delims += s[p];
59     return delims;
60   }
61   // will not return blanks
tokenizeMultipleDelimiters(const string & s,const string & c)62   vector <string> tokenizeMultipleDelimiters(const string &s, const string &c)
63   {
64     uint p = 0;
65     vector <string> ans;
66     string tmp;
67     while (p < s.length()) {
68       tmp = "";
69       while (p < s.length() && c.find(s[p], 0) != string::npos)
70 	p++;
71       while (p < s.length() && c.find(s[p], 0) == string::npos) {
72 	tmp += s[p];
73 	p++;
74       }
75       if (tmp != "")
76 	ans.push_back(tmp);
77     }
78     return ans;
79   }
80 
rangeErrorExit(const string & str,const string & delims)81   void rangeErrorExit(const string &str, const string &delims) {
82     cerr << "ERROR: Invalid delimiter sequence for specifying range: " << endl;
83     cerr << "  Template string: " << str << endl;
84     cerr << "  Delimiter sequence found: " << delims << endl;
85     cerr << "Range in must have format {start:end} with no other " << RANGE_DELIMS
86 	 << " chars" << endl;
87     exit(1);
88   }
89 
90   // basic range template: expand "{start:end}" to vector <string> with one entry per range element
91   // if end==start-1, will return empty
expandRangeTemplate(const string & str)92   vector <string> expandRangeTemplate(const string &str) {
93     vector <string> ret;
94     string delims = findDelimiters(str, RANGE_DELIMS);
95     if (delims.empty())
96       ret.push_back(str);
97     else if (delims == RANGE_DELIMS) {
98       vector <string> tokens = tokenizeMultipleDelimiters(str, RANGE_DELIMS);
99       for (int i = 0; i < (int) str.size(); i++)
100 	if (str[i] == ':' && (str[i-1] == '{' || str[i+1] == '}'))
101 	  rangeErrorExit(str, delims);
102       int startInd = (str[0] != RANGE_DELIMS[0]), endInd = startInd+1;
103       string prefix, suffix;
104       if (str[0] != RANGE_DELIMS[0]) prefix = tokens[0];
105       if (str[str.length()-1] != RANGE_DELIMS[2]) suffix = tokens.back();
106       int start = StringUtils::stoi(tokens[startInd]), end = StringUtils::stoi(tokens[endInd]);
107       if (start > end+1 || end > start+1000000) {
108 	cerr << "ERROR: Invalid range in template string: " << str << endl;
109 	cerr << "  Start: " << start << endl;
110 	cerr << "  End: " << end << endl;
111 	exit(1);
112       }
113       for (int i = start; i <= end; i++)
114 	ret.push_back(prefix + itos(i) + suffix);
115     }
116     else
117       rangeErrorExit(str, delims);
118     return ret;
119   }
120 
expandRangeTemplates(const vector<string> & rangeTemplates)121   vector <string> expandRangeTemplates(const vector <string> &rangeTemplates) {
122     vector <string> expanded;
123     for (uint i = 0; i < rangeTemplates.size(); i++) {
124       vector <string> range = expandRangeTemplate(rangeTemplates[i]);
125       expanded.insert(expanded.end(), range.begin(), range.end());
126     }
127     return expanded;
128   }
129 }
130