# This file was *autogenerated* from the file utils_gap_interface.sage
from sage.all_cmdline import *   # import sage library
_sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0); _sage_const_5 = Integer(5); _sage_const_4 = Integer(4)
"""
####################################################################################################
# This module contains utilities for using the sage-gap-interface
####################################################################################################




##############################################################################
#       Copyright (C) 2016 Sebastian Oehms <[email protected]>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#
#  The full text of the GPL is available at:
#
#                  http://www.gnu.org/licenses/
##############################################################################



This module contains the following functions:

  - "gap_word_problem": an improvement of the sage method word_problem belonging to the matrix group and permutation group
                      classes which uses the gap function "EpimorphismFromFreeGroup"

  - "expand_map_from_generators": function to create a map between groups given on generators by use of the gap_word_problem
                      function. It can be used in some cases where the gap function "GroupHomomorphismByImages" fails

  - "gap_hom": function to create a map between groups given on generators by use of the gap function 
                      "GroupHomomorphismByImages" or the former function "expand_map_from_generators" in the cases where
                      "GroupHomomorphismByImages" fails

  - "gap_centralizer": calculates the centralizer of an element in a group using the gap function "Centralizer"

  - "gap_extend_workspace": extends the gap workspace by use of the sage gap_extend_workspace function

AUTHOR

  - Sebastian Oehms, Sept. 2016

"""

from sage.groups.libgap_wrapper import ParentLibGAP
from lib.utils_sys import *



###############################################################################
# Capsules needed in the main functions
###############################################################################

# -------------------------------------------------------------------------------
# gap_result
# return the gap-boolean fail as None 
# -------------------------------------------------------------------------------
def gap_result( gap_element ):
    """
    this function converts the gap boolean fail into a None object
 
    INPUT:
 
     - an arbtrary gap_element

    OUTPUT:

      the element itself or None if it is the gap-boolean fail.

    """
    if gap_element != None:
        if 'is_bool' in dir(gap_element):
            if gap_element.is_bool(): 
               if gap_element == libgap.eval('fail'):
                   return None
    return gap_element




# -------------------------------------------------------------------------------
# gap_group_map
# used to convert gap maps into sage function objects 
# -------------------------------------------------------------------------------
def gap_group_map( groupEle, GroupTo, gapHom  ):
    """
    This function can be used to converts a gap group map which was defined by the gap functions like 
    'GroupHomomorphismByImages' or 'IsomorhismPermGroup' or others having an ImageElm attribute into a 
    sage function object

    If you don't see this well formatted type 

    sage: print gap_group_map.__doc__

    INPUT:

       - "groupEle": an element of the source group, whose image should be obtained, as a sage object.

       - "GroupTo":  the  target group as a sage object

       - "gapHom":   the gap homomorphism which should be applied to groupEle and which has been defined by a gap functions 
                     like 'GroupHomomorphismByImages' or 'IsomorhismPermGroup'

    OUTPUT:

       the value of groupEle under gap_hom as object of GroupTo
       
    RAISE:

      - RuntimeError: "gap cannot compute image of %s" If gap fails to compute the image

    USAGE:

       if you have defined a target group 'my_target_group' in your programm and a gap function 'my_gap_hom' then you
       can obtain a sage function object by setting

       def my_group_map( groupEle ):
            return gap_group_map( groupEle, my_target_group, my_gap_hom )

    EXAMPLE:

       isoToPerm = Group_gap.IsomorphismPermGroup()

       permGens = isoToPerm.Image().GeneratorsOfGroup().sage()
       PermGroup = PermutationGroup( permGens )

       def groupIso( groupEle ):
           return gap_group_map( groupEle, PermGroup, isoToPerm )


    AUTHOR

      - Sebastian Oehms, Sept. 2016

    """
    try:
        groupEle_gap = groupEle.gap()
    except:
        groupEle_gap = gap(groupEle)

    imGroupEle_gap = gap_result( gapHom.ImageElm( groupEle_gap ) )

    if imGroupEle_gap == None:
        raise RuntimeError( "gap cannot compute image of %s"%(groupEle_gap) )

    try:
        imGroupEle = GroupTo(imGroupEle_gap.sage())
    except:
        imGroupEle = GroupTo( imGroupEle_gap )

    return  imGroupEle







