Repository: LMFDB/lmfdb

Issue:
Dirichlet pages do not list their modulus in their properties or gaga box
The information is contained in the label which is displayed as $\chi_m (n, \cdot)$, but to make things easier for newcomers it would be better if there was an explicit field in the basic properties section and the properties box giving the modulus of the character.
On pages like Character/Dirichlet/5/1

We already list this information in the search results as a separate column so this seems to be a (minor) oversight than a deliberate choice not to include duplicate info.


Discussion:
Agreed, I would put the modulus at the top right above the conductor.
Even more noticeably missing, there is no portrait!
Alex Best suggests using a pictorial representation of the Gauss sum (just plotting the roots of unity is a little boring).

Resolving patch:
diff --git a/lmfdb/artin_representations/math_classes.py b/lmfdb/artin_representations/math_classes.py
--- a/lmfdb/artin_representations/math_classes.py
+++ b/lmfdb/artin_representations/math_classes.py
@@ -3,7 +3,7 @@
 from six import string_types
 from lmfdb import db
 from lmfdb.utils import url_for, pol_to_html
-from lmfdb.utils.utilities import web_latex, coeff_to_poly
+from lmfdb.utils.utilities import web_latex, coeff_to_poly, letters2num, num2letters
 from sage.all import PolynomialRing, QQ, ComplexField, exp, pi, Integer, valuation, CyclotomicField, RealField, log, I, factor, crt, euler_phi, primitive_root, mod, next_prime, PowerSeriesRing
 from lmfdb.galois_groups.transitive_group import (
     group_display_knowl, group_display_short, small_group_display_knowl)
@@ -74,20 +74,6 @@ def process_polynomial_over_algebraic_integer(seq, field, root_of_unity):
     PP = PolynomialRing(field, "x")
     return PP([process_algebraic_integer(x, root_of_unity) for x in seq])
 
-# Conversion from numbers to letters and back
-def letters2num(s):
-    letters = [ord(z)-96 for z in list(s)]
-    ssum = 0
-    for j in range(len(letters)):
-        ssum = ssum*26+letters[j]
-    return ssum
-
-def num2letters(n):
-    if n <= 26:
-        return chr(96+n)
-    else:
-        return num2letters(int((n-1)/26))+chr(97+(n-1)%26)
-
 class ArtinRepresentation(object):
     def __init__(self, *x, **data_dict):
         if len(x) == 0:
diff --git a/lmfdb/characters/web_character.py b/lmfdb/characters/web_character.py
--- a/lmfdb/characters/web_character.py
+++ b/lmfdb/characters/web_character.py
@@ -66,6 +66,7 @@
 
 from lmfdb import db
 from lmfdb.utils import web_latex
+from lmfdb.utils.utilities import num2letters
 from lmfdb.logger import make_logger
 from lmfdb.nfutils.psort import ideal_label, ideal_from_label
 from lmfdb.number_fields.web_number_field import WebNumberField
@@ -767,10 +768,13 @@ def vflabel(self):
 
     @lazy_attribute
     def properties(self):
-        f = [("Conductor", [self.conductor]),
-                ("Order", [self.order]),
-                ("Real", [self.isreal]),
-                ("Primitive", [self.isprimitive])]
+        f = [
+            ("Modulus", [self.modulus]),
+            ("Conductor", [self.conductor]),
+            ("Order", [self.order]),
+            ("Real", [self.isreal]),
+            ("Primitive", [self.isprimitive])
+        ]
         if self.isminimal:
             f.append(("Minimal", [self.isminimal]))
         if self.parity:
@@ -1178,7 +1182,7 @@ def friends(self):
         myrep = db.artin_reps.lucky({'Dets': {'$contains': label}})
         if not myrep is None:
             j=myrep['Dets'].index(label)
-            artlabel = myrep['Baselabel']+'c'+str(j+1)
+            artlabel = myrep['Baselabel']+'.'+num2letters(j+1)
             friendlist.append(('Artin representation '+artlabel,
                 url_for('artin_representations.render_artin_representation_webpage', label=artlabel)))
 
diff --git a/lmfdb/utils/utilities.py b/lmfdb/utils/utilities.py
--- a/lmfdb/utils/utilities.py
+++ b/lmfdb/utils/utilities.py
@@ -291,6 +291,25 @@ def str_to_CBF(s):
             res  +=  sign * CBF(b)* CBF.gens()[0]
         return res
 
+# Conversion from numbers to letters and back
+def letters2num(s):
+    r"""
+    Convert a string into a number
+    """
+    letters = [ord(z)-96 for z in list(s)]
+    ssum = 0
+    for j in range(len(letters)):
+        ssum = ssum*26+letters[j]
+    return ssum
+
+def num2letters(n):
+    r"""
+    Convert a number into a string of letters
+    """
+    if n <= 26:
+        return chr(96+n)
+    else:
+        return num2letters(int((n-1)/26))+chr(97+(n-1)%26)
 
 
 def to_dict(args, exclude = [], **kwds):


Test patch: