This is a detailed guide on what methods are available in this package, and how to use them.
We represent circle packings by Eisenstein quadruples, i.e. a primitive integral vector
In general, methods for Eisenstein circle packings are of the form eis_METHOD.
The method eis_makeall(a) returns all primitive quadruples containing curvature a. We make a the first curvature, put it in standard position, and ensure it is 1-reduced: swapping the curvatures in index 2/3/4 do not decrease the quadruple. Possible Eisenstein quadruples be checked with eis_check(v), which validates the input.
? v = eis_makeall(-3)
%1 = [Vecsmall([-3, 17, 20, 4]), Vecsmall([-3, 5, 14, 10])]
? eis_check(v[2])
%2 = 1
? eis_check([-3, 17, 20, 2]) /*Does not satisfy the Eisenstein equation*/
%3 = 0
We can execute circle swaps and reduce the quadruple with eis_swap and eis_reduce. Note that these always return t_VECSMALLs, as this is the preferred way to work with the quadruples.
? v = eis_swap(Vecsmall([-4, 18, 19, 7]), [1, 2, 1, 3, 4, 2])
%1 = Vecsmall([216, 806, 251, 927])
? eis_swap(v, 2)
%2 = Vecsmall([216, 128, 251, 927])
? eis_reduce(v)
%3 = Vecsmall([-4, 18, 19, 7])
? eis_reduce(v, 2)
%4 = [Vecsmall([-4, 12, 19, 7]), Vecsmall([2, 4, 1, 3, 2, 1, 2])]
? eis_reduce(Vecsmall([9, 5, -2, 4]), 0)
%5 = Vecsmall(9, 5, -2, 4)
? eis_reduce(Vecsmall([9, 5, -2, 4]), 1)
%6 = Vecsmall(-2, 4, 9, 5)
Passing between binary quadratic forms and Eisenstein quadruples is accomplished with eis_quadrupletoqf and eis_qftoquadruple. We format (primitive positive semidefinite first-odd) binary quadratic forms as length 3 t_VECSs. We do not use the built-in t_QFB data type, as we are working with a different equivalence to ususal (
? q = eis_quadrupletoqf(Vecsmall([-7, 33, 40, 10]), 2)
%1 = [73, 23, 13]
? eis_qftoquadruple(q, 33, 4) /*Remake the quadruple, but put it in the 4th position.*/
%2 = Vecsmall([40, 10, -7, 33])
? eis_qfreduce([247, 905, 829])
%3 = [1, 1, 7]
The type of a quadruple determines the modulo 4 behaviour of the curvatures in the packing. There are two types,
? eis_type(Vecsmall([-4, 28, 33, 5]))
%1 = [3, 1]
? eis_type(Vecsmall([-4, 12, 19, 7]))
%2 = [3, 3]
The reciprocity obstructions are determined by the [3, 1], those are the same, else they are negatives of each other.
? eis_chi2(Vecsmall([-2, 8, 11, 3]))
%1 = [1, -1]
? eis_chi2(Vecsmall([-2, 4, 9, 5]))
%2 = [1, 1]
? eis_type(Vecsmall([-2, 4, 9, 5]), 1)
%3 = [3, 1, [1, 1]]
To study the curvatures/quadruples that appear/are missing, use the methods eis_frequencies, eis_missing, eis_quadruples, and eis_sporadic.
? eis_missing(Vecsmall([-3, 5, 14, 10]), [10^5, 10^5+1000])
%1 = [[Vecsmall([100352]), Vecsmall([100489]), Vecsmall([100398, 100854]), Vecsmall([])], [Vecsmall([100352, 100872]), Vecsmall([100489]), Vecsmall([]), Vecsmall([])]]
? eis_quadruples(Vecsmall([-6, 14, 23, 15]), 30)
%2 = [Vecsmall([-6, 14, 23, 15]), Vecsmall([-6, 20, 23, 15]), Vecsmall([-6, 20, 23, 19]), Vecsmall([-6, 14, 23, 19])]
? fr = eis_frequencies(Vecsmall([-5, 11, 20, 12]), [10^5, 10^5+20])
%3 = [[Vecsmall([3, 4, 8, 7, 7, 5]), 0, Vecsmall([1, 3, 4, 2, 1]), Vecsmall([4, 4, 6, 7, 4])], [Vecsmall([7, 1, 11, 5, 4, 5]), 0, Vecsmall([6, 1, 4, 5, 3]), Vecsmall([6, 4, 5, 7, 8])]]
? vector(21, i, eis_frequencies_search([10^5, 10^5+20], fr, 10^5+i-1))
%4 = [[3, 7], [0, 0], [1, 6], [4, 6], [4, 1], [0, 0], [3, 1], [4, 4], [8, 11], [0, 0], [4, 4], [6, 5], [7, 5], [0, 0], [2, 5], [7, 7], [7, 4], [0, 0], [1, 3], [4, 8], [5, 5]]
? vector(21, i, eis_frequencies_search([10^5, 10^5+20], fr[1], 10^5+i-1))
%5 = [3, 0, 1, 4, 4, 0, 3, 4, 8, 0, 4, 6, 7, 0, 2, 7, 7, 0, 1, 4, 5]
? eis_sporadic(Vecsmall([-3, 5, 14, 10]), [10^5, 10^5+1000])
%6 = [[Vecsmall([]), Vecsmall([]), Vecsmall([100398, 100854]), Vecsmall([])], [Vecsmall([100872]), Vecsmall([]), Vecsmall([]), Vecsmall([])]]
As seen above, the method eis_frequencies_search is a convenient way to process the output of eis_frequencies.
Circles in an Eisenstein packing are stored as an integral quadruple eis_circles, and compiled into a pdf picture (using LaTeX and tikz) using eis_display. Automatic compiling is recommended, see the "Optional Packages" section of the README for more. Note that eis_display has many options for the output, be sure to check the help with ?eis_display.
? c = eis_circles(Vecsmall([-13, 27, 54, 32]), 60)
%1 = [[-13, -12, -20, 3], [27, 26, 42, -7], [54, 50, 84, -11], [32, 28, 48, -7], [55, 50, 86, -9], [50, 48, 80, -9]]
? t = eis_display(c, "pic1") /*Default options*/
%2 = ["pic1", 1, 0]
? f(x) = {return(min(gcd(x[1], x[2]), 2));}; /*Colour based on if the curvature and co-curvature are coprime*/
? t1 = eis_display(c, "pic2", , f)
%4 = ["pic2", 1, 0]
You may wish to edit the raw LaTeX file of the circle packing and recompile. The corresponding file above will be found in ./images/build/pic1_build.tex. Once you edit it, you can use the output of eis_display to recompile: tex_recompile(t).
The file makepictures.gp also includes some methods to collect circle packing pictures into one file, draw the strip packing out more, and draw parts of the Eisenstein Schmidt arrangement. See the inline help in the file for how to use it. For example, to make a nice strip packing picture, you can do:
? \r makepictures
? c = strippack_cut(200, 3, 0.2, 4.8);
? eis_display(c, "strip");
To draw part of the Eisenpint Schmidt arrangement, you can do:
? \r makepictures
? c = schmidt_circles_box(20, 1, 6, 4);
? eis_display(c, "eisenpint20", 0);
Let [a, b]~. We supply a few methods to compute basic operations with Eisenstein integers.
In general, methods for Eisenstein integers are of the form eisMETHOD (no underscore).
? a = [34, -41]~; b = [-43, 29]~;
? eismul(a, b)
%2 = [-273, 1560]~
? eisnorm(a)
%3 = 1443
? eisconj(a)
%4 = [-7, 41]
? eisgcd(a, b)
%5 = [1, -11]~
? eisbezout(a, b)
%6 = [[2, -2]~, [1, -2]~, [1, -11]~]
? eismul(a, [2, -2]~) + eismul(b, [1, -2]~)
%7 = [1, -11]~
? eisdivalg([2, 11]~, [5, -3]~)
%8 = [[-2, 3]~, [3, -1]~]
? eismul([-2, 3]~, [5, -3]~) + [3, -1]~
%9 = [2, 11]~
? eisunits()
%10 = [[1, 0]~, [-1, 0]~, [0, 1]~, [0, -1]~, [1, -1]~, [-1, 1]~]
We have a few methods to work with
? M = [[1, 2]~, [3,- 2]~; [4, 5]~, [2, 17]~]; N = [[-3, 0]~, [2, 5]~; [-3, -4]~, [1, 2]~];
? eis_matdet(M)
%2 = [-54, 58]~
? print(eis_matmul(M, N))
%3 = [[-20, -4]~, [-1, 19]~; [50, -142]~, [-49, 110]~]
? T = eis_matfrombotrow([4, 5]~, [2, 11]~ ); print(T);
%4 = [[3, -1]~, [4, 0]~; [4, 5]~, [2, 11]~]
? eis_matdet(T)
%5 = [1, 0]~
? eis_matfrombotrow([1, 2]~, [1, 2]~)
%6 = 0
For each
Here are the basic methods:
? M = [[-1, 4]~, [-3, 5]~; [1, 2]~, [0, 3]~];
? c = eis_tocircle(M)
%2 = [3, 7, 8, 0]
? eis_coset(c)
%3 = 5
? eis_coset(M) /*Allows for c or M or [M, c] input*/
%4 = 5
? eis_location(c) /*Allows for c or M or [M, c] input*/
%5 = [0, 0]~
? c2 = eis_trans(c, [2, 3]~) /*Allows for c or M or [M, c] input*/
%6 = [3, 104, 29, -3]
? eis_location(c2)
%7 = [2, 3]~
? eis_coset(c2)
%8 = 6
? M1 = eis_tomat(c2) /*We can also go backwards from the reduced quadruple to a matrix*/
%9 =
[[-7, 3]~ [-9, -11]~]
[[-1, 1]~ [-3, 0]~]
? eis_tocircle(M1)
%10 = [3, 104, 29, -3]
? eis_chi2([9, 8, 16, 3]) /*Also allows for circle input (as long as it is coset 0/4)*/
%11 = 1
We also allow such quadruples where eis_(coset/location/trans) methods also allow input of M or [M, c], where c=eis_tomat(M). In the last case, the output retains the same format.
If a circle begins in coset 0/4, we ensure that the matrix output is in the Eisenpint group.
To compute the inversive distance between circles (necessarily an integer or half-integer),
? eis_invdist([3, 7, 8, 0], [3, 7, 8, 0])
%1 = 1
? eis_invdist([-6, -5, -6, 5], [-6, 0, -1, 0])
%2 = -73/2
Starting with an Eisenstein quadruple, we can find where a component circle located in the En-Schmidt arrangement (a representative of the equivalence class, which is either the real line, or whose centre lies in the rectangle from 0 - 1 - 1+sqrt(3)/2i - sqrt(3)/2i. The equivalence classes lie in the unoriented En-Schmidt arrangement, hence the curvature may negate.
? eis_toschmidt(Vecsmall([-5, 19, 24, 8]), 2)
%1 = [[[-1, -2]~, [8, -4]~; [-2, -1]~, [7, -6]~], [19, 20, 36, 5]]
We also provide methods to pass between these representations and quadratic forms, with eis_toqf and the previously mentioned eis_to(mat/circle). If we start with a quadratic form, the circle is guaranteed to be in coset 0/4, and the matrix in the Eisenpint group.
? q1 = eis_toqf([19, 20, 36, 5])
%1 = [49, 17, 7]
? q2 = eis_toqf([[-1, -2]~, [8, -4]~; [-2, -1]~, [7, -6]~])
%2 = [43, -11, 7]
? eis_qfreduce(q1) == eis_qfreduce(q2)
%3 = 1
? c1 = eis_tocircle(q1) /*Outputs an equivalent circle, not necessarily equal!*/
%4 = [-19, -20, -36, -31]
? M1 = eis_tomat(q2) /*Outputs an equivalent matrix, not necessarily equal!*/
%5 =
[[3, -2]~ [-4, -4]~]
[[3, -1]~ [-1, -6]~]
? eis_qfreduce(eis_toqf(M1)) == eis_qfreduce(eis_toqf(c1))
%6 = 1
We can find all the circles in the full Eisenstein Schmidt arrangement of curvature N whose centre lies inside the fundamental polygon (parallelogram with vertices of eis_schmidtfund(N). The set of all such circles is then obtained via all translations by eis_circletrans. Note that replacing
? eis_schmidtfund(4)
%1 = [[4, 4, 8, 3], [4, 2, 5, 0], [4, 1, 4, 1], [4, 10, 11, 0], [4, 1, 3, -1], [4, 4, 5, -3]]
? eis_schmidtfund(-4)
%2 = [[-4, -3, -4, 3], [-4, 0, -1, 0], [-4, -6, -8, 1], [-4, -4, -7, 0], [-4, -6, -9, -1], [-4, -3, -7, -3]]
We can also work with the 10 cosets of circles. The function eis_schmidtselect allows you to collect a range of curvatures, and output all circles in a range of cosets whose circle centres lie inside the parallelogram with vertices of
? eis_schmidtselect([1, 2], [0, 4])
%1 = [[1, 4, 4, 1], [-1, -2, -2, 1], [2, 2, 4, 1], [2, 8, 8, 3], [-2, -6, -4, 3], [-2, -12, -8, 1]]
We use qfblift internally, but leave public access as it is an independantly useful function. Given a primitive integral t_QFB q of discriminant Dn^2, we find a reduced primitive integral q' of discriminant D and a primitive integral matrix M of determinant |n| for which
? qfblift(Qfb(71, 139, 72), 7)
%1 = [Qfb(2, -1, 3), [-4, -5; 3, 2]]
? 2*(-4*x-5*y)^2-(-4*x-5*y)*(3*x+2*y)+3*(3*x+2*y)^2
%2 = 71*x^2 + 139*y*x + 72*y^2
We include some linear regression methods, which are useful for data analysis. Ordinary least squares regression is captured in OLS, and we have two useful shortcuts for common situations. If we want to regress on OLS_nointercept, and if we want to regress on OLS_single.
? X = [1,1,1,1;1,2,3,4;1,4,9,16];
? y = [3,7,13,21]~;
? OLS(X, y) /*Returns [params, rsq]*/
%3 = [[1, 1, 1]~, 1]
? rsquared(X, y, [1, 1, 1]~)
%4 = 1
? rsquared(X, y, [1, 1, 1/2]~)
%5 = 191/368
? y2 = [3, 6, 14, 20]~;
? OLS(X, y2)
%7 = [[-1/4, 43/20, 3/4]~, 3526/3575]
? OLS_nointercept([1, 2, 3, 4], [2, 4, 6, 8]) /*Returns [k, rsq]*/
%8 = [2, 1]
? OLS_single([1, 2, 3, 4], [3, 5, 7, 9]) /*Returns [[a, b]~, rsq]*/
%9 = [[1, 2]~, 1]
Finally, the file strongapprox.gp includes naive methods to compute the sizes of SL(2, Z[omega]) (mod N), as well as the group E'' (mod N), which are used to verify the exact bad modulus in strong approximation.