Logo Search packages:      
Sourcecode: magnus version File versions  Download package

fastProblems.C

// Copyright (C) 1995 The New York Group Theory Cooperative
// See magnus/doc/COPYRIGHT for the full notice.
//
// Contents: Implementations of CM classes for fast problems
//
// Principal Author: Roger Needham, Dmitry Bormotov
//
// Status: in progress
//
// Revision History:
//
// * 7/96 Dmitry B. made porting to gcc 2.7.2.
//
// * 10/11/96 EP added:
//
//               void SGOfFreeWhiteheadReduction::takeControl( )
//               void SGOfFreeIsAFreeFactor::takeControl( )
//
// * 96/10/10 @dp added:
//          FreeGetNextN_thElement::takeControl(),
//          TerminalSegmentOfWord::takeControl(),
//          SegmentOfWord::takeControl()
//
// * 96/10/16 @dp added:
//          SGOfFreeIsMalnormal::takeControl( ),
//          APOfFree_DoEltsCommute::takeControl( ),
//          APOfFreeIsHyperbolic::takeControl( ),
//          APOfFreeNumberOfSubstitutions::takeControl( ).
//
//
// *00/04/04 @db: FastComputation is under reconstruction - ATS is
// about to be introduced
//


#include "fastProblems.h"
#include "OutMessages.h"
#include "conversions.h"

// Includes required by the computations:

#include "SMFPGroup.h"
#include "SMWord.h"
#include "SMSubgroup.h"
#include "SMMap.h"
#include "SMHomomorphism.h"
#include "SMEquation.h"
#include "SMList.h"

#include "ORProblems.h"
#include "SGofFreeGroup.h"

#include "MagnusBreakdown.h"
#include "File.h"
#include "Whitehead.h"
#include "OneRelatorGroupWithTorsion.h"
#include "Products.h"
#include "CommutatorIterator.h"
#include "APwithOneRelator.h"
#include "RipsConstruction.h"

//#define ATS


//---------------------------------------------------------------------------//
//------------------------- FastComputation ---------------------------------//
//---------------------------------------------------------------------------//


void FastComputation::viewStructure(ostream& ostr) const
{
  //#ifdef ATS
  ProblemView pv( ostr, oid(), Text("Temporary"),
              Text("Temporary"),
              "none",
              "Temporary"
              );

  pv.startItemGroup();

  //pv.add("For the use of genetic algorithm", GA.arcSlotID(), 100);

  pv.done();
  //#endif
}

#ifndef ATS

FastComputation::FastComputation( ) : ComputationManager( )
{
  resultIsFast();
}

#else

FastComputation::FastComputation( ) : ComputationManager( true )
{
  //attachClient( *this );
  //resultIsFast();
}

#endif


//---------------------------------------------------------------------------//
//--------------------- Free Group Computations -----------------------------//
//---------------------------------------------------------------------------//


void CommutatorInFree::takeControl( )
{
  Word u, v;
  FreeGroup F = theWord.getParent().getFreePreimage();
  LogMessage msg( theWord );
  msg << Name( theWord );

  if ( F.isCommutator( theWord.getWord(), u, v ) ) {
       msg << " is the commutator of ";
       F.printWord( msg, u );
       msg << " and ";
       F.printWord( msg, v );
  } else {
       msg << " is not a commutator";
  }
  msg << " in " << Name( Parent( theWord ) ) << ".";

  msg.send();
}


void FreeInCommutatorSG::takeControl( )
{
  FreeGroup F = theWord.getParent().getFreePreimage();
  LogMessage msg( theWord );

  msg << Name( theWord ) << " is";
  if ( !F.inCommutatorSG(theWord.getWord()) )
    msg << " not";
  msg << " in the commutator subgroup of " << Name( Parent( theWord ) ) << ".";

  msg.send();
}


void ProductOfCommutators::takeControl( )
{
  FreeGroup F = theWord.getParent().getFreePreimage();
  
  Chars result = F.productOfCommutators(theWord.getWord(),theFile);
  LogMessage msg( theWord );
  
  if ( result == Chars("not in derived") )
    msg << Name( theWord )  << " isn't in derived subgroup.";
  else
    if ( result == Chars("trivial") )
      msg << Name( theWord ) << " is trivial.";
    else
     {
       msg << Name( theWord ) << " can be rewritten as:  " << result << " . ";
       
       LogMessage msg1( theWord );
       msg1 << Link("Click here to see the steps of the rewriting process" , "FastCommutators" , theFile.getFileName() );
       msg1.send();
     }
  
  msg.send();
}

void ProductOfSquares::takeControl( )
{
  FreeGroup F = theWord.getParent().getFreePreimage();
  
  Chars result = F.productOfSquares(theWord.getWord(),theFile);
  LogMessage msg( theWord );
  
  if ( result == Chars("not in square") )
    msg << Name( theWord )  << " isn't in subgroup generated by all squares.";
  else
    if ( result == Chars("trivial") )
      msg << Name( theWord ) << " is trivial.";
    else
      {
      msg << Name( theWord ) << " can be rewritten as:  " << result << " . ";
  
      LogMessage msg1( theWord );
      msg1 << Link("Click here to see the steps of the rewriting process" , "FastSquares" , theFile.getFileName() );
      msg1.send();
      }

  msg.send();
}

void FreeIsElementAProperPower::takeControl( )
{
  Word w = theWord.getWord().cyclicallyReduce();
  LogMessage msg( theWord );
  msg << Name( theWord );

  if ( w.isProperPower() ) {
    int r = maximalRoot( w );
    msg << " is a proper power. " << Name( theWord ) << " is equal ";
    theWord.getParent().getFreePreimage().printWord(msg,
          w.initialSegment( w.length()/r ) );
    msg << " in power " << r;
  }
  else 
    msg << " is not a proper power";
  
  msg << ".";

  msg.send();
}


