Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_polynomial.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
57#ifndef TPL_POLYNOMIAL_H
58#define TPL_POLYNOMIAL_H
59
60#include <cmath>
61#include <initializer_list>
62#include <limits>
63#include <sstream>
64#include <string>
65#include <type_traits>
66#include <utility>
67#include <numeric> // std::gcd
68#include <random> // std::mt19937_64
69
70#include <ah-errors.H>
71#include <tpl_dynMapTree.H>
72#include <tpl_array.H>
73#include <htlist.H>
74#include <modular_arithmetic.H> // mod_inv, mod_mul
75
76namespace Aleph {
77
79namespace polynomial_detail {
80
86template <typename C>
87C power(C base, size_t exp)
88{
89 C result = C(1);
90 while (exp > 0)
91 {
92 if (exp & 1)
93 result = result * base;
94 base = base * base;
95 exp >>= 1;
96 }
97 return result;
98}
99
100template <typename Int>
101 requires std::is_integral_v<Int>
102[[nodiscard]] uint64_t abs_to_u64(Int value) noexcept
103{
104 if constexpr (std::is_signed_v<Int>)
105 {
106 using Unsigned = std::make_unsigned_t<Int>;
107 const Unsigned magnitude = value < Int{} ? Unsigned{} - static_cast<Unsigned>(value)
108 : static_cast<Unsigned>(value);
109 return static_cast<uint64_t>(magnitude);
110 }
111 else
112 return static_cast<uint64_t>(value);
113}
114
115template <typename Int>
116 requires std::is_integral_v<Int>
118{
119 if (modulus == 0)
120 return 0;
121
122 if constexpr (std::is_signed_v<Int>)
123 {
124 const auto mod_i64 = static_cast<int64_t>(modulus);
125 auto rem = static_cast<int64_t>(value % static_cast<Int>(mod_i64));
126 if (rem < 0)
127 rem += mod_i64;
128 return static_cast<uint64_t>(rem);
129 }
130 else
131 return static_cast<uint64_t>(value % static_cast<Int>(modulus));
132}
133
134template <typename Int>
135 requires std::is_integral_v<Int>
137{
138 if constexpr (std::is_signed_v<Int>)
139 {
140 const uint64_t canonical = modulus == 0 ? 0 : value % modulus;
141 const uint64_t half = modulus / 2;
142 if (canonical > half)
143 return static_cast<Int>(static_cast<int64_t>(canonical) - static_cast<int64_t>(modulus));
144 return static_cast<Int>(canonical);
145 }
146 else
147 return static_cast<Int>(modulus == 0 ? 0 : value % modulus);
148}
149
150} // end namespace polynomial_detail
151
165template <typename Coefficient = double>
167{
168public:
170
171private:
172 using Term = std::pair<size_t, Coefficient>;
174
175 // --- Epsilon-based zero detection for floating-point types ----------
176
182 static constexpr Coefficient epsilon() noexcept
183 {
184 if constexpr (std::is_floating_point_v<Coefficient>)
185 return Coefficient(128) * std::numeric_limits<Coefficient>::epsilon();
186 else
187 return Coefficient{};
188 }
189
195 static bool coeff_is_zero(const Coefficient &c) noexcept
196 {
197 if constexpr (std::is_floating_point_v<Coefficient>)
198 return std::abs(c) <= epsilon();
199 else
200 return c == Coefficient{};
201 }
202
205 {
206 DynList<size_t> zeros;
207 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
208 {
209 auto &p = it.get_curr();
210 if (coeff_is_zero(p.second))
211 zeros.append(p.first);
212 }
213 zeros.for_each([this](size_t e)
214 {
215 coeffs.remove_key(e);
216 });
217 }
218
221 {
222 auto *p = coeffs.search(exp);
223 return p != nullptr ? p->second : Coefficient{};
224 }
225
227 Coefficient *coeff_ptr(size_t exp) noexcept
228 {
229 auto *p = coeffs.search(exp);
230 return p != nullptr ? &p->second : nullptr;
231 }
232
234 const Coefficient *coeff_ptr(size_t exp) const noexcept
235 {
236 auto *p = coeffs.search(exp);
237 return p != nullptr ? &p->second : nullptr;
238 }
239
241 void add_to_coeff(size_t exp, const Coefficient &delta)
242 {
243 if (coeff_is_zero(delta))
244 return;
245
246 if (auto *slot = coeff_ptr(exp); slot != nullptr)
247 {
248 *slot = *slot + delta;
249 if (coeff_is_zero(*slot))
250 coeffs.remove_key(exp);
251 }
252 else
253 coeffs.insert(exp, delta);
254 }
255
257 void add_scaled_shifted(const Gen_Polynomial &src, const Coefficient &scale, size_t shift = 0)
258 {
259 if (coeff_is_zero(scale) or src.is_zero())
260 return;
261
262 src.for_each_term([this, &scale, shift](size_t exp, const Coefficient &c)
263 {
264 add_to_coeff(exp + shift, c * scale);
265 });
266 }
267
270 {
271 if (coeffs.is_empty())
272 return;
273
274 if (coeff_is_zero(s))
275 {
277 return;
278 }
279
280 if (s == Coefficient(1))
281 return;
282
283 DynList<size_t> zeros;
284 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
285 {
286 auto &p = it.get_curr();
287 p.second = p.second * s;
288 if (coeff_is_zero(p.second))
289 zeros.append(p.first);
290 }
291
292 zeros.for_each([this](size_t exp)
293 {
294 coeffs.remove_key(exp);
295 });
296 }
297
300 {
301 ah_domain_error_if(coeff_is_zero(s)) << "polynomial division by zero scalar";
302
303 if (coeffs.is_empty())
304 return;
305
306 if (s == Coefficient(1))
307 return;
308
309 DynList<size_t> zeros;
310 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
311 {
312 auto &p = it.get_curr();
313 p.second = p.second / s;
314 if (coeff_is_zero(p.second))
315 zeros.append(p.first);
316 }
317
318 zeros.for_each([this](size_t exp)
319 {
320 coeffs.remove_key(exp);
321 });
322 }
323
325 template <class Op>
326 void for_each_term_desc(Op &&op) const
327 {
329 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
330 {
331 const auto &p = it.get_curr();
332 desc_terms.append(&p);
333 }
334
335 for (size_t i = desc_terms.size(); i > 0; --i)
336 {
337 const Term &term = *desc_terms(i - 1);
338 op(term.first, term.second);
339 }
340 }
341
344 {
345 if (is_zero())
346 return Gen_Polynomial();
347
348 Gen_Polynomial result;
349 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
350 {
351 auto &p = it.get_curr();
352 result.add_to_coeff(p.first, p.second * c);
353 result.add_to_coeff(p.first + 1, p.second);
354 }
355 return result;
356 }
357
358public:
359 // =================================================================
360 // Construction
361 // =================================================================
362
364 Gen_Polynomial() = default;
365
369 explicit Gen_Polynomial(const Coefficient &c)
370 {
371 if (not coeff_is_zero(c))
372 coeffs[0] = c;
373 }
374
380 {
381 if (not coeff_is_zero(c))
382 coeffs[exp] = c;
383 }
384
392 Gen_Polynomial(std::initializer_list<Coefficient> il)
393 {
394 size_t exp = 0;
395 for (auto &c : il)
396 {
397 if (not coeff_is_zero(c))
398 coeffs[exp] = c;
399 ++exp;
400 }
401 }
402
410 {
411 size_t exp = 0;
412 l.for_each([this, &exp](const Coefficient &c)
413 {
414 if (not coeff_is_zero(c))
415 coeffs[exp] = c;
416 ++exp;
417 });
418 }
419
427 explicit Gen_Polynomial(const DynList<std::pair<size_t, Coefficient>> &term_list)
428 {
429 term_list.for_each([this](const Term &t)
430 {
431 if (not coeff_is_zero(t.second))
432 add_to_coeff(t.first, t.second);
433 });
434 }
435
436 // =================================================================
437 // Static factory methods
438 // =================================================================
439
442 {
443 return Gen_Polynomial();
444 }
445
448 {
449 return Gen_Polynomial(Coefficient(1));
450 }
451
455 [[nodiscard]] static Gen_Polynomial x_to(size_t n)
456 {
457 return Gen_Polynomial(Coefficient(1), n);
458 }
459
470 {
471 Gen_Polynomial result(Coefficient(1)); // start with constant 1
472 roots.for_each([&result](const Coefficient &r)
473 {
474 result = result.multiply_by_monic_linear(-r); // (x - r)
475 });
476 return result;
477 }
478
494 const DynList<std::pair<Coefficient, Coefficient>> &points)
495 {
496 ah_domain_error_if(points.is_empty()) << "interpolate requires at least one point";
497
498 const size_t n = points.size();
501 xs.putn(n);
502 dd.putn(n);
503
504 size_t idx = 0;
505 points.for_each([&](const std::pair<Coefficient, Coefficient> &pt)
506 {
507 xs[idx] = pt.first;
508 dd[idx] = pt.second;
509 ++idx;
510 });
511
512 for (size_t i = 0; i < n; ++i)
513 for (size_t j = i + 1; j < n; ++j)
514 ah_domain_error_if(coeff_is_zero(xs[i] - xs[j])) << "interpolate: duplicate x-values";
515
516 for (size_t order = 1; order < n; ++order)
517 for (size_t i = n - 1; i >= order; --i)
518 {
519 Coefficient denom = xs[i] - xs[i - order];
520 dd[i] = (dd[i] - dd[i - 1]) / denom;
521 if (i == order)
522 break;
523 }
524
525 Gen_Polynomial result(dd[0]);
527 for (size_t i = 1; i < n; ++i)
528 {
529 basis = basis.multiply_by_monic_linear(-xs[i - 1]);
530 if (not coeff_is_zero(dd[i]))
531 result.add_scaled_shifted(basis, dd[i]);
532 }
533
534 return result;
535 }
536
537 // =================================================================
538 // Properties
539 // =================================================================
540
543 {
544 return coeffs.is_empty();
545 }
546
553 {
554 if (coeffs.is_empty())
555 return 0;
556 return coeffs.max().first;
557 }
558
561 {
562 return coeffs.size();
563 }
564
567 {
568 return coeffs.is_empty() or (coeffs.size() == 1 and coeffs.min().first == 0);
569 }
570
573 {
574 return coeffs.size() == 1;
575 }
576
579 {
580 return not coeffs.is_empty() and coeff_is_zero(coeffs.max().second - Coefficient(1));
581 }
582
583 // =================================================================
584 // Coefficient access
585 // =================================================================
586
589 {
590 return coeff_at(exp);
591 }
592
597 {
598 if (coeffs.is_empty())
599 return Coefficient{};
600 return coeffs.max().second;
601 }
602
604 [[nodiscard]] bool has_term(size_t exp) const noexcept
605 {
606 return coeffs.has(exp);
607 }
608
609 // =================================================================
610 // Evaluation
611 // =================================================================
612
623 {
624 if (coeffs.is_empty())
625 return Coefficient{};
626
627 if (x == Coefficient{})
628 return coeff_at(0);
629
630 Coefficient result = Coefficient{};
631 size_t prev_exp = 0;
632 bool first = true;
633
634 for_each_term_desc([&](size_t exp, const Coefficient &c)
635 {
636 if (first)
637 {
638 result = c;
639 prev_exp = exp;
640 first = false;
641 return;
642 }
643
644 result = result * polynomial_detail::power(x, prev_exp - exp) + c;
645 prev_exp = exp;
646 });
647
648 if (prev_exp > 0)
649 result = result * polynomial_detail::power(x, prev_exp);
650
651 return result;
652 }
653
664 {
665 Coefficient result = Coefficient{};
666 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
667 {
668 auto &p = it.get_curr();
669 result = result + p.second * polynomial_detail::power(x, p.first);
670 }
671 return result;
672 }
673
683 {
684 if (coeffs.is_empty())
685 return Coefficient{};
686
687 if (x == Coefficient{})
688 return coeff_at(0);
689
690 if (num_terms() <= 2)
691 return sparse_eval(x);
692
693 return horner_eval(x);
694 }
695
698 {
699 return eval(x);
700 }
701
712 {
714 points.for_each([this, &results](const Coefficient &x)
715 {
716 results.append(eval(x));
717 });
718 return results;
719 }
720
721 // =================================================================
722 // Arithmetic operators
723 // =================================================================
724
727 {
728 Gen_Polynomial result;
729 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
730 {
731 auto &p = it.get_curr();
732 result.coeffs[p.first] = -p.second;
733 }
734 return result;
735 }
736
739 {
740 Gen_Polynomial result = *this;
741 result += q;
742 return result;
743 }
744
747 {
748 if (this == &q)
749 {
751 return *this;
752 }
753
754 for (auto it = q.coeffs.get_it(); it.has_curr(); it.next_ne())
755 {
756 auto &p = it.get_curr();
757 add_to_coeff(p.first, p.second);
758 }
759 return *this;
760 }
761
764 {
765 Gen_Polynomial result = *this;
766 result += c;
767 return result;
768 }
769
772 {
773 add_to_coeff(0, c);
774 return *this;
775 }
776
779 {
780 Gen_Polynomial result = *this;
781 result -= q;
782 return result;
783 }
784
787 {
788 if (this == &q)
789 {
791 return *this;
792 }
793
794 for (auto it = q.coeffs.get_it(); it.has_curr(); it.next_ne())
795 {
796 auto &p = it.get_curr();
797 add_to_coeff(p.first, -p.second);
798 }
799 return *this;
800 }
801
804 {
805 Gen_Polynomial result = *this;
806 result -= c;
807 return result;
808 }
809
812 {
813 add_to_coeff(0, -c);
814 return *this;
815 }
816
823 {
824 if (is_zero() or q.is_zero())
825 return Gen_Polynomial();
826
827 Gen_Polynomial result;
828 const Gen_Polynomial *outer = this;
829 const Gen_Polynomial *inner = &q;
830
831 if (q.num_terms() < num_terms())
832 {
833 outer = &q;
834 inner = this;
835 }
836
837 for (auto it1 = outer->coeffs.get_it(); it1.has_curr(); it1.next_ne())
838 {
839 auto &t1 = it1.get_curr();
840 for (auto it2 = inner->coeffs.get_it(); it2.has_curr(); it2.next_ne())
841 {
842 auto &t2 = it2.get_curr();
843 result.add_to_coeff(t1.first + t2.first, t1.second * t2.second);
844 }
845 }
846 return result;
847 }
848
851 {
852 *this = *this * q;
853 return *this;
854 }
855
858 {
859 if (coeff_is_zero(s))
860 return Gen_Polynomial();
861 Gen_Polynomial result;
862 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
863 {
864 auto &p = it.get_curr();
865 Coefficient prod = p.second * s;
867 result.coeffs[p.first] = prod;
868 }
869 return result;
870 }
871
874 {
875 scale_inplace(s);
876 return *this;
877 }
878
883 {
884 ah_domain_error_if(coeff_is_zero(s)) << "polynomial division by zero scalar";
885 Gen_Polynomial result;
886 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
887 {
888 auto &p = it.get_curr();
889 Coefficient q = p.second / s;
890 if (not coeff_is_zero(q))
891 result.coeffs[p.first] = q;
892 }
893 return result;
894 }
895
898 {
900 return *this;
901 }
902
903 // =================================================================
904 // Polynomial division
905 // =================================================================
906
917 [[nodiscard]] std::pair<Gen_Polynomial, Gen_Polynomial> divmod(const Gen_Polynomial &d) const
918 {
919 ah_domain_error_if(d.is_zero()) << "polynomial division by zero";
920
921 if (is_zero() or degree() < d.degree())
922 return {Gen_Polynomial(), *this};
923
924 Gen_Polynomial q; // quotient
925 Gen_Polynomial r = *this; // remainder
926 const size_t d_degree = d.degree();
927 const Coefficient d_lc = d.leading_coeff();
928
929 while (not r.is_zero())
930 {
931 const size_t r_degree = r.degree();
932 if (r_degree < d_degree)
933 break;
934
935 const Coefficient scale = r.leading_coeff() / d_lc;
937 << "divmod: inexact coefficient division (leading_coeff=" << r.leading_coeff()
938 << " / divisor_lc=" << d_lc << " = 0). "
939 << "For integral/non-field coefficients, use pseudo_divmod() instead.";
940 const size_t shift = r_degree - d_degree;
941
943 r.add_scaled_shifted(d, -scale, shift);
944 }
945
946 return {q, r};
947 }
948
951 {
952 return divmod(d).first;
953 }
954
957 {
958 return divmod(d).second;
959 }
960
963 {
964 *this = divmod(d).first;
965 return *this;
966 }
967
970 {
971 *this = divmod(d).second;
972 return *this;
973 }
974
975 // =================================================================
976 // Calculus
977 // =================================================================
978
985 {
986 Gen_Polynomial result;
987 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
988 {
989 auto &p = it.get_curr();
990 if (p.first == 0)
991 continue; // derivative of constant is 0
992 Coefficient dc = p.second * Coefficient(p.first);
993 if (not coeff_is_zero(dc))
994 result.coeffs[p.first - 1] = dc;
995 }
996 return result;
997 }
998
1011 {
1012 Gen_Polynomial result = *this;
1013 for (size_t i = 0; i < n and not result.is_zero(); ++i)
1014 result = result.derivative();
1015 return result;
1016 }
1017
1029 {
1030 Gen_Polynomial result;
1031 if (not coeff_is_zero(C))
1032 result.coeffs[0] = C;
1033 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1034 {
1035 auto &p = it.get_curr();
1036 Coefficient ic = p.second / Coefficient(p.first + 1);
1037 if (not coeff_is_zero(ic))
1038 result.coeffs[p.first + 1] = ic;
1039 }
1040 return result;
1041 }
1042
1053 {
1054 Coefficient result = Coefficient{};
1055
1056 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1057 {
1058 auto &p = it.get_curr();
1059 const size_t next_exp = p.first + 1;
1060 const Coefficient scale = p.second / Coefficient(next_exp);
1061 if (coeff_is_zero(scale))
1062 continue;
1063
1064 result
1065 = result
1067 }
1068
1069 return result;
1070 }
1071
1072 // =================================================================
1073 // Composition and power
1074 // =================================================================
1075
1083 {
1084 if (is_zero())
1085 return Gen_Polynomial();
1086
1087 if (q.is_constant())
1088 return Gen_Polynomial(eval(q[0]));
1089
1090 Gen_Polynomial result;
1091 size_t prev_exp = 0;
1092 bool first = true;
1093
1094 for_each_term_desc([&](size_t exp, const Coefficient &c)
1095 {
1096 if (first)
1097 {
1098 result = Gen_Polynomial(c);
1099 prev_exp = exp;
1100 first = false;
1101 return;
1102 }
1103
1104 const size_t gap = prev_exp - exp;
1105 if (gap == 1)
1106 result *= q;
1107 else if (gap > 1)
1108 result *= q.pow(gap);
1109
1110 result.add_to_coeff(0, c);
1111 prev_exp = exp;
1112 });
1113
1114 if (prev_exp == 1)
1115 result *= q;
1116 else if (prev_exp > 1)
1117 result *= q.pow(prev_exp);
1118
1119 return result;
1120 }
1121
1127 [[nodiscard]] Gen_Polynomial pow(size_t n) const
1128 {
1129 Gen_Polynomial result(Coefficient(1));
1130 Gen_Polynomial base = *this;
1131 while (n > 0)
1132 {
1133 if (n & 1)
1134 result *= base;
1135 base *= base;
1136 n >>= 1;
1137 }
1138 return result;
1139 }
1140
1141 // =================================================================
1142 // GCD
1143 // =================================================================
1144
1155 {
1156 while (not b.is_zero())
1157 {
1158 Gen_Polynomial r = a % b;
1159 a = b;
1160 b = r;
1161 }
1162 // Make monic
1163 if (not a.is_zero())
1164 a /= a.leading_coeff();
1165 return a;
1166 }
1167
1183
1185 {
1188
1189 while (not b.is_zero())
1190 {
1191 auto [q, r] = a.divmod(b);
1192 a = b;
1193 b = r;
1194 Gen_Polynomial s2 = s0 - q * s1;
1195 Gen_Polynomial t2 = t0 - q * t1;
1196 s0 = s1;
1197 s1 = s2;
1198 t0 = t1;
1199 t1 = t2;
1200 }
1201
1202 if (not a.is_zero())
1203 {
1205 a /= lc;
1206 s0 /= lc;
1207 t0 /= lc;
1208 }
1209
1210 return {a, s0, t0};
1211 }
1212
1222 {
1223 if (a.is_zero() or b.is_zero())
1224 return Gen_Polynomial();
1225 Gen_Polynomial g = gcd(a, b);
1226 return a / g * b;
1227 }
1228
1240 [[nodiscard]] std::pair<Gen_Polynomial, Gen_Polynomial> pseudo_divmod(const Gen_Polynomial &d) const
1241 {
1242 ah_domain_error_if(d.is_zero()) << "pseudo-division by zero polynomial";
1243
1244 if (is_zero() or degree() < d.degree())
1245 return {Gen_Polynomial(), *this};
1246
1247 const Coefficient lc_d = d.leading_coeff();
1248 const size_t d_degree = d.degree();
1249 size_t delta = degree() - d_degree + 1;
1250
1252 Gen_Polynomial r = *this;
1253
1254 while (not r.is_zero())
1255 {
1256 const size_t r_degree = r.degree();
1257 if (r_degree < d_degree)
1258 break;
1259
1260 const Coefficient lc_r = r.leading_coeff();
1261 const size_t exp = r_degree - d_degree;
1262
1263 // Multiply q and r by lc(d) to avoid division
1265 r.scale_inplace(lc_d);
1266 --delta;
1267
1268 q.add_to_coeff(exp, lc_r);
1269 r.add_scaled_shifted(d, -lc_r, exp);
1270 }
1271
1272 // Multiply by remaining powers of lc(d)
1273 if (delta > 0)
1274 {
1277 r.scale_inplace(scale);
1278 }
1279
1280 return {q, r};
1281 }
1282
1283 // =================================================================
1284 // Algebraic transformations
1285 // =================================================================
1286
1293 {
1294 ah_domain_error_if(is_zero()) << "cannot make zero polynomial monic";
1295 return *this / leading_coeff();
1296 }
1297
1304 {
1305 if (is_zero())
1306 return Gen_Polynomial();
1307 size_t d = degree();
1308 Gen_Polynomial result;
1309 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1310 {
1311 auto &p = it.get_curr();
1312 result.coeffs[d - p.first] = p.second;
1313 }
1314 return result;
1315 }
1316
1322 {
1323 Gen_Polynomial result;
1324 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1325 {
1326 auto &p = it.get_curr();
1327 result.coeffs[p.first] = (p.first % 2 == 0) ? p.second : -p.second;
1328 }
1329 return result;
1330 }
1331
1343 {
1344 return compose(Gen_Polynomial({k, Coefficient(1)}));
1345 }
1346
1353 {
1354 if (k == 0)
1355 return *this;
1356 Gen_Polynomial result;
1357 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1358 {
1359 auto &p = it.get_curr();
1360 result.coeffs[p.first + k] = p.second;
1361 }
1362 return result;
1363 }
1364
1373 {
1374 if (k == 0)
1375 return *this;
1376 Gen_Polynomial result;
1377 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1378 {
1379 auto &p = it.get_curr();
1380 if (p.first >= k)
1381 result.coeffs[p.first - k] = p.second;
1382 }
1383 return result;
1384 }
1385
1394 {
1395 Gen_Polynomial result;
1396 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1397 {
1398 auto &p = it.get_curr();
1399 if (p.first < n)
1400 result.coeffs[p.first] = p.second;
1401 }
1402 return result;
1403 }
1404
1412 {
1413 if (is_zero())
1414 return Array<Coefficient>();
1415 size_t d = degree();
1416 Array<Coefficient> result(d + 1, Coefficient{});
1417 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1418 {
1419 auto &p = it.get_curr();
1420 result(p.first) = p.second;
1421 }
1422 return result;
1423 }
1424
1438 {
1439 if (is_zero() or is_constant())
1440 return *this;
1441 Gen_Polynomial g = gcd(*this, derivative());
1442 return *this / g;
1443 }
1444
1445 // =================================================================
1446 // Root analysis (floating-point coefficients)
1447 // =================================================================
1448
1458 {
1459 ah_domain_error_if(is_zero()) << "cauchy_bound: zero polynomial has no roots";
1460 if (is_constant())
1461 return Coefficient{};
1462
1463 const size_t d = degree();
1464 const Coefficient lc = leading_coeff();
1465 if constexpr (std::is_floating_point_v<Coefficient>)
1466 {
1468 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1469 {
1470 auto &p = it.get_curr();
1471 if (p.first == d)
1472 continue;
1473 const Coefficient ratio = std::abs(p.second / lc);
1474 if (ratio > max_ratio)
1475 max_ratio = ratio;
1476 }
1477 return Coefficient(1) + max_ratio;
1478 }
1479 else
1480 {
1481 long double max_ratio_ld = 0.0L;
1482 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1483 {
1484 auto &p = it.get_curr();
1485 if (p.first == d)
1486 continue;
1487 const long double ratio
1488 = std::fabs(static_cast<long double>(p.second) / static_cast<long double>(lc));
1489 if (ratio > max_ratio_ld)
1490 max_ratio_ld = ratio;
1491 }
1492 return Coefficient(1) + static_cast<Coefficient>(std::ceil(max_ratio_ld));
1493 }
1494 }
1495
1504 [[nodiscard]] size_t sign_variations() const
1505 {
1506 if (coeffs.size() < 2)
1507 return 0;
1508
1509 size_t changes = 0;
1510 bool have_prev = false;
1511 bool prev_positive = false;
1512
1513 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1514 {
1515 auto &p = it.get_curr();
1516 bool positive;
1517 if constexpr (std::is_floating_point_v<Coefficient>)
1518 positive = p.second > epsilon();
1519 else
1520 positive = p.second > Coefficient{};
1521
1522 bool is_neg;
1523 if constexpr (std::is_floating_point_v<Coefficient>)
1524 is_neg = p.second < -epsilon();
1525 else
1526 is_neg = p.second < Coefficient{};
1527
1529 continue; // skip zero coefficients
1530
1532 ++changes;
1534 have_prev = true;
1535 }
1536 return changes;
1537 }
1538
1550 {
1552 chain.append(*this);
1553 if (is_zero() or is_constant())
1554 return chain;
1555
1556 Gen_Polynomial p0 = *this;
1558 chain.append(p1);
1559
1560 while (not p1.is_zero())
1561 {
1562 Gen_Polynomial r = -(p0 % p1);
1563 if (r.is_zero())
1564 break;
1565 chain.append(r);
1566 p0 = p1;
1567 p1 = r;
1568 }
1569
1570 return chain;
1571 }
1572
1580 const Coefficient &x)
1581 {
1582 size_t changes = 0;
1583 bool have_prev = false;
1584 bool prev_positive = false;
1585
1586 chain.for_each([&](const Gen_Polynomial &p)
1587 {
1588 Coefficient val = p(x);
1589 if (coeff_is_zero(val))
1590 return; // skip zeros in Sturm evaluation
1591
1592 bool positive = val > Coefficient{};
1594 ++changes;
1596 have_prev = true;
1597 });
1598
1599 return changes;
1600 }
1601
1615 [[nodiscard]] size_t count_real_roots(const Coefficient &a, const Coefficient &b) const
1616 {
1617 if (b < a)
1618 return count_real_roots(b, a);
1619
1620 if (is_zero())
1621 return 0;
1622
1623 auto chain = sturm_chain();
1624 size_t sa = sturm_sign_changes(chain, a);
1625 size_t sb = sturm_sign_changes(chain, b);
1626 size_t count = sa > sb ? sa - sb : 0;
1627
1628 // Sturm's theorem counts roots in (a, b]; add root at a if present
1629 if (coeff_is_zero((*this)(a)))
1630 ++count;
1631
1632 return count;
1633 }
1634
1641 {
1642 if (is_zero() or is_constant())
1643 return 0;
1644 Coefficient bound = cauchy_bound();
1645 return count_real_roots(-bound, bound);
1646 }
1647
1662 Coefficient b,
1663 Coefficient tol = Coefficient(1e-12),
1664 size_t max_iter = 200) const
1665 {
1666 if (b < a)
1667 std::swap(a, b);
1668
1669 Coefficient fa = eval(a), fb = eval(b);
1670 if (coeff_is_zero(fa))
1671 return a;
1672 if (coeff_is_zero(fb))
1673 return b;
1674
1677 << "bisect_root: f(a) and f(b) must have opposite signs";
1678
1679 for (size_t i = 0; i < max_iter; ++i)
1680 {
1681 Coefficient mid = (a + b) / Coefficient(2);
1682 if (b - a < tol)
1683 return mid;
1685 if (coeff_is_zero(fm))
1686 return mid;
1687 if (fa > Coefficient{} == fm > Coefficient{})
1688 {
1689 a = mid;
1690 fa = fm;
1691 }
1692 else
1693 {
1694 b = mid;
1695 fb = fm;
1696 }
1697 }
1698 return (a + b) / Coefficient(2);
1699 }
1700
1711 Coefficient tol = Coefficient(1e-12),
1712 size_t max_iter = 100) const
1714 {
1716
1717 for (size_t i = 0; i < max_iter; ++i)
1718 {
1719 Coefficient fx = eval(x0);
1720 if (coeff_is_zero(fx))
1721 return x0;
1722 Coefficient dfx = dp.eval(x0);
1723 ah_domain_error_if(coeff_is_zero(dfx)) << "newton_root: derivative is zero at x = " << x0;
1724 Coefficient x1 = x0 - fx / dfx;
1725 Coefficient delta = x1 - x0;
1726 if constexpr (std::is_floating_point_v<Coefficient>)
1727 {
1728 if (std::abs(delta) < tol)
1729 return x1;
1730 }
1731 else
1732 {
1733 if (delta == Coefficient{})
1734 return x1;
1735 }
1736 x0 = x1;
1737 }
1738 return x0;
1739 }
1740
1741 // =================================================================
1742 // Comparison
1743 // =================================================================
1744
1750 [[nodiscard]] bool operator==(const Gen_Polynomial &q) const noexcept
1751 {
1752 if (this == &q)
1753 return true;
1754
1755 auto it1 = coeffs.get_it();
1756 auto it2 = q.coeffs.get_it();
1757
1758 while (it1.has_curr() and it2.has_curr())
1759 {
1760 auto &lhs = it1.get_curr();
1761 auto &rhs = it2.get_curr();
1762
1763 if (lhs.first == rhs.first)
1764 {
1765 if (not coeff_is_zero(lhs.second - rhs.second))
1766 return false;
1767 it1.next_ne();
1768 it2.next_ne();
1769 continue;
1770 }
1771
1772 if (lhs.first < rhs.first)
1773 {
1774 if (not coeff_is_zero(lhs.second))
1775 return false;
1776 it1.next_ne();
1777 continue;
1778 }
1779
1780 if (not coeff_is_zero(rhs.second))
1781 return false;
1782 it2.next_ne();
1783 }
1784
1785 while (it1.has_curr())
1786 {
1787 if (not coeff_is_zero(it1.get_curr().second))
1788 return false;
1789 it1.next_ne();
1790 }
1791
1792 while (it2.has_curr())
1793 {
1794 if (not coeff_is_zero(it2.get_curr().second))
1795 return false;
1796 it2.next_ne();
1797 }
1798
1799 return true;
1800 }
1801
1803 [[nodiscard]] bool operator!=(const Gen_Polynomial &q) const noexcept
1804 {
1805 return not(*this == q);
1806 }
1807
1808 // =================================================================
1809 // Iteration
1810 // =================================================================
1811
1816 template <class Op>
1817 void for_each_term(Op &&op) const
1818 {
1819 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1820 {
1821 auto &p = it.get_curr();
1822 op(p.first, p.second);
1823 }
1824 }
1825
1828 {
1830 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1831 {
1832 auto &p = it.get_curr();
1833 result.append({p.first, p.second});
1834 }
1835 return result;
1836 }
1837
1840 {
1841 return coeffs.keys();
1842 }
1843
1846 {
1847 return coeffs.values();
1848 }
1849
1850 // =================================================================
1851 // String representation
1852 // =================================================================
1853
1861 [[nodiscard]] std::string to_str(const std::string &var = "x") const
1862 {
1863 if (coeffs.is_empty())
1864 return "0";
1865
1866 std::ostringstream oss;
1867 bool first = true;
1868
1869 for_each_term_desc([&](size_t exp, const Coefficient &c)
1870 {
1871 bool negative = false;
1872 if constexpr (requires { c < Coefficient{}; })
1873 negative = c < Coefficient{};
1874
1875 Coefficient ac = negative ? -c : c;
1876
1877 // Sign
1878 if (first)
1879 {
1880 if (negative)
1881 oss << "-";
1882 }
1883 else
1884 oss << (negative ? " - " : " + ");
1885
1886 // Coefficient and variable
1887 if (exp == 0)
1888 oss << ac;
1889 else
1890 {
1891 if (not(ac == Coefficient(1)))
1892 oss << ac;
1893 oss << var;
1894 if (exp > 1)
1895 oss << "^" << exp;
1896 }
1897
1898 first = false;
1899 });
1900
1901 return oss.str();
1902 }
1903
1904 // =================================================================
1905 // Layer 5 — Exact Univariate Factorization
1906 // =================================================================
1907
1913 struct SfdTerm
1914 {
1917 };
1918
1924 [[nodiscard]] Coefficient get_coeff(size_t exp) const noexcept
1925 {
1926 return coeff_at(exp);
1927 }
1928
1934 void set_coeff(size_t exp, const Coefficient &c)
1935 {
1936 if (auto *slot = coeff_ptr(exp); slot != nullptr)
1937 {
1938 if (coeff_is_zero(c))
1939 coeffs.remove_key(exp);
1940 else
1941 *slot = c;
1942 }
1943 else if (not coeff_is_zero(c))
1944 coeffs.insert(exp, c);
1945 }
1946
1960 {
1961 if (coeffs.is_empty())
1962 return Coefficient{};
1963
1964 Coefficient result = Coefficient{};
1965 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
1966 {
1967 auto &p = it.get_curr();
1968 result = std::gcd(result, p.second);
1969 }
1970
1971 // Ensure sign follows leading coefficient
1972 const Coefficient lc = leading_coeff();
1973 if (lc < Coefficient{} and result > Coefficient{})
1974 result = -result;
1975
1976 return result;
1977 }
1978
1989 {
1990 if (is_zero())
1991 return Gen_Polynomial();
1992
1993 Coefficient c = content();
1994 if (c == Coefficient{} or c == Coefficient(1) or c == Coefficient(-1))
1995 {
1996 // Already primitive; ensure leading coefficient is positive
1997 Gen_Polynomial result = *this;
1998 if (leading_coeff() < Coefficient{})
1999 result.scale_inplace(Coefficient(-1));
2000 return result;
2001 }
2002
2003 Gen_Polynomial result;
2004 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
2005 {
2006 auto &p = it.get_curr();
2007 result.coeffs.insert(p.first, p.second / c);
2008 }
2009
2010 // Ensure leading coefficient is positive
2011 if (result.leading_coeff() < Coefficient{})
2012 result.scale_inplace(Coefficient(-1));
2013
2014 return result;
2015 }
2016
2030 requires std::is_integral_v<Coefficient>
2031 {
2032 if (a.is_zero())
2033 return b.primitive_part();
2034 if (b.is_zero())
2035 return a.primitive_part();
2036
2037 a = a.primitive_part();
2038 b = b.primitive_part();
2039
2040 while (not b.is_zero())
2041 {
2042 auto [quo, rem] = a.pseudo_divmod(b);
2043 a = b;
2044 b = rem.is_zero() ? rem : rem.primitive_part();
2045 }
2046
2047 return a.primitive_part();
2048 }
2049
2067 {
2068 DynList<SfdTerm> result;
2069
2070 if (is_constant())
2071 return result;
2072
2074 Gen_Polynomial fp = f.derivative();
2075
2076 Gen_Polynomial g = integer_gcd(f, fp);
2078
2079 size_t multiplicity = 1;
2080
2081 while (not h.is_constant())
2082 {
2084 h = integer_gcd(g, h);
2086
2087 if (not w.is_constant())
2088 result.append(SfdTerm{w.primitive_part(), multiplicity});
2089
2090 g = _integer_exact_quot(g, h);
2091 multiplicity++;
2092 }
2093
2094 // Final factor
2095 if (not g.is_constant())
2096 result.append(SfdTerm{g.primitive_part(), multiplicity});
2097
2098 return result;
2099 }
2100
2112 requires std::is_integral_v<Coefficient>
2113 {
2114 ah_domain_error_if(b.is_zero()) << "_integer_exact_quot: zero divisor";
2115 if (b.is_constant())
2116 {
2117 Coefficient d = b.leading_coeff();
2119 for (auto it = a.coeffs.get_it(); it.has_curr(); it.next_ne())
2120 {
2121 auto &p = it.get_curr();
2122 ah_domain_error_if(not coeff_is_zero(p.second % d))
2123 << "_integer_exact_quot: inexact constant division for coefficient "
2124 << p.second << " and divisor " << d;
2125 r.coeffs.insert(p.first, p.second / d);
2126 }
2127 return r;
2128 }
2129 auto [q, rem] = a.pseudo_divmod(b);
2130 ah_domain_error_if(not rem.is_zero())
2131 << "_integer_exact_quot: divisor does not divide dividend exactly";
2132 return q.is_zero() ? q : q.primitive_part();
2133 }
2134
2136 requires std::is_integral_v<Coefficient>
2137 {
2139 f.for_each_term([&reduced, mod](size_t exp, const Coefficient &coeff)
2140 {
2141 Coefficient c = coeff % mod;
2142 if (c < Coefficient{})
2143 c = c + mod;
2144 if (c != Coefficient{})
2145 reduced.set_coeff(exp, c);
2146 });
2147 return reduced;
2148 }
2149
2151 requires std::is_integral_v<Coefficient>
2152 {
2153 if (mod == 0 or mod == 1)
2154 return 0;
2155
2156 uint64_t result = 0;
2157 f.for_each_term([&result, x, mod](size_t exp, const Coefficient &coeff)
2158 {
2160 const uint64_t xpow = mod_exp(x, exp, mod);
2161 const uint64_t term = mod_mul(c, xpow, mod);
2162 result = (result + term) % mod;
2163 });
2164 return result;
2165 }
2166
2169 Coefficient mod)
2170 requires std::is_integral_v<Coefficient>
2171 {
2172 ah_domain_error_if(f.degree() == 0)
2173 << "divide_by_monic_linear_mod: constant polynomial is not divisible by x - r";
2174
2175 const size_t degree = f.degree();
2177
2178 synthetic(degree) = ((f.get_coeff(degree) % mod) + mod) % mod;
2179
2180 for (size_t idx = degree; idx-- > 0;)
2181 {
2182 Coefficient term = (f.get_coeff(idx) % mod + mod) % mod;
2183 Coefficient next = (term + (synthetic(idx + 1) * root) % mod) % mod;
2184 synthetic(idx) = next;
2185 }
2186
2188 << "divide_by_monic_linear_mod: x - r does not divide polynomial modulo p";
2189
2191 for (size_t exp = 0; exp < degree; ++exp)
2192 if (synthetic(exp + 1) != Coefficient{})
2194
2195 return quotient;
2196 }
2197
2199 requires std::is_integral_v<Coefficient>
2200 {
2201 DynList<Coefficient> result;
2202
2204 if (abs_value == 0)
2205 return result;
2206
2207 for (uint64_t d = 1; d * d <= abs_value; ++d)
2208 if (abs_value % d == 0)
2209 {
2210 result.append(static_cast<Coefficient>(d));
2211 const uint64_t other = abs_value / d;
2212 if (other != d)
2213 result.append(static_cast<Coefficient>(other));
2214 }
2215
2216 return result;
2217 }
2218
2229 requires std::is_integral_v<Coefficient>
2230 {
2231 try
2232 {
2233 auto [q, r] = f.divmod(candidate);
2234 if (not r.is_zero())
2235 return false;
2236 quotient = std::move(q);
2237 return true;
2238 }
2239 catch (const std::domain_error &)
2240 {
2241 return false;
2242 }
2243 }
2244
2246 Coefficient a,
2247 Coefficient b,
2249 requires std::is_integral_v<Coefficient>
2250 {
2251 if (f.is_zero() or f.degree() == 0)
2252 return false;
2253
2254 if (a == Coefficient{})
2255 return false;
2256
2257 const size_t degree = f.degree();
2259
2260 const Coefficient leading = f.get_coeff(degree);
2261 if (leading % a != Coefficient{})
2262 return false;
2263
2264 q_coeffs(degree - 1) = leading / a;
2265
2266 for (size_t k = degree - 1; k > 0; --k)
2267 {
2268 const Coefficient numerator = f.get_coeff(k) - b * q_coeffs(k);
2269 if (numerator % a != Coefficient{})
2270 return false;
2271 q_coeffs(k - 1) = numerator / a;
2272 }
2273
2274 if (f.get_coeff(0) - b * q_coeffs(0) != Coefficient{})
2275 return false;
2276
2278 for (size_t exp = 0; exp < degree; ++exp)
2279 if (q_coeffs(exp) != Coefficient{})
2280 quotient.set_coeff(exp, q_coeffs(exp));
2281
2282 return true;
2283 }
2284
2286 Gen_Polynomial &factor,
2288 requires std::is_integral_v<Coefficient>
2289 {
2290 if (f.is_zero() or f.degree() == 0)
2291 return false;
2292
2293 if (f.get_coeff(0) == Coefficient{})
2294 {
2296 {
2297 factor = Gen_Polynomial(Coefficient(1), 1);
2298 return true;
2299 }
2300 return false;
2301 }
2302
2305
2306 for (auto den_it = denominator_divs.get_it(); den_it.has_curr(); den_it.next_ne())
2307 {
2308 const Coefficient den = den_it.get_curr();
2309
2310 for (auto num_it = numerator_divs.get_it(); num_it.has_curr(); num_it.next_ne())
2311 {
2312 const Coefficient num_abs = num_it.get_curr();
2313
2314 for (Coefficient sign : {Coefficient(1), Coefficient(-1)})
2315 {
2316 const Coefficient num = sign * num_abs;
2318 != 1)
2319 continue;
2320
2322 {
2323 factor = Gen_Polynomial();
2324 factor.set_coeff(0, -num);
2325 factor.set_coeff(1, den);
2326 return true;
2327 }
2328 }
2329 }
2330 }
2331
2332 return false;
2333 }
2334
2344 Gen_Polynomial &factor,
2346 requires std::is_integral_v<Coefficient>
2347 {
2348 if (f.is_zero() or f.degree() < 2)
2349 return false;
2350
2351 if (f.get_coeff(0) == Coefficient{})
2352 return false;
2353
2356 if (leading_divs.is_empty() or constant_divs.is_empty())
2357 return false;
2358
2359 const double abs_lc = std::abs(static_cast<double>(f.leading_coeff()));
2360 double max_ratio = 0.0;
2361 for (auto it = f.coeffs.get_it(); it.has_curr(); it.next_ne())
2362 {
2363 const auto &term = it.get_curr();
2364 if (term.first == f.degree())
2365 continue;
2366 const double ratio = std::abs(static_cast<double>(term.second)) / abs_lc;
2367 if (ratio > max_ratio)
2368 max_ratio = ratio;
2369 }
2370
2371 const double root_bound = 1.0 + max_ratio;
2372
2373 for (auto a_it = leading_divs.get_it(); a_it.has_curr(); a_it.next_ne())
2374 {
2375 const Coefficient a = a_it.get_curr();
2376 const int64_t b_bound
2377 = static_cast<int64_t>(std::ceil(2.0 * std::abs(static_cast<double>(a)) * root_bound));
2378
2379 for (auto c_it = constant_divs.get_it(); c_it.has_curr(); c_it.next_ne())
2380 {
2381 const Coefficient c_abs = c_it.get_curr();
2382
2383 for (Coefficient sign : {Coefficient(1), Coefficient(-1)})
2384 {
2385 const Coefficient c = sign * c_abs;
2386
2387 for (int64_t b_i = -b_bound; b_i <= b_bound; ++b_i)
2388 {
2389 const Coefficient b = static_cast<Coefficient>(b_i);
2390 const uint64_t gcd_ab
2392 if (std::gcd(gcd_ab, polynomial_detail::abs_to_u64(c)) != 1)
2393 continue;
2394
2396 candidate.set_coeff(0, c);
2397 if (b != Coefficient{})
2398 candidate.set_coeff(1, b);
2399 candidate.set_coeff(2, a);
2400
2403 {
2404 factor = candidate.primitive_part();
2405 quotient = q.is_zero() ? q : q.primitive_part();
2406 return true;
2407 }
2408 }
2409 }
2410 }
2411 }
2412
2413 return false;
2414 }
2415
2425 Gen_Polynomial &factor,
2427 requires std::is_integral_v<Coefficient>
2428 {
2429 if (f.is_zero() or f.degree() < 3)
2430 return false;
2431
2432 if (f.get_coeff(0) == Coefficient{})
2433 return false;
2434
2437 if (leading_divs.is_empty() or constant_divs.is_empty())
2438 return false;
2439
2440 const double abs_lc = std::abs(static_cast<double>(f.leading_coeff()));
2441 double l2_norm_sq = 0.0;
2442 for (auto it = f.coeffs.get_it(); it.has_curr(); it.next_ne())
2443 {
2444 const auto &term = it.get_curr();
2445 const double coeff_abs = std::abs(static_cast<double>(term.second));
2447 }
2448
2449 const int64_t coeff_bound = static_cast<int64_t>(std::ceil(8.0 * std::sqrt(l2_norm_sq) / abs_lc));
2450 if (coeff_bound <= 0)
2451 return false;
2452
2453 for (auto a_it = leading_divs.get_it(); a_it.has_curr(); a_it.next_ne())
2454 {
2455 const Coefficient a = a_it.get_curr();
2456 if (polynomial_detail::abs_to_u64(a) > static_cast<uint64_t>(coeff_bound))
2457 continue;
2458
2459 for (auto d_it = constant_divs.get_it(); d_it.has_curr(); d_it.next_ne())
2460 {
2461 const Coefficient d_abs = d_it.get_curr();
2463 continue;
2464
2465 for (Coefficient sign : {Coefficient(1), Coefficient(-1)})
2466 {
2467 const Coefficient d = sign * d_abs;
2468
2469 for (int64_t b_i = -coeff_bound; b_i <= coeff_bound; ++b_i)
2470 {
2471 const Coefficient b = static_cast<Coefficient>(b_i);
2472 const uint64_t gcd_ab
2474
2475 for (int64_t c_i = -coeff_bound; c_i <= coeff_bound; ++c_i)
2476 {
2477 const Coefficient c = static_cast<Coefficient>(c_i);
2479 if (std::gcd(gcd_abc, polynomial_detail::abs_to_u64(d)) != 1)
2480 continue;
2481
2483 candidate.set_coeff(0, d);
2484 if (c != Coefficient{})
2485 candidate.set_coeff(1, c);
2486 if (b != Coefficient{})
2487 candidate.set_coeff(2, b);
2488 candidate.set_coeff(3, a);
2489
2492 {
2493 factor = candidate.primitive_part();
2494 quotient = q.is_zero() ? q : q.primitive_part();
2495 return true;
2496 }
2497 }
2498 }
2499 }
2500 }
2501 }
2502
2503 return false;
2504 }
2505
2524 requires std::is_integral_v<Coefficient>
2525 {
2526 ah_domain_error_if(p <= Coefficient(1)) << "factor_mod_p: modulus p must be > 1, got " << p;
2527
2529
2530 if (f.is_zero() or f.is_constant())
2531 return result;
2532
2533 Gen_Polynomial remaining = reduce_coeffs_mod(f, p);
2534
2535 for (Coefficient r = Coefficient{}; r < p; r = r + Coefficient(1))
2536 {
2537 while (
2538 not remaining.is_zero() and remaining.degree() > 0
2539 and eval_mod_u64(remaining,
2542 == 0)
2543 {
2544 Gen_Polynomial factor;
2545 factor.set_coeff(0, ((-r) % p + p) % p);
2546 factor.set_coeff(1, Coefficient(1));
2547 result.append(factor);
2548 remaining = divide_by_monic_linear_mod(remaining, r, p);
2549 }
2550 }
2551
2552 if (not remaining.is_zero() and not remaining.is_constant())
2553 result.append(remaining);
2554
2555 return result;
2556 }
2557
2571 {
2572 if (is_zero())
2573 return 0.0;
2574
2575 const size_t d = degree();
2576 if (d == 0)
2577 return 0.0;
2578
2580 for (auto it = coeffs.get_it(); it.has_curr(); it.next_ne())
2581 {
2582 auto &p = it.get_curr();
2583 Coefficient abs_c = p.second < Coefficient{} ? -p.second : p.second;
2584 if (abs_c > max_coeff)
2585 max_coeff = abs_c;
2586 }
2587
2588 const auto d_real = static_cast<double>(d);
2589 const auto max_real = static_cast<double>(max_coeff);
2590 const double two_pow_d = polynomial_detail::power(2.0, d);
2591
2592 return std::sqrt(d_real + 1.0) * two_pow_d * max_real;
2593 }
2594
2618 Coefficient p,
2619 size_t levels)
2620 requires std::is_integral_v<Coefficient>
2621 {
2622 ah_domain_error_if(p <= Coefficient(1)) << "hensel_lift: modulus p must be > 1, got " << p;
2623 ah_domain_error_if(levels == 0)
2624 << "hensel_lift: levels must be >= 1, got " << levels;
2625 ah_domain_error_if(levels > 30)
2626 << "hensel_lift: levels must be <= 30 to avoid overflow, got " << levels;
2627
2628 if (factors.is_empty())
2629 return factors;
2630
2631 const size_t target_steps = static_cast<size_t>(1) << levels;
2633 const auto checked_mul_u64 = [](uint64_t lhs, uint64_t rhs, const char *name) -> uint64_t
2634 {
2635 ah_domain_error_if(rhs != 0 and lhs > std::numeric_limits<uint64_t>::max() / rhs)
2636 << "hensel_lift: overflow computing " << name << " (" << lhs << " * " << rhs << ")";
2637 return lhs * rhs;
2638 };
2639
2641 for (size_t i = 1; i < target_steps; ++i)
2642 target_u64 = checked_mul_u64(target_u64, p_u64, "target_u64");
2643
2644 ah_domain_error_if(target_u64 > static_cast<uint64_t>(std::numeric_limits<Coefficient>::max()))
2645 << "hensel_lift: target modulus " << target_u64
2646 << " does not fit in coefficient type";
2647 const Coefficient p_power = static_cast<Coefficient>(target_u64);
2649
2651 for (auto it = factors.get_it(); it.has_curr(); it.next_ne())
2652 {
2653 const Gen_Polynomial &factor = it.get_curr();
2654 bool lifted_linear_factor = false;
2655
2656 if (factor.degree() == 1 and factor.get_coeff(1) == Coefficient(1))
2657 {
2660 bool simple = true;
2661
2662 for (size_t step = 1; step < target_steps; ++step)
2663 {
2664 const uint64_t next_mod = checked_mul_u64(modulus, p_u64, "next_mod");
2667
2668 if (f_val % modulus != 0 or std::gcd(deriv_modp, p_u64) != 1)
2669 {
2670 simple = false;
2671 break;
2672 }
2673
2674 const uint64_t e = (f_val / modulus) % p_u64;
2676 const uint64_t t = (p_u64 - mod_mul(e, inv, p_u64)) % p_u64;
2677
2678 const uint64_t increment = checked_mul_u64(modulus, t, "root increment");
2679 root = static_cast<uint64_t>((static_cast<unsigned __int128>(root) + increment)
2680 % next_mod);
2681 modulus = next_mod;
2682 }
2683
2684 if (simple)
2685 {
2687 lifted.set_coeff(0,
2688 polynomial_detail::centered_from_mod_u64<Coefficient>(
2690 lifted.set_coeff(1, Coefficient(1));
2691 result.append(lifted);
2692 lifted_linear_factor = true;
2693 }
2694 }
2695
2697 continue;
2698
2700 factor.for_each_term([&normalized, p_power](size_t exp, const Coefficient &coeff)
2701 {
2703 if (canonical < Coefficient{})
2704 canonical += p_power;
2705 if (canonical != Coefficient{})
2706 normalized.set_coeff(exp, canonical);
2707 });
2708 result.append(normalized);
2709 }
2710
2711 return result;
2712 }
2713
2746 {
2747 DynList<SfdTerm> result;
2748
2749 if (is_zero() or is_constant())
2750 return result;
2751
2752 const Coefficient c = content();
2753 if (c != Coefficient(1))
2754 result.append(SfdTerm{Gen_Polynomial(c), 1});
2755
2756 // Step 1: Get primitive part
2758
2759 // Step 2: Compute square-free decomposition
2760 DynList<SfdTerm> sfd_terms = prim.yun_sfd();
2761
2762 if (sfd_terms.is_empty())
2763 return result;
2764
2765 // Step 3: For each square-free group, attempt factorization
2766 for (auto it = sfd_terms.get_it(); it.has_curr(); it.next_ne())
2767 {
2768 auto &sfd_term = it.get_curr();
2769 Gen_Polynomial factor = sfd_term.factor;
2770 size_t mult = sfd_term.multiplicity;
2771 Gen_Polynomial remaining = factor.primitive_part();
2772
2773 while (remaining.degree() > 0)
2774 {
2777 break;
2778
2779 result.append(SfdTerm{exact_linear.primitive_part(), mult});
2780 remaining = quotient.is_zero() ? quotient : quotient.primitive_part();
2781 }
2782
2783 while (remaining.degree() > 1)
2784 {
2787 break;
2788
2789 result.append(SfdTerm{exact_quadratic.primitive_part(), mult});
2790 remaining = quotient.is_zero() ? quotient : quotient.primitive_part();
2791 }
2792
2793 while (remaining.degree() > 2)
2794 {
2797 break;
2798
2799 result.append(SfdTerm{exact_cubic.primitive_part(), mult});
2800 remaining = quotient.is_zero() ? quotient : quotient.primitive_part();
2801 }
2802
2803 if (remaining.is_constant() or remaining.is_zero())
2804 continue;
2805
2806 // Try modular factorization with a few small primes and exact redivision.
2807 for (Coefficient p : {Coefficient(3), Coefficient(5), Coefficient(7), Coefficient(11)})
2808 {
2809 if (remaining.leading_coeff() % p == Coefficient{})
2810 continue;
2811
2812 auto mod_factors = factor_mod_p(remaining, p);
2813 if (mod_factors.size() <= 1)
2814 continue;
2815
2816 auto lifted = hensel_lift(remaining, mod_factors, p, 2);
2817
2818 bool changed = false;
2819 for (auto jt = lifted.get_it(); jt.has_curr(); jt.next_ne())
2820 {
2821 Gen_Polynomial cand = jt.get_curr().primitive_part();
2822 if (cand.is_zero() or cand.is_constant() or cand.degree() != 1)
2823 continue;
2824
2827 remaining, cand.get_coeff(1), cand.get_coeff(0), quotient))
2828 continue;
2829
2830 result.append(SfdTerm{cand, mult});
2831 remaining = quotient.is_zero() ? quotient : quotient.primitive_part();
2832 changed = true;
2833
2834 if (remaining.is_constant())
2835 break;
2836 }
2837
2838 if (changed)
2839 {
2840 if (remaining.is_constant())
2841 break;
2842 }
2843 }
2844
2845 if (not remaining.is_constant() and not remaining.is_zero())
2846 result.append(SfdTerm{remaining.primitive_part(), mult});
2847 }
2848
2849 return result;
2850 }
2851};
2852
2853// =================================================================
2854// Free functions
2855// =================================================================
2856
2858template <typename C>
2860{
2861 return p * s;
2862}
2863
2865template <typename C>
2867{
2868 return p + s;
2869}
2870
2872template <typename C>
2874{
2875 return Gen_Polynomial<C>(s) - p;
2876}
2877
2879template <typename C>
2880std::ostream &operator<<(std::ostream &out, const Gen_Polynomial<C> &p)
2881{
2882 return out << p.to_str();
2883}
2884
2885// =================================================================
2886// Convenience type aliases
2887// =================================================================
2888
2891
2894
2897
2900
2901} // end namespace Aleph
2902
2903#endif // TPL_POLYNOMIAL_H
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
long double h
Definition btreepic.C:154
long double w
Definition btreepic.C:153
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
void putn(const size_t n)
Reserve n additional logical slots in the array without value-initializing them.
Definition tpl_array.H:305
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
Generic key-value map implemented on top of a binary search tree.
Univariate polynomial over a generic coefficient ring.
Gen_Polynomial nth_derivative(size_t n) const
-th derivative .
std::pair< size_t, Coefficient > Term
static Gen_Polynomial divide_by_monic_linear_mod(const Gen_Polynomial &f, Coefficient root, Coefficient mod)
Gen_Polynomial(const DynList< std::pair< size_t, Coefficient > > &term_list)
Sparse construction from a list of (exponent, coefficient) pairs.
DynList< Gen_Polynomial > sturm_chain() const
Sturm sequence of this polynomial.
bool is_monic() const noexcept
True if leading coefficient equals 1.
Coefficient horner_eval(const Coefficient &x) const
Evaluate using gap-aware Horner's method.
Gen_Polynomial negate_arg() const
Argument negation: .
static Gen_Polynomial zero()
The zero polynomial.
void for_each_term(Op &&op) const
Iterate over non-zero terms in ascending exponent order.
static Gen_Polynomial one()
The constant polynomial 1.
Coefficient content() const
Content: GCD of all coefficients.
void divide_scalar_inplace(const Coefficient &s)
Divide every coefficient in place by a scalar.
size_t num_terms() const noexcept
Number of non-zero terms.
void add_scaled_shifted(const Gen_Polynomial &src, const Coefficient &scale, size_t shift=0)
Add scale times src shifted by shift to this polynomial.
DynList< size_t > exponents() const
All exponents with non-zero coefficients (sorted ascending).
void add_to_coeff(size_t exp, const Coefficient &delta)
Add a delta to one coefficient, erasing the term if it cancels.
Coefficient * coeff_ptr(size_t exp) noexcept
Pointer to stored coefficient, or null if absent.
Gen_Polynomial operator/(const Coefficient &s) const
Divide by a scalar.
static DynList< Gen_Polynomial > factor_mod_p(const Gen_Polynomial &f, Coefficient p)
Factorization over integers mod p (trial method).
Gen_Polynomial primitive_part() const
Primitive part: polynomial divided by its content.
Coefficient cauchy_bound() const
Cauchy upper bound on absolute value of roots.
Gen_Polynomial & operator-=(const Gen_Polynomial &q)
In-place subtraction.
Gen_Polynomial & operator-=(const Coefficient &c)
In-place subtraction of a scalar constant term.
Gen_Polynomial & operator*=(const Coefficient &s)
In-place scalar multiplication.
static bool try_extract_rational_linear_factor(const Gen_Polynomial &f, Gen_Polynomial &factor, Gen_Polynomial &quotient)
void remove_zeros()
Remove all entries whose coefficient is (approximately) zero.
Coefficient eval(const Coefficient &x) const
Evaluate with adaptive strategy.
Gen_Polynomial operator/(const Gen_Polynomial &d) const
Polynomial quotient.
Coefficient coeff_at(size_t exp) const
Read coefficient at exponent (0 if absent).
Gen_Polynomial & operator+=(const Gen_Polynomial &q)
In-place addition.
Gen_Polynomial truncate(size_t n) const
Truncate to degree less than n.
std::string to_str(const std::string &var="x") const
Human-readable string representation.
Gen_Polynomial operator-(const Gen_Polynomial &q) const
Polynomial subtraction.
static Gen_Polynomial from_roots(const DynList< Coefficient > &roots)
Build polynomial from its roots: .
static Gen_Polynomial interpolate(const DynList< std::pair< Coefficient, Coefficient > > &points)
Polynomial interpolation through a set of points.
Gen_Polynomial operator*(const Gen_Polynomial &q) const
Polynomial multiplication.
bool is_constant() const noexcept
True if constant or zero.
size_t count_real_roots(const Coefficient &a, const Coefficient &b) const
Count distinct real roots in via Sturm's theorem.
void scale_inplace(const Coefficient &s)
Multiply every coefficient in place by a scalar.
Gen_Polynomial pow(size_t n) const
Exponentiation by repeated squaring.
Coefficient operator[](size_t exp) const
Coefficient at exponent exp (0 if absent).
DynList< SfdTerm > yun_sfd() const
Yun's square-free decomposition (SFD).
Gen_Polynomial operator+(const Coefficient &c) const
Add a scalar constant term.
size_t sign_variations() const
Count sign changes in the coefficient sequence.
bool is_monomial() const noexcept
True if exactly one non-zero term.
bool has_term(size_t exp) const noexcept
True if there is a non-zero coefficient at exp.
const Coefficient * coeff_ptr(size_t exp) const noexcept
Pointer to stored coefficient, or null if absent.
static Xgcd_Result xgcd(Gen_Polynomial a, Gen_Polynomial b)
static Gen_Polynomial integer_gcd(Gen_Polynomial a, Gen_Polynomial b)
Polynomial GCD over integers (Euclidean algorithm with primitive parts).
double mignotte_bound() const
Mignotte bound on integer roots.
Gen_Polynomial compose(const Gen_Polynomial &q) const
Composition .
Coefficient sparse_eval(const Coefficient &x) const
Evaluate using sparse evaluation.
static constexpr Coefficient epsilon() noexcept
Tolerance threshold for floating-point zero detection.
Coefficient definite_integral(const Coefficient &a, const Coefficient &b) const
Definite integral .
bool is_zero() const noexcept
True if this is the zero polynomial.
Gen_Polynomial operator*(const Coefficient &s) const
Multiply by a scalar.
static bool try_exact_candidate_division(const Gen_Polynomial &f, const Gen_Polynomial &candidate, Gen_Polynomial &quotient)
Try exact division but treat inexact integral division as a rejected candidate.
Gen_Polynomial & operator+=(const Coefficient &c)
In-place addition of a scalar constant term.
Gen_Polynomial operator+(const Gen_Polynomial &q) const
Polynomial addition.
size_t count_all_real_roots() const
Total number of distinct real roots.
static size_t sturm_sign_changes(const DynList< Gen_Polynomial > &chain, const Coefficient &x)
Count sign changes in a Sturm chain at a given point.
static Gen_Polynomial _integer_exact_quot(const Gen_Polynomial &a, const Gen_Polynomial &b)
Integer-safe exact quotient using pseudo-division.
Gen_Polynomial(const DynList< Coefficient > &l)
Dense construction from DynList.
Coefficient bisect_root(Coefficient a, Coefficient b, Coefficient tol=Coefficient(1e-12), size_t max_iter=200) const
Find a root by bisection in .
void for_each_term_desc(Op &&op) const
Iterate over non-zero terms in descending exponent order.
Gen_Polynomial(const Coefficient &c)
Constant polynomial .
size_t degree() const noexcept
Degree of the polynomial.
DynList< SfdTerm > factorize() const
Main factorization over integers.
Gen_Polynomial shift_down(size_t k) const
Divide by (shift exponents down).
static DynList< Gen_Polynomial > hensel_lift(const Gen_Polynomial &f, DynList< Gen_Polynomial > factors, Coefficient p, size_t levels)
Hensel lifting of modular factors.
DynList< std::pair< size_t, Coefficient > > terms_list() const
All non-zero terms as a sorted list of (exponent, coefficient).
std::pair< Gen_Polynomial, Gen_Polynomial > divmod(const Gen_Polynomial &d) const
Polynomial long division: returns (quotient, remainder).
static bool try_extract_primitive_quadratic_factor(const Gen_Polynomial &f, Gen_Polynomial &factor, Gen_Polynomial &quotient)
Try to extract an exact primitive quadratic factor over .
Gen_Polynomial & operator/=(const Coefficient &s)
In-place scalar division.
static Gen_Polynomial lcm(const Gen_Polynomial &a, const Gen_Polynomial &b)
Least common multiple.
static uint64_t eval_mod_u64(const Gen_Polynomial &f, uint64_t x, uint64_t mod)
Array< Coefficient > to_dense() const
Dense coefficient vector.
Gen_Polynomial shift_up(size_t k) const
Multiply by (shift exponents up).
DynMapTree< size_t, Coefficient > coeffs
Gen_Polynomial shift(const Coefficient &k) const
Taylor shift: .
static Gen_Polynomial gcd(Gen_Polynomial a, Gen_Polynomial b)
Greatest common divisor (Euclidean algorithm).
Gen_Polynomial square_free() const
Square-free part: .
Gen_Polynomial derivative() const
Formal derivative .
DynList< Coefficient > multi_eval(const DynList< Coefficient > &points) const
Evaluate at multiple points.
std::pair< Gen_Polynomial, Gen_Polynomial > pseudo_divmod(const Gen_Polynomial &d) const
Pseudo-division for non-field coefficient types.
Gen_Polynomial & operator*=(const Gen_Polynomial &q)
In-place multiplication.
Gen_Polynomial integral(const Coefficient &C=Coefficient{}) const
Formal indefinite integral with constant of integration.
Gen_Polynomial operator-() const
Unary negation.
static bool coeff_is_zero(const Coefficient &c) noexcept
Test whether a coefficient is (approximately) zero.
static Gen_Polynomial x_to(size_t n)
The monomial .
static bool try_divide_by_linear_factor(const Gen_Polynomial &f, Coefficient a, Coefficient b, Gen_Polynomial &quotient)
bool operator!=(const Gen_Polynomial &q) const noexcept
Inequality comparison.
Gen_Polynomial()=default
Default constructor: the zero polynomial.
Gen_Polynomial multiply_by_monic_linear(const Coefficient &c) const
Specialized multiplication by a monic linear factor .
static bool try_extract_primitive_cubic_factor(const Gen_Polynomial &f, Gen_Polynomial &factor, Gen_Polynomial &quotient)
Try to extract an exact primitive cubic factor over .
Gen_Polynomial & operator/=(const Gen_Polynomial &d)
In-place polynomial quotient.
bool operator==(const Gen_Polynomial &q) const noexcept
Equality comparison.
Coefficient leading_coeff() const noexcept
Leading coefficient (of the highest-degree term).
Gen_Polynomial to_monic() const
Make monic: divide by leading coefficient.
Gen_Polynomial & operator%=(const Gen_Polynomial &d)
In-place polynomial remainder.
Coefficient newton_root(Coefficient x0, Coefficient tol=Coefficient(1e-12), size_t max_iter=100) const
Find a root by Newton-Raphson iteration.
Gen_Polynomial(std::initializer_list< Coefficient > il)
Dense construction from initializer list.
Gen_Polynomial operator%(const Gen_Polynomial &d) const
Polynomial remainder.
Coefficient operator()(const Coefficient &x) const
Syntactic sugar: p(x) calls eval.
static Gen_Polynomial reduce_coeffs_mod(const Gen_Polynomial &f, Coefficient mod)
Gen_Polynomial operator-(const Coefficient &c) const
Subtract a scalar constant term.
void set_coeff(size_t exp, const Coefficient &c)
Set coefficient at exponent; removes entry if zero.
Gen_Polynomial reverse() const
Reciprocal polynomial: .
DynList< Coefficient > coefficients() const
All non-zero coefficients in ascending exponent order.
Gen_Polynomial(const Coefficient &c, size_t exp)
Monomial .
static DynList< Coefficient > positive_divisors(Coefficient value)
Coefficient get_coeff(size_t exp) const noexcept
Coefficient accessor (read-only at exponent).
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
Definition ah-dry.H:779
__gmp_expr< T, __gmp_unary_expr< __gmp_expr< T, U >, __gmp_exp_function > > exp(const __gmp_expr< T, U > &expr)
Definition gmpfrxx.h:4066
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
Singly linked list implementations with head-tail access.
Safe modular arithmetic, extended Euclidean algorithm, and Chinese Remainder Theorem.
C power(C base, size_t exp)
Fast exponentiation by squaring.
uint64_t normalize_mod_u64(Int value, const uint64_t modulus) noexcept
uint64_t abs_to_u64(Int value) noexcept
Int centered_from_mod_u64(const uint64_t value, const uint64_t modulus) noexcept
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Gen_MultiPolynomial< C, M > operator+(const C &s, const Gen_MultiPolynomial< C, M > &p)
Scalar + polynomial (commutative).
uint64_t mod_inv(const uint64_t a, const uint64_t m)
Modular Inverse.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Gen_MultiPolynomial< C, M > operator-(const C &s, const Gen_MultiPolynomial< C, M > &p)
Scalar - polynomial.
Matrix< Trow, Tcol, NumType > operator*(const NumType &scalar, const Matrix< Trow, Tcol, NumType > &m)
Scalar-matrix multiplication (scalar * matrix).
Definition al-matrix.H:995
uint64_t mod_exp(uint64_t base, uint64_t exp, const uint64_t m)
Modular exponentiation.
uint64_t mod_mul(uint64_t a, uint64_t b, uint64_t m)
Safe 64-bit modular multiplication.
void next()
Advance all underlying iterators (bounds-checked).
Definition ah-zip.H:171
std::ostream & operator<<(std::ostream &osObject, const Field< T > &rightOp)
Definition ahField.H:121
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
STL namespace.
Square-free decomposition term: factor and multiplicity.
Extended GCD: computes g, s, t such that .
Gen_Polynomial t
Bezout coefficient for the second argument.
Gen_Polynomial s
Bezout coefficient for the first argument.
Gen_Polynomial g
Greatest common divisor (monic).
Aleph::DynList< T > keys() const
Definition ah-dry.H:1729
static int * k
gsl_rng * r
Dynamic array container with automatic resizing.
Dynamic key-value map based on balanced binary search trees.
DynList< int > l