CGAL 6.1 - Modular Arithmetic

Loading...
Searching...
No Matches
User Manual

Authors
  Michael Hemmer and Sylvain Pion

# Introduction

Modular arithmetic is a fundamental tool in modern algebra systems. In conjunction with the Chinese remainder theorem it serves as the workhorse in several algorithms computing the gcd, resultant etc. Moreover, it can serve as a very efficient filter, since it is often possible to exclude that some value is zero by computing its modular correspondent with respect to one prime only.

# Residue and Modularizable

First of all, this package introduces a type `Residue`. It represents \( \mathbb{Z}{/p\mathbb{Z}}\) for some prime \( p\). The prime number \( p\) is stored in a static member variable. The class provides static member functions to change this value.

Changing the prime invalidates already existing objects of this type. However, already existing objects do not lose their value with respect to the old prime and can be reused after restoring the old prime. Since the type is based on double arithmetic the prime is restricted to values less than \( 2^{26}\). The initial value of \( p\) is 67108859.

Moreover, the package introduces the concept `Modularizable`. An algebraic structure `T` is considered as `Modularizable` if there is a mapping from `T` into an algebraic structure that is based on the type `Residue`. For scalar types, e.g. Integers, this mapping is just the canonical homomorphism into \( \mathbb{Z}{/p\mathbb{Z}}\) represented by `Residue`. For compound types, e.g. Polynomials, the mapping is applied to the coefficients of the compound type. The mapping is provided by the class `Modular_traits<T>`. The class `Modular_traits<T>` is designed such that the concept `Modularizable` can be considered as optional, i.e., `Modular_traits<T>` provides a tag that can be used for dispatching.

## Example

In the following example modular arithmetic is used as a filter on order to avoid unnecessary gcd computations of polynomials. A `gcd` computation can be very costly due to coefficient growth within the Euclidean algorithm.

The general idea is that firstly the gcd is computed with respect to one prime only. If this modular gcd is constant we can (in most cases) conclude that the actual gcd is constant as well.

For this purpose the example introduces the function `may_have_common_factor()`. Note that there are two versions of this function, namely for the case that the coefficient type is `Modularizable` and that it is not. If the type is not `Modularizable` the filter is just not applied and the function returns true.

Further note that the implementation of class `Residue` requires a mantissa precision according to the IEEE Standard for Floating-Point Arithmetic (IEEE 754). However, on some processors the traditional FPU uses an extended precision. Hence, it is indispensable that the proper mantissa length is enforced before performing any arithmetic operations. Moreover, it is required that numbers are rounded to the next nearest value. This can be ensured using `Protect_FPU_rounding` with `CGAL_FE_TONEAREST`, which also enforces the required precision as a side effect.

File Modular_arithmetic/modular_filter.cpp

#include <CGAL/config.h>
#include <iostream>
#ifdef CGAL_USE_GMP
#include <CGAL/Gmpz.h>
#include <CGAL/Polynomial.h>
// Function in case Polynomial is Modularizable
template< typename Polynomial >
bool may_have_common_factor(
const Polynomial& p1, const Polynomial& p2, CGAL::Tag_true){
std::cout<< "The type is modularizable" << std::endl;
// Enforce IEEE double precision and rounding mode to nearest
// before using modular arithmetic
CGAL::Protect_FPU_rounding<true> pfr(CGAL_FE_TONEAREST);
// Use Modular_traits to convert to polynomials with modular coefficients
typedef CGAL::Modular_traits<Polynomial> MT;
typedef typename MT::Residue_type MPolynomial;
typedef typename MT::Modular_image Modular_image;
MPolynomial mp1 = Modular_image()(p1);
MPolynomial mp2 = Modular_image()(p2);
// check for unlucky primes, the polynomials should not lose a degree
typename CGAL::Polynomial_traits_d<Polynomial>::Degree degree;
typename CGAL::Polynomial_traits_d<MPolynomial>::Degree mdegree;
if ( degree(p1) != mdegree(mp1)) return true;
if ( degree(p2) != mdegree(mp2)) return true;
// compute gcd for modular images
MPolynomial mg = CGAL::gcd(mp1,mp2);
// if the modular gcd is not trivial: return true
if ( mdegree(mg) > 0 ){
std::cout << "The gcd may be non trivial" << std::endl;
return true;
}else{
std::cout << "The gcd is trivial" << std::endl;
return false;
}
}
// This function returns true, since the filter is not applicable
template< typename Polynomial >
bool may_have_common_factor(
const Polynomial&, const Polynomial&, CGAL::Tag_false){
std::cout<< "The type is not modularizable" << std::endl;
return true;
}
template< typename Polynomial >
Polynomial modular_filtered_gcd(const Polynomial& p1, const Polynomial& p2){
typedef CGAL::Modular_traits<Polynomial> MT;
typedef typename MT::Is_modularizable Is_modularizable;
// Try to avoid actual gcd computation
if (may_have_common_factor(p1,p2, Is_modularizable())){
// Compute gcd, since the filter indicates a common factor
return CGAL::gcd(p1,p2);
}else{
typename CGAL::Polynomial_traits_d<Polynomial>::Univariate_content content;
typename CGAL::Polynomial_traits_d<Polynomial>::Construct_polynomial construct;
return construct(CGAL::gcd(content(p1),content(p2))); // return trivial gcd
}
}
int main(){
CGAL::IO::set_pretty_mode(std::cout);
typedef CGAL::Gmpz NT;
typedef CGAL::Polynomial<NT> Poly;
CGAL::Polynomial_traits_d<Poly>::Construct_polynomial construct;
Poly f1=construct(NT(2), NT(6), NT(4));
Poly f2=construct(NT(12), NT(4), NT(8));
Poly f3=construct(NT(3), NT(4));
std::cout << "f1 : " << f1 << std::endl;
std::cout << "f2 : " << f2 << std::endl;
std::cout << "compute modular filtered gcd(f1,f2): " << std::endl;
Poly g1 = modular_filtered_gcd(f1,f2);
std::cout << "gcd(f1,f2): " << g1 << std::endl;
std::cout << std::endl;
Poly p1 = f1*f3;
Poly p2 = f2*f3;
std::cout << "f3 : " << f3 << std::endl;
std::cout << "p1=f1*f3 : " << p1 << std::endl;
std::cout << "p2=f2*f3 : " << p2 << std::endl;
std::cout << "compute modular filtered gcd(p1,p2): " << std::endl;
Poly g2 = modular_filtered_gcd(p1,p2);
std::cout << "gcd(p1,p2): " << g2 << std::endl;
}
#else
int main (){
std::cout << " This example needs GMP! " << std::endl;
}
#endif
Gmpz.h
CGAL::Gmpz
CGAL::Modular_traits
An instance of Modular_traits is a model of ModularTraits, where T is the associated type.
Definition: Modular_traits.h:14
CGAL::Polynomial_traits_d
CGAL::Polynomial
CGAL::gcd
result_type gcd(const NT1 &x, const NT2 &y)
CGAL::degree
Polynomial_traits_d< Polynomial_d >::Degree::result_type degree(const Polynomial_d &p, int i, index=Polynomial_traits_d< Polynomial_d >::d-1)
CGAL::IO::set_pretty_mode
Mode set_pretty_mode(std::ios &s)
CGAL::Boolean_tag
CGAL::Protect_FPU_rounding

# Design and Implementation History

The class `Residue` is based on the C-code of Sylvain Pion et. al. as it was presented in [2].

The remaining part of the package is the result of the integration process of the NumeriX library of Exacus [1] into CGAL.