def cmp_ir(self,z): """ returns -1 for left, 0 for in, and 1 for right from initial region cut line is on the north ray from L. """ L = self.L x0 = self.x0 if x0 > 0.5: if real(z) > real(L) and abs(z) < abs(L): return 0 if real(z) < real(L): return -1 if real(z) > real(L): return 1 else: if imag(z) > imag(L): if real(z) > real(L): return 1 if real(z) < real(L): return -1 if real(z) < real(L) and real(z) > log(real(L)) + log(sqrt(1+tan(imag(z))**2)): return 0 if real(z) > real(L): return 1 if real(z) < real(L): return -1
def calc_slog(self): RP = FormalPowerSeriesRing(RealField(self.iprec)) ev = self.eigenvalues a1 = self.coeffs_1 N = self.N #how can this be made picklable? class _SexpCoeffs1(FormalPowerSeries0): def coeffs(self,n): if n==0: return 0 return sum([a1[k]*log(ev[k])**n for k in xrange(N)])/factorial(n) class _SexpCoeffs0(FormalPowerSeries0): def coeffs(self,n): if n==0: return 0 return sum([a0[k]*log(ev[k])**n for k in xrange(N)])/factorial(n) self.sexp_coeffs_1 = _SexpCoeffs1(RP,min_index=1) self.slog_coeffs_1 = self.sexp_coeffs_1.inv() if self.L != None: return self.L b = self.b iprec = self.iprec if b > (e**(1/e)).n(iprec): L = ComplexField(iprec)(0.5) for n in range(100): L = log(L)/log(b) else: L = RealField(iprec)(0) for n in range(100): L = b**L self.L = L return self
def bkz_runtime_k_bkz2(k, n): """ Runtime estimation given ‘k‘ and assuming [CheNgu12]_ estimates are correct. The constants in this function were derived as follows based on Table 4 in [CheNgu12]_:: sage: dim = [100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250] sage: nodes = [39.0, 44.0, 49.0, 54.0, 60.0, 66.0, 72.0, 78.0, 84.0, 96.0, 99.0, 105.0, 111.0, 120.0, 127.0, 134.0] sage: times = [c + log(200,2).n() for c in nodes] sage: T = zip(dim, nodes) sage: var("a,b,c,k") sage: f = a*k*log(k, 2.0) + b*k + c sage: f = f.function(k) sage: f.subs(find_fit(T, f, solution_dict=True)) k |--> 0.270188776350190*k*log(k) - 1.0192050451318417*k + 16.10253135200765 .. [CheNgu12] Yuanmi Chen and Phong Q. Nguyen. BKZ 2.0: Better lattice security estimates ( Full Version). 2012. http://www.di.ens.fr/~ychen/research/Full_BKZ.pdf """ repeat = _sage_const_3 * log(n, _sage_const_2) - _sage_const_2 * log( k, _sage_const_2) + log(log(n, _sage_const_2), _sage_const_2) return RR(_sage_const_0p270188776350190 * k * log(k) - _sage_const_1p0192050451318417 * k + _sage_const_16p10253135200765 + repeat)
def _estimated_time(M2, M1, length, p): """ Find the estimated time to extend congruences mod M1 to consistent congruences mod M2. INPUT: - ``M2`` -- an integer (the new modulus) - ``M1`` -- an integer (the old modulus) - ``length`` -- a list (the current length of the list of congruences mod ``M1``) - ``p`` -- a prime OUTPUT: - The estimated run time of the "CRT" step to combine consistent congruences. EXAMPLES:: sage: sage.combinat.binary_recurrence_sequences._estimated_time(2**4*3**2*5*7*11*13*17, 2**4*3**2*5*7*11*13, 20, 7) 106.211159309421 """ #The heuristic run time of the CRT step to go from modulus M1 to M2 #length is the current length of cong Q = p * log(M2) #Size of our primes. NPrimes = log(M2/M1) / log(Q) #The number of primes return (length * (Q/p)**NPrimes).n()
def cmp_ir(self,z): """ returns -1 for left, 0 for in, and 1 for right from initial region cut line is on the north ray from pfp. Works only for real x0. """ pfp = self.pfp x0 = self.x0 if x0 > 0.5: print z,abs(z) if real(z) >= real(pfp) and abs(z) < abs(pfp): return 0 if real(z) < real(pfp): return -1 if real(z) > real(pfp): return 1 else: if imag(z) > imag(pfp): if real(z) > real(pfp): return 1 if real(z) < real(pfp): return -1 if real(z) < real(pfp) and real(z) > log(real(pfp)) + log(sqrt(1+tan(imag(z))**2)): return 0 if real(z) > real(pfp): return 1 if real(z) < real(pfp): return -1
def _estimated_time(M2, M1, length, p): """ Find the estimated time to extend congruences mod M1 to consistent congruences mod M2. INPUT: - ``M2`` -- an integer (the new modulus) - ``M1`` -- an integer (the old modulus) - ``length`` -- a list (the current length of the list of congruences mod ``M1``) - ``p`` -- a prime OUTPUT: - The estimated run time of the "CRT" step to combine consistent congruences. EXAMPLES:: sage: sage.combinat.binary_recurrence_sequences._estimated_time(2**4*3**2*5*7*11*13*17, 2**4*3**2*5*7*11*13, 20, 7) 106.211159309421 """ #The heuristic run time of the CRT step to go from modulus M1 to M2 #length is the current length of cong Q = p * log(M2) #Size of our primes. NPrimes = log(M2 / M1) / log(Q) #The number of primes return (length * (Q / p)**NPrimes).n()
def __init__(self, n, instance='key', m=None): """ Construct LWE instance parameterised by security parameter ``n`` where all other parameters are chosen as in [CGW13]_. INPUT: - ``n`` - security parameter (integer >= 89) - ``instance`` - one of - "key" - the LWE-instance that hides the secret key is generated - "encrypt" - the LWE-instance that hides the message is generated (default: ``key``) - ``m`` - number of allowed samples or ``None`` in which case ``m`` is chosen as in [CGW13_]. (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import UniformNoiseLWE sage: UniformNoiseLWE(89) LWE(89, 154262477, UniformSampler(0, 351), 'noise', 131) sage: UniformNoiseLWE(89, instance='encrypt') LWE(131, 154262477, UniformSampler(0, 497), 'noise', 181) """ if n < 89: raise TypeError("Parameter too small") n2 = n C = 4 / sqrt(2 * pi) kk = floor((n2 - 2 * log(n2, 2)**2) / 5) n1 = floor((3 * n2 - 5 * kk) / 2) ke = floor((n1 - 2 * log(n1, 2)**2) / 5) l = floor((3 * n1 - 5 * ke) / 2) - n2 sk = ceil((C * (n1 + n2))**(3 / 2)) se = ceil((C * (n1 + n2 + l))**(3 / 2)) q = next_prime( max(ceil((4 * sk)**((n1 + n2) / n1)), ceil((4 * se)**((n1 + n2 + l) / (n2 + l))), ceil(4 * (n1 + n2) * se * sk + 4 * se + 1))) if kk <= 0: raise TypeError("Parameter too small") if instance == 'key': D = UniformSampler(0, sk - 1) if m is None: m = n1 LWE.__init__(self, n=n2, q=q, D=D, secret_dist='noise', m=m) elif instance == 'encrypt': D = UniformSampler(0, se - 1) if m is None: m = n2 + l LWE.__init__(self, n=n1, q=q, D=D, secret_dist='noise', m=m) else: raise TypeError("Parameter instance=%s not understood." % (instance))
def strain_sensitivity(freq): r""" Return LISA strain spectral sensitivity at a given frequency. The strain spectral sensitivity is the square root of the effective noise power spectral density (cf. :func:`power_spectral_density`). INPUT: - ``freq`` -- frequency `f` (in `\mathrm{Hz}`) OUTPUT: - strain sensitivity `S(f)^{1/2}` (in `\mathrm{Hz}^{-1/2}`) EXAMPLES:: sage: from kerrgeodesic_gw import lisa_detector sage: hn = lisa_detector.strain_sensitivity sage: hn(1.e-1) # tol 1.0e-13 5.82615031500758e-20 sage: hn(1.e-2) # tol 1.0e-13 1.654806317072275e-20 sage: hn(1.e-3) # tol 1.0e-13 1.8082609253700212e-19 :: sage: plot_loglog(hn, (1e-5, 1), plot_points=2000, ymin=1e-20, ymax=1e-14, ....: axes_labels=[r"$f\ [\mathrm{Hz}]$", ....: r"$S(f)^{1/2} \ \left[\mathrm{Hz}^{-1/2}\right]$"], ....: gridlines='minor', frame=True, axes=False) Graphics object consisting of 1 graphics primitive .. PLOT:: from kerrgeodesic_gw import lisa_detector hn = lisa_detector.strain_sensitivity g = plot_loglog(hn, (1e-5, 1), plot_points=2000, ymin=1e-20, ymax=1e-14, \ axes_labels=[r"$f\ [\mathrm{Hz}]$", \ r"$S(f)^{1/2} \ \left[\mathrm{Hz}^{-1/2}\right]$"], \ gridlines='minor', frame=True, axes=False) sphinx_plot(g) """ global _sensitivity_spline if not _sensitivity_spline: data = [] file_name = os.path.join(os.path.dirname(__file__), "data/Sensitivity_LISA_SciRD1806_Alloc.dat") with open(file_name, "r") as data_file: for dline in data_file: f, s = dline.split('\t') data.append((log(RDF(f), 10), log(sqrt(RDF(s)), 10))) _sensitivity_spline = spline(data) if freq < 1.e-5 or freq > 1.: raise ValueError("frequency {} Hz is out of range".format(freq)) freq = RDF(freq) return RDF(10)**(_sensitivity_spline(log(freq, 10)))
def calc_prec(): if self.prec != None: return self.prec iv0 = IntuitiveAbel(self.bsym,self.N-1,iprec=self.iprec,x0=self.x0sym) self.iv0 = iv0 self.err = abs(iv0.sexp(0.5) - self.sexp(0.5)) print "err:", self.err.n(20) self.prec = floor(-log(self.err)/log(2.0))
def discrete_curve_2(nb_equipes, max_points=100): r""" """ from sage.misc.functional import round from sage.functions.log import log from sage.rings.integer_ring import ZZ f = lambda p:(max_points-1)*(1-log(p)/log(nb_equipes))+1 L = [ZZ(round(f(p=i))) for i in range(1,nb_equipes+1)] return L
def discrete_curve_2(nb_equipes, max_points=100): r""" """ from sage.misc.functional import round from sage.functions.log import log from sage.rings.integer_ring import ZZ f = lambda p: (max_points - 1) * (1 - log(p) / log(nb_equipes)) + 1 L = [ZZ(round(f(p=i))) for i in range(1, nb_equipes + 1)] return L
def _height(P, check=True): if check: assert P.curve() == self._E, "the point P must lie on the curve from which the height function was created" Q = n * P cQ = denominator(Q[0]) uQ = self.lift(Q, prec=prec) si = self.__padic_sigma_square(uQ, prec=prec) nn = self._q.valuation() qEu = self._q / p ** nn return -(log(si*self._Csquare()/cQ) + log(uQ)**2/log(qEu)) / n**2
def newton_sqrt(self, f, x0, prec): r""" Takes the square root of the power series `f` by Newton's method NOTE: this function should eventually be moved to `p`-adic power series ring INPUT: - f power series wtih coefficients in `\QQ_p` or an extension - x0 seeds the Newton iteration - prec precision OUTPUT: the square root of `f` EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x) sage: Q = H(0,0) sage: u,v = H.local_coord(Q,prec=100) sage: K = Qp(11,5) sage: HK = H.change_ring(K) sage: L.<a> = K.extension(x^20-11) sage: HL = H.change_ring(L) sage: S = HL(u(a),v(a)) sage: f = H.hyperelliptic_polynomials()[0] sage: y = HK.newton_sqrt( f(u(a)^11), a^11,5) sage: y^2 - f(u(a)^11) O(a^122) AUTHOR: - Jennifer Balakrishnan """ z = x0 try: x = f.parent().variable_name() if x != 'a': #this is to distinguish between extensions of Qp that are finite vs. not S = f.base_ring()[[x]] x = S.gen() except ValueError: pass z = x0 loop_prec = (log(RR(prec)) / log(RR(2))).ceil() for i in range(loop_prec): z = (z + f / z) / 2 try: return z + O(x**prec) except (NameError, ArithmeticError, TypeError): return z
def __init__(self, n, instance='key', m=None): """ Construct LWE instance parameterised by security parameter ``n`` where all other parameters are chosen as in [CGW2013]_. INPUT: - ``n`` - security parameter (integer >= 89) - ``instance`` - one of - "key" - the LWE-instance that hides the secret key is generated - "encrypt" - the LWE-instance that hides the message is generated (default: ``key``) - ``m`` - number of allowed samples or ``None`` in which case ``m`` is chosen as in [CGW2013]_. (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import UniformNoiseLWE sage: UniformNoiseLWE(89) LWE(89, 154262477, UniformSampler(0, 351), 'noise', 131) sage: UniformNoiseLWE(89, instance='encrypt') LWE(131, 154262477, UniformSampler(0, 497), 'noise', 181) """ if n<89: raise TypeError("Parameter too small") n2 = n C = 4/sqrt(2*pi) kk = floor((n2-2*log(n2, 2)**2)/5) n1 = floor((3*n2-5*kk)/2) ke = floor((n1-2*log(n1, 2)**2)/5) l = floor((3*n1-5*ke)/2)-n2 sk = ceil((C*(n1+n2))**(3/2)) se = ceil((C*(n1+n2+l))**(3/2)) q = next_prime(max(ceil((4*sk)**((n1+n2)/n1)), ceil((4*se)**((n1+n2+l)/(n2+l))), ceil(4*(n1+n2)*se*sk+4*se+1))) if kk<=0: raise TypeError("Parameter too small") if instance == 'key': D = UniformSampler(0, sk-1) if m is None: m = n1 LWE.__init__(self, n=n2, q=q, D=D, secret_dist='noise', m=m) elif instance == 'encrypt': D = UniformSampler(0, se-1) if m is None: m = n2+l LWE.__init__(self, n=n1, q=q, D=D, secret_dist='noise', m=m) else: raise TypeError("Parameter instance=%s not understood."%(instance))
def slog(self,z,n=None): slog = self.slog b = self.b if n == None: n = self.N if self.cmp_ir(z) == -1: return slog(b**z)-1 if self.cmp_ir(z) == +1: return slog(log(z)/log(b))+1 return self.slog_1t(z)
def bkz_runtime_k_sieve(k, n): """ Runtime estimation given ‘k‘ and assuming sieving is used to realise the SVP oracle. For small ‘k‘ we use estimates based on experiments. For ‘k ě 90‘ we use the asymptotics. """ repeat = _sage_const_3 *log(n, _sage_const_2 ) - _sage_const_2 *log(k, _sage_const_2 ) + log(log(n, _sage_const_2 ), _sage_const_2 ) if k < _sage_const_90 : return RR(_sage_const_0p45 *k + _sage_const_12p31 ) + repeat else: # we simply pick the same additive constant 12.31 as above return RR(_sage_const_0p3366 *k + _sage_const_12p31 ) + repeat
def bkz_runtime_k_sieve(k, n): """ Runtime estimation given ‘k‘ and assuming sieving is used to realise the SVP oracle. For small ‘k‘ we use estimates based on experiments. For ‘k ě 90‘ we use the asymptotics. """ repeat = _sage_const_3 * log(n, _sage_const_2) - _sage_const_2 * log( k, _sage_const_2) + log(log(n, _sage_const_2), _sage_const_2) if k < _sage_const_90: return RR(_sage_const_0p45 * k + _sage_const_12p31) + repeat else: # we simply pick the same additive constant 12.31 as above return RR(_sage_const_0p3366 * k + _sage_const_12p31) + repeat
def sexp_0(self,t): #convergence radius 1 t = self._in_prec(t) sexp = self.sexp_0 b = self.b #development point -1 convergence radius 1 if real(t)>1: return b**(sexp(t-1)) if real(t)<0: #sage bug, log(z,b) does not work for complex z return log(sexp(t+1))/log(b) return self.sexp_0_raw(t)
def sexp_1(self,t): t = self._in_prec(t) sexp = self.sexp_1 b = self.b IM = self.IM N = self.N #development point 0 convergence radius 2 if real(t)>1: return b**(sexp(t-1)) if real(t)<0: #sage bug, log(z,b) does not work for complex z return log(sexp(t+1))/log(b) return self.sexp_1_raw(t)
def log(self,workprec=Infinity): from sage.functions.log import log from sage.functions.other import floor if workprec is Infinity: raise ApproximationError("unable to compute log to infinite precision") parent = self.parent() pow = parent(-1) res = parent(0) t = parent(1) - self iter = workprec + floor(log(workprec)/log(parent._p)) + 1 for i in range(1,iter): pow *= t res += pow / parent(i) res = res.truncate(workprec) return res
def params_str(d, keyword_width=None): """ Return string of key,value pairs as a string "key0: value0, key1: value1" :param d: report dictionary :keyword_width:keys are printed with this width """ if d is None: return s = [] for k in d: v = d[k] if keyword_width: fmt = u"%%%ds" % keyword_width k = fmt % k if ZZ(_sage_const_1 ) / _sage_const_2048 < v < _sage_const_2048 or v == _sage_const_0: try: s.append(u"%s: %9d" % (k, ZZ(v))) except TypeError: if v < _sage_const_2p0 and v >= _sage_const_0p0: s.append(u"%s: %9.7f" % (k, v)) else: s.append(u"%s: %9.4f" % (k, v)) else: t = u"«2^%.1f" % log(v, _sage_const_2).n() s.append(u"%s: %9s" % (k, t)) return u", ".join(s)
def calc_prec(self): if self.prec != None: return self.prec mp0 = MatrixPowerSexp(self.bsym,self.N-1,iprec=self.iprec,x0=self.x0sym) sexp_precision=RR(1)*log(abs(self.sexp_1(0.5)-mp0.sexp_1(0.5)),2.0) self.prec = (-sexp_precision).floor() print "sexp precision: " , self.prec cprec = self.prec+ceil(log(self.N)/log(2.0)) #self.eigenvalues = [ ev.n(cprec) for ev in self.eigenvalues ] #self.IM = self.IM.n(cprec) #self.b = self.bsym.n(cprec) return self
def L_invariant(self, prec=20): r""" Returns the *mysterious* `\mathcal{L}`-invariant associated to an elliptic curve with split multiplicative reduction. One instance where this constant appears is in the exceptional case of the `p`-adic Birch and Swinnerton-Dyer conjecture as formulated in [MTT]_. See [Col]_ for a detailed discussion. INPUT: - ``prec`` - the `p`-adic precision, default is 20. REFERENCES: [MTT]_ .. [Col] Pierre Colmez, Invariant `\mathcal{L}` et derivees de valeurs propres de Frobenius, preprint, 2004. EXAMPLES:: sage: eq = EllipticCurve('130a1').tate_curve(5) sage: eq.L_invariant(prec=10) 5^3 + 4*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 3*5^8 + 5^9 + O(5^10) """ if not self.is_split(): raise RuntimeError("The curve must have split multiplicative " "reduction") qE = self.parameter(prec=prec) n = qE.valuation() u = qE / self._p ** n # the p-adic logarithm of Iwasawa normalised by log(p) = 0 return log(u) / n
def params_str(d, keyword_width=None): """ Return string of key,value pairs as a string "key0: value0, key1: value1" :param d: report dictionary :keyword_width:keys are printed with this width """ if d is None: return s = [] for k in d: v = d[k] if keyword_width: fmt = u"%%%ds" % keyword_width k = fmt % k if ZZ(_sage_const_1 )/_sage_const_2048 < v < _sage_const_2048 or v == _sage_const_0 : try: s.append(u"%s: %9d" % (k, ZZ(v))) except TypeError: if v < _sage_const_2p0 and v >= _sage_const_0p0 : s.append(u"%s: %9.7f" % (k, v)) else: s.append(u"%s: %9.4f" % (k, v)) else: t = u"«2^%.1f" % log(v, _sage_const_2 ).n() s.append(u"%s: %9s" % (k, t)) return u", ".join(s)
def L_invariant(self, prec=20): r""" Returns the *mysterious* `\mathcal{L}`-invariant associated to an elliptic curve with split multiplicative reduction. One instance where this constant appears is in the exceptional case of the `p`-adic Birch and Swinnerton-Dyer conjecture as formulated in [MTT]_. See [Col]_ for a detailed discussion. INPUT: - ``prec`` - the `p`-adic precision, default is 20. REFERENCES: [MTT]_ .. [Col] Pierre Colmez, Invariant `\mathcal{L}` et derivees de valeurs propres de Frobenius, preprint, 2004. EXAMPLES:: sage: eq = EllipticCurve('130a1').tate_curve(5) sage: eq.L_invariant(prec=10) 5^3 + 4*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 3*5^8 + 5^9 + O(5^10) """ if not self.is_split(): raise RuntimeError("The curve must have split multiplicative " "reduction") qE = self.parameter(prec=prec) n = qE.valuation() u = qE / self._p**n # the p-adic logarithm of Iwasawa normalised by log(p) = 0 return log(u) / n
def Dini(a=1, b=1, name="Dini's surface"): r""" Returns Dini's surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = a \cos(u)\sin(v); \\ y(u, v) & = a \sin(u)\sin(v); \\ z(u, v) & = u + \log(\tan(v/2)) + \cos(v). \end{aligned} INPUT: - ``a, b`` -- surface parameters. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: dini = surfaces.Dini(a=3, b=4); dini Parametrized surface ('Dini's surface') with equation (3*cos(u)*sin(v), 3*sin(u)*sin(v), 4*u + 3*cos(v) + 3*log(tan(1/2*v))) sage: dini.plot() # not tested -- known bug (see #10132) """ u, v = var("u, v") dini_eq = [a * cos(u) * sin(v), a * sin(u) * sin(v), a * (cos(v) + log(tan(v / 2))) + b * u] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(dini_eq, coords, name)
def Dini(a=1, b=1, name="Dini's surface"): r""" Return Dini's surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = a \cos(u)\sin(v); \\ y(u, v) & = a \sin(u)\sin(v); \\ z(u, v) & = u + \log(\tan(v/2)) + \cos(v). \end{aligned} INPUT: - ``a, b`` -- surface parameters. - ``name`` -- string. Name of the surface. For more information, see :wikipedia:`Dini%27s_surface`. EXAMPLES:: sage: dini = surfaces.Dini(a=3, b=4); dini Parametrized surface ('Dini's surface') with equation (3*cos(u)*sin(v), 3*sin(u)*sin(v), 4*u + 3*cos(v) + 3*log(tan(1/2*v))) sage: dini.plot() Graphics3d Object """ u, v = var('u, v') dini_eq = [ a * cos(u) * sin(v), a * sin(u) * sin(v), a * (cos(v) + log(tan(v / 2))) + b * u ] coords = ((u, 0, 2 * pi), (v, 0, 2 * pi)) return ParametrizedSurface3D(dini_eq, coords, name)
def _prec_for_solve_diff_eqn_families(M, p): #UPDATE THIS with valuation of K[0]-1 and K[1] r""" A helper function for determining the (relative) precision of the input to solve_diff_eqn required in order obtain an answer with (relative) precision ``M``. The parameter ``p`` is the prime and ``k`` is the weight. Given input precision `M_\text{in}`, the output has precision .. MATH:: M = M_\text{in} - \lceil\log_p(M_\text{in}) - 3. """ # Do we need the weight? # A good guess to begin: if M < 1: raise ValueError("Desired precision M(=%s) must be at least 1."%(M)) cp = (p - 2) / (p - 1) Min = ZZ(3 + M + ceil(ZZ(M).log(p))) # It looks like usually there are no iterations # For low M, there can be 1 or 2 while M > Min*cp - ceil(log((Min * cp),p)) - 3: #THINK ABOUT THIS MORE Min += 1 #print("An iteration in _prec_solve_diff_eqn") return Min
def Dini(a=1, b=1, name="Dini's surface"): r""" Returns Dini's surface, with parametrization .. MATH:: \begin{aligned} x(u, v) & = a \cos(u)\sin(v); \\ y(u, v) & = a \sin(u)\sin(v); \\ z(u, v) & = u + \log(\tan(v/2)) + \cos(v). \end{aligned} INPUT: - ``a, b`` -- surface parameters. - ``name`` -- string. Name of the surface. EXAMPLES:: sage: dini = surfaces.Dini(a=3, b=4); dini Parametrized surface ('Dini's surface') with equation (3*cos(u)*sin(v), 3*sin(u)*sin(v), 4*u + 3*cos(v) + 3*log(tan(1/2*v))) sage: dini.plot() # not tested -- known bug (see #10132) """ u, v = var('u, v') dini_eq = [a*cos(u)*sin(v), a*sin(u)*sin(v), a*(cos(v) + log(tan(v/2))) + b*u] coords = ((u, 0, 2*pi), (v, 0, 2*pi)) return ParametrizedSurface3D(dini_eq, coords, name)
def error_function(model, N, x0): """ Compute the error function of a truncated ODE. INPUT: - ``model`` -- Polynomial ODE or string containing the model in text format - ``N`` -- integer; truncation order - ``x0`` -- list; initial point OUTPUT: - ``Ts`` -- convergence time computed from the reduced quadratic system - ``error`` -- function of `t`, the estimated truncation error in the supremum norm EXAMPLES:: sage: from carlin.transformation import error_function sage: from carlin.library import quadratic_scalar as P sage: Ts, error = error_function(P(0.5, 2), 2, [0, 0.5]) sage: Ts 0.8109... sage: error 0.5*(2.0*e^(0.5*t) - 2.0)^2*e^(0.5*t)/(-2.0*e^(0.5*t) + 3.0) """ from numpy.linalg import norm from sage.symbolic.ring import SR if isinstance(model, str): [F, n, k] = get_Fj_from_model(model) elif isinstance(model, PolynomialODE): [F, n, k] = get_Fj_from_model(model.funcs(), model.dim(), model.degree()) [Fquad, nquad, kquad] = quadratic_reduction(F, n, k) ch = characteristics(Fquad, nquad, kquad) norm_F1_tilde, norm_F2_tilde = ch['norm_Fi_inf'] x0_hat = [kron_power(x0, i + 1) for i in range(k - 1)] #transform to flat list x0_hat = [item for sublist in x0_hat for item in sublist] norm_x0_hat = norm(x0_hat, ord=inf) beta0 = ch['beta0_const'] * norm_x0_hat Ts = 1 / norm_F1_tilde * log(1 + 1 / beta0) t = SR.var('t') error = norm_x0_hat * exp( norm_F1_tilde * t) / (1 + beta0 - beta0 * exp(norm_F1_tilde * t)) * ( beta0 * (exp(norm_F1_tilde * t) - 1))**N return [Ts, error]
def super(self,x): """ Development point is x0-1 """ if isinstance(x,float) and self.iprec != None: x = RealField(self.iprec)(x) super = self.super super_raw = self.super_raw b = self.b c = self.c xt = x - c if real(xt)<-0.5: return log(super(x+1))/log(b) if real(xt)>0.5: return b**(super(x-1)) return super_raw(x)
def calc_prec(self): if self.prec != None: return self.prec iv0 = IntuitiveTetration(self.bsym,self.N-1,iprec=self.iprec,x0=self.x0sym) self.iv0 = iv0 d = lambda x: self.slog(x) - iv0.slog(x) maximum = find_maximum_on_interval(d,0,1,maxfun=20) minimum = find_minimum_on_interval(d,0,1,maxfun=20) print "max:", maximum[0].n(20), 'at:', maximum[1] print "min:", minimum[0].n(20), 'at:', minimum[1] self.err = max( abs(maximum[0]), abs(minimum[0])) print "slog err:", self.err.n(20) self.prec = floor(-self.err.log(2)) self.sexp_err = abs(iv0.sexp(0.5) - self.sexp(0.5)) print "sexp err:", self.sexp_err.n(20) self.sexp_prec = floor(-log(self.sexp_err)/log(2.0)) return self
def _height(P, check=True): if check: assert P.curve( ) == self._E, "the point P must lie on the curve from which the height function was created" Q = n * P cQ = denominator(Q[0]) q = self.parameter(prec=prec) nn = q.valuation() precp = prec + nn + 2 uQ = self.lift(Q, prec=precp) si = self.__padic_sigma_square(uQ, prec=precp) q = self.parameter(prec=precp) nn = q.valuation() qEu = q / p**nn res = -(log(si * self._Csquare(prec=precp) / cQ) + log(uQ)**2 / log(qEu)) / n**2 R = Qp(self._p, prec) return R(res)
def _find_N_43(): """ Find the precision used for thm 4.3 in Goncalves for p >> 0, N = N0 + 2 """ p = self._p r = self._r d = self._d delta = self._delta N0 = self._N0 left_side = N0 + floor(log((d * p * (r - 1) + r) / delta) / log(p)) def right_side_log(n): return floor(log(p * (r * n - 1) - r) / log(p)) n = left_side while n <= left_side + right_side_log(n): n += 1 return n
def __init__(self, N, delta=0.01, m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP11]_. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is used (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLindnerPeikert sage: RingLindnerPeikert(N=16) RingLWE(16, 1031, DiscreteGaussianPolynomialSamplerRejection(8, 2.803372, 53, 4), x^8 + 1, 'noise', 24) """ n = euler_phi(N) if m is None: m = 3 * n # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = var('c') c = find_root(2 * n * log(c) + n * (1 - c**2) + 40 * log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2 * n * log(2 / delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP11]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP11]_ s = sqrt(s_t_bound * floor(q / 4)) # Transform s into stddev stddev = s / sqrt(2 * pi.n()) D = DiscreteGaussianPolynomialSampler(n, stddev) RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m)
def __init__(self, n, delta=0.01, m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP11]_. INPUT: - ``n`` - security parameter (integer > 0) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``m=2*n + 128`` as in [LP11]_ (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import LindnerPeikert sage: LindnerPeikert(n=20) LWE(20, 2053, DiscreteGaussianSamplerRejection(3.600954, 53, 4), 'noise', 168) """ if m is None: m = 2 * n + 128 # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # (c*exp((1-c**2)/2))**(2*n) == 2**-40 # log((c*exp((1-c**2)/2))**(2*n)) == -40*log(2) # (2*n)*log(c*exp((1-c**2)/2)) == -40*log(2) # 2*n*(log(c)+log(exp((1-c**2)/2))) == -40*log(2) # 2*n*(log(c)+(1-c**2)/2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = var('c') c = find_root(2 * n * log(c) + n * (1 - c**2) + 40 * log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2 * n * log(2 / delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP11]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP11]_ s = sqrt(s_t_bound * floor(q / 4)) # Transform s into stddev stddev = s / sqrt(2 * pi.n()) D = DiscreteGaussianSampler(stddev) LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
def newton_sqrt(self, f, x0, prec): r""" Takes the square root of the power series `f` by Newton's method NOTE: this function should eventually be moved to `p`-adic power series ring INPUT: - ``f`` -- power series with coefficients in `\QQ_p` or an extension - ``x0`` -- seeds the Newton iteration - ``prec`` -- precision OUTPUT: the square root of `f` EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x) sage: Q = H(0,0) sage: u,v = H.local_coord(Q,prec=100) sage: K = Qp(11,5) sage: HK = H.change_ring(K) sage: L.<a> = K.extension(x^20-11) sage: HL = H.change_ring(L) sage: S = HL(u(a),v(a)) sage: f = H.hyperelliptic_polynomials()[0] sage: y = HK.newton_sqrt( f(u(a)^11), a^11,5) sage: y^2 - f(u(a)^11) O(a^122) AUTHOR: - Jennifer Balakrishnan """ z = x0 loop_prec = (log(RR(prec)) / log(RR(2))).ceil() for i in range(loop_prec): z = (z + f / z) / 2 return z
def __init__(self, n, delta=0.01, m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``n`` - security parameter (integer > 0) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``m=2*n + 128`` as in [LP2011]_ (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import LindnerPeikert sage: LindnerPeikert(n=20) LWE(20, 2053, Discrete Gaussian sampler over the Integers with sigma = 3.600954 and c = 0, 'noise', 168) """ if m is None: m = 2*n + 128 # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # (c*exp((1-c**2)/2))**(2*n) == 2**-40 # log((c*exp((1-c**2)/2))**(2*n)) == -40*log(2) # (2*n)*log(c*exp((1-c**2)/2)) == -40*log(2) # 2*n*(log(c)+log(exp((1-c**2)/2))) == -40*log(2) # 2*n*(log(c)+(1-c**2)/2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) == -40*log(2) # 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = SR.var('c') c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP2011]_ s = sqrt(s_t_bound*floor(q/4)) # Transform s into stddev stddev = s/sqrt(2*pi.n()) D = DiscreteGaussianDistributionIntegerSampler(stddev) LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
def test_random_extension_fields(self, num=128): n = 7 k = 3 for i in range(num): p = 2 q = randint(log(n, p).N().ceil(), 64) # FIXME: 2**16 has sometimes errors due to some internal infinity problem if q == 16: continue o = p**q s = randint(0, o - 1) assert s == templ_generic(n, k, o, s) assert s == templ_generic(n, k, o, s, 'bw', None, 1)
def bkz_runtime_k_bkz2(k, n): """ Runtime estimation given ‘k‘ and assuming [CheNgu12]_ estimates are correct. The constants in this function were derived as follows based on Table 4 in [CheNgu12]_:: sage: dim = [100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250] sage: nodes = [39.0, 44.0, 49.0, 54.0, 60.0, 66.0, 72.0, 78.0, 84.0, 96.0, 99.0, 105.0, 111.0, 120.0, 127.0, 134.0] sage: times = [c + log(200,2).n() for c in nodes] sage: T = zip(dim, nodes) sage: var("a,b,c,k") sage: f = a*k*log(k, 2.0) + b*k + c sage: f = f.function(k) sage: f.subs(find_fit(T, f, solution_dict=True)) k |--> 0.270188776350190*k*log(k) - 1.0192050451318417*k + 16.10253135200765 .. [CheNgu12] Yuanmi Chen and Phong Q. Nguyen. BKZ 2.0: Better lattice security estimates ( Full Version). 2012. http://www.di.ens.fr/~ychen/research/Full_BKZ.pdf """ repeat = _sage_const_3 *log(n, _sage_const_2 ) - _sage_const_2 *log(k, _sage_const_2 ) + log(log(n, _sage_const_2 ), _sage_const_2 ) return RR(_sage_const_0p270188776350190 *k*log(k) - _sage_const_1p0192050451318417 *k + _sage_const_16p10253135200765 + repeat)
def power_spectral_density(freq): r""" Return the effective power spectral density (PSD) of the detector noise at a given frequency. INPUT: - ``freq`` -- frequency `f` (in `\mathrm{Hz}`) OUTPUT: - effective power spectral density `S(f)` (in `\mathrm{Hz}^{-1}`) EXAMPLES:: sage: from kerrgeodesic_gw import lisa_detector sage: Sn = lisa_detector.power_spectral_density sage: Sn(1.e-1) # tol 1.0e-13 3.3944027493062926e-39 sage: Sn(1.e-2) # tol 1.0e-13 2.738383947022306e-40 sage: Sn(1.e-3) # tol 1.0e-13 3.269807574220045e-38 """ global _psd_spline if not _psd_spline: data = [] file_name = os.path.join(os.path.dirname(__file__), "data/Sensitivity_LISA_SciRD1806_Alloc.dat") with open(file_name, "r") as data_file: for dline in data_file: f, s = dline.split('\t') data.append((log(RDF(f), 10), log(RDF(s), 10))) _psd_spline = spline(data) if freq < 1.e-5 or freq > 1.: raise ValueError("frequency {} Hz is out of range".format(freq)) freq = RDF(freq) return RDF(10)**(_psd_spline(log(freq, 10)))
def __init__(self, N, delta=0.01, m=None): """ Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where the modulus ``q`` and the ``stddev`` of the noise is chosen as in [LP2011]_. INPUT: - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2) - ``delta`` - error probability per symbol (default: 0.01) - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is used (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import RingLindnerPeikert sage: RingLindnerPeikert(N=16) RingLWE(16, 1031, Discrete Gaussian sampler for polynomials of degree < 8 with σ=2.803372 in each component, x^8 + 1, 'noise', 24) """ n = euler_phi(N) if m is None: m = 3*n # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40 # i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0 c = SR.var('c') c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10) # Upper bound on s**2/t s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n() # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_ q = next_prime(floor(2**round(log(256 / s_t_bound, 2)))) # Gaussian parameter as defined in [LP2011]_ s = sqrt(s_t_bound*floor(q/4)) # Transform s into stddev stddev = s/sqrt(2*pi.n()) D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, stddev) RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m)
def _derivative_(self, z, diff_param=None): r""" The derivative of `\operatorname{Li}(z) is `1/log(z)`. EXAMPLES:: sage: x = var('x') sage: f = log_integral_offset(x) sage: f.diff(x) 1/log(x) sage: f = log_integral_offset(x^2) sage: f.diff(x) 2*x/log(x^2) """ return 1 / log(z)
def _derivative_(self, z, diff_param=None): """ The derivative of `\operatorname{Li}(z) is `1/log(z)`. EXAMPLES:: sage: x = var('x') sage: f = log_integral_offset(x) sage: f.diff(x) 1/log(x) sage: f = log_integral_offset(x^2) sage: f.diff(x) 2*x/log(x^2) """ return 1/log(z)
def _2f1(a, b, c, z): """ Evaluation of 2F1(a, b; c; z), assuming a, b, c positive integers or half-integers """ if b == c: return (1 - z) ** (-a) if a == c: return (1 - z) ** (-b) if a == 0 or b == 0: return Integer(1) if a > b: a, b = b, a if b >= 2: F1 = _2f1(a, b - 1, c, z) F2 = _2f1(a, b - 2, c, z) q = (b - 1) * (z - 1) return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + (b - c - 1) * F2) / q) if c > 2: # how to handle this case? if a - c + 1 == 0 or b - c + 1 == 0: raise NotImplementedError F1 = _2f1(a, b, c - 1, z) F2 = _2f1(a, b, c - 2, z) r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) r2 = (c - 1) * (c - 2) * (1 - z) q = (a - c + 1) * (b - c + 1) * z return (r1 * F1 + r2 * F2) / q if (a, b, c) == (R12, 1, 2): return (2 - 2 * sqrt(1 - z)) / z if (a, b, c) == (1, 1, 2): return -log(1 - z) / z if (a, b, c) == (1, R32, R12): return (1 + z) / (1 - z) ** 2 if (a, b, c) == (1, R32, 2): return 2 * (1 / sqrt(1 - z) - 1) / z if (a, b, c) == (R32, 2, R12): return (1 + 3 * z) / (1 - z) ** 3 if (a, b, c) == (R32, 2, 1): return (2 + z) / (2 * (sqrt(1 - z) * (1 - z) ** 2)) if (a, b, c) == (2, 2, 1): return (1 + z) / (1 - z) ** 3 raise NotImplementedError
def _2f1(a, b, c, z): """ Evaluation of 2F1(a, b, c, z), assuming a, b, c positive integers or half-integers """ if b == c: return (1 - z)**(-a) if a == c: return (1 - z)**(-b) if a == 0 or b == 0: return Integer(1) if a > b: a, b = b, a if b >= 2: F1 = _2f1(a, b - 1, c, z) F2 = _2f1(a, b - 2, c, z) q = (b - 1) * (z - 1) return (((c - 2 * b + 2 + (b - a - 1) * z) * F1 + (b - c - 1) * F2) / q) if c > 2: # how to handle this case? if a - c + 1 == 0 or b - c + 1 == 0: raise NotImplementedError F1 = _2f1(a, b, c - 1, z) F2 = _2f1(a, b, c - 2, z) r1 = (c - 1) * (2 - c - (a + b - 2 * c + 3) * z) r2 = (c - 1) * (c - 2) * (1 - z) q = (a - c + 1) * (b - c + 1) * z return (r1 * F1 + r2 * F2) / q if (a, b, c) == (R12, 1, 2): return (2 - 2 * sqrt(1 - z)) / z if (a, b, c) == (1, 1, 2): return -log(1 - z) / z if (a, b, c) == (1, R32, R12): return (1 + z) / (1 - z)**2 if (a, b, c) == (1, R32, 2): return 2 * (1 / sqrt(1 - z) - 1) / z if (a, b, c) == (R32, 2, R12): return (1 + 3 * z) / (1 - z)**3 if (a, b, c) == (R32, 2, 1): return (2 + z) / (2 * (sqrt(1 - z) * (1 - z)**2)) if (a, b, c) == (2, 2, 1): return (1 + z) / (1 - z)**3 raise NotImplementedError
def newton_iteration(G, n): r"""Returns a truncated series `y = y(x)` satisfying .. math:: G(x,y(x)) \equiv 0 \bmod{x^r} where $r = \ceil{\log_2{n}}$. Based on the algorithm in [XXX]. Parameters ---------- G, x, y : polynomial A polynomial in `x` and `y`. n : int Requested degree of the series expansion. Notes ----- This algorithm returns the series up to order :math:`2^r > n`. Any choice of order below :math:`2^r` will return the same series. """ R = G.parent() x, y = R.gens() if n < 0: raise ValueError('Number of terms must be positive. (n=%d' % n) elif n == 0: return R(0) phi = G phiprime = phi.derivative(y) try: pi = R(x).polynomial(x) gi = R(0) si = R(phiprime(x, gi)).polynomial(x).inverse_mod(pi) except NotImplementedError: raise ValueError('Newton iteration for computing regular part of ' 'Puiseux expansion failed. Curve is most likely ' 'not regular at center.') r = ceil(log(n, 2)) for i in range(r): gi, si, pi = newton_iteration_step(phi, phiprime, gi, si, pi) return R(gi)
def newton_iteration(G, n): r"""Returns a truncated series `y = y(x)` satisfying .. math:: G(x,y(x)) \equiv 0 \bmod{x^r} where $r = \ceil{\log_2{n}}$. Based on the algorithm in [XXX]. Parameters ---------- G, x, y : polynomial A polynomial in `x` and `y`. n : int Requested degree of the series expansion. Notes ----- This algorithm returns the series up to order :math:`2^r > n`. Any choice of order below :math:`2^r` will return the same series. """ R = G.parent() x,y = R.gens() if n < 0: raise ValueError('Number of terms must be positive. (n=%d'%n) elif n == 0: return R(0) phi = G phiprime = phi.derivative(y) try: pi = R(x).polynomial(x) gi = R(0) si = R(phiprime(x,gi)).polynomial(x).inverse_mod(pi) except NotImplementedError: raise ValueError('Newton iteration for computing regular part of ' 'Puiseux expansion failed. Curve is most likely ' 'not regular at center.') r = ceil(log(n,2)) for i in range(r): gi,si,pi = newton_iteration_step(phi,phiprime,gi,si,pi) return R(gi)
def check_logarithmic_edge_equations_and_positivity(self, NumericalField): """ Check that the shapes have positive imaginary part and that the logarithmic gluing equations have small error. The shapes are coerced into the field given as argument before the logarithm is computed. It can be, e.g., a ComplexIntervalField. """ # For each edge for edge in self.mcomplex.Edges: # The complex interval arithmetic value of the logarithmic # version of the edge equation. log_sum = 0 # Iterate through edge embeddings for tet, perm in edge.embeddings(): shape = CuspCrossSectionBase._shape_for_edge_embedding( tet, perm) numerical_shape = NumericalField(shape) log_shape = log(numerical_shape) # Note that this is true for z in R, R < 0 as well, # but then it would fail for 1 - 1/z or 1 / (1-z) if not (log_shape.imag() > 0): raise ShapePositiveImaginaryPartNumericalVerifyError( numerical_shape) # Take logarithm and accumulate log_sum += log_shape twoPiI = NumericalField.pi() * NumericalField(2j) if not abs(log_sum - twoPiI) < 1e-7: raise EdgeEquationLogLiftNumericalVerifyError(log_sum)
def _N0_nodenominators(p, g, n): """ Return the necessary p-adic precision for the Frobenius matrix to deduce the characteristic polynomial of Frobenius using the Newton identities, using :meth:`charpoly_frobenius`, which assumes that the Frobenius matrix is integral, i.e., has no denominators. INPUT: - `p` - prime - `g` - genus - `n` - degree of residue field TESTS:: sage: sage.schemes.cyclic_covers.cycliccover_finite_field._N0_nodenominators(4999, 4, 5) 11 """ return max( ceil(log(2 * (2 * g) / ZZ(i), p) + (n * i) / ZZ(2)) for i in range(1, g + 1))
def __init__(self, n, secret_dist='uniform', m=None): """ Construct LWE instance parameterised by security paramter ``n`` where the modulus ``q`` and the ``stddev`` of the noise are chosen as in [Reg09]_. INPUT: - ``n`` - security paramter (integer > 0) - ``secret_dist`` - distribution of the secret. See documentation of :class:`LWE` for details (default='uniform') - ``m`` - number of allowed samples or ``None`` if no such limit exists (default: ``None``) EXAMPLES:: sage: Regev(n=20) LWE(20, 401, DiscreteGaussianSamplerRejection(1.915069, 401, 4), 'uniform', None) """ q = ZZ(next_prime(n**2)) s = RR(1 / (RR(n).sqrt() * log(n, 2)**2) * q) D = DiscreteGaussianSampler(s / sqrt(2 * pi.n()), q) LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m)
def __init__(self, n, secret_dist='uniform', m=None): """ Construct LWE instance parameterised by security parameter ``n`` where the modulus ``q`` and the ``stddev`` of the noise are chosen as in [Reg09]_. INPUT: - ``n`` - security parameter (integer > 0) - ``secret_dist`` - distribution of the secret. See documentation of :class:`LWE` for details (default='uniform') - ``m`` - number of allowed samples or ``None`` if no such limit exists (default: ``None``) EXAMPLES:: sage: from sage.crypto.lwe import Regev sage: Regev(n=20) LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 1.915069 and c = 401, 'uniform', None) """ q = ZZ(next_prime(n**2)) s = RR(1/(RR(n).sqrt() * log(n, 2)**2) * q) D = DiscreteGaussianDistributionIntegerSampler(s/sqrt(2*pi.n()), q) LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m)
def gghlite_latex_table(L, K, **kwds): """ Generate a table with parameter estimates for ‘λ P L‘ and ‘κ P K‘. :param L: a list of ‘λ‘ :param K: a list of ‘κ‘ :returns: a string, ready to be pasted into TeX """ ret = [] for l in L: for k in K: line = [] current = gghlite_brief(l, k, **kwds) line.append("%3d" % current[u"λ"]) line.append("%3d" % current[u"κ"]) line.append("$2^{%2d}$" % log(current["n"], _sage_const_2 )) t = u"$«2^{%7.1f}$" % log(current["q"], _sage_const_2 ).n() line.append(u"%9s" % (t,)) t = u"$«2^{%4.1f}$" % log(current["|enc|"], _sage_const_2 ).n() line.append(u"%9s" % (t,)) t = u"$«2^{%4.1f}$" % log(current["|par|"], _sage_const_2 ).n() line.append(u"%9s" % (t,)) line.append("%8.6f" % current[u"δ_0"]) t = u"$«2^{%5.1f}$" % log(current[u"bkz2"], _sage_const_2 ) line.append(u"%9s" % (t,)) t = u"$«2^{%5.1f}$" % log(current[u"sieve"], _sage_const_2 ) line.append(u"%9s" % (t,)) ret.append(u" & ".join(line) + "\\\\") ret.append(r"\midrule") header = [] header.append(r"\begin{tabular*}{0.75\textwidth}{@{\extracolsep{\fill}} " + ("r" * _sage_const_9 ) + "}") header.append(r"\toprule") line = u"$λ$ & $κ$ & $n$ & $q$ & \\encs & \\pars & $δ_0$ & BKZ Enum & BKZ Sieve\\\\" header.append(line) header.append(r"\midrule") ret = header + ret ret.append(r"\end{tabular*}") return "\n".join(ret)
def gghlite_latex_table(L, K, **kwds): """ Generate a table with parameter estimates for ‘λ P L‘ and ‘κ P K‘. :param L: a list of ‘λ‘ :param K: a list of ‘κ‘ :returns: a string, ready to be pasted into TeX """ ret = [] for l in L: for k in K: line = [] current = gghlite_brief(l, k, **kwds) line.append("%3d" % current[u"λ"]) line.append("%3d" % current[u"κ"]) line.append("$2^{%2d}$" % log(current["n"], _sage_const_2)) t = u"$«2^{%7.1f}$" % log(current["q"], _sage_const_2).n() line.append(u"%9s" % (t, )) t = u"$«2^{%4.1f}$" % log(current["|enc|"], _sage_const_2).n() line.append(u"%9s" % (t, )) t = u"$«2^{%4.1f}$" % log(current["|par|"], _sage_const_2).n() line.append(u"%9s" % (t, )) line.append("%8.6f" % current[u"δ_0"]) t = u"$«2^{%5.1f}$" % log(current[u"bkz2"], _sage_const_2) line.append(u"%9s" % (t, )) t = u"$«2^{%5.1f}$" % log(current[u"sieve"], _sage_const_2) line.append(u"%9s" % (t, )) ret.append(u" & ".join(line) + "\\\\") ret.append(r"\midrule") header = [] header.append(r"\begin{tabular*}{0.75\textwidth}{@{\extracolsep{\fill}} " + ("r" * _sage_const_9) + "}") header.append(r"\toprule") line = u"$λ$ & $κ$ & $n$ & $q$ & \\encs & \\pars & $δ_0$ & BKZ Enum & BKZ Sieve\\\\" header.append(line) header.append(r"\midrule") ret = header + ret ret.append(r"\end{tabular*}") return "\n".join(ret)
def _name_maker(self, names): r""" Helper function to create names of elements of algebraic structures. INPUT: Identical to the input for :class:`OperationTable` and :meth:`change_names`, so look there for details. OUTPUT: - ``width`` - an integer giving the maximum width of the strings describing the elements. This is used for formatting the ASCII version of the table. - ``name_list`` - a list of strings naming the elements, in the same order as given by the :meth:`list` method. - ``name_dict`` - a dictionary giving the correspondence between the strings and the actual elements. So the keys are the strings and the values are the elements of the structure. EXAMPLES: This routine is tested extensively in the :class:`OperationTable` and :meth:`change_names` methods. So we just just demonstrate the nature of the output here. :: sage: from sage.matrix.operation_table import OperationTable sage: G=SymmetricGroup(3) sage: T=OperationTable(G, operator.mul) sage: w, l, d = T._name_maker('letters') sage: w 1 sage: l[0] 'a' sage: d['a'] () TESTS: We test the error conditions here, rather than as part of the doctests for the :class:`OperationTable` and :meth:`change_names` methods that rely on this one. :: sage: from sage.matrix.operation_table import OperationTable sage: G=AlternatingGroup(3) sage: T=OperationTable(G, operator.mul) sage: T._name_maker(['x']) Traceback (most recent call last): ... ValueError: list of element names must be the same size as the set, 1 != 3 sage: T._name_maker(['x', 'y', 4]) Traceback (most recent call last): ... ValueError: list of element names must only contain strings, not 4 sage: T._name_maker('blatzo') Traceback (most recent call last): ... ValueError: element names must be a list, or one of the keywords: 'letters', 'digits', 'elements' """ from sage.functions.log import log name_list = [] if names == 'digits': if self._n == 0 or self._n == 1: width = 1 else: width = int(log(self._n - 1, 10)) + 1 for i in range(self._n): name_list.append('{0:0{1}d}'.format(i, width)) elif names == 'letters': from string import ascii_lowercase as letters from sage.rings.integer import Integer base = len(letters) if self._n == 0 or self._n == 1: width = 1 else: width = int(log(self._n - 1, base)) + 1 for i in range(self._n): places = Integer(i).digits(base=base, digits=letters, padto=width) places.reverse() name_list.append(''.join(places)) elif names == 'elements': width = 0 for e in self._elts: estr = repr(e) if len(estr) > width: width = len(estr) name_list.append(estr) elif isinstance(names, list): if len(names) != self._n: raise ValueError( 'list of element names must be the same size as the set, %s != %s' % (len(names), self._n)) width = 0 for str in names: if not isinstance(str, six.string_types): raise ValueError( 'list of element names must only contain strings, not %s' % str) if len(str) > width: width = len(str) name_list.append(str) else: raise ValueError( "element names must be a list, or one of the keywords: 'letters', 'digits', 'elements'" ) name_dict = {} for i in range(self._n): name_dict[name_list[i]] = self._elts[i] return width, name_list, name_dict
def encrypt(self, P, K, seed=None): r""" Apply the Blum-Goldwasser scheme to encrypt the plaintext ``P`` using the public key ``K``. INPUT: - ``P`` -- a non-empty string of plaintext. The string ``""`` is an empty string, whereas ``" "`` is a string consisting of one white space character. The plaintext can be a binary string or a string of ASCII characters. Where ``P`` is an ASCII string, then ``P`` is first encoded as a binary string prior to encryption. - ``K`` -- a public key, which is the product of two Blum primes. - ``seed`` -- (default: ``None``) if `p` and `q` are Blum primes and `n = pq` is a public key, then ``seed`` is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. If ``seed=None``, then the function would generate its own random quadratic residue in `(\ZZ/n\ZZ)^{\ast}`. Where a value for ``seed`` is provided, it is your responsibility to ensure that the seed is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. OUTPUT: - The ciphertext resulting from encrypting ``P`` using the public key ``K``. The ciphertext `C` is of the form `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i` is a sub-block of binary string and `x_{t+1}` is the result of the `t+1`-th iteration of the Blum-Blum-Shub algorithm. ALGORITHM: The Blum-Goldwasser encryption algorithm is described in Algorithm 8.56, page 309 of [MenezesEtAl1996]_. The algorithm works as follows: #. Let `n` be a public key, where `n = pq` is the product of two distinct Blum primes `p` and `q`. #. Let `k = \lfloor \log_2(n) \rfloor` and `h = \lfloor \log_2(k) \rfloor`. #. Let `m = m_1 m_2 \cdots m_t` be the message (plaintext) where each `m_i` is a binary string of length `h`. #. Choose a random seed `x_0`, which is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. That is, choose a random `r \in (\ZZ/n\ZZ)^{\ast}` and compute `x_0 = r^2 \bmod n`. #. For `i` from 1 to `t`, do: #. Let `x_i = x_{i-1}^2 \bmod n`. #. Let `p_i` be the `h` least significant bits of `x_i`. #. Let `c_i = p_i \oplus m_i`. #. Compute `x_{t+1} = x_t^2 \bmod n`. #. The ciphertext is `c = (c_1, c_2, \dots, c_t, x_{t+1})`. The value `h` in the algorithm is the sub-block length. If the binary string representing the message cannot be divided into blocks of length `h` each, then other sub-block lengths would be used instead. The sub-block lengths to fall back on are in the following order: 16, 8, 4, 2, 1. EXAMPLES: The following encryption example is taken from Example 8.57, pages 309--310 of [MenezesEtAl1996]_. Here, we encrypt a binary string:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: p = 499; q = 547; n = p * q sage: P = "10011100000100001100" sage: C = bg.encrypt(P, n, seed=159201); C ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) Convert the ciphertext sub-blocks into a binary string:: sage: bin = BinaryStrings() sage: bin(flatten(C[0])) 00100000110011100100 Now encrypt an ASCII string. The result is random; no seed is provided to the encryption function so the function generates its own random seed:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: K = 32300619509 sage: P = "Blum-Goldwasser encryption" sage: bg.encrypt(P, K) # random ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \ [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \ [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \ [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \ [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \ [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \ [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \ [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \ [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \ [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \ [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279) TESTS: The plaintext cannot be an empty string. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.encrypt("", 3) Traceback (most recent call last): ... ValueError: The plaintext cannot be an empty string. """ # sanity check if P == "": raise ValueError("The plaintext cannot be an empty string.") n = K k = floor(log(n, base=2)) h = floor(log(k, base=2)) bin = BinaryStrings() M = "" try: # the plaintext is a binary string M = bin(P) except TypeError: # encode the plaintext as a binary string # An exception might be raised here if P cannot be encoded as a # binary string. M = bin.encoding(P) # the number of plaintext sub-blocks; each sub-block has length h t = 0 try: # Attempt to use t and h values from the algorithm described # in [MenezesEtAl1996]. t = len(M) / h # If the following raises an exception, then we can't use # the t and h values specified by [MenezesEtAl1996]. mod(len(M), t) # fall back to using other sub-block lengths except TypeError: # sub-blocks of length h = 16 if mod(len(M), 16) == 0: h = 16 t = len(M) // h # sub-blocks of length h = 8 elif mod(len(M), 8) == 0: h = 8 t = len(M) // h # sub-blocks of length h = 4 elif mod(len(M), 4) == 0: h = 4 t = len(M) // h # sub-blocks of length h = 2 elif mod(len(M), 2) == 0: h = 2 t = len(M) // h # sub-blocks of length h = 1 else: h = 1 t = len(M) // h # If no seed is provided, select a random seed. x0 = seed if seed is None: zmod = IntegerModRing(n) # K = n = pq r = zmod.random_element().lift() while gcd(r, n) != 1: r = zmod.random_element().lift() x0 = power_mod(r, 2, n) # perform the encryption to_int = lambda x: int(str(x)) C = [] for i in xrange(t): x1 = power_mod(x0, 2, n) p = least_significant_bits(x1, h) # xor p with a sub-block of length h. There are t sub-blocks of # length h each. C.append( list(map(xor, p, [to_int(_) for _ in M[i * h:(i + 1) * h]]))) x0 = x1 x1 = power_mod(x0, 2, n) return (C, x1)
def subexpressions_list(f, pars=None): """ Construct the lists with the intermediate steps on the evaluation of the function. INPUT: - ``f`` -- a symbolic function of several components. - ``pars`` -- a list of the parameters that appear in the function this should be the symbolic constants that appear in f but are not arguments. OUTPUT: - a list of the intermediate subexpressions that appear in the evaluation of f. - a list with the operations used to construct each of the subexpressions. each element of this list is a tuple, formed by a string describing the operation made, and the operands. For the trigonometric functions, some extra expressions will be added. These extra expressions will be used later to compute their derivatives. EXAMPLES:: sage: from sage.interfaces.tides import subexpressions_list sage: var('x,y') (x, y) sage: f(x,y) = [x^2+y, cos(x)/log(y)] sage: subexpressions_list(f) ([x^2, x^2 + y, sin(x), cos(x), log(y), cos(x)/log(y)], [('mul', x, x), ('add', y, x^2), ('sin', x), ('cos', x), ('log', y), ('div', log(y), cos(x))]) :: sage: f(a)=[cos(a), arctan(a)] sage: from sage.interfaces.tides import subexpressions_list sage: subexpressions_list(f) ([sin(a), cos(a), a^2, a^2 + 1, arctan(a)], [('sin', a), ('cos', a), ('mul', a, a), ('add', 1, a^2), ('atan', a)]) :: sage: from sage.interfaces.tides import subexpressions_list sage: var('s,b,r') (s, b, r) sage: f(t,x,y,z)= [s*(y-x),x*(r-z)-y,x*y-b*z] sage: subexpressions_list(f,[s,b,r]) ([-y, x - y, s*(x - y), -s*(x - y), -z, r - z, (r - z)*x, -y, (r - z)*x - y, x*y, b*z, -b*z, x*y - b*z], [('mul', -1, y), ('add', -y, x), ('mul', x - y, s), ('mul', -1, s*(x - y)), ('mul', -1, z), ('add', -z, r), ('mul', x, r - z), ('mul', -1, y), ('add', -y, (r - z)*x), ('mul', y, x), ('mul', z, b), ('mul', -1, b*z), ('add', -b*z, x*y)]) :: sage: var('x, y') (x, y) sage: f(x,y)=[exp(x^2+sin(y))] sage: from sage.interfaces.tides import * sage: subexpressions_list(f) ([x^2, sin(y), cos(y), x^2 + sin(y), e^(x^2 + sin(y))], [('mul', x, x), ('sin', y), ('cos', y), ('add', sin(y), x^2), ('exp', x^2 + sin(y))]) """ from sage.functions.trig import sin, cos, arcsin, arctan, arccos variables = f[0].arguments() if not pars: parameters = [] else: parameters = pars varpar = list(parameters) + list(variables) F = symbolic_expression([i(*variables) for i in f]).function(*varpar) lis = flatten([fast_callable(i,vars=varpar).op_list() for i in F], max_level=1) stack = [] const =[] stackcomp=[] detail=[] for i in lis: if i[0] == 'load_arg': stack.append(varpar[i[1]]) elif i[0] == 'ipow': if i[1] in NN: basis = stack[-1] for j in range(i[1]-1): a=stack.pop(-1) detail.append(('mul', a, basis)) stack.append(a*basis) stackcomp.append(stack[-1]) else: detail.append(('pow',stack[-1],i[1])) stack[-1]=stack[-1]**i[1] stackcomp.append(stack[-1]) elif i[0] == 'load_const': const.append(i[1]) stack.append(i[1]) elif i == 'mul': a=stack.pop(-1) b=stack.pop(-1) detail.append(('mul', a, b)) stack.append(a*b) stackcomp.append(stack[-1]) elif i == 'div': a=stack.pop(-1) b=stack.pop(-1) detail.append(('div', a, b)) stack.append(b/a) stackcomp.append(stack[-1]) elif i == 'add': a=stack.pop(-1) b=stack.pop(-1) detail.append(('add',a,b)) stack.append(a+b) stackcomp.append(stack[-1]) elif i == 'pow': a=stack.pop(-1) b=stack.pop(-1) detail.append(('pow', b, a)) stack.append(b**a) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='log': a=stack.pop(-1) detail.append(('log', a)) stack.append(log(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='exp': a=stack.pop(-1) detail.append(('exp', a)) stack.append(exp(a)) stackcomp.append(stack[-1]) elif i[0] == 'py_call' and str(i[1])=='sin': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(sin(a)) elif i[0] == 'py_call' and str(i[1])=='cos': a=stack.pop(-1) detail.append(('sin', a)) detail.append(('cos', a)) stackcomp.append(sin(a)) stackcomp.append(cos(a)) stack.append(cos(a)) elif i[0] == 'py_call' and str(i[1])=='tan': a=stack.pop(-1) b = sin(a) c = cos(a) detail.append(('sin', a)) detail.append(('cos', a)) detail.append(('div', b, c)) stackcomp.append(b) stackcomp.append(c) stackcomp.append(b/c) stack.append(b/c) elif i[0] == 'py_call' and str(i[1])=='arctan': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('add', 1, a*a)) detail.append(('atan', a)) stackcomp.append(a*a) stackcomp.append(1+a*a) stackcomp.append(arctan(a)) stack.append(arctan(a)) elif i[0] == 'py_call' and str(i[1])=='arcsin': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('mul', -1, a*a)) detail.append(('add', 1, -a*a)) detail.append(('pow', 1- a*a, 0.5)) detail.append(('asin', a)) stackcomp.append(a*a) stackcomp.append(-a*a) stackcomp.append(1-a*a) stackcomp.append(sqrt(1-a*a)) stackcomp.append(arcsin(a)) stack.append(arcsin(a)) elif i[0] == 'py_call' and str(i[1])=='arccos': a=stack.pop(-1) detail.append(('mul', a, a)) detail.append(('mul', -1, a*a)) detail.append(('add', 1, -a*a)) detail.append(('pow', 1- a*a, 0.5)) detail.append(('mul', -1, sqrt(1-a*a))) detail.append(('acos', a)) stackcomp.append(a*a) stackcomp.append(-a*a) stackcomp.append(1-a*a) stackcomp.append(sqrt(1-a*a)) stackcomp.append(-sqrt(1-a*a)) stackcomp.append(arccos(a)) stack.append(arccos(a)) elif i[0] == 'py_call' and 'sqrt' in str(i[1]): a=stack.pop(-1) detail.append(('pow', a, 0.5)) stackcomp.append(sqrt(a)) stack.append(sqrt(a)) elif i == 'neg': a = stack.pop(-1) detail.append(('mul', -1, a)) stack.append(-a) stackcomp.append(-a) return stackcomp,detail
def encrypt(self, P, K, seed=None): r""" Apply the Blum-Goldwasser scheme to encrypt the plaintext ``P`` using the public key ``K``. INPUT: - ``P`` -- a non-empty string of plaintext. The string ``""`` is an empty string, whereas ``" "`` is a string consisting of one white space character. The plaintext can be a binary string or a string of ASCII characters. Where ``P`` is an ASCII string, then ``P`` is first encoded as a binary string prior to encryption. - ``K`` -- a public key, which is the product of two Blum primes. - ``seed`` -- (default: ``None``) if `p` and `q` are Blum primes and `n = pq` is a public key, then ``seed`` is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. If ``seed=None``, then the function would generate its own random quadratic residue in `(\ZZ/n\ZZ)^{\ast}`. Where a value for ``seed`` is provided, it is your responsibility to ensure that the seed is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. OUTPUT: - The ciphertext resulting from encrypting ``P`` using the public key ``K``. The ciphertext `C` is of the form `C = (c_1, c_2, \dots, c_t, x_{t+1})`. Each `c_i` is a sub-block of binary string and `x_{t+1}` is the result of the `t+1`-th iteration of the Blum-Blum-Shub algorithm. ALGORITHM: The Blum-Goldwasser encryption algorithm is described in Algorithm 8.56, page 309 of [MvOV1996]_. The algorithm works as follows: #. Let `n` be a public key, where `n = pq` is the product of two distinct Blum primes `p` and `q`. #. Let `k = \lfloor \log_2(n) \rfloor` and `h = \lfloor \log_2(k) \rfloor`. #. Let `m = m_1 m_2 \cdots m_t` be the message (plaintext) where each `m_i` is a binary string of length `h`. #. Choose a random seed `x_0`, which is a quadratic residue in the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. That is, choose a random `r \in (\ZZ/n\ZZ)^{\ast}` and compute `x_0 = r^2 \bmod n`. #. For `i` from 1 to `t`, do: #. Let `x_i = x_{i-1}^2 \bmod n`. #. Let `p_i` be the `h` least significant bits of `x_i`. #. Let `c_i = p_i \oplus m_i`. #. Compute `x_{t+1} = x_t^2 \bmod n`. #. The ciphertext is `c = (c_1, c_2, \dots, c_t, x_{t+1})`. The value `h` in the algorithm is the sub-block length. If the binary string representing the message cannot be divided into blocks of length `h` each, then other sub-block lengths would be used instead. The sub-block lengths to fall back on are in the following order: 16, 8, 4, 2, 1. EXAMPLES: The following encryption example is taken from Example 8.57, pages 309--310 of [MvOV1996]_. Here, we encrypt a binary string:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: p = 499; q = 547; n = p * q sage: P = "10011100000100001100" sage: C = bg.encrypt(P, n, seed=159201); C ([[0, 0, 1, 0], [0, 0, 0, 0], [1, 1, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0]], 139680) Convert the ciphertext sub-blocks into a binary string:: sage: bin = BinaryStrings() sage: bin(flatten(C[0])) 00100000110011100100 Now encrypt an ASCII string. The result is random; no seed is provided to the encryption function so the function generates its own random seed:: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: K = 32300619509 sage: P = "Blum-Goldwasser encryption" sage: bg.encrypt(P, K) # random ([[1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0], \ [1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0], \ [0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1], \ [1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0], \ [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1], \ [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], \ [1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], \ [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0], \ [1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1], \ [1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1], \ [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0], \ [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1]], 3479653279) TESTS: The plaintext cannot be an empty string. :: sage: from sage.crypto.public_key.blum_goldwasser import BlumGoldwasser sage: bg = BlumGoldwasser() sage: bg.encrypt("", 3) Traceback (most recent call last): ... ValueError: The plaintext cannot be an empty string. """ # sanity check if P == "": raise ValueError("The plaintext cannot be an empty string.") n = K k = floor(log(n, base=2)) h = floor(log(k, base=2)) bin = BinaryStrings() M = "" try: # the plaintext is a binary string M = bin(P) except TypeError: # encode the plaintext as a binary string # An exception might be raised here if P cannot be encoded as a # binary string. M = bin.encoding(P) # the number of plaintext sub-blocks; each sub-block has length h t = 0 try: # Attempt to use t and h values from the algorithm described # in [MvOV1996]. t = len(M) / h # If the following raises an exception, then we can't use # the t and h values specified by [MvOV1996]. mod(len(M), t) # fall back to using other sub-block lengths except TypeError: # sub-blocks of length h = 16 if mod(len(M), 16) == 0: h = 16 t = len(M) // h # sub-blocks of length h = 8 elif mod(len(M), 8) == 0: h = 8 t = len(M) // h # sub-blocks of length h = 4 elif mod(len(M), 4) == 0: h = 4 t = len(M) // h # sub-blocks of length h = 2 elif mod(len(M), 2) == 0: h = 2 t = len(M) // h # sub-blocks of length h = 1 else: h = 1 t = len(M) // h # If no seed is provided, select a random seed. x0 = seed if seed is None: zmod = IntegerModRing(n) # K = n = pq r = zmod.random_element().lift() while gcd(r, n) != 1: r = zmod.random_element().lift() x0 = power_mod(r, 2, n) # perform the encryption to_int = lambda x: int(str(x)) C = [] for i in range(t): x1 = power_mod(x0, 2, n) p = least_significant_bits(x1, h) # xor p with a sub-block of length h. There are t sub-blocks of # length h each. C.append(list(map(xor, p, [to_int(_) for _ in M[i*h : (i+1)*h]]))) x0 = x1 x1 = power_mod(x0, 2, n) return (C, x1)