-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcalculator.py
132 lines (108 loc) · 3.78 KB
/
calculator.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
import fractions
def kronecker_delta(i, j):
return int(i == j)
def factorial(n):
product = 1
for i in range(1, n+1):
product *= i
return product
def binomial(n, r):
return (factorial(n)//(factorial(r)*factorial(n-r)))
def bernoulli(n) -> fractions.Fraction:
"""
Return B- ("B minus" — Bernoulli numbers with B(1) = -1/2 rather than +1/2)
"""
# Partially derived from: https://rosettacode.org/wiki/Bernoulli_numbers#Python
if n == 1:
return fractions.Fraction(-1,2)
b = [0 for _ in range(n + 1)]
for m in range(n + 1):
b[m] = fractions.Fraction(1, (m+1))
for j in range(m, 0, -1):
b[j-1] = j*(b[j-1]-b[j])
return b[0]
def lowest_common_denominator(*fractions: fractions.Fraction):
highest = {}
for fraction in fractions:
factors = prime_factorisation(fraction.denominator)
for factor, order in factors.items():
if factor not in highest:
highest[factor] = order
elif order > highest[factor]:
highest[factor] = order
product = 1
for factor, order in highest.items():
product *= factor ** order
return product
def prime_factorisation(n):
powers = {}
i = 2
while n > 1:
if n % i == 0:
n //= i
try:
powers[i] += 1
except KeyError:
powers[i] = 1
elif i == 2:
i = 3
else:
i += 2
while not is_prime(i):
i += 2
return powers
def is_prime(n):
if n == 1:
return False
if n == 2:
return True
if n % 2 == 0:
return False
i = 3
while i*i <= n:
if n % i == 0:
return False
i += 2
return True
def faulhaber(p, variable="n", fraction_mode = "standard") -> str:
"""
Returns a string that represents the formula for the sum of k^p from 1 to `variable`,
a value of the user's choice (such as n). The `fraction_mode` value can either be
`standard` or `latex`: the former produces e.g. `1/6`, whereas the latter `\frac{1}{6}`.
"""
if fraction_mode not in ("standard", "latex"):
raise ValueError(f"Fraction mode must be one of 'standard' or 'latex', not {fraction_mode}.")
if len(variable) > 1:
variable = f"({variable})"
coefficients = [[i, (-1)**(kronecker_delta(i, p)) * binomial(p + 1, i) * bernoulli(p+1-i)] for i in range(p + 1, 0, -1)]
coefficients = [c for c in coefficients if c[1] != 0]
lcd = lowest_common_denominator(*[c[1] for c in coefficients])
for c in coefficients:
c[1] *= lcd
output = f"{fractions.Fraction(1, (p+1)*lcd)}(" if fraction_mode == "standard" else f"\\frac{{1}}{{{(p+1)*lcd}}}("
for i, c in enumerate(coefficients):
if c[0] == 0:
continue
power = len(coefficients) - i
output += f"{abs(c[1]) if c[1] != 1 else ''}{variable}{f'^{power}' if power != 1 else ''}" if c[1] != 0 else ""
if i != len(coefficients) - 1:
if coefficients[i+1][1] == 0:
continue
output += " + " if coefficients[i+1][1] > 0 else " - "
output += ")"
return output
def main():
p = None
while p is None:
try:
p = int(input("Please input a power to get the partial sum for: "))
except ValueError:
pass
var = input("Please enter a variable (n by default): ")
var = "n" if var == "" else var
fraction_format = input("Please enter what fraction format you would like ('standard'/'latex', standard by default): ")
if fraction_format not in ("standard", "latex"):
fraction_format = "standard"
print(faulhaber(p, var, fraction_format))
if __name__ == "__main__":
main()