function [reconstruction,coarse_approximations]=gsp_pyramid_synthesis(Gs,coarsest_approximation,prediction_errors,param)
%GSP_PYRAMID_SYNTHESIS Synthesizes a signal from its graph pyramid transform coefficients 
%   Usage:  reconstruction=gsp_pyramid_synthesis(Gs,coarsest_approximation,prediction_errors);
%           reconstruction=gsp_pyramid_synthesis(Gs,coarsest_approximation,prediction_errors,param);
%
%   Input parameters:
%         Gs                      : A multiresolution sequence of graph structures, including the idx parameters tracking the subsampling pattern.
%         coarsest_approximation  : The coarsest approximation of the original signal.
%         prediction_errors       : Cell array with the prediction errors at each level.
%   Output parameters:
%         reconstruction          : The synthesized signal.
%         coarse_approximations   : Sequence of coarse approximations
%
%   'gsp_pyramid_synthesis(Gs,coarsest_approximation,prediction_errors)' 
%   synthesizes a signal from its graph pyramid transform coefficients. 
%  
%   param is a structure containing optional arguments including
%
%    param.regularize_epsilon : Interpolation parameter.
%    param.least_squares : Set to 1 to use the least squares synthesis 
%     (default=0)  
%    param.use_landweber : Set to 1 to use the Landweber iteration 
%     approximation in the least squares synthesis.
%    param.landweber_its : Number of iterations in the Landweber 
%     approximation for least squares synthesis.
%    param.landweber_tau : Parameter for the Landweber iteration.
%    param.h_filters : A cell array of graph spectral filters. If just
%     one filter is included, it is used at every level of the pyramid. 
%     These filters are required for least squares synthesis, but not for 
%     the direct synthesis method 
%     Default 
%
%            h(x) = 0.5 / ( 0.5 + x)
%   
%   Please read the documentation of GSP_FILTER_ANALYSIS for other
%   optional arguments.
%
%   See also: gsp_graph_multiresolution gsp_pyramid_analysis 
%             gsp_pyramid_analysis_single gsp_pyramid_cell2coeff
%             gsp_pyramid_synthesis_single
%
%   Demo: gsp_demo_pyramid
% 
%   References:
%     D. I. Shuman, M. J. Faraji, and P. Vandergheynst. A framework for
%     multiscale transforms on graphs. arXiv preprint arXiv:1308.4942, 2013.
%     
%     
%
%   Url: https://epfl-lts2.github.io/gspbox-html/doc/operators/gsp_pyramid_synthesis.html

% Copyright (C) 2013-2016 Nathanael Perraudin, Johan Paratte, David I Shuman.
% This file is part of GSPbox version 0.7.5
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

% If you use this toolbox please kindly cite
%     N. Perraudin, J. Paratte, D. Shuman, V. Kalofolias, P. Vandergheynst,
%     and D. K. Hammond. GSPBOX: A toolbox for signal processing on graphs.
%     ArXiv e-prints, Aug. 2014.
% http://arxiv.org/abs/1408.5781

%   Author : David I Shuman, Nathanael Perraudin.
%   Date   : 26 November 2015
%   Testing: test_pyramid
  

% Read input parameters 
if nargin<4
    param=struct;
end
if ~isfield(param, 'least_squares') param.least_squares=0; end

num_levels=length(prediction_errors);

if param.least_squares
    if ~isfield(param, 'h_filters')
        error('h-filter not provided');
    end
end

if ~isfield(param,'h_filters')
    param.h_filters=cell(num_levels,1);
    for i=1:num_levels
        param.h_filters{i}=@(x) .5./(.5+x);
    end
elseif length(param.h_filters)==1
    if iscell(param.h_filters)
        filter=param.h_filters{1};
    else
        filter=param.h_filters;
    end
    param.h_filters=cell(num_levels,1);
    for i=1:num_levels
        param.h_filters{i}=filter;
    end
elseif length(param.h_filters)~=num_levels
    error('param.h_filters should be a cell array of length 1 or num_levels');
end


% Compute the inverse pyramid transform 
coarse_approximations = cell(num_levels+1,1);
coarse_approximations{num_levels+1} = coarsest_approximation;

for i=num_levels:-1:1
    param.h_filter=param.h_filters{i};
    coarse_approximations{i}=gsp_pyramid_synthesis_single(Gs{i},coarse_approximations{i+1},prediction_errors{i},Gs{i+1}.mr.idx,param);
end
reconstruction=coarse_approximations{1};


