MCPcopy Create free account
hub / github.com/ElementsProject/elements / poly_find_roots

Function poly_find_roots

src/minisketch/tests/pyminisketch.py:260–310  ·  view source on GitHub ↗

Find the roots of poly if fully factorizable with unique roots, [] otherwise.

(poly, gf)

Source from the content-addressed store, hash-verified

258 return out
259
260def poly_find_roots(poly, gf):
261 """Find the roots of poly if fully factorizable with unique roots, [] otherwise."""
262 assert len(poly) > 0
263 # If the polynomial is constant (and nonzero), it has no roots.
264 if len(poly) == 1:
265 return []
266 # Make the polynomial monic (which doesn't change its roots).
267 poly = poly_monic(poly, gf)
268 # If the polynomial is of the form x+a, return a.
269 if len(poly) == 2:
270 return [poly[0]]
271 # Otherwise, first test that poly can be completely factored into unique roots. The polynomial
272 # x^(2^fieldsize)-x has every field element once as root. Thus we want to know that that is a
273 # multiple of poly. Compute x^(field_size) mod poly, which needs to equal x if that is the case
274 # (unless poly has degree <= 1, but that case is handled above).
275 if poly_frobeniusmod(poly, gf) != [0, 1]:
276 return []
277
278 def rec_split(poly, randv):
279 """Recursively split poly using the Berlekamp trace algorithm."""
280 # See https://hal.archives-ouvertes.fr/hal-00626997/document.
281 assert len(poly) > 1 and poly[-1] == 1 # Require a monic poly.
282 # If poly is of the form x+a, its root is a.
283 if len(poly) == 2:
284 return [poly[0]]
285 # Try consecutive randomization factors randv, until one is found that factors poly.
286 while True:
287 # Compute the trace of (randv*x) mod poly. This is a polynomial that maps half of the
288 # domain to 0, and the other half to 1. Which half that is is controlled by randv.
289 # By taking it modulo poly, we only add a multiple of poly. Thus the result has at least
290 # the shared roots of the trace polynomial and poly still, but may have others.
291 trace = poly_tracemod(poly, randv, gf)
292 # Using the set {2^i*a for i=0..fieldsize-1} gives optimally independent randv values
293 # (no more than fieldsize are ever needed).
294 randv = gf.mul2(randv)
295 # Now take the GCD of this trace polynomial with poly. The result is a polynomial
296 # that only has the shared roots of the trace polynomial and poly as roots.
297 gcd = poly_gcd(trace, poly, gf)
298 # If the result has a degree higher than 1, and lower than that of poly, we found a
299 # useful factorization.
300 if len(gcd) != len(poly) and len(gcd) > 1:
301 break
302 # Otherwise, continue with another randv.
303 # Find the actual factors: the monic version of the GCD above, and poly divided by it.
304 factor1 = poly_monic(gcd, gf)
305 factor2, _ = poly_divmod(poly, gcd, gf)
306 # Recurse.
307 return rec_split(factor1, randv) + rec_split(factor2, randv)
308
309 # Invoke the recursive splitting with a random initial factor, and sort the results.
310 return sorted(rec_split(poly, random.randrange(1, 1 << gf.field_size)))
311
312class TestPolyFindRoots(unittest.TestCase):
313 """Test class for poly_find_roots."""

Callers 2

field_size_testMethod · 0.85
decodeMethod · 0.85

Calls 4

poly_monicFunction · 0.85
poly_frobeniusmodFunction · 0.85
rec_splitFunction · 0.85
randrangeMethod · 0.80

Tested by

no test coverage detected