/*-------------------------------------

Package : Certificates of positivity in the simplicial Bernstein basis

Author: Richard Leroy

First version : 07/01/07

Updated : 09/01/11

Updated again: 01/02/2017 (fix on standard_mid global m in standard_mid)
--------------------------------------*/

/*-------------------------------------

Procedure: nonneg

Input: List L of the Bernstein expansion of a polynomial P of degree d in k variables

Output: True if all the Bernstein coefficients are nonnegative, else false.

--------------------------------------*/

nonneg(L,d,k):=
  block([i,bool],
  bool:true,
  for i:1 thru length(L) do
  (
  if L[i]<0 then bool:false 
  ),
  return(bool)
  );

/*-------------------------------------

Procedure : pos

Input: List L of the Bernstein expansion of a polynomial P of degree d in k variables

Output: True if the Bernstein coefficients satisfy the certificates of positivity, else false.

Reminder: the certificate of positivy says that all the Bernstein coefficients are nonnegative, 
and the coefficients corresponding to the vertices of the simplex are positive.

---------------------------------------*/

pos(L,d,k):=
  block([i,som,bool],
  som:sommets(d,k),
  bool:true,
  for i:1 thru length(L) do
  (
  if L[i]<0 then bool:false
  ),
  for i:1 thru length(som) do
  (
  if L[som[i]]<=0 then bool:false
  ),
  return(bool)
  );

/*-------------------------------------

Procedure: monmin2

Input: list L

Output: the minimum of L, together with a corresponding index.

--------------------------------------*/  

monmin2(L):=
  block([m,i,j],
  m:L[1],
  j:1,
  for i:2 thru length(L) do
  (
  if L[i]<m then
	(m:L[i],
  	j:i)
  ),
  return([m,j])
  );  
  
/*-------------------------------------

Procedure: compositions_leq

Input: multiindex gamm

Output: list of all the (weak) compositions of the same size as alpha 
that are lower or equal to gamm componentwise (wrt lexicographic order).

--------------------------------------*/

compositions_leq(gamm):=
  block([k,l],
  k:length(gamm),
  if k = 1 then return(makelist([i],i,0,gamm[1])) else
	(
	l:compositions_leq(rest(gamm,1)),
	return(create_list(cons(i,l[j]),i,0,gamm[1],j,1,length(l)))
	)
  );

/*-------------------------------------

Procedure: compositions

Input: nonnegative integers n,k

Output: list of all the (weak) compositions of n in k nonnegative integers (wrt lexicographic order).

--------------------------------------*/  

compositions(n,k):=
  block([i,res,l,par],
  if k=1 then return([[n]]) else
  res:[],
    for i : 0 thru n do
    (
    par:makelist(cons(i,l),l,compositions(n-i,k-1)),
    res:append(par,res)
    ),
  return(res)
  );

/*-------------------------------------

Procedure: mulcoeff

Input: a polynomial pol in the variables vars, and a multiindex alpha.

Output: coefficient of pol corresponding to the monomial of degree alpha

--------------------------------------*/  
 
mulcoeff(pol,vars,alpha):=
  block([par,res,coe],
  if length(vars)=1 then return(coeff(pol,vars[1],alpha[1])) else
  	(	
  	return(mulcoeff(coeff(pol,vars[1],alpha[1]),rest(vars,1),rest(alpha,1)))
  	)
  );

/*-------------------------------------

Procedure: simplex2bar

Input: a simplex simpl, given as an ordered list of vertices, and a list a variables vars

Output: list of the barycentric coordinates associated to simpl (expressed in the variables vars).

--------------------------------------*/  

simplex2bar(simpl,vars):= 
  block([i,j,len,res,l,eq],
  res:[],
  len: length(simpl[1]),
  for i : 1 thru len do
    (
    eq:sum(l[j]*simpl[j+1][i],j,0,len)-vars[i],
    res:endcons(eq,res)
    ),
  res:endcons(sum(l[j],j,0,len)-1,res),
  res:linsolve(res,makelist(l[j],j,0,len)),
  return(makelist(second(res[j]),j,1,len+1))
  );

/*-------------------------------------

Procedure: dominant

Input: polynomial P in the variables vars

Output: degree (given as a multiindex) of the dominant coefficient of P wrt lexicographic order on the variables vars

--------------------------------------*/

