1/*==============================================================================
   2|
   3|  NAME
   4|
   5|      ppPolynomial.cpp
   6|
   7|  DESCRIPTION
   8|
   9|      Polynomial arithmetic and polynomial exponentiation classes.
  10|
  11|      User manual and technical documentation are described in detail in my web page at
  12|      http://seanerikoconnor.freeservers.com/Mathematics/AbstractAlgebra/PrimitivePolynomials/overview.html
  13|
  14|  LEGAL
  15|
  16|     Primpoly Version 16.3 - A Program for Computing Primitive Polynomials.
  17|     Copyright (C) 1999-2024 by Sean Erik O'Connor.  All Rights Reserved.
  18|
  19|     This program is free software: you can redistribute it and/or modify
  20|     it under the terms of the GNU General Public License as published by
  21|     the Free Software Foundation, either version 3 of the License, or
  22|     (at your option) any later version.
  23|
  24|     This program is distributed in the hope that it will be useful,
  25|     but WITHOUT ANY WARRANTY; without even the implied warranty of
  26|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27|     GNU General Public License for more details.
  28|
  29|     You should have received a copy of the GNU General Public License
  30|     along with this program.  If not, see http://www.gnu.org/licenses/.
  31|     
  32|     The author's address is seanerikoconnor!AT!gmail!DOT!com
  33|     with the !DOT! replaced by . and the !AT! replaced by @
  34|
  35==============================================================================*/
  36
  37/*------------------------------------------------------------------------------
  38|                                Include Files                                 |
  39------------------------------------------------------------------------------*/
  40
  41#include <cstdlib>      // abort()
  42#include <iostream>     // Basic stream I/O.
  43#include <iomanip>      // I/O manipulators.
  44#include <new>          // set_new_handler()
  45#include <cmath>        // Basic math functions e.g. sqrt()
  46#include <complex>      // Complex data type and operations.
  47#include <fstream>      // File stream I/O.
  48#include <sstream>      // String stream I/O.
  49#include <vector>       // STL vector class.
  50#include <string>       // STL string class.
  51#include <algorithm>    // Iterators.
  52#include <stdexcept>    // Exceptions.
  53#include <cassert>      // assert()
  54
  55using namespace std ;
  56
  57
  58/*------------------------------------------------------------------------------
  59|                                PP Include Files                              |
  60------------------------------------------------------------------------------*/
  61
  62#include "Primpoly.hpp"         // Global functions.
  63#include "ppArith.hpp"          // Basic arithmetic functions.
  64#include "ppBigInt.hpp"         // Arbitrary precision integer arithmetic.
  65#include "ppOperationCount.hpp" // OperationCount collection for factoring and poly finding.
  66#include "ppFactor.hpp"         // Prime factorization and Euler Phi.
  67#include "ppPolynomial.hpp"     // Polynomial operations and mod polynomial operations.
  68#include "ppParser.hpp"         // Parsing of polynomials and I/O services.
  69#include "ppUnitTest.hpp"       // Complete unit test.
  70
  71
  72/*=============================================================================
  73|
  74| NAME
  75|
  76|     Polynomial()
  77|
  78| DESCRIPTION
  79|
  80|    Default constructor for Polynomial class.  Constructs the zero degree
  81|    polynomial p(x) = 0 (mod 2)
  82|
  83| EXAMPLE
  84|
  85|    Polynomial p ;
  86|
  87+============================================================================*/
  88
  89Polynomial::Polynomial()
  90                : f_()
  91                , n_( 0 )
  92                , p_( 2 )
  93                , mod( p_ )
  94{
  95    // f(x) = 0
  96    f_.push_back( 0 ) ;
  97}
  98
  99
 100/*=============================================================================
 101 |
 102 | NAME
 103 |
 104 |     Polynomial()
 105 |
 106 | DESCRIPTION
 107 |
 108 |     Constructor for a polynomial from a vector of integers.
 109 |
 110 | EXAMPLE
 111 |
 112 |    vector<ppunit> v { 1, 2, 3 } ;
 113 |    Polynomial p{ v } ;
 114 |    Polynomial p( v ) ;
 115 |
 116 +============================================================================*/
 117
 118Polynomial::Polynomial( const vector<ppuint> v )
 119: n_{ static_cast<int>( v.size() - 1) }
 120, p_( 2 )
 121, mod( p_ )
 122{
 123    // Copy over the polynomial coefficients.
 124    f_ = v ;
 125}
 126
 127
 128/*=============================================================================
 129|
 130| NAME
 131|
 132|     ~Polynomial
 133|
 134| DESCRIPTION
 135|
 136|     Destructor.
 137|
 138| EXAMPLE
 139|
 140|     void add1( const Polynomial & f )
 141|     {
 142|         const Polynomial g{ 1u } ;
 143|         return f + g ;
 144|         // Destructor for g automatically called as f goes out of scope.
 145|     }
 146|
 147+============================================================================*/
 148
 149Polynomial::~Polynomial()
 150{
 151    // vector f_ frees itself and mod_ calls its own destructor.
 152}
 153
 154
 155/*=============================================================================
 156|
 157| NAME
 158|
 159|     Polynomial
 160|
 161| DESCRIPTION
 162|
 163|     Copy constructor.
 164|
 165| EXAMPLE
 166|
 167|     try
 168|     {
 169|         Polynomial f ;
 170|         Polynomial f( g ) ;
 171|     }
 172|     catch( PolynomialError & e )
 173|     {
 174|         cout << "Error in constructing polynomial f(x) or g(x)" << endl ;
 175|     }
 176|
 177+============================================================================*/
 178
 179Polynomial::Polynomial( const Polynomial & g )
 180                : f_( g.f_ )
 181                , n_( g.n_ )
 182                , p_( g.p_ )
 183                , mod( p_ )
 184{
 185    // The classes in the initializer above all copy themselves.
 186}
 187
 188
 189/*=============================================================================
 190|
 191| NAME
 192|
 193|     Polynomial::operator=
 194|
 195| DESCRIPTION
 196|
 197|     Safe assigment operator for polynomials, f( x ) = g( x )
 198|     which leaves the polynomial f( x ) unchanged if an exception is thrown.
 199|
 200| EXAMPLE
 201|
 202|     try
 203|     {
 204|         Polynomial f ;
 205|         Polynomial g ;
 206|         f = g ;
 207|     }
 208|     catch( PolynomialError & e )
 209|     {
 210|         cout << "Error in constructing polynomial f(x)" << endl ;
 211|     }
 212|
 213+============================================================================*/
 214
 215Polynomial & Polynomial::operator=( const Polynomial & g )
 216{
 217    // Check for assigning to oneself by comparing the unique pointers to the classes for speed.
 218    // If the user does f = f, just pass back a reference to the unchanged polynomial f.
 219    if (this == &g)
 220        return *this ;
 221
 222    // Assign the scalars first.
 223    n_ = g.n_ ;
 224    p_ = g.p_ ;
 225
 226    // And the modulus functionoid.
 227    mod( g.p_ ) ;
 228
 229    // Overwrite the old polynomial coefficients in f_ with the new coefficients in g.f_:
 230    //   1) Delete the old polynomial coefficients, i.e. destruct the vector valued member variable f_.
 231    //   2) Construct a new vector f_.
 232    //   3) Copy the coefficients from g to f_.
 233    // But here's the problem:  if we fail to construct the new f_ and throw an exception,
 234    // e.g by requesting a bad vector size, we've destroyed the existing polynomial coefficients f_.
 235    //
 236    // The following solution guarantees that if f = g throws an exception, the value of f is unchanged.
 237    try
 238    {
 239        // Create a temporary copy of the polynomial coefficients.
 240        vector<ppuint> tempCoeff{ g.f_ } ;
 241
 242        // Move the old values into the temporary, and the new values into the object.
 243        // The library function swap() exchanges the values in the two containers,
 244        // guarantees no exceptions will be thrown.
 245        // The temporary containing the old values will be destroyed when we leave scope.
 246        swap( f_, tempCoeff ) ;
 247    }
 248    // Failed to construct tempPoly.
 249    catch( bad_alloc & e )
 250    {
 251        throw PolynomialError( "Polynomial.operator=() had a bad_alloc memory allocation failure", __FILE__, __LINE__ ) ;
 252    }
 253
 254    // Return a reference to the altered object.
 255    return *this ;
 256}
 257
 258
 259/*=============================================================================
 260|
 261| NAME
 262|
 263|     Polynomial::operator=( string )
 264|
 265| DESCRIPTION
 266|
 267|     Assigment operator for string to polynomial, f( x ) = "polynomial"
 268|
 269| EXAMPLE
 270|
 271|     try
 272|     {
 273|         Polynomial f ;
 274|         f = "x^2 + 1" ;
 275|     }
 276|     catch( PolynomialError & e )
 277|     {
 278|         cout << "Error in constructing polynomial f(x)" << endl ;
 279|     }
 280|
 281+============================================================================*/
 282
 283Polynomial & Polynomial::operator=( string s )
 284{    
 285    try
 286    {
 287        // Construct a new polynomial from the string.
 288        Polynomial g( s ) ;
 289
 290        n_ = g.n_ ;
 291        p_ = g.p_ ;
 292        mod( g.p_ ) ;
 293
 294        // Right, no exceptions were thrown from the constructor, so
 295        // we've got a new polynomial object now.
 296        // Trash the existing polynomial.
 297        f_.clear() ;
 298        f_ = g.f_ ;
 299    }
 300    catch( bad_alloc & e )
 301    {
 302        throw PolynomialRangeError( "Polynomial.operator=(string) had a bad_alloc memory allocation failure", __FILE__, __LINE__ ) ;
 303    }
 304
 305    // Return a reference to assigned object to make chaining possible.
 306    return *this ;
 307}
 308
 309
 310/*=============================================================================
 311|
 312| NAME
 313|
 314|     Polynomial, construct from string.
 315|
 316| DESCRIPTION
 317|
 318|     Construct a polynomial from a string.
 319|
 320| EXAMPLE
 321|
 322|     try
 323|     {
 324|         Polynomial f( "x^2 + 2x + 1, 3" ) ;
 325|     }
 326|     catch( PolynomialRangeError & e )
 327|     {
 328|         cout << "Error in construction polynomial f(x) from string" << endl ;
 329|     }
 330|
 331 , n_( 0 )
 332 , p_( 2 )
 333+============================================================================*/
 334
 335Polynomial::Polynomial( string s, ppuint p )
 336                : f_()
 337                , mod( 0 )
 338                , n_( 0 )
 339                , p_( 2 )
 340{
 341    //  The polynomial string must have at least one character in it.
 342    // Not really an exception but more of a user input error.
 343    if (s.empty())
 344        throw PolynomialRangeError( "polynomial string is empty" ) ;
 345
 346    try
 347    {
 348        // Initialize an LALR(1) parser for polynomials.
 349        PolyParser< PolySymbol, PolyValue > pp ;
 350
 351        PolyValue v = pp.parse( s ) ;
 352
 353        // Get the modulus specified by the polynomial.
 354        p_ = v.scalar_ ;
 355
 356        // If the modulus is explicitly input, use that instead of the polynomial's modulus.
 357        if (p > 0)
 358            p_ = p ;
 359
 360        // Sanity check the modulus.
 361        if (p_ <= 0)
 362        {
 363            ostringstream os ;
 364            os << "Error.  Polynomial modulus p must be > 0 but p = " << p_ << endl ;
 365            throw PolynomialRangeError( os.str() ) ;
 366        };
 367        // TODO:  check upper range.
 368
 369        mod.set( p_ ) ;
 370
 371        // Sanity check the degree of the polynomial.
 372        n_ = static_cast<int>( v.f_.size() ) - 1 ;
 373        if (n_ < 0)
 374        {
 375            ostringstream os ;
 376            os << "Error.  Polynomial degree n must be >= 0 but n = " << n_ << endl ;
 377            throw PolynomialRangeError( os.str() ) ;
 378        }
 379
 380        // Reduce all the (positive) polynomial coefficients modulo p.
 381        vector<ppuint>::iterator i ;
 382        for (i = v.f_.begin() ;  i != v.f_.end() ;  ++i)
 383            *i = mod( *i ) ;
 384
 385        // Copy over the polynomial coefficients.
 386        f_ = v.f_ ;
 387    }
 388    catch( ParserError & e )
 389    {
 390        ostringstream os ;
 391        os << "Error in parser converting polynomial from string " << s << " for p = " << p_ << " " << e.what() ;
 392        throw PolynomialRangeError( os.str() ) ;
 393    }
 394}
 395
 396
 397/*=============================================================================
 398|
 399| NAME
 400|
 401|     string
 402|
 403| DESCRIPTION
 404|
 405|     Convert a polynomial to a string.
 406|
 407| EXAMPLE
 408|
 409|     try
 410|     {
 411|         Polynomial f( "x^2 + 1,3" ) ;
 412|         string poly = f ;
 413|     }
 414|     catch( PolynomialRangeError & e )
 415|         cout << "Error in construction polynomial f(x) from string" << endl ;
 416|
 417+============================================================================*/
 418
 419// Operator casting to string type.
 420Polynomial::operator string() const
 421{
 422    // Set up a string stream for convenience.
 423    ostringstream os ;
 424
 425    // Spin out the polynomial coefficients from high to low degree.
 426    // Special case of f(x) = const.
 427    if (f_.size() == 1)
 428    {
 429        if (!(os << f_[ 0 ]))
 430        {
 431            ostringstream os ;
 432            os << "Error in converting polynomial to string: "
 433               << " with n = " << n_ << " and p = " << p_ ;
 434            throw PolynomialRangeError( os.str() ) ;
 435        }
 436    }
 437    else
 438    {
 439        int lowestDegreeTerm = -1 ;
 440        for (int deg = n_ ;  deg >= 0 ;  --deg)
 441            if (f_[ deg ] != 0)
 442                lowestDegreeTerm = deg ;
 443
 444        for (int deg = n_ ;  deg >= 0 ;  --deg)
 445        {
 446            if (f_[ deg ] != 0)
 447            {
 448                // x^n has a nonzero coefficient.
 449                ppuint coeff = f_[ deg ] ;
 450
 451                 // Print coeff of x^n unless it is 1.
 452                 // But print the constant term regardless.
 453                if (coeff != 1 || deg == 0)
 454                {
 455                    string extraBlank = deg == 0 ? "" : " " ;
 456
 457                    if (!(os << coeff << extraBlank))
 458                     {
 459                        ostringstream os ;
 460                        os << "Error in converting polynomial to string: "
 461                        << " with n = " << n_ << " and p = " << p_ ;
 462                        throw PolynomialRangeError( os.str() ) ;
 463                     }
 464                }
 465
 466                // Print x (no exponent).
 467                if (deg == 1)
 468                {
 469                    if ( !(os << "x") )
 470                     {
 471                        ostringstream os ;
 472                        os << "Error in converting polynomial to string: "
 473                        << " with n = " << n_ << " and p = " << p_ ;
 474                        throw PolynomialRangeError( os.str() ) ;
 475                     }
 476                }
 477                // Print x^n ... x^2
 478                else if (deg != 0)
 479                {
 480                    if (!(os << "x ^ " << deg))
 481                     {
 482                        ostringstream os ;
 483                        os << "Error in converting polynomial to string: "
 484                        << " with n = " << n_ << " and p = " << p_
 485                        << " in file " << __FILE__ << " line " << __LINE__ ;
 486                        throw PolynomialRangeError( os.str() ) ;
 487                     }
 488                }
 489
 490                // Print +, but only when followed by a lower degree term or constant.
 491                // e.g. x^2 + 2 x, x + 3
 492                if (lowestDegreeTerm != -1 && deg > lowestDegreeTerm)
 493                    if (!(os << " + "))
 494                     {
 495                        ostringstream os ;
 496                        os << "Error in converting polynomial to string with n = " << n_ << " and p = " << p_ ;
 497                        throw PolynomialRangeError( os.str() ) ;
 498                     }
 499            } // end coeff != 0.
 500        } // end for deg
 501    } // end f(x) = const.
 502
 503    // Print out the modulus.
 504    if (!(os << ", " << p_  ))
 505                     {
 506                        ostringstream os ;
 507                        os << "Error in converting polynomial to string: "
 508                        << " with n = " << n_ << " and p = " << p_ ;
 509                        throw PolynomialRangeError( os.str() ) ;
 510                     }
 511
 512    // Return the string from the stream.
 513    return os.str() ;
 514}
 515
 516
 517/*=============================================================================
 518|
 519| NAME
 520|
 521|     operator << for Polynomial
 522|
 523| DESCRIPTION
 524|
 525|     Print a polynomial to the output stream.
 526|
 527| EXAMPLE
 528|
 529|     try
 530|     {
 531|         Polynomial f( "x^2 + 1,3" ) ;
 532|         cout << f << endl ;
 533|     }
 534|     catch( PolynomialRangeError & e )
 535|     {
 536|         cout << "Error in outputting polynomial f(x)" << endl ;
 537|     }
 538|
 539+============================================================================*/
 540
 541ostream & operator<<( ostream & out, const Polynomial & p )
 542{
 543    // Cast to polynomial to a string first, then output as a string.
 544    // May throw a PolynomialRangeError.
 545    out << static_cast<string>( p ) ;
 546
 547    return out ;
 548}
 549
 550
 551/*=============================================================================
 552|
 553| NAME
 554|
 555|     Operator >> for Polynomial
 556|
 557| DESCRIPTION
 558|
 559|     Polynomial stream input.
 560|
 561| EXAMPLE
 562|
 563|     try
 564|     {
 565|
 566|         Polynomial f ;
 567|         cin >> f ;
 568|     }
 569|     catch( PolynomialRangeError & e )
 570|     {
 571|         cout << "Error in inputting polynomial f(x)" << endl ;
 572|     }
 573|
 574+============================================================================*/
 575
 576istream & operator>>( istream & in, Polynomial & p )
 577{
 578    // Input as a string.
 579    string s ;
 580    in >> s ;
 581
 582    // Copy into argument polynomial.  Can throw an exception.
 583    p = Polynomial( s ) ;
 584
 585    return in ;
 586}
 587
 588
 589/*=============================================================================
 590 |
 591 | NAME
 592 |
 593 |     Polynomial operator==
 594 |
 595 | DESCRIPTION
 596 |
 597 |     Polynomial equality test operator.
 598 |
 599 | EXAMPLE
 600 |
 601 |     try
 602 |     {
 603 |         Polynomial f1( "2x^2 + 1, 3" ) ;
 604 |         Polynomial f2( "2x^2 + 1", 3 ) ;
 605 |
 606 |         if (f1 == f2)
 607 |             cout << "f1 = " << f1 << " == " << f2 << endl ;
 608 |         else
 609 |            cout << "f1 = " << f1 << " != " << f2 << endl ;
 610 |
 611 +============================================================================*/
 612
 613bool operator==( const Polynomial & p1, const Polynomial & p2 )
 614{
 615    // The degrees and moduli have to match.
 616    if ((p1.n_ != p2.n_) || (p1.p_ != p2.p_))
 617       return false ;
 618        
 619    // Test coefficients for equality.
 620    for (int i = 0 ;  i <= p1.n_ ;  ++i)
 621        if (p1.f_[ i ] != p2.f_[ i ])
 622            return false ;
 623    
 624    return true ;
 625}
 626
 627bool operator!=( const Polynomial & p1, const Polynomial & p2 )
 628{
 629    return !( p1 == p2) ;
 630}
 631
 632
 633/*=============================================================================
 634|
 635| NAME
 636|
 637|     Polynomial operator[]
 638|
 639| DESCRIPTION
 640|
 641|     Polynomial indexing operator which allows an lvalue:  f[ 33 ] = 2 ;
 642|     If we don't have a coefficient of this degree, create it and backfill
 643|     earlier coefficients with zero.
 644|
 645|     Throws an exception if out of bounds.
 646|
 647| EXAMPLE
 648|
 649|     try
 650|     {
 651|         Polynomial f( "2x^2 + 1, 3" ) ;
 652|
 653|         f[ 5 ] = 2 ;
 654|
 655|         // Now f(x) = 2 x^5 + 0 x^4 + 0 x^3 + 2 x^2 + 0 x + 1
 656|         // f_.size() => 5 + 1 = 6
 657|
 658|     }
 659|     catch( PolynomialRangeError & e )
 660|     {
 661|         cout << "Error in assigning polynomial f(x) coefficient" << endl ;
 662|     }
 663|
 664+============================================================================*/
 665
 666ppuint & Polynomial::operator[]( int i )
 667{
 668    // We attempt to access beyond the current degree.
 669    if (i > n_)
 670    {
 671        try
 672        {
 673            // Extend the vector size with zeros.
 674            f_.resize( i+1, 0 ) ;
 675            n_ = i ;
 676        }
 677        // Failed to resize the polynomial.
 678        catch( length_error & e )
 679        {
 680            throw PolynomialError( "Polynomial.operator[]:  failed to resize", __FILE__, __LINE__ ) ;
 681        }
 682    }
 683
 684    // Return a reference to the coefficient.
 685    return f_[ i ] ;
 686}
 687
 688
 689/*=============================================================================
 690|
 691| NAME
 692|
 693|     Polynomial operator[]
 694|
 695| DESCRIPTION
 696|
 697|     Polynomial indexing operator for read only access:  int coeff = f[ 33 ] ;
 698|     Throws an exception if out of bounds.
 699|
 700| EXAMPLE
 701|
 702|     try
 703|     {
 704|         Polynomial f ;
 705|         int value = f[ 33 ] ;
 706|     }
 707|     catch( PolynomialRangeError & e )
 708|     {
 709|         cout << "Error in getting polynomial f(x) coefficient" << endl ;
 710|     }
 711|
 712+============================================================================*/
 713
 714const ppuint Polynomial::operator[]( int i ) const
 715{
 716    // We throw our own exception here.
 717    if (i > n_)
 718	 {
 719		ostringstream os ;
 720		os << "Error accessing polynomial with coefficients p[0]...p[n] = (" ;
 721		for (int j = 0 ;  j <= n_ ;  ++j)
 722			os << f_[ j ] << " " ;
 723		os << ")" << endl
 724		   << " at index i = " << i
 725		   << " of degree n = " << n_ << " modulo p = " << p_ ;
 726		throw PolynomialRangeError( os.str(), __FILE__, __LINE__ ) ;
 727	 }
 728
 729    return f_[ i ] ;
 730}
 731
 732
 733/*=============================================================================
 734|
 735| NAME
 736|
 737|     deg
 738|     modulus
 739|     setModulus
 740|
 741| DESCRIPTION
 742|
 743|     Return the degree of f(x).
 744|
 745| EXAMPLE
 746|
 747|     try
 748|     {
 749|         Polynomial f ;
 750|         cout << "Degree of f(x) = " << f.deg() << endl ;
 751|     }
 752|
 753+============================================================================*/
 754
 755int Polynomial::deg() const
 756{
 757    return n_ ;
 758}
 759
 760// Return the modulus p of f(x).
 761ppuint Polynomial::modulus() const
 762{
 763    return p_ ;
 764}
 765
 766void Polynomial::setModulus( ppuint p )
 767{
 768    p_ = p ;
 769
 770    // And the modulus functionoid.
 771    mod( p_ ) ;
 772}
 773
 774
 775/*=============================================================================
 776|
 777| NAME
 778|
 779|     Polynomial operator+=
 780|
 781| DESCRIPTION
 782|
 783|     Polynomial sum f(x) += g(x)
 784|
 785|
 786| EXAMPLE
 787|
 788|     try
 789|     {
 790|         Polynomial f ;
 791|         Polynomial g ;
 792|         f += g ;
 793|     }
 794|     catch( PolynomialRangeError & e )
 795|     {
 796|         cout << "Error in polynomial sum f(x) += g(x)" << endl ;
 797|     }
 798|
 799+============================================================================*/
 800
 801Polynomial & Polynomial::operator+=( const Polynomial & g )
 802{
 803    // f(x) = x^2 +   + 1
 804    // g(x) =       x + 3
 805    //
 806    // f(x) =       x + 3
 807    // g(x) = x^2 +   + 1
 808    //
 809    int minDeg = (n_ < g.n_) ? n_ : g.n_ ;
 810
 811    // Add coefficients modulo p for smaller degree terms.
 812    for (int i = 0 ;  i <= minDeg ;  ++i)
 813        f_[ i ] = mod( f_[ i ] + g.f_[ i ] ) ;
 814
 815    // If g(x) has larger degree, extend f(x) and copy the coefficients of g(x).
 816    if (g.n_ > n_)
 817    {
 818        // Extend the vector size with zeros.
 819        try
 820        {
 821            f_.resize( g.n_ + 1, 0 ) ;
 822        }
 823        // Failed to resize the polynomial.
 824        catch( length_error & e )
 825        {
 826            throw PolynomialError( "Polynomial::operator+= had a length_error exception while resizing the polynomial", __FILE__, __LINE__ ) ;
 827        }
 828
 829        for (int i = n_ + 1 ;  i <= g.n_ ;  ++i)
 830            f_[ i ] = g.f_[ i ] ;
 831    }
 832
 833    // Trim leading zero coefficients, but leave a constant term of zero.
 834    while( f_.back() == 0 && f_.size() > 1)
 835    {
 836       f_.pop_back() ;
 837	   --n_ ;
 838	}
 839
 840    // Return current object now containing the sum.
 841    return *this ;
 842}
 843
 844
 845/*=============================================================================
 846|
 847| NAME
 848|
 849|     Polynomial operator+()
 850|
 851| DESCRIPTION
 852|
 853|     Add polynomials.
 854|
 855+============================================================================*/
 856
 857const Polynomial operator+( const Polynomial & f, const Polynomial &g )
 858{
 859    // Do + in terms of += to maintain consistency.
 860    // Copy construct temporary copy, then add to it.
 861    // Return value optimization compiles away the copy constructor.
 862    // const on return type disallows doing (u+v) = w ;
 863    return Polynomial( f ) += g ;
 864}
 865
 866/*=============================================================================
 867|
 868| NAME
 869|
 870|     Polynomial operator*=()
 871|
 872| DESCRIPTION
 873|
 874|     Scalar multiply polynomials.
 875|
 876+============================================================================*/
 877
 878Polynomial & Polynomial::operator*=( const ppuint k )
 879{
 880    // Multiply coefficients modulo p.
 881    for (int i = 0 ;  i <= n_ ;  ++i)
 882        f_[ i ] = mod( f_[ i ] * k ) ;
 883
 884    // Return current object now containing the scalar product.
 885    return *this ;
 886}
 887
 888
 889/*=============================================================================
 890|
 891| NAME
 892|
 893|     Polynomial operator*()
 894|
 895| DESCRIPTION
 896|
 897|     Scalar multiply polynomials.
 898|
 899+============================================================================*/
 900
 901const Polynomial operator*( const Polynomial & f, const ppuint k )
 902{
 903    // Do * in terms of *= to maintain consistency.
 904    // Copy construct temporary copy, then add to it.
 905    // Return value optimization compiles away the copy constructor.
 906    // const on return type disallows doing (u*k) = w ;
 907    return Polynomial( f ) *= k ;
 908}
 909
 910
 911/*=============================================================================
 912|
 913| NAME
 914|
 915|     Polynomial operator()
 916|
 917| DESCRIPTION
 918|
 919|     Evaluate the monic polynomial f( x ) with modulo p arithmetic.
 920|
 921|               n         n-1
 922|     f( x ) = x  +  a   x  + ... + a    0 <= a  < p
 923|                     n-1            0         i
 924|
 925| EXAMPLE
 926|                                  4
 927|     Let n = 4, p = 5 and f(x) = x  + 3x + 3.
 928|
 929|     By Horner's rule, f(x) = (((x + 0)x + 0)x + 3)x + 3.
 930|
 931|     Then f(2) = (((2 + 0)2 + 0)2 + 3) = (8 + 3)2 + 3 = 1 + 2 + 3 (mod 5) = 0.
 932|     and f(3) = (((3 + 0)3 + 0)3 + 3)3 + 3 (mod 5) = 3
 933|
 934| METHOD
 935|
 936|     By Horner's rule, f(x) = ( ... ( (x + a   )x + ... )x + a .
 937|                                            n-1               0
 938|
 939|     We evaluate recursively, f := f * x + a (mod p), starting
 940|                                            i
 941|     with f = 1 and i = n-1.
 942|
 943+============================================================================*/
 944
 945ppuint
 946Polynomial::operator()( int x )
 947{
 948    ppuint val = 1 ;
 949
 950    for (int degree = n_- 1 ;  degree >= 0 ;  --degree)
 951        val = mod( val * x + f_[ degree ]) ;
 952
 953    return( val ) ;
 954}
 955
 956
 957/*=============================================================================
 958|
 959| NAME
 960|
 961|     hasLinearFactor
 962|
 963| DESCRIPTION
 964|
 965|     Check if the polynomial f(x) has any linear factors.
 966|
 967|     Polynomial f ; // A polynomial f(x) of degree n, modulo p.
 968|     bool hasFactor = f.hasLinearFactor() ;
 969|
 970|     hasFactor is true if f( a ) = 0 (mod p) for a = 1, 2, ... p-1,
 971|     and is false otherwise.
 972|
 973|     i.e. check if f(x) contains a linear factor (x - a).  We don't need to test
 974|     for the root a = 0 because f(x) was chosen in main to have a non-zero
 975|     constant term, hence no zero root.
 976|
 977| EXAMPLE
 978|                                  4                               2   2
 979|     Let n = 4, p = 5 and f(x) = x  + 3x + 3.  Then f(x) = (x + 3)  (x + 4x + 2)
 980|
 981|      Then f(0) = 3 (mod 5), f(1) = 2 (mod 5), but
 982|     f(2) = 0 (mod 5), so we exit immediately with a true.
 983|
 984|                       4      2
 985|      However, f(x) = x  + 3 x  + x + 1 is irreducible, so has no linear factors.
 986|
 987| METHOD
 988|
 989|    Evaluate f(x) at x = 0, ..., p-1 by Horner's rule.  Return instantly the
 990|    moment f(x) evaluates to 0.
 991|
 992+============================================================================*/
 993
 994bool
 995Polynomial::hasLinearFactor()
 996{
 997    for (int i = 0 ;  i <= p_ - 1 ;  ++i)
 998        if ((*this)( i ) == 0)
 999            return( true ) ;
1000
1001    return( false ) ;
1002}
1003
1004
1005/*=============================================================================
1006|
1007| NAME
1008|
1009|     Polynomial::isInteger
1010|
1011| DESCRIPTION
1012|
1013|     Return true if a polynomial is a constant.
1014|
1015| EXAMPLE
1016|
1017|     Polynomial p( "2 x ^ 2 " ) ;
1018|     p.isInteger -> false
1019|
1020|     Polynomial p( "2 " ) ;
1021|     p.isInteger -> true
1022|
1023| METHOD
1024|
1025|     A constant polynomial is zero in its first through n th degree
1026|     terms.  Return immediately with false if any such term is non-zero.
1027|
1028+============================================================================*/
1029
1030bool
1031Polynomial::isInteger() const
1032{
1033    // Degree 0 is constant.
1034    if (n_ == 0)
1035        return true ;
1036
1037    // Not integer if any coefficients above zero degree term are non-zero.
1038    for (int i = 1 ;  i <= n_ ;  ++i)
1039        if (f_[ i ] != 0)
1040            return( false ) ;
1041
1042    return( true ) ;
1043}
1044
1045
1046/*=============================================================================
1047 |
1048 | NAME
1049 |
1050 |   initialTrialPoly
1051 |
1052 | DESCRIPTION
1053 |
1054 |     Create an initial monic polynomial
1055 |                   n
1056 |         f( x ) = x
1057 |
1058 | EXAMPLE
1059 |                              4
1060 |      Let n = 4.  Set f(x) = x  - 1.
1061 |
1062 |
1063 +============================================================================*/
1064
1065void TrialPolynomial::initialTrialPoly( const ppuint n, const ppuint p )
1066{
1067    (*this).setModulus(p);
1068
1069    // Allocate enough coefficients for an nth degree polynomial and
1070    // initialize all intermediate coefficients to 0.
1071    
1072    if (n > numeric_limits<int>::max())
1073    {
1074        ostringstream os ;
1075        os << "Polynomial::initialTrialPoly:  n = " << n << " is too large for an array index" ;
1076        throw PolynomialRangeError( os.str(), __FILE__,  __LINE__ ) ;
1077    }
1078    
1079    (*this)[ static_cast<int>( n ) ] = 1 ;
1080    f_[ 0 ] = 0 ;
1081}
1082
1083
1084/*=============================================================================
1085 |
1086 | NAME
1087 |
1088 |     nextTrialPoly
1089 |
1090 | DESCRIPTION
1091 |
1092 |     Return the next monic polynomial in the sequence after f(x), explained
1093 |     below
1094 |
1095 | EXAMPLE
1096 |                                            3
1097 |      Let n = 3 and p = 5.  Suppose f(x) = x  + 4 x + 4.  As a mod p number,
1098 |      this is 1 0 4 4.  Adding 1 gives 1 0 4 5.  We reduce modulo
1099 |      5 and propagate the carry to get the number 1 0 5 0.  Propagating
1100 |      the carry again and reducing gives 1 1 0 0.  The next polynomial
1101 |                      3    2
1102 |      after f(x) is  x  + x .
1103 |
1104 | METHOD
1105 |
1106 |      Think of the polynomial coefficients as the digits of a number written
1107 |      in base p.  The "next" polynomial is the one you would get by adding 1
1108 |      to this number in multiple precision arithmetic.  Our intention is to
1109 |      run through all possible monic polynomials modulo p.
1110 |
1111 |      Propagate carries in digits 1 through n-2 when any digit exceeds p.  No
1112 |      carries take place in the n-1 st digit because our polynomial is monic.
1113 |
1114 |      TODO:  Find polynomials in order of Hamming weight?
1115 |
1116 +============================================================================*/
1117
1118void TrialPolynomial::nextTrialPoly()
1119{
1120    ++f_[ 0 ] ;     // Add 1, i.e. increment the coefficient of the x term.
1121
1122    //   Sweep through the number from right to left, propagating carries.  Skip
1123    //   the constant and the nth degree terms.
1124    for (int digit_num = 0 ;  digit_num <= n_ - 2 ;  ++digit_num)
1125    {
1126        if (f_[ digit_num ] == p_)   //  Propagate carry to next digit.
1127        {
1128            f_[ digit_num ] = 0 ;
1129            ++f_[ digit_num + 1 ] ;
1130        }
1131    }
1132}
1133
1134
1135
1136/*------------------------------------------------------------------------------
1137|                              PolyMod Implementation                          |
1138------------------------------------------------------------------------------*/
1139
1140/*=============================================================================
1141 |
1142 | NAME
1143 |
1144 |     PolyMod default constructor
1145 |
1146 | DESCRIPTION
1147 |
1148 |
1149 | EXAMPLE
1150 |
1151 | METHOD
1152 |
1153 +============================================================================*/
1154
1155PolyMod::PolyMod()
1156           : g_()
1157           , f_()
1158           , powerTable_()
1159           , mod( f_.modulus() )
1160{
1161    constructPowerTable() ;
1162    modf() ;
1163}
1164
1165
1166/*=============================================================================
1167 |
1168 | NAME
1169 |
1170 |     PolyMod destructor
1171 |
1172 | DESCRIPTION
1173 |
1174 |
1175 | EXAMPLE
1176 |
1177 | METHOD
1178 |
1179 +============================================================================*/
1180
1181PolyMod::~PolyMod()
1182{
1183// Member fields will clean up themselves.
1184}
1185
1186
1187/*=============================================================================
1188 |
1189 | NAME
1190 |
1191 |     PolyMod constructor
1192 |
1193 | DESCRIPTION
1194 |
1195 |     Given polynomials f( x ) and g( x ) where g is a string,
1196 |     construct p( x ) = g( x ) mod f( x ).
1197 |
1198 | EXAMPLE
1199 |
1200 | METHOD
1201 |
1202 +============================================================================*/
1203
1204PolyMod::PolyMod( const string & g, const Polynomial & f )
1205         : g_( g )
1206         , f_( f )
1207         , powerTable_()
1208         , mod( f.modulus() )
1209{
1210    constructPowerTable() ;
1211    modf() ;
1212}
1213
1214
1215/*=============================================================================
1216 |
1217 | NAME
1218 |
1219 |     PolyMod constructor
1220 |
1221 | DESCRIPTION
1222 |
1223 |     Given polynomials f( x ) and g( x ), construct p( x ) = g( x ) mod f( x ).
1224 |
1225 | EXAMPLE
1226 |
1227 | METHOD
1228 |
1229 +============================================================================*/
1230
1231PolyMod::PolyMod( const Polynomial & g, const Polynomial & f )
1232         : g_( g )
1233         , f_( f )
1234         , powerTable_()
1235         , mod( f.modulus() )
1236{
1237    constructPowerTable() ;
1238    modf() ;
1239}
1240
1241
1242/*=============================================================================
1243 |
1244 | NAME
1245 |
1246 |     PolyMod string operator
1247 |
1248 | DESCRIPTION
1249 |
1250 |     Given g( x ) mod f( x ), return g( x ) as a string.
1251 |
1252 | EXAMPLE
1253 |
1254 | METHOD
1255 |
1256 +============================================================================*/
1257
1258// Operator casting to string type.
1259PolyMod::operator string() const
1260{
1261return static_cast<string>( g_ ) ;
1262}
1263
1264
1265/*=============================================================================
1266 |
1267 | NAME
1268 |
1269 |     Operator << for PolyMod
1270 |
1271 | DESCRIPTION
1272 |
1273 |     Given g( x ) mod f( x ), output g( x ) as a string.
1274 |
1275 | EXAMPLE
1276 |
1277 | METHOD
1278 |
1279 +============================================================================*/
1280
1281ostream & operator<<( ostream & out, const PolyMod & p )
1282{
1283    // Cast to polynomial to a string first, then output as a string.
1284    // May throw a PolynomialRangeError.
1285    out << static_cast<string>( p.g_ ) ;
1286
1287    return out ;
1288}
1289
1290
1291/*=============================================================================
1292 |
1293 | NAME
1294 |
1295 |     getf
1296 |
1297 | DESCRIPTION
1298 |
1299 |     Given g( x ) mod f( x ), return f( x ).
1300 |
1301 | EXAMPLE
1302 |
1303 | METHOD
1304 |
1305 +============================================================================*/
1306
1307const Polynomial PolyMod::getf() const
1308{
1309    return f_ ;
1310}
1311
1312
1313/*=============================================================================
1314 |
1315 | NAME
1316 |
1317 |     getModulus
1318 |
1319 | DESCRIPTION
1320 |
1321 |     Given g( x ) mod (f( x ), p) return p.
1322 |
1323 | EXAMPLE
1324 |
1325 | METHOD
1326 |
1327 +============================================================================*/
1328
1329const ppuint PolyMod::getModulus() const
1330{
1331    return f_.modulus() ;
1332}
1333
1334
1335/*=============================================================================
1336 |
1337 | NAME
1338 |
1339 |     PolyMod copy constructor
1340 |
1341 | DESCRIPTION
1342 |
1343 |     Copy g2 to g( x ) mod (f( x ), p)
1344 |
1345 | EXAMPLE
1346 |
1347 | METHOD
1348 |
1349 +============================================================================*/
1350
1351PolyMod::PolyMod( const PolyMod & g2 )
1352         : g_( g2.g_ )
1353         , f_( g2.f_ )
1354         , powerTable_( g2.powerTable_ )
1355         , mod( f_.modulus() )
1356{
1357}
1358
1359
1360/*=============================================================================
1361 |
1362 | NAME
1363 |
1364 |     operator=
1365 |
1366 | DESCRIPTION
1367 |
1368 |     PolyMod assignment operator.
1369 |
1370 +============================================================================*/
1371
1372PolyMod & PolyMod::operator=( const PolyMod & g2 )
1373{
1374    // Check for assigning to oneself:  just pass back a reference to the unchanged object.
1375    if (this == &g2)
1376        return *this ;
1377
1378    g_ = g2.g_ ;
1379    g_ = g2.f_ ;
1380
1381    powerTable_ = g2.powerTable_ ;
1382    mod = g2.mod ;
1383
1384    // Return a reference to the altered object.
1385    return *this ;
1386}
1387
1388
1389/*=============================================================================
1390 |
1391 | NAME
1392 |
1393 |     operator[]
1394 |
1395 | DESCRIPTION
1396 |
1397 |     Bounds checked indexing operator for read only access:
1398 |         coeff = p[ i ] ;
1399 |
1400 +============================================================================*/
1401
1402const ppuint PolyMod::operator[]( int i ) const
1403{
1404    // Can throw an exception.
1405    return g_[ i ] ;
1406}
1407
1408
1409/*=============================================================================
1410|
1411| NAME
1412|
1413|     constructPowerTable
1414|
1415| DESCRIPTION
1416|
1417|     Construct a table of powers of x:
1418|
1419|      n                     2n-2
1420|     x  (mod f(x), p)  ... x    (mod f(x), p)
1421|
1422|
1423|    powerTable_[i][j] is the coefficient of
1424|     j       n+i
1425|    x   in  x   (mod f(x), p) where 0 <= i <= n-2 and 0 <= j <= n-1.
1426|
1427| EXAMPLE
1428|                                  4     2                     4
1429|     Let n = 4, p = 5 and f(x) = x  +  x  +  2x  +  3.  Then x  =
1430|
1431|         2                  2
1432|     -( x  +  2x  + 3) = 4 x  + 3 x + 2 (mod f(x), 5), and we get
1433|
1434|      4                    2
1435|     x  (mod f(x), 5) = 4 x  + 3 x + 2 = powerTable_[0].
1436|
1437|      5                       2                 3      2
1438|     x  (mod f(x), 5) = x( 4 x  + 3 x + 2) = 4 x  + 3 x  + 2x
1439|                      = powerTable_[1].
1440|
1441|      6                       3      2           4      3      2
1442|     x  (mod f(x), 5) = x( 4 x  + 3 x + 2 x) = 4x  + 3 x  + 2 x
1443|
1444|                              2                 3      2
1445|                      = 4 ( 4x  + 3 x + 2) + 3 x  + 2 x  =
1446|
1447|                           3     2
1448|                      = 3 x + 3 x + 2 x + 3 = powerTable_[2].
1449|
1450|                                    j
1451|     powerTable_[i][j]:       | 0  1  2  3
1452|                           ---+-------------
1453|                            0 | 2  3  4  0
1454|                        i   1 | 0  2  3  4
1455|                            2 | 3  2  3  3
1456|
1457| NOTES
1458|                              n-1
1459|     Beginning with t( x ) = x,    compute the next power of x from the last
1460|                                                         n
1461|     one by multiplying by x.  If necessary, substitute x  =
1462|             n-1
1463|     -( a   x    + ... + a ) to reduce the degree.  This formula comes from
1464|         n-1              0
1465|                               n         n-1
1466|     the observation f( x ) = x   + a   x    + ... + a    = 0 (mod f(x), p).
1467|                                     n-1              0
1468|
1469+============================================================================*/
1470
1471void PolyMod::constructPowerTable()
1472{
1473    // Get hold of the degree of f(x).
1474    int n = f_.deg() ;
1475
1476    // Empty the power table.
1477    powerTable_.clear() ;
1478
1479    //
1480    //  t(x) is temporary storage for x ^ k (mod f(x),p)
1481    //   n <= k <= 2n-2.  Its degree can go as high as
1482    //   n before it is reduced again.
1483    Polynomial t ;
1484
1485
1486    //                         n-1
1487    //    Initialize t( x ) = x    mod p.
1488    t[ n-1 ] = 1 ;
1489    
1490    // In Microsoft Visual Studio C++ 2008 we get garbage placed in t[ n ] in the loop
1491    // at j = n in the step
1492    //     t[ j ] = t[ j-1 ] ;
1493    // Why?  We first access the value of t[j-1], the compiler places it in a temporary,
1494    // we then access t[n], and this causes a resize of f_ in Polynomial.operator[],
1495    // then t[ j ] = garbage since we apparently lose the temporary. Does not happen if
1496    // we rewrite the step as
1497	//			int tmp ;
1498    //   		tmp = t[ j-1 ] ;
1499    //          t[ j ] = tmp ;
1500    // or alternatively, we prevent resizing occurring by pre-expanding:
1501	t[ n   ] = 0 ;  // Expand the size of t(x) now since we'll access t[n] later.  
1502
1503    t.setModulus( getModulus() ) ;
1504
1505    try
1506	{
1507		//                                      i+n
1508		//  Fill the ith row of the table with x   (mod f(x), p)
1509		//  for i = 0 ... n-2.
1510		//
1511		for (int i = 0 ;  i <= n - 2 ;  ++i)
1512		{
1513			// Compute t(x) = x t(x) by shifting the coefficients
1514			// to the left and filling with zero.
1515			for (int j = n ;  j >= 1 ;  --j)
1516                t[ j ] = t[ j-1 ] ;
1517
1518			t[ 0 ] = 0 ;
1519
1520			//  Coefficient of the x ^ n degree term of t(x).
1521			ppsint coeff = 0 ;
1522			if ( (coeff = t[ n ]) != 0)
1523			{
1524				//  Zero out the x ^ n th term.
1525				t[ n ] = 0 ;
1526
1527				//          n       n                        n-1
1528				// Replace x  with x  (mod f(x), p) = -(a   x   + ... + a )
1529				//                                         n-1             0
1530				for (int j = 0 ;  j <= n-1 ;  ++j)
1531
1532					t[ j ] = mod( t[ j ] +
1533								  mod( -coeff * f_[ j ]) ) ;
1534			}  // end if
1535
1536			//  Copy t(x) into row i of power_table.
1537			powerTable_.push_back( t ) ;
1538
1539		} // end for
1540
1541		#ifdef DEBUG_PP_POLYNOMIAL
1542        cout << "PowerTable of polynomials x^n ... x^2n-2 mod f(x), p" << endl ;
1543        cout << "f(x) = " << getf() << " n = " << n << " p = " << getModulus() << endl ;
1544        for  (int i = n ;  i <= 2*n-2 ;  ++i)
1545            cout << "powerTable[ x^" << i << " ] = " << powerTable_[ offset(i) ] << endl ;
1546		#endif
1547    }
1548    catch( bad_alloc & e )
1549    {
1550        throw PolynomialRangeError( "PolyMod::constructPowerTable had a bad_alloc memory allocation failure", __FILE__, __LINE__ ) ;
1551    }
1552
1553    // t will be automagically freed upon exit.
1554    return ;
1555}
1556
1557
1558/*=============================================================================
1559|
1560| NAME
1561|
1562|     modf
1563|
1564| DESCRIPTION
1565|
1566|     Reduce g(x) modulo f(x), and p.
1567|
1568| EXAMPLE
1569|                                  4     2
1570|     Let n = 4, p = 5 and f(x) = x  +  x  +  2x  +  3.
1571|
1572|      6                       3      2           4      3      2
1573|     x  (mod f(x), 5) = x( 4 x  + 3 x + 2 x) = 4x  + 3 x  + 2 x
1574|
1575|
1576+============================================================================*/
1577
1578void PolyMod::modf()
1579{
1580    // Get hold of the degree of f(x).
1581    int n = f_.deg() ;
1582    int m = g_.deg() ;
1583
1584    if (m > 2 * n - 2)
1585    {
1586        ostringstream os ;
1587        os << "Error in PolyMod::modf():  degree of g(x) higher than power table can handle with deg f = " << n 
1588           << " deg g = " << m << " and p = " << getModulus() ;
1589        throw PolynomialRangeError( os.str(), __FILE__, __LINE__ ) ;
1590    }
1591
1592
1593    //                                      i+n
1594    //  Fill the ith row of the table with x   (mod f(x), p)
1595    //  for i = 0 ... n-2.
1596    //
1597    for (int i = n ;  i <= m ;  ++i)
1598    {
1599        #ifdef DEBUG_PP_POLYNOMIAL
1600        cout << "\nBefore converting, g( x ) = " << g_ << endl ;
1601        #endif
1602
1603        //  Coefficient of the x ^ i degree term of g(x).
1604        ppuint coeff{ 0 } ;
1605        if ( (coeff = g_[ i ]) != 0)
1606        {
1607            //  Subtract (zero out) the x ^ i th term.
1608            g_[ i ] = 0 ;
1609
1610            //          i       i
1611            // Replace x  with x  (mod f(x), p) from the power table * coeff.
1612            g_ += (powerTable_[ offset(i) ] * coeff) ;
1613         }
1614
1615         #ifdef DEBUG_PP_POLYNOMIAL
1616         cout << "\nAfter converting with coeff = " << coeff << " g( x ) = " << g_ << endl ;
1617         #endif
1618
1619    } // end for
1620
1621    return ;
1622}
1623
1624
1625/*=============================================================================
1626 |
1627 | NAME
1628 |
1629 |     autoconvolve
1630 |
1631 | DESCRIPTION
1632 |
1633 |      Compute a convolution-like sum for use in function coeffOfSquare,
1634 |
1635 |      upper
1636 |      ---
1637 |      \    t  t       But define the sum to be 0 when lower > upper to catch
1638 |      /     i  k-i    the special cases that come up in function coeffOfSquare.
1639 |      ---
1640 |      i=lower
1641 |
1642 |      where
1643 |                                  n-1
1644 |     Coefficients of t(x) = t    x    + ... + t x  + t
1645 |                             n-1               1      0
1646 |
1647 | EXAMPLE
1648 |                        3      2
1649 |      Suppose t(x) = 4 x  +  x  +  3 x  +  3, lower = 1, upper = 3, n = 3,
1650 |
1651 |      and p = 5.  For k = 3, autoConvolve = t[ 1 ] t[ 2 ] + t[ 2 ] t[ 1 ] +
1652 |
1653 |      t[ 3 ] t[ 0 ] = 3 * 1 + 1 * 3 + 4 * 3 = 18 mod 5 = 3.  For lower = 0,
1654 |
1655 |      upper = -1, or for lower = 3 and upper = 2, autoConvolve = 0, no matter what
1656 |
1657 |      k is.
1658 |
1659 | METHOD
1660 |
1661 |     A "for" loop in the C language is not executed when its lower limit
1662 |
1663 |     exceeds its upper limit, leaving sum = 0.
1664 |
1665 +============================================================================*/
1666
1667ppuint autoConvolve( const Polynomial & t, int k, int lower, int upper )
1668{
1669    ModP<ppuint,ppsint> mod( t.modulus() ) ;
1670    int deg_t = t.deg() ;
1671
1672    ppuint sum { 0 } ;
1673    for (int i = lower ;  i <= upper ;  ++i)
1674    {
1675        // Coeff is zero if higher or lower than degree of polynomial.
1676        ppuint coeff_ti{ 0u } ;
1677        ppuint coeff_tkmi{ 0u } ;
1678
1679        if (i <= deg_t && i >= 0)
1680            coeff_ti = t[ i ] ;
1681
1682        if (k-i <= deg_t && k-i >= 0)
1683            coeff_tkmi = t[ k - i ] ;
1684
1685        sum = mod( sum + mod( coeff_ti * coeff_tkmi )) ;
1686    }
1687
1688    return( sum ) ;
1689}
1690
1691
1692/*=============================================================================
1693 |
1694 | NAME
1695 |
1696 |     convolve
1697 |
1698 | DESCRIPTION
1699 |
1700 |      Compute a convolution-like sum,
1701 |
1702 |      upper
1703 |      ---
1704 |      \    s  t       But define the sum to be 0 when lower > upper to catch
1705 |      /     i  k-i    the special cases
1706 |      ---
1707 |      i=lower
1708 |
1709 |      where
1710 |                                   n-1
1711 |      Coefficients of s(x) = s    x    + ... + s x  + s
1712 |                              n-1               1      0
1713 |                                      n-1
1714 |      Coefficients of t(x) = t    x    + ... + t x  + t
1715 |                              n-1               1      0
1716 |
1717 |      0 <= k <= 2n - 2
1718 |      0 <= lower <= n-1
1719 |      0 <= upper <= n-1
1720 |
1721 | EXAMPLE
1722 |                        3      2
1723 |      Suppose s(x) = 4 x  +  x  +  3 x  +  3,
1724 |
1725 |                        3     2
1726 |      Suppose t(x) = 4 x  +  x  +  3 x  +  3,
1727 |
1728 |
1729 |      lower = 1, upper = 3, n = 3,
1730 |
1731 |      and p = 5.  For k = 3, convolve = t[ 1 ] t[ 2 ] + t[ 2 ] t[ 1 ] +
1732 |
1733 |      t[ 3 ] t[ 0 ] = 3 * 1 + 1 * 3 + 4 * 3 = 18 mod 5 = 3.  For lower = 0,
1734 |
1735 |      upper = -1, or for lower = 3 and upper = 2, convolve = 0, no matter what
1736 |
1737 |      k is.
1738 |
1739 | METHOD
1740 |
1741 |     A "for" loop in the C language is not executed when its lower limit
1742 |
1743 |     exceeds its upper limit, leaving sum = 0.
1744 |
1745 +============================================================================*/
1746
1747ppuint convolve( const Polynomial & s, const Polynomial & t,
1748               const int k, const int lower, const int upper )
1749{
1750    ppuint sum{ 0 } ;     // Convolution sum.
1751
1752    ModP<ppuint,ppsint> mod( s.modulus() ) ; // Initialize the functionoid.
1753
1754    int deg_s{ s.deg() } ;
1755    int deg_t{ t.deg() } ;
1756
1757    for (int i = lower ;  i <= upper ;  ++i)
1758    {
1759        // Coeff is zero if higher or lower than degree of polynomial.
1760        ppuint coeff_s{ 0u } ;
1761        ppuint coeff_t{ 0u } ;
1762
1763        if (i <= deg_s && i >= 0)
1764            coeff_s = s[ i ] ;
1765
1766        if (k-i <= deg_t && k-i >= 0)
1767            coeff_t = t[ k- i ] ;
1768
1769       sum = mod( sum + mod( coeff_s * coeff_t )) ;
1770    }
1771
1772    return( sum ) ;
1773}
1774
1775
1776/*=============================================================================
1777 |
1778 | NAME
1779 |
1780 |     coeffOfSquare
1781 |
1782 | DESCRIPTION
1783 |                                     th                2
1784 |      Return the coefficient of the k   power of x in g ( x )  modulo p,
1785 |      given of g(x) of degree <= n-1.
1786 |
1787 |      where 0 <= k <= 2n-2
1788 |
1789 | EXAMPLE
1790 |                                     3     2                 2
1791 |     Let n = 4, p = 5, and g(x) = 4 x  +  x  + 3 x + 3.  g(x) =
1792 |
1793 |      6      5
1794 |     x  + 3 x + 3 x + 3 x + 4, all modulo 5.
1795 |
1796 |             k        |  0  1  2  3  4  5  6
1797 |      ----------------+---------------------
1798 |      coeffOfSquare   |  4  3  0  0  0  3  1
1799 |
1800 | METHOD
1801 |                                                          2
1802 |     The formulas were gotten by writing out the product g (x) explicitly.
1803 |
1804 |     The sum is 0 in two cases:
1805 |
1806 |         (1) when k = 0 and the limits of summation are 0 to -1
1807 |
1808 |         (2) k = 2n - 2, when the limits of summation are n to n-1.
1809 |
1810 |     To derive the formulas, let
1811 |
1812 |                      n-1
1813 |     Let g(x) = g    x     +  ... + g x + g
1814 |                 n-1                 1     0
1815 |
1816 |     Look at the formulas in coeffOfProduct for each power of x,
1817 |        replacing s with t, and observe that half of the terms are
1818 |        duplicates, so we can save computation time.
1819 |
1820 |     Inspection yields the formulas,
1821 |
1822 |     for 0 <= k <= n-1, even k,
1823 |
1824 |      k/2-1
1825 |       ---             2
1826 |    2  \   g  g     + g
1827 |       /    i  k-i     k/2
1828 |       ---
1829 |       i=0
1830 |
1831 |     for 0 <= k <= n-1, odd k,
1832 |
1833 |       (k-1)/2
1834 |       ---
1835 |     2 \   g  g
1836 |       /    i  k-i
1837 |       ---
1838 |       i=0
1839 |
1840 |     and for n <= k <= 2n-2, even k,
1841 |
1842 |       n-1
1843 |       ---            2
1844 |    2  \   g  g    + g
1845 |       /    i  k-i    k/2
1846 |       ---
1847 |       i=k/2+1
1848 |
1849 |       and for n <= k <= 2n-2, odd k,
1850 |
1851 |       n-1
1852 |       ---
1853 |    2  \   g  g
1854 |       /    i  k-i
1855 |       ---
1856 |       i=(k+1)/2
1857 |
1858 +============================================================================*/
1859
1860ppuint coeffOfSquare( const Polynomial & g, const int k, const int n )
1861{
1862    ModP<ppuint,ppsint> mod( g.modulus() ) ; // Initialize the functionoid.
1863
1864                        //                          2
1865    ppuint sum { 0 } ;      // kth coefficient of g( x )
1866
1867    // Coeff is zero if higher or lower than degree of polynomial.
1868    ppuint coeff_gkd2 { 0 } ;
1869    if (k/2 <= g.deg() && k/2 >= 0)
1870        coeff_gkd2 = g[ k/2 ] * g[ k/2 ] ;
1871
1872    if (0 <= k && k <= n-1)
1873    {
1874        if (k % 2 == 0)        // Even k
1875            sum = mod( mod( 2 * autoConvolve( g, k, 0, k/2 - 1) ) + coeff_gkd2 ) ;
1876
1877         else                  // Odd k
1878             sum = mod( 2 * autoConvolve( g, k, 0, (k-1)/2)) ;
1879    }
1880    else if (n <= k && k <= 2 * n - 2)
1881    {
1882
1883        if (k % 2 == 0)        // Even k
1884            sum = mod( mod( 2 * autoConvolve( g, k, k/2 + 1, n-1)) + coeff_gkd2 ) ;
1885
1886         else                  // Odd k
1887             sum = mod( 2 * autoConvolve( g, k, (k+1)/2, n-1)) ;
1888    }
1889
1890    return( sum ) ;
1891}
1892
1893
1894/*=============================================================================
1895 |
1896 | NAME
1897 |
1898 |     coeffOfProduct
1899 |
1900 | DESCRIPTION
1901 |                                     th
1902 |      Return the coefficient of the k   power of x in s( x ) t( x )  modulo p.
1903 |
1904 | EXAMPLE
1905 |                               3     2                  2
1906 |   Let n = 4, p = 5, t(x) = 4 x  +  x  + 4, s( x ) = 3 x  + x + 2
1907 |
1908 |                            5      4      3      2
1909 |   then s ( x ) t( x ) = 2 x  + 2 x  + 4 x  + 4 x  + 4 x + 3
1910 |
1911 |   We'll do the case k=3,
1912 |
1913 |   t3 s0 + t2 s1 + t1 s2 + t0 s3 = 4 * 2 + 1 * 1 + 0 * 3 + 4 * 0 = 9 = 4 (mod 5).
1914 |
1915 |             k       |  0  1  2  3  4  5  6
1916 |      -----------------+---------------------
1917 |      coeffOfProduct |  3  4  4  4  2  2  0
1918 |
1919 | METHOD
1920 |
1921 |     The formulas were gotten by writing out the product s(x) t (x) explicitly.
1922 |
1923 |     The sum is 0 in two cases:
1924 |
1925 |         (1) when k = 0 and the limits of summation are 0 to -1
1926 |
1927 |         (2) k = 2n - 2, when the limits of summation are n to n-1.
1928 |
1929 |
1930 |     To derive the formulas, let
1931 |
1932 |                       n-1
1933 |     Let s (x) = s    x     +  ... + s x + s  and
1934 |                  n-1                 1     0
1935 |
1936 |                       n-1
1937 |         t (x) = t    x     +  ... + t x + t
1938 |                  n-1                 1     0
1939 |
1940 |     and multiply out the terms, collecting like powers of x:
1941 |
1942 |
1943 |     Power of x     Coefficient
1944 |     ==========================
1945 |      2n-2
1946 |     x              s    t
1947 |                     n-1  n-1
1948 |
1949 |      2n-3
1950 |     x              s    t    +  s    t
1951 |                     n-2  n-1     n-1  n-2
1952 |
1953 |      2n-4
1954 |     x              s    t    +  s    t    +  s    t
1955 |                     n-3  n-1     n-2  n-2     n-3  n-1
1956 |
1957 |      2n-5
1958 |     x              s    t    +  s    t    +  s    t    +  s    t
1959 |                     n-4  n-1     n-3  n-2     n-2  n-3     n-1  n-4
1960 |
1961 |      . . .
1962 |
1963 |      n
1964 |     x              s  t    +  s  t    + ...  +  s    t
1965 |                     1  n-1     2  n-2            n-1  1
1966 |
1967 |      n-1
1968 |     x              s  t    +  s  t    + ...  +  s    t
1969 |                     0  n-1     1  n-2            n-1  0
1970 |
1971 |     . . .
1972 |
1973 |      3
1974 |     x              s  t  +  s  t  +  s  t  +  s  t
1975 |                     0  3     1  2     2  1     3  0
1976 |
1977 |      2
1978 |     x              s  t  +  s  t  +  s  t
1979 |                     0  2     1  1     2  0
1980 |
1981 |
1982 |     x              s  t  +  s  t
1983 |                     0  1     1  0
1984 |
1985 |     1              s  t
1986 |                     0  0
1987 |
1988 |
1989 |     Inspection yields the formulas,
1990 |
1991 |
1992 |     for 0 <= k <= n-1,
1993 |
1994 |       k
1995 |      ---
1996 |      \   s  t
1997 |      /    i  k-i
1998 |         ---
1999 |      i=0
2000 |
2001 |
2002 |     and for n <= k <= 2n-2,
2003 |
2004 |      n-1
2005 |      ---
2006 |      \   s  t
2007 |      /    i  k-i
2008 |      ---
2009 |     i=k-n+1
2010 |
2011 +============================================================================*/
2012
2013ppuint coeffOfProduct( const Polynomial & s, const Polynomial & t, const int k, const int n )
2014{
2015    // Check if p is the same for s and t, and check the degree of s and t are < n.
2016	if (s.modulus() != t.modulus() || s.deg()> n || t.deg() > n)
2017	    throw PolynomialRangeError( "coeffOfProduct:  degree or modulus doesn't agree for polynomials s and t",
2018                                   __FILE__, __LINE__ ) ;
2019
2020    ppuint sum { 0 } ;      // kth coefficient of t(x) ^ 2.
2021
2022    if (0 <= k && k <= n-1)
2023    {
2024        sum = convolve( s, t, k, 0, k ) ;
2025    }
2026    else if (n <= k && k <= 2 * n - 2)
2027    {
2028        sum = convolve( s, t, k, k - n + 1, n - 1 ) ;
2029    }
2030
2031    return( sum ) ;
2032}
2033
2034
2035/*=============================================================================
2036 |
2037 | NAME
2038 |
2039 |     *
2040 |
2041 | DESCRIPTION
2042 |
2043 |      Compute s( x ) t( x ) (mod f(x), p)
2044 |
2045 |      s(x), of degree <= n-1.
2046 |      t(x), of degree <= n-1.
2047 |
2048 |      Uses a precomputed table of powers of x,
2049 |      powerTable contains x ^ k (mod f(x), p) for n <= k <= 2n-2, f monic.
2050 |
2051 | EXAMPLE
2052 |                                      3    2                 2
2053 |     Let n = 4 and p = 5, t( x ) = 4 x  + x + 4, s( x ) = 3 x + x + 2
2054 |
2055 |                             5      4     3     2
2056 |     Then s( x ) t( x ) = 2 x  + 2 x + 4 x + 4 x + 4 x + 3, modulo 5,
2057 |
2058 |                                          4   2
2059 |     and after reduction modulo f( x ) = x + x + 2 x + 3, using the power
2060 |
2061 |                        4      2               5      3      2
2062 |     table entries for x  = 4 x + 3 x + 2 and x  = 4 x  + 3 x + 2 x, we get
2063 |
2064 |                                        3      2
2065 |     s( x ) t( x ) (mod f( x ), p) = 2 x  + 3 x  + 4 x + 2
2066 |
2067 |
2068 | METHOD
2069 |
2070 |     Compute the coefficients using the function coeffOfProduct.
2071 |
2072 |     The next step is to reduce s(x) t(x) modulo f(x) and p.  To do so, replace
2073 |
2074 |                            k                                      k
2075 |     each non-zero term t  x,  n <= k <= 2n-2, by the term t * [ x   mod f(x), p)]
2076 |                         k                                  k
2077 |
2078 |     which we get from the array powerTable.
2079 |
2080 +============================================================================*/
2081
2082const PolyMod operator*( const PolyMod & s,
2083                         const PolyMod & t )
2084{
2085    // Do * in terms of *= to maintain consistency.
2086    // Return value optimization compiles away the copy constructor.
2087    // const on return type disallows doing (u*v) = w ;
2088    return PolyMod( s ) *= t ;
2089}
2090
2091
2092/*=============================================================================
2093 |
2094 | NAME
2095 |
2096 |    *=
2097 |
2098 | DESCRIPTION
2099 |
2100 |     C-like multiply by operator
2101 |
2102 +============================================================================*/
2103
2104PolyMod &
2105PolyMod::operator*=( const PolyMod & t )
2106{
2107    int i, j ;   //                 k             2
2108    ppuint coeff;  // Coefficient of x  term of t(x)
2109
2110    // Temporary storage for the new t(x).  Can have degree up to n.
2111    Polynomial temp ;
2112
2113    // Get hold of the degree of f(x).
2114    int n = f_.deg() ;
2115
2116    //                               0        n-1
2117    //  Compute the coefficients of x , ..., x.   These terms do not require
2118    //  reduction mod f(x) because their degree is less than n.
2119    for (i = 0 ;  i <= n ;  ++i)
2120        temp[ i ] = coeffOfProduct( g_, t.g_, i, n ) ;
2121
2122    //                               n        2n-2             k
2123    //  Compute the coefficients of x , ..., x.    Replace t  x  with
2124    //                                                      k
2125    //          k
2126    //  t  * [ x  (mod f(x), p) ] from array powerTable when t is
2127    //   k                                                    k
2128    //  non-zero.
2129    for (i = n ;  i <= 2 * n - 2 ;  ++i)
2130        if ( (coeff = coeffOfProduct( g_, t.g_, i, n)) != 0 )
2131            for (j = 0 ;  j <= n - 1 ;  ++j)
2132                temp[ j ] = mod( temp[ j ] +
2133                                 mod( coeff * powerTable_[ offset(i) ] [ j ])) ;
2134
2135    for (i = 0 ;  i <= n - 1 ;  ++i)
2136        g_[ i ] = temp[ i ] ;
2137
2138    // Return (reference to) the product.
2139    return *this ;
2140}
2141
2142
2143/*=============================================================================
2144 |
2145 | NAME
2146 |
2147 |     timesX
2148 |
2149 | DESCRIPTION
2150 |
2151 |      Compute x g(x) (mod f(x), p)
2152 |
2153 | EXAMPLE
2154 |
2155 |     g.timesX( t ) ;
2156 |
2157 | EXAMPLE
2158 |                                     3       2
2159 |     Let n = 4, p = 5, and g(x) = 2 x  +  4 x  + 3 x.  Let f(x) =
2160 |      4    2                                  4      3      2
2161 |     x  + x  + 2 x + 3.  Then x t (x) = 2 x  + 4 x  + 3 x  and
2162 |                                      2                3     2
2163 |     x g(x) (mod f(x), p) = 2 * (4 x + 3 x + 2) + 4 x + 3 x  =
2164 |        3    2
2165 |     4 x  + x + x + 4.
2166 |          3     2
2167 |     = 4 x + 2 x + 3 x + 2.
2168 |
2169 | METHOD
2170 |
2171 |     Uses a precomputed table of powers of x.
2172 |
2173 |                           n-1         n-2
2174 |     Multiply g(x) = g    x   +  g    x   + ... + g  by shifting the coefficients
2175 |                      n-1         n-2              0
2176 |
2177 |                          n
2178 |     to the left.  If an x   term appears, eliminate it by
2179 |
2180 |     substitution using powerTable.
2181 |
2182 +============================================================================*/
2183
2184void PolyMod::timesX()
2185{
2186    int n = f_.deg() ;
2187
2188    #ifdef DEBUG_PP_POLYNOMIAL
2189    cout << "timesX:  g( x ) = " << g_ << endl ;
2190    #endif
2191
2192    //  Multiply g(x) by x by shifting the coefficients left in the array, giving
2193    //         n-1
2194    //   g    x    + ... + g  x + 0
2195    //    n-2               1
2196    //
2197    // but save the coefficient g    first before overwriting it.
2198    //                           n-1
2199    ppuint g_coeff{ g_[ n - 1 ] } ;
2200
2201    for (int i = n-1 ;  i >= 1 ;  --i)
2202        g_[ i ] = g_[ i-1 ] ;
2203
2204    g_[ 0 ] = 0 ;
2205
2206    //                 n                n
2207    //    Replace g   x  with g    * [ x  (mod f(x), p) ] using
2208    //             n-1         n-1
2209    //     n
2210    //    x  from powerTable
2211
2212    if (g_coeff != 0)
2213    {
2214        for (int i = 0 ;  i <= n - 1 ;  ++i)
2215            g_[ i ] = mod( g_[ i ] +
2216                           mod( g_coeff * powerTable_[ offset(n) ] [ i ] )) ;
2217    }
2218
2219    #ifdef DEBUG_PP_POLYNOMIAL
2220    cout << "timesX:  x g( x ) = " << g_ << endl ;
2221    #endif
2222}
2223
2224
2225/*=============================================================================
2226 |
2227 | NAME
2228 |
2229 |      square
2230 |
2231 | DESCRIPTION
2232 |
2233 |               2
2234 |      Compute g (x) (mod f(x), p)
2235 |
2236 | EXAMPLE
2237 |
2238 |     g.square() ;
2239 |
2240 |
2241 | EXAMPLE
2242 |                                     3     2
2243 |     Let n = 4, p = 5, and g(x) = 4 x  +  x  + 4.  Let f(x) =
2244 |
2245 |      4    2                   2       6      5     4      3     2
2246 |     x  + x  + 2 x + 3.  Then t (x) = x  + 3 x  +  x  + 2 x + 3 x +  1
2247 |
2248 |     Now subsituting powers of x modulo f(x) from the power table,
2249 |
2250 |       2                         3     2
2251 |      t (x) (mod f(x), p) =  (3 x + 3 x + 2 x + 3) +
2252 |
2253 |             3      2                 2                3     2
2254 |     3 * (4 x  + 3 x + 2 x) + 4 * (4 x + 3 x + 2) + 4 x + 4 x + 3 x + 1
2255 |
2256 |          3     2
2257 |     = 2 x + 4 x + x + 1.
2258 |
2259 |
2260 | METHOD
2261 |
2262 |     Uses a precomputed table of powers of x.
2263 |
2264 |
2265 |          2            2n-2              n         n-1
2266 |     Let g (x) = g    x     +  ... + g  x  +  g   x   +  ... + g .
2267 |                  2n-2                n        n-1              0
2268 |
2269 |     Compute the coefficients g  using the function coeffOfSquare.
2270 |                               k
2271 |
2272 |                                 2
2273 |     The next step is to reduce g (x) modulo f(x).  To do so, replace
2274 |
2275 |                            k                                      k
2276 |     each non-zero term g  x,  n <= k <= 2n-2, by the term g * [ x   mod f(x), p)]
2277 |                         k                                  k
2278 |
2279 |     which we get from the array powerTable_.
2280 |
2281 +============================================================================*/
2282
2283void PolyMod::square()
2284{
2285    // Get hold of the degree of f(x).
2286    int n = f_.deg() ;
2287
2288    #ifdef DEBUG_PP_POLYNOMIAL
2289    cout << "square:  g( x ) = " << g_ << endl ;
2290    #endif
2291
2292    // Temporary storage for the new g(x).  Can have degree up to n.
2293    Polynomial t ;
2294
2295    //                               0        n-1
2296    //  Compute the coefficients of x , ..., x.   These terms do not require
2297    //
2298    //  reduction mod f(x) because their degree is less than n.
2299    for (int i = 0 ;  i <= n ;  ++i)
2300        t[ i ] = coeffOfSquare( g_, i, n ) ;
2301
2302    //                               n        2n-2             k
2303    //  Compute the coefficients of x , ..., x.    Replace g  x  with
2304    //          k                                           k
2305    //  g  * [ x  (mod f(x), p) ] from array powerTable_ when g is
2306    //   k                                                     k
2307    //  non-zero.
2308    for (int i = n ;  i <= 2 * n - 2 ;  ++i)
2309    {
2310        ppuint coeff{ 0 } ;
2311
2312        if ( (coeff = coeffOfSquare( g_, i, n )) != 0 )
2313
2314            for (int j = 0 ;  j <= n- 1 ;  ++j)
2315
2316                t[ j ] = mod( t[ j ] + mod( coeff * powerTable_[ offset(i) ] [ j ])) ;
2317    }
2318
2319    for (int i = 0 ;  i <= n - 1 ;  ++i)
2320
2321        g_[ i ] = t[ i ] ;
2322
2323    #ifdef DEBUG_PP_POLYNOMIAL
2324    cout << "square:  g( x ) ^ 2 = " << g_ << endl ;
2325    #endif
2326}
2327
2328
2329/*=============================================================================
2330 |
2331 | NAME
2332 |
2333 |     power
2334 |
2335 | DESCRIPTION
2336 |                       m
2337 |      Compute g(x) = x  (mod f(x), p).
2338 |
2339 | EXAMPLE
2340 |                               4    2
2341 |     Let n = 4, p = 5, f(x) = x  + x  + 2 x + 3, and m = 156.
2342 |
2343 |     156 = 0  . . . 0  1  0  0  1  1  1  0  0 (binary representation)
2344 |           |<- ignore ->| S  S SX SX SX  S  S (exponentiation rule,
2345 |                                               S = square, X = multiply by x)
2346 |      m
2347 |     x  (mod f(x), p) =
2348 |
2349 |          2     2
2350 | 6   S   x  =  x
2351 |
2352 |          4       2
2353 | 5   S   x  =  4 x  + 3 x + 2
2354 |
2355 |
2356 |          8       3      2
2357 | 4   S   x  =  4 x  + 4 x + 1
2358 |
2359 |          9       3     2
2360 | 4   X   x  =  4 x  +  x + 3 x + 3
2361 |
2362 |
2363 |          18       2
2364 | 3   S   x  =  2 x  +  x + 2
2365 |
2366 |          19      3     2
2367 | 3   X   x  =  2 x  +  x  + 2 x
2368 |
2369 |
2370 |          38      3       2
2371 | 2   S   x  =  2 x  +  4 x  +  3 x
2372 |
2373 |          39      3       2
2374 | 2   X   x  =  4 x  +  2 x  +  x + 4
2375 |
2376 |
2377 |          78      3       2
2378 | 1   S   x  =  4 x  +  2 x  +  3 x + 2
2379 |
2380 |          156
2381 | 0   S   x    = 3
2382 |
2383 | METHOD
2384 |
2385 |     Exponentiation by repeated squaring, using precomputed table of
2386 |     powers.  See ART OF COMPUTER PROGRAMMING, vol. 2, 2nd Ed.,
2387 |     D. E. Knuth,  pgs 441-443.
2388 |
2389 |      n         2n-2
2390 |     x,  ... , x    (mod f(x), p)
2391 |
2392 |                     m
2393 |     to find g(x) = x   (mod f(x), p), expand m into binary,
2394 |
2395 |            k        k-1
2396 |     m = a 2  + a   2    + . . . + a 2 + a
2397 |          k      k-1                1     0
2398 |
2399 |                             m
2400 |     where a = 1, and split x   apart into
2401 |            k
2402 |
2403 |             k      k
2404 |            2      2  a             2 a    a
2405 |      m                k-1             1    0
2406 |     x  =  x     x           . . .  x     x
2407 |
2408 |
2409 |     Then to raise x to the mth power, do
2410 |
2411 |
2412 |     t( x ) = x
2413 |
2414 |     return if m = 1
2415 |
2416 |
2417 |     for i = k-1 downto 0 do
2418 |
2419 |                    2
2420 |         t(x) = t(x)  (mod f(x), p)       // Square each time.
2421 |
2422 |         if a = 1 then
2423 |             i
2424 |
2425 |             t(x) = x t(x) (mod f(x), p)  // Times x only if current bit is 1
2426 |         endif
2427 |
2428 |     endfor
2429 |                                                         k
2430 |                                                        2
2431 |     The initial t(x) = x gets squared k times to give x  .  If a  = 1 for
2432 |                                                                 i
2433 |     0 <= i <= k-1, we multiply by x which then gets squared i times more
2434 |
2435 |               i
2436 |              2
2437 |     to give x .  On a binary computer, we use bit shifting and masking to
2438 |
2439 |     identify the k bits  { a    . . .  a  } to the right of the leading 1
2440 |                             k-1         0
2441 |
2442 |     bit.  There are log ( m ) - 1 squarings and (number of 1 bits) - 1
2443 |                            2
2444 |     multiplies.
2445 |
2446 +============================================================================*/
2447
2448const PolyMod power( const PolyMod & g1, const BigInt & m )
2449{
2450    // Return if g(x) != x
2451    if (g1.f_.deg() == 1 && g1[ 0 ] == 0 && g1[ 1 ] == 1)
2452    {
2453        ostringstream os ;
2454        os << "Error in PolyMod::power():  g( x ) != x "
2455           << "with deg g = " << g1.f_.deg() << " m = " << m ;
2456        throw PolynomialRangeError( os.str(), __FILE__, __LINE__ ) ;
2457    }
2458
2459    // Exit right away if m = 1 and return a copy of g(x).
2460    PolyMod g( g1 ) ;
2461
2462    if (m == static_cast<BigInt>( 1u ))
2463        return g ;
2464
2465    // Find the number of the leading bit.
2466    int bitNum = m.maxBitNumber() ; // Number of highest possible bit.
2467
2468    #ifdef DEBUG_PP_POLYNOMIAL
2469    cout << "initial max bitNum = " << bitNum << endl ;
2470    cout << "g( x ) = " << g << endl ;
2471    #endif
2472
2473    while (!m.testBit( bitNum ))
2474        --bitNum ;
2475
2476    #ifdef DEBUG_PP_POLYNOMIAL
2477    cout << "after skipping leading 0 bits, bitNum = " << bitNum << endl ;
2478    #endif
2479
2480    if (bitNum == -1)
2481    {
2482        ostringstream os ;
2483        os << "PolyMod::x_to_power " << "bitNum == -1 internal error in PolyMod" ;
2484        throw PolynomialRangeError( os.str(), __FILE__, __LINE__ ) ;
2485    }
2486
2487    #ifdef DEBUG_PP_POLYNOMIAL
2488    cout << "\nAfter skipping zero bits, bitNum = " << bitNum << endl ;
2489    #endif
2490
2491    //  Exponentiation by repeated squaring.  Discard the leading 1 bit.
2492    //  Thereafter, square for every 0 bit;  square and multiply by x for
2493    //  every 1 bit.
2494    while ( --bitNum >= 0 )
2495    {
2496        g.square() ;
2497
2498        if (m.testBit( bitNum ))
2499           g.timesX() ;
2500
2501        #ifdef DEBUG_PP_POLYNOMIAL
2502        cout << "S " ;
2503        if (m.testBit( bitNum ))
2504            cout << "X " ;
2505        cout << "Bit num = " << bitNum << " g( x ) = " << g << endl ;
2506        #endif
2507    }
2508
2509    #ifdef DEBUG_PP_POLYNOMIAL
2510    cout << "Out of the loop bitNum = " << bitNum << " g( x ) = " << g << endl ;
2511    #endif
2512
2513    return g ;
2514}
2515
2516
2517/*=============================================================================
2518 |
2519 | NAME
2520 |
2521 |    isInteger
2522 |
2523 | DESCRIPTION
2524 |
2525 |    Getter function.
2526 |
2527 +============================================================================*/
2528
2529bool PolyMod::isInteger() const
2530{
2531    return g_.isInteger() ;
2532}
2533
2534
2535
2536/*------------------------------------------------------------------------------
2537|                            PolyOrder Implementation                          |
2538------------------------------------------------------------------------------*/
2539
2540/*=============================================================================
2541 |
2542 | NAME
2543 |
2544 |    PolyOrder()
2545 |
2546 | DESCRIPTION
2547 |
2548 |    Set a new value of f(x) with same degree n and modulus p.
2549 |
2550 +============================================================================*/
2551
2552void PolyOrder::resetPolynomial( const Polynomial & f )
2553{
2554    f_ = f ;
2555}
2556
2557
2558/*=============================================================================
2559 |
2560 | NAME
2561 |
2562 |     PolyOrder()
2563 |
2564 | DESCRIPTION
2565 |
2566 |     Initialize.  Mainly do the prime factoring.
2567 |
2568 +============================================================================*/
2569
2570PolyOrder::PolyOrder( const Polynomial & f )
2571             : f_( f )
2572             , n_( f.deg() )
2573             , p_( f.modulus() )
2574             , mod( f.modulus() )
2575             , p_to_n_minus_1_( BigInt( 0u ))
2576             , r_( 0u )
2577             , a_( 0u )
2578             , factors_of_p_to_n_minus_1_()
2579             , factors_of_R_()
2580             , num_prim_poly_( 0u )
2581             , max_num_poly_( 0u )
2582             , Q_( 0 )
2583             , nullity_( 0 )
2584             , statistics_()
2585{
2586    // This is the most time consuming step for large n:
2587    //               n
2588    //              p  - 1
2589    //  Compute r = -------- and factor r into the product of primes.
2590    //              p - 1
2591    try
2592    {
2593        computeMaxNumPoly() ;
2594        factorR() ;
2595        computeNumPrimPoly() ;
2596    }
2597    catch( BigIntMathError & e )
2598    {
2599        ostringstream os ;
2600        os << "PolyOrder: problem computing p^n or r = (p^n - 1 )/ (p - 1), or factoring r, or finding EulerPhi( p^n - 1 )/ n "
2601           << " p = " << p_ << " n = " << n_
2602           << " [ " << e.what() << " ] " ;
2603       throw PolynomialRangeError( os.str(), __FILE__, __LINE__ ) ;
2604    }
2605
2606    // Copy the factoring statistics, and others.
2607    statistics_ = factors_of_R_.statistics_ ;
2608    statistics_.p = p_ ;
2609    statistics_.n = n_ ;
2610    statistics_.max_num_possible_poly = max_num_poly_ ;
2611    statistics_.num_primitive_poly = num_prim_poly_ ;
2612
2613    // Prepare the Q matrix to the proper size.
2614    try
2615    {
2616        Q_.clear() ;
2617        Q_.resize( n_ ) ;
2618
2619        for (int row = 0 ;  row < n_ ;  ++row)
2620        {
2621            Q_[ row ].resize( n_ ) ;
2622        }
2623
2624    }
2625    // Failed to resize Q matrix.
2626    catch( length_error & e )
2627    {
2628        throw PolynomialError( "PolyOrder::PolyOrder had a length_error exception and failed to allocate the Q matrix", __FILE__, __LINE__ ) ;
2629    }
2630}
2631
2632
2633 /*=============================================================================
2634  |
2635  | NAME
2636  |
2637  |    factorR
2638  |
2639  | DESCRIPTION
2640  |
2641  |    This is the most time consuming step for large n due to the integer
2642  |    factorization.
2643  |
2644  |                                             n
2645  |    Find the maximum number of polynomials  p
2646  |
2647  |    Find
2648  |              n
2649  |             p  - 1
2650  |        r = --------
2651  |             p - 1
2652  |
2653  |    Compute the prime factorization of r.
2654  |                                                 n
2655  |    Find number of primitive polynomials = Phi( p - 1 ) / n
2656  |
2657  | EXAMPLE
2658  |
2659  |    See the examples in the code below.
2660  |
2661  +============================================================================*/
2662
2663void PolyOrder::factorR()
2664{
2665       //  n
2666       // p - 1
2667       p_to_n_minus_1_ = BigInt( max_num_poly_ - static_cast<BigInt>( 1u ) ) ;
2668     
2669      //         n
2670      // Factor p  - 1 into primes.
2671      // Pass in p and n in case we can do a fast table lookup.
2672      factors_of_p_to_n_minus_1_ = Factorization<BigInt>( p_to_n_minus_1_,
2673                                                        FactoringAlgorithm::Automatic, p_, n_ ) ;
2674     
2675     //       n
2676     //      p - 1
2677     // r = -------
2678     //      p - 1
2679     r_ = p_to_n_minus_1_ / (p_ - 1u) ;
2680     
2681    #ifdef DEBUG_PP_FACTOR
2682    cout << "p = " << p_ << endl ;
2683    cout << "n = " << n_ << endl ;
2684    cout << "p^n = " << max_num_poly_ << endl ;
2685    cout << "r = (p^n-1)/(p-1) = " << r_ << endl ;
2686    cout << "p_to_n_minus_1 = " << p_to_n_minus_1_ << endl ;
2687    cout << "factorization of p^n - 1 = " << endl ;
2688    for (unsigned int i = 0 ;  i < factors_of_p_to_n_minus_1_.numDistinctFactors() ;  ++i)
2689         cout << factors_of_p_to_n_minus_1_.primeFactor( i ) << " ^ " << factors_of_p_to_n_minus_1_.multiplicity( i ) << endl ;
2690    #endif // DEBUG_PP_FACTOR
2691     
2692     //                                                 n
2693     // Factor r by starting with the factorization of p - 1
2694     
2695     // Now we have to divide out all factors of (p - 1).
2696     // e.g.
2697     //
2698     //  n        8       5  2
2699     // p - 1 = 19 - 1 = 2  3  5  17 181 3833
2700     //
2701     //                      2
2702     // p - 1 = 19 - 1 = 2  3
2703     //
2704     //  n        8       4
2705     // p - 1 = 19 - 1 = 2     5  17 181 3833
2706     // -----   ------
2707     // p - 1   19 - 1
2708     //
2709     
2710     //                           n
2711     // Copy over the factors of p  - 1
2712     factors_of_R_ = factors_of_p_to_n_minus_1_ ;
2713     
2714     // We're done if p - 1 = 1.
2715     if (p_ > 2)
2716     {
2717         // Factor p - 1 into primes.
2718         Factorization<BigInt> factors_of_p_minus_1( static_cast<BigInt>( p_ - 1u ) ) ;
2719         
2720         #ifdef DEBUG_PP_FACTOR
2721         cout << "factorization of p - 1 = " << endl ;
2722         for (unsigned int i = 0 ;  i < factors_of_p_minus_1.numDistinctFactors() ;  ++i)
2723              cout << factors_of_p_minus_1.primeFactor( i ) << " ^ " << factors_of_p_minus_1.multiplicity( i ) << endl ;
2724         #endif // DEBUG_PP_FACTOR
2725
2726         //                                             n                   n
2727         // p-1 cannot have more distinct factors than p - 1 since p - 1 | p  - 1
2728         if (factors_of_p_minus_1.numDistinctFactors() > factors_of_p_to_n_minus_1_.numDistinctFactors())
2729         {
2730             ostringstream os ;
2731             os << "factorR "
2732                << " number of distinct prime factors for p-1   = " << factors_of_p_minus_1.numDistinctFactors() << " > "
2733                << " number of distinct prime factors for p^n-1 = " << factors_of_p_to_n_minus_1_.numDistinctFactors()
2734                << " which is not possible since (p-1) | (p^n - 1)" ;
2735             throw BigIntUnderflow( os.str(), __FILE__, __LINE__ ) ;
2736         }
2737
2738         // Divide out p-1, one prime factor at a time.
2739         for (int i = 0, j = 0 ;  i < factors_of_p_minus_1.numDistinctFactors() ;  ++i)
2740         {
2741             BigInt factor_of_p_m_1 = factors_of_p_minus_1.primeFactor( i ) ;
2742             BigInt factor_of_r     = factors_of_R_.primeFactor( j ) ;
2743             
2744             // Divide out the common prime factor.  Advance to next prime factor in the numerator.
2745             if (factor_of_p_m_1 == factor_of_r)
2746             {
2747                 factors_of_R_[ j ].count_ -= factors_of_p_minus_1[ i ].count_ ;
2748                 ++j ;
2749             }
2750             // Factor in denominator < factor in numerator.  Advance to next factor in denominator.
2751             else if (factor_of_p_m_1 > factor_of_r)
2752                 continue ;
2753             // Factor in denominator > factor in numerator.  Advance to next factor in numerator.  All smaller factors in numerator should have been divided out already.
2754             else
2755                 ++j ;
2756             
2757             #ifdef DEBUG_PP_FACTOR
2758             cout <<  " i = " << i << " prime factor of p-1 = " << factor_of_p_m_1  << "    j = " << j << " prime factor num = " << factor_of_r << endl ;
2759             #endif // DEBUG_PP_FACTOR
2760         }
2761
2762        #ifdef DEBUG_PP_FACTOR
2763         cout << "factorization of r = " << endl ;
2764         for (unsigned int i = 0 ;  i < factors_of_R_.numDistinctFactors() ;  ++i)
2765             cout << factors_of_R_.primeFactor( i ) << " ^ " << factors_of_R_.multiplicity( i ) << endl ;
2766        #endif // DEBUG_PP_FACTOR
2767     }
2768     
2769     return ;
2770}
2771
2772
2773/*=============================================================================
2774 |
2775 | NAME
2776 |
2777 |    computeMaxNumPoly
2778 |
2779 | DESCRIPTION
2780 |                                                                   n
2781 |    Maximum number of possible polynomials of degree n modulo p = p
2782 |
2783 | EXAMPLE
2784 |
2785 +============================================================================*/
2786
2787void PolyOrder::computeMaxNumPoly()
2788{
2789    max_num_poly_ = power( p_, n_ ) ;
2790
2791    return ;
2792}
2793
2794
2795/*=============================================================================
2796 |
2797 | NAME
2798 |
2799 |    computeNumPrimPoly
2800 |
2801 | DESCRIPTION
2802 |                                                 n
2803 |    Find number of primitive polynomials = Phi( p - 1 ) / n
2804 |
2805 | EXAMPLE
2806 |
2807 |    See the examples in the code below.
2808 |
2809 +============================================================================*/
2810
2811void PolyOrder::computeNumPrimPoly()
2812{
2813    //                                                     n
2814    // Compute the number of primitive polynomials = Phi( p - 1 ) / n
2815    //
2816    // Recall Euler's totient is
2817    //
2818    //              -----               -----             -----
2819    // Phi[ n ] = n  | | (1 - 1/p ) = n  | |  (p - 1)  /   | |  p
2820    //                           i              i                i
2821    //           p = all distinct
2822    //            i
2823    //           prime factors of n
2824    //
2825    // For example,
2826    //
2827    //   8                     5  2
2828    // 19 - 1 = 16983563040 = 2  3  5  17 181 3833
2829    //
2830    //        8           8
2831    // Phi( 19 - 1 ) = (19 - 1) (2-1) (3-1) (5-1) (17-1) (181-1) (3833-1) / (2 3 5 17 181 3833) = 4237885440
2832    //
2833    // You can check with Wolfram Alpha on the web,
2834    //
2835    //     http://www.wolframalpha.com/input/?i=eulerphi%28+19^8-1%29
2836    //                                                   8
2837    // then the number of primitive polynomials = Phi( 19 - 1) / 8 = 529735680
2838
2839    num_prim_poly_ = p_to_n_minus_1_ ;
2840    vector<BigInt> distinct_factors_of_p_to_n_minus_1_ = factors_of_p_to_n_minus_1_.getDistinctPrimeFactors() ;
2841      
2842    for (auto & f : distinct_factors_of_p_to_n_minus_1_)
2843        num_prim_poly_ *= (f - static_cast<BigInt>( 1u )) ;
2844
2845    for (auto & f : distinct_factors_of_p_to_n_minus_1_)
2846        num_prim_poly_ /=  f ;
2847
2848    num_prim_poly_ /= static_cast<BigInt>( n_ ) ;
2849      
2850    return ;
2851}
2852
2853
2854/*=============================================================================
2855 |
2856 | NAME
2857 |
2858 |     orderM
2859 |
2860 | DESCRIPTION
2861 |                  m
2862 |     Check that x  (mod f(x), p) is not an integer for m = r / p  but skip
2863 |                                                                i
2864 |                                            n
2865 |                                           p  - 1           th
2866 |     this test if p  | (p-1).  Recall r = -------, and p = i   prime in
2867 |                   i                       p - 1        i
2868 |
2869 |     the factorization of r.
2870 |
2871 |
2872 | EXAMPLE
2873 |                                            2
2874 |      Let n = 4 and p = 5.  Then r = 156 = 2 * 3 * 13, and p = 2, p = 3,
2875 |                                                            1      2
2876 |
2877 |      and p = 13.  m = { 156/2, 156/3, 156/13 } = { 78, 52, 12 }.  We can
2878 |           3
2879 |
2880 |      skip the test for m = 78 because p = 2 divides p-1 = 4.  Exponentiation
2881 |                                        1
2882 |
2883 |             52       3   2                                    12
2884 |      gives x    = 2 x + x + 4 x, which is not an integer and x   =
2885 |
2886 |         3       2
2887 |      4 x  +  2 x  +  4 x  + 3 which is not an integer either, so we return
2888 |
2889 |      true.
2890 |
2891 | METHOD
2892 |
2893 |     Exponentiate x with x_to_power and test the result with is_integer.
2894 |     Return right away if the result is not an integer.
2895 |
2896 +============================================================================*/
2897
2898bool PolyOrder::orderM()
2899{
2900    ppuint p{ f_.modulus() } ;
2901
2902    for (int i = 0 ;  i < factors_of_R_.numDistinctFactors() ;  ++i)
2903    {
2904        // Can we skip this order m test?
2905        if (!factors_of_R_.skipTest( p, i ))
2906        {
2907            BigInt m = r_ / factors_of_R_.primeFactor( i ) ;
2908
2909            Polynomial x1( "x" ) ;
2910            x1.setModulus( p ) ;
2911            PolyMod x( x1, f_ ) ;
2912
2913            PolyMod x_to_m = power( x, m ) ;
2914
2915            #ifdef DEBUG_PP_POLYNOMIAL
2916            cout << "Prime factor p[ " << i << " ] = " << factors_of_R_.primeFactor( i ) << endl ;
2917            cout << "m = " << m << endl ;
2918            cout << "x^m = " << x_to_m << endl ;
2919            #endif
2920
2921            // Early out.
2922            if (x_to_m.isInteger())
2923                return( false ) ;
2924        }
2925    }
2926
2927    return( true ) ;
2928
2929}
2930
2931
2932/*=============================================================================
2933 |
2934 | NAME
2935 |
2936 |     orderR
2937 |
2938 | DESCRIPTION
2939 |                                               n
2940 |              r                               p - 1
2941 |     Compute x  (mod f(x), p) = a, where r = -------
2942 |                                              p - 1
2943 |
2944 |     If a is not an integer, return 0, else return a itself.
2945 |
2946 | EXAMPLE
2947 |              4    2
2948 |      f(x) = x  + x  + 2 x + 3, n = 4 and p = 5.  Then r = 156 and
2949 |
2950 |       r    156
2951 |      x  = x    = 3 (mod f(x), 5) = 3, so we return 3.
2952 |
2953 |                      4
2954 |      But for f(x) = x  + x + 3, n = 4, p = 5,
2955 |
2956 |       r    156      3
2957 |      x  = x    = 3 x + 2 x + 1 (mod f(x), 5) so we return 0.
2958 |
2959 | METHOD
2960 |                           r
2961 |     First compute g(x) = x (mod f(x), p).
2962 |     Then test if g(x) is a constant polynomial.
2963 |
2964 +============================================================================*/
2965
2966ppuint PolyOrder::orderR()
2967{
2968    Polynomial x1( "x", p_ ) ;
2969    PolyMod x( x1, f_ ) ;
2970
2971    PolyMod x_to_r = power( x, r_ ) ;
2972
2973    #ifdef DEBUG_PP_POLYNOMIAL
2974    cout << "r = " << r_ << endl ;
2975    cout << "x^r = " << x_to_r << endl ;
2976    cout << "is integer = " << x_to_r.isInteger() << endl ;
2977    #endif
2978
2979    if (x_to_r.isInteger())
2980        //  Return the value a = constant term of g(x).
2981        return x_to_r[ 0 ] ;
2982    else
2983        return 0 ;
2984}
2985
2986
2987/*=============================================================================
2988 |
2989 | NAME
2990 |
2991 |     max_order
2992 |
2993 | DESCRIPTION
2994 |               k                                  n
2995 |     Check if x  = 1 (mod f(x), p) only when k = p  - 1 and not for any smaller
2996 |     power of k, i.e. that f(x) is a primitive polynomial.
2997 |
2998 | INPUT
2999 |
3000 |      f (int *)                Monic polynomial f(x).
3001 |      n      (int, n >= 1)     Degree of f(x).
3002 |      p      (int)             Modulo p coefficient arithmetic.
3003 |
3004 | RETURNS
3005 |
3006 |      true    if f( x ) is primitive.
3007 |      false   if it isn't.
3008 |
3009 | EXAMPLE
3010 |
3011 |                4
3012 |      f( x ) = x  + x  +  1 is a primitive polynomial modulo p = 2,
3013 |                                          4
3014 |      because it generates the group GF( 2  ) with the exception of
3015 |                             2         15
3016 |      zero.  The powers {x, x , ... , x  } modulo f(x), mod 2 are
3017 |                                         16       4
3018 |      distinct and not equal to 1 until x   (mod x  + x + 1, 2) = 1.
3019 |
3020 | METHOD
3021 |
3022 |     Confirm f(x) is primitive using the definition of primitive
3023 |     polynomial as a generator of the Galois group
3024 |          n                   n
3025 |     GF( p ) by testing that p - 1 is the smallest power for which
3026 |      k
3027 |     x = 1 (mod f(x), p).
3028 |
3029 +============================================================================*/
3030
3031bool PolyOrder::maximal_order()
3032{
3033    //  Highest possible order for x.
3034    BigInt maxOrder = power( f_.modulus(), f_.deg()) - static_cast<BigInt>( 1u ) ;
3035
3036    BigInt k { 1u } ;
3037    Polynomial x1( "x", f_.modulus() ) ;
3038    PolyMod x( x1, f_ ) ;    // g(x) = x (mod f(x), p)
3039
3040    while ( k <= maxOrder )
3041    {
3042        PolyMod x_to_power = power( x, k ) ; // x^k
3043
3044        if (x_to_power.isInteger() &&
3045            x_to_power[0] == 1u &&
3046            k < maxOrder)
3047        {
3048            return false ;
3049        }
3050
3051        ++k ;
3052
3053    }
3054
3055    return true ;
3056}
3057
3058
3059/*=============================================================================
3060 |
3061 | NAME
3062 |
3063 |     hasMultipleDistinctFactors
3064 |
3065 | DESCRIPTION
3066 |
3067 |    Returns true if the monic polynomial f( x ) has two or more distinct
3068 |    irreducible factors, false otherwise.
3069 |
3070 |    If earlyOut is false, compute the exact nullity in findNullity() instead
3071 |    of stopping when the nullity is >= 2.
3072 |
3073 | EXAMPLE
3074 |
3075 |    Let n = 4, p = 5
3076 |
3077 |              4    2
3078 |    f( x ) = x  + x  + 2 x + 3 is irreducible, so has one distinct factor.
3079 |
3080 |              4    3   2                  4
3081 |    f( x ) = x + 4x + x + 4x + 1 = (x + 1)  has one distinct factor.
3082 |
3083 |              3         2
3084 |    f( x ) = x  + 3 = (x + 3x + 4)(x + 2) has two distinct irreducible factors.
3085 |
3086 |              4    3    2                          2
3087 |    f( x ) = x + 3x + 3x + 3x + 2 = (x + 1) (x + 2) (x + 3) has 3 distinct
3088 |    irreducible factors.
3089 |
3090 |            4
3091 |    f(x) = x + 4 = (x+1)(x+2)(x+3)(x+4) has 4 distinct irreducible factors.
3092 |
3093 | METHOD
3094 |
3095 |       Berlekamp's method for factoring polynomials over GF( p ), modified to test
3096 |       for irreducibility only.
3097 |
3098 |       See my notes;  I skip the polynomial GCD step which ensures polynomials
3099 |       are square-free due to time constraints, but this requires a proof that
3100 |       the method is still valid.
3101 |
3102 +============================================================================*/
3103
3104bool PolyOrder::hasMultipleDistinctFactors( bool earlyOut )
3105
3106{
3107    // Generate the Q-I matrix.
3108    generateQMatrix() ;
3109
3110    // Find nullity of Q-I
3111    findNullity( earlyOut ) ;
3112
3113    // If nullity_ >= 2, f( x ) is a reducible polynomial modulo p since it has
3114    // two or more distinct irreducible factors.
3115    //                                     e
3116    // Nullity of 1 implies f( x ) = p( x )  for some power e >= 1 so we cannot
3117    // determine reducibility.
3118    if (nullity_ >= 2)
3119        return true ;
3120
3121    return false ;
3122
3123}
3124
3125
3126/*=============================================================================
3127 |
3128 | NAME
3129 |
3130 |     isPrimitive
3131 |
3132 | DESCRIPTION
3133 |
3134 |     Check if a given polynomial f(x) of degree n modulo p is primitive.
3135 |
3136 +============================================================================*/
3137
3138bool PolyOrder::isPrimitive()
3139{
3140    bool isPrimitive = false ;
3141    ++statistics_.num_poly_tested ;
3142
3143    try
3144    {
3145        BigInt max_num_possible_poly = power( p_, n_ ) ;
3146        ArithModP modp( p_ ) ;
3147
3148        // Constant coefficient of f(x) * (-1)^n must be a primitive root of p.
3149        if (modp.constCoeffIsPrimitiveRoot( f_[0], f_.deg() ))
3150        {
3151            ++statistics_.num_where_const_coeff_is_primitive_root ;
3152
3153            #ifdef DEBUG_PP_PRIMITIVITY
3154            cout << "    (-1)^n const coeff " << f_[ 0 ] << " is primitive root of " << p_ << endl ;
3155            #endif
3156
3157            // f(x) can't have any linear factors.
3158            if (!f_.hasLinearFactor())
3159            {
3160                ++statistics_.num_free_of_linear_factors ;
3161
3162                #ifdef DEBUG_PP_PRIMITIVITY
3163                cout << "    No linear factors" << endl ;
3164                #endif
3165
3166                // Do more in-depth checking.
3167
3168                // f(x) can't have two or more distinct irreducible factors.
3169                if (!hasMultipleDistinctFactors())
3170                {
3171                    ++statistics_.num_irreducible_to_power ;
3172
3173                    #ifdef DEBUG_PP_PRIMITIVITY
3174                    cout << "    One distinct irreducible factor (possibly repeated)" << endl ;
3175                    #endif
3176
3177                    //  r
3178                    // x  (mod f(x), p) = a_ must be an integer.
3179                    ppuint a{ orderR() } ;
3180                    if (a != 0)
3181                    {
3182                        ++statistics_.num_order_r ;
3183
3184                        #ifdef DEBUG_PP_PRIMITIVITY
3185                        cout << "    x^r = a (integer)" << endl ;
3186                        #endif
3187
3188                         //              n
3189                         // Check if (-1)  (constant coefficient of f(x)) = a_ (mod p)
3190                         //
3191                         if (modp.constCoeffTest( f_[ 0 ], a, f_.deg() ))
3192                         {
3193                             ++statistics_.num_passing_const_coeff_test ;
3194
3195                             #ifdef DEBUG_PP_PRIMITIVITY
3196                             cout << "    a (integer) = (-1)^n f[0]" << endl ;
3197                             #endif
3198
3199                             //  x^m != integer for all m = r / q, q a prime divisor of r.
3200                             if (orderM())
3201                             {
3202                                 ++statistics_.num_order_m ;
3203
3204                                 #ifdef DEBUG_PP_PRIMITIVITY
3205                                 cout << "    x^m != integer for m = r / prime divisor of r" << endl ;
3206                                 #endif
3207
3208                                 isPrimitive = true ;
3209                                 goto Exit ;
3210
3211                             } // end order m
3212                         } // end const coeff test
3213                    } // end order r
3214                } // end can't determine if reducible
3215            } // end no linear factors
3216        } // end constant coefficient primitive.
3217    }
3218    catch( ArithModPError & e )
3219    {
3220        ostringstream os ;
3221        os << "PolyOrder::isPrimitive had a mod p arithmetic error [ " << e.what() << " ] " ;
3222        throw PolynomialRangeError( os.str(), __FILE__, __LINE__ ) ;
3223    }
3224
3225    Exit:
3226        return isPrimitive ;
3227}
3228
3229
3230/*=============================================================================
3231 |
3232 | NAME
3233 |     generateQMatrix
3234 |
3235 | DESCRIPTION
3236 |
3237 |     Generate n x n matrix Q - I, where rows of Q are the powers,
3238 |
3239 |         p                2p                      (n-1) p
3240 |     1, x  (mod f(x),p), x  (mod f(x), p), ... , x       (mod f(x), p)
3241 |
3242 |     for monic polynomial f(x).
3243 |
3244 | EXAMPLE
3245 |
3246 |             4   2
3247 |     f(x) = x + x + 2 x + 3, n = 4, p = 5
3248 |
3249 |         (    1     )    (    1              )       ( 1    0    0    0 )
3250 |         (          )    (                   )       (                  )
3251 |         (     5    )    (         2       3 )       (                  )
3252 |     Q = (    x     )    (  2x + 3x   + 4 x  )       ( 0    2    3    4 )
3253 |         (          )    (                   )       (                  )
3254 |         (          )    (         2     3   )       (                  )
3255 |         (     10   ) =  (  3 + 4 x   + x    )   =   (                  )
3256 |         (    x     )    (                   )       ( 3    0    4    1 )
3257 |         (          )    (                   )       (                  )
3258 |         (     15   )    (          2    3   )       (                  )
3259 |         (    x     )    (   4x + 4x + 3x    )       ( 0    4    4    3 )
3260 |         (          )    (                   )       (                  )
3261 |
3262 |                                                                 2    3
3263 |                                                       1   x    x    x
3264 |              ( 0 0 0 0 )
3265 |              ( 0 1 3 4 )
3266 |     Q - I =  ( 3 0 3 1 )
3267 |              ( 0 4 4 2 )
3268 |
3269 |
3270 |     The left nullspace has dimension = 1 with basis { (1 0 0 0) }.
3271 |
3272 +============================================================================*/
3273
3274void PolyOrder::generateQMatrix()
3275{
3276    // Check for invalid inputs.
3277    if (n_ < 2 || p_ < 2)
3278        throw PolynomialRangeError( "generateQMatrix has n < 2 or p < 2", __FILE__, __LINE__ ) ;
3279
3280    // Row 0 of Q = (1 0 ... 0).
3281    Q_[ 0 ][ 0 ] = 1 ;
3282    for (int i = 1 ;  i < n_ ;  ++i)
3283        Q_[ 0 ][ i ] = 0 ;
3284
3285    //             p
3286    // Let q(x) = x (mod f(x),p)
3287    // and Q[ 1 ] = coefficients of q(x).
3288    Polynomial x1( "x", p_ ) ;
3289    PolyMod x( x1, f_ ) ;
3290    PolyMod xp = power( x, static_cast< BigInt >( p_ ) ) ;
3291
3292    #ifdef DEBUG_PP_POLYNOMIAL
3293        cout << "x ^ p (mod f(x),p) = " << xp << endl ;
3294    cout << "initial Q matrix " << printQMatrix() ;
3295    #endif
3296
3297    PolyMod q = xp ;
3298
3299    for (int i = 0 ;  i < n_ ;  ++i)
3300        Q_[ 1 ][ i ] = xp[ i ] ;
3301
3302    //               pk
3303    // Row k of Q = x   (mod f(x), p) 2 <= k <= n-1, computed by
3304    //                                   p
3305    // multiplying each previous row by x (mod f(x),p).
3306    for (int k = 2 ;  k <= n_- 1 ;  ++k)
3307    {
3308        q *= xp ;
3309
3310        #ifdef DEBUG_PP_POLYNOMIAL
3311        cout << "x ^ pk (mod f(x),p) = " << q << " for k = " << k << endl ;
3312        #endif
3313
3314        for (int i = 0 ;  i < n_ ;  ++i)
3315             Q_[ k ][ i ] = q[ i ] ;
3316    }
3317
3318    #ifdef DEBUG_PP_POLYNOMIAL
3319    cout << "computed Q matrix " << printQMatrix() ;
3320    #endif
3321
3322    //  Subtract Q - I
3323    for (int row = 0 ;  row < n_ ;  ++row)
3324    {
3325        Q_[ row ][ row ] = mod( Q_[ row ][ row ] - 1 ) ;
3326    }
3327
3328    #ifdef DEBUG_PP_POLYNOMIAL
3329    cout << "computed Q-I matrix " << printQMatrix() ;
3330    #endif
3331
3332    return ;
3333}
3334
3335
3336/*=============================================================================
3337 |
3338 | NAME
3339 |
3340 |    printQMatrix
3341 |
3342 | DESCRIPTION
3343 |
3344 |    Print the matrix to the console.
3345 |
3346 +============================================================================*/
3347 
3348string PolyOrder::printQMatrix() const
3349{
3350    // Print the matrix as a string.
3351    ostringstream os ;
3352
3353    os << endl ;
3354    for (int row = 0 ;  row < n_ ;  ++row)
3355    {
3356        os << "( " ;
3357        for (int col = 0 ;  col < n_ ;  ++col)
3358        {
3359            os << setw( 4 ) << setfill( ' ' ) << Q_[ row ][ col ] ;
3360        }
3361        os << " )" << endl ;
3362    }
3363
3364    return os.str() ;
3365}
3366
3367
3368/*=============================================================================
3369 |
3370 | NAME
3371 |
3372 |    findNullity
3373 |
3374 | DESCRIPTION
3375 |
3376 |     Computes the nullity of Q.  
3377 |     If earlyOut is true, stop when the nullity is >= 2 and return 2.
3378 |
3379 | EXAMPLE
3380 |
3381 |     Let p = 5 and n = 3.  We use the facts that -1 = 4 (mod 5), 1/2 = 3, -1/2 = 2,
3382 |     1/3 = 2, -1/3 = 3, 1/4 = 4, -1/4 = 1.
3383 |
3384 |     Consider the matrix
3385 |
3386 |         ( 2 3 4 )
3387 |     Q = ( 0 2 1 )
3388 |         ( 3 3 3 )
3389 |
3390 |     Begin with row 1.  No pivotal columns have been selected yet.  Scan row 1 and
3391 |     pick column 1 as the pivotal column because it contains a nonzero element.
3392 |
3393 |     Normalizing column 1 by multiplying by -1/pivot = -1/2 = 2 gives
3394 |
3395 |         ( 4 3 4 )
3396 |         ( 0 2 1 )
3397 |         ( 1 3 3 )
3398 |
3399 |      Now perform column reduction on column 2 by multiplying the pivotal column 1
3400 |      by 3 (the column 2 element in the current row) and adding back to row 2.
3401 |
3402 |         ( 4 0 4 )
3403 |         ( 0 2 1 )
3404 |         ( 1 1 3 )
3405 |
3406 |      Column reduce column 3 by multiplying the pivotal column by 4 and adding back to row 3,
3407 |
3408 |         ( 4 0 0 )
3409 |         ( 0 2 1 )
3410 |         ( 1 1 2 )
3411 |
3412 |      For row 2, pick column 2 as the pivotal column, normalize it and reduce columns 1, then 3,
3413 |
3414 |         ( 4 0 0 )    ( 4 0 0 )    ( 4 0 0 )    ( 4 0 0 )
3415 |         ( 0 2 1 ) => ( 0 4 1 ) => ( 0 4 1 ) => ( 0 4 0 )
3416 |         ( 1 1 2 )    ( 1 2 2 )    ( 1 2 2 )    ( 1 2 4 )
3417 |                  norm.         c.r. 1      c.r. 3
3418 |
3419 |      For row 3, we must pick column 3 as pivotal column because we've used up columns 1 and 2,
3420 |
3421 |         ( 4 0 0 )    ( 4 0 0 )    ( 4 0 0 )    ( 4 0 0 )
3422 |         ( 0 4 0 ) => ( 0 4 0 ) => ( 0 4 0 ) => ( 0 4 0 )
3423 |         ( 1 2 4 )    ( 1 2 4 )    ( 1 2 4 )    ( 0 0 4 )
3424 |                  norm.        c.r. 1        c.r. 2
3425 |
3426 |      The nullity is zero, since we were always able to find a pivot in each row.
3427 |
3428 | METHOD
3429 |
3430 |       Modified from ART OF COMPUTER PROGRAMMING, Vol. 2, 2nd ed., Donald E. Knuth, Addison-Wesley.
3431 |
3432 |       We combine operations of normalization of columns,
3433 |
3434 |       (       c       )                         (        C       )
3435 |       (       c       )                         (        C       )
3436 |       (       .       )                         (        C       )
3437 |       ( . . . q . . . ) row  ================>  ( . . . -1 . . . ) row
3438 |       (       c       )                         (        C       )
3439 |       (       c       )      column times       (        C       )
3440 |       (       c       )      -1/a modulo p      (        C       )
3441 |            pivotCol                                   pivotCol
3442 |
3443 |       and column reduction,
3444 |
3445 |       (        C      b       )                         (       C        B       )
3446 |       (        C      b       )                         (       C        B       )
3447 |       (        C      b       )                         (       C        B       )
3448 |       ( . . . -1 . . .e . . . ) row  ================>  ( . . . -1 . . . 0 . . . )
3449 |       (        C      b       )                         (       C        B       )
3450 |       (        C      b       )    pivotCol times       (       C        B       )
3451 |       (        C      b       )    e added back to col  (       C        B       )
3452 |            pivotCol  col                                       col
3453 |
3454 |       to reduce the matrix to a form in which columns having pivots are zero until
3455 |       the pivotal row.
3456 |
3457 |       The column operations don't change the left nullspace of the
3458 |       matrix.
3459 |
3460 |       The matrix rank is the number of pivotal rows since they are linearly
3461 |       independent.  The nullity is then the number of non-pivotal rows.
3462 |
3463 +============================================================================*/
3464
3465void PolyOrder::findNullity( bool earlyOut )
3466{
3467    try
3468    {
3469        InverseModP invmod( p_ ) ;
3470
3471        #ifdef DEBUG_PP_POLYNOMIAL
3472        cout << "Q-I matrix " << printQMatrix() ;
3473        #endif
3474
3475        vector<bool>pivotInCol( n_, false ) ; // Is false if the column has no pivotal element.
3476
3477        nullity_ = 0 ;
3478        int pivotCol = -1 ; // No pivots yet.
3479
3480        // Sweep through each row.
3481        for (int row = 0 ;  row < n_ ;  ++row)
3482        {
3483            // Search for a pivot in this row:  a non-zero element
3484            // in a column which had no previous pivot.
3485            bool found = false ;
3486            for (int col = 0 ;  col < n_ ;  ++col)
3487            {
3488                if (Q_[ row ][ col ] > 0 && !pivotInCol[ col ])
3489                {
3490                    found = true ;
3491                    pivotCol = col ;
3492                    break ;
3493                }
3494            }
3495
3496            // No pivot;  increase nullity by 1.
3497            if (found == false)
3498            {
3499                ++nullity_ ;
3500
3501                // Early out.
3502                if (earlyOut && nullity_ >= 2)
3503                    goto EarlyOut ;
3504            }
3505            // Found a pivot, q.
3506            else
3507            {
3508                ppsint q = Q_[ row ][ pivotCol ] ;
3509
3510                // Compute -1/q (mod p)
3511                ppsint t = mod( -invmod( q )) ;
3512
3513                // Normalize the pivotal column.
3514                for (int r = 0 ;  r < n_ ;  ++r)
3515                {
3516                    Q_[ r ][ pivotCol ] = mod( t * Q_[ r ][ pivotCol ]) ;
3517                }
3518
3519                // Do column reduction:  Add C times the pivotal column to the other
3520                // columns where C = element in the other column at current row.
3521                for (int col = 0 ;  col < n_ ;  ++col)
3522                {
3523                    if (col != pivotCol)
3524                    {
3525                        q = Q_[ row ][ col ] ;
3526
3527                        for (int r = 0 ;  r < n_ ;  ++r)
3528                        {
3529                            t = mod( q * Q_[ r ][ pivotCol ]) ;
3530                            Q_[ r ][ col ] = mod( t + Q_[ r ][ col ] ) ;
3531                        }
3532                    }
3533                }
3534
3535                // Record the presence of a pivot in this column.
3536                pivotInCol[ pivotCol ] = true ;
3537
3538                #ifdef DEBUG_PP_POLYNOMIAL
3539                cout << "row = " << row << " pivot = " << q << " (-1/q) = " << t << " nullity = " << nullity_
3540                     << " Row reduced Q-I matrix " << printQMatrix() ;
3541                #endif
3542
3543            } // found a pivot
3544
3545        } // end for row
3546
3547        EarlyOut: ;
3548            #ifdef DEBUG_PP_POLYNOMIAL
3549            cout << "Row reduced Q-I matrix " << printQMatrix() ;
3550            #endif
3551    }
3552    catch( ArithModPError & e )
3553    {
3554        ostringstream os ;
3555        os << "PolyOrder::findNullity failed in matrix row reduction [ " << e.what() << " ] " ;
3556        throw PrimpolyError( os.str(), __FILE__, __LINE__ ) ;
3557    }
3558
3559    // Automagically free pivotInCol and mod objects.
3560
3561} // ===================== end of function findNullity =====================