%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Copyright 2014 National Renewable Energy Laboratory and National
% Technology & Engineering Solutions of Sandia, LLC (NTESS).
% Under the terms of Contract DE-NA0003525 with NTESS,
% the U.S. Government retains certain rights in this software.
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

classdef waveClass<handle
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % The  ``waveClass`` creates a ``waves`` object saved to the MATLAB
    % workspace. The ``waveClass`` includes properties and methods used
    % to define WEC-Sim's wave input.
    %
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    properties (SetAccess = 'public', GetAccess = 'public')%input file
        bem             = struct(...                % (`structure`) Defines the BEM data implementation.
            'option',       'EqualEnergy',...       %
            'count',        [], ...                 %
            'frequency',    [], ...                 %
            'range',        [])                     % (`structure`) Defines the BEM data implementation. ``option`` (`string`) Method of frequency discretization for irregular waves, options include: ``'EqualEnergy'`` or ``'Traditional'``. Default = ``'EqualEnergy'``. ``count`` (`integer`) Number of interpolated wave frequencies, only used for ``irregular`` and ``spectrumImport``. Number of frequencies used varies depending on ``bem.option``, 1000 for ``'Traditional'``, and 500 for ``'EqualEnergy'`` and ``Imported``. Default = ``[]``. ``frequency`` (`vector`) Wave frequencies [rad/s] from BEM. Default = ``[]``. ``range`` (`2x1 vector`) Min and max wave frequency [rad/s], only used for ``irregular`` and ``spectrumImport``. If not specified, the BEM data frequency range is used. Default = ``[]``
        current         = struct(...                % (`structure`) Defines the current implementation.
            'option',       3,...                   %
            'depth',        0, ...                  %
            'direction',    0, ...                  %
            'speed',        0)                      % (`structure`) Defines the current implementation. ``option`` (`integer`) Define the sub-surface current model to be used in WEC-Sim, options include: ``0`` for depth-independent model, ``1`` for 1/7 power law variation with depth, ``2`` for linear variation with depth, or ``3`` for no current. Default = ``3``, ``depth`` (`float`) Current depth [m]. Define the depth over which the sub-surface current is modeled. Must be defined for options ``1`` and ``2``. The current is not calculated for any depths greater than the specified current depth. Default = ``0``, ``direction`` (`float`) Current direction [deg]. Surface current direction defined using WEC-Sim global coordinate system. Default = ``0``, ``speed``  (`float`) Current seed [m/s]. Surface current speed that is uniform along the water column. Default = ``0``
        direction       = 0;                        % (`float`) Incident wave direction(s) [deg]. Incident wave direction defined using WEC-Sim global coordinate system. Should be defined as a column vector for more than one wave direction. Default = ``0``
        elevationFile   = 'NOT DEFINED';            % (`string`) Data file that contains the times-series data file. Default = ``'NOT DEFINED'``
        gamma           = [];                       % (`float`) Defines gamma, only used for ``JS`` wave spectrum type. Default = ``[]``
        height          = 'NOT DEFINED';            % (`float`) Wave height [m]. Defined as wave height for ``regular``, or significant wave height for ``irregular``. Default =  ``'NOT DEFINED'``
        marker          = struct(...                % (`structure`) Defines the wave marker.
            'location',     [],...                  %
            'size',         10, ...                 %
            'style',        1)                      % (`structure`) Defines the wave marker. `loc` (`nx2 vector`) Marker [X,Y] locations [m]. Default = ``[]``. ``size`` (`float`) Marker size in Pixels. Default = ``10``. ``style`` Marker style, options include: ``1``: Sphere, ``2``: Cube, ``3``: Frame. Default = ``1``: Sphere
        period          = 'NOT DEFINED';            % (`float`) Wave period [s] . Defined as wave period for ``regular``, peak period for ``irregular``, or period of BEM data used for hydrodynamic coefficients for ``noWave``. Default = ``'NOT DEFINED'``
        phaseSeed       = 0;                        % (`integer`) Defines the random phase seed, only used for ``irregular`` and ``spectrumImport`` waves. Default = ``0``
        spectrumFile    = 'NOT DEFINED';            % (`string`) Data file that contains the spectrum data file.  Default = ``'NOT DEFINED'``
        spectrumType    = 'NOT DEFINED';            % (`string`) Specifies the wave spectrum type, options inlcude:``PM`` or ``JS``. Default = ``'NOT DEFINED'``
        viz             = struct(...                %
            'numPointsX',   50, ...                 %
            'numPointsY',   50 );                   % (`structure`) Defines visualization options, structure contains the fields ``numPointsX`` for the number of visualization points in x direction, and ``numPointsY`` for the number of visualization points in y direction.
        waterDepth      = [];                       % (`float`) Water depth [m]. Default to BEM water depth if not set.
        spread          = 1;                        % (`float`) Wave Spread probability associated with wave direction(s). Should be defined as a column vector for more than one wave direction. Default = ``1``
    end

    properties (SetAccess = 'private', GetAccess = 'public')%internal
        % The following properties are private, for internal use by WEC-Sim
        amplitude       = [];                       % Wave amplitude [m]. For regular waves or 2*(wave spectrum vector) for irregular waves
        deepWater       = [];                       % Deep water or not, depending on input from WAMIT, NEMOH and AQWA
        dOmega          = 0;                        % Frequency spacing [rad] for ``irregular`` waves.
        omega           = [];                       % Wave frequency (regular waves) or wave frequency vector (irregular waves), where omega = 2*pi/period [rad/s]
        phase           = 0;                        % (`float`) Wave phase [rad] . Only used for ``irregular`` waves.
        power           = [];                       % Wave Power Per Unit Wave Crest [W/m]
        spectrum        = [];                       % Wave Spectrum [m^2-s/rad] for ``Traditional``
        type            = 'NOT DEFINED';            % (`string`) Specifies the wave type, options include:``noWave``, ``noWaveCIC``, ``regular``, ``regularCIC``, ``irregular``, ``spectrumImport``, or ``elevationImport``. Default = ``'NOT DEFINED'``
        typeNum         = [];                       % Number to represent different type of waves
        waveAmpTime     = [];                       % Wave elevation time history [m] at the (0, 0, 0) origin
        waveAmpTimeViz  = [];                       % Wave elevation time history at marker locations specified by user [m]
        wavenumber      = [];                       % Wave Number, where wavenumber = 2*pi/wavelength [rad/m]
    end

    methods (Access = 'public')
        function obj = waveClass(type)
            % This method initializes the ``waveClass`` and creates a ``waves`` object.
            %
            % Parameters
            % ------------
            %     type : string
            %         String specifying the wave type, options include:
            %
            %           noWave
            %               No waves
            %
            %           noWaveCIC
            %               No waves but with the Convolution Integral Calculation to calculate radiation effects
            %
            %           regular
            %               Regular waves
            %
            %           regularCIC
            %               Regular waves with Convolution Integral Calculation to calculate radiation effects
            %
            %           irregular
            %           	Irregular Waves
            %
            %           spectrumImport
            %           	Irregular Waves with predefined phase
            %
            %           elevationImport
            %               Irregular Waves with predefined elevation
            %
            % Returns
            % ------------
            %     waves : obj
            %         waveClass object
            %

            obj.type = type;
            switch obj.type
                case {'noWave'}         % No Waves with Constant Hydrodynamic Coefficients
                    obj.typeNum = 0;
                case {'noWaveCIC'}      % No Waves w/Convolution Integral Calculation
                    obj.typeNum = 1;
                case {'regular'}        % Regular Waves with Constant Hydrodynamic Coefficients
                    obj.typeNum = 10;
                case {'regularCIC'}     % Regular Waves w/Convolution Integral Calculation
                    obj.typeNum = 11;
                case 'irregular'        % Irregular Waves with 'PM' or 'JS' wave spectrum
                    obj.typeNum = 20;
                case 'spectrumImport'   % Irregular waves with imported wave spectrum
                    obj.typeNum = 21;
                case 'elevationImport'        % Waves with imported wave elevation time-history
                    obj.typeNum = 30;
            end
        end

        function checkInputs(obj)
            % This method checks WEC-Sim user inputs and generates error messages if parameters are not properly defined.

            % check wave type
            types = {'noWave', 'noWaveCIC', 'regular', 'regularCIC', 'irregular', 'spectrumImport', 'elevationImport'};
            if sum(strcmp(types,obj.type)) ~= 1
                error(['Unexpected wave environment type setting, choose from: ' ...
                    '"noWave", "noWaveCIC", "regular", "regularCIC", "irregular", "spectrumImport", and "elevationImport".'])
            end
            % check 'waves.bem' fields
            if length(fieldnames(obj.bem)) ~=4
                error(['Unrecognized method, property, or field for class "waveClass", ' ...
                    '"waveClass.bem" structure must only include fields: "option", "count", "frequency", "range"']);
            end
            % check 'waves.current' fields
            if length(fieldnames(obj.current)) ~=4
                error(['Unrecognized method, property, or field for class "waveClass", ' ...
                    '"waveClass.current" structure must only include fields: "option", "depth", "direction", "speed"']);
            end
            % 'elevationFile' defined for 'elevationImport' case
            if strcmp(obj.type,'elevationImport')
                if strcmp(obj.elevationFile,'NOT DEFINED')
                    error('The "waves.elevationFile" must be defined when using the "elevationImport" wave type');
                end
                if ~strcmp(obj.period,'NOT DEFINED') || ~strcmp(obj.height,'NOT DEFINED')
                    warning('"waves.period" and "waves.height" are not used for "etaImport" wave types')
                end
            end
            % 'spectrumFile' defined for 'spectrumImport' case
            if strcmp(obj.type,'spectrumImport')
                if strcmp(obj.spectrumFile,'NOT DEFINED')
                    error('The "wave.spectrumFile" must be defined when using the "spectrumImport" wave type');
                end
                if ~strcmp(obj.period,'NOT DEFINED') || ~strcmp(obj.height,'NOT DEFINED')
                    warning('"waves.period" and "waves.height" are not used for "spectrumImport" wave types')
                end
            end

            % Check 'marker.location'
            if ~isempty(obj.marker.location) && ~ndims(obj.marker.location)==2
                error('The coordinates of the visualization markers should have an ordinate (y-coordinate) and an abscissa (x-coordinate)')
            end
            % Check wave spread
            if sum(obj.spread)~=1
                error('The wave spread should always sum to 1 to preserve spectrum/energy accuracy.')
            end

            % Check inputs based on type
            if strcmp(obj.type,'noWave') && strcmp(obj.period,'NOT DEFINED')
                error('"waves.period" must be defined for the hydrodynamic data period when using the "noWave" wave type');
            end
            if strcmp(obj.type,'noWaveCIC') && ~strcmp(obj.period,'NOT DEFINED')
                warning('"waves.period" is not used for the hydrodynamic data period when using the "noWaveCIC" wave type')
            end
            if strcmp(obj.type,'regular') || strcmp(obj.type,'regularCIC')
                if strcmp(obj.period,'NOT DEFINED') || strcmp(obj.height,'NOT DEFINED')
                    error('"waves.period" and "waves.height" need to be defined for "regular" and "regularCIC" wave types')
                end
                if ~strcmp(obj.spectrumType,'NOT DEFINED')
                    warning('"waves.spectrumType" is not used for the "regular" and "regularCIC" wave types')
                end
            end
            if strcmp(obj.type,'irregular')
                if strcmp(obj.spectrumType,'NOT DEFINED')
                    error('"waves.spectrumType" needs to be defined for "irregular" wave types')
                end
                if strcmp(obj.period,'NOT DEFINED') || strcmp(obj.height,'NOT DEFINED')
                    error('"waves.period" and "waves.height" need to be defined for "irregular" wave types')
                end
            end
        end

        function setup(obj,bemFreq,bemWaterDepth,rampTime,dt,maxIt,time,g,rho)
            % This method calculates WEC-Sim's wave properties
            % based on the specified wave type.

            if ~isempty(obj.marker.location)==1
                if ~width(obj.marker.location)==2
                    error('The coordinates of the visualization markers should have an ordinate (y-coordinate) and an abscissa (x-coordinate)')
                end
            end
            obj.bem.frequency = bemFreq;
            if isempty(obj.bem.range) && isempty(obj.bem.frequency)
                % No .h5 file and no frequency range defined --> error
                error('Must define frequency range in waves.bem.range when zero hydro bodies are used (no .h5 file).');
            elseif isempty(obj.bem.range) && ~isempty(obj.bem.frequency)
                % Use .h5 file range if not defined in input file
                obj.bem.range = obj.bem.frequency;
            elseif ~isempty(obj.bem.range) && ~isempty(obj.bem.frequency)
                % check that input file frequency range is not larger than
                % available BEM data
                if obj.bem.range(1) < min(obj.bem.frequency) || obj.bem.range(1) > max(obj.bem.frequency)
                    warning('Min frequency range outside BEM data, min frequency set to min BEM frequency')
                    obj.bem.range(1) = min(obj.bem.frequency);
                end
                if obj.bem.range(2) < min(obj.bem.frequency) || obj.bem.range(2) > max(obj.bem.frequency)
                    warning('Max frequency range outside BEM data, max frequency set to max BEM frequency')
                    obj.bem.range(2) = max(obj.bem.frequency);
                end
            end

            obj.setWaterDepth(bemWaterDepth);

            switch obj.type
                case {'noWave','noWaveCIC'}
                    if isempty(obj.omega) && strcmp(obj.period,'NOT DEFINED')
                        obj.omega = min(obj.bem.range);
                        obj.period = 2*pi/obj.omega;
                    elseif isempty(obj.omega)
                        obj.omega = 2*pi/obj.period;
                    else
                        obj.period = 2*pi/obj.omega;
                    end
                    obj.height = 0;
                    obj.amplitude = obj.height/2;
                    obj.wavenumber = calcWaveNumber(obj.omega,obj.waterDepth,g,obj.deepWater);
                    obj.waveElevNowave(time);
                case {'regular','regularCIC'}
                    if isempty(obj.omega) && strcmp(obj.period,'NOT DEFINED')
                        obj.omega = min(obj.bem.range);
                        obj.period = 2*pi/obj.omega;
                    elseif isempty(obj.omega)
                        obj.omega = 2*pi/obj.period;
                    else
                        obj.period = 2*pi/obj.omega;
                    end
                    obj.amplitude = obj.height/2;
                    obj.wavenumber = calcWaveNumber(obj.omega,obj.waterDepth,g,obj.deepWater);
                    obj.waveElevReg(rampTime, time);
                    obj.wavePowerReg(g,rho);
                case {'irregular','spectrumImport'}
                    if strcmp(obj.type,'spectrumImport')
                        obj.height = 0;
                        obj.period = 0;
                        obj.bem.option = 'Imported';
                        obj.spectrumType = 'spectrumImport';
                    end
                    minFrequency=min(obj.bem.range);
                    maxFrequency=max(obj.bem.range);
                    switch obj.bem.option
                        case {'Traditional'}
                            if isempty(obj.bem.count)
                                obj.bem.count = 1000;
                            end
                            obj.omega = (minFrequency:(maxFrequency-minFrequency)/(obj.bem.count-1):maxFrequency)';
                            obj.dOmega = ones(obj.bem.count,1).*(maxFrequency-minFrequency)./(obj.bem.count-1);
                        case {'EqualEnergy'}
                            bemCount_interp = 500000;
                            obj.omega = (minFrequency:(maxFrequency-minFrequency)/bemCount_interp:maxFrequency)';
                            obj.dOmega = mean(diff(obj.omega));
                            if isempty(obj.bem.count)
                                obj.bem.count = 500;
                            end
                        case {'Imported'}
                            data = importdata(obj.spectrumFile);
                            freqData = data(:,1);
                            freqLoc = freqData >= min(obj.bem.range)/2/pi & freqData <= max(obj.bem.range)/2/pi;
                            obj.omega    = freqData(freqLoc).*2.*pi;
                            obj.bem.count = length(obj.omega);
                            obj.dOmega(1,1)= obj.omega(2)-obj.omega(1);
                            obj.dOmega(2:obj.bem.count-1,1)=(obj.omega(3:end)-obj.omega(1:end-2))/2;
                            obj.dOmega(obj.bem.count,1)= obj.omega(end)-obj.omega(end-1);
                    end
                    obj.setWavePhase;
                    obj.irregWaveSpectrum(g,rho)
                    obj.wavenumber = calcWaveNumber(obj.omega,obj.waterDepth,g,obj.deepWater);
                    obj.waveElevIrreg(rampTime, time, obj.dOmega);
                case {'elevationImport'}    %  This does not account for wave direction
                    % Import 'elevationImport' time-series here and interpolate
                    data = importdata(obj.elevationFile) ;    % Import time-series
                    obj.waveElevUser(rampTime, maxIt, data, time);
            end
        end

        function listInfo(obj)
            % This method prints wave information to the MATLAB Command Window.

            fprintf('\nWave Environment: \n')
            switch obj.type
                case 'noWave'
                    fprintf('\tWave Type                            = No Wave (Constant Hydrodynamic Coefficients)\n')
                    fprintf('\tHydro Data Wave Period, period (sec)    	= %G\n',obj.period)
                case 'regular'
                    fprintf('\tWave Type                            = Regular Waves (Constant Hydrodynamic Coefficients)\n')
                    fprintf('\tWave Height, height (m)                   = %G\n',obj.height)
                    fprintf('\tWave Period, period (sec)                 = %G\n',obj.period)
                case 'noWaveCIC'
                    fprintf('\tWave Type                            = No Wave (Convolution Integral Calculation)\n')
                case 'regularCIC'
                    fprintf('\tWave Type                            = Regular Waves (Convolution Integral Calculation)\n')
                    fprintf('\tWave Height, height (m)                   = %G\n',obj.height)
                    fprintf('\tWave Period, period (sec)                 = %G\n',obj.period)
                case 'irregular'
                    if obj.phaseSeed == 0
                        fprintf('\tWave Type                            = Irregular Waves (Arbitrary Random Phase)\n')
                    else
                        fprintf('\tWave Type                            = Irregular Waves (Predefined Random Phase)\n')
                    end
                    obj.printWaveSpectrumType;
                    fprintf('\tSignificant Wave Height, Hs      (m) = %G\n',obj.height)
                    fprintf('\tPeak Wave Period, Tp           (sec) = %G\n',obj.period)
                case 'spectrumImport'
                    if size(importdata(obj.spectrumFile),2) == 3
                        fprintf('\tWave Type                            = Irregular waves with imported wave spectrum (Imported Phase)\n')
                    elseif obj.phaseSeed == 0
                        fprintf('\tWave Type                            = Irregular waves with imported wave spectrum (Random Phase)\n')
                    else
                        fprintf('\tWave Type                            = Irregular waves with imported wave spectrum (Seeded Phase)\n')
                    end
                    obj.printWaveSpectrumType;
                case 'elevationImport'
                    fprintf( '\tWave Type                           = Waves with imported wave elevation time-history\n')
                    fprintf(['\tWave Elevation Time-Series File    	= ' obj.elevationFile '  \n'])
            end
        end

        function calculateElevation(obj,rampTime,timeseries)
            % Calculates the wave elevation based on the wave type.
            % Used by postProcess.m
            %
            % Parameters
            % ------------
            %     waves : obj
            %         waveClass object
            %
            %     rampTime : float
            %         ramp time from the simulation class
            %
            %     timeseries : float array
            %         Array of all simulation output time steps
            %
            switch obj.type
                case {'noWave','noWaveCIC'}
                    obj.waveElevNowave(timeseries);

                case {'regular','regularCIC'}
                    obj.waveElevReg(rampTime, timeseries);

                case {'irregular','spectrumImport'}
                    obj.waveElevIrreg(rampTime, timeseries, obj.dOmega);

                case {'elevationImport'}
                    % Wave elevation is a necessary pre-processing step for
                    % the eta import case.
                    % Used by waveClass.setup()
            end
        end

        function Z = waveElevationGrid(obj, t, X, Y, it, g)
            % This method calculates wave elevation on a grid at a given
            % time, used by: :func:`write_paraview_wave`.
            %
            % Parameters
            % ------------
            %     waves: obj
            %         waveClass object
            %
            %     t : float
            %         the current time
            %
            %     X : matrix
            %         (m x n) matrix of X coordinates at which to calculate the wave elevation
            %
            %     Y : matrix
            %         (m x n) matrix of Y coordinates at which to calculate the wave elevation
            %
            %     it : time step iteration
            %
            %     g : gravitational acceleration constant from simulationClass
            %
            %
            % Returns
            % ---------
            %     Z : matrix
            %         (m x n) matrix of Z coordinates of the wave elevation
            %
            switch obj.type
                case {'noWave','noWaveCIC'}
                    Z = zeros (size (X));
                case {'regular', 'regularCIC'}
                    Xt = X*cos (obj.direction*pi/180) + Y * sin(obj.direction*pi/180);
                    Z = obj.amplitude * cos(-1 * obj.wavenumber * Xt  +  obj.omega * t);
                case {'irregular', 'spectrumImport'}
                    Z = zeros (size (X));
                    for idir=1:length(obj.direction)
                        Xt = X*cos(obj.direction(idir)*pi/180) + Y*sin(obj.direction(idir)*pi/180);
                        for iw = 1:length(obj.omega)
                            Z = Z + sqrt(obj.amplitude(iw)*obj.spread(idir).*obj.dOmega(iw)) * cos(-1*obj.wavenumber(iw)*Xt + obj.omega(iw)*t + obj.phase(iw,idir));
                        end
                    end
                case{'elevationImport'}
                    if it ==1
                        warning('Paraview wave surface discretization for qualitative purposes only.')
                    end
                    Z = zeros(size(X));
                    WaveEle = obj.waveAmpTime(:,2);
                    TimeWaveEle = obj.waveAmpTime(:,1);
                    L = length(TimeWaveEle);
                    Fs = 1/(TimeWaveEle(2)-TimeWaveEle(1));
                    YY = fft(WaveEle);
                    P2 = abs(YY/L);
                    P1 = P2(1:round(L/2)+1);
                    P1(2:end-1) = 2*P1(2:end-1);
                    ff = Fs*(0:round(L/2))/L;
                    PhasesEtaImp2 = angle(YY/L);
                    PhasesEtaImp1 = PhasesEtaImp2(1:round(L/2)+1);
                    kWaveEle = (2*pi.*ff).^2./g; % deep water wave approximation
                    Xt = X*cos(obj.direction*pi/180) + Y*sin(obj.direction*pi/180);
                    Zint=zeros(size(X));
                    for iw = 2:length(ff)
                        Zint = Zint + P1(iw) * cos(-1*kWaveEle(iw)*Xt + 2*pi*ff(iw).*t+ PhasesEtaImp1(iw));
                    end
                    Z = Zint;
            end
        end

        function plotElevation(obj,rampTime)
            % This method plots wave elevation time-history.
            %
            % Parameters
            % ------------
            %     rampTime : float, optional
            %         Specify wave ramp time to include in plot
            %
            % Returns
            % ------------
            %     figure : fig
            %         Plot of wave elevation versus time
            %

            arguments
                obj
                rampTime double {mustBeReal, mustBeNonNan, mustBeFinite} = 0
            end

            figure
            plot(obj.waveAmpTime(:,1),obj.waveAmpTime(:,2))
            title('Wave Surfave Elevation')
            if nargin==2
                hold on
                line([rampTime,rampTime],[1.5*min(obj.waveAmpTime(:,2)),1.5*max(obj.waveAmpTime(:,2))],'Color','k')
                title(['Wave Surface Elevation, Ramp Time ' num2str(rampTime) ' (s)'])
            end
            xlabel('Time (s)')
            ylabel('Elevation (m)')
        end

        function plotSpectrum(obj)
            % This method plots the wave spectrum.
            %
            % Returns
            % ------------
            %     figure : fig
            %         Plot of wave spectrum versus wave frequency
            %
            m0 = trapz(obj.omega,obj.spectrum);
            HsTest = 4*sqrt(m0);
            [~,I] = max(abs(obj.spectrum));
            wp = obj.omega(I);
            TpTest = 2*pi/wp;

            figure
            plot(obj.omega,obj.spectrum,'s-')
            hold on
            line([wp,wp],[0,max(obj.spectrum)],'Color','k')
            xlim([0 max(obj.omega)])
            title([obj.spectrumType, ' Spectrum, T_p= ' num2str(TpTest) ' [s], '  'H_m_0= ' num2str(HsTest), ' [m]'])
            if strcmp(obj.spectrumType,'JS') == 1
                title([obj.spectrumType, ' Spectrum, T_p= ' num2str(TpTest) ' [s], H_m_0= ' num2str(HsTest), ' [m], gamma = ' num2str(obj.gamma)])
            end
            xlabel('Frequency (rad/s)')
            ylabel('Spectrum (m^2-s/rad)');
        end

    end

    methods (Access = 'protected')
        function setWavePhase(obj)
            % Sets the irregular wave's random phase
            % used by: :meth:`waveClass.setup`.
            if obj.phaseSeed ~= 0
                rng(obj.phaseSeed);     % Phase seed = 1,2,3,...,etc
            else
                rng('shuffle');         % Phase seed shuffled
            end
            switch obj.bem.option
                case {'EqualEnergy','Traditional'}
                    obj.phase = 2*pi*rand(length(obj.direction),obj.bem.count);
                case {'Imported'}
                    data = importdata(obj.spectrumFile);
                    if size(data,2) == 3
                        freqData = data(:,1);
                        freqLoc = freqData>=min(obj.bem.range)/2/pi & freqData<=max(obj.bem.range)/2/pi;
                        phaseData = data(freqLoc,3);
                        obj.phase = phaseData';
                    else
                        obj.phase = 2*pi*rand(1,obj.bem.count);
                    end
            end
            obj.phase = obj.phase';
        end

        function setWaterDepth(obj,bemWaterDepth)
            % Set the water depth. If defined in input file, BEM depth is
            % not used. used by: :meth:`waveClass.setup`.
            if isempty(obj.waterDepth)
                if isempty(bemWaterDepth)
                    error('Must define water depth in waves.waterDepth when zero hydro bodies (no .h5 file).');
                elseif strcmp(bemWaterDepth,'infinite')
                    obj.deepWater = 1;
                    obj.waterDepth = 200;
                    fprintf('\tInfinite water depth specified in BEM and "waves.waterDepth" not specified in input file.\n')
                    fprintf('Set water depth to 200m for visualization.\n')
                else
                    obj.deepWater = 0;
                    obj.waterDepth = double(bemWaterDepth);
                end
            else
                if ~isempty(bemWaterDepth)
                    warning('Because water depth is specified in the wecSimInputFile, the water depth from the BEM data is ignored')
                end
            end
        end

        function waveElevNowave(obj,timeseries)
            % Set noWave elevation time-history
            % used by: :meth:`waveClass.setup`.
            % Used by postProcess for variable step solvers
            obj.waveAmpTime         = zeros(length(timeseries),2);
            obj.waveAmpTime(:,1)    = timeseries;

            if ~isempty(obj.marker.location)
                SZwaveAmpTimeViz = size(obj.marker.location);
                obj.waveAmpTimeViz = zeros(length(timeseries),SZwaveAmpTimeViz(1)+1);
                obj.waveAmpTimeViz(:,1) = timeseries;
            end
        end

        function waveElevReg(obj,rampTime,timeseries)
            % Calculate regular wave elevation time history
            % used by: :meth:`waveClass.setup`.
            % Used by postProcess for variable step solvers
            maxIt = length(timeseries);
            rampFunction = (1+cos(pi+pi*timeseries/rampTime))/2;
            rampFunction(timeseries>=rampTime) = 1;

            obj.waveAmpTime = zeros(maxIt,2);
            obj.waveAmpTime(:,1) = timeseries;
            obj.waveAmpTime(:,2) = rampFunction.*(obj.amplitude*cos(obj.omega*timeseries));

            % Wave Marker
            if ~isempty(obj.marker.location)==1
                if width(obj.marker.location)~=2
                    error('The coordinates of the visualization markers should have an ordinate (y-coordinate) and an abscissa (x-coordinate)')
                end
            end
            if ~isempty(obj.marker.location)
                SZwaveAmpTimeViz = size(obj.marker.location);
                obj.waveAmpTimeViz = zeros(maxIt,SZwaveAmpTimeViz(1)+1);
                for j = 1:SZwaveAmpTimeViz(1)
                    obj.waveAmpTimeViz(:,1) = timeseries;
                    obj.waveAmpTimeViz(:,j+1) = rampFunction.*obj.amplitude.*cos(obj.omega*timeseries - obj.wavenumber*(obj.marker.location(j,1).*cos(obj.direction*pi/180) + obj.marker.location(j,2).*sin(obj.direction*pi/180)));
                end
            end
        end

        function wavePowerReg(obj,g,rho)
            % Calculate wave power per unit wave crest for regular waves
            if obj.deepWater == 1
                % Deepwater Approximation
                obj.power = 1/(8*pi)*rho*g^(2)*(obj.amplitude).^(2).*obj.period;
            else
                % Full Wave Power Equation
                obj.power = rho*g*(obj.amplitude).^(2)/4*sqrt(g./obj.wavenumber.*tanh(obj.wavenumber.*obj.waterDepth))*(1+2*obj.wavenumber.*obj.waterDepth./sinh(2*obj.wavenumber.*obj.waterDepth));
            end
        end

        function irregWaveSpectrum(obj,g,rho)
            % Calculate wave spectrum vector (obj.amplitude)
            % Used by wavesIrreg (wavesIrreg used by: :meth:`waveClass.setup`.)
            frequency = obj.omega/(2*pi);
            Tp = obj.period;
            Hs = obj.height;
            switch obj.spectrumType
                case {'PM','JS'}
                    % Pierson-Moskowitz Spectrum from IEC TS 62600-2 ED2 Annex C.2 (2019)
                    bPM = (5/4)*(1/Tp)^(4);
                    aPM =  bPM*(Hs/2)^2;
                    fSpectrum  = (aPM*frequency.^(-5).*exp(-bPM*frequency.^(-4)));            % Wave Spectrum [m^2-s] for 'EqualEnergy'
                    if strcmp(obj.spectrumType,'JS')
                        % JONSWAP Spectrum from IEC TS 62600-2 ED2 Annex C.2 (2019)
                        fp = 1/Tp;
                        siga = 0.07;sigb = 0.09;                                    % cutoff frequencies for gamma function
                        [lind,~] = find(frequency<=fp);
                        [hind,~] = find(frequency>fp);
                        gammaAlpha = zeros(size(frequency));
                        if isempty(obj.gamma)
                            TpsqrtHs = Tp/sqrt(Hs);
                            if TpsqrtHs <= 3.6
                                obj.gamma = 5;
                            elseif TpsqrtHs > 5
                                obj.gamma = 1;
                            else
                                obj.gamma = exp(5.75 - 1.15*TpsqrtHs);
                            end
                        end
                        gammaAlpha(lind) = obj.gamma.^exp(-(frequency(lind)-fp).^2/(2*siga^2*fp^2));
                        gammaAlpha(hind) = obj.gamma.^exp(-(frequency(hind)-fp).^2/(2*sigb^2*fp^2));
                        C = 1 - 0.287*log(obj.gamma);
                        fSpectrum = C*fSpectrum.*gammaAlpha;                                % Wave Spectrum [m^2-s]
                    end
                    obj.spectrum = fSpectrum./(2*pi);                                       % Wave Spectrum [m^2-s/rad]
                case 'spectrumImport' % Imported Wave Spectrum
                    data = importdata(obj.spectrumFile);
                    freqData = data(:,1);
                    S_data = data(:,2);
                    freqLoc = freqData >= min(obj.bem.range)/2/pi & freqData <= max(obj.bem.range)/2/pi;
                    fSpectrum = S_data(freqLoc);                                    % Wave Spectrum [m^2-s] for 'EqualEnergy'
                    obj.spectrum = fSpectrum./(2*pi);                                       % Wave Spectrum [m^2-s/rad] for 'Traditional'
                    fprintf('\t"spectrumImport" uses the number of imported wave frequencies (not "Traditional" or "EqualEnergy")\n')
                case {'BS'}
                    error('Following IEC Standard, our Bretschneider Sprectrum (BS) option is exactly how the Pierson-Moskowitz (PM) Spectrum is defined. Please use PM instead');
            end
            % Power per Unit Wave Crest
            obj.wavenumber = calcWaveNumber(obj.omega,obj.waterDepth,g,obj.deepWater); %Calculate Wave Number for Larger Number of Frequencies Before Down Sampling in Equal Energy Method
            if obj.deepWater == 1
                % Deepwater Approximation
                obj.power = sum(1/2*rho*g^(2)*fSpectrum/(2*pi).*obj.dOmega./obj.omega);
            else
                % Full Wave Power Equation
                obj.power = sum((1/2)*rho*g.*fSpectrum/(2*pi).*obj.dOmega.*sqrt(g./obj.wavenumber.*tanh(obj.wavenumber.*obj.waterDepth)).*(1 + 2.*obj.wavenumber.*obj.waterDepth./sinh(2.*obj.wavenumber.*obj.waterDepth)));
            end
            %
            switch obj.bem.option
                case {'EqualEnergy'}
                    m0 = trapz(frequency,abs(fSpectrum));
                    numBins = obj.bem.count+1;
                    a_targ = m0/(numBins);
                    SfInt = cumtrapz(frequency,fSpectrum);
                    wn(1) = 1;
                    for kk = 1:numBins
                        jj = 1;
                        tmpa{kk}(1) = 0;
                        while tmpa{kk}(jj)-kk*a_targ < 0
                            tmpa{kk}(jj+1) = SfInt(wn(kk)+jj);
                            jj = jj+1;
                            if wn(kk)+jj >=length(fSpectrum)
                                break
                            end
                        end
                        [a_targ_real(kk),wna(kk)] = min(abs(tmpa{kk}-kk*a_targ));
                        wn(kk+1) = wna(kk)+wn(kk);
                        a_bins(kk) = trapz(frequency(wn(kk):wn(kk+1)),abs(fSpectrum(wn(kk):wn(kk+1))));
                    end
                    obj.omega = 2*pi*frequency(wn(2:end-1));
                    obj.dOmega = [obj.omega(1)-2*pi*frequency(wn(1)); diff(obj.omega)];
                    obj.spectrum = obj.spectrum(wn(2:end-1));                             % Wave Spectrum [m^2-s/rad]
            end
            obj.amplitude = 2 * obj.spectrum;                                              % Wave Amplitude [m]
        end

        function waveElevIrreg(obj,rampTime,timeseries,df)
            % Calculate irregular wave elevetaion time history
            % used by: :meth:`waveClass.setup`.
            % Used by postProcess for variable time step
            maxIt = length(timeseries);
            rampFunction = (1+cos(pi+pi*timeseries/rampTime))/2;
            rampFunction(timeseries>=rampTime) = 1;

            obj.waveAmpTime = zeros(maxIt,2);
            obj.waveAmpTime(:,1) = timeseries;

            %  Wave Markers
            if ~isempty(obj.marker.location)
                if width(obj.marker.location)~=2
                    error('The coordinates of the visualization markers should have an ordinate (y-coordinate) and an abscissa (x-coordinate)')
                end
            end
            if ~isempty(obj.marker.location)
               SZwaveAmpTimeViz = size(obj.marker.location);
               obj.waveAmpTimeViz = zeros(maxIt,SZwaveAmpTimeViz(1)+1);
               obj.waveAmpTimeViz(:,1) = timeseries;
            end

            % Calculate eta
            for i = 1:length(timeseries)
                tmp  = sqrt(obj.amplitude.*df*obj.spread);
                tmp1 = tmp.*real(exp(sqrt(-1).*(obj.omega.*timeseries(i) + obj.phase)));
                obj.waveAmpTime(i,2) = rampFunction(i)*sum(tmp1,'all');

                % Wave Markers
                if ~isempty(obj.marker.location)
                    for j = 1:SZwaveAmpTimeViz(1)
                        tmp14 = tmp.*real(exp(sqrt(-1).*(obj.omega.*timeseries(i) ...
                            - obj.wavenumber*(obj.marker.location(j,1).*cos(obj.direction*pi/180) ...
                            + obj.marker.location(j,2).*sin(obj.direction*pi/180)) + obj.phase)));
                        obj.waveAmpTimeViz(i,j+1) = rampFunction(i).*sum(tmp14,'all');
                    end
                end
            end
        end

        function waveElevUser(obj,rampTime,maxIt,data,time)
            % Calculate imported wave elevation time history
            % used by: :meth:`waveClass.setup`.
            rampFunction = (1+cos(pi+pi*time/rampTime))/2;
            rampFunction(time>=rampTime) = 1;

            obj.waveAmpTime = zeros(maxIt+1,2);
            data_t = data(:,1)';                    % Data Time [s]
            data_x = data(:,2)';                    % Wave Surface Elevation [m]
            obj.waveAmpTime(:,1) = time;
            obj.waveAmpTime(:,2) = rampFunction.*interp1(data_t,data_x,time);

            if any(~isempty(obj.marker.location))
                warning('Cannot use wave gauges or visualization markers with eta import. Gauges and markers removed.');
                obj.marker.location = [];
            end
        end

        function printWaveSpectrumType(obj)
            % Lists the wave spectrum type
            % Used by  :meth:`waveClass.listInfo`.
            if strcmp(obj.spectrumType,'JS')
                fprintf('\tSpectrum Type                        = JONSWAP \n')
            elseif strcmp(obj.spectrumType,'PM')
                fprintf('\tSpectrum Type                        = Pierson-Moskowitz  \n')
            elseif strcmp(obj.spectrumType,'spectrumImport')
                fprintf('\tSpectrum Type                        = Imported Spectrum \n')
            end
        end
    end
