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