dominant(P,vars):=
  block([i,l,expo,c,d],
  l:length(vars),
  expo:[],
  c:P,
  for i : 1 thru l do
  	(
  	d:hipow(c,vars[i]),
  	c:coeff(c,vars[i],d),
  	expo:endcons(d,expo)
  	),
  return(expo)
  );
  
/*-------------------------------------

Procedure: sparse_pol

Input: a polynomial P in the variables vars

Output: list of all the monomials that appear in P with a nonzero coefficient

--------------------------------------*/

sparse_pol(P,vars):=
  block([i,k,reste,d,mon,c,expo],
  k:length(vars),
  reste:P,
  expo:[],
  while not(reste=0) do
	(
	d:dominant(reste,vars),
	c:mulcoeff(reste,vars,d),
	reste:reste-c*product(vars[i]**d[i],i,1,k),
	expo:endcons(d,expo)
	),
  return(expo)
  );

/*-------------------------------------

Procedure: sparse_standard2bern

Input: a polynomial P in the variables vars, a nonnegative integer d

Output: Bernstein expansion of degree d of P wrt the standard simplex (uses the previous procedure)

--------------------------------------*/

sparse_standard2bern(P,vars,d):=
  block([alph,bet,gamm,i,deg,coeff,res,k,mon,multg,multa,expo],
  k:length(vars),
  res:[],
  expo:sparse_pol(P,vars),
  for gamm in compositions(d,k+1) do
  	(
  	multg:apply(multinomial_coeff,gamm),
  	coeff:0,
  		for alph in expo do
  			(
  			bet:gamm-cons(0,alph),
  			if apply(min,bet) >= 0 then
  				(
  				deg:sum(alph[a],a,1,length(alph)),
  				multa:apply(multinomial_coeff,bet),
  				coeff:coeff+mulcoeff(P,vars,alph)*multa/multg
  				)
  			),
  	res:endcons(coeff,res)
  	),
  return(res)
  );

/*-------------------------------------

Procedure: monom2bern

Input: a polynomial P in the variables vars, a nonnegative integer d and a simplex V (given as the list of its vertices)

Output: Bernstein expansion of degree d of P wrt the simplex V (uses the previous procedure)

--------------------------------------*/  

monom2bern(P,V,vars,d):=
	block([i,j,k,aff,g,varbis,vc],
	k:length(vars),
	varbis:makelist(concat(vc,i),i,1,k),
	aff:transpose(apply(matrix,rest(V,1)))-transpose(apply(matrix,makelist(V[1],i,1,k))),
	aff:aff.varbis+V[1],
	g:expand(subst(makelist(vars[i]=aff[i,1],i,1,k),P)),
	return(sparse_standard2bern(g,varbis,d))
	);

/*-------------------------------------

Procedure: bern2monom

Input: the list L of the Bernstein expansion of degree d of a polynomial P (expressed in the variables vars) wrt a simplex V

Output: expression of P in the monomial basis

--------------------------------------*/

bern2monom(L,V,vars,d):=
	block([k,i,j,D,P],
	k:length(vars),
	/*D:concat(makelist(0,i,0,k),makelist(flatten([makelist(0,i,0,j-1),[1],makelist(0,i,j+1,k)]),j,0,k)),*/
	P:deCasteljau(V,L,d,vars),
	P:expand(P[1][2][1])
	);

/*-------------------------------------

Procedure: deCasteljau

Input: the Bernstein expansion ber of degree deg of a polynomial P wrt to a simplex simpl, and an point M.

Output: list of all the items [V,B] where V is a subsimplex of simpl and B the corresponding Bernstein expansion of P, 
for all subsimplices V of simpl defined by the point M.

--------------------------------------*/

deCasteljau(simpl,ber,deg,M):=
  block([i,j,k,alpha,bet,var,l,cmp,res],local(bcastel),
  k:length(simpl)-1,
  var:varCasteljau,
  bet:subst( makelist( var[j]=M[j],j,1,k), simplex2bar(simpl,var)),
  j:1,
  for alpha in compositions(deg,k+1) do
  (
  bcastel[alpha]:ber[j],
  j:j+1
  ),
  for l:1 thru deg do
    (
    cmp:compositions(deg-l,k+1),
    for alpha in cmp do
       (
       bcastel[alpha]:sum(bet[i+1]*
                bcastel[append(rest(alpha,-length(alpha)+i),[alpha[i+1]+1],
                         rest(alpha,i+1))],i,0,k)
       )
    ),
   res:makelist([subst([simpl[j+1]=M],simpl),makelist(bcastel[append(makelist(alpha[i],i,1,j),[0],makelist(alpha[i],i,j+2,k+1))],alpha,compositions(deg,k+1))],j,0,k),
  return(res)
  );