end


function boxes1 = cascade_test(model, prec, testset, year, suffix)

% boxes1 = cascade_test(cls, model, testset, year, suffix)
% Compute bounding boxes in a test set.
% boxes1 are detection windows and scores.
%
% prec specifies the target level of precision that wish to achieve.
% if you let prec = [], a threshold tuned to reach the prec-equals-recall
% point will be selected.

% Now we also save the locations of each filter for rescoring
% parts1 gives the locations for the detections in boxes1
% (these are saved in the cache file, but not returned by the function)

setVOCyear = year;
globals;
pascal_init;

cls = model.class;
ids = textread(sprintf(VOCopts.imgsetpath, testset), '%s');

[prec, recall, thresh] = cascade_thresh(model, year, prec);
model.thresh = thresh;
pca = 5;
model = cascade_model(model, model.year, pca, model.thresh);

% run detector in each image
try
  load([cachedir cls '_boxes_' testset '_' suffix]);
catch
  times = zeros(length(ids), 2);
  % parfor gets confused if we use VOCopts
  opts = VOCopts;
  % parallel implementation disabled for single-threaded tests
  %parfor i = 1:length(ids);
  for i = 1:length(ids);
    if strcmp('inriaperson', cls)
      % INRIA uses a mixutre of PNGs and JPGs, so we need to use the annotation
      % to locate the image.  The annotation is not generally available for PASCAL
      % test data (e.g., 2009 test), so this method can fail for PASCAL.
      rec = PASreadrecord(sprintf(opts.annopath, ids{i}));
      im = imread([opts.datadir rec.imgname]);
    else
      im = imread(sprintf(opts.imgpath, ids{i}));
    end
    th = tic();
    pyra = featpyramid(im, model);
    time_feat = toc(th);

    th = tic();
    [dets, boxes] = cascade_detect(pyra, model, model.thresh);
    time_det = toc(th);

    if ~isempty(boxes)
      [dets boxes] = clipboxes(im, dets, boxes);
      I = nms(dets, 0.5);
      boxes1{i} = dets(I,[1:4 end]);
      parts1{i} = boxes(I,:);
    else
      boxes1{i} = [];
      parts1{i} = [];
    end
    times(i,:) = [time_det time_feat];
    fprintf('%s: testing: %s %s, %d/%d (avg time %.3f)\n', cls, testset, year, ...
            i, length(ids), mean(times(1:i,1)));
  end
  save([cachedir cls '_boxes_' testset '_' suffix], ...
       'boxes1', 'parts1', 'times');
