
#include "epph.h"


/*
  This is a head file for the used C files

*/



/*
-------------------------- Function eplb -----------------------------

 Euclidean Projection onto l1 Ball (eplb)
 
        min  1/2 ||x- v||_2^2
        s.t. ||x||_1 <= z
 
 which is converted to the following zero finding problem
 
        f(lambda)= sum( max( |v|-lambda,0) )-z=0

 For detail, please refer to our paper:

 Jun Liu and Jieping Ye. Efficient Euclidean Projections in Linear Time,
 ICML 2009.  
 
 Usage (in matlab):
 [x, lambda, iter_step]=eplb(v, n, z, lambda0);
 

-------------------------- Function eplb -----------------------------
 */

void eplb(double * x, double *root, int * steps, double * v,int n, double z, double lambda0)
{
    
    int i, j, flag=0;
    int rho_1, rho_2, rho, rho_T, rho_S;
    int V_i_b, V_i_e, V_i;
    double lambda_1, lambda_2, lambda_T, lambda_S, lambda;
    double s_1, s_2, s, s_T, s_S, v_max, temp;
    double f_lambda_1, f_lambda_2, f_lambda, f_lambda_T, f_lambda_S;
    int iter_step=0;
        
    /* find the maximal absolute value in v
     * and copy the (absolute) values from v to x
     */

	if (z< 0){
		printf("\n z should be nonnegative!");
		return;
	}
           
    V_i=0;    
    if (v[0] !=0){
        rho_1=1;
        s_1=x[V_i]=v_max=fabs(v[0]);
        V_i++;
    }
    else{
        rho_1=0;
        s_1=v_max=0;
    }    
    
    for (i=1;i<n; i++){
        if (v[i]!=0){
            x[V_i]=fabs(v[i]); s_1+= x[V_i]; rho_1++; 
            
            if (x[V_i] > v_max)
                v_max=x[V_i];
            V_i++;
        }
    }
    
    /* If ||v||_1 <= z, then v is the solution  */
    if (s_1 <= z){
        flag=1;        lambda=0;
        for(i=0;i<n;i++){
            x[i]=v[i];
        }
        *root=lambda;
        *steps=iter_step;
        return;
    }
    
    lambda_1=0; lambda_2=v_max;
    f_lambda_1=s_1 -z;
    /*f_lambda_1=s_1-rho_1* lambda_1 -z;*/
    rho_2=0; s_2=0; f_lambda_2=-z; 
    V_i_b=0; V_i_e=V_i-1;
    
    lambda=lambda0; 
    if ( (lambda<lambda_2) && (lambda> lambda_1) ){ 
    /*-------------------------------------------------------------------
                  Initialization with the root
     *-------------------------------------------------------------------
     */
           
        i=V_i_b; j=V_i_e; rho=0; s=0;
        while (i <= j){            
            while( (i <= V_i_e) && (x[i] <= lambda) ){
                i++;
            }
            while( (j>=V_i_b) && (x[j] > lambda) ){
                s+=x[j];                
                j--;
            }
            if (i<j){
                s+=x[i];
                
                temp=x[i];  x[i]=x[j];  x[j]=temp;
                i++;  j--;
            }
		}
        
        rho=V_i_e-j;  rho+=rho_2;  s+=s_2;        
		f_lambda=s-rho*lambda-z;
        
        if ( fabs(f_lambda)< delta ){
            flag=1;
		}
		
		if (f_lambda <0){
			lambda_2=lambda; s_2=s;	rho_2=rho; f_lambda_2=f_lambda;

			V_i_e=j;  V_i=V_i_e-V_i_b+1;
		}
		else{
			lambda_1=lambda; rho_1=rho;	s_1=s; f_lambda_1=f_lambda;

			V_i_b=i; V_i=V_i_e-V_i_b+1;
		}

		if (V_i==0){
			/*printf("\n rho=%d, rho_1=%d, rho_2=%d",rho, rho_1, rho_2);

            printf("\n V_i=%d",V_i);*/
            
			lambda=(s - z)/ rho;
			flag=1;
		}       
     /*-------------------------------------------------------------------
                          End of initialization
      *--------------------------------------------------------------------
      */       
        
    }/* end of if(!flag) */
    
    while (!flag){
        iter_step++;
        
        /* compute lambda_T  */
        lambda_T=lambda_1 + f_lambda_1 /rho_1;
        if(rho_2 !=0){
            if (lambda_2 + f_lambda_2 /rho_2 >	lambda_T)
                lambda_T=lambda_2 + f_lambda_2 /rho_2;
        }
        
        /* compute lambda_S */
        lambda_S=lambda_2 - f_lambda_2 *(lambda_2-lambda_1)/(f_lambda_2-f_lambda_1);
        
        if (fabs(lambda_T-lambda_S) <= delta){
            lambda=lambda_T; flag=1;
            break;
        }
        
        /* set lambda as the middle point of lambda_T and lambda_S */
        lambda=(lambda_T+lambda_S)/2;
        
        s_T=s_S=s=0;
        rho_T=rho_S=rho=0;
        i=V_i_b; j=V_i_e;
        while (i <= j){            
            while( (i <= V_i_e) && (x[i] <= lambda) ){
                if (x[i]> lambda_T){
                    s_T+=x[i]; rho_T++;
                }
                i++;
            }
            while( (j>=V_i_b) && (x[j] > lambda) ){
                if (x[j] > lambda_S){
                    s_S+=x[j]; rho_S++;
                }
                else{
                    s+=x[j];  rho++;
                }
                j--;
            }
            if (i<j){
                if (x[i] > lambda_S){
                    s_S+=x[i]; rho_S++;
                }
                else{
                    s+=x[i]; rho++;
                }
                
                if (x[j]> lambda_T){
                    s_T+=x[j]; rho_T++;
                }
                
                temp=x[i]; x[i]=x[j];  x[j]=temp;
                i++; j--;
            }
		}
        
        s_S+=s_2; rho_S+=rho_2;
        s+=s_S; rho+=rho_S;
        s_T+=s; rho_T+=rho;
        f_lambda_S=s_S-rho_S*lambda_S-z;
        f_lambda=s-rho*lambda-z;
        f_lambda_T=s_T-rho_T*lambda_T-z;
        
        /*printf("\n %d & %d  & %5.6f & %5.6f & %5.6f & %5.6f & %5.6f \\\\ \n \\hline ", iter_step, V_i, lambda_1, lambda_T, lambda, lambda_S, lambda_2);*/
                
        if ( fabs(f_lambda)< delta ){
            /*printf("\n lambda");*/
            flag=1;
            break;
        }
        if ( fabs(f_lambda_S)< delta ){
           /* printf("\n lambda_S");*/
            lambda=lambda_S; flag=1;
            break;
        }
        if ( fabs(f_lambda_T)< delta ){
           /* printf("\n lambda_T");*/
            lambda=lambda_T; flag=1;
            break;
        }        
        
        /*
        printf("\n\n f_lambda_1=%5.6f, f_lambda_2=%5.6f, f_lambda=%5.6f",f_lambda_1,f_lambda_2, f_lambda);
        printf("\n lambda_1=%5.6f, lambda_2=%5.6f, lambda=%5.6f",lambda_1, lambda_2, lambda);
        printf("\n rho_1=%d, rho_2=%d, rho=%d ",rho_1, rho_2, rho);
         */
        
        if (f_lambda <0){
            lambda_2=lambda;  s_2=s;  rho_2=rho;
            f_lambda_2=f_lambda;            
            
            lambda_1=lambda_T; s_1=s_T; rho_1=rho_T;
            f_lambda_1=f_lambda_T;
            
            V_i_e=j;  i=V_i_b;
            while (i <= j){
                while( (i <= V_i_e) && (x[i] <= lambda_T) ){
                    i++;
                }
                while( (j>=V_i_b) && (x[j] > lambda_T) ){
                    j--;
                }
                if (i<j){                    
                    x[j]=x[i];
                    i++;   j--;
                }
            }            
            V_i_b=i; V_i=V_i_e-V_i_b+1;
        }
        else{
            lambda_1=lambda;  s_1=s; rho_1=rho;
            f_lambda_1=f_lambda;
            
            lambda_2=lambda_S; s_2=s_S; rho_2=rho_S;
            f_lambda_2=f_lambda_S;
            
            V_i_b=i;  j=V_i_e;
            while (i <= j){
                while( (i <= V_i_e) && (x[i] <= lambda_S) ){
                    i++;
                }
                while( (j>=V_i_b) && (x[j] > lambda_S) ){
                    j--;
                }
                if (i<j){
                    x[i]=x[j];
                    i++;   j--;
                }
            }
            V_i_e=j; V_i=V_i_e-V_i_b+1;
        }
        
        if (V_i==0){
            lambda=(s - z)/ rho; flag=1;
            /*printf("\n V_i=0, lambda=%5.6f",lambda);*/
            break;
        }
    }/* end of while */
    
    
    for(i=0;i<n;i++){        
        if (v[i] > lambda)
            x[i]=v[i]-lambda;
        else
            if (v[i]< -lambda)
                x[i]=v[i]+lambda;
            else
                x[i]=0;
    }
    *root=lambda;
    *steps=iter_step;
}






/*
-------------------------- Function epp1 -----------------------------

 The L1-norm Regularized Euclidean Projection (epp1)
 
        min  1/2 ||x- v||_2^2 + rho ||x||_1
 
 which has the closed form solution

             x= sign(v) max( |v|- rho, 0)
 
Usage (in matlab)
 x=epp1(v, n, rho); 

-------------------------- Function epp1 -----------------------------
 */

void  epp1(double *x, double *v, int n, double rho){
	int i;

	/*
	we assume rho>=0
	*/

	for(i=0;i<n;i++){
		if (fabs(v[i])<=rho)
			x[i]=0;
        else 
            if (v[i]< -rho)
                x[i]=v[i]+rho;
            else
				x[i]=v[i]-rho;
    }
}





/*
-------------------------- Function epp2 -----------------------------

 The L2-norm Regularized Euclidean Projection (epp2)
 
        min  1/2 ||x- v||_2^2 + rho ||x||_2
 
 which has the closed form solution

             x= max( ||v||_2- rho, 0) / ||v||_2 * v
 
Usage (in matlab)
 x=epp2(v, n, rho); 

-------------------------- Function epp2 -----------------------------
 */

void  epp2(double *x, double *v, int n, double rho){
	int i;
	double v2=0, ratio;

	/*
	we assume rho>=0
	*/

	for(i=0; i< n; i++){
		v2+=v[i]*v[i];
	}
	v2=sqrt(v2);

	if (rho >= v2)
		for(i=0;i<n;i++)
			x[i]=0;
	else{
		ratio= (v2-rho) /v2;
        for(i=0;i<n;i++)
			x[i]=v[i]*ratio;
		}
}





