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