%% Mark up time series
% Time series has a pattern. The pattern consists of several segments. Each
% segment could be represented as a simple parametric regression function.
% The whole pattern is the sequence of concantenated regression models.
%
% The following information is given:
% 1) One-dimensional time series and
% 2) a set of the regression models. 
% Each model defines:
% - type of regression function (linear, exponential, gaussian, etc.);
% - parameters of the function;
% - minimal and maximal number of samples in the segment;
% - maximal value of the variance of the residuals.
%
% One must compute starts of the segments. Note that there is 
% - no gap between the segments; 
% - there could be any gap between patterns; 
% - segments may vary in length;
% - there are broken, unknown, etc., patterns. 
%
% The main problem is how to define the optimal segment start point between 
% two neighbours segments.
% 
% The principle of the algorithm:
% 1) There is the regression function 'any' for each pattern to begin with. 
% 2) The 1-st point (start of the segment) is the point between 'any' and
% the 1st segment of the pattern. 
% 3) Continue for each segments' pair of the patters.
%
% How to find the point?
% Since each segment could very it's length from min to max, assume the
% Cartesian product of the length of both segments - antecedent and
% consequent. For each pair of segments calculate sum of variances of the
% segment residuals  The minimal variance defines length of the segments
% and so, the point between them.

%% Load the time series
x = dlmread('xMarkUp.csv');
%x(1:1500) = [];
figure; hold on
plot(1:length(x),x,'g.');
axis tight
xlabel time
ylabel value
title('The time series to mark up');

%% Define regression functions for segments
% It is a structure array with the following fields:
%
% The dummy segment (for any possible gap between patterns)
mark(1).func    = 'mark_any';   % function name, the function must be in the Matlab path
mark(1).w       = [];           % regression function parameters (there are no parameters for this function
mark(1).tmin    = 0;            % minimal length of the segment
mark(1).tmax    = 100;          % maximal length of the segment 
                                % NOTE! set the value no more than the length of the pattern 
mark(1).err = 0.1e-6;           % maximal variance of the regression residuals (0.1e-6 for this function)
% The 1st segment
mark(2).func    = 'mark_anylin';
mark(2).w       = 10.818;  
mark(2).tmin    = 9;  %10;
mark(2).tmax    = 12; %15;
mark(2).err     = 5^2;
% The 2nd segment
mark(3).func    = 'mark_const'; % the first constant
mark(3).w       = 150; % 
mark(3).tmin    = 28;
mark(3).tmax    = 36;% 32 GD100;% 
mark(3).err     = 5^2;
% The 3rd segment
mark(4).func    = 'mark_anylin';
mark(4).w       = 4.9548; 
mark(4).tmin    = 36;%24;
mark(4).tmax    = 44;%46;
mark(4).err     = 12^2; 
% The 4rh segment
mark(5).func    = 'mark_anyconst'; % the second constant
mark(5).w       = []; 
mark(5).tmin    = 40; %62 GD100; % 40 ONLY for IPOH
mark(5).tmax    = 68;%80
mark(5).err     = 8^2;
% The 5th (last) segment
mark(6).func    = 'mark_anyexp';
mark(6).w       = [298.7405   -0.0574]; % 
mark(6).tmin    = 80;
mark(6).tmax    = 80;
mark(6).err     = 7^2;
%% Discussion 
% The regression parameters are defined manually. There are two ways to get
% mark the segment: 1) check the variance of the residuals 2) check the
% regression parameters have acceptable values. We chose the 1st way. Below
% we define each segment manually and estimate its possible length and
% parameters.

%% Find the parameters of the 1-st segment, line
ptr_list = [0  
            255 
            255+12+1 
            255+12+39+1
            255+12+39+30+1
            255+12+39+30+60+1
            ]; % Define starts of each segment
        
% 1) Observe the segments one-by-one
for segNum = [2 3 4 5 6] % 
    % 2) Set the start of the segment manually
    ptr = ptr_list(segNum); 
    fprintf(1,'\nSegment %d\n', segNum-1);
    
    % 3) Calculate parameters and variance for each length 
    vecErr = []; % vector of segment variances
    matW = [];    % matrix of segment parameters
    figure; hold on    
    
    for timRelative = mark(segNum).tmin  : mark(segNum).tmax        
        tim = [ptr:ptr+timRelative]';  % time ticks of the segment
        seg = x(tim);               % segment values
        % 4) Get the parameters and variance
        [y, err, w] = feval(mark(segNum).func, mark(segNum).w, seg);    
        vecErr = [vecErr; err];
        matW =  [ matW;, w];
        % 5) Use the parameters to show possible variances for each segment length        
        %[y, vecSig2(end+1) ] = feval(mark(segNum).func, mark(segNum).w, x(ptr:ptr+ti));        
        % 6) Plot the result
        %subplot(5,1,segNum-1); hold on
        plot(tim, seg, 'b.');
        plot(tim, y, 'r-');
        xlabel('time'); ylabel('value');
        title(['Segment ', num2str(segNum-1)]);        
        fprintf(1, '    len = %d, err = %0.2f,             w = %s\n',  timRelative, err, num2str(w));
    end
