1/*==============================================================================
2|
3| NAME
4|
5| Primpoly.cpp
6|
7| DESCRIPTION
8|
9| Program for finding primitive polynomials of degree n modulo p for
10| any prime p >= 2 and any n >= 2.
11|
12| Useful for generating PN sequences and finite fields for error control coding.
13|
14| Please see the user manual and complete technical documentation at
15|
16| http://seanerikoconnor.freeservers.com/Mathematics/AbstractAlgebra/PrimitivePolynomials/overview.html
17|
18| This is a console app to be run in a terminal window. The OS calls main(), which returns a status to the OS.
19|
20| LEGAL
21|
22| Primpoly Version 16.3 - A Program for Computing Primitive Polynomials.
23| Copyright (C) 1999-2024 by Sean Erik O'Connor. All Rights Reserved.
24|
25| This program is free software: you can redistribute it and/or modify
26| it under the terms of the GNU General Public License as published by
27| the Free Software Foundation, either version 3 of the License, or
28| (at your option) any later version.
29|
30| This program is distributed in the hope that it will be useful,
31| but WITHOUT ANY WARRANTY; without even the implied warranty of
32| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33| GNU General Public License for more details.
34|
35| You should have received a copy of the GNU General Public License
36| along with this program. If not, see http://www.gnu.org/licenses/.
37|
38| The author's address is seanerikoconnor!AT!gmail!DOT!com
39| with the !DOT! replaced by . and the !AT! replaced by @
40|
41==============================================================================*/
42
43
44/*------------------------------------------------------------------------------
45| Include Files |
46------------------------------------------------------------------------------*/
47
48#include <cstdlib> // abort()
49#include <iostream> // Basic stream I/O.
50#include <new> // set_new_handler()
51#include <cmath> // Basic math functions e.g. sqrt()
52#include <limits> // Numeric limits.
53#include <complex> // Complex data type and operations.
54#include <fstream> // File stream I/O.
55#include <sstream> // String stream I/O.
56#include <vector> // STL vector class.
57#include <string> // STL string class.
58#include <algorithm> // Iterators.
59#include <stdexcept> // Exceptions.
60#include <cassert> // assert()
61
62using namespace std ; // I don't want to use the std:: prefix everywhere.
63
64#include "ctype.h" // C string functions.
65
66
67/*------------------------------------------------------------------------------
68| PP Include Files |
69------------------------------------------------------------------------------*/
70
71#include "Primpoly.hpp" // Global functions.
72#include "ppArith.hpp" // Basic arithmetic functions.
73#include "ppBigInt.hpp" // Arbitrary precision integer arithmetic.
74#include "ppOperationCount.hpp" // OperationCount collection for factoring and poly finding.
75#include "ppFactor.hpp" // Prime factorization and Euler Phi.
76#include "ppPolynomial.hpp" // Polynomial operations and mod polynomial operations.
77#include "ppParser.hpp" // Parsing of polynomials and I/O services.
78#include "ppUnitTest.hpp" // Complete unit test.
79
80
81/*=============================================================================
82|
83| NAME
84|
85| Message strings.
86|
87| DESCRIPTION
88|
89| Helpful information returned by top level main() function.
90|
91+============================================================================*/
92
93static const string legalNotice
94{
95 "\n"
96 "Primpoly Version 16.3 - A Program for Computing Primitive Polynomials.\n"
97 "Copyright (C) 1999-2024 by Sean Erik O'Connor. All Rights Reserved.\n"
98 "\n"
99 "Primpoly comes with ABSOLUTELY NO WARRANTY; for details see the\n"
100 "GNU General Public License. This is free software, and you are welcome\n"
101 "to redistribute it under certain conditions; see the GNU General Public License\n"
102 "for details.\n\n"
103} ;
104
105static const string helpText
106{
107 "This program generates primitive polynomials of degree n modulo p.\n"
108 "\n"
109 "Usage: Generate a single random polynomial of degree n modulo p where p is a prime >= 2 and n is an integer >= 2\n"
110 " Primpoly p n\n"
111 "Example:\n"
112 " Primpoly 2 4\n"
113 " Self-check passes...\n"
114 " Primitive polynomial modulo 2 of degree 4\n"
115 " x ^ 4 + x + 1, 2\n"
116 "Usage: Test whether a polynomial is primitive modulo p.\n"
117 " Primpoly -t ""<Polynomial to test>, p""\n"
118 " If you leave off the modulus p we default to p = 2\n"
119 "Examples:\n"
120 " Primpoly -t ""x^4 + x + 1, 2""\n"
121 " Self-check passes...\n"
122 " x ^ 4 + x + 1, 2 is primitive!\n"
123 "\n"
124 " Primpoly -t ""x^4 + x + 1""\n"
125 " Self-check passes...\n"
126 " x ^ 4 + x + 1, 2 is primitive!\n"
127 "Usage: Generate all primitive polynomial of degree n modulo p.\n"
128 " Primpoly -a p n\n"
129 "Example:\n"
130 " Primpoly -a 2 4\n"
131 " Self-check passes...\n"
132 " Primitive polynomial modulo 2 of degree 4\n"
133 " x ^ 4 + x + 1, 2\n"
134 " Primitive polynomial modulo 2 of degree 4\n"
135 " x ^ 4 + x ^ 3 + 1, 2\n"
136 "Usage: Same but show computation statistics.\n"
137 " Primpoly -s p n\n"
138 "Examples: \n"
139 "\n"
140 " Primpoly.exe -s 13 19\n"
141 " Self-check passes...\n"
142 " Primitive polynomial modulo 13 of degree 19\n"
143 " x ^ 19 + 9 x + 2, 13\n"
144 "\n"
145 " +--------- OperationCount --------------------------------\n"
146 " |\n"
147 " | Integer factorization: Table lookup + Trial division + Pollard Rho\n"
148 " |\n"
149 " | Number of trial divisions : 0\n"
150 " | Number of gcd's computed : 9027\n"
151 " | Number of primality tests : 2\n"
152 " | Number of squarings: 9026\n"
153 " |\n"
154 " | Polynomial Testing\n"
155 " |\n"
156 " | Total num. degree 19 poly mod 13 : 1461920290375446110677\n"
157 " | Number of possible primitive poly: 6411930599771980992\n"
158 " | Polynomials tested : 120\n"
159 " | Const. coeff. was primitive root : 46\n"
160 " | Free of linear factors : 11\n"
161 " | Irreducible to power >=1 : 1\n"
162 " | Had order r (x^r = integer) : 1\n"
163 " | Passed const. coeff. test : 1\n"
164 " | Had order m (x^m != integer) : 1\n"
165 " |\n"
166 " +-----------------------------------------------------\n"
167 "Usage: Print help message.\n"
168 " Primpoly -h\n"
169 " <Prints this help message.>\n"
170 "\n\n"
171 "Primitive polynomials find many uses in mathematics and communications\n"
172 "engineering:\n"
173 " * Generation of pseudonoise (PN) sequences for spread spectrum\n"
174 " communications and chip fault testing.\n"
175 " * Generating Sobol sequences for high dimensional numerical integration.\n"
176 " * Generation of CRC and Hamming codes.\n"
177 " * Generation of Galois (finite) fields for use in decoding Reed-Solomon\n"
178 " and BCH error correcting codes.\n"
179 "\n"
180 "For detailed technical information, see my web page\n"
181 " http://seanerikoconnor.freeservers.com/Mathematics/AbstractAlgebra/PrimitivePolynomials/overview.html\n"
182 "\n"
183} ;
184
185
186/*=============================================================================
187|
188| NAME
189|
190| main
191|
192| DESCRIPTION
193|
194| Program for finding primitive polynomials of degree n modulo p for
195| any prime p >= 2 and any n >= 2.
196|
197| Useful for generating PN sequences and finite fields for error control coding.
198|
199| Run the program by typing
200|
201| ./Primpoly p n
202|
203| You will get an nth degree primitive polynomial modulo p.
204|
205| To see all the options type
206|
207| ./Primpoly -h
208|
209| Please see the user manual and complete technical documentation at
210|
211| http://seanerikoconnor.freeservers.com/Mathematics/AbstractAlgebra/PrimitivePolynomials/overview.html
212|
213| The author's address is seanerikoconnor!AT!gmail!DOT!com
214| with the !DOT! replaced by . and the !AT! replaced by @
215|
216+============================================================================*/
217
218int main( int argc, const char * argv[] )
219{
220 try
221 {
222 // Make my lawyers happy.
223 cout << legalNotice ;
224
225 // Set up the full parser for both command line parsing and polynomial parsing.
226 PolyParser<PolySymbol, PolyValue> parser ;
227 parser.parseCommandLine( argc, argv ) ;
228
229 // Print the help message only and exit.
230 if (parser.print_help_)
231 {
232 cout << helpText ;
233 return static_cast<int>( ReturnStatus::AskForHelp ) ;
234 }
235
236 #ifdef SELF_CHECK
237 // Do a self check always. We might fail to pass one or more unit tests, or the unit test itself might fail.
238 try
239 {
240 UnitTest unitTest ;
241 if (!unitTest.run())
242 throw PrimpolyError( "Self-check failed!" ) ;
243 else
244 cout << "Self-check passes..." << endl ;
245 }
246 catch (UnitTestError & e)
247 {
248 throw PrimpolyError( static_cast<string>( "Could not run the self-check!\n" ) + " [ " + e.what() + " ] " ) ;
249 }
250 #endif
251
252 // The user input a polynomial. Test it for primitivity.
253 if (parser.test_polynomial_for_primitivity_)
254 {
255 // Test for primitivity with the quick test.
256 Polynomial f( parser.test_polynomial_ ) ;
257 #ifdef DEBUG_PP_PRIMITIVITY
258 cout << "Factoring into primes r = (p^n-1)/(p-1) = " << " for n = " << parser.n << " p = " << parser.p << endl ;
259 #endif // DEBUG_PP_PRIMITIVITY
260 PolyOrder order( f ) ;
261 cout << f << " is " << (order.isPrimitive() ? "" : "NOT") << " primitive!" << endl ;
262
263 if (parser.print_operation_count_)
264 cout << order.statistics_ << endl ;
265
266 // Also do a very slow maximal order test for primitivity, if asked to do so.
267 if (parser.slow_confirm_)
268 {
269 cout << confirmWarning ;
270 cout << " confirmed " << (order.maximal_order() ? "" : "NOT") << " primitive!" << endl ;
271 }
272 }
273 // Find one primitive polynomial at random. Optionally, find all primitive polynomials.
274 else
275 {
276 // Generate and test all possible n th degree, monic, modulo p polynomials
277 // f(x). A polynomial is primitive if passes all the tests successfully.
278 // n
279 // Initialize f(x) to x + (-1). Then, when f(x) passes through function
280 // n
281 // nextTrialPoly for the first time, it will have the correct value, x
282 TrialPolynomial f ;
283 f.initialTrialPoly( parser.n, parser.p ) ;
284 #ifdef DEBUG_PP_PRIMITIVITY
285 cout << "Factoring into primes r = (p^n-1)/(p-1) = " << " for n = " << parser.n << " p = " << parser.p << endl ;
286 #endif // DEBUG_PP_PRIMITIVITY
287 PolyOrder order( f ) ;
288
289 bool is_primitive_poly{ false } ;
290 bool tried_all_poly{ false } ;
291 bool stopTesting{ false } ;
292
293 BigInt num_poly( 0u ) ;
294 BigInt num_primitive_poly( 0u ) ;
295
296 if (parser.list_all_primitive_polynomials_)
297 cout << "\n\nThere are " << order.getNumPrimPoly() << " primitive polynomials modulo " << f.modulus() << " of degree " << f.deg() << "\n\n" ;
298
299 do {
300 ++num_poly ;
301
302 #ifdef DEBUG_PP_PRIMITIVITY
303 cout << "Testing polynomial # " << num_poly << ") p(x) = " << f << " for primitivity" << endl ;
304 #endif // DEBUG_PP_PRIMITIVITY
305
306 order.resetPolynomial( f ) ;
307 is_primitive_poly = order.isPrimitive() ;
308
309 if (is_primitive_poly)
310 {
311 ++num_primitive_poly ;
312 cout << "\n\nPrimitive polynomial modulo " << f.modulus() << " of degree " << f.deg() << "\n\n" ;
313 cout << f ;
314 cout << endl << endl ;
315
316 // Do a very slow maximal order test for primitivity.
317 if (parser.slow_confirm_)
318 {
319 cout << confirmWarning ;
320 if (order.maximal_order())
321 cout << f << " confirmed primitive!" << endl ;
322 else
323 {
324 ostringstream os ;
325 os << "Fast test says " << f << " is a primitive polynomial but slow test disagrees.\n" ;
326 throw PolynomialError( os.str() ) ;
327 }
328 }
329
330 // Early out if we've found all the primitive polynomials.
331 if (num_primitive_poly >= order.getNumPrimPoly())
332 break ;
333 }
334
335 tried_all_poly = (num_poly >= order.getMaxNumPoly()) ;
336 stopTesting = tried_all_poly || (!parser.list_all_primitive_polynomials_ && is_primitive_poly) ;
337
338 f.nextTrialPoly() ; // Try next polynomal in sequence.
339 } while( !stopTesting ) ;
340
341 if (parser.print_operation_count_)
342 cout << order.statistics_ << endl ;
343
344 // Didn't find a primitive polynomial in the find-only-one-primitive-polynomial case, which is an error.
345 if (!parser.list_all_primitive_polynomials_ && !is_primitive_poly)
346 {
347 ostringstream os ;
348 os << "Tested all " << order.getMaxNumPoly() << " possible polynomials, but failed to find a primitive polynomial" ;
349 throw PolynomialError( os.str() ) ;
350 }
351 }
352 return static_cast<int>( ReturnStatus::Success ) ;
353 }
354 // Catch all exceptions and report what happened to the user.
355 // First do the user-defined exceptions.
356 catch( PrimpolyError & e )
357 {
358 cerr << "\nTop Level Error: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
359 return static_cast<int>( ReturnStatus::InternalError ) ;
360 }
361 catch( ParserError & e )
362 {
363 cerr << "Inputs are incorrect or out of range: " << " [ " << e.what() << " ] " << endl << helpText ;
364 return static_cast<int>( ReturnStatus::RangeError ) ;
365 }
366 catch( FactorError & e )
367 {
368 cerr << "Error in prime factorization: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
369 return static_cast<int>( ReturnStatus::InternalError ) ;
370 }
371 catch( BigIntRangeError & e )
372 {
373 cerr << "Internal range error in multiple precision arithmetic: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
374 return static_cast<int>( ReturnStatus::InternalError ) ;
375 }
376 catch( BigIntDomainError & e )
377 {
378 cerr << "Internal domain error in multiple precision arithmetic: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
379 return static_cast<int>( ReturnStatus::InternalError ) ;
380 }
381 catch( BigIntUnderflow & e )
382 {
383 cerr << "Internal underflow error in multiple precision arithmetic: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
384 return static_cast<int>( ReturnStatus::InternalError ) ;
385 }
386 catch( BigIntOverflow & e )
387 {
388 cerr << "Internal overflow error in multiple precision arithmetic: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
389 return static_cast<int>( ReturnStatus::InternalError ) ;
390 }
391 catch( BigIntZeroDivide & e )
392 {
393 cerr << "Internal zero divide error in multiple precision arithmetic: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
394 return static_cast<int>( ReturnStatus::InternalError ) ;
395 }
396 catch( BigIntMathError & e )
397 {
398 cerr << "Internal math error in multiple precision arithmetic: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
399 return static_cast<int>( ReturnStatus::InternalError ) ;
400 }
401 catch( ArithModPError & e )
402 {
403 cerr << "Internal modulo p arithmetic error: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
404 return static_cast<int>( ReturnStatus::InternalError ) ;
405 }
406 catch( PolynomialRangeError & e )
407 {
408 cout << "Error. Polynomial has bad syntax or coefficients are out of range. " << " [ " << e.what() << " ] " << endl << helpText ;
409 return static_cast<int>( ReturnStatus::RangeError ) ;
410 }
411 catch( PolynomialError & e )
412 {
413 cerr << "Error in polynomial arithmetic: " << " [ " << e.what() << " ] " << endl << writeToAuthorMessage ;
414 return static_cast<int>( ReturnStatus::InternalError ) ;
415 }
416 // Standard library exceptions.
417 catch( bad_alloc & e )
418 {
419 cerr << "Error allocating memory: " << " [ " << e.what() << " ] "<< endl ;
420 cerr << "Run on a different computer with more RAM or virtual memory." << writeToAuthorMessage ;
421 return static_cast<int>( ReturnStatus::InternalError ) ;
422 }
423 catch( exception & e )
424 {
425 cerr << "System error: " << e.what() << endl << writeToAuthorMessage ;
426 return static_cast<int>( ReturnStatus::InternalError ) ;
427 }
428 // Catch all other uncaught exceptions, which would otherwise call terminate()
429 // which in turn calls abort() and which would halt this program.
430 //
431 // Limitations:
432 // We can't handle the case where terminate() gets called because the
433 // exception mechanism finds a corrupt stack or catches a destructor
434 // throwing an exception.
435 //
436 // Also we can't catch exceptions which are thrown by initializing or
437 // destructing global variables.
438 catch( ... )
439 {
440 cerr << "Unexpected exception: " << endl << writeToAuthorMessage ;
441 return static_cast<int>( ReturnStatus::InternalError ) ;
442 }
443
444 return static_cast<int>( ReturnStatus::Success ) ;
445
446} //========================== end of function main ========================