end


function obj = eq_hand_eye_func_new(in1,in2,in3,in4,in5)
coef_f0_q_sym1 = in1(:,1);
coef_f0_q_sym2 = in1(:,2);
coef_f0_q_sym3 = in1(:,3);
coef_f0_q_sym4 = in1(:,4);
coef_f0_q_sym5 = in1(:,5);
coef_f0_q_sym6 = in1(:,6);
coef_f0_q_sym7 = in1(:,7);
coef_f0_q_sym8 = in1(:,8);
coef_f0_q_sym9 = in1(:,9);
coef_f0_q_sym10 = in1(:,10);
coef_f0_q_sym11 = in1(:,11);
coef_f1_q_sym1 = in2(:,1);
coef_f0_q_sym12 = in1(:,12);
coef_f1_q_sym2 = in2(:,2);
coef_f0_q_sym13 = in1(:,13);
coef_f1_q_sym3 = in2(:,3);
coef_f0_q_sym14 = in1(:,14);
coef_f1_q_sym4 = in2(:,4);
coef_f0_q_sym15 = in1(:,15);
coef_f1_q_sym5 = in2(:,5);
coef_f0_q_sym16 = in1(:,16);
coef_f1_q_sym6 = in2(:,6);
coef_f0_q_sym17 = in1(:,17);
coef_f1_q_sym7 = in2(:,7);
coef_f0_q_sym18 = in1(:,18);
coef_f1_q_sym8 = in2(:,8);
coef_f0_q_sym19 = in1(:,19);
coef_f1_q_sym9 = in2(:,9);
coef_f0_q_sym20 = in1(:,20);
coef_f0_q_sym21 = in1(:,21);
coef_f2_q_sym1 = in3(:,1);
coef_f0_q_sym22 = in1(:,22);
coef_f2_q_sym2 = in3(:,2);
coef_f0_q_sym23 = in1(:,23);
coef_f2_q_sym3 = in3(:,3);
coef_f0_q_sym24 = in1(:,24);
coef_f2_q_sym4 = in3(:,4);
coef_f2_q_sym5 = in3(:,5);
coef_f2_q_sym6 = in3(:,6);
coef_f2_q_sym7 = in3(:,7);
coef_f2_q_sym8 = in3(:,8);
coef_f2_q_sym9 = in3(:,9);
coef_f3_q_sym1 = in4(:,1);
coef_f3_q_sym2 = in4(:,2);
coef_f3_q_sym3 = in4(:,3);
coef_f3_q_sym4 = in4(:,4);
coef_f3_q_sym5 = in4(:,5);
coef_f3_q_sym6 = in4(:,6);
coef_f3_q_sym7 = in4(:,7);
coef_f3_q_sym8 = in4(:,8);
coef_f3_q_sym9 = in4(:,9);
coef_f1_q_sym10 = in2(:,10);
coef_f1_q_sym11 = in2(:,11);
coef_f1_q_sym12 = in2(:,12);
coef_f1_q_sym13 = in2(:,13);
coef_f1_q_sym14 = in2(:,14);
coef_f1_q_sym15 = in2(:,15);
coef_f1_q_sym16 = in2(:,16);
coef_f1_q_sym17 = in2(:,17);
coef_f1_q_sym18 = in2(:,18);
coef_f1_q_sym19 = in2(:,19);
coef_f1_q_sym20 = in2(:,20);
coef_f1_q_sym21 = in2(:,21);
coef_f1_q_sym22 = in2(:,22);
coef_f1_q_sym23 = in2(:,23);
coef_f1_q_sym24 = in2(:,24);
coef_f2_q_sym10 = in3(:,10);
coef_f2_q_sym11 = in3(:,11);
coef_f2_q_sym12 = in3(:,12);
coef_f2_q_sym13 = in3(:,13);
coef_f2_q_sym14 = in3(:,14);
coef_f2_q_sym15 = in3(:,15);
coef_f2_q_sym16 = in3(:,16);
coef_f2_q_sym17 = in3(:,17);
coef_f2_q_sym18 = in3(:,18);
coef_f2_q_sym19 = in3(:,19);
coef_f2_q_sym20 = in3(:,20);
coef_f2_q_sym21 = in3(:,21);
coef_f2_q_sym22 = in3(:,22);
coef_f2_q_sym23 = in3(:,23);
coef_f2_q_sym24 = in3(:,24);
coef_f3_q_sym10 = in4(:,10);
coef_f3_q_sym11 = in4(:,11);
coef_f3_q_sym12 = in4(:,12);
coef_f3_q_sym13 = in4(:,13);
coef_f3_q_sym14 = in4(:,14);
coef_f3_q_sym15 = in4(:,15);
coef_f3_q_sym16 = in4(:,16);
coef_f3_q_sym17 = in4(:,17);
coef_f3_q_sym18 = in4(:,18);
coef_f3_q_sym19 = in4(:,19);
coef_f3_q_sym20 = in4(:,20);
coef_f3_q_sym21 = in4(:,21);
coef_f3_q_sym22 = in4(:,22);
coef_f3_q_sym23 = in4(:,23);
coef_f3_q_sym24 = in4(:,24);
q0 = in5(1,:);
q1 = in5(2,:);
q2 = in5(3,:);
q3 = in5(4,:);
t2 = q0.^2;
t3 = q0.^3;
t4 = q1.^2;
t6 = q1.^3;
t7 = q2.^2;
t8 = q2.^3;
t9 = q3.^2;
t10 = q3.^3;
t5 = t2.^2;
obj = [-coef_f1_q_sym1.*t5+coef_f0_q_sym18.*t4-coef_f1_q_sym11.*t2+coef_f0_q_sym12.*t4.^2+coef_f0_q_sym11.*q0.*q1+coef_f0_q_sym22.*q1.*q2+coef_f0_q_sym24.*q1.*q3-coef_f1_q_sym18.*q0.*q1-coef_f1_q_sym22.*q0.*q2-coef_f1_q_sym24.*q0.*q3+coef_f0_q_sym1.*q1.*t3+coef_f0_q_sym5.*q0.*t6-coef_f1_q_sym2.*q1.*t3-coef_f1_q_sym3.*q2.*t3-coef_f1_q_sym4.*q3.*t3+coef_f0_q_sym13.*q2.*t6+coef_f0_q_sym14.*q3.*t6+coef_f0_q_sym19.*q1.*t8+coef_f0_q_sym23.*q1.*t10-coef_f1_q_sym12.*q0.*t6-coef_f1_q_sym19.*q0.*t8-coef_f1_q_sym23.*q0.*t10+coef_f0_q_sym2.*t2.*t4-coef_f1_q_sym5.*t2.*t4+coef_f0_q_sym15.*t4.*t7-coef_f1_q_sym8.*t2.*t7+coef_f0_q_sym17.*t4.*t9-coef_f1_q_sym10.*t2.*t9+coef_f0_q_sym3.*q1.*q2.*t2+coef_f0_q_sym4.*q1.*q3.*t2+coef_f0_q_sym6.*q0.*q2.*t4+coef_f0_q_sym7.*q0.*q3.*t4+coef_f0_q_sym8.*q0.*q1.*t7+coef_f0_q_sym10.*q0.*q1.*t9-coef_f1_q_sym6.*q1.*q2.*t2-coef_f1_q_sym7.*q1.*q3.*t2+coef_f0_q_sym16.*q2.*q3.*t4-coef_f1_q_sym9.*q2.*q3.*t2+coef_f0_q_sym20.*q1.*q3.*t7+coef_f0_q_sym21.*q1.*q2.*t9-coef_f1_q_sym13.*q0.*q2.*t4-coef_f1_q_sym14.*q0.*q3.*t4-coef_f1_q_sym15.*q0.*q1.*t7-coef_f1_q_sym17.*q0.*q1.*t9-coef_f1_q_sym20.*q0.*q3.*t7-coef_f1_q_sym21.*q0.*q2.*t9+coef_f0_q_sym9.*q0.*q1.*q2.*q3-coef_f1_q_sym16.*q0.*q1.*q2.*q3;-coef_f2_q_sym1.*t5+coef_f0_q_sym22.*t7-coef_f2_q_sym11.*t2+coef_f0_q_sym19.*t7.^2+coef_f0_q_sym11.*q0.*q2+coef_f0_q_sym18.*q1.*q2+coef_f0_q_sym24.*q2.*q3-coef_f2_q_sym18.*q0.*q1-coef_f2_q_sym22.*q0.*q2-coef_f2_q_sym24.*q0.*q3+coef_f0_q_sym1.*q2.*t3+coef_f0_q_sym8.*q0.*t8+coef_f0_q_sym12.*q2.*t6+coef_f0_q_sym15.*q1.*t8-coef_f2_q_sym2.*q1.*t3-coef_f2_q_sym3.*q2.*t3-coef_f2_q_sym4.*q3.*t3+coef_f0_q_sym20.*q3.*t8+coef_f0_q_sym23.*q2.*t10-coef_f2_q_sym12.*q0.*t6-coef_f2_q_sym19.*q0.*t8-coef_f2_q_sym23.*q0.*t10+coef_f0_q_sym3.*t2.*t7+coef_f0_q_sym13.*t4.*t7-coef_f2_q_sym5.*t2.*t4+coef_f0_q_sym21.*t7.*t9-coef_f2_q_sym8.*t2.*t7-coef_f2_q_sym10.*t2.*t9+coef_f0_q_sym2.*q1.*q2.*t2+coef_f0_q_sym4.*q2.*q3.*t2+coef_f0_q_sym5.*q0.*q2.*t4+coef_f0_q_sym6.*q0.*q1.*t7+coef_f0_q_sym9.*q0.*q3.*t7+coef_f0_q_sym10.*q0.*q2.*t9+coef_f0_q_sym14.*q2.*q3.*t4+coef_f0_q_sym16.*q1.*q3.*t7+coef_f0_q_sym17.*q1.*q2.*t9-coef_f2_q_sym6.*q1.*q2.*t2-coef_f2_q_sym7.*q1.*q3.*t2-coef_f2_q_sym9.*q2.*q3.*t2-coef_f2_q_sym13.*q0.*q2.*t4-coef_f2_q_sym14.*q0.*q3.*t4-coef_f2_q_sym15.*q0.*q1.*t7-coef_f2_q_sym17.*q0.*q1.*t9-coef_f2_q_sym20.*q0.*q3.*t7-coef_f2_q_sym21.*q0.*q2.*t9+coef_f0_q_sym7.*q0.*q1.*q2.*q3-coef_f2_q_sym16.*q0.*q1.*q2.*q3;coef_f0_q_sym24.*t9-coef_f3_q_sym1.*t5-coef_f3_q_sym11.*t2+coef_f0_q_sym23.*t9.^2+coef_f0_q_sym11.*q0.*q3+coef_f0_q_sym18.*q1.*q3+coef_f0_q_sym22.*q2.*q3-coef_f3_q_sym18.*q0.*q1-coef_f3_q_sym22.*q0.*q2-coef_f3_q_sym24.*q0.*q3+coef_f0_q_sym1.*q3.*t3+coef_f0_q_sym10.*q0.*t10+coef_f0_q_sym12.*q3.*t6+coef_f0_q_sym17.*q1.*t10+coef_f0_q_sym19.*q3.*t8+coef_f0_q_sym21.*q2.*t10-coef_f3_q_sym2.*q1.*t3-coef_f3_q_sym3.*q2.*t3-coef_f3_q_sym4.*q3.*t3-coef_f3_q_sym12.*q0.*t6-coef_f3_q_sym19.*q0.*t8-coef_f3_q_sym23.*q0.*t10+coef_f0_q_sym4.*t2.*t9+coef_f0_q_sym14.*t4.*t9+coef_f0_q_sym20.*t7.*t9-coef_f3_q_sym5.*t2.*t4-coef_f3_q_sym8.*t2.*t7-coef_f3_q_sym10.*t2.*t9+coef_f0_q_sym2.*q1.*q3.*t2+coef_f0_q_sym3.*q2.*q3.*t2+coef_f0_q_sym5.*q0.*q3.*t4+coef_f0_q_sym7.*q0.*q1.*t9+coef_f0_q_sym8.*q0.*q3.*t7+coef_f0_q_sym9.*q0.*q2.*t9+coef_f0_q_sym13.*q2.*q3.*t4+coef_f0_q_sym15.*q1.*q3.*t7+coef_f0_q_sym16.*q1.*q2.*t9-coef_f3_q_sym6.*q1.*q2.*t2-coef_f3_q_sym7.*q1.*q3.*t2-coef_f3_q_sym9.*q2.*q3.*t2-coef_f3_q_sym13.*q0.*q2.*t4-coef_f3_q_sym14.*q0.*q3.*t4-coef_f3_q_sym15.*q0.*q1.*t7-coef_f3_q_sym17.*q0.*q1.*t9-coef_f3_q_sym20.*q0.*q3.*t7-coef_f3_q_sym21.*q0.*q2.*t9+coef_f0_q_sym6.*q0.*q1.*q2.*q3-coef_f3_q_sym16.*q0.*q1.*q2.*q3;t2+t4+t7+t9-1.0];


