Contents
Integer Factorization
Divisibility Poset
by William Stein
xxxxxxxxxx
def _(n=(5..100)):
Poset(([1..n], lambda x, y: y%x == 0) ).show()
Factor Trees
by William Stein
xxxxxxxxxx
import random
def ftree(rows, v, i, F):
if len(v) > 0: # add a row to g at the ith level.
rows.append(v)
w = []
for i in range(len(v)):
k, _, _ = v[i]
if k is None or is_prime(k):
w.append((None,None,None))
else:
d = random.choice(divisors(k)[1:-1])
w.append((d,k,i))
e = k//d
if e == 1:
w.append((None,None))
else:
w.append((e,k,i))
if len(w) > len(v):
ftree(rows, w, i+1, F)
def draw_ftree(rows,font):
g = Graphics()
for i in range(len(rows)):
cur = rows[i]
for j in range(len(cur)):
e, f, k = cur[j]
if not e is None:
if is_prime(e):
c = (1,0,0)
else:
c = (0,0,.4)
g += text(str(e), (j*2-len(cur),-i), fontsize=font, rgbcolor=c)
if not k is None and not f is None:
g += line([(j*2-len(cur),-i), ((k*2)-len(rows[i-1]),-i+1)],
alpha=0.5)
return g
def factor_tree(n=100, font=(10, (8..20)), redraw=['Redraw']):
n = Integer(n)
rows = []
v = [(n,None,0)]
ftree(rows, v, 0, factor(n))
show(draw_ftree(rows, font), axes=False)
More complicated demonstration using Mathematica: http://demonstrations.wolfram.com/FactorTrees/
Factoring an Integer
by Timothy Clemans
Sage implementation of the Mathematica demonstration of the same name. http://demonstrations.wolfram.com/FactoringAnInteger/
xxxxxxxxxx
def _(r=selector(range(0,10000,1000), label='range', buttons=True), n=slider(0,1000,1,2,'n',False)):
if not r and n in (0, 1):
n = 2
s = '$%d = %s$' % (r + n, factor(r + n))
s = s.replace('*', '\\times')
pretty_print(html(s))
Prime Numbers
Illustrating the prime number theorem
by William Stein
xxxxxxxxxx
def _(N=(100,list(range(2,2000)))):
pretty_print(html(r"<font color='red'>$\pi(x)$</font> and <font color='blue'>$x/(\log(x)-1)$</font> for $x < %s$"%N))
show(plot(prime_pi, 0, N, color='red') + plot(x/(log(x)-1), 5, N, color='blue'))
Prime Spiral - Square FIXME
by David Runde
xxxxxxxxxx
def square_prime_spiral(start=1, end=100, size_limit = 10, show_lines=false, invert=false, x_cord=0, y_cord=0, n = 0):
"""
REFERENCES:
Alpern, Dario. "Ulam's Spiral". http://www.alpertron.com.ar/ULAM.HTM
Sacks, Robert. http://www.NumberSpiral.com
Ventrella, Jeffery. "Prime Numbers are the Holes Behind Complex Composite Patterns". http://www.divisorplot.com
Williamson, John. Number Spirals. http://www.dcs.gla.ac.uk/~jhw/spirals/index.html jhw@dcs.gla.ac.uk
Weisstein, Eric W. "Prime-Generating Polynomial." From MathWorld--A Wolfram Web Resource. http://mathworld.wolfram.com/Prime-GeneratingPolynomial.html
"""
#Takes an (x,y) coordinate (and the start of the spiral) and gives its corresponding n value
def find_n(x,y, start):
if x>0 and y>-x and y<=x: return 4*(x-1)^2 + 5*(x-1) + (start+1) + y
elif x<=0 and y>=x and y<=-x: return 4*x^2 - x + (start) -y
elif y>=0 and -y+1 <= x and y-1 >= x: return 4*y^2 -y + start -x
elif y<0 and -x >= y and y<x: return 4*(y+1)^2 -11*(y+1) + (start+7) +x
else: print('NaN')
#Takes in an n and the start value of the spiral and gives its (x,y) coordinate
def find_xy(num, start):
num = num - start +1
bottom = floor(sqrt(num))
top = ceil(sqrt(num))
if bottom^2 < num and num<=bottom^2+bottom+1:
if bottom%2 == 0:
x=-bottom/2
y=-x-(num-bottom^2)+1
else:
x=bottom/2+1/2
y=-x + (num-bottom^2)
else:
if top%2 == 0:
y=top/2
x=-top/2+1+top^2-num
else:
y=-top/2+1/2
x=top/2 -1/2 - (top^2-num)
x = Integer(x)
y = Integer(y)
return (x,y)
if start < 1 or end <=start: print("invalid start or end value")
if n > end: print("WARNING: n is larger than the end value")
#Changes the entry of a matrix by taking the old matrix and the (x,y) coordinate (in matrix coordinates) and returns the changed matrix
def matrix_morph(M, x, y, set):
N = copy(M)
N[x-1,y] = set
M = N
return M
#These functions return an int based on where the t is located in the spiral
def SW_NE(t, x, y, start):
if -y<x: return 4*t^2 + 2*t -x+y+start
else: return 4*t^2 + 2*t +x-y+start
def NW_SE(t, x, y, start):
if x<y: return 4*t^2 -x-y+start
else: return 4*t^2 + 4*t +x+y+start
size = ceil(sqrt(end-start+1)) #Size of the matrix
num=copy(start) # Start number (might not be used)
x = ceil(size/2) #starting center x of the matrix (in matrix coordinates)
y = copy(x) #starting center y of the matrix (in matrix coordinates)
if n !=0: x_cord, y_cord = find_xy(n, start) #Overrides the user given x and y coordinates
xt = copy(x_cord)
yt = copy(y_cord)
countx=0
county=0
overcount = 1
if size <= size_limit: M = matrix(ZZ, size+1) # Allows the numbers to be seen in the smaller matricies
else: M = matrix(GF(2), size+1) # Restricts the entries to 0 or 1
main_list = set()
if show_lines:
for t in [(-size-1)..size+1]:
m= SW_NE(t, xt, yt, start)
if m.is_pseudoprime(): main_list.add(m)
m= NW_SE(t, xt, yt, start)
if m.is_pseudoprime(): main_list.add(m)
else: main_list = set(prime_range(end))
#This for loop changes the matrix by spiraling out from the center and changing each entry as it goes. It is faster than the find_xy function above.
for num in [start..end]:
if countx < overcount:
if overcount % 2 == 1: x+=1
else: x-=1
countx += 1
elif county < overcount:
if overcount % 2 == 1: y+=1
else: y-=1
county += 1
else:
overcount += 1
countx=2
county=0
if overcount % 2 == 1: x+=1
else: x-=1
if not invert and num in main_list:
if size <= size_limit: M = matrix_morph(M, x, y, num)
else: M = matrix_morph(M, x, y, 1)
elif invert and num not in main_list: #This does the opposite of the above if statement by changing the matrix only when a number is not in the list of allowable primes
if size <= size_limit: M = matrix_morph(M, x, y, num)
else: M = matrix_morph(M, x, y, 1)
if n != 0:
print('(to go from x,y coords to an n, reset by setting n=0)')
(x_cord, y_cord) = find_xy(n, start)
print('(x,y) =', (x_cord, y_cord), '<=> n =', find_n(x_cord, y_cord, start))
print(' ')
print("SW/NE line")
if -y_cord<x_cord: print('4*t^2 + 2*t +', -x_cord+y_cord+start)
else: print('4*t^2 + 2*t +', +x_cord-y_cord+start)
print("NW/SE line")
if x_cord<y_cord: print('4*t^2 +', -x_cord-y_cord+start)
else: print('4*t^2 + 4*t +', +x_cord+y_cord+start)
if size <= size_limit: show(M) #Displays the matrix with integer entries
else:
M.visualize_structure() # Displays the final resulting matrix as a series of pixels (1 <=> pixel on)
#matrix_plot(M)
Prime Spiral - Polar
by David Runde
Needs fix for show_factors
xxxxxxxxxx
def polar_prime_spiral(start=1, end=2000, show_factors = false, highlight_primes = false, show_curves=true, n = 0):
#For more information about the factors in the spiral, visit http://www.dcs.gla.ac.uk/~jhw/spirals/index.html by John Williamson.
if start < 1 or end <=start: print("invalid start or end value")
if n > end: print("WARNING: n is greater than end value")
def f(n):
return (sqrt(n)*cos(2*pi*sqrt(n)), sqrt(n)*sin(2*pi*sqrt(n)))
list =[]
list2=[]
if show_factors == false:
for i in [start..end]:
if i.is_pseudoprime(): list.append(f(i-start+1)) #Primes list
else: list2.append(f(i-start+1)) #Composites list
P = points(list)
R = points(list2, alpha = .1) #Faded Composites
else:
for i in [start..end]:
list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) #resizes each of the dots depending of the number of factors of each number
if i.is_pseudoprime() and highlight_primes: list2.append(f(i-start+1))
P = plot(list)
p_size = 5 #the orange dot size of the prime markers
if not highlight_primes: list2 = [(f(n-start+1))]
R=points(list2, hue = .1, pointsize = p_size)
if n > 0:
print('n = {}'.format(factor(n)))
p = 1
#The X which marks the given n
W1 = disk((f(n-start+1)), p, (pi/6, 2*pi/6))
W2 = disk((f(n-start+1)), p, (4*pi/6, 5*pi/6))
W3 = disk((f(n-start+1)), p, (7*pi/6, 8*pi/6))
W4 = disk((f(n-start+1)), p, (10*pi/6, 11*pi/6))
Q = plot(W1+W2+W3+W4, alpha = .1)
n=n-start+1 #offsets the n for different start values to ensure accurate plotting
if show_curves:
begin_curve = 0
t = var('t')
a=1
b=0
if n > (floor(sqrt(n)))^2 and n <= (floor(sqrt(n)))^2 + floor(sqrt(n)):
c = -((floor(sqrt(n)))^2 - n)
c2= -((floor(sqrt(n)))^2 + floor(sqrt(n)) - n)
else:
c = -((ceil(sqrt(n)))^2 - n)
c2= -((floor(sqrt(n)))^2 + floor(sqrt(n)) - n)
print('Pink Curve: n^2 +', c)
print('Green Curve: n^2 + n +', c2)
def g(m): return (a*m^2+b*m+c);
def r(m) : return sqrt(g(m))
def theta(m) : return r(m)- m*sqrt(a)
S1 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), begin_curve, ceil(sqrt(end-start)), rgbcolor=hue(0.8), thickness = .2) #Pink Line
b=1
c= c2;
S2 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), begin_curve, ceil(sqrt(end-start)), rgbcolor=hue(0.6), thickness = .2) #Green Line
show(R+P+S1+S2+Q, aspect_ratio = 1, axes = false)
else: show(R+P+Q, aspect_ratio = 1, axes = false)
else: show(R+P, aspect_ratio = 1, axes = false)
Modular Forms
Computing modular forms
by William Stein
xxxxxxxxxx
def _(N=[1..100], k=selector([2,4,..,12],nrows=1), prec=(3..40),
group=[(Gamma0, 'Gamma0'), (Gamma1, 'Gamma1')]):
M = CuspForms(group(N),k)
print(M)
print('\n' * 3)
print("Computing basis...\n\n")
if M.dimension() == 0:
print("Space has dimension 0")
else:
prec = max(prec, M.dimension() + 1)
for f in M.basis():
view(f.q_expansion(prec))
print("\n\n\nDone computing basis.")
Computing the cuspidal subgroup
by William Stein
ncols not working
xxxxxxxxxx
pretty_print(html('<h1>Cuspidal Subgroups of Modular Jacobians J0(N)</h1>'))
def _(N=selector([1..8*13], ncols=8, width=10, default=10)):
A = J0(N)
print(A.cuspidal_subgroup())
A Charpoly and Hecke Operator Graph
by William Stein
xxxxxxxxxx
# Note -- in Sage-2.10.3; multiedges are missing in plots; loops are missing in 3d plots
def f(N = prime_range(11,400),
p = selector(prime_range(2,12),nrows=1),
three_d = ("Three Dimensional", False)):
S = SupersingularModule(N)
T = S.hecke_matrix(p)
G = DiGraph(T, multiedges=not three_d)
if three_d:
G.remove_loops()
html("<h1>Charpoly and Hecke Graph: Level %s, T_%s</h1>"%(N,p))
show(T.charpoly().factor())
if three_d:
show(G.plot3d(), aspect_ratio=[1,1,1])
else:
show(G.plot(),figsize=7)
Modular Arithmetic
Quadratic Residue Table FIXME
by Emily Kirkman
xxxxxxxxxx
from numpy import array as narray
def quad_res_plot(first_n_odd_primes = (20,200),display_size=[7..15]):
# Compute numpy matrix of legendre symbols
r = int(first_n_odd_primes)
np = [nth_prime(i+2) for i in range(r)]
leg = [[legendre_symbol(np[i], np[j]) for i in range(r)] for j in range(r)]
na = narray(leg)
for i in range(r):
for j in range(r):
if na[i][j] == 1 and Mod((np[i]-1)*(np[j]-1)//4,2) == 0:
na[i][j] = 2
m = matrix(na)
# Define plot structure
MP = matrix_plot(m, cmap='Oranges')
for i in range(r):
if np[-1] < 100:
MP += text('%d'%nth_prime(i+2),(-.75,r-i-.5), rgbcolor='black')
MP += text('%d'%nth_prime(i+2), (i+.5,r+.5), rgbcolor='black')
if len(np) < 75:
MP += line([(0,i),(r,i)], rgbcolor='black')
MP += line([(i,0),(i,r)], rgbcolor='black')
if np[-1] < 100:
for i in range(r): # rows
for j in range(r): # cols
if m[j][i] == 0:
MP += text('0',(i+.5,r-j-.5),rgbcolor='black')
elif m[j][i] == -1:
MP += text('N',(i+.5,r-j-.5),rgbcolor='black')
elif m[j][i] == 1:
MP += text('A',(i+.5,r-j-.5),rgbcolor='black')
elif m[j][i] == 2:
MP += text('S',(i+.5,r-j-.5),rgbcolor='black')
MP += line([(0,r),(r,r)], rgbcolor='black')
MP += line([(r,0),(r,r)], rgbcolor='black')
MP += line([(0,0),(r,0)], rgbcolor='black')
MP += line([(0,0),(0,r)], rgbcolor='black')
if len(np) < 75:
MP += text('q',(r/2,r+2), rgbcolor='black', fontsize=15)
MP += text('p',(-2.5,r/2), rgbcolor='black', fontsize=15)
MP.show(axes=False, ymax=r, figsize=[display_size,display_size])
html('Symmetry of Prime Quadratic Residues mod the first %d odd primes.'%r)
Cubic Residue Table FIXME
by Emily Kirkman
xxxxxxxxxx
def power_residue_symbol(alpha, p, m):
if p.divides(alpha): return 0
if not p.is_prime():
return prod(power_residue_symbol(alpha,ell,m)^e
for ell, e in p.factor())
F = p.residue_field()
N = p.norm()
r = F(alpha)^((N-1)/m)
k = p.number_field()
for kr in k.roots_of_unity():
if r == F(kr):
return kr
def cubic_is_primary(n):
g = n.gens_reduced()[0]
a,b = g.polynomial().coefficients()
return Mod(a,3)!=0 and Mod(b,3)==0
from numpy import array as narray
def cubic_sym(n=(10..35),display_size=[7..15]):
# Compute numpy matrix of primary cubic residue symbols
r = n
m=3
D.<w> = NumberField(x^2+x+1)
it = D.primes_of_degree_one_iter()
pp = []
while len(pp) < r:
k = it.next()
if cubic_is_primary(k):
pp.append(k)
n = narray([ [ power_residue_symbol(pp[i].gens_reduced()[0], pp[j], m) \
for i in range(r) ] for j in range(r) ])
# Convert to integer matrix for gradient colors
for i in range(r):
for j in range(r):
if n[i][j] == w:
n[i][j] = int(-1)
elif n[i][j] == w^2:
n[i][j] = int(-2)
elif n[i][j] == 1:
n[i][j] = int(1)
m = matrix(n)
# Define plot structure
MP = matrix_plot(m,cmap="Blues")
for i in range(r):
MP += line([(0,i),(r,i)], rgbcolor='black')
MP += line([(i,0),(i,r)], rgbcolor='black')
for j in range(r):
if m[i][j] == -2:
MP += text(r'$\omega^2$',(i+.5,r-j-.5),rgbcolor='black')
if m[i][j] == -1:
MP += text(r'$\omega $',(i+.5,r-j-.5),rgbcolor='black')
if m[i][j] == 0:
MP += text('0',(i+.5,r-j-.5),rgbcolor='black')
if m[i][j] == 1:
MP += text('R',(i+.5,r-j-.5),rgbcolor='white')
MP += line([(0,r),(r,r)], rgbcolor='black')
MP += line([(r,0),(r,r)], rgbcolor='black')
MP += line([(0,0),(r,0)], rgbcolor='black')
MP += line([(0,0),(0,r)], rgbcolor='black')
MP += text(r'$ \pi_1$',(r/2,r+2), rgbcolor='black', fontsize=25)
MP += text(r'$ \pi_2$',(-2.5,r/2), rgbcolor='black', fontsize=25)
pretty_print(html('Symmetry of Primary Cubic Residues mod ' \
+ r'%d primary primes in $ \mathbf Z[\omega]$.'%r))
MP.show(axes=False, figsize=[display_size,display_size])
Cyclotomic Fields
Gauss and Jacobi Sums in Complex Plane
by Emily Kirkman
xxxxxxxxxx
def jacobi_sum(e,f):
# If they are both trivial, return p
if e.is_trivial() and f.is_trivial():
return (e.parent()).order() + 1
# If they are inverses of each other, return -e(-1)
g = e*f
if g.is_trivial():
return -e(-1)
# If both are nontrivial, apply mult. formula:
elif not e.is_trivial() and not f.is_trivial():
return e.gauss_sum()*f.gauss_sum()/g.gauss_sum()
# If exactly one is trivial, return 0
else:
return 0
def latex2(e):
return latex(list(e.values_on_gens()))
def jacobi_plot(p, e_index, f_index, with_text=True):
# Set values
G = DirichletGroup(p)
e = G[e_index]
f = G[f_index]
ef = e*f
js = jacobi_sum(e,f)
e_gs = e.gauss_sum()
f_gs = f.gauss_sum()
ef_gs = (e*f).gauss_sum()
# Compute complex coordinates
f_pt = list(f.values_on_gens()[0].complex_embedding())
e_pt = list(e.values_on_gens()[0].complex_embedding())
ef_pt = list(ef.values_on_gens()[0].complex_embedding())
f_gs_pt = list(f_gs.complex_embedding())
e_gs_pt = list(e_gs.complex_embedding())
ef_gs_pt = list(ef_gs.complex_embedding())
try:
js = int(js)
js_pt = [CC(js)]
except:
js_pt = list(js.complex_embedding())
# Define plot structure
S = circle((0,0),1,rgbcolor='yellow')
S += line([e_pt,e_gs_pt], rgbcolor='red', thickness=4)
S += line([f_pt,f_gs_pt], rgbcolor='blue', thickness=3)
S += line([ef_pt,ef_gs_pt], rgbcolor='purple',thickness=2)
S += point(e_pt,pointsize=50, rgbcolor='red')
S += point(f_pt,pointsize=50, rgbcolor='blue')
S += point(ef_pt,pointsize=50,rgbcolor='purple')
S += point(f_gs_pt,pointsize=75, rgbcolor='black')
S += point(e_gs_pt,pointsize=75, rgbcolor='black')
S += point(ef_gs_pt,pointsize=75, rgbcolor='black')
S += point(js_pt,pointsize=100,rgbcolor='green')
if with_text:
S += text('$J(%s,%s) = %s$'%(latex2(e),latex2(f),latex(js)),
(3,2.5),fontsize=15, rgbcolor='black')
else:
html('$$J(%s,%s) = %s$$'%(latex2(e),latex2(f),latex(js)))
return S
def single_jacobi_plot(p=prime_range(3,100), e_range=(0..100), f_range=(0..100)):
e_index = floor((p-2)*e_range/100)
f_index = floor((p-2)*f_range/100)
S = jacobi_plot(p,e_index,f_index,with_text=False)
S.show(aspect_ratio=1)
Exhaustive Jacobi Plotter
by Emily Kirkman
xxxxxxxxxx
def jacobi_sum(e,f):
# If they are both trivial, return p
if e.is_trivial() and f.is_trivial():
return (e.parent()).order() + 1
# If they are inverses of each other, return -e(-1)
g = e*f
if g.is_trivial():
return -e(-1)
# If both are nontrivial, apply mult. formula:
elif not e.is_trivial() and not f.is_trivial():
return e.gauss_sum()*f.gauss_sum()/g.gauss_sum()
# If exactly one is trivial, return 0
else:
return 0
def latex2(e):
return latex(list(e.values_on_gens()))
def jacobi_plot(p, e_index, f_index, with_text=True):
# Set values
G = DirichletGroup(p)
e = G[e_index]
f = G[f_index]
ef = e*f
js = jacobi_sum(e,f)
e_gs = e.gauss_sum()
f_gs = f.gauss_sum()
ef_gs = (e*f).gauss_sum()
# Compute complex coordinates
f_pt = list(f.values_on_gens()[0].complex_embedding())
e_pt = list(e.values_on_gens()[0].complex_embedding())
ef_pt = list(ef.values_on_gens()[0].complex_embedding())
f_gs_pt = list(f_gs.complex_embedding())
e_gs_pt = list(e_gs.complex_embedding())
ef_gs_pt = list(ef_gs.complex_embedding())
try:
js = int(js)
js_pt = [CC(js)]
except:
js_pt = list(js.complex_embedding())
# Define plot structure
S = circle((0,0),1,rgbcolor='yellow')
S += line([e_pt,e_gs_pt], rgbcolor='red', thickness=4)
S += line([f_pt,f_gs_pt], rgbcolor='blue', thickness=3)
S += line([ef_pt,ef_gs_pt], rgbcolor='purple',thickness=2)
S += point(e_pt,pointsize=50, rgbcolor='red')
S += point(f_pt,pointsize=50, rgbcolor='blue')
S += point(ef_pt,pointsize=50,rgbcolor='purple')
S += point(f_gs_pt,pointsize=75, rgbcolor='black')
S += point(e_gs_pt,pointsize=75, rgbcolor='black')
S += point(ef_gs_pt,pointsize=75, rgbcolor='black')
S += point(js_pt,pointsize=100,rgbcolor='green')
if with_text:
S += text('$J(%s,%s) = %s$'%(latex2(e),latex2(f),latex(js)),
(3,2.5),fontsize=15, rgbcolor='black')
else:
pretty_print(html('$$J(%s,%s) = %s$$'%(latex2(e),latex2(f),latex(js))))
return S
def exhaustive_jacobi_plot(p=prime_range(3,8)):
ga = [jacobi_plot(p,i,j) for i in range(p-1) for j in range(p-1)[i:]]
for i in range(len(ga)):
ga[i].save('j%d.png'%i,figsize=4,aspect_ratio=1,
xmin=-2.5,xmax=5, ymin=-2.5, ymax=2.5)
# Since p is odd, will have n = p-1 even. So 1+2+...+n = (n/2)*(n+1).
# We divide this by rows of 2.
rows = ceil(p*(p-1)/4)
s='<table bgcolor=lightgrey cellpadding=2>'
for i in range(rows):
s+='<tr><td align="center"><img src="cell://j%d.png"></td>'%(2*i)
s+='<td align="center"><img src="cell://j%d.png"></td></tr>'%(2*i+1)
s+='</table>'
pretty_print(html(s))
Elliptic Curves
Adding points on an elliptic curve
by David Møller Hansen
xxxxxxxxxx
def point_txt(P,name,rgbcolor):
if (P.xy()[1]) < 0:
r = text(name,[float(P.xy()[0]),float(P.xy()[1])-1],rgbcolor=rgbcolor)
elif P.xy()[1] == 0:
r = text(name,[float(P.xy()[0]),float(P.xy()[1])+1],rgbcolor=rgbcolor)
else:
r = text(name,[float(P.xy()[0]),float(P.xy()[1])+1],rgbcolor=rgbcolor)
return r
E = EllipticCurve('37a')
list_of_points = E.integral_points()
html("Graphical addition of two points $P$ and $Q$ on the curve $ E: %s $"%latex(E))
def line_from_curve_points(E,P,Q,style='-',rgb=(1,0,0),length=25):
"""
P,Q two points on an elliptic curve.
Output is a graphic representation of the straight line intersecting with P,Q.
"""
# The function tangent to P=Q on E
if P == Q:
if P[2]==0:
return line([(1,-length),(1,length)],linestyle=style,rgbcolor=rgb)
else:
# Compute slope of the curve E in P
l=-(3*P[0]^2 + 2*E.a2()*P[0] + E.a4() - E.a1()*P[1])/((-2)*P[1] - E.a1()*P[0] - E.a3())
f(x) = l * (x - P[0]) + P[1]
return plot(f(x),-length,length,linestyle=style,rgbcolor=rgb)
# Trivial case of P != R where P=O or R=O then we get the vertical line from the other point
elif P[2] == 0:
return line([(Q[0],-length),(Q[0],length)],linestyle=style,rgbcolor=rgb)
elif Q[2] == 0:
return line([(P[0],-length),(P[0],length)],linestyle=style,rgbcolor=rgb)
# Non trivial case where P != R
else:
# Case where x_1 = x_2 return vertical line evaluated in Q
if P[0] == Q[0]:
return line([(P[0],-length),(P[0],length)],linestyle=style,rgbcolor=rgb)
#Case where x_1 != x_2 return line trough P,R evaluated in Q"
l=(Q[1]-P[1])/(Q[0]-P[0])
f(x) = l * (x - P[0]) + P[1]
return plot(f(x),-length,length,linestyle=style,rgbcolor=rgb)
def _(P=selector(list_of_points,label='Point P'),Q=selector(list_of_points,label='Point Q'), marked_points = checkbox(default=True,label = 'Points'), Lines = selector([0..2],nrows=1), Axes=True):
curve = E.plot(rgbcolor = (0,0,1),xmin=-5,xmax=5,plot_points=300)
R = P + Q
Rneg = -R
l1 = line_from_curve_points(E,P,Q)
l2 = line_from_curve_points(E,R,Rneg,style='--')
p1 = plot(P,rgbcolor=(1,0,0),pointsize=40)
p2 = plot(Q,rgbcolor=(1,0,0),pointsize=40)
p3 = plot(R,rgbcolor=(1,0,0),pointsize=40)
p4 = plot(Rneg,rgbcolor=(1,0,0),pointsize=40)
textp1 = point_txt(P,"$P$",rgbcolor=(0,0,0))
textp2 = point_txt(Q,"$Q$",rgbcolor=(0,0,0))
textp3 = point_txt(R,"$P+Q$",rgbcolor=(0,0,0))
if Lines==0:
g=curve
elif Lines ==1:
g=curve+l1
elif Lines == 2:
g=curve+l1+l2
if marked_points:
g=g+p1+p2+p3+p4
if P != Q:
g=g+textp1+textp2+textp3
else:
g=g+textp1+textp3
g.axes_range(xmin=-5,xmax=5,ymin=-13,ymax=13)
show(g,axes = Axes)
Plotting an elliptic curve over a finite field
xxxxxxxxxx
E = EllipticCurve('37a')
def _(p=slider(prime_range(1000), default=389)):
show(E)
print("p = %s" % p)
show(E.change_ring(GF(p)).plot(), xmin=0, ymin=0)
Cryptography
The Diffie-Hellman Key Exchange Protocol
by Timothy Clemans and William Stein
xxxxxxxxxx
def diffie_hellman(bits=slider(8, 513, 4, 8, 'Number of bits', False),
button=selector(["Show new example"],label='',buttons=True)):
maxp = 2 ^ bits
p = random_prime(maxp)
k = GF(p)
if bits > 100:
g = k(2)
else:
g = k.multiplicative_generator()
a = ZZ.random_element(10, maxp)
b = ZZ.random_element(10, maxp)
pretty_print(html("""
<style>
.gamodp, .gbmodp {
color:#000;
padding:5px
}
.gamodp {
background:#846FD8
}
.gbmodp {
background:#FFFC73
}
.dhsame {
color:#000;
font-weight:bold
}
</style>
<h2 style="color:#000;font-family:Arial, Helvetica, sans-serif">%s-Bit Diffie-Hellman Key Exchange</h2>
<ol style="color:#000;font-family:Arial, Helvetica, sans-serif">
<li>Alice and Bob agree to use the prime number p = %s and base g = %s.</li>
<li>Alice chooses the secret integer a = %s, then sends Bob (<span class="gamodp">g<sup>a</sup> mod p</span>):<br/>%s<sup>%s</sup> mod %s = <span class="gamodp">%s</span>.</li>
<li>Bob chooses the secret integer b=%s, then sends Alice (<span class="gbmodp">g<sup>b</sup> mod p</span>):<br/>%s<sup>%s</sup> mod %s = <span class="gbmodp">%s</span>.</li>
<li>Alice computes (<span class="gbmodp">g<sup>b</sup> mod p</span>)<sup>a</sup> mod p:<br/>%s<sup>%s</sup> mod %s = <span class="dhsame">%s</span>.</li>
<li>Bob computes (<span class="gamodp">g<sup>a</sup> mod p</span>)<sup>b</sup> mod p:<br/>%s<sup>%s</sup> mod %s = <span class="dhsame">%s</span>.</li>
</ol>
""" % (bits, p, g, a, g, a, p, (g^a), b, g, b, p, (g^b), (g^b), a, p,
(g^ b)^a, g^a, b, p, (g^a)^b)))
Other
Continued Fraction Plotter
by William Stein
crows not working
xxxxxxxxxx
def _(number=e, ymax=selector([5,20,..,400],nrows=2), clr=Color('purple'), prec=[500,1000,..,5000]):
c = list(continued_fraction(RealField(prec)(number))); print(c)
show(line([(i,z) for i, z in enumerate(c)],rgbcolor=clr),ymax=ymax,figsize=[10,2])
Computing Generalized Bernoulli Numbers
by William Stein (Sage-2.10.3)
xxxxxxxxxx
def _(m=selector([1..15],nrows=2), n=(7,[3..10])):
G = DirichletGroup(m)
s = r"<h3>First n=%s Bernoulli numbers attached to characters with modulus m=%s</h3>"%(n,m)
s += r'<table border=1>'
s += r'<tr bgcolor="#edcc9c"><td align=center>$\chi$</td><td>Conductor</td>' + \
''.join(r'<td>$B_{%s,\chi}$</td>'%k for k in [1..n]) + '</tr>'
for eps in G.list():
v = ''.join(['<td align=center bgcolor="#efe5cd">$%s$</td>'%latex(eps.bernoulli(k)) for k in [1..n]])
s += '<tr><td bgcolor="#edcc9c">%s</td><td bgcolor="#efe5cd" align=center>%s</td>%s</tr>\n'%(
eps, eps.conductor(), v)
s += '</table>'
pretty_print(html(s))
Fundamental Domains of SL_2(ZZ)
by Robert Miller
xxxxxxxxxx
L = [[-0.5, 2.0^(x/100.0) - 1 + sqrt(3.0)/2] for x in range(1000, -1, -1)]
R = [[0.5, 2.0^(x/100.0) - 1 + sqrt(3.0)/2] for x in range(1000)]
xes = [x/1000.0 for x in range(-500,501,1)]
M = [[x,abs(sqrt(x^2-1))] for x in xes]
fundamental_domain = L+M+R
fundamental_domain = [[x-1,y] for x,y in fundamental_domain]
def _(gen = selector(['t+1', 't-1', '-1/t'], buttons=True,nrows=1)):
global fundamental_domain
if gen == 't+1':
fundamental_domain = [[x+1,y] for x,y in fundamental_domain]
elif gen == 't-1':
fundamental_domain = [[x-1,y] for x,y in fundamental_domain]
elif gen == '-1/t':
new_dom = []
for x,y in fundamental_domain:
sq_mod = x^2 + y^2
new_dom.append([(-1)*x/sq_mod, y/sq_mod])
fundamental_domain = new_dom
P = polygon(fundamental_domain)
P.ymax(1.2); P.ymin(-0.1)
P.show()
Multiple Zeta Values
by Akhilesh P.
Computing Multiple Zeta values
Word Input
xxxxxxxxxx
R=RealField(10)
def _( weight=(5,(2..100))):
n=weight
a=[0 for i in range(n-1)]
a.append(1)
def _(v=('word', input_grid(1, n, default=[a], to_value=lambda x: vector(flatten(x)))), accuracy=(100..100000)):
D=accuracy
a=[v[i] for i in range(len(v))]
DD=int(3.321928*D)+int(R(log(3.321928*D))/R(log(10)))+4
RIF=RealIntervalField(DD)
def Li(word):
n=int(DD*log(10)/log(2))+1
B=[]
L=[]
S=[]
count=-1
k=len(word)
for i in range(k):
B.append(RIF('0'))
L.append(RIF('0'))
if(word[i]==1 and i<k-1):
S.append(RIF('0'))
count=count+1
T=RIF('1')
for m in range(n):
T=T/2
B[k-1]=RIF('1')/(m+1)
j=count
for i in range(k-2,-1,-1):
if(word[i]==0):
B[i]=B[i+1]/(m+1)
elif(word[i]==1):
B[i]=S[j]/(m+1)
S[j]=S[j]+B[i+1]
j=j-1
L[i]=T*B[i]+L[i]
L[k-1]=T*B[k-1]+L[k-1]
return(L)
def dual(a):
b=list()
b=a
b=b[::-1]
for i in range(len(b)):
b[i]=1-b[i]
return(b)
def zeta(a):
b=dual(a)
l1=Li(a)+[1]
l2=Li(b)+[1]
Z=RIF('0')
for i in range(len(l1)):
Z=Z+l1[i]*l2[len(a)-i]
return(Z)
u=zeta(a)
RIF=RealIntervalField(int(3.321928*D))
u=u/1
print(u)
Composition Input
xxxxxxxxxx
R=RealField(10)
def _( Depth=(5,(2..100))):
n=Depth
a=[2]
a=a+[1 for i in range(n-1)]
def _(v=('Composition', input_grid(1, n, default=[a], to_value=lambda x: vector(flatten(x)))), accuracy=(100..100000)):
D=accuracy
a=[v[i] for i in range(len(v))]
def comptobin(a):
word=[]
for i in range(len(a)):
word=word+[0]*(a[i]-1)+[1]
return(word)
a=comptobin(a)
DD=int(3.321928*D)+int(R(log(3.321928*D))/R(log(10)))+4
RIF=RealIntervalField(DD)
def Li(word):
n=int(DD*log(10)/log(2))+1
B=[]
L=[]
S=[]
count=-1
k=len(word)
for i in range(k):
B.append(RIF('0'))
L.append(RIF('0'))
if(word[i]==1 and i<k-1):
S.append(RIF('0'))
count=count+1
T=RIF('1')
for m in range(n):
T=T/2
B[k-1]=RIF('1')/(m+1)
j=count
for i in range(k-2,-1,-1):
if(word[i]==0):
B[i]=B[i+1]/(m+1)
elif(word[i]==1):
B[i]=S[j]/(m+1)
S[j]=S[j]+B[i+1]
j=j-1
L[i]=T*B[i]+L[i]
L[k-1]=T*B[k-1]+L[k-1]
return(L)
def dual(a):
b=list()
b=a
b=b[::-1]
for i in range(len(b)):
b[i]=1-b[i]
return(b)
def zeta(a):
b=dual(a)
l1=Li(a)+[1]
l2=Li(b)+[1]
Z=RIF('0')
for i in range(len(l1)):
Z=Z+l1[i]*l2[len(a)-i]
return(Z)
u=zeta(a)
RIF=RealIntervalField(int(3.321928*D))
u=u/1
print(u)
Program to Compute Integer Relation between Multiple Zeta Values
xxxxxxxxxx
from mpmath import *
print("Enter the number of composition")
def _( n=(5,(2..100))):
a=[]
for i in range(n):
a.append([i+2,1])
print("In each box Enter composition as an array")
def _(v=('Compositions', input_box( default=a, to_value=lambda x: vector(flatten(x)))), accuracy=(100..100000)):
D=accuracy
R=RealField(10)
a=v
def comptobin(a):
word=[]
for i in range(len(a)):
word=word+[0]*(a[i]-1)+[1]
return(word)
DD=int(D)+int(R(log(3.321928*D))/R(log(10)))+4
RIF=RealIntervalField(DD)
mp.dps=DD
def Li(word):
n=int(DD*log(10)/log(2))+1
B=[]
L=[]
S=[]
count=-1
k=len(word)
for i in range(k):
B.append(mpf('0'))
L.append(mpf('0'))
if(word[i]==1 and i<k-1):
S.append(mpf('0'))
count=count+1
T=mpf('1')
for m in range(n):
T=T/2
B[k-1]=mpf('1')/(m+1)
j=count
for i in range(k-2,-1,-1):
if(word[i]==0):
B[i]=B[i+1]/(m+1)
elif(word[i]==1):
B[i]=S[j]/(m+1)
S[j]=S[j]+B[i+1]
j=j-1
L[i]=T*B[i]+L[i]
L[k-1]=T*B[k-1]+L[k-1]
return(L)
def dual(a):
b=list()
b=a
b=b[::-1]
for i in range(len(b)):
b[i]=1-b[i]
return(b)
def zeta(a):
b=dual(a)
l1=Li(a)+[1]
l2=Li(b)+[1]
Z=mpf('0')
for i in range(len(l1)):
Z=Z+l1[i]*l2[len(a)-i]
return(Z)
zet=[]
for i in range(n):
zet.append((zeta(comptobin(a[i]))))
mp.dps=D
for i in range(n):
zet[i]=zet[i]/1
print("zeta(", a[i], ")=", zet[i])
u=pslq(zet,tol=10**-D,maxcoeff=100,maxsteps=10000)
print("the Intger Relation between the above zeta values given by the vector")
print(u)
Word to composition
xxxxxxxxxx
def _( weight=(7,(2..100))):
n=weight
a=[0 for i in range(n-1)]
a.append(1)
def _(v=('word', input_grid(1, n, default=[a], to_value=lambda x: vector(flatten(x))))):
a=[v[i] for i in range(len(v))]
def bintocomp(a):
b=[]
count=1
for j in range(len(a)):
if(a[j]==0):
count=count+1
else:
b.append(count)
count=1
return(b)
print("Composition is {}".format(bintocomp(a)))
Composition to Word
xxxxxxxxxx
def _( Depth=(7,(1..100))):
n=Depth
a=[]
a.append(2)
a=a+[1 for i in range(1,n)]
def _(v=('composition', input_grid(1, n, default=[a], to_value=lambda x: vector(flatten(x))))):
a=[v[i] for i in range(len(v))]
def comptobin(a):
word=[]
for i in range(len(a)):
word=word+[0]*(a[i]-1)+[1]
return(word)
print("Word is {}".format(comptobin(a)))
Dual of a Word
xxxxxxxxxx
def _( weight=(7,(2..100))):
n=weight
a=[0 for i in range(n-1)]
a.append(1)
def _(v=('word', input_grid(1, n, default=[a], to_value=lambda x: vector(flatten(x))))):
a=[v[i] for i in range(len(v))]
def dual(a):
b=list()
b=a
b=b[::-1]
for i in range(len(b)):
b[i]=1-b[i]
return(b)
print("Dual word is {}"?format(dual(a)))
Shuffle product of two Words
xxxxxxxxxx
def _( w1=(2,(2..100)), w2=(2,(2..100))):
a=[0]
b=[0 for i in range(w2-1)]
a=a+[1 for i in range(1,w1)]
b=b+[1]
import itertools
#this program gives the list of all binary words of weight n and depth k
def _(v1=('word1', input_grid(1, w1, default=[a], to_value=lambda x: vector(flatten(x)))), v2=('word2', input_grid(1, w2, default=[b], to_value=lambda x: vector(flatten(x))))):
a=[v1[i] for i in range(len(v1))]
b=[v2[i] for i in range(len(v2))]
def kbits(n, k):
result = []
for bits in itertools.combinations(range(n), k):
s = ['0'] * n
for bit in bits:
s[bit] = '1'
result.append(''.join(s))
return result
def sort(a,l,m):
b=[]
n=len(a)
for i in range(n):
b.append(a[i])
for j in range(l-1,-1,-1):
k=0
for t in range(m+1):
for i in range(n):
if(a[i][j]== t):
b[k]=a[i]
k=k+1
for i in range(n):
a[i]=b[i]
return(a)
def count(a):
n=len(a)
b=[]
b.append(a[0])
m=[]
m.append(1)
c=0
for i in range(1,n):
if(a[i]==a[i-1]):
m[c]=m[c]+1
else:
b.append(a[i])
m.append(1)
c=c+1
return(b,m)
def shuffle(a,b):
r=len(a)
s=len(b)
# Generating an array of strings containing all combinations of weight r+s and depth s
M=kbits(r+s,s)
n=len(M)
a1= []
for i in range(n):
a1.append(list(M[i]))
# The zeroes are replaced by the entries of a and the ones by the entries of b
a2= []
for i in range(n):
a2.append([])
count0=0
count1=0
for j in range(s+r):
if(a1[i][j]=='0'):
a2[i].append(a[count0])
count0=count0+1
if(a1[i][j]=='1'):
a2[i].append(b[count1])
count1=count1+1
# Reordering in lexicographic order the entries of a2: this is done by first reordering them according to the last digit, then the next to last digit, etc
a3=sort(a2,r+s,max(a+b+[0]))
# Getting the same list without repetitions and with multiplicities
a4=count(a3)
return(a4)
c=shuffle(a,b)
for i in range(len(c[0])-1):
print(c[1][i],"*",c[0][i] ,"+ ")
print(c[1][len(c[0])-1],"*",c[0][len(c[0])-1])
Shuffle Regularization at 0
xxxxxxxxxx
def _( w=(2,(2..100))):
a=[0]
a=a+[1 for i in range(1,w)]
import itertools
#this program gives the list of all binary words of weight n and depth k
def _(v=('word', input_grid(1, w, default=[a], to_value=lambda x: vector(flatten(x))))):
a=[v[i] for i in range(len(v))]
def kbits(n, k):
result = []
for bits in itertools.combinations(range(n), k):
s = ['0'] * n
for bit in bits:
s[bit] = '1'
result.append(''.join(s))
return result
def sort(a,l,m):
b=[]
n=len(a)
for i in range(n):
b.append(a[i])
for j in range(l-1,-1,-1):
k=0
for t in range(m+1):
for i in range(n):
if(a[i][j]== t):
b[k]=a[i]
k=k+1
for i in range(n):
a[i]=b[i]
return(a)
def sort1(a,l,m):
b=[]
b.append([])
b.append([])
n=len(a[0])
for i in range(n):
b[0].append(a[0][i])
b[1].append(a[1][i])
for j in range(l-1,-1,-1):
k=0
for t in range(m+1):
for i in range(n):
if(a[0][i][j]== t):
b[0][k]=a[0][i]
b[1][k]=a[1][i]
k=k+1
for i in range(n):
a[0][i]=b[0][i]
a[1][i]=b[1][i]
return(a)
def count(a):
n=len(a)
b=[]
b.append(a[0])
m=[]
m.append(1)
c=0
for i in range(1,n):
if(a[i]==a[i-1]):
m[c]=m[c]+1
else:
b.append(a[i])
m.append(1)
c=c+1
return(b,m)
def count1(a):
n=len(a[0])
b=[]
b.append([])
b.append([])
b[0].append(a[0][0])
b[1].append(a[1][0])
c=0
for i in range(1,n):
if(a[0][i]==a[0][i-1]):
b[1][c]=b[1][c]+a[1][i]
else:
b[0].append(a[0][i])
b[1].append(a[1][i])
c=c+1
return(b)
def shuffle(a,b):
r=len(a)
s=len(b)
# Generating an array of strings containing all combinations of weight r+s and depth s
M=kbits(r+s,s)
n=len(M)
a1= []
for i in range(n):
a1.append(list(M[i]))
# The zeroes are replaced by the entries of a and the ones by the entries of b
a2= []
for i in range(n):
a2.append([])
count0=0
count1=0
for j in range(s+r):
if(a1[i][j]=='0'):
a2[i].append(a[count0])
count0=count0+1
if(a1[i][j]=='1'):
a2[i].append(b[count1])
count1=count1+1
# Reordering in lexicographic order the entries of a2: this is done by first reordering them according to the last digit, then the next to last digit, etc
a3=sort(a2,r+s,max(a+b+[0]))
# Getting the same list without repetitions and with multiplicities
a4=count(a3)
return(a4)
def Regshuf0(a):
r=[]
r.append([])
r.append([])
t=0
c=1
for i in range(len(a)+1):
if(t==0):
b=shuffle(a[:len(a)-i],a[len(a)-i:])
for j in range(len(b[0])):
r[0].append(b[0][j])
r[1].append(b[1][j]*c)
c=-c
if(i<len(a)):
if(a[len(a)-1-i]==1):
t=1
r=sort1(r,len(a),max(a+[0]))
r=count1(r)
rg=[]
rg.append([])
rg.append([])
for i in range(len(r[0])):
if(r[1][i] is not 0):
rg[0].append(r[0][i])
rg[1].append(r[1][i])
return(rg)
c = Regshuf0(a)
for i in range(len(c[0])-1):
if(c[1][i] != 0):
print(c[1][i],"*",c[0][i] ,"+ ")
if(c[1][len(c[0])-1] != 0):
print(c[1][len(c[0])-1],"*",c[0][len(c[0])-1])
Shuffle Regularization at 1
xxxxxxxxxx
def _( w=(2,(2..20))):
a=[0]
a=a+[1 for i in range(1,w)]
import itertools
#this program gives the list of all binary words of weight n and depth k
def _(v=('word', input_grid(1, w, default=[a], to_value=lambda x: vector(flatten(x))))):
a=[v[i] for i in range(len(v))]
def kbits(n, k):
result = []
for bits in itertools.combinations(range(n), k):
s = ['0'] * n
for bit in bits:
s[bit] = '1'
result.append(''.join(s))
return result
def sort(a,l,m):
b=[]
n=len(a)
for i in range(n):
b.append(a[i])
for j in range(l-1,-1,-1):
k=0
for t in range(m+1):
for i in range(n):
if(a[i][j]== t):
b[k]=a[i]
k=k+1
for i in range(n):
a[i]=b[i]
return(a)
def sort1(a,l,m):
b=[]
b.append([])
b.append([])
n=len(a[0])
for i in range(n):
b[0].append(a[0][i])
b[1].append(a[1][i])
for j in range(l-1,-1,-1):
k=0
for t in range(m+1):
for i in range(n):
if(a[0][i][j]== t):
b[0][k]=a[0][i]
b[1][k]=a[1][i]
k=k+1
for i in range(n):
a[0][i]=b[0][i]
a[1][i]=b[1][i]
return(a)
def count(a):
n=len(a)
b=[]
b.append(a[0])
m=[]
m.append(1)
c=0
for i in range(1,n):
if(a[i]==a[i-1]):
m[c]=m[c]+1
else:
b.append(a[i])
m.append(1)
c=c+1
return(b,m)
def count1(a):
n=len(a[0])
b=[]
b.append([])
b.append([])
b[0].append(a[0][0])
b[1].append(a[1][0])
c=0
for i in range(1,n):
if(a[0][i]==a[0][i-1]):
b[1][c]=b[1][c]+a[1][i]
else:
b[0].append(a[0][i])
b[1].append(a[1][i])
c=c+1
return(b)
def shuffle(a,b):
r=len(a)
s=len(b)
# Generating an array of strings containing all combinations of weight r+s and depth s
M=kbits(r+s,s)
n=len(M)
a1= []
for i in range(n):
a1.append(list(M[i]))
# The zeroes are replaced by the entries of a and the ones by the entries of b
a2= []
for i in range(n):
a2.append([])
count0=0
count1=0
for j in range(s+r):
if(a1[i][j]=='0'):
a2[i].append(a[count0])
count0=count0+1
if(a1[i][j]=='1'):
a2[i].append(b[count1])
count1=count1+1
# Reordering in lexicographic order the entries of a2: this is done by first reordering them according to the last digit, then the next to last digit, etc
a3=sort(a2,r+s,max(a+b+[0]))
# Getting the same list without repetitions and with multiplicities
a4=count(a3)
return(a4)
def Regshuf1(a):
r=[]
r.append([])
r.append([])
t=0
c=1
for i in range(len(a)+1):
if(t==0):
b=shuffle(a[:i],a[i:])
for j in range(len(b[0])):
r[0].append(b[0][j])
r[1].append(b[1][j]*c)
c=-c
if(i<len(a)):
if(a[i]==0):
t=1
r=sort1(r,len(a),max(a+[0]))
r=count1(r)
rg=[]
rg.append([])
rg.append([])
for i in range(len(r[0])):
if(r[1][i] is not 0):
rg[0].append(r[0][i])
rg[1].append(r[1][i])
return(rg)
c = Regshuf1(a)
for i in range(len(c[0])-1):
if(c[1][i] != 0):
print(c[1][i],"*",c[0][i] ,"+ ")
if(c[1][len(c[0])-1] != 0):
print(c[1][len(c[0])-1],"*",c[0][len(c[0])-1])