High School Calculator Computes Analytical Number Theory

Most of the time Desmos is used to draw polynomials to find roots. However, not many people know how far you can go. This article is absolutely useless from a scientific point of view, yet it is interesting to trace how limitations force creativity. My goal was to build a prime generating function f(1)=p1=2f(1) = p_1 = 2 without explicit if statements, conditional functions, and advanced functions like mod\text{mod} and gcd\text{gcd}.

What Is a Prime Number?

Quick recap for those who may not be familiar with the topic. A prime number is a number that has no divisors besides one and itself. 11 is excluded from the set of primes.

Is This an Integer?

My thought process was the following: to check whether a number is prime, we have to know if it has any divisors. If a,b,ab∈Za,b,\frac{a}{b} \in \mathbb{Z}, then bb is a divisor of aa. This method is based on knowing that the inputs and output are integers. So, let’s build it.

cos⁡(πx)=±1\cos(\pi x) = \pm 1 when xx is an integer; everywhere else it’s strictly between −1-1 and 11. Take the absolute value, and integers give exactly 11; everything else gives something strictly less than 11. Discard everything after the decimal point (or floor it) and non-integers collapse to 00.

Z(x)=⌊ ∣cos⁡(πx)∣ ⌋Z(x) = \left\lfloor \, |\cos(\pi x)| \, \right\rfloor
y=\left|\cos\left(\pi x\right)\right|
Z\left(x\right)=\operatorname{floor}\left(\left|\cos\left(\pi x\right)\right|\right)
y=Z\left(x\right)
\left(\left[-10,...,10\right],Z\left(\left[-10,...,10\right]\right)\right)
The wave, and the switch it collapses to. Drag and zoom it.

Z(x)Z(x) is an “is this an integer” detector, built entirely out of trigonometry and a floor function. That’s the whole trick.

Divisibility Without Modulo

d(x,y)=Z(y)⋅Z ⁣(xy)d(x,y) = Z(y) \cdot Z\!\left(\frac{x}{y}\right)

This says: yy is an integer, and x/yx/y is an integer. If both hold, yy divides xx cleanly. We just built a divisibility test without a single modulo operator. Interestingly, a third factor, Z(x)Z(x), is unnecessary. If yy and x/yx/y are both integers, x=y⋅(x/y)x = y \cdot (x/y) has to be an integer too.

How Many?

D(x)=∑n=2x−1d(x,n)D(x) = \sum_{n=2}^{x-1} d(x,n)

This counts divisors of xx strictly between 11 and xx. It is a specific design choice. I didn’t want to map 22 to 11 (its two divisors are the number itself and 11). D(x)=0D(x) = 0 exactly when xx has no divisors in that range, which for x≥2x \ge 2 is just the definition of prime. D(7)=0D(7)=0 (prime), D(9)=1D(9)=1, D(4)=1D(4)=1.

p(x)=⌊11+D(x)⌋p(x) = \left\lfloor \frac{1}{1 + D(x)} \right\rfloor

This is an old trick. 11=1\frac{1}{1}=1 when D(x)=0D(x)=0, so xx is prime. The D(x)D(x) range is [0,∞)[0,\infty); therefore 11+n<1\frac{1}{1+n}<1 if n>0n>0. Floor it and we get a boolean operator that outputs 11 if xx is prime and 00 if xx isn’t prime.

One real edge case worth knowing about: D(1)D(1) is an empty sum, so it’s 00 too. This means p(1)=1p(1)=1, falsely flagging 11 as prime. It never causes a problem in my case because everywhere pp actually gets used, the sum starts at n=2n=2, so p(1)p(1) is never called. But it’s a trap hidden in pp on its own.

Brute Force Without Memory

∑n=2kp(n)=π(k)\sum_{n=2}^{k}p(n)=\pi(k), the count of primes up to kk. Let’s define an intermediate function g(n,k)=n−π(k)g(n,k)=n-\pi(k). Say, n=3n=3, π(4)=2\pi(4)=2, π(5)=3\pi(5)=3. We can observe that the moment kk is pnp_n, g(n,k)g(n,k) becomes 00. Now take an absolute value to make sure it stays non-negative.

f(n,k)=⌊11+∣ n−π(k) ∣⌋f(n,k) = \left\lfloor \frac{1}{1 + \left|\, n - \pi(k) \,\right|} \right\rfloor

f(n,k)f(n,k) is the same trick again: 11 when π(pn)=n<π(pn+1)\pi(p_n)=n<\pi(p_{n+1}). This is bad. f(3,5)=f(3,6)=1f(3,5)=f(3,6)=1; we need to track the value of kk not when ff is 11 but when ff changes. This is a simple discrete derivative: Δn(k)=f(n,k)−f(n,k−1)\Delta_n(k)=f(n,k)-f(n,k-1). As soon as kk is pnp_n, f(n,k)=1f(n,k)=1, f(n,k−1)=0f(n,k-1)=0, so the difference is 11. The next kk will evaluate both functions to 11 and therefore the difference is 00, as we wanted.

Isolating pnp_n

Δn(k)\Delta_n(k) tracks both rise and fall. Therefore, when kk becomes pn+1p_{n+1}, the difference evaluates to −1-1. In order to track when the positive change occurs, we construct a simple function: 12−Δn(k)\frac{1}{2-\Delta_n(k)}; it evaluates to 11 when Δn(k)=1\Delta_n(k)=1 and 1/31/3 when Δn(k)=−1\Delta_n(k)=-1. The last thing is to floor the expression so it equals 11 exactly once when k=pnk=p_n.

pn=∑k=2Nk⌊12−Δn(k)⌋p_n = \sum_{k=2}^{N} k \left\lfloor \frac{1}{2 - \Delta_n(k)} \right\rfloor

Sum from k=2k=2 up to some NN. Once the fraction is 11, we multiply it by kk, which is precisely pnp_n. Voila, we got a prime generating function from the simple idea of an “is this an integer” detector.

When to Stop

The interesting part is the upper bound NN, which has to be sufficiently large to guarantee pnp_n actually falls inside the range being summed. N=⌊n(ln⁡n+ln⁡ln⁡n)⌋N = \left\lfloor n(\ln n + \ln \ln n) \right\rfloor is the real theorem (Rosser–Schoenfeld) but is only proven to exceed pnp_n for n≥6n \ge 6. N=⌊n(ln⁡(n+1)+2)⌋N = \left\lfloor n(\ln(n+1) + 2) \right\rfloor fixes the small cases but overcounts as nn goes further from 11.

The Actual Point

The same move gets reused at every layer here: build a test that’s exactly 11 at the one case you care about and 00 everywhere else, then let a sum or a gate pick that one case out. Integer detector, divisibility test, divisor sum, prime function. None of the individual steps are hard, and each one is just the last idea reused. Give yourself % and a for loop and the whole chain is a few lines. Without them, you are rebuilding most of elementary number theory from first principles.