function script_rfcn_VOC0712_ResNet101_OHEM_rpn()
% script_rfcn_VOC0712_ResNet101_OHEM_rpn()
% RFCN training and testing with OHEM using ResNet101 model and RPN
% proposals
% --------------------------------------------------------
% R-FCN implementation
% Modified from MATLAB Faster R-CNN (https://github.com/shaoqingren/faster_rcnn)
% Copyright (c) 2016, Jifeng Dai
% Licensed under The MIT License [see LICENSE for details]
% --------------------------------------------------------

clc;
clear mex;
clear is_valid_handle; % to clear init_key
run(fullfile(fileparts(fileparts(mfilename('fullpath'))), 'startup'));
%% -------------------- CONFIG --------------------
opts.caffe_version          = 'caffe_rfcn';
opts.gpu_id                 = auto_select_gpu;
active_caffe_mex(opts.gpu_id, opts.caffe_version);

% model
model                       = Model.ResNet101_for_RFCN_VOC0712_OHEM();
% cache name
opts.cache_name             = 'rfcn_VOC0712_ResNet101_OHEM_rpn_resnet101';
% config
conf                        = rfcn_config_ohem('image_means', model.mean_image);
% train/test data
fprintf('Loading dataset...')
dataset                     = [];
dataset                     = Dataset.voc0712_trainval_sp(dataset, 'train', conf.use_flipped, 'resnet101');
dataset                     = Dataset.voc2007_test_sp(dataset, 'test', false, 'resnet101');
fprintf('Done.\n');

% do validation, or not
opts.do_val                 = true;

%% -------------------- TRAINING --------------------

opts.rfcn_model        = rfcn_train(conf, dataset.imdb_train, dataset.roidb_train, ...
                                'do_val',           opts.do_val, ...
                                'imdb_val',         dataset.imdb_test, ...
                                'roidb_val',        dataset.roidb_test, ...
                                'solver_def_file',  model.solver_def_file, ...
                                'net_file',         model.net_file, ...
                                'cache_name',       opts.cache_name, ...
                                'caffe_version',    opts.caffe_version);
assert(exist(opts.rfcn_model, 'file') ~= 0, 'not found trained model');

%% -------------------- TESTING --------------------
                          rfcn_test(conf, dataset.imdb_test, dataset.roidb_test, ...
                                'net_def_file',     model.test_net_def_file, ...
                                'net_file',         opts.rfcn_model, ...
                                'cache_name',       opts.cache_name,...
                                'ignore_cache',     true);

end


% mario_huggins.m - Nick Clark 28/9/2007
%
% USE HEADPHONES
% WAIT A FEW SECONDS FOR PROCESSING
% LISTEN CAREFULLY AND ENJOY!
%
% Just run this script with the huggins.m in the same directory or in your
% path.  This is a cool litle demo to show how huggins binaural pitch can
% be made into chords. Thanks to James Humes on matlabcentral for inspiring
% me to do this to the mario theme song . . man - this is probably the most
% tragic way I have ever spent a Friday night.
%
% This demo only uses the alto and tenor parts as Huggins pitch is a
% pretty weak percept at the best of times.  You'll need a pretty good set
% of headphones to hear this too - it will not work properly over
% loudspeakers.

t = 0.15; %single beat duration
base_pitch = 440; %tuning parameter 440Hz default
introONLY = 0; %set to one if you only want top hear the intro
sr = 16e3;

%%%%% Notes and timing %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% The transcription here was originally done by Stewart Bozarth and the
% painstaking programming of the score into MATLAB code was taken from
% James Humes' fileexchange submission
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if introONLY
    keysT = [ 56 56 0 56 0 52 56 0 59 0 0 47 0 0 ];
    keysA=  [ 46 46 0 46 0 46 46 0 51 0 0 47 0 0 ];
    durA =  [ t t t t t t t t t t 2*t t t 2*t];
else %whole song
    keysT = [  56 56 0 56 0 52 56 0 59 0 0 47 0 0 52 0 47 0 44 0 0 49 0 51 0 50 49 0 47 0 56 0 59 0 61 0 57 59 0 56 0 52 54 51 0 52 0 47 0 44 0 0 49 0 51 0 50 49 0 47 0 56 0 59 0 61 0 57 59 0 56 0 52 54 51 0 0 59 58 57 55 0 56 0 48 49 52 0 49 52 54 0 59 58 57 55 0 56 0 64 0 64 64 0 0 0 59 58 57 55 0 56 0 48 49 52 0 49 52 54 0 55 0 0 54 0 52 0 0 0 0 59 58 57 55 0 56 0 48 49 52 0 49 52 54 0 59 58 57 55 0 56 0 64 0 64 64 0 0 0 59 58 57 55 0 56 0 48 49 52 0 49 52 54 0 55 0 0 54 0 52 0 0 0 52 52 0 52 0 52 54 0 56 52 0 49 47 0 0 52 52 0 52 0 52 54 56 0 0 52 52 0 52 0 52 54 0 56 52 0 49 47 0 0 56 56 0 56 0 52 56 0 59 0 0 47 0 0 56 52 0 47 0 48 0 49 57 0 57 49 0 0 51 0 61 0 61 0 61 0 59 0 57 0 56 52 0 49 47 0 0 56 52 0 47 0 48 0 49 57 0 57 49 0 0 47 57 0 57 57 0 56 0 54 0 52 0 0 0 52 0 47 0 44 0 49 51 49 48 50 48 47];
    keysA = [  46 46 0 46 0 46 46 0 51 0 0 47 0 0 44 0 40 0 35 0 0 40 0 42 0 41 40 0 40 0 47 0 51 0 52 0 49 51 0 49 0 56 45 42 0 44 0 40 0 35 0 0 40 0 42 0 41 40 0 40 0 47 0 51 0 52 0 49 51 0 49 0 56 45 42 0 0 56 55 54 51 0 52 0 44 45 47 0 40 44 45 0 56 55 54 51 0 52 0 59 0 59 59 0 0 0 56 55 54 51 0 52 0 44 45 47 0 40 44 45 0 48 0 0 45 0 44 0 0 0 0 56 55 54 51 0 52 0 44 45 47 0 40 44 45 0 56 55 54 51 0 52 0 59 0 59 59 0 0 0 56 55 54 51 0 52 0 44 45 47 0 40 44 45 0 48 0 0 45 0 44 0 0 0 48 48 0 48 0 48 50 0 47 44 0 44 40 0 0 48 48 0 48 0 48 50 47 0 0 48 48 0 48 0 48 50 0 47 44 0 44 40 0 0 46 46 0 46 0 46 46 0 51 0 0 47 0 0 52 49 0 44 0 44 0 45 52 0 52 45 0 0 47 0 57 0 57 0 57 0 56 0 54 0 52 49 0 45 44 0 0 52 49 0 44 0 44 0 45 52 0 52 45 0 0 47 54 0 54 54 0 52 0 51 0 47 44 0 44 40 0 0 44 0 40 49 51 49 48 50 48 47];
    durA =  [  t t t t t t t t t t 2*t t t 2*t t 2*t t 2*t t t t t t t t t t t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t t t t t t t t t t t 2*t t 2*t t 2*t t t t t t t t t t t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t t t t t t t t t t t 2*t 2*t t t t t t t t t t t t t t t 2*t t t t t t t t t t t t t 2*t 2*t t t t t t t t t t t t t t t 2*t t t t t 2*t t t 2*t 4*t 2*t t t t t t t t t t t t t t t 2*t t t t t t t t t t t t t 2*t 2*t t t t t t t t t t t t t t t 2*t t t t t 2*t t t 2*t 4*t t t t t t t t t t t t t t t 2*t t t t t t t t t 4*t 4*t t t t t t t t t t t t t t t 2*t t t t t t t t t t t 2*t t t 2*t t t t t 2*t t t t t t t t t 2*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t t t t t t t 2*t t t t t 2*t t t t t t t t t 2*t t t t t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t (2/3)*t t t t t t t 2*t t 2*t t 2*t t t 4*t 4*t t 6*t];
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Convert key values and make song (may take some time - please wait)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
freqA = base_pitch*2.^((keysA-49)/12);
freqT = base_pitch*2.^((keysT-49)/12);

song = [0 0];
for n = 1:length(keysT)
    song = [song; huggins([freqA(n) freqT(n)],sr,durA(n))];
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Add a 2.5kHz low-pass filter to make things a little less painful
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[b,a] = butter(4,2500*(sr^-1),'low');
song = filter(b,a,song);

soundsc(song, sr)


% ------------------------------------------------------------------------
%  Copyright (C)
%  Torr Vision Group (TVG)
%  University of Oxford - UK
%
%  Qizhu Li <liqizhu@robots.ox.ac.uk>
%  August 2018
% ------------------------------------------------------------------------
% This file is part of the weakly-supervised training method presented in:
%    Qizhu Li*, Anurag Arnab*, Philip H.S. Torr,
%    "Weakly- and Semi-Supervised Panoptic Segmentation,"
%    European Conference on Computer Vision (ECCV) 2018.
% Please consider citing the paper if you use this code.
% ------------------------------------------------------------------------
% This function removes thing-class predictions outside their bounding
% boxes.
%  INPUT:
%  - pred_label : prediction
%  - bbox_masks : object bounding box masks produced by make_bbox_masks.m
%  - thing_classes : list of train ids for thing classes
%  - ignore_label  : label to ignore, normally 255
%
%  OUTPUT:
%  - modified_label: processed prediction
%
%  DEMO:
%  - See scripts/clean_label.m
% ------------------------------------------------------------------------

function modified_label = apply_bbox_prior(pred_label, bbox_masks, thing_classes, ignore_label)
modified_label = pred_label;
for j = reshape(thing_classes, 1, [])
    if isempty(bbox_masks{j+1})
        modified_label(modified_label == j) = ignore_label;
    else
        prediction_bbox_prior_mask = modified_label == j;
        gt_bbox_mask = bbox_masks{j+1};
        ignore_mask = and(prediction_bbox_prior_mask, ~gt_bbox_mask);
        modified_label(ignore_mask) = ignore_label;
    end
end
end


function [region_feats, regions] = convFeat_to_poolFeat_multi_region(...
    multiple_region_params, conv_feats, boxes, random_scale)
% convFeat_to_poolFeat_multi_region given the convolutional features of an
% image, it adaptively pools fixed size region features for each bounding
% box and for multiple type of regions.
%
% INPUTS:
% 1) multiple_region_params: is a M x 1 vector of objects of type struct that
% specify the region pooling parameters for each of the M types of regions
% 2) conv_feats: is a K x 1 cell vector or vector of objects, with K >=1
% different type of convolutional features.
% 3) boxes: a N x 4 array with the bounding box coordinates in the form of
% [x0,y0,x1,y1] (where (x0,y0) is the top-left corner and (x1,y1) the
% bottom left corner)
% 4) random_scale: a boolean value that if set to true then each bounding
% box is projected to the convolutional features of a random scale of the
% image.
%
% OUTPUTS:
% 1) region_feats: is a M x 1 cell array where region_feats{i} is a N x F_i
% array with the region features of each of the N bounding boxes for the
% i-th type of region. F_i is the number of features of the i-th type of
% region.
% 2) regions: is a M x 1 cell array with the region coordinates of each
% bounding box and for each type of region. Specifically, regions{i} is a
% N x 8 array that contains the region coordinates of each of the N bounding
% boxes for the i-th type of region. Note that each region is represented
% by 8 values [xo0, yo0, xo1, yo1, xi0, yi0, xi1, yi1] that correspond to
% its outer rectangle [xo0, yo0, xo1, yo1] and its inner rectangle
% [xi0, yi0, xi1, yi1].
%
%
% This file is part of the code that implements the following ICCV2015 accepted paper:
% title: "Object detection via a multi-region & semantic segmentation-aware CNN model"
% authors: Spyros Gidaris, Nikos Komodakis
% institution: Universite Paris Est, Ecole des Ponts ParisTech
% Technical report: http://arxiv.org/abs/1505.01749
% code: https://github.com/gidariss/mrcnn-object-detection
%
% Part of the code in this file comes from the SPP-Net code:
% https://github.com/ShaoqingRen/SPP_net
%
% AUTORIGHTS
% --------------------------------------------------------
% Copyright (c) 2015 Spyros Gidaris
%
% "Object detection via a multi-region & semantic segmentation-aware CNN model"
% Technical report: http://arxiv.org/abs/1505.01749
% Licensed under The MIT License [see LICENSE for details]
% ---------------------------------------------------------
% Copyright (c) 2014, Shaoqing Ren
%
% This file is part of the SPP code and is available
% under the terms of the Simplified BSD License provided in
% LICENSE. Please retain this notice and LICENSE if you use
% this file (or any portion of it) in your project.
% ---------------------------------------------------------

