1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT license.
3 
4 #pragma once
5 
6 #include "seal/util/common.h"
7 #include "seal/util/defines.h"
8 #include "seal/util/pointer.h"
9 #include <algorithm>
10 #include <cstddef>
11 #include <cstdint>
12 #include <iostream>
13 #include <iterator>
14 #include <stdexcept>
15 #include <tuple>
16 #include <type_traits>
17 #include <utility>
18 #include <vector>
19 
20 namespace seal
21 {
22     class Ciphertext;
23 
24     namespace util
25     {
26         class NTTTables;
27 
28         /**
29         @par PolyIter, RNSIter, and CoeffIter
30         In this file we define a set of custom iterator classes ("SEAL iterators") that are used throughout Microsoft
31         SEAL for easier iteration over ciphertext polynomials, their RNS components, and the coefficients in the RNS
32         components. All SEAL iterators satisfy the C++ LegacyRandomAccessIterator requirements. SEAL iterators are ideal
33         to use with the SEAL_ITERATE macro, which expands to std::for_each_n in C++17 and to seal::util::seal_for_each_n
34         in C++14. All SEAL iterators derive from SEALIterBase.
35 
36         The most important SEAL iterator classes behave as illustrated by the following diagram:
37 
38         +-------------------+
39         |    Pointer & Size |  Construct  +-----------------+
40         | or Ciphertext     |------------>| (Const)PolyIter |  Iterates over RNS polynomials in a ciphertext
41         +-------------------+             +--------+--------+  (coeff_modulus_size-many RNS components)
42                                                    |
43                                                    |
44                                                    | Dereference
45                                                    |
46                                                    |
47                                                    v
48            +----------------+  Construct   +----------------+
49            | Pointer & Size |------------->| (Const)RNSIter |  Iterates over RNS components in an RNS polynomial
50            +----------------+              +-------+--------+  (poly_modulus_degree-many coefficients)
51                                                    |
52                                                    |
53                                                    | Dereference
54                                                    |
55                                                    |
56                                                    v
57           +----------------+  Construct  +------------------+
58           | Pointer & Size |------------>| (Const)CoeffIter |  Iterates over coefficients (std::uint64_t) in a single
59           +----------------+             +---------+--------+  RNS polynomial component
60                                                    |
61                                                    |
62                                                    | Dereference
63                                                    |
64                                                    |
65                                                    v
66                                       +-------------------------+
67                                       | (const) std::uint64_t & |
68                                       +-------------------------+
69 
70         @par PtrIter and StrideIter
71         PtrIter<T *> and StrideIter<T *> are both templated SEAL iterators that wrap raw pointers. The difference
72         between these two types is that advancing PtrIter<T *> always advances the wrapped pointer by one, whereas the
73         step size (stride) can be set to be anything for a StrideIter<T *>. CoeffIter is a typedef of
74         PtrIter<std::uint64_t *> and and RNSIter is almost the same as StrideIter<std::uint64_t *>, but still a
75         different type.
76 
77         +----------+  Construct   +-------------------+
78         | MyType * |------------->| PtrIter<MyType *> |  Simple wrapper for raw pointers
79         +----------+              +----+----------+---+
80                                        |          |
81                                        |          |
82                           Dereference  |          |     PtrIter<MyType *>::ptr()
83                                        |          |  or implicit conversion
84                                        |          |
85                                        v          v
86                                 +----------+  +----------+
87                                 | MyType & |  | MyType * |
88                                 +----------+  +----------+
89 
90         +----------+  Construct   +----------------------+
91         | MyType * |------------->| StrideIter<MyType *> |  Simple wrapper for raw pointers with custom stride size
92         +----------+              +-----+----------+-----+
93                                         |          |
94                                         |          |
95                            Dereference  |          |     StrideIter<MyType *>::ptr()
96                                         |          |  or implicit conversion
97                                         |          |
98                                         v          v
99                                  +----------+  +----------+
100                                  | MyType & |  | MyType * |
101                                  +----------+  +----------+
102 
103         @par IterTuple
104         An extremely useful template class is the (variadic) IterTuple<...> that allows multiple SEAL iterators to be
105         zipped together. An IterTuple is itself a SEAL iterator and nested IterTuple types are used commonly in the
106         library. Dereferencing an IterTuple always yields an std::tuple, with each IterTuple element dereferenced.
107         Since an IterTuple can be constructed from an std::tuple holding the respective single-parameter constructor
108         arguments for each iterator, the dereferenced std::tuple can often be directly passed on to functions expecting
109         an IterTuple.
110 
111         The individual components of an IterTuple can be accessed with the seal::util::get<i>(...) functions. The
112         behavior of IterTuple is summarized in the following diagram:
113 
114              +-----------------------------------------+
115              | IterTuple<PolyIter, RNSIter, CoeffIter> |
116              +--------------------+--------------------+
117                                   |
118                                   |
119                                   | Dereference
120                                   |
121                                   |
122                                   v
123         +--------------------------------------------------+
124         | std::tuple<RNSIter, CoeffIter, std::uint64_t &>> |
125         +------+-------------------+-------------------+---+
126                |                   |                   |
127                |                   |                   |
128                |  std::get<0>      |  std::get<1>      |  std::get<2>
129                |                   |                   |
130                |                   |                   |
131                v                   v                   v
132         +-------------+    +---------------+   +-----------------+
133         |   RNSIter   |    |   CoeffIter   |   | std::uint64_t & |
134         +-------------+    +---------------+   +-----------------+
135 
136         Sometimes we have to use multiple nested iterator tuples. In this case accessing the nested iterators can be
137         tedious with nested get<...> calls. Consider the following, where encrypted1 and encrypted2 are Ciphertexts
138         and destination is either a Ciphertext or a PolyIter:
139 
140         IterTuple<PolyIter, PolyIter> I(encrypted1, encrypted2);
141         IterTuple<decltype(I), PolyIter> J(I, destination);
142         auto encrypted1_iter = get<0>(get<0>(J));
143         auto encrypted2_iter = get<1>(get<0>(J));
144 
145         An easier way is to use another form of get<...> that accepts multiple indices and accesses the structure in a
146         nested manner. For example, in the above we could also write:
147 
148         auto encrypted1_iter = get<0, 0>(J));
149         auto encrypted2_iter = get<0, 1>(J));
150 
151         Note that the innermost tuple index appears first in the list, i.e. the order is reversed from what appears in
152         a nested get<...> call. The reason for this reversal is that, when deducing what the iterators are, one first
153         examines at the innermost scope, and last the outermost scope, corresponding now to the order of the indices.
154         We have also provided similar functions for nested std::tuple objects, which is necessary when accessing
155         the dereferencing of a nested IterTuple.
156 
157         @par Typedefs for common PtrIter types
158         It is very common to use the types PtrIter<Modulus *> and PtrIter<NTTTables *>. To simplify the
159         notation, we have set up typedefs for these: ModulusIter and NTTTablesIter. There are also constant versions
160         ConstModulusIter and ConstNTTTablesIter, wrapping pointers to constant Modulus and NTTTables instead.
161 
162         @par Creating SEAL iterators
163         Iterators are easiest to create using the variadic iter function that, when given one or more arguments that can
164         naturally be converted to SEAL iterators, outputs an appropriate iterator, or iterator tuple. Consider again the
165         code snippet above, and how confusing the template parameters can become to write. Instead, we can simply write:
166 
167         auto I = iter(encrypted1, encrypted2);
168         auto J = iter(I, destination);
169         auto encrypted1_iter = get<0, 0>(J));
170         auto encrypted2_iter = get<0, 1>(J));
171 
172         There are three ways to create IterTuples from the iter function. The first way is by passing an IterTuple as
173         input, in which case iter outputs a copy of it; there should be no reason to do this. The second way is by
174         passing a variadic set of constructor arguments; iter will output an IterTuple consisting of SEAL iterators
175         that are compatible with the given constructor arguments. The third way is by passing a std::tuple consisting
176         of a variadic set of constructor arguments; the behavior is as in the second way.
177 
178         @par Reversing direction with ReverseIter
179         In addition to the iterator types described above, we provide ReverseIter<SEALIter> that reverses the direction
180         of iteration. ReverseIter<SEALIter> dereferences to the same type as SEALIter: for example, dereferencing
181         ReverseIter<RNSIter> results in CoeffIter, not ReverseIter<CoeffIter>.
182 
183         It is easy to create a ReverseIter from a given SEAL iterator using the function reverse_iter. For example,
184         reverse_iter(encrypted) will return a ReverseIter<PolyIter> if encrypted is either a PolyIter, or a Ciphertext.
185         When passed multiple arguments, reverse_iter returns an appropriate ReverseIter<IterTuple<...>>. For example,
186         reverse_iter(encrypted1, encrypted2) returns ReverseIter<IterTuple<PolyIter, PolyIter>> if encrypted1 and
187         encrypted2 are PolyIter or Ciphertext objects.
188 
189         @par SEAL_ITERATE
190         SEAL iterators are made to be used with the SEAL_ITERATE macro to iterate over a certain number of steps, and
191         for each step call a given lambda function. In C++17 SEAL_ITERATE expands to std::for_each_n, and in C++14 it
192         expands to seal::util::seal_for_each_n -- a custom implementation. For example, the following snippet appears
193         in Evaluator::bfv_multiply:
194 
195         SEAL_ITERATE(
196             iter(encrypted1, encrypted1_q, encrypted1_Bsk),
197             encrypted1_size,
198             behz_extend_base_convert_to_ntt);
199 
200         Here an IterTuple<PolyIter, PolyIter, PolyIter> is created with the iter function; the argument types are
201         Ciphertext (encrypted1), PolyIter (encrypted1_q), and PolyIter (encrypted1_Bsk). The iterator is advanced
202         encrypted1_size times, and each time the lambda function behz_extend_base_convert_to_ntt is called with the
203         iterator tuple dereferenced. The lambda function starts as follows:
204 
205         auto behz_extend_base_convert_to_ntt = [&](auto I) {
206             set_poly(get<0>(I), coeff_count, base_q_size, get<1>(I));
207             ntt_negacyclic_harvey_lazy(get<1>(I), base_q_size, base_q_ntt_tables);
208             ...
209         });
210 
211         Here the parameter I is of type IterTuple<RNSIter, RNSIter, RNSIter>. Inside the lambda function we first copy
212         the RNS polynomial from get<0>(I) (encrypted1) to get<1>(I) (encrypted1_q) and transform it to NTT form. We use
213         an overload of ntt_negacyclic_harvey_lazy that takes an RNSIter, size of the RNS base, and ConstNTTTablesIter as
214         arguments and converts each RNS component separately. Looking at seal/util/ntt.h we see that the function
215         ntt_negacyclic_harvey_lazy is again implemented using SEAL_ITERATE. Specifically, it contains the following:
216 
217         SEAL_ITERATE(iter(operand, tables), coeff_modulus_size, [&](auto I) {
218             ntt_negacyclic_harvey_lazy(get<0>(I), get<1>(I));
219         });
220 
221         Here iter outputs an IterTuple<RNSIter, ConstNTTTablesIter>. In this case the lambda function to be called
222         is defined inline. The argument I takes values IterTuple<CoeffIter, const NTTTables *>, and for each step the
223         CoeffIter overload of ntt_negacyclic_harvey_lazy is called, with a reference to a matching NTTTables object.
224 
225         @par Coding conventions
226         There are two important coding conventions in the above code snippets that are to be observed:
227 
228             1. Use I, J, K, ... for the lambda function parameters representing SEAL iterators. This is compact and
229                makes it very clear that the objects in question are SEAL iterators since such variable names should not
230                be used in SEAL in any other context.
231             2. Lambda functions passed to SEAL_ITERATE should almost always (see 3.) take a parameter of type auto. This
232                will produce simple looking code that performs well with the expected outcome.
233             3. The only exception to 2. is when SEAL_ITERATE operates on a single PtrIter<T *>: dereferencing returns a
234                T &, which may be important to forward by reference to the lambda function. For an example of this, see
235                seal::util::ntt_negacyclic_harvey in seal/util/ntt.h, where the lambda function parameter is auto &.
236 
237                Note: IterTuple<PolyIter, CoeffIter> will dereference to std::tuple<RNSIter, std::uint64_t &>, which can
238                safely be passed by value to the lambda function. Hence, a parameter of type auto in the lambda function
239                will most likely work as expected.
240 
241                Note: Another approach that would always behave correctly is by using a forwarding reference auto && as
242                the lambda function parameter. However, we feel that this unnecessarily complicates the code for a minor
243                benefit.
244 
245         @par Iterator overloads of common functions
246         Some functions have overloads that directly take either CoeffIter, RNSIter, or PolyIter inputs, and apply the
247         operation in question to the entire structure as indicated by the iterator. For example, the function
248         seal::util::negate_poly_coeffmod can negate a single RNS component modulo a given Modulus (CoeffIter overload),
249         an entire RNS polynomial modulo an array of matching Modulus elements (RNSIter overload), or an array of RNS
250         polynomials (PolyIter overload).
251 
252         @par Indexing with SeqIter
253         Sometimes inside SEAL_ITERATE lambda functions it is convenient to know the index of the iteration. This can be
254         done using a SeqIter<T> iterator. The template parameter is an arithmetic type for the index counter.
255 
256         The easiest way to create SeqIter objects is using the seq_iter function. For example, seq_iter(0) returns a
257         SeqIter<int> object with initial value 0. Alternatively, the iter function will detect arithmetic types passed
258         to it and create SeqIter objects from them. For example, calling iter(0) is equivalent to calling seq_iter(0),
259         and this works also for multi-argument calls to iter. Dereferencing a SeqIter object returns the current value.
260         For opposite direction indexing, simply wrap a SeqIter into a ReverseIter, or call reverse_iter directly with
261         the start index.
262 
263         @par Note on allocations
264         In the future we hope to use the parallel version of std::for_each_n, introduced in C++17. For this to work, be
265         mindful of how you use heap allocations in the lambda functions. Specifically, in heavy lambda functions it is
266         probably a good idea to call seal::util::allocate inside the lambda function for any allocations needed, rather
267         than using allocations captured from outside the lambda function.
268 
269         @par Iterators to temporary allocations
270         In many cases one may want to allocate a temporary buffer and create an iterator pointing to it. However, care
271         must be taken to use the correct size parameters now both for the allocation, as well as for setting up the
272         iterator. For this reason, we provide a few helpful macros that set up the Pointer and only expose the iterator
273         to the function. For example, instead of writing the following error-prone code:
274 
275         auto temp_alloc(allocate_poly_array(count, poly_modulus_degree, coeff_modulus_size, pool));
276         PolyIter temp(temp_alloc.get(), poly_modulus_degree, coeff_modulus_size);
277 
278         we can simply write:
279 
280         SEAL_ALLOCATE_GET_POLY_ITER(temp, count, poly_modulus_degree, coeff_modulus_size, pool);
281 
282         However, the latter does not expose the name of the allocation itself. There are similar macros for allocating
283         buffers and setting up PtrIter<T *>, StrideIter<T *>, RNSIter, and CoeffIter objects as well.
284         */
285 
286         class SEALIterBase
287         {};
288 
289         template <typename T, typename>
290         class SeqIter;
291 
292         template <typename T>
293         class PtrIter;
294 
295         template <typename T>
296         class StrideIter;
297 
298         class RNSIter;
299 
300         class ConstRNSIter;
301 
302         class PolyIter;
303 
304         class ConstPolyIter;
305 
306         template <typename SEALIter>
307         class ReverseIter;
308 
309         template <typename... SEALIters>
310         class IterTuple;
311 
312         using CoeffIter = PtrIter<std::uint64_t *>;
313 
314         using ConstCoeffIter = PtrIter<const std::uint64_t *>;
315 
316         using ConstModulusIter = PtrIter<const Modulus *>;
317 
318         using ConstNTTTablesIter = PtrIter<const NTTTables *>;
319 
320         using ModulusIter = PtrIter<Modulus *>;
321 
322         using NTTTablesIter = PtrIter<NTTTables *>;
323 
324         namespace iterator_internal
325         {
326             template <typename Enable, typename... Ts>
327             struct IterType;
328 
329             template <typename T>
330             struct IterType<std::enable_if_t<std::is_arithmetic<T>::value>, T>
331             {
332                 using type = SeqIter<T, void>;
333             };
334 
335             template <typename T>
336             struct IterType<
337                 std::enable_if_t<std::is_base_of<SEALIterBase, std::decay_t<T>>::value && !std::is_pointer<T>::value>,
338                 T>
339             {
340                 using type = std::decay_t<T>;
341             };
342 
343             template <>
344             struct IterType<void, Ciphertext &>
345             {
346                 using type = PolyIter;
347             };
348 
349             template <>
350             struct IterType<void, const Ciphertext &>
351             {
352                 using type = ConstPolyIter;
353             };
354 
355             template <typename T>
356             struct IterType<void, const T *>
357             {
358                 using type = PtrIter<const T *>;
359             };
360 
361             template <typename T>
362             struct IterType<void, T *>
363             {
364                 using type = PtrIter<T *>;
365             };
366 
367             template <typename T>
368             struct IterType<void, const T *&>
369             {
370                 using type = PtrIter<const T *>;
371             };
372 
373             template <typename T>
374             struct IterType<void, T *&>
375             {
376                 using type = PtrIter<T *>;
377             };
378 
379             template <typename T>
380             struct IterType<void, std::vector<T> &>
381             {
382                 using type = PtrIter<T *>;
383             };
384 
385             template <typename T>
386             struct IterType<void, const std::vector<T> &>
387             {
388                 using type = PtrIter<const T *>;
389             };
390 
391             template <typename T>
392             struct IterType<void, Pointer<T> &>
393             {
394                 using type = PtrIter<T *>;
395             };
396 
397             template <typename T>
398             struct IterType<void, const Pointer<T> &>
399             {
400                 using type = PtrIter<T *>;
401             };
402 
403             template <typename T>
404             struct IterType<void, ConstPointer<T> &>
405             {
406                 using type = PtrIter<const T *>;
407             };
408 
409             template <typename T>
410             struct IterType<void, const ConstPointer<T> &>
411             {
412                 using type = PtrIter<const T *>;
413             };
414         } // namespace iterator_internal
415 
416         template <typename T, typename = std::enable_if_t<std::is_arithmetic<T>::value>>
417         class SeqIter : public SEALIterBase
418         {
419         public:
420             using self_type = SeqIter;
421 
422             // Standard iterator typedefs
423             using value_type = T;
424             using pointer = void;
425             using reference = const value_type &;
426             using iterator_category = std::random_access_iterator_tag;
427             using difference_type = std::ptrdiff_t;
428 
429             SeqIter() = default;
430 
431             SeqIter(T start) : value_(start)
432             {}
433 
434             SeqIter(const self_type &copy) = default;
435 
436             self_type &operator=(const self_type &assign) = default;
437 
438             SEAL_NODISCARD inline reference operator*() const noexcept
439             {
440                 return value_;
441             }
442 
443             template <typename SizeT>
444             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
445             {
446                 return value_ + static_cast<T>(n);
447             }
448 
449             inline self_type &operator++() noexcept
450             {
451                 value_++;
452                 return *this;
453             }
454 
455             inline self_type operator++(int) noexcept
456             {
457                 self_type result(value_);
458                 value_++;
459                 return result;
460             }
461 
462             inline self_type &operator--() noexcept
463             {
464                 value_--;
465                 return *this;
466             }
467 
468             inline self_type operator--(int) noexcept
469             {
470                 self_type result(value_);
471                 value_--;
472                 return result;
473             }
474 
475             template <typename SizeT>
476             inline self_type &operator+=(SizeT n) noexcept
477             {
478                 value_ += static_cast<T>(n);
479                 return *this;
480             }
481 
482             template <typename SizeT>
483             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
484             {
485                 return value_ + static_cast<T>(n);
486             }
487 
488             template <typename SizeT>
489             inline self_type &operator-=(SizeT n) noexcept
490             {
491                 value_ -= static_cast<T>(n);
492                 return *this;
493             }
494 
495             template <typename SizeT>
496             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
497             {
498                 return value_ - static_cast<T>(n);
499             }
500 
501             SEAL_NODISCARD inline difference_type operator-(const self_type &b) const noexcept
502             {
503                 return static_cast<difference_type>(value_) - static_cast<difference_type>(b.value_);
504             }
505 
506             SEAL_NODISCARD inline bool operator==(const self_type &compare) const noexcept
507             {
508                 return value_ == *compare;
509             }
510 
511             template <typename S, typename = std::enable_if_t<std::is_arithmetic<S>::value>>
512             SEAL_NODISCARD inline bool operator==(S compare) const noexcept
513             {
514                 return value_ == compare;
515             }
516 
517             SEAL_NODISCARD inline bool operator!=(const self_type &compare) const noexcept
518             {
519                 return !(*this == compare);
520             }
521 
522             template <typename S, typename = std::enable_if_t<std::is_arithmetic<S>::value>>
523             SEAL_NODISCARD inline bool operator!=(S compare) const noexcept
524             {
525                 return !(*this == compare);
526             }
527 
528             SEAL_NODISCARD inline bool operator<(const self_type &compare) const noexcept
529             {
530                 return value_ < *compare;
531             }
532 
533             template <typename S, typename = std::enable_if_t<std::is_arithmetic<S>::value>>
534             SEAL_NODISCARD inline bool operator<(S compare) const noexcept
535             {
536                 return value_ < compare;
537             }
538 
539             SEAL_NODISCARD inline bool operator>(const self_type &compare) const noexcept
540             {
541                 return value_ > *compare;
542             }
543 
544             template <typename S, typename = std::enable_if_t<std::is_arithmetic<S>::value>>
545             SEAL_NODISCARD inline bool operator>(S compare) const noexcept
546             {
547                 return value_ > compare;
548             }
549 
550             SEAL_NODISCARD inline bool operator<=(const self_type &compare) const noexcept
551             {
552                 return !(value_ > *compare);
553             }
554 
555             template <typename S, typename = std::enable_if_t<std::is_arithmetic<S>::value>>
556             SEAL_NODISCARD inline bool operator<=(S compare) const noexcept
557             {
558                 return !(value_ > compare);
559             }
560 
561             SEAL_NODISCARD inline bool operator>=(const self_type &compare) const noexcept
562             {
563                 return !(value_ < *compare);
564             }
565 
566             template <typename S, typename = std::enable_if_t<std::is_arithmetic<S>::value>>
567             SEAL_NODISCARD inline bool operator>=(S compare) const noexcept
568             {
569                 return !(value_ < compare);
570             }
571 
572             SEAL_NODISCARD inline operator T() const noexcept
573             {
574                 return value_;
575             }
576 
577         private:
578             T value_ = 0;
579         };
580 
581         template <typename T>
582         SEAL_NODISCARD inline auto seq_iter(T value = 0) -> SeqIter<T>
583         {
584             return value;
585         }
586 
587         template <typename T>
588         class PtrIter<T *> : public SEALIterBase
589         {
590         public:
591             using self_type = PtrIter<T *>;
592 
593             // Standard iterator typedefs
594             using value_type = T &;
595             using pointer = T *;
596             using reference = value_type;
597             using iterator_category = std::random_access_iterator_tag;
598             using difference_type = std::ptrdiff_t;
599 
600             PtrIter() = default;
601 
602             template <typename S>
603             PtrIter(S *ptr) noexcept : ptr_(ptr)
604             {}
605 
606             template <typename S>
607             PtrIter(PtrIter<S *> copy) noexcept : ptr_(copy.ptr())
608             {}
609 
610             template <typename S>
611             PtrIter(std::vector<S> &arr) noexcept : PtrIter(arr.data())
612             {}
613 
614             template <typename S>
615             PtrIter(const std::vector<S> &arr) noexcept : PtrIter(arr.data())
616             {}
617 
618             template <typename S>
619             PtrIter(const Pointer<S> &arr) noexcept : PtrIter(arr.get())
620             {}
621 
622             template <typename S>
623             inline self_type &operator=(const PtrIter<S *> &assign) noexcept
624             {
625                 ptr_ = assign.ptr();
626                 return *this;
627             }
628 
629             SEAL_NODISCARD inline reference operator*() const noexcept
630             {
631                 return *ptr_;
632             }
633 
634             template <typename SizeT>
635             SEAL_NODISCARD inline reference operator[](SizeT n) const noexcept
636             {
637                 return ptr_[n];
638             }
639 
640             inline self_type &operator++() noexcept
641             {
642                 ptr_++;
643                 return *this;
644             }
645 
646             inline self_type operator++(int) noexcept
647             {
648                 self_type result(ptr_);
649                 ptr_++;
650                 return result;
651             }
652 
653             inline self_type &operator--() noexcept
654             {
655                 ptr_--;
656                 return *this;
657             }
658 
659             inline self_type operator--(int) noexcept
660             {
661                 self_type result(ptr_);
662                 ptr_--;
663                 return result;
664             }
665 
666             template <typename SizeT>
667             inline self_type &operator+=(SizeT n) noexcept
668             {
669                 ptr_ += n;
670                 return *this;
671             }
672 
673             template <typename SizeT>
674             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
675             {
676                 return ptr_ + n;
677             }
678 
679             template <typename SizeT>
680             inline self_type &operator-=(SizeT n) noexcept
681             {
682                 ptr_ -= n;
683                 return *this;
684             }
685 
686             template <typename SizeT>
687             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
688             {
689                 return ptr_ - n;
690             }
691 
692             template <typename S>
693             SEAL_NODISCARD inline difference_type operator-(const PtrIter<S *> &b) const noexcept
694             {
695                 return std::distance(b.ptr(), ptr_);
696             }
697 
698             template <typename S>
699             SEAL_NODISCARD inline bool operator==(const PtrIter<S *> &compare) const noexcept
700             {
701                 return ptr_ == compare.ptr();
702             }
703 
704             template <typename S>
705             SEAL_NODISCARD inline bool operator!=(const PtrIter<S *> &compare) const noexcept
706             {
707                 return !(*this == compare);
708             }
709 
710             template <typename S>
711             SEAL_NODISCARD inline bool operator<(const PtrIter<S *> &compare) const noexcept
712             {
713                 return ptr_ < compare.ptr();
714             }
715 
716             template <typename S>
717             SEAL_NODISCARD inline bool operator>(const PtrIter<S *> &compare) const noexcept
718             {
719                 return ptr_ > compare.ptr();
720             }
721 
722             template <typename S>
723             SEAL_NODISCARD inline bool operator<=(const PtrIter<S *> &compare) const noexcept
724             {
725                 return !(ptr_ > compare.ptr());
726             }
727 
728             template <typename S>
729             SEAL_NODISCARD inline bool operator>=(const PtrIter<S *> &compare) const noexcept
730             {
731                 return !(ptr_ < compare.ptr());
732             }
733 
734             SEAL_NODISCARD explicit inline operator bool() const noexcept
735             {
736                 return nullptr != ptr_;
737             }
738 
739             SEAL_NODISCARD inline reference operator->() const noexcept
740             {
741                 return **this;
742             }
743 
744             SEAL_NODISCARD inline pointer ptr() const noexcept
745             {
746                 return ptr_;
747             }
748 
749             SEAL_NODISCARD inline operator pointer() const noexcept
750             {
751                 return ptr_;
752             }
753 
754         private:
755             pointer ptr_ = nullptr;
756         };
757 
758         template <typename T>
759         class StrideIter<T *> : public SEALIterBase
760         {
761         public:
762             using self_type = StrideIter;
763 
764             // Standard iterator typedefs
765             using value_type = PtrIter<T *>;
766             using pointer = T *;
767             using reference = const value_type &;
768             using iterator_category = std::random_access_iterator_tag;
769             using difference_type = std::ptrdiff_t;
770 
771             StrideIter() = default;
772 
773             template <typename S>
774             StrideIter(S *ptr, std::size_t stride) noexcept : ptr_it_(ptr), stride_(stride)
775             {}
776 
777             template <typename S>
778             StrideIter(StrideIter<S *> copy) noexcept : ptr_it_(copy.ptr()), stride_(copy.stride())
779             {}
780 
781             template <typename S>
782             StrideIter(std::vector<S> &arr, std::size_t stride) noexcept : StrideIter(arr.data(), stride)
783             {}
784 
785             template <typename S>
786             StrideIter(const std::vector<S> &arr, std::size_t stride) noexcept : StrideIter(arr.data(), stride)
787             {}
788 
789             template <typename S>
790             StrideIter(const Pointer<S> &arr, std::size_t stride) noexcept : StrideIter(arr.get(), stride)
791             {}
792 
793             template <typename S>
794             inline self_type &operator=(const StrideIter<S *> &assign) noexcept
795             {
796                 ptr_it_ = assign.ptr();
797                 stride_ = assign.stride();
798                 return *this;
799             }
800 
801             SEAL_NODISCARD inline reference operator*() const noexcept
802             {
803                 return ptr_it_;
804             }
805 
806             template <typename SizeT>
807             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
808             {
809                 self_type result(*this);
810                 result += static_cast<difference_type>(n);
811                 return *result;
812             }
813 
814             inline self_type &operator++() noexcept
815             {
816                 ptr_it_ += static_cast<difference_type>(stride_);
817                 return *this;
818             }
819 
820             inline self_type operator++(int) noexcept
821             {
822                 self_type result(*this);
823                 ptr_it_ += static_cast<difference_type>(stride_);
824                 return result;
825             }
826 
827             inline self_type &operator--() noexcept
828             {
829                 ptr_it_ -= static_cast<difference_type>(stride_);
830                 return *this;
831             }
832 
833             inline self_type operator--(int) noexcept
834             {
835                 self_type result(*this);
836                 ptr_it_ -= static_cast<difference_type>(stride_);
837                 return result;
838             }
839 
840             template <typename SizeT>
841             inline self_type &operator+=(SizeT n) noexcept
842             {
843                 ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(stride_);
844                 return *this;
845             }
846 
847             template <typename SizeT>
848             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
849             {
850                 self_type result(*this);
851                 result.ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(stride_);
852                 return result;
853             }
854 
855             template <typename SizeT>
856             inline self_type &operator-=(SizeT n) noexcept
857             {
858                 ptr_it_ -= static_cast<difference_type>(n) * static_cast<difference_type>(stride_);
859                 return *this;
860             }
861 
862             template <typename SizeT>
863             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
864             {
865                 return *this + (-static_cast<difference_type>(n));
866             }
867 
868             template <typename S>
869             SEAL_NODISCARD inline difference_type operator-(const StrideIter<S *> &b) const
870             {
871 #ifdef SEAL_DEBUG
872                 if (!stride_)
873                 {
874                     throw std::logic_error("stride cannot be zero");
875                 }
876                 if (stride_ != b.stride())
877                 {
878                     throw std::invalid_argument("incompatible iterators");
879                 }
880 #endif
881                 return (ptr_it_ - *b) / static_cast<difference_type>(stride_);
882             }
883 
884             template <typename S>
885             SEAL_NODISCARD inline bool operator==(const StrideIter<S *> &compare) const noexcept
886             {
887                 return ptr_it_ == *compare;
888             }
889 
890             template <typename S>
891             SEAL_NODISCARD inline bool operator!=(const StrideIter<S *> &compare) const noexcept
892             {
893                 return !(*this == compare);
894             }
895 
896             template <typename S>
897             SEAL_NODISCARD inline bool operator<(const StrideIter<S *> &compare) const noexcept
898             {
899                 return ptr_it_ < *compare;
900             }
901 
902             template <typename S>
903             SEAL_NODISCARD inline bool operator>(const StrideIter<S *> &compare) const noexcept
904             {
905                 return ptr_it_ > *compare;
906             }
907 
908             template <typename S>
909             SEAL_NODISCARD inline bool operator<=(const StrideIter<S *> &compare) const noexcept
910             {
911                 return !(ptr_it_ > *compare);
912             }
913 
914             template <typename S>
915             SEAL_NODISCARD inline bool operator>=(const StrideIter<S *> &compare) const noexcept
916             {
917                 return !(ptr_it_ < *compare);
918             }
919 
920             SEAL_NODISCARD explicit inline operator bool() const noexcept
921             {
922                 return static_cast<bool>(ptr_it_);
923             }
924 
925             SEAL_NODISCARD inline reference operator->() const noexcept
926             {
927                 return **this;
928             }
929 
930             SEAL_NODISCARD inline std::size_t stride() const noexcept
931             {
932                 return stride_;
933             }
934 
935             SEAL_NODISCARD inline pointer ptr() const noexcept
936             {
937                 return ptr_it_.ptr();
938             }
939 
940             SEAL_NODISCARD inline operator pointer() const noexcept
941             {
942                 return ptr_it_.operator pointer();
943             }
944 
945         private:
946             PtrIter<T *> ptr_it_ = {};
947 
948             std::size_t stride_ = 0;
949         };
950 
951         class RNSIter : public SEALIterBase
952         {
953         public:
954             friend class PolyIter;
955 
956             using self_type = RNSIter;
957 
958             // Standard iterator typedefs
959             using value_type = CoeffIter;
960             using pointer = void;
961             using reference = const value_type &;
962             using iterator_category = std::random_access_iterator_tag;
963             using difference_type = std::ptrdiff_t;
964 
965             RNSIter() : ptr_it_(), step_size_(0)
966             {}
967 
968             RNSIter(std::uint64_t *ptr, std::size_t poly_modulus_degree) : ptr_it_(ptr), step_size_(poly_modulus_degree)
969             {}
970 
971             RNSIter(const self_type &copy) = default;
972 
973             self_type &operator=(const self_type &assign) = default;
974 
975             template <typename S>
976             RNSIter(const StrideIter<S *> &stride_it) : RNSIter(stride_it.ptr(), stride_it.stride())
977             {}
978 
979             template <typename S>
980             inline self_type &operator=(const StrideIter<S *> &assign)
981             {
982                 ptr_it_ = assign;
983                 step_size_ = assign.stride();
984                 return *this;
985             }
986 
987             SEAL_NODISCARD inline reference operator*() const noexcept
988             {
989                 return ptr_it_;
990             }
991 
992             template <typename SizeT>
993             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
994             {
995                 self_type result(*this);
996                 result += static_cast<difference_type>(n);
997                 return *result;
998             }
999 
1000             inline self_type &operator++() noexcept
1001             {
1002                 ptr_it_ += static_cast<difference_type>(step_size_);
1003                 return *this;
1004             }
1005 
1006             inline self_type operator++(int) noexcept
1007             {
1008                 self_type result(*this);
1009                 ptr_it_ += static_cast<difference_type>(step_size_);
1010                 return result;
1011             }
1012 
1013             inline self_type &operator--() noexcept
1014             {
1015                 ptr_it_ -= static_cast<difference_type>(step_size_);
1016                 return *this;
1017             }
1018 
1019             inline self_type operator--(int) noexcept
1020             {
1021                 self_type result(*this);
1022                 ptr_it_ -= static_cast<difference_type>(step_size_);
1023                 return result;
1024             }
1025 
1026             template <typename SizeT>
1027             inline self_type &operator+=(SizeT n) noexcept
1028             {
1029                 ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1030                 return *this;
1031             }
1032 
1033             template <typename SizeT>
1034             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
1035             {
1036                 self_type result(*this);
1037                 result.ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1038                 return result;
1039             }
1040 
1041             template <typename SizeT>
1042             inline self_type &operator-=(SizeT n) noexcept
1043             {
1044                 ptr_it_ -= static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1045                 return *this;
1046             }
1047 
1048             template <typename SizeT>
1049             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
1050             {
1051                 return *this + (-static_cast<difference_type>(n));
1052             }
1053 
1054             SEAL_NODISCARD inline difference_type operator-(const self_type &b) const
1055             {
1056 #ifdef SEAL_DEBUG
1057                 if (!step_size_)
1058                 {
1059                     throw std::logic_error("step_size cannot be zero");
1060                 }
1061                 if (step_size_ != b.step_size_)
1062                 {
1063                     throw std::invalid_argument("incompatible iterators");
1064                 }
1065 #endif
1066                 return (ptr_it_ - b.ptr_it_) / static_cast<difference_type>(step_size_);
1067             }
1068 
1069             SEAL_NODISCARD inline bool operator==(const self_type &compare) const noexcept
1070             {
1071                 return ptr_it_ == compare.ptr_it_;
1072             }
1073 
1074             SEAL_NODISCARD inline bool operator!=(const self_type &compare) const noexcept
1075             {
1076                 return !(*this == compare);
1077             }
1078 
1079             SEAL_NODISCARD inline bool operator<(const self_type &compare) const noexcept
1080             {
1081                 return ptr_it_ < compare.ptr_it_;
1082             }
1083 
1084             SEAL_NODISCARD inline bool operator>(const self_type &compare) const noexcept
1085             {
1086                 return ptr_it_ > compare.ptr_it_;
1087             }
1088 
1089             SEAL_NODISCARD inline bool operator<=(const self_type &compare) const noexcept
1090             {
1091                 return !(ptr_it_ > compare.ptr_it_);
1092             }
1093 
1094             SEAL_NODISCARD inline bool operator>=(const self_type &compare) const noexcept
1095             {
1096                 return !(ptr_it_ < compare.ptr_it_);
1097             }
1098 
1099             SEAL_NODISCARD inline operator std::uint64_t *() const noexcept
1100             {
1101                 return static_cast<std::uint64_t *>(ptr_it_);
1102             }
1103 
1104             SEAL_NODISCARD explicit inline operator bool() const noexcept
1105             {
1106                 return static_cast<bool>(ptr_it_);
1107             }
1108 
1109             SEAL_NODISCARD inline reference operator->() const noexcept
1110             {
1111                 return **this;
1112             }
1113 
1114             SEAL_NODISCARD inline std::size_t poly_modulus_degree() const noexcept
1115             {
1116                 return step_size_;
1117             }
1118 
1119         private:
1120             CoeffIter ptr_it_ = {};
1121 
1122             std::size_t step_size_ = 0;
1123         };
1124 
1125         class ConstRNSIter : public SEALIterBase
1126         {
1127         public:
1128             friend class ConstPolyIter;
1129 
1130             using self_type = ConstRNSIter;
1131 
1132             // Standard iterator typedefs
1133             using value_type = ConstCoeffIter;
1134             using pointer = void;
1135             using reference = const value_type &;
1136             using iterator_category = std::random_access_iterator_tag;
1137             using difference_type = std::ptrdiff_t;
1138 
1139             ConstRNSIter() : ptr_it_(), step_size_(0)
1140             {}
1141 
1142             ConstRNSIter(const std::uint64_t *ptr, std::size_t poly_modulus_degree)
1143                 : ptr_it_(ptr), step_size_(poly_modulus_degree)
1144             {}
1145 
1146             ConstRNSIter(const self_type &copy) = default;
1147 
1148             self_type &operator=(const self_type &assign) = default;
1149 
1150             ConstRNSIter(const RNSIter &copy)
1151                 : ptr_it_(static_cast<const std::uint64_t *>(copy)), step_size_(copy.poly_modulus_degree())
1152             {}
1153 
1154             template <typename S>
1155             ConstRNSIter(const StrideIter<S *> &stride_it) : ConstRNSIter(stride_it.ptr(), stride_it.stride())
1156             {}
1157 
1158             template <typename S>
1159             inline self_type &operator=(const StrideIter<S *> &assign)
1160             {
1161                 ptr_it_ = assign;
1162                 step_size_ = assign.stride();
1163                 return *this;
1164             }
1165 
1166             SEAL_NODISCARD inline reference operator*() const noexcept
1167             {
1168                 return ptr_it_;
1169             }
1170 
1171             template <typename SizeT>
1172             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
1173             {
1174                 self_type result(*this);
1175                 result += static_cast<difference_type>(n);
1176                 return *result;
1177             }
1178 
1179             inline self_type &operator++() noexcept
1180             {
1181                 ptr_it_ += static_cast<difference_type>(step_size_);
1182                 return *this;
1183             }
1184 
1185             inline self_type operator++(int) noexcept
1186             {
1187                 self_type result(*this);
1188                 ptr_it_ += static_cast<difference_type>(step_size_);
1189                 return result;
1190             }
1191 
1192             inline self_type &operator--() noexcept
1193             {
1194                 ptr_it_ -= static_cast<difference_type>(step_size_);
1195                 return *this;
1196             }
1197 
1198             inline self_type operator--(int) noexcept
1199             {
1200                 self_type result(*this);
1201                 ptr_it_ -= static_cast<difference_type>(step_size_);
1202                 return result;
1203             }
1204 
1205             template <typename SizeT>
1206             inline self_type &operator+=(SizeT n) noexcept
1207             {
1208                 ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1209                 return *this;
1210             }
1211 
1212             template <typename SizeT>
1213             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
1214             {
1215                 self_type result(*this);
1216                 result.ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1217                 return result;
1218             }
1219 
1220             template <typename SizeT>
1221             inline self_type &operator-=(SizeT n) noexcept
1222             {
1223                 ptr_it_ -= static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1224                 return *this;
1225             }
1226 
1227             template <typename SizeT>
1228             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
1229             {
1230                 return *this + (-static_cast<difference_type>(n));
1231             }
1232 
1233             SEAL_NODISCARD inline difference_type operator-(const self_type &b) const
1234             {
1235 #ifdef SEAL_DEBUG
1236                 if (!step_size_)
1237                 {
1238                     throw std::logic_error("step_size cannot be zero");
1239                 }
1240                 if (step_size_ != b.step_size_)
1241                 {
1242                     throw std::invalid_argument("incompatible iterators");
1243                 }
1244 #endif
1245                 return (ptr_it_ - b.ptr_it_) / static_cast<difference_type>(step_size_);
1246             }
1247 
1248             SEAL_NODISCARD inline bool operator==(const self_type &compare) const noexcept
1249             {
1250                 return ptr_it_ == compare.ptr_it_;
1251             }
1252 
1253             SEAL_NODISCARD inline bool operator!=(const self_type &compare) const noexcept
1254             {
1255                 return !(*this == compare);
1256             }
1257 
1258             SEAL_NODISCARD inline bool operator<(const self_type &compare) const noexcept
1259             {
1260                 return ptr_it_ < compare.ptr_it_;
1261             }
1262 
1263             SEAL_NODISCARD inline bool operator>(const self_type &compare) const noexcept
1264             {
1265                 return ptr_it_ > compare.ptr_it_;
1266             }
1267 
1268             SEAL_NODISCARD inline bool operator<=(const self_type &compare) const noexcept
1269             {
1270                 return !(ptr_it_ > compare.ptr_it_);
1271             }
1272 
1273             SEAL_NODISCARD inline bool operator>=(const self_type &compare) const noexcept
1274             {
1275                 return !(ptr_it_ < compare.ptr_it_);
1276             }
1277 
1278             SEAL_NODISCARD inline operator const std::uint64_t *() const noexcept
1279             {
1280                 return static_cast<const std::uint64_t *>(ptr_it_);
1281             }
1282 
1283             SEAL_NODISCARD explicit inline operator bool() const noexcept
1284             {
1285                 return static_cast<bool>(ptr_it_);
1286             }
1287 
1288             SEAL_NODISCARD inline reference operator->() const noexcept
1289             {
1290                 return **this;
1291             }
1292 
1293             SEAL_NODISCARD inline std::size_t poly_modulus_degree() const noexcept
1294             {
1295                 return step_size_;
1296             }
1297 
1298         private:
1299             ConstCoeffIter ptr_it_ = {};
1300 
1301             std::size_t step_size_ = 0;
1302         };
1303 
1304         class PolyIter : public SEALIterBase
1305         {
1306         public:
1307             using self_type = PolyIter;
1308 
1309             // Standard iterator typedefs
1310             using value_type = RNSIter;
1311             using pointer = void;
1312             using reference = const value_type &;
1313             using iterator_category = std::random_access_iterator_tag;
1314             using difference_type = std::ptrdiff_t;
1315 
1316             PolyIter() : rns_it_(nullptr, 0), coeff_modulus_size_(0), step_size_(0)
1317             {}
1318 
1319             PolyIter(std::uint64_t *ptr, std::size_t poly_modulus_degree, std::size_t coeff_modulus_size)
1320                 : rns_it_(ptr, poly_modulus_degree), coeff_modulus_size_(coeff_modulus_size),
1321                   step_size_(mul_safe(poly_modulus_degree, coeff_modulus_size_))
1322             {}
1323 
1324             PolyIter(Ciphertext &ct);
1325 
1326             PolyIter(const self_type &copy) = default;
1327 
1328             self_type &operator=(const self_type &assign) = default;
1329 
1330             SEAL_NODISCARD inline reference operator*() const noexcept
1331             {
1332                 return rns_it_;
1333             }
1334 
1335             template <typename SizeT>
1336             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
1337             {
1338                 self_type result(*this);
1339                 result += static_cast<difference_type>(n);
1340                 return *result;
1341             }
1342 
1343             inline self_type &operator++() noexcept
1344             {
1345                 rns_it_.ptr_it_ += step_size_;
1346                 return *this;
1347             }
1348 
1349             inline self_type operator++(int) noexcept
1350             {
1351                 self_type result(*this);
1352                 rns_it_.ptr_it_ += step_size_;
1353                 return result;
1354             }
1355 
1356             inline self_type &operator--() noexcept
1357             {
1358                 rns_it_.ptr_it_ -= step_size_;
1359                 return *this;
1360             }
1361 
1362             inline self_type operator--(int) noexcept
1363             {
1364                 self_type result(*this);
1365                 rns_it_.ptr_it_ -= step_size_;
1366                 return result;
1367             }
1368 
1369             template <typename SizeT>
1370             inline self_type &operator+=(SizeT n) noexcept
1371             {
1372                 rns_it_.ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1373                 return *this;
1374             }
1375 
1376             template <typename SizeT>
1377             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
1378             {
1379                 self_type result(*this);
1380                 result.rns_it_.ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1381                 return result;
1382             }
1383 
1384             template <typename SizeT>
1385             inline self_type &operator-=(SizeT n) noexcept
1386             {
1387                 rns_it_.ptr_it_ -= static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1388                 return *this;
1389             }
1390 
1391             template <typename SizeT>
1392             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
1393             {
1394                 return *this + (-static_cast<difference_type>(n));
1395             }
1396 
1397             SEAL_NODISCARD inline difference_type operator-(const self_type &b) const
1398             {
1399 #ifdef SEAL_DEBUG
1400                 if (!step_size_)
1401                 {
1402                     throw std::logic_error("step_size cannot be zero");
1403                 }
1404                 if (step_size_ != b.step_size_)
1405                 {
1406                     throw std::invalid_argument("incompatible iterators");
1407                 }
1408                 if (coeff_modulus_size_ != b.coeff_modulus_size_)
1409                 {
1410                     throw std::invalid_argument("incompatible iterators");
1411                 }
1412 #endif
1413                 return (rns_it_.ptr_it_ - b.rns_it_.ptr_it_) / static_cast<difference_type>(step_size_);
1414             }
1415 
1416             SEAL_NODISCARD inline bool operator==(const self_type &compare) const noexcept
1417             {
1418                 return rns_it_ == compare.rns_it_;
1419             }
1420 
1421             SEAL_NODISCARD inline bool operator!=(const self_type &compare) const noexcept
1422             {
1423                 return !(*this == compare);
1424             }
1425 
1426             SEAL_NODISCARD inline bool operator<(const self_type &compare) const noexcept
1427             {
1428                 return rns_it_ < compare.rns_it_;
1429             }
1430 
1431             SEAL_NODISCARD inline bool operator>(const self_type &compare) const noexcept
1432             {
1433                 return rns_it_ > compare.rns_it_;
1434             }
1435 
1436             SEAL_NODISCARD inline bool operator<=(const self_type &compare) const noexcept
1437             {
1438                 return !(rns_it_ > compare.rns_it_);
1439             }
1440 
1441             SEAL_NODISCARD inline bool operator>=(const self_type &compare) const noexcept
1442             {
1443                 return !(rns_it_ < compare.rns_it_);
1444             }
1445 
1446             SEAL_NODISCARD inline operator std::uint64_t *() const noexcept
1447             {
1448                 return rns_it_;
1449             }
1450 
1451             SEAL_NODISCARD explicit inline operator bool() const noexcept
1452             {
1453                 return static_cast<bool>(rns_it_);
1454             }
1455 
1456             SEAL_NODISCARD inline reference operator->() const noexcept
1457             {
1458                 return **this;
1459             }
1460 
1461             SEAL_NODISCARD inline std::size_t poly_modulus_degree() const noexcept
1462             {
1463                 return rns_it_.poly_modulus_degree();
1464             }
1465 
1466             SEAL_NODISCARD inline std::size_t coeff_modulus_size() const noexcept
1467             {
1468                 return coeff_modulus_size_;
1469             }
1470 
1471         private:
1472             RNSIter rns_it_ = {};
1473 
1474             std::size_t coeff_modulus_size_ = 0;
1475 
1476             std::size_t step_size_ = 0;
1477         };
1478 
1479         class ConstPolyIter : public SEALIterBase
1480         {
1481         public:
1482             using self_type = ConstPolyIter;
1483 
1484             // Standard iterator typedefs
1485             using value_type = ConstRNSIter;
1486             using pointer = void;
1487             using reference = const value_type &;
1488             using iterator_category = std::random_access_iterator_tag;
1489             using difference_type = std::ptrdiff_t;
1490 
1491             ConstPolyIter() : rns_it_(nullptr, 0), coeff_modulus_size_(0), step_size_(0)
1492             {}
1493 
1494             ConstPolyIter(const std::uint64_t *ptr, std::size_t poly_modulus_degree, std::size_t coeff_modulus_size)
1495                 : rns_it_(ptr, poly_modulus_degree), coeff_modulus_size_(coeff_modulus_size),
1496                   step_size_(mul_safe(poly_modulus_degree, coeff_modulus_size_))
1497             {}
1498 
1499             ConstPolyIter(const Ciphertext &ct);
1500 
1501             ConstPolyIter(Ciphertext &ct);
1502 
1503             ConstPolyIter(const self_type &copy) = default;
1504 
1505             self_type &operator=(const self_type &assign) = default;
1506 
1507             ConstPolyIter(const PolyIter &copy)
1508                 : rns_it_(static_cast<const std::uint64_t *>(copy), copy.poly_modulus_degree()),
1509                   coeff_modulus_size_(copy.coeff_modulus_size()),
1510                   step_size_(mul_safe(rns_it_.poly_modulus_degree(), coeff_modulus_size_))
1511             {}
1512 
1513             SEAL_NODISCARD inline reference operator*() const noexcept
1514             {
1515                 return rns_it_;
1516             }
1517 
1518             template <typename SizeT>
1519             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
1520             {
1521                 self_type result(*this);
1522                 result += static_cast<difference_type>(n);
1523                 return *result;
1524             }
1525 
1526             inline self_type &operator++() noexcept
1527             {
1528                 rns_it_.ptr_it_ += step_size_;
1529                 return *this;
1530             }
1531 
1532             inline self_type operator++(int) noexcept
1533             {
1534                 self_type result(*this);
1535                 rns_it_.ptr_it_ += step_size_;
1536                 return result;
1537             }
1538 
1539             inline self_type &operator--() noexcept
1540             {
1541                 rns_it_.ptr_it_ -= step_size_;
1542                 return *this;
1543             }
1544 
1545             inline self_type operator--(int) noexcept
1546             {
1547                 self_type result(*this);
1548                 rns_it_.ptr_it_ -= step_size_;
1549                 return result;
1550             }
1551 
1552             template <typename SizeT>
1553             inline self_type &operator+=(SizeT n) noexcept
1554             {
1555                 rns_it_.ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1556                 return *this;
1557             }
1558 
1559             template <typename SizeT>
1560             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
1561             {
1562                 self_type result(*this);
1563                 result.rns_it_.ptr_it_ += static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1564                 return result;
1565             }
1566 
1567             template <typename SizeT>
1568             inline self_type &operator-=(SizeT n) noexcept
1569             {
1570                 rns_it_.ptr_it_ -= static_cast<difference_type>(n) * static_cast<difference_type>(step_size_);
1571                 return *this;
1572             }
1573 
1574             template <typename SizeT>
1575             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
1576             {
1577                 return *this + (-static_cast<difference_type>(n));
1578             }
1579 
1580             SEAL_NODISCARD inline difference_type operator-(const self_type &b) const
1581             {
1582 #ifdef SEAL_DEBUG
1583                 if (!step_size_)
1584                 {
1585                     throw std::logic_error("step_size cannot be zero");
1586                 }
1587                 if (step_size_ != b.step_size_)
1588                 {
1589                     throw std::invalid_argument("incompatible iterators");
1590                 }
1591                 if (coeff_modulus_size_ != b.coeff_modulus_size_)
1592                 {
1593                     throw std::invalid_argument("incompatible iterators");
1594                 }
1595 #endif
1596                 return (rns_it_.ptr_it_ - b.rns_it_.ptr_it_) / static_cast<difference_type>(step_size_);
1597             }
1598 
1599             SEAL_NODISCARD inline bool operator==(const self_type &compare) const noexcept
1600             {
1601                 return rns_it_ == compare.rns_it_;
1602             }
1603 
1604             SEAL_NODISCARD inline bool operator!=(const self_type &compare) const noexcept
1605             {
1606                 return !(*this == compare);
1607             }
1608 
1609             SEAL_NODISCARD inline bool operator<(const self_type &compare) const noexcept
1610             {
1611                 return rns_it_ < compare.rns_it_;
1612             }
1613 
1614             SEAL_NODISCARD inline bool operator>(const self_type &compare) const noexcept
1615             {
1616                 return rns_it_ > compare.rns_it_;
1617             }
1618 
1619             SEAL_NODISCARD inline bool operator<=(const self_type &compare) const noexcept
1620             {
1621                 return !(rns_it_ > compare.rns_it_);
1622             }
1623 
1624             SEAL_NODISCARD inline bool operator>=(const self_type &compare) const noexcept
1625             {
1626                 return !(rns_it_ < compare.rns_it_);
1627             }
1628 
1629             SEAL_NODISCARD inline operator const std::uint64_t *() const noexcept
1630             {
1631                 return rns_it_;
1632             }
1633 
1634             SEAL_NODISCARD explicit inline operator bool() const noexcept
1635             {
1636                 return static_cast<bool>(rns_it_);
1637             }
1638 
1639             SEAL_NODISCARD inline reference operator->() const noexcept
1640             {
1641                 return **this;
1642             }
1643 
1644             SEAL_NODISCARD inline std::size_t poly_modulus_degree() const noexcept
1645             {
1646                 return rns_it_.poly_modulus_degree();
1647             }
1648 
1649             SEAL_NODISCARD inline std::size_t coeff_modulus_size() const noexcept
1650             {
1651                 return coeff_modulus_size_;
1652             }
1653 
1654         private:
1655             ConstRNSIter rns_it_ = {};
1656 
1657             std::size_t coeff_modulus_size_ = 0;
1658 
1659             std::size_t step_size_ = 0;
1660         };
1661 
1662         template <typename SEALIter>
1663         class ReverseIter : public SEALIter
1664         {
1665         public:
1666             static_assert(
1667                 std::is_base_of<SEALIterBase, SEALIter>::value,
1668                 "Template parameter must derive from seal::util::SEALIterBase");
1669 
1670             using self_type = ReverseIter<SEALIter>;
1671 
1672             // Standard iterator typedefs
1673             using value_type = typename std::iterator_traits<SEALIter>::value_type;
1674             using pointer = typename std::iterator_traits<SEALIter>::pointer;
1675             using reference = typename std::iterator_traits<SEALIter>::reference;
1676             using iterator_category = typename std::iterator_traits<SEALIter>::iterator_category;
1677             using difference_type = typename std::iterator_traits<SEALIter>::difference_type;
1678 
1679             ReverseIter() : SEALIter()
1680             {}
1681 
1682             ReverseIter(const SEALIter &copy) : SEALIter(copy)
1683             {}
1684 
1685             ReverseIter(const self_type &copy) = default;
1686 
1687             self_type &operator=(const self_type &assign) = default;
1688 
1689             template <typename SizeT>
1690             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
1691             {
1692                 self_type result(*this);
1693                 result += static_cast<difference_type>(n);
1694                 return *result;
1695             }
1696 
1697             inline self_type &operator++() noexcept
1698             {
1699                 SEALIter::operator--();
1700                 return *this;
1701             }
1702 
1703             inline self_type operator++(int) noexcept
1704             {
1705                 self_type result(*this);
1706                 SEALIter::operator--();
1707                 return result;
1708             }
1709 
1710             inline self_type &operator--() noexcept
1711             {
1712                 SEALIter::operator++();
1713                 return *this;
1714             }
1715 
1716             inline self_type operator--(int) noexcept
1717             {
1718                 self_type result(*this);
1719                 SEALIter::operator++();
1720                 return result;
1721             }
1722 
1723             template <typename SizeT>
1724             inline self_type &operator+=(SizeT n) noexcept
1725             {
1726                 SEALIter::operator-=(n);
1727                 return *this;
1728             }
1729 
1730             template <typename SizeT>
1731             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
1732             {
1733                 self_type result(*this);
1734                 result += n;
1735                 return result;
1736             }
1737 
1738             template <typename SizeT>
1739             inline self_type &operator-=(SizeT n) noexcept
1740             {
1741                 SEALIter::operator+=(n);
1742                 return *this;
1743             }
1744 
1745             template <typename SizeT>
1746             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
1747             {
1748                 return *this + (-static_cast<difference_type>(n));
1749             }
1750 
1751             SEAL_NODISCARD inline difference_type operator-(const self_type &b) const
1752             {
1753                 // Note the reversed order
1754                 return static_cast<SEALIter>(*b) - static_cast<SEALIter>(*this);
1755             }
1756 
1757             SEAL_NODISCARD inline bool operator<(const self_type &compare) const noexcept
1758             {
1759                 // Note the reversed order
1760                 return static_cast<SEALIter>(*this) > static_cast<SEALIter>(*compare);
1761             }
1762 
1763             SEAL_NODISCARD inline bool operator>(const self_type &compare) const noexcept
1764             {
1765                 // Note the reversed order
1766                 return static_cast<SEALIter>(*this) < static_cast<SEALIter>(*compare);
1767             }
1768 
1769             SEAL_NODISCARD inline bool operator<=(const self_type &compare) const noexcept
1770             {
1771                 // Note the reversed order
1772                 return !(*this > compare);
1773             }
1774 
1775             SEAL_NODISCARD inline bool operator>=(const self_type &compare) const noexcept
1776             {
1777                 return !(*this < compare);
1778             }
1779 
1780             SEAL_NODISCARD inline reference operator->() const noexcept
1781             {
1782                 return **this;
1783             }
1784         };
1785 
1786         namespace iterator_internal
1787         {
1788             template <typename... SEALIters>
1789             struct extend_iter_tuple;
1790 
1791             template <typename SEALIter, typename... Rest>
1792             struct extend_iter_tuple<SEALIter, IterTuple<Rest...>>
1793             {
1794                 using type = IterTuple<SEALIter, Rest...>;
1795             };
1796 
1797             template <typename SEALIter1, typename SEALIter2>
1798             struct extend_iter_tuple<SEALIter1, SEALIter2>
1799             {
1800                 using type = IterTuple<SEALIter1, SEALIter2>;
1801             };
1802 
1803             template <typename... Ts>
1804             struct extend_std_tuple;
1805 
1806             template <typename T, typename... Rest>
1807             struct extend_std_tuple<T, std::tuple<Rest...>>
1808             {
1809                 using type = std::tuple<T, Rest...>;
1810             };
1811 
1812             template <typename T1, typename T2>
1813             struct extend_std_tuple<T1, T2>
1814             {
1815                 using type = std::tuple<T1, T2>;
1816             };
1817         } // namespace iterator_internal
1818 
1819         template <typename SEALIter, typename... Rest>
1820         class IterTuple<SEALIter, Rest...> : public SEALIterBase
1821         {
1822         public:
1823             static_assert(
1824                 std::is_base_of<SEALIterBase, SEALIter>::value,
1825                 "Template parameter must derive from seal::util::SEALIterBase");
1826 
1827             using self_type = IterTuple<SEALIter, Rest...>;
1828 
1829             // Standard iterator typedefs
1830             using value_type = typename iterator_internal::extend_std_tuple<
1831                 typename std::iterator_traits<SEALIter>::value_type,
1832                 typename std::iterator_traits<IterTuple<Rest...>>::value_type>::type;
1833             using pointer = void;
1834             using reference = const value_type &;
1835             using iterator_category = std::random_access_iterator_tag;
1836             using difference_type = std::ptrdiff_t;
1837 
1838             IterTuple() = default;
1839 
1840             IterTuple(SEALIter first, IterTuple<Rest...> rest) : first_(first), rest_(rest){};
1841 
1842             IterTuple(SEALIter first, Rest... rest) : first_(first), rest_(rest...)
1843             {}
1844 
1845             template <typename... Ts>
1846             IterTuple(const std::tuple<Ts...> &tp)
1847                 : IterTuple(seal_apply(
1848                       [](auto &&... args) -> IterTuple { return { std::forward<decltype(args)>(args)... }; },
1849                       std::forward<decltype(tp)>(tp)))
1850             {
1851                 static_assert(
1852                     sizeof...(Ts) == sizeof...(Rest) + 1, "std::tuple size does not match seal::util::IterTuple size");
1853             }
1854 
1855             template <typename... Ts>
1856             IterTuple(std::tuple<Ts...> &&tp)
1857                 : IterTuple(seal_apply(
1858                       [](auto &&... args) -> IterTuple && { return { std::forward<decltype(args)>(args)... }; },
1859                       std::forward<decltype(tp)>(tp)))
1860             {
1861                 static_assert(
1862                     sizeof...(Ts) == sizeof...(Rest) + 1, "std::tuple size does not match seal::util::IterTuple size");
1863             }
1864 
1865             IterTuple(const self_type &copy) = default;
1866 
1867             self_type &operator=(const self_type &assign) = default;
1868 
1869             SEAL_NODISCARD inline value_type operator*() const noexcept
1870             {
1871                 return seal_apply(
1872                     [this](auto &&... args) -> value_type {
1873                         return { *first_, std::forward<decltype(args)>(args)... };
1874                     },
1875                     *rest_);
1876             }
1877 
1878             template <typename SizeT>
1879             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
1880             {
1881                 self_type result(*this);
1882                 result += static_cast<difference_type>(n);
1883                 return *result;
1884             }
1885 
1886             inline self_type &operator++() noexcept
1887             {
1888                 first_++;
1889                 rest_++;
1890                 return *this;
1891             }
1892 
1893             inline self_type operator++(int) noexcept
1894             {
1895                 self_type result(*this);
1896                 first_++;
1897                 rest_++;
1898                 return result;
1899             }
1900 
1901             inline self_type &operator--() noexcept
1902             {
1903                 first_--;
1904                 rest_--;
1905                 return *this;
1906             }
1907 
1908             inline self_type operator--(int) noexcept
1909             {
1910                 self_type result(*this);
1911                 first_--;
1912                 rest_--;
1913                 return result;
1914             }
1915 
1916             template <typename SizeT>
1917             inline self_type &operator+=(SizeT n) noexcept
1918             {
1919                 first_ += n;
1920                 rest_ += n;
1921                 return *this;
1922             }
1923 
1924             template <typename SizeT>
1925             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
1926             {
1927                 self_type result(*this);
1928                 result += n;
1929                 return result;
1930             }
1931 
1932             template <typename SizeT>
1933             inline self_type &operator-=(SizeT n) noexcept
1934             {
1935                 first_ -= n;
1936                 rest_ -= n;
1937                 return *this;
1938             }
1939 
1940             template <typename SizeT>
1941             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
1942             {
1943                 return *this + (-static_cast<difference_type>(n));
1944             }
1945 
1946             SEAL_NODISCARD inline difference_type operator-(const self_type &b) const
1947             {
1948                 auto first = first_ - b.first_;
1949 #ifdef SEAL_DEBUG
1950                 auto rest = rest_ - b.rest_;
1951                 if (first != rest)
1952                 {
1953                     throw std::invalid_argument("incompatible iterators");
1954                 }
1955 #endif
1956                 return first;
1957             }
1958 
1959             SEAL_NODISCARD inline bool operator==(const self_type &compare) const noexcept
1960             {
1961                 return (first_ == compare.first_) && (rest_ == compare.rest_);
1962             }
1963 
1964             SEAL_NODISCARD inline bool operator!=(const self_type &compare) const noexcept
1965             {
1966                 return !(*this == compare);
1967             }
1968 
1969             SEAL_NODISCARD inline bool operator<(const self_type &compare) const
1970             {
1971                 auto first = first_ < compare.first_;
1972 #ifdef SEAL_DEBUG
1973                 auto rest = rest_ < compare.rest_;
1974                 if (first != rest)
1975                 {
1976                     throw std::invalid_argument("incompatible iterators");
1977                 }
1978 #endif
1979                 return first;
1980             }
1981 
1982             SEAL_NODISCARD inline bool operator>(const self_type &compare) const
1983             {
1984                 auto first = first_ > compare.first_;
1985 #ifdef SEAL_DEBUG
1986                 auto rest = rest_ > compare.rest_;
1987                 if (first != rest)
1988                 {
1989                     throw std::invalid_argument("incompatible iterators");
1990                 }
1991 #endif
1992                 return first;
1993             }
1994 
1995             SEAL_NODISCARD inline bool operator<=(const self_type &compare) const
1996             {
1997                 auto first = !(first_ > compare.first_);
1998 #ifdef SEAL_DEBUG
1999                 auto rest = !(rest_ > compare.rest_);
2000                 if (first != rest)
2001                 {
2002                     throw std::invalid_argument("incompatible iterators");
2003                 }
2004 #endif
2005                 return first;
2006             }
2007 
2008             SEAL_NODISCARD inline bool operator>=(const self_type &compare) const
2009             {
2010                 auto first = !(first_ < compare.first_);
2011 #ifdef SEAL_DEBUG
2012                 auto rest = !(rest_ < compare.rest_);
2013                 if (first != rest)
2014                 {
2015                     throw std::invalid_argument("incompatible iterators");
2016                 }
2017 #endif
2018                 return first;
2019             }
2020 
2021             SEAL_NODISCARD explicit inline operator bool() const noexcept
2022             {
2023                 return static_cast<bool>(first_) && static_cast<bool>(rest_);
2024             }
2025 
2026             SEAL_NODISCARD inline value_type operator->() const noexcept
2027             {
2028                 return **this;
2029             }
2030 
2031             SEAL_NODISCARD inline const SEALIter &first() const noexcept
2032             {
2033                 return first_;
2034             }
2035 
2036             SEAL_NODISCARD inline const IterTuple<Rest...> &rest() const noexcept
2037             {
2038                 return rest_;
2039             }
2040 
2041         private:
2042             SEALIter first_ = {};
2043 
2044             IterTuple<Rest...> rest_ = {};
2045         };
2046 
2047         template <typename SEALIter>
2048         class IterTuple<SEALIter> : public SEALIterBase
2049         {
2050         public:
2051             static_assert(
2052                 std::is_base_of<SEALIterBase, SEALIter>::value,
2053                 "Template parameter must derive from seal::util::SEALIterBase");
2054 
2055             using self_type = IterTuple<SEALIter>;
2056 
2057             // Standard iterator typedefs
2058             using value_type = std::tuple<typename std::iterator_traits<SEALIter>::value_type>;
2059             using pointer = void;
2060             using reference = const value_type &;
2061             using iterator_category = std::random_access_iterator_tag;
2062             using difference_type = std::ptrdiff_t;
2063 
2064             IterTuple(){};
2065 
2066             IterTuple(SEALIter first) : first_(first)
2067             {}
2068 
2069             template <typename T>
2070             IterTuple(const std::tuple<T> &tp) : IterTuple(std::get<0>(std::forward<decltype(tp)>(tp)))
2071             {}
2072 
2073             template <typename T>
2074             IterTuple(std::tuple<T> &&tp) : IterTuple(std::get<0>(std::forward<decltype(tp)>(tp)))
2075             {}
2076 
2077             IterTuple(const self_type &copy) = default;
2078 
2079             self_type &operator=(const self_type &assign) = default;
2080 
2081             SEAL_NODISCARD inline value_type operator*() const noexcept
2082             {
2083                 return *first_;
2084             }
2085 
2086             template <typename SizeT>
2087             SEAL_NODISCARD inline value_type operator[](SizeT n) const noexcept
2088             {
2089                 self_type result(*this);
2090                 result += static_cast<difference_type>(n);
2091                 return *result;
2092             }
2093 
2094             inline self_type &operator++() noexcept
2095             {
2096                 first_++;
2097                 return *this;
2098             }
2099 
2100             inline self_type operator++(int) noexcept
2101             {
2102                 self_type result(*this);
2103                 first_++;
2104                 return result;
2105             }
2106 
2107             inline self_type &operator--() noexcept
2108             {
2109                 first_--;
2110                 return *this;
2111             }
2112 
2113             inline self_type operator--(int) noexcept
2114             {
2115                 self_type result(*this);
2116                 first_--;
2117                 return result;
2118             }
2119 
2120             template <typename SizeT>
2121             inline self_type &operator+=(SizeT n) noexcept
2122             {
2123                 first_ += n;
2124                 return *this;
2125             }
2126 
2127             template <typename SizeT>
2128             SEAL_NODISCARD inline self_type operator+(SizeT n) const noexcept
2129             {
2130                 self_type result(*this);
2131                 result += n;
2132                 return result;
2133             }
2134 
2135             template <typename SizeT>
2136             inline self_type &operator-=(SizeT n) noexcept
2137             {
2138                 first_ -= n;
2139                 return *this;
2140             }
2141 
2142             template <typename SizeT>
2143             SEAL_NODISCARD inline self_type operator-(SizeT n) const noexcept
2144             {
2145                 return *this + (-static_cast<difference_type>(n));
2146             }
2147 
2148             SEAL_NODISCARD inline difference_type operator-(const self_type &b) const
2149             {
2150                 return first_ - b.first_;
2151             }
2152 
2153             SEAL_NODISCARD inline bool operator==(const self_type &compare) const noexcept
2154             {
2155                 return first_ == compare.first_;
2156             }
2157 
2158             SEAL_NODISCARD inline bool operator!=(const self_type &compare) const noexcept
2159             {
2160                 return !(*this == compare);
2161             }
2162 
2163             SEAL_NODISCARD inline bool operator<(const self_type &compare) const noexcept
2164             {
2165                 return first_ < compare.first_;
2166             }
2167 
2168             SEAL_NODISCARD inline bool operator>(const self_type &compare) const noexcept
2169             {
2170                 return first_ > compare.first_;
2171             }
2172 
2173             SEAL_NODISCARD inline bool operator<=(const self_type &compare) const noexcept
2174             {
2175                 return !(first_ > compare.first_);
2176             }
2177 
2178             SEAL_NODISCARD inline bool operator>=(const self_type &compare) const noexcept
2179             {
2180                 return !(first_ < compare.first_);
2181             }
2182 
2183             SEAL_NODISCARD explicit inline operator bool() const noexcept
2184             {
2185                 return static_cast<bool>(first_);
2186             }
2187 
2188             SEAL_NODISCARD inline reference operator->() const noexcept
2189             {
2190                 return **this;
2191             }
2192 
2193             SEAL_NODISCARD inline const SEALIter &first() const noexcept
2194             {
2195                 return first_;
2196             }
2197 
2198         private:
2199             SEALIter first_ = {};
2200         };
2201 
2202         // Out-of-class operator+ for all SEAL iterators
2203         template <
2204             typename SizeT, typename SEALIter,
2205             typename = std::enable_if_t<std::is_base_of<SEALIterBase, SEALIter>::value>>
2206         SEAL_NODISCARD inline SEALIter operator+(SizeT n, SEALIter it)
2207         {
2208             return it + n;
2209         }
2210 
2211         namespace iterator_internal
2212         {
2213             template <std::size_t N>
2214             struct GetHelperStruct
2215             {
2216                 template <typename SEALIter, typename... Rest>
2217                 SEAL_NODISCARD static auto Apply(const IterTuple<SEALIter, Rest...> &it) noexcept
2218                 {
2219                     return GetHelperStruct<N - 1>::Apply(it.rest());
2220                 }
2221             };
2222 
2223             template <>
2224             struct GetHelperStruct<0>
2225             {
2226                 template <typename SEALIter, typename... Rest>
2227                 SEAL_NODISCARD static auto Apply(const IterTuple<SEALIter, Rest...> &it) noexcept
2228                 {
2229                     return it.first();
2230                 }
2231             };
2232 
2233             template <std::size_t N, typename... SEALIters>
2234             SEAL_NODISCARD inline auto get(const IterTuple<SEALIters...> &it) noexcept
2235             {
2236                 static_assert(N < sizeof...(SEALIters), "seal::util::IterTuple index out of range");
2237                 return iterator_internal::GetHelperStruct<N>::Apply(it);
2238             }
2239 
2240             template <typename T, typename... Rest>
2241             struct IterType<
2242                 seal_void_t<
2243                     std::enable_if_t<(sizeof...(Rest) > 0)>, typename IterType<void, T>::type,
2244                     typename IterType<void, Rest...>::type>,
2245                 T, Rest...>
2246             {
2247                 using type = typename extend_iter_tuple<
2248                     typename IterType<void, T>::type, typename IterType<void, Rest...>::type>::type;
2249             };
2250 
2251             template <typename... Ts>
2252             struct IterType<void, const std::tuple<Ts...> &>
2253             {
2254                 using type = typename IterType<void, Ts...>::type;
2255             };
2256 
2257             template <typename... Ts>
2258             struct IterType<void, std::tuple<Ts...> &>
2259             {
2260                 using type = typename IterType<void, Ts...>::type;
2261             };
2262         } // namespace iterator_internal
2263 
2264         template <
2265             std::size_t N, std::size_t... Rest, typename... SEALIters, typename = std::enable_if_t<sizeof...(Rest)>>
2266         SEAL_NODISCARD inline auto get(const IterTuple<SEALIters...> &it) noexcept
2267         {
2268             return get<Rest...>(iterator_internal::get<N>(it));
2269         }
2270 
2271         template <std::size_t N, typename... SEALIters>
2272         SEAL_NODISCARD inline auto get(const IterTuple<SEALIters...> &it) noexcept
2273         {
2274             return iterator_internal::get<N>(it);
2275         }
2276 
2277         template <std::size_t N, std::size_t... Rest, typename... Ts, typename = std::enable_if_t<sizeof...(Rest)>>
2278         SEAL_NODISCARD inline auto get(const std::tuple<Ts...> &tp) noexcept
2279         {
2280             return get<Rest...>(std::get<N>(tp));
2281         }
2282 
2283         template <typename... Ts>
2284         SEAL_NODISCARD inline auto iter(Ts &&... ts) noexcept -> typename iterator_internal::IterType<void, Ts...>::type
2285         {
2286             return { std::forward<Ts>(ts)... };
2287         }
2288 
2289         template <typename... Ts>
2290         SEAL_NODISCARD inline auto reverse_iter(Ts &&... ts) noexcept
2291             -> ReverseIter<typename iterator_internal::IterType<void, Ts...>::type>
2292         {
2293             return typename iterator_internal::IterType<void, Ts...>::type(std::forward<Ts>(ts)...);
2294         }
2295     } // namespace util
2296 } // namespace seal
2297