G: %s
' % g_name(type, rank) # ---------------------------------------------------------------------- # Parsing input # ---------------------------------------------------------------------- _TOKEN = re.compile(r"^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?" r"(/[+-]?(\d+\.?\d*|\.\d+))?$") def parse_lambda(text): """Split a typed lambda into coordinates (as spherical.cgi testOne). Coordinates are separated by commas and/or spaces; brackets are ignored. """ text = re.sub(r"[()\[\]{}]", " ", text or "") tokens = [t for t in re.split(r"[,\s]+", text.strip()) if t] if not tokens: raise InputError("Please enter lambda: a list of numbers such as " ".75 1 1/2 0.") for t in tokens: if not _TOKEN.match(t): raise InputError("Could not read the coordinate " "“%s”: use numbers such as 2, " "-1.34 or 7/8." % html.escape(t)) if "/" in t: d = t.split("/")[1] if str_to_num(d) == 0: raise InputError("Can not have 0 as the denominator " "(“%s”)." % html.escape(t)) return tokens def processLine(type, line, coordinates): """One line of a file of lambdas (spherical.cgi processLine). As in the original, everything after # is ignored, as are all characters except digits, '.', '/', commas and spaces (so minus signs are dropped). Returns None for a line with no parameter. """ line = line.rstrip("\n") line = re.sub(r"#.*", "", line) line = re.sub(r"[,\s]+", ",", line) line = re.sub(r"[^0-9,\./]", "", line) if not line or line == "0": return None items = [t for t in line.split(",") if t != ""] if not items: return None for t in items: if "/" in t: num, d = (t.split("/") + [""])[:2] if str_to_num(d) == 0 or not re.match(r"^[0-9.]+$", num or "x"): raise InputError("Could not read the coordinate " "“%s”." % html.escape(t)) elif not re.match(r"^(\d+\.?\d*|\.\d+)$", t): raise InputError("Could not read the coordinate " "“%s”." % html.escape(t)) if not re.search("bourbaki|gap", coordinates or ""): # The Perl sorted the coordinates numerically ("3/2" counts as # 3) before testing. process() sorts them again, so this only # matters for ties; it is kept for fidelity. (For weight # coordinates the Perl sort permuted the coordinates, which # was a bug, so it is not done.) items = sorted(items, key=lambda t: -_perl_num(t)) return items def _perl_num(t): v = str_to_num(t) return float(v) # ---------------------------------------------------------------------- # One result (spherical.cgi outputOne) # ---------------------------------------------------------------------- def verdict(result): if result == 1: return 'UNITARY' return 'NOT UNITARY' def _msg(message): return esc(message).replace("\n", "lambda: (%s) %s
\n'
% (lam, verdict(r.result)))
p("Weight Coordinates (Bourbaki): [%s]
\n"
% ",".join(esc(x) for x in r.lambdaWeight))
p("Weight Coordinates (Gap): [[%s]]
\n"
% ",".join(esc(x) for x in r.lambdaWeightGap))
p("Orbit O: (%s)
\n" % ", ".join(esc(x) for x in r.O))
p("Centralizer Z:%s
\n" % centralizer)
p("Levi Factor M: %s
| lambda standard | lambda weight | '
'Orbit O | Centralizer Z | Levi Factor M | ' 'Unitary? |
|---|
%s
' % title) if not detail: out.append(LOW_HEAD) count = 0 for n, line in enumerate((text or "").splitlines(), 1): try: items = processLine(type, line, coordinates) except InputError as err: out.append(_line_error(n, str(err), detail)) continue if items is None: continue count += 1 try: r = _run(type, items, coordinates) except Exception as err: # show and go on out.append(_line_error(n, html.escape(str(err)), detail)) continue if detail and not (unitaryOnly and not r.result): out.append(showG(type, len(r.lambdaWeight))) out.append(outputOne(r, detail, unitaryOnly)) if not detail: out.append(LOW_FOOT) if count == 0: out.append('') return "".join(out) def _line_error(n, msg, detail): text = "Line %d: %s" % (n, msg) if detail: return '%s
' % text return 'Testing points from file ' '%s
' % (files[key], files[key])) if not detail: out.append(LOW_HEAD) for line in (text or "").splitlines(): items = processLine(type, line, "") if items is None: continue r = _run(type, items, "") out.append(outputOne(r, detail, unitaryOnly)) if not detail: out.append(LOW_FOOT) return "".join(out) MAX_UNIPOTENT_RANK = 16 def unipotentLambdas(type, rank): """The unipotent parameters for a type and rank, in the Perl order. One for each partition of n (A: n = rank+1; B: n = 2 rank, odd rows of even multiplicity; C: n = 2 rank + 1 and D: n = 2 rank, even rows of even multiplicity), coordinates 1,2,..,(r-1)/2 for an odd row r and 1/2,3/2,..,(r-1)/2 for an even row. """ parity = -1 if type == "A": n = rank + 1 elif type == "B": n = 2 * rank parity = 1 elif type == "C": n = 2 * rank + 1 parity = 0 elif type == "D": # The Perl tested "B" twice here, so type D never worked; the # table in its comments gives n = 2r and parity 0. n = 2 * rank parity = 0 parts = [] for x in partitions(n): d = {} for j in x: d[j] = d.get(j, 0) + 1 parts.append(d) if parity >= 0: good = [] for d in parts: ok = True for rowLength in d: if (rowLength & 1) != (parity & 1): continue if d[rowLength] & 1: ok = False break if ok: good.append(d) parts = good result = [] for d in reversed(parts): lam = [] for r in d: base = frac(1) if (r & 1) else frac(1 / 2) for i in range(0, r // 2): v = spherical.padd(base, i) lam.extend([v] * d[r]) if type == "A": lam.extend([spherical.psub(spherical.pneg(base), i)] * d[r]) if type == "A": lam.extend([frac(0)] * (rank + 1 - len(lam))) else: lam.extend([0] * (rank - len(lam))) result.append(lam) return result def unipotent(type, rank, detail=True, unitaryOnly=False): """All unipotent representations (Submit=unipotent).""" try: rank = int(str(rank).strip()) except ValueError: raise InputError("The rank must be a positive integer.") if rank < 1: raise InputError("The rank must be a positive integer.") if rank > MAX_UNIPOTENT_RANK: raise InputError("Please choose a rank of at most %d (the number " "of unipotent parameters grows quickly)." % MAX_UNIPOTENT_RANK) if type == "D" and rank < 2: raise InputError("For type D the rank must be at least 2.") out = ['Showing unitary points only
') out.append(diagram_details(type, rank, "")) if not detail: out.append(LOW_HEAD) for lam in unipotentLambdas(type, rank): r = _run(type, lam, "") out.append(outputOne(r, detail, unitaryOnly)) if not detail: out.append(LOW_FOOT) return "".join(out) # ---------------------------------------------------------------------- # Dynkin diagram and coordinates (Submit=diagram) # ---------------------------------------------------------------------- def _matrix_text(m): lines = [] rootsystem.showMatrix(m, lines) return "".join(lines) def diagram_html(type, rank, gap=""): """Dynkin diagram and the coordinate change matrices.""" out = ['Alternate Labelling (see " 'Root Systems for more ' "information)
") out.append("%s" % esc("".join( rootsystem.diagram(type, rank, gap)))) out.append("
This labelling is used in the fundamental weight " "coordinates.
") fw = fundamentalWeights(type, rank, gap) tsc = transpose(simpleCoRoots(type, rank, gap)) out.append("Fundamental Weights in standard coordinates:\nM=\n%s\n"
"M-1 (=transpose of simple coroots in standard "
"coordinates):\n%s(standard coordinates)*M-1="
"(weight coordinates)"
% (esc(_matrix_text(fw)), esc(_matrix_text(tsc))))
return "".join(out)
def diagram_details(type, rank, gap=""):
try:
body = diagram_html(type, rank, gap)
except Exception:
return ""
return ('For more detail see the ' 'Root System section.
' '%s' % (esc(type), rank, esc(text))) def run(action, **kw): """Entry point for the page: returns {'html': ...} or {'error': ...}.""" fn = {"testOne": testOne, "example": example, "file": testList, "fileExample": fileExample, "unipotent": unipotent, "roots": rootSystem, "rootsText": rootSystemText, "diagram": diagram_html}[action] try: return {"html": fn(**kw)} except InputError as err: return {"error": str(err)} except RecursionError: return {"error": "This computation is too large."} except (ValueError, ZeroDivisionError) as err: return {"error": html.escape(str(err))}