if ~exist('random_scale', 'var'), random_scale = false; end

num_regions  = length(multiple_region_params);
region_feats = cell(num_regions,1);
regions      = cell(num_regions,1);

for p = 1:num_regions, region_feats{p} = single([]); end
for p = 1:num_regions, regions{p}   = single([]); end

if isempty(boxes), return; end

% pool the region features the bounding boxes for each type of region
if length(conv_feats) == 1
    for p = 1:num_regions
        [region_feats{p}, regions{p}] = convFeat_to_poolFeat(...
            multiple_region_params(p), conv_feats, boxes, random_scale);
    end
else
    if iscell(conv_feats)
        for p = 1:num_regions
            [region_feats{p}, regions{p}]  = convFeat_to_poolFeat(...
                multiple_region_params(p), conv_feats{multiple_region_params(p).feat_id}, ...
                boxes, random_scale);
        end
    elseif isstruct(conv_feats)
        for p = 1:num_regions
            [region_feats{p}, regions{p}]  = convFeat_to_poolFeat(...
                multiple_region_params(p), conv_feats(multiple_region_params(p).feat_id), ...
                boxes, random_scale);
        end
    end
end
end


% pop_interp() - interpolate data channels
%
% Usage: EEGOUT = pop_interp(EEG, badchans, method);
%
% Inputs:
%     EEG      - EEGLAB dataset
%     badchans - [integer array] indices of channels to interpolate.
%                For instance, these channels might be bad.
%                [chanlocs structure] channel location structure containing
%                either locations of channels to interpolate or a full
%                channel structure (missing channels in the current
%                dataset are interpolated).
%     method   - [string] method used for interpolation (default is 'spherical').
%                'invdist' uses inverse distance on the scalp
%                'spherical' uses superfast spherical interpolation.
%                'spacetime' uses griddata3 to interpolate both in space
%                and time (very slow and cannot be interupted).
% Output:
%     EEGOUT   - data set with bad electrode data replaced by
%                interpolated data
%
% Author: Arnaud Delorme, CERCO, CNRS, 2009-