/*-------------------------------------

Procedure: deCasteljauarete

Input: the Bernstein expansion ber of degree deg of a polynomial P wrt a simplex simpl = [V_0,...,V_k], indices i,j corresponding to two vertices M_i, M_j of simpl, and a real 0<x<1.

Output: list of the Bernstein expansion of degree deg of P wrt the two simplices resulted from the subdivision of simpl at the point M = (1-x)*M_i + x*M_j.

--------------------------------------*/

deCasteljauarete(simpl,ber,deg,i,j,x):=
  block([k,alpha,bet,gamm,l,cmp,res,M,p,L,dprime,indi,resi,resj,C],local(bcastel),
  k:length(simpl)-1,
  M:(1-x)*simpl[i]+x*simpl[j],
  if k<2 then return(deCasteljau(simpl,ber,deg,M)),
  p:1,
  cmp:compositions(deg,k+1),
  for alpha in cmp do
  (
  bcastel[alpha]:ber[p],
  p:p+1
  ),
  cmp:compositions(deg,k),
  for l:1 thru deg do
    cmp:append(cmp,compositions(deg-l,k-1)),
  for bet in cmp do
    (
    dprime:deg-sum(bet[l],l,1,length(bet)),
    L:[],
    for gamm:0 thru dprime do
      (
      indi:append(makelist(bet[p],p,1,i-1),[dprime-gamm],makelist(bet[p],p,i,j-2),[gamm],makelist(bet[p],p,j-1,k-1)),
      L:append(L,[bcastel[indi]])
      ),
    C:deCasteljau([[0],[1]],L,dprime,[x]),
    for gamm:0 thru dprime do
      (
      indi:append(makelist(bet[p],p,1,i-1),[dprime-gamm],makelist(bet[p],p,i,j-2),[gamm],makelist(bet[p],p,j-1,k-1)),
      resi[indi]:C[1][2][gamm+1],
      resj[indi]:C[2][2][gamm+1]
      )
    ),
  res:[[subst([simpl[i]=M],simpl),makelist(resi[alpha],alpha,compositions(deg,k+1))],[subst([simpl[j]=M],simpl),makelist(resj[alpha],alpha,compositions(deg,k+1))]],
  return(res)
  );

/*-------------------------------------

Procedure: sommets

Input: two nonnegative integers d and k

Output: list of the multiindices labelling the Bernstein coefficients corresponding to the vertices of a simplex 
among the Bernstein expansion of degree d of a polynomial in k variables (wrt the lexicographic order)

--------------------------------------*/

sommets(d,k):=
  block([i,j,som,indii],
  som:[1],
  indii:1,
  for j:0 thru k-1 do
  (
  indii:indii+sum(binomial(d-i+k-1-j,k-1-j),i,1,d-1)+1,
  som:endcons(indii,som)
  ),
  return(som)
  );

/*-------------------------------------

Procedure: norm

Input: vector u of IR^k

Output: the square of the Euclidean norm of u: ||u||^2

--------------------------------------*/

norm(u):=
  block([i],
  return(sum(u[i]^2,i,1,length(u)))
  );

/*-------------------------------------

Procedure: diam

Input: simplex V

Output: indices of two vertices of V that realize the diameter of V

--------------------------------------*/

diam(V):=
  block([i,j,k,m,L,indi,r,q],
  k:length(V)-1,
  L:create_list(norm(V[i]-V[j]), i, 1, k+1, j, 1, k+1),
  indi:1,
  m:L[1],
  for i:2 thru length(L) do
  	(
  	if L[i]>m then
  	(
  	indi:i,
  	m:L[i]
  	)
  	),
  r:mod(indi,k+1),
  q:quotient(indi,k+1),
  if r = 0 then return([min(k+1,q),max(k+1,q)]) else return([min(r,q+1),max(r,q+1)])
  );

