6"""Native Python (slow) reimplementation of libminisketch' algorithms.""" 
   26    2**8 + 2**4 + 2**3 + 2**1 + 1,
 
   31    2**13 + 2**4 + 2**3 + 2**1 + 1,
 
   34    2**16 + 2**5 + 2**3 + 2**1 + 1,
 
   37    2**19 + 2**5 + 2**2 + 2**1 + 1,
 
   42    2**24 + 2**4 + 2**3 + 2**1 + 1,
 
   44    2**26 + 2**4 + 2**3 + 2**1 + 1,
 
   45    2**27 + 2**5 + 2**2 + 2**1 + 1,
 
   50    2**32 + 2**7 + 2**3 + 2**2 + 1,
 
   55    2**37 + 2**6 + 2**4 + 2**1 + 1,
 
   56    2**38 + 2**6 + 2**5 + 2**1 + 1,
 
   58    2**40 + 2**5 + 2**4 + 2**3 + 1,
 
   61    2**43 + 2**6 + 2**4 + 2**3 + 1,
 
   63    2**45 + 2**4 + 2**3 + 2**1 + 1,
 
   66    2**48 + 2**5 + 2**3 + 2**2 + 1,
 
   68    2**50 + 2**4 + 2**3 + 2**2 + 1,
 
   69    2**51 + 2**6 + 2**3 + 2**1 + 1,
 
   71    2**53 + 2**6 + 2**2 + 2**1 + 1,
 
   74    2**56 + 2**7 + 2**4 + 2**2 + 1,
 
   77    2**59 + 2**7 + 2**4 + 2**2 + 1,
 
   79    2**61 + 2**5 + 2**2 + 2**1 + 1,
 
   82    2**64 + 2**4 + 2**3 + 2**1 + 1
 
   86    """Class to perform GF(2^field_size) operations on elements represented as integers. 
   88    Given that elements are represented as integers, addition 
is simply xor, 
and not 
   93        """Construct a GF2Ops object for the specified field size.""" 
   99        """Multiply x by 2 in GF(2^field_size).""" 
  106        """Multiply x by y in GF(2^field_size).""" 
  116        """Square x in GF(2^field_size).""" 
  117        return self.
mul(x, x)
 
  120        """Compute the inverse of x in GF(2^field_size).""" 
  126        r1l, r2l = self.
field_size + 1, r2.bit_length()
 
  131            r1l = r1.bit_length()
 
  140    """Test class for basic arithmetic properties of GF2Ops.""" 
  143        """Test operations for given field_size.""" 
  147            x = random.randrange(1 << field_size)
 
  148            y = random.randrange(1 << field_size)
 
  151            self.assertEqual(x2, gf.mul(x, 2)) 
 
  152            self.assertEqual(x2, gf.mul(2, x)) 
 
  153            self.assertEqual(xy == 0, x == 0 
or y == 0)
 
  154            self.assertEqual(xy == x, y == 1 
or x == 0)
 
  155            self.assertEqual(xy == y, x == 1 
or y == 0)
 
  156            self.assertEqual(xy, gf.mul(y, x)) 
 
  159                for _ 
in range(field_size):
 
  161                self.assertEqual(xp, x) 
 
  164                self.assertEqual(y == yi, y == 1) 
 
  165                self.assertEqual(gf.mul(y, yi), 1) 
 
  167                self.assertEqual(y, yii) 
 
  171                    self.assertEqual(xyi, gf.mul(xi, yi)) 
 
  175        for field_size 
in range(2, 65):
 
  192    """Return a monic version of the polynomial poly.""" 
  194    inv = gf.inv(poly[-1])
 
  195    return [gf.mul(inv, v) 
for v 
in poly]
 
  198    """Return the polynomial (quotient, remainder) of poly divided by mod.""" 
  199    assert len(mod) > 0 
and mod[-1] == 1 
 
  200    if len(poly) < len(mod):
 
  203    div = [0 
for _ 
in range(len(val) - len(mod) + 1)]
 
  204    while len(val) >= len(mod):
 
  206        div[len(val) - len(mod)] = term
 
  210            for x 
in range(len(mod) - 1):
 
  211                val[1 + x - len(mod)] ^= gf.mul(term, mod[x])
 
  213    while len(val) > 0 
and val[-1] == 0:
 
  218    """Return the polynomial GCD of a and b.""" 
  229    """Return the square of polynomial poly.""" 
  236    return [0 
if i & 1 
else gf.sqr(poly[i // 2]) 
for i 
in range(2 * len(poly) - 1)]
 
  239    """Compute y + y^2 + y^4 + ... + y^(2^(field_size-1)) mod poly, where y = param*x.""" 
  241    for _ 
in range(gf.field_size - 1):
 
  254    """Compute x^(2^field_size) mod poly.""" 
  256    for _ 
in range(gf.field_size):
 
  261    """Find the roots of poly if fully factorizable with unique roots, [] otherwise.""" 
  278    def rec_split(poly, randv):
 
  279        """Recursively split poly using the Berlekamp trace algorithm.""" 
  281        assert len(poly) > 1 
and poly[-1] == 1 
 
  294            randv = gf.mul2(randv)
 
  300            if len(gcd) != len(poly) 
and len(gcd) > 1:
 
  307        return rec_split(factor1, randv) + rec_split(factor2, randv)
 
  310    return sorted(rec_split(poly, random.randrange(1, 1 << gf.field_size)))
 
  313    """Test class for poly_find_roots.""" 
  316        """Run tests for given field_size.""" 
  318        for test_size 
in [0, 1, 2, 3, 10]:
 
  319            roots = [random.randrange(1 << field_size) 
for _ 
in range(test_size)]
 
  320            roots_set = set(roots)
 
  324                new_poly = [0] + poly
 
  325                for n, c 
in enumerate(poly):
 
  326                    new_poly[n] ^= gf.mul(c, root)
 
  331            if len(roots) == len(roots_set):
 
  332                self.assertEqual(found_roots, sorted(roots))
 
  334                self.assertEqual(found_roots, [])
 
  338        for field_size 
in range(2, 65):
 
  342    """Implement the Berlekamp-Massey algorithm. 
  344    Takes as input a sequence of GF(2^field_size) elements, 
