def legendre_ch(d): """ Return the mod abs(disc Q[√d]) Legendre character. This is the character ch with modulus D=abs(disc Q[√d]) such that for any odd prime p, ch(p)=(d/p) (i.e. ch(p)=0 if p|d ch(p)=1 if d is a square mod p ch(p)=-1 if d is not a square mod p """ if not squarefree(d): raise ValueError('%d is not square free' % (d, )) abs_disc = abs(discriminant(d)) vals = [0] * abs_disc for k in range(abs_disc): if gcd(abs_disc, k) == 1: n = k while True: if isprime(n) and n % 2 == 1: vals[k] = legendre(d, n) break n += abs_disc return lambda a: vals[a % abs_disc]
def kappa(m): r""" :param m: a square-free integer :return: kappa = lim_{t->infty} #{J | ||J|| < t}/t for Q[√m]. We have that :math: abs(L(chi, 1)) = h*kappa where h is the class number of Q[√m], chi is the 'legendre character' and L is the associated Dirichlet L function. """ if not squarefree(m): raise ValueError('%d is not square free.' % (m, )) if m == 1: return 1 disc = discriminant(m) if m > 0: u = fundamental_unit(m).real() return 2 * np.log(u) / np.sqrt(abs(disc)) else: if m == -1: w = 2 elif m == -3: w = 3 else: w = 1 return np.pi / (w * np.sqrt(abs(disc)))
def ideal_class_number(dd): r""" Return the ideal class number of Q[√d], for squarefree integer dd. """ if not squarefree(dd): raise ValueError("%d is not squarefree." % (dd, )) if dd == 1: return 1 abs_disc = abs(discriminant(dd)) ch = legendre_ch(dd) z = np.exp(2.0j * np.pi / abs_disc) rho = np.abs(1.0 / np.sqrt(abs(abs_disc)) * sum( ch(k) * np.log(1 - z**(-k)) for k in range(1, abs_disc) if gcd(k, abs_disc) == 1)) k = kappa(dd) # magically rho should be an integral multiple of kappa. # make sure this is true before we return the rounded answer. assert (abs(round(rho / k) - rho / k) < 0.001) return int(round(rho / k))
def partial_jacobi_sum(modulus, ub): """ add up (j/modulus) for j in [0..ub]. use a sieve approach (though it doesn't seem to be buying us much over a good jacobi symbol implementation. """ # modulus must be odd and squarefree assert squarefree(modulus) and (modulus % 2) m = modulus J = np.ones((ub+1, ), dtype=np.int8) # this will only store 0, +1, -1's P = np.ones((ub+1, ), dtype=np.int8) P[0] = 0 P[1] = 0 J[0] = 0 for p in range(2, ub+1): if P[p]: # p is prime P[p*p::p] = 0 # strike higher multiples if m % p: # p does not divide m poverm = jacobi2(p, m) if poverm == 1: pass else: pk = p while pk <= ub: J[pk::pk] *= -1 pk *= p else: J[p::p] = 0 return J
def test_ideal_class_number(): for d in range(1, 500): if squarefree(d): _ = ideal_class_number(d)
def test_squarefree(self): self.assertEqual(True, squarefree(1), '1 is square free') self.assertEqual(True, squarefree(-1), '-1 is square free') self.assertEqual(False, squarefree(4), '4 is not square free') self.assertEqual(False, squarefree(18), '18 is not square free')