from sage.all_cmdline import * from CocksPinchVariant import * import sage.rings.integer from BLS12 import * # from BLS24 import * from KSS16 import * # from KSS18 import * from BN import * from MNT6 import * from final_expo_k57 import * from enumerate_sparse_T import bit_positions_2naf, bit_positions mystery_201903151748_simon_a_raison=False # TODO: take into account h_t not always being 0 in the k=6 or k=8 # cases... [WIP for k=8 -- want to automate a bit] Qmsi = QQ['m,s,inv'] m,s,inv = Qmsi.gens() #F_{p^k} arithmetic cost def cost_m(k) : # return the cost of a multiplication over F_{p^k} if k%2 == 0 : return 3*cost_m(k//2) elif k%3 == 0 : return 6*cost_m(k//3) elif k == 1: return m elif k == 5 : return 13*m elif k == 7 : return 22*m else : return 'not done for this embedding degree' # special cases def densexsparse_m6(k): assert k%6==0 return 13*cost_m(k//6) # aurore thesis def densexsparse_m8(k): assert k%8==0 return 8*cost_m(k//4) # ??????????????????????BarDuq18??? sparse_m12 = 13*cost_m(2) # BarDuq 39 = 3*13 and 3 is the cost of Mp2 = 3 Mp sparse_m16 = 8*cost_m(4) def cost_s(k) : # return the cost of a square over F_{p^k} if k%2 == 0 : return 2*cost_m(k//2) elif k%3 == 0 : return 2*cost_m(k//3) + 3 * cost_s(k//3) elif k == 1: return m elif k == 5 : return 13*m elif k == 7 : return 22*m else : return 'not done for this embedding degree' # special cases cyclo_s6 = 3*cost_s(2) # eprint 2009/565 cyclo_s8 = 2*cost_s(4) # eprint 2009/565 section 3.1 compr_s12 = 12*m # BarDuq cyclo_s12 = 18*m cyclo_s16 = 2*cost_s(8) def cost_prenorm(k,ell): # returns the cost of computing # prenorm_{j,\ell}(a)=a^(1+p^j+...+p^{j*(ell-1)}) # in F_p^k. This is used only for k odd. # (we have norm(a) = a * prenorm_{1,k-1}(a^p) ) # Note that the cost is independent of j. if ell == 1: return 0 elif is_prime(ell): return cost_m(k) + cost_f(k) + cost_prenorm(k, ell-1) else: # It's not necessarily the best strategy. E.g. for k=67, we're # led to compute prenorm for ell=33 (ell=66 at first, but let's # focus on the sub call). That is in fact best done by going to # 32 first (cost F+M, then 5F+5M). In contrast, the factoring # approach below costs 7F+7M... mu = factor(ell)[0][0] return cost_prenorm(k, mu) + cost_prenorm(k, ell // mu) # prenorm_{j,u*v}(a) = prenorm_{j*v,u}(prenorm_{j,v}(a)) # and raising to the power p^{j*v} costs exacly one frobenius. def cost_i(k) : # return the cost of an inversion over F_{p^k} if k%2 == 0 : return 2*cost_s(k//2) + 2*cost_m(k//2) + cost_i(k//2) elif k%3 == 0 : return 3*cost_s(k//3) + 9*cost_m(k//3) + cost_i(k//3) elif k == 1: return inv elif k == 5 : # u1 = frob(a) # u3 = frob(frob(u1)) # costs only 1 frob # v = u1 * u3 # v = a^(p+p^3) # w = frob(v) # v = a^(p^2+p^4) # b = v * w # n = coeff(a,0)*coeff(b,0) + alpha*sum([coeff(a,i)*coeff(b,k-i) for i in range(1,k)]) # ni = inv(n) # ai = ni * a return 3*cost_f(k) + 2*cost_m(k) + inv + 2*k*m # elif k == 7 : # # u1 = frob(a) # # u4 = frob(frob(frob(u1))) # # v = u1 * u4 # v = a^(p+p^4) # # w = frob(v) # v = a^(p^2+p^5) # # z = frob(w) # v = a^(p^3+p^6) # # b = v * w * z # # n = coeff(a,0)*coeff(b,0) + alpha*sum([coeff(a,i)*coeff(b,k-i) for i in range(1,k)]) # # ni = inv(n) # # ai = ni * a return 4*cost_f(k) + 3*cost_m(k) + inv + 2*k*m elif k%2 == 1: # generalization of the above. # Note that we can go further. If (k-1)/2 >= 4, then we may apply # the same trick to save some more multiplications. return cost_f(k) + cost_prenorm(k, k-1) + inv + 2*k*m # elif k == 5 or k == 7 : # return (k-1)*cost_f(k) + (k-2)*cost_m(k) + inv + 2*k*m else : return 'not done for this embedding degree' def cost_f(k, d=1) : # return the cost of a d-Frobenius over F_{p^k} assert k % d == 0 if (k//d) % 2 == 0 : # for F_{p^{k/d}} a tower defined by binomials, the multipliers in # the Frobenius (p^d-th power) expressions are all powers of a # k/d-th root of unity. If k/d is even, one of them is -1. At any # rate, this root of unity boils down to a scalar, therefore we # don't need cost_m(d) but really d * cost_m(1) return (k//d-2) * d * cost_m(1) else: return (k//d-1) * d * cost_m(1) def cost_i_and_f(k) : if k % 2 == 0 or k % 3 == 0: return cost_i(k) + cost_f(k) elif k == 5 or k == 7: # Then we know that the inversion computes the Frobenius anyway. return cost_i(k) else : return 'not done for this embedding degree' def table_costFpk(k_list): K = ''.join(["&%d"%k for k in k_list]) M = ''.join(["&%s"%cost_m(k)(m=1)+r"\bfm" for k in k_list]) S = ''.join(["&%s"%cost_s(k)(m=1)+r"\bfm" for k in k_list]) F = ''.join(["&%s"%cost_f(k)(m=1)+r"\bfm" for k in k_list]) sc_dict = {6:cyclo_s6, 8:cyclo_s8, 12:cyclo_s12, 16:cyclo_s16} SC = ''.join(["&%s"%sc_dict[k](m=1)+r"\bfm" if k in sc_dict else "&" for k in k_list]) I0 = ''.join(["&%s"%cost_i(k)(m=1,inv=0)+r"\bfm" for k in k_list]) I1 = ''.join(["&%s"%cost_i(k)(m=1,inv=25)+r"\bfm" for k in k_list]) F = F.replace(r"&1\bfm",r"&\bfm") M = M.replace(r"&1\bfm",r"&\bfm") S = S.replace(r"&1\bfm",r"&\bfm") I0 = I0.replace(r"&0\bfm",r"&0") F = F.replace(r"&0\bfm",r"&0") contents = [ r"$$\begin{array}{|c|" + "c|" * len(k_list) + "}", r"\hline", "k" + K + r"\\", r"\hline", r"\bfm_k" + M + r"\\", r"\bfs_k" + S + r"\\", r"\bff_k" + F + r"\\", r"\bfs_k^{\text{cyclo}}" + SC + r"\\", r"\bfi_k-\bfi_1" + I0 + r"\\", r"\text{\bfi_k, with \bfi_1=25\bfm}" + I1 + r"\\", r"\hline", r"\end{array}$$", ] print "% This table is generated by:" print "%% PYTHONPATH=cocks-pinch-variant/ sage -c 'load(\"cocks-pinch-variant/cost_pairing.py\"); table_costFpk(%s)'" % (k_list) for s in contents: print s def Hw(x) : return len(bit_positions_2naf(x)) proof.arithmetic(False) C5=CocksPinchVariantResult(5,10000000147,0xe000000000008000,1,ht=3,hy=0x11e36418c7c8b454,max_B1=600) C6=CocksPinchVariantResult(6,3,0xefffffffffffffe00000000000000000,1,ht=-1,hy=0xffbbffffffffffffc020,allowed_cofactor=420,allowed_size_cofactor=10,max_B1=600) C7=CocksPinchVariantResult(7,20,0x5fffb820248,6,ht=-2,allowed_cofactor=1232,allowed_size_cofactor=10,max_B1=600) #C8=CocksPinchVariantResult(8,4,0xffffffffeff7c200,5,ht=5,hy=-0xd700,allowed_cofactor=420,allowed_size_cofactor=10,max_B1=600) C8=CocksPinchVariantResult(8,4,0xffc00020fffffffc,1,ht=1,hy=0xdc04,allowed_cofactor=420,allowed_size_cofactor=10,max_B1=600) CMNT6=MNT6(u=873723667900031396506414143162332159382674816702805606206979732381600254701804231398281169537138620,a=209307816050232262803672282154940341360062431838092388077917610639183322072827259682607127795420474686833003315766797546568469776750651773087882545447646552119008299040167030969895802846139484415144,b=2319663192174958547181026340141410918530227127674793888869119262391240421488942353013995765010333162065568990954578077256489549792305772041454141172011940607053889955897003759289947924385489341215143,D=8317003,c=1) CBN446=BN(u=2**110+2**36+1,b=2**8+1) CBLS446=BLS12(u=-(2**74+2**73+2**63+2**57+2**50+2**17+1),b=1) CBN12=BN(eval(preparse("2^114+2^101-2^14-1"))) CBLS12=BLS12(eval(preparse("-2^77+2^50+2^33"))) CKSS16=KSS16(eval(preparse("2^35-2^32-2^18+2^8+1"))) C1=Integer(3072) def finite_field_cost(logp): #time_m words = ceil(RR(logp)/64) if words == 5 : time_m = 35 # relic benchmark if words == 6 : time_m = 65 # relic benchmark if words == 7 : time_m = 85 # relic benchmark if words == 8 : time_m = 106 # relic benchmark elif words == 9 : time_m = 129 # relic benchmark elif words == 10 : time_m = 154 # relic benchmark elif words == 11 : time_m = 1.5*11**2 elif words == 48 : time_m = 4882 # gmp benchmark return time_m def is_one_of_our_known_pairing_friendly_curves(C): return isinstance(C, BN) or \ isinstance(C, BLS12) or \ isinstance(C, KSS16) or \ isinstance(C, MNT6) or \ False; # isinstance(C, BLS24) or \ # isinstance(C, KSS18) or \ def polymorphic_get_logp(C): if is_one_of_our_known_pairing_friendly_curves(C): return C.p().nbits() elif isinstance(C, CocksPinchVariantResult): return ZZ(C.p).nbits() elif isinstance(C, Integer): return C else: raise ValueError("not implemented") def polymorphic_get_logr(C): if is_one_of_our_known_pairing_friendly_curves(C): return C.r().nbits() elif isinstance(C, CocksPinchVariantResult): return ZZ(C.r).nbits() elif isinstance(C, Integer): return 256 else: raise ValueError("not implemented") def polymorphic_get_name(C): if isinstance(C, CocksPinchVariantResult): return "$k=%s$" % C.k elif isinstance(C, BN): return 'BN' elif isinstance(C, MNT6): return 'MNT6' elif isinstance(C, BLS12): return 'BLS12' # elif isinstance(C, BLS24): # return 'BLS24' elif isinstance(C, KSS16): return 'KSS16' # elif isinstance(C, KSS18): # return 'KSS18' elif isinstance(C, Integer): return '$k=1$' else: raise ValueError("not implemented") def polymorphic_get_miller_loop_length(C): if isinstance(C, CocksPinchVariantResult): return C.T elif isinstance(C, BN): return 6*C.u()+2 elif isinstance(C, BLS12): # or isinstance(C, BLS24): return C.u() elif isinstance(C, KSS16): # or isinstance(C, KSS18): return C.u() elif isinstance(C, MNT6): return C.tr() - 1 elif isinstance(C, Integer): raise ValueError("not implemented") else: raise ValueError("not implemented") def polymorphic_get_embedding_degree(C): if isinstance(C, CocksPinchVariantResult): return C.k elif is_one_of_our_known_pairing_friendly_curves(C): return C.k() elif isinstance(C, Integer): return 1 else: raise ValueError("not implemented") def polymorphic_get_fD(C): if isinstance(C, CocksPinchVariantResult): return C.fD elif is_one_of_our_known_pairing_friendly_curves(C): return C.D() elif isinstance(C, Integer): return 1 else: raise ValueError("not implemented") def millerLoopCost(C): k = polymorphic_get_embedding_degree(C) D = polymorphic_get_fD(C) name = polymorphic_get_name(C) # k, D, name, logp, logT, HwT) : #extra computations for BN, BLS and KSS (see BarDuq Table2) miller_fixup = 0 if k == 5 or k == 7 : cost_addline = 10*cost_m(k) + 3*cost_s(k) cost_doubleline = 6*cost_m(k) + 4*cost_s(k) + 2*k*cost_m(1) cost_verticalline = k*cost_m(1) cost_update1 = 4*cost_m(k) + 2*cost_s(k) cost_update2 = 4*cost_m(k) elif k == 6 and D == 3 : cost_addline = 10*cost_m(k//6) + 2*cost_s(k//6) + (k//3)*cost_m(1) cost_doubleline = 2*cost_m(k//6) + 7*cost_s(k//6) + (k//3)*cost_m(1) cost_verticalline = 0 cost_update1 = cost_s(k)+densexsparse_m6(k) cost_update2 = densexsparse_m6(k) elif k%4 == 0 and D == 4 : cost_addline = 9*cost_m(k//4) + 5*cost_s(k//4) + (k//2)*cost_m(1) cost_doubleline = 2*cost_m(k//4) + 8*cost_s(k//4) + (k//2)*cost_m(1) cost_verticalline = 0 cost_update1 = cost_s(k)+densexsparse_m8(k) cost_update2 = densexsparse_m8(k) if name == 'KSS16' : # extra partial add and partial double + 3 frob and 2 multiplications if mystery_201903151748_simon_a_raison: miller_fixup = (cost_m(k//4) + 5 *cost_s(k//4) + k//2 * cost_m(1)) + \ + (5*cost_m(k//4) + 2*cost_s(k//4) + k//2 * cost_m(1)) else: miller_fixup = 5*cost_m(k//4) + cost_s(k//4) + k * cost_m(1) miller_fixup += 3*cost_f(k) + 2*sparse_m16 elif k == 12 and D == 3: cost_doubleline = 3*cost_m(k//6) + 6*cost_s(k//6) + (k//3)*cost_m(1) cost_addline = 11*cost_m(k//6) + 2*cost_s(k//6) + (k//3)*cost_m(1) cost_verticalline = 0 cost_update1 = cost_s(k)+densexsparse_m6(k) cost_update2 = densexsparse_m6(k) if name == 'BN': # for Q1 = [p]Q and Q2=[p]Q1 # first extra add # second light add # 2 multiplications miller_fixup = 4*cost_f(k) + \ + cost_addline + \ + 4*cost_m(k//6) + 4*cost_m(1) + \ + 2*sparse_m12 elif k == 1: cost_addline=None cost_doubleline=None cost_verticalline=None cost_update1=None cost_update2=None tot_miller = 4626 * m + cost_i(k) if k!= 1 : T = polymorphic_get_miller_loop_length(C) logT = T.nbits() HwT = Hw(T) tot_miller = (logT-1) * (cost_doubleline + cost_verticalline) \ + (logT-2) * cost_update1 \ + (HwT-1) * (cost_addline + cost_verticalline + cost_update2) \ + (cost_i(k) if k%2==1 else 0) \ + miller_fixup return [cost_addline, cost_doubleline, cost_verticalline, cost_update1, cost_update2, ZZ(tot_miller(m=1,s=1,inv=25))] def cost_firstexp(k): assert k in [5,6,7,8] if Integer(k).is_prime(): return cost_i_and_f(k)+cost_m(k) if k == 6 : # a <- a^(1+p) c0 = cost_f(k) + cost_m(k) # a = a0 + a1y ; a^(p^3) = a0-a1y ; norm_{6/3}(a) = a0^2-y^2a1^2 # with cheap multiplication by y^2; then a^(p^3-1) = # 1/norm*(a0^2+y^2a1^2-2ya0a1) c1 = 2*cost_s(k//2) c1 += cost_m(k//2) c1 += cost_i(k//2) c1 += 2*cost_m(k//2) return c0 + c1 if k == 8 : return cost_i(k) + cost_m(k) def finalExpoCost(C): k = polymorphic_get_embedding_degree(C) name = polymorphic_get_name(C) if isinstance(C, Integer): tot_expo = 4100 * cost_m(1) return tot_expo(m=1,s=1,inv=25) elif name == 'BN': T = C.u() logT = T.nbits() HwT = Hw(T) #BN_expo_z = 4*(114 - 1)*cost_m(2) + (6*3 - 3)*cost_m(2) + 3*cost_m(12) + 3*3*cost_s(2) + cost_i(2) BN_expo_z = 4*(logT - 1)*cost_m(2) + (6*(HwT-1) - 3)*cost_m(2) + (HwT-1)*cost_m(12) + 3*(HwT-1)*cost_s(2) + cost_i(2) #BarDuq says 114*compr_s12 + 3* cost_m(12) + (i + (24*4 - 5)*cost_m(1)) tot_expo = cost_i(12) + 12*cost_m(12) + 3*cyclo_s12 + 4* cost_f(12) + 3*BN_expo_z return tot_expo(m=1,s=1,inv=25) elif name == 'BLS12': T = C.u() logT = T.nbits() HwT = Hw(T) #BLS_expo_z = 4*(77 - 1)*cost_m(2)+ (6*2 - 3)*cost_m(2) + 2*cost_m(12) + 3*2*cost_s(2) + cost_i(2) BLS_expo_z = 4*(logT - 1)*cost_m(2)+ (6*(HwT-1) - 3)*cost_m(2) + (HwT-1)*cost_m(12) + 3*(HwT-1)*cost_s(2) + cost_i(2) #4*(log(u)-1)*m_i + (6*hw(u) - 3) *m_i + hw(u) * m_{6i} + 3*hw(u) * s_i + 1*I_i #BarDuq says 77*compr_s12 + 2*cost_m(12) + (i + (24*3 - 5)*cost_m(1)) tot_expo = cost_i(12) + 12*cost_m(12) + 2*cyclo_s12 + 4*cost_f(12) + 5*BLS_expo_z return tot_expo(m=1,s=1,inv=25) elif name == 'KSS16': KSS16_expo_z = 34*cyclo_s16 + 4*cost_m(16) tot_expo = 34*cyclo_s16 + 32*cost_m(16)+24*cost_m(4)+8*cost_f(16)+cost_i(16) + 9*KSS16_expo_z return tot_expo(m=1,s=1,inv=25) if not isinstance(C, CocksPinchVariantResult): # FE for MNT6 BLS24 KSS18 not done raise ValueError("not implemented") logp = C.p.nbits() logr = C.r.nbits() T = C.T D = C.fD i = C.i logT = T.nbits() HwT = Hw(T) HwCofr=Hw(C.twist(0)['card']//C.r) hy = ZZ(C.hy) ht = ZZ(C.ht) loghy=hy.nbits(); Hwhy=Hw(hy) loght=ht.nbits(); Hwht=Hw(ht) c1 = cost_firstexp(k) # Now compute c2 (second part of FE) if k==5 or k==7: assert isinstance(C, CocksPinchVariantResult) cost_T = (logT-1)*cost_s(k) + (HwT-1)*cost_m(k) # see final-expo-k57.sage c2 = (k-2)*(cost_f(k) + cost_T + 2*cost_m(k)) + cost_m(k) logc = logp - logr c2 += (logc -1)*cost_s(k) + (HwCofr-1)*cost_m(k) # one inversion costs (k-1) Frobenius since norm == 1 cost_inv_torus = (k-1) * cost_f(k) if i > 1: c2 += cost_inv_torus if i < k-1 and 2*i > k: # this just happens to match the formulas that we have. c2 += cost_inv_torus # cost for k=5 i=1: 1c + 3T + 7M + 3p # cost for k=5 i=2: 1I + 1c + 3T + 7M + 3p # cost for k=5 i=3: 2I + 1c + 3T + 7M + 3p # cost for k=5 i=4: 1I + 1c + 3T + 7M + 3p # cost for k=7 i=1: 1c + 5T + 11M + 5p # cost for k=7 i=2: 1I + 1c + 5T + 11M + 5p # cost for k=7 i=3: 1I + 1c + 5T + 11M + 5p # cost for k=7 i=4: 2I + 1c + 5T + 11M + 5p # cost for k=7 i=5: 2I + 1c + 5T + 11M + 5p # cost for k=7 i=6: 1I + 1c + 5T + 11M + 5p elif k == 6 : # See: # sage: attach("formules-familles-CocksPinch.sage") # sage: formulas(6) assert D==3 # start with this: c_exp_T = (logT-1)*cyclo_s6 + (HwT-1)*cost_m(k) c2 = c_exp_T + cost_f(k) + cyclo_s6 + 4*cost_m(k) if i == 5: # extra cost for raising to the power p+t0 = p+2-T: we # need a square... c2 += cyclo_s6 # Then, see code/formules-familles-CocksPinch.sage # it's a slight mess, to be honest. assert (1 + ht + hy) % 2 == 0 if ht % 2 == 0: hu = ht//2 hz = -1 if T%3 == 0 else 1 hw = (hy-hz)//2 else: hu = (ht+1)//2 hw = hy//2 U = T - (T % 3) logU=U.nbits(); HwU=Hw(U) loghu=hu.nbits(); Hwhu=Hw(hu) loghw=hw.nbits(); Hwhw=Hw(hw) c_exp_U = (logU-1)*cyclo_s6 + (HwU-1)*cost_m(k) c_exp_hu = (loghu-1)*cyclo_s6 + (Hwhu-1)*cost_m(k) c_exp_hw = (loghw-1)*cyclo_s6 + (Hwhw-1)*cost_m(k) c2 += 12*cost_m(k) + 2*cost_s(k) + 2*(c_exp_U + c_exp_hu + c_exp_hw) elif k == 8 : # See: # sage: attach("formules-familles-CocksPinch.sage") # sage: formulas(8) assert D==4 assert T%2==0 assert ht % 2 == 1 # t0 is odd, so that ht must be odd too. Raising to the power # ht+1 or ht-1 costs at most as much as raising to the power ht, # and maybe one multiplication less. Note also that ht+1 is # necessarily even. c_exp_T = (logT-1)*cyclo_s8 + (HwT-1)*cost_m(k) # first, this: (because phi_8(p)/r = 1 + c(T^2+p^2)(p-T)) # recall that a p^2-power cost is the same as a p-power # for the reason explained in remark \ref{frob}. # What happen if i != 1 when the expo is to the power T^i ? c2 = 3*c_exp_T + 2*cost_f(8) + 3*cost_m(k) # Then, see code/formules-familles-CocksPinch.sage # for raising to the power c, we get: # 11M + 2u + 4T + 2y # with one of the multiplies that (for i=3 and i=7) can be # elided if T=0 mod 4. This is with u = multiplication by # (h_t+1)//2. hu = (ht+1)//2; loghu=hu.nbits(); Hwhu=Hw(hu) c_exp_hy = (loghy-1)*cyclo_s8 + (Hwhy-1)*cost_m(k) c_exp_hu = (loghu-1)*cyclo_s8 + (Hwhu-1)*cost_m(k) c2 += 4 * c_exp_T + 2 * c_exp_hu + 2 * c_exp_hy + 10 * cost_m(k) # Do we miss some inversions ? if (T%4 == 2 or i == 1 or i == 5): c2 += cost_m(k) tot_expo = c1 + c2 return tot_expo(m=1,s=1,inv=25) def pairingCost(C): costMiller = millerLoopCost(C) costFinalExp = finalExpoCost(C) logp = polymorphic_get_logp(C) time_m = finite_field_cost(logp) tot_miller = costMiller[-1] time_miller = round(tot_miller * time_m/1000000, 2) tot_expo = costFinalExp time_expo = round(tot_expo * time_m/1000000, 2) return dict( k=polymorphic_get_embedding_degree(C), D=polymorphic_get_fD(C), name=polymorphic_get_name(C), logp=polymorphic_get_logp(C), time_m=time_m, tot_miller=tot_miller, time_miller=time_miller, tot_expo=tot_expo, time_expo=time_expo, tot_pairing = tot_miller + tot_expo, time_pairing = round(time_miller+time_expo, 2), ) def table_cost_pairing() : timing_recap = [] for C in [C5,C6,C7,C8,CBN446,CBLS446,CBN12,CBLS12,CKSS16,C1]: L=pairingCost(C) timing_recap.append(L) #timing recap is generated print "% This table is generated by:" print "% PYTHONPATH=cocks-pinch-variant sage -c 'load(\"cocks-pinch-variant/cost_pairing.py\"); table_cost_pairing()'" def wrap_cell(cell): return "\\begin{tabular}{@{}c@{}} %s \\end{tabular}" % cell for L in timing_recap : cell0 = "%s & %d-bit" % (L['name'], L['logp']) cell_miller = "%d\\bfm \\\\ %sms" % (L['tot_miller'], L['time_miller']) cell_miller = wrap_cell(cell_miller) cell_expo = "%d\\bfm \\\\ %sms" % (L['tot_expo'], L['time_expo']) cell_expo = wrap_cell(cell_expo) cell_pairing = " %d\\bfm & %sms" % (L['tot_pairing'], L['time_pairing']) print cell0 + " & " + cell_miller + " &" print " " + cell_expo + " & " print " " + cell_pairing + " \\\\" print " \\hline "