and returns the shortest LSFR
 
  345    that generates it, represented 
as a polynomial.
 
  351    for n, discrepancy 
in enumerate(syndromes):
 
  353        for i 
in range(1, len(current)):
 
  354            discrepancy ^= gf.mul(syndromes[n - i], current[i])
 
  358            x = n + 1 - (len(current) - 1) - (len(prev) - 1)
 
  359            if 2 * (len(current) - 1) <= n:
 
  361                current.extend(0 
for _ 
in range(len(prev) + x - len(current)))
 
  362                mul = gf.mul(discrepancy, b_inv)
 
  363                for i, v 
in enumerate(prev):
 
  364                    current[i + x] ^= gf.mul(mul, v)
 
  366                b_inv = gf.inv(discrepancy)
 
  368                mul = gf.mul(discrepancy, b_inv)
 
  369                for i, v 
in enumerate(prev):
 
  370                    current[i + x] ^= gf.mul(mul, v)
 
  374    """A Minisketch sketch. 
  376    This represents a sketch of a certain capacity, with elements of a certain bit size.
 
  380        """Initialize an empty sketch with the specified field_size size and capacity.""" 
  387        """Add an element to this sketch. 1 <= element < 2**field_size.""" 
  388        sqr = self.
gf.sqr(element)
 
  391            element = self.
gf.mul(sqr, element)
 
  394        """Compute how many bytes a serialization of this sketch will be in size.""" 
  398        """Serialize this sketch to bytes.""" 
  405        """Deserialize a byte array into this sketch, overwriting its contents.""" 
  407        val = int.from_bytes(byte_data, 
'little')
 
  412        """Return a clone of this sketch.""" 
  419        """Merge a sketch with another sketch. Corresponds to XOR'ing their serializations.""" 
  420        assert self.
capacity == other.capacity
 
  426        """Decode the contents of this sketch. 
  428        Returns either a list of elements or None if undecodable.
 
  435        all_syndromes = [0 
for _ 
in range(2 * len(self.
odd_syndromes))]
 
  438            all_syndromes[i * 2 + 1] = self.
gf.sqr(all_syndromes[i])
 
  446        if max_count 
is not None and len(poly) > 1 + max_count:
 
  458    """Test class for Minisketch.""" 
  462        """Construct two random lists of elements in [1..2**field_size-1]. 
  464        Each list will have unique elements that don't appear in the other (num_a_only in the first 
  465        and num_b_only 
in the second), 
and num_both elements will appear 
in both.
""" 
  468        for _ 
in range(num_a_only + num_b_only + num_both):
 
  470                r = random.randrange(1, 1 << field_size)
 
  474        full_a = sample[:num_a_only + num_both]
 
  475        full_b = sample[num_a_only:]
 
  476        random.shuffle(full_a)
 
  477        random.shuffle(full_b)
 
  478        return full_a, full_b
 
  481        """Test Minisketch methods for a specific field and capacity.""" 
  482        used_capacity = random.randrange(capacity + 1)
 
  483        num_a = random.randrange(used_capacity + 1)
 
  484        num_both = random.randrange(min(2 * capacity, (1 << field_size) - 1 - used_capacity) + 1)
 
  485        full_a, full_b = self.
construct_data(field_size, num_a, used_capacity - num_a, num_both)
 
  492        sketch_combined = sketch_a.clone()
 
  493        sketch_b_ser = sketch_b.serialize()
 
  494        sketch_b_received = 
Minisketch(field_size, capacity)
 
  495        sketch_b_received.deserialize(sketch_b_ser)
 
  496        sketch_combined.merge(sketch_b_received)
 
  497        decode = sketch_combined.decode()
 
  498        self.assertEqual(decode, sorted(set(full_a) ^ set(full_b)))
 
  502        for field_size 
in range(2, 65):
 
  503            for capacity 
in [0, 1, 2, 5, 10, field_size]:
 
  506if __name__ == 
'__main__':
 
def __init__(self, field_size)
def __init__(self, field_size, capacity)
def deserialize(self, byte_data)
def decode(self, max_count=None)
def serialized_size(self)
def field_size_test(self, field_size)
def construct_data(cls, field_size, num_a_only, num_b_only, num_both)
def field_size_capacity_test(self, field_size, capacity)
def field_size_test(self, field_size)
def poly_divmod(poly, mod, gf)
def poly_tracemod(poly, param, gf)
def poly_frobeniusmod(poly, gf)
def poly_find_roots(poly, gf)
def berlekamp_massey(syndromes, gf)