/*
-------------------------- Function eppInf -----------------------------

 The LInf-norm Regularized Euclidean Projection (eppInf)
 
        min  1/2 ||x- v||_2^2 + rho ||x||_Inf
 
 which is can be solved by using eplb
 
Usage (in matlab)
 [x, lambda, iter_step]=eppInf(v, n, rho, rho0); 

-------------------------- Function eppInf -----------------------------
*/

void  eppInf(double *x, double * c, int * iter_step, double *v,  int n, double rho, double c0){
	int i, steps;

   /*
	we assume rho>=0
	*/

    eplb(x, c, &steps, v, n, rho, c0);

	for(i=0; i< n; i++){
		x[i]=v[i]-x[i];
	}
	iter_step[0]=steps;
	iter_step[1]=0;
}




/*
-------------------------- Function zerofind -----------------------------


   Find the root for the function: f(x) = x + c x^{p-1} - v, 
                                   0 <= x <= v, v>=0
                                   1< p < infty, p \neq 2
   
   Property: when p>2, f(x) is a convex function
             when 1<p<2, f(x) is a concave function

   Method: we use Newton's method (other methods such as bisection can also work)

   Note: we donot check the valid of the parameter. 
   Since it is only employed in eepO, 
   we can assure that these parameters satisfy the above conditions.

   
 
Usage (in matlab)
 [root, interStep]=eppInf(v, p, c, x0); 

-------------------------- Function zerofind -----------------------------
*/