/*-------------------------------------

Procedure: bound

Input: a polynomial P of degree d in the variables var

Output: theoritical lower bound on the minimum of P over the standard simplex, if P is positive on it.

--------------------------------------*/

bound(P,var,d):=
	block([i,j,k,c,tau],
	k:length(var),
	c:coeffs(P,var,d),
	tau:length(num2list(max(apply(max,c),-apply(min,c)),2)),
	return(2^(-(tau+1)*d^(k+1))*d^(-(k+1)*d^k)*binomial(d+k,k+1)^(-d^k*(d-1)))
	);

/*-------------------------------------

Procedure: coeffs

Input: a polynomial P of degree D in variables var

Output: list of all the coefficients of P (wrt the lexicographic order on the variables).

--------------------------------------*/

coeffs(P,var,D):=
	block([i,j,a,k,v],
	k:length(var),
	apply(append,makelist(makelist(subst(makelist(v=0,v,var),ratcoeff(P,product(var[i]^(a[i]),i,1,k))),a,compositions(D-j,k)),j,0,D)
	));

/*-------------------------------------

Procedure: multiCertificate

Input: a polynomial P in the variables vars, a simplex V, a nonnegative integer d, a subdivision method sub (e.g. bisection, bisection_castel, standard_triang2),
and a choice for the certificate (pos for a certificate of positivity, nonneg for a certificate of nonnegativity).

Output:

- if P is positive on the standard simplex :a certificate of positivity for P expressed as a list of items [V,B], 
where the collection of subsimplices V covers the standard simplex, 
and B is the corresponding Berstein expansion of degree d of P on each W

- if not, an item [M,v] where M is a point of the standard simplex and v is a the value of P at M that is lower than the theoritical bound 
on the minimum of a positive polynomial

Warning: if P is nonnegative but not positive, the computation can take a very long time, due to the theoritical bound, which can be very small.

--------------------------------------*/

multiCertificate(P,V,vars,d,sub,cert):=
  block([k,bool,actif,passif,alpha,a,v,arete,w,Delta,m,N0,L,s,res,value,value_new,N,W,l],
  k:length(V)-1,
  res:[],
  L:monom2bern(P,V,vars,d),
  s:sommets(d,k),
  actif:[[V,L]],
  passif:[],
  if cert=pos then m:bound(P,vars,d) else m:0,
  value:[0,m+1],
  while length(actif)>0 and value[2]>=m do
  (
  a:[],
  for v in actif do
  	(
  	if apply(cert,[v[2],d,k]) then passif:endcons(v,passif)
  	else
  	(
	for i:1 thru k+1 do
		(
		l:v[2][s[i]],
		if not(apply(cert,[[l],0,0])) then
			(
			res:([v[1][i],l]),
			return()
			)
		),
	if length(res)>0 then
		(
		return()
		),
	W:apply(sub,[v[1],v[2],d]),
	
	for w in W do
		(
		alpha:monmin2(w[2])[2],
		alpha:compositions(d,k+1)[alpha],
		N:sum(alpha[i]*w[1][i],i,1,k+1)/d,
		value_new:subst(makelist(vars[i]=N[i],i,1,k),P),
		if value_new<value[2] then value:[N,value_new]
		),

	a:append(a,W)	
	)
	),
	actif:a
 ),
 if length(res)>0 then 
    return(res),
 if length(actif)=0 then  
    return(passif)
 else 
    return(value)
 );
 
/*-------------------------------------

Procedure: bisection_castel

Input: the Bernstein expansion L of degree d of a polynomial P wrt a simplex V

Output: list of the items [W,B] where B is the Bernstein expansion of degree d of P wrt the simplex W,
for all the subsimplices W of V generated by the bisection of V at the control point corresponding to 
the multiindex associated to a minimal Berstein coefficient among L.

--------------------------------------*/

bisection_castel(V,L,d):=
	block([arete,w,k,c,j,alpha,N,W,w,res,C],
	res:[],
	k:length(V)-1,
	c:compositions(d,k+1),
	i:monmin2(L)[2],
	alpha:c[i],
	j:[],
	for l:1 thru length(alpha) do
		(
		if not(alpha[l]=0) then j:endcons([l,alpha[l]],j)
		),
	if length(j)=2 then return(deCasteljauarete(V,L,d,j[1][1],j[2][1],j[1][2]/d))
	else
	(
	N:sum(alpha[i]*V[i],i,1,k+1)/d,
	return(deCasteljau(V,L,d,N))
	)		
	);