# -------------------------------------------------------------------------------
# register_hom
# definition as homomorphism and registration
# -------------------------------------------------------------------------------
def register_hom( GroupFrom, GroupTo, groupMap, verbose = False ):
    """
    This function registers the function object GroupMap as a Hom-Object, coerecion and conversion to the sage group 
    GroupTo

    If you don't see this well formatted type 

    sage: print register_hom.__doc__

    INPUT:

       - "GroupFrom": source group as a sage object

       - "GroupTo":   target group as a sage object

       - "GroupMap":  the map to be registered as a sage function object

       - "verbose":   optional keyword to print time stamp debugging messages
                      This keyword uses the timeStampControl class. For more information on this type

                     print setupTimeStamp.__doc__
                     print timeStampControl.__doc__
                     print timeStampControl.print_timestamp.__doc__
                     print print_time_tb.__doc__

    OUTPUT:

       the function object groupMap as an element of Hom( GroupFrom, GroupTo )


    AUTHOR

      - Sebastian Oehms, Sept. 2016

    """

    TimeStamp = setupTimeStamp( verbose = verbose )
    TimeStamp.print_timestamp( Text = "Begin", Level = TsLevel.StackInfo ) 
              

    try:
        #---------------------------------------------------------------------------------------
        # Note: for sage version less than 7.2 this may succeed even though groupMap fails to
        # be a homomorhism (see differences in groups/matrix_gps/morphism.py
        # and groups/matrix_gps/homset.py)
        #--------------------------------------------------------------------------------------- 
        groupHom = Hom( GroupFrom, GroupTo )(groupMap)
    except:
        groupHom = groupMap
        TimeStamp.print_timestamp( Text = "no hom", Level = TsLevel.Debug ) 

    try:
        GroupTo.register_coercion( groupHom )
    except:
        TimeStamp.print_timestamp( Text = "no coercion", Level = TsLevel.Debug ) 

    try:
        GroupTo.register_conversion( groupHom )
    except:
        TimeStamp.print_timestamp( Text = "no conversion", Level = TsLevel.Debug ) 

    TimeStamp.print_timestamp( Text = "End", Level = TsLevel.StackInfo ) 

    return groupHom



###############################################################################
# Group Hom Via GAP main-Function: gap_hom
###############################################################################

