########################################################################
#
#  Software for "Roots of Dehn twists"
#
#     by Darryl McCullough and Kashyap Rajeevsarathy
#
#  Version of May 22, 2009
#
#  Contact:  dmccullough@math.ou.edu
#
#  Written for GAP 4.4.
#
########################################################################
#
#  The output appears on the console and is also written to a file 
#  named  roots.out . To change the name of the output file, alter 
#  the String constant in the next command. 

outputfile := String("roots.out");

#
########################################################################
#
#  To find the Margalit-Schleimer roots for a given value of g:
#  
#  gap> MSRoots( 17 );
#  For g = 17 there are 8 Margalit-Schleimer roots:
#  ( 35, 0, ( 2, 2 ); ( 31, 35 ) )
#  ( 35, 0, ( 3, 19 ); ( 13, 35 ) )
#  ( 35, 0, ( 4, 13 ); ( 18, 35 ) )
#  ( 35, 0, ( 9, 23 ); ( 3, 35 ) )
#  ( 35, 0, ( 12, 17 ); ( 6, 35 ) )
#  ( 35, 0, ( 18, 34 ); ( 18, 35 ) )
#  ( 35, 0, ( 24, 33 ); ( 13, 35 ) )
#  ( 35, 0, ( 27, 32 ); ( 11, 35 ) )
#  
#  To find the number of Margalit-Schleimer roots for a given value of g:
#
#  gap> NumMSRoots(1);   
#  1
#  gap> NumMSRoots(2);   
#  2
#  gap> NumMSRoots(1108);
#  369
#  
#  To find the number of Margalit-Schleimer roots for the values of
#  g in the interval [r, r+s], use NumMSRootsRange( r, s ):
#
#  gap> NumMSRootsRange(1,4);   
#  NumMSRoots( 1 ) = 1
#  NumMSRoots( 2 ) = 2
#  NumMSRoots( 3 ) = 3
#  NumMSRoots( 4 ) = 2
#  NumMSRoots( 5 ) = 5
#  gap> NumMSRootsRange(1105,3);
#  NumMSRoots( 1105 ) = 293
#  NumMSRoots( 1106 ) = 1106
#  NumMSRoots( 1107 ) = 662
#  NumMSRoots( 1108 ) = 369
#
########################################################################
#
#  For degrees and genera of specific ( d, e ) roots:
#
#  gap> N(3,5);
#  15
#  gap> G(3,5);
#  11
#  gap> N(3,35);
#  105
#  gap> N(15,7);
#  105
#  gap> G(3,35);
#  86
#  gap> G(15,7);
#  94
#
#  To find the degrees of all (d,e) roots for a given g (i. e. on the
#  surface of genus g+1):
#
#  gap> DERoots( 53135 ); 
#  [ 53141, 53361, 55671 ]
#
#  To find them in the range m <= g <= m + n:
#
#  gap> DERootsRange(101,19);
#  DERoots( 101 ) = [ 105, 115, 123 ]
#  DERoots( 102 ) = [ 103, 105, 123 ]
#  DERoots( 103 ) = [ 105, 115 ]
#  DERoots( 104 ) = [ 105 ]
#  DERoots( 105 ) = [  ]
#  DERoots( 106 ) = [ 107, 117, 129 ]
#  DERoots( 107 ) = [ 119, 129 ]
#  DERoots( 108 ) = [ 109 ]
#  DERoots( 109 ) = [ 111, 117 ]
#  DERoots( 110 ) = [ 111, 117, 119 ]
#  DERoots( 111 ) = [  ]
#  DERoots( 112 ) = [ 113, 115, 117, 125, 135 ]
#  DERoots( 113 ) = [  ]
#  DERoots( 114 ) = [ 115 ]
#  DERoots( 115 ) = [ 117, 119, 121 ]
#  DERoots( 116 ) = [ 117, 141 ]
#  DERoots( 117 ) = [ 141 ]
#  DERoots( 118 ) = [ 119 ]
#  DERoots( 119 ) = [ 135 ]
#  DERoots( 120 ) = [ 121, 133 ]
#
#  To find the genera such that there is a (d,e) root of degree n:
#
#  gap> DERootGenera(105);
#  [ 86, 87, 92, 94, 97, 99, 100, 101, 102, 103, 104 ]
#
#  and its range version:
#
#  gap> DERootGeneraRange(105,34);
#  DERootGenera( 105 ) = [ 86, 87, 92, 94, 97, 99, 100, 101, 102, 103, 104 ]
#  DERootGenera( 107 ) = [ 106 ]
#  DERootGenera( 109 ) = [ 108 ]
#  DERootGenera( 111 ) = [ 91, 92, 109, 110 ]
#  DERootGenera( 113 ) = [ 112 ]
#  DERootGenera( 115 ) = [ 101, 103, 112, 114 ]
#  DERootGenera( 117 ) = [ 97, 106, 109, 110, 112, 115, 116 ]
#  DERootGenera( 119 ) = [ 107, 110, 115, 118 ]
#  DERootGenera( 121 ) = [ 115, 120 ]
#  DERootGenera( 123 ) = [ 101, 102, 121, 122 ]
#  DERootGenera( 125 ) = [ 112, 122, 124 ]
#  DERootGenera( 127 ) = [ 126 ]
#  DERootGenera( 129 ) = [ 106, 107, 127, 128 ]
#  DERootGenera( 131 ) = [ 130 ]
#  DERootGenera( 133 ) = [ 120, 123, 129, 132 ]
#  DERootGenera( 135 ) = [ 112, 119, 121, 127, 128, 130, 131, 132, 133, 134 ]
#  DERootGenera( 137 ) = [ 136 ]
#  DERootGenera( 139 ) = [ 138 ]
#
########################################################################