% Copyright (C) Arnaud Delorme, CERCO, 2009, arno@salk.edu
%
% 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 2 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, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [EEG com] = pop_interp(EEG, bad_elec, method)

    com = '';
    if nargin < 1
        help pop_interp;
        return;
    end;

    if nargin < 2
        disp('Warning: interpolation can be done on the fly in studies');
        disp('         this function will actually create channels in the dataset');
        disp('Warning: do not interpolate channels before running ICA');
        disp('You may define channel location to interpolate in the channel');
        disp('editor and declare such channels as non-data channels');

        enablenondat = 'off';
        if isfield(EEG.chaninfo, 'nodatchans')
            if ~isempty(EEG.chaninfo.nodatchans)
                enablenondat = 'on';
            end;
        end;
        cb_nondat   = [ 'tmpchaninfo = EEG.chaninfo; [chanlisttmp chanliststr] = pop_chansel( { tmpchaninfo.nodatchans.labels } );' ...
                      'if ~isempty(chanlisttmp),' ...
                      '   set(gcbf, ''userdata'', EEG.chaninfo.nodatchans(chanlisttmp));' ...
                      '   set(findobj(gcbf, ''tag'', ''chanlist''), ''string'', chanliststr);' ...
                      'end;' ...
                      'clear chanlisttmp chanliststr tmpchaninfo;' ];

        cb_otherdat = [ 'tmpanswer = inputdlg2({ ''Dataset index'' }, ''Choose dataset'', 1, { '''' });' ...
                      'if ~isempty(tmpanswer),' ...
                      '   tmpanswernum = round(str2num(tmpanswer{1}));' ...
                      '   if ~isempty(tmpanswernum),' ...
                      '       if tmpanswernum > 0 & tmpanswernum < length(ALLEEG),' ...
                      '           tmpchans1 = ALLEEG(tmpanswernum).chanlocs;' ...
                      '           tmpchans2 = EEG.chanlocs;' ...
                      '           tmpchanlist = setdiff( { tmpchans1.labels }, { tmpchans2.labels } );' ...
                      '           if ~isempty(tmpchanlist),' ...
                      '               set(gcbf, ''userdata'', ALLEEG(tmpanswernum).chanlocs);' ...
                      '               tmpchanlist(2,:) = { '' '' };' ...
                      '               set(findobj(gcbf, ''tag'', ''chanlist''), ''string'', [ tmpchanlist{:} ]);' ...
                      '           else,' ...
                      '               warndlg2(''No new channels in this dataset'');' ...
                      '           end;' ...
                      '       else,' ...
                      '           warndlg2(''Wrong index'');' ...
                      '       end;' ...
                      '   end;' ...
                      'end;' ...
                      'clear tmpanswer tmpanswernum;' ];

        uilist = { { 'Style' 'text' 'string' 'What channel(s) to interpolate' 'fontweight' 'bold' } ...
                   { 'style' 'text' 'string' 'none' 'tag' 'chanlist' } ...
                   { 'style' 'pushbutton' 'string' 'Select from non-data channels' 'callback' 'pop_interp(''nondatchan'',gcbf);' 'enable' enablenondat } ...
                   { 'style' 'pushbutton' 'string' 'Select from other dataset' 'callback' 'pop_interp(''selectchan'',gcbf);'} ...
                   { 'style' 'pushbutton' 'string' 'Use list of other dataset' 'callback' 'pop_interp(''uselist'',gcbf);'} ...
                   { } ...
                   { 'style' 'text'  'string' 'Interpolation method'} ...
                   { 'style' 'popupmenu'  'string' 'Spherical|Planar (slow)'  'tag' 'method' } ...
                   };

        geom = { 1 1 1 1 1 1 [1.8 1] };
        [res userdata tmp restag ] = inputgui( 'uilist', uilist, 'title', 'Interpolate channel(s) -- pop_interp()', 'geometry', geom, 'helpcom', 'pophelp(''pop_interp'')');
        if isempty(res) | isempty(userdata), return; end;

        if restag.method == 1
             method = 'spherical';
        else method = 'invdist';
        end;
        bad_elec = userdata.chans;

        com = sprintf('EEG = pop_interp(EEG, %s, ''%s'');', userdata.chanstr, method);
        if ~isempty(findstr('nodatchans', userdata.chanstr))
            eval( [ userdata.chanstr '=[];' ] );
        end;

    elseif isstr(EEG)
        command = EEG;
        clear EEG;
        fig = bad_elec;
        userdata = get(fig, 'userdata');

        if strcmpi(command, 'nondatchan')
            global EEG;
            tmpchaninfo = EEG.chaninfo;
            [chanlisttmp chanliststr] = pop_chansel( { tmpchaninfo.nodatchans.labels } );
            if ~isempty(chanlisttmp),
                userdata.chans   = EEG.chaninfo.nodatchans();
                userdata.chanstr = [ 'EEG.chaninfo.nodatchans([' num2str(chanlisttmp) '])' ];
                set(fig, 'userdata', userdata);
                set(findobj(fig, 'tag', 'chanlist'), 'string', chanliststr);
            end;
        else
            global ALLEEG EEG;
            tmpanswer = inputdlg2({ 'Dataset index' }, 'Choose dataset', 1, { '' });
            if ~isempty(tmpanswer),
                tmpanswernum = round(str2num(tmpanswer{1}));
                if ~isempty(tmpanswernum),
                    if tmpanswernum > 0 & tmpanswernum < length(ALLEEG),
                        TMPEEG = ALLEEG(tmpanswernum);

                        tmpchans1 = TMPEEG.chanlocs;
                        if strcmpi(command, 'selectchan')
                            chanlist = pop_chansel( { tmpchans1.labels } );
                        else
                            chanlist = 1:length(TMPEEG.chanlocs); % use all channels
                        end

                        % look at what new channels are selected
                        tmpchans2 = EEG.chanlocs;
                        [tmpchanlist chaninds] = setdiff( { tmpchans1(chanlist).labels }, { tmpchans2.labels } );
                        if ~isempty(tmpchanlist),
                            if length(chanlist) == length(TMPEEG.chanlocs)
                                userdata.chans   = TMPEEG.chanlocs;
                                userdata.chanstr = [ 'ALLEEG(' tmpanswer{1} ').chanlocs' ];
                            else
                                userdata.chans   = TMPEEG.chanlocs(chanlist(sort(chaninds)));
                                userdata.chanstr = [ 'ALLEEG(' tmpanswer{1} ').chanlocs([' num2str(chanlist(sort(chaninds))) '])' ];
                            end;
                            set(fig, 'userdata', userdata);
                            tmpchanlist(2,:) = { ' ' };
                            set(findobj(gcbf, 'tag', 'chanlist'), 'string', [ tmpchanlist{:} ]);
                        else
                            warndlg2('No new channels selected');
                        end;
                    else
                        warndlg2('Wrong index');
                    end;
                end;
            end;
        end;
        return;
    end;

    EEG = eeg_interp(EEG, bad_elec, method);


function [sourcemodel, cfg] = ft_prepare_leadfield(cfg, data)

% FT_PREPARE_LEADFIELD computes the forward model for many dipole locations
% on a regular 2D or 3D sourcemodel and stores it for efficient inverse modelling
%
% Use as
%   [sourcemodel] = ft_prepare_leadfield(cfg, data)
%
% It is necessary to input the data on which you want to perform the
% inverse computations, since that data generally contain the gradiometer
% information and information about the channels that should be included in
% the forward model computation. The data structure can be either obtained
% from FT_PREPROCESSING, FT_FREQANALYSIS or FT_TIMELOCKANALYSIS. If the data is empty,
% all channels will be included in the forward model.
%
% The configuration should contain
%   cfg.channel            = Nx1 cell-array with selection of channels (default = 'all'),
%                            see FT_CHANNELSELECTION for details
%
% The positions of the sources can be specified as a regular 3-D
% sourcemodel that is aligned with the axes of the head coordinate system
%   cfg.xgrid      = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
%   cfg.ygrid      = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
%   cfg.zgrid      = vector (e.g.   0:1:20) or 'auto' (default = 'auto')
%   cfg.resolution = number (e.g. 1 cm) for automatic sourcemodel generation
% Alternatively the position of a few sources at locations of interest can
% be specified, for example obtained from an anatomical or functional MRI
%   cfg.sourcemodel.pos        = N*3 matrix with position of each source
%   cfg.sourcemodel.inside     = N*1 vector with boolean value whether sourcemodel point is inside brain (optional)
%   cfg.sourcemodel.dim        = [Nx Ny Nz] vector with dimensions in case of 3-D sourcemodel (optional)
%
% The volume conduction model of the head should be specified as
%   cfg.headmodel     = structure with volume conduction model, see FT_PREPARE_HEADMODEL
%
% The EEG or MEG sensor positions can be present in the data or can be specified as
%   cfg.elec          = structure with electrode positions or filename, see FT_READ_SENS
%   cfg.grad          = structure with gradiometer definition or filename, see FT_READ_SENS
%
% Optionally, you can modify the leadfields by reducing the rank (i.e.
% remove the weakest orientation), or by normalizing each column.
%   cfg.reducerank      = 'no', or number (default = 3 for EEG, 2 for MEG)
%   cfg.normalize       = 'yes' or 'no' (default = 'no')
%   cfg.normalizeparam  = depth normalization parameter (default = 0.5)
%   cfg.backproject     = 'yes' or 'no' (default = 'yes') determines when reducerank is applied
%                         whether the lower rank leadfield is projected back onto the original
%                         linear subspace, or not.
%
% Depending on the type of headmodel, some additional options may be
% specified.
%
% For OPENMEEG based headmodels:
%   cfg.openmeeg.batchsize    = scalar (default 100e3), number of dipoles
%                               for which the leadfield is computed in a
%                               single call to the low-level code. Trades off
%                               memory efficiency for speed.
%   cfg.openmeeg.dsm          = 'no'/'yes', reuse existing DSM if provided
%   cfg.openmeeg.keepdsm      = 'no'/'yes', option to retain DSM (no by default)
%   cfg.openmeeg.nonadaptive  = 'no'/'yes'
%
% For SINGLESHELL based headmodels:
%   cfg.singleshell.batchsize = scalar or 'all' (default 1), number of dipoles
%                               for which the leadfield is computed in a
%                               single call to the low-level code. Trades off
%                               memory efficiency for speed.
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
% If you specify this option the input data will be read from a *.mat
% file on disk. This mat files should contain only a single variable named 'data',
% corresponding to the input structure.
%
% See also FT_SOURCEANALYSIS, FT_DIPOLEFITTING, FT_PREPARE_HEADMODEL, FT_PREPARE_SOURCEMODEL

% Undocumented local options:
% cfg.feedback
% cfg.sel50p      = 'no' (default) or 'yes'
% cfg.lbex        = 'no' (default) or a number that corresponds with the radius
% cfg.mollify     = 'no' (default) or a number that corresponds with the FWHM

% Copyright (C) 2004-2013, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar data
ft_preamble provenance data
ft_preamble trackconfig

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

% the data can be passed as input arguments or can be read from disk
hasdata = exist('data', 'var');

if ~hasdata
  % the data variable will be passed to the prepare_headmodel function below
  % where it would be used for channel selection
  data = [];
else
  % check if the input data is valid for this function
  data = ft_checkdata(data);
end

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'renamed', {'hdmfile', 'headmodel'});
cfg = ft_checkconfig(cfg, 'renamed', {'vol',     'headmodel'});
cfg = ft_checkconfig(cfg, 'renamed', {'grid',    'sourcemodel'});
cfg = ft_checkconfig(cfg, 'renamed', {'om',      'openmeeg'});
cfg = ft_checkconfig(cfg, 'renamed', {'elecfile', 'elec'});
cfg = ft_checkconfig(cfg, 'renamed', {'gradfile', 'grad'});
cfg = ft_checkconfig(cfg, 'renamed', {'optofile', 'opto'});

% set the defaults
cfg.normalize      = ft_getopt(cfg, 'normalize',      'no');
cfg.normalizeparam = ft_getopt(cfg, 'normalizeparam', 0.5);
cfg.lbex           = ft_getopt(cfg, 'lbex',           'no');
cfg.sel50p         = ft_getopt(cfg, 'sel50p',         'no');
cfg.feedback       = ft_getopt(cfg, 'feedback',       'text');
cfg.mollify        = ft_getopt(cfg, 'mollify',        'no');
cfg.patchsvd       = ft_getopt(cfg, 'patchsvd',       'no');
cfg.backproject    = ft_getopt(cfg, 'backproject',    'yes'); % determines whether after rank reduction the subspace projected leadfield is backprojected onto the original space
% cfg.reducerank   = ft_getopt(cfg, 'reducerank', 'no');      % the default for this depends on EEG/MEG and is set below

cfg = ft_checkconfig(cfg, 'renamed', {'tightgrid', 'tight'});  % this is moved to cfg.sourcemodel.tight by the subsequent createsubcfg
cfg = ft_checkconfig(cfg, 'renamed', {'sourceunits', 'unit'}); % this is moved to cfg.sourcemodel.unit by the subsequent createsubcfg

% put the low-level options pertaining to the sourcemodel in their own field
cfg = ft_checkconfig(cfg, 'createsubcfg', {'sourcemodel'});
% move some fields from cfg.sourcemodel back to the top-level configuration
cfg = ft_checkconfig(cfg, 'createtopcfg', {'sourcemodel'});

% this code expects the inside to be represented as a logical array
cfg.sourcemodel = ft_checkconfig(cfg.sourcemodel, 'renamed',  {'pnt' 'pos'});
cfg = ft_checkconfig(cfg, 'inside2logical', 'yes');

if strcmp(cfg.sel50p, 'yes') && strcmp(cfg.lbex, 'yes')
  ft_error('subspace projection with either lbex or sel50p is mutually exclusive');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% collect and preprocess the electrodes/gradiometer and head model
[headmodel, sens, cfg] = prepare_headmodel(cfg, data);

% set the default for reducing the rank of the leadfields
if ft_senstype(sens, 'eeg')
  cfg.reducerank = ft_getopt(cfg, 'reducerank', 3);
else
  cfg.reducerank = ft_getopt(cfg, 'reducerank', 2);
end

% construct the sourcemodel for which the leadfield will be computed
tmpcfg           = keepfields(cfg, {'sourcemodel', 'mri', 'headshape', 'symmetry', 'smooth', 'threshold', 'spheremesh', 'inwardshift', 'xgrid' 'ygrid', 'zgrid', 'resolution', 'tight', 'warpmni', 'template', 'showcallinfo'});
tmpcfg.headmodel = headmodel;
if ft_senstype(sens, 'eeg')
  tmpcfg.elec = sens;
elseif ft_senstype(sens, 'meg')
  tmpcfg.grad = sens;
end
sourcemodel = ft_prepare_sourcemodel(tmpcfg);

% check whether units are equal (NOTE: this was previously not required,
% this check can be removed if the underlying bug is resolved. See
% http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=2387
if ~isfield(headmodel, 'unit') || ~isfield(sourcemodel, 'unit') || ~isfield(sens, 'unit')
  ft_warning('cannot determine the units of all geometric objects required for leadfield computation (headmodel, sourcemodel, sensor configuration). THIS CAN LEAD TO WRONG RESULTS! (refer to http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=2387)');
else
  if ~strcmp(headmodel.unit, sourcemodel.unit) || ~strcmp(sourcemodel.unit, sens.unit)
    ft_error('geometric objects (headmodel, sourcemodel, sensor configuration) are not expressed in the same units (this used to be allowed, and will be again in the future, but for now there is a bug which prevents a correct leadfield from being computed; see http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=2387)');
  end
end

% find the indices of all sourcemodel points that are inside the brain
insideindx = find(sourcemodel.inside);

if ft_headmodeltype(headmodel, 'openmeeg')

  ft_hastoolbox('openmeeg', 1);  % add to path (if not yet on path)

  % repeated system calls to the openmeeg executable makes it rather slow
  % calling it once is much more efficient
  fprintf('calculating leadfield for all positions at once, this may take a while...\n');

  if(~isfield(cfg,'openmeeg'))
    cfg.openmeeg = [];
  end
  batchsize   = ft_getopt(cfg.openmeeg, 'batchsize',100e3); % number of voxels per DSM batch; set to e.g. 1000 if not much RAM available
  dsm         = ft_getopt(cfg.openmeeg, 'dsm');             % reuse existing DSM if provided
  keepdsm     = ft_getopt(cfg.openmeeg, 'keepdsm', 'no');   % retain DSM
  nonadaptive = ft_getopt(cfg.openmeeg, 'nonadaptive', 'no');

  ndip       = length(insideindx);
  numchunks  = ceil(ndip/batchsize);
  if(numchunks > 1)
    if istrue(keepdsm)
      ft_warning('Keeping DSM output not supported when the computation is split into batches')
    end
    keepdsm = false;
  end

  try
    % DSM computation is computationally intensive:
    % As it can be reused with same voxel sourcemodel (i.e. if voxels are defined in
    % MRI coordinates rather than MEG coordinates), optionally save result.
    % Dense voxel grids may require several gigabytes of RAM, so optionally
    % split into smaller batches

    [h2sens,ds2sens] = ft_sensinterp_openmeeg(sourcemodel.pos(insideindx,:), headmodel, sens);

    % use pre-existing DSM if present
    if(~isempty(dsm))
      lf = ds2sens + h2sens*headmodel.mat*dsm;
    else
      lf = zeros(size(ds2sens)); % pre-allocate Msensors x Nvoxels

      for ii = 1:numchunks
        % select sourcemodel positions for this batch
        diprange = (((ii-1)*batchsize + 1):(min((ii)*batchsize,ndip)));
        % remap with 3 orientations per position
        diprangeori = [((ii-1)*3*batchsize + 1):(min((ii)*3*batchsize,3*ndip))];
        dsm = ft_sysmat_openmeeg(sourcemodel.pos(insideindx(diprange),:), headmodel, sens, nonadaptive);
        lf(:,diprangeori) = ds2sens(:,diprangeori) + h2sens*headmodel.mat*dsm;

        if istrue(keepdsm)
          % retain DSM in cfg if desired
          cfg.openmeeg.dsm = dsm;
        end

        dipindx = insideindx(diprange);
      end
    end
  catch
    me = lasterror;
    rethrow(me);
  end

  % apply montage, if applicable
  if isfield(sens, 'tra')
    lf = sens.tra * lf;
  end

  % lead field computation already done, but pass to ft_compute_leadfield so that
  % any post-computation options can be applied (e.g., normalization, etc.)
  lf = ft_compute_leadfield(sourcemodel.pos(diprange,:), sens, headmodel, 'lf', lf, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam, 'backproject', cfg.backproject);

  % reshape result into sourcemodel.leadfield cell-array
  for i=1:ndip
    sourcemodel.leadfield{insideindx(i)} = lf(:,3*(i-1) + [1:3]);
  end
  clear lf

elseif ft_headmodeltype(headmodel, 'singleshell')
  cfg.singleshell = ft_getopt(cfg, 'singleshell', []);
  batchsize       = ft_getopt(cfg.singleshell, 'batchsize', 1);
  if ischar(batchsize) && strcmp(batchsize, 'all')
    batchsize = length(insideindx);
  end

  dippos     = sourcemodel.pos(insideindx,:);
  ndip       = length(insideindx);
  numchunks  = ceil(ndip/batchsize);

  ft_progress('init', cfg.feedback, 'computing leadfield');
  for k = 1:numchunks
    ft_progress(k/numchunks, 'computing leadfield %d/%d\n', k, numchunks);
    diprange = (((k-1)*batchsize + 1):(min(k*batchsize,ndip)));
    tmp      = ft_compute_leadfield(dippos(diprange,:), sens, headmodel, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam, 'backproject', cfg.backproject);
    for i=1:length(diprange)
      thisindx = insideindx(diprange(i));
      if istrue(cfg.backproject)
        sourcemodel.leadfield{thisindx} = tmp(:,(i-1)*3+(1:3));
      else
        sourcemodel.leadfield{thisindx} = tmp(:,(i-1)*cfg.reducerank+(1:cfg.reducerank));
      end

      if isfield(cfg, 'sourcemodel') && isfield(cfg.sourcemodel, 'mom')
        % multiply with the normalized dipole moment to get the leadfield in the desired orientation
        sourcemodel.leadfield{thisindx} = sourcemodel.leadfield{thisindx} * sourcemodel.mom(:,thisindx);
      end
    end
  end
  ft_progress('close');

else
  ft_progress('init', cfg.feedback, 'computing leadfield');
  for i=1:length(insideindx)
    % compute the leadfield on all sourcemodel positions inside the brain
    ft_progress(i/length(insideindx), 'computing leadfield %d/%d\n', i, length(insideindx));
    thisindx = insideindx(i);
    sourcemodel.leadfield{thisindx} = ft_compute_leadfield(sourcemodel.pos(thisindx,:), sens, headmodel, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam, 'backproject', cfg.backproject);

    if isfield(cfg, 'sourcemodel') && isfield(cfg.sourcemodel, 'mom')
      % multiply with the normalized dipole moment to get the leadfield in the desired orientation
      sourcemodel.leadfield{thisindx} = sourcemodel.leadfield{thisindx} * sourcemodel.mom(:,thisindx);
    end
  end % for all sourcemodel locations inside the brain
  ft_progress('close');
end

% represent the leadfield for positions outside the brain as empty array
sourcemodel.leadfield(~sourcemodel.inside) = {[]};

% add the label of the channels
sourcemodel.label           = sens.label;
sourcemodel.leadfielddimord = '{pos}_chan_ori';

% mollify the leadfields
if ~strcmp(cfg.mollify, 'no')
  sourcemodel = mollify(cfg, sourcemodel);
end

% combine leadfields in patches and do an SVD on them
if ~strcmp(cfg.patchsvd, 'no')
  sourcemodel = patchsvd(cfg, sourcemodel);
end

% compute the 50 percent channel selection subspace projection
if ~strcmp(cfg.sel50p, 'no')
  sourcemodel = sel50p(cfg, sourcemodel, sens);
end

% compute the local basis function expansion (LBEX) subspace projection
if ~strcmp(cfg.lbex, 'no')
  sourcemodel = lbex(cfg, sourcemodel);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble trackconfig
ft_postamble previous   data
ft_postamble provenance sourcemodel
ft_postamble history    sourcemodel
ft_postamble savevar    sourcemodel


function [fboxes,cs] = transfer_friends(models, bboxes)
%transfer friends onto detections
error('deprecated function.. see VOCinit');

VOCinit;
% if isfield(models{i},'detect_add_flip') && models{i}.detect_add_flip == 1
%   models{i}.gt_box = flip_box(models{i}.gt_box,[recs.size.height ...
%                     recs.size.width]);
% end

hasfriends = cellfun(@(x)size(x.friendbb,1),models);
hasfriends = find(hasfriends);

fboxes = cell(1,length(bboxes));
cs = cell(1,length(bboxes));

for i = 1:length(bboxes)
  curb = bboxes{i};
  goods = ismember(curb(:,6),hasfriends);
  curb = curb(goods,:);

  fboxes{i} = zeros(0,7);
  cs{i} = cell(0,1);
  for j = 1:size(curb,1)
    exid = curb(j,6);
    curg = models{exid}.gt_box;
    others = models{exid}.friendbb;

    if isfield(models{exid},'detect_add_flip') && models{exid}.detect_add_flip == 1
      sizeI = models{exid}.sizeI;
      %curg = flip_box(curg,sizeI);
      others = flip_box(others,sizeI);
    end

    xform_cd = find_xform(curg, curb(j,:));

    %news = others;

    if isfield(models{exid},'detect_add_flip') && models{exid}.detect_add_flip == 1
      others = flip_box(others,sizeI);
    end

    news = apply_xform(others,xform_cd);

    %if isfield(models{exid},'detect_add_flip') && models{exid}.detect_add_flip == 1
    %  news = flip_box(news,sizeI);
    %end

    news(:,5) = j;
    news(:,6) = curb(j,6);
    news(:,7) = curb(j,end);
    fboxes{i} = [fboxes{i}; news];

    cs{i} = [cs{i} models{exid}.friendclass];

  end

end