# -------------------------------------------------------------------------------
# gap_word_problem (needed in expand_map_from_generators)
# -------------------------------------------------------------------------------
def gap_word_problem( element, genList, verbose = False ):
    """
    variant of the sage word_problem-method which behaves identical for permutation
    groups as well as for matrix-group and other groups

    If you don't see this well formatted type 

    sage: print gap_word_problem.__doc__

    Three sage-classes have got a word_problem method as an implementation of the gap function "EpimorphismFromFreeGroup":

     - GroupElementMixinLibGAP ( sage.groups.libgap_mixin )
     - PermutationGroupElement ( sage.groups.perm_gps.permgroup_element )
     - DualAbelianGroupElement ( sage.groups.abelian_gps.dual_abelian_group_element ) 

    But all three methods behave different and are implemented differently. None of these methods returns the result as
    a list, in order to use it in a function. It seems that the original purpose is only to display the result.
    Also note, that the PermutationGroupElement method displays the result returned by gap in a wrong way, as can be seen 
    from the following example:

    sage: S3=SymmetricGroup(4)
    sage: s1, s2 = S3.gens()
    sage: s = s1**3*s2*s1; s
    (2,3)
    sage:  sw = s.word_problem(S3.gens())
             x2^-1*x1^-1*(x1^-1*x2^-1)^2*x2^-1*x1^-2*x2^-1*x1^-1
             [['(1,2)', -1], ['(1,2,3,4)', -1], ['((1,2,3,4)', -1], ['(1,2)', -1], ['(1,2,3,4)', -2], 
              ['(1,2)', -1], ['(1,2,3,4)', -1]]
    sage: sw
    ('x2^-1*x1^-1*(x1^-1*x2^-1)^2*x2^-1*x1^-2*x2^-1*x1^-1',
     '(1,2)^-1*(1,2,3,4)^-1*((1,2,3,4)^-1*(1,2)^-1)^2*(1,2)^-1*(1,2,3,4)^-2*(1,2)^-1*(1,2,3,4)^-1')

    Here the output displayed in the second line (which is only printed and not returned as result of the function) 
    ignores the brackets (...)**2 (around the expression "x1^-1*x2^-1") returned by gap. 

    The function "gap_word_problem" deals with the same task, but returns the result in list form. Continuing with the
    above example we get

    sage: swg = gap_word_problem(s, S3.gens()); swg
    [(1,2), -1, (1,2,3,4), -2, (1,2), -1, (1,2,3,4), -1, (1,2), -2, (1,2,3,4), -2, (1,2), -1, (1,2,3,4), -1]
    sage: check=S3.one()
    sage: for i in range(len(swg)/2):
              check = check * swg[2*i]**swg[2*i+1]
          ....:     
    sage: check == s
    True


    INPUT:

        - "element": an element in a group to be represented in terms of a set of generators

        - "genList": list of generators in the same group as element, such that element lies in the subgroup generated 
                     by them
        - "verbose": optional keyword to print time stamp debugging messages
                     This keyword uses the timeStampControl class. For more information on this type

                     print setupTimeStamp.__doc__
                     print timeStampControl.__doc__
                     print timeStampControl.print_timestamp.__doc__
                     print print_time_tb.__doc__


    OUTPUT:

        a word in the generators in "genList" representing "element" as a list of length 2*l if l is the length of the 
        word. This list has the form (w_1, e_1, w_2, e_2, .... , w_l, e_l) where the w_i in genList are the generators
        in the word expression whereas the e_i are the corresponding exponents, that is
        
           element = w_1**e_1 * w_2**e_2 * .... * w_l**e_  

    RAISE:

        - TypeError: "generator genList[0] must have a parent" if the first element of genList has no attribute "parent"

        - TypeError: "parent of generator genList[0] must be an instance of Group" if the first element of genList does
                     not belong to a group

    EXAMPLE:

    continuing the example above we look at the type returned

        sage: type(sw)
        <type 'tuple'>
        sage: type(sw[1])
        <type 'str'>
        sage: type(sw[0])
        <type 'str'>

        sage: type(swg)
        <type 'list'>
        sage: type(swg[0])
        <type 'sage.groups.perm_gps.permgroup_element.PermutationGroupElement'>
        sage: type(swg[1])
        <type 'sage.rings.integer.Integer'>
        sage: 

    an example of matrix groups

        sage: GL3_3=GL(3,3)
        sage: g1, g2 = GL3_3.gens()
        sage: g1, g2 = GL3_3.gens(); g1, g2
        (
         [2 0 0]  [2 0 1]
         [0 1 0]  [2 0 0]
         [0 0 1], [0 2 0]
        )
        sage: g = g1**2*g2*g1; g
        [1 0 1]
        [1 0 0]
        [0 2 0]
        sage: gwg = gap_word_problem(g, [g1,g2] ); gwg
        [
        [2 0 1]     [2 0 0]    
        [2 0 0]     [0 1 0]    
        [0 2 0], 1, [0 0 1], -1
        ]
        sage: 
        sage: check=GL3_3.one()
        sage: for i in range(len(gwg)/2):
                  check = check * gwg[2*i]**gwg[2*i+1]
              ....:     
        sage: check == g
        True

    an example of fintely preseted groups

        sage: B3 = BraidGroup(3); B3
        Braid group on 3 strands
        sage: b1, b2 = B3.gens()
        sage: b1, b2 = B3.gens(); b1, b2
        (s0, s1)
        sage: 
        sage: 
        sage: b = b1**2*b2*b1; b
        s0^2*s1*s0
        sage: bwg = gap_word_problem(b, [b1,b2] ); bwg
        [s0, 1, s0, 1, s1, 1, s0, 1]
        sage: check=B3.one()
        sage: for i in range(len(bwg)/2):
                  check = check * bwg[2*i]**bwg[2*i+1]
        ....:     
        sage: check == b
        True


    AUTHOR

      - Sebastian Oehms, Sept. 2016

    """

    TimeStamp = setupTimeStamp( verbose = verbose )
    TimeStamp.print_timestamp( Text = "Begin", Level = TsLevel.StackInfo )    

    if 'Tietze' in dir(element):
        result = []
        for i in element.Tietze():
            if i > _sage_const_0 :
                result.append( genList[i-_sage_const_1 ] )
                result.append( _sage_const_1  )
            elif i < _sage_const_0 :
                result.append( genList[-i-_sage_const_1 ] )
                result.append( -_sage_const_1  )
        return result

    n=len(genList)
    cmd="Fg:=FreeGroup(%d);"%(n)
    libgap.eval(cmd)
    for i in range(n):
        cmd="x%d := GeneratorsOfGroup(Fg)[%d];"%(i+_sage_const_1 ,i+_sage_const_1 )
        libgap.eval(cmd) 

    if 'parent' not in dir( genList[_sage_const_0 ] ):
        raise TypeError( "generator %s must have a parent"%(genList[_sage_const_0 ]) )

    GroupFrom = genList[_sage_const_0 ].parent()

    if isinstance( GroupFrom, sage.groups.old.Group ) == False and isinstance( GroupFrom, sage.groups.group.Group ) == False:
        raise TypeError( "parent of generator %s must be an instance of Group"%(genList[_sage_const_0 ]) )


    if 'gap' in dir(GroupFrom):
        G = GroupFrom.gap()
    else:
        G = gap(GroupFrom)

    if 'gap' in dir(GroupFrom(element)):
        g = GroupFrom(element).gap()
    else:
        g = GroupFrom(element)

    try:
       genList_gap = [gen.gap() for gen in genList]
    except:
       genList_gap = [libgap(gen) for gen in genList]

    H=G.Subgroup( genList_gap )
    ans = H.EpimorphismFromFreeGroup().PreImagesRepresentative(g)
    ans_repl = ans.UnderlyingElement()
    num = ans_repl.NumberSyllables().sage()

    result = []
    exponent_syllable  = libgap.eval('ExponentSyllable')
    generator_syllable = libgap.eval('GeneratorSyllable')
    for i in range(num):
        exponent  = exponent_syllable(ans_repl, i+_sage_const_1 ).sage()
        generator = generator_syllable(ans_repl, i+_sage_const_1 ).sage() - _sage_const_1 
        result.append( genList[generator] )
        result.append( exponent )

    TimeStamp.print_timestamp( Text = "End", Level = TsLevel.StackInfo )    

    return result




