GCC Code Coverage Report


Directory: src/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 87.3% 764 / 0 / 875
Functions: 99.3% 138 / 0 / 139
Branches: 66.4% 568 / 34 / 890

fft/series_core.hpp
Line Branch Exec Source
1 #pragma once
2
3 #include <algorithm>
4 #include <cassert>
5 #include <concepts>
6 #include <cstddef>
7 #include <functional>
8 #include <optional>
9 #include <span>
10 #include <utility>
11 #include <vector>
12
13 #include "fft/multiply.hpp"
14
15 // ==== value types ====
16
17 namespace ecnerwala::series {
18
19 // A series is either exact (a finite series, R[x] sitting inside R[[x]]: the
20 // length is just the support bound) or trunc (a known prefix of an infinite
21 // series: the length is the precision, and products truncate to it).
22
23 // Non-owning view of power series coefficients: the span pattern (contiguous
24 // window + series semantics), borrowed from an owning series-like type.
25 template <fft::engine E, bool exact_>
26 struct span {
27 using T = typename E::value_type;
28 using engine_t = E;
29 static constexpr bool exact_v = exact_;
30
31 span() = default;
32 10549081 explicit span(std::span<const T> s_) : s(s_) {}
33 // exact -> trunc is implicit, trunc -> exact is explicit
34 template <bool oe> requires (oe != exact_)
35 explicit(oe < exact_) span(span<E, oe> o) : s(o.coeffs()) {}
36
37
9/12
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 60 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 59 times.
✓ Branch 8 → 9 taken 55 times.
✓ Branch 8 → 10 taken 4 times.
✓ Branch 12 → 13 taken 3971680 times.
✓ Branch 12 → 15 taken 2 times.
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 17 times.
✓ Branch 61 → 12 taken 578 times.
✓ Branch 61 → 62 taken 43 times.
6628756 int len() const { return sz(s); }
38
4/6
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 42 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 60 times.
✓ Branch 13 → 14 taken 3971648 times.
✓ Branch 13 → 15 taken 32 times.
3971946 const T& operator[](int i) const { return s[size_t(i)]; }
39
3/3
✓ Branch 12 → 13 taken 2245 times.
✓ Branch 17 → 18 taken 59 times.
✓ Branch 27 → 28 taken 578 times.
5330 auto begin() const { return s.begin(); }
40
2/2
✓ Branch 12 → 13 taken 1667 times.
✓ Branch 17 → 18 taken 59 times.
3856 auto end() const { return s.end(); }
41 // engine primitives borrow through std::span's range constructor
42
1/1
✓ Branch 32 → 33 taken 6 times.
4625307 std::span<const T> coeffs() const { return s; }
43 // the first n coefficients; requires n <= len().
44 // Widening past len() is explicit: see with_len.
45 5426 span first(int n) const {
46 6302 assert(n <= len());
47 5426 return span(s.first(size_t(n)));
48 }
49
50 private:
51 std::span<const T> s;
52 };
53
54 // `vec` represents both exact (finite) power series (R[x]) and prefixes of infinite power series (R[[x]]), depending on the flag.
55 // `exact` and `trunc` are aliases.
56 //
57 // Operators here are typically permissive: they will accept combinations of unequal types and lengths.
58 template <fft::engine E, bool exact_>
59
21/21
✓ Branch 11 → 16 taken 9 times.
✓ Branch 14 → 15 taken 20 times.
✓ Branch 15 → 16 taken 6 times.
✓ Branch 15 → 17 taken 578 times.
✓ Branch 25 → 26 taken 578 times.
✓ Branch 28 → 29 taken 10 times.
✓ Branch 31 → 32 taken 578 times.
✓ Branch 43 → 44 taken 1 time.
✓ Branch 48 → 59 taken 6 times.
✓ Branch 145 → 146 taken 5 times.
✓ Branch 151 → 152 taken 1 time.
✓ Branch 171 → 172 taken 1 time.
✓ Branch 306 → 307 taken 1 time.
✓ Branch 349 → 350 taken 1 time.
✓ Branch 393 → 394 taken 1 time.
✓ Branch 407 → 408 taken 1 time.
✓ Branch 507 → 508 taken 1 time.
✓ Branch 621 → 622 taken 1 time.
✓ Branch 733 → 734 taken 1 time.
✓ Branch 1041 → 1042 taken 1 time.
✓ Branch 1073 → 1074 taken 1 time.
1979598 struct vec : public std::vector<typename E::value_type> {
60 using T = typename E::value_type;
61 using engine_t = E;
62 static constexpr bool exact_v = exact_;
63
53/58
✓ Branch 9 → 10 taken 59 times.
✓ Branch 10 → 11 taken 220 times.
✓ Branch 11 → 12 taken 14 times.
✓ Branch 12 → 13 taken 791 times.
✓ Branch 16 → 17 taken 115 times.
✓ Branch 17 → 18 taken 61 times.
✓ Branch 18 → 19 taken 34 times.
✓ Branch 20 → 21 taken 24 times.
✓ Branch 21 → 22 taken 59 times.
✓ Branch 27 → 28 taken 578 times.
✓ Branch 31 → 32 taken 11 times.
✓ Branch 33 → 34 taken 37 times.
✓ Branch 34 → 35 taken 11 times.
✓ Branch 40 → 57 taken 20 times.
✓ Branch 41 → 42 taken 537 times.
✗ Branch 42 → 59 not taken.
✗ Branch 48 → 53 not taken.
✗ Branch 48 → 55 not taken.
✗ Branch 48 → 65 not taken.
✓ Branch 52 → 53 taken 22 times.
✗ Branch 54 → 59 not taken.
✓ Branch 55 → 56 taken 59 times.
✓ Branch 56 → 59 taken 80 times.
✓ Branch 58 → 59 taken 6 times.
✓ Branch 58 → 61 taken 207 times.
✓ Branch 64 → 67 taken 10 times.
✓ Branch 66 → 67 taken 17 times.
✓ Branch 72 → 73 taken 2 times.
✓ Branch 73 → 74 taken 6 times.
✓ Branch 93 → 94 taken 1 time.
✓ Branch 103 → 104 taken 34 times.
✓ Branch 103 → 119 taken 6 times.
✓ Branch 106 → 107 taken 20 times.
✓ Branch 109 → 110 taken 20 times.
✓ Branch 116 → 117 taken 1 time.
✓ Branch 131 → 132 taken 6 times.
✓ Branch 136 → 137 taken 20 times.
✓ Branch 139 → 140 taken 20 times.
✓ Branch 158 → 159 taken 1 time.
✓ Branch 173 → 174 taken 6 times.
✓ Branch 185 → 186 taken 6 times.
✓ Branch 222 → 223 taken 34 times.
✓ Branch 228 → 229 taken 1 time.
✓ Branch 260 → 261 taken 1 time.
✓ Branch 266 → 267 taken 1 time.
✓ Branch 305 → 306 taken 1 time.
✓ Branch 354 → 355 taken 25 times.
✓ Branch 374 → 375 taken 1 time.
✓ Branch 380 → 381 taken 1 time.
✓ Branch 392 → 393 taken 6 times.
✓ Branch 402 → 403 taken 1 time.
✓ Branch 454 → 455 taken 1 time.
✓ Branch 608 → 609 taken 1 time.
✓ Branch 797 → 798 taken 1 time.
✓ Branch 880 → 881 taken 7 times.
✓ Branch 905 → 906 taken 1 time.
✓ Branch 1007 → 1008 taken 1 time.
✓ Branch 1078 → 1079 taken 1 time.
1975622 using std::vector<T>::vector;
64
65 // a free const borrow of the coefficients: implicit
66
2/4
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1974 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 59 times.
10536881 operator span<E, exact_>() const {
67
2/4
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 198 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 59 times.
10538079 return span<E, exact_>(std::span<const T>(*this));
68 }
69
70 // exact -> trunc is implicit, trunc -> exact is explicit
71 template <bool oe> requires (oe != exact_)
72
2/2
✓ Branch 726 → 727 taken 1 time.
✓ Branch 819 → 820 taken 1 time.
2 explicit(oe < exact_) vec(const vec<E, oe>& p) : std::vector<T>(p) {}
73 template <bool oe> requires (oe != exact_)
74 153 explicit(oe < exact_) vec(vec<E, oe>&& p) : std::vector<T>(std::move(p)) {}
75
76 // adopt a plain coefficient vector
77 2652532 explicit vec(std::vector<T> v) : std::vector<T>(std::move(v)) {}
78 // materialize an owned copy of any borrowed series, of either exactness
79
1/1
✓ Branch 13 → 14 taken 6 times.
29 explicit vec(span<E, exact_> s) : std::vector<T>(s.begin(), s.end()) {}
80
1/1
✓ Branch 13 → 14 taken 12 times.
56 explicit vec(span<E, !exact_> s) : std::vector<T>(s.begin(), s.end()) {}
81
82
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1667 times.
1831 span<E, exact_> first(int n) const { return span<E, exact_>(*this).first(n); }
83
84
2/4
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2651880 times.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 537 times.
13454280 int len() const {
85
43/61
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 31 times.
✓ Branch 4 → 5 taken 20 times.
✓ Branch 4 → 6 taken 11 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 20 times.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 40 times.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 12812345 times.
✓ Branch 9 → 10 taken 200 times.
✓ Branch 9 → 11 taken 40 times.
✗ Branch 9 → 22 not taken.
✓ Branch 10 → 11 taken 14 times.
✓ Branch 10 → 12 taken 46415355 times.
✗ Branch 10 → 38 not taken.
✓ Branch 12 → 6 taken 46413579 times.
✓ Branch 12 → 13 taken 2357 times.
✓ Branch 12 → 14 taken 22956811 times.
✗ Branch 12 → 37 not taken.
✗ Branch 12 → 68 not taken.
✗ Branch 12 → 78 not taken.
✓ Branch 14 → 8 taken 10144438 times.
✓ Branch 14 → 15 taken 42 times.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 644060 times.
✓ Branch 22 → 23 taken 644061 times.
✓ Branch 22 → 44 taken 1 time.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 260 not taken.
✓ Branch 31 → 32 taken 20 times.
✓ Branch 31 → 37 taken 80 times.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 17 times.
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 595 times.
✓ Branch 36 → 19 taken 938 times.
✓ Branch 36 → 37 taken 616 times.
✓ Branch 36 → 56 taken 41 times.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 537 times.
✓ Branch 38 → 39 taken 20 times.
✓ Branch 38 → 40 taken 100 times.
✓ Branch 38 → 44 taken 80 times.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 537 times.
✓ Branch 41 → 23 taken 249 times.
✓ Branch 41 → 42 taken 571 times.
✓ Branch 47 → 48 taken 6 times.
✓ Branch 55 → 35 taken 60 times.
✓ Branch 55 → 56 taken 2 times.
✓ Branch 56 → 38 taken 249 times.
✓ Branch 56 → 57 taken 11 times.
✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 621 times.
✓ Branch 60 → 32 taken 88 times.
✓ Branch 60 → 61 taken 3 times.
✓ Branch 61 → 12 taken 578 times.
✓ Branch 61 → 62 taken 43 times.
✗ Branch 336 → 337 not taken.
✓ Branch 336 → 338 taken 25 times.
82839431 return int(this->size());
86 }
87 int degree() const requires (exact_) {
88 return len() - 1;
89 }
90 void extend(int sz) {
91 assert(sz >= len());
92 this->resize(sz);
93 }
94
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 9 times.
15 void shrink(int sz) {
95 21 assert(sz <= len());
96 15 this->resize(sz);
97 15 }
98 // multiply by x^n within the fixed precision window
99 void shift_trunc(int n = 1) requires (!exact_) {
100 assert(n >= 0 && n <= len());
101 std::rotate(this->begin(), this->end()-n, this->end());
102 std::fill(this->begin(), this->begin()+n, T(0));
103 }
104 // divide by x^n and 0-pad within the fixed precision window
105 void unshift_trunc(int n = 1) requires (!exact_) {
106 assert(n >= 0 && n <= len());
107 std::fill(this->begin(), this->begin()+n, T(0));
108 std::rotate(this->begin(), this->begin()+n, this->end());
109 }
110
111 // in-place forms require that the result's exactness/length must equal this operand's
112 template <bool oe> requires (exact_ <= oe)
113 vec& operator += (const vec<E, oe>& o) {
114 if constexpr (exact_) { if (o.len() > len()) this->resize(o.len()); }
115 else if constexpr (!oe) { if (o.len() < len()) this->resize(o.len()); }
116 for (int i = 0; i < std::min(len(), o.len()); i++) {
117 (*this)[i] += o[i];
118 }
119 return *this;
120 }
121 template <bool oe> requires (exact_ <= oe)
122
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 578 times.
612 vec& operator -= (const vec<E, oe>& o) {
123 if constexpr (exact_) { if (o.len() > len()) this->resize(o.len()); }
124
3/6
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 578 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 12 taken 578 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 39 taken 34 times.
680 else if constexpr (!oe) { if (o.len() < len()) this->resize(o.len()); }
125
6/8
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 10645015 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 10645015 times.
✓ Branch 17 → 8 taken 10644437 times.
✓ Branch 17 → 18 taken 578 times.
✓ Branch 63 → 27 taken 249 times.
✓ Branch 63 → 64 taken 34 times.
10645864 for (int i = 0; i < std::min(len(), o.len()); i++) {
126
2/2
✓ Branch 8 → 9 taken 4673577 times.
✓ Branch 8 → 10 taken 5970860 times.
21289123 (*this)[i] -= o[i];
127 }
128 612 return *this;
129 }
130
131 69 vec& operator *= (const T& n) {
132
8/8
None:
✓ Branch 11 → 10 taken 4745532 times.
✓ Branch 11 → 12 taken 17 times.
✓ Branch 23 → 22 taken 4745532 times.
✓ Branch 23 → 24 taken 17 times.
✓ Branch 30 → 29 taken 4745532 times.
✓ Branch 30 → 31 taken 17 times.
ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::operator*=(modnum<998244353> const&):
✓ Branch 22 → 9 taken 411 times.
✓ Branch 22 → 23 taken 18 times.
14237112 for (auto& v : *this) v *= n;
133 18 return *this;
134 }
135 friend vec operator * (const vec& a, const T& n) {
136 vec r(a.size());
137 for (int i = 0; i < a.len(); i++) {
138 r[i] = a[i] * n;
139 }
140 return r;
141 }
142 friend vec operator * (const T& n, const vec& a) {
143 vec r(a.size());
144 for (int i = 0; i < a.len(); i++) {
145 r[i] = n * a[i];
146 }
147 return r;
148 }
149
150 1373 vec& operator *= (const vec& o) {
151
1/1
✓ Branch 4 → 5 taken 158 times.
2904 return *this = (*this) * o;
152 }
153
154 };
155
156 template <fft::engine E> using exact = vec<E, true>;
157 template <fft::engine E> using trunc = vec<E, false>;
158
159 // Series-like concepts: the binary operators below are written once as constrained
160 // templates and dispatch on which memoized transforms an operand carries.
161 // A series-like type exposes its engine/exactness and its coefficients as a
162 // span borrow of exactly len() coefficients; cached wrappers additionally
163 // expose their transform caches (filling them is logically const).
164 template <typename S>
165 concept like = fft::engine<typename S::engine_t> && requires(const S& s, int i) {
166 { S::exact_v } -> std::convertible_to<bool>;
167 { s.len() } -> std::same_as<int>;
168 { s[i] } -> std::convertible_to<const typename S::engine_t::value_type&>;
169 // borrows straight into the engine primitives
170 { std::span<const typename S::engine_t::value_type>(s) };
171 // and into the series layer's own span, keeping the exactness tag
172 requires std::convertible_to<const S&, span<typename S::engine_t, S::exact_v>>;
173 // first(n): the first n coefficients, borrowed; requires n <= len().
174 // The result is itself like (concepts can't self-reference).
175 // Cached types keep a cache in the result only when it still serves the whole borrow.
176 { s.first(i) } -> std::convertible_to<span<typename S::engine_t, S::exact_v>>;
177 };
178 template <typename S>
179 concept exact_like = like<S> && S::exact_v;
180 template <typename S>
181 concept trunc_like = like<S> && !S::exact_v;
182
183 // carries one extendable transform of the whole coefficient sequence
184 template <typename S>
185 concept has_cache = like<S> && requires(const S& s) {
186 { s.cache() } -> std::same_as<fft::transformed<typename S::engine_t>&>;
187 };
188
189 template <fft::engine E, bool exact_>
190 struct maybe_cached;
191
192 // A borrowed series paired with the transform serving it: the
193 // normalized operand form fed to the cached fft:: entry points. Models has_cache.
194 template <fft::engine E, bool exact_>
195 struct cached_span {
196 using engine_t = E;
197 static constexpr bool exact_v = exact_;
198
199 span<E, exact_> s;
200 std::reference_wrapper<fft::transformed<E>> f;
201
202 2654741 cached_span(span<E, exact_> s_, fft::transformed<E>& f_) : s(s_), f(f_) {}
203 // exact -> trunc is implicit, trunc -> exact is explicit
204 template <bool oe> requires (oe != exact_)
205 explicit(oe < exact_) cached_span(cached_span<E, oe> o) : s(span<E, exact_>(o.s)), f(o.f) {}
206
207
3/6
✓ Branch 2 → 3 taken 20 times.
✗ Branch 2 → 4 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 20 times.
✓ Branch 10 → 11 taken 100 times.
✗ Branch 10 → 29 not taken.
740 int len() const { return s.len(); }
208
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 20 times.
220 const typename E::value_type& operator[](int i) const { return s[i]; }
209
1/1
✓ Branch 14 → 15 taken 20 times.
4625281 operator std::span<const typename E::value_type>() const { return s.coeffs(); }
210 2655820 operator span<E, exact_>() const { return s; }
211 maybe_cached<E, exact_> first(int n) const;
212 7283913 fft::transformed<E>& cache() const { return f; }
213 };
214
215 // carries a whole-sequence cache only sometimes, decided at runtime
216 template <typename S>
217 concept has_cache_opt = like<S> && requires(const S& s) {
218 { s.cache_opt() } -> std::same_as<std::optional<std::reference_wrapper<fft::transformed<typename S::engine_t>>>>;
219 };
220
221 namespace detail {
222 // the operand's whole cache, if it carries one
223 template <like S>
224 13192720 std::optional<std::reference_wrapper<fft::transformed<typename S::engine_t>>> cache_of(const S& s) {
225 13193180 if constexpr (has_cache<S>) return s.cache();
226 20 else if constexpr (has_cache_opt<S>) return s.cache_opt();
227 3862 else return std::nullopt;
228 }
229 /* namespace detail */ }
230
231 // A borrowed series which may carry the transform serving it: the runtime
232 // counterpart of cached_span in the borrow hierarchy
233 // prefix_cached/cached -> maybe_cached/cached_span -> span.
234 template <fft::engine E, bool exact_>
235 struct maybe_cached {
236 using T = typename E::value_type;
237 using engine_t = E;
238 static constexpr bool exact_v = exact_;
239
240 span<E, exact_> s;
241 std::optional<std::reference_wrapper<fft::transformed<E>>> f;
242
243 explicit maybe_cached(span<E, exact_> s_) : s(s_) {}
244 23 maybe_cached(span<E, exact_> s_, fft::transformed<E>& f_) : s(s_), f(f_) {}
245 maybe_cached(cached_span<E, exact_> c) : s(c.s), f(c.f) {}
246 // borrow any like operand whole, taking along whatever cache it carries
247 template <like S> requires std::same_as<typename S::engine_t, E> && (S::exact_v == exact_)
248 maybe_cached(const S& o) : s(o), f(detail::cache_of(o)) {}
249
250 6 int len() const { return s.len(); }
251 6 const T& operator[](int i) const { return s[i]; }
252 operator std::span<const T>() const { return s.coeffs(); }
253 20 operator span<E, exact_>() const { return s; }
254 maybe_cached first(int n) const {
255 return n == len() ? *this : maybe_cached(s.first(n));
256 }
257 20 std::optional<std::reference_wrapper<fft::transformed<E>>> cache_opt() const { return f; }
258 };
259
260 template <fft::engine E, bool exact_>
261 maybe_cached<E, exact_> cached_span<E, exact_>::first(int n) const {
262 return maybe_cached<E, exact_>(*this).first(n);
263 }
264
265 // An owned series copy at an adjusted logical length: the result of with_len.
266 // Carries a reference to a source cache when it still serves the copied
267 // coefficients (a zero tail doesn't change the transform), so the source
268 // must outlive the result.
269 template <fft::engine E>
270 struct resized {
271 using T = typename E::value_type;
272 using engine_t = E;
273 static constexpr bool exact_v = false;
274
275 trunc<E> s;
276 std::optional<std::reference_wrapper<fft::transformed<E>>> f;
277
278 int len() const { return s.len(); }
279 const T& operator[](int i) const { return s[size_t(i)]; }
280 operator std::span<const T>() const { return std::span<const T>(s); }
281 operator span<E, false>() const { return s; }
282 maybe_cached<E, false> first(int n) const {
283 span<E, false> v = s;
284 if (n == len() && f) return {v, f->get()};
285 return maybe_cached<E, false>(v.first(n));
286 }
287 std::optional<std::reference_wrapper<fft::transformed<E>>> cache_opt() const { return f; }
288 };
289
290 // copy any operand to logical length n: extending zero-fills, shrinking truncates
291 template <like S>
292 resized<typename S::engine_t> with_len(const S& s, int n) {
293 using E = typename S::engine_t;
294 using T = typename E::value_type;
295 auto p = s.first(std::min(n, s.len()));
296 resized<E> r;
297 r.s.assign(size_t(n), T{});
298 std::span<const T> pc(p);
299 std::copy(pc.begin(), pc.end(), r.s.begin());
300 r.f = detail::cache_of(p);
301 return r;
302 }
303
304 // carries memoized transforms of power-of-two prefixes (see prefix_cached):
305 // product operands truncate to a covered scale to reuse them.
306 // Trunc-only: an exact operand participates whole, so has_cache covers it.
307 template <typename S>
308 concept has_prefix_cache = like<S> && !S::exact_v && requires(const S& s, int n) {
309 { s.prefix_cache(n) } -> std::same_as<fft::transformed<typename S::engine_t>&>;
310 };
311
312 // Wrapper around vec which caches the transform of the whole series.
313 // Ops exploit the cache whenever the whole span participates; a trunc series'
314 // whole-sequence transform is still useful for middle products and repeated
315 // full-precision use.
316 template <fft::engine E, bool exact_>
317
2/2
✓ Branch 13 → 14 taken 1325940 times.
✓ Branch 17 → 18 taken 11 times.
19131946 struct cached {
318 using T = typename E::value_type;
319 using engine_t = E;
320 static constexpr bool exact_v = exact_;
321
322 6593380 cached() = default;
323 // moving coefficients in or out is free: implicit on rvalues, explicit copy otherwise
324 4624051 cached(vec<E, exact_>&& s_) : s(std::move(s_)) {}
325 43 explicit cached(const vec<E, exact_>& s_) : s(s_) {}
326 182 operator vec<E, exact_>() && { return std::move(s); }
327
328
22/33
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2651900 times.
✓ Branch 4 → 5 taken 2651880 times.
✓ Branch 4 → 6 taken 31 times.
✗ Branch 4 → 8 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2651891 times.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 11 times.
✓ Branch 7 → 8 taken 8 times.
✓ Branch 7 → 9 taken 2651880 times.
✓ Branch 7 → 10 taken 3 times.
✓ Branch 8 → 9 taken 8 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 11 times.
✓ Branch 11 → 12 taken 11 times.
✓ Branch 15 → 16 taken 260 times.
✗ Branch 15 → 43 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 7 times.
✓ Branch 24 → 25 taken 6 times.
✗ Branch 24 → 91 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 3 times.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 1 time.
✓ Branch 30 → 31 taken 1 time.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 1 time.
✓ Branch 42 → 43 taken 7 times.
✓ Branch 42 → 44 taken 260 times.
✓ Branch 66 → 35 taken 39 times.
✓ Branch 66 → 67 taken 1 time.
2654584 int len() const { return s.len(); }
329 // unwrap to the owned coefficients
330 const vec<E, exact_>& uncached() const { return s; }
331 1327576 const T& operator[](int i) const { return s[size_t(i)]; }
332 8 auto begin() const { return s.cbegin(); }
333 6 auto end() const { return s.cend(); }
334
5/7
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 11 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 11 times.
✓ Branch 7 → 8 taken 11 times.
✓ Branch 7 → 9 taken 9 times.
✓ Branch 9 → 10 taken 9 times.
1119 operator span<E, exact_>() const { return s; }
335 maybe_cached<E, exact_> first(int n) const {
336 return n == len() ? maybe_cached<E, exact_>(s, f) : maybe_cached<E, exact_>(s.first(n));
337 }
338 // the transform of the coefficients, fed to the cached fft:: entry points alongside them
339 10533209 fft::transformed<E>& cache() const { return f; }
340
341 template <like S>
342 10 friend bool operator==(const cached& a, const S& b) {
343 10 span<E, S::exact_v> bs = b;
344
10/18
bool ecnerwala::series::operator==<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 19 → 20 taken 3 times.
✗ Branch 19 → 41 not taken.
✓ Branch 30 → 31 taken 3 times.
✓ Branch 41 → 42 taken 3 times.
✗ Branch 41 → 43 not taken.
✓ Branch 43 → 44 taken 3 times.
✗ Branch 43 → 54 not taken.
✓ Branch 54 → 55 taken 3 times.
✗ Branch 54 → 56 not taken.
bool ecnerwala::series::operator==<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 22 → 23 taken 7 times.
✗ Branch 22 → 44 not taken.
✓ Branch 33 → 34 taken 7 times.
✓ Branch 44 → 45 taken 7 times.
✗ Branch 44 → 46 not taken.
✓ Branch 46 → 47 taken 7 times.
✗ Branch 46 → 57 not taken.
✓ Branch 57 → 58 taken 7 times.
✗ Branch 57 → 59 not taken.
70 return a.len() == bs.len() && std::equal(a.s.begin(), a.s.end(), bs.begin());
345 }
346
347 private:
348 vec<E, exact_> s;
349 mutable fft::transformed<E> f; // memoized transform: filling it is logically const
350 };
351
352 template <fft::engine E> using cached_exact = cached<E, true>;
353 template <fft::engine E> using cached_trunc = cached<E, false>;
354
355 namespace detail {
356 // Normalize a whole-span operand to a cached_span: the coefficients borrowed
357 // together with the cache serving them (the operand's own, or tmp otherwise).
358 // The whole-span multiply/square/middle_product paths run entirely on this form.
359 template <like S>
360 10536640 cached_span<typename S::engine_t, S::exact_v> as_cached_span(const S& s, fft::transformed<typename S::engine_t>& tmp) {
361
2/4
ecnerwala::series::cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 20 times.
ecnerwala::series::cached_span<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 7879964 times.
10536640 auto co = cache_of(s);
362
21/42
ecnerwala::series::cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t::transformed&):
✓ Branch 13 → 14 taken 20 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✓ Branch 2 → 3 taken 96 times.
✗ Branch 2 → 4 not taken.
✓ Branch 13 → 14 taken 20 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t::transformed&):
✓ Branch 13 → 14 taken 20 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::engine_t, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::engine_t::transformed&):
✓ Branch 13 → 14 taken 20 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✓ Branch 2 → 3 taken 2563 times.
✗ Branch 2 → 4 not taken.
✓ Branch 13 → 14 taken 461 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✓ Branch 2 → 3 taken 2652017 times.
✗ Branch 2 → 4 not taken.
✓ Branch 13 → 14 taken 367 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t::transformed&):
✓ Branch 13 → 14 taken 20 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✓ Branch 2 → 3 taken 96 times.
✗ Branch 2 → 4 not taken.
✓ Branch 13 → 14 taken 20 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::maybe_cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::maybe_cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::maybe_cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::maybe_cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::maybe_cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✓ Branch 13 → 14 taken 20 times.
✗ Branch 13 → 18 not taken.
ecnerwala::series::cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 25 times.
ecnerwala::series::cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::engine_t::transformed&):
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 25 times.
ecnerwala::series::cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 24 times.
ecnerwala::series::cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 20 times.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 30 times.
ecnerwala::series::cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 17 taken 25 times.
ecnerwala::series::cached_span<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::detail::as_cached_span<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✓ Branch 4 → 5 taken 7879964 times.
✗ Branch 4 → 6 not taken.
✓ Branch 15 → 16 taken 787 times.
✗ Branch 15 → 20 not taken.
10540279 return {s, co ? co->get() : tmp};
363 }
364 /* namespace detail */ }
365
366 // Newton inversion: 1/a mod x^a.len(). Generic over any engine; per doubling step
367 // n -> m = 2n this is 5 transforms of size m, reusing b's transform for both circular
368 // products; in each product the wraparound only contaminates coefficients [0, n)
369 // which are already known.
370 //
371 // This is correct for non-commutative rings.
372 // TODO: reuse/populate the operand's whole/prefix transform caches
373 template <trunc_like S>
374
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 146 times.
180 trunc<typename S::engine_t> ps_inv(const S& a) {
375 using E = typename S::engine_t;
376 using T = typename E::value_type;
377 180 int N = a.len();
378
1/2
✓ Branch 5 → 6 taken 146 times.
✗ Branch 5 → 46 not taken.
248 trunc<E> r(size_t(N), T{});
379
5/10
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 238 not taken.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 318 not taken.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 5 → 6 taken 146 times.
✗ Branch 5 → 46 not taken.
✓ Branch 21 → 22 taken 31 times.
✗ Branch 21 → 318 not taken.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 286 not taken.
180 if (N == 0) return r;
380
2/2
✓ Branch 6 → 7 taken 137 times.
✓ Branch 6 → 8 taken 9 times.
180 int s = nextPow2(N);
381
5/5
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 30 → 31 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 30 → 31 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 8 → 9 taken 146 times.
✓ Branch 30 → 31 taken 31 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 30 → 31 taken 1 time.
214 std::vector<T> b(size_t(s), T{});
382 214 b[0] = inv(a[0]);
383
10/10
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 159 → 50 taken 9 times.
✓ Branch 159 → 160 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 239 → 50 taken 9 times.
✓ Branch 239 → 240 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 42 → 11 taken 1667 times.
✓ Branch 42 → 43 taken 146 times.
✓ Branch 239 → 50 taken 106 times.
✓ Branch 239 → 240 taken 31 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 207 → 50 taken 9 times.
✓ Branch 207 → 208 taken 1 time.
2485 for (int n = 1; n < N; n *= 2) {
384 1800 int m = 2 * n;
385
5/5
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 61 → 62 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 61 → 62 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 12 → 13 taken 1667 times.
✓ Branch 61 → 62 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 61 → 62 taken 9 times.
1800 auto ta = E::transform(a.first(std::min(N, m)), m);
386
6/7
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 69 → 70 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 69 → 70 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 1667 times.
✓ Branch 15 → 16 taken 1667 times.
✓ Branch 69 → 70 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 69 → 70 taken 9 times.
1800 auto tb = E::transform(std::span<const T>(b).first(n), m);
387 // e = a*b mod x^m; only e[n..m) is needed (and is wraparound-free).
388
6/7
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 72 → 73 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 72 → 73 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 16 → 17 taken 1667 times.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 1667 times.
✓ Branch 72 → 73 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 72 → 73 taken 9 times.
1800 auto e = fft::buffer_pool<T>::get(m);
389
10/10
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 79 → 80 taken 9 times.
✓ Branch 81 → 82 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 79 → 80 taken 9 times.
✓ Branch 81 → 82 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 19 → 20 taken 1667 times.
✓ Branch 20 → 21 taken 1667 times.
✓ Branch 79 → 80 taken 106 times.
✓ Branch 81 → 82 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 79 → 80 taken 9 times.
✓ Branch 81 → 82 taken 9 times.
1800 E::finish(E::mul(ta, tb, m), e.span());
390
10/10
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 99 → 85 taken 511 times.
✓ Branch 99 → 100 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 115 → 101 taken 511 times.
✓ Branch 115 → 116 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 24 → 23 taken 22233283 times.
✓ Branch 24 → 25 taken 1667 times.
✓ Branch 115 → 101 taken 1309 times.
✓ Branch 115 → 116 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 99 → 85 taken 511 times.
✓ Branch 99 → 100 taken 9 times.
22240767 for (int i = 0; i < n; i++) e[i] = T{};
391
5/5
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 110 → 111 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 126 → 127 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 25 → 26 taken 1667 times.
✓ Branch 126 → 127 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 110 → 111 taken 9 times.
1933 auto te = E::transform(std::span<const T>(e.span()), m);
392
6/7
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 114 → 115 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 130 → 131 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 26 → 27 taken 1667 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 1667 times.
✓ Branch 130 → 131 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 114 → 115 taken 9 times.
1800 auto c = fft::buffer_pool<T>::get(m);
393 // b' = 2b - b*(a*b): keep b on the left of e = a*b
394
10/10
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 121 → 122 taken 9 times.
✓ Branch 123 → 124 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 137 → 138 taken 9 times.
✓ Branch 139 → 140 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 29 → 30 taken 1667 times.
✓ Branch 30 → 31 taken 1667 times.
✓ Branch 137 → 138 taken 106 times.
✓ Branch 139 → 140 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 121 → 122 taken 9 times.
✓ Branch 123 → 124 taken 9 times.
1800 E::finish(E::mul(tb, te, m), c.span());
395
10/10
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 147 → 127 taken 297 times.
✓ Branch 147 → 148 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 179 → 159 taken 297 times.
✓ Branch 179 → 180 taken 9 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 35 → 33 taken 18840532 times.
✓ Branch 35 → 36 taken 1667 times.
✓ Branch 179 → 159 taken 848 times.
✓ Branch 179 → 180 taken 106 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 147 → 127 taken 297 times.
✓ Branch 147 → 148 taken 9 times.
18845810 for (int i = n; i < std::min(m, N); i++) b[i] = -c[i];
396 }
397
4/4
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&):
✓ Branch 171 → 172 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&):
✓ Branch 251 → 252 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 251 → 252 taken 31 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t, false> ecnerwala::series::ps_inv<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&):
✓ Branch 219 → 220 taken 1 time.
350 std::copy(b.begin(), b.begin() + N, r.begin());
398 180 return r;
399 180 }
400 // TODO: operator / can be done slightly faster than ps_inv:
401 // we only need the n/2 terms of ps_inv(), and can do the last Newton step directly on the quotient
402
403 // Both consume whole-sequence transforms by nature (the full span always
404 // participates), so only whole caches apply, never prefix caches.
405 template <like A>
406 30 auto square(const A& a) {
407 using E = typename A::engine_t;
408 using T = typename E::value_type;
409 30 fft::transformed<E> ta_;
410 30 auto av = detail::as_cached_span(a, ta_);
411 if constexpr (A::exact_v) {
412 // like operator*, an exact square returns has_cache, adopting the
413 // pointwise product as the result's transform when the engine supports it
414 6 std::vector<T> coeffs;
415 6 fft::transformed<E> f;
416
2/2
auto ecnerwala::series::square<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 16 → 17 taken 4 times.
auto ecnerwala::series::square<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 16 → 17 taken 2 times.
6 fft::square_cached<E>(av, av.cache(), coeffs, f);
417 30 cached<E, true> w(exact<E>(std::move(coeffs)));
418 12 w.cache() = std::move(f);
419 6 return w;
420 12 } else {
421 96 trunc<E> r(size_t(a.len()), T{});
422
1/1
✓ Branch 34 → 35 taken 24 times.
48 fft::square<E>(av, av.cache(), std::span<T>(r));
423 24 return r;
424 }
425 30 }
426
427 // a*b + c*d, all exact; returns has_cache, adopting the summed pointwise
428 // product as the result's transform when the engine supports it. Reuses each
429 // operand's whole cache. Requires a*b and c*d to have equal length.
430 template <like A, like B, like C, like D>
431 requires fft::same_engine<A, B> && fft::same_engine<A, C> && fft::same_engine<A, D>
432 && A::exact_v && B::exact_v && C::exact_v && D::exact_v
433 644116 cached<typename A::engine_t, true> multiply_add2(
434 const A& a, const B& b, const C& c, const D& d) {
435 using E = typename A::engine_t;
436 using T = typename E::value_type;
437 644116 fft::transformed<E> ta_, tb_, tc_, td_;
438 644116 auto av = detail::as_cached_span(a, ta_), bv = detail::as_cached_span(b, tb_);
439 644116 auto cv = detail::as_cached_span(c, tc_), dv = detail::as_cached_span(d, td_);
440 644116 std::vector<T> coeffs;
441
1/1
✓ Branch 2 → 3 taken 644051 times.
644116 fft::transformed<E> f;
442
2/2
✓ Branch 2 → 3 taken 644051 times.
✓ Branch 52 → 53 taken 65 times.
644311 fft::multiply_add2_cached<E>(
443 av, av.cache(),
444 bv, bv.cache(),
445 cv, cv.cache(),
446 dv, dv.cache(),
447 coeffs, f
448 );
449 644376 cached<E, true> w(exact<E>(std::move(coeffs)));
450 644116 w.cache() = std::move(f);
451 644181 return w;
452 644441 }
453
454 // coefficients [b.len()-1, a.len()) of a*b; requires a.len() >= b.len() > 0.
455 // The kernel b participates whole, so it must be exact; the result mirrors a's kind.
456 template <like A, exact_like B> requires fft::same_engine<A, B>
457 2652143 vec<typename A::engine_t, A::exact_v> middle_product(const A& a, const B& b) {
458 using E = typename A::engine_t;
459 2652143 fft::transformed<E> ta_, tb_;
460 2652143 auto av = detail::as_cached_span(a, ta_);
461
1/1
✓ Branch 2 → 3 taken 2651880 times.
2652143 auto bv = detail::as_cached_span(b, tb_);
462
3/3
ecnerwala::series::vec<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::middle_product<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 27 → 28 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::middle_product<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 2 → 3 taken 2651880 times.
✓ Branch 27 → 28 taken 262 times.
5304549 return vec<E, A::exact_v>(fft::middle_product<E>(
463 av, av.cache(),
464 bv, bv.cache()
465 2652669 ));
466 2652406 }
467
468 namespace detail {
469 1327890 template <bool ea, bool eb> int product_prec(int la, int lb) {
470
2/4
✓ Branch 2 → 3 taken 1326258 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 1326258 times.
✗ Branch 3 → 5 not taken.
1326258 if constexpr (ea && eb) return la > 0 && lb > 0 ? la + lb - 1 : 0;
471 1632 else return ea ? lb : eb ? la : std::min(la, lb);
472 }
473
474 // Normalize a product operand at the given precision to a borrowed series + the
475 // whole cache serving it: a prefix cache at scale nextPow2(prec), or the whole
476 // span with the operand's own cache, or a truncated span with the caller's
477 // throwaway cache.
478 // A whole cache of an over-length operand (len > prec, which pins the other,
479 // necessarily trunc, operand's span at exactly prec) is only worth using when
480 // the untruncated span doesn't grow the transform size: a 2x'd inverse
481 // transform costs more than the saved forward transform.
482 template <like S>
483
5/10
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 96 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2543 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 106 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 96 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2651891 times.
2655740 auto product_operand(const S& s, int prec, fft::transformed<typename S::engine_t>& tmp) {
484 using E = typename S::engine_t;
485 if constexpr (has_prefix_cache<S>) {
486
1/1
✓ Branch 21 → 22 taken 20 times.
40 return s.first(std::min(s.len(), nextPow2(prec)));
487 } else {
488 2655720 span<E, S::exact_v> v = s;
489
7/14
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✓ Branch 2 → 3 taken 20 times.
✗ Branch 2 → 6 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 96 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 2543 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 106 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 96 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 20 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✓ Branch 4 → 5 taken 2651891 times.
✗ Branch 4 → 8 not taken.
2656668 int used = std::min(v.len(), prec);
490
19/38
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✓ Branch 24 → 25 taken 20 times.
✗ Branch 24 → 51 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::engine_t::transformed&):
✓ Branch 24 → 25 taken 20 times.
✗ Branch 24 → 51 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✓ Branch 2 → 3 taken 20 times.
✗ Branch 2 → 6 not taken.
✓ Branch 24 → 25 taken 20 times.
✗ Branch 24 → 51 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✓ Branch 24 → 25 taken 20 times.
✗ Branch 24 → 51 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 96 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 2543 times.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 48 taken 438 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 106 times.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 48 taken 74 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 8 taken 96 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t::transformed&):
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 44 taken 20 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t::transformed&):
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 44 taken 20 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 20 times.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 44 taken 20 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t::transformed&):
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 44 taken 20 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✓ Branch 26 → 27 taken 3 times.
✗ Branch 26 → 57 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✓ Branch 4 → 5 taken 2651891 times.
✗ Branch 4 → 8 not taken.
✓ Branch 26 → 27 taken 273 times.
✗ Branch 26 → 57 not taken.
2656668 if (auto co = cache_of(s)) {
491
9/38
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✓ Branch 33 → 34 taken 20 times.
✗ Branch 33 → 45 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::engine_t::transformed&):
✓ Branch 33 → 34 taken 20 times.
✗ Branch 33 → 45 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✓ Branch 3 → 4 taken 20 times.
✗ Branch 3 → 5 not taken.
✓ Branch 33 → 34 taken 20 times.
✗ Branch 33 → 45 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✓ Branch 33 → 34 taken 20 times.
✗ Branch 33 → 45 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 42 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 42 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t::transformed&):
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 38 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t::transformed&):
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 38 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 38 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t::transformed&):
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 38 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✓ Branch 37 → 38 taken 1 time.
✓ Branch 37 → 51 taken 2 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 2651891 times.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 51 taken 273 times.
2652704 if (s.len() <= prec || fft::detail::conv_size_for(s.len() + prec - 1).n
492
11/38
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✓ Branch 44 → 45 taken 15 times.
✓ Branch 44 → 51 taken 5 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true>::engine_t::transformed&):
✓ Branch 44 → 45 taken 15 times.
✓ Branch 44 → 51 taken 5 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✓ Branch 4 → 5 taken 18 times.
✓ Branch 4 → 6 taken 2 times.
✓ Branch 44 → 45 taken 15 times.
✓ Branch 44 → 51 taken 5 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, int, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✓ Branch 44 → 45 taken 15 times.
✓ Branch 44 → 51 taken 5 times.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>::engine_t::transformed&):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 48 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 48 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, int, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>::engine_t::transformed&):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>::engine_t::transformed&):
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 44 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>::engine_t::transformed&):
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 44 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 44 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> >(ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&, int, ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false>::engine_t::transformed&):
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 44 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, int, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t::transformed&):
✓ Branch 50 → 51 taken 1 time.
✗ Branch 50 → 57 not taken.
auto ecnerwala::series::detail::product_operand<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, int, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t::transformed&):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 57 not taken.
101 == fft::detail::conv_size_for(2 * prec - 1).n) {
493 2652245 return cached_span<E, S::exact_v>{v, co->get()};
494 }
495 }
496 3475 return cached_span<E, S::exact_v>{v.first(used), tmp};
497 }
498 }
499 /* namespace detail */ }
500
501 template <like A, like B> requires fft::same_engine<A, B>
502 3 vec<typename A::engine_t, A::exact_v && B::exact_v> operator + (const A& a, const B& b) {
503 using T = typename A::engine_t::value_type;
504 5 int n = (A::exact_v && B::exact_v) ? std::max(a.len(), b.len())
505 4 : A::exact_v ? b.len() : B::exact_v ? a.len() : std::min(a.len(), b.len());
506 9 vec<typename A::engine_t, A::exact_v && B::exact_v> r(size_t(n), T(0));
507
6/6
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::operator+<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 91 → 37 taken 23 times.
✓ Branch 91 → 92 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::operator+<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 75 → 21 taken 23 times.
✓ Branch 75 → 76 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::operator+<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 91 → 37 taken 37 times.
✓ Branch 91 → 92 taken 1 time.
86 for (int i = 0; i < n; i++) {
508
7/12
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::operator+<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 46 → 47 taken 23 times.
✗ Branch 46 → 55 not taken.
✓ Branch 67 → 68 taken 23 times.
✗ Branch 67 → 76 not taken.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::operator+<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 30 → 31 taken 23 times.
✗ Branch 30 → 39 not taken.
✓ Branch 51 → 52 taken 23 times.
✗ Branch 51 → 60 not taken.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::operator+<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 46 → 47 taken 23 times.
✓ Branch 46 → 55 taken 14 times.
✓ Branch 67 → 68 taken 37 times.
✗ Branch 67 → 76 not taken.
263 r[i] = (i < a.len() ? a[i] : T(0)) + (i < b.len() ? b[i] : T(0));
509 }
510 3 return r;
511 }
512 template <like A, like B> requires fft::same_engine<A, B>
513
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 578 times.
614 vec<typename A::engine_t, A::exact_v && B::exact_v> operator - (const A& a, const B& b) {
514 using T = typename A::engine_t::value_type;
515 38 int n = (A::exact_v && B::exact_v) ? std::max(a.len(), b.len())
516
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 578 times.
714 : A::exact_v ? b.len() : B::exact_v ? a.len() : std::min(a.len(), b.len());
517 686 vec<typename A::engine_t, A::exact_v && B::exact_v> r(size_t(n), T(0));
518
8/8
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::operator-<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 75 → 21 taken 23 times.
✓ Branch 75 → 76 taken 1 time.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::operator-<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 20 → 8 taken 12812331 times.
✓ Branch 20 → 21 taken 578 times.
✓ Branch 91 → 37 taken 339 times.
✓ Branch 91 → 92 taken 34 times.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::operator-<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 91 → 37 taken 37 times.
✓ Branch 91 → 92 taken 1 time.
12813344 for (int i = 0; i < n; i++) {
519
9/16
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::operator-<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 30 → 31 taken 23 times.
✗ Branch 30 → 39 not taken.
✓ Branch 51 → 52 taken 23 times.
✗ Branch 51 → 60 not taken.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>::exact_v> ecnerwala::series::operator-<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 10 → 11 taken 12812331 times.
✗ Branch 10 → 12 not taken.
✓ Branch 14 → 15 taken 12812331 times.
✗ Branch 14 → 16 not taken.
✓ Branch 46 → 47 taken 339 times.
✗ Branch 46 → 55 not taken.
✓ Branch 67 → 68 taken 339 times.
✗ Branch 67 → 76 not taken.
ecnerwala::series::vec<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::engine_t, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v&&ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>::exact_v> ecnerwala::series::operator-<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 46 → 47 taken 23 times.
✓ Branch 46 → 55 taken 14 times.
✓ Branch 67 → 68 taken 37 times.
✗ Branch 67 → 76 not taken.
38438204 r[i] = (i < a.len() ? a[i] : T(0)) - (i < b.len() ? b[i] : T(0));
520 }
521 614 return r;
522 }
523
524 // The single multiplication operator: each operand is normalized to a borrowed
525 // series + whole cache (see detail::product_operand), then multiplied once.
526 // An exact x exact product returns a has_cache result, going through
527 // fft::multiply_cached so the pointwise product is adopted as the result's
528 // transform whenever the engine supports it.
529 template <like A, like B> requires fft::same_engine<A, B>
530 1327890 auto operator * (const A& a, const B& b) {
531 using E = typename A::engine_t;
532 using T = typename E::value_type;
533
6/12
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1266 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 53 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 11 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 1325940 times.
1327890 constexpr bool ea = A::exact_v, eb = B::exact_v;
534
6/12
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1266 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 53 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 11 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1325940 times.
1328717 int prec = detail::product_prec<ea, eb>(a.len(), b.len());
535
69/130
auto ecnerwala::series::operator*<ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > >, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 20 → 21 taken 8 times.
✗ Branch 20 → 40 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 49 taken 8 times.
auto ecnerwala::series::operator*<ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > >, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > >(ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&):
✓ Branch 23 → 24 taken 2 times.
✗ Branch 23 → 46 not taken.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&):
✓ Branch 6 → 7 taken 48 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 48 times.
✗ Branch 7 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&):
✓ Branch 20 → 21 taken 8 times.
✗ Branch 20 → 40 not taken.
✓ Branch 28 → 29 taken 8 times.
✗ Branch 28 → 40 not taken.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 40 not taken.
✓ Branch 28 → 29 taken 1 time.
✗ Branch 28 → 40 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 49 taken 1 time.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 34 not taken.
✓ Branch 25 → 26 taken 1 time.
✗ Branch 25 → 34 not taken.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 43 taken 1 time.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 6 → 7 taken 1266 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 1266 times.
✗ Branch 7 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 1266 times.
✓ Branch 17 → 18 taken 205 times.
✗ Branch 17 → 34 not taken.
✓ Branch 25 → 26 taken 205 times.
✗ Branch 25 → 34 not taken.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 43 taken 205 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 34 not taken.
✓ Branch 25 → 26 taken 1 time.
✗ Branch 25 → 34 not taken.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 43 taken 1 time.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 6 → 7 taken 53 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 53 times.
✗ Branch 7 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 53 times.
✓ Branch 16 → 17 taken 35 times.
✗ Branch 16 → 33 not taken.
✓ Branch 24 → 25 taken 35 times.
✗ Branch 24 → 33 not taken.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 42 taken 35 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&):
✓ Branch 6 → 7 taken 48 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 48 times.
✗ Branch 7 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&):
✓ Branch 16 → 17 taken 20 times.
✓ Branch 16 → 32 taken 5 times.
✓ Branch 22 → 23 taken 20 times.
✗ Branch 22 → 32 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 41 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> const&):
✓ Branch 16 → 17 taken 20 times.
✓ Branch 16 → 32 taken 5 times.
✓ Branch 22 → 23 taken 20 times.
✗ Branch 22 → 32 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 41 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 2 → 3 taken 20 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 20 times.
✗ Branch 3 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 7 taken 20 times.
✓ Branch 16 → 17 taken 20 times.
✓ Branch 16 → 32 taken 5 times.
✓ Branch 22 → 23 taken 20 times.
✗ Branch 22 → 32 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 41 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&):
✓ Branch 16 → 17 taken 20 times.
✓ Branch 16 → 32 taken 5 times.
✓ Branch 22 → 23 taken 20 times.
✗ Branch 22 → 32 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 41 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 20 → 21 taken 2 times.
✗ Branch 20 → 40 not taken.
✓ Branch 31 → 32 taken 2 times.
✗ Branch 31 → 40 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 49 taken 2 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 6 → 7 taken 11 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 11 times.
✗ Branch 7 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 11 times.
✓ Branch 20 → 21 taken 7 times.
✗ Branch 20 → 40 not taken.
✓ Branch 31 → 32 taken 7 times.
✗ Branch 31 → 40 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 49 taken 7 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 19 → 20 taken 2 times.
✗ Branch 19 → 39 not taken.
✓ Branch 30 → 31 taken 2 times.
✗ Branch 30 → 39 not taken.
✗ Branch 38 → 39 not taken.
✓ Branch 38 → 48 taken 2 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 6 → 7 taken 1325940 times.
✗ Branch 6 → 9 not taken.
✓ Branch 7 → 8 taken 1325940 times.
✗ Branch 7 → 9 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 1325940 times.
✓ Branch 22 → 23 taken 132 times.
✗ Branch 22 → 45 not taken.
✓ Branch 33 → 34 taken 132 times.
✗ Branch 33 → 45 not taken.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 54 taken 132 times.
1328848 if (prec == 0 || a.len() == 0 || b.len() == 0) {
536 if constexpr (ea && eb) return cached<E, true>{};
537 60 else return trunc<E>(size_t(prec), T(0));
538 }
539 1327870 fft::transformed<E> ta_, tb_;
540
2/2
auto ecnerwala::series::operator*<ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > >, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 52 → 55 taken 8 times.
auto ecnerwala::series::operator*<ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > >, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > >(ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&):
✓ Branch 58 → 61 taken 2 times.
1327870 auto va = detail::product_operand(a, prec, ta_);
541
5/5
auto ecnerwala::series::operator*<ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > >, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > >(ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&):
✓ Branch 62 → 63 taken 2 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&):
✓ Branch 54 → 57 taken 8 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 13 → 14 taken 1266 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 9 → 10 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 13 → 14 taken 11 times.
1327870 auto vb = detail::product_operand(b, prec, tb_);
542 if constexpr (ea && eb) {
543 1326258 std::vector<T> coeffs;
544 1326258 fft::transformed<E> f;
545
4/4
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&):
✓ Branch 12 → 13 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 12 → 13 taken 53 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&):
✓ Branch 12 → 13 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 12 → 13 taken 1325940 times.
1326258 auto ca = detail::as_cached_span(va, ta_), cb = detail::as_cached_span(vb, tb_);
546
7/7
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&):
✓ Branch 12 → 13 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 12 → 13 taken 53 times.
✓ Branch 72 → 73 taken 35 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&):
✓ Branch 12 → 13 taken 48 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 78 → 79 taken 2 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 12 → 13 taken 1325940 times.
✓ Branch 84 → 85 taken 132 times.
1326427 fft::multiply_cached<E>(
547 ca, ca.cache(),
548 cb, cb.cache(),
549 coeffs, f
550 );
551 1326934 cached<E, true> w(exact<E>(std::move(coeffs)));
552 1326258 w.cache() = std::move(f);
553 1326258 return w;
554 1326644 } else {
555
6/9
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 13 → 14 taken 1266 times.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 1266 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 9 → 10 taken 20 times.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 13 → 14 taken 11 times.
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 11 times.
2242 trunc<E> r(size_t(prec), T(0));
556
3/6
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 1266 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 16 taken 11 times.
1612 auto ca = detail::as_cached_span(va, ta_), cb = detail::as_cached_span(vb, tb_);
557
16/16
auto ecnerwala::series::operator*<ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > >, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 93 → 94 taken 8 times.
auto ecnerwala::series::operator*<ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > >, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > >(ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&):
✓ Branch 99 → 100 taken 2 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::prefix_cached<ecnerwala::fft::engines::ntt<modnum<998244353> > > const&):
✓ Branch 93 → 94 taken 8 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 93 → 94 taken 1 time.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 87 → 88 taken 1 time.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 16 → 17 taken 1266 times.
✓ Branch 87 → 88 taken 205 times.
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 87 → 88 taken 1 time.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::crt<modnum<1000000007>, mod_goldilocks, modnum<2013265921> >, true> const&):
✓ Branch 85 → 86 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<mod_goldilocks>, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<mod_goldilocks>, true> const&):
✓ Branch 85 → 86 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 12 → 13 taken 20 times.
✓ Branch 85 → 86 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::split<modnum<1000000007> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::split<modnum<1000000007> >, true> const&):
✓ Branch 85 → 86 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 93 → 94 taken 2 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 16 → 17 taken 11 times.
✓ Branch 93 → 94 taken 7 times.
2242 fft::multiply<E>(
558 ca, ca.cache(),
559 cb, cb.cache(),
560
3/3
auto ecnerwala::series::operator*<ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 16 → 17 taken 1266 times.
auto ecnerwala::series::operator*<ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false>, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> >(ecnerwala::series::span<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&, ecnerwala::series::cached_span<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&):
✓ Branch 12 → 13 taken 20 times.
auto ecnerwala::series::operator*<ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true>, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> >(ecnerwala::series::cached<ecnerwala::fft::engines::ntt<modnum<998244353> >, true> const&, ecnerwala::series::vec<ecnerwala::fft::engines::ntt<modnum<998244353> >, false> const&):
✓ Branch 16 → 17 taken 11 times.
1612 std::span<T>(r)
561 );
562 1612 return r;
563 1612 }
564 1328382 }
565
566 // Wrapper around trunc which caches transform(s[:2^k]) for all k,
567 // matching the doubling shape of ps_inv/exp so they can populate the caches.
568 // TODO: make ps_inv/exp populate these
569 template <fft::engine E>
570 struct prefix_cached {
571 using T = typename E::value_type;
572
573 using engine_t = E;
574 static constexpr bool exact_v = false;
575
576 prefix_cached() = default;
577 // moving coefficients in or out is free: implicit on rvalues, explicit copy otherwise
578 prefix_cached(trunc<E>&& s_) : s(std::move(s_)) {}
579 4 explicit prefix_cached(const trunc<E>& s_) : s(s_) {}
580 operator trunc<E>() && { return std::move(s); }
581
582
4/8
✓ Branch 31 → 32 taken 8 times.
✗ Branch 31 → 40 not taken.
✓ Branch 34 → 35 taken 2 times.
✗ Branch 34 → 46 not taken.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 49 taken 8 times.
✗ Branch 45 → 46 not taken.
✓ Branch 45 → 55 taken 2 times.
199 int len() const { return s.len(); }
583 // unwrap to the owned coefficients
584 const trunc<E>& uncached() const { return s; }
585 const T& operator[](int i) const { return s[size_t(i)]; }
586 auto begin() const { return s.cbegin(); }
587 auto end() const { return s.cend(); }
588 operator span<E, false>() const { return s; }
589
590 // extend precision: appends coefficients, keeping all covering caches valid
591 1 void append(std::span<const T> tail) {
592
1/1
✓ Branch 22 → 23 taken 1 time.
5 s.insert(s.end(), tail.begin(), tail.end());
593 1 }
594
595 // The first k coefficients, with the covering prefix cache when one lines
596 // up with k (k a power of two, or k == len()); uncached otherwise.
597 23 maybe_cached<E, false> first(int k) const {
598 46 assert(k <= len());
599 23 span<E, false> v = s.first(k);
600 23 int n = nextPow2(k);
601
2/3
✓ Branch 37 → 38 taken 23 times.
✗ Branch 37 → 41 not taken.
✓ Branch 39 → 40 taken 23 times.
46 if (std::min(n, len()) == k) return {v, prefix_cache(n)};
602 return maybe_cached<E, false>(v);
603 }
604 // the whole-sequence transform: the prefix cache covering all of len()
605 fft::transformed<E>& cache() const { return prefix_cache(nextPow2(len())); }
606 // cache over the prefix of length min(n, len()); n a power of two
607 23 fft::transformed<E>& prefix_cache(int n) const {
608 23 assert(n > 0 && !(n & (n-1)));
609
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 23 times.
23 int k = __builtin_ctz(unsigned(n));
610
2/2
✓ Branch 11 → 12 taken 3 times.
✓ Branch 11 → 16 taken 20 times.
23 if (k >= sz(caches)) caches.resize(size_t(k) + 1);
611 23 auto& c = caches[k];
612 46 int e = std::min(n, len());
613
2/2
✓ Branch 40 → 41 taken 8 times.
✓ Branch 40 → 84 taken 15 times.
23 if (c.len != e) {
614
1/1
✓ Branch 50 → 51 taken 8 times.
24 c.t = E::transform(s.first(e), 2 * n);
615 8 c.len = e;
616 }
617 23 return c.t;
618 }
619
620 private:
621 trunc<E> s;
622 // memoized transforms: logically const; len tracks how much of s each covers
623 38 struct entry { fft::transformed<E> t; int len = 0; };
624 mutable std::vector<entry> caches;
625 };
626
627 /* namespace ecnerwala::series */ }
628