-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMain.py
638 lines (540 loc) · 24 KB
/
Main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
import tkinter as tk
from tkinter import ttk
#Multiplication table
def create_table(root, data, columns):
# Create a frame to hold the treeview and the scrollbar
frame = ttk.Frame(root)
frame.pack(expand=True, fill='both')
# Add an extra column for row headers
all_columns = ['X'] + columns
# Create a Treeview widget
tree = ttk.Treeview(frame, columns=all_columns, show='headings')
# Format columns
tree.column('X', width=100, minwidth=100, anchor='center')
tree.heading('X', text='X')
for column in columns:
tree.column(column, width=100, minwidth=100, anchor='center')
tree.heading(column, text=column)
# Insert data into the treeview with row headers
for row_header, row in zip(columns, data):
tree.insert('', 'end', values=(row_header, *row))
# Add a vertical scrollbar
scrollbar = ttk.Scrollbar(frame, orient="vertical", command=tree.yview)
tree.configure(yscrollcommand=scrollbar.set)
# Pack the scrollbar and the treeview
scrollbar.pack(side='right', fill='y')
tree.pack(expand=True, fill='both')
# Field Algebra
def generate_polynomial(poly):
ans = ""
for i in range(len(poly)):
if(poly[i] == 0): continue
else: ans += f"{poly[i]} x^{i} + "
if(ans): ans = ans[:-2]
else: return '0'
return ans.strip()
def define_field(p, m, irreducible_poly):
def generate_field_elements(comb, sol, idx):
if(idx == m):
sol.add(tuple(comb))
return
for i in range(p):
comb.append(i)
generate_field_elements(comb, sol, idx + 1)
comb.pop()
return sol
sol = generate_field_elements([], set(), 0)
field_elements = list(map(list, sol))
field_elements.sort()
for i in range(p ** m):
while(field_elements[i] and field_elements[i][-1] == 0): field_elements[i].pop()
if(field_elements[i] == []): field_elements[i] = [0]
def degree(poly):
while poly and poly[-1] == 0:
poly.pop() # normalize
return len(poly)-1
#in a field mod p, n and n + p are congurent
def congruence(n,m):
while ((n % p) % m) != 0:
n = n + p
return n
def rem(u, v):
n, m = degree(u), degree(v)
if m < 0: raise ZeroDivisionError
if n >= m:
while n >= m:
d = [0] * (n - m) + v
if u[-1] % float(v[-1]) != 0:
mult = congruence(u[-1],float(v[-1])) / float(v[-1])
else:
mult = u[-1] / float(d[-1])
d = [(coeff * mult) % p for coeff in d]
u = [(coeffu - coeffd) % p for coeffu, coeffd in zip(u, d)]
n = degree(u)
r = u
else:
r = u
return r
def add(u, v):
n, m = len(u), len(v)
m_, ans = max(n, m), []
u = u + [0] * (m_ - n)
v = v + [0] * (m_ - m)
for i in range(m_):
ans.append((u[i] + v[i]) % p)
while(ans and ans[-1] == 0):
ans.pop()
if(not ans): return [0]
r = rem(ans, irreducible_poly)
return r
# u(x) - v(x)
def subtract(u, v):
n, m = len(u), len(v)
m_, ans = max(n, m), []
u = u + [0] * (m_ - n)
v = v + [0] * (m_ - m)
for i in range(m_):
ans.append((u[i] - v[i]) % p)
while(ans and ans[-1] == 0):
ans.pop()
if(not ans): return [0]
r = rem(ans, irreducible_poly)
return r
def multiply(u, v):
n, m = len(u), len(v)
ans = [0 for _ in range(n + m)]
for i in range(n):
for j in range(m):
ans[i + j] += u[i] * v[j]
for i in range(n + m):
ans[i] %= p
while(ans and ans[-1] == 0):
ans.pop()
if(not ans): return [0]
r = rem(ans, irreducible_poly)
return r
def mult_table():
#returns a list of list of polynomials
mult = []
for ele1 in field_elements:
temp = []
for ele2 in field_elements:
ele = multiply(ele1, ele2)
poly = generate_polynomial(ele)
temp.append(poly)
mult.append(temp)
return mult
def divide(u, v):
q, r = [0], u
divisorDeg = degree(v)
divisorLC = v[-1]
while degree(r) >= divisorDeg:
monomialExponent = degree(r) - divisorDeg
monomialZeros = [0 for _ in range(monomialExponent)]
if r[-1] % divisorLC != 0:
monomialDivisor = monomialZeros + [congruence(r[-1], divisorLC) // divisorLC]
else:
monomialDivisor = monomialZeros + [r[-1] // divisorLC]
q = add(q, monomialDivisor)
r = subtract(r, multiply(monomialDivisor, v))
return q, r
def find_inverse():
d = {}
for ele in field_elements:
for ele2 in field_elements:
if(tuple(ele2) in d): continue
if(multiply(ele, ele2) == [1]):
d[tuple(ele)] = tuple(ele2)
d[tuple(ele2)] = tuple(ele)
return d
inverse = find_inverse()
multiplication_table = mult_table()
return field_elements, inverse, multiplication_table
# GUI
def go_to_next_page(entry1, entry2):
# Get the values from the entry widgets
p = entry1.get()
m = entry2.get()
# Validate the input
if p.isdigit() and m.isdigit():
# Open the next page with the values
next_page(int(p), int(m))
else:
# Show error message and clear entry widgets
tk.messagebox.showerror("Error", "Please enter valid numbers.")
entry1.delete(0, tk.END)
entry2.delete(0, tk.END)
def generate_irreducible_poly(p, m, entries):
# Created the irreducible_poly format
irreducible_poly = ""
for i in range(m + 1):
entries[i] %= p
if(entries[i] == 0): continue
else: irreducible_poly += f"{entries[i]} x^{m - i} + "
if(irreducible_poly): irreducible_poly = irreducible_poly[:-2]
else: return '0'
return irreducible_poly
def next_page(p, m):
# Created the next page
next_page_window = tk.Toplevel(root)
next_page_window.title("Irreducible Polynomial")
next_page_window.geometry('590x500')
# Created heading label
heading_label = ttk.Label(next_page_window, text="Enter Coefficients of Irreducible Polynomial", font=("Helvetica", 20, "bold"))
heading_label.grid(row=0, column=0, columnspan=2, pady=5)
# Created entry widgets
entries = []
for i in range(m + 1):
# Negative and 0 are allowed, if blank then assume 0 and throw error at which coefficients user did not enter correct value
label = ttk.Label(next_page_window, text=f"Enter coefficient for x**{m - i}:")
label.grid(row=i+1, column=0, padx=5, pady=5, sticky="e")
entry = ttk.Entry(next_page_window)
entry.grid(row=i+1, column=1, padx=5, pady=5)
entries.append(entry)
# Created button to generate irreducible_poly
generate_button = ttk.Button(next_page_window, text="Generate Irreducible Polynomial", command=lambda: show_irreducible_poly(p, m, entries, next_page_window))
generate_button.grid(row=m + 2, column=0, columnspan=2, padx=5, pady=10)
def show_irreducible_poly(p, m, entries, next_page_window):
error = []
values = []
# Validation
for i in range(m + 1):
e = entries[i].get()
if(e == ''): e = 0 # Blank space considered as 0
elif(e[0] == '-'): # Negative coefficients allowed
if(e[1:].isdigit()): e = -1 * int(e[1:])
else: error.append(i)
elif(e.isdigit()): e = int(e) # Positive coefficients
else: error.append(i) # Not a digit
values.append(e)
if(error):
tk.messagebox.showerror("Error", "Please enter valid coefficients")
for i in error: entries[i].delete(0, tk.END)
elif((values[0] % p) == 0):
tk.messagebox.showerror("Error", "Please enter degree m polynomial")
entries[0].delete(0, tk.END)
else:
# Generate the irreducible_poly
irreducible_poly = generate_irreducible_poly(p, m, values)
next_page_window.destroy()
# Created label to display the irreducible_poly
irreducible_poly_label = ttk.Label(root, text="Irreducible Polynomial Entered")
irreducible_poly_label.pack()
irreducible_poly_text = tk.Text(root, height=2, width=60)
irreducible_poly_text.insert(tk.END, irreducible_poly)
irreducible_poly_text.pack(pady=5)
irreducible_poly = values[::-1]
calc_button = ttk.Button(root, text="Calculator", command=lambda: calculator(irreducible_poly, p, m))
calc_button.pack(pady=10)
def calculator(irreducible_poly, p, m):
# Created the new popup window
calculator_window = tk.Toplevel(root)
calculator_window.title("Calculator")
calculator_window.geometry("2500x600")
field_elements, inverse, multiplication_table = define_field(p, m, irreducible_poly)
field_elements_polynomials = {}
for ele in field_elements:
field_elements_polynomials[generate_polynomial(ele)] = ele
# Created a heading for the calculator page
heading_label = ttk.Label(calculator_window, text="Calculator", font=("Helvetica", 20, "bold"))
heading_label.pack(pady=10)
# Created a text box to display the input
input_textbox = tk.Text(calculator_window, height=2, width=60)
input_textbox.pack(pady=10)
# Created a dropdown menu with p ** m elements
label_frame = ttk.Frame(calculator_window)
label_frame.pack(pady=10)
# Created a label for the dropdown menu
label = ttk.Label(label_frame, text="Field Elements: ")
label.pack(side=tk.LEFT)
# Created a Combobox with rounded corners
combobox_style = ttk.Style()
combobox_style.configure('Rounded.TCombobox', borderwidth=1, relief="solid", padding=5, bordercolor="gray", fieldbackground="white", foreground="black")
field_element_combobox = ttk.Combobox(label_frame, style='Rounded.TCombobox')
field_element_combobox['values'] = [i for i in field_elements_polynomials] #zip of field elements in polynomial form and coefficient list for user and developer respectively
field_element_combobox.current(0) # Set the default value
field_element_combobox.pack(side=tk.LEFT)
def append_field_element():
selected_element = field_element_combobox.get()
current_input = input_textbox.get("1.0", tk.END).strip() # Get current input text
if(current_input == 'Error'):
all_clear()
current_input = ""
if(current_input in field_elements_polynomials):
all_clear()
current_input = field_elements_polynomials[current_input]
new_input = f"{current_input} {field_elements_polynomials[selected_element]}" # Append selected element
input_textbox.delete("1.0", tk.END) # Clear current input
input_textbox.insert(tk.END, new_input) # Set new input
# Created "Enter" button for dropdown menu
enter_button = ttk.Button(label_frame, text="Enter", command=append_field_element)
enter_button.pack(side=tk.LEFT, padx=5)
# Function to update input text box
def append_operator(button_label):
current_input = input_textbox.get("1.0", tk.END).strip() # Get current input text
if(current_input == 'Error'):
all_clear()
current_input = ""
if(current_input in field_elements_polynomials):
all_clear()
current_input = field_elements_polynomials[current_input]
new_input = f"{current_input} {button_label}" # Build new input
input_textbox.delete("1.0", tk.END) # Clear current input
input_textbox.insert(tk.END, new_input) # Set new input
# Created buttons frame for operators
operators_frame = ttk.Frame(calculator_window)
operators_frame.pack(pady=10)
# Button labels for operators
operators = ['+', '-', '*', '/', '(', ')']
# Created operator buttons and assign functions to them
for label in operators:
button = ttk.Button(operators_frame, text=label, command=lambda label=label: append_operator(label))
button.pack(side=tk.LEFT, padx=5)
def all_clear():
input_textbox.delete("1.0", tk.END) # Clear current input
def backspace():
current_input = input_textbox.get("1.0", tk.END).strip() # Get current input text
new_input = " ".join(current_input.split()[:-1])
input_textbox.delete("1.0", tk.END) # Clear current input
input_textbox.insert(tk.END, new_input) # Set new input
def calculate(current_input):
def operandStack(listNumbers: str):
numbers = listNumbers[1:len(listNumbers) - 1].split(',')
return list(map(int, numbers))
def splitInputString(input: str) -> list:
input = input.replace(' ', '')
inputStack, inputOperand, index = [], [], 0
# Check for balanced parentheses, otherwise throw error
parenthesesArray = [char for char in input if char in ['(', ')']]
balancedStack = []
for element in parenthesesArray:
if element == '(':
balancedStack.append(element)
else:
if len(balancedStack) == 0: return 'Error'
else:
balancedStack.pop()
if len(balancedStack) != 0: return 'Error'
# Checking for balanced polynomial brackets
bracketsArray = [char for char in input if char in ['[', ']']]
balancedStack = []
for element in bracketsArray:
if element == '[':
balancedStack.append(element)
if len(balancedStack) > 1: return 'Error'
else:
if len(balancedStack) == 0: return 'Error'
else:
balancedStack.pop()
if len(balancedStack) != 0: return 'Error'
while index < len(input):
if input[index] in ['+', '-', '*', '/', '(', ')']:
inputStack.append(input[index])
index += 1
else:
if input[index] == '[':
inputOperand = []
closingBracket = index + input[index:].index(']')
inputStack.append(operandStack(input[index:closingBracket + 1]))
index = closingBracket + 1
# Two consecutive operators or two consecutive operands cannot be evaluated together (for example: a * + b, a b * c)
for index in range(0, len(inputStack) - 1, 1):
if inputStack[index] in ['+', '-', '*', '/'] and inputStack[index + 1] in ['+', '-', '*', '/']: return 'Error'
if isinstance(inputStack[index], list) and isinstance(inputStack[index + 1], list): return 'Error'
return inputStack
def parse(inputString: str):
a = splitInputString(inputString)
if(a != 'Error'): tokenStack = ['('] + a + [')']
else: return a
positionIndex = 0
def tokenStackElement():
nonlocal positionIndex
return tokenStack[positionIndex]
def moveIndex():
nonlocal positionIndex
positionIndex += 1
def parsePrimaryExpression():
token = tokenStackElement()
# If token type is a polynomial
if isinstance(token, list) and positionIndex < len(tokenStack):
moveIndex()
return token
elif token == '(' and positionIndex < len(tokenStack):
moveIndex()
expression = parseExpression()
moveIndex()
return expression
def parseMulExpression():
expression = parsePrimaryExpression()
token = tokenStackElement()
while (positionIndex < len(tokenStack) and (token == '*' or token == '/')):
moveIndex()
rightHandExpression = parsePrimaryExpression()
expression = (token, expression, rightHandExpression)
token = tokenStackElement()
return expression
def parseExpression():
expression = parseMulExpression()
token = tokenStackElement()
while (positionIndex < len(tokenStack) and (token == '+' or token == '-')):
moveIndex()
rightHandExpression = parseMulExpression()
expression = (token, expression, rightHandExpression)
token = tokenStackElement()
return expression
result = parsePrimaryExpression()
return result
def degree(poly):
while poly and poly[-1] == 0:
poly.pop() # normalize
return len(poly)-1
def congruence(n,m):
while ((n % p) % m) != 0:
n = n + p
return n
def rem(u, v):
n, m = degree(u), degree(v)
if m < 0: raise ZeroDivisionError
if n >= m:
while n >= m:
d = [0] * (n - m) + v
if u[-1] % float(v[-1]) != 0:
mult = congruence(u[-1],float(v[-1])) / float(v[-1])
else:
mult = u[-1] / float(d[-1])
d = [(coeff * mult) % p for coeff in d]
u = [(coeffu - coeffd) % p for coeffu, coeffd in zip(u, d)]
n = degree(u)
r = u
else:
r = u
return r
def add(u, v):
n, m = len(u), len(v)
m_, ans = max(n, m), []
u = u + [0] * (m_ - n)
v = v + [0] * (m_ - m)
for i in range(m_):
ans.append((u[i] + v[i]) % p)
while(ans and ans[-1] == 0):
ans.pop()
if(not ans): return [0]
r = rem(ans, irreducible_poly)
return r
# u(x) - v(x)
def subtract(u, v):
n, m = len(u), len(v)
m_, ans = max(n, m), []
u = u + [0] * (m_ - n)
v = v + [0] * (m_ - m)
for i in range(m_):
ans.append((u[i] - v[i]) % p)
while(ans and ans[-1] == 0):
ans.pop()
if(not ans): return [0]
r = rem(ans, irreducible_poly)
return r
def multiply(u, v):
n, m = len(u), len(v)
ans = [0 for _ in range(n + m)]
for i in range(n):
for j in range(m):
ans[i + j] += u[i] * v[j]
for i in range(n + m):
ans[i] %= p
while(ans and ans[-1] == 0):
ans.pop()
if(not ans): return [0]
r = rem(ans, irreducible_poly)
return r
def divide(u, v):
q, r = [0], u
divisorDeg = degree(v)
divisorLC = v[-1]
while degree(r) >= divisorDeg:
monomialExponent = degree(r) - divisorDeg
monomialZeros = [0 for _ in range(monomialExponent)]
if r[-1] % divisorLC != 0:
monomialDivisor = monomialZeros + [congruence(r[-1], divisorLC) // divisorLC]
else:
monomialDivisor = monomialZeros + [r[-1] // divisorLC]
q = add(q, monomialDivisor)
r = subtract(r, multiply(monomialDivisor, v))
return q, r
def evaluateExpr(expressionTuple):
if isinstance(expressionTuple, list):
return expressionTuple
elif isinstance(expressionTuple, tuple):
operator, operator1, operator2 = expressionTuple
if(operator1 == None or operator2 == None or evaluateExpr(operator2) == 'Error' or evaluateExpr(operator1) == 'Error' or evaluateExpr(operator2) == [] or evaluateExpr(operator2) == ''): return 'Error'
if operator == '+':
return add(evaluateExpr(operator1), evaluateExpr(operator2))
elif operator == '-':
return subtract(evaluateExpr(operator1), evaluateExpr(operator2))
elif operator == '*':
return multiply(evaluateExpr(operator1), evaluateExpr(operator2))
elif operator == '/':
if(evaluateExpr(operator2) == [0] or evaluateExpr(operator2) == [0.0] or evaluateExpr(operator2) == [] or evaluateExpr(operator2) == ''): return 'Error'
return multiply(evaluateExpr(operator1), inverse[tuple(evaluateExpr(operator2))])
e = parse(current_input)
if(e != 'Error'): new_input = evaluateExpr(e)
else: new_input = 'Error'
return new_input
def submit():
current_input = input_textbox.get("1.0", tk.END).strip() # Get current input text
ans = calculate(current_input)
if(ans != 'Error'):
for i in field_elements_polynomials:
if(field_elements_polynomials[i] == ans):
ans = i
break
input_textbox.delete("1.0", tk.END) # Clear current input
input_textbox.insert(tk.END, ans) # Set new input
def display():
columns = [i for i in field_elements_polynomials]
create_table(calculator_window, multiplication_table, columns)
# Created buttons frame for special buttons (AC, Backspace, Submit)
special_buttons_frame = ttk.Frame(calculator_window)
special_buttons_frame.pack(pady=10)
# Created buttons frame for display button
display_buttons_frame = ttk.Frame(calculator_window)
display_buttons_frame.pack(pady=15)
# Created an All Clear button
AC_button = ttk.Button(special_buttons_frame, text='AC', command=all_clear)
AC_button.pack(side=tk.LEFT, padx=5)
# Created a backspace button with Unicode symbol
backspace_button = ttk.Button(special_buttons_frame, text='\u21e6', command=backspace)
backspace_button.pack(side=tk.LEFT, padx=5)
# Created a Enter button with Unicode symbol
submit_button = ttk.Button(special_buttons_frame, text='=', command=submit)
submit_button.pack(side=tk.LEFT, padx=5)
#Created a button for displaying multiplication table
display_button = ttk.Button(display_buttons_frame, text = 'Display', command=display)
display_button.pack(side = tk.LEFT, padx = 5)
# Created the main window
root = tk.Tk()
root.title("User Input")
root.geometry("500x500")
# Created heading label
heading_label = ttk.Label(root, text="Specifications for Calculator", font=("Helvetica", 20, "bold"))
heading_label.pack(pady=10)
# Created frame for first set of entry widgets
top_frame1 = ttk.Frame(root)
top_frame1.pack(pady=10)
# Created frame for second set of entry widgets
top_frame2 = ttk.Frame(root)
top_frame2.pack(pady=10)
# Created entry widgets for numbers
p_label = ttk.Label(top_frame1, text="Enter Prime Number p:")
p_label.grid(row=0, column=0, pady=(10, 0), padx=10, sticky="e")
entry1 = ttk.Entry(top_frame1)
entry1.grid(row=0, column=1, pady=(10, 0), padx=10)
m_label = ttk.Label(top_frame2, text="Enter degree m:")
m_label.grid(row=0, column=0, pady=(10, 0), padx=10, sticky="e")
entry2 = ttk.Entry(top_frame2)
entry2.grid(row=0, column=1, pady=(10, 0), padx=10)
# Created button to go to next page
next_button = ttk.Button(root, text="Next", command=lambda: go_to_next_page(entry1, entry2))
next_button.pack(pady=10)
# Run the Tkinter event loop
root.mainloop()