# -------------------------------------------------------------------------------
# expand_map_from_generators (needed in gap_hom)
# -------------------------------------------------------------------------------
def expand_map_from_generators( GroupFrom, GroupTo, GensFrom = None, GensTo = None, verbose = False):
    """
    expand a map given on generators of GroupFrom into another GroupTo. If the optional keyword GensFrom and GensTo 
    are not set the generators of GroupFrom and GroupTo will be used instead

    Note: it is not checked if this gives a well defined homomorphis. This must be known or checked separately


    If you don't see this well formatted type 

    sage: print expand_map_from_generators.__doc__

    This functin is based on the gap_word_problem function and therefor on the gap function "EpimorphismFromFreeGroup"

    INPUT:

        - "GroupFrom": the source group of the map to be generated 

        - "GroupTo":   the target group of the map to be generated 

        - "GensFrom":  (optional keyword, default = None takes GroupFrom.gens()) set of generators of GroupFrom which
                       should be mapped to the according elements (same order) of GensTo resp. GroupTo.gens()  

        - "GensTo":    (optional keyword, default = None takes Groupo.gens()) set of generators of GroupTo which
                       should be the images of the according elements (same order) of GensFrom resp. GroupFrom.gens()  

        - "verbose": optional keyword to print time stamp debugging messages
                     This keyword uses the timeStampControl class. For more information on this type

                     print setupTimeStamp.__doc__
                     print timeStampControl.__doc__
                     print timeStampControl.print_timestamp.__doc__
                     print print_time_tb.__doc__


    OUTPUT:

        a function object of the following form:
        
        INPUT:

          - "Ele" an arbitrary element of GroupFrom

        OUTPUT:

          the corresponding element in GroupTo under the map defined


    RAISE:

        - TypeError: "GroupFrom  must be an instance of Group" 

        - TypeError: "GroupTo  must be an instance of Group" 

        - ValueError: "incompatible length of generator sets" if the list of generators of source and targed have different
                      length

    EXAMPLE:

        sage: S3=SymmetricGroup(4); S3
        Symmetric group of order 4! as a permutation group

        sage: s1, s2 = S3.gens()
        sage: s = s1**3*s2*s1; s
        (2,3) 
        sage: 
        sage: GU3 = GU(3,2); GU3
        General Unitary Group of degree 3 over Finite Field in a of size 2^2 with respect to hermitian form 
        [0 0 1]
        [0 1 0]
        [1 0 0]
        sage: 
        sage: S3toGU3 = expand_map_from_generators( S3, GU3); S3toGU3
        <function Map at 0x9ac3e0d4>
        sage: g = S3toGU3(s); g
        [1 a a]
        [a a 0]
        [a 0 0]

     note that S3toGU3 is not a group homomorphism

        sage: g1 = S3toGU3(s1); g1
        [a 0 0]
        [0 1 0]
        [0 0 a]
        sage: g2 = S3toGU3(s2); g2
        [    0     0     1]
        [    0     1     1]
        [    1     1 a + 1]
        sage: g_check = g1**3*g2*g1; g_check 
        [0 0 a]
        [0 1 a]
        [a 1 1]
        sage: g == g_check
        False


    AUTHOR

      - Sebastian Oehms, Sept. 2016

    """

    TimeStamp = setupTimeStamp( verbose = verbose )
    TimeStamp.print_timestamp( Text = "Begin", Level = TsLevel.StackInfo )    

    if isinstance( GroupFrom, sage.groups.old.Group ) == False and isinstance( GroupFrom, sage.groups.group.Group ) == False:
        raise TypeError( "GroupFrom  must be an instance of Group" )

    if isinstance( GroupTo, sage.groups.old.Group ) == False and isinstance( GroupTo, sage.groups.group.Group ) == False:
        raise TypeError( "GroupTo must be an instance of Group" )

    if  GensFrom != None:
        Fgens = [GroupFrom(g) for g in GensFrom] 
    else:
        Fgens = GroupFrom.gens()

    if  GensTo != None:
        Tgens = [GroupTo(g) for g in GensTo] 
    else:
        Tgens = GroupTo.gens()

    if len(Fgens) !=len(Tgens):
        raise ValueError( "incompatible length of generator sets" )

    def Map( Ele ):
        EleGens = gap_word_problem(Ele, Fgens)
        anzLetter = len( EleGens )/_sage_const_2 
        ToEle = GroupTo.one()
        for i in range(anzLetter):
           for fGen in Fgens:
                if EleGens[_sage_const_2 *i] == fGen:
                   tGen = Tgens[Fgens.index(fGen)]
                   ToEle = ToEle * tGen**(EleGens[_sage_const_2 *i+_sage_const_1 ])
        return ToEle

    TimeStamp.print_timestamp( Text = "End", Level = TsLevel.StackInfo )    

    return Map



