1 //==============================================================================
2 //
3 //  This file is part of GPSTk, the GPS Toolkit.
4 //
5 //  The GPSTk is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU Lesser General Public License as published
7 //  by the Free Software Foundation; either version 3.0 of the License, or
8 //  any later version.
9 //
10 //  The GPSTk 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 Lesser General Public License for more details.
14 //
15 //  You should have received a copy of the GNU Lesser General Public
16 //  License along with GPSTk; if not, write to the Free Software Foundation,
17 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 //  This software was developed by Applied Research Laboratories at the
20 //  University of Texas at Austin.
21 //  Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 //  This software was developed by Applied Research Laboratories at the
28 //  University of Texas at Austin, under contract to an agency or agencies
29 //  within the U.S. Department of Defense. The U.S. Government retains all
30 //  rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 //  Pursuant to DoD Directive 523024
33 //
34 //  DISTRIBUTION STATEMENT A: This software has been approved for public
35 //                            release, distribution is unlimited.
36 //
37 //==============================================================================
38 
39 /**
40  * @file Namelist.cpp
41  * Implementation of class Namelist.
42  * class gpstk::Namelist encapsulates a list of labels for use with classes Matrix,
43  * Vector and SRI.
44  */
45 
46 //------------------------------------------------------------------------------------
47 // system includes
48 #include <string>
49 #include <vector>
50 #include <algorithm>
51 #include <ostream>
52 #include <fstream> // for copyfmt
53 // GPSTk
54 #include "Exception.hpp"
55 #include "StringUtils.hpp"
56 #include "Namelist.hpp"
57 
58 using namespace std;
59 
60 namespace gpstk
61 {
62 
63 using namespace StringUtils;
64 
65 //------------------------------------------------------------------------------------
66 // constructor given dimension - creates default labels
Namelist(const unsigned int & n)67 Namelist::Namelist(const unsigned int& n)
68 {
69 try {
70    if(n == 0) return;
71    string name;
72    for(unsigned int i=0; i<n; i++) {
73       ostringstream oss;
74       oss << "NAME" << setw(3) << setfill('0') << i;
75       name = oss.str();
76       labels.push_back(name);
77    }
78 }
79 catch(Exception& e) { GPSTK_RETHROW(e); }
80 }
81 
82 // explicit constructor - only a unique subset of names will be included.
Namelist(const vector<string> & names)83 Namelist::Namelist(const vector<string>& names)
84 {
85 try {
86    for(unsigned int i=0; i<names.size(); i++) {
87       bool unique=true;
88       for(unsigned int j=i+1; j<names.size(); j++) {
89          if(names[i] == names[j]) { unique=false; break; }
90       }
91       if(unique) labels.push_back(names[i]);
92    }
93 }
94 catch(Exception& e) { GPSTK_RETHROW(e); }
95 }
96 
97 // add a name to the Namelist; throw if the name is not unique.
operator +=(const string & name)98 Namelist& Namelist::operator+=(const string& name)
99 {
100 try {
101    if(contains(name))
102       GPSTK_THROW(Exception("Name is not unique: " + name));
103    labels.push_back(name);
104    return *this;
105 }
106 catch(Exception& e) { GPSTK_RETHROW(e); }
107 }
108 
109 // remove a name from the Namelist; does nothing if the name is not found.
operator -=(const string & name)110 Namelist& Namelist::operator-=(const string& name)
111 {
112 try {
113    vector<string>::iterator it;
114    it = find(labels.begin(),labels.end(),name);
115    if(it != labels.end())
116       labels.erase(it);
117    return *this;
118 }
119 catch(Exception& e) { GPSTK_RETHROW(e); }
120 }
121 
122 // swap two elements, as given by their indexes; no effect if either index
123 // is out of range.
swap(const unsigned int & i,const unsigned int & j)124 void Namelist::swap(const unsigned int& i, const unsigned int& j)
125 {
126 try {
127    if(i == j) return;
128    if(i >= labels.size() || j >= labels.size()) return;
129    string str = labels[i];
130    labels[i] = labels[j];
131    labels[j] = str;
132 }
133 catch(Exception& e) { GPSTK_RETHROW(e); }
134 }
135 
136 // reorder the list by sorting
sort(void)137 void Namelist::sort(void)
138 {
139 try {
140    // compiler tries Namelist::sort() first...
141    std::sort(labels.begin(),labels.end());
142 }
143 catch(Exception& e) { GPSTK_RETHROW(e); }
144 }
145 
146 // resize the list by either truncation or adding default names.
resize(unsigned int n)147 void Namelist::resize(unsigned int n)
148 {
149 try {
150    if(n == labels.size()) return;
151    int N=labels.size();
152    while(labels.size() < n) {
153       string s;
154       do {
155          ostringstream oss;
156          oss << "NAME" << setw(3) << setfill('0') << N;
157          s = oss.str();
158          N++;
159       } while(contains(s));
160       labels.push_back(s);
161    }
162    while(labels.size() > n) {
163       labels.pop_back();
164    }
165 }
166 catch(Exception& e) { GPSTK_RETHROW(e); }
167 }
168 
169 // randomize the list
randomize(long seed)170 void Namelist::randomize(long seed)
171 {
172 try {
173    if(labels.size() <= 1) return;
174    //random_shuffle(labels.begin(), labels.end());
175    if(seed) std::srand(seed);
176    for(int i=labels.size()-1; i>0; --i) {
177       using std::swap;
178       swap(labels[i], labels[std::rand() % (i+1)]);
179    }
180 }
181 catch(Exception& e) { GPSTK_RETHROW(e); }
182 }
183 
184 // is the Namelist valid? checks for repeated names
valid(void) const185 bool Namelist::valid(void) const
186 {
187 try {
188    for(unsigned int i=0; i<labels.size(); i++)
189       for(unsigned int j=i+1; j<labels.size(); j++)
190          if(labels[i] == labels[j]) return false;
191    return true;
192 }
193 catch(Exception& e) { GPSTK_RETHROW(e); }
194 }
195 
196 // does the Namelist contain the input name?
contains(const string & name) const197 bool Namelist::contains(const string& name) const
198 {
199 try {
200    for(unsigned int i=0; i<labels.size(); i++) {
201       if(labels[i] == name) return true;
202    }
203    return false;
204 }
205 catch(Exception& e) { GPSTK_RETHROW(e); }
206 }
207 
208 // are two Namelists identical, ignoring a permutation?
operator ==(const Namelist & N1,const Namelist & N2)209 bool operator==(const Namelist& N1, const Namelist& N2)
210 {
211 try {
212    if(N1.size() != N2.size()) return false;
213    if(N1.size() == 0) return true;
214    for(unsigned int i=0; i<N1.size(); i++) {
215       unsigned int match=0;
216       for(unsigned int j=0; j<N2.size(); j++)
217          if(N1.labels[i] == N2.labels[j]) match++;
218       if(match != 1) return false;     // if > 1, N2 is invalid
219    }
220    return true;
221 }
222 catch(Exception& e) { GPSTK_RETHROW(e); }
223 }
224 
225 // are two Namelists different, ignoring a permutation?
operator !=(const Namelist & N1,const Namelist & N2)226 bool operator!=(const Namelist& N1, const Namelist& N2)
227 {
228 try {
229    return !(N1==N2);
230 }
231 catch(Exception& e) { GPSTK_RETHROW(e); }
232 }
233 
234 // are two Namelists exactly identical, even considering permutations?
identical(const Namelist & N1,const Namelist & N2)235 bool identical(const Namelist& N1, const Namelist& N2)
236 {
237 try {
238    if(N1.size() != N2.size()) return false;
239    if(N1.size() == 0) return true;
240    for(unsigned int i=0; i<N1.size(); i++) {
241       if(N1.labels[i] != N2.labels[i]) return false;
242    }
243    return true;
244 }
245 catch(Exception& e) { GPSTK_RETHROW(e); }
246 }
247 
248 // construct the subset Namelist which is common to the two input (AND)
operator &(const Namelist & N1,const Namelist & N2)249 Namelist operator&(const Namelist& N1, const Namelist& N2)
250 {
251 try {
252    Namelist N(N1);
253    N &= N2;
254    return N;
255 }
256 catch(Exception& e) { GPSTK_RETHROW(e); }
257 }
258 
259 // merge two Namelists, i.e. construct a non-redundant combination (OR)
operator |(const Namelist & N1,const Namelist & N2)260 Namelist operator|(const Namelist& N1, const Namelist& N2)
261 {
262 try {
263    Namelist N(N1);
264    N |= N2;
265    return N;
266 }
267 catch(Exception& e) { GPSTK_RETHROW(e); }
268 }
269 
270 // construct the subset Namelist which is NOT common to two others (XOR)
operator ^(const Namelist & N1,const Namelist & N2)271 Namelist operator^(const Namelist& N1, const Namelist& N2)
272 {
273 try {
274    Namelist N(N1);
275    N ^= N2;
276    return N;
277 }
278 catch(Exception& e) { GPSTK_RETHROW(e); }
279 }
280 
281 // replace this with (this & input) (AND - common to both)
operator &=(const Namelist & N)282 Namelist& Namelist::operator&=(const Namelist& N)
283 {
284 try {
285    Namelist NAND;
286    for(unsigned int i=0; i<N.labels.size(); i++)
287       if(contains(N.labels[i])) NAND += N.labels[i];
288    *this = NAND;
289    return *this;
290 }
291 catch(Exception& e) { GPSTK_RETHROW(e); }
292 }
293 
294 // replace this with (this | input) (OR - merge - superset)
295 // NB new elements must be added at the end (for class SRI).
operator |=(const Namelist & N)296 Namelist& Namelist::operator|=(const Namelist& N)
297 {
298 try {
299    Namelist NOR(*this);
300    for(unsigned int i=0; i<N.labels.size(); i++)
301       if(!(contains(N.labels[i]))) NOR += N.labels[i];
302    *this = NOR;
303    return *this;
304 }
305 catch(Exception& e) { GPSTK_RETHROW(e); }
306 }
307 
308 // replace this with (this ^ input) (XOR - not common)
operator ^=(const Namelist & N)309 Namelist& Namelist::operator^=(const Namelist& N)
310 {
311 try {
312    unsigned int i;
313    Namelist NXOR;
314    for(i=0; i<labels.size(); i++)
315       if(!(N.contains(labels[i]))) NXOR += labels[i];
316    for(i=0; i<N.labels.size(); i++)
317       if(!(contains(N.labels[i]))) NXOR += N.labels[i];
318    *this = NXOR;
319    return *this;
320 }
321 catch(Exception& e) { GPSTK_RETHROW(e); }
322 }
323 
324 
325 // access to a specific name, given its index; may be used as lvalue.
326 //string& Namelist::operator[](const unsigned int in)
327 //{
328 //   if(in >= labels.size()) throw ...
329 //   return labels[in];
330 //}
331 
332 // access to a specific name, given its index.
333 // returns 'out-of-range' if the index is out of range.
getName(const unsigned int in) const334 string Namelist::getName(const unsigned int in) const
335 {
336 try {
337    if(in >= labels.size()) return string("out-of-range");
338    return labels[in];
339 }
340 catch(Exception& e) { GPSTK_RETHROW(e); }
341 }
342 
343 // assign a specific name, given its index;
344 // no effect if the index is out of range or the name is not unique.
345 // return true if successful
setName(const unsigned int in,const string & name)346 bool Namelist::setName(const unsigned int in, const string& name)
347 {
348 try {
349    if(in >= labels.size()) return false;
350    if(labels[in] == name) return true;    // NB b/c contains(name) would be true..
351    if(contains(name)) return false;
352    labels[in] = name;
353    return true;
354 }
355 catch(Exception& e) { GPSTK_RETHROW(e); }
356 }
357 
358 // return the index of the name in the list that matches the input, -1 if not found.
index(const string & name) const359 int Namelist::index(const string& name) const
360 {
361 try {
362    for(unsigned int i=0; i<labels.size(); i++)
363       if(labels[i] == name) return i;
364    return -1;
365 }
366 catch(Exception& e) { GPSTK_RETHROW(e); }
367 }
368 
369 // output operator
operator <<(ostream & os,const Namelist & N)370 ostream& operator<<(ostream& os, const Namelist& N)
371 {
372 try {
373    if(N.labels.size() > 0) {
374       for(unsigned int i=0; i<N.labels.size(); i++)
375          os << " / " << N.labels[i];
376       os << " / ";
377    }
378    return os;
379 }
380 catch(Exception& e) { GPSTK_RETHROW(e); }
381 }
382 
operator <<(ostream & os,const LabeledVector & LV)383 ostream& operator<<(ostream& os, const LabeledVector& LV)
384 {
385 try {
386    size_t i;
387    string s;
388    //ofstream savefmt;
389    //savefmt.copyfmt(os);
390    //int wid=os.width(),prec=os.precision();
391 
392    // print message or blanks
393    os << LV.tag << " ";
394    if(LV.msg.size() > 0)
395       s = LV.msg;
396    else
397       s = rightJustify(string(""),LV.msg.size()); //LV.wid);
398    os << s << "   ";
399 
400    // print each label
401    for(i=0; i<LV.NL.size(); i++) {
402       if(int(LV.NL.getName(i).size()) > LV.wid)
403          s = leftJustify(LV.NL.getName(i),LV.wid);
404       else
405          s = rightJustify(LV.NL.getName(i),LV.wid);
406       os << s;
407       if(i-LV.NL.size()+1) os << " ";
408    }
409    os << endl;       // next line
410 
411    // print same space as with labels
412    s = rightJustify(string(""),LV.msg.size()+2); //LV.wid);
413    os << LV.tag << " " << s << " ";
414    if(LV.form == 1) os << fixed;
415    if(LV.form == 2) os << scientific;
416    for(i=0; i<LV.V.size(); i++) {
417       //os.copyfmt(savefmt);
418       //os << LV.V(i);
419       os << setw(LV.wid) << setprecision(LV.prec) << LV.V(i);
420       if(i-LV.V.size()+1) os << " ";
421    }
422 
423    return os;
424 }
425 catch(Exception& e) { GPSTK_RETHROW(e); }
426 }
427 
operator <<(ostream & os,const LabeledMatrix & LM)428 ostream& operator<<(ostream& os, const LabeledMatrix& LM)
429 {
430 try {
431    int nspace;
432    size_t i, j, jlast, n;
433    string s;
434    const Namelist *pNLcol = &LM.NLcols;
435    const Namelist *pNLrow = &LM.NLrows;
436 
437       // first make sure we have both namelists
438    if(LM.NLrows.size() == 0 && LM.NLcols.size() == 0) {
439       os << " Error -- Namelists in LabeledMatrix are empty! ";
440       return os;
441    }
442    if(LM.NLrows.size() == 0) pNLrow = pNLcol;
443    if(LM.NLcols.size() == 0) pNLcol = pNLrow;
444 
445       // on column labels line
446    os << setw(0);
447    if(LM.rc == 0) {    // only if printing both column and row labels
448       os << LM.tag << " ";                                       // tag
449       if(LM.msg.size() > 0)                                      // msg
450          s = LM.msg + "  ";
451       else
452          s = rightJustify(string(" "),LM.wid);
453       os << s << " ";
454       if(int(LM.msg.size()) > 0 && int(LM.msg.size()) < LM.wid)
455          os << rightJustify(string(" "),LM.wid-LM.msg.size());   // space
456    }
457       // print column labels
458    if(LM.rc != 1) { // but not if 'rows only'
459       n = (LM.M.cols() < pNLcol->size() ? LM.M.cols() : pNLcol->size());
460       if(LM.rc == 2) os << " ";
461       for(i=0; i<n; i++) {
462          if(int(pNLcol->getName(i).size()) > LM.wid)
463             s = leftJustify(pNLcol->getName(i),LM.wid);
464          else
465             s = rightJustify(pNLcol->getName(i),LM.wid);
466          os << s;                                                // label
467          if(i<n-1) os << " ";
468       }
469       os << endl;
470    }
471 
472    if(LM.form == 1) os << fixed;
473    if(LM.form == 2) os << scientific;
474    if(int(LM.msg.size()) > LM.wid) nspace = LM.msg.size()-LM.wid+2;
475    else if(int(LM.msg.size())) nspace = 2;
476    else nspace = 0;
477 
478       // print one row per line
479    for(i=0; i<LM.M.rows(); i++) {
480       os << LM.tag << " ";                                       // tag
481       if(nspace) os << rightJustify(string(" "),nspace);         // space
482          // print row labels
483       if(LM.rc != 2) { // but not if 'columns only'
484          if(int(pNLrow->getName(i).size()) > LM.wid)
485             s = leftJustify(pNLrow->getName(i),LM.wid);
486          else
487             s = rightJustify(pNLrow->getName(i),LM.wid);
488          os << s << " ";                                          // label
489       }
490          // finally, print the data
491       jlast = (LM.sym ? i+1 : LM.M.cols());
492       for(j=0; j<jlast; j++) {
493          if(LM.cln && ::fabs(LM.M(i,j)) < ::pow(10.0,-LM.prec))   // clean print
494             os << rightJustify("0",LM.wid);
495          else
496             os << setw(LM.wid) << setprecision(LM.prec) << LM.M(i,j);
497          if(j<jlast-1) os << " ";                                 // data
498       }
499       if(i<LM.M.rows()-1) os << endl;
500    }
501 
502    return os;
503 }
504 catch(Exception& e) { GPSTK_RETHROW(e); }
505 }
506 
507 } // end namespace gpstk
508 
509 //------------------------------------------------------------------------------------
510 //------------------------------------------------------------------------------------
511