📚 The CoCalc Library - books, templates and other resources
License: OTHER
#######################################################1## ##2## absalgtext.sage ##3## ##4## These routines are used with the sage workbooks ##5## that are provided with the book "Abstract ##6## Algebra, an Interactive Approach" by William ##7## Paulsen. ##8## ##9## Users of this file are encouraged to buy a copy ##10## of the textbook. ##11## ##12#######################################################1314## First of all, we need to see that the Small Groups package is loaded with Sage. It is not automatically loaded when Sage is installed, so15## we probably have to load it the first time this package is run.1617try:18gap.eval("SmallGroup(1,1)")19except:20print "Additional databases must be downloaded from the internet. Please wait."21try:22install_package('database_gap')23except:24print "Error in installing database_gap."25print "For Linux users, this will have to be installed manually. Type:"26print " sudo sage -i database_gap"27print "in the terminal window."28else:29print "These new databases will be available the next time Sage is started."303132######################################################33## ##34## GAP routines ##35## ##36## These routines are written in GAP, not Sage. ##37## They are evaluated using gap.eval( ), in order ##38## that we may use these routines later. ##39## ##40######################################################4142gap.eval("TempFreeGroup := FreeGroup(0);")43gap.eval("CurrentGroup := TempFreeGroup/[];")44gap.eval("CurrentPCGroup := TempFreeGroup/[];")4546gap.eval("ITER_POLY_WARN := false;"); # So we can define polynomials over a ring without a warning message.4748# These functions will be introduced later, but to prevent49# "Variable must have a value" errors, we set them to 0 here50gap.eval("Mult := 0")51gap.eval("AddC := 0")52gap.eval("PermToInt := 0")5354gap.eval("QuickTable := function(L) " +55"local i, j, k, LL, G, n, R, M, p; " +56"LL := List(L); " +57"G := Flat(LL); " + # find the base group58"n := Size(LL); " +59"M := NullMat(n,n); " + # matrix of zeros, mutable60"for i in [1..n] do " +61"for j in [1..n] do " +62"if IsList(LL[i]) then " +63"p := Mult(G,LL[i],LL[j]); " + # Multiply two cosets64"else " +65"p := LL[i]*LL[j]; " +66"fi; " +67"for k in [1..n] do " +68"if IsList(p) then " +69"if( Set(p) = Set(LL[k]) ) then " +70"M[i][j] := k; " +71"break; " +72"fi; " +73"elif (p = LL[k]) then " +74"M[i][j] := k; " +75"break; " +76"fi; " +77"od; " +78"od; " +79"od; " +80"return M; " +81"end;")8283gap.eval("QuickAdd := function(L) " +84"local i, j, k, LL, G, n, R, M, p; " +85"LL := List(L); " +86"G := Flat(LL); " + # find the base group87"n := Size(LL); " +88"M := NullMat(n,n); " + # matrix of zeros, mutable89"for i in [1..n] do " +90"for j in [1..n] do " +91"if IsList(LL[i]) then " +92"p := AddC(G,LL[i],LL[j]); " + # Add two cosets93"else " +94"p := LL[i] + LL[j]; " +95"fi; " +96"for k in [1..n] do " +97"if IsList(p) then " +98"if( Set(p) = Set(LL[k]) ) then " +99"M[i][j] := k; " +100"break; " +101"fi; " +102"elif (p = LL[k]) then " +103"M[i][j] := k; " +104"break; " +105"fi; " +106"od; " +107"od; " +108"od; " +109"return M; " +110"end;")111112gap.eval("ListGroup := function(g) " +113"local L, Ord, n, iList, pointer, Cont, GList, ele; " +114"if (IsGroup(g) = false) then " +115'Print("Not a group\n"); ' +116"return fail; " +117"fi; " +118"if (Size(g) = infinity) then " +119'Print("Infinite group\n"); ' +120"return fail; " +121"fi; " +122"L := GeneratorsOfGroup(g); " +123"Ord := List(L, x->Order(x)); " +124# do a arbitrarily nested for loop125"n := Size(L); " +126"GList := [ ]; " +127"iList := List([1..n],x->0); " + # Vector of zeros, of length n128"Cont := true; " +129"while Cont do " +130"ele := Product([1..n],x->L[x]^iList[x]); " +131"if not (ele in GList) then " +132"Add(GList,ele); " +133"fi; " +134# increment the iList135"pointer := 1; " +136"iList[pointer] := iList[pointer] + 1; " +137"while (iList[pointer] >= Ord[pointer]) do " +138"iList[pointer] := 0; " +139"pointer := pointer + 1; " +140"if (pointer > n) then " +141"Cont := false; " +142"pointer := 1; " +143"fi; " +144"iList[pointer] := iList[pointer] + 1; " +145"od; " +146"od; " +147"return GList; " +148"end;")149150gap.eval("ListRing := function(g) " +151"local L, Ord, n, iList, pointer, Cont, GList, ele; " +152"if (IsRing(g) = false) then " +153'Print("Not a ring\n"); ' +154"return fail; " +155"fi; " +156"if (Size(g) = infinity) then " +157'Print("Infinite ring\n"); ' +158"return fail; " +159"fi; " +160"L := GeneratorsOfRing(g); " +161"Ord := List(L, x->Size(Ring(x))); " +162# do a arbitrarily nested for loop163"n := Size(L); " +164"GList := [ ]; " +165"iList := List([1..n],x->0); " + # Vector of zeros, of length n166"Cont := true; " +167"while Cont do " +168"ele := Sum([1..n],x->iList[x]*L[x]); " +169"if not (ele in GList) then " +170"Add(GList,ele); " +171"fi; " +172# increment the iList173"pointer := 1; " +174"iList[pointer] := iList[pointer] + 1; " +175"while (iList[pointer] >= Ord[pointer]) do " +176"iList[pointer] := 0; " +177"pointer := pointer + 1; " +178"if (pointer > n) then " +179"Cont := false; " +180"pointer := 1; " +181"fi; " +182"iList[pointer] := iList[pointer] + 1; " +183"od; " +184"od; " +185"return GList; " +186"end;")187188gap.eval("CheckRing := function(R) " +189"local i,j,k,T,L; " +190"if Size(R) = infinity then " +191"L := GeneratorsOfRing(R); " +192"else " +193"L := List(R); " +194"fi; " +195"T := true; " +196"for i in L do " +197"for j in L do " +198"for k in L do " +199"if ( (i+j)*k <> (i*k) + (j*k)) then " +200"T := false; " +201"fi; " +202"od; " +203"od; " +204"od; " +205"if (not T) then " +206'return "Ring is not left distributive."; ' +207"else " +208"for i in L do " +209"for j in L do " +210"for k in L do " +211"if ( i*(j+k) <> (i*j) + (i*k)) then " +212"T := false; " +213"fi; " +214"od; " +215"od; " +216"od; " +217"if (not T) then " +218'return "Ring is not right distributive."; ' +219"else " +220"for i in L do " +221"for j in L do " +222"for k in L do " +223"if ( (i*j)*k <> i*(j*k) ) then " +224"T := false; " +225"fi; " +226"od; " +227"od; " +228"od; " +229"if T then " +230'return "This is a ring."; ' +231"else " +232'return "Associative law does not hold."; ' +233"fi; " +234"fi; " +235"fi; " +236"end;")237238gap.eval("Mult := function( G, H, Y ) " +239"local GG, XX, YY, i, j, FlatG, SizeG, p, base, k, L; " +240# Find the base group241"GG := List(G); " +242"FlatG := Flat(GG); " +243# Determine the operation244"SizeG := Size(FlatG); " +245# base = 0 means G is a group, base > 0 means multiply mod base,246# base < 0 means add mod base.247"base := 0; " +248"if (Number(FlatG, IsInt) = SizeG) then " +249"base := Maximum(FlatG) + 1; " +250"if (0 in FlatG) then " +251"base := -base; " +252"fi; " +253"fi; " +254"L := []; " +255"if (IsList(H) or IsGroup(H) or IsRing(H)) then " +256"XX := List(H); " +257"else " +258"XX := [H]; " +259"fi; " +260"if (IsList(Y) or IsGroup(Y) or IsRing(Y)) then " +261"YY := List(Y); " +262"else " +263"YY := [Y]; " +264"fi; " +265# assume first that XX and YY are simple lists. " +266"for i in [1..Size(XX)] do " +267"for j in [1..Size(YY)] do " +268"if(IsList(XX[i]) or IsList(YY[j]) ) then " +269"Add(L, Mult(FlatG, XX[i], YY[j])); " + # recursively defined for sets of sets270"else " +271# find the product p " +272"if (base > 0) then " +273"p := (XX[i] * YY[j]) mod base; " +274"elif (base < 0) then " +275"p := (XX[i] + YY[j]) mod (- base); " +276"else " +277"p := XX[i]*YY[j]; " +278"fi; " +279"for k in [1..Size(GG)] do " +280"if (p = GG[k]) then " +281"Add(L, GG[k]); " +282"break; " +283"fi; " +284"od; " +285"fi; " +286"od; " +287"od; " +288"Sort(L); " +289# remove duplicates from the list290"for i in Reversed([2..Size(L)]) do " +291"for j in Reversed([1..i-1]) do " +292"if (L[i] = L[j]) then " +293"Remove(L,i); " +294"break; " +295"fi; " +296"od; " +297"od; " +298"return L; " +299"end;")300301gap.eval("LSide := 0;")302gap.eval("RSide := 0;")303304gap.eval("LeftSide := function(x) " +305"LSide := x; " +306"end;")307308gap.eval("RightSide := function(x) " +309"RSide := x; " +310"end;")311312gap.eval("Prod := function(G) " +313"local i, j, k, p, L; " +314"L := []; " +315"for i in [1..Size(LSide)] do " +316"for j in [1..Size(RSide)] do " +317# find the product p318"p := LSide[i]*RSide[j]; " +319"if (G = []) then " +320"Add(L, p); " +321"else " +322"for k in [1..Size(G)] do " +323"if (p = G[k]) then " +324"Add(L, G[k]); " +325"break; " +326"fi; " +327"od; " +328"fi; " +329"od; " +330"od; " +331"Sort(L); " +332# remove duplicates from the list (which will be together if sorted)333"for i in Reversed([2..Size(L)]) do " +334"if (L[i] = L[i-1]) then " +335"Remove(L,i); " +336"fi; " +337"od; " +338"return L; " +339"end;")340341gap.eval("AddCoset := function(R) " +342"local i, j, k, p, L; " +343"L := []; " +344"for i in [1..Size(LSide)] do " +345"for j in [1..Size(R)] do " +346# find the sum p " +347"p := LSide[i] + R[j]; " +348"Add(L, p); " +349"od; " +350"od; " +351"Sort(L); " +352# remove duplicates from the list (which will be together if sorted)353"for i in Reversed([2..Size(L)]) do " +354"if (L[i] = L[i-1]) then " +355"Remove(L,i); " +356"fi; " +357"od; " +358"return L; " +359"end;")360361gap.eval("OrigGroup := 0;")362363gap.eval("OriginalGroup := function(x) " +364"OrigGroup := x; " +365"end;")366367gap.eval("Conform := function(sub) " +368"local i, k, L; " +369"L := []; " +370"for i in [1..Size(sub)] do " +371"for k in [1..Size(OrigGroup)] do " +372"if (sub[i] = OrigGroup[k]) then " +373"Add(L, OrigGroup[k]); " +374"break; " +375"fi; " +376"od; " +377"od; " +378"return L; " +379"end;")380381gap.eval("LftCoset := function(H) " +382"local GG, HH, i, j, k, q, base, p, FlatQ, SizeG, t; " +383"GG := OrigGroup; " +384"SizeG := Size(GG); " +385"HH := Conform(H); " +386"Sort(HH); " +387"q := [HH]; " +388"Print(q); " +389"FlatQ := Flat(HH); " +390"for i in [1..SizeG] do " +391"if not(GG[i] in FlatQ) then " +392"t := []; " +393"for j in [1..Size(HH)] do " +394"p := GG[i]*HH[j]; " +395"for k in [1..SizeG] do " +396"if p = GG[k] then " +397"Add(t, GG[k]); " +398"Add(FlatQ, GG[k]); " +399"fi; " +400"od; " +401"od; " +402"Sort(t); " +403"Add(q, t); " +404"Print(q); " +405"fi; " +406"od; " +407"return q; " +408"end;")409410gap.eval("RtCoset := function(H) " +411"local GG, HH, i, j, k, q, p, FlatQ, SizeG, t; " +412"GG := OrigGroup; " +413"SizeG := Size(GG); " +414"HH := Conform(H); " +415"Sort(HH); " +416"q := [HH]; " +417"FlatQ := Flat(HH); " +418"for i in [1..SizeG] do " +419"if not(GG[i] in FlatQ) then " +420"t := []; " +421"for j in [1..Size(HH)] do " +422"p := HH[j]*GG[i]; " +423"for k in [1..SizeG] do " +424"if p = GG[k] then " +425"Add(t, GG[k]); " +426"Add(FlatQ, GG[k]); " +427"fi; " +428"od; " +429"od; " +430"Sort(t); " +431"Add(q, t); " +432"fi; " +433"od; " +434"return q; " +435"end;")436437gap.eval("RingCoset := function(H) " +438"local GG, HH, i, j, k, q, p, FlatQ, SizeG, t; " +439"GG := OrigGroup; " +440"SizeG := Size(GG); " +441"HH := H; " +442"Sort(HH); " +443"q := [HH]; " +444"FlatQ := Flat(HH); " +445"for i in [1..SizeG] do " +446"if not(GG[i] in FlatQ) then " +447"t := []; " +448"for j in [1..Size(HH)] do " +449"p := HH[j] + GG[i]; " +450"Add(t, p); " +451"Add(FlatQ, p); " +452"od; " +453"Sort(t); " +454"Add(q, t); " +455"fi; " +456"od; " +457"return q; " +458"end;")459460gap.eval("NormClosure := function(S) " +461"local GG, SS, NN, LL; " +462"SS := Group(S); " + # make both S and G into groups.463"GG := Group(OrigGroup); " +464"NN := NormalClosure(GG, SS); " +465"LL := List(NN); " +466"return Conform(LL); " +467"end;")468469gap.eval("IdealClosure := function(S) " +470"local RR, SS, II; " +471"SS := List(S); " + # make S into a list.472"RR := Ring(OrigGroup); " + # make GG into a ring473"II := Ideal(RR, SS); " +474"return List(II); " +475"end;")476477gap.eval("ConjClasses := function(G) " +478"local GG, SS, LL; " +479"OriginalGroup(G); " +480"GG := Group(G); " +481"SS := ConjugacyClasses(GG); " +482"LL := List(SS, x -> Conform(List(x))); " +483"return LL; " +484"end;")485486gap.eval("CommSubgroup := function(K) " +487"local HH, KK, MM; " +488"HH := Group(OrigGroup); " +489"KK := Group(K); " +490"MM := CommutatorSubgroup(HH, KK); " +491"if IsSubset(HH, MM) then " + # we can use OrigGroup to Conform the list492"return Conform(List(MM)); " +493"else " +494"return List(MM); " +495"fi; " +496"end;")497498gap.eval("AddC := function( G, H, Y ) " +499"local GG, XX, YY, i, j, FlatG, SizeG, p, base, k, L; " +500# Find the base ring.501"GG := List(G); " +502"FlatG := Flat(GG); " +503# Find the operation504"SizeG := Size(FlatG); " +505# base = 0 means G is a ring, base > 0 means add mod base.506"base := 0; " +507"if (Number(FlatG, IsInt) = SizeG) then " +508"base := Maximum(FlatG) + 1; " +509"fi; " +510"L := []; " +511"if (IsList(H) or IsRing(H)) then " +512"XX := List(H); " +513"else " +514"XX := [H]; " + # list with 1 element515"fi; " +516"if (IsList(Y) or IsRing(Y)) then " +517"YY := List(Y); " +518"else " +519"YY := [Y]; " + # list with 1 element520"fi; " +521# assume first that XX and YY are simple lists.522"for i in [1..Size(XX)] do " +523"for j in [1..Size(YY)] do " +524"if(IsList(XX[i]) or IsList(YY[j]) ) then " +525"Add(L, AddC(FlatG, XX[i], YY[j])); " + # recursively defined for sets of sets526"else " +527# find the sum p528"if (base > 0) then " +529"p := (XX[i] + YY[j]) mod base; " +530"else " +531"p := XX[i]+YY[j]; " +532"fi; " +533"for k in [1..Size(GG)] do " +534"if (p = GG[k]) then " +535"Add(L, GG[k]); " +536"break; " +537"fi; " +538"od; " +539"fi; " +540"od; " +541"od; " +542"Sort(L); " +543# remove duplicates from the list (which will be together if sorted)544"for i in Reversed([2..Size(L)]) do " +545"if (L[i] = L[i-1]) then " +546"Remove(L,i); " +547"fi; " +548"od; " +549"return L; " +550"end;")551552gap.eval("ListCurrentGroup := function(x) " +553"return List(CurrentGroup); " +554"end;")555556gap.eval("ListCurrentPCGroup := function(x) " +557"return List(CurrentPCGroup); " +558"end;")559560561562563564565566567568## Set up type of elements to test whether an element is of a certain type569570GapInstance = type(gap("(1,2)")) # used for isinstance to detect Gap elements571IntModType = type(Integers(2)(1)) # used for isinstance to detect multiplication mod n572IntType = type(1)573GFType = type(GF(2))574QuatType = type(QuaternionAlgebra(SR, -1, -1).gens()[0])575576## Set flags to their default position577578reset("e")579E = e # since e is often used for the identity element of a group, we need a way to access 2.718281828...580581LastInit = "None"582UnivFieldVar = ""583LastFieldVar = ""584#_QuickMult_ = False585586## Allow alias of several Sage functions to be consistent with the CamelCase convention of Mathematica587588def EulerPhi(x):589return euler_phi(x)590591def PowerMod(x, r, n):592return pow(x, r, n)593594def PartitionsP(x):595return Partitions(x).cardinality()596597def ConwayPolynomial(p,d):598return conway_polynomial(p, d)599600def List(x):601if str(parent(x)) == 'Gap':602return list(x.List())603else:604return list(x)605606def PolynomialQuotient(dividend, divisor):607return (dividend._maxima_().divide(divisor).sage())[0]608609def PolynomialRemainder(dividend, divisor):610return (dividend._maxima_().divide(divisor).sage())[1]611612maxima_calculus('algebraic: true;') # allows Together to also rationalize the denominator.613614def Together(x):615if isinstance(x, QuatType):616return x[0].simplify_rational() + x[1].simplify_rational() * i + x[2].simplify_rational() * j + x[3].simplify_rational() * k617return x.simplify_rational()618619def AddMod(x):620return ZGroup(x)621622def MultMod(x):623return ZRing(x)624625def TrigReduce(x):626y = expand(x)627try:628y = y.trig_reduce()629except:630return y631y = y.subs(cos(4*pi/9) == cos(pi/9) - cos(2*pi/9))632y = y.subs(cos(5*pi/9) == cos(2*pi/9) - cos(pi/9))633y = y.subs(cos(3*pi/7) == cos(2*pi/7) - cos(pi/7) + 1/2)634y = y.subs(cos(4*pi/7) == cos(pi/7) - cos(2*pi/7) - 1/2)635y = expand(y)636return y #y.trig_reduce()637638########################################################639## ##640## Graphical Routines ##641## ##642## These routines take advantage of Sage's graphics ##643## capabilities. They include Terry the triangle, ##644## swapping books, the octahedron, and the Pyriminx ##645## puzzle. ##646## ##647########################################################648649ColorTable = {0:'#000000', 1:'#FFFF00', 2:'#FF00FF', 3:'#00FFFF', 4:'#FF0000', 5:'#00FF00', 6:'#9966CC', 7:'#FF9900', 8:'#B3CCB3',6509:'#CC9980', 10:'#8099CC', 11:'#CC3380', 12:'#CC9933', 13:'#999933', 14:'#FF6666', 15:'#33CCB3', 16:'#FF99FF', 17:'#B3FF00',65118:'#0000FF', 19:'#996666', 20:'#0099FF', 21:'#FFCCB3', 22:'#6633FF', 23:'#FFCC66', 24:'#339966', 25:'#80CCCC', 26:'#66CC80',65227:'#00FF99', 28:'#CC8033', 29:'#3333E5'}653654655TerryColor = ['#FF0000', '#00FF00', '#00FFFF']656657def ShowTerry():658global TerryColor659global Stay660global RotLft661global RotRt662global Spin663global FlipLft664global FlipRt665global IdentityElement666if(IdentityElement != "Stay"):667# If Terry's group is not already defined, set up the variable names.668Stay = var('Stay')669RotLft = var('RotLft')670RotRt = var('RotRt')671Spin = var('Spin')672FlipLft = var('FlipLft')673FlipRt = var('FlipRt')674sqrt3 = sqrt(3).numerical_approx()675Graff = Graphics()676Graff += polygon([(0,0),(0,2),(sqrt3,-1)], color = TerryColor[0])677Graff += polygon([(0,0),(0,2),(-sqrt3,-1)], color = TerryColor[1])678Graff += polygon([(0,0),(sqrt3,-1),(-sqrt3,-1)], color = TerryColor[2])679return Graff.show(axes = false, aspect_ratio = 1, xmin = -2, xmax = 2, ymin = -2, ymax = 2)680681def Terry(*args):682global TerryColor683sqrt3 = sqrt(3).numerical_approx()684v = []685if(IdentityElement == 'Stay'):686## If InitTerry has been defined, then like Mathematica, we want Terry to take the shortcut.687Prod = Stay688for arg in args:689Prod = Prod * arg690newList = []691newList.append(Prod)692else:693newList = []694for arg in args:695newList.append(arg)696for arg in newList:697if(arg == Stay):698Graff = Graphics()699Graff += polygon([(0,0),(0,2),(sqrt3,-1)], color = TerryColor[0])700Graff += polygon([(0,0),(0,2),(-sqrt3,-1)], color = TerryColor[1])701Graff += polygon([(0,0),(sqrt3,-1),(-sqrt3,-1)], color = TerryColor[2])702for i in range(5):703v.append(Graff)704elif(arg == RotRt):705for i in range(6):706th = 2*pi*i/3/5.0 # theta ranges from 0 to 2pi/3, inclusive707Graff = Graphics()708Graff += polygon([(0,0),(2*sin(th),2*cos(th)), (2*sin(th + 2*pi/3),2*cos(th + 2*pi/3))], color = TerryColor[0])709Graff += polygon([(0,0), (2*sin(th + 2*pi/3),2*cos(th + 2*pi/3)), (2*sin(th - 2*pi/3),2*cos(th - 2*pi/3))], color = TerryColor[2])710Graff += polygon([(0,0), (2*sin(th - 2*pi/3),2*cos(th - 2*pi/3)), (2*sin(th),2*cos(th))], color = TerryColor[1])711v.append(Graff)712Temp = TerryColor[0]713TerryColor[0] = TerryColor[1]714TerryColor[1] = TerryColor[2]715TerryColor[2] = Temp716elif(arg == RotLft):717for i in range(6):718th = 2*pi*i/3/5.0 # theta ranges from 0 to 2pi/3, inclusive719Graff = Graphics()720Graff += polygon([(0,0),(-2*sin(th),2*cos(th)), (2*sin(2*pi/3 - th),2*cos(2*pi/3 - th))], color = TerryColor[0])721Graff += polygon([(0,0), (2*sin(2*pi/3-th),2*cos(2*pi/3-th)), (-2*sin(th + 2*pi/3),2*cos(th + 2*pi/3))], color = TerryColor[2])722Graff += polygon([(0,0), (-2*sin(th + 2*pi/3),2*cos(th + 2*pi/3)), (-2*sin(th),2*cos(th))], color = TerryColor[1])723v.append(Graff)724Temp = TerryColor[0]725TerryColor[0] = TerryColor[2]726TerryColor[2] = TerryColor[1]727TerryColor[1] = Temp728elif(arg == Spin):729for i in range(6):730th = pi*i/5.0 # theta ranges from 0 to pi, inclusive731Graff = Graphics()732Graff += polygon([(0,0),(0,2),(sqrt3*cos(th),sin(th)/2-1)], color = TerryColor[0])733Graff += polygon([(0,0), (sqrt3*cos(th),sin(th)/2-1), (-sqrt3*cos(th),-sin(th)/2-1)], color = TerryColor[2])734Graff += polygon([(0,0), (-sqrt3*cos(th),-sin(th)/2-1), (0,2)], color = TerryColor[1])735v.append(Graff)736Temp = TerryColor[0]737TerryColor[0] = TerryColor[1]738TerryColor[1] = Temp739elif(arg == FlipRt):740for i in range(6):741th = pi*i/5.0 # theta ranges from 0 to pi, inclusive742Graff = Graphics()743Graff += polygon([(0,0),(-sqrt3,-1),(sqrt3/2 - sqrt3*cos(th)/2 - sqrt3*sin(th)/4, 1/2 + 3*cos(th)/2 - sin(th)/4)], color = TerryColor[1])744Graff += polygon([(0,0), (sqrt3/2 - sqrt3*cos(th)/2 - sqrt3*sin(th)/4, 1/2 + 3*cos(th)/2 - sin(th)/4),745(sqrt3/2 + sqrt3*cos(th)/2 + sqrt3*sin(th)/4, 1/2 - 3*cos(th)/2 + sin(th)/4)], color = TerryColor[0])746Graff += polygon([(0,0), (sqrt3/2 + sqrt3*cos(th)/2 + sqrt3*sin(th)/4, 1/2 - 3*cos(th)/2 + sin(th)/4),747(-sqrt3,-1)], color = TerryColor[2])748v.append(Graff)749Temp = TerryColor[1]750TerryColor[1] = TerryColor[2]751TerryColor[2] = Temp752elif(arg == FlipLft):753for i in range(6):754th = pi*i/5.0 # theta ranges from 0 to pi, inclusive755Graff = Graphics()756Graff += polygon([(0,0),(sqrt3,-1),(-sqrt3/2 - sqrt3*cos(th)/2 + sqrt3*sin(th)/4, 1/2 - 3*cos(th)/2 - sin(th)/4)], color = TerryColor[2])757Graff += polygon([(0,0), (-sqrt3/2 - sqrt3*cos(th)/2 + sqrt3*sin(th)/4, 1/2 - 3*cos(th)/2 - sin(th)/4),758(-sqrt3/2 + sqrt3*cos(th)/2 - sqrt3*sin(th)/4, 1/2 + 3*cos(th)/2 + sin(th)/4)], color = TerryColor[1])759Graff += polygon([(0,0), (-sqrt3/2 + sqrt3*cos(th)/2 - sqrt3*sin(th)/4, 1/2 + 3*cos(th)/2 + sin(th)/4),760(sqrt3,-1)], color = TerryColor[0])761v.append(Graff)762Temp = TerryColor[0]763TerryColor[0] = TerryColor[2]764TerryColor[2] = Temp765else:766print("Invalid rotation")767aniTerry = animate(v, xmin = -2, xmax = 2, ymin = -2, ymax = 2, aspect_ratio = 1, axes = false)768return aniTerry.show(delay = 50, iterations = 1)769770BookColor = [] # Initialize so we can use it later771772def ShowBooks():773global BookColor774Graff = Graphics()775for i in range(len(BookColor)):776Graff += polygon([(2*i,-i),(2*i+.16,-.34-i),(2*i+.382,-.618-i),(2*i+.66,-.84-i),(2*i+1,-1-i),(2*i+1.421,-1.079-i),(2*i+2,-1-i), (2*i+2,-.875-i),(2*i+2.25,-.75-i),(2*i+5.333,2.333-i),(2*i+5.333,9.333-i),(2*i+5.167,9.417-i),(2*i+3.5,7.75-i),(2*i+3.5,10.25-i), (2*i+3.333,10.333-i),(2*i+.25,7.25-i),(2*i+.125,7-i),(2*i,7-i)], color = BookColor[i])777Graff += polygon([(2*i+4.5,8.75-i),(2*i+2,6.25-i),(2*i+2,6-i),(2*i+1.421,5.921-i),(2*i+1,6-i), (2*i+.66,6.16-i),(2*i+.382,6.382-i),(2*i+.271,6.521-i),(2*i+3.167,9.417-i)], rgbcolor=(1,1,1))778Graff += line([(2*i,-i),(2*i+.16,-.34-i),(2*i+.382,-.618-i),(2*i+.66,-.84-i),(2*i+1,-1-i),(2*i+1.421,-1.079-i),(2*i+2,-1-i),(2*i+2,6-i), (2*i+1.421,5.921-i),(2*i+1,6-i),(2*i+.66,6.16-i),(2*i+.382,6.382-i),(2*i+.16,6.66-i),(2*i,7-i),(2*i,-i)], rgbcolor=(0,0,0))779Graff += line([(2*i+3.167,9.417-i),(2*i+4.5,8.75-i)], rgbcolor=(0,0,0))780Graff += line([(2*i+5.333,9.333-i),(2*i+2.25,6.25-i),(2*i+2,6.125-i)], rgbcolor=(0,0,0))781Graff += line([(2*i,7-i),(2*i+.25,7-i),(2*i+3.5,10.25-i)], rgbcolor=(0,0,0))782Graff += line([(2*i+2,6-i),(2*i+2,6.25-i),(2*i+5.167,9.417-i),(2*i+5.333,9.333-i)], rgbcolor=(0,0,0))783Graff += line([(2*i+.25,6.547-i),(2*i+.25,7-i)], rgbcolor=(0,0,0))784Graff += line([(2*i+.125,7-i),(2*i+.25,7.25-i),(2*i+3.333,10.333-i),(2*i+3.5,10.25-i),(2*i+3.5,9.25-i)], rgbcolor=(0,0,0))785i = len(BookColor)-1786Graff += line([(2*i+2.25,6.25-i),(2*i+2.25,-.75-i),(2*i+5.333,2.333-i),(2*i+5.333,9.333-i)], rgbcolor=(0,0,0))787return Graff.show(axes = false)788789def InitBooks(n):790global BookColor791global Stay792global Left793global Right794global First795global Last796global Rev797Stay = var('Stay')798Left = var('Left')799Right = var('Right')800First = var('First')801Last = var('Last')802Rev = var('Rev')803BookColor = []804for i in range(n):805BookColor.append(ColorTable[i+4])806return ShowBooks()807808def MoveBooks(*args):809global BookColor810n = len(BookColor)811if n > 1:812for arg in args:813if (arg == Stay):814ShowBooks()815elif (arg == Left):816BookColor.append(BookColor.pop(0))817ShowBooks()818elif (arg == Right):819BookColor.insert(0,BookColor.pop())820ShowBooks()821elif (arg == First):822Temp = BookColor[0]823BookColor[0] = BookColor[1]824BookColor[1] = Temp825ShowBooks()826elif (arg == Last):827Temp = BookColor[n-1]828BookColor[n-1] = BookColor[n-2]829BookColor[n-2] = Temp830ShowBooks()831elif (arg == Rev):832BookColor.reverse()833ShowBooks()834else:835print("Invalid step")836return837838OctColor = [(1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1), (0,1,1), (1,.6,0), (0,0,0)]839840def OctProj(x,y,z): # projects a 3D point into 2D, using the projection angle that was used in TeX841return (-0.3*x + y, -0.375*x -0.15*y + z)842843def ShowOctahedron():844global OctColor845Graff = Graphics()846Graff += polygon([OctProj(0,0,1), OctProj(0,1,0), OctProj(1,0,0)], rgbcolor = OctColor[0])847Graff += polygon([OctProj(0,0,1), OctProj(0,-1,0), OctProj(1,0,0)], rgbcolor = OctColor[2])848Graff += polygon([OctProj(0,0,-1), OctProj(0,1,0), OctProj(1,0,0)], rgbcolor = OctColor[4])849Graff += polygon([OctProj(0,0,-1), OctProj(0,-1,0), OctProj(1,0,0)], rgbcolor = OctColor[6])850return Graff.show(axes = false, aspect_ratio = 1)851852def ShowOctahedronWithQuaternions():853global OctColor854Graff = Graphics()855Graff += polygon([OctProj(0,0,1), OctProj(0,1,0), OctProj(1,0,0)], rgbcolor = OctColor[0])856Graff += polygon([OctProj(0,0,1), OctProj(0,-1,0), OctProj(1,0,0)], rgbcolor = OctColor[2])857Graff += polygon([OctProj(0,0,-1), OctProj(0,1,0), OctProj(1,0,0)], rgbcolor = OctColor[4])858Graff += polygon([OctProj(0,0,-1), OctProj(0,-1,0), OctProj(1,0,0)], rgbcolor = OctColor[6])859Graff += text("i", OctProj(0,1.1,0), fontsize=20, rgbcolor=(0,0,0))860Graff += text("-i", OctProj(0,-1.1,0), fontsize=20, rgbcolor=(0,0,0))861Graff += text("j", OctProj(0.7,0,0), fontsize=20, rgbcolor=(0,0,0))862Graff += text("k", OctProj(0,0,1.1), fontsize=20, rgbcolor=(0,0,0))863Graff += text("-k", OctProj(0,0,-1.1), fontsize=20, rgbcolor=(0,0,0))864Graff += text("<-------- -j", OctProj(-1,0,0), horizontal_alignment='left', fontsize=20, rgbcolor=(0,0,0))865return Graff.show(axes = false, aspect_ratio = 1)866867def InitOctahedron():868global OctColor869global a870global b871global c872a = var('a')873b = var('b')874c = var('c')875OctColor = [(1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1), (0,1,1), (1,.6,0), (0,0,0)]876return ShowOctahedron()877878def RotateOctahedron(*args):879global OctColor880v = []881for arg in args:882if(arg == a):883for i in range(11):884th = pi*i/10.0 # theta ranges from 0 to pi, inclusive885X = OctProj(numerical_approx((1 + cos(th))/2), numerical_approx((-1 + cos(th))/2), numerical_approx(-sqrt(2)*sin(th)/2))886nX = OctProj(numerical_approx((-1 - cos(th))/2), numerical_approx((1 - cos(th))/2), numerical_approx(sqrt(2)*sin(th)/2))887Y = OctProj(numerical_approx((-1 + cos(th))/2), numerical_approx((1 + cos(th))/2), numerical_approx(-sqrt(2)*sin(th)/2))888nY = OctProj(numerical_approx((1 - cos(th))/2), numerical_approx((-1 - cos(th))/2), numerical_approx(sqrt(2)*sin(th)/2))889Z = OctProj(numerical_approx(sqrt(2)*sin(th)/2), numerical_approx(sqrt(2)*sin(th)/2), numerical_approx(cos(th)))890nZ = OctProj(numerical_approx(-sqrt(2)*sin(th)/2), numerical_approx(-sqrt(2)*sin(th)/2), numerical_approx(-cos(th)))891Graff = Graphics()892if th < 0.3455: # cutoff for this particular projection function893Graff += polygon([X,Y,Z], rgbcolor = OctColor[0])894Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])895Graff += polygon([X,Y,nZ], rgbcolor = OctColor[4])896Graff += polygon([X,nY,nZ], rgbcolor = OctColor[6])897elif th < 0.5278:898Graff += polygon([X,Y,Z], rgbcolor = OctColor[0])899Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])900Graff += polygon([X,Y,nZ], rgbcolor = OctColor[4])901Graff += polygon([nX,Y,Z], rgbcolor = OctColor[1])902elif th < 1.7593:903Graff += polygon([X,Y,Z], rgbcolor = OctColor[0])904Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])905Graff += polygon([nX,nY,Z], rgbcolor = OctColor[3])906Graff += polygon([nX,Y,Z], rgbcolor = OctColor[1])907elif th < 1.9478:908Graff += polygon([nX,nY,nZ], rgbcolor = OctColor[7])909Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])910Graff += polygon([nX,nY,Z], rgbcolor = OctColor[3])911Graff += polygon([nX,Y,Z], rgbcolor = OctColor[1])912else:913Graff += polygon([nX,nY,nZ], rgbcolor = OctColor[7])914Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])915Graff += polygon([nX,nY,Z], rgbcolor = OctColor[3])916Graff += polygon([X,nY,nZ], rgbcolor = OctColor[6])917v.append(Graff)918Temp = OctColor[0]919OctColor[0] = OctColor[7]920OctColor[7] = Temp921Temp = OctColor[3]922OctColor[3] = OctColor[4]923OctColor[4] = Temp924Temp = OctColor[1]925OctColor[1] = OctColor[5]926OctColor[5] = Temp927Temp = OctColor[2]928OctColor[2] = OctColor[6]929OctColor[6] = Temp930elif(arg == b):931for i in range(11):932th = 2*pi*i/3/10.0 # theta ranges from 0 to 2pi/3, inclusive933X = OctProj(numerical_approx(1/3 + 2*cos(th)/3), numerical_approx(1/3 - cos(th)/3 + sin(th)/sqrt(3)), numerical_approx(1/3 - cos(th)/3 - sin(th)/sqrt(3)))934nX = OctProj(numerical_approx(-1/3 - 2*cos(th)/3), numerical_approx(-1/3 + cos(th)/3 - sin(th)/sqrt(3)), numerical_approx(-1/3 + cos(th)/3 + sin(th)/sqrt(3)))935Y = OctProj(numerical_approx(1/3 - cos(th)/3 - sin(th)/sqrt(3)), numerical_approx(1/3 + 2*cos(th)/3), numerical_approx(1/3 - cos(th)/3 + sin(th)/sqrt(3)))936nY = OctProj(numerical_approx(-1/3 + cos(th)/3 + sin(th)/sqrt(3)), numerical_approx(-1/3 - 2*cos(th)/3), numerical_approx(-1/3 + cos(th)/3 - sin(th)/sqrt(3)))937Z = OctProj(numerical_approx(1/3 - cos(th)/3 + sin(th)/sqrt(3)), numerical_approx(1/3 - cos(th)/3 - sin(th)/sqrt(3)), numerical_approx(1/3 + 2*cos(th)/3))938nZ = OctProj(numerical_approx(-1/3 + cos(th)/3 - sin(th)/sqrt(3)), numerical_approx(-1/3 + cos(th)/3 + sin(th)/sqrt(3)), numerical_approx(-1/3 - 2*cos(th)/3))939Graff = Graphics()940if th < .68:941Graff += polygon([X,Y,Z], rgbcolor = OctColor[0])942Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])943Graff += polygon([X,Y,nZ], rgbcolor = OctColor[4])944Graff += polygon([X,nY,nZ], rgbcolor = OctColor[6])945elif th < 1.09:946Graff += polygon([X,Y,Z], rgbcolor = OctColor[0])947Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])948Graff += polygon([X,Y,nZ], rgbcolor = OctColor[4])949Graff += polygon([nX,Y,Z], rgbcolor = OctColor[1])950else:951Graff += polygon([X,Y,Z], rgbcolor = OctColor[0])952Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])953Graff += polygon([nX,nY,Z], rgbcolor = OctColor[3])954Graff += polygon([nX,Y,Z], rgbcolor = OctColor[1])955v.append(Graff)956Temp = OctColor[1]957OctColor[1] = OctColor[4]958OctColor[4] = OctColor[2]959OctColor[2] = Temp960Temp = OctColor[3]961OctColor[3] = OctColor[5]962OctColor[5] = OctColor[6]963OctColor[6] = Temp964elif(arg == c):965for i in range(11):966th = pi*i/2/10.0 # theta ranges from 0 to pi/2, inclusive967X = OctProj(1.0,0.0,0.0)968nX = OctProj(-1.0,0.0,0.0)969Y = OctProj(0.0, numerical_approx(cos(th)), numerical_approx(-sin(th)))970nY = OctProj(0.0, numerical_approx(-cos(th)), numerical_approx(sin(th)))971Z = OctProj(0.0, numerical_approx(sin(th)), numerical_approx(cos(th)))972nZ = OctProj(0.0, numerical_approx(-sin(th)), numerical_approx(-cos(th)))973Graff = Graphics()974Graff += polygon([X,Y,Z], rgbcolor = OctColor[0])975Graff += polygon([X,nY,Z], rgbcolor = OctColor[2])976Graff += polygon([X,Y,nZ], rgbcolor = OctColor[4])977Graff += polygon([X,nY,nZ], rgbcolor = OctColor[6])978v.append(Graff)979Temp = OctColor[0]980OctColor[0] = OctColor[2]981OctColor[2] = OctColor[6]982OctColor[6] = OctColor[4]983OctColor[4] = Temp984Temp = OctColor[1]985OctColor[1] = OctColor[3]986OctColor[3] = OctColor[7]987OctColor[7] = OctColor[5]988OctColor[5] = Temp989else:990print("Invalid rotation")991aniOct = animate(v, xmin = -1, xmax = 1, ymin = -1.03, ymax = 1.03, aspect_ratio = 1, axes = false)992return aniOct.show(delay = 50, iterations = 1)993994PuzColor = [(1,0,0), (1,0,0), (1,0,0), (1,0,0), (1,0,0), (1,0,0), (1,1,0), (1,1,0), (1,1,0), (1,1,0), (1,1,0), (1,1,0), (0,1,0), (0,1,0), (0,1,0), (0,1,0), (0,1,0), (0,1,0), (0,0,1), (0,0,1), (0,0,1), (0,0,1), (0,0,1), (0,0,1)]995996def PuzProj(x,y,z): # projects a 3D point into 2D, using the projection angle that was used in TeX997return (-0.45*x + 0.55*y+0.05*z, -0.3125*x -0.3125*y + .5625*z)998999def ShowPuzzle():1000global PuzColor1001Graff = Graphics()1002if PuzColor[0] != 0:1003Graff += polygon([PuzProj(-1,-1,-1),PuzProj(-3,1,-1),PuzProj(-3,-1,1)], rgbcolor = PuzColor[18])1004Graff += polygon([PuzProj(-1,-1,-1),PuzProj(-3,-1,1),PuzProj(-1,-3,1)], rgbcolor = PuzColor[19])1005Graff += polygon([PuzProj(-1,-1,-1),PuzProj(-1,-3,1),PuzProj(1,-3,-1)], rgbcolor = PuzColor[20])1006Graff += polygon([PuzProj(-1,-1,-1),PuzProj(1,-3,-1),PuzProj(1,-1,-3)], rgbcolor = PuzColor[21])1007Graff += polygon([PuzProj(-1,-1,-1),PuzProj(1,-1,-3),PuzProj(-1,1,-3)], rgbcolor = PuzColor[22])1008Graff += polygon([PuzProj(-1,-1,-1),PuzProj(-1,1,-3),PuzProj(-3,1,-1)], rgbcolor = PuzColor[23])1009Graff += polygon([PuzProj(-1,1,1),PuzProj(1,3,1),PuzProj(1,1,3)], rgbcolor = PuzColor[0])1010Graff += polygon([PuzProj(-1,1,1),PuzProj(1,1,3),PuzProj(-1,-1,3)], rgbcolor = PuzColor[1])1011Graff += polygon([PuzProj(-1,1,1),PuzProj(-1,-1,3),PuzProj(-3,-1,1)], rgbcolor = PuzColor[2])1012Graff += polygon([PuzProj(-1,1,1),PuzProj(-3,-1,1),PuzProj(-3,1,-1)], rgbcolor = PuzColor[3])1013Graff += polygon([PuzProj(-1,1,1),PuzProj(-3,1,-1),PuzProj(-1,3,-1)], rgbcolor = PuzColor[4])1014Graff += polygon([PuzProj(-1,1,1),PuzProj(-1,3,-1),PuzProj(1,3,1)], rgbcolor = PuzColor[5])1015Graff += polygon([PuzProj(1,-1,1),PuzProj(1,1,3),PuzProj(3,1,1)], rgbcolor = PuzColor[6])1016Graff += polygon([PuzProj(1,-1,1),PuzProj(3,1,1),PuzProj(3,-1,-1)], rgbcolor = PuzColor[7])1017Graff += polygon([PuzProj(1,-1,1),PuzProj(3,-1,-1),PuzProj(1,-3,-1)], rgbcolor = PuzColor[8])1018Graff += polygon([PuzProj(1,-1,1),PuzProj(1,-3,-1),PuzProj(-1,-3,1)], rgbcolor = PuzColor[9])1019Graff += polygon([PuzProj(1,-1,1),PuzProj(-1,-3,1),PuzProj(-1,-1,3)], rgbcolor = PuzColor[10])1020Graff += polygon([PuzProj(1,-1,1),PuzProj(-1,-1,3),PuzProj(1,1,3)], rgbcolor = PuzColor[11])1021Graff += polygon([PuzProj(1,1,-1),PuzProj(3,1,1),PuzProj(1,3,1)], rgbcolor = PuzColor[12])1022Graff += polygon([PuzProj(1,1,-1),PuzProj(1,3,1),PuzProj(-1,3,-1)], rgbcolor = PuzColor[13])1023Graff += polygon([PuzProj(1,1,-1),PuzProj(-1,3,-1),PuzProj(-1,1,-3)], rgbcolor = PuzColor[14])1024Graff += polygon([PuzProj(1,1,-1),PuzProj(-1,1,-3),PuzProj(1,-1,-3)], rgbcolor = PuzColor[15])1025Graff += polygon([PuzProj(1,1,-1),PuzProj(1,-1,-3),PuzProj(3,-1,-1)], rgbcolor = PuzColor[16])1026Graff += polygon([PuzProj(1,1,-1),PuzProj(3,-1,-1),PuzProj(3,1,1)], rgbcolor = PuzColor[17])1027Graff += line([(-0.3214, 0.0625),(0.8214, 0.0625)], rgbcolor = (0,0,0))1028Graff += line([(-0.57857, -0.6875),(0.33571, 0.9125)], rgbcolor = (0,0,0))1029Graff += line([(0.27857, -0.6875),(-0.23571, 0.2125)], rgbcolor = (0,0,0))1030Graff += line([PuzProj(3,1,1), PuzProj(1,3,1), PuzProj(1,1,3), PuzProj(3,1,1), PuzProj(3,-1,-1), PuzProj(-1,-1,3), PuzProj(-1,3,-1), PuzProj(3,-1,-1), PuzProj(1,-3,-1), PuzProj(1,1,3), PuzProj(-3,1,-1), PuzProj(-3,-1,1), PuzProj(1,3,1), PuzProj(1,-1,-3), PuzProj(-1,1,-3), PuzProj(3,1,1), PuzProj(-1,-3,1), PuzProj(-1,-1,3), PuzProj(1,1,3)], rgbcolor = (0,0,0))1031Graff += line([PuzProj(1,-3,-1),PuzProj(-1,-3,1)], rgbcolor = (0,0,0))1032Graff += line([PuzProj(3,-1,-1),PuzProj(1,-1,-3)], rgbcolor = (0,0,0))1033Graff += line([PuzProj(-1,-1,3),PuzProj(-3,-1,1)], rgbcolor = (0,0,0))1034Graff += line([PuzProj(-1,3,-1),PuzProj(1,3,1)], rgbcolor = (0,0,0))1035Graff += line([PuzProj(-1,3,-1),PuzProj(-3,1,-1)], rgbcolor = (0,0,0))1036Graff += line([PuzProj(-1,3,-1),PuzProj(-1,1,-3)], rgbcolor = (0,0,0))1037else:1038Graff += polygon([PuzProj(-1,-1,-1),PuzProj(-3,1,-1),PuzProj(-3,-1,1)], rgbcolor = PuzColor[18])1039Graff += polygon([PuzProj(-1,-1,-1),PuzProj(-1,-3,1),PuzProj(1,-3,-1)], rgbcolor = PuzColor[20])1040Graff += polygon([PuzProj(-1,-1,-1),PuzProj(1,-1,-3),PuzProj(-1,1,-3)], rgbcolor = PuzColor[22])1041Graff += polygon([PuzProj(-1,1,1),PuzProj(1,1,3),PuzProj(-1,-1,3)], rgbcolor = PuzColor[1])1042Graff += polygon([PuzProj(-1,1,1),PuzProj(-3,-1,1),PuzProj(-3,1,-1)], rgbcolor = PuzColor[3])1043Graff += polygon([PuzProj(-1,1,1),PuzProj(-1,3,-1),PuzProj(1,3,1)], rgbcolor = PuzColor[5])1044Graff += polygon([PuzProj(1,-1,1),PuzProj(3,1,1),PuzProj(3,-1,-1)], rgbcolor = PuzColor[7])1045Graff += polygon([PuzProj(1,-1,1),PuzProj(1,-3,-1),PuzProj(-1,-3,1)], rgbcolor = PuzColor[9])1046Graff += polygon([PuzProj(1,-1,1),PuzProj(-1,-1,3),PuzProj(1,1,3)], rgbcolor = PuzColor[11])1047Graff += polygon([PuzProj(1,1,-1),PuzProj(1,3,1),PuzProj(-1,3,-1)], rgbcolor = PuzColor[13])1048Graff += polygon([PuzProj(1,1,-1),PuzProj(-1,1,-3),PuzProj(1,-1,-3)], rgbcolor = PuzColor[15])1049Graff += polygon([PuzProj(1,1,-1),PuzProj(3,-1,-1),PuzProj(3,1,1)], rgbcolor = PuzColor[17])1050Graff += line([PuzProj(3,1,1), PuzProj(-1,-3,1), PuzProj(1,-3,-1), PuzProj(1,1,3), PuzProj(-1,-1,3), PuzProj(3,-1,-1), PuzProj(3,1,1), PuzProj(-1,1,-3), PuzProj(1,-1,-3), PuzProj(1,3,1), PuzProj(-1,3,-1), PuzProj(3,-1,-1)], rgbcolor = (0,0,0))1051Graff += line([PuzProj(1,3,1), PuzProj(-3,-1,1), PuzProj(-3,1,-1), PuzProj(1,1,3)], rgbcolor = (0,0,0))1052Graff += line([PuzProj(-1,-1,3), PuzProj(-1,3,-1)], rgbcolor = (0,0,0))1053Graff += line([(-2.15, 0.0625), (-1.2457, 0.0625)], rgbcolor = (0,0,0))1054Graff += line([(1.85, 0.0625), (1.3357, 0.0625)], rgbcolor = (0,0,0))1055Graff += line([(0.85, -1.6875), (0.5643, -1.1875)], rgbcolor = (0,0,0))1056Graff += line([(-1.15, -1.6875), (-0.8543, -1.1875)], rgbcolor = (0,0,0))1057Graff += line([(-1.15, 1.8125), (-0.6929, 1.0125)], rgbcolor = (0,0,0))1058Graff += line([(0.85, 1.8125), (0.5929, 1.3625)], rgbcolor = (0,0,0))1059Graff += line([(-0.6237, -0.7664), (0.3763, 0.9836)], rgbcolor = (0,0,0))1060Graff += line([(-0.87, 0.0625), (1.13, 0.0625)], rgbcolor = (0,0,0))1061Graff += line([(0.4654, -1.0144), (-0.5346, 0.7356)], rgbcolor = (0,0,0))1062return Graff.show(axes = false, aspect_ratio = 1)10631064def InitPuzzle():1065global PuzColor1066global f1067global b1068global r1069global l1070f = var('f')1071b = var('b')1072r = var('r')1073l = var('l')1074PuzColor = [(1,0,0), (1,0,0), (1,0,0), (1,0,0), (1,0,0), (1,0,0), (1,1,0), (1,1,0), (1,1,0), (1,1,0), (1,1,0), (1,1,0), (0,1,0), (0,1,0), (0,1,0), (0,1,0), (0,1,0), (0,1,0), (0,0,1), (0,0,1), (0,0,1), (0,0,1), (0,0,1), (0,0,1)]1075return ShowPuzzle()10761077def RotatePuzzle(*args):1078global PuzColor1079for arg in args:1080if arg == f:1081Temp = PuzColor[0]1082PuzColor[0] = PuzColor[6]1083PuzColor[6] = PuzColor[12]1084PuzColor[12] = Temp1085Temp = PuzColor[1]1086PuzColor[1] = PuzColor[7]1087PuzColor[7] = PuzColor[13]1088PuzColor[13] = Temp1089Temp = PuzColor[5]1090PuzColor[5] = PuzColor[11]1091PuzColor[11] = PuzColor[17]1092PuzColor[17] = Temp1093elif arg == b:1094Temp = PuzColor[1]1095PuzColor[1] = PuzColor[18]1096PuzColor[18] = PuzColor[9]1097PuzColor[9] = Temp1098Temp = PuzColor[2]1099PuzColor[2] = PuzColor[19]1100PuzColor[19] = PuzColor[10]1101PuzColor[10] = Temp1102Temp = PuzColor[3]1103PuzColor[3] = PuzColor[20]1104PuzColor[20] = PuzColor[11]1105PuzColor[11] = Temp1106elif arg == r:1107Temp = PuzColor[3]1108PuzColor[3] = PuzColor[13]1109PuzColor[13] = PuzColor[22]1110PuzColor[22] = Temp1111Temp = PuzColor[4]1112PuzColor[4] = PuzColor[14]1113PuzColor[14] = PuzColor[23]1114PuzColor[23] = Temp1115Temp = PuzColor[5]1116PuzColor[5] = PuzColor[15]1117PuzColor[15] = PuzColor[18]1118PuzColor[18] = Temp1119elif arg == l:1120Temp = PuzColor[7]1121PuzColor[7] = PuzColor[20]1122PuzColor[20] = PuzColor[15]1123PuzColor[15] = Temp1124Temp = PuzColor[8]1125PuzColor[8] = PuzColor[21]1126PuzColor[21] = PuzColor[16]1127PuzColor[16] = Temp1128Temp = PuzColor[9]1129PuzColor[9] = PuzColor[22]1130PuzColor[22] = PuzColor[17]1131PuzColor[17] = Temp1132else:1133print("Invalid rotation")1134ShowPuzzle()1135return None11361137def PuzzlePosition1():1138global PuzColor1139PuzColor = [(1,1,0), (1,0,0), (1,0,0), (1,0,0), (1,0,0), (1,0,0), (0,1,0), (1,1,0), (1,1,0), (1,1,0), (1,1,0), (1,1,0), (1,0,0), (0,1,0), (0,1,0), (0,1,0), (0,1,0), (0,1,0), (0,0,1), (0,0,1), (0,0,1), (0,0,1), (0,0,1), (0,0,1)]1140return ShowPuzzle()11411142def PuzzlePosition2():1143global PuzColor1144PuzColor = [(1,0,0), (1,1,0), (1,0,0), (0,0,1), (1,0,0), (0,1,0), (1,1,0), (0,1,0), (1,1,0), (0,0,1), (1,1,0), (1,0,0), (0,1,0), (1,0,0), (0,1,0), (0,0,1), (0,1,0), (1,1,0), (1,0,0), (0,0,1), (1,1,0), (0,0,1), (0,1,0), (0,0,1)]1145return ShowPuzzle()11461147def HideCorners():1148global PuzColor1149for i in [0,2,4,6,8,10,12,14,16,19,21,23]:1150PuzColor[i] = 01151return ShowPuzzle()115211531154def ShowRationals(a,b):1155import fractions1156A = numerical_approx(a)1157B = numerical_approx(b)1158if A == B:1159print "Endpoints must be different"1160return None1161if A > B:1162q = A1163A = B1164B = q1165QuoList = []1166r = 11167q = 11168while r < 18:1169PrintPoint = False1170for p in range(ceil(A*q), B*q +1):1171if fractions.gcd(p, q) == 1:1172QuoList.append((numerical_approx(p)/numerical_approx(q), 0.75^(r-1)))1173PrintPoint = True1174if PrintPoint:1175r = r+11176q = q+11177P = list_plot(QuoList, aspect_ratio = (B-A)/2, frame = True, axes = False)1178# G = Graphics()1179# G += point(QuoList)1180# G.show(aspect_ratio = (B-A)/2, frame = True, axes = False)1181# return G1182return P11831184def CountableQ(n):1185import fractions1186global QuoList1187global G1188G = Graphics()1189NN = abs(floor(n))1190if NN == 0:1191NN = 11192QuoList = []1193LineList = []1194for q in range(1,17):1195for p in range(-NN*q, NN*q +1):1196if fractions.gcd(p, q) == 1:1197QuoList.append((numerical_approx(p)/numerical_approx(q), 0.75^(q-1)))1198G += point(QuoList)1199for q in range(-floor(NN/2),(NN+1)/2):1200G += line([(2*q,1),(2*q+1,1)])1201for q in range(1, NN+1):1202G += line([(q-.5,.75),(q,1)])1203G += line([(-q+.5,.75),(-q,1)])1204G += line([(-numerical_approx(1)/(q+1),(.75)^q),(numerical_approx(1)/(q+1),(.75)^q)])1205for p in range(1,NN):1206for q in range(2, NN-p+2):1207G += line([(p+numerical_approx(1)/(q+1)-1,(.75)^q), (p-numerical_approx(1)/(q+1),(.75)^q), (p+numerical_approx(1)/q,(.75)^(q-1))])1208G += line([(1-p-numerical_approx(1)/(q+1),(.75)^q), (numerical_approx(1)/(q+1)-p,(.75)^q), (-p-numerical_approx(1)/q,(.75)^(q-1))])1209return G.show(aspect_ratio = NN, frame = True, axes = False)12101211def PolarPlot():1212Graff = Graphics()1213Graff.set_aspect_ratio(1.0)1214Graff += line([(0,0), (3,4)], rgbcolor=(0,0,0))1215Graff += arc((0,0), 1, sector = (0, 0.92729), rgbcolor=(0,0,0))1216Graff += text("0", (0.7, 0.4), rgbcolor=(0,0,0), fontsize = 18)1217Graff += text("-", (0.7, 0.4), rgbcolor=(0,0,0), fontsize = 18) # How to make a theta1218Graff += text("r", (1.5, 2.5), rgbcolor=(0,0,0), fontsize = 18)1219Graff += text("(x,y) = x + y i", (3.2, 4), rgbcolor=(0,0,0), horizontal_alignment = "left", fontsize = 18)1220return Graff.show()12211222def DrawGalois(n):1223Graff = Graphics()1224Graff += line([(.47, .95), (0.05, .55)], rgbcolor=(0,0,0))1225Graff += line([(.49, .95), (0.35, .55)], rgbcolor=(0,0,0))1226Graff += line([(.51, .95), (0.65, .55)], rgbcolor=(0,0,0))1227Graff += line([(.53, .95), (0.95, .55)], rgbcolor=(0,1,0))1228Graff += line([(.47, .05), (0.05, .45)], rgbcolor=(0,1,0))1229Graff += line([(.49, .05), (0.35, .45)], rgbcolor=(0,1,0))1230Graff += line([(.51, .05), (0.65, .45)], rgbcolor=(0,1,0))1231Graff += line([(.53, .05), (0.95, .45)], rgbcolor=(0,1,0))1232Graff += line([(.50, .05), (0.50, .95)], rgbcolor=(0,1,0))1233Graff += text("Z", (0.76, 0.77), rgbcolor=(0,0,1), fontsize = 12)1234Graff += text("2", (0.775, 0.76), rgbcolor=(0,0,1), fontsize = 9)1235Graff += text("S", (0.52, 0.77), rgbcolor=(0,0,1), fontsize = 12)1236Graff += text("3", (0.535, 0.76), rgbcolor=(0,0,1), fontsize = 9)1237Graff += text("Z", (0.22, 0.23), rgbcolor=(0,0,1), fontsize = 12)1238Graff += text("2", (0.235, 0.22), rgbcolor=(0,0,1), fontsize = 9)1239Graff += text("Z", (0.39, 0.23), rgbcolor=(0,0,1), fontsize = 12)1240Graff += text("2", (0.405, 0.22), rgbcolor=(0,0,1), fontsize = 9)1241Graff += text("Z", (0.59, 0.23), rgbcolor=(0,0,1), fontsize = 12)1242Graff += text("2", (0.605, 0.22), rgbcolor=(0,0,1), fontsize = 9)1243Graff += text("Z", (0.76, 0.23), rgbcolor=(0,0,1), fontsize = 12)1244Graff += text("3", (0.775, 0.22), rgbcolor=(0,0,1), fontsize = 9)1245if n == 1:1246Graff += text("Q", (0.494, 1.0), rgbcolor=(0,0,0), fontsize = 12, horizontal_alignment = "left")1247Graff += text("I", (0.501, 1.0), rgbcolor=(0,0,0), fontsize = 10, horizontal_alignment = "left")1248Graff += text("Q(2 )", (-0.04, 0.5), rgbcolor=(0,0,0), fontsize = 12, horizontal_alignment = "left")1249Graff += text("I", (-0.033, 0.5), rgbcolor=(0,0,0), fontsize = 10, horizontal_alignment = "left")1250Graff += text("1/3", (0.005, 0.51), rgbcolor=(0,0,0), fontsize = 9, horizontal_alignment = "left")1251Graff += text("Q(w 2 )", (0.30, 0.5), rgbcolor=(0,0,0), fontsize = 12, horizontal_alignment = "left")1252Graff += text("I", (0.307, 0.5), rgbcolor=(0,0,0), fontsize = 10, horizontal_alignment = "left")1253Graff += text("1/3", (0.38, 0.51), rgbcolor=(0,0,0), fontsize = 9, horizontal_alignment = "left")1254Graff += text("3", (0.35, 0.49), rgbcolor=(0,0,0), fontsize = 9, horizontal_alignment = "left")1255Graff += text("Q(w 2 )", (0.60, 0.5), rgbcolor=(0,0,0), fontsize = 12, horizontal_alignment = "left")1256Graff += text("I", (0.607, 0.5), rgbcolor=(0,0,0), fontsize = 10, horizontal_alignment = "left")1257Graff += text("2 1/3", (0.653, 0.51), rgbcolor=(0,0,0), fontsize = 9, horizontal_alignment = "left")1258Graff += text("3", (0.65, 0.49), rgbcolor=(0,0,0), fontsize = 9, horizontal_alignment = "left")1259Graff += text("Q((-3) )", (1.0, 0.5), rgbcolor=(0,0,0), fontsize = 12, horizontal_alignment = "left")1260Graff += text("1/2", (1.08, 0.51), rgbcolor=(0,0,0), fontsize = 9, horizontal_alignment = "left")1261Graff += text("I", (1.007, 0.5), rgbcolor=(0,0,0), fontsize = 10, horizontal_alignment = "left")1262Graff += text("Q(2 , w 2 )", (0.4, 0.0), rgbcolor=(0,0,0), fontsize = 12, horizontal_alignment = "left")1263Graff += text("I", (0.407, 0.0), rgbcolor=(0,0,0), fontsize = 10, horizontal_alignment = "left")1264Graff += text("1/3 1/3", (0.443, 0.01), rgbcolor=(0,0,0), fontsize = 9, horizontal_alignment = "left")1265Graff += text("3", (0.513, -0.01), rgbcolor=(0,0,0), fontsize = 9, horizontal_alignment = "left")1266if n == 2:1267Graff += text("S", (0.49, 1.0), rgbcolor=(0,0,0), fontsize = 12)1268Graff += text("3", (0.505, 0.99), rgbcolor=(0,0,0), fontsize = 9)1269Graff += text("{(), (1 2)}", (0.0, 0.5), rgbcolor=(0,0,0), fontsize = 12, horizontal_alignment = "right")1270Graff += text("{(), (1 3)}", (0.35, 0.5), rgbcolor=(0,0,0), fontsize = 12)1271Graff += text("{(), (2 3)}", (0.65, 0.5), rgbcolor=(0,0,0), fontsize = 12)1272Graff += text("A", (1.0, 0.5), rgbcolor=(0,0,0), fontsize = 12)1273Graff += text("3", (1.015, 0.49), rgbcolor=(0,0,0), fontsize = 9)1274Graff += text("{()}", (0.5, 0.0), rgbcolor=(0,0,0), fontsize = 12)1275return Graff.show(axes = False)12761277######################################################1278## ##1279## Utility routines - each of these does something ##1280## simple in a Python way ##1281## ##1282######################################################12831284def Flatten(Set): # Fast way to flatten a list by 1 level1285if isinstance(Set, list):1286if len(Set) > 0:1287if isinstance(Set[0], (list, GroupSet)):1288return [item for sublist in Set for item in sublist]1289return Set12901291def Uniquify(Set): # Eliminate duplicates in a list while preserving order1292if isinstance(Set, list):1293out = []1294[out.append(item) for item in Set if item not in out]1295return out12961297def IsSubset(shortlist, longlist): # determines if shortlist is a subset of longlist1298for item in shortlist:1299if not(item in longlist):1300return false1301return true13021303def Intersection(arg1, *args):1304if len(args) == 0: #If there is only one arguement, see if it is a list of lists.1305T = list(arg1)1306if isinstance(T[0], list):1307S = Uniquify(T[0])1308for i in range(1,len(T)):1309S = [item for item in S if item in T[i] ]1310return sorted(S)1311if isinstance(arg1, GroupSet):1312S = Uniquify(arg1._List)1313else:1314S = Uniquify(arg1)1315for arg in args:1316if isinstance(arg, GroupSet):1317S = [item for item in S if item in arg._List ]1318else:1319S = [item for item in S if item in arg ]1320if isinstance(arg1, GroupSet):1321return GroupSet(sorted(S))1322return sorted(S)13231324def ConvertIdentity(str): # Converts <Identity ...> in a string to the name of the identity1325ss = str1326ii = ss.find("<") # if we find a <, we can assume that everything up to > will be converted to1327if ii > -1: # the identity element.1328jj = ss.find(">",ii)1329if jj > -1: # here is a twist. When the identity element is displayed in a pc1330if(len(ss)>jj+7): # group, it is "<identity> of ..." (of... is not in the brackets!)1331if(ss[jj+1:jj+8] == " of ..."):1332jj = jj + 71333ss = ss[:ii] + IdentityElement + ss[jj+1:]1334return ss13351336#################################################################1337## ##1338## Many of the group elements are defined as instances of a ##1339## class. Here we see the classes that define the different ##1340## types of groups. ##1341## ##1342#################################################################13431344class SumModN:1345def __init__(self, x, mod):1346self._x = x1347self._mod = mod1348def __eq__(self, other):1349if isinstance(other, SumModN):1350return (self._x == other._x) and (self._mod == other._mod)1351else:1352return NotImplemented1353def __ne__(self, other):1354result = self.__eq__(other)1355if result is NotImplemented:1356return result1357return not result1358def __mul__(self, other):1359if isinstance(other, SumModN):1360if self._mod == other._mod:1361return SumModN((self._x + other._x) % self._mod, self._mod )1362return NotImplemented1363# We also allow addition by an integer, to allow for the CircleGraph with Add(n)1364def __add__(self, other):1365if isinstance(other, (int, long, IntType)):1366return SumModN((self._x + other) % self._mod, self._mod )1367return NotImplemented1368def __radd__(self, other):1369if isinstance(other, (int, long, IntType)):1370return SumModN((self._x + other) % self._mod, self._mod )1371return NotImplemented1372def __pow__(self, exp):1373if isinstance(exp, (int, long, IntType)):1374return SumModN((self._x * exp) % self._mod, self._mod )1375return NotImplemented1376# We want subgroups to appear in order, such as [0,2,4,6,8]1377def __cmp__(self, other):1378if isinstance(other, SumModN):1379if self._mod == other._mod:1380return cmp(self._x, other._x)1381else:1382return NotImplemented1383def __str__(self):1384return str(self._x)1385def __repr__(self):1386return str(self._x)13871388####################################################################1389##1390## The GroupSet class is in essence a wrapper for a list of elements.1391## GroupSets differ from lists and sets in the following ways:1392## 1) we can add and multiply two GroupSets together, like cosets1393## 2) Unlike sets, GroupSets can contain mutable items, such as other GroupSets1394## 3) Unlike sets, GroupSets do not have to be sorted, although the sum and1395## product of GroupSets yields a _sorted_ GroupSet, for comparison1396## 4) Unlike lists, GroupSets are displayed with curly braces1397##1398########################################################################13991400class GroupSet:1401def __init__(self, L):1402if isinstance(L, list):1403NewList = []1404for item in L: # Strip the wrappers off of the Gap elements1405if isinstance(item, GapElement):1406NewList.append(item._x)1407else:1408NewList.append(item)1409self._List = NewList1410else:1411return NotImplemented1412def __eq__(self, other):1413if isinstance(other, GroupSet):1414# Since both cosets should already be sorted, we just have to compare the lists to each other.1415return(self._List == other._List)1416return NotImplemented1417def __ne__(self, other):1418result = self.__eq__(other)1419if result is NotImplemented:1420return result1421return not result1422def __mul__(self, other):1423if isinstance(other, GroupSet):1424# we are multiplying two Cosets together1425if (len(self._List) == 0) or (len(other._List) == 0): # if either is the empty coset, return the empty coset.1426return GroupSet([])1427if isinstance(self._List[0], GapInstance) and isinstance(other._List[0], GapInstance):1428# deligate the job to Gap for speed purposes1429# if _QuickMult_ is set to true, we can assume that these are both cosets of the same subgroup.1430# in which case, we can save time by multiplying the FIRST element of the first coset by the second coset.1431# Actually, this didn't save time1432#if _QuickMult_:1433# LL1 = gap([self._List[0]])1434#else:1435LL1 = gap(self._List)1436LL2 = gap(other._List)1437G = gap([])1438LL1.LeftSide()1439LL2.RightSide()1440Prod1 = list(G.Prod())1441return GroupSet(Prod1)1442else:1443# Find all possible products1444Prod = []1445for x1 in self._List:1446for x2 in other._List:1447## We could be dealing with cosets of cosets.1448## In which case, there is the posibility that x1 or x2 is a GroupSet1449## This will not cause a problem if x1 is a GroupSet, since we will just have recursion.1450## But if x2 is a GroupSet and x1 is NOT, we need to convert x1 to a GroupSet with a single element.1451if isinstance(x2, GroupSet) and (not isinstance(x1, GroupSet)):1452p = GroupSet([x1]) * x21453else:1454p = x1 * x21455Prod.append(p)1456Prod.sort()1457for i in range(len(Prod)-1,0,-1): # Count backwards1458if Prod[i] == Prod[i-1]: # Delete duplicates - which will be together after the sort1459del Prod[i]1460return GroupSet(Prod)1461# if other is not a coset, it might be a list or single element1462if isinstance(other, list):1463return self * GroupSet(other)1464# last chance - single element1465return self * GroupSet([other])1466def __rmul__(self, other):1467# obviously other is not a GroupSet, or it would have been covered by __mul__1468if isinstance(other, list):1469return GroupSet(other) * self1470# last chance - single element1471return GroupSet([other]) * self1472def __add__(self, other):1473if isinstance(other, GroupSet):1474# we are adding two Cosets together1475if (len(self._List) == 0) or (len(other._List) == 0): # if either is the empty coset, return the empty coset.1476return GroupSet([])1477if isinstance(self._List[0], GapInstance) and isinstance(other._List[0], GapInstance):1478# deligate the job to Gap for speed purposes1479LL1 = gap(self._List)1480LL2 = gap(other._List)1481LL1.LeftSide()1482Sum1 = list(LL2.AddCoset())1483return GroupSet(Sum1)1484else:1485# Find all possible sums1486PSum = []1487for x1 in self._List:1488for x2 in other._List:1489## We could be dealing with cosets of cosets.1490## In which case, there is the posibility that x1 or x2 is a GroupSet1491## This will not cause a problem if x1 is a GroupSet, since we will just have recursion.1492## But if x2 is a GroupSet and x1 is NOT, we need to convert x1 to a GroupSet with a single element.1493if isinstance(x2, GroupSet) and (not isinstance(x1, GroupSet)):1494p = GroupSet([x1]) * x21495else:1496p = x1 * x21497PSum.append(p)1498PSum.sort()1499for i in range(len(PSum)-1,0,-1): # Count backwards1500if PSum[i] == PSum[i-1]: # Delete duplicates - which will be together after the sort1501del PSum[i]1502return GroupSet(PSum)1503# if other is not a coset, it might be a list or single element1504if isinstance(other, list):1505return self + GroupSet(other)1506# last chance - single element1507return self + GroupSet([other])1508def __radd__(self, other):1509# obviously other is not a GroupSet, or it would have been covered by __add__1510if isinstance(other, list):1511return GroupSet(other) + self1512# last chance - single element1513return GroupSet([other]) + self1514def __neg__(self):1515# this is only used for rings. We take the negative of all of the elements in the list.1516N = []1517for item in self._List:1518N.append(-item)1519N.sort()1520return GroupSet(N)1521def __sub__(self, other):1522return self + (-other) # this should work is all cases.1523def __cmp__(self, other):1524if isinstance(other, GroupSet):1525return cmp(sorted(self._List), sorted(other._List))1526return NotImplemented1527def __len__(self):1528return len(self._List)1529def __iter__(self): # Allows iteration constructions such as1530return iter(self._List) # [order(x) for x in G]1531def __getitem__(self, index):1532return self._List[index]1533def __pow__(self, exp): # Raising a set to a power, think of it as a coset (element of a quotient group)1534if isinstance(exp, (int, long, IntType)):1535x = self._List[0]^(exp - 1)1536return self * x1537return NotImplemented1538def Sort(self):1539self._List.sort()1540return None1541def Conformity(self, other): # puts the elements in self into the format given in the larger group other1542if isinstance(self._List[0], GroupSet):1543## self is a coset of a coset. Recursively do the Conformity to each of self's members1544for i in range(len(self._List)):1545self._List[i].Conformity(other)1546return self1547if isinstance(other, GroupSet):1548if isinstance(other._List[0], GapInstance): # if not a Gap object, do nothing1549Ggap = gap(other._List)1550Ggap.OriginalGroup()1551Lgap = gap(self._List)1552L = Lgap.Conform()1553self._List = list(L)1554return self1555def __str__(self):1556s = str(self._List)1557s = "{" + s[1:-1] + "}" # replace the square brackets (guaranteed to be on the outside) with curly braces.1558s = ConvertIdentity(s)1559return(s)1560def __repr__(self):1561return str(self)15621563# this is a class wrapper that wraps around Gap elements, so that we can apply non-Gap operations on it.15641565class GapElement:1566def __init__(self, x):1567if isinstance(x, GapInstance):1568self._x = x1569else:1570return NotImplemented1571def __eq__(self, other):1572if isinstance(other, GapElement):1573return(self._x == other._x)1574# if isinstance(other, GapInstance): #Don't rely on this, since other == self would always give False1575# return(self._x == other)1576return NotImplemented1577def __ne__(self, other):1578result = self.__eq__(other)1579if result is NotImplemented:1580return result1581return not result1582def __cmp__(self, other):1583if isinstance(other, GapElement):1584return cmp(self._x, other._x)1585return NotImplemented1586def __mul__(self, other):1587if isinstance(other, GapElement):1588return GapElement(self._x * other._x)1589if isinstance(other, GroupSet):1590return GroupSet([self]) * other1591if isinstance(other, GapInstance):1592return GapElement(self._x * other)1593if isinstance(other, (int, long, IntType)):1594return GapElement(self._x * other)1595return NotImplemented1596def __rmul__(self, other):1597if isinstance(other, (int, long, IntType)):1598return GapElement(other * self._x)1599# all other cases should have been covered by __mul__1600return NotImplemented1601def __add__(self, other):1602if isinstance(other, GapElement):1603return GapElement(self._x + other._x)1604if isinstance(other, GroupSet):1605return GroupSet([self]) + other1606return NotImplemented1607def __sub__(self, other):1608if isinstance(other, GapElement):1609return GapElement(self._x - other._x)1610if isinstance(other, GroupSet):1611return GroupSet([self]) + (-other)1612return NotImplemented1613def __neg__(self):1614return GapElement(- self._x)1615def __pow__(self, exp):1616if isinstance(exp, (int, long, IntType)):1617return GapElement(self._x^exp)1618return NotImplemented1619def __str__(self):1620s = str(self._x)1621s = ConvertIdentity(s)1622return s1623def __repr__(self): # Make the wrapper invisible1624return str(self)1625162616271628class Homomorph:1629def __init__(self, domain, target):1630# convert domain and target to lists, and strip off wrappers if present.1631if isinstance(domain, GroupSet):1632self.Domain = domain._List1633elif isinstance(domain, list):1634NewList = []1635for item in domain: # Strip the wrappers off of the Gap elements1636if isinstance(item, GapElement):1637NewList.append(item._x)1638else:1639NewList.append(item)1640self.Domain = NewList1641else:1642print("Domain must be a group or list")1643if isinstance(target, GroupSet):1644self.Target = target._List1645elif isinstance(target, list):1646NewList = []1647for item in target: # Strip the wrappers off of the Gap elements1648if isinstance(item, GapElement):1649NewList.append(item._x)1650else:1651NewList.append(item)1652self.Target = NewList1653else:1654print("Target must be a group or list")1655# TODO: we may eventually allow target to be a field1656self.In = []1657self.Out = []1658self.IsRing = false # For RingHomo, this would be set to true after initialization1659def Set(self, input, output): ## Assumes the wrappers are stripped from input and output1660# Always conform both the input and output to the Domain and Target.1661# For groups, this will put the elements in standard form (ListGroup format)1662# For rings, it will allow us to enter 1 for (1 mod 6).1663if input in self.In:1664self.Out[self.In.index(input)] = self.Target[self.Target.index(output)]1665else:1666self.In.append(self.Domain[self.Domain.index(input)])1667self.Out.append(self.Target[self.Target.index(output)])1668def __call__(self, value):1669valuex = value1670if isinstance(value, GapElement):1671valuex = value._x1672try:1673output = self.Out[self.In.index(valuex)]1674if isinstance(output, GapInstance):1675return GapElement(output)1676return output1677except ValueError:1678print("Homomorphism is not defined at that value.")1679def Finish(self):1680gen = copy(self.In)1681grp = copy(self.In)1682if isinstance(gen[0], GapInstance): # Domain consists of Gap elements, so we use Gap to assist.1683# First find the multiplication table of the domain1684fDomain = gap(self.Domain) #1685M = fDomain.QuickTable() # will produce a 1 index matrix1686if self.IsRing:1687A = fDomain.QuickAdd() # will produce a 1 index matrix for addition1688# convert the homomorphism to a translation dictionary, which uses an integer for the element1689tran = [self.Domain.index(key) + 1 for key in gen]1690gen = copy(tran)1691grp = copy(tran)1692for g1 in grp:1693for g2 in gen:1694z = int(M[g1][g2])1695prod = self.Out[grp.index(g1)] * self.Out[grp.index(g2)] # multiplication is in the target group - so no tables1696if self.IsRing and isinstance(prod, GroupSet):1697prod = prod + self.Out[0] + (-self.Out[0])1698if z in grp:1699# z is already in the list, check that fun[g1]*fun[g2] = fun[g1*g2]1700if prod != self.Out[grp.index(z)]:1701print str(self.Out[grp.index(g1)]) + " * " + str(self.Out[grp.index(g2)] ) + " is not " + str(self.Out[grp.index(z)])1702return "Homomorphism failed"1703else:1704if not(prod in self.Target):1705print str(prod) + " is not in target."1706return "Homomorphism failed"1707prod = self.Target[self.Target.index(prod)]1708grp.append(z)1709self.In.append(self.Domain[z-1])1710self.Out.append(prod)1711if self.IsRing:1712z = int(A[g1][g2])1713if z in grp:1714# z is already in the list, check that fun[g1] + fun[g2] = fun[g1 + g2]1715if self.Out[grp.index(g1)] + self.Out[grp.index(g2)] != self.Out[grp.index(z)]:1716print str(self.Out[grp.index(g1)]) + " + " + str(self.Out[grp.index(g2)] ) + " is not " + str(self.Out[grp.index(z)])1717return "Homomorphism failed"1718else:1719summ = self.Out[grp.index(g1)] + self.Out[grp.index(g2)]1720if not(summ in self.Target):1721print str(prod) + " is not in target."1722return "Homomorphism failed"1723grp.append(z)1724self.In.append(self.Domain[z-1])1725self.Out.append(summ)1726else:1727for g1 in grp:1728for g2 in gen:1729z = g1 * g21730prod = self.Out[self.In.index(g1)] * self.Out[self.In.index(g2)]1731if self.IsRing and isinstance(z, GroupSet):1732z = z + g1 + (-g1) #completes the coset1733if self.IsRing and isinstance(prod, GroupSet):1734prod = prod + self.Out[0] + (-self.Out[0])1735if z in grp:1736# z is equivalent to an element in grp, but may have a different form1737# replace z with the form that is in grp.1738# z = grp[grp.index(z)]1739if prod != self.Out[self.In.index(z)]:1740print str(self.Out[self.In.index(g1)])+" * "+str(self.Out[self.In.index(g2)])+" is not "+str(self.Out[self.In.index(z)])1741return "Homomorphism failed"1742else:1743if not(prod in self.Target):1744print str(prod) + " is not in target."1745return "Homomorphism failed"1746prod = self.Target[self.Target.index(prod)]1747grp.append(z)1748self.In.append(z)1749self.Out.append(prod)1750if self.IsRing:1751z = g1 + g21752if z in grp:1753if self.Out[self.In.index(g1)] + self.Out[self.In.index(g2)] != self.Out[self.In.index(z)]:1754print str(self.Out[self.In.index(g1)])+" + "+str(self.Out[self.In.index(g2)])+" is not "+str(self.Out[self.In.index(z)])1755return "Homomorphism failed"1756else:1757summ = self.Out[self.In.index(g1)] + self.Out[self.In.index(g2)]1758if not(summ in self.Target):1759print str(summ) + " is not in target."1760return "Homomorphism failed"1761grp.append(z)1762self.In.append(z)1763self.Out.append(summ)1764if len(self.In) == len(self.Domain):1765return "Homomorphism defined"1766else:1767return "Homomorphism consistent, but not defined for the whole domain."1768def GetDomain(self):1769return self.Domain1770def GetRange(self):1771# This is what gets tricky. We want to remove duplicates in self.Out, and put them in the order of self.Target1772tempRange = []1773for g1 in self.Target:1774if (g1 in self.Out):1775tempRange.append(g1)1776return tempRange1777def Inv(self, value):1778# On input, value will be a list of elements (stripped of GapElement).1779# returns a sorted list of elements so that Fun(x) = value1780# value may be a list, and in fact, if the elements of the target are lists, add an extra list to value1781n = len(self.Out)1782R = []1783for i in range(n):1784if self.Out[i] in value:1785if not self.In[i] in R:1786R.append(self.In[i])1787return sorted(R)17881789class FieldHomo:1790def __init__(self):1791self.In = []1792self.Out = []1793self.Key = []1794self.RationalMap = false # assume it is for the current field extension.1795if GenList == []:1796self.RationalMap = true1797def Set(self, input, output):1798if input in self.In:1799self.Out[self.In.index(input)] = output1800else:1801self.In.append(input)1802self.Out.append(output)1803def __call__(self, value):1804x = value1805if self.RationalMap:1806x = TrigReduce(x)1807vec = Compon(x, self.In)1808total = vec[-1] # last component is the rational part1809for i in range(len(self.In)):1810total = total + vec[i]*self.Out[i]1811return expand(total)1812else:1813if self.Key == []: # CheckHomo was never done.1814flag = self.Finish()1815if not(flag):1816return None1817try:1818array = Vectorize(value)1819except:1820# if it did not vectorize, we must have entered a rational number1821return value1822n = len(array)1823total = 01824for i in range(n):1825total = total + array[i]*self.Key[i]1826return total1827def Finish(self): #Really CheckHomo1828# This checks whether the field homomorphism works.1829# In the case of a homomorphism on a field extension, it constructs1830# the vector that is used for the call function1831if self.RationalMap:1832n = len(self.In)1833for i in range(n):1834for j in range(n):1835z = TrigReduce(self.Out[i] * self.Out[j])1836w = TrigReduce(self.In[i] * self.In[j])1837if self(w) != z:1838return "f(" + str(self.In[i]) + ")*f(" + str(self.In[j]) + ") is not " + str(z) + "."1839return true1840else:1841KeyList = []1842n = len(GenList)1843OutList = []1844try:1845for i in range(n):1846OutList.append(self.Out[self.In.index(GenList[i])])1847except:1848print "The mappings for all of the generators has not been defined."1849return false1850iList = [0 for i in DegreeList] # Generalized for loop1851Cont = true1852while Cont:1853ele = 11854for i in range(n):1855ele = ele * OutList[i]^iList[i]1856KeyList.append(ele)1857# increment the iList1858pointer = 01859iList[pointer] = iList[pointer] + 11860while iList[pointer] >= DegreeList[pointer]:1861iList[pointer] = 01862pointer = pointer + 11863if pointer == n:1864Cont = false1865pointer = 01866iList[pointer] = iList[pointer] + 11867self.Key = KeyList1868# Check to see if homomorphism is consistent1869for i in range(n):1870ele1 = GenList[i]^(DegreeList[i]-1)1871ele2 = GenList[i]1872ele3 = GenList[i]^DegreeList[i]1873if self(ele1) * self(ele2) != self(ele3):1874print "Inconsistent definition."1875return false1876return true18771878DisplayPermInt = false18791880class Perm:1881def __init__(self, *args):1882L = list(args)1883if len(L) == 1: # allow for a single argument of a list1884if isinstance(L, list):1885L = L[0]1886# test to see if this is the numbers from 1 to n1887if sorted(L) != range(1,len(L)+1):1888raise ValueError("Must be a rearrangement of the numbers 1 through n.")1889else:1890# delete trailing number if in position1891while (len(L)>0) and (L[-1] == len(L)):1892L.pop()1893self._P = L1894def __eq__(self, other):1895if isinstance(other, Perm):1896return self._P == other._P1897else:1898return NotImplemented1899def __ne__(self, other):1900result = self.__eq__(other)1901if result is NotImplemented:1902return result1903return not result1904def __cmp__(self, other):1905if isinstance(other, Perm):1906if len(self._P) < len(other._P):1907return -11908if len(self._P) > len(other._P):1909return 11910n = len(self._P)1911for i in range(n-1,0,-1):1912if self._P[i] < other._P[i]:1913return 11914if self._P[i] > other._P[i]:1915return -11916return 01917else:1918return NotImplemented1919def __lt__(self, other):1920if isinstance(other, Perm):1921return (self.__cmp__(other) == -1)1922else:1923return NotImplemented1924def __gt__(self, other):1925if isinstance(other, Perm):1926return (self.__cmp__(other) == 1)1927else:1928return NotImplemented1929def __le__(self, other):1930if isinstance(other, Perm):1931return (self.__cmp__(other) < 1)1932else:1933return NotImplemented1934def __ge__(self, other):1935if isinstance(other, Perm):1936return (self.__cmp__(other) > -1)1937else:1938return NotImplemented1939def __call__(self, value):1940if value < 1:1941return value1942try:1943return self._P[value-1]1944except: # probably out of range1945return value1946def __mul__(self, other):1947if isinstance(other, Perm):1948L = copy(other._P)1949M = copy(self._P)1950u = max(len(L), len(M))1951# get both lists to be the same length1952for i in range(len(L)+1, u+1):1953L.append(i)1954for i in range(len(M)+1, u+1):1955M.append(i)1956## The following line uses left to right multiplication, assuming (fog)(x) = g(f(x)).1957# T = [L[M[i]-1] for i in range(u)]1958## The following line uses right to left multiplication, assuming (fog)(x) = f(g(x)).1959T = [M[L[i]-1] for i in range(u)]1960return Perm(T)1961elif isinstance(other, Cycle):1962return self.ToCycle() * other1963else:1964return NotImplemented1965def __pow__(self, exp):1966if isinstance(exp, (int, long, IntType)):1967if exp == 0:1968return Perm()1969elif exp == 1:1970return self1971elif exp > 1:1972return (self^(exp-1)) * self #Recursive definition1973elif exp == -1:1974L = self._P1975u = len(L)1976T = copy(L)1977for i in range(u):1978T[L[i]-1] = i + 11979return Perm(T)1980else: # exp < -11981return (self^(-1))^(-exp)1982else:1983return NotImplemented1984def ToCycle(self):1985T = copy(self._P)1986n = len(T)1987R = []1988for i in range(n):1989if T[i] > 0:1990L = [i+1]1991j = T[i]1992T[i] = 01993while T[j-1] > 0:1994L.append(j)1995k = j1996j = T[k-1]1997T[k-1] = 01998if len(L) > 1:1999R.append(L)2000ret = Cycle()2001ret._C = R2002return ret2003def ToInt(self):2004total = 12005u = len(self._P)2006for i in range(1,u):2007for j in range(i):2008if self._P[j] > self._P[i]:2009total = total + factorial(i)2010return total2011def __str__(self):2012if DisplayPermInt:2013return str(self.ToInt())2014return "P" + str(tuple(self._P))2015def __repr__(self):2016return str(self)20172018# This is a user defined error to handle the case where a non-integer cycle is converted to a permutation.2019class CycleToPermError(Exception):2020def __init__(self, value):2021self.value = value2022def __str__(self):2023return repr(self.value)20242025class Cycle:2026def __init__(self, *args):2027L = list(args)2028if len(L) == 1: # allow for a single argument of a list2029if isinstance(L, list):2030L = L[0]2031# test to see if there are any duplicates in the list2032if len(Uniquify(L)) != len(L):2033raise ValueError("Cannot have duplicates in cycle.")2034elif len(L) == 0:2035self._C = [] # Note that the identity element is stored as an empty list.2036else:2037# Remove GapElement wrappers if necessary2038NewL = []2039for item in L:2040if isinstance(item, GapElement):2041NewL.append(item._x)2042else:2043NewL.append(item)2044# rotate list so that it begins with the smallest value2045n = NewL.index(min(NewL))2046for i in range(n):2047NewL.append(NewL.pop(0))2048self._C = [NewL]2049def __eq__(self, other):2050if isinstance(other, Cycle):2051return self._C == other._C2052else:2053return NotImplemented2054def __ne__(self, other):2055result = self.__eq__(other)2056if result is NotImplemented:2057return result2058return not result2059def __lt__(self, other):2060if isinstance(other, Cycle):2061try:2062return self.ToPerm() < other.ToPerm()2063except CycleToPermError:2064return self._C < other._C2065elif isinstance(other, Perm):2066return self.ToPerm() < other2067else:2068return NotImplemented2069def __le__(self, other):2070if isinstance(other, Cycle):2071try:2072return self.ToPerm() <= other.ToPerm()2073except CycleToPermError:2074return self._C <= other._C2075elif isinstance(other, Perm):2076return self.ToPerm() <= other2077else:2078return NotImplemented2079def __gt__(self, other):2080result = self.__le__(other)2081if result is NotImplemented:2082return result2083return not result2084def __ge__(self, other):2085result = self.__lt__(other)2086if result is NotImplemented:2087return result2088return not result2089def __call__(self, value):2090x = value2091n = len(self._C)2092for i in range(n):2093if x in self._C[i]:2094j = self._C[i].index(x) + 12095if j == len(self._C[i]):2096j = 02097x = self._C[i][j]2098return x2099def __mul__(self, other):2100if isinstance(other, Cycle):2101## The following 2 lines uses left to right multiplication, assuming (fog)(x) = g(f(x)).2102# S = copy(self._C)2103# S.extend(other._C) # combine the two lists into one list of lists2104## The following 2 lines uses right to left multiplication, assuming (fog)(x) = f(g(x)).2105S = copy(other._C)2106S.extend(self._C) # combine the two lists into one list of lists2107m = len(S)2108T = Uniquify(Flatten(S)) # find all of the symbols in the list2109T = sorted(T)2110R = []2111n = len(T)2112for i in range(n):2113if T[i] != None: # None is the indication that this symbol has been used2114L = []2115j = T[i]2116while j in T:2117L.append(j)2118T[T.index(j)] = None2119for k in range(m):2120if j in S[k]:2121q = S[k].index(j)+12122if q == len(S[k]):2123q = 02124j = S[k][q]2125if len(L) > 1:2126R.append(L)2127ret = Cycle()2128ret._C = R2129return ret2130elif isinstance(other, Perm):2131return self * other.ToCycle()2132else:2133return NotImplemented2134def __pow__(self, exp):2135if isinstance(exp, (int, long, IntType)) :2136if exp == 0:2137return Cycle()2138elif exp == 1:2139return self2140elif exp > 1:2141return (self^(exp-1)) * self #Recursive definition2142elif exp == -1:2143R = []2144for L in copy(self._C):2145LL = copy(L)2146LL.reverse()2147LL.insert(0, LL.pop()) # rotates last position to first2148R.append(LL)2149ret = Cycle()2150ret._C = R2151return ret2152else: # exp < -12153return (self^(-1))^(-exp)2154else:2155return NotImplemented2156def __str__(self):2157if len(self._C) == 0:2158return "()"2159s = ""2160for i in range(len(self._C)):2161s = s + str(tuple(self._C[i]))2162s = ConvertIdentity(s)2163return s2164def __repr__(self):2165return str(self)2166def ToPerm(self):2167if len(self._C) == 0:2168return Perm()2169L = Flatten(self._C)2170n = max(L)2171m = min(L)2172## m should be a positive integer, and n should also be an integer, or else return an error.2173if isinstance(n, (int, long, IntType)) and isinstance(m, (int, long, IntType)) and m > 0:2174PP = range(1,n+1)2175for R in self._C:2176m = len(R)2177for i in range(m-1):2178PP[R[i]-1] = R[i+1]2179PP[R[-1]-1] = R[0]2180return Perm(PP)2181else:2182raise CycleToPermError(m)218321842185class Twople:2186def __init__(self, xvalue, yvalue):2187# Strip away wrappers if present2188if isinstance(xvalue, GapElement):2189self._x = xvalue._x2190else:2191self._x = xvalue2192if isinstance(yvalue, GapElement):2193self._y = yvalue._x2194else:2195self._y = yvalue2196def __eq__(self, other):2197if isinstance(other, Twople):2198return (self._x == other._x) and (self._y == other._y)2199else:2200return NotImplemented2201def __ne__(self, other):2202result = self.__eq__(other)2203if result is NotImplemented:2204return result2205return not result2206def __mul__(self, other):2207if isinstance(other, Twople):2208return Twople( self._x * other._x, self._y * other._y )2209else:2210return NotImplemented2211def __pow__(self, exp):2212if isinstance(exp, (int, long, IntType)) :2213return Twople(self._x^exp, self._y^exp)2214else:2215return NotImplemented2216def __str__(self):2217return "(" + str(self._x) + ", " + str(self._y) + ")"2218def __repr__(self):2219return "(" + str(self._x) + ", " + str(self._y) + ")"22202221class Basis:2222def __init__(self, *args):2223if len(args) == 1:2224BasList = [1]2225VecList = args[0]2226else:2227BasList = args[0]2228VecList = args[1]2229# BasList and VecList will be lists, we need to vectorize each combination of BasList * VecList.2230self._n = len(BasList)2231self._m = len(VecList)2232self._bas = BasList2233self._worked = false2234dimen = self._n * self._m2235M = []2236for j in range(self._m):2237for i in range(self._n):2238v = BasList[i]*VecList[j]2239w = Vectorize(v)2240if not isinstance(w, list):2241w = [w]2242# pad w with zeros to make it of length dimen2243while len(w) < dimen:2244w.append(0)2245M.append(w)2246Mat = matrix(M) # convert to a matrix2247try:2248Inv = ~Mat # take the inverse matrix2249except:2250return None2251self.inv = Inv2252self._worked = true2253return None2254def Coeff(self, value):2255if not(self._worked):2256print "basis is not linearly independent"2257return false2258dimen = self._n * self._m2259w = Vectorize(value)2260if not isinstance(w, list):2261w = [w]2262# pad w with zeros to make it of length dimen2263while len(w) < dimen:2264w.append(0)2265T = matrix(w) * self.inv # Use inverse matrix to find new vector2266# T will be a row matrix, not a list, so we have to convert it to a list.2267L = []2268for j in range(self._m):2269tot = 02270for i in range(self._n):2271k = T[0][i + j*self._n]2272tot = tot + k*self._bas[i]2273L.append(tot)2274return L22752276##############################################################################2277##2278## Main group defining routines2279##2280##############################################################################22812282def ZGroup(n):2283v = []2284for i in range(n):2285v.append(SumModN(i,n))2286return GroupSet(v)22872288def ZStar(n):2289v = []2290R = Integers(n) # Built in Zn ring2291for i in range(1,n):2292if gcd(i,n) == 1:2293v.append(R(i))2294return GroupSet(v)22952296def ZRing(n):2297v = []2298R = Integers(n) # Built in Zn ring2299for i in range(n):2300v.append(R(i))2301return GroupSet(v)23022303def InitQuaternions():2304global i2305global j2306global k2307# By defining Quaternions with SR, (Symbolic Ring), we allow for constructions like a + b*i + c*j + d*k2308Q.<i,j,k> = QuaternionAlgebra(SR,-1,-1)2309return GroupSet([1, i, j, k, -1, -i, -j, -k])23102311def InitSkew9():2312global a2313global b2314# This defines an unusual skew field of dimension 9 over the rational numbers. It is defined by2315# a^3 = 3a+1, b^3 = 2, and b*a =(2-a^2)*b2316A = FreeAlgebra(SR, 2, "x")2317F = A.monoid()2318a = F.gens()[0]2319b = F.gens()[1]2320mons = [F(1), a, a^2, b, a*b, a^2*b, b^2, a*b^2, a^2*b^2]2321M = MatrixSpace(QQ, 9)2322mats = [M([0,1,0, 0,0,0,0,0,0,23230,0,1, 0, 0, 0, 0, 0, 0,23241,3,0, 0, 0, 0, 0, 0, 0,23250,0,0, 2, 0,-1, 0, 0, 0,23260,0,0,-1,-1, 0, 0, 0, 0,23270,0,0, 0,-1,-1, 0, 0, 0,23280,0,0, 0, 0, 0,-2,-1, 1,23290,0,0, 0, 0, 0, 1, 1,-1,23300,0,0, 0 ,0 ,0,-1,-2, 1]),2331M([0,0,0,1,0,0,0,0,0,23320,0,0,0,1,0,0,0,0,23330,0,0,0,0,1,0,0,0,23340,0,0,0,0,0,1,0,0,23350,0,0,0,0,0,0,1,0,23360,0,0,0,0,0,0,0,1,23372,0,0,0,0,0,0,0,0,23380,2,0,0,0,0,0,0,0,23390,0,2,0,0,0,0,0,0])]2340Skew9.<a,b> = A.quotient(mons, mats)2341return GroupSet([a, b])23422343## The main groups are interfaced with Gap. These routines set up a quotient group of a FreeGroup in Gap23442345IdentityElement = "e"2346GeneratorList = []2347RelationsList = []2348gap.eval("TempFreeGroup := FreeGroup(0)")2349gap.eval("CurrentGroup := TempFreeGroup/[]")2350CurrentGroup = gap("CurrentGroup")23512352def InitGroup(name_of_identity_element):2353global IdentityElement2354global GeneratorList2355global RelationsList2356global CurrentGroup2357global LastInit2358IdentityElement = name_of_identity_element2359GeneratorList = []2360RelationsList = []2361LastInit = "InitGroup"2362gap.eval("TempFreeGroup := FreeGroup(0)")2363gap.eval("CurrentGroup := TempFreeGroup/[]")2364CurrentGroup = gap("CurrentGroup")2365gap.eval(IdentityElement + ":= Identity(CurrentGroup);")2366tmpstr = "global " + IdentityElement + "; " + IdentityElement + " = GapElement(CurrentGroup.Identity())"2367exec(tmpstr)2368return "New group with identity " + name_of_identity_element23692370def AddGroupVar(*args):2371global GeneratorList2372global CurrentGroup2373if len(args) == 0:2374print("Must have at least one argument")2375else:2376for newvar in args:2377GeneratorList.append(newvar)2378tmpG = len(GeneratorList)2379tmpstr = "TempFreeGroup := FreeGroup(["2380for tmpI in range(tmpG-1):2381tmpstr = tmpstr + '"' + GeneratorList[tmpI] + '", '2382tmpstr = tmpstr + '"' + GeneratorList[tmpG-1] + '"])'2383gap.eval(tmpstr)2384gap.eval("AssignGeneratorVariables(TempFreeGroup)")2385tmpR = len(RelationsList)2386if tmpR == 0:2387gap.eval("CurrentGroup := TempFreeGroup/[]")2388else:2389tmpstr = "CurrentGroup := TempFreeGroup/["2390for tmpI in range(tmpR-1):2391tmpstr = tmpstr + RelationsList[tmpI] + ", "2392tmpstr = tmpstr + RelationsList[tmpR-1] + "];"2393gap.eval(tmpstr)2394gap.eval("AssignGeneratorVariables(CurrentGroup)")2395CurrentGroup = gap("CurrentGroup")2396gap.eval(IdentityElement + ":= Identity(CurrentGroup);")2397tmpstr = "global " + IdentityElement + "; " + IdentityElement + " = GapElement(CurrentGroup.Identity())"2398exec(tmpstr)2399for tmpI in range(tmpG):2400tmpstr = "global " + GeneratorList[tmpI] + "; " + GeneratorList[tmpI] + ' = GapElement(gap("' + GeneratorList[tmpI] + '"))'2401exec(tmpstr)2402return None24032404def Define(element1, element2):2405global LastFieldVar2406global GenList2407global DegreeList2408if LastInit == "InitGroup":2409# when defining a Gap group, element1 and element2 will probably have the GapElement wrapper.2410if isinstance(element1, GapElement):2411elem1 = element1._x2412else:2413elem1 = element12414if isinstance(element2, GapElement):2415elem2 = element2._x2416else:2417elem2 = element22418global RelationsList2419global CurrentGroup2420if (elem1 in CurrentGroup) and (elem2 in CurrentGroup):2421tmpstr = str(elem1 * (elem2)^(-1))2422RelationsList.append(tmpstr)2423tmpG = len(GeneratorList)2424tmpstr = "TempFreeGroup := FreeGroup(["2425for tmpI in range(tmpG-1):2426tmpstr = tmpstr + '"' + GeneratorList[tmpI] + '", '2427tmpstr = tmpstr + '"' + GeneratorList[tmpG-1] + '"])'2428gap.eval(tmpstr)2429gap.eval("AssignGeneratorVariables(TempFreeGroup)")2430tmpR = len(RelationsList)2431tmpstr = "CurrentGroup := TempFreeGroup/["2432for tmpI in range(tmpR-1):2433tmpstr = tmpstr + RelationsList[tmpI] + ", "2434tmpstr = tmpstr + RelationsList[tmpR-1] + "];"2435gap.eval(tmpstr)2436gap.eval("AssignGeneratorVariables(CurrentGroup)")2437CurrentGroup = gap("CurrentGroup")2438gap.eval(IdentityElement + ":= Identity(CurrentGroup);")2439tmpstr = "global " + IdentityElement + "; " + IdentityElement + " = GapElement(CurrentGroup.Identity())"2440exec(tmpstr)2441for tmpI in range(tmpG):2442tmpstr = "global " + GeneratorList[tmpI] + "; " + GeneratorList[tmpI] + ' = GapElement(gap("' + GeneratorList[tmpI] + '"))'2443exec(tmpstr)2444return None2445elif LastInit == "InitDomain":2446if LastFieldVar == "":2447return "New variable has not been defined yet."2448# we create a polynomial by subtracting the two elements2449DefPoly = element1 - element22450polystr = preparse(str(DefPoly))2451n = DefPoly.degree()2452if BaseCharacteristic > 0 and isinstance(CurrentField, GFType):2453# This is the first extension on a finite field.2454# In order to enable the factoring algorithm, we will construct2455# a Galois field using this polynomial.2456# Note that this polynomial MUST be irreducible over GF(n)2457# for this to work.2458m = BaseCharacteristic^n # size of new field2459tmpstr = "global CurrentField; global " + LastFieldVar + "; CurrentField = GF(" + str(m) + ', name = "'2460tmpstr = tmpstr + LastFieldVar + '", modulus = ' + polystr + "); " + LastFieldVar + " = CurrentField._first_ngens(1)[0]"2461exec(tmpstr)2462elif BaseCharacteristic == 0:2463# extension of an infinite field2464tmpstr = "global CurrentField; global " + LastFieldVar + "; CurrentField = CurrentField.extension(" + polystr + ", names=('"2465tmpstr = tmpstr + LastFieldVar + "',)); " + LastFieldVar + " = CurrentField._first_ngens(1)[0]"2466exec(tmpstr)2467elif len(GenList) > 1:2468return "Can't do extension of an extension of an extension of a finite field!"2469elif DegreeList[0] == 0:2470# We are doing an extension of a rational function field of finite characteristic. Proceed as an infinite field.2471tmpstr = "global CurrentField; global " + LastFieldVar + "; CurrentField = CurrentField.extension(" + polystr + ", names=('"2472tmpstr = tmpstr + LastFieldVar + "',)); " + LastFieldVar + " = CurrentField._first_ngens(1)[0]"2473exec(tmpstr)2474else:2475# We are trying to extend an extension to a finite field. We must first convert the GF definition of CurrentGroup to2476# an extension of Z_n.2477# Note: factorization will no longer be possible for this extension of an extension of a finite characteristic.2478FirstVar = CurrentField.variable_names()[0]2479OldPolyStr = str(CurrentField.modulus())2480OldPolyStr = OldPolyStr.replace("x", FirstVar) # x may not be defined, so convert to the variable that will be defined2481OldPolyStr = preparse(OldPolyStr)2482# reset FirstVar to be the variable of GF(n), rather than the field.2483tmpstr = "global " + FirstVar + '; TempDomain = GF(' + str(BaseCharacteristic) + ')["' + FirstVar + '"]; '2484tmpstr = tmpstr + FirstVar + " = TempDomain._first_ngens(1)[0]"2485exec(tmpstr)2486# now we redefine the field in terms of an extension of GF(p), rather than GF(p^n).2487tmpstr = "global CurrentField; global " + FirstVar + "; CurrentField = GF(" + str(BaseCharacteristic) + ").extension("2488tmpstr = tmpstr + OldPolyStr + ", names=('"2489tmpstr = tmpstr + FirstVar + "',)); " + FirstVar + " = CurrentField._first_ngens(1)[0]"2490exec(tmpstr)2491# We still have to redefine the new variable to be a variable of this new field.2492tmpstr = "global " + LastFieldVar + '; TempDomain = CurrentField["' + LastFieldVar + '"]; '2493tmpstr = tmpstr + LastFieldVar + " = TempDomain._first_ngens(1)[0]"2494exec(tmpstr)2495# We are now ready to define the new field as an extension of this extension2496tmpstr = "global CurrentField; global " + LastFieldVar + "; CurrentField = CurrentField.extension("2497tmpstr = tmpstr + polystr + ", names=('"2498tmpstr = tmpstr + LastFieldVar + "',)); " + LastFieldVar + " = CurrentField._first_ngens(1)[0]"2499exec(tmpstr)2500# We are almost done. Unfortunately, the FirstVar element is not set to an element of the new field, but of2501# the intermediate field. This causes a problem if we try to multiply FirstVar with LastFieldVar, giving2502# a NonImplemented error. So we need to set FirstVar to the element in the new field corresponding the the first2503# generator. This is tricky, since CurrentField.gens() is missing this information. The only approach is a Monte Carlo method!2504tmpstr = "global TempFirstVar; TempFirstVar = " + FirstVar2505exec(tmpstr)2506tmpstr = "global TempLastVar; TempLastVar = " + LastFieldVar2507exec(tmpstr)2508NewFirstVar = CurrentField.random_element()*TempLastVar2509while str(NewFirstVar) != str(TempFirstVar):2510NewFirstVar = CurrentField.random_element()*TempLastVar2511tmpstr = "global " + FirstVar + "; " + FirstVar + " = NewFirstVar"2512exec(tmpstr)2513# reset the first var of GenList so that it works.2514GenList[0] = NewFirstVar2515GenList.append(CurrentField.gen())2516DegreeList.append(n)2517LastFieldVar = "" # So we cannot immediately do another extension, but first define the next variable.2518if UnivFieldVar != "":2519tmpstr = "global " + UnivFieldVar + '; TempDomain = CurrentField["' + UnivFieldVar + '"]; '2520tmpstr = tmpstr + UnivFieldVar + " = TempDomain._first_ngens(1)[0]"2521exec(tmpstr)2522else:2523return "Nothing is initialized yet."25242525def ToPolycyclic():2526global GeneratorList2527global CurrentGroup2528try:2529gap.eval("CurrentPCGroup := PcGroupFpGroup(CurrentGroup)")2530except:2531print "Group definition doesn't meet all the polycyclic requirements."2532else:2533gap.eval("AssignGeneratorVariables(CurrentPCGroup)")2534print "Group converted to the Polycyclic format."2535tmpG = len(GeneratorList)2536CurrentGroup = gap("CurrentPCGroup")2537gap.eval(IdentityElement + ":= Identity(CurrentPCGroup);")2538tmpstr = "global " + IdentityElement + "; " + IdentityElement + " = GapElement(CurrentGroup.Identity())"2539exec(tmpstr)2540for tmpI in range(tmpG):2541tmpstr = "global " + GeneratorList[tmpI] + "; " + GeneratorList[tmpI] + ' = GapElement(gap("' + GeneratorList[tmpI] + '"))'2542exec(tmpstr)2543x = gap([])2544L = x.ListCurrentPCGroup()2545return GroupSet(list(L))25462547def GroupStringConvert(gapstr):2548# StructureDescription uses GAP to identify a group, and returns a string with the name of the group.2549# Unfortunately, GAP's convention is slightly different than the textbook. GAP used C5 for Z_5, and D8 for D_42550# This routine converts the string of the group name to the name that the user will be more familiar with.2551str2 = ''2552L = len(gapstr)2553i = 1 # this will be the pointer2554while i < L:2555char = gapstr[i]2556if char != '"':2557# remove the quotation marks2558if char == 'C':2559# a 'C' followed by a number should be replaced with a 'Z'2560if (gapstr[i+1] >= '0' and gapstr[i+1] <= '9'):2561str2 += 'Z'2562else:2563str2 += char2564elif (char == 'D' and gapstr[i-1] != 'Q'):2565# a 'D' followed by a number should have the number halved. Not applied to QD162566str2 += char2567strnum = ""2568j = i+12569while (gapstr[j] >= '0' and gapstr[j] <= '9'): # no fear of out of range, since the last character is '"'.2570strnum += gapstr[j]2571j = j+12572i = i+12573str2 = str2 + str(int(strnum)/2)2574else:2575str2 += char2576i = i+12577return(str2)25782579def StructureDescription(*args):2580# This uses the small groups package from GAP to identify the group CurrentGroup. This should have been installed the first time this2581# package is run.2582try:2583if len(args) == 0:2584gapstr = gap.eval("StructureDescription(CurrentGroup)")2585else:2586gapstr = "Perms := Group(" + str(PermToCycle(NthPerm(args[0])))2587for i in range(1, len(args)):2588gapstr = gapstr + "," + str(PermToCycle(NthPerm(args[i])))2589gapstr = gapstr + ")"2590gap.eval(gapstr)2591gapstr = gap.eval("StructureDescription(Perms)")2592except:2593print 'Error in finding the structure of the group.'2594else:2595print GroupStringConvert(gapstr)25962597def GaloisType(poly):2598# This uses GAP to find the Galois group of the polynomial. It assumes that the polynomial has been defined in QQ[x], for2599# some variable x2600varname = poly.variable_name()2601deg = poly.degree()2602nstr = gap.eval(varname + ' := Indeterminate(Rationals, "' + varname + '");; GaloisType(' + str(poly) + ')')2603outstr = gap.eval("StructureDescription(TransitiveGroup(" + str(deg) + "," + nstr + "))")2604# Although TransitiveGroup gives more information than StructureDescription, students would not understand the notation produced by2605# the output of TransitiveGroup. Thus, we find the StructureDescription to make it easier for the students.2606print GroupStringConvert(outstr)260726082609def InitTerry():2610global IdentityElement2611global GeneratorList2612global RelationsList2613global CurrentGroup2614global Stay2615global RotLft2616global RotRt2617global Spin2618global FlipRt2619global FlipLft2620gap.eval('ff := FreeGroup("RotLft","RotRt","Spin","FlipLft","FlipRt")')2621gap.eval('CurrentGroup := ff/[ff.1^3,ff.2^3,ff.1*ff.2,ff.3^2,ff.4^2,ff.5^2,ff.5*ff.2*ff.4,ff.3*ff.2*ff.5]')2622gap.eval('AssignGeneratorVariables(CurrentGroup)')2623gap.eval('SetReducedMultiplication(CurrentGroup)')2624CurrentGroup = gap('CurrentGroup')2625Stay = GapElement(gap('Identity(CurrentGroup)'))2626RotLft = GapElement(gap('RotLft'))2627RotRt = GapElement(gap('RotRt'))2628Spin = GapElement(gap('Spin'))2629FlipLft = GapElement(gap('FlipLft'))2630FlipRt = GapElement(gap('FlipRt'))2631IdentityElement = 'Stay'2632return GroupSet([Stay, FlipRt, RotRt, FlipLft, RotLft, Spin])26332634###################################################################################2635##2636## Once the Gap group is defined, there are several ways for it to be displayed2637##2638###################################################################################26392640def ListGroup(): # this will only be used for groups defined in GAP2641str = gap.eval("Size(CurrentGroup)")2642if str == 'infinity':2643return 'Group is infinite.'2644gap.eval("GList := ListGroup(CurrentGroup)")2645GList = gap('GList')2646return GroupSet(list(GList))26472648def ListRing(): # this will only be used for rings defined by InitRing2649str = gap.eval("Size(CurrentRing)")2650if str == 'infinity':2651return 'Ring is infinite.'2652gap.eval("RList := ListRing(CurrentRing)")2653RList = gap('RList')2654return GroupSet(list(RList))26552656def SetReducedMult(): # this will only be used for groups defined in GAP2657gap.eval("SetReducedMultiplication(CurrentGroup)")2658return None26592660def Conform(S_GroupSet, G_GroupSet):2661if (isinstance(S_GroupSet, GroupSet) and isinstance(G_GroupSet, GroupSet)):2662return S_GroupSet.Conformity(G_GroupSet)26632664def Group(*args):2665global gen2666global grp2667if len(args) == 0: # If no arguments, do the same thing as ListGroup.2668return ListGroup()2669else:2670# We can assume that all arguments will be of the same type, so we can strip the GapElement wrappers2671v = []2672for arg in args:2673if isinstance(arg, GapElement):2674v.append(arg._x)2675else:2676v.append(arg)2677if isinstance(v[0], GapInstance): # Delicate the job to GAP2678L = gap(v) #2679Subgroup = L.Group()2680GList = Subgroup.List()2681return GroupSet(list(GList))2682else:2683gen = Uniquify(v) # removes duplicates2684grp = sorted(gen) # sorts the list2685for g1 in grp:2686for g2 in gen:2687z = g1 * g22688if not (z in grp):2689grp.append(z)2690return GroupSet(sorted(grp)) # TODO: determine sorting algorithm26912692##################################################################################################2693##2694## Graphical display routines2695##2696################################################################################################26972698FilterCommas = false26992700#2701# This converts a GAP object (or similar object) to a string. Spaces are removed, and "<identity>" is replaced with2702# the current value of IdentityElement. Mainly this is used for multiplication tables, but it can also be used for circle graphs.2703#27042705def ObjectToStr(obj):2706global FilterCommas2707ss = str(parent(obj))2708if isinstance(obj, (GapInstance, list)): # might have <identity...> that needs to be converted2709ss = str(obj)2710ss = ConvertIdentity(ss)2711ss = ss.replace(" ","") # Remove the spaces2712if (FilterCommas == true):2713ss = ss.replace(",","")2714else: # Easy case: do a simple convertion2715ss = str(obj)2716ss = ss.replace(" ","") # Remove the spaces2717return ss # TODO: If obj is a list, it might be a list of Gap objects, or a list of list of Gap objects, etc.27182719def Add(x):2720L = ["Add"]2721L.append(x)2722return(L)27232724def Mult(x):2725L = ["LeftMult"]2726L.append(x)2727return(L)27282729def LeftMult(x):2730L = ["LeftMult"]2731L.append(x)2732return(L)27332734def RightMult(x):2735L = ["RightMult"]2736L.append(x)2737return(L)27382739def Pow(x):2740L = ["Pow"]2741L.append(x)2742return(L)27432744Inv = ["Inv"]27452746def CircleGraph(V, *args): # each args will either be a permutation, an automorphism, or an ordered pair of the form [string, element]2747L = [] # to complicate matters, there may have several arguments2748if isinstance(V, GroupSet):2749ListV = V._List2750else:2751TempList = list(V) # If a list was entered, the elements may be wrapped in GapElement.2752ListV = [] # In this case, we must strip off the wrapper.2753for item in TempList:2754if isinstance(item, GapElement):2755ListV.append(item._x)2756elif isinstance(item, str):2757ListV.append(eval(item))2758else:2759ListV.append(item)2760n = len(ListV)2761for i in range(n):2762L.append(ObjectToStr(V[i]))2763pos_dict = {} # set up the dots in a circle, going clockwise from the top2764for i in range(n):2765x = float(-cos(pi/2 + ((2*pi)/n)*i))2766y = float(sin(pi/2 + ((2*pi)/n)*i))2767pos_dict[L[i]] = [x,y]2768g = DiGraph(loops = true, multiedges = true)2769# find the maximum length of the strings in L (at least 2)2770maxString = 22771for i in range(n):2772if len(L[i])> maxString:2773maxString = len(L[i])2774question = false2775currentcolor = 42776for op in args:2777edges_covered = []2778while len(edges_covered) < n:2779# find first element not already covered2780k = 02781while k in edges_covered:2782k = k + 12783currentpoint = ListV[k]2784while ((k > -1) and (not (k in edges_covered))): # This must run at least once, since we searched for a point not in edges_covered2785currentpoint = ListV[k]2786edges_covered.append(k)2787# Find the next point, based upon the operation in op2788if isinstance(op, list):2789if op[0] == "Add":2790if isinstance(op[1], GapElement):2791nextpoint = currentpoint + op[1]._x2792else:2793nextpoint = currentpoint + op[1]2794elif op[0] == "LeftMult":2795if isinstance(op[1], GapElement):2796nextpoint = currentpoint * op[1]._x2797else:2798nextpoint = currentpoint * op[1]2799elif op[0] == "RightMult":2800# This is the one case that can be a headache2801# if V was a list of cosets, then currentpoint will be2802# a GroupSet, otherwise it would be a GapElement2803if isinstance(op[1], GapElement):2804if isinstance(currentpoint, GroupSet):2805nextpoint = op[1] * currentpoint2806else:2807nextpoint = op[1]._x * currentpoint2808else:2809nextpoint = op[1] * currentpoint2810elif op[0] == "Pow":2811nextpoint = currentpoint^(op[1])2812elif op[0] == "Inv":2813try:2814nextpoint = currentpoint^(-1)2815except ZeroDivisionError:2816nextpoint = "?"2817else:2818nextpoint = currentpoint # Catches exceptions2819elif isinstance(op, (Cycle, Perm, FieldHomo)):2820nextpoint = op(currentpoint)2821elif isinstance(op, Homomorph):2822nextpoint = op(currentpoint)2823# the homomorphism might put the answer in a wrapper, which we must take off2824if isinstance(nextpoint, GapElement):2825nextpoint = nextpoint._x2826else:2827# TODO: determine other types of operations that could happen2828nextpoint = "?"2829# Find the position of the nextpoint in the list2830if (nextpoint in ListV): # Add this edge to the graph2831ll = ListV.index(nextpoint)2832g.add_edge((L[k], L[ll], currentcolor))2833k = ll2834else:2835if(question == false): # add a "?" to the position list2836pos_dict["?"] = [0,0]2837question = true2838g.add_edge((L[k], "?", currentcolor))2839k = -1 # get out of this while loop2840currentpoint = nextpoint2841if len(args) == 1: # change the color after each cycle, only if there is only one function2842currentcolor = currentcolor + 12843currentcolor = currentcolor + 1 # update for each function2844vertsize = 70*(maxString + 1)2845return g.plot(pos = pos_dict, edge_colors = g._color_by_label(ColorTable), vertex_size = vertsize,2846xmin = -1.025, xmax = 1.025, ymin = -1.05, ymax = 1.0)28472848QuotientRing = false28492850def MultTable(v):2851Graff = Graphics()2852if isinstance(v, GroupSet):2853GG = v._List2854else:2855TempList = list(v) # If a list was entered, the elements may be wrapped in GapElement.2856GG = [] # In this case, we must strip off the wrapper.2857for item in TempList:2858if isinstance(item, GapElement):2859GG.append(item._x)2860else:2861GG.append(item)2862n = len(GG)2863if(n > 28):2864return "Group too big to display table."2865L = []2866for i in range(n):2867L.append(ObjectToStr(GG[i]))2868if isinstance(GG[0], GapInstance):2869GG = gap(GG) # Convert list to a GAP object2870M = GG.QuickTable() # will produce a 1 index matrix2871# elif isinstance(GG[0], (int, long, IntType)) and isinstance(GG[-1], (int, long, IntType)) and (not InitPermMultiplication) and (min(GG) > -1):2872# # for a list of integers, try to determine the operation2873# base = max(GG) + 12874# M = matrix(n+1)2875# if 0 in GG:2876# # assume group is addition modulo base2877# for i in range(n):2878# for j in range(n):2879# w = (GG[i] + GG[j]) % base2880# if w in GG:2881# M[i+1, j+1] = GG.index(w)+12882# else:2883# # assume group is multiplication modulo base2884# for i in range(n):2885# for j in range(n):2886# w = (GG[i] * GG[j]) % base2887# if w in GG:2888# M[i+1, j+1] = GG.index(w)+12889else: #defalt case - use multiplication2890# GFlat = []2891# if isinstance(GG[0], list): # GG is a list of lists2892# GFlat = Flatten(GG)2893M = matrix(n+1)2894for i in range(n):2895for j in range(n):2896w = GG[j] * GG[i]2897if w in GG:2898M[j+1, i+1] = GG.index(w)+12899else:2900# second chance - w might be a _subset_ of an element in GG2901if isinstance(w, GroupSet):2902for k in range(n):2903if IsSubset(w._List, GG[k]):2904if QuotientRing:2905M[j+1, i+1] = k + 12906else:2907M[j+1, i+1] = -(k + 1)2908Graff += text(w,(i+1.5, n-j-0.5), rgbcolor=(0,0,0))2909break2910Graff += line([(1,0),(1,n),(n+1,n)], rgbcolor=(0,0,0))2911# find the maximum length of the strings in L2912maxString = 02913for i in range(n):2914if len(L[i])> maxString:2915maxString = len(L[i])2916for i in range(n):2917Graff += polygon([(i+1, n+1),(i+2, n+1),(i+2, n),(i+1, n)], color = ColorTable[i+1])2918Graff += polygon([(0, n-i),(1, n-i),(1, n-i-1),(0, n-i-1)], color = ColorTable[i+1])2919if (maxString*(n+1) < 64):2920Graff += text(L[i],(i+1.5, n+0.5), rgbcolor=(0,0,0))2921Graff += text(L[i],(0.5, n-i-0.5), rgbcolor=(0,0,0))2922else:2923Graff += text(L[i],(-0.01*(n+1),n-i-0.5), rgbcolor=(0,0,0), horizontal_alignment='right')2924for i in range(n):2925for j in range(n):2926p = int(M[j+1][i+1]) # note: have to convert the GAP integer to a sage integer2927Graff += polygon([(i+1, n-j),(i+2, n-j),(i+2, n-j-1),(i+1, n-j-1)], color = ColorTable[abs(p)])2928if ((p > 0) and (maxString*(n+1) < 64)):2929Graff += text(L[p-1],(i+1.5, n-j-0.5), rgbcolor=(0,0,0))2930return Graff.show(axes = false)29312932def AddTable(v):2933Graff = Graphics()2934if isinstance(v, GroupSet):2935GG = v._List2936else:2937TempList = list(v) # If a list was entered, the elements may be wrapped in GapElement.2938GG = [] # In this case, we must strip off the wrapper.2939for item in TempList:2940if isinstance(item, GapElement):2941GG.append(item._x)2942else:2943GG.append(item)2944n = len(GG)2945if(n > 28):2946return "Ring too big to display table."2947L = []2948for i in range(n):2949L.append(ObjectToStr(GG[i]))2950s = str(parent(GG[0]))2951if s == 'Gap': #Delegate the task to GAP2952GG = gap(GG) # Convert list to a GAP object2953M = GG.QuickAdd() # will produce a 1 index matrix2954else: # probably Ring of integers modulo n2955M = matrix(n+1)2956for i in range(n):2957for j in range(n):2958w = GG[i] + GG[j]2959if w in v:2960M[i+1, j+1] = GG.index(w)+12961Graff += line([(1,0),(1,n),(n+1,n)], rgbcolor=(0,0,0))2962# find the maximum length of the strings in L2963maxString = 02964for i in range(n):2965if len(L[i])> maxString:2966maxString = len(L[i])2967for i in range(n):2968Graff += polygon([(i+1, n+1),(i+2, n+1),(i+2, n),(i+1, n)], color = ColorTable[i+1])2969Graff += polygon([(0, n-i),(1, n-i),(1, n-i-1),(0, n-i-1)], color = ColorTable[i+1])2970if (maxString*(n+1) < 64):2971Graff += text(L[i],(i+1.5, n+0.5), rgbcolor=(0,0,0))2972Graff += text(L[i],(0.5, n-i-0.5), rgbcolor=(0,0,0))2973else:2974Graff += text(L[i],(-0.01*(n+1),n-i-0.5), rgbcolor=(0,0,0), horizontal_alignment='right')2975for i in range(n):2976for j in range(n):2977p = int(M[j+1][i+1]) # note: have to convert the GAP integer to a sage integer2978Graff += polygon([(i+1, n-j),(i+2, n-j),(i+2, n-j-1),(i+1, n-j-1)], color = ColorTable[p])2979if ((p > 0) and (maxString*(n+1) < 64)):2980Graff += text(L[p-1],(i+1.5, n-j-0.5), rgbcolor=(0,0,0))2981return Graff.show(axes = false)298229832984def GraphHomo(fun):2985global maxString2986global Lrange2987global Ldomain2988global pos_dict2989# This graphs the function, using the notation given in the list domain.2990domain = fun.GetDomain()2991fRange = fun.GetRange()2992# Convert domain and range to strings2993m = len(domain)2994n = len(fRange)2995Ldomain = []2996Lrange = []2997for i in range(m):2998Ldomain.append(ObjectToStr(domain[i]))2999for i in range(n):3000Lrange.append(ObjectToStr(fRange[i]))3001graff = Graphics()3002for domnum in range(m):3003key = domain[domnum]3004rannum = fRange.index(fun.Out[fun.In.index(key)])3005if m == 1:3006x = 0.53007else:3008x = 1 - (numerical_approx(domnum)/(m-1))3009if n == 1:3010y = 0.53011else:3012y = 1 - (numerical_approx(rannum)/(n-1))3013graff += arrow((0,x), (1,y), color = ColorTable[4+rannum])3014if m == 1:3015graff += point((0, 0.5), rgbcolor = (0,0,0), size = 50)3016graff += text(Ldomain[0],(-0.02, 0.5), rgbcolor=(0,0,0), horizontal_alignment='right')3017else:3018for i in range(m):3019y = 1 - (numerical_approx(i)/(m-1))3020graff += point((0, y), rgbcolor = (0,0,0), size = 50)3021graff += text(Ldomain[i],(-0.02, y), rgbcolor=(0,0,0), horizontal_alignment='right')3022if n == 1:3023graff += point((1, 0.5), rgbcolor = (0,0,0), size = 50)3024graff += text(Lrange[0],(1.02, 0.5), rgbcolor=(0,0,0), horizontal_alignment='left')3025else:3026for i in range(n):3027y = 1 - (numerical_approx(i)/(n-1))3028graff+= point((1, y), rgbcolor = (0,0,0), size = 50)3029graff += text(Lrange[i],(1.02, y), rgbcolor=(0,0,0), horizontal_alignment='left')3030return graff.show(axes = false)30313032#######################################################################################3033##3034## RSA routines3035##3036#######################################################################################30373038def NextPrime(n):3039if n%2 == 0:3040n = n+13041while not(is_prime(n)):3042n = n+23043return n30443045def CentToAscii(x):3046if x < 1:3047y = 323048elif x < 30:3049y = x + 643050elif x < 47:3051y = x + 183052elif x < 48:3053y = 333054elif x < 81:3055y = x + 463056elif x < 95:3057y = x - 473058elif x == 95:3059y = 1963060elif x == 96:3061y = 2033062elif x == 97:3063y = 2143064elif x == 98:3065y = 2203066elif x == 99:3067y = 2233068else:3069y = 03070return y30713072def AsciiToCent(x):3073if x < 33:3074y = 03075elif x < 34:3076y = 473077elif x < 48:3078y = x + 473079elif x < 65:3080y = x - 183081elif x < 94:3082y = x - 643083elif x < 127:3084y = x - 463085else:3086y = 03087return y30883089def MessageToNumber(s):3090total = 03091for i in range(len(s)):3092total = total * 100 + AsciiToCent(ord(s[i]))3093return total30943095def NumberToMessage(n):3096strlist = ""3097Temp = int(n)3098while Temp > 0:3099m = Temp % 1003100Temp = Temp // 1003101strlist = chr(CentToAscii(m)) + strlist3102return strlist31033104######################################################################################31053106def C(*args):3107L = [];3108for arg in args: # Convert args to a list, which will support indexing3109L.append(arg)3110return Cycle(L)31113112def P(*args):3113L = [];3114for arg in args: # Convert args to a list, which will support indexing3115L.append(arg)3116return Perm(L)31173118def Order(x):3119if isinstance(x, GapElement):3120xx = x._x3121else:3122xx = x3123if isinstance(xx, GapInstance): # Delicate the job to GAP3124n = xx.Group().Size()3125if n == gap('infinity'):3126return Infinity3127else:3128return int(n)3129elif isinstance(xx, SumModN):3130return xx._mod/GCD(x._x, x._mod)3131else:3132# since we do not know what type of element it is, we do not know the identity element.3133# Our strategy is to find an n > 1 for which x^n = x, and return n-1.3134y = x*x3135n = 13136while x != y:3137y = y * x3138n = n + 13139return n31403141def RootCount(G, k):3142# Counts the number of elements for which g^k = e.3143count = 0;3144for x in G:3145t = x3146for i in range(k):3147t = t*x3148if t == x:3149count = count + 13150return count31513152def Generators(G):3153# finds all of the generators of the group, assuming it is cyclic.3154L = [];3155n = len(G)3156for x in G:3157if (Order(x) == n):3158L.append(x)3159return L31603161def InitRing(*args):3162# actually, all this does is var's the varables in *args, and forms a list of these variables in GeneratorsOfRing.3163# But this sets up the ability to form a ring from using the orders, and the mini-multiplication table.3164global GeneratorsOfRing3165GeneratorsOfRing = []3166for arg in args:3167var(arg)3168tmpstr = 'global ' + arg3169exec(tmpstr)3170tmpstr = 'GeneratorsOfRing.append(' + arg + ')'3171exec(tmpstr)3172return None31733174def DefineRing(CharR, GenTable):3175# CharR is an integer vector showing the orders on the basis elements.3176# GenTable is a mini-multiplication table showing the products of two basis elements, written as a linear combination of the basis elements.3177global CurrentRing3178TempT = []3179n = len(GeneratorsOfRing)3180for i in range(n):3181TempT1 = []3182for j in range(n):3183TempVars = []3184TempCk = []3185for k in range(n):3186m = SR(GenTable[i][j]).coefficient(GeneratorsOfRing[k],1)3187m = int(m)3188if m != 0:3189TempVars.append(k+1) #GAP is 1 indexed3190TempCk.append(m)3191TempT1.append([TempVars, TempCk])3192TempT.append(TempT1)3193TempT.append(0) # Neither symmetric or anti-symmetric is assumed.3194TempT.append(0) # Add zero of the defining ring3195tmpstr = 'CurrentRing := RingByStructureConstants(' + str(CharR) + ',' + str(TempT) + ',['3196for i in range(n):3197tmpstr = tmpstr + '"' + str(GeneratorsOfRing[i]) + '"'3198if i < n-1:3199tmpstr = tmpstr + ','3200tmpstr = tmpstr + '])'3201gap.eval(tmpstr)3202for i in range(n):3203tmpstr = 'global ' + str(GeneratorsOfRing[i]) + '; ' + str(GeneratorsOfRing[i]) + ' = GapElement(gap("CurrentRing.' + str(i+1) + '"))'3204exec(tmpstr)3205CurrentRing = gap("CurrentRing")3206return None32073208def NumberSmallRings(size):3209return int(gap.eval("NumberSmallRings(" + str(size) + ")"))32103211def SmallRing(size, idnum):3212gap.eval("CurrentRing := SmallRing(" + str(size) + "," + str(idnum) + ")")3213CurrentRing = gap('CurrentRing')3214L = CurrentRing.GeneratorsOfRing() # This will be 1 indexed from GAP3215n = len(L)3216for i in range(n):3217tmpstr = 'global ' + str(L[i+1]) + '; ' + str(L[i+1]) + ' = GapElement(gap("CurrentRing.' + str(i+1) + '"))'3218exec(tmpstr)3219gap.eval("RList := ListRing(CurrentRing)")3220RList = gap('RList')3221return GroupSet(list(RList))32223223def CheckRing():3224s = gap.eval("CheckRing(CurrentRing)")3225ss = str(s)3226ss = ss.replace('"','')3227print ss3228return None32293230def FindUnity(*args):3231if len(args) == 0:3232Rring = gap(CurrentRing)3233ident = Rring.Identity()3234if str(ident) == 'fail':3235print "No identity element found."3236return None3237return GapElement(ident)3238R_list = args[0]3239# R must be a list or GroupSet for this to work.3240# Returns the identity element, if there is one3241if isinstance(R_list, list):3242R_ring = GroupSet(R_list)3243else:3244R_ring = R_list3245if isinstance(R_ring._List[0], GapInstance): #delegate the task to Gap3246Rset = gap(R_ring._List)3247Rring = Rset.Ring()3248ident = Rring.Identity()3249if str(ident) == 'fail':3250print "No identity element found."3251return None3252return GapElement(ident)3253for i in R_ring._List:3254TestFlag = true3255for j in R_ring._List:3256if ((i * j != j) or (j * i != j)):3257TestFlag = false3258if TestFlag:3259return i3260print "No identity element found."3261return None32623263def Ring(*args):3264global gen3265global grp3266if len(args) == 0: # If no arguments, do the same thing as ListRing.3267return ListRing()3268else:3269# We can assume that all arguments will be of the same type, so we can strip the GapElement wrappers3270v = []3271for arg in args:3272if isinstance(arg, GapElement):3273v.append(arg._x)3274else:3275v.append(arg)3276if isinstance(v[0], GapInstance): # Delicate the job to GAP3277L = gap(v) #3278Subring = L.Ring()3279GList = Subring.List()3280return GroupSet(list(GList))3281else:3282gen = Uniquify(v) # removes duplicates3283grp = sorted(gen) # sorts the list3284for g1 in grp:3285for g2 in gen:3286z = g1 * g23287if not (z in grp):3288grp.append(z)3289z = g1 + g23290if not (z in grp):3291grp.append(z)3292return GroupSet(sorted(grp))32933294def LftCoset(G_list, H_list):3295# both G and H must be lists or Cosets for this to work3296# if they are lists, convert to Cosets (which will remove wrappers if present)3297# We can assume that H is a subgroup of G3298# Note: The cosets in the output MUST be sorted in order to allow for MultTable to work3299if isinstance(G_list, list):3300G_coset = GroupSet(G_list)3301else:3302G_coset = G_list3303if isinstance(H_list, list):3304H_coset = GroupSet(H_list)3305else:3306H_coset = H_list3307if not (isinstance(G_coset, GroupSet) and isinstance(H_coset, GroupSet)):3308return "Error: both parameters must be lists or subgroups"3309H_coset.Sort()3310if isinstance(G_coset._List[0], GapInstance): # May delegate the task to Gap3311# If G is a list of GapElements, then H will be too. We can delegate the task to GAP3312Ggap = gap(G_coset._List)3313Ggap.OriginalGroup()3314Hgap = gap(H_coset._List)3315OutGap = Hgap.LftCoset()3316## OutGap will be a gap list of lists. We need to convert to a GroupSet of GroupSets3317q = []3318OutList = list(OutGap)3319for item in OutList:3320NewCoset = GroupSet(list(item))3321q.append(NewCoset)3322return GroupSet(q)3323# We can save a little time by starting with the original subgroup H3324q = [H_coset]3325flat = H_coset._List3326for g in G_coset:3327if not(g in flat):3328newCoset = GroupSet([g]) * H_coset3329q.append(newCoset)3330flat = Flatten(q)3331return GroupSet(q)33323333def RtCoset(G_list, H_list):3334# both G and H must be lists or GroupSets for this to work3335# if they are lists, convert to GroupSets (which will remove wrappers if present)3336# We can assume that H is a subgroup of G3337# Note: The cosets in the output MUST be sorted in order to allow for MultTable to work3338if isinstance(G_list, list):3339G_coset = GroupSet(G_list)3340else:3341G_coset = G_list3342if isinstance(H_list, list):3343H_coset = GroupSet(H_list)3344else:3345H_coset = H_list3346if not (isinstance(G_coset, GroupSet) and isinstance(H_coset, GroupSet)):3347return "Error: both parameters must be lists or subgroups"3348H_coset.Sort()3349if isinstance(G_coset._List[0], GapInstance): # May delegate the task to Gap3350# If G is a list of GapElements, then H will be too. We can delegate the task to GAP3351Ggap = gap(G_coset._List)3352Ggap.OriginalGroup()3353Hgap = gap(H_coset._List)3354OutGap = Hgap.RtCoset()3355## OutGap will be a gap list of lists. We need to convert to a GroupSet of GroupSets3356q = []3357OutList = list(OutGap)3358for item in OutList:3359NewCoset = GroupSet(list(item))3360q.append(NewCoset)3361return GroupSet(q)3362# We can save a little time by starting with the original subgroup H3363q = [H_coset]3364flat = H_coset._List3365for g in G_coset:3366if not(g in flat):3367newCoset = H_coset * GroupSet([g])3368q.append(newCoset)3369flat = Flatten(q)3370return GroupSet(q)33713372def Coset(R_list, H_list):3373# both R and H must be lists or GroupSets for this to work3374# if they are lists, convert to GroupSets (which will remove wrappers if present)3375# We can assume that H is a subring of R3376# Note: The cosets in the output MUST be sorted in order to allow for MultTable to work3377if isinstance(R_list, list):3378R_coset = GroupSet(R_list)3379else:3380R_coset = R_list3381if isinstance(H_list, list):3382H_coset = GroupSet(H_list)3383else:3384H_coset = H_list3385if not (isinstance(R_coset, GroupSet) and isinstance(H_coset, GroupSet)):3386return "Error: both parameters must be lists or subrings"3387H_coset.Sort()3388if isinstance(R_coset._List[0], GapInstance): # May delegate the task to Gap3389# If R is a list of GapElements, then H will be too. We can delegate the task to GAP3390Rgap = gap(R_coset._List)3391Rgap.OriginalGroup()3392Hgap = gap(H_coset._List)3393OutGap = Hgap.RingCoset()3394## OutGap will be a gap list of lists. We need to convert to a GroupSet of GroupSets3395q = []3396OutList = list(OutGap)3397for item in OutList:3398NewCoset = GroupSet(list(item))3399q.append(NewCoset)3400return GroupSet(q)3401# We can save a little time by starting with the original subgroup H3402q = [H_coset]3403flat = H_coset._List3404for g in G_coset:3405if not(g in flat):3406newCoset = GroupSet([g]) + H_coset3407q.append(newCoset)3408flat = Flatten(q)3409return GroupSet(q)34103411def RingHomo(domain, target):3412funct = Homomorph(domain, target)3413funct.IsRing = True3414return funct34153416def HomoDef(Fun, input, output):3417if isinstance(Fun, FieldHomo): # this case, there is nothing to check3418Fun.Set(input, output)3419elif isinstance(Fun, Homomorph):3420## we have to remove the wrappers from input and output3421## Also, if input or output is a list, convert to a GroupSet - either domain or target is a quotient group.3422inputx = input3423if isinstance(input, GapElement):3424inputx = input._x3425if isinstance(input, list):3426inputx = GroupSet(input)3427outputx = output3428if isinstance(output, GapElement):3429outputx = output._x3430if isinstance(output, list):3431outputx = GroupSet(output)3432elif not(inputx in Fun.Domain):3433print "Second argument must be in the domain group."3434elif not(outputx in Fun.Target):3435print "Last argument must be in the target group."3436else:3437Fun.Set(inputx, outputx)3438else:3439print "First argument must be a homomorphism."34403441def FinishHomo(Fun):3442return Fun.Finish()34433444def CheckHomo(Fun):3445return Fun.Finish()34463447def Image(Fun, L):3448# L might be a GroupSet, a list, or a single element.3449# If it is a GroupSet, we can safely assume that we want the corresponding list.3450# If it is a list, then we must strip the GapElement wrappers.3451if isinstance(L, list):3452LL = []3453for item in L:3454if isinstance(item, GapElement):3455LL.append(item._x)3456else:3457LL.append(item)3458elif isinstance(L, GroupSet):3459LL = L._List3460elif isinstance(L, GapElement):3461LL = [L._x]3462else:3463return Fun(L)3464R = []3465for g1 in LL:3466z = Fun.Out[Fun.In.index(g1)]3467if not z in R:3468R.append(z)3469return GroupSet(sorted(R))34703471def HomoInv(Fun, L):3472# L might be a GroupSet, a list, or a single element.3473# If it is a GroupSet, we can safely assume that we want the corresponding list.3474# If it is a list, then we must strip the GapElement wrappers.3475if isinstance(L, list):3476LL = []3477for item in L:3478if isinstance(item, GapElement):3479LL.append(item._x)3480else:3481LL.append(item)3482elif isinstance(L, GroupSet):3483LL = L._List3484elif isinstance(L, GapElement):3485LL = [L._x]3486else:3487LL = [L]3488return GroupSet(Fun.Inv(LL))34893490def HomoRange(Fun):3491return Fun.GetRange()34923493def Kernel(Fun):3494R = Fun.Target3495if Fun.IsRing:3496Q = [R[0] + (-R[0])]3497else:3498Q = [R[0]^0]3499return GroupSet(Fun.Inv(Q))35003501def CycleToPerm(Cyc):3502if isinstance(Cyc, Cycle):3503return Cyc.ToPerm()3504else:3505return NotImplemented35063507def PermToCycle(P_Perm):3508if isinstance(P_Perm, Perm):3509return P_Perm.ToCycle()3510else:3511return NotImplemented35123513def PermToInt(P_Perm):3514if isinstance(P_Perm, Perm):3515return P_Perm.ToInt()3516else:3517return NotImplemented35183519def Signature(C_Cycle):3520if isinstance(C_Cycle, Cycle):3521count = 13522for item in C_Cycle._C:3523if len(item) %2 == 0:3524count = - count3525return count3526elif isinstance(C_Cycle, Perm):3527return Signature(C_Cycle.ToCycle()) # Probably a faster way to do this, but this will work.3528else:3529return NotImplemented35303531def Unfactorial(n):3532# returns the smallest number for which i! >= n3533index = 03534Temp = 13535while Temp < n:3536index = index + 13537Temp = Temp * index3538return index35393540def NthPerm(n_int):3541global Temp3542global digit3543if n <= 0:3544return "Argument must be a positive integer"3545u = Unfactorial(n_int)3546Temp = range(u,0,-1) # goes from [u, u-1, u-2, ... , 1]3547S = []3548for k in range(u, 0, -1):3549digit = (((n_int-1) % factorial(k)) // factorial(k-1))3550S.append(Temp[digit])3551del Temp[digit]3552S.reverse()3553return Perm(S)355435553556def DirectProduct(HList, KList):3557if isinstance(HList, (list, GroupSet)) and isinstance(KList, (list, GroupSet)):3558L = []3559for i in HList:3560for j in KList:3561L.append(Twople(i,j))3562return GroupSet(L)3563else:3564return NotImplemented35653566def GroupCenter(Glist):3567# This finds the elements of Glist that commute with all of the elements of Glist3568# If Glist is a group or subgroup, the result we automatically be a subgroup.3569# We will keep order of the elements the same as the order in Glist3570# Glist might either be a GroupSet or a list. If it is a list, we may have to unwrap the elements.3571if isinstance(Glist, GroupSet):3572G = Glist3573else:3574G = GroupSet(Glist)3575g = []3576for i in G._List:3577include = true3578for j in G._List:3579if (i*j) != (j*i):3580include = false # i is not in the center3581break3582if include:3583g.append(i)3584return GroupSet(g)35853586def Normalizer(Glist, Hlist):3587# This finds the elements of Glist for which g.h.g^-1 is in Hlist for all h in Hlist.3588# If Glist is a group or subgroup, the result we automatically be a subgroup.3589# We will keep order of the elements the same as the order in Glist3590# Glist might either be a GroupSet or a list. If it is a list, we may have to unwrap the elements.3591if isinstance(Glist, GroupSet):3592G = Glist3593else:3594G = GroupSet(Glist)3595if isinstance(Hlist, GroupSet):3596H = Hlist3597elif isinstance(Hlist, list):3598H = GroupSet(Hlist)3599else:3600H = GroupSet([Hlist]) # in case H is a single element3601answer = []3602for g in G._List:3603include = true3604for h in H._List:3605if not((g * h) * g^(-1) in H._List):3606include = false # i is not in the normalizer3607break3608if include:3609answer.append(g)3610return GroupSet(answer)36113612def NormalClosure(Glist, Slist):3613# This finds the smallest normal subgroup of Glist that contains the elements of Slist.3614if isinstance(Glist, GroupSet):3615G = Glist3616else:3617G = GroupSet(Glist)3618if isinstance(Slist, GroupSet):3619S = Slist3620elif isinstance(Slist, list):3621S = GroupSet(Slist)3622else:3623S = GroupSet([Slist]) # in case S is a single element3624# if the elements are GAP elements, deligate the take to GAP3625if isinstance(G._List[0], GapInstance):3626Ggap = gap(G._List)3627Ggap.OriginalGroup()3628Sgap = gap(S._List)3629Ngap = Sgap.NormClosure()3630return GroupSet(list(Ngap))3631# find all conjugates of the elements in S3632gen = []3633n = len(G._List) # size of original group3634for g in G._List:3635for h in S._List:3636u = (g * h) * g^(-1)3637if not(u in gen):3638gen.append(u)3639# gen is now the set of generators for the group3640grp = copy(gen)3641for g1 in grp:3642for g2 in gen:3643u = g1 * g23644if not (u in grp):3645grp.append(u)3646if len(grp)*2 > n:3647return G3648return GroupSet(sorted(grp)) # TODO: determine sorting algorithm36493650def Ideal(Rlist, Slist):3651# This finds the smallest ideal of Rlist that contains the elements of Slist.3652if isinstance(Rlist, GroupSet):3653R = Rlist3654else:3655R = GroupSet(Rlist)3656if isinstance(Slist, GroupSet):3657S = Slist3658elif isinstance(Slist, list):3659S = GroupSet(Slist)3660else:3661S = GroupSet([Slist]) # in case S is a single element3662# if the elements are GAP elements, deligate the take to GAP3663if isinstance(R._List[0], GapInstance):3664Rgap = gap(R._List)3665Rgap.OriginalGroup()3666Sgap = gap(S._List)3667Igap = Sgap.IdealClosure()3668return GroupSet(list(Igap))3669# first find the union of S, R.S, S.R, and R.S.R3670gen = (R * S) * R3671for item in S:3672if not(item in gen):3673T.append(item)3674T = R * S3675for item in T:3676if not(item in gen):3677T.append(item)3678T = S * R3679for item in T:3680if not(item in gen):3681T.append(item)3682n = len(R._List) # size of original group3683# gen is now the set of additive generators for the ideal3684id = copy(gen)3685for g1 in grp:3686for g2 in id:3687u = g1 + g23688if not (u in grp):3689grp.append(u)3690if len(grp)*2 > n:3691return R3692return GroupSet(sorted(grp)) # TODO: determine sorting algorithm36933694def ConjugacyClasses(Glist):3695# This finds the conjugacy classes of the group in Glist.3696if isinstance(Glist, GroupSet):3697G = Glist3698else:3699G = GroupSet(Glist)3700# If the elements are Gap elements, delegate the task to Gap.3701if isinstance(G._List[0], GapInstance):3702Ggap = gap(G._List)3703LL = Ggap.ConjClasses() # will produce a list of lists, in Gap3704L = list(LL)3705CC = []3706for item in L:3707CC.append(GroupSet(list(item)))3708return CC3709CC = []3710FlatC = []3711for g in G._List:3712if not(g in FlatC):3713Class = []3714for h in G._List:3715u = (h * g) * h^(-1)3716if not(u in Class):3717Class.append(u)3718Class.sort()3719CC.append(GroupSet(Class))3720FlatC = Flatten(CC)3721return GroupSet(CC)37223723# This command assumes that both HList and KList are subgroups of some larger group.3724# If one of the groups is a subgroup of the other, Commutator is faster.3725def MutualCommutator(HList, KList):3726if isinstance(HList, GroupSet):3727H = HList3728else:3729H = GroupSet(HList)3730if isinstance(KList, GroupSet):3731K = KList3732else:3733K = GroupSet(KList)3734#If the elements are in GAP, delegate the job to GAP3735if isinstance(H._List[0], GapInstance) and isinstance(K._List[0], GapInstance):3736Hgap = gap(H._List)3737Hgap.OriginalGroup()3738Kgap = gap(K._List)3739Agap = Kgap.CommSubgroup()3740return GroupSet(list(Agap))3741gen = []3742for h in H._List:3743for k in K._List:3744u = (h^(-1) * k^(-1)) * (h * k)3745if not(u in gen):3746gen.append(u)3747grp = copy(gen)3748for g1 in grp:3749for g2 in gen:3750z = g1 * g23751if not (z in grp):3752grp.append(z)3753return GroupSet(sorted(grp))37543755# This command assumes that KList is a subset of GList.3756# If K generates the group G, then this gives the derived group G'3757def Commutator(GList, KList):3758if isinstance(GList, GroupSet):3759G = GList3760else:3761G = GroupSet(GList)3762if isinstance(KList, GroupSet):3763K = KList3764else:3765K = GroupSet(KList)3766#If the elements are in GAP, delegate the job to GAP3767if isinstance(G._List[0], GapInstance):3768Ggap = gap(G._List)3769Ggap.OriginalGroup()3770Kgap = gap(K._List)3771Agap = Kgap.CommSubgroup()3772return GroupSet(list(Agap))3773n = len(G._List)3774gen = []3775for h in G._List:3776for k in K._List:3777u = (h^(-1) * k^(-1)) * (h * k)3778if not(u in gen):3779gen.append(u)3780grp = copy(gen)3781for g1 in grp:3782for g2 in gen:3783z = g1 * g23784if not (z in grp):3785grp.append(z)3786if len(grp)*2 > n:3787# over half the elements, return original list3788return G3789return GroupSet(sorted(grp))37903791def ExpressAsWord(genList, target):3792# This routine interfaces with GAP, to find the shortest way of expressing a target permutation in terms of a list of permutations.3793# genList must be a list of strings, each of which represents the variable that a permutation has been defined.3794# target is the permutation that must be reached.3795#3796# There is a twist. GAP interprets permutations multiplied from left to right, assuming (fog)(x) = g(f(x)).3797# If we multiply permutations from right to left (assuming (fog)(x) = f(g(x)) ), then we have to invert all of the input and output3798# permutations to compensate.3799#3800# First we evaluate the strings in genList to find the permutations.3801permList = []3802for item in genList:3803perm = eval(item)3804if isinstance(perm, Perm):3805perm = PermToCycle(perm)3806## Take the inverse of the permutation to compensate GAP.3807perm = perm^-13808permList.append(perm)3809tmpstr = "Perms := Group(" + str(permList[0])3810for i in range(1, len(genList)):3811tmpstr = tmpstr + ", " + str(permList[i])3812tmpstr = tmpstr + ")"3813gap.eval(tmpstr)3814#print tmpstr3815tmpstr = 'phi := EpimorphismFromFreeGroup(Perms:names:=["' + genList[0] + '"'3816for i in range(1, len(genList)):3817tmpstr = tmpstr + ', "' + genList[i] + '"'3818tmpstr = tmpstr + "])"3819gap.eval(tmpstr)3820#print tmpstr3821if isinstance(target, Perm):3822tar = PermToCycle(target)3823else:3824tar = target3825## Take the inverse of the target to compensate GAP.3826tar = tar^-13827tmpstr = "PreImagesRepresentative( phi," + str(tar) + ")"3828outstr = gap.eval(tmpstr)3829#print tmpstr3830return outstr38313832def AddRingVar(str):3833# str is a string which will be the name of the variable.3834tmpstr = str + ' := Indeterminate(CurrentRing, "' + str + '")'3835gap.eval(tmpstr)3836tmpstr = "global " + str + "; " + str + ' = GapElement(gap("' + str + '"))'3837exec(tmpstr)3838return None38393840GenList = []3841DegreeList = []38423843def InitDomain(arg1, *args):3844global LastInit3845global UnivFieldVar3846global BaseCharacteristic3847global CurrentField3848global GenList3849global DegreeList3850GenList = []3851DegreeList = []3852# if there is one argument, it must be an integer, 0 or positive prime.3853# if there are two arguments, the second one must be a string, giving a name of the universal variable.3854if not(isinstance(arg1, (int, long, IntType))):3855return "First argument must be an integer."3856if (arg1 < 0):3857return "First argument must be positive."3858if (arg1 > 0) and not(arg1 in Primes()):3859return str(arg1) + " is not prime."3860if arg1 == 0:3861CurrentField = QQ3862else:3863CurrentField = GF(arg1)3864BaseCharacteristic = arg13865LastInit = "InitDomain"3866if len(args) == 0:3867UnivFieldVar = ""3868return None # we are done3869UnivFieldVar = args[0]3870tmpstr = "global " + UnivFieldVar + '; TempDomain = CurrentField["' + UnivFieldVar + '"]; ' + UnivFieldVar + " = TempDomain._first_ngens(1)[0]"3871exec(tmpstr)3872return None38733874def AddFieldVar(str):3875if (BaseCharacteristic > 0) and (len(GenList) > 1):3876if DegreeList[0] > 0:3877return "Sage is unable to define a variable on an extension of an extension of a finite field."3878global LastFieldVar3879LastFieldVar = str3880tmpstr = "global " + str + '; TempDomain = CurrentField["' + str + '"]; ' + str + " = TempDomain._first_ngens(1)[0]"3881exec(tmpstr)3882return None38833884def RationalFunctions(str):3885global GenList3886global DegreeList3887tmpstr = "global " + str + "; global CurrentField; CurrentField = FunctionField(CurrentField, names = ('" + str + "',)); "3888tmpstr = tmpstr + str + " = CurrentField._first_ngens(1)[0]"3889exec(tmpstr)3890GenList.append(str)3891DegreeList.append(0)38923893def ListField():3894if BaseCharacteristic == 0:3895return "Field is infinite!"3896if len(GenList) == 2: # in this case, list will not work. Use a less efficient method.3897return Ring(GenList[0], GenList[1])3898if len(GenList) == 1 and DegreeList[0] == 0:3899return "Field is infinite!"3900return GroupSet(sorted(list(CurrentField)))39013902def Compon(exp, L_List):3903inp = copy(exp)3904out = []3905for item in L_List:3906a = inp.coefficient(item)3907inp = expand(inp - a*item)3908out.append(a)3909out.append(inp)3910return out39113912def Vectorize(exp):3913try:3914L = vector(exp)3915except: # not a vector - we are at the lowest level.3916return exp3917output = []3918for item in L:3919if isinstance(item, (Rational, IntModType, int, long, IntType)):3920output.append(item)3921else:3922output.append(Vectorize(item))3923return Flatten(output)39243925def InterpolationPolynomial( pointsList, varname):3926Rpolyring = PolynomialRing(CurrentField, varname)3927return Rpolyring.lagrange_polynomial(pointsList)39283929def Cyclotomic(n, str):3930TempR = QQ[str]3931return TempR.cyclotomic_polynomial(n)39323933def ToBasis(*args):3934if (len(args) == 0 or len(args) > 2):3935print "One or two arguments are required"3936return None3937if len(args) == 1:3938B = Basis(args[0])3939else:3940B = Basis(args[0], args[1])3941if not (B._worked):3942print "Error: linearly dependent."3943return false3944else:3945print "Sucessful mapping constructed."3946return B39473948def Coefficients(Bas, value):3949if isinstance(Bas, Basis):3950return Bas.Coeff(value)3951print "First argument must be a basis set by ToBasis."3952return false39533954def SimpleExtension(arg1, arg2):3955# finds a single element so that Q(w) = Q(arg1, arg2)3956# Note that Sage has several ways of computing this w, but3957# we want to be consistent with the textbook, and return3958# arg1 + n*arg2 for the smallest positive n that works.3959if BaseCharacteristic > 0:3960print "Algorithm does not work on finite fields."3961return false3962w = arg1 + arg23963v = Vectorize(w)3964n = len(v)3965cont = true3966while cont:3967v = Vectorize(w)3968n = len(v)3969B = [w^i for i in range(n)]3970NB = Basis(B)3971if NB._worked:3972cont = false3973else:3974w = w + arg2 # try again with a different w3975return w3976397739783979print("Initialization Done")398039813982