void zerofind(double *root, int * iterStep, double v, double p, double c, double x0){
  
	double x, f, fprime, p1=p-1, pp;
	int step=0;

   
   if (v==0){
	   *root=0;	   *iterStep=0;	   return;
   }

   if (c==0){
	   *root=v;	   * iterStep=0;	   return;
   }

	      
   if ( (x0 <v) && (x0>0) )
	   x=x0;
   else
	   x=v;


   pp=pow(x, p1);
   f= x + c* pp -v;


   /*
   We apply the Newton's method for solving the root
   */
   while (1){
	   step++;

	   fprime=1 + c* p1 * pp / x; 
	                    /* 
						The derivative at the current solution x
	                    */

	   x = x- f/fprime; /*
						The new solution is computed by the Newton method
	                     */

	         

	   if (p>2){
		   if (x>v){
			   x=v;
		   }
	   }
	   else{
		   if ( (x<0) || (x>v)){
			   x=1e-30;			  

			   f= x+c* pow(x,p1)-v;

			   if (f>0){ /*
						  If f(x) = x + c x^{p-1} - v <0 at x=1e-30, 
						  this shows that the real root is between (0, 1e-30).
						  For numerical reason, we just set x=0
				          */

				   *root=x;
				   * iterStep=step;

				   break;
			   }
		   }
	   }
	   /*
	    This makes sure that x lies in the interval [0, v]
		*/

	   pp=pow(x, p1);
	   f= x + c* pp -v; 
	                    /* 
						The function value at the new solution
	                    */

	   if ( fabs(f) <= delta){
		   *root=x;
		   * iterStep=step;
		   break;
	   }

	   if (step>=innerIter){
		   printf("\n The number of steps exceed %d, in finding the root for f(x)= x + c x^{p-1} - v, 0< x< v.", innerIter);
		   printf("\n If you meet with this problem, please contact Jun Liu (j.liu@asu.edu). Thanks!");
           return;
	   }

   }

   /*
   printf("\n x=%e, f=%e, step=%d\n",x, f, step);
   */

}





/*
-------------------------- Function norm -----------------------------

   Compute the p-norm

-------------------------- Function norm -----------------------------
*/

double norm(double * v, double p, int n){
   int i;
   double t=0;


   /*
   we assume that v[i]>=0
                   p>1
   */

   for(i=0;i<n;i++)
	   t+=pow(v[i], p);

   return( pow(t, 1/p) );
}





/*
-------------------------- Function eppInf -----------------------------

 The Lp-norm Regularized Euclidean Projection (eppO) for 1< p<Inf
 
        min  1/2 ||x- v||_2^2 + rho ||x||_p
 
 We solve two simple zero finding algorithms


Usage (in matlab)
 [x, c, iter_step]=eppO(v, n, rho, p); 

-------------------------- Function eppInf -----------------------------
*/

