classdef tseries < userdataobj
    % tseries  Time series objects and functions.
    %
    % Tseries methods:
    %
    % Constructor
    % ============
    %
    % * [`tseries`](tseries/tseries) - Create new time series (tseries) object.
    %
    % Getting information about tseries objects
    % ==========================================
    %
    % * [`daily`](tseries/daily) - Calendar view of a daily tseries object.
    % * [`enddate`](tseries/enddate) - Date of the last available observation in a tseries object.
    % * [`freq`](tseries/freq) - Frequency of a tseries object.
    % * [`get`](tseries/get) - Query tseries object property.
    % * [`length`](tseries/length) - Length of tseries object.
    % * [`ndims`](tseries/ndims) - Number of dimensions in tseries object data.
    % * [`size`](tseries/size) - Size of tseries object data.
    % * [`startdate`](tseries/startdate) - Date of the first available observation in a tseries object.
    % * [`yearly`](tseries/yearly) - Display tseries object one full year per row.
    %
    % Referencing tseries objects
    % ============================
    %
    % * [`subsasgn`](tseries/subsasgn) - Subscripted assignment for tseries objects.
    % * [`subsref`](tseries/subsref) - Subscripted reference function for tseries objects.
    %
    % Maths and statistics functions and operators
    % =============================================
    %
    % Some of the following functions require the Statistics Toolbox.
    %
    % `+`, `-`, `*`, `\`, `/`, `^`, `&`, `|`, `~`, `==`, `~=`, `>=`, `>`, `<`,
    % `<=`, `abs`, `acos`, `asin`, `atan`, `atan2`, `ceil`, `cos`,  `exp`, `floor`,
    % `imag`, `isinf`, `isnan`, `log`, `log10`, `real`, `round`, `sin`, `sqrt`,
    % `tan`, `normpdf`, `normcdf`, `prctile`, `lognpdf`, `logncdf`
    %
    % The behaviour of the following functions depend on the dimension along
    % which they are performed.
    %
    % Some of the following functions require the Statistics Toolbox.
    %
    % `all`, `any`, `cumprod`, `cumsum`, `find`, `geomean`, `max`, `mean`,
    % `median`, `min`, `mode`, `nanmean`, `nanstd`, `nansum`, `nanvar`, `prod`,
    % `std`, `sum`, `var`
    %
    % Filters
    % ========
    %
    % * [`arf`](tseries/arf) - Run autoregressive function on a tseries object.
    % * [`bpass`](tseries/bpass) - Band-pass filter.
    % * [`bwf`](tseries/bwf) - Butterworth filter with tunes.
    % * [`bwf2`](tseries/bwf2) - Swap output arguments of the Butterworth filter with tunes.
    % * [`detrend`](tseries/detrend) - Remove a linear time trend.
    % * [`expsmooth`](tseries/expsmooth) - Exponential smoothing.
    % * [`hpf`](tseries/hpf) - Hodrick-Prescott filter with tunes.
    % * [`hpf2`](tseries/hpf2) - Swap output arguments of the Hodrick-Prescott filter with tunes.
    % * [`fft`](tseries/fft) - Discrete Fourier transform of tseries object.
    % * [`llf`](tseries/llf) - Local linear trend (or random-walk-plus-noise) filter with tunes.
    % * [`llf2`](tseries/llf2) - Swap output arguments of the local linear trend filter with tunes.
    % * [`moving`](tseries/moving) - Apply function to moving window of observations.
    % * [`trend`](tseries/trend) - Estimate a time trend.
    % * [`x12`](tseries/x12) - Access to X12 seasonal adjustment program.
    %
    % Estimation and sample characteristics
    % ======================================
    %
    % Note that most of the sample characteristics are listed above in the
    % Maths and statistics functions and operatiors section.
    %
    % * [`acf`](tseries/acf) - Sample autocovariance and autocorrelation functions.
    % * [`hpdi`](tseries/hpdi) - Highest probability density interval.
    % * [`chowlin`](tseries/chowlin) - Chow-Lin distribution of low-frequency observations over higher-frequency periods.
    % * [`regress`](tseries/regress) - Ordinary or weighted least-square regression.
    %
    % Visualising tseries objects
    % ============================
    %
    % * [`area`](tseries/area) - Area graph for tseries objects.
    % * [`bar`](tseries/bar) - Bar graph for tseries objects.
    % * [`barcon`](tseries/barcon) - Contribution bar graph for tseries objects.
    % * [`errorbar`](tseries/errorbar) - Line plot with error bars.
    % * [`plot`](tseries/plot) - Line graph for tseries objects.
    % * [`plotcmp`](tseries/plotcmp) - Comparison graph for two time series.
    % * [`plotpred`](tseries/plotpred) - Plot Kalman filter predictions.
    % * [`plotyy`](tseries/plotyy) - Line plot function with LHS and RHS axes for time series.
    % * [`scatter`](tseries/scatter) - Scatter graph for tseries objects.
    % * [`spy`](tseries/spy) - Visualise tseries observations that pass a test.
    % * [`stem`](tseries/stem) - Plot tseries as discrete sequence data.
    %
    % Manipulating tseries objects
    % =============================
    %
    % * [`empty`](tseries/empty) - Empty tseries object preserving its size in 2nd and higher dimensions.
    % * [`permute`](tseries/permute) - Permute dimensions of a tseries object.
    % * [`redate`](tseries/redate) - Change time dimension of a tseries object.
    % * [`reshape`](tseries/reshape) - Reshape size of time series in 2nd and higher dimensions.
    % * [`resize`](tseries/resize) - Clip tseries object down to specified date range.
    % * [`sort`](tseries/sort) - Sort tseries columns by specified criterion.
    %
    % Converting tseries objects
    % ===========================
    %
    % * [`convert`](tseries/convert) - Convert tseries object to a different frequency.
    % * [`double`](tseries/double) - Return tseries observations as double-precision numeric array.
    % * [`doubledata`](tseries/doubledata) - Convert tseries observations to double precision.
    % * [`single`](tseries/single) - Return tseries observations as single-precision numeric array.
    % * [`singledata`](tseries/singledata) - Convert tseries observations to single precision.
    %
    % Other tseries functions
    % ========================
    %
    % * [`apct`](tseries/apct) - Annualised percent rate of change.
    % * [`bsxfun`](tseries/bsxfun) - Standard BSXFUN implemented for tseries objects.
    % * [`cumsumk`](tseries/cumsumk) - Cumulative sum with a k-period leap.
    % * [`destdise`](tseries/destdise) - Destandardise tseries object by applying specified standard deviation and mean to it.
    % * [`diff`](tseries/diff) - First difference.
    % * [`interp`](tseries/interp) - Interpolate missing observations.
    % * [`normalise`](tseries/normalise) - Normalise data to particular date.
    % * [`pct`](tseries/pct) - Percent rate of change.
    % * [`round`](tseries/round) - Round tseries data to specified number of decimals.
    % * [`stdise`](tseries/stdise) - Standardise tseries data by subtracting mean and dividing by std deviation.
    % * [`windex`](tseries/windex) - Simple weighted or Divisia index.
    % * [`wmean`](tseries/wmean) - Weighted average of time series observations.
    %
    % Getting on-line help on tseries functions
    % ==========================================
    %
    %     help tseries
    %     help tseries/function_name
    %
    
    % -IRIS Toolbox.
    % -Copyright (c) 2007-2012 Jaromir Benes.
    
    properties
        start = NaN;
        data = zeros(0,1);
    end
    
    methods
        function this = tseries(varargin)
            % tseries  Create new time series (tseries) object.
            %
            % Syntax
            % =======
            %
            %     X = tseries()
            %     X = tseries(DATES,VALUES)
            %     X = tseries(DATES,VALUES,COMMENTS)
            %
            % Input arguments
            % ================
            %
            % * `DATES` [ numeric ] - Dates for which observations will be supplied;
            % `dates` do not have to be sorted in ascending order. If `dates` is scalar
            % and `values` have multiple rows, then the date in `dates` is interpreted
            % as a startdate for the time series.
            %
            % * `VALUES` [ numeric | function_handle ] - Numerical values
            % (observations) arranged columnwise, or a function that will be used to
            % create an N-by-1 array of values, where N is the number of `dates`.
            %
            % * `COMMENTS` [ char | cellstr ] - Comment or comments attached to each
            % column of observations.
            %
            % Output arguments
            % =================
            %
            % * `X` [ tseries ] - New tseries object.
            %
            % Description
            % ============
            %
            % Example
            % ========
            
            % -IRIS Toolbox.
            % -Copyright (c) 2007-2012 Jaromir Benes.
            
            this = this@userdataobj();
            this.Comment = {''};
            
            % Empty call.
            if nargin == 0
                return
            end
            % Tseries input.
            if nargin == 1 && istseries(varargin{1})
                this = varargin{1};
                return
            end
            % Struct input. Called from within load(), loadobj(), loadstruct(), cat(),
            % hdataouput().
            if nargin == 1 && isstruct(varargin{1})
                if isfield(varargin{1},'start') ...
                        && isfield(varargin{1},'data') ...
                        && isfield(varargin{1},'Comment') ...
                        && isfield(varargin{1},'UserData')
                    this.start = varargin{1}.start;
                    this.data = varargin{1}.data;
                    this.Comment = varargin{1}.Comment;
                    this.UserData = varargin{1}.UserData;
                else
                    this = mystruct2obj(this,varargin{1});
                end
                return
            end
            
            % User dates.
            usrdates = varargin{1}(:);
            nper = length(usrdates);
            
            % User data.
            if nargin < 2
                usrdata = nan(size(usrdates,1),1);
            else
                usrdata = varargin{2};
            end
            
            % User comments.
            if nargin < 3
                usrcomment = '';
            else
                usrcomment = varargin{3};
            end
            
            % Parse required input arguments.
            P = inputParser();
            P.addRequired('dates',@isnumeric);
            P.addRequired('data',@(x) ...
                isnumeric(x) || islogical(x) || ischar(x) || isfunc(x));
            P.addRequired('comments',@(x) ischar(x) || iscellstr(x));
            P.parse(usrdates,usrdata,usrcomment);
            
            %**************************************************************
            
            % Find out the date frequency and check for its consistency.
            freq = datfreq(usrdates);
            freq(isnan(freq)) = [];
            if length(freq) > 1 && any(freq(1) ~= freq(2:end))
                error('All dates must have the same frequency.');
            end
            
            % Create data from a function handle or function name.
            if ischar(usrdata) && strcmpi(usrdata,'lintrend')
                usrdata = (1 : nper).';
            elseif ischar(usrdata) || isfunc(usrdata)
                try
                    usrdata = feval(usrdata,[nper,1]);
                catch %#ok<CTCH>
                    usrdata = feval(char(usrdata),[nper,1]);
                end
            elseif isnumeric(usrdata) || islogical(usrdata)
                if sum(size(usrdata) > 1) == 1 ...
                        && length(usrdata) > 1 ...
                        && nper > 1
                    % Squeeze if scalar time series is entered as an
                    % other-than-columnwise vectors.
                    usrdata = usrdata(:);
                elseif length(usrdata) == 1 && nper > 1
                    % Expand scalar `usrdata` point to match more than one of
                    % `usrdates`.
                    usrdata = usrdata(ones(size(usrdates)));
                end
            end
            
            % If `usrdates` is scalar and `usrdata` have multiple rows,
            % treat the `usrdates` as a start date and expand it accordingly.
            if nper == 1 && size(usrdata,1) > 1
                usrdates = usrdates + (0 : size(usrdata,1)-1);
            end
            
            % Initialise the time series start date, data.
            this = init(this,usrdates,usrdata);
            
            % Populate comments for each column.
            commentsize = size(this.data);
            commentsize(1) = 1;
            this.Comment = cell(commentsize);
            this.Comment(:) = {''};
            if isempty(usrcomment)
                this.Comment(:) = {''};
            elseif ischar(usrcomment)
                this.Comment(:) = {usrcomment};
            else
                this.Comment(:) = usrcomment(:);
            end
            
            if ~isempty(this.data) && any(any(isnan(this.data([1,end],:))))
                this = mytrim(this);
            end
            
        end
        
    end
    
    methods
        varargout = acf(varargin)
        varargout = apct(varargin)
        varargout = area(varargin)
        varargout = arf(varargin)
        varargout = bar(varargin)
        varargout = barcon(varargin)
        varargout = bpass(varargin)
        varargout = bwf(varargin)
        varargout = bwf2(varargin)
        varargout = bsxfun(varargin)
        varargout = chowlin(varargin)
        varargout = comment(varargin)
        varargout = conbar(varargin)
        varargout = convert(varargin)
        varargout = cumsumk(varargin)
        varargout = daily(varargin)
        varargout = destdise(varargin)
        varargout = detrend(varargin)
        varargout = diff(varargin)
        varargout = double(varargin)
        varargout = doubledata(varargin)
        varargout = empty(varargin)
        varargout = enddate(varargin)
        varargout = errorbar(varargin)
        varargout = expsmooth(varargin)
        varargout = fft(varargin)
        varargout = find(varargin)
        varargout = freq(varargin)
        varargout = get(varargin);
        varargout = horzcat(varargin)
        varargout = hpdi(varargin)
        varargout = hpf(varargin)
        varargout = hpf2(varargin)
        varargout = interp(varargin)
        varargout = isempty(varargin)
        varargout = isscalar(varargin)
        varargout = length(varargin)
        varargout = llf(varargin)
        varargout = llf2(varargin)
        varargout = moving(varargin)
        varargout = ndims(varargin)
        varargout = normalise(varargin)
        varargout = pct(varargin)
        varargout = permute(varargin)
        varargout = plot(varargin)
        varargout = plotcmp(varargin)
        varargout = plotyy(varargin)
        varargout = plotpred(varargin)
        varargout = range(varargin)
        varargout = regress(varargin)
        varargout = reshape(varargin)
        varargout = resize(varargin)
        varargout = round(varargin)
        varargout = scatter(varargin)
        varargout = single(varargin)
        varargout = singledata(varargin)
        varargout = size(varargin)
        varargout = sort(varargin)
        varargout = spy(varargin)
        varargout = startdate(varargin)
        varargout = stdise(varargin)
        varargout = stem(varargin)
        varargout = subsasgn(varargin)
        varargout = subsref(varargin)
        varargout = trend(varargin)
        varargout = vertcat(varargin)
        varargout = wmean(varargin)
        varargout = x12(varargin)
        varargout = yearly(varargin)
    end
    
    methods (Hidden)
        disp(varargin)
        varargout = mytrim(varargin)
        varargout = cat(varargin)
        varargout = cut(varargin)
        varargout = destdize(varargin)
        varargout = df(varargin)
        varargout = divisia(varargin)
        varargout = specrange(varargin)
        varargout = loadobj(varargin)
        varargout = maxabs(varargin)
        varargout = normalize(varargin)
        varargout = rangedata(varargin)
        varargout = replace(varargin)
        varargout = saveobj(varargin)
        varargout = stdize(varargin)
    end
    
    methods (Access=protected,Hidden)
        mydispheader(varargin)
        varargout = myfilter(varargin)
        varargout = mygetdata(varargin)
        varargout = myinit(varargin)
        varargout = mystruct2obj(varargin)
        varargout = binop(varargin)
        varargout = unop(varargin)
        varargout = lagorlead(varargin)
        varargout = catcheck(varargin)
        function dispcomment(varargin)
        end
    end
    
    methods (Static,Hidden)
        timeline(varargin)
        varargout = mybarcon(varargin)
        varargout = mybpass(varargin)
        varargout = mychristianofitzgerald(varargin)
        varargout = mycumsumk(varargin)
        varargout = mydestdize(varargin)
        varargout = mydiff(varargin)
        varargout = myexpsmooth(varargin)
        varargout = myhpdi(varargin)
        varargout = myjusterrorbars(varargin)
        varargout = mymoving(varargin)
        varargout = mynanmean(varargin)
        varargout = mynanstd(varargin)
        varargout = mynansum(varargin)
        varargout = mynanvar(varargin)
        varargout = mypct(varargin)
        varargout = myrmse(varargin)
        varargout = myshift(varargin)
        varargout = mystdize(varargin)
        varargout = mytrend(varargin)
        varargout = gplot(varargin)
    end
    
    methods (Static)
        varargout = edit(varargin)
    end
    
    methods (Hidden)
        function x = abs(x)
            x.data = abs(x.data);
        end
        function x = acos(x)
            x.data = acos(x.data);
            x = mytrim(x);
        end
        function x = and(x,y)
            x = binop(@and,x,y);
        end
        function x = asin(x)
            x.data = asin(x.data);
            x = mytrim(x);
        end
        function x = atan(x)
            x.data = atan(x.data);
            x = mytrim(x);
        end
        function x = atan2(x)
            x.data = atan2(x.data);
            x = mytrim(x);
        end
        function x = ceil(x)
            x.data = ceil(x.data);
        end
        function x = complex(x)
            x.data = complex(x.data);
        end
        function x = cos(x)
            x.data = cos(x.data);
            x = mytrim(x);
        end
        function x = eq(a,b)
            x = binop(@eq,a,b);
        end
        function x = exp(x)
            x.data = exp(x.data);
            x = mytrim(x);
        end
        function x = floor(x)
            x.data = floor(x.data);
        end
        function x = ge(a,b)
            x = binop(@ge,a,b);
        end
        function x = gt(a,b)
            x = binop(@gt,a,b);
        end
        function x = imag(x)
            x = unop(@imag,x,0);
            x = mytrim(x);
        end
        function x = isinf(x)
            x.data = isinf(x.data);
        end
        function x = isnan(x)
            x.data = isnan(x.data);
        end
        function flag = isreal(x)
            flag = isreal(x.data);
        end
        function x = ldivide(a,b)
            x = binop(@ldivide,a,b);
        end
        function x = le(a,b)
            x = binop(@le,a,b);
        end
        function x = log(x)
            x.data = log(x.data);
            x = mytrim(x);
        end
        function x = log10(x)
            x.data = log10(x.data);
            x = mytrim(x);
        end
        function x = lt(a,b)
            x = binop(@lt,a,b);
        end
        function x = minus(a,b)
            x = binop(@minus,a,b);
        end
        function x = mldivide(x,y)
            if (isa(x,'tseries') && isa(y,'tseries')) ...
                    || (isnumeric(y) && length(y) == 1)
                x = binop(@ldivide,x,y);
            else
                x = binop(@mldivide,x,y);
            end
        end
        function x = mpower(x,y)
            x = binop(@power,x,y);
        end
        function x = mrdivide(x,y)
            if (isa(x,'tseries') && isa(y,'tseries')) ...
                    || (isnumeric(x) && length(x) == 1)
                x = binop(@rdivide,x,y);
            else
                x = binop(@mrdivide,x,y);
            end
        end
        function x = mtimes(x,y)
            if isa(x,'tseries') && isa(y,'tseries')
                x = binop(@times,x,y);
            else
                x = binop(@mtimes,x,y);
            end
        end
        function x = nanmean(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@tseries.mynanmean,x,dim,dim);
        end
        function x = nanstd(x,flag,dim)
            if nargin < 2
                flag = 0;
            end
            if nargin < 3
                dim = 1;
            end
            x = unop(@tseries.mynanstd,x,dim,flag,dim);
        end
        function x = nansum(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@tseries.mynansum,x,dim,dim);
        end
        function x = nanvar(x,flag,dim)
            if nargin < 2
                flag = 0;
            end
            if nargin < 3
                dim = 1;
            end
            x = unop(@tseries.mynanvar,x,dim,flag,dim);
        end
        function x = ne(x,y)
            x = binop(@ne,x,y);
        end
        function x = normcdf(x,varargin)
            x.data = normcdf(x.data,varargin{:});
            x = mytrim(x);
        end
        function x = normpdf(x,varargin)
            x.data = normpdf(x.data,varargin{:});
            x = mytrim(x);
        end
        function x = logncdf(x,varargin)
            x.data = logncdf(x.data,varargin{:});
            x = mytrim(x);
        end
        function x = lognpdf(x,varargin)
            x.data = lognpdf(x.data,varargin{:});
            x = mytrim(x);
        end
        function x = not(x)
            x.data = not(x.data);
        end
        function x = or(x,y)
            x = binop(@or,x,y);
        end
        function x = plus(x,y)
            x = binop(@plus,x,y);
        end
        function x = power(x,y)
            x = binop(@power,x,y);
        end
        function x = prod(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@prod,x,dim,dim);
        end
        function x = rdivide(x,y)
            x = binop(@rdivide,x,y);
        end
        function x = real(x)
            x.data = real(x.data);
            x = mytrim(x);
        end
        function x = sin(x)
            x.data = sin(x.data);
            x = mytrim(x);
        end
        function x = sqrt(x)
            x.data = sqrt(x.data);
            x = mytrim(x);
        end
        function x = tan(x)
            x.data = tan(x.data);
            x = mytrim(x);
        end
        function x = times(x,y)
            x = binop(@times,x,y);
        end
        function x = uminus(x)
            x.data = -x.data;
        end
        function x = uplus(x)
        end
        
        % Functions whose behaviour depends on the dimension.
        function x = any(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@any,x,dim,dim);
        end
        function x = all(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@all,x,dim,dim);
        end
        function x = cumprod(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@cumprod,x,0,dim);
        end
        function x = cumsum(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@cumsum,x,0,dim);
        end
        function a = geomean(x,dim)
            if nargin < 2
                dim = 1;
            end
            a = unop(@geomean,x,dim,dim);
        end
        function x = max(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@max,x,dim,[],dim);
        end
        function x = mean(x,dim)
            if nargin  < 2
                dim = 1;
            end
            x = unop(@mean,x,dim,dim);
        end
        function x = median(x,dim)
            if nargin  < 2
                dim = 1;
            end
            x = unop(@median,x,dim,dim);
        end
        function x = min(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@min,x,dim,[],dim);
        end
        function x = mode(x,dim)
            if nargin  < 2
                dim = 1;
            end
            x = unop(@mode,x,dim,dim);
        end
        function x = prctile(x,p,dim)
            if nargin < 3
                dim = 1;
            end
            x = unop(@prctile,x,dim,p,dim);
        end
        function x = rmse(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@tseries.myrmse,x,dim,dim);
        end
        function x = std(x,flag,dim)
            if nargin < 2
                flag = 0;
            end
            if nargin < 3
                dim = 1;
            end
            x = unop(@std,x,dim,flag,dim);
        end
        function x = sum(x,dim)
            if nargin < 2
                dim = 1;
            end
            x = unop(@sum,x,dim,dim);
        end
        function x = var(x,flag,dim)
            if nargin < 2
                flag = 0;
            end
            if nargin < 3
                dim = 1;
            end
            x = unop(@var,x,dim,flag,dim);
        end
        
        % Indexing.
        function index = end(x,k,n) %#ok<INUSD>
            if k == 1
                index = x.start + size(x.data,1) - 1;
            else
                index = size(x.data,k);
            end
        end
        function n = numel(x,varargin)
            n = 1;
        end
        
    end
    
end