modular_forms 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c2b8536ed2846f289afd967456cdd892858ad3af312acffab3e312c57b367a38
4
+ data.tar.gz: ea900a9133578ce1278141bda388d85fc222419ac2de207c50a10c243176166f
5
+ SHA512:
6
+ metadata.gz: 7542e5fa3105f3008bdf192c2471e950898567aca9c133ebb50c7e0d65ec1bd0c3b1765c944257c59782bd26f2f849f15d7653652d14844d844a91ab1174b7b9
7
+ data.tar.gz: 6878c24ff277884ff52aeb3ad54073ae50001b3f0f792b317b1d4ed7caf40151a867124a394eef0731846f1d3732b096f741e4e8c6e2473f9b172eddaed0d237
data/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # Modular Forms
2
+
3
+ A creative toolkit for exploring modular forms and elliptic curves through [Sonic Pi](https://sonic-pi.net/).
4
+
5
+ ![Modular Forms - Image](modular_forms.png)
6
+
7
+ ## Project Status
8
+
9
+ This is a pre-alpha release of `modular_forms`. At this stage, only a subset of core mathematical definitions and operations is implemented.
10
+ Future updates might include a DSL, depending on how the library is used and the interest from the community.
11
+
12
+ Contributions are welcome! Feel free to fork, make changes, and submit a pull request.
13
+
14
+ ## Features
15
+
16
+ - **Accessible to both musicians and coders**: No math expertise required. Create musical patterns, rhythms, timbres, and harmonies by experimenting with mathematical ideas and turning them into sound and effects intuitively.
17
+ - **Interactive Educational Resource**: Use **Sonic Pi** to discover introductory number theory in a hands-on, immersive way, gaining insights into abstract concepts through math in action.
18
+
19
+ ## Purpose and Scope
20
+
21
+ Given the vastness of the field, this tool intentionally focuses on a limited subset of definitions, without covering all aspects of each. Below is a list of the implemented modules:
22
+
23
+ - [Eisenstein Series](#eisenstein-series)
24
+ - [Eta Functions and Eta Quotients](#eta-functions-and-eta-quotients)
25
+ - [Theta functions](#theta-functions)
26
+ - [Ramanujan Tau Function](#ramanujan-tau-function)
27
+ - [J-Function](#j-function)
28
+ - [Hecke Operators](#hecke-operators)
29
+ - [SL(2,Z) Group](#sl2z-group)
30
+ - [Dirichlet Characters](#dirichlet-characters)
31
+ - [Elliptic Curves over Rationals](#elliptic-curves-over-rationals)
32
+ - [Elliptic Curves over Finite Fields](#elliptic-curves-over-finite-fields)
33
+ - [Newforms Invariants](#newforms-invariants)
34
+
35
+ ### Not Optimized for Computational Efficiency
36
+
37
+ This library is designed for creative exploration rather than maximum computational efficiency. It is not intended to replace advanced mathematical software. Instead, it draws inspiration from tools like SageMath, Pari/GP, and the LMFDB database.
38
+
39
+ ### Goal
40
+ The goal is simple: to provide an accessible and creative starting point for those who wish to explore, learn, and uncover new ideas, regardless of their mathematical background.
41
+
42
+ ## Installation
43
+
44
+ You can install the `modular_forms` gem directly from **RubyGems** or clone it from GitHub.
45
+
46
+ ```bash
47
+ gem install modular_forms
48
+ ```
49
+
50
+ ## How to use?
51
+
52
+ You can dive into the beauty of math, both in Ruby and Sonic Pi, creating music in real-time. Here is a simple example of how to use **modular_forms** to generate a basic musical pattern:
53
+
54
+ ```rb
55
+ # If you are using Ruby, simply load the gem with its name
56
+ # after installing it via 'gem install modular_forms'.
57
+ require 'modular_forms'
58
+
59
+ # If you are using Sonic Pi, replace <PATH>
60
+ # with the full path to the 'modular_forms.rb' file
61
+ # inside the gem installation on your system.
62
+ require "<PATH>/modular_forms.rb"
63
+
64
+ # Calculate the Eisenstein series of weight k = 4
65
+ eisenstein_melody = ModularForms.eisenstein_serie(4)
66
+
67
+ # Play the melody in a loop with a mathematical transformation
68
+ 120.times do
69
+ play eisenstein_melody.next % 12 * 7
70
+ sleep 0.5
71
+ end
72
+ ```
73
+
74
+ ## Implemented Modular Forms, Elliptic Curves, and Related Definitions
75
+
76
+ ### Eisenstein Series
77
+
78
+ 1. `ModularForms.eisenstein_series(weight_k, galois_field = nil)`
79
+ 2. `ModularForms.eisenstein_series_product(weight_k1, weight_k2, prec)`
80
+ 3. `ModularForms.eisenstein_series_pow(weight_k, power, prec)`
81
+
82
+ ### Eta Functions and Eta Quotients
83
+
84
+ 4. `ModularForms.dedekind_eta_function(m_scale = 1, pentagonal_coefs = false)`
85
+ 5. `ModularForms.dedekind_eta_pow(power, prec, m_scale = 1)`
86
+ 6. `ModularForms.dedekind_sum(h, k)`
87
+ 7. `ModularForms.eta_product(eta1, eta2, prec = nil)`
88
+ 8. `ModularForms.eta_quotient(num_eta, den_eta, prec)`
89
+
90
+ ### Theta functions
91
+
92
+ 9. `ModularForms.jacobi_theta_function(jacobi_index = 3, square_coefs = false)`
93
+ 10. `ModularForms.jacobi_theta_function_pow(jacobi_index, power, prec)`
94
+
95
+ ### Ramanujan Tau Function
96
+
97
+ 11. `ModularForms.ramanujan_tau_function`
98
+
99
+ ### J-Function
100
+
101
+ 12. `ModularForms.j_function(prec)`
102
+
103
+ ### Hecke Operators
104
+
105
+ 13. `ModularForms.hecke_operator_prime_non_cusp(non_cusp_form_arr, prime, weight_k, prec)`
106
+ 14. `ModularForms.hecke_operator_prime_cusp(cusp_form_arr, prime, weight_k, prec)`
107
+
108
+ ### SL(2,Z) Group
109
+
110
+ 15. `ModularForms.t_gen_matrix(n_power)`
111
+ 16. `ModularForms.s_gen_matrix(n_power)`
112
+ 17. `ModularForms.u_gen_matrix(mod_n)`
113
+ 18. `ModularForms.st_gen_matrix(n_power)`
114
+ 19. `ModularForms.product_gen_mats(gen_mat_a, gen_mat_b)`
115
+ 20. `ModularForms.gamma0_index(n)`
116
+ 21. `ModularForms.gamma1_index(n)`
117
+
118
+ ### Dirichlet Characters
119
+
120
+ 22. `ModularForms.dirichlet_trivchar(modq, a)`
121
+ 23. `ModularForms.conrey_p_pminus1(modp, a)`
122
+ 24. `ModularForms.gauss_sum_triv(dirichlet_q, a)`
123
+ 25. `ModularForms.gauss_sum_conrey_p_minus1(dirichlet_q, a, parity)`
124
+
125
+ ### Elliptic Curves over Rationals
126
+
127
+ 26. `ModularForms.elliptic_curve_q(coefs)`
128
+ 27. `ModularForms.discriminant_q(curve)`
129
+ 28. `ModularForms.j_invariant_q(curve)`
130
+ 29. `ModularForms.point_on_curve_q?(curve, point)`
131
+ 30. `ModularForms.point_addition_q(curve, p, q)`
132
+ 31. `ModularForms.scalar_mul_point_q(curve, n, point)`
133
+ 32. `ModularForms.isogeny_2deg_q(curve, point_2tor)`
134
+ 33. `ModularForms.isogeny_ndeg_q(curve, point_ntor, order)`
135
+ 34. `ModularForms.weil_height(x_point)`
136
+ 35. `ModularForms.canonical_height(curve, point, prec = 64)`
137
+
138
+ ### Elliptic Curves over Finite Fields
139
+
140
+ 36. `ModularForms.elliptic_curve_fp(p, coefs)`
141
+ 37. `ModularForms.point_on_curve_modp?(curve, point)`
142
+ 38. `ModularForms.discriminant_modp(curve)`
143
+ 39. `ModularForms.j_invariant_modp(curve)`
144
+ 40. `ModularForms.point_addition_modp(curve, p_point, q_point)`
145
+ 41. `ModularForms.scalar_mul_point_modp(curve, n, point)`
146
+ 42. `ModularForms.points_fp(curve, point_at_infinity = false)`
147
+ 43. `ModularForms.cardinality_fp(curve)`
148
+ 44. `ModularForms.quadratic_twist_fp(curve)`
149
+
150
+ ### Newforms Invariants
151
+ 45. `ModularForms.analytic_conductor(level_n, weight_k)`
152
+
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+
5
+ module ModularForms
6
+ module Core
7
+ # ModularForms::Core::DedekindEtaFunctions
8
+ #
9
+ # This module provides a generator for Dedekind eta functions.
10
+ module DedekindEtaFunctions
11
+ def self.eta_function(m_scale = 1, pent_coefs = false) # rubocop:disable Style/OptionalBooleanParameter,Metrics/MethodLength,Metrics/AbcSize
12
+ Enumerator.new do |q|
13
+ q << 1
14
+ (m_scale - 1).times do
15
+ q << 0
16
+ end
17
+ (1..Float::INFINITY).each do |tau|
18
+ pentagonal_num_minus = ((3 * tau**2 - tau) / 2) * m_scale
19
+ pentagonal_num_plus = ((3 * tau**2 + tau) / 2) * m_scale
20
+ min_next_pent = ((3 * (tau + 1)**2 - (tau + 1)) / 2) * m_scale
21
+ sign = (-1)**tau
22
+ q << (pent_coefs == false ? sign : sign * pentagonal_num_minus)
23
+ (pentagonal_num_plus - pentagonal_num_minus - 1).abs.times do
24
+ q << 0
25
+ end
26
+ q << (pent_coefs == false ? sign : sign * pentagonal_num_plus)
27
+ (min_next_pent - pentagonal_num_plus - 1).abs.times do
28
+ q << 0
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.eta_function_pow(power, precision, m_scale = 1)
35
+ vec = eta_function(m_scale).take(precision)
36
+ eta_q_coefs = [1]
37
+ power.times do
38
+ eta_q_coefs = NumericHelpers.linear_convolve(eta_q_coefs, vec, precision)
39
+ end
40
+ eta_q_coefs
41
+ end
42
+
43
+ def self.eta_product(eta1, eta2, prec = nil)
44
+ NumericHelpers.linear_convolve(eta1, eta2, prec)
45
+ end
46
+
47
+ def self.eta_quotient(num_eta, den_eta, prec)
48
+ NumericHelpers.quotient_of_series(num_eta, den_eta, prec)
49
+ end
50
+
51
+ def self.centered_fractional_part(num, den)
52
+ return 0 if num % den == 0 # rubocop:disable Style/NumericPredicate
53
+
54
+ Rational(num, den) - (num / den).floor - Rational(1, 2)
55
+ end
56
+
57
+ def self.dedekind_sum(h, k)
58
+ raise "#{k} parameter must be greater than 0" if k < 1
59
+
60
+ s = 0
61
+ (1..k - 1).each do |n|
62
+ s += centered_fractional_part(n, k) * centered_fractional_part(h * n, k)
63
+ end
64
+ s
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+ require 'bigdecimal'
5
+
6
+ module ModularForms
7
+ module Core
8
+ # ModularForms::Core::DirichletCharacters
9
+ #
10
+ # This module provides generators for Dirichlet characters and related topics.
11
+ module DirichletCharacters
12
+ PI2 = 2 * Math::PI
13
+
14
+ PARITY = {
15
+ EVEN: 'even',
16
+ ODD: 'odd'
17
+ }.freeze
18
+
19
+ def self.dirichlet_trivchar(modq, a)
20
+ NumericHelpers.gcd(a, modq) == 1 ? 1 : 0
21
+ end
22
+
23
+ def self.conrey_p_pminus1(modp, a)
24
+ raise "#{modp} must be a prime number" if !NumericHelpers.prime_number?(modp) # rubocop:disable Style/NegatedIf
25
+ return 0 if a % modp == 0 # rubocop:disable Style/NumericPredicate
26
+ return 1 if NumericHelpers.euler_criterion(a, modp)
27
+
28
+ -1 if !NumericHelpers.euler_criterion(a, modp) # rubocop:disable Style/NegatedIf
29
+ end
30
+
31
+ def self.exponential(a, r, q)
32
+ Complex.polar(1, PI2 * (a * r) / q)
33
+ end
34
+
35
+ def self.gauss_sum_triv(q_char, a)
36
+ t_a = 0 + 0i
37
+ (0..q_char - 1).each do |r|
38
+ t_a += dirichlet_trivchar(q_char, r) * exponential(a, r, q_char)
39
+ end
40
+ t_a.real.to_f.round(2)
41
+ end
42
+
43
+ def self.gauss_sum_conrey_p_minus1(q_char, a, parity) # rubocop:disable Metrics/AbcSize
44
+ raise "Parity '#{parity}' must be either 'even' or 'odd'" if parity != PARITY[:ODD] && parity != PARITY[:EVEN]
45
+
46
+ t_a = 0 + 0i
47
+ (0..q_char - 1).each do |r|
48
+ t_a += conrey_p_pminus1(q_char, r) * exponential(a, r, q_char)
49
+ end
50
+
51
+ return t_a.real.round(10) if parity == PARITY[:EVEN]
52
+
53
+ Complex(0, t_a.imaginary.round(10)) if parity == PARITY[:ODD]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+
5
+ module ModularForms
6
+ module Core
7
+ # ModularForms::Core::EisensteinSeries
8
+ #
9
+ # This module provides a generator for Eisenstein series.
10
+ module EisensteinSeries
11
+ def self.eisenstein_series(k, gal_f = nil) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
12
+ raise ArgumentError, 'k must be even and >= 2' if k < 2 || k % 2 != 0 # rubocop:disable Style/EvenOdd
13
+
14
+ bernoulli_k = NumericHelpers.bernoulli_numbers_arr(k)[k]
15
+ normalization_factor = - Rational(2 * k, bernoulli_k)
16
+ Enumerator.new do |q|
17
+ q << 1
18
+ (1..Float::INFINITY).each do |tau|
19
+ nth_term = (normalization_factor * NumericHelpers.divisor_function_sigma(tau, k - 1)).to_i
20
+ if gal_f != nil # rubocop:disable Style/ConditionalAssignment,Style/NonNilCheck
21
+ q << NumericHelpers.galois_field(nth_term, gal_f)
22
+ else
23
+ q << nth_term
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def self.eisenstein_series_product(weight_k1, weight_k2, precision)
30
+ vec1 = eisenstein_series(weight_k1).take(precision)
31
+ vec2 = eisenstein_series(weight_k2).take(precision)
32
+ NumericHelpers.linear_convolve(vec1, vec2, precision)
33
+ end
34
+
35
+ def self.eisenstein_series_pow(weight_k, power, precision)
36
+ vec = eisenstein_series(weight_k).take(precision)
37
+ eisenstein_q_coefs = [1]
38
+ power.times do
39
+ eisenstein_q_coefs = NumericHelpers.linear_convolve(eisenstein_q_coefs, vec, precision)
40
+ end
41
+ eisenstein_q_coefs
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+ require_relative './elliptic_curves_q'
5
+
6
+ module ModularForms
7
+ module Core
8
+ # ModularForms::Core::EllipticCurvesFp
9
+ #
10
+ # This module provides methods for generating points on Elliptic Curves over Finite Fields (short Weierstrass form)
11
+ module EllipticCurvesFp
12
+ def self.reduction_modp(n, modp)
13
+ ((n % modp) + modp) % modp
14
+ end
15
+
16
+ def self.elliptic_curve_fp(p, coefs)
17
+ a, b = coefs
18
+ raise "#{p} is not a prime number" if NumericHelpers.prime_number?(p) == false
19
+
20
+ d = reduction_modp(EllipticCurvesQ.discriminant(a, b), p)
21
+
22
+ a_modp = reduction_modp(a, p)
23
+ b_modp = reduction_modp(b, p)
24
+ raise "y^2=x^3 #{a_modp}x #{b_modp} defines a singular curve" if d == 0 # rubocop:disable Style/NumericPredicate
25
+
26
+ puts "y^2 = x^3 #{a_modp}x #{b_modp} over Finite Field #{p}"
27
+ { a: a, b: b, p: p }
28
+ end
29
+
30
+ def self.point_on_curve_modp?(curve, point)
31
+ return true if point == nil # rubocop:disable Style/NilComparison
32
+
33
+ a, b, p = curve.values_at(:a, :b, :p)
34
+ x, y = point
35
+ x_modp = reduction_modp(x, p)
36
+ y_modp = reduction_modp(y, p)
37
+ coordinates = reduction_modp(y**2, p) == reduction_modp(x**3 + x * a + b, p)
38
+
39
+ raise "Coordinates [#{x_modp},#{y_modp}] do not define a point on curve" unless coordinates
40
+
41
+ true
42
+ end
43
+
44
+ def self.discriminant_modp(curve)
45
+ a, b, p = curve.values_at(:a, :b, :p)
46
+ reduction_modp(-16 * (4 * a**3 + 27 * b**2), p)
47
+ end
48
+
49
+ def self.fermat_inverse_modp(a, p)
50
+ a.pow(p - 2, p)
51
+ end
52
+
53
+ def self.j_invariant_modp(curve)
54
+ a, b, p = curve.values_at(:a, :b, :p)
55
+ reduction_modp(1728 * (4 * a**3) * fermat_inverse_modp(4 * a**3 + 27 * b**2, p), p)
56
+ end
57
+
58
+ def self.point_addition_modp(curve, p_point, q_point) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
59
+ a, _, p = curve.values_at(:a, _, :p)
60
+ return p_point if q_point == nil # rubocop:disable Style/NilComparison
61
+ return q_point if p_point == nil # rubocop:disable Style/NilComparison
62
+
63
+ x1, y1 = p_point
64
+ x2, y2 = q_point
65
+ return nil if x1 == x2 && reduction_modp(y1 + y2, p) == 0 # rubocop:disable Style/NumericPredicate
66
+
67
+ if p_point != q_point
68
+ num1 = y2 - y1
69
+ den1 = x2 - x1
70
+ lambda_m = num1 * fermat_inverse_modp(den1, p)
71
+ else
72
+ num2 = 3 * x1**2 + a
73
+ den2 = 2 * y1
74
+ lambda_m = num2 * fermat_inverse_modp(den2, p)
75
+ end
76
+
77
+ x3 = lambda_m**2 - x1 - x2
78
+ y3 = lambda_m * (x1 - x3) - y1
79
+ [reduction_modp(x3, p), reduction_modp(y3, p)]
80
+ end
81
+
82
+ def self.scalar_mul_point_modp(curve, n, point)
83
+ n_times_point = nil
84
+ addend = point
85
+
86
+ while n > 0 # rubocop:disable Style/NumericPredicate
87
+ n_times_point = point_addition_modp(curve, n_times_point, addend) if n.odd?
88
+ addend = point_addition_modp(curve, addend, addend)
89
+ n >>= 1
90
+ end
91
+
92
+ n_times_point
93
+ end
94
+
95
+ def self.points(curve, point_at_infinity = false) # rubocop:disable Metrics/MethodLength,Style/OptionalBooleanParameter,Metrics/AbcSize
96
+ a, b, p = curve.values_at(:a, :b, :p)
97
+
98
+ list_of_points = point_at_infinity ? [nil] : []
99
+ (0..(p - 1)).each do |x|
100
+ eq_rh = (x**3 + a * x + b) % p
101
+ (0..((p - 1) / 2)).each do |y|
102
+ if ((y**2) % p) == eq_rh
103
+ list_of_points << [x, y]
104
+ list_of_points << [x, p - y] if p - y < p
105
+ end
106
+ end
107
+ end
108
+
109
+ list_of_points
110
+ end
111
+
112
+ def self.cardinality(curve)
113
+ points(curve, true).length
114
+ end
115
+
116
+ def self.quadratic_twist(curve)
117
+ a, b, p = curve.values_at(:a, :b, :p)
118
+
119
+ d_non_square = NumericHelpers.square_modp_list(p, 2)[1]
120
+ index = rand(d_non_square.length)
121
+ d = d_non_square[index]
122
+
123
+ elliptic_curve_fp(p, [(a * d**2) % p, (b * d**3) % p])
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+
5
+ module ModularForms
6
+ module Core
7
+ # ModularForms::Core::EllipticCurves
8
+ #
9
+ # This module provides methods for generating points on Elliptic Curves (short Weierstrass form) over Q
10
+ module EllipticCurvesQ
11
+ def self.discriminant(a, b)
12
+ -16 * (4 * a**3 + 27 * b**2)
13
+ end
14
+
15
+ def self.elliptic_curve_q(coefs)
16
+ a, b = coefs
17
+ raise "y^2=x^3 #{a}x #{b} defines a singular curve" if discriminant(a, b) == 0 # rubocop:disable Style/NumericPredicate
18
+
19
+ puts "y^2 = x^3 #{a}x #{b}"
20
+ { a: a, b: b }
21
+ end
22
+
23
+ def self.point_on_curve?(curve, point)
24
+ return true if point == nil # rubocop:disable Style/NilComparison
25
+
26
+ a, b = curve.values_at(:a, :b)
27
+ x, y = point
28
+ coordinates = y**2 == x**3 + x * a + b
29
+ raise "Coordinates [#{x},#{y}] do not define a point on curve" unless coordinates
30
+
31
+ true
32
+ end
33
+
34
+ def self.negate_p(point)
35
+ raise ArgumentError, 'Expected a point with two coordinates [x, y]' unless point.length == 2
36
+
37
+ x, y = point
38
+ [x, -y]
39
+ end
40
+
41
+ def self.discriminant_qq(curve)
42
+ a, b = curve.values_at(:a, :b)
43
+ -16 * (4 * a**3 + 27 * b**2)
44
+ end
45
+
46
+ def self.j_invariant(curve)
47
+ a, b = curve.values_at(:a, :b)
48
+ return 0 if a == 0 # rubocop:disable Style/NumericPredicate
49
+ return 1728 if b == 0 # rubocop:disable Style/NumericPredicate
50
+
51
+ 1728 * Rational(4 * a**3, 4 * a**3 + 27 * b**2)
52
+ end
53
+
54
+ def self.point_addition(curve, p, q) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
55
+ return p if q == nil # rubocop:disable Style/NilComparison
56
+ return q if p == nil # rubocop:disable Style/NilComparison
57
+
58
+ x1, y1 = p
59
+ x2, y2 = q
60
+ return nil if x1 == x2 && y1 + y2 == 0 # rubocop:disable Style/NumericPredicate
61
+
62
+ a, = curve.values_at(:a)
63
+ if p != q # rubocop:disable Style/ConditionalAssignment
64
+ lambda_m = Rational(y2 - y1, x2 - x1)
65
+ else
66
+ lambda_m = Rational(3 * x1**2 + a, 2 * y1)
67
+ end
68
+ x3 = lambda_m**2 - x1 - x2
69
+ y3 = lambda_m * (x1 - x3) - y1
70
+ [x3, y3]
71
+ end
72
+
73
+ def self.scalar_mul_point(curve, n, point)
74
+ n_times_point = nil
75
+ addend = point
76
+
77
+ while n > 0 # rubocop:disable Style/NumericPredicate
78
+ n_times_point = point_addition(curve, n_times_point, addend) if n.odd?
79
+ addend = point_addition(curve, addend, addend)
80
+ n >>= 1
81
+ end
82
+
83
+ n_times_point
84
+ end
85
+
86
+ def self.isogeny_2deg(curve, point_2tor)
87
+ a, b = curve.values_at(:a, :b)
88
+ x0 = point_2tor[0]
89
+ t = 3 * x0**2 + a
90
+ w = x0 * t
91
+ a_isog = a - 5 * t
92
+ b_isog = b - 7 * w
93
+ elliptic_curve_q([a_isog, b_isog])
94
+ end
95
+
96
+ def self.isogeny_ndeg(curve, point_ntor, order) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
97
+ a, b = curve.values_at(:a, :b)
98
+ t = 0
99
+ w = 0
100
+ (1..order - 1).each do |n|
101
+ x0, y0 = scalar_mul_point(curve, n, point_ntor)
102
+ t_q = 3 * x0**2 + a
103
+ u_q = 2 * y0**2
104
+ w_q = u_q + t_q * x0
105
+ t += t_q
106
+ w += w_q
107
+ end
108
+ a_isog = a - 5 * t
109
+ b_isog = b - 7 * w
110
+ elliptic_curve_q([a_isog, b_isog])
111
+ end
112
+
113
+ def self.weil_height(x_point)
114
+ return 0 if x_point == nil # rubocop:disable Style/NilComparison
115
+
116
+ Math.log(NumericHelpers.q_height(x_point))
117
+ end
118
+
119
+ def self.canonical_height(curve, point, prec = 64)
120
+ n = prec
121
+ dim = 1 # To be implemented in a new module for ECurves over extensions of the rationals
122
+ two_pow_n = scalar_mul_point(curve, n, point)[0]
123
+ Rational(1, dim) * (weil_height([two_pow_n.numerator,
124
+ two_pow_n.denominator]) * n**-2)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+
5
+ module ModularForms
6
+ module Core
7
+ # ModularForms::Core::HeckeOperators
8
+ #
9
+ # This module provides methods for applying Hecke operators to modular forms.
10
+ module HeckeOperators
11
+ def self.hecke_prime_non_cusp(non_cusp_form, p, k, precision) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
12
+ raise ArgumentError, 'p must be a prime number' unless NumericHelpers.prime_number?(p)
13
+
14
+ q_size = non_cusp_form.length
15
+ minimum_q_size = (precision - 1) * p + 1
16
+ raise ArgumentError, "size of q-expansion must be at least #{minimum_q_size}" if q_size < minimum_q_size
17
+
18
+ normalization_factor = p**(k - 1)
19
+ (0..precision - 1).map do |n|
20
+ if n % p != 0
21
+ non_cusp_form[p * n]
22
+ else
23
+ non_cusp_form[p * n] + normalization_factor * non_cusp_form[n / p]
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.hecke_prime_cusp(cusp_form, p, k, precision) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
29
+ raise ArgumentError, 'p must be a prime number' unless NumericHelpers.prime_number?(p)
30
+
31
+ q_size = cusp_form.length
32
+ minimum_q_size = precision * p
33
+ raise ArgumentError, "size of q-expansion must be at least #{minimum_q_size}" if q_size < minimum_q_size
34
+
35
+ cusp_form.unshift(0)
36
+ normalization_factor = p**(k - 1)
37
+
38
+ (1..precision).map do |n|
39
+ if n % p != 0
40
+ cusp_form[p * n]
41
+ else
42
+ cusp_form[p * n] + normalization_factor * cusp_form[n / p]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+ require_relative 'eisenstein_series'
5
+ require_relative 'dedekind_eta_functions'
6
+
7
+ module ModularForms
8
+ module Core
9
+ # ModularForms::Core::KleinJInvariant
10
+ #
11
+ # This module provides an array of values for the j-invariant function.
12
+ module KleinJInvariant
13
+ def self.j_function(precision)
14
+ e4_cubed = EisensteinSeries.eisenstein_series_pow(4, 3, precision + 1)
15
+ delta_discriminant = DedekindEtaFunctions.eta_function_pow(24, precision + 1)
16
+ NumericHelpers.quotient_of_series(e4_cubed, delta_discriminant, precision)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+
5
+ module ModularForms
6
+ module Core
7
+ # ModularForms::Core::NewFormInvariants
8
+ #
9
+ # This module provides methods for NewForm Invariants
10
+ module NewFormInvariants
11
+ BERNOULLI = NumericHelpers.bernoulli_numbers_arr(16).reject { |bm| bm == 0 } # rubocop:disable Style/NumericPredicate
12
+ BERNOULLI_B_2m = BERNOULLI[2..16].each_with_index.map do |b2m, index|
13
+ Rational(b2m, (2 * (index + 1))).abs # abs => all B2m positive
14
+ end
15
+
16
+ def self.digamma_func(z) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
17
+ partial_sum = 0.0
18
+ while z < 10
19
+ partial_sum -= Rational(1, z)
20
+ z += 1
21
+ end
22
+ inv_z = Rational(1 / z)
23
+ inv_z2 = inv_z * inv_z
24
+ partial_sum += Math.log(z) - Rational(1, 2) * inv_z
25
+ BERNOULLI_B_2m.each_with_index do |b2m, index|
26
+ partial_sum += (-1)**(index + 1) * b2m * (inv_z2**(index + 1))
27
+ end
28
+ partial_sum
29
+ end
30
+
31
+ def self.analytic_conductor(level_n, weight_k)
32
+ psi = digamma_func(Rational(weight_k, 2))
33
+ num = Math.exp(psi)
34
+ den = 2 * Math::PI
35
+ (level_n * (num / den)**2).round(12)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModularForms
4
+ module Core
5
+ # ModularForms::Core::NumericHelpers
6
+ #
7
+ # Contains functions used in the definitions of modular forms,
8
+ # as well as other related numerical operations.
9
+ module NumericHelpers
10
+ def self.factorial_iter(num)
11
+ t = 1
12
+ (1..num).each do |i|
13
+ t *= i
14
+ end
15
+ t
16
+ end
17
+
18
+ def self.binomial_coefficient(n, k)
19
+ factorial_iter(n) / (factorial_iter(k) * factorial_iter(n - k))
20
+ end
21
+
22
+ def self.bernoulli_numbers_arr(n, bernoulli_numbers = [1])
23
+ return bernoulli_numbers[n] if n < bernoulli_numbers.length
24
+
25
+ (bernoulli_numbers.length..n).each do |k|
26
+ sum = Rational(0, 1)
27
+ (0...k).each do |j|
28
+ sum += binomial_coefficient(k + 1, j) * bernoulli_numbers[j]
29
+ end
30
+ bernoulli_numbers[k] = -sum / (k + 1)
31
+ end
32
+ bernoulli_numbers
33
+ end
34
+
35
+ def self.divisor_function_sigma(n, z)
36
+ total_sum = 0
37
+ (1..n).each do |d|
38
+ if n % d == 0 # rubocop:disable Style/NumericPredicate,Style/IfUnlessModifier
39
+ total_sum += d**z
40
+ end
41
+ end
42
+ total_sum
43
+ end
44
+
45
+ def self.prime_number?(n)
46
+ if !n.is_a?(Integer) # rubocop:disable Style/IfUnlessModifier,Style/NegatedIf
47
+ return false
48
+ end
49
+ return false if n < 2
50
+
51
+ (2..Math.sqrt(n)).each do |i|
52
+ if (n % i) == 0 # rubocop:disable Style/NumericPredicate,Style/IfUnlessModifier
53
+ return false
54
+ end
55
+ end
56
+ true
57
+ end
58
+
59
+ def self.galois_field(n, modp)
60
+ if prime_number?(modp) == false # rubocop:disable Style/IfUnlessModifier
61
+ raise ArgumentError, 'GF must be a prime number'
62
+ end
63
+
64
+ ((n % modp) + modp) % modp
65
+ end
66
+
67
+ def self.linear_convolve(vec1, vec2, max_degree = nil) # rubocop:disable Metrics/AbcSize
68
+ max_degree == nil ? max_degree = (vec1.size + vec2.size) / 2 : max_degree # rubocop:disable Style/NilComparison
69
+ arr_product = Array.new(max_degree, 0)
70
+ (0..max_degree - 1).each do |n|
71
+ (0..n).each do |m|
72
+ if m < vec1.size && (n - m) < vec2.size # rubocop:disable Style/IfUnlessModifier
73
+ arr_product[n] += vec1[m] * vec2[n - m]
74
+ end
75
+ end
76
+ end
77
+ arr_product
78
+ end
79
+
80
+ def self.quotient_of_series(num, den, max_degree)
81
+ arr_coefs = []
82
+ (0..max_degree).each do |n|
83
+ sum = num[n] || 0
84
+ (1..n).each do |k|
85
+ sum -= (den[k] || 0) * (arr_coefs[n - k] || 0)
86
+ end
87
+ arr_coefs << sum / den[0].to_i
88
+ end
89
+ arr_coefs
90
+ end
91
+
92
+ def self.euler_criterion(x, pmod)
93
+ (x**((pmod - 1) / 2)) % pmod == 1
94
+ end
95
+
96
+ def self.square_modp_list(pmod, init = 1)
97
+ squares_fp = []
98
+ non_squares_fp = []
99
+
100
+ (init..pmod - 1).each do |x|
101
+ if euler_criterion(x, pmod)
102
+ squares_fp << x
103
+ else
104
+ non_squares_fp << x
105
+ end
106
+ end
107
+
108
+ [squares_fp, non_squares_fp]
109
+ end
110
+
111
+ def self.gcd(a, m)
112
+ a, m = m, a % m while m != 0
113
+ a.abs
114
+ end
115
+
116
+ def self.q_height(q_number)
117
+ x, y = q_number
118
+ [x.abs, y.abs].max
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+
5
+ module ModularForms
6
+ module Core
7
+ # ModularForms::Core::RamanujanTauFunction
8
+ #
9
+ # This module provides a generator for Ramanujan tau functions.
10
+ module RamanujanTauFunction
11
+ def self.niebur_sigma_formula # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
12
+ Enumerator.new do |q|
13
+ (1..Float::INFINITY).each do |n|
14
+ sum_term = 0
15
+ (1..(n - 1)).each do |i|
16
+ sum_term += i**2 * (35 * i**2 - 52 * i * n + 18 * n**2) *
17
+ NumericHelpers.divisor_function_sigma(i, 1) *
18
+ NumericHelpers.divisor_function_sigma(n - i, 1)
19
+ end
20
+ q << n**4 * NumericHelpers.divisor_function_sigma(n, 1) - 24 * sum_term
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+
5
+ module ModularForms
6
+ module Core
7
+ # ModularForms::Core::SL2Zgroups
8
+ #
9
+ # This module provides matrices from SL_2(Z) and related algebraic structures.
10
+ module SL2Zgroups
11
+ T_MATRIX = [[1, 1], [0, 1]].freeze
12
+ S_MATRIX = [[0, -1], [1, 0]].freeze
13
+ NEGATIVE_S_MATRIX = [[0, 1], [-1, 0]].freeze
14
+ I_MATRIX = [[1, 0], [0, 1]].freeze
15
+ NEGATIVE_I_MATRIX = [[-1, 0], [0, -1]].freeze
16
+ ST_MATRIX = [[0, -1], [1, 1]].freeze
17
+ U_MATRIX = [[1, 0], [1, 1]].freeze
18
+
19
+ def self.t_matrix(n_power)
20
+ [[1, n_power], [0, 1]]
21
+ end
22
+
23
+ def self.s_matrix(n_power)
24
+ matrix_s_power_map = {
25
+ 1 => S_MATRIX,
26
+ 2 => NEGATIVE_I_MATRIX,
27
+ 3 => NEGATIVE_S_MATRIX,
28
+ 0 => I_MATRIX
29
+ }
30
+ matrix_s_power_map[n_power % 4]
31
+ end
32
+
33
+ def self.st_matrix(n_power)
34
+ matrix_st_power_map = {
35
+ 1 => ST_MATRIX,
36
+ 2 => [[-1, -1], [1, 0]],
37
+ 3 => NEGATIVE_I_MATRIX,
38
+ 4 => [[0, 1], [-1, -1]],
39
+ 5 => [[1, 1], [-1, 0]],
40
+ 0 => I_MATRIX
41
+ }
42
+ matrix_st_power_map[n_power % 6]
43
+ end
44
+
45
+ def self.u_matrix(n_power_mod)
46
+ [[1, 0], [n_power_mod, 1]]
47
+ end
48
+
49
+ def self.product_gen_matrices(gen_mat_a, gen_mat_b) # rubocop:disable Metrics/AbcSize
50
+ [
51
+ [gen_mat_a[0][0] * gen_mat_b[0][0] + gen_mat_a[0][1] * gen_mat_b[1][0],
52
+ gen_mat_a[0][0] * gen_mat_b[0][1] + gen_mat_a[0][1] * gen_mat_b[1][1]],
53
+ [gen_mat_a[1][0] * gen_mat_b[0][0] + gen_mat_a[1][1] * gen_mat_b[1][0],
54
+ gen_mat_a[1][0] * gen_mat_b[0][1] + gen_mat_a[1][1] * gen_mat_b[1][1]]
55
+ ]
56
+ end
57
+
58
+ def self.index_gamma0(n)
59
+ product = 1
60
+ (2..n).each do |p|
61
+ if NumericHelpers.prime_number?(p) && n % p == 0 # rubocop:disable Style/NumericPredicate
62
+ product *= 1 + Rational(1, p)
63
+ end
64
+ end
65
+ (n * product).to_i
66
+ end
67
+
68
+ def self.index_gamma1(n)
69
+ product = 1
70
+ (2..n).each do |p|
71
+ if NumericHelpers.prime_number?(p) && n % p == 0 # rubocop:disable Style/NumericPredicate
72
+ product *= 1 - Rational(1, p * p)
73
+ end
74
+ end
75
+ (n * n * product).to_i
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core/numeric_helpers/numeric_helpers'
4
+
5
+ module ModularForms
6
+ module Core
7
+ # ModularForms::Core::ThetaFunctions
8
+ # This module provides a generator for Theta functions.
9
+ module ThetaFunctions
10
+ def self.jacobi_theta_function(jacobi_index = 3, square_coefs = false) # rubocop:disable Style/OptionalBooleanParameter,Metrics/MethodLength,Metrics/AbcSize
11
+ Enumerator.new do |q|
12
+ q << 1
13
+ (1..Float::INFINITY).each do |tau|
14
+ square_num = tau**2
15
+ square_next_num = (tau + 1)**2
16
+ sign = jacobi_index == 3 ? 1**tau : (-1)**tau
17
+ q << (square_coefs == false ? sign * 2 : sign * square_num)
18
+ (square_next_num - square_num - 1).abs.times do
19
+ q << 0
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.jacobi_theta_function_pow(jacobi_index, power, precision)
26
+ vec = jacobi_theta_function(jacobi_index).take(precision)
27
+ theta_q_coefs = [1]
28
+ power.times do
29
+ theta_q_coefs = NumericHelpers.linear_convolve(theta_q_coefs, vec, precision)
30
+ end
31
+ theta_q_coefs
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core/eisenstein_series'
4
+ require_relative 'core/elliptic_curves_q'
5
+ require_relative 'core/dedekind_eta_functions'
6
+ require_relative 'core/ramanujan_tau_function'
7
+ require_relative 'core/theta_functions'
8
+ require_relative 'core/klein_j_invariant'
9
+ require_relative 'core/hecke_operators'
10
+ require_relative 'core/sl2z_groups'
11
+ require_relative 'core/elliptic_curves_fp'
12
+ require_relative 'core/dirichlet_characters'
13
+ require_relative 'core/newform_invariants'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModularForms
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './modular_forms/core'
4
+
5
+ # ModularForms
6
+ #
7
+ # This module provides a set of tools for working with modular forms and elliptic curves.
8
+ # It is designed for use with Sonic Pi, enabling creative exploration
9
+ # of these mathematical structures and their operations through sound.
10
+
11
+ # NOTE: While the module is designed for creative exploration, it is not optimized for high
12
+ # performance and should not be considered a replacement for specialized mathematical software.
13
+ module ModularForms # rubocop:disable Metrics/ModuleLength
14
+ module_function
15
+
16
+ def eisenstein_series(weight_k, gal_f = nil)
17
+ Core::EisensteinSeries.eisenstein_series(weight_k, gal_f)
18
+ end
19
+
20
+ def eisenstein_series_product(weight_k1, weight_k2, prec)
21
+ Core::EisensteinSeries.eisenstein_series_product(weight_k1, weight_k2, prec)
22
+ end
23
+
24
+ def eisenstein_series_pow(weight_k, power, prec)
25
+ Core::EisensteinSeries.eisenstein_series_pow(weight_k, power, prec)
26
+ end
27
+
28
+ def dedekind_eta_function(m_scale = 1, pentagonal_coefs = false) # rubocop:disable Style/OptionalBooleanParameter
29
+ Core::DedekindEtaFunctions.eta_function(m_scale, pentagonal_coefs)
30
+ end
31
+
32
+ def dedekind_eta_pow(power, prec, m_scale = 1)
33
+ Core::DedekindEtaFunctions.eta_function_pow(power, prec, m_scale)
34
+ end
35
+
36
+ def dedekind_sum(h, k)
37
+ Core::DedekindEtaFunctions.dedekind_sum(h, k)
38
+ end
39
+
40
+ def ramanujan_tau_function
41
+ Core::RamanujanTauFunction.niebur_sigma_formula
42
+ end
43
+
44
+ def jacobi_theta_function(jacobi_index = 3, square_coefs = false) # rubocop:disable Style/OptionalBooleanParameter
45
+ Core::ThetaFunctions.jacobi_theta_function(jacobi_index, square_coefs)
46
+ end
47
+
48
+ def jacobi_theta_function_pow(jacobi_index, power, prec)
49
+ Core::ThetaFunctions.jacobi_theta_function_pow(jacobi_index, power, prec)
50
+ end
51
+
52
+ def j_function(prec)
53
+ Core::KleinJInvariant.j_function(prec)
54
+ end
55
+
56
+ def hecke_operator_prime_non_cusp(non_cusp_form_arr, prime, weight_k, prec)
57
+ Core::HeckeOperators.hecke_prime_non_cusp(non_cusp_form_arr, prime, weight_k, prec)
58
+ end
59
+
60
+ def hecke_operator_prime_cusp(cusp_form_arr, prime, weight_k, prec)
61
+ Core::HeckeOperators.hecke_prime_cusp(cusp_form_arr, prime, weight_k, prec)
62
+ end
63
+
64
+ def gamma0_index(n)
65
+ Core::SL2Zgroups.index_gamma0(n)
66
+ end
67
+
68
+ def gamma1_index(n)
69
+ Core::SL2Zgroups.index_gamma1(n)
70
+ end
71
+
72
+ def t_gen_matrix(n_power)
73
+ Core::SL2Zgroups.t_matrix(n_power)
74
+ end
75
+
76
+ def s_gen_matrix(n_power)
77
+ Core::SL2Zgroups.s_matrix(n_power)
78
+ end
79
+
80
+ def u_gen_matrix(mod_n)
81
+ Core::SL2Zgroups.u_matrix(mod_n)
82
+ end
83
+
84
+ def st_gen_matrix(n_power)
85
+ Core::SL2Zgroups.st_matrix(n_power)
86
+ end
87
+
88
+ def product_gen_mats(gen_mat_a, gen_mat_b)
89
+ Core::SL2Zgroups.product_gen_matrices(gen_mat_a, gen_mat_b)
90
+ end
91
+
92
+ def dirichlet_trivchar(modq, a)
93
+ Core::DirichletCharacters.dirichlet_trivchar(modq, a)
94
+ end
95
+
96
+ def conrey_p_pminus1(modp, a)
97
+ Core::DirichletCharacters.conrey_p_pminus1(modp, a)
98
+ end
99
+
100
+ def gauss_sum_triv(dirichlet_q, a)
101
+ Core::DirichletCharacters.gauss_sum_triv(dirichlet_q, a)
102
+ end
103
+
104
+ def gauss_sum_conrey_p_minus1(dirichlet_q, a, parity)
105
+ Core::DirichletCharacters.gauss_sum_conrey_p_minus1(dirichlet_q, a, parity)
106
+ end
107
+
108
+ def elliptic_curve_q(coefs)
109
+ Core::EllipticCurvesQ.elliptic_curve_q(coefs)
110
+ end
111
+
112
+ def discriminant_q(curve)
113
+ Core::EllipticCurvesQ.discriminant_qq(curve)
114
+ end
115
+
116
+ def j_invariant_q(curve)
117
+ Core::EllipticCurvesQ.j_invariant(curve)
118
+ end
119
+
120
+ def point_on_curve_q?(curve, point)
121
+ Core::EllipticCurvesQ.point_on_curve?(curve, point)
122
+ end
123
+
124
+ def point_addition_q(curve, p, q)
125
+ Core::EllipticCurvesQ.point_addition(curve, p, q)
126
+ end
127
+
128
+ def scalar_mul_point_q(curve, n, point)
129
+ Core::EllipticCurvesQ.scalar_mul_point(curve, n, point)
130
+ end
131
+
132
+ def isogeny_2deg_q(curve, point_2tor)
133
+ Core::EllipticCurvesQ.isogeny_2deg(curve, point_2tor)
134
+ end
135
+
136
+ def isogeny_ndeg_q(curve, point_ntor, order)
137
+ Core::EllipticCurvesQ.isogeny_ndeg(curve, point_ntor, order)
138
+ end
139
+
140
+ def weil_height(x_point)
141
+ Core::EllipticCurvesQ.weil_height(x_point)
142
+ end
143
+
144
+ def canonical_height(curve, point, prec = 64)
145
+ Core::EllipticCurvesQ.canonical_height(curve, point, prec)
146
+ end
147
+
148
+ def elliptic_curve_fp(p, coefs)
149
+ Core::EllipticCurvesFp.elliptic_curve_fp(p, coefs)
150
+ end
151
+
152
+ def point_on_curve_modp?(curve, point)
153
+ Core::EllipticCurvesFp.point_on_curve_modp?(curve, point)
154
+ end
155
+
156
+ def discriminant_modp(curve)
157
+ Core::EllipticCurvesFp.discriminant_modp(curve)
158
+ end
159
+
160
+ def j_invariant_modp(curve)
161
+ Core::EllipticCurvesFp.j_invariant_modp(curve)
162
+ end
163
+
164
+ def point_addition_modp(curve, p_point, q_point)
165
+ Core::EllipticCurvesFp.point_addition_modp(curve, p_point, q_point)
166
+ end
167
+
168
+ def scalar_mul_point_modp(curve, n, point)
169
+ Core::EllipticCurvesFp.scalar_mul_point_modp(curve, n, point)
170
+ end
171
+
172
+ def points_fp(curve, point_at_infinity = false) # rubocop:disable Style/OptionalBooleanParameter
173
+ Core::EllipticCurvesFp.points(curve, point_at_infinity)
174
+ end
175
+
176
+ def cardinality_fp(curve)
177
+ Core::EllipticCurvesFp.cardinality(curve)
178
+ end
179
+
180
+ def quadratic_twist_fp(curve)
181
+ Core::EllipticCurvesFp.quadratic_twist(curve)
182
+ end
183
+
184
+ def eta_product(eta1, eta2, prec = nil)
185
+ Core::DedekindEtaFunctions.eta_product(eta1, eta2, prec)
186
+ end
187
+
188
+ def eta_quotient(num_eta, den_eta, prec)
189
+ Core::DedekindEtaFunctions.eta_quotient(num_eta, den_eta, prec)
190
+ end
191
+
192
+ def analytic_conductor(level_n, weight_k)
193
+ Core::NewFormInvariants.analytic_conductor(level_n, weight_k)
194
+ end
195
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: modular_forms
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Edgar Armando Delgado Vega
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Modular Forms is an accessible interface for experimenting with modular
13
+ forms and elliptic curves through algorithmic composition and live coding using
14
+ Sonic Pi.
15
+ email:
16
+ - edelve91@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files:
20
+ - README.md
21
+ files:
22
+ - README.md
23
+ - lib/modular_forms.rb
24
+ - lib/modular_forms/core.rb
25
+ - lib/modular_forms/core/dedekind_eta_functions.rb
26
+ - lib/modular_forms/core/dirichlet_characters.rb
27
+ - lib/modular_forms/core/eisenstein_series.rb
28
+ - lib/modular_forms/core/elliptic_curves_fp.rb
29
+ - lib/modular_forms/core/elliptic_curves_q.rb
30
+ - lib/modular_forms/core/hecke_operators.rb
31
+ - lib/modular_forms/core/klein_j_invariant.rb
32
+ - lib/modular_forms/core/newform_invariants.rb
33
+ - lib/modular_forms/core/numeric_helpers/numeric_helpers.rb
34
+ - lib/modular_forms/core/ramanujan_tau_function.rb
35
+ - lib/modular_forms/core/sl2z_groups.rb
36
+ - lib/modular_forms/core/theta_functions.rb
37
+ - lib/modular_forms/version.rb
38
+ homepage: https://github.com/edelveart/modular_forms
39
+ licenses:
40
+ - MIT
41
+ metadata:
42
+ homepage_uri: https://github.com/edelveart/modular_forms
43
+ source_code_uri: https://github.com/edelveart/modular_forms/
44
+ rdoc_options:
45
+ - "--main"
46
+ - README.md
47
+ - "--line-numbers"
48
+ - "--inline-source"
49
+ - "--quiet"
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 3.0.0
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubygems_version: 3.6.9
64
+ specification_version: 4
65
+ summary: A creative toolkit for exploring modular forms and elliptic curves through
66
+ Sonic Pi.
67
+ test_files: []