/*-------------------------------------

Procedure: bisection

Input: the Bernstein expansion L of degree d of a polynomial P wrt a simplex V

Output: list of the items [W,B] where B is the Bernstein expansion of degree d of P wrt the simplex W,
for the two subsimplices W of V generated by the bisection of V at the midpoint of a diameter of V.

--------------------------------------*/

bisection(V,L,d):=	
	block([arete],
	arete:diam(V),
	return(deCasteljauarete(V,L,d,min(arete[1],arete[2]),max(arete[1],arete[2]),1/2))
	);

/*-------------------------------------

Procedure: bisection_max

Input: the Bernstein expansion L of degree d of a polynomial P wrt a simplex V

Output: list of the items [W,B] where B is the Bernstein expansion of degree d of P wrt the simplex W,
for the two subsimplices W of V generated by the bisection of V at a point of a diameter of V
"maximizing" the volume of one subsimplex satisfying the certificate of positivity (cert= pos) or nonnegativity (cert=nonneg)

--------------------------------------*/	

bisection_max(V,L,d,cert):=
	block([arete,W,i,n,k,bool,res],
	arete:diam(V),
	i:1,
	n:20,
	k:length(V)-1,
	res:[V,L],
	bool:apply(cert,[L,d,k]),
	while ((not bool) and i<n/2) do
		(
		W:deCasteljauarete(V,L,d,min(arete[1],arete[2]),max(arete[1],arete[2]),i/n),
		if (apply(cert,[W[1][2],d,k])) then
			(bool:true,
			res:W
			)
		else
			(
			W:deCasteljauarete(V,L,d,min(arete[1],arete[2]),max(arete[1],arete[2]),(n-i)/n),
			if (apply(cert,[W[2][2],d,k])) then
				(bool:true,
				res:W
				)
			),
		i:i+1
		),
	if i>=n/2 then return(deCasteljauarete(V,L,d,min(arete[1],arete[2]),max(arete[1],arete[2]),1/2)) else return(res)
	);

bisection_max_pos(V,L,d):=	
	block(
	return(bisection_max(V,L,d,pos))
	);
	

bisection_max_nonneg(V,L,d):=	
	block(
	return(bisection_max(V,L,d,nonneg))
	);

	
/*-------------------------------------

Procedure: num2list

Input: two nonnegative integers n and b

Output: the expression of n in base b.

--------------------------------------*/  

num2list(n,b):=block([lst:[],a:n],
 for i:1 while a#0 do (
   lst:cons(?mod(a,b),lst),
   a:(a-lst[1])/b
   ),
 return(lst)
 );

/*-------------------------------------

Procedure: standard_triang2

Input: list L of the Bernstein expansion of degree d of a polynomial P wrt to a simplex V

Output: list of all the items [W,B] where W is a subsimplex of V and B the corresponding Bernstein expansion of degree d of P, 
for all the subsimplices W of the standard triangulation of degree 2 of V.

--------------------------------------*/

