 But based on Hendrik Lenstra's comment from yesterday's lecture, which I completely agree to, he objected to the fact that we were on the one hand not checking primes past the square root, and on the other hand starting at 2p rather than p squared, because the same logic tells you that any multiple of p between 2p and p squared, you've already crossed off because it's divisible by a smaller prime, and that's exactly the same logic you used to start at the square root of p. I agree with him, but maybe took the action he wasn't expecting, which is to get rid of the square roots entirely, because I think it's a simpler algorithm. It has exactly the same asymptotic complexity, and I expect it's also exactly the algorithm that Eratosthenes used. Even the constant factors are the same. So in practice, for the small examples, it's going to make the timings a little slower than what we saw yesterday, but not by a significant amount. And I think the algorithm's cleaner because now we don't have to worry about questions like what happens when you take the floor of the square root. There's nothing here but integers. So this is just, I guess, the life lesson here is the pedagogically optimal algorithm is not necessarily the same as the computationally optimal algorithm. OK. Next announcement is I know I promised you to talk about sums that we were going to talk about, sums of cubes. And when I'm preparing the lectures for today, for this week, I had planned to cover both some algorithms having to do with writing integers as sums of integer cubes and also some algorithms to do with point counting on elliptic curves. Unfortunately, it's... A dupe? Yeah. Could you try if it's possible to put the mic a little closer to your mouth? Oh, sure. Yeah. It's a little, somehow it's not... Is that better? Maybe they have to adjust the volume. Can you hear people in the back can hear me OK? It's a little quieter than usual. Yeah, OK. All right, I'm getting thumbs up in the back. But please, if at any point anything is not visible on the screen or you can't hear me, don't be shy, let me know. But it became clear to me that we're not going to have time to cover both. And I think it's much better to carefully and thoroughly and making sure that we understand everything every step of the way, cover one thing than rather try to cover two things shabbily. So for those of you who are disappointed, well, you'll have to content yourself with the equation you saw on my t-shirt that I wore yesterday and today's t-shirt. And if you really want to learn about the algorithm that was used to find these solutions, if you happen to be in town or able to watch it on video, I don't know if they're going to broadcast it. I think they might be the Mordell 2022 conference in honor of the 100th anniversary of Mordell. I'm going to be speaking about exactly this algorithm at that conference in Oxford sort of in mid-August. So, yeah, anybody who knows me knows that when it comes down to a decision between sums of cubes and elliptic curves, as much as I love sums of cubes, elliptic curves were really my first mathematical love and I can't abandon them. So we're going to talk about elliptic curves and, in particular, point counting on elliptic curves. So that's what we're going to spend the next three lectures doing. We're going to look at various algorithms for counting points on elliptic curves. Now, there are not lots of good reasons, and I'll mention some of them, why you might want to actually count points on elliptic curves. But my motivation for using this example is really because it's a great sort of scaffold, a common framework that lets me introduce a lot of different algorithms that arise when you're trying to do computations in computer algebra systems. So whether, you know, working over finite fields, things like pencil lifting, CRT, evaluation, interpolation of polynomials, working in quotient rings, there's just a lot of sort of standard machinery that you need to do almost anything interesting in a computer algebra system and almost all of that gets used in one or more point counting algorithms for elliptic curves. Okay, and I'm going to try and, as I said, my goal is not to teach you any new math. You may learn some accidentally. I will be putting in references to anything that I'm citing here that is not familiar to you, but I'm going to try and keep the definitions to the absolute minimum that we need to actually understand what the algorithms are doing. Okay. All right. So just to recall, for those who might not be familiar with them, a elliptic curve over a field K, I'll always denote my elliptic curves E, or maybe I'll use an E prime or an E twiddle, is a smooth projected curve of genus one with a distinguished rational point. And the wonderful thing is that the rational points on an elliptic curve form an abelian group where you define the group laws defined by just stating the edict that three lines, three points on a line should always sum to zero. And zero is the identity element of the group, which is precisely our distinguished point. We're free to choose that distinguished point. It can be any rational point on our curve, but there's a standard convention for the form of the curves. We're going to always assume without loss of generality that our curves are defined by some plain cubic equation. And in that setting, it's customary to take our distinguished point to be the pointed infinity. And we're going to want to consider algorithms that work potentially in any characteristic. So I'm not necessarily going to assume up front that we can put our elliptic curve in the form y squared equals x cubed plus ax plus b. But we'll eventually get to algorithms where it'll just be simpler and more convenient to do that, and we'll make that assumption and worry about what to do in characteristic two and three when we need to. But the first, I think, all the algorithms I'm going to present today are agnostic as far as characteristic. They'll work for any elliptic curve over any finite field. We're going to be focusing specifically on finite fields, but some of the things we'll talk about also have applications to elliptic curves over other fields. In particular, tomorrow, we're going to be especially interested in the problem of counting points on elliptic curves over finite fields where we're working with a fixed elliptic curve and considering its reduction mod p for many different primes p. So that reduces to a whole sequence of point counting problems of elliptic curves over finite fields, but they're all related to a single elliptic curve over a global field. And this is going to be important for many reasons that I'll discuss. So maybe just to finish up with the motivation. So the problem of counting points on, rational points on elliptic curve over finite field is completely equivalent to computing the trace of Frobenius. And if you're not familiar with the trace of Frobenius, you could take this as a definition. It's q plus 1 minus the number of points on the elliptic curve. And we'll make use of a few well-known facts about the trace of Frobenius. Why might you want to count points on elliptic curves? Well, if you're interested in cryptographic applications of elliptic curves that depend on the discrete logarithm problem, you really, really want to be able to count points on elliptic curves because you won't know that your system is secure if you don't. In fact, one of the problem sessions later this week will get a chance to break the discrete log problem on an elliptic curve over a cryptographic size field, something that is allegedly impossible. But actually, it's entirely possible if the group order of your elliptic curve isn't particularly prime. OK, another reason, even if you're thinking about cryptographic applications of elliptic curves, not necessarily based on the discrete log problem, but maybe isogenic-based cryptography, there are situations where you still might want to know things about your elliptic curve where knowing the trace of Frobenius would be very useful. For example, if you want to compute the anamorphism ring of your elliptic curve, computing the trace of Frobenius is the standard first step. Of course, if you just wanted to know if your elliptic curve is super singular or ordinary, you could determine that by just asking is the trace of Frobenius 0 mod p where p is the characteristic of fq. However, there are better ways to test whether an elliptic curve is super singular or not, and you get a chance to implement one of those better ways in one of the problem sessions. OK, but point counting is a commonly used way to do it just because all these computer algebra systems know how to count points. But another completely sort of separate reason to be interested in point counting is that if you're interested in elliptic curves over q or even over a number field, there are a lot of both open and fairly recently closed conjectures about such elliptic curves that involve the traces of Frobenius, a sub p for all the primes, infinitely many primes p, where we're reducing our elliptic curve over q modulo p for all, but finally many primes where we might get a singular elliptic curve, we're going to get an elliptic curve over fp, and we can count points on fp to compute the a sub p that appear in both the modular form, the Fourier coefficients of the modular form that corresponds to our elliptic curve by the modularity theorem, and the a sub p's are also, appear, determine the Dirichlet coefficients of the L function of the elliptic curve. And while the modularity theorem is now proved as is the cytotape theorem, which is about the distribution of these Frobenius traces when we normalize them by dividing by the square root of p, there are many other conject open conjectures about the sequence of Frobenius traces associated to an elliptic curve over q. The Lang-Trotter conjecture is one example, more famous example is the Birch and Swinnerton-Dyer conjecture. You might object and say, wait, I don't see any Frobenius traces in the Birch and Swinnerton-Dyer formula, but in fact you do the L function. If you want to compute the L function and elliptic curve, well, you need to compute the Dirichlet series, the Dirichlet coefficients of that L function in order to have any hope of computing its order of vanishing, and that reduces just to computing a whole bunch of Frobenius traces. Fortunately, you don't need infinitely many of them, finitely many are enough. Okay. And then the last reason why you might be interested, and this is especially relevant for those who are interested in David, the course David Harvey's going to be teaching on computing zeta functions in week three, basically all the algorithms we know for counting points are more generally computing zeta functions of higher genus curves are generalizations of algorithms for elliptic curves. And so it's very useful to study them sort of in the simplest non-trivial setting, which is an elliptic curve. I mean, you might say, why go to genus one? Why not count points on genus zero? Well, unfortunately, or fortunately, depending on your perspective, counting points on genus zero curves is very easy. They're always Q plus one. Okay. All right. So any questions on just the background setup terminology before we start digging into the algorithms? Okay. Great. So our first algorithm to compute, to count points is going to be sort of the absolute most naive algorithm you could imagine. We have a plane projective cubic, a smooth projective cubic curve in P2, and we just want to count the number of projective points on the curve. We're just going to count projective solutions to our equation g of x, y, z equals zero. I'm using the letter g because I want to save the letter f for my y squared equals f of x. So our g is a homogeneous polynomial in three variables of degree three, and we can count points by just counting projective solutions. How do we do that? Well, we could first count all the projective points that have a non-zero z-coordinate, so that would not be counting all the possible pairs x not y not in fq, such that when we plug x not y not one into g, we get zero. And then to account for the projective points that have zero z-coordinates, well, some of those will have a non-zero y-coordinate, so we should count all of those by plugging in x not one zero. And then finally, there may or may not be a single projective point that has a non-zero x-coordinate, but the y and z-coordinates are both zero. And so I've denoted this. So that last term in this sum is either zero or one, and those little square brackets are what's known as Iverson notation, which is really popularized by Donald Knuth. It's just a very compact way of turning a predicate into an integer that's zero or one. So this is a Boolean question. Is g one zero zero equal to zero? The answer is yes. It evaluates to one. If the answer is no, it evaluates to zero. And I think you've all already seen examples of how to implement exactly that conditional expression in all four of the computer algebra systems. They all have a built-in ternary operator that does exactly what Iverson notation does. OK. Now, if you stare at that equation for a moment, you realize that the fact that I had an elliptic curve on the left-hand side is completely irrelevant. The point-counting mechanism works just fine for any plane curve, for any projective plane curve. It doesn't even need to be smooth, and it doesn't need to have degree three. And so we'll just write our algorithm generically, even though we plan to apply it to elliptic curves. OK. And so I almost feel silly writing down the algorithm because I basically already told you the algorithm. Compute the sum that we just wrote down. OK. So, but just to lay out the steps, just be very precise about it. Our input is some non-zero homogeneous polynomial in three variables. Our output is the number of solutions, points where that polynomial vanishes in P2. The FQ rational points where it vanishes. And we compute that by first counting the points with non-zero z-coordinate, then counting the points with zero z-coordinate but non-zero y-coordinate, and then finally adding in either zero or one to account for whether there is a point with non-zero x-coordinate and zero y-nz-coordinate. And how long does this algorithm take? Well, it's going to involve some arithmetic multiply ring operations over our finite field. We're going to need to do some multiplications and additions to evaluate g at the points we're plugging in. And so that is going to take the cost of multiplying two integers of length with n bits. We know is n log n as spectacularly proved recently by David Harvey and van der Hoven. And in fact, that result generalizes at least under some suitable heuristic assumptions to, that are certainly true, to multiplying polynomials over finite fields. And you can reduce, so you can effectively get the same complexity bound when working with polynomials over finite fields. And this is how we're going to represent an arbitrary finite field, fq. We would represent it using some defining polynomial for fq as an extension of fp. And we'd be working in the ring of fp bracket x modulo, that defining polynomial. And so all of our finite field operations would ultimately turn into ring operations in that polynomial ring. And if you constrain q to the qs that one is ever likely to actually be interested when point counting on ellipt occurs, which means q is probably either prime or a very large power of a very small prime. In both of those cases, you don't need any heuristics. You get exactly the complexity that I've written here. But the important part of the complexity bound is not the log q, log log q. It's the q squared, right? This is going to take a long time. On the other hand, the beauty of this algorithm is it just works. And when q is like two or three, which can be annoying special, or when q is a very small prime, which can be annoying special cases in our other algorithms, it's nice to have something in your back pocket that is simple and just always works. And so that's what this algorithm is. So let's just go look at the implementation, which is going to be extremely simple. It's worth stepping through it. Today, I'm not going to do what I did yesterday and walk you, march you through four different implementations of the same algorithm. I'm just going to show you one implementation. And I've intentionally included dead links for some of the other systems because you're going to get a chance to fill in some of those links during the problem sessions. Okay. So what is this algorithm doing? I've called my function p2 count naive. The first thing it's doing, and this is, there's similar things, expressions one can use in Oscar or in Sage. GP is a little different. We'll talk, I refer you to the, there's a getting started.md document and also a how to finite field document that will explain how to work with finite fields in all four systems. And GP is sort of the odd one out in this area. And that the other three systems all explicitly represent fields, rings as objects in their own right. Whereas in GP, GP really just represents elements of those structures and the element itself, but the elements itself knows what structure it lives in. And in some sense can stand in as a representative of that structure. So like sort of a non-intuitive way to get a finite, a random element of a finite field in GP is take any element of FQ and just do FF random of that element. And it's basically saying, go to the parent of this element and pick a random element. All right, but we won't be talking about GP today. So RXYZ is the parent of the polynomial that's coming in F. I called it RXYZ, because I'm expecting F to be a polynomial in three variables I'm thinking of as XYZ. You could even put in an assert that verifies that fact that somebody didn't pass in a polynomial from some ring you have no understanding of. FQ is the base ring of that polynomial ring. That's our finite, which we expect to be a finite field. You could also put in an assert to verify that fact. And now we're going to just compute the three terms in the sum. We're gonna first count all of the projected points with Z-coordinate one. And so the way this is being done is using a list comprehension. This is actually much more efficient, not just in magma, but in all of these systems. Any time you can express a computation using a list comprehension rather than a loop or worse a nested series of loops, you're better off to use a list comprehension. I think both pedagogically, it more naturally matches our mathematical thought. What I wrote on the slide when I had the set of XY such that law, but also from practical performance, this will be executed entirely sort of inside a low-level function. The algebra system whereas, depending on how it's implemented, the loop may have to involve more work to sort of execute each step of the loop one by one. Some of these systems are smart enough to try and avoid doing that when they can, especially Julia, but you're not guaranteed. Okay, so this list comprehension is the add plus is saying sum all the elements of this list comprehension and the elements are one for all of the A-Bs in FQ such that when I evaluate my polynomial which I've unfortunately called F here, I called it G on the other slide, so I'll change that later, that where the polynomial F evaluates to 0 when I plug in A, B, and 1. My A is my X naught and my B is my Y naught. And the ZZ here is telling, is just a way to ensure that I want to tell Magma that I want this to be a list of integers, not a list of finite field elements or something else. In this case, it's just a sort of a safety factor. In this case, it would probably work without that, but there are other going to be other situations where you really don't want it. When you write 1, it's sort of ambiguous. There's a bunch of rings and fields floating around. Which one do you mean? Do you mean the one in the polynomial ring? Do you mean the one in the finite field? No, I mean the one in Z. That's the one I want, because that's the one I'm summing to compute an integer. So the add plus operator means sum this list comprehension. That's going to get all the projective points with Z coordinate non-zero. Then the next line is checking all the ones with Y coordinate non-zero and Z coordinate zero. And then the last line is returning the count that we've computed so far, plus this expression, which is precisely capturing the Iverson notation. It's going to evaluate to one if F vanishes at one zero zero and zero otherwise. And so we could go ahead and run that. All that did was compile it. That just makes sure it has no syntax errors. But maybe I should run some sanity checks to make sure it's actually working. I've implemented a function here to just compute a random elliptic curve that'll be useful when we're testing things. And it's just sort of the most convenient way to do that is to just construct it from by pick a random J invariant and construct an elliptic curve with that J invariant. And I think all four systems have a method to do that. And then I'm just going to try for a bunch of small prime powers Q. I'm going to count points on my random elliptic curve and check that it actually agrees with the value that magma computes. And this pound E is actually calling the built-in magma function to count the number of FQ rational points on E. It says all as well. That's good. And I could even try a larger Q and time it. And it takes about a second when Q is about 1,000, which you shouldn't be super excited about. I guess that's not very fast on their hand. It's not horrible. It should be reassuring that if I said for all Qs less than, say, 49, we're just going to call this algorithm, you wouldn't object, because it would be reasonably quick. Okay. Any questions on this slide before I switch? Okay. All right. So that was our implementation of this naive pointing count that works, as I mentioned, for any plane projective curve. Now let's do something a bit less naive. Still going to be naive, but we're going to be less naive. If you think about it for a moment, when we plug in, we're always plugging in at least one constant into our polynomial and three variables because we want to count projective points. There's really only two variables in some sense. So when we're plugging in 1 for Z, and then we're trying all the possible combinations of x-naught and y-naught, but we could instead notice that whenever we plug in, say, a y-naught, what we're left with is a univariate polynomial, and we want to know the roots of that univariate polynomial over the fq-rational roots of that univariate polynomial, which is going to be at most degree three. So we're just trying to count does this cubic polynomial have zero, one, or three roots? That's really the only thing we want to know. And that can be done very efficiently with a completely deterministic algorithm by computing the degree of an appropriate GCD. And so we wanted to compute, so for any polynomial h here, h would be an instantiation of a Gx, y-naught, 1 for some y-naught. We can compute the number of fq-rational roots as the GCD of x to the q minus x with h. That's going to be some polynomial. Take its degree. The degree of that polynomial is precisely the number of roots. Why does that work? Well, x to the q minus a, x is really representing the action of Frobenius, and to be fq-rational means precisely to be fixed under the action of the q power of Frobenius, automorphism of your finite field. So that's why this works, and I'll give you notes in the references where you can learn lots more about this algorithm and generalizations of it. I'll just ask you if this fact isn't familiar to you, I'll just ask you to accept it for the moment. If there is one key point, if you just blindly type in what's written here, you will get a very, very slow algorithm, especially if q is cryptographically large. And the reason is that while x, it's easy to write down x to the q minus x, no matter how big x to the q is, the very next step, the very first step of that algorithm is going to compute x to the q minus x modulo h. And if you're doing that completely naively, that might be a very slow computation because that could result in intermediate steps that have polynomials with cryptographically many terms. What you want to do instead is compute x to the q minus q x modulo h before computing it manually yourself before computing the GCD by working in the quotient ring fq back at x mod h. So let me show that explicitly, make that a little more explicit by showing you the algorithm. So our first step of our algorithm is we're going to construct a quotient ring. We're going to take the polynomial ring over fq and quotient out by our polynomial h. And we should definitely think of this as a ring. It's not necessarily a field. We're not expecting that there's no reason for h to be irreducible.