# -------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
# gap_hom (main-function)
# -------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
def gap_hom( GroupFrom, GroupTo, GensFrom=None, GensTo=None, check = False, verbose = False ):
    """
    This function creates a map from GroupFrom to GroupTo via the GAP-function 'GroupHomomorphismByImages' such that 
    generators GensFrom are mapped to GensTo. If the optional parameter GensFrom and GensTo are not set the generators 
    of GroupFrom and GroupTo will be used instead.

    If GroupHomomorphismByImages fails on the task the function expand_map_from_generators is used for a second try
    This function is based on GAP-function 'EpimorphismFromFreeGroup' (via gap_word_problem). For more information
    on this type

        sage: print expand_map_from_generators.__doc__
        sage: print gap_word_problem.__doc__


    Note: it is not checked if this gives a well defined homomorphism by default. To achieve this set the 
    keyword check = True

    If you don't see this well formatted type 

    sage: print expand_map_from_generators.__doc__

    INPUT:

        - "GroupFrom": the source group of the map to be generated 

        - "GroupTo":   the target group of the map to be generated 

        - "GensFrom":  (optional keyword, default = None takes GroupFrom.gens()) set of generators of GroupFrom which
                       should be mapped to the according elements (same order) of GensTo resp. GroupTo.gens()  

        - "GensTo":    (optional keyword, default = None takes Groupo.gens()) set of generators of GroupTo which
                       should be the images of the according elements (same order) of GensFrom resp. GroupFrom.gens()  

        - "check":     optional boolean, default = False. If set to True the constructed map is tested to be a a well 
                       defined group homomorphism. Depending wether the gap function "GroupHomomorphismByImages"
                       can be used the check is perfomed by gap. In the other case a NotImplemented Exception is raised.

        - "verbose": optional keyword to print time stamp debugging messages
                     This keyword uses the timeStampControl class. For more information on this type

                     print setupTimeStamp.__doc__
                     print timeStampControl.__doc__
                     print timeStampControl.print_timestamp.__doc__
                     print print_time_tb.__doc__


    OUTPUT:

        an element of Hom( GroupFrom, GroupTo )


    RAISE:

        - TypeError: "GroupFrom  must be an instance of Group" 

        - TypeError: "GroupTo  must be an instance of Group" 

        - ValueError: "incompatible length of generator sets" if the list of generators of source and targed have different
                      length

        - RuntimeError: "One does not map to one" if check = True, else a warning

        - RuntimeError: "No map definition possible" the test that the contructed map works failed

        - RuntimeError: "gap cannot compute image of ..." occurs when the constructed map is applied: calculation of 
                        the image of a specific element failed 

        - NotImplementedError: "no check for expand_map_from_generators - try again with check = False" occurs if 
                      check is set to True and gap function "GroupHomomorphismByImages" can not be applied


    EXAMPLE:

     
        sage: B3 = BraidGroup(3)
        sage: S3=SymmetricGroup(3)
        sage: Im=[S3((1,2)), S3((2,3))]; Im
        [(1,2), (2,3)]
        sage: b2s = gap_hom( B3, S3, GensTo=Im ); b2s
        Generic morphism:
        From: Braid group on 3 strands
        To:   Symmetric group of order 3! as a permutation group
        sage: b1, b2 = B3.gens(); b1, b2
        (s0, s1)
        sage: b2s(b1*b2*b1)
        (1,3)
        sage: b2s(b2*b1*b2)
        (1,3)

    check if the map is indeed a homomorphism of groups

        sage: b2s_check = gap_hom( B3, S3, GensTo=Im, check=True )
        sage: b2s(b1) == b2s_check(b1)
        True
        sage: b2s(b2) == b2s_check(b2)
        True

    CAUTION: by default it is not checked if the map is well defined nor a homomorphism:

        sage: b2s_wrong = gap_hom( B3, S3 ); b2s_wrong
        Generic morphism:
          From: Braid group on 3 strands
          To:   Symmetric group of order 3! as a permutation group
        sage: 
        sage: b = b1*b2*b1
        sage: bb = b2*b1*b2
        sage: b == bb
        True
        sage: b2s_wrong(b) == b2s_wrong(bb)
        False

    You can check this as follows:

        sage: b2s_wrong = gap_hom( B3, S3, check=True ); b2s_wrong
        ............

        RuntimeError: No map definition possible
        sage: 


    AUTHOR

      - Sebastian Oehms, Sept. 2016

    """

    TimeStamp = setupTimeStamp( verbose = verbose )
    TimeStamp.print_timestamp( Text = "Begin", Level = TsLevel.StackInfo ) 


    # -----------------------------------------------------------------------------------------------------
    # checks and preperations
    # -----------------------------------------------------------------------------------------------------

    if isinstance( GroupFrom, sage.groups.old.Group ) == False and isinstance( GroupFrom, sage.groups.group.Group ) == False:
        raise TypeError( "GroupFrom  must be an instance of Group" )

    if isinstance( GroupTo, sage.groups.old.Group ) == False and isinstance( GroupTo, sage.groups.group.Group ) == False:
        raise TypeError( "GroupTo must be an instance of Group" )


    try:
        GroupFrom_gap = GroupFrom.gap()
    except:
        GroupFrom_gap = gap(GroupFrom)
        TimeStamp.print_timestamp( Text = "gap(GroupFrom)", Level = TsLevel.Debug ) 

    try:
        GroupTo_gap   = GroupTo.gap()
    except:
        GroupTo_gap   = gap(GroupTo)
        TimeStamp.print_timestamp( Text = "gap(GroupTo)", Level = TsLevel.Debug ) 

    if GensFrom == None:
        GensFrom_gap = GroupFrom_gap.GeneratorsOfGroup() 
    else:
        try:
            GensFrom_gap = [gen.gap() for gen in GensFrom]
        except:
            GensFrom_gap = [libgap(gen) for gen in GensFrom]
            TimeStamp.print_timestamp( Text = "libgap(gen) GensFrom", Level = TsLevel.Debug ) 

    if GensTo == None:
        GensTo_gap = GroupTo_gap.GeneratorsOfGroup() 
    else:
        try:
            GensTo_gap = [gen.gap() for gen in GensTo]
        except:
            GensTo_gap = [libgap(gen) for gen in GensTo]
            TimeStamp.print_timestamp( Text = "libgap(gen) GensTo", Level = TsLevel.Debug ) 


    if len(GensFrom_gap) !=len(GensTo_gap):
        raise ValueError( "incompatible length of generator sets" )

    # -----------------------------------------------------------------------------------------------------
    # do the job
    # -----------------------------------------------------------------------------------------------------

    try:
        if check == False:
            gHom_gap = gap_result( GroupFrom_gap.GroupHomomorphismByImagesNC( GroupTo_gap, GensFrom_gap, GensTo_gap ) )
        else:
            gHom_gap = gap_result( GroupFrom_gap.GroupHomomorphismByImages( GroupTo_gap, GensFrom_gap, GensTo_gap ) )

        if gHom_gap == None:            
            if check == False:
                groupMap = expand_map_from_generators( GroupFrom, GroupTo, GensFrom, GensTo, verbose = verbose )
            else:
                raise NotImplementedError( "no check for expand_map_from_generators - try again with check = False" )

            TimeStamp.print_timestamp( Text = "expand_map_from_generators pos1", Level = TsLevel.Debug ) 
        else:
            def groupMap( groupEle ):
                return gap_group_map( groupEle, GroupTo, gHom_gap )

            TimeStamp.print_timestamp( Text = "GroupHomomorphis", Level = TsLevel.Debug ) 

    except:
        if check == False:
            groupMap = expand_map_from_generators( GroupFrom, GroupTo, GensFrom, GensTo, verbose = verbose )
        else:
            raise NotImplementedError( "no check for expand_map_from_generators - try again with check = False" )
 
        TimeStamp.print_timestamp( Text = "expand_map_from_generators pos2", Level = TsLevel.Debug ) 


    # -------------------------------------------------------------------------------------
    # Check if groupMap works
    # ------------------------------------------------------------------------------------- 

    try:
        One = groupMap( GroupFrom.one() )
        TimeStamp.print_timestamp( Text = "checked if groupMap works", Level = TsLevel.Debug ) 
        if One != GroupTo.one():
            if check == True:
                raise RuntimeError( "One does not map to one" )
            else:
                print "Warning: one does not map to one"
    except:
        raise RuntimeError( "No map definition possible" )


    # -------------------------------------------------------------------------------------
    # definition as homomorphism and registration
    # ------------------------------------------------------------------------------------- 

    groupHom = register_hom( GroupFrom, GroupTo, groupMap, verbose = verbose )

    TimeStamp.print_timestamp( Text = "End", Level = TsLevel.StackInfo ) 

    return groupHom