end


%% The main mark up function 

[patterns, err] = markup(mark, x, 1);
%[patterns, err, params] = markup(mark, x);
%% Plot the result of the marking with the fixed regression parameters
figure; hold on
plot(1:length(x),x,'g.');
axis tight
yminmax = ylim;
colors = {'r','b','y','c','m'};
for segTimeNaN = patterns'
    segTime = segTimeNaN(find (~isnan(segTimeNaN)) );    
    for segNum = 1:length(segTime)-1                         
        tim = [segTime(segNum) : segTime(segNum+1) - 1]'; %NOTE minus one
        seg = x(tim);        
        [segY, seg1sig] = feval(mark(segNum+1).func, mark(segNum+1).w, seg);  % calculate the variance (fitness)      
        plot(tim, seg, [colors{segNum},'.']);
        plot(tim, segY, [colors{segNum},'-']);
    end
end
xlabel time
ylabel value
title('All patterns, including the broken ones');

%% Plot only correct patterns
[patterns, err] = markup(mark, x);
figure; hold on
plot(1:length(x),x,'g.');
axis tight
yminmax = ylim;
colors = {'r','b','y','c','m'};
for segTimeNaN = patterns'
    if ~any(isnan(segTimeNaN))    
        segTime = segTimeNaN;
        for segNum = 1:length(segTime)-1                         
            tim = [segTime(segNum) : segTime(segNum+1) - 1]'; %NOTE minus one
            seg = x(tim);        
            [segY, seg1sig, w] = feval(mark(segNum+1).func, mark(segNum+1).w, seg);  % calculate the variance (fitness)      
            plot(tim, seg, [colors{segNum},'.']);
            plot(tim, segY, [colors{segNum},'-']);
        end
    end
end
xlabel time
ylabel value
title('There is only one correct pattern in the time series');
%% Appendix: library of the regression functions
% now 7 functions are suggested: 
% mark_any, mark_const, mark_lin, mark_exp
% mark_any_const, mark_anylin, mark_anyexp
% The example of mark_exp:
% function [y, err, w] = mark_exp(w, x)
% mark time series x, the exponential function
%
% [idx, w, err] = mark_exp(w, x)
%
% w [1,W] parameters of the model here y = w(1) + w(2)*x;
% x [m,1] time series, the depended variable of the regression fit 
%
% y [m,1] the calculated depended variable
% mse [scalar] the residual variance, MSE
% w [1,W] parameters of the function
%
% if the regression parameters are required, the Levenberg_Markquardt Method will
% be called, and the parameters will be returned together with new MSE.

% Example
% x = [298; 294; 284; 272; 260; 248; 238; 226; 216; 206; 196; 186; 178; 170; 162; 154; 148; 140; 134; 128; 122; 118; 112; 108; 102;  98;  94;  90;  88;  84;  80;  78;  76;  72;  70;  68;  66;  64;  62;  60;  58;  56;  56;  54;  54;  52;  50;  50;  48;  48;  48;  46;  46;  46;  46;  44;  44;  44;  44;  42;  42;  42;  42;  42;  40;  40;  40;  40;  40;  40;  40;  40;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38;  38];
% w = [[30 300 -0.05]]
% [y1, err] = mark_exp(w, x)
% [y2, err, w] = mark_exp(w, x)
% tim = [1:length(x)]';
% figure; hold on
% plot(tim, x, 'k.');
% plot(tim, y1, 'b-');
% plot(tim, y2, 'r-');
% legend('source', 'manual parameters', 'optimised parameters');
% xlabel('time'); ylabel('value');

% f = inline('w(1) + w(2) * exp(w(3) * (1:length(x))'')', 'w', 'x');
% if nargout > 2
%     w = nlinfit((1:length(x))', x, f, w);
% end
% y = f(w,x);
% r = x-y;
% err = ( (r'*r) / length(x) );   
% return
