#! /usr/bin/python3 # Last edited on 2026-02-22 19:54:44 by stolfi # To be included in python progs. import sys, os, re from sys import stderr as err from math import inf, sqrt, nan, isnan, isfinite from error_funcs import arg_error, file_line_error, prog_error def compute_stats(vals, nact = None): # Compute average and deviation of the list of numbers {vals}. If # {nact} is provided, uses {nact} as the number of entries instead of # {len(vals)}. # Returns # # {vnum} number of entries used in the averages ({nact} if given, else {len(vals)}). # {vtot} sum of all entries. # {vmin} smallest of all entries. # {vsin} second-smallest of all entries. # {vmax} largest of all entries. # {vsax} second-largest of all entries. # {vavg} average of all entries ({vtot/vnum}). # {vdev} standard deviation of all entries. # # Some of these values may be {nan}. vnum = len(vals) if nact == None else nact vmin = nan; vmax = nan; vavg = nan; vdev = nan vsin = nan; vsax = nan if vnum >= 1 and len(vals) >= 1: vtot = 0; vmin = +inf; vsin = +inf vmax = -inf; vsax = -inf for v in vals: if v < vmin: vsin = vmin; vmin = v if v > vmax: vsax = vmax; vmax = v vtot += v vavg = vtot/vnum if vnum >= 2 and len(vals) >= 2: sum_ds2 = 0; for v in vals: ds = v - vavg sum_ds2 += ds*ds vdev = sqrt(sum_ds2/(vnum-1)) return vnum, vtot, vmin, vsin, vmax, vsax, vavg, vdev # ---------------------------------------------------------------------- def print_stats(title, vnum, vtot, vmin, vsin, vmax, vsax, vavg, vdev): # Prints the given stats to {stderr}. # Ignores any that are {nan} or {None}. err.write("::: statistics of %s :::\n" % title) err.write(f"{vnum:7.2f} values assumed\n") if vtot != None and isfinite(vtot): err.write(f"{vtot:7d} total\n") if vmin != None and isfinite(vmin): err.write(f"{vmin:7d} smallest\n") if vsin != None and isfinite(vsin): err.write(f"{vsin:7d} second smallest\n") if vmax != None and isfinite(vmax): err.write(f"{vmax:7d} largest\n") if vsax != None and isfinite(vsax): err.write(f"{vsax:7d} second largest\n") if vavg != None and isfinite(vavg): err.write(f"{vavg:7.2f} average\n") if vdev != None and isfinite(vdev): err.write(f"{vdev:7.2f} deviation\n") err.write(":::::::::::::::::::::::::::::::::::::::::::\n") return # ---------------------------------------------------------------------- def compute_and_print_stats(title, vals, nact = None): # Same as {compute_stats}, but prints the stats to {stderr} # before returning them. vnum, vtot, vmin, vsin, vmax, vsax, vavg, vdev = compute_stats(vals, nact) print_stats(title, vnum, vtot, vmin, vsin, vmax, vsax, vavg, vdev) return vnum, vtot, vmin, vsin, vmax, vsax, vavg, vdev # ---------------------------------------------------------------------- def enc_from_unit(unit): # Returns the encoding {enc} of the ".ivp" file required to extract # or count unts of type {unit}. enc = None if unit == "ch": # Chinese characters: enc = "chu" elif unit == "ps": # Isolated pinyin syllables: enc = "pys" elif unit == "ec" or unit == "wc" or unit == "wp": # EVA characters or words: enc = "eva" else: arg_error(f"invalid {unit = !r}") return enc # ---------------------------------------------------------------------- def name_for_tex_macro(name): # Converts an string {name} to a camel-case string # suitable to include in the name of TeX macros. E.g., # "starps-fu-wc3b-DAIIN.ivp" -> "StarpsFuWcCbDAIINIvp". # The {name} should contain only letters, digits, or [-,.]. # Digits are replaced by uppercase letters [A-J]. assert not re.match(r"[^-,.a-zA-Z0-9]", name), "invalid char" # Map digits to letters A-J: table = str.maketrans('0123456789', 'ABCDEFGHIJ') name = name.translate(table) # Split at '-' and '.', capitalize each segment: els = re.split(r"[-,.]", name) txels = [] for el in els: txel = el[0].upper() + el[1] txels.append(txel) txname = "".join(txels) return txname # ----------------------------------------------------------------------