1 // -*- C++ -*-
2 //
3 // This file is part of YODA -- Yet more Objects for Data Analysis
4 // Copyright (C) 2008-2021 The YODA collaboration (see AUTHORS for details)
5 //
6 #ifndef YODA_POINT2D_H
7 #define YODA_POINT2D_H
8 
9 #include "YODA/Point.h"
10 #include "YODA/Exceptions.h"
11 #include "YODA/Utils/MathUtils.h"
12 #include <utility>
13 
14 
15 namespace YODA {
16 
17 
18   /// A 2D data point to be contained in a Scatter2D
19   class Point2D : public Point {
20   public:
21 
22     /// @name Constructors
23     /// @{
24 
25     // Default constructor
Point2D()26     Point2D() {  }
27 
28 
29     /// Constructor from values with optional symmetric errors
30     Point2D(double x, double y, double ex=0.0, double ey=0.0, std::string source="")
_x(x)31       : _x(x), _y(y)
32     {
33       _ex = std::make_pair(ex, ex);
34       _ey[source] = std::make_pair(ey, ey);
35     }
36 
37 
38     /// Constructor from values with explicit asymmetric errors
39     Point2D(double x, double y,
40             double exminus,
41             double explus,
42             double eyminus,
43             double eyplus, std::string source="")
_x(x)44       : _x(x), _y(y)
45     {
46       _ex = std::make_pair(exminus, explus);
47       _ey[source] = std::make_pair(eyminus, eyplus);
48     }
49 
50 
51     // /// Constructor from values with symmetric errors on x and asymmetric errors on y
52     // Point2D(double x, double y, double ex, const std::pair<double,double>& ey)
53     //   : _x(x), _y(y), _ey(ey)
54     // {
55     //   _ex = std::make_pair(ex, ex);
56     // }
57 
58 
59     // /// Constructor from values with asymmetric errors on x and symmetric errors on y
60     // Point2D(double x, double y, const std::pair<double,double>& ex, double ey)
61     //   : _x(x), _y(y), _ex(ex)
62     // {
63     //   _ey = std::make_pair(ey, ey);
64     // }
65 
66 
67     /// Constructor from values with asymmetric errors on both x and y
68     Point2D(double x, double y, const std::pair<double,double>& ex, const std::pair<double,double>& ey, std::string source="")
_x(x)69       : _x(x), _y(y)
70     {
71       _ex = ex;
72       _ey[source] = ey;
73     }
74 
75 
76     /// Copy constructor
Point2D(const Point2D & p)77     Point2D(const Point2D& p)
78       : _x(p._x), _y(p._y)
79     {
80       _ex = p._ex;
81       _ey = p._ey;
82       this->setParent( p.getParent() );
83     }
84 
85 
86     /// Copy assignment
87     Point2D& operator = (const Point2D& p) {
88       _x = p._x;
89       _y = p._y;
90       _ex = p._ex;
91       _ey = p._ey;
92       this->setParent( p.getParent() );
93       return *this;
94     }
95 
96     /// @}
97 
98 
99   public:
100 
101     /// Space dimension of the point
dim()102     size_t dim() { return 2; }
103 
104 
105     /// @name Value accessors
106     /// @{
107 
108     /// Get x value
x()109     double x() const { return _x; }
110 
111     /// Set x value
setX(double x)112     void setX(double x) { _x = x; }
113 
114     /// Get y value
y()115     double y() const { return _y; }
116 
117     /// Set y value
setY(double y)118     void setY(double y) { _y = y; }
119 
120     /// @todo Uniform "coords" accessor across all Scatters: returning fixed-size tuple?
121 
122     /// Get x,y value pair
xy()123     std::pair<double,double> xy() const { return std::make_pair(_x, _y); }
124 
125     /// Set x and y values
setXY(double x,double y)126     void setXY(double x, double y) { setX(x); setY(y); }
127 
128     /// Set x and y values
setXY(const std::pair<double,double> & xy)129     void setXY(const std::pair<double,double>& xy) { setX(xy.first); setY(xy.second); }
130 
131     /// @}
132 
133 
134     /// @name x error accessors
135     /// @{
136 
137 
138     /// Get x-error values
xErrs()139     const std::pair<double,double>& xErrs() const {
140       return _ex;
141     }
142 
143     /// Get negative x-error value
xErrMinus()144     double xErrMinus() const {
145       return _ex.first;
146     }
147 
148     /// Get positive x-error value
xErrPlus()149     double xErrPlus() const {
150       return _ex.second;
151 
152     }
153 
154     /// Get average x-error value
xErrAvg()155     double xErrAvg() const {
156       return (_ex.first + _ex.second)/2.0;
157     }
158 
159     /// Set negative x error
setXErrMinus(double exminus)160     void setXErrMinus(double exminus) {
161       _ex.first = exminus;
162     }
163 
164     /// Set positive x error
setXErrPlus(double explus)165     void setXErrPlus(double explus) {
166       _ex.second = explus;
167     }
168 
169     /// Set symmetric x error
setXErr(double ex)170     void setXErr(double ex) {
171       setXErrMinus(ex);
172       setXErrPlus(ex);
173     }
174 
175     /// Set symmetric x error (alias)
setXErrs(double ex)176     void setXErrs(double ex) {
177       setXErr(ex);
178     }
179 
180     /// Set asymmetric x error
setXErrs(double exminus,double explus)181     void setXErrs(double exminus, double explus) {
182       setXErrMinus(exminus);
183       setXErrPlus(explus);
184     }
185 
186     /// Set asymmetric x error
setXErrs(const std::pair<double,double> & ex)187     void setXErrs(const std::pair<double,double>& ex) {
188       _ex = ex;
189     }
190 
191     /// Get value minus negative x-error
192     /// @todo Remove (or extend) when multiple errors are supported
193     /// No: doesn't need to change since (for now) we only store multiple
194     /// errors for the highest dimentsion
xMin()195     double xMin() const {
196       return _x - _ex.first;
197     }
198 
199     /// Get value plus positive x-error
200     /// @todo Remove (or extend) when multiple errors are supported
201     /// No: doesn't need to change since (for now) we only store multiple
202     /// errors for the highest dimentsion
xMax()203     double xMax() const {
204       return _x + _ex.second;
205     }
206 
207     /// @}
208 
209 
210     /// @name y error accessors
211     /// @{
212 
213     /// Get y-error values
214     const std::pair<double,double>& yErrs(std::string source="") const {
215       if (source!="") getVariationsFromParent();
216       if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source);
217       return _ey.at(source);
218     }
219 
220     /// Get negative y-error value
221     double yErrMinus(std::string source="") const {
222       if (source!="") getVariationsFromParent();
223       if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source);
224       return _ey.at(source).first;
225     }
226 
227     /// Get positive y-error value
228     double yErrPlus(std::string source="") const {
229       if (source!="") getVariationsFromParent();
230       if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source);
231       return _ey.at(source).second;
232     }
233 
234     /// Get average y-error value
235     double yErrAvg(std::string source="") const {
236       if (source!="") getVariationsFromParent();
237       if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source);
238       double res=(fabs(_ey.at(source).first) + fabs(_ey.at(source).second))/2.;
239       return res;
240     }
241 
242     /// Set negative y error
243     void setYErrMinus(double eyminus, std::string source="") {
244       if (!_ey.count(source)) {
245         _ey[source] = std::make_pair(0.,0.);
246       }
247       _ey.at(source).first = eyminus;
248     }
249 
250     /// Set positive y error
251     void setYErrPlus(double eyplus, std::string source="") {
252       if (!_ey.count(source)) _ey[source] = std::make_pair(0.,0.);
253       _ey.at(source).second = eyplus;
254     }
255 
256     /// Set symmetric y error
257     void setYErr(double ey, std::string source="") {
258       setYErrMinus(ey, source );
259       setYErrPlus(ey, source );
260     }
261 
262     /// Set symmetric y error (alias)
263     void setYErrs(double ey, std::string source="") {
264       setYErr(ey, source);
265     }
266 
267     /// Set asymmetric y error
268     void setYErrs(double eyminus, double eyplus, std::string source="") {
269       setYErrMinus(eyminus, source);
270       setYErrPlus(eyplus, source );
271     }
272 
273     /// Set asymmetric y error
274     void setYErrs(const std::pair<double,double>& ey, std::string source="") {
275       _ey[source] = ey;
276     }
277 
278     /// Get value minus negative y-error
279     double yMin(std::string source="") const {
280       if (source!="") getVariationsFromParent();
281       if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source);
282       return _y - _ey.at(source).first;
283     }
284 
285     /// Get value plus positive y-error
286     double yMax(std::string source="") const {
287       if (source!="") getVariationsFromParent();
288       if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source);
289       return _y + _ey.at(source).second;
290     }
291 
292     /// @}
293 
294 
295 
296 
297     /// @name Combined x/y value and error setters
298     /// @{
299 
300     /// Set x value and symmetric error
setX(double x,double ex)301     void setX(double x, double ex) {
302       setX(x);
303       setXErrs(ex);
304     }
305 
306     /// Set x value and asymmetric error
setX(double x,double exminus,double explus)307     void setX(double x, double exminus, double explus) {
308       setX(x);
309       setXErrs(exminus, explus);
310     }
311 
312     /// Set x value and asymmetric error
setX(double x,std::pair<double,double> & ex)313     void setX(double x, std::pair<double,double>& ex) {
314       setX(x);
315       setXErrs(ex);
316     }
317 
318 
319     /// Set y value and symmetric error
320     void setY(double y, double ey, std::string source="") {
321       setY(y);
322       setYErrs(ey, source);
323     }
324 
325     /// Set y value and asymmetric error
326     void setY(double y, double eyminus, double eyplus, std::string source="") {
327       setY(y);
328       setYErrs(eyminus, eyplus, source);
329     }
330 
331     /// Set y value and asymmetric error
332     void setY(double y, std::pair<double,double>& ey, std::string source="") {
333       setY(y);
334       setYErrs(ey, source);
335     }
336 
337     /// @}
338 
339 
340     // @name Manipulations
341     /// @{
342 
343     /// Scaling of x axis
scaleX(double scalex)344     void scaleX(double scalex) {
345       setX(x()*scalex);
346       setXErrs(xErrMinus()*scalex, xErrPlus()*scalex);
347     }
348 
349     /// Scaling of y axis
scaleY(double scaley)350     void scaleY(double scaley) {
351       setY(y()*scaley);
352       for (const auto   &source : _ey){
353         setYErrs(yErrMinus()*scaley, yErrPlus()*scaley, source.first);
354       }
355     }
356 
357     /// Scaling of both axes
scaleXY(double scalex,double scaley)358     void scaleXY(double scalex, double scaley) {
359       scaleX(scalex);
360       scaleY(scaley);
361     }
362 
363     /// Scaling along direction @a i
scale(size_t i,double scale)364     void scale(size_t i, double scale) {
365       switch (i) {
366       case 1: scaleX(scale); break;
367       case 2: scaleY(scale); break;
368       default: throw RangeError("Invalid axis int, must be in range 1..dim");
369       }
370     }
371 
372     /// @}
373 
374 
375     /// @name Integer axis accessor equivalents
376     /// @{
377 
378     /// Get the point value for direction @a i
val(size_t i)379     double val(size_t i) const {
380       switch (i) {
381       case 1: return x();
382       case 2: return y();
383       default: throw RangeError("Invalid axis int, must be in range 1..dim");
384       }
385     }
386     /// Set the point value for direction @a i
setVal(size_t i,double val)387     void setVal(size_t i, double val) {
388       switch (i) {
389       case 1: setX(val); break;
390       case 2: setY(val); break;
391       default: throw RangeError("Invalid axis int, must be in range 1..dim");
392       }
393     }
394 
395     /// Get error map for direction @a i
396     const std::map< std::string, std::pair<double,double>> & errMap() const;
397 
398     // Parse the variations from the parent AO if it exists
399     void getVariationsFromParent() const;
400 
401     // set the "" error source to the sum in quad of the existing variations
updateTotalUncertainty()402     void updateTotalUncertainty() {
403         float totalUp = 0.;
404         float totalDn = 0.;
405         for (const auto& variation : getParent()->variations()) {
406           if (variation=="") continue;
407           float thisUp = yErrPlus(variation);
408           float thisDn = yErrMinus(variation);
409           totalUp += thisUp*thisUp;
410           totalDn += thisDn*thisDn;
411         }
412 
413         totalUp = sqrt(totalUp);
414         totalDn = sqrt(totalDn);
415         setErrPlus(2, totalUp);
416         setErrMinus(2, totalDn);
417     }
418 
419     /// Get error values for direction @a i
420     const std::pair<double,double>& errs(size_t i, std::string source="") const {
421       switch (i) {
422       case 1: return xErrs();
423       case 2: return yErrs(source);
424       default: throw RangeError("Invalid axis int, must be in range 1..dim");
425       }
426     }
427     /// Get negative error value for direction @a i
428     double errMinus(size_t i, std::string source="") const {
429       switch (i) {
430       case 1: return xErrMinus();
431       case 2: return yErrMinus(source);
432       default: throw RangeError("Invalid axis int, must be in range 1..dim");
433       }
434     }
435     /// Get positive error value for direction @a i
436     double errPlus(size_t i, std::string source="") const {
437       switch (i) {
438       case 1: return xErrPlus();
439       case 2: return yErrPlus(source);
440       default: throw RangeError("Invalid axis int, must be in range 1..dim");
441       }
442     }
443     /// Get average error value for direction @a i
444     double errAvg(size_t i, std::string source="") const {
445       switch (i) {
446       case 1: return xErrAvg();
447       case 2: return yErrAvg(source);
448       default: throw RangeError("Invalid axis int, must be in range 1..dim");
449       }
450     }
451 
452     /// Set negative error for direction @a i
453     void setErrMinus(size_t i, double eminus, std::string source="") {
454       switch (i) {
455       case 1: setXErrMinus(eminus); break;
456       case 2: setYErrMinus(eminus, source); break;
457       default: throw RangeError("Invalid axis int, must be in range 1..dim");
458       }
459     }
460     /// Set positive error for direction @a i
461     void setErrPlus(size_t i, double eplus, std::string source="") {
462       switch (i) {
463       case 1: setXErrPlus(eplus); break;
464       case 2: setYErrPlus(eplus, source); break;
465       default: throw RangeError("Invalid axis int, must be in range 1..dim");
466       }
467     }
468 
469     /// Set symmetric error for direction @a i
470     void setErr(size_t i, double e, std::string source="") {
471       switch (i) {
472       case 1: setXErrs(e); break;
473       case 2: setYErrs(e, source); break;
474       default: throw RangeError("Invalid axis int, must be in range 1..dim");
475       }
476     }
477     /// Set asymmetric error for direction @a i
478     void setErrs(size_t i, double eminus, double eplus, std::string source="") {
479       switch (i) {
480       case 1: setXErrs(eminus, eplus); break;
481       case 2: setYErrs(eminus, eplus, source); break;
482       default: throw RangeError("Invalid axis int, must be in range 1..dim");
483       }
484     }
485     /// Set asymmetric error for direction @a i
486     void setErrs(size_t i, std::pair<double,double>& e, std::string source="") {
487       switch (i) {
488       case 1: setXErrs(e); break;
489       case 2: setYErrs(e, source); break;
490       default: throw RangeError("Invalid axis int, must be in range 1..dim");
491       }
492     }
493 
494     /// Set value and symmetric error for direction @a i
495     void set(size_t i, double val, double e,  std::string source="") {
496       switch (i) {
497       case 1: setX(val, e); break;
498       case 2: setY(val, e, source); break;
499       default: throw RangeError("Invalid axis int, must be in range 1..dim");
500       }
501     }
502     /// Set value and asymmetric error for direction @a i
503     void set(size_t i, double val, double eminus, double eplus,  std::string source="") {
504       switch (i) {
505       case 1: setX(val, eminus, eplus); break;
506       case 2: setY(val, eminus, eplus, source); break;
507       default: throw RangeError("Invalid axis int, must be in range 1..dim");
508       }
509     }
510     /// Set value and asymmetric error for direction @a i
511     void set(size_t i, double val, std::pair<double,double>& e,  std::string source="") {
512       switch (i) {
513       case 1: setX(val, e); break;
514       case 2: setY(val, e, source); break;
515       default: throw RangeError("Invalid axis int, must be in range 1..dim");
516       }
517     }
518 
519 
520     /// @}
521 
522 
523   protected:
524 
525     /// @name Value and error variables
526     /// @{
527 
528 
529     double _x;
530     double _y;
531     std::pair<double,double> _ex;
532     // a map of the errors for each source. Nominal stored under ""
533     // to ensure backward compatibility
534     std::map< std::string, std::pair<double,double> > _ey;
535 
536     /// @}
537 
538   };
539 
540 
541 
542   /// @name Comparison operators
543   /// @{
544 
545   /// Equality test of x & y characteristics only
546   /// @todo Base on a named fuzzyEquals(a,b,tol=1e-3) unbound function
547   inline bool operator==(const YODA::Point2D& a, const YODA::Point2D& b) {
548     if (!YODA::fuzzyEquals(a.x(), b.x()) ||
549         !YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus()) ||
550         !YODA::fuzzyEquals(a.xErrPlus(),  b.xErrPlus()) ) return false;
551     if (!YODA::fuzzyEquals(a.y(), b.y()) ||
552         !YODA::fuzzyEquals(a.yErrMinus(), b.yErrMinus()) ||
553         !YODA::fuzzyEquals(a.yErrPlus(),  b.yErrPlus()) ) return false;
554     return true;
555   }
556 
557   /// Equality test of x characteristics only
558   inline bool operator != (const YODA::Point2D& a, const YODA::Point2D& b) {
559     return !(a == b);
560   }
561 
562   /// Less-than operator used to sort bins by x-ordering
563   inline bool operator < (const YODA::Point2D& a, const YODA::Point2D& b) {
564     if (!YODA::fuzzyEquals(a.x(), b.x())) {
565       return a.x() < b.x();
566     }
567     if (!YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus())) {
568       return a.xErrMinus() < b.xErrMinus();
569     }
570     if (!YODA::fuzzyEquals(a.xErrPlus(), b.xErrPlus())) {
571       return a.xErrPlus() < b.xErrPlus();
572     }
573     return false;
574   }
575 
576   /// Less-than-or-equals operator used to sort bins by x-ordering
577   inline bool operator <= (const YODA::Point2D& a, const YODA::Point2D& b) {
578     if (a == b) return true;
579     return a < b;
580   }
581 
582   /// Greater-than operator used to sort bins by x-ordering
583   inline bool operator > (const YODA::Point2D& a, const YODA::Point2D& b) {
584     return !(a <= b);
585   }
586 
587   /// Greater-than-or-equals operator used to sort bins by x-ordering
588   inline bool operator >= (const YODA::Point2D& a, const YODA::Point2D& b) {
589     return !(a < b);
590   }
591 
592   /// @}
593 
594 
595 }
596 
597 #endif
598