File indexing completed on 2025-11-01 08:59:35
0001
0002
0003
0004
0005
0006 #ifndef YODA_Scatter_h
0007 #define YODA_Scatter_h
0008
0009 #include "YODA/AnalysisObject.h"
0010 #include "YODA/Point.h"
0011 #include "YODA/Transformation.h"
0012 #include "YODA/Utils/Traits.h"
0013 #include "YODA/Utils/ndarray.h"
0014 #include <vector>
0015 #include <set>
0016 #include <string>
0017 #include <utility>
0018 #include <memory>
0019
0020 namespace YODA {
0021
0022
0023 namespace {
0024
0025
0026
0027
0028 template<class T, class U, typename = void>
0029 struct HalfValsHalfPairs : std::false_type { };
0030
0031 template<class T, size_t... Is>
0032 struct HalfValsHalfPairs<T, std::index_sequence<Is...>,
0033 std::enable_if_t<( std::is_same<double,
0034 std::common_type_t<std::tuple_element_t<Is,T>...,
0035 double>>::value &&
0036 std::is_same<std::pair<double,double>,
0037 std::common_type_t<std::tuple_element_t<sizeof...(Is)+Is,T>...,
0038 std::pair<double,double>>>::value
0039 )>> : std::true_type { };
0040 }
0041
0042
0043 class Scatter {
0044 public:
0045
0046
0047
0048
0049 virtual ~Scatter() {}
0050
0051
0052
0053
0054
0055 virtual size_t dim() const noexcept = 0;
0056
0057
0058
0059
0060
0061
0062 virtual void reset() = 0;
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074 virtual size_t numPoints() const = 0;
0075
0076
0077
0078
0079
0080
0081 virtual void rmPoint(size_t index) = 0;
0082
0083
0084 virtual void rmPoints(std::vector<size_t> indices) {
0085
0086 std::sort(indices.begin(), indices.end(), std::greater<size_t>());
0087 for (size_t i : indices) rmPoint(i);
0088 }
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146 };
0147
0148
0149
0150
0151
0152 template <size_t N>
0153 class ScatterND : public AnalysisObject, public Scatter {
0154 protected:
0155
0156
0157 using Pair = std::pair<double,double>;
0158 using ValVec = std::vector<double>;
0159 using PairVec = std::vector<Pair>;
0160 using ValList = std::initializer_list<double>;
0161 using PairList = std::initializer_list<Pair>;
0162
0163
0164 template<typename Arr>
0165 using containedType = std::decay_t<decltype(*std::declval<Arr>().begin())>;
0166
0167
0168 template<typename Arr>
0169 using containsPair = typename std::is_same<containedType<Arr>, Pair>;
0170
0171
0172
0173 template<typename T, typename U>
0174 using enableIfNestedArrayWithPair = std::enable_if_t<(isIterable<T,U,containedType<T>,containedType<U>> &&
0175 containsPair<containedType<U>>::value)>;
0176
0177
0178 template<typename... Args>
0179 using isAllVals = std::is_same<double, std::common_type_t<Args..., double>>;
0180
0181
0182
0183
0184 template<typename... Args>
0185 using isHalfValsHalfPairs = HalfValsHalfPairs<std::tuple<Args...>, std::make_index_sequence<N>>;
0186
0187 public:
0188
0189
0190 using NdVal = Utils::ndarray<double, N>;
0191 using NdValPair = Utils::ndarray<std::pair<double,double>, N>;
0192 using Point = PointND<N>;
0193 using Points = std::vector<Point>;
0194 using Ptr = std::shared_ptr<ScatterND>;
0195 using AnalysisObject::operator =;
0196
0197
0198
0199
0200
0201
0202 ScatterND(const std::string& path = "", const std::string& title="")
0203 : AnalysisObject("Scatter"+std::to_string(N)+"D", path, title) { }
0204
0205
0206 ScatterND(const Points& points,
0207 const std::string& path="", const std::string& title="")
0208 : AnalysisObject("Scatter"+std::to_string(N)+"D", path, title),
0209 _points(points) { }
0210
0211
0212 ScatterND(Points&& points,
0213 const std::string& path="", const std::string& title="")
0214 : AnalysisObject("Scatter"+std::to_string(N)+"D", path, title),
0215 _points(std::move(points)) { }
0216
0217
0218 template <typename ValRange = std::initializer_list<ValList>,
0219 typename = std::enable_if_t<isIterable<ValRange, containedType<ValRange>>>>
0220 ScatterND(ValRange&& positions, const std::string& path="", const std::string& title="")
0221 : AnalysisObject("Scatter"+std::to_string(N)+"D", path, title) {
0222 for (size_t i = 0; i < positions.size(); ++i) {
0223 addPoint(PointND<N>(std::forward<containedType<ValRange>>(positions[i])));
0224 }
0225 }
0226
0227
0228 template <typename ValRange = std::initializer_list<ValList>,
0229 typename = std::enable_if_t<isIterable<ValRange, containedType<ValRange>>>>
0230 ScatterND(ValRange&& positions, ValRange&& errors, const std::string& path="", const std::string& title="")
0231 : AnalysisObject("Scatter"+std::to_string(N)+"D", path, title) {
0232 if (positions.size() != errors.size()) throw RangeError("Number of errors doesn't match number of positions");
0233 for (size_t i = 0; i < positions.size(); ++i) {
0234 addPoint(PointND<N>(std::forward<containedType<ValRange>>(positions[i]),
0235 std::forward<containedType<ValRange>>(errors[i])));
0236 }
0237 }
0238
0239
0240 template <typename ValRange = std::initializer_list<ValList>,
0241 typename PairRange = std::initializer_list<PairList>,
0242 typename = enableIfNestedArrayWithPair<ValRange,PairRange>>
0243 ScatterND(ValRange&& positions, PairRange&& errors, const std::string& path="", const std::string& title="")
0244 : AnalysisObject("Scatter"+std::to_string(N)+"D", path, title) {
0245 if (positions.size() != errors.size()) throw RangeError("Number of error pairs doesn't match number of positions");
0246 for (size_t i = 0; i < positions.size(); ++i) {
0247 addPoint(PointND<N>(std::forward<containedType<ValRange>>(positions[i]),
0248 std::forward<containedType<PairRange>>(errors[i])));
0249 }
0250 }
0251
0252
0253 ScatterND(const ScatterND<N>& s, const std::string& path = "")
0254 : AnalysisObject("Scatter"+std::to_string(N)+"D", (path != "")? path : s.path(), s, s.title()),
0255 _points(s._points) { }
0256
0257
0258 ScatterND(ScatterND<N>&& s, const std::string& path = "")
0259 : AnalysisObject("Scatter"+std::to_string(N)+"D", (path != "")? path : s.path(), s, s.title()),
0260 _points(std::move(s._points)) {
0261 }
0262
0263
0264
0265 ScatterND<N>& operator = (const ScatterND<N>& s) {
0266 if (this != &s) {
0267 AnalysisObject::operator = (s);
0268 _points = s._points;
0269 }
0270 return *this;
0271 }
0272
0273
0274 ScatterND<N>& operator = (ScatterND<N>&& s) {
0275 if (this != &s) {
0276 AnalysisObject::operator = (s);
0277 _points = std::move(s._points);
0278 }
0279 return *this;
0280 }
0281
0282
0283 ScatterND<N> clone() const {
0284 return ScatterND<N>(*this);
0285 }
0286
0287
0288 ScatterND<N>* newclone() const {
0289 return new ScatterND<N>(*this);
0290 }
0291
0292
0293
0294
0295 size_t dim() const noexcept { return N; }
0296
0297
0298
0299
0300
0301 void reset() {
0302 _points.clear();
0303 }
0304
0305
0306 void scale(const NdVal& scales) {
0307 for (PointND<N>& p : _points) p.scale(scales);
0308 }
0309
0310 void scale(const std::vector<double>& scales) {
0311 if (scales.size() != N) throw RangeError("Expected " + std::to_string(N) + " scale factors");
0312 for (PointND<N>& p : _points) p.scale(scales);
0313 }
0314
0315
0316 void scale(const size_t i, double factor) {
0317 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0318 for (PointND<N>& p : _points) p.scale(i, factor);
0319 }
0320
0321
0322 void scaleVal(const size_t i, double factor) {
0323 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0324 for (PointND<N>& p : _points) p.scaleVal(i, factor);
0325 }
0326
0327
0328 void scaleErr(const size_t i, double factor) {
0329 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0330 for (PointND<N>& p : _points) p.scaleErr(i, factor);
0331 }
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343 size_t numPoints() const {
0344 return _points.size();
0345 }
0346
0347
0348
0349 Points& points() {
0350 return _points;
0351 }
0352
0353
0354
0355 const Points& points() const {
0356 return _points;
0357 }
0358
0359
0360
0361 PointND<N>& point(size_t index) {
0362 return _points.at(index);
0363 }
0364
0365
0366
0367 const PointND<N>& point(size_t index) const {
0368 return _points.at(index);
0369 }
0370
0371
0372
0373
0374
0375
0376
0377
0378 ScatterND<N>& addPoint(const PointND<N>& pt) {
0379 _points.push_back(pt);
0380 return *this;
0381 }
0382
0383
0384 ScatterND<N>& addPoint(PointND<N>&& pt) {
0385 _points.push_back(std::move(pt));
0386 return *this;
0387 }
0388
0389
0390 template <typename ValRange = ValList,
0391 typename = std::enable_if_t<isIterable<ValRange>>>
0392 ScatterND<N>& addPoint(ValRange&& pos) {
0393 _points.push_back(PointND<N>(std::forward<ValRange>(pos)));
0394 return *this;
0395 }
0396
0397
0398 template <typename ValRange = ValList,
0399 typename = std::enable_if_t<isIterable<ValRange>>>
0400 ScatterND<N>& addPoint(ValRange&& pos, ValRange&& err) {
0401 _points.push_back(PointND<N>(std::forward<ValRange>(pos), std::forward<ValRange>(err)));
0402 return *this;
0403 }
0404
0405
0406 template <typename ValRange = ValList,
0407 typename = std::enable_if_t<isIterable<ValRange>>>
0408 ScatterND<N>& addPoint(ValRange&& pos, ValRange&& errdn, ValRange&& errup) {
0409 _points.push_back(PointND<N>(std::forward<ValRange>(pos),
0410 std::forward<ValRange>(errdn),
0411 std::forward<ValRange>(errup)));
0412 return *this;
0413 }
0414
0415
0416 template <typename ValRange = ValList, typename PairRange = PairList>
0417 auto addPoint(ValRange&& pos, PairRange&& err)
0418 -> std::enable_if_t<(isIterable<ValRange,PairRange> && containsPair<PairRange>::value), ScatterND<N>>& {
0419 _points.push_back(PointND<N>(pos, err));
0420 return *this;
0421 }
0422
0423
0424
0425
0426
0427
0428
0429
0430
0431
0432
0433
0434
0435
0436
0437
0438
0439
0440
0441
0442
0443
0444 template<typename... Args>
0445 auto addPoint(Args&&... args)
0446 -> std::enable_if_t<(sizeof...(Args) == 2*N || sizeof...(Args) == 3*N), ScatterND<N>>& {
0447 return addPoint_aux(std::make_tuple(std::forward<Args>(args)...), std::make_index_sequence<N>{});
0448 }
0449
0450 protected:
0451
0452
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462
0463
0464
0465
0466
0467 template<typename... Args, size_t... Is>
0468 auto addPoint_aux(std::tuple<Args...>&& t, std::index_sequence<Is...>)
0469 -> std::enable_if_t<(isAllVals<Args...>::value), ScatterND<N>>& {
0470 if constexpr(sizeof...(Args) == 2*N) {
0471 _points.push_back(
0472 PointND<N>( ValVec{ static_cast<double>(std::get<Is>(t))...},
0473 PairVec{{static_cast<double>(std::get<N+Is>(t)),
0474 static_cast<double>(std::get<N+Is>(t))}...} ));
0475 }
0476 else {
0477 _points.push_back(PointND<N>( ValVec{ static_cast<double>(std::get<Is>(t))...},
0478 PairVec{{static_cast<double>(std::get<N+2*Is>(t)),
0479 static_cast<double>(std::get<N+2*Is+1>(t))}...} ));
0480 }
0481 return *this;
0482 }
0483
0484
0485
0486
0487
0488
0489
0490
0491
0492
0493
0494
0495
0496
0497
0498 template<typename... Args, size_t... Is>
0499 auto addPoint_aux(std::tuple<Args...>&& t, std::index_sequence<Is...>)
0500 -> std::enable_if_t<(sizeof...(Args) == 2*N && isHalfValsHalfPairs<Args...>::value), ScatterND<N>>& {
0501 _points.push_back(PointND<N>( ValVec{ static_cast<double>(std::get<Is>(t))...},
0502 PairVec{ static_cast<Pair>(std::get<N+Is>(t))...} ));
0503 return *this;
0504 }
0505
0506
0507 public:
0508
0509
0510 ScatterND<N>& addPoints(Points pts) {
0511 for (const PointND<N>& pt : pts) addPoint(pt);
0512 return *this;
0513 }
0514
0515 void rmPoint(size_t index) {
0516 _points.erase(_points.begin()+index);
0517 }
0518
0519
0520
0521
0522
0523
0524 size_t lengthContent(bool fixed_length = false) const noexcept {
0525 if (fixed_length) return 0;
0526 return numPoints() * Point::DataSize::value;
0527 }
0528
0529 std::vector<double> serializeContent(bool fixed_length = false) const noexcept {
0530
0531 if (fixed_length) return { };
0532
0533 std::vector<double> rtn;
0534 rtn.reserve(lengthContent());
0535 for (size_t i = 0; i < numPoints(); ++i) {
0536 std::vector<double> pdata = point(i)._serializeContent();
0537 rtn.insert(std::end(rtn),
0538 std::make_move_iterator(std::begin(pdata)),
0539 std::make_move_iterator(std::end(pdata)));
0540 }
0541 return rtn;
0542 }
0543
0544 void deserializeContent(const std::vector<double>& data) {
0545
0546 if (data.size() % Point::DataSize::value)
0547 throw UserError("Length of serialized data should be a multiple of "+std::to_string(Point::DataSize::value)+"!");
0548
0549 const size_t nPoints = data.size()/Point::DataSize::value;
0550 const auto itr = data.cbegin();
0551 reset();
0552 for (size_t i = 0; i < nPoints; ++i) {
0553 addPoint(Point());
0554 auto first = itr + i*Point::DataSize::value;
0555 auto last = first + Point::DataSize::value;
0556 point(i)._deserializeContent(std::vector<double>{first, last});
0557 }
0558
0559 }
0560
0561
0562
0563
0564
0565
0566
0567 ScatterND<N>& combineWith(const ScatterND<N>& other) {
0568 addPoints(other.points());
0569 return *this;
0570 }
0571
0572
0573 ScatterND<N>& combineWith(ScatterND<N>&& other) {
0574 addPoints(std::move(other._points));
0575 return *this;
0576 }
0577
0578
0579 ScatterND<N>& combineWith(const std::vector< ScatterND<N> >& others) {
0580 for (const ScatterND<N>& s : others) combineWith(s);
0581 return *this;
0582 }
0583
0584
0585 ScatterND<N>& combineWith(std::vector< ScatterND<N> >&& others) {
0586 for (ScatterND<N>&& s : others) combineWith(std::move(s));
0587 return *this;
0588 }
0589
0590
0591
0592
0593
0594
0595
0596 ValVec vals(const size_t i) const {
0597 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0598 ValVec rtn; rtn.reserve(_points.size());
0599 for (const auto& pt : _points) {
0600 rtn.push_back( pt.val(i) );
0601 }
0602 return rtn;
0603 }
0604
0605
0606 ValVec mins(const size_t i) const {
0607 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0608 ValVec rtn; rtn.reserve(_points.size());
0609 for (const auto& pt : _points) {
0610 rtn.push_back( pt.min(i) );
0611 }
0612 return rtn;
0613 }
0614
0615
0616 ValVec maxs(const size_t i) const {
0617 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0618 ValVec rtn; rtn.reserve(_points.size());
0619 for (const auto& pt : _points) {
0620 rtn.push_back( pt.max(i) );
0621 }
0622 return rtn;
0623 }
0624
0625
0626 double min(const size_t i) const {
0627 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0628 const ValVec cvals = vals(i);
0629 return *std::min_element(cvals.begin(), cvals.end());
0630 }
0631
0632
0633 double max(const size_t i) const {
0634 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0635 const ValVec cvals = vals(i);
0636 return *std::max_element(cvals.begin(), cvals.end());
0637 }
0638
0639
0640 PairVec errs(const size_t i) const {
0641 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0642 PairVec rtn; rtn.reserve(_points.size());
0643 for (const auto& pt : _points) {
0644 rtn.push_back( pt.errs(i) );
0645 }
0646 return rtn;
0647 }
0648
0649
0650 ValVec errAvgs(const size_t i) const {
0651 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0652 ValVec rtn; rtn.reserve(_points.size());
0653 for (const auto& pt : _points) {
0654 rtn.push_back( pt.errAvg(i) );
0655 }
0656 return rtn;
0657 }
0658
0659
0660 ValVec xVals() const { return vals(0); }
0661
0662
0663 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 2)>>
0664 ValVec yVals() const { return vals(1); }
0665
0666
0667 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 3)>>
0668 ValVec zVals() const { return vals(2); }
0669
0670
0671 ValVec xMins() const { return mins(0); }
0672
0673
0674 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 2)>>
0675 ValVec yMins() const { return mins(1); }
0676
0677
0678 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 3)>>
0679 ValVec zMins() const { return mins(2); }
0680
0681
0682 ValVec xMaxs() const { return maxs(0); }
0683
0684
0685 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 2)>>
0686 ValVec yMaxs() const { return maxs(1); }
0687
0688
0689 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 3)>>
0690 ValVec zMaxs() const { return maxs(2); }
0691
0692
0693 double xMin() const { return min(0); }
0694
0695
0696 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 2)>>
0697 double yMin() const { return min(1); }
0698
0699
0700 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 3)>>
0701 double zMin() const { return min(2); }
0702
0703
0704 double xMax() const { return max(0); }
0705
0706
0707 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 2)>>
0708 double yMax() const { return max(1); }
0709
0710
0711 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 3)>>
0712 double zMax() const { return max(2); }
0713
0714
0715 PairVec xErrs() const { return errs(0); }
0716
0717
0718 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 2)>>
0719 PairVec yErrs() const { return errs(1); }
0720
0721
0722 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 3)>>
0723 PairVec zErrs() const { return errs(2); }
0724
0725
0726 ValVec xErrAvgs() const { return errAvgs(0); }
0727
0728
0729 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 2)>>
0730 ValVec yErrAvgs() const { return errAvgs(1); }
0731
0732
0733 template<size_t axisN = N, typename = std::enable_if_t<(axisN >= 3)>>
0734 ValVec zErrAvgs() const { return errAvgs(2); }
0735
0736
0737
0738
0739
0740
0741
0742 void _renderYODA(std::ostream& os, const int width = 13) const noexcept {
0743
0744 os << "# ";
0745 for (size_t i = 0; i < N; ++i) {
0746 os << std::setw(width - int(i? 0 : 2)) << std::left << ("val" + std::to_string(i+1)) << "\t"
0747 << std::setw(width) << std::left << ("err" + std::to_string(i+1) + "-") << "\t"
0748 << std::setw(width) << std::left << ("err" + std::to_string(i+1) + "+") << "\t";
0749 }
0750 os << "\n";
0751
0752 for (const auto& pt : _points) {
0753 pt._renderYODA(os, width);
0754 }
0755 }
0756
0757
0758 void _renderFLAT(std::ostream& os, const int width = 13) const noexcept { _renderYODA(os, width); }
0759
0760
0761
0762
0763
0764
0765 std::vector<Pair> edges(const size_t i) const {
0766 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0767 std::vector<Pair> rtn;
0768 rtn.resize(numPoints());
0769 size_t j = 0;
0770 for (const Point& p : points()) {
0771 rtn[j++] = std::make_pair(p.min(i), p.max(i));
0772 }
0773 std::sort(rtn.begin(), rtn.end());
0774 rtn.erase(std::unique(rtn.begin(), rtn.end()), rtn.end());
0775 return rtn;
0776 }
0777
0778
0779
0780 private:
0781
0782 Points _points;
0783
0784 };
0785
0786
0787
0788
0789
0790 template <int N>
0791 inline ScatterND<N> combine(ScatterND<N> a, const ScatterND<N>& b) {
0792 a.combineWith(b);
0793 return a;
0794 }
0795
0796 template <int N>
0797 inline ScatterND<N> combine(ScatterND<N> a, ScatterND<N>&& b) {
0798 a.combineWith(std::move(b));
0799 return a;
0800 }
0801
0802 template <int N>
0803 inline ScatterND<N> combine(const std::vector< ScatterND<N> >& scatters) {
0804 ScatterND<N> rtn;
0805 rtn.combineWith(scatters);
0806 return rtn;
0807 }
0808
0809 template <int N>
0810 inline ScatterND<N> combine(std::vector< ScatterND<N> >&& scatters) {
0811 ScatterND<N> rtn;
0812 rtn.combineWith(std::move(scatters));
0813 return rtn;
0814 }
0815
0816
0817
0818
0819
0820
0821
0822
0823
0824
0825
0826
0827
0828
0829
0830
0831
0832
0833
0834
0835
0836
0837
0838
0839
0840
0841
0842
0843
0844
0845
0846
0847
0848
0849
0850
0851
0852
0853
0854
0855
0856
0857
0858
0859
0860
0861
0862
0863
0864
0865
0866
0867
0868
0869 template<size_t N>
0870 inline void transform(ScatterND<N>& s, const Trf<N>& fn, const size_t i) {
0871 for (auto& p : s.points()) {
0872 p.transform(i, fn);
0873 }
0874 }
0875
0876 template<size_t N, typename FN>
0877 inline void transform(ScatterND<N>& s, const FN& fn, const size_t i) {
0878 transform(s, Trf<N>(fn), i);
0879 }
0880
0881 template<size_t N, typename FN>
0882 inline void transformX(ScatterND<N>& s, const FN& fn) {
0883 transform(s, fn, 0);
0884 }
0885
0886 template<size_t N, typename FN>
0887 inline void transformY(ScatterND<N>& s, const FN& fn) {
0888 transform(s, fn, 1);
0889 }
0890
0891 template<size_t N, typename FN>
0892 inline void transformZ(ScatterND<N>& s, const FN& fn) {
0893 transform(s, fn, 2);
0894 }
0895
0896
0897
0898
0899
0900
0901 using Scatter1D = ScatterND<1>;
0902 using Scatter2D = ScatterND<2>;
0903 using Scatter3D = ScatterND<3>;
0904 using Scatter4D = ScatterND<4>;
0905 using S1D = Scatter1D;
0906 using S2D = Scatter2D;
0907 using S3D = Scatter3D;
0908 using S4D = Scatter4D;
0909
0910
0911
0912
0913 }
0914
0915 #endif