# -------------------------------------------------------------------------------
# gap_as_permutation_group
# -------------------------------------------------------------------------------
def gap_as_permutation_group( Group, verbose = False ):
    """
    This function is an extension of the Sage as_permutation_group-method of the classes
 
     - sage.groups.matrix_gps/finitely_generated.FinitelyGeneratedMatrixGroup_gap
     - sage.groups.finitely_presente.FinitelyPresentedGroup

    using the GAP-function 'IsomorphismPermGroup'. It can be applied to other cases of groups.
    Furthermore the isompophism map returned by gap is registered as a conversion map to and from the permutation group
    defined (which is not the case for the above mentioned methods)

    If you don't see this well formatted type 

    sage: print gap_as_permutation_group.__doc__

    INPUT:

       - "Group": the group for which an isomorphism to a permutation group should be found

       - "verbose": optional keyword to print time stamp debugging messages
                    This keyword uses the timeStampControl class. For more information on this type

                    print setupTimeStamp.__doc__
                    print timeStampControl.__doc__
                    print timeStampControl.print_timestamp.__doc__
                    print print_time_tb.__doc__

    OUTPUT:

       - an isomporphic permutation group to Group together with the isomorphism registered as conversion maps
         for Group and the returned permutation group

    RAISE:

       - TypeError: "Group  must be an instance of Group"

       - RuntimeError: "gap fails to compute permutation group!"

       - NotImplementedError: "Group must be finite."

    EXAMPLE:

       sage: Sp4 = Sp(4,3); Sp4
       Symplectic Group of degree 4 over Finite Field of size 3
       sage: PSp4 = gap_as_permutation_group( Sp4 ); PSp4
       Permutation Group with generators [(1,2)(3,5)(4,7)(6,10)(8,13)(9,15)(11,18)(12,20)(14,19)(16,24)(17,26)(21,31)
       (22,33)(23,35)(25,32)(27,40)(28,42)(29,44)(30,46)(34,51)(36,53)(37,54)(38,55)(39,50)(43,60)(45,62)(47,56)(48,59)
       (49,64)(57,66)(58,65)(61,63)(69,73)(71,75)(74,78)(76,77), (1,3,6,11,19,29,45,63,72)(2,4,8,14,18,28,43,61,70)
       (5,9,16,25,38,56,62,71,76)(7,12,21,32,49,53,60,69,74)(10,17,27,41,59,68,40,58,44)(13,22,34,52,54,67,51,66,42)
       (15,23,36)(20,30,47)(24,37,55)(26,39,57)(31,48,64)(33,50,65)(73,77,79)(75,78,80)]
       sage: s1, s2 = Sp4.gens(); s1, s2
       (
       [2 0 0 0]  [1 0 1 0]
       [0 1 0 0]  [1 0 0 0]
       [0 0 1 0]  [0 1 0 1]
       [0 0 0 2], [0 2 0 0]
       )
       sage: s = s1*s2**2; s
       [2 2 2 2]
       [1 0 1 0]
       [1 2 0 0]
       [1 0 0 0]
       sage: 
       sage: sp2p = PSp4.convert_map_from( Sp4 )
       sage: p2sp = Sp4.convert_map_from( PSp4 )
       sage: 
       sage: p1 = sp2p( s1 ); p1
       (1,2)(3,5)(4,7)(6,10)(8,13)(9,15)(11,18)(12,20)(14,19)(16,24)(17,26)(21,31)(22,33)(23,35)(25,32)(27,40)(28,42)(29,44)
       (30,46)(34,51)(36,53)(37,54)(38,55)(39,50)(43,60)(45,62)(47,56)(48,59)(49,64)(57,66)(58,65)(61,63)(69,73)(71,75)
       (74,78)(76,77)
       sage: p2 = sp2p( s2 ); p2
       (1,3,6,11,19,29,45,63,72)(2,4,8,14,18,28,43,61,70)(5,9,16,25,38,56,62,71,76)(7,12,21,32,49,53,60,69,74)      
       (10,17,27,41,59,68,40,58,44)(13,22,34,52,54,67,51,66,42)(15,23,36)(20,30,47)(24,37,55)(26,39,57)(31,48,64)(33,50,65)
       (73,77,79)(75,78,80)
       sage: p = sp2p( s ); p
       (1,8,34,42,61)(2,6,27,44,63)(3,16,55,62,72)(4,21,64,60,70)(5,11,43,74,75)(7,14,45,76,73)(9,36,69,79,77)
       (10,19,28,22,65)(12,47,71,80,78)(13,18,29,17,57)(15,25,53,23,35)(20,32,56,30,46)(24,38,37,51,54)(26,41,68,58,50)
       (31,49,48,40,59)(33,52,67,66,39)
       sage: s1 == p2sp( p1 )
       True
       sage: s2 == p2sp( p2 )
       True
       sage: s == p2sp( p)
       True
       sage: 



    AUTHOR

      - Sebastian Oehms, Sept. 2016

    """

    TimeStamp = setupTimeStamp( verbose = verbose )
    TimeStamp.print_timestamp( Text = "Begin", Level = TsLevel.StackInfo ) 

    # -----------------------------------------------------------------------------------------------------
    # checks and preperations
    # -----------------------------------------------------------------------------------------------------

    if isinstance( Group, sage.groups.old.Group ) == False and isinstance( Group, sage.groups.group.Group ) == False:
        raise TypeError( "Group  must be an instance of Group" )

    if not Group.is_finite():
       raise NotImplementedError("Group must be finite.")

    try:
        Group_gap = Group.gap()
    except:
      Group_gap = gap(Group)

    isoToPerm = gap_result( Group_gap.IsomorphismPermGroup() )

    if isoToPerm == None:
       raise RuntimeError("gap fails to compute permutation group!")
    
    isoToPermInv = isoToPerm.InverseGeneralMapping()
    permGens = isoToPerm.Image().GeneratorsOfGroup().sage()
    PermGroup = PermutationGroup( permGens )

    TimeStamp.print_timestamp( Text = "PermutationGroup defined", Level = TsLevel.Debug )

    def groupIso( groupEle ):
        return gap_group_map( groupEle, PermGroup, isoToPerm )

    def groupIsoInv ( groupEle ):
        return gap_group_map( groupEle, Group, isoToPermInv )

    TimeStamp.print_timestamp( Text = "groupIso defined", Level = TsLevel.Debug )


    # -------------------------------------------------------------------------------------
    # definition as homomorphism and registration
    # ------------------------------------------------------------------------------------- 

    register_hom( Group, PermGroup, groupIso, verbose = verbose )


    # -------------------------------------------------------------------------------------
    # now for inverse mapping
    # ------------------------------------------------------------------------------------- 

    register_hom( PermGroup, Group, groupIsoInv, verbose = verbose )

    TimeStamp.print_timestamp( Text = "End", Level = TsLevel.StackInfo ) 

    return PermGroup



















