00001 
00007 #include <string>
00008 #include <iostream>
00009 #include <stdexcept>
00010 #include <cmath>
00011 #include <gmp.h>
00012 #include "utils.H"
00013 
00014 #include "qsieve.H"
00015 
00016 using namespace std;
00017 
00018 extern mpz_t n;
00019 
00020 
00021 ostream& operator<< (ostream& ostr, const CRelation& GL)
00022 {
00023   if (GL.Relation_dense) ostr << "dense"; else ostr << "sparse";
00024   ostr << "1=";
00025   
00026   char *str;
00027   str = new char [mpz_sizeinbase(GL.Delta,10)+2]; mpz_get_str(str, 10, GL.Delta);
00028   ostr << str << "^2";
00029   delete [] str; 
00030   
00031   if (GL.Relation_dense)
00032     { 
00033       for (int i=GL.Relation_dense->first(); i>=0; i=GL.Relation_dense->next(i))
00034         ostr << "*p" << i;
00035     }
00036   else
00037     {
00038       const CTinyFBsizetypeVector& R = *GL.Relation_sparse;
00039       for (int pos=0; pos<R.size(); ++pos)
00040         ostr << "*p" << R[pos];
00041     }
00042   
00043   return ostr;
00044 }
00045 
00046 
00047 void CRelation::convert_Relation_to_dense()
00048 {
00049   
00050   if (Relation_dense)
00051     { cerr << "Relation is already dense!" << endl; return; }
00052   Relation_dense = new myBitString;
00053   for (int pos=Relation_sparse->size()-1; pos>=0; --pos)
00054     Relation_dense->set((*Relation_sparse)[pos]);
00055   
00056   delete Relation_sparse; Relation_sparse=NULL; 
00057 }
00058 
00059 void CRelation::convert_Relation_to_sparse()
00060 {
00061   
00062   if (Relation_sparse)
00063     { cerr << "Relation is already sparse!" << endl; return; }
00064   Relation_sparse = new CTinyFBsizetypeVector(Relation_dense->count(1));
00065   for (int i=Relation_dense->first(); i>=0; i=Relation_dense->next(i))
00066     Relation_sparse->fast_append(i);
00067   
00068   delete Relation_dense; Relation_dense=NULL; 
00069 }
00070 
00071 
00072 void CRelation::combine(const CRelation& GL2)
00073   
00074   
00075 {
00076   
00077   
00078   
00079   
00080   
00081 
00082 
00083 
00084   mpz_set_ui(x,1); 
00085 
00086   if ( GL2.Relation_dense ) 
00087     {
00088       if (!Relation_dense) 
00089         convert_Relation_to_dense(); 
00090       
00091       
00092       
00093       
00094       for (int i=GL2.Relation_dense->last(); i>0; i=GL2.Relation_dense->prev(i))
00095         if (Relation_dense->test(i))
00096          {
00097            mpz_mul_ui(x,x,PrimeNumbers[i]);
00098            if (mpz_cmp(x,n)>=0)
00099             {
00100               mpz_mod(x,x,n); mpz_mul(Delta,Delta,x);
00101               mpz_mod(Delta,Delta,n); mpz_set_ui(x,1);
00102             }
00103          }
00104       
00105       (*Relation_dense)._xor(*GL2.Relation_dense); 
00106     }
00107   else 
00108     if (Relation_dense) 
00109       { 
00110         
00111         
00112         for (int pos=0; pos<GL2.Relation_sparse->size(); ++pos)
00113           {
00114             if (Relation_dense->test((*GL2.Relation_sparse)[pos]))
00115               if ((*GL2.Relation_sparse)[pos]>0)
00116                 {
00117                   mpz_mul_ui(x,x,PrimeNumbers[(*GL2.Relation_sparse)[pos]]);
00118                   if (mpz_cmp(x,n)>=0)
00119                     {
00120                       mpz_mod(x,x,n); mpz_mul(Delta,Delta,x);
00121                       mpz_mod(Delta,Delta,n); mpz_set_ui(x,1);
00122                     }
00123                 } 
00124             Relation_dense->invert((*GL2.Relation_sparse)[pos]);
00125           }
00126       }
00127     else
00128       {
00129         
00130         int p1 = 0;
00131         int p2 = 0;
00132         const CTinyFBsizetypeVector& R = *Relation_sparse;
00133         const CTinyFBsizetypeVector& R2 = *GL2.Relation_sparse;
00134         CTinyFBsizetypeVector neueMenge(R.size()+R2.size());
00135         
00136         while (p2<R2.size() && p1<R.size())
00137           {
00138             while (R2[p2]<R[p1])
00139               {
00140                 neueMenge.fast_append(R2[p2]); ++p2;
00141                 if (p2==R2.size()) goto done;
00142               }
00143             while (R[p1]<R2[p2])
00144               {
00145                 neueMenge.fast_append(R[p1]); ++p1;
00146                 if (p1==R.size()) goto done;
00147               }
00148             while (R2[p2]==R[p1])
00149               {
00150                 if (R[p1]>0)
00151                   {
00152                     mpz_mul_ui(x,x,PrimeNumbers[R[p1]]);
00153                     if (mpz_cmp(x,n)>=0)
00154                       {
00155                         mpz_mod(x,x,n); mpz_mul(Delta,Delta,x);
00156                         mpz_mod(Delta,Delta,n); mpz_set_ui(x,1);
00157                       }
00158                   } 
00159                 ++p1; ++p2;
00160                 if (p2==R2.size()) goto done;
00161                 if (p1==R.size()) goto done;      
00162               }
00163           }
00164       done:
00165         while (p2!=R2.size()) { neueMenge.fast_append(R2[p2]); ++p2; }
00166         while (p1!=R.size()) { neueMenge.fast_append(R[p1]); ++p1; }
00167         Relation_sparse->copy_from(neueMenge); 
00168         
00169       }
00170   
00171   
00172   mpz_mul(Delta,Delta,x); mpz_mod(Delta,Delta,n);
00173   
00174   
00175   
00176   mpz_mul(Delta,Delta,GL2.Delta); mpz_mod(Delta,Delta,n);
00177   
00178   relevant_factor=largest_factor_in_Relation();
00179   
00180 }
00181 
00182 
00183 
00184 
00185 
00186 
00187 
00188 void CRelation::multi_combine_init()
00189 {
00190   
00191   
00192   
00193   
00194   if (pMulticombineData==NULL)
00195    {
00196      MARK; cerr << "Error: pMulticombineData invalid!" << endl;
00197      exit(1);
00198    }
00199   if (!pMulticombineData->multi_combine_Counter) return;
00200   pMulticombineData->multi_combine_Counter=0;
00201   for (int i=0; i<StaticFactorbaseSettings::Size(); i++) pMulticombineData->ExponentArray[i]=0;
00202 }
00203 
00204 void CRelation::multi_combine_exit()
00205 {
00206   if (!pMulticombineData->multi_combine_Counter) return; 
00207 #ifdef VERBOSE
00208   cout << "Multicombine_exit after " << pMulticombineData->multi_combine_Counter
00209        << " calls." << endl;
00210 #endif
00211 
00212   if (pMulticombineData->multi_combine_Counter>std::numeric_limits<TExponentArrayElement>::max())
00213    {
00214      MARK;
00215      cerr << "Elements of CRelation::ExponentArray may have exceeded limits," << endl
00216           << "you have to increase the type of CRelation::TExponentArrayElement" << endl
00217           << "and recompile the program to assure correct computation." << endl
00218           << "(Data computed so far are still correct and can be reused afterwards.)" << endl;
00219      exit(8);
00220    }
00221 
00222   optisize(); 
00223 
00224   
00225   mpz_set_ui(y,1); 
00226   pMulticombineData->ExponentArray[0]=0; 
00227 
00228   
00229 
00230 
00231 
00232 
00233 
00234 
00235 
00236 #define MY_BASIS_WORKAROUND
00237 #if !defined(__GNUC__) || !defined(MY_BASIS_WORKAROUND)
00238   
00239   
00240   
00241   mpz_t* const Basis = new mpz_t[StaticFactorbaseSettings::Size()];
00242   bool* const Flag = new bool[StaticFactorbaseSettings::Size()];
00243 #else
00244   #warning "enabling workaround for possible Basis assertion failure"
00245   
00246   
00247   __extension__ mpz_t Basis[StaticFactorbaseSettings::Size()];
00248   __extension__ bool Flag[StaticFactorbaseSettings::Size()];
00249 #endif  
00250   for (int i=0; i<StaticFactorbaseSettings::Size(); ++i) Flag[i]=false; 
00251 
00252   for (int i=1; i<StaticFactorbaseSettings::Size(); ++i)
00253    if (pMulticombineData->ExponentArray[i])
00254     {
00255       const unsigned long int e=pMulticombineData->ExponentArray[i];
00256       pMulticombineData->ExponentArray[i]=0; 
00257 
00258       if (e>=StaticFactorbaseSettings::Size())
00259        {
00260          cout << "Unexpected exponent " << e << "! Use normal exponentiation..." << endl;
00261          mpz_set_ui(y,PrimeNumbers[i]); mpz_powm_ui(y,y,e,n);
00262          mpz_mul(Delta,Delta,y);
00263          continue;
00264        }
00265 
00266       if (Flag[e])
00267        {
00268          
00269          mpz_mul_ui(Basis[e],Basis[e],PrimeNumbers[i]);
00270          if (mpz_cmp(Basis[e],n)>=0)
00271           { 
00272             
00273             mpz_mod(Basis[e],Basis[e],n);
00274           }
00275        }
00276       else 
00277        { 
00278          
00279          Flag[e]=true;
00280          mpz_init_set_ui(Basis[e],PrimeNumbers[i]);
00281        }
00282     }
00283 
00284  
00285 
00286 
00287 
00288      
00289 
00290   unsigned int e2 = StaticFactorbaseSettings::Size()-1;
00291   Flag[0]=true; 
00292   while (!Flag[e2]) --e2; 
00293 
00294   mpz_set_ui(y,1);
00295   if (e2>1)
00296    for (unsigned int e=e2-1; e>0; --e)
00297     if (Flag[e])
00298      {
00299        mpz_mul(Basis[e],Basis[e],Basis[e2]);
00300        if (mpz_cmp(Basis[e],n)>=0)
00301         {
00302           
00303           mpz_mod(Basis[e],Basis[e],n);
00304         }
00305        if (e2-e>1) mpz_powm_ui(Basis[e2],Basis[e2],e2-e,n);
00306        mpz_mul(y,y,Basis[e2]);
00307        if (mpz_cmp(y,n)>=0)
00308         {
00309           
00310           mpz_mod(y,y,n);
00311         }
00312        mpz_clear(Basis[e2]); 
00313        e2=e;
00314      }
00315 
00316   if (e2>1) mpz_powm_ui(Basis[e2],Basis[e2],e2,n);
00317   if (e2>0)
00318    {
00319       mpz_mul(y,y,Basis[e2]); mpz_mod(y,y,n);
00320       mpz_clear(Basis[e2]); 
00321    }
00322 
00323 #if !defined(__GNUC__) || !defined(MY_BASIS_WORKAROUND)
00324   delete [] Basis; delete [] Flag;
00325 #endif
00326   
00327   mpz_mul(Delta,Delta,y); mpz_mod(Delta,Delta,n);
00328   pMulticombineData->multi_combine_Counter=0;
00329 #ifdef VERBOSE
00330   cout << "Multi_combine_exit finished." << endl;
00331 #endif
00332 }
00333 
00334 void CRelation::multi_combine_main(const CRelation& GL2)
00335   
00336   
00337 {
00338   
00339   adjust_multi_combine(); 
00340 
00341   
00342 
00343 
00344 
00345 
00346   if ( GL2.Relation_dense ) 
00347     {
00348       if (!Relation_dense) 
00349         convert_Relation_to_dense(); 
00350 
00351       
00352 
00353 
00354 
00355 
00356 
00357 
00358 
00359 
00360       Relation_dense->test_and_add_carry(*GL2.Relation_dense,pMulticombineData->ExponentArray);
00361       (*Relation_dense)._xor(*GL2.Relation_dense); 
00362     }
00363   else 
00364     if (Relation_dense) 
00365       { 
00366         
00367         const CTinyFBsizetypeVector& R2 = *GL2.Relation_sparse;
00368         for (int pos=0; pos<R2.size(); ++pos)
00369           {
00370             if (Relation_dense->test_and_invert(R2[pos])) ++pMulticombineData->ExponentArray[R2[pos]];
00371           }
00372       }
00373     else
00374       { 
00375         int p1 = 0;
00376         int p2 = 0;
00377         const CTinyFBsizetypeVector& R  = *Relation_sparse;
00378         const CTinyFBsizetypeVector& R2 = *GL2.Relation_sparse;
00379         CTinyFBsizetypeVector neueMenge(R.size()+R2.size());
00380         
00381         while (p2<R2.size() && p1<R.size())
00382           {
00383             while (R2[p2]<R[p1])
00384               {
00385                 neueMenge.fast_append(R2[p2]); ++p2;
00386                 if (p2==R2.size()) goto done;
00387               }
00388             while (R[p1]<R2[p2])
00389               {
00390                 neueMenge.fast_append(R[p1]); ++p1;
00391                 if (p1==R.size()) goto done;
00392               }
00393             while (R2[p2]==R[p1])
00394               {
00395                 ++pMulticombineData->ExponentArray[R[p1]];
00396                 ++p1; ++p2;
00397                 if (p2==R2.size()) goto done;
00398                 if (p1==R.size()) goto done;      
00399               }
00400           }
00401       done:
00402         while (p2!=R2.size()) { neueMenge.fast_append(R2[p2]); ++p2; }
00403         while (p1!=R.size()) { neueMenge.fast_append(R[p1]); ++p1; }
00404         Relation_sparse->copy_from(neueMenge); 
00405       }
00406 
00407   
00408   
00409   mpz_mul(Delta,Delta,GL2.Delta); mpz_mod(Delta,Delta,n);
00410   
00411   
00412   
00413   
00414   
00415   
00416 
00417   relevant_factor=largest_factor_in_Relation();
00418   
00419 }
00420 
00421 
00422 
00423 void CRelation::seek_emergency_default_handler(istream &, const streampos)
00424 {
00425   throw ios_base::failure("invalid seek position on istream");
00426 }
00427 
00428 
00429 void (*CRelation::seek_emergency_handler)(istream &, const streampos) = CRelation::seek_emergency_default_handler;
00430 
00431 CmpqsFactor CRelation::combine(istream &in, const streampos pos)
00432 {
00433   const streampos merkfpos=in.tellg(); 
00434   in.seekg(pos); if (in.peek()==EOF) seek_emergency_handler(in,pos);
00435   CmpqsFactor f=combine(in);
00436   in.seekg(merkfpos); 
00437   return f;
00438 }
00439 
00440 CmpqsFactor CRelation::multi_combine_main(istream &in, const streampos pos)
00441 {
00442   const streampos merkfpos=in.tellg(); 
00443   in.seekg(pos); if (in.peek()==EOF) seek_emergency_handler(in,pos);
00444   CmpqsFactor f=multi_combine_main(in);
00445   in.seekg(merkfpos); 
00446   return f;
00447 }
00448 
00449 
00450 int CStreamDecoder::GetValue()
00451 {
00452   if (ahead_value>0) { int w=ahead_value; ahead_value=0; return w; }
00453 
00454   char c;
00455   in >> c;
00456   if ( c>='A' && c<='Z' ) return c-('A'-1); 
00457   if ( c>='g' && c<='z' )
00458    {
00459      int h = (c-'g');
00460      ahead_value=(h%5)+1; 
00461      return (h/5)+1; 
00462    }
00463   if (c=='0') return 0; 
00464   if (c=='$') return -1;
00465   if (c=='%') return -2;
00466 
00467   if (c==' ')
00468    {
00469      MARK;
00470      cerr << "GetValue: Read a space? How to handle this?" << endl;
00471      in.setstate(std::ios::failbit);
00472      throw std::runtime_error("GetValue: read invalid character");
00473    }
00474 
00475   
00476   int w;
00477   in.unget();
00478   in >> w;
00479   return w;
00480 }
00481 
00482 void CStreamEncoder::PutValue(const int w)
00483 {
00484   if (w<=5 && w>=1 && prev_value>0) 
00485     {
00486       out << char( (prev_value-1)*5 + w-1 + 'g' );
00487       prev_value=0;
00488       return;
00489     }
00490 
00491   if (prev_value>0)
00492    {
00493      out << char(prev_value+('A'-1)); 
00494      prev_value=0;
00495    }
00496 
00497   if (w>26) 
00498    {
00499      out << w << " "; 
00500      return;
00501    }
00502   if (w>0)
00503    {
00504      if (w<=4) { prev_value=w; return; } 
00505      out << char(w+('A'-1)); 
00506      return;
00507    }
00508   switch (w)
00509    {
00510      case -2: out << "%"; break;
00511      case -1: out << "$"; break;
00512      case  0: out << "0"; break;
00513      default:
00514       MARK;
00515       cerr << "PutValue: don't know how to handle this..." << endl;
00516       cerr << "w=" << w << endl;
00517       out.setstate(std::ios::failbit);
00518       throw std::invalid_argument("PutValue: invalid value");
00519    }
00520 }
00521 
00522 bool CRelation::is_valid() const
00523 {
00524   mpz_t x,y;
00525   mpz_init(x);
00526   mpz_init_set_ui(y,1);
00527   if (Relation_sparse)
00528     {
00529       for (int i=0; i<Relation_sparse->size(); ++i)
00530         {
00531           mpz_mul_si(y,y,PrimeNumbers[(*Relation_sparse)[i]]);
00532           mpz_mod(y,y,n);
00533         }
00534     }
00535   else
00536     {
00537       for (int i=Relation_dense->first(); i>=0; i=Relation_dense->next(i))
00538         {
00539           mpz_mul_si(y,y,PrimeNumbers[i]);
00540           mpz_mod(y,y,n);
00541         }
00542     }
00543   
00544   mpz_invert(x,Delta,n); mpz_powm_ui(x,x,2,n);
00545 
00546 #ifdef VERBOSE
00547   cout << "checking for congruency: ";
00548   cout << x << " = " << y << " (mod N)" << endl;
00549 #endif
00550   if (mpz_cmp(x,y)!=0) 
00551     {
00552 #ifdef VERBOSE_WARN
00553       MARK; cout << "Relation is invalid: Values are NOT congruent!" << endl;
00554 #endif
00555       mpz_clear(x); mpz_clear(y);
00556       return false;
00557     }
00558  mpz_clear(x); mpz_clear(y);   
00559  return true;
00560 }
00561 
00562 
00563 #include <fstream>
00564 #include <set>
00565 struct myintcompare_
00566 {
00567   inline bool operator() (const int i1, const int i2) const
00568    {
00569      return i1 < i2;
00570    }
00571 };
00572 typedef std::set<int, myintcompare_> TMyIntSet;
00573 
00574 bool CRelation::is_valid(istream &in)
00575 {
00576   static TMyIntSet InductiveDynamicFactorSet; 
00577 
00578   CStreamDecoder enc_in(in);
00579   bool retval = false;
00580 
00581   string s;
00582   CmpqsFactor MainFactor;
00583 
00584   mpz_t x,y,Delta;
00585   mpz_init_set_ui(x,1);
00586   mpz_init_set_ui(y,1);
00587   mpz_init_set_ui(Delta,1);
00588  
00589   
00590   const ios::fmtflags oldFlags = in.flags(); 
00591   in.unsetf(ios::hex); in.setf(ios::dec); 
00592   in.unsetf(ios::showbase); 
00593  
00594   in >> s; 
00595   if (s != "G")
00596     {
00597 #ifdef VERBOSE_WARN
00598       MARK;
00599       cerr << "error: wrong position while reading relation" << endl;
00600 #endif
00601       retval=false; goto done;
00602     }
00603   in >> MainFactor; 
00604   in >> s; 
00605   mpz_set_str(Delta, s.c_str(), mpzbase_f);
00606   mpz_mod(Delta, Delta, n);
00607 
00608   
00609   in.unsetf(ios::dec); in.setf(ios::hex); 
00610   in.unsetf(ios::showbase); 
00611   
00612   signed int distance; 
00613 
00614   {
00615     const size_t sizeinbase_of_n = mpz_sizeinbase(n,2);
00616     mpz_set_ui(x,1); 
00617 
00618     distance=enc_in.GetValue();
00619     int index=distance;
00620     while (distance >= 0)
00621      {
00622        if (index>=StaticFactorbaseSettings::Size())
00623         {
00624   #ifdef VERBOSE_INFO
00625           MARK;
00626   #endif
00627   #ifdef VERBOSE_WARN
00628         cerr << "static factor outside the factorbase (index " << index << ")" << endl;
00629   #endif
00630         retval=false; goto done;
00631         }
00632 
00633        mpz_mul_si(x,x,PrimeNumbers[index]);
00634        if (mpz_sizeinbase(x,2)>sizeinbase_of_n)
00635         {
00636           mpz_mod(x,x,n); mpz_mul(y,y,x);
00637           mpz_mod(y,y,n); mpz_set_ui(x,1);
00638         }
00639        distance=enc_in.GetValue(); index+=distance;
00640      }
00641     mpz_mul(y,y,x); mpz_mod(y,y,n);
00642   }
00643   
00644   if (distance == -2) 
00645     {
00646       
00647 
00648       
00649 
00650       int Korrekturfaktor;
00651       while ( (Korrekturfaktor=enc_in.GetValue())>0 )
00652        {
00653           const TMyIntSet::const_iterator p = InductiveDynamicFactorSet.find(Korrekturfaktor);
00654           if (p==InductiveDynamicFactorSet.end())
00655             {
00656 #ifdef VERBOSE_INFO
00657               MARK;
00658 #endif
00659 #ifdef VERBOSE_WARN
00660               cerr << "Unknown correction value (Korrekturfaktor): " << Korrekturfaktor << endl;
00661 #endif
00662               retval=false; goto done;
00663             }
00664           mpz_mul_ui(y,y,Korrekturfaktor); mpz_mod(y,y,n);
00665         }
00666     }
00667 
00668   if (MainFactor.LP1()) mpz_mul_si(y,y,MainFactor.LP1());
00669   if (MainFactor.LP2()) mpz_mul_si(y,y,MainFactor.LP2());
00670   mpz_mod(y,y,n);
00671 
00672   mpz_invert(x,Delta,n); mpz_powm_ui(x,x,2,n);
00673   
00674 #ifdef VERBOSE
00675   cout << "checking for congruency: ";
00676   cout << x << " = " << y << " (mod N)" << endl;
00677 #endif
00678   if (mpz_cmp(x,y)!=0) 
00679     {
00680 #ifdef VERBOSE_WARN
00681       MARK; cout << "Relation is invalid: Values are NOT congruent!" << endl;
00682 #endif
00683       retval=false; goto done;
00684     }
00685 
00686   if (MainFactor.IsTypeOf(CmpqsFactor::single_large_prime))
00687    {
00688      
00689      
00690      
00691      InductiveDynamicFactorSet.insert(MainFactor.int_value());
00692    }
00693 
00694   retval=true; 
00695 done:
00696   mpz_clear(Delta); mpz_clear(y); mpz_clear(x);
00697   in.flags(oldFlags); 
00698   return retval;
00699 }
00700 
00701 void CRelation::SanityCheckRelationsFile(const std::string FileName)
00702 {
00703 #ifdef VERBOSE_NOTICE
00704   cout << "Performing sanity check for " << FileName << endl;
00705 #endif
00706   std::ifstream MyFile(FileName.c_str());
00707   if (!MyFile)
00708    {
00709      cerr << "problems to open \"" << FileName << "\"!" << endl;
00710      exit(1);
00711    }
00712 
00713   MyFile.seekg(0,ios::beg);
00714   unsigned int count = 0, line=0, invalid=0, obsolete=0;
00715   while (MyFile)
00716    {
00717      while (MyFile.peek()=='U') { ++line; ++obsolete; MyFile.ignore(1000000,'\n'); }
00718      while (MyFile.peek()=='G')
00719       {
00720         ++count; ++line;
00721         if (!is_valid(MyFile))
00722          {
00723            ++invalid;
00724            cerr << "invalid Relation in " << FileName << ", line " << line << endl;
00725            MyFile.ignore(1000000,'\n');
00726          } else MyFile.ignore(1,'\n');
00727        #ifdef VERBOSE_NOTICE
00728         if (count%500==0) cout << count << " relations validated.\r" << flush;
00729        #endif
00730       }
00731      if (MyFile.peek()!=EOF && MyFile.peek()!='U')
00732       {
00733         ++line; ++invalid;
00734         cerr << "Garbage in " << FileName << ", line " << line << endl;
00735         cerr << "Giving up!" << endl;
00736         break;
00737         
00738       }
00739      
00740      
00741      MyFile.get();
00742      if (MyFile.eof()) break;
00743      MyFile.unget();
00744    }
00745 #ifdef VERBOSE_NOTICE
00746   cout << line << " lines scanned.       " << endl;
00747   if (obsolete) cout << obsolete << " obsolete relations skipped." << endl;
00748   cout << count << " relations validated." << endl;
00749 #endif
00750   if (invalid)
00751    {
00752      cout << invalid << " INVALID relations detected in " << FileName << endl;
00753      exit(1);
00754    }
00755 }