function ts = dseriesQ2M(ds, method, shift)

% INPUTS
% - ds       [dseries]    Quarterly object.
% - method   [char]       1×m array, method used for the time aggregation, possible values are:
%                            - 'equal-per-quarter',
%                            - 'cubic-spline'
% - shift    [logical]    scalar, If true, M3 data is consistent with Q1 data, otherwise M1 data is consistent with the first quarter. .
%
% OUTPUTS
% - ts       [dseries]    Monthly object.

% Copyright © 2024-2025 Dynare Team
%
% This code 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.
%
% Dynare dates submodule 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 Dynare.  If not, see <http://www.gnu.org/licenses/>.

if ~isequal(ds.freq, 4)
    error('Conversion only allowed for quarterly data!')
end

if nargin<3
    % Q1 -> M3 if true, Q1 -> M1 otherwise
    shift = true;
end

switch ds.dates.subperiod(1)
  case 1
    isubperiod = 1;
  case 2
    isubperiod = 4;
  case 3
    isubperiod = 7;
  otherwise
    isubperiod = 10;
end

switch ds.dates.subperiod(end)
  case 1
    lsubperiod = 3;
  case 2
    lsubperiod = 6;
  case 3
    lsubperiod = 9;
  otherwise
    lsubperiod = 12;
end

dM = dates(12, [ds.dates.year(1) isubperiod]):dates(12, [ds.dates.year(end) lsubperiod]);

tdata = NaN(dM.ndat, ds.vobs);

switch method
  case 'equal-per-quarter'
    counter = 1;
    for i = 1:3:dM.ndat
        tdata(i:i+2,:) = repmat(ds.data(counter,:), [3,1]);
        counter = counter+1;
    end
  case 'cubic-spline'
    for i = 1:length(ds.name)
        fop.date = firstobservedperiod(ds{ds.name{i}});
        fop.year = fop.date.year(1);
        switch fop.date.subperiod
          case 1
            fop.subperiod = 1;
          case 2
            fop.subperiod = 4;
          case 3
            fop.subperiod = 7;
          otherwise
            fop.subperiod = 10;
        end
        lop.date = lastobservedperiod(ds{ds.name{i}});
        lop.year = lop.date.year(1);
        switch lop.date.subperiod
          case 1
            lop.subperiod = 3;
          case 2
            lop.subperiod = 6;
          case 3
            lop.subperiod = 9;
          otherwise
            lop.subperiod = 12;
        end
        X = 1:(lop.date-fop.date+1);
        x = X(1):(1/3):(X(end)+1);
        x = x(1:end-1);
        id1 = find(dM==dates(12, fop.year, fop.subperiod));
        id2 = find(dM==dates(12, lop.year, lop.subperiod));
        tdata(id1:id2, i) = spline(X, ds{ds.name{i}}(fop.date:lop.date).data, x);
    end
  otherwise
    error('Unknow method.')
end

if shift
    dM = dM+2;
    dM = dM(1:end-2);
    tdata = tdata(1:end-2,:);
end

ts = dseries(tdata, dM, ds.name, ds.tex);

return % --*-- Unit tests --*--

%@test:1
ds = dseries(randn(10,2), '2000Q2');

try
    ts = dseriesQ2M(ds, 'equal-per-quarter');
    t(1) = true;
catch
    t(1) = false;
end

if t(1)
    t(2) = ts.vobs==2;
    t(3) = ts.nobs==28;
    t(4) = ts.dates(1)==dates('2000M6');
    t(5) = ts.dates(end)==dates('2002M9');
end

T = all(t);
%@eof:1


%@test:2
ds = dseries(randn(10,2), '2000Q2');

try
    ts = dseriesQ2M(ds, 'equal-per-quarter', false);
    t(1) = true;
catch
    t(1) = false;
end

if t(1)
    t(2) = ts.vobs==2;
    t(3) = ts.nobs==30;
    t(4) = ts.dates(1)==dates('2000M4');
    t(5) = ts.dates(end)==dates('2002M9');
end

T = all(t);
%@eof:2


%@test:3
ds = dseries(randn(10,2), '2000Q2');

try
    ts = dseriesQ2M(ds, 'cubic-spline');
    t(1) = true;
catch
    t(1) = false;
end

if t(1)
    t(2) = ts.vobs==2;
    t(3) = ts.nobs==28;
    t(4) = ts.dates(1)==dates('2000M6');
    t(5) = ts.dates(end)==dates('2002M9');
end

T = all(t);
%@eof:3


%@test:4
ds = dseries(randn(10,2), '2000Q2');

try
    ts = dseriesQ2M(ds, 'cubic-spline', false);
    t(1) = true;
catch
    t(1) = false;
end

if t(1)
    t(2) = ts.vobs==2;
    t(3) = ts.nobs==30;
    t(4) = ts.dates(1)==dates('2000M4');
    t(5) = ts.dates(end)==dates('2002M9');
end

T = all(t);
%@eof:4