# -------------------------------------------------------------------------------
# gap_centralizer
# -------------------------------------------------------------------------------
def gap_centralizer( Group, Element, verbose = False ):
    """
    This function returns the centralizer of an Element of the Group using the GAP-function
    'Centralizer'. This extends the use of this GAP-function in sage to Groups which are not
    permutation groups (note that the sage-method centralizer is defined for peremutation group, only)

    If you don't see this well formatted type 

    sage: print gap_centralizer.__doc__

    INPUT:

       - "Group": the group in which the centralizer should be calculated

       - "Element": element in Group which shoul commute with all elements of the centralizer

       - "verbose": optional keyword to print time stamp debugging messages
                    This keyword uses the timeStampControl class. For more information on this type
  
                    print setupTimeStamp.__doc__
                    print timeStampControl.__doc__
                    print timeStampControl.print_timestamp.__doc__
                    print print_time_tb.__doc__

    OUTPUT:

      the maximal subgroup of "Group" whose elements commute with the given "Element" as sage subgroup of "Group" 


    EXAMPLES:
 
       sage: Sp4 = Sp(4,3)
       sage: s1, s2 = Sp4.gens(); s1, s2
       (
       [2 0 0 0]  [1 0 1 0]
       [0 1 0 0]  [1 0 0 0]
       [0 0 1 0]  [0 1 0 1]
       [0 0 0 2], [0 2 0 0]
       )
       sage: s = s1*s2**2; s
       [2 2 2 2]
       [1 0 1 0]
       [1 2 0 0]
       [1 0 0 0]
       sage: CSp4s = gap_centralizer( Sp4, s ); CSp4s
       Matrix group over Finite Field of size 3 with 2 generators (
       [2 2 2 2]  [2 0 0 0]
       [1 0 1 0]  [0 2 0 0]
       [1 2 0 0]  [0 0 2 0]
       [1 0 0 0], [0 0 0 2]
       )
       sage: c1, c2 = CSp4s.gens()
       sage: cs1=Sp4(c1); cs1
       [2 2 2 2]
       [1 0 1 0]
       [1 2 0 0]
       [1 0 0 0]
       sage: cs2=Sp4(c2); cs2
       [2 0 0 0]
       [0 2 0 0]
       [0 0 2 0]
       [0 0 0 2]
       sage: cs1*s == s * cs1
       True
       sage: cs2*s == s * cs2
       True
       sage: 


    AUTHOR

      - Sebastian Oehms, Sept. 2016

    """

    TimeStamp = setupTimeStamp( verbose = verbose )
    TimeStamp.print_timestamp( Text = "Begin", Level = TsLevel.StackInfo ) 

    try:
        Group_gap = Group.gap()
    except:
        Group_gap = gap(Group)

    try:
        Element_gap = Element.gap()
    except:
        Element_gap = gap(Element)

    TimeStamp.print_timestamp( Text = "Calling Gap", Level = TsLevel.Body ) 

    centralizer_gap = Group_gap.Centralizer(Element_gap)

    TimeStamp.print_timestamp( Text = "setting sage generator list", Level = TsLevel.Body ) 

    GenList = [ Group(gen) for gen in centralizer_gap.GeneratorsOfGroup() ]

    TimeStamp.print_timestamp( Text = "defining subgroup", Level = TsLevel.Body ) 

    centralizer = Group.subgroup( GenList )

    TimeStamp.print_timestamp( Text = "End", Level = TsLevel.StackInfo ) 

    return centralizer