void FreeMaximalRootOfElement::takeControl( )
{
  Word w = theWord.getWord().cyclicallyReduce();
  SMObject* smo =
    new SMWord( theWord.getParent(),
            w.initialSegment( w.length()/maximalRoot( w ) ),
            Text("The maximal root of") + Name( theWord )
            );
  
  ListOf<OID> dependencies( theWord.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
  
  //@rn Factor out somewhere
}

void FreeCentolizerOfElement::takeControl( )
{
  Word w = theWord.getWord().cyclicallyReduce();
  VectorOf<Word> generator(1);
  generator[0] = w.initialSegment( w.length()/maximalRoot( w ) );
  SGofFreeGroup S2(theWord.getParent().getFreePreimage(), generator);
  SMObject* smo = new SMSubgroup(theWord.getParent(),S2,
                Text("The centralizer of") + Name( theWord ) );
  
  ListOf<OID> dependencies( theWord.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
  
}

void FreeGetN_thElement::takeControl( )
{
  char s[10];
  sprintf(s,"%u",theNumber);
  
  SMObject* smo =
    new SMWord( theGroup,
            theGroup.getFreePreimage().getN_thElement(theNumber),
            Text("The") + Text(s) + Text("- th element of")
            + Name( theGroup )
            );
  
  ListOf<OID> dependencies( theGroup );
  CheckinMessage( *smo, "", dependencies ).send();
  
  //@rn Factor out somewhere
}
void WordProblemInFree::takeControl( )
{
  Word u, v;
  FreeGroup F = theWord.getParent().getFreePreimage();
  LogMessage msg( theWord );
  msg << Name( theWord );

  if ( F.wordProblem( theWord.getWord() ) ) {
       msg << " is trivial";
  } else {
       msg << " is not trivial";
  }
  msg << " in " << Name( Parent( theWord ) ) << ".";

  msg.send();
}


void WordsAreEqual::takeControl( )
{
  LogMessage msg( word1, word2 );

  msg << Name( word1 ) << " is ";

  if ( word1.getWord().freelyReduce() != word2.getWord().freelyReduce() )
       msg << "not ";

  msg << "equal to " << Name( word2 ) << ".";

  msg.send();
}


void EndoOnFreeIsMono::takeControl( )
{
  if( map.mic.isMono() == dontknow ) {
    const Map& M = map.getMap();
    SGofFreeGroup S( map.getDomain().getFreePreimage(), M.generatingImages() );
    bool isMono = ( S.rank() == M.generatingImages().length() );
    map.mic.putIsMono( isMono );
  }

  LogMessage msg( map );
  msg << Name( map ) << " is ";
  if ( map.mic.isMono() == no ) 
    msg << "not ";
  msg << "an monomorphism of " << Name( Domain( map ) ) << ".";
  msg.send();
}

void EndoOnFreeIsEpi::takeControl( )
{
  if( map.mic.isEpi() == dontknow ) {
    const FreeGroup& F = map.getDomain().getFreePreimage();
    SGofFreeGroup S( F, map.getMap().generatingImages() );
    bool isEpi = true;
    for( int i = 1; i <= F.numberOfGenerators(); ++i ) {
      if( !S.contains( Generator(i) ) ) {
      isEpi = false;
      break;
      }
    }
    map.mic.putIsEpi( isEpi );
  }

  LogMessage msg( map );
  msg << Name( map ) << " is ";
  if ( map.mic.isEpi() == no ) 
    msg << "not ";
  msg << "an epimorphism of " << Name( Domain( map ) ) << ".";
  msg.send();
}

struct IsAuto;

void EndoOnFreeIsAut::takeControl( )
{
  LogMessage msg( map );
  msg << Name( map ) << " is ";

  if ( ! map.getDomain().getFreePreimage()
         .isAutomorphism( map.getMap().generatingImages() )
       )
    msg << "not ";
  else {
    IsAuto IA(map);
    FEDataUpdate( IA, True() ).send();
  }

  msg << "an automorphism of " << Name( Domain( map ) ) << ".";
  msg.send();
}


void EndoOnFreeIsInner::takeControl( )
{
  LogMessage msg( map );
  msg << Name( map ) << " is ";

  if ( ! map.getDomain().getFreePreimage()
         .isInnerAutomorphism( map.getMap().generatingImages() )
       )
    msg << "not ";
  // We do not record innerness in the FE...

  msg << "an inner automorphism of " << Name( Domain( map ) ) << ".";
  msg.send();
}

void EndoOnFreeIsIAAut::takeControl( )
{
  LogMessage msg( map );
  msg << Name( map ) << " is ";

  bool answer = map.getDomain().getFreePreimage()
    .isIAAutomorphism( map.getMap().generatingImages() );

  if( !answer )
    msg << "not ";
  // We do not record IA-ness in the FE...

  msg << "an IA-automorphism of " << Name( Domain( map ) ) << ".";
  msg.send();
}


void InverseAuto::takeControl( )
{
  SMObject* smo =
    new SMHomomorphism( map.getDomain(),
                  Map( map.getDomain().getFPGroup(),
                     map.getDomain().getFPGroup(),
                     map.getDomain().getFreePreimage()
                     .inverseAutomorphism( map.getMap().generatingImages() )
                     ),
                  Text("The inverse of") + Name( map )
                  );
  
  ListOf<OID> dependencies( map.getDomain() );
  CheckinMessage( *smo, "", dependencies ).send();

  IsAuto IA( *smo );
  FEDataUpdate( IA, True() ).send(); 
}

void AutoWhiteheadDecomposition::takeControl( )
{
  const FreeGroup& F = map.getDomain().getFreePreimage();
  ListOf<ElementaryWhiteheadAuto> decoList = 
    whiteheadDecomposition( map.getMap().generatingImages() ).getAutoList();

  
  File file;
  file.setColor( titleColor );
  file << "Decomposition of the automorphism of free group" << endl
       << "into a sequence of elementary Whitehead automorphisms :" << endl << endl;
  file.setColor( mainColor );
  ListIterator< ListOf<ElementaryWhiteheadAuto> > I(decoList);
  const VectorOf<Chars> genNames = F.namesOfGenerators();
  int i = 0;
  for( ; !I.done(); I.next() ) {
    file << ' ' << ++i << ". ";
    I.value().printOn( file, genNames );
    file << endl;
  }
  file << end;

  LogMessage msg( map );
  msg << Link( Chars("Click here to see the Whitehead decomposition "
                 "of the automorpisms ") + Text( Name(map) ),
             "WhiteheadDecomposition",
             file.getFileName() 
             );
  msg << ".";
  msg.send();
}


void WordInSGOfFree::takeControl( )
{
  LogMessage msg( word );
  msg << Name( word ) << " does ";

  Word w = word.getWord();
  SGofFreeGroup H = subgroup.getSubgroup();
  FreeGroup F = H.parentGroup();
  bool represent = H.contains( w );
  if ( ! represent  )
       msg << "not ";

  msg << "represent an element of " << Name( subgroup ) << ".";
  msg.send();

  if( represent ) {

    Word wInSubgroupGens = expressWordInSubgroupGenerators( H, w );

    File logFile;
    logFile.setColor( titleColor );
    logFile << "The subroup " << Name( subgroup ) 
          << " of free group is generated by " << endl << endl;
    
    logFile.setColor( mainColor );
    VectorOf<Word> hGens = H.generators();
    for( int i = 0; i < hGens.length(); ++i ) {
      logFile << " h" << i+1 << " = ";
      F.printWord( logFile, hGens[i] );
      logFile << endl;
    }
    logFile << endl;
    
    logFile.setColor( titleColor );
    logFile << "The given word is expressed in the subgroup generators as follows:" 
          << endl << endl;

    VectorOf<Chars> hNames( hGens.length() );
    for( int i = 0; i < hNames.length(); ++i ) {
      char tmpName[20];
      sprintf( tmpName, "h%d", i+1);
      hNames[i] = tmpName;
    }
    FreeGroup FH( hNames );
    logFile.setColor( mainColor );
    logFile << " w = ";
    FH.printWord( logFile, wInSubgroupGens );

    
    LogMessage msg( word );
    msg << Link( 
            Chars("Click here to see the word ") + Text(Name(word))
            + Chars(" written in subgroup generators"),
            "WordInSubgroupGenerators",
            logFile.getFileName()
            )
      << ".";
    msg.send();
  }
}


void PowerOfWordInSGOfFree::takeControl( )
{
  LogMessage msg( word );
  msg << Name( word ) << " to ";

  long answer = subgroup.getSubgroup().powerInSubgroup( word.getWord() );
  if ( answer != 0 )
       msg << "the power " << answer;
  else
       msg << "no power other than 0";

  msg << " represents an element of " << Name( subgroup ) << ".";
  msg.send();
}

void ConjugacyProblemInFree::takeControl( )
{
  LogMessage msg( word1,word2 );

  Word conjugator;
  Trichotomy answer = word1.getParent().getFreePreimage().
    conjugacyProblem(word1.getWord(),word2.getWord(),conjugator);

  if ( answer == yes )
    {
      msg << Name( word1 ) << " and  " << Name( word2 ) << " are conjugate: ";
      msg << "X " << Name( word1 ) << " x = " <<  Name( word2 ) << ", where x = ";
      word1.getParent().getFPGroup().printWord(msg,conjugator);
    }
  else
    msg << Name( word1 ) << " and  " << Name( word2 ) << " are not conjugate ";
  
  msg << ".";
  msg.send();
}

void ConjugateOfWordInSGOfFree::takeControl( )
{
  LogMessage msg( word );

  Word conjugator;

  if ( subgroup.getSubgroup().conjugateInSubgroup( word.getWord(), conjugator ) )
       {
            msg << Name( word ) << " conjugated by ";
            word.getParent().getFPGroup().printWord( msg, conjugator );
       }
  else
       msg << "No conjugate of " << Name( word );

  msg << " represents an element of " << Name( subgroup ) << ".";

  msg.send();
}


void WordInNielsenBasisSGOfFree::takeControl( )
{
  LogMessage msg( subgroup, word );
  SGofFreeGroup S( subgroup.getSubgroup() );
  if( !S.contains( word.getWord() ) ) {
    msg << Name( subgroup ) << " does not contain " << Name(word);
  }
  else {
    FreeGroup F( S.nielsenBasis().length() );
    msg << "The canonical decomposition of " << Name( word ) 
      << " in the Nielsen basis for " << Name( subgroup ) 
      << " : " << Name( word ) << " = ";
    F.printWord( msg, S.inNielsenWords( word.getWord() ) );
  }
  msg << ".";
  msg.send();
}

void SchreierRepOfWordInSGOfFree::takeControl( )
{
  SMObject* smo =
       new SMWord( word.getParent(),
                 subgroup.getSubgroup()
                 .rightSchreierRepresentative( word.getWord() ),
                 Text("The right Schreier representative of") + Name( word )
                 + Text("mod") + Name( subgroup )
                 );

  ListOf<OID> dependencies( word.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void SGOfFreeContainment::takeControl( )
{
  LogMessage msg( subgroup1, subgroup2 );
  msg << Name( subgroup1 ) << " does ";

  if ( ! subgroup1.getSubgroup().contains( subgroup2.getSubgroup() ) )
       msg << "not ";

  msg << "contain " << Name( subgroup2 ) << ".";
  msg.send();
}

void SGOfFreeAreEqual::takeControl( )
{
  LogMessage msg( subgroup1, subgroup2 );
  msg << Name( subgroup1 ) << " and " << Name( subgroup2 ) << " are";

  if ( ! subgroup1.getSubgroup().equalTo( makeSetOf( 
       subgroup2.getSubgroup().generators() ) ) )
    msg << " not";
  
  msg << " equal.";
  msg.send();
}

void SGOfFreeJoin::takeControl( )
{
  SGofFreeGroup S2 = subgroup2.getSubgroup();
  SMObject* smo =
    new SMSubgroup( subgroup1.getParent(),
                subgroup1.getSubgroup().join( S2 ),
                Text("The join of") + Name( subgroup1 ) + Text("and")
                + Name( subgroup2 )
                );
  
  ListOf<OID> dependencies( subgroup1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}

void SGOfFreeIntersection::takeControl( )
{
  SGofFreeGroup S2 = subgroup2.getSubgroup();
  SMObject* smo =
    new SMSubgroup( subgroup1.getParent(),
                subgroup1.getSubgroup()
                .intersection( S2 ),
                Text("The intersection of") + Name( subgroup1 ) + Text("and")
                + Name( subgroup2 )
                );
  
  ListOf<OID> dependencies( subgroup1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}

void SGOfFreeIsNormal::takeControl( )
{
  LogMessage msg( subgroup );
  msg << Name( subgroup ) << " is ";
  
  bool isnormal = subgroup.getSubgroup().isNormal();

  subgroup.sic.putIsNormal(isnormal);

  if ( ! isnormal )
       msg << "not ";



  msg << "normal in " << Name( Parent( subgroup ) ) << ".";
  msg.send();
}

void SGOfFreeIsAFreeFactor::takeControl( )
{
  LogMessage msg( subgroup );
  msg << Name( subgroup ) << " is ";

  if ( ! subgroup.getSubgroup().isAFreeFactor() )
       msg << "not ";

  msg << "a free factor of " << Name( Parent( subgroup ) ) << ".";
  msg.send();
}

// Conjugates each word of the given vector with the word `w'.
// This is duplicate of static function in SGofFreeGroup.C
// @dp: This function is for SGOfFreeIsMalnormal::takeControl() only.

static VectorOf<Word> conjugateBy(const VectorOf<Word>& V, const Word& w)
{ 
  VectorOf<Word> result(V.length());
  for( int i = 0; i < V.length(); ++i ) 
    result[i] = Word( Word(V[i]).conjugateBy(w) ).freelyReduce();
  return result;
}

void SGOfFreeIsMalnormal::takeControl( )
{
  LogMessage msg( subgroup );
  Word conjugator;
  SGofFreeGroup S = subgroup.getSubgroup();
  
  if ( S.isMalnormal( conjugator ) ) 
    msg << Name( subgroup ) << " is malnormal in " 
      << Name( Parent( subgroup ) ) << ".";
  else {
    FPGroup G = subgroup.getParent().getFPGroup();
    SGofFreeGroup S1( FreeGroup(G.numberOfGenerators()), 
                  conjugateBy(S.generators(), conjugator) );
    SGofFreeGroup S2 = S.intersection( S1 );
    msg << Name( subgroup ) << " is not malnormal in " 
      << Name( Parent( subgroup ) ) << " because an intersection of " 
      << Name( subgroup ) << " and " 
      << Name( subgroup ) << " conjugated by ";
    G.printWord( msg, conjugator );
    msg << " is not trivial. The intersection is gp( ";
    int numOfGens = S2.generators().length();
    for( int i = min( 4, numOfGens-1 ); i >= 0; --i ) {
      G.printWord( msg, S2.generators()[i] );
      if( i != 0) msg << ", ";
    }
    if( numOfGens > 5 ) msg << ", ...";
    msg << " ).";
  }

  msg.send();
}

void QuadEquationSurfaceForm::takeControl( )
{
  LogMessage msg( equation );
  msg << "A surface form of " << Name( equation ) << " : ";

  QEqnSolutionsInFreeGroup solver( equation.getFreeGroup(), equation.getWord(),
                           equation.numberOfVariables() );
  Word surf = solver.surfaceForm();
  equation.getFreeGroup().printWord( msg, surf );
  msg << ".";
  msg.send();

  LogMessage msg1( equation );
  msg1 << "Mapped by automorphism : ";
  msg1.send();

  LogMessage msg2( equation );
  solver.toSurfaceForm().printOn(msg2);
  msg2.send();
}


void SGOfFreeWhiteheadReduction::takeControl( )
{
  SGofFreeGroup sg(vect.getParent().getFreePreimage(), vect.getWords());
  VectorOf<Word> answer = sg.findWhiteheadBasis();
  SMObject* smo =
    new SMVectorOfWords( vect.getParent(), answer,
                   Text("A Whitehead reduction of tuple") + Name(vect));

  ListOf<OID> dependencies( vect.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void SGOfFreeNielsenBasis::takeControl( )
{
  const FreeGroup F = subgroup.getParent().getFreePreimage();
  const VectorOf<Word> generators = subgroup.getSubgroup().generators();
  FreeGroup::NielsenBasis nielsenBasisProblem = F.nielsenBasis( generators, true );
  VectorOf<Word> nielsenBasis = nielsenBasisProblem.newGenerators();

  // Print the basis into a file.  
  File file;
  file.setColor( titleColor );
  file << "The generators of a Nielsen basis for subgroup of free group:"
       << endl << endl;
  file.setColor( mainColor );
  for( int i = 0; i < nielsenBasis.length(); ++i ) {
    file << 'x' << i+1 << " = ";
    F.printWord( file, nielsenBasis[i] );
    file <<endl;
  }
  file << end;
  
  {
    // Make link to the file with computed nielsen basis.
    LogMessage msg( subgroup );
    msg << Link( Chars("Click here to see the generators of a Nielsen basis of ")+
             Text( Name(subgroup) ), "NielsenBasis", 
             file.getFileName() ) << ".";
    msg.send();
  }

  {
    // Make link to the file with details of computation of nielsen basis.
    LogMessage msg( subgroup );
    msg << Link( Chars("Click here to see computation details of a Nielsen basis of ")
             + Text( Name(subgroup) ), "NielsenBasisDetails",
             nielsenBasisProblem.getFileName() ) << "."; 
    msg.send();
  }
}


void SGOfFreeIndex::takeControl( )
{
  LogMessage msg( subgroup );
  msg << Name( subgroup ) << " has ";
  int index = subgroup.getSubgroup().findIndex();
  if ( index == 0 ) msg << "infinite index";
  else msg << "index " << index;
  msg << " in " << Name( Parent( subgroup ) ) << ".";
  
  msg.send();
}

void SGOfFreeRank::takeControl( )
{
  LogMessage msg( subgroup );
  msg << "The rank of " << Name( subgroup ) << " is  "
      << subgroup.getSubgroup().rank() << ".";
  msg.send();
}


void SGOfFreeNormaliser::takeControl( )
{
  SMObject* smo =
    new SMSubgroup( subgroup.getParent(),
                SGofFreeGroup(subgroup.getParent().getFreePreimage(),
                          subgroup.getSubgroup().normalizer() ),
                Text("The normaliser of") + Name( subgroup ) + Text("in")
                + Name( Parent( subgroup ) ) );
  ListOf<OID> dependencies( subgroup.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void SGOfFreeHallCompletion::takeControl( )
{
  SMObject* smo =
    new SMSubgroup( subgroup.getParent(),
                subgroup.getSubgroup().MHallCompletion(),
                Text("A finite index subgroup of")
                + Name( Parent( subgroup ) ) + "with"
                + Name( subgroup ) + Text("as a free factor") );
  ListOf<OID> dependencies( subgroup.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}

void NormalApproximationProblem::takeControl()
{
  SMFPGroup& G = subgroup.getParent();
  VectorOf<Word> v = subgroup.getSubgroup().generators();
  SetOf<Word> s;
  for(int i=0; i<v.length(); i++) s |= v[i];
  for(int i=1; i<=level; i++) {
    Word w = G.getFreePreimage().getN_thElement(i);
    SetOf<Word> s1;
    SetIterator<Word> I(s);
    for(I.reset(); !I.done(); I.next()) s1 |= (w*I.value()*w.inverse()).freelyReduce();
    s |= s1;
  }
  VectorOf<Word> v1;
  SetIterator<Word> I(s);
  for(I.reset(); !I.done(); I.next()) v1.append(I.value());
  SMObject* smo =
    new SMSubgroup( G, SGofFreeGroup(G.getFreePreimage(), v1),
                Text(level) + "-th Normal Approximation of" 
                + Name(subgroup) );
  ListOf<OID> dependencies( G );
  CheckinMessage( *smo, "", dependencies ).send();
}

void FreeIsSGTrivial::takeControl( )
{
  LogMessage msg( subgroup );

  msg << Name( subgroup ) << " is";

  if ( !subgroup.getSubgroup().isTrivial() )
    msg << " not";
  msg << " trivial in " << Name( Parent( subgroup ) ) << ".";
  
  msg.send();
}


void FreeIsAutomatic::takeControl( )
{
  LogMessage msg( group );

  msg << "Free groups are automatic. If X freely generates the "
         "finitely generated free group F, then (A,L) is a rational structure "
         "for F, where here A is the union of X and the set of all inverses of"
         " elements of X and L is the set of all reduced X-words.";
  
  msg.send();
}

void FreeIsHyperbolic::takeControl( )
{
  LogMessage msg( group );

  msg << "Free groups are hyperbolic.";
  
  msg.send();
}

//---------------------------------------------------------------------------//
//---------------------- Misc Fast Computations -----------------------------//
//---------------------------------------------------------------------------//



void FreelyReduceWord::takeControl( )
{
  SMObject* smo =
    new SMWord( theWord.getParent(),
            theWord.getWord().freelyReduce(),
            Text("The freely reduced form of") + Name( theWord )
            );

  ListOf<OID> dependencies( theWord.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
  
  //@rn Factor out somewhere
}


void CyclicallyReduceWord::takeControl( )
{
  SMObject* smo =
    new SMWord( theWord.getParent(),
            theWord.getWord().cyclicallyReduce(),
            Text("The cyclically reduced form of") + Name( theWord ) );
  
  ListOf<OID> dependencies( theWord.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void FormalInverseOfWord::takeControl( )
{
  SMObject* smo =
    new SMWord( theWord.getParent(),
            theWord.getWord().inverse(),
            Text("The inverse of") + Name( theWord )
            );
  
  ListOf<OID> dependencies( theWord.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void WordLength::takeControl( )
{
  SMFPGroup& theGroup = theWord.getParent();
  Word w = theWord.getWord().freelyReduce();
  if( theGroup.gic.isAbelian()==yes)
    w = AbelianWord(theGroup.getFPGroup().numberOfGenerators(), w).getWord();
  LogMessage msg( theWord );
  msg << "The length of " << Name( theWord ) 
      << " in the freely reduced form is equal to " 
      << w.length() << ".";
  msg.send();
}


void InitialSegmentOfWord::takeControl( )
{
  SMFPGroup& theGroup = theWord.getParent();
  Word w = theWord.getWord();
  if(theGroup.gic.isAbelian()==yes)
    w = AbelianWord(theGroup.getFPGroup().numberOfGenerators(), w).getWord();
  int trueLen = min( theLength, w.length() );
  char s[10];
  sprintf(s,"%u",trueLen);
  SMObject* smo =
    new SMWord( theGroup, w.initialSegment( trueLen ),
            Text("The ") + Text(s) + Text("-th initial segment of ")
            + Name( theWord ) );
  
  ListOf<OID> dependencies( theGroup );
  CheckinMessage( *smo, "", dependencies ).send();
  
  //@rn Factor out somewhere
}

void TerminalSegmentOfWord::takeControl( )
{
  SMFPGroup& theGroup = theWord.getParent();
  Word w = theWord.getWord();
  if(theGroup.gic.isAbelian()==yes)
    w = AbelianWord(theGroup.getFPGroup().numberOfGenerators(), w).getWord();
  int trueLen = min( theLength, w.length() );
  char s[10];
  sprintf(s,"%u",trueLen);
  SMObject* smo =
    new SMWord( theGroup, w.terminalSegment( trueLen ),
            Text("The ") + Text(s) + Text("-th terminal segment of ")
            + Name( theWord ) );
  ListOf<OID> dependencies( theGroup );
  CheckinMessage( *smo, "", dependencies ).send();
}


void SegmentOfWord::takeControl( )
{
  SMFPGroup& theGroup = theWord.getParent();
  Word w = theWord.getWord();
  if(theGroup.gic.isAbelian()==yes)
    w = AbelianWord(theGroup.getFPGroup().numberOfGenerators(), w).getWord();
  int start = min( w.length(), theStart );
  int stop  = min( w.length(), theStart + theLength - 1 );
  char startText[10];
  sprintf(startText,"%u",start);
  char lenText[10];
  sprintf( lenText,"%u", stop-start+1 );
  SMObject* smo =
    new SMWord( theGroup, w.subword(start-1, stop),
            Text("User defined a subword of length") + Text(lenText)
            + Text("(starting from") + startText + Text(") of")
            + Name( theWord ) );
  ListOf<OID> dependencies( theGroup );
  CheckinMessage( *smo, "", dependencies ).send();
}


void FreeGetNextN_thElement::takeControl( )
{
  SMFPGroup& theGroup = theWord.getParent();
  const FreeGroup& F = theGroup.getFreePreimage();
  int number = F.numberOfElement( theWord.getWord() ) + theNumber;
  char s[20];
  sprintf(s,"%u", theNumber );
  SMObject* smo =
    new SMWord( theGroup, F.getN_thElement(number),
            Text("The") + Text(s) + Text("- th element of")
            + Name( theGroup ) + Text("(after the word") 
            + Name(theWord) + Text("in lex order)") );
  ListOf<OID> dependencies( theGroup );
  CheckinMessage( *smo, "", dependencies ).send();  
}



void FormalProductOfWords::takeControl( )
{
  SMObject* smo =
    new SMWord( word1.getParent(),
            (word1.getWord() * word2.getWord()).freelyReduce(),
            Text("The product of") + Name( word1 )
            + Text("and") + Name( word2 ) );
  ListOf<OID> dependencies( word1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}

void ConjugateOfWord::takeControl( )
{
  SMObject* smo =
    new SMWord( theWord1.getParent(),
            (theWord2.getWord().inverse() * theWord1.getWord() 
             * theWord2.getWord()).freelyReduce(),
            Text("The conjugate of") + Name( theWord1 )
            + Text("by") + Name( theWord2 )     );

  ListOf<OID> dependencies( theWord1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}

void CommutatorOfWords::takeControl( )
{
  SMObject* smo =
    new SMWord( theWord1.getParent(),
            (theWord1.getWord().inverse() * theWord2.getWord().inverse()
             * theWord1.getWord() * theWord2.getWord()).freelyReduce(),
            Text("The commutator of") + Name( theWord1 )
            + Text("and") + Name( theWord2 ) );
  
  ListOf<OID> dependencies( theWord1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}

void PowerOfMap::takeControl( )
{
  //@rn Need new member in Map to do this
      
  Map m = map.getMap();
  Map result = composition(m, m);
  // We know the power is at least 2, since we put that restriction
  // in the menu definition.
       
  int count = power - 1;
  while ( --count ) result = composition(m, result);

  SMObject* smo =
    new SMMap( map.getDomain(), map.getRange(), result,
             Text("The map") + Name( map ) + Text("to the power") + power );
  ListOf<OID> dependencies( map.getDomain() );
  dependencies.append( map.getRange() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void ComposeMaps::takeControl( )
{
  if( map1.getDomain().oid() == map2.getRange().oid() ) {
    SMHomomorphism* smo = new SMHomomorphism
      ( map1.getDomain(),
      composition( map2.getMap(), map1.getMap() ),
      Text("The composition") + Name( map2 ) + Text(Name( map1 )) );
    
    ListOf<OID> dependencies( map1.getDomain() );
    CheckinMessage( *smo, "", dependencies ).send();
  }

  else {
    SMHomomorphism2* smo = new SMHomomorphism2
      ( map1.getDomain(), map2.getRange(),
      composition( map2.getMap(), map1.getMap() ),
      Text("The composition") + Name( map2 ) + Text(Name( map1 )) );
    
    ListOf<OID> dependencies( map1.getDomain() );
    dependencies.append( map2.getRange() );
    CheckinMessage( *smo, "", dependencies ).send();
  }
}


void FreeAreHomsEqual::takeControl( )
{
  FreeGroup F = map1.getDomain().getFreePreimage();
  int numOfGens = F.numberOfGenerators();
  Map m1 = map1.getMap();
  Map m2 = map2.getMap();
  
  bool bEqual = true;
  for( int i = 0; i < numOfGens; ++i )
    if( !F.areEqual(m1.generatingImages(i), m2.generatingImages(i))) {
      bEqual = false;
      break;
    }
  
  LogMessage msg( map1, map2 );
  
  msg << Name( map1 ) << " is ";

  if ( !bEqual )  msg << "not ";
  msg << "equal to " << Name( map2 ) << ".";
  
  msg.send();
}


void ImageUnderMap::takeControl( )
{
  Word tmpWord = map.getMap().imageOf( word.getWord() );
  if ( map.getRange().gic.isAbelian()==yes)
    tmpWord = AbelianWord( map.getRange().getFPGroup().numberOfGenerators(),
                     tmpWord ).getWord();
  SMObject* smo =
    new SMWord( map.getRange(),tmpWord,
            Text("The image of") + Name( word ) 
            + Text("under") + Name( map ) );
  
  ListOf<OID> dependencies( map.getRange() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void SGImageUnderMap::takeControl( )
{
  Map M = map.getMap();
  Word tmpWord;
  VectorOf<Word> gens = subgroup.getSubgroup().generators();
  int VLen = gens.length();
  for( int i = 0; i < VLen; ++i ){
    tmpWord = M.imageOf(gens[i]).freelyReduce();
    if ( map.getRange().gic.isAbelian()==yes)
      tmpWord = AbelianWord( map.getRange().getFPGroup().numberOfGenerators(),
                         tmpWord ).getWord();    
    gens[i] = tmpWord;
  }
      
  SMObject* smo = new SMSubgroup
    ( map.getRange(),
      SGofFreeGroup( map.getRange().getFreePreimage(), gens ),
      Text("The image of") + Name( subgroup ) + Text("under") + Name( map )
      );
  
  ListOf<OID> dependencies( map.getRange() );
  CheckinMessage( *smo, "", dependencies ).send();
}


ExtendFreeByAut::ExtendFreeByAut(const SMMap& m)
: theGroup( m.getDomain() ), theMap( m ) { }


void ExtendFreeByAut::takeControl( )
{
  //@rn  An unfortunate chicken-and-egg problem: to make the SMFPGroup,
  //@rn  we must supply the FPGroup, but we can't do that without the
  //@rn  FreeByCylic, which knows what the generators and relations are.
  //@rn  The real FreeByCylic is held by the GIC, which does not exist
  //@rn  until the SMFPGroup does.
  //@rn  So we make a fake, temp FreeByCylic (jeez).

  FreeByCyclic temp( theGroup.getFreePreimage(), theMap.getMap() );
  
  SMFPGroup* smo =
    new SMFPGroup( FPGroup( temp.namesOfGenerators(), temp.getRelators() ),
               Text("Extension of") + Name(theGroup) + Text("by") + Name(theMap)
               );

  smo->gic.putIsFreeByCyclic( theGroup.getFreePreimage(), theMap.getMap() );

  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies ).send();
}

static 
bool expressAsConjugate( const SetOf<Word>& S, const Word& w, 
                   Word& u, Word& conjugator )
{
  SetIterator<Word> I(S);
  for( ; !I.done(); I.next() ) {
    u = I.value();
    if( u.length() != w.length() )
      continue;
    for( int i = 0; i < u.length(); ++i ) {
      if( w == u.cyclicallyPermute( i ) ) {
      conjugator = u.initialSegment(i);
      return true;
      }
    }
  }
  u = conjugator = Word();
  return false;
}

static 
void printRelatorAsConjugateOfOriginalOne( 
  File& file,  const FPGroup& G, const Word& relator, int relNo )
{
  Word originalRelator, conjugator;
  int power = 1;

  if( !expressAsConjugate( G.getRelators(), relator, 
                     originalRelator, conjugator ) ) {
    power = -1;
    expressAsConjugate( G.getRelators(), relator.inverse(), 
                  originalRelator, conjugator );
    originalRelator = originalRelator.inverse();
  }

  if( conjugator.length() == 0 ) {
    file << "r" << relNo << " is ";
    if( power == -1 ) 
      file << " inverse of ";
    file << "the original relator.";
  }
  else {
    file << "r" << relNo 
       << " can be expressed as a conjugate of the original relator"
       << "as follows:\n      r" << relNo << " = (";
    G.printWord( file, originalRelator );
    file << ") ^ (";
    G.printWord( file, conjugator );
    file << ").";
  }
  file << '\n' << endl;
}

static 
Chars mscInfo( const FPGroup& G )
  // Prints info about structure of the given MSC-presentation 
  // in the file and returns the file name.
{
  File file;
  // 1. Prints symmetrized set of relators.
  SymmetricRelators symRels = G.getRelators();
  SymmetricRelatorsIterator I( symRels );

  file << 
"The symmetrized set of relators for the given presentation is as follows:\n ";
  int n = 0;
  for( ; !I.done(); I.next() ) {
    file << "r" << ++n << " = ";
    G.printWord( file, I.value() );
    file << '\n';
  }
  file << '\n';

  int lambda = G.cancellationLambda();

  if( !lambda ) {
    file << "The relators have no common pieces." << '\n';
  }
  else {
    // 2. Shows the longest common piece.
    Word R1, R2;
    int N1 = 0, N2 = 0;
    int rMinLen = 1, rCommonLen = 0;

    int i = 1;
    for( I.reset(); !I.done(); I.next(), ++i ) {
      Word r1 = I.value();
      SymmetricRelatorsIterator J = I;
      int j = i+1;
      for( J.next(); !J.done(); J.next(), ++j ) {
      Word r2 = J.value();
      int commonLen = r1.agreementLength( r2 );
      int minLen = min( r1.length(), r2.length() );

      if(  commonLen != 0 && minLen * rCommonLen < commonLen * rMinLen ) {
        // found longest relative common piece.
        N1 = i;
        N2 = j;
        R1 = r1;
        R2 = r2;
        rMinLen = minLen;
        rCommonLen = commonLen;
      }
      }
    }
    file << "r" << N1 << " and r" << N2 << " have longest common piece, ";
    G.printWord( file, R1.initialSegment(rCommonLen) );
    file << ".\n";
    
    // 3. Expresses r1 and r2 as conjugates of the original relators.
    file << '\n';
    printRelatorAsConjugateOfOriginalOne( file, G, R1, N1 );
    printRelatorAsConjugateOfOriginalOne( file, G, R2, N2 );
  }

  file << end;

  return file.getFileName();
}

void FPIsMSC::takeControl( )
{
  LogMessage msg( theGroup );

  int lambda = theGroup.getFPGroup().cancellationLambda();

  if ( lambda < 6 && lambda != 0 ) {

    msg << "The presentation of " << Name( theGroup )
      << " is not metric small cancellation; it is only C'(1/"
      << lambda << ')';
    msg.send();
  } else {
    File file;
    
    theGroup.gic.putHaveMSC( theGroup.getFPGroup(), lambda );
    
    msg << "The presentation of " << Name( theGroup ) << " is C'(";
    if ( lambda != 0 ) msg << "1/";
    msg << lambda << "). ";
    msg.send();
    
    LogMessage msg1( theGroup );
    msg1 << Link( Chars("Click here to details about the group presentation"),
              "MSCInfo", mscInfo( theGroup.getFPGroup() ) );      
    msg1.send();
  }
}

void FastHomology::takeControl( )
{
  // We assume that the menu item which causes this to be invoked ensures
  // that the selected group is one-relator, or abelian.
  //@rn The abelian case is not handled yet.

  LogMessage msg( theGroup );
  msg << "The integral homology groups of " << Name( theGroup ) << " are: ";

  // Which case are we doing?

  if ( theGroup.gic.isOneRelatorWithTorsion() ) {
    // We rely on the assumption that the GCM has already checked not only
    // for 1-relatorness, but for torsion.
    
    // Worth the cost to compute max'l root again:
    Word rel = theGroup.gic.getOneRelator();
    int e = maximalRoot( rel );
    Word r = rel.initialSegment( e );
    
    // Dimension 2 is the only special case:
    
    msg << "H_2( " << Name( theGroup ) << " ) = ";
    if ( r.allExponentSumsZero() ) {
      msg << "Z_" << e << ", ";
    } else {
      msg << "0, ";
    }
    
    // All other dimensions:
    
    msg << "H_2n( " << Name( theGroup ) << " ) = 0 for n > 1, ";
    msg << "H_2n-1( " << Name( theGroup ) << " ) = Z_" << e
      << " for n > 1.";

    msg.send();

  } else if ( theGroup.gic.isOneRelator() ) {

    // We rely on the assumption that the GCM has already checked not only
    // for 1-relatorness.

    // Dimension 2 is the only special case:
    
    msg << "H_2( " << Name( theGroup ) << " ) = ";
    if ( theGroup.gic.getOneRelator().allExponentSumsZero() ) {
      msg << "Z, ";
    } else {
      msg << "0, ";
    }
    
    // All other dimensions:

    msg << "H_n( " << Name( theGroup ) << " ) = 0 for n > 2.";

    msg.send();
  } else if ( theGroup.getCheckinType() == SMFPGroup::ABELIAN ) {

    //@rn Temporarily bad case.
    Warning msg;
    msg << "Homology of abelian groups not implemented yet; ignoring request.";
    msg.send();

  } else {
       
    // Bad case.
    Warning msg;
    msg << "Bad static group type passed to FastHomology; ignoring request.";
    msg.send();
  }
}


void SubgroupJoin::takeControl( )
{
  SetOf<Word> S(makeSetOf(subgroup2.getSubgroup().generators()));
  VectorOf<Word> gensS1 = subgroup1.getSubgroup().generators();
  for (int i=0;i<gensS1.length();i++)
    S |= gensS1[i];
  
  SGofFreeGroup SG( subgroup1.getSubgroup().parentGroup(), makeVectorOf(S) );

  SMObject* smo =
    new SMSubgroup( subgroup1.getParent(), SG,
                Text("The join of") + Name( subgroup1 ) + "and"
                + Name( subgroup2 )
                );
  
  ListOf<OID> dependencies( subgroup1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void SubgroupConjugateBy::takeControl( )
{
  VectorOf<Word> result(theSubgroup.getSubgroup().generators());

  for( int i = 0; i < result.length(); ++i ) 
    result[i] = Word( Word(result[i]).conjugateBy(theWord.getWord()) ).freelyReduce();
  
  SGofFreeGroup SG( theSubgroup.getSubgroup().parentGroup(), result );

  SMObject* smo =
    new SMSubgroup( theSubgroup.getParent(), SG,
                Text(Name( theSubgroup )) + Text("conjugated by") + Name( theWord )
                );
  
  ListOf<OID> dependencies( theSubgroup.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}


//--------------------- Abelian Group Computations --------------------------//


void FastAbelianForm::takeControl( )
{
  FPGroup G = theWord.getParent().getFPGroup();
  AbelianGroup A(G);
  LogMessage msg( theWord );
  msg << "The reduced form of " << Name( theWord ) << " is ";
  G.printWord( msg, A.oldInAbelianForm( theWord.getWord() ).getWord() );
  msg << ".";
  msg.send();
}


void FastInverseInAbelianForm::takeControl( )
{
  FPGroup G = theWord.getParent().getFPGroup();
  AbelianGroup A(G);
  LogMessage msg( theWord );
  msg << "The inverse of " << Name( theWord ) << " is ";
  //  << " in abelian form is ";
  G.printWord( msg, 
             A.oldInAbelianForm( theWord.getWord() ).inverse().getWord() );
  msg << ".";
  msg.send();
}


void ProductInAbelianForm::takeControl( )
{
  FPGroup G = word1.getParent().getFPGroup();
  AbelianGroup A(G);
  /*  LogMessage msg( word1, word2 );
      msg << "The product " << Name( word1 ) << " " << Name( word2 ) << " is ";
      G.printWord( msg, A.oldInAbelianForm( word1.getWord() * word2.getWord() )
      .getWord() );
      msg << ".";
      msg.send();
  */

  SMObject* smo =
    new SMWord( word1.getParent(),
            A.oldInAbelianForm( word1.getWord() * word2.getWord() )
            .getWord(),
            Text("The product of") + Name( word1 )
            + Text("and") + Name( word2 ));
  ListOf<OID> dependencies( word1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}

void AbelianSGJoin::takeControl( )
{
  AbelianGroup A(subgroup1.getParent().getFPGroup());
  SGofFreeGroup S2(subgroup1.getParent().getFreePreimage(),
               A.joinSubgroups(subgroup1.getSubgroup().generators(),
                           subgroup2.getSubgroup().generators()));
  SMObject* smo = new SMSubgroup( subgroup1.getParent(),S2,
                          Text("The join of") + Name( subgroup1 ) 
                          + Text("and") + Name( subgroup2 ) );
  ListOf<OID> dependencies( subgroup1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}

void AbelianIsAutomatic::takeControl()
{
  LogMessage("All abelian groups are automatic.").send();
}

void AbelianIsConfluent::takeControl()
{
  LogMessage("All abelian groups have a confluent rewriting system.").send();
}

//----------------------- MSC Group Computations ----------------------------//

void MSCOrder::takeControl( )
{
  if( theGroup.gic.haveOrder() )
    LogMessage(theGroup.gic.getOrderMessage()).send();
  else
    theGroup.gic.putHaveOrder( theGroup.gic.getMSC().order() );
}

void MSCIsTrivial::takeControl( )
{
  bool answer = ( theGroup.gic.getMSC().isTrivial() == yes );
  
  LogMessage msg( *this, theGroup );
  
  msg << Name( theGroup ) << " is";
  
  if( !answer )
    msg << " not";
  
  msg << " trivial.";

  msg.send();
}


void MSCIsFinite::takeControl( )
{
  if( theGroup.gic.isFinite() != dontknow )
    LogMessage(theGroup.gic.getFiniteMessage()).send();
  else {
    bool answer = ( theGroup.gic.getMSC().isFinite() == yes );
    theGroup.gic.putIsFinite(answer);
  }
}


void MSCIsAbelian::takeControl( )
{
  bool answer = ( theGroup.gic.getMSC().isAbelian() == yes );
  
  LogMessage msg( *this, theGroup );
  
  msg << Name( theGroup ) << " is";
  
  if( !answer )
    msg << " not";
  
  msg << " abelian.";

  msg.send();
  if( answer ) theGroup.gic.putIsAbelian();
}


// ------------------ One Relator Group Computations ----------------------- //


void ORIsTrivial::takeControl( )
{
  bool answer = ORProblems( theGroup.getFPGroup().numberOfGenerators(),
                      theGroup.gic.getOneRelator() ).isTrivial();
  
  LogMessage msg( theGroup );
  
  msg << Name( theGroup ) << " is";
  
  if( !answer )
    msg << " not";
  
  msg << " trivial.";

  msg.send();
}


void ORIsAbelian::takeControl( )
{
  bool answer = ORProblems( theGroup.getFPGroup().numberOfGenerators(),
                      theGroup.gic.getOneRelator() ).isAbelian();
  
  LogMessage msg( theGroup );
  
  msg << Name( theGroup ) << " is";
  
  if( !answer )
    msg << " not";
  
  msg << " abelian.";

  msg.send();
  if( answer ) theGroup.gic.putIsAbelian();
}


void ORIsFinite::takeControl( )
{
  if( theGroup.gic.isFinite() != dontknow )
    LogMessage(theGroup.gic.getFiniteMessage()).send();
  else {
    bool answer = ORProblems( theGroup.getFPGroup().numberOfGenerators(),
                        theGroup.gic.getOneRelator() ).isFinite();
    theGroup.gic.putIsFinite( answer );
  }
}

void OROrder::takeControl( )
{
  if( theGroup.gic.haveOrder() )
    LogMessage(theGroup.gic.getOrderMessage()).send();
  else {
    int theOrder = ORProblems( theGroup.getFPGroup().numberOfGenerators(),
                         theGroup.gic.getOneRelator() ).order();
    theGroup.gic.putHaveOrder( theOrder );
  }
}

/*
void ORWithTorsionWordProblem::takeControl()
{
  SMFPGroup& theGroup = theWord.getParent();
  OneRelatorGroupWithTorsion  G( theGroup.getFPGroup().namesOfGenerators(), 
                         theGroup.gic.getOneRelator() );

  ProductOfRelatorConjugates prodDeco;
  Trichotomy trivial = G.wordProblem( theWord.getWord(), prodDeco );

  if( trivial == yes ) {
    DetailedReport report( theGroup.getFPGroup() );
    report.printDehnTransformationDetails( theWord.getWord() );

    LogMessage msg( theWord );
    msg << Name( theWord ) << " is trivial in " << Name( theGroup )
      << " -- this follows by using Dehn's algorithm."
      << Link( Chars("Click here to see details of the computation"),
             "WordProblemDetails", report.getFileName()
             );
    msg.send();
  }
  else if( trivial == no ) {
    LogMessage msg( theWord );
    msg << Name( theWord ) << " is not trivial in " << Name( theGroup ) 
      << " -- this follows by using Dehn's algorithm.";
    msg.send();
  }
  else {
    LogMessage msg( theWord );
    msg << "Internal error -- Magnus cannot determine whether " 
      << Name( theWord ) << " is trivial or not in " << Name( theGroup ) 
      << " by using Dehn's algorithm.";
    msg.send();
  }
}
*/

void ORWithTorsionAreEltsEqual::takeControl()
{
  SMFPGroup& theGroup = theWord1.getParent();
  OneRelatorGroupWithTorsion  G( theGroup.getFPGroup().namesOfGenerators(), 
                         theGroup.gic.getOneRelator() );
  Word elt = (theWord1.getWord()*theWord2.getWord().inverse()).freelyReduce();
  ProductOfRelatorConjugates prodDeco;
  Trichotomy trivial = G.wordProblem( elt, prodDeco );

  if( trivial == yes ) {
    DetailedReport report( theGroup.getFPGroup() );
    report.file() << Name(theWord1) << " * " << Name(theWord2) << "^-1 = ";
    G.printWord( report.file(), elt );
    report.file() << ".\n";
    report.printDehnTransformationDetails( elt );

    LogMessage msg( theWord1, theWord2 );
    msg << Name( theWord1 ) << " is equal to " << Name(theWord2) 
      << " in " << Name( theGroup ) 
      << " -- this follows by using Dehn's algorithm."
      << Link( Chars("Click here to see details of the computation"),
             "WordProblemDetails", report.getFileName()
             );
    msg.send();
  }
  else if( trivial == no ) {
    LogMessage msg( theWord1, theWord2 );
    msg << Name( theWord1 ) << " is not equal to " << Name(theWord2) 
      << " in " << Name( theGroup ) 
      << " -- this follows by using Dehn's algorithm.";
    msg.send();
  }
  else {
    LogMessage msg( theWord1, theWord2 );
    msg << "Internal error -- Magnus cannot determine whether " 
      << Name( theWord1 ) << " is equal to " << Name( theWord2 ) 
      << " -- by using Dehn's algorithm.";
    msg.send();
  }
}


void ORWithTorsionExtendedWordProblem::takeControl()
{
  OneRelatorGroupWithTorsion 
    G( theWord.getParent().getFPGroup().namesOfGenerators(), 
       theWord.getParent().gic.getOneRelator() );

  VectorOf<Word> subgroupWords = theSubgroup.getSubgroup().generators();
  VectorOf<Generator> subgroupGens( subgroupWords.length() );

  for( int i = 0; i < subgroupWords.length(); ++i ) {
    Word gen = subgroupWords[i].freelyReduce();
    if( gen.length() != 1 ) {
      Message msg("Warning");
      msg << "The given subgroup is not a Magnus one.";
      msg.send();
      return;
    }
    subgroupGens[i] = gen[0];
  }

  if( !G.isProperMagnusSubgroup(subgroupGens) ) {
    Message msg("Warning");
    msg << "The given subgroup is not a Magnus one.";
    msg.send();
    return;
  }

  Word wDecomposition;
  bool answer = G.doesMagnusSubgroupContainElt( subgroupGens, 
                                    theWord.getWord(), 
                                    wDecomposition
                                    );

  LogMessage msg( theSubgroup, theWord );

  msg << Name( theWord ) << " is ";
  if ( !answer ) msg << "not ";
  msg << "expressed in generators of " << Name(theSubgroup);
  
  if( answer ) {
    msg << " : " << Name(theWord) << " = ";
    theSubgroup.getSubgroup().printWord( msg, wDecomposition );
  }
  msg << '.';
  msg.send();
  
}


void ORWithTorsionEltFiniteOrder::takeControl()
{
  OneRelatorGroupWithTorsion
    G( theWord.getParent().getFPGroup().namesOfGenerators(), 
       theWord.getParent().gic.getOneRelator() );

  Word w = theWord.getWord();
  Word st, x;
  ProductOfRelatorConjugates wDeco;
  int order = G.powerOfElt( w, st, x, wDeco );
  
  {  
    // Report the order of the element .
    LogMessage msg( theWord );
    msg << Name(theWord);
    if( order ) 
      msg << " has order " << order;
    else
      msg << " has infinite order";
    msg << ".";
    msg.send();
  }

  {
    // Write into secondary logfile a decomposition of the element.
    File file;
    file.setColor( titleColor );
    file << "Decomposition of the word is \n\n";
    file.setColor( mainColor );

    if( st.length() ) {
      if( x.length() ) file << "(";
      G.printWord( file, st );
      if( x.length() ) {
      file << ") ^ (";
      G.printWord( file, x);
      file << ")";
      }
    }
    
    if( wDeco.length() ) {
      
      Word theRelator = G.relator();
      int decoLength = wDeco.length();
      for( int i = 0; i < decoLength; ++i ) {
        file.setColor( titleColor );
        if( i || i == 0 && order != 1 ) 
          file << " * ";
        file << ( wDeco[i].relator == theRelator ? "r^(" : "R^(" );
        file.setColor( mainColor );
        G.printWord( file, wDeco[i].conjugator );
        file.setColor( titleColor );
        file << ')';
      }
    }
    file << '\n'<< end;

    LogMessage msg( theWord );
    msg << Link( Chars("Click here to see decomposition of  ") 
             + Text( Name(theWord) ), "OREltDeco", file.getFileName()
             );      
    msg << '.';
    msg.send();
  }
}

void ORWithTorsionCentralizerOfElt::takeControl()
{
  OneRelatorGroupWithTorsion 
    G( theWord.getParent().getFPGroup().namesOfGenerators(), 
       theWord.getParent().gic.getOneRelator()
     );
  Word w = theWord.getWord();

  LogMessage msg( theWord );
  msg << "The centralizer of " << Name(theWord) 
      << " in one-relator group " << Name(theWord.getParent()) 
      << " with torsion is generated by ";
  G.printVectorOfWords(msg, G.centralizerOfElt( w ));
  msg << ".";
  msg.send();
}


void ORWithTorsionConjugacyProblem::takeControl()
{
  Word theRelator = word1.getParent().gic.getOneRelator();
  OneRelatorGroupWithTorsion 
    G( word1.getParent().getFPGroup().namesOfGenerators(), theRelator );
  
  Word conjugator;
  Trichotomy answer = G.conjugacyProblem( word1.getWord(), word2.getWord(),
                                conjugator );

  LogMessage msg( word1,word2 );
  if ( answer == yes )
    {
      msg << Name( word1 ) << " and  " << Name( word2 ) 
        << " are conjugate in " << Name( word1.getParent() ) << " : ";
      msg << "X " << Name( word1 ) << " x = " <<  Name( word2 ) 
        << ", where x = ";
      word1.getParent().getFPGroup().printWord(msg,conjugator);
    }
  else {
    Word maxRoot;
    int maxPower;
    maximalRootInFreeGroup( theRelator, maxRoot, maxPower );
    if( maxPower == 2 ) {
      msg << "Magnus cannot determine whether " << Name( word1 ) << " and " 
        << Name( word2 ) << " are conjugate or not in " 
        << Name( word1.getParent() ) << " -- a solution can be found for N>2"
        << "where a defining relator R=S^N";
    }
    else {
      msg << Name( word1 ) << " and " << Name( word2 ) 
        << " are not conjugate in " << Name( word1.getParent() );
    }
  }
  
  msg << ".";
  msg.send();

}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//  Make Menu:                                                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


void MakeCyclicDecomposition::takeControl( )
{
  if( !theGroup.gic.haveCyclicDecomposition() ) {

    Message msg;

    msg << 
      "To construct canonical presentation of an abelian group you have"
      " to compute it first. To do so just choose the corresponding item in "
      "the tools menu.";

    msg.send();
  }

  else {

    AbelianGroup CD = theGroup.gic.getCyclicDecomposition()
      .getCanonicalSmithPresentation();
    
    SMFPGroup* smo
      = new SMFPGroup( CD.getFPGroup(),
                   Text("The canonical presentation of")
                   + Name( theGroup ),
                   SMFPGroup::ABELIAN
                   );
    
    smo->gic.putIsAbelian();
    smo->gic.putHaveCanonicalSmithPresentation(CD);
    
    ListOf<OID> dependencies;
    CheckinMessage( *smo, "", dependencies ).send();
    
    VectorOf<AbelianWord> a1 = 
      theGroup.gic.getCyclicDecomposition().oldToNewGens();
    VectorOf<Word> v1( a1.length() );
    for( int i = 0; i < a1.length(); ++i )
      v1[i] = a1[i].getWord();
 
    VectorOf<AbelianWord> a2 = 
      theGroup.gic.getCyclicDecomposition().newToOldGens();
    VectorOf<Word> v2( a2.length() );
    for( int i = 0; i < a2.length(); ++i )
      v2[i] = a2[i].getWord();
    

    SMHomomorphism2* hom1 = new SMHomomorphism2
      ( theGroup, *smo,
      Map( theGroup.getFPGroup(), smo->getFPGroup(), v1 ),
      Text("The isomorphism from generators of") + Name(theGroup)
      + Text("to") + Name(*smo)
      );

    dependencies = ListOf<OID>( theGroup );
    dependencies.append( *smo );
    CheckinMessage( *hom1, "", dependencies ).send();


    SMHomomorphism2* hom2 = new SMHomomorphism2
      ( *smo, theGroup,
      Map(  smo->getFPGroup(), theGroup.getFPGroup(), v2 ),
      Text("The isomorphism from generators of") + Name(*smo)
      + Text("to") + Name(theGroup)
      );

    dependencies = ListOf<OID>( *smo );
    dependencies.append( theGroup );
    CheckinMessage( *hom2, "", dependencies ).send();
  }
}


// ------------------------------ Quotients -------------------------------- //



void MakeQuotientFromSubgroup::takeControl( )
{
  if( theSubgroup.sic.isNormal() == dontknow && theSubgroup.getParent().gic.isAbelian() != yes) {

    Message msg;

    msg << 
      "Please use the subgroup tool in Magnus which determines if a subgroup is normal.";
    msg.send();
  } else if ( theSubgroup.sic.isNormal() == no  && theSubgroup.getParent().gic.isAbelian() != yes) {
     Message msg;
     
     msg << "Subgroup " << Name( theSubgroup ) << " in not normal. Cannot make quotient group " 
       << Name( theSubgroup.getParent()) << "/" << Name(theSubgroup) << ".";
     msg.send(); 
  } else {
    SMFPGroup& pGroup = theSubgroup.getParent();

    SetOf<Word> newRelators( pGroup.getFPGroup().getRelators());
    VectorOf<Word> sGens = theSubgroup.getSubgroup().generators();

    for (int i=0;i<sGens.length();i++)
      newRelators |= sGens[i];

    FPGroup G( pGroup.getFPGroup().namesOfGenerators(),
             newRelators);
    
    Chars text = Text("Quotient group ") + Name( pGroup ) + Text("/") + Name(theSubgroup);
    SMFPGroup* smo;
    
    if( pGroup.gic.isAbelian() == yes ) {
      smo = new SMFPGroup( G, text, SMFPGroup::ABELIAN );
      smo->gic.putIsAbelian();
    }
    else if( pGroup.gic.isNilpotent() == yes ) {
      
      int theClass = pGroup.gic.getNilpotentcyClass();
      char strClass[10];
      sprintf(strClass,"%u", theClass);
      
      smo = new SMFPGroup( G, text, SMFPGroup::NILPOTENT );
      smo->gic.putIsNilpotent( theClass );
    }
    else 
      smo = new SMFPGroup( G, text );
    
    ListOf<OID> dependencies;
    CheckinMessage( *smo, "", dependencies ).send();
    
    
    // Make the homomorphism from the group to its quotient
    
    int numOfGens = pGroup.getFPGroup().numberOfGenerators();
    VectorOf<Word> v(numOfGens);
    for( int i = 0; i < numOfGens; ++i )
      v[i]  = Word(Generator(i+1));
    
    SMHomomorphism2* hom = new SMHomomorphism2
      ( pGroup, *smo, Map( pGroup.getFPGroup(), smo->getFPGroup(), v),
      Text("The homomorphism from generators of") + Name(pGroup)
      + Text("to") + Name(*smo)
      );
    
    dependencies = ListOf<OID>( pGroup );
    dependencies.append( *smo );
    CheckinMessage( *hom, "", dependencies ).send();
    
  }
}

void MakeAbelianQuotient::takeControl( )
{
  SMFPGroup* smo
    = new SMFPGroup( theGroup.getFPGroup(),
                 Text("User defined abelian quotient of")
                 + Name( theGroup ),
                 SMFPGroup::ABELIAN
                 );
  
  smo->gic.putIsAbelian();

  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies ).send();


  // Make the homomorphism from the group to its quotient

  int numOfGens = theGroup.getFPGroup().numberOfGenerators();
  VectorOf<Word> v(numOfGens);
  for( int i = 0; i < numOfGens; ++i )
    v[i]  = Word(Generator(i+1));

  SMHomomorphism2* hom = new SMHomomorphism2
    ( theGroup, *smo, Map( theGroup.getFPGroup(), smo->getFPGroup(), v),
      Text("The homomorphism from generators of") + Name(theGroup)
      + Text("to") + Name(*smo)
      );
  
  dependencies = ListOf<OID>( theGroup );
  dependencies.append( *smo );
  CheckinMessage( *hom, "", dependencies ).send();
}


void MakeNilpotentQuotient::takeControl( )
{
  if( ( theGroup.gic.isNilpotent() == yes 
      || theGroup.gic.isFreeNilpotent() == yes ) 
      && theClass >= theGroup.gic.getNilpotentcyClass( ) ) {

    Message msg;
    msg << "This number is bigger than the class of " 
      << Name( theGroup ) << "."; 
    msg.send();
    return;
  }

  char strClass[10];
  sprintf(strClass,"%u",theClass);
  
  SMFPGroup* smo
    = new SMFPGroup( theGroup.getFPGroup(),
                 Text("User defined nilpotent quotient of class")
                 + Text(strClass) + Text("of") + Name( theGroup ),
                 SMFPGroup::NILPOTENT
                 );
  
  smo->gic.putIsNilpotent( theClass );
  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies ).send();


  // Make the homomorphism from the group to its quotient

  int numOfGens = theGroup.getFPGroup().numberOfGenerators();
  VectorOf<Word> v(numOfGens);
  for( int i = 0; i < numOfGens; ++i )
    v[i]  = Word(Generator(i+1));

  SMHomomorphism2* hom = new SMHomomorphism2
    ( theGroup, *smo, Map( theGroup.getFPGroup(), smo->getFPGroup(), v),
      Text("The homomorphism from generators of") + Name(theGroup)
      + Text("to") + Name(*smo)
      );
  
  dependencies = ListOf<OID>( theGroup );
  dependencies.append( *smo );
  CheckinMessage( *hom, "", dependencies ).send();
}


void MakeQuotient::takeControl( )
{
  FPGroup G( theGroup.getFPGroup().namesOfGenerators(),
           theGroup.getFPGroup().getRelators());
  G.addRelators(relators);

  Chars text = Text("User defined quotient of") + Name( theGroup );
  SMFPGroup* smo;
  
  if( theGroup.gic.isAbelian() == yes ) {
    smo = new SMFPGroup( G, text, SMFPGroup::ABELIAN );
    smo->gic.putIsAbelian();
  }
  else if( theGroup.gic.isNilpotent() == yes ) {
    
    int theClass = theGroup.gic.getNilpotentcyClass();
    char strClass[10];
    sprintf(strClass,"%u", theClass);
    
    smo = new SMFPGroup( G, text, SMFPGroup::NILPOTENT );
    smo->gic.putIsNilpotent( theClass );
  }
  else 
    smo = new SMFPGroup( G, text );
  
  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies ).send();


  // Make the homomorphism from the group to its quotient

  int numOfGens = theGroup.getFPGroup().numberOfGenerators();
  VectorOf<Word> v(numOfGens);
  for( int i = 0; i < numOfGens; ++i )
    v[i]  = Word(Generator(i+1));

  SMHomomorphism2* hom = new SMHomomorphism2
    ( theGroup, *smo, Map( theGroup.getFPGroup(), smo->getFPGroup(), v),
      Text("The homomorphism from generators of") + Name(theGroup)
      + Text("to") + Name(*smo)
      );
  
  dependencies = ListOf<OID>( theGroup );
  dependencies.append( *smo );
  CheckinMessage( *hom, "", dependencies ).send();
}


// ------------------------------ Products --------------------------------- //


void MakeAPOfFree::takeControl( )
{
  SGofFreeGroup S1 = subgroup1.getSubgroup( );
  SGofFreeGroup S2 = subgroup2.getSubgroup( );

  if( S1.generators().length() != S2.generators().length() ) {
    Message msg;
    msg << "The number of generators of the subgroups has to be equal.";
    msg.send();
    return;
  }

  if( S1.nielsenBasis().length() != S2.nielsenBasis().length() ) {
    Message msg;
    msg << "The associated subgroups must have the same rank.";
    msg.send();
    return;
  }
  
  SMFPGroup* smo;

  
  if( S1.generators().length() == 1 ) {

    APwithOneRelator AP(S2, S1);

    smo = new SMFPGroup
      ( AP,
      Text("User defined amalgamated product <")
      + Name(Parent(subgroup2)) + Text("*") + Name(Parent(subgroup1)) + Text(";")
      + Name(subgroup2) + Text("=") + Name(subgroup1) + Text(">"),
      SMFPGroup::AP_FREE_CYCLIC
      );

    smo->gic.putHaveAPOfFree(AP);
  }
  else {

    AmalgProductOfFreeGroups AP(S2, S1);

    smo = new SMFPGroup
    ( AP,
      Text("User defined amalgamated product <")
      + Name(Parent(subgroup2)) + Text("*") + Name(Parent(subgroup1)) + Text(";")
      + Name(subgroup2) + Text("=") + Name(subgroup1) + Text(">"),
      SMFPGroup::AP_FREE
      );

    smo->gic.putHaveAPOfFree(AP);
  }
 
  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies ).send();
}


void APOfFreeReducedForm::takeControl( )
{
  FPGroup G = theWord.getParent().getFPGroup();
  VectorOf<Word> dec = theWord.getParent().gic.getAPOfFree()
    .reducedDecomposition(theWord.getWord());
  
  LogMessage msg( theWord );
  msg << "The reduced form of " << Name( theWord ) << " is ";
  G.printVectorOfWords(msg, dec);
  msg << ".";
  msg.send();
}


void APOfFreeNormalForm::takeControl( )
{
  FPGroup G = theWord.getParent().getFPGroup();
  VectorOf<Word> dec = theWord.getParent().gic.getAPOfFree()
    .normalDecomposition(theWord.getWord());
  
  LogMessage msg( theWord );
  msg << "The normal form of " << Name( theWord ) << " is ";
  G.printVectorOfWords(msg, dec);
  msg << ".";
  msg.send();
}


void APOfFreeCyclicNormalForm::takeControl( )
{
  FPGroup G = theWord.getParent().getFPGroup();
  VectorOf<Word> dec;
  Word conjugator;
  
  theWord.getParent().gic.getAPOfFree()
    .cyclicDecomposition(theWord.getWord(), dec, conjugator);
  
  LogMessage msg( theWord );
  msg << "The cyclic normal form of " << Name( theWord ) << " is ";
  G.printVectorOfWords(msg, dec);
  msg << ".";
  msg.send();
}


void APOfFreeIsTrivial::takeControl( )
{
  bool answer = ( theGroup.gic.getAPOfFree().isTrivial() == yes );
  
  LogMessage msg( *this, theGroup );
  msg << Name( theGroup ) << " is";
  if( !answer )
    msg << " not";
  msg << " trivial.";

  msg.send();
}

void APOfFreeIsHyperbolic::takeControl( )
{
  AmalgProductOfFreeGroups G = theGroup.gic.getAPOfFree();
  Trichotomy answer = G.isHyperbolic();
  
  LogMessage msg( *this, theGroup );
  
  if( answer == dontknow )
    msg << "Magnus cannot determine whether " << Name( theGroup ) << " is hyperbolic.";
  else {
    msg << Name( theGroup );
    if( answer == yes ) 
      msg << " is hyperbolic because at least one of the associated subgroups is malnormal.";
    else
      msg << " is not hyperbolic.";
  }

  msg.send();
}

void APOfFreeIsFinite::takeControl( )
{
  bool answer = ( theGroup.gic.getAPOfFree().isFinite() == yes );
  
  LogMessage msg( *this, theGroup );
  msg << Name( theGroup ) << " is ";
  if( !answer )
    msg << "in";
  msg << "finite.";

  msg.send();
}


void APOfFreeIsAbelian::takeControl( )
{
  bool answer = ( theGroup.gic.getAPOfFree().isAbelian() == yes );
  
  LogMessage msg( *this, theGroup );
  msg << Name( theGroup ) << " is";
  if( !answer )
    msg << " not";
  msg << " abelian.";

  msg.send();
}


void APOfFreeOrder::takeControl( )
{
  if( theGroup.gic.haveOrder() )
    LogMessage(theGroup.gic.getOrderMessage()).send();
  else
    theGroup.gic.putHaveOrder( theGroup.gic.getAPOfFree().order() );
}


void APOfFreeWordProblem::takeControl( )
{
  bool answer = theWord.getParent().gic.getAPOfFree()
    .wordProblem( theWord.getWord() );

  LogMessage msg( theWord );

  msg << Name( theWord ) << " is";
  if( !answer )
    msg << " not";
  msg << " trivial";
  msg << " in " << Name( Parent( theWord ) ) << ".";

  msg.send();
}


void APOfFreeNumberOfSubstitutions::takeControl( )
{
  LogMessage msg( theWord );

  FPGroup G = theWord.getParent().getFPGroup();
  AmalgProductOfFreeGroups AP( theWord.getParent().gic.getAPOfFree() );

  if( AP.wordProblem( theWord.getWord() ) ) {

    int numOfSubsts = AP.numberOfSubstitutions( theWord.getWord() );    
    msg << "The number of uses of a relation a = b  to deduce that " 
      << Name( theWord ) << " = 1 in " << Name( theWord.getParent() ) 
      << " is " << numOfSubsts;
  }
  else {
    msg << "The word " << Name( theWord ) << " is not represent 1 in "
      << Name( theWord.getParent() );
  }
  msg << ".";

  msg.send();
}



void APOfFreeAreEqual::takeControl( )
{
  bool answer = word1.getParent().gic.getAPOfFree()
    .areEqual( word1.getWord(), word2.getWord() );
  
  LogMessage msg( word1, word2 );

  msg << Name( word1 ) << " is";
  if ( !answer )
    msg << " not";
  msg << " equal to " << Name( word2 )
    // @am added explanation
      << ": they have different normal forms"
      << ".";

  msg.send();
}


void APOfFree_DoEltsCommute::takeControl( )
{
  bool answer = word1.getParent().gic.getAPOfFree()
    .commute( word1.getWord(), word2.getWord() );
  
  LogMessage msg( word1, word2 );

  msg << Name( word1 );
  if ( !answer )
    msg << " does not commute";
  else
    msg << " commutes";
  msg << " with " << Name( word2 ) << ".";

  msg.send();
}


void APOfFreeIsSGTrivial::takeControl( )
{
  bool answer = subgroup.getParent().gic.getAPOfFree()
    .isSubgroupTrivial( subgroup.getSubgroup().generators() );

  LogMessage msg( subgroup );

  msg << Name( subgroup ) << " is";

  if ( !answer )
    msg << " not";
  msg << " trivial in " << Name( Parent( subgroup ) ) << ".";
  
  msg.send();
}


void CheckinAPOfFree::takeControl( )
{
  Message msg;
  msg << 
    "Check-in the two factors and their respective subgroups, which must "
    "have the same number of generators. The generators of the two "
    "subgroups must be arranged so that the mapping which sends the n-th "
    "generator of the first subgroup to the n-th generator of the second "
    "subgroup defines an isomorphism between them. Then go to the Make "
    "menu. (Note: amalgamated products are, as of now, only implemented "
    "when the factors are checked in as free.)";
  msg.send();
}


void APOfFreeIsSGAbelian::takeControl( )
{
  bool answer =
    ( theSubgroup.getParent().gic.getAPOfFree()
      .isSubgroupAbelian( theSubgroup.getSubgroup().generators() ) );
  
  LogMessage msg( *this, theSubgroup );
  msg << Name( theSubgroup ) << " is";
  if( !answer )
    msg << " not";
  msg << " abelian.";

  msg.send();
}


void APOfFreeCyclic_CentralizerOfElt::takeControl( )
{
  FPGroup G = theWord.getParent().getFPGroup();
  APwithOneRelator AP( theWord.getParent().gic.getAPOfFree() );
  VectorOf<Word> cent = AP.centralizerOf( theWord.getWord() );
  
  LogMessage msg( theWord );
  msg << "The centralizer of " << Name( theWord ) << " is gp ";
  G.printVectorOfWords(msg, cent, "(", ")");
  msg << ".";
  msg.send();
}

void APOfFreeCyclic_ConjugacyProblem::takeControl( )
{
  Word conjugator;
  APwithOneRelator AP( word1.getParent().gic.getAPOfFree() );
  Trichotomy answer =
    AP.conjugacyProblem( word1.getWord(), word2.getWord(), conjugator );

  LogMessage msg( word1, word2 );

  if( answer == dontknow )

    msg << "Magnus cannot determine whether " << Name( word1 ) << " and "
      << Name( word2 ) << " are conjugate or not";

  else {
    msg << Name( word1 ) << " and " << Name( word2 ) << " are";

    if( answer == no )
      msg << " not conjugate";
    else {
      msg << " conjugate: " << "X " << Name( word1 ) << " x = "
        << Name( word2 ) << ", where x = ";
      word1.getParent().getFPGroup().printWord( msg, conjugator );
    }
  }

  msg << ".";
  msg.send();
}


void APOfFreeCyclic_MaximalRoot::takeControl( )
{
  Word root;
  int power;

  APwithOneRelator AP( theWord.getParent().gic.getAPOfFree() );
  AP.maximalRoot( theWord.getWord(), root, power );

  SMObject* smo =
    new SMWord( theWord.getParent(), root,
            Text("The maximal root of") + Name( theWord )
            );
  
  ListOf<OID> dependencies( theWord.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
}


void APOfFreeCyclic_IsEltAProperPower::takeControl( )
{
  Word root;
  int power;

  APwithOneRelator AP( theWord.getParent().gic.getAPOfFree() );
  AP.maximalRoot( theWord.getWord(), root, power );

  LogMessage msg( theWord );
  msg << Name( theWord );

  if ( power > 1 ) {
    msg << " is a proper power. " << Name( theWord ) << " is equal ";
    theWord.getParent().getFPGroup().printWord(msg, root);
    msg << " in power " << power;
  }
  else 
    msg << " is not a proper power";
  
  msg << ".";

  msg.send();
}


void APOfFreeCyclic_IsEltAProperPowerOfSecond::takeControl( )
{
  int power;
  APwithOneRelator AP( word1.getParent().gic.getAPOfFree() );
  bool answer = AP.isProperPowerOfSecond( word1.getWord(), word2.getWord(), power );

  LogMessage msg( word1, word2 );

  msg << Name( word1 ) << " is";
  if ( !answer )
    msg << " not";
  msg << " a proper power of " << Name( word2 ) << ".";
  if( answer )
    msg << Name( word1 ) << " is equal " << Name( word2 ) << " in power " << power << ".";
  msg.send();
}

//---------------------------------------------------------------------------//
//--------------------- Nilpotent  Group Computations -----------------------//
//---------------------------------------------------------------------------//


void FNGAutoIsIAAut::takeControl( )
{
  LogMessage msg( map );
  msg << Name( map ) << " is ";
  int theNumberOfGenerators = map.getDomain().getFPGroup().numberOfGenerators();
  VectorOf<Word> V =  map.getMap().generatingImages();
  bool answer = true;

  for( int i = theNumberOfGenerators-1; i >= 0; --i ) {
    AbelianWord tmp( theNumberOfGenerators, inv(Generator(i+1)) * V[i] );
    if( tmp.fullLength() != 0 )
      answer = false;
  }
  
  if( !answer )
    msg << "not ";

  msg << "an IA-automorphism of " << Name( Domain( map ) ) << ".";
  msg.send();
}

void SGOfNGjoinSubgroupProblem::takeControl( )
{
  FPGroup theGroup = subgroup1.getParent().getFPGroup();
  NilpotentGroup group(theGroup.namesOfGenerators(),
                   subgroup1.getParent().gic.getNilpotentcyClass(),
                   makeVectorOf(theGroup.getRelators()));

  SGOfNilpotentGroup SG1(group, subgroup1.getSubgroup().generators());
  VectorOf<Word> SG2 = subgroup2.getSubgroup().generators();

  VectorOf<Word> basis = SG1.join(SG2);
  SGofFreeGroup F(subgroup1.getParent().getFreePreimage(), basis);

  SMObject* smo = 
    new SMSubgroup( subgroup1.getParent(), F,
                Text("The join of") + Name( subgroup1 ) + Text("and")
                + Name( subgroup2 )
                );

  ListOf<OID> dependencies( subgroup1.getParent() );
  CheckinMessage( *smo, "", dependencies ).send();
  
}

void NGLCStermGensProblem::takeControl( )
{
  if (number > group.gic.getNilpotentcyClass()){
    Message msg;
    msg << "Number of quotient is bigger than nilpotency class of the group.";
    msg.send();
    return;
  }
  FPGroup theGroup = group.getFPGroup();
  NilpotentGroup ng(theGroup.namesOfGenerators(),
                   group.gic.getNilpotentcyClass(),
                   makeVectorOf(theGroup.getRelators()));

  VectorOf<Word> lcsTerm = ng.getLCSterm( number );
  SGofFreeGroup F(group.getFreePreimage(),lcsTerm);

  SMObject* smo = 
    new SMSubgroup( group, F,
                Text("The ") + Text(Chars(number)+ordinalPostfix(number))+
                Text(" term of the lower central series of ") + Name( group ) 
                );

  ListOf<OID> dependencies( group );
  CheckinMessage( *smo, "", dependencies ).send();
  
}
// ------------------------------------------------------ //
// --------------- Products ------------------------------//
// ------------------------------------------------------ //
MakeFreeProduct::MakeFreeProduct( class SMFPGroup& g1, class SMFPGroup& g2) 
  : group1(g1), group2(g2), 
    fp( NULL ),
    fpGroup1(g1.getFPGroup()), fpGroup2(g2.getFPGroup()),
    isAbelian( false )
{
  if (g1.gic.isNilpotent() == yes || g1.gic.isFreeNilpotent() == yes ||
      g2.gic.isNilpotent() == yes || g2.gic.isFreeNilpotent() == yes )
    error("MakeFreeProduct( class SMFPGroup& g1, class SMFPGroup& g2) : "
        "can not compute products of nilpotent groups");
  
  if (g1.gic.isAbelian() == yes && g2.gic.isAbelian() == yes ){
    isAbelian = true;
    return;
  }
  
  if (g1.gic.isAbelian() == yes &&  fpGroup1.numberOfGenerators() > 0 ){
    CommutatorIterator I( fpGroup1.numberOfGenerators() , 2 );
    SetOf<Word> relators;
    for (I.reset();!I.done();I.next()){
      if (I.value().length() > 0)
      relators |= I.value();
    }    
    fpGroup1.addRelators( relators );
  } 
  
  if (g2.gic.isAbelian() == yes &&  fpGroup2.numberOfGenerators() > 0){
    
    CommutatorIterator I( fpGroup2.numberOfGenerators() , 2 );
    SetOf<Word> relators;
    
    for (I.reset();!I.done();I.next()){
      if (I.value().length() > 0)
      relators |= I.value();
    }
    fpGroup2.addRelators( relators );
    
  } 

}
void MakeFreeProduct::makeHomomorphisms(SMFPGroup* smo)
{
  SMHomomorphism2* hom1 = new SMHomomorphism2
    ( group1, *smo, fp->mapFromG1(),
      Text("The homomorphism from generators of") + Name(group1)
      + Text("to") + Name(*smo)
      );

     ListOf<OID> dependencies = ListOf<OID>( group1 );
    dependencies.append( *smo );
    CheckinMessage( *hom1, "", dependencies ).send();

    SMHomomorphism2* hom2 = new SMHomomorphism2
      ( group2, *smo, fp->mapFromG2(),
      Text("The homomorphism from generators of") + Name(group2)
      + "to" + Name(*smo)
      );
    
    dependencies = ListOf<OID>( group2 );
    dependencies.append( *smo );
    CheckinMessage( *hom2, "", dependencies ).send();

    SMHomomorphism2* hom3 = new SMHomomorphism2
      ( *smo, group1, fp->mapToG1(),
      Text("The homomorphism from generators of") + Name(*smo)
      + "to" + Name(group1)
      );
    
    dependencies = ListOf<OID>( *smo );
    dependencies.append( group1 );
    CheckinMessage( *hom3, "", dependencies ).send();

    SMHomomorphism2* hom4 = new SMHomomorphism2
      ( *smo, group2, fp->mapToG2(),
      Text("The homomorphism from generators of") + Name(*smo)
      + "to" + Name(group2)
      );
    
    dependencies = ListOf<OID>( *smo );
    dependencies.append( group2 );
    CheckinMessage( *hom4, "", dependencies ).send();  
}

void MakeFreeProduct::takeControl( )
{

  fp = new FreeProduct(fpGroup1,fpGroup2);

  SMFPGroup::Checkin_Type productType = SMFPGroup::FP;
  if (isAbelian)
    productType = SMFPGroup::ABELIAN;
  
  SMFPGroup* smo = 
    new   SMFPGroup(*fp,   Text("Free product ") + Name( group1 )
                + Text(" * ")+Name( group2 ),productType );
  

  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies ).send();

  makeHomomorphisms( smo );

}

void MakeDirectProduct::takeControl( )
{

  fp = new DirectProduct(fpGroup1,fpGroup2);
  
  SMFPGroup::Checkin_Type productType = SMFPGroup::FP;
  if (isAbelian)
    productType = SMFPGroup::ABELIAN;

  SMFPGroup* smo = 
    new   SMFPGroup(*fp,   Text("Direct product"), 
                productType,
                Text(Name( group1 )) + Text(" X ") + Name( group2 )
                ); 
  
  
  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies ).send();
  
  File file;
  FPGroup G = *fp;
  file << G << '\n';

  LogMessage msg( *smo );
  msg << Link( Chars("Click here to see presentation of ") + 
             Text( Name( *smo) ),
             "GroupPresentation",
             file.getFileName() 
             );
  msg << ".";
  msg.send();

  makeHomomorphisms( smo );

}

void MakeFactorGroup::takeControl( )
{
  FreeGroup F = theGroup.getFreePreimage();
  SGofFreeGroup H = theSubgroup.getSubgroup();

  if( theGroup.getCheckinType() == SMFPGroup::FREE && ! H.isNormal() 
      || theGroup.getCheckinType() != SMFPGroup::FREE 
         && theSubgroup.sic.isNormal() == dontknow ) {
    Message msg;
    msg << "Please first check whether the subgroup "
      << Name(theSubgroup) << " is normal or not.";
    msg.send();
    return;
  }

  FPGroup G( F.namesOfGenerators(), makeSetOf(H.generators()) );

  SMFPGroup::Checkin_Type productType = SMFPGroup::FP;
  if( theGroup.gic.isAbelian() == yes )
    productType = SMFPGroup::ABELIAN;

  SMFPGroup* smo = 
    new SMFPGroup( G, Text("Factor group"), 
               SMFPGroup::FP,
               Text(Name( theGroup )) + Text("/") + Name( theSubgroup )
               ); 
  
  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies ).send();
  
  File file;
  file << G << '\n';

  LogMessage msg( *smo );
  msg << Link( Chars("Click here to see presentation of ") + 
             Text( Name( *smo) ),
             "GroupPresentation",
             file.getFileName() 
             );
  msg << ".";
  msg.send();

}



// -------------------- SMList ------------------------ //

void MakeListOfWords::takeControl()
{
  
  SMList<Word>* smo = 
    new   SMList<Word>(enumerator,
                   Text("List of words from ") + 
                   Name(enumerator)
                   ); 
  
  
  ListOf<OID> dependencies( smo->getGroup() );
  CheckinMessage( *smo, "", dependencies ).send();
  
  LogMessage msg( *smo );
  msg << Link( Chars("Click here to see the contents of ") + 
             Text( Name( *smo) ),
             "WEnumerator",
             smo->getData().getDataFileName() 
             );
  msg.send();
     
}


// --------------------------- RipsConstruction ---------------------------- //


void MakeRipsConstruction::takeControl( )
{
  File f;
  FPGroup G = RipsConstruction(theGroup.getFPGroup())
    .getRipsConstruction(&f);

  if( G.numberOfGenerators() == 0 ) {
    Message msg;
    msg << "Cannot make the Rips construction: no free letters available";
    msg.send();
    return;
  }

  SMFPGroup* smo = 
    new SMFPGroup (G, Text("User defined the Rips construction of") 
               + Name( theGroup ), SMFPGroup::FP);
  
  ListOf<OID> dependencies;
  CheckinMessage( *smo, "", dependencies, false ).send();
  
  LogMessage msg( *this, *smo );
  msg << Link( Chars("Click here to see the relators of ") + 
             Text( Name( *smo) ), "RipsConstruction", f.getFileName() );
  msg.send();
}

Generated by  Doxygen 1.6.0   Back to index