N := function( d, e)
   return (d * e)/GcdInt( d, e );
   end;  

G := function( d, e )
   return (d * e)/GcdInt( d, e ) - ( d + e )/ ( 2 * GcdInt( d, e ) );
   end;

FactorPairs := function( n )
   local
      factorList, prime, power, maxPower, divisorPair, 
         divisorsList, count, addDivisorsList;

   factorList := PrimePowersInt( n );
   addDivisorsList := [ ];
   divisorsList := [ [1,1] ];
   for count in [1..Length(factorList)/2] do
      prime := factorList[2*count-1];
      maxPower := factorList[2*count];        
      for power in [1..maxPower] do
         for divisorPair in divisorsList do
            Append( addDivisorsList, 
               [[ prime^power * divisorPair[1], divisorPair[2] ]] );
            Append( addDivisorsList, 
               [[ divisorPair[1], prime^power * divisorPair[2] ]] );
         od;
      od;
      Append( divisorsList, addDivisorsList );
      addDivisorsList := [ ];
   od;
   return 
      Filtered( divisorsList, divisorPair -> ( divisorPair[1] < n) and 
         ( divisorPair[2] < n ) );
   end;
   
DERootGenera := function( n )
   # negative input value signals do not write output to file
   # (OK, I admit it's an ugly hack!)
   local
      generaList, divisorPair, output;

   if IsEvenInt(n) then
      Print("n must be odd.\n");
      AppendTo(outputfile, "n must be odd.\n" );
      return;
   fi;

   # check for no output
   if n < 0 then
      output := false;
      n := -n;
   else
      output := true;
   fi;

   generaList := [ ];
   for divisorPair in FactorPairs( n ) do
      Append( generaList, [n - (divisorPair[1] + divisorPair[2])/2 ] );
   od;
   Sort( generaList );
   if output then
      AppendTo( outputfile, "DERootGenera( ", n, " ) = ",
                DuplicateFreeList( generaList ),"\n");
   fi;
   return DuplicateFreeList( generaList );
   end;