def gap_extend_workspace( new_size ):
    """
    extends the minimum GAP workspace to the given value if possible:


    If you don't see this well formatted type 

    sage: print gap_extend_workspace.__doc__

    INPUT:

       - "new_size" the new size in byte to which the gap workspace should be extended

    RAISE:

        TypeError: "new_size must be of type Integer!"

    MESSAGES:

     informational:

        "Note: GAP workspace has been extended: %d -> %d"%(akt_memory_pool_size, new_memory_pool_size)

     warning:

        "Warning: cannont extend the GAP workspace more than half of swap and 4/5 of ram" if new_size is byond the limits
                                                                                          of hardware recources  


    EXAMPLES::

      on an Acer AO Happy under LinuxMint 17.3 Mate with 2 GB RAM:

       sage: oneHundertMB = 100*1024**2
       sage: gap_extend_workspace( 8*oneHundertMB )
       Note: GAP workspace has been extended: 262144000 -> 838860800
       sage: 
       sage: gap_extend_workspace( 13*oneHundertMB )
       Warning: cannont extend the GAP workspace more than half of swap and 4/5 of ram
       Note: GAP workspace has been extended: 262144000 -> 1058121318
       sage: 

    AUTHOR

      - Sebastian Oehms, Sept. 2016

    """

    if isinstance( new_size, Integer ) == False:
        raise TypeError( "new_size must be of type Integer!" )
    
    from sage.interfaces.gap import get_gap_memory_pool_size, set_gap_memory_pool_size 
    from sage.misc.memory_info import MemoryInfo
    
    akt_memory_pool_size = get_gap_memory_pool_size()
    new_memory_pool_size = int(new_size) 

    mem = MemoryInfo()

    if ( new_memory_pool_size > mem.available_swap() // _sage_const_2  ) and ( new_memory_pool_size >  _sage_const_4 * mem.available_ram() // _sage_const_5  ):
        print "Warning: cannont extend the GAP workspace more than half of swap and 4/5 of ram"  
        new_memory_pool_size = max(mem.available_swap() // _sage_const_2 , _sage_const_4 *mem.available_ram()  // _sage_const_5  )

    if new_memory_pool_size > akt_memory_pool_size:
        gap_reset_workspace( max_workspace_size = new_memory_pool_size )
        print "Note: GAP workspace has been extended: %d -> %d"%(akt_memory_pool_size, new_memory_pool_size)