standard_mid(k,d):=
	block([M,m:make_array('any,d,k+1),n,y,i,j,color,t,s],
	t:[],
	s:matrix(makelist(1/d,i,1,d)),
	for n:0 thru d^k-1 do
	(
	y:num2list(n,d),
	y:append(makelist(0,i,1,k-length(y)),y),
	color:0,
	for i:0 thru d-1 do
		(
		m[i,0]:color,
		for j:1 thru k do
			(
			(if y[j]=i then color:color+1),
			m[i,j]:color
			)
		),
	t:cons(apply(matrix,makelist(makelist(m[i,j],j,0,k),i,0,d-1)),t)
	),
	return(t)
	);

standard(V,d):=
	block([t,k,T,i,j,l],
	k:length(V)-1,
	T:standard_mid(k,d),
	return(makelist(makelist(sum(V[t[i,j]+1],i,1,d)/2,j,1,k+1),t,T))
	);

standard_triang(V,P,vars,d,l):=
	block([i,j,k,T,L,res,t],
	k:length(V)-1,
	res:[],
	T:standard(V,l),
	for t in T do
		(
		i:monom2bern(P,t,vars,d),
		res:cons([t,i],res)
		),
	return(res)
	);

standard_triang2(V,L,d):=
	block([P,k,varbis,vc],
	k:length(V)-1,
	varbis::makelist(concat(vc,i),i,1,k),
	P:bern2monom(L,V,varbis,d),
	return(standard_triang(V,P,varbis,d,2))
	);	

/*-------------------------------------

Procedure: sec_diff

Input: Bernstein expansion B of degree d of a polynomial P wrt a simplex V

Output: List of all the second differences of P.

--------------------------------------*/

sec_diff(V,B,d):=
	block([i,j,k,alpha,bet,gam,var,l,cmp,res,e],local(bcastel),
	res:[],
	k:length(V)-1,
	e:makelist(0,i,0,k),
	j:1,
	for i:0 thru k do
	(
	e[i+1]:append(makelist(0,j,0,i-1),[1],makelist(0,j,i+1,k))
	),
	for alpha in compositions(d,k+1) do
	(
	bcastel[alpha]:B[j],
	j:j+1
	),
	for gam in compositions(d-2,k+1) do
	(
	for j:1 thru k do
			(
			res:endcons(bcastel[gam+e[1]+e[j]]+bcastel[gam+e[k+1]+e[j+1]]-bcastel[gam+e[k+1]+e[j]]-bcastel[gam+e[1]+e[j+1]],res)
			),
		for i:1 thru k do
		(
			for j:i+1 thru k do
			(
			res:endcons(bcastel[gam+e[i+1]+e[j]]+bcastel[gam+e[i]+e[j+1]]-bcastel[gam+e[i]+e[j]]-bcastel[gam+e[i+1]+e[j+1]],res)
			)
		)
	),
	res:max(apply(max,res),-apply(min,res)),
	return(res)
	);

/*-------------------------------------

Procedure: random_color

Input: none

Output: code of a random color

--------------------------------------*/

random_color():=
  block([randcol],
  randcol:['1,'2,'3,'4,'5,'6,'7,'8,'9,'0,'A,'B,'C,'D,'E,'F],
    concat("#",randcol[random(16)+1],randcol[random(16)+1],randcol[random(16)+1],randcol[random(16)+1],randcol[random(16)+1],randcol[random(16)+1])
  );
  
/*-------------------------------------

Procedure: drawMultiCerticificate

Input: Output L of multiCertificate in dimension 2 when a polynomial P has been proven to be positive, and an integer s

Output: Vizualization of the subsimplices generated by multiCertificate.
The size of the image is defined by s (usually 500).

--------------------------------------*/  

load(draw);

drawMultiCertificate(L,s):=
  block([gone,t],
  t:concat(length(L)," subsimplices"),
  if length(L[1]) = 2 then
  (
  gone:append([dimensions=[s,s],xtics=false,ytics=false,axis_bottom=false,axis_left=false,axis_top=false,axis_right=false,colorbox=false,title=t],flatten(makelist([fill_color=random_color(),polygon(L[i][1])],i,1,length(L))))
  )
  else
  (
  gone:append([dimensions=[s,s],xtics=false,ytics=false,axis_bottom=false,axis_left=false,axis_top=false,axis_right=false,colorbox=false,fill_color=red, title=t],flatten(makelist([polygon(L[i])],i,1,length(L))))
  ),
  apply(draw2d,gone)
  );

/*-------------------------------------

Procedure: wxdrawMultiCerticificate

Same procedure as drawMultiCertificate, except that the vizualization is generated inside wxMaxima

--------------------------------------*/ 

wxdrawMultiCertificate(L,s):=
  block([gone,t],
  t:concat(length(L)," subsimplices"),
  if length(L[1]) = 2 then
  (
  gone:append([dimensions=[s,s],xtics=false,ytics=false,axis_bottom=false,axis_left=false,axis_top=false,axis_right=false,colorbox=false,color=black],flatten(makelist([fill_color=white,polygon(L[i][1])],i,1,length(L))))
  )
  else
  (
  gone:append([dimensions=[s,s],xtics=false,ytics=false,axis_bottom=false,axis_left=false,axis_top=false,axis_right=false,colorbox=false,fill_color=white,color=black],flatten(makelist([polygon(L[i])],i,1,length(L))))
  ),
  apply(wxdraw2d,gone),
  return(t)
  );



