DERoots := function( g )
   local
      rootList, n, startN, endN;
   rootList := [ ];

   if IsOddInt( g ) then 
      startN := g;
   else 
      startN := g+1; 
   fi;

   if IsOddInt( Int( (6*(g+2))/5 ) ) then
      endN := Int( (6*(g+2))/5 );
   else
      endN := Int( (6*(g+2))/5 ) - 1;
   fi;

   for n in [startN,startN+2..endN] do
      if g in DERootGenera( -n ) then
         Append( rootList, [n] );
      fi;
      # -n was used to suppress writing output to file by DERootGenera()
   od;
   AppendTo( outputfile, "DERoots( ", g, " ) = ", rootList, "\n");
   return rootList;
   end;
         
DERootGeneraRange := function( n, k )
   local
      startN, endN, degree;

   if IsEvenInt(n) then
      startN := n+1;
   else
      startN := n;
   fi;

   if IsEvenInt(n+k) then
      endN := n+k-1;
   else
      endN := n+k;
   fi;

   for degree in [startN, startN+2..endN] do
      Print("DERootGenera( ",degree," ) = ",DERootGenera( degree ),"\n");
   od;
   end;

DERootsRange := function( g, k )
   local
      genus;

   for genus in [g..g+k] do
      Print("DERoots( ",genus," ) = ",DERoots( genus ),"\n");
   od;
   end;

# functions for finding Margalit-Schleimer roots

FindInverse := function( x, n )
   local
      y;
   if x < 0 then
      for y in [1..n-1] do
         if RemInt( x*y, -n ) = 1-n then
            return y;
         fi;
      od;
   else
      for y in [1..n-1] do
         if RemInt( x*y, n ) = 1 then
            return y;
         fi;
      od;
   fi;
   end;
   
PairOrderFunction := function( x, y )
   local
      a1, a2;
   a1 := x[1];
   a2 := y[1];
   if a1 < a2 then
      return true;
   else
      return false;
   fi;
   end;

SumProductSolutions := function( n )
   local
      solutionsList, aInverse, aSolution, a, b;
   
   solutionsList := [ ];
   for aInverse in [2..(n+1)/2] do;
      if GcdInt( aInverse, n ) = 1 and GcdInt( 1 - aInverse, n ) = 1 then
         a := FindInverse( aInverse, n );
         b := FindInverse( 1 - aInverse, n );
         aSolution := [a, b];
         Sort( aSolution );
         Append( solutionsList, [ aSolution ] );
      fi;
   od;
   Sort( solutionsList, PairOrderFunction );
   return solutionsList;
   end;

MSRoots := function( g )
   local
      n, aSolution, a, b, c;
   if g < 1 then
      AppendTo(outputfile, "g must be odd at least 1");
      Print("g must be odd at least 1");
      return;
   fi;

   n := 2*g + 1;
   
   if Length(SumProductSolutions( n )) = 1 then
      AppendTo(outputfile, "For g = ",g," there is one Margalit-Schleimer root:\n");
      Print("For g = ",g," there is one Margalit-Schleimer root:\n");
   else
      AppendTo(outputfile, "For g = ",g," there are ",Length(SumProductSolutions( n )),
             " Margalit-Schleimer roots:\n");
      Print("For g = ",g," there are ",Length(SumProductSolutions( n )),
             " Margalit-Schleimer roots:\n");
   fi;

   for aSolution in SumProductSolutions( n ) do
      a := aSolution[1];
      b := aSolution[2];
      c := n - a - b;
      if c < 0 then
         c := n + c;
      fi;
      AppendTo(outputfile, "( ", n, ", 0, ( ", a, ", ", b , " ); ( ",
               c , ", ", n , " ) )\n");
      Print("( ", n, ", 0, ( ", a, ", ", b , " ); ( ", c , ", ", n , " ) )\n");
   od;
   end;

NumMSRoots := function( g )
   local
      output;

   AppendTo(outputfile, "NumMSRoots( ", g, " ) = ",
            Length( SumProductSolutions( 2 * g + 1 ) ),"\n");

   return Length( SumProductSolutions( 2 * g + 1 ) );
   end;

NumMSRootsRange := function( g, k )
   local
      genus;

   for genus in [g..g+k] do
      Print("NumMSRoots( ", genus," ) = ", NumMSRoots( genus ),"\n");
   od;
   end;