void  eppO(double *x, double * cc, int * iter_step, double *v,  int n, double rho, double p){

	int i, *flag, bisStep, newtonStep=0, totoalStep=0;	
	double vq=0, epsilon, vmax=0, vmin=1e10; /* we assume that the minimal value in |v| is less than 1e10*/
	double q=1/(1-1/p), c, c1, c2, root, f, xp;

	double x_diff=0; /* this value denotes the maximal difference of the x values computed from c1 and c2*/
	double temp;
	int p_n=1; /* p_n indicates the previous phi(c) is positive or negative*/

	flag=(int *)malloc(sizeof(int)*n);

	/*
	compute vq, the q-norm of v
	flag denotes the sign of v:
	                  flag[i]=0 denotes v[i] is non-negative
					  flag[i]=1 denotes v[i] is negative
	vmin and vmax are the maximal and minimal value of |v| (excluding 0)
	*/
	for(i=0; i< n; i++){

		x[i]=0;

		if (v[i]==0)
			flag[i]=0;
		else
		{		
			if (v[i]>0)
				flag[i]=0;
			else
			{
				flag[i]=1;
				v[i]=-v[i];/*
						   we set v[i] to its absolute value
						   */
			}
			
			vq+=pow(v[i], q);
			
			
			if (v[i]>vmax)
				vmax=v[i];

			if (v[i]<vmin)
				vmin=v[i];		
		}
	}
	vq=pow(vq, 1/q);

	/*
	zero solution
	*/
	if (rho >= vq){
		*cc=0;
		iter_step[0]=iter_step[1]=0;

			
		for(i=0;i<n;i++){
			if (flag[i])				
				v[i]=-v[i]; /* set the value of v[i] back*/
		}

		free(flag);
		return;
	}

	/*
	compute epsilon 
	initialize c1 and c2, the interval where the root lies
	*/
	epsilon=(vq -rho)/ vq;
	if (p>2){

		if ( log((1-epsilon) * vmin) - (p-1) * log( epsilon* vmin ) >= 709 )
		{
			/* If this contition holds, we have c2 >= 1e308, exceeding the machine precision.

			   In this case, the reason is that p is too large 
			   and meanwhile epsilon * vmin is typically small.

               For numerical stablity, we just regard p=inf, and run eppInf
			*/

			
			for(i=0;i<n;i++){
				if (flag[i])				
					v[i]=-v[i]; /* set the value of v[i] back*/
			}

			eppInf(x, cc, iter_step, v,  n, rho, 0);

			free(flag);
			return;
		}

		c1= (1-epsilon) * vmax / pow(epsilon* vmax, p-1);
		c2= (1-epsilon) * vmin / pow(epsilon* vmin, p-1);
	}
	else{ /*1 < p < 2*/

		c2= (1-epsilon) * vmax / pow(epsilon* vmax, p-1);
		c1= (1-epsilon) * vmin / pow(epsilon* vmin, p-1);
	}


	/*
	printf("\n c1=%e, c2=%e", c1, c2);
	*/

	if (fabs(c1-c2) <= delta){
		c=c1;
	}
	else
		c=(c1+c2)/2;
 
	
	bisStep =0;

	while(1){
		bisStep++;

		/*compute the root corresponding to c*/
		x_diff=0;
		for(i=0;i<n;i++){
			zerofind(&root, &newtonStep, v[i], p, c, x[i]);

			temp=fabs(root-x[i]);
			if (x_diff< temp )
				x_diff=temp; /*x_diff denotes the largest gap to the previous solution*/

			x[i]=root;
			totoalStep+=newtonStep;
		}

		xp=norm(x, p, n);

		f=rho * pow(xp, 1-p) - c;

		if ( fabs(f)<=delta || fabs(c1-c2)<=delta )
			break;
		else{
			if (f>0){
				if ( (x_diff <=delta) && (p_n==0) )
					break;

				c1=c;  p_n=1;
			}
			else{

				if ( (x_diff <=delta) && (p_n==1) )
					break;

				c2=c;  p_n=0;
			}
		}
		c=(c1+c2)/2;

		if (bisStep>=outerIter){


			if ( fabs(c1-c2) <=delta * c2 )
				break;
			else{
				printf("\n The number of bisection steps exceed %d.", outerIter);
				printf("\n c1=%e, c2=%e, x_diff=%e, f=%e",c1,c2,x_diff,f);
				printf("\n If you meet with this problem, please contact Jun Liu (j.liu@asu.edu). Thanks!");
				
				return;
			}
		}

		/*
		printf("\n c1=%e, c2=%e, f=%e, newtonStep=%d", c1, c2, f, newtonStep);
		*/
	}
    
	/*
    printf("\n c1=%e, c2=%e, x_diff=%e, f=%e, bisStep=%d, totoalStep=%d",c1,c2, x_diff, f,bisStep,totoalStep);
	*/

	for(i=0;i<n;i++){
		if (flag[i]){
			x[i]=-x[i];
			v[i]=-v[i];
		}
	}
	free(flag);

	*cc=c;

	iter_step[0]=bisStep;
	iter_step[1]=totoalStep;

}





/*
-------------------------- Function epp -----------------------------

 The Lp-norm Regularized Euclidean Projection (epp) for all p>=1
 
        min  1/2 ||x- v||_2^2 + rho ||x||_p
 

 This function uses the previously defined functions.


Usage (in matlab)
 [x, c, iter_step]=eppO(v, n, rho, p, c0); 

-------------------------- Function epp -----------------------------
*/

void epp(double *x, double * c, int * iter_step, double * v, int n, double rho, double p, double c0){


	if (rho <0){
		printf("\n rho should be non-negative!");
        exit(1);
    }

	if (p==1){
		epp1(x, v, n, rho);
		*c=0;
		iter_step[0]=iter_step[1]=0;
	}
	else
		if (p==2){
			epp2(x, v, n, rho);
			*c=0;
			iter_step[0]=iter_step[1]=0;
		}
		else
			if (p>=1e6) /* when p >=1e6, we treat it as infity*/
				eppInf(x, c, iter_step, v,  n, rho, c0);
			else
				eppO(x, c, iter_step, v,  n, rho, p);
}
