GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
############################################################################# ## #W Whitehd.gi FGA package Christian Sievers ## ## Computations with Whitehead automorphisms ## #Y 2004 - 2012 ## InstallMethod( FGA_WhiteheadAutomorphisms, "for finitely generated free groups", [ CanComputeWithInverseAutomaton ], function( G ) local ngens, ngen, combs, auts, L, R; ngens := [ 1 .. RankOfFreeGroup( G ) ]; auts := []; for ngen in ngens do combs := Combinations( Difference( ngens, [ngen] )); for L in combs do for R in combs do if L <> [] or R <> [] then Add( auts, FGA_WhiteheadAutomorphism( G, ngen, L, R )); fi; od; od; od; return auts; end ); InstallMethod( FGA_NielsenAutomorphisms, "for finitely generated free groups", [ CanComputeWithInverseAutomaton ], G -> Filtered( f -> FGA_WhiteheadParams(f).isnielsen ) ); InstallGlobalFunction( FGA_WhiteheadAutomorphism, function( G, ngen, L, R ) local gens, gen, ng, g, img, imginv, imgs, imgsinv, aut, autinv; imgs := []; imgsinv := []; gens := GeneratorsOfGroup( G ); gen := gens[ngen]; for ng in [ 1 .. RankOfFreeGroup( G ) ] do img := gens[ng]; imginv := img; if ng in L then img := LeftQuotient( gen, img ); imginv := gen * imginv; fi; if ng in R then img := img * gen; imginv := imginv / gen; fi; Add( imgs, img ); Add( imgsinv, imginv); od; aut := GroupHomomorphismByImagesNC( G, G, GeneratorsOfGroup(G), imgs ); autinv := GroupHomomorphismByImagesNC( G, G, GeneratorsOfGroup(G), imgsinv ); SetInverse( aut, autinv ); SetInverse( autinv, aut ); SetFGA_WhiteheadParams( aut , rec( gen := ngen, L := L, R := R, isnielsen := Length(L)+Length(R)=1 ) ); SetFGA_WhiteheadParams( autinv, true ); return aut; end ); InstallGlobalFunction( FGA_WhiteheadAnalyse, function( whs, elm, act , len , val, comb , combrest ) # [w] * e * (e*w->e) * (e->Int) * v * (v*w->v) * (v*e->r) -> r local l, newl, wh, bestwh , newelm, bestnewelm; # Int , w , Maybe w, e l := len( elm ); while true do bestwh := fail; for wh in whs do newelm := act( elm, wh ); newl := len( newelm ); if newl < l then l := newl; bestwh := wh; bestnewelm := newelm; fi; od; if bestwh=fail then return combrest( val, elm ); fi; val := comb( val, bestwh ); elm := bestnewelm; od; # not reached end ); ######################################################################## # Equation numbers and pages refer to # Jakob Nielsen: Die Isomorphismengruppe der freien Gruppen # see ../doc/manual.bib ######################################################################## InstallGlobalFunction( FGA_WhiteheadToPQOU, function ( w , p , q , o , u ) # w * g * g * g * g -> g local n ,g, whp, word, sign, nik; n := RankOfFreeGroup( Source ( w ) ); if FGA_WhiteheadParams(w) = true then w := Inverse(w); sign := -1; else sign := 1; fi; whp:= FGA_WhiteheadParams(w); word := One(p); for g in [ 1 .. n ] do if g in whp.L or g in whp.R then # using and possibly combining eq. (12) and (11) # for V_{g,gen}^-1 and U_{g,gen} nik := FGA_NikToPQ( g, whp.gen, p, q ); word := word * nik^-1; if g in whp.L then word := word * o * u^sign * o; # eq. (7) fi; if g in whp.R then word := word * u^sign; fi; word := word * nik; fi; od; return word; end ); InstallGlobalFunction( FGA_NikToPQ, function( i , k , p , q ) # Int * g * g * g -> g # eq. (8) local l; l := k-i; if i<k then l := l-1; fi; return (q*p)^l * q^(i-1); end ); InstallGlobalFunction( FGA_TiToPQ, function( i , p , q ) # Int * g * g -> g # follows from eq. at the middle of page 171 return q^(2-i)*p*(q*p)^(i-2); end ); InstallGlobalFunction( FGA_ExtSymListRepToPQO, function( target, p , q , o ) # [Int] * g * g * g -> g local rank, word1, word2, lastshift, i, t, f2, P, Q, Pperm, Qperm, homperm, homrep, perm; f2 := FreeGroup("P","Q"); P := f2.1; Q := f2.2; word1 := One(p); word2 := word1; rank := Length( target ); Pperm := (1,2); Qperm := PermList(Concatenation([2..rank],[1])); homperm := GroupHomomorphismByImagesNC( f2, SymmetricGroup( rank ), GeneratorsOfGroup(f2), [ Pperm, Qperm ] ); homrep := GroupHomomorphismByImagesNC( f2, Group( p, q ), GeneratorsOfGroup( f2 ), [ p, q ] ); # first get rid of extendedness, using o and q lastshift := 1; for i in [ 1 .. rank ] do if not IsPosInt( target[i] ) then word1 := word1 * q^(lastshift-i) * o; lastshift := i; target[i] := AbsInt(target[i]); fi; od; word1 := word1 * q^(lastshift-1); # now target is a permutation, represent it as such target := SortingPerm(target); # decompose it as product of powers of T_i, compare p. 171 while not IsOne( target ) do i := LargestMovedPoint( target ); t := i^target; perm := FGA_TiToPQ( i, P, Q ); word2 := (perm^homrep)^(t-i) * word2; target := target * (perm^homperm)^(i-t); od; return word1*word2; end ); InstallGlobalFunction( FGA_CurryAutToPQOU, function( p, q, o, u) return function( aut ) local fg, words, wh; fg := Source( aut ); words := List( GeneratorsOfGroup( fg ), gen -> gen ^ aut ); wh := FGA_WhiteheadAutomorphisms( fg ); # use Nielsen generators only wh := Filtered( wh, f -> FGA_WhiteheadParams(f).isnielsen ); wh := Concatenation( wh, List( wh, Inverse )); return FGA_WhiteheadAnalyse( wh, words, OnTuples, l -> Sum( l, Length ), One( p ), function( v, w ) return FGA_WhiteheadToPQOU( Inverse(w), p, q, o, u ) * v; end, function( v, e ) e := List( e, g -> LetterRepAssocWord(g)[1] ); return FGA_ExtSymListRepToPQO( e, p, q, o ) * v; end ); end; end );