def find_octad_permutation_odd(v, result, verbose=0): """ Find a suitable permutation for an octad. Similar to function ``find_octad_permutation`` in module ``mmgroup.dev.generators.gen_leech_reduce_n``. Here ``v, o, c`` are as in that function; but the scalar product of ``o`` and ``c`` must be 1. Apart from that operation is as in function ``find_octad_permutation``. We compute a permutation that maps octad ``o`` to the standard octad (0,1,2,3,4,5,6,7). If the cocode part ``c`` of ``v`` is not a suboctad of octad ``o`` then we map (one shortest representative of) ``c`` into the set (0,1,2,3,...7,8). """ coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) src = mat24.vect_to_list(vect, 5) if mat24.cocode_weight(coc) == 4: sextet = mat24.cocode_to_sextet(coc) for i in range(0, 24, 4): syn = (1 << sextet[i]) | (1 << sextet[i + 1]) syn |= (1 << sextet[i + 2]) | (1 << sextet[i + 3]) special = syn & vect if special & (special - 1): break else: syn = mat24.cocode_syndrome(coc, 24) src.append(mat24.lsbit24(syn & ~vect)) return apply_perm(v, src, OCTAD_PLUS, 6, result, verbose)
def random_dodecad(): while True: gc = random.randint(0, 0xfff) if mat24.gcode_weight(gc) == 3: v = mat24.gcode_to_vect(gc) w, l = mat24.vect_to_bit_list(v) assert w == 12 l = l[:12] random.shuffle(l) return l
def __pow__(self, other): if isinstance(other, Integral): return PLoop(mat24.pow_ploop(self.value, other & 3)) elif isinstance(other, PLoop): comm = mat24.ploop_comm(self.value, other.value) << 12 return PLoop(self.value ^ comm) elif isinstance(other, Parity): if mat24.gcode_weight(self.value) & 1: raise ValueError("Parker Loop element has order > 2") return PLoop(mat24.pow_ploop(self.value, other.value)) else: return NotImplemented
def __truediv__(self, other): if isinstance(other, Integral): other = abs(other) if other == 1: return self elif other == 2: return Parity(0) elif other == 4: return Parity(mat24.gcode_weight(self.value) & 1) else: raise ValueError(ERR_DIV4 % type(self)) else: return NotImplemented
def split_octad(self): """Split Parker loop element ``a`` into central element and octad :return: a triple ``(es, eo, o)`` with ``a = (-1)**e1 * PLoopOmega**eo * o``. Here ``o`` is either the neutral Parker loop element ``PLoopOne`` or Parker loop element corresponding to a positive octad. ``es`` and ``eo`` are ``0`` or ``1``. An *octad* is a Golay code word (of type |GCode|) of weight ``8``. :raise: * Raise ValueError if this is not possible. """ v = self.value e1, eo, w = (v >> 12) & 1, 0, mat24.gcode_weight(v) if mat24.gcode_weight(v) > 3: v, eo, w = v ^ 0x800, 1, 6 - w if w <= 2: return e1, eo, PLoop(v & 0xfff) raise ValueError("Cannot convert Golay code word to octad")
def split_octad(self): """Split an octad from the Golay code word ``g``. :return: A triple ``(0, eo, o)`` with ``g = Omega * eo + o``. Here ``Omega`` is the Golay code word containing one bits only, ``eo`` is ``0`` or ``1``, and ``v`` is a Golay code word of type |GCode| which is either the zero code word or an octad, i.e. a code word of weight ``8``. This method is for compatibility with the corresponding method in class |PLoop|. :raise: * ValueError if this is not possible. """ v = self.value eo, w = 0, mat24.gcode_weight(v) if mat24.gcode_weight(v) > 3: v, eo, w = v ^ 0x800, 1, 6 - w if w <= 2: return 0, eo, GCode(v & 0xfff) raise ValueError("Cannot convert Golay code word to octad")
def __truediv__(self, other): if isinstance(other, PLoop): return PLoop( mat24.mul_ploop(self.value, mat24.pow_ploop(other.value, 3))) elif isinstance(other, Integral): if abs(other) == 1: return PLoop(self.value ^ ((other & 2) << 11)) elif abs(other) == 2: return Parity(0) elif abs(other) == 4: return Parity(mat24.gcode_weight(self.value) & 1) else: raise TypeError(ERR_DIV4 % type(self)) else: return NotImplemented
def find_octad_permutation(v, result, verbose=0): coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) src = mat24.vect_to_list(vect, 5) syn = mat24.cocode_syndrome(coc, src[0]) & ~vect if syn: v5 = (1 << src[0]) | (1 << src[1]) | (1 << src[2]) v5 |= syn special = mat24.syndrome(v5, 24) src = src[:3] src.append(mat24.lsbit24(special & vect)) src.append(mat24.lsbit24(vect & ~(special | v5))) src.append(mat24.lsbit24(syn)) syn &= ~(1 << src[-1]) src.append(mat24.lsbit24(syn)) return apply_perm(v, src, OCTAD, len(src), result, verbose)
def leech2_start_type4(v): """Return subtype of a Leech lattice frame ``v`` used for reduction The function returns the subtype of a vector ``v`` of type 4 in the Leech lattice modulo 2. Parameter ``v2`` must be in Leech lattice encoding. The function returns the subtype of ``v`` that will be used for reduction in function ``gen_leech2_reduce_type4``. In that function we take care of the special case that ``v + v0`` is of type 2 for a specific short vector ``v0``. A simpler (but slower) implementation of thhis function is: If ``v ^ v0`` is of type 2 the return the subtype of ``v ^ v0``. Otherwise return the subtype of ``v``. The function returns 0 if ``v`` is equal to ``Omega`` and a negative value if ``v`` has not type 4. This is a refernece implementation for function ``gen_leech2_start_type4()`` in file ``gen_leech.c``. """ if v & 0x7ff800 == 0: # Then v or v + Omega is an even cocode element. # Return 0 if v == Omega and -1 if v == 0. if v & 0x7fffff == 0: return 0 if v & 0x800000 else -1 # Let w be the cocode weight. Return -2 if w == 2. if mat24.cocode_weight(v) != 4: return -2 # Here v has type 4. Let v23 be the standard type-2 vector. # Return 0x20 if v ^ v23 has type 2 and 0x40 otherwise. return 0x20 if mat24.cocode_weight(v ^ 0x200) == 2 else 0x40 if mat24.scalar_prod(v >> 12, v): # Then v has type 3 and we return -3 return -3 if v & 0x800: # Then the cocode word 'coc' of v is odd. coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff syn = mat24.cocode_syndrome(coc) # If 'coc' has weight 1 then v is of type 2 and we return -2. if (syn & (syn - 1)) == 0: return -2 # Here v is of type 4. # Return 0x21 if v ^ v23 is of type 2 and 0x43 otherwise. if (syn & 0xc) == 0xc and (v & 0x200000) == 0: return 0x21 return 0x43 # Let w be the weight of Golay code part divided by 4 w = mat24.gcode_weight(v >> 12) if w == 3: # Then the Golay code part of v is a docecad and we return 0x46. return 0x46 # Here the Golay code part of v is a (possibly complemented) octad. # Add Omega to v if Golay part is a complemented octad. v ^= (w & 4) << 21 # Put w = 1 if that part is an octad and w = 0 otherwise. w = (w >> 1) & 1 # Let 'octad' be the octad in the Golay code word in vector rep. octad = mat24.gcode_to_vect(v >> 12) coc = v ^ mat24.ploop_theta(v >> 12) # cocode element of v # Return -2 if v is of type 2. sub = suboctad_type(octad, w, coc) if sub == 0: return -2 # Return 0x22 if v ^ v23 is shsort if suboctad_type(octad, w, coc ^ 0x200) == 0: return 0x22 # Otherwise return the subtype of v return 0x44 if sub & 2 else 0x42
def reduce_type4_std(v, verbose=0): r"""Map type-4 vector in Leech lattice to standard vector This is (almost) a python implementation of the C function ``gen_leech2_reduce_type4`` in file ``gen_leech.c``. Let ``v \in \Lambda / 2 \Lambda`` of type 4 be given by parameter ``v`` in Leech lattice encoding. Let ``Omega`` be the type- vector in the Leech lattice corresponding to the standard coordinate frame in the Leech lattice. Then the function constructs a ``g \in G_{x0}`` that maps ``v`` to ``Omega``. The element ``g`` is returned as a word in the generators of ``G_{x0}`` of length ``n \leq 6``. Each atom of the word ``g`` is encoded as defined in the header file ``mmgroup_generators.h``. The function stores ``g`` as a word of generators in the array ``pg_out`` and returns the length ``n`` of that word. It returns a negative number in case of failure, e.g. if ``v`` is not of type 4. We remark that the C function ``gen_leech2_reduce_type4`` treats certain type-4 vectors ``v`` in a special way as indicated in function ``reduce_type4``. """ if verbose: print("Transforming type-4 vector %s to Omega" % hex(v & 0x1ffffff)) vtype = gen_leech2_subtype(v) result = [] for _i in range(5): coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff if verbose: vt = gen_leech2_subtype(v) coc_anchor = 0 if vt in [0x42, 0x44]: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) coc_anchor = mat24.lsbit24(vect) coc_syn = Cocode(coc).syndrome_list(coc_anchor) gcode = mat24.gcode_to_vect(v >> 12) print("Round %d, v = %s, subtype %s, gcode %s, cocode %s" % (_i, hex(v & 0xffffff), hex(vt), hex(gcode), coc_syn)) assert vtype == gen_leech2_subtype(v) if vtype == 0x48: if verbose: res = list(map(hex, result)) print("Transformation is\n", res) return np.array(result, dtype=np.uint32) elif vtype == 0x40: if v & 0x7ffbff: syn = mat24.cocode_syndrome(coc, 0) src = mat24.vect_to_list(syn, 4) v = apply_perm(v, src, LSTD, 4, result, verbose) #print("after type 40", hex(v), Cocode(v).syndrome(0)) exp = 2 - ((v >> 23) & 1) vtype = 0x48 elif vtype in [0x42, 0x44]: exp = xi_reduce_octad(v) if exp < 0: v = find_octad_permutation(v, result, verbose) exp = xi_reduce_octad(v) assert exp >= 0 vtype = 0x40 elif vtype == 0x46: exp = xi_reduce_dodecad(v, verbose) if exp < 0: vect = mat24.gcode_to_vect(v >> 12) src = mat24.vect_to_list(vect, 4) v = apply_perm(v, src, LSTD, len(src), result, verbose) exp = xi_reduce_dodecad(v, verbose) assert exp >= 0 vtype = 0x44 elif vtype == 0x43: exp = xi_reduce_odd_type4(v, verbose) if exp < 0: vect = mat24.gcode_to_vect(v >> 12) syn = mat24.cocode_syndrome(coc, 24) src = mat24.vect_to_list(syn, 3) #print("coc list", src) v = apply_perm(v, src, LSTD[1:], len(src), result, verbose) exp = xi_reduce_odd_type4(v, verbose) assert exp > 0 vtype = 0x42 + ((exp & 0x100) >> 7) exp &= 3 else: raise ValueError("WTF") if exp: exp = 0xE0000003 - exp v_old = v v = gen_leech2_op_atom(v, exp) assert v & 0xfe000000 == 0, (hex(v_old), hex(exp), hex(v)) result.append(exp) raise ValueError("WTF1")
def reduce_type2_ortho(v, verbose=0): r"""Map (orthgonal) short vector in Leech lattice to standard vector This is a python implementation of the C function ``gen_leech2_reduce_type2_ortho`` in file ``gen_leech.c``. Let ``v \in \Lambda / 2 \Lambda`` of type 2 be given by parameter ``v`` in Leech lattice encoding. In the real Leech lattice, (the origin of) the vector ``v`` must be orthogonal to the standard short vector ``beta``. Here ``beta`` is the short vector in the Leech lattice propotional to ``e_2 - e_3``, where ``e_i`` is the ``i``-th basis vector of ``\{0,1\}^{24}``. Let ``beta'`` be the short vector in the Leech lattice propotional to ``e_2 + e_3``. Then the function constructs a ``g \in G_{x0}`` that maps ``v`` to ``beta'`` and fixes ``beta``. The element ``g`` is returned as a word in the generators of ``G_{x0}`` of length ``n \leq 6``. Each atom of the word ``g`` is encoded as defined in the header file ``mmgroup_generators.h``. The function stores ``g`` as a word of generators in the array ``pg_out`` and returns the length ``n`` of that word. It returns a negative number in case of failure, e.g. if ``v`` is not of type 2, or not orthogonal to ``beta'`` in the real Leech lattice. """ vtype = gen_leech2_subtype(v) if (vtype >> 4) != 2: raise ValueError("Vector is not short") if gen_leech2_type(v ^ 0x200) != 4: raise ValueError("Vector not orthogonal to standard vector") result = [] for _i in range(4): if verbose: coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff vt = gen_leech2_subtype(v) coc_anchor = 0 if vt in [0x22]: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) coc_anchor = mat24.lsbit24(vect) coc_syn = Cocode(coc).syndrome_list(coc_anchor) gcode = mat24.gcode_to_vect(v >> 12) print("Round %d, v = %s, subtype %s, gcode %s, cocode %s" % (_i, hex(v & 0xffffff), hex(vt), hex(gcode), coc_syn)) assert vtype == gen_leech2_subtype(v) if vtype == 0x21: exp = xi_reduce_odd_type2(v) vtype = 0x22 elif vtype == 0x22: exp = xi_reduce_octad(v) if exp < 0: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) if vect & 0x0c: vect &= ~0x0c src = mat24.vect_to_list(vect, 2) + [2, 3] dest = [0, 1, 2, 3] else: src = [2, 3] + mat24.vect_to_list(vect, 3) v5 = (1 << src[2]) | (1 << src[3]) | (1 << src[4]) v5 |= 0x0c special = mat24.syndrome(v5, 24) src.append(mat24.lsbit24(special & vect)) dest = [2, 3, 4, 5, 6, 7] v = apply_perm(v, src, dest, len(src), result, verbose) exp = xi_reduce_octad(v) assert exp >= 0 vtype = 0x20 elif vtype == 0x20: if ((v & 0xffffff) == 0x800200): return np.array(result, dtype=np.uint32) syn = (mat24.cocode_syndrome(v, 0)) & ~0xc if syn and syn != 3: src = mat24.vect_to_list(syn, 2) + [2, 3] v = apply_perm(v, src, [0, 1, 2, 3], 4, result, verbose) exp = 2 - ((v >> 23) & 1) else: raise ValueError("WTF") if exp: exp = 0xE0000003 - exp v = gen_leech2_op_atom(v, exp) result.append(exp) raise ValueError("WTF1")
def reduce_type2(v, verbose=1): r"""Map (orthgonal) short vector in Leech lattice to standard vector This is a python implementation of the C function ``gen_leech2_reduce_type2`` in file ``gen_leech.c``. Let ``v \in \Lambda / 2 \Lambda`` of type 2 be given by parameter ``v`` in Leech lattice encoding. Let ``beta`` be the short vector in the Leech lattice propotional to ``e_2 - e_3``, where ``e_i`` is the ``i``-th basis vector of ``\{0,1\}^{24}``. Then the function constructs a ``g \in G_{x0}`` that maps ``v`` to ``beta``. The element ``g`` is returned as a word in the generators of ``G_{x0}`` of length ``n \leq 6``. Each atom of the word ``g`` is encoded as defined in the header file ``mmgroup_generators.h``. The function stores ``g`` as a word of generators in the array ``pg_out`` and returns the length ``n`` of that word. It returns a negative number in case of failure, e.g. if ``v`` is not of type 2. """ vtype = gen_leech2_subtype(v) if (vtype >> 4) != 2: raise ValueError("Vector is not short") result = [] for _i in range(4): if verbose: coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff vt = gen_leech2_subtype(v) coc_anchor = 0 if vt in [0x22]: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) coc_anchor = mat24.lsbit24(vect) coc_syn = Cocode(coc).syndrome_list(coc_anchor) gcode = mat24.gcode_to_vect(v >> 12) print("Round %d, v = %s, subtype %s, gcode %s, cocode %s" % (_i, hex(v & 0xffffff), hex(vt), hex(gcode), coc_syn)) assert vtype == gen_leech2_subtype(v) if vtype == 0x21: exp = xi_reduce_odd_type2(v) vtype = 0x22 elif vtype == 0x22: exp = xi_reduce_octad(v) if exp < 0: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) src = mat24.vect_to_list(vect, 4) dest = [0, 1, 2, 3] v = apply_perm(v, src, dest, 4, result, verbose) exp = xi_reduce_octad(v) assert exp >= 0 vtype = 0x20 elif vtype == 0x20: exp = 0 # map v to stadard cocode word [2,3] if v & 0x7fffff != 0x200: syn = (mat24.cocode_syndrome(v, 0)) src = mat24.vect_to_list(syn, 2) v = apply_perm(v, src, [2, 3], 2, result, verbose) # correct v2 if v2 is the cocode word [2,3] + Omega if v & 0x800000: atom = 0xC0000200 # operation y_d such that d has odd scalar # product with cocode word [2,3] v = gen_leech2_op_atom(v, atom) result.append(atom) assert v & 0xffffff == 0x200 return np.array(result, dtype=np.uint32) else: raise ValueError("WTF") if exp: exp = 0xE0000003 - exp v = gen_leech2_op_atom(v, exp) result.append(exp) raise ValueError("WTF1")
def test_Parker_loop(): print("\nTesting operation on Parker loop") for i in range(200): # Test power map, inveriosn, sign, theta, and conversion to GCode n1 = randint(0, 0x1fff) p1 = PLoop(n1) assert p1.ord == n1 == p1.gcode + 0x1000 * (1 - p1.sign) / 2 if (i < 2): print("Testing Parker loop element", p1, ", GCode = ", GCode(p1)) assert len(p1) == mat24.gcode_weight(n1) << 2 assert p1.theta() == Cocode(mat24.ploop_theta(n1)) assert p1 / 4 == p1.theta(p1) == Parity(mat24.ploop_cocycle(n1, n1)) assert p1**2 == PLoopZ(p1 / 4) == (-PLoopZ())**(p1 / 4) assert p1**(-5) == PLoopZ(p1 / 4) * p1 == (-1)**(p1 / 4) * p1 == 1 / p1 assert (1 / p1).ord ^ n1 == (mat24.gcode_weight(n1) & 1) << 12 assert -1 / p1 == -p1**3 == -(1 / p1) assert p1 * (-1 / p1) == PLoopZ(1) == -PLoopZ() assert abs(p1).ord == GCode(p1).ord == p1.ord & 0xfff assert p1 != GCode(p1) assert +p1 == 1 * p1 == p1 * 1 == p1 assert p1 != -p1 and p1 != ~p1 assert (-p1).ord == p1.ord ^ 0x1000 assert (~p1).ord == p1.ord ^ 0x800 s, o, p1_pos = p1.split() assert s == (p1.ord >> 12) & 1 assert o == (p1.ord >> 11) & 1 assert p1.sign == (-1)**s assert p1_pos.ord == p1.ord & 0x7ff assert PLoopZ(s, o) * p1_pos == p1 assert PLoopZ(0, o) * p1_pos == abs(p1) assert PLoopZ(s + 1, o) * p1_pos == -p1 assert -p1 == -1 * p1 == p1 * -1 == p1 / -1 == -1 / p1**-5 assert PLoopZ(s, 1 + o) * p1_pos == ~p1 assert PLoopZ(1 + s, 1 + o) * p1_pos == ~-p1 == -~p1 == ~p1 / -1 assert 2 * p1 == GCode(0) == -2 * GCode(p1) assert -13 * p1 == GCode(p1) == 7 * GCode(p1) if len(p1) & 7 == 0: assert p1**Parity(1) == p1 assert p1**Parity(0) == PLoop(0) else: with pytest.raises(ValueError): p1**Parity(randint(0, 1)) assert p1.bit_list == mat24.gcode_to_bit_list(p1.value & 0xfff) assert p1.bit_list == GcVector(p1).bit_list assert p1.bit_list == GCode(p1).bit_list assert PLoop(GcVector(p1) + 0) == PLoop(GCode(p1) + 0) == abs(p1) assert p1 + 0 == 0 + p1 == GCode(p1) + 0 == 0 + GCode(p1) assert Cocode(GcVector(p1)) == Cocode(0) assert p1 / 2 == Parity(0) # Test Parker loop multiplication and commutator n2 = randint(0, 0x1fff) p2 = PLoop(n2) coc = Cocode(mat24.ploop_cap(n1, n2)) if (i < 1): print("Intersection with", p2, "is", coc) p2inv = p2**-1 assert p1 * p2 == PLoop(mat24.mul_ploop(p1.ord, p2.ord)) assert p1 / p2 == p1 * p2**(-1) assert p1 + p2 == p1 - p2 == GCode(p1 * p2) == GCode(n1 ^ n2) assert (p1 * p2) / (p2 * p1) == PLoopZ((p1 & p2) / 2) assert p1 & p2 == coc assert p1.theta() == Cocode(mat24.ploop_theta(p1.ord)) assert p1.theta(p2) == Parity(mat24.ploop_cocycle(p1.ord, p2.ord)) assert (p1 & p2) / 2 == p1.theta(p2) + p2.theta(p1) assert p1 & p2 == p1.theta() + p2.theta() + (p1 + p2).theta() assert int((p1 & p2) / 2) == mat24.ploop_comm(p1.ord, p2.ord) assert GcVector(p1 & p2) == GcVector(p1) & GcVector(p2) assert ~GcVector(p1 & p2) == ~GcVector(p1) | ~GcVector(p2) assert Cocode(GcVector(p1 & p2)) == p1 & p2 # Test associator n3 = randint(0, 0x1fff) p3 = PLoop(n3) assert p1 * p2 * p3 / (p1 * (p2 * p3)) == PLoopZ(p1 & p2 & p3) assert int(p1 & p2 & p3) == mat24.ploop_assoc(p1.ord, p2.ord, p3.ord) i = randint(-1000, 1000) par = Parity(i) s3 = ((p3 & p1) & p2) + par assert s3 == ((p3 & p1) & p2) + par assert s3 == i + (p1 & (p2 & p3)) assert s3 == par + (p1 & (p2 & p3)) # Test some operations leading to a TypeError with pytest.raises(TypeError): p1 & p2 & p3 & p1 with pytest.raises(TypeError): coc & coc with pytest.raises(TypeError): GCode(p1) * GCode(p2) with pytest.raises(TypeError): 1 / GCode(p2) with pytest.raises(ValueError): coc / 4 with pytest.raises(TypeError): p1 * coc with pytest.raises(TypeError): coc * p1 types = [GcVector, GCode, Cocode, PLoop] for type_ in types: with pytest.raises(TypeError): int(type_(0)) print("Parker Loop test passed")
def __len__(self): return mat24.gcode_weight(self.value) << 2
def op_xy(v, eps, e, f): """Multiply unit vector v with group element This function multplies a (multiple of a) unit vector v with the group element g = d_<eps> * (x_<e>)**(-1) * (y_<f>)**(-1) . It returns the vector w = v * g. This function uses the same formula for calculating w = v * g, which is used in the implementation of the monster group for computing v = w * g ** (-1). Input vector v must be given as a tuple as described in class mmgroup.structures.abstract_mm_rep_space.AbstractMmRepSpace. Output vector is returned as a tuple of the same shape. """ if len(v) == 3: v = (1,) + v v_value, v_tag, v_d, v_j = v parity = (eps >> 11) & 1 # parity of eps if v_tag == 'X': w_d = v_d ^ (f & 0x7ff) sign = v_d >> 12 d = v_d & 0x7ff c = m24.vect_to_cocode(1 << v_j) sign += m24.gcode_weight(e ^ f) sign += m24.gcode_weight(f) sign += m24.gcode_weight(d) * (parity + 1) sign += m24.gcode_weight(d ^ e ^ f) sign += m24.scalar_prod(e, c) cc = c if parity else 0 cc ^= eps ^ m24.ploop_theta(f) ^ m24.ploop_cap(e, f) sign += m24.scalar_prod(d, cc) sign += f >> 12 return (-1)**sign * v_value, 'X', w_d, v_j elif v_tag in 'YZ': tau = v_tag == 'Y' sigma = (tau + parity) & 1 w_tag = "ZY"[sigma] sign = (v_d >> 12) + ((v_d >> 11) & tau) w_d = (v_d ^ e ^ (f & ~-sigma)) & 0x7ff #print("YZ, w_d", hex(w_d), hex(v_d ^ (e & 0x7ff) ^ (f & sigma_1)), err) # Next we check the sign d = v_d & 0x7ff c = m24.vect_to_cocode(1 << v_j) sign += m24.ploop_cocycle(f, e) * (sigma + 1) sign += m24.gcode_weight(f) * (sigma) sign += m24.gcode_weight(d ^ e) sign += m24.gcode_weight(d ^ e ^ f) sign += m24.scalar_prod(f, c) cc = eps ^ m24.ploop_theta(e) cc ^= m24.ploop_theta(f) * ((sigma ^ 1) & 1) sign += m24.scalar_prod(d, cc) sign += (e >> 12) + (f >> 12) * (sigma + 1) sign += ((e >> 11) & sigma) return (-1)**sign * v_value, w_tag, w_d, v_j elif v_tag == 'T': d = m24.octad_to_gcode(v_d) w_j = v_j ^ as_suboctad(f, d) sign = m24.gcode_weight(d ^ e) + m24.gcode_weight(e) sign += m24.scalar_prod(d, eps) sign += m24.suboctad_scalar_prod(as_suboctad(e ^ f, d), v_j) sign += m24.suboctad_weight(v_j) * parity return (-1)**sign * v_value, 'T', v_d, w_j elif v_tag in 'BC': m = v_tag == 'C' c = m24.vect_to_cocode((1 << v_d) ^ (1 << v_j)) n = m ^ m24.scalar_prod(f, c) w_tag = "BC"[n] w_i, w_j = max(v_d, v_j), min(v_d, v_j) sign = m * parity + m24.scalar_prod(e ^ f, c) return (-1)**sign * v_value, w_tag, w_i, v_j elif v_tag == 'A': w_i, w_j = max(v_d, v_j), min(v_d, v_j) c = m24.vect_to_cocode((1 << v_d) ^ (1 << v_j)) sign = m24.scalar_prod(f, c) return (-1)**sign * v_value,'A', w_i, v_j else: raise ValueError("Bad tag " + v_tag)