classdef EQ
    %EQ Summary of this class goes here
    %   Detailed explanation goes here

    properties
        Ne %Number of feed forward coefficients (1st, 2nd and 3rd order)
        Nb %Number of decision feedback coefficients (1st, 2nd and 3rd order)
        K  %Number of samples per symbol
        delay %Delay of incoming signal
        training_length %Number of training symbols
        training_loops %Number of loops through sequence for training mode
        ideal_dfe %Error free DFE decisions

        DB_aim %Aim at duobinary output sequence

        M %Order of the PAM constellation (only relevant in case of DB aim)

        FFEmu %mu parameter for FFE part in training mode (0 means normalized LMS)
        DFEmu % mu parameter for DFE part in training mode
        dd_loops % Number of loops through sequence for DD mode
        DDmu % mu parameters for DD mode (individual value for each order)
        DCmu % mu parameter for the dc tap

        l1act %Activate/deactive l1 regularization
        rho %Parameter for speed of coeff shrinking
        epsilon %Reciprocal value of the magnitude of the coeff to converge to zero (1st,2nd,3rd order)
        thres %Theshold for neglecting coefficienties (1st,2nd,3rd order)
        static_act %Activate/deactive static coefficient reduction

        mode2nd %0: no reduction | 1: polynomial | 2: restricted to interval
        len_2nd %length of the interval (only for 2nd order mode = 2)

        mode3rd %0: no reduction | 1: polynomial | 2: restricted to interval
        len_3rd %length of the interval (only for 3rd order mode = 3/4)

        plottrain
        plotfinal
        load_decisions

        save_taps


        %during simulation
        k0
        b
        b2
        b3
        e
        e2
        e3

        coeff_number
        constellation_in

        error_log

    end

    methods
        function obj = EQ(options)
            %EQ Construct an instance of this class
            %   Detailed explanation goes here
            arguments(Input)
                options.Ne = [10 0 0] %Number of feed forward coefficients (1st, 2nd and 3rd order)
                options.Nb = [10 0 0]%Number of decision feedback coefficients (1st, 2nd and 3rd order)
                options.K  = 1 %Number of samples per symbol
                options.delay = 0 %Delay of incoming signal
                options.training_length = 1024 %Number of training symbols
                options.training_loops = 1 %Number of loops through sequence for training mode
                options.ideal_dfe = 0 %Error free DFE decisions

                options.DB_aim %Aim at duobinary output sequence

                options.M = 1 %Order of the PAM constellation (only relevant in case of DB aim)

                options.FFEmu = 0 %mu parameter for FFE part in training mode (0 means normalized LMS)
                options.DFEmu = 0.005 % mu parameter for DFE part in training mode
                options.dd_loops = 1% Number of loops through sequence for DD mode
                options.DDmu = [0.0004 0.0004 0.0004 0.0004 ] % mu parameters for DD mode (individual value for each order)
                options.DCmu = 0.005 % mu parameter for the dc tap

                options.l1act = 0 %Activate/deactive l1 regularization
                options.rho = 5e-4%Parameter for speed of coeff shrinking
                options.epsilon = [10 100 1000] %Reciprocal value of the magnitude of the coeff to converge to zero (1st,2nd,3rd order)
                options.thres = [5e-3 4e-3 5e-4]%Theshold for neglecting coefficienties (1st,2nd,3rd order)
                options.static_act = 0 %Activate/deactive static coefficient reduction

                options.mode2nd = 1%0: no reduction | 1: polynomial | 2: restricted to interval
                options.len_2nd = 1 %length of the interval (only for 2nd order mode = 2)

                options.mode3rd = 1%0: no reduction | 1: polynomial | 2: restricted to interval
                options.len_3rd = 1%length of the interval (only for 3rd order mode = 3/4)

                options.plottrain = 0
                options.plotfinal = 0
                options.load_decisions = 0
                options.save_taps = 0
            end

            fn = fieldnames(options);
            for n = 1:numel(fn)
                obj.(fn{n}) = options.(fn{n});
            end

        end

        function [signalclass_out,error_log] = process(obj,signalclass_in, reference_signalclass_in)

            % actual processing of the signal (steps 1. - 3.)
            [signalclass_in.signal,error_log] = obj.process_(signalclass_in.signal', reference_signalclass_in.signal');

            signalclass_in.signal = signalclass_in.signal';
            % append to logbook
            lbdesc = ['EQ '];
            signalclass_in  = signalclass_in.logbookentry(lbdesc);

            % write to output
            signalclass_out = signalclass_in;

        end

        function [yout,error_log] = process_(obj,data_in,ref_in)
            %METHOD1 Summary of this method goes here
            %   Detailed explanation goes here

            if obj.DB_aim
                ref_DB = zeros(size(ref_in));
                for k = 1:length(ref_in)
                    if k == 1
                        ref_DB(k) = ref_in(k);
                    else
                        ref_DB(k) = ref_in(k) + ref_in(k-1);
                    end
                end
                ref_in = ref_DB;
            end

            ref = [zeros(1,obj.Nb(1)-1) ref_in zeros(1,obj.Nb(1))];

            if isreal(ref)
                cplx = 0;
            else
                cplx = 1;
            end

            if obj.static_act
                obj.mode2nd = obj.mode2nd + 1;
                obj.mode3rd = obj.mode3rd + 1;
            else
                obj.mode2nd = 1;
                obj.mode3rd = 1;
            end

            if obj.mode2nd == 1
                N2 = (obj.Ne(2)*(obj.Ne(2)+1))/2; % Number of coefficients for second order
            elseif obj.mode2nd == 2
                N2 = obj.Ne(2);
            elseif obj.mode2nd == 3
                N2 = (obj.len_2nd+1)*(2*obj.Ne(2)-obj.len_2nd)/2;
            elseif obj.mode2nd == 4
                N2 = (ceil(obj.Ne(2)/2)+1)*(2*obj.Ne(2)-ceil(obj.Ne(2)/2))/2;
            end

            if obj.mode3rd == 1
                if cplx
                    N3 = obj.Ne(3)^2*(obj.Ne(3)+1)/2;
                else
                    N3 = obj.Ne(3)*(obj.Ne(3)+1)*(obj.Ne(3)+2)/6; % Number of coefficients for third order
                end
            elseif obj.mode3rd == 2
                N3 = obj.Ne(3);
            elseif obj.mode3rd == 3
                N3 = obj.Ne(3)^2;
            elseif obj.mode3rd == 4
                N3 = round(1/6*(obj.len_3rd+1)*(obj.len_3rd+2)*(3*obj.Ne(3)-2*obj.len_3rd));
            elseif obj.mode3rd == 5
                N3 = 2*obj.Ne(3)*obj.len_3rd-obj.len_3rd*(obj.len_3rd+1)+obj.Ne(3);
            end

            Nb2 = (obj.Nb(2)*(obj.Nb(2)+1))/2;
            Nb3 = obj.Nb(3)*(obj.Nb(3)+1)*(obj.Nb(3)+2)/6;

            data_in = data_in/sqrt(mean(abs(data_in).^2));  % power normalization of input sequence

            if obj.FFEmu == 0
                norm_fac2 = sqrt(mean(abs(data_in.^2).^2));     % power normalization for second and third order terms
                norm_fac3 = sqrt(mean(abs(data_in.^3).^2));     % (not necessary, but seems to be more stable if applied --> same as different mu values for linear and nl terms)
            else
                norm_fac2 = 1;
                norm_fac3 = 1;
            end

            norm_fac_DFE2 = sqrt(mean(abs(ref_in.^2).^2)); % same for DFE input (reference)
            norm_fac_DFE3 = sqrt(mean(abs(ref_in.^3).^2));

            data = [zeros(1,floor(obj.Ne(1)/2)) data_in zeros(1,obj.Ne(1))];

            delta_2 = round((obj.Ne(1)-obj.Ne(2))/2);
            delta_3 = round((obj.Ne(1)-obj.Ne(3))/2);

            delta_DFE2 = 1;%round((obj.Nb-obj.Nb(2))/2);
            delta_DFE3 = 1;%round((obj.Nb-obj.Nb(3))/2);

            % calculate the indices for the combination of second and third order symbols
            % - done in advance because it's the same for each iteration, so time
            % can be saved

            [ind_mat_2nd,ind_mat_3rd] = obj.calc_ind(obj.Ne(2),N2,obj.Ne(3),N3,obj.mode2nd,obj.mode3rd,obj.len_2nd,obj.len_3rd,cplx);

            [ind_mat_DFE_2nd,ind_mat_DFE_3rd] = obj.calc_DFE_ind(obj.Nb(2),Nb2,obj.Nb(3),Nb3);

            if obj.l1act
                epsilon_ = diag([ones(1,obj.Ne(1))*obj.epsilon(1) ones(1,N2)*obj.epsilon(2) ones(1,N3)*obj.epsilon(3)]);
            end

            obj.k0 = obj.delay; % input delay compared to training sequence
            error_log = [];
            
            if 1 %  obj.active
                %% Calculation of the filter coefficients in training based LMS mode

                e_ = zeros(obj.Ne(1)+N2+N3,1); % initialization of filter coefficients

                b_ = zeros(obj.Nb(1)+Nb2+Nb3,1);

                e_dc = mean(data_in);          % initilaization of the dc tap with the mean value of the whole data

                for trainloops = 1:obj.training_loops
                    cnt = 1;
                    m = obj.k0+1; % starting symbol index at the delay compared to the training sequence

                   

                    % n => index in rx data sequence
                    %Step From:  Oversampling(=2) * Startdelay + 1
                    %Step Width: Oversampling(=2)
                    %Step To:    Oversampling(=2) * Training Length
                    for n = obj.K*obj.k0+1:obj.K:obj.K*obj.training_length

                        % m => index in reference sequence
                        m = m+1;

                        % 
                        %dc_ = mean(data(obj.Ne(1)+n+(obj.K-1):-1:n+obj.K).');
                        % cut symbols from rx data sequence
                        X_1 = data(obj.Ne(1)+n+(obj.K-1):-1:n+obj.K).';

                        [X_2,X_3] = obj.calc_nl_vecs(X_1,ind_mat_2nd,ind_mat_3rd,norm_fac2,norm_fac3,delta_2,delta_3,cplx);

                        input_vec = [X_1;X_2;X_3];

                        % cut symbols from desired data sequence (correct symbols in training mode)
                        D_1 = ref(obj.Nb(1)-obj.k0+m-2:-1:m-obj.k0-1).';

                        [D_2,D_3] = obj.calc_nl_vecs(D_1,ind_mat_DFE_2nd,ind_mat_DFE_3rd,norm_fac_DFE2,norm_fac_DFE3,delta_DFE2,delta_DFE3,cplx);

                        reference_vec = [D_1;D_2;D_3];

                        e_ffe = e_.'*input_vec;
                        e_dfe = b_.'*reference_vec;

                        error = e_dc + e_ffe - e_dfe - ref_in(m-obj.k0);
                       
                        %error = e_dc + e_.'*input_vec - b_.'*reference_vec  - ref_in(m-obj.k0);     
                        
                        if real(obj.FFEmu)
                            if obj.l1act
                                sgn_e = e_;
                                sgn_e(e_~=0) = e_(e_~=0)./abs(e_(e_~=0));
                                e_ = e_ - obj.rho*sgn_e./(1+epsilon_*abs(e_)) - error*input_vec*obj.FFEmu;
                            else
                                e_ = e_ - error*conj(input_vec)*obj.FFEmu; %classic LMS gradient decay
                            end
                        else
                            if obj.l1act
                                sgn_e = e_;
                                sgn_e(e_~=0) = e_(e_~=0)./abs(e_(e_~=0));
                                e_ = e_ - obj.rho*sgn_e./(1+epsilon_*abs(e_)) - error*input_vec/(input_vec.'*input_vec);
                            else
                                e_ = e_ - error*input_vec/(input_vec.'*input_vec);
                            end
                        end

                        e_dc = e_dc - obj.DCmu*error;

                        obj.error_log.e_ffe(cnt,trainloops) = e_ffe;
                        obj.error_log.e_dfe(cnt,trainloops) = e_dfe;
                        obj.error_log.e_(cnt,trainloops) = error;

                        cnt = cnt+1;
                        if obj.Nb(1) > 0
                            b_ = b_ + obj.DFEmu*error*reference_vec; % Seems like normalized DFE has worse performance
                        end

%                         figure(111);stem((e_),'Markersize',2);ylim([-1 1]);title('FFE Filter Taps');
% 
%                         figure(222);
%                         stem(input_vec);ylim([-3 3]);
%                         hold on;
%                         stem(reference_vec);ylim([-3 3]);
%                         yline(error,'LineWidth',2); title('Input Vector');
%                         hold off

                    end
                end


                %%
                % Plot the intermediate coefficients after training mode
                obj.b = b_(1:obj.Nb(1));
                obj.b2 = b_(obj.Nb(1)+1:obj.Nb(1)+Nb2);
                obj.b3 = b_(obj.Nb(1)+Nb2+1:end);
                obj.e = e_(1:obj.Ne(1));
                obj.e2 = e_(obj.Ne(1)+1:obj.Ne(1)+N2);
                obj.e3 = e_(obj.Ne(1)+N2+1:end);

                if obj.plottrain
                    figure(8052)
                    sgtitle('Training Coeff')
                    subplot(2,3,1); stem((obj.e),'Markersize',2);
                    title('FFE coeff linear')
                    xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                    subplot(2,3,2); stem(obj.e2,'Markersize',2);
                    title('FFE coeff nl 2nd')
                    xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                    subplot(2,3,3); stem(obj.e3,'Markersize',2);
                    title('FFE coeff nl 3rd')
                    xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                    subplot(2,3,4);stem(obj.b,'Markersize',2);
                    title('DFE coeff linear')
                    xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                    subplot(2,3,5);stem(obj.b2,'Markersize',2);
                    title('DFE coeff nl 2nd')
                    xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                    subplot(2,3,6);stem(obj.b3,'Markersize',2);
                    title('DFE coeff nl 3rd')
                    xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                    %set(gcf,'Position',[200 500 700 400])
                end


                if obj.l1act
                    neg_lin = find(abs(obj.e) < obj.thres(1));
                    neg_2nd = find(abs(obj.e2) < obj.thres(2));
                    neg_3rd = find(abs(obj.e3) < obj.thres(3));

                    neg = [neg_lin;neg_2nd+obj.Ne(1);neg_3rd+obj.Ne(1)+N2]; % indices of the neglected coefficients

                    rel_lin = find(abs(obj.e) >= obj.thres(1));
                    rel_2nd = find(abs(obj.e2) >= obj.thres(2));
                    rel_3rd = find(abs(obj.e3) >= obj.thres(3));

                    obj.coeff_number = length(rel_lin)+2*length(rel_2nd)+3*length(rel_3rd);

                    rel = [rel_lin;rel_2nd+obj.Ne(1);rel_3rd+obj.Ne(1)+N2]; % indices of the relevant coefficients
                    e_(neg) = 0;

                    ind_mat_2nd(neg_2nd,:) = [];

                    ind_mat_3rd(neg_3rd,:) = [];
                end

                %% decision directed mode
                if ~obj.DB_aim
                    constellation_in_ = unique(ref_in); % getting the symbol constellation from reference data
                else
                    if obj.M == 2
                        constellation_in_ = [-3 -2 -1 0 1 2 3]/sqrt(5)*2;
                    elseif obj.M == 2.5
                        constellation_in_ = [-5 -4 -3 -2 -1 0 1 2 3 4 5]/sqrt(10)*2;
                    elseif obj.M == 3
                        constellation_in_ = [-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7]/sqrt(21)*2;
                    else
                        constellation_in_ = unique(ref_in);
                    end
                end

                obj.constellation_in = constellation_in_;

                if obj.l1act
                    coeff = [e_(rel);b_];  % combine FFE and DFE coefficient vectors for DD mode
                else
                    coeff = [e_;b_];
                end

                for dd_loop = 1:obj.dd_loops
                    cnt = 1;
                    m = 0;
                    output_vec = zeros(1,floor(length(data_in)/obj.K)); % initilaization of the output vector
                    dd_DFE = zeros(obj.Nb(1),1);
                    D_2 = zeros(Nb2,1);
                    D_3 = zeros(Nb3,1);

                    if all(obj.DDmu == obj.DDmu(1))
                        mu_mat = obj.DDmu(1);
                    else
                        if obj.l1act
                            mu_mat = diag([ones(1,length(rel_lin))*obj.DDmu(1) ones(1,length(rel_2nd))*obj.DDmu(2) ones(1,length(rel_3rd))*obj.DDmu(3) ones(1,obj.Nb)*obj.DDmu(4)]);
                        else
                            mu_mat = diag([ones(1,obj.Ne(1))*obj.DDmu(1) ones(1,N2)*obj.DDmu(2) ones(1,N3)*obj.DDmu(3) ones(1,obj.Nb(1)+Nb2+Nb3)*obj.DDmu(4)]);
                        end
                    end

                    if obj.load_decisions
                        pathn = evalin('base','modeldir');
                        temp = load([pathn, 'MLSE_out', '.mat']) ;
                        %eval(['dd_out_vals = temp.', 'a', ';']) ;
                        dd_out_vals=temp.a;
                        dd_out = zeros(size(data_in));
                        dd_out(1:2:length(data_in)) = dd_out_vals;
                    else
                        dd_out = zeros(size(data_in));
                    end

                    for k = 1:obj.K:length(data_in)
                        m=m+1; % Symbol index

                        X_1 = data(obj.Ne(1)+k-1:-1:k).';

                        [X_2,X_3] = obj.calc_nl_vecs(X_1,ind_mat_2nd,ind_mat_3rd,norm_fac2,norm_fac3,delta_2,delta_3,cplx);

                        if obj.l1act
                            input_vec = [X_1(rel_lin);X_2;X_3;-dd_DFE;-D_2;-D_3];
                        else
                            input_vec = [X_1;X_2;X_3;-dd_DFE;-D_2;-D_3];
                        end

                        output_vec(m) = e_dc + input_vec.'*coeff;

                        if ~obj.load_decisions
                            [~,dd_idx] = min(abs(output_vec(m) - constellation_in_)); % decision for closest constellation point
                            dd_out(k) = constellation_in_(dd_idx);
                        end


                        if obj.Nb(1) > 0
                            dd_DFE(2:end) = dd_DFE(1:end-1);
                            dd_DFE(1) = dd_out(k);

                            if obj.ideal_dfe && m > obj.k0
                                dd_DFE(1) = ref_in(m-obj.k0);
                            end
                            [D_2,D_3] = obj.calc_nl_vecs(dd_DFE,ind_mat_DFE_2nd,ind_mat_DFE_3rd,norm_fac_DFE2,norm_fac_DFE3,delta_DFE2,delta_DFE3,cplx);
                        end
                        %                     if dd_loop ~= 21
                        error = output_vec(m) - dd_out(k);
                        %                     else
                        %                         error = 0;
                        %                     end

                        coeff = coeff - mu_mat*error*conj(input_vec);
                        %                     e_save(:,save_ind) = coeff;
                        %                     save_ind = save_ind+1;

                        if 1%mu_mat ~= 0
                             e_dc = e_dc - obj.DCmu*error;
                             error_log(cnt,dd_loop) = e_dc;
                             cnt = cnt+1;
                        end

                    end
                end

                %figure(2023);plot(error_log(:,1))
                % shifting the output sequence by k0 symbols
                yout = (circshift(output_vec.',-(obj.k0))).'; %(circshift(dd_out.',-(obj.k0))).';

                e_ = coeff(1:end-obj.Nb(1)-Nb2-Nb3);
                b_ = coeff(end-obj.Nb(1)-Nb2-Nb3+1:end);

                if obj.l1act
                    obj.e = e_(1:length(rel_lin));
                    obj.e2 = e_(length(rel_lin)+1:length(rel_lin)+length(rel_2nd));
                    obj.e3 = e_(length(rel_lin)+length(rel_2nd)+1:end);
                else
                    obj.e = e_(1:obj.Ne(1));
                    obj.e2 = e_(obj.Ne(1)+1:obj.Ne(1)+N2);
                    obj.e3 = e_(obj.Ne(1)+N2+1:end);
                end
                obj.b = b_(1:obj.Nb(1));
                obj.b2 = b_(obj.Nb(1)+1:obj.Nb(1)+Nb2);
                obj.b3 = b_(obj.Nb(1)+Nb2+1:end);

                % plot the final coefficients after DD mode
                if obj.plotfinal
                    figure(8054)
                    if obj.l1act
                        sgtitle('Final Coeff')
                        subplot(2,3,1); stem(rel_lin,obj.e,'Markersize',2);
                        title('FFE coeff linear')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,2); stem(rel_2nd,obj.e2,'Markersize',2);
                        title('FFE coeff nl 2nd')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,3); stem(rel_3rd,obj.e3,'Markersize',2);
                        title('FFE coeff nl 3rd')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,4);stem(obj.b,'Markersize',2);
                        title('DFE coeff linear')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,5);stem(obj.b2,'Markersize',2);
                        title('DFE coeff nl 2nd')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,6);stem(obj.b3,'Markersize',2);
                        title('DFE coeff nl 3rd')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                    else
                        sgtitle('Final Coeff')
                        subplot(2,3,1); stem(obj.e/max(e_),'Markersize',2);
                        title('FFE coeff linear')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,2); stem(obj.e2,'Markersize',2);
                        title('FFE coeff nl 2nd')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,3); stem(obj.e3,'Markersize',2);
                        title('FFE coeff nl 3rd')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,4);stem(obj.b,'Markersize',2);
                        title('DFE coeff linear')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,5);stem(obj.b2,'Markersize',2);
                        title('DFE coeff nl 2nd')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                        subplot(2,3,6);stem(obj.b3,'Markersize',2);
                        title('DFE coeff nl 3rd')
                        xlabel('coefficient index'); ylabel('value'); set(gca,'Fontsize',12)
                    end
                    set(gcf,'Position',[1000 500 700 400])
                end

                % save frequency response to the work space
                if obj.save_taps
                    % save the FFE coefficients to the work space
                    % pathn = evalin('base','modeldir');
                    % eval([obj.field_ffe, ' = obj.e ;']) ;
                    % eval([obj.field_dfe, ' = b ;']) ;
                    % eval(['save(''', pathn, '\',obj.filen,''', ''', obj.field_ffe,''', ''',obj.field_dfe,''') ;']) ;

                    save("coefficients",obj.e, obj.b);
                end

            else
                yout = data_in;
            end

        end


        function [X_2,X_3] = calc_nl_vecs(obj,X_1,ind_mat_2,ind_mat_3,norm_fac2,norm_fac3,delta_2,delta_3,cplx)
            % calculation of the vectors containing all combinations of input symbols
            % of second and third order based on the linear symbols

            if ind_mat_2(1) > 0
                input_vec_se = X_1(delta_2:end)/norm_fac2;%(K*(k0-1):end)
                X_2 = input_vec_se(ind_mat_2(:,1)).*input_vec_se(ind_mat_2(:,2));
            else
                X_2 = [];
            end

            if ind_mat_3(1) > 0
                if cplx
                    input_vec_th = X_1(delta_3:end)/norm_fac3;
                    X_3 = input_vec_th(ind_mat_3(:,1)).*input_vec_th(ind_mat_3(:,2)).*conj(input_vec_th(ind_mat_3(:,3)));
                else
                    input_vec_th = X_1(delta_3:end)/norm_fac3;
                    X_3 = input_vec_th(ind_mat_3(:,1)).*input_vec_th(ind_mat_3(:,2)).*input_vec_th(ind_mat_3(:,3));
                end
            else
                X_3 = [];
            end
        end

        function [ind_mat_2nd,ind_mat_3rd] = calc_ind(obj,Ne2,N2,Ne3,N3,mode2nd,mode3rd,len_2nd,len_3rd,cplx)

            if Ne2 > 0
                ind_mat_2nd = NaN(N2,2);
                count=1;
                if mode2nd == 1
                    for t = 1:Ne2
                        for u = t:Ne2
                            ind_mat_2nd(count,:) = [t u];
                            count = count + 1 ;
                        end
                    end
                elseif mode2nd == 2
                    for t = 1:Ne2
                        ind_mat_2nd(t,:) = [t t];
                    end
                elseif mode2nd == 3
                    for t = 1:Ne2
                        for u = t:Ne2
                            if u-t<=len_2nd
                                ind_mat_2nd(count,:) = [t u];
                                count = count + 1 ;
                            end
                        end
                    end
                elseif mode2nd == 4
                    for t = 1:Ne2
                        for u = t:Ne2
                            if u-t<=ceil(Ne2/2)
                                ind_mat_2nd(count,:) = [t u];
                                count = count + 1 ;
                            end
                        end
                    end
                end

            else
                ind_mat_2nd = 0;
            end

            if Ne3 > 0
                ind_mat_3rd = NaN(N3,3);
                count=1;
                if mode3rd == 1
                    if cplx
                        for t = 1:Ne3
                            for u = t:Ne3
                                for v = 1:Ne3
                                    ind_mat_3rd(count,:) = [t u v];
                                    count = count + 1 ;
                                end
                            end
                        end
                    else
                        for t = 1:Ne3
                            for u = t:Ne3
                                for v = u:Ne3
                                    ind_mat_3rd(count,:) = [t u v];
                                    count = count + 1 ;
                                end
                            end
                        end
                    end
                elseif mode3rd == 2
                    for t = 1:Ne3
                        ind_mat_3rd(t,:) = [t t t];
                    end
                elseif mode3rd == 3
                    for t = 1:Ne3
                        for u = t:Ne3
                            ind_mat_3rd(count,:) = [t t u];
                            if t ~= u
                                count = count + 1;
                                ind_mat_3rd(count,:) = [t u u];
                            end
                            count = count + 1;
                        end
                    end
                elseif mode3rd == 4
                    for t = 1:Ne3
                        for u = t:Ne3
                            for v = u:Ne3
                                if u-t<=len_3rd && v-t<=len_3rd
                                    ind_mat_3rd(count,:) = [t u v];
                                    count = count + 1 ;
                                end
                            end
                        end
                    end
                elseif mode3rd == 5
                    for t = 1:Ne3
                        for u = t:Ne3
                            if u-t<=len_3rd
                                ind_mat_3rd(count,:) = [t t u];
                                if t ~= u
                                    count = count + 1;
                                    ind_mat_3rd(count,:) = [t u u];
                                end
                                count = count + 1;
                            end
                        end
                    end

                    ind_mat_3rd2 = NaN(N3,3);
                    count = 1;
                    %         for t = 1:Ne3
                    %             ind_mat_3rd2(count,:) = [t t t];
                    %             count = count + 1;
                    %         end
                    for t = 1:Ne3
                        %             ind_mat_3rd2(count,:) = [t t t];
                        %             count = count + 1;
                        for u = t:min(Ne3,t+len_3rd)
                            for v = unique([t u])
                                ind_mat_3rd2(count,:) = [t v u];
                                count = count + 1;
                                %                     ind_mat_3rd2(count,:) = [t u u];
                                %                     count = count + 1;
                            end
                        end
                    end
                end
            else
                ind_mat_3rd = 0;
            end

        end

        function [ind_mat_2nd,ind_mat_3rd] = calc_DFE_ind(obj,Ne2,N2,Ne3,N3)

            if Ne2 > 0
                ind_mat_2nd = NaN(N2,2);
                count=1;
                for t = 1:Ne2
                    for u = t:Ne2
                        ind_mat_2nd(count,:) = [t u];
                        count = count + 1 ;
                    end
                end
            else
                ind_mat_2nd = 0;
            end

            if Ne3 > 0
                ind_mat_3rd = NaN(N3,3);
                count=1;
                for t = 1:Ne3
                    for u = t:Ne3
                        for v = u:Ne3
                            ind_mat_3rd(count,:) = [t u v];
                            count = count + 1 ;
                        end
                    end
                end
            else
                ind_mat_3rd = 0;
            end

        end

    end
end

