%SurfPointFeature  SURF point corner feature object
%
% A subclass of OrientedScalePointFeature for SURF features.
%
% Methods::
% plot         Plot feature position
% plot_scale   Plot feature scale
% distance     Descriptor distance
% ncc          Descriptor similarity
% match        Match features
% uv           Return feature coordinate
% display      Display value
% char         Convert value to string
%
% Properties::
% u             horizontal coordinate
% v             vertical coordinate
% strength      feature strength
% scale         feature scale
% theta         feature orientation [rad]
% descriptor    feature descriptor (vector)
% image_id      index of image containing feature
%
% Properties of a vector of SurfCornerFeature objects are returned as a vector.
% If F is a vector (Nx1) of SurfCornerFeature objects then F.u is a 2xN matrix
% with each column the corresponding u coordinate.
%
% Notes::
% - SurfCornerFeature is a reference object.
% - SurfCornerFeature objects can be used in vectors and arrays
%
% Reference::
% "SURF: Speeded Up Robust Features",
% Herbert Bay, Andreas Ess, Tinne Tuytelaars, Luc Van Gool,
% Computer Vision and Image Understanding (CVIU),
% Vol. 110, No. 3, pp. 346--359, 2008
%
% See also ISURF, PointFeature, ScalePointFeature, OrientedScalePointFeature, SiftPointFeature.

classdef SurfPointFeature < OrientedScalePointFeature

    properties
        %image_id_
    end % properties

    methods
        function f = SurfPointFeature(varargin)
        %SurfPointFeature.SurfPointFeature Create a SURF point feature object
        %
        % F = SurfPointFeature() is a point feature object with null parameters.
        %
        % F = SurfPointFeature(U, V) is a point feature object with specified
        % coordinates.
        %
        % F = SurfPointFeature(U, V, STRENGTH) as above but with specified strength.
        %
        % F = SurfScalePointFeature(U, V, STRENGTH, SCALE) as above but with specified
        % feature scale.
        %
        % F = SurfPointFeature(U, V, STRENGTH, SCALE, THETA) as above but with specified
        % feature orientation.
        %
        % See also isurf, OrientedScalePointFeature.

            f = f@OrientedScalePointFeature(varargin{:});  % invoke the superclass constructor
        end

        function val = image_id(features)
            val = [features.image_id_];
        end

        function [m,corresp] = match(f1, f2, varargin)
        %SurfPointFeature.match Match SURF point features
        %
        % M = F.match(F2, OPTIONS) is a vector of FeatureMatch objects that
        % describe candidate matches between the two vectors of SURF
        % features F and F2.  Correspondence is based on descriptor
        % similarity.
        %
        % [M,C] = F.match(F2, OPTIONS) as above but returns a correspodence
        % matrix where each row contains the indices of corresponding features
        % in F and F2  respectively.
        %
        % Options::
        % 'thresh',T    Match threshold
        % 'top',N       Take strongest N matches
        %
        % Notes::
        % - to obtain all matches use 'top', Inf
        %
        % See also FeatureMatch.

            if isempty(f2)
                m = [];
                corresp = [];
                return;
            end

            % HACK, pg 463 requires median (default)
            opt.thresh = [];
            opt.top = [];
            opt.all = false;
            opt = tb_optparse(opt, varargin);

            % Put the landmark descriptors in a matrix
            D1 = f1.descriptor;
            D2 = f2.descriptor;

            % Find the best matches

%             err=zeros(1,length(f1));
%             cor1=1:length(f1);
%             cor2=zeros(1,length(f1));
%             for i=1:length(f1)
%                 distance = sum((D2-repmat(D1(:,i),[1 length(f2)])).^2,1);
%                 [err(i),cor2(i)] = min(distance);
%             end

            % vectorized code (much faster)
            cor1 = 1:length(f1);
            [cor2,err] = closest(D1, D2);
            err = err.^2;  % closest returns distance, old code used distance squared

            % Sort matches on vector distance
            [err, ind] = sort(err);
            cor1=cor1(ind);
            cor2=cor2(ind);

            % Build a list of FeatureMatch objects
            m = [];
            cor = [];
            for i=1:length(f1)
                k1 = cor1(i);
                k2 = cor2(i);
                mm = FeatureMatch(f1(k1), f2(k2), err(i));
                m = [m mm];
                cor(:,i) = [k1 k2]';
            end

            % find the strongest matches
            if ~opt.all
                if ~isempty(opt.top)
                    % take the N strongest

                    k = min(opt.top, numcols(cor));
                    cor(:,k+1:end) = [];
                    m(k+1:end) = [];
                else
                    % use a threshold

                    if isempty(opt.thresh)
                        % use median of errors if no threshold given
                        thresh = median(err);
                    else
                        thresh = opt.thresh;
                    end

                    k = err > thresh;
                    cor(:,k) = [];
                    m(k) = [];
                end
            end

            if nargout > 1
                corresp = cor;
            end
        end

    end % methods

    methods(Static)

        % the MEX functions live in a private subdirectory, so these static methods
        % provide convenient access to them

        function Ipts = surf(im, opt)

            if exist('detectSURFFeatures') && 0
                % Use the CVST version
                fprintf('Using CVST\n');

                % do some option translation
                points = detectSURFFeatures(im);

                if ~isinf(opt.nfeat)
                    % choose strongest
                    points = points.selectStrongest(opt.nfeat);
                end

                [features,valid_points] = extractFeatures(im, points);

                p = valid_points.Location;
                Ipts = struct(...
                    'x',           num2cell(p(:,1)), ...
                    'y',           num2cell(p(:,2)), ...
                    'scale',       num2cell(valid_points.Scale), ...
                    'strength',    num2cell(valid_points.Metric), ...
                    'orientation', num2cell(valid_points.Orientation), ...
                    'descriptor',  num2cell(features, 2)   )';

            elseif exist('OpenSurf')
                % Use OpenSurf (MATLAB)
                params.octaves = opt.octaves;   % for OpenSurf
                if ~isempty(opt.thresh)
                    params.tresh = opt.thresh;      % for OpenSurf, (sic)
                end

                Ipts = OpenSurf(im, params);

            if false
                % Use surfmex (mex OpenCV wrapper)

                %if exist('surfpoints') == 3
                fprintf('MEX\n');
                % do the OpenCV/MEX version
                % put the results into the same return format as OpenSurf
                params.extended = 0;
                params.nOctaves = opt.octaves;
                if ~isempty(opt.thresh)
                    params.hessianThreshold = opt.thresh;
                end

                try
                    [p,d,l,info] = surfpoints(iint(im), params);
                catch me
                    if strcmp(me.identifier, 'MATLAB:UndefinedFunction')
                        error('MVTB:SurfPointFeature:notinstalled', 'Contributed software for SURF features is not installed')
                    end
                end

                % returns
                % p    point coordinates, one per column
                % d    SURF descriptor, one per column
                % l    sign of the Laplacian (light or dark feature)
                % info other parameters, per row: scale, strength,
                %      orientation

                % put the data into a vector of structs format to
                % match OpenSurf
                Ipts = struct('x', num2cell(p(1,:)), ...
                              'y', num2cell(p(2,:)), ...
                              'scale', num2cell(info(1,:)), ...
                              'strength', num2cell(info(2,:)), ...
                              'orientation', num2cell(info(3,:)), ...
                              'descriptor', num2cell(d,1)   );

            end
        end
    end
    end
end % classdef


%*********************************************************
%% svec: compute the vector svec(M),
%%
%%   x = svec(blk,M,isspx);
%%
%% SDPT3: version 3.1
%% Copyright (c) 1997 by
%% K.C. Toh, M.J. Todd, R.H. Tutuncu
%% Last Modified: 16 Sep 2004
%%**********************************************************

  function x = svec(blk,M,isspx);

   if iscell(M)
      if (size(blk,1) ~= size(M,1))
         error('svec: number of rows in blk and M not equal');
      end
      if (nargin == 2)
         %%if (size(M,2) == 1)
         %%   isspx = zeros(size(blk,1),1);
         %%else
         %%   isspx = ones(size(blk,1),1);
         %%end
         isspx = ones(size(blk,1),1);
      else
         if (length(isspx) < size(blk,1))
            isspx = ones(size(blk,1),1);
         end
      end
      x = cell(size(blk,1),1);
      for p=1:size(blk,1)
         pblk = blk(p,:);
         n = sum(pblk{2});  m = size(M,2);
         if strcmp(pblk{1},'s')
            n2 = sum(pblk{2}.*(pblk{2}+1))/2;
            if (isspx(p));
               x{p} = sparse(n2,m);
            else
               x{p} = zeros(n2,m);
            end
            numblk = length(pblk{2});
            if (pblk{2} > 0)
               for k = 1:m
                  if (numblk > 1) & ~issparse(M{p,k});
                     x{p}(:,k) = mexsvec(pblk,sparse(M{p,k}),isspx(p));
                  else
                     x{p}(:,k) = mexsvec(pblk,M{p,k},isspx(p));
                  end
               end
            end
	 else
            if (isspx(p))
               x{p} = sparse(n,m);
            else
               x{p} = zeros(n,m);
            end
            for k = 1:m
                x{p}(:,k) = M{p,k};
            end
         end
      end
   else
      if strcmp(blk{1},'s')
         numblk = length(blk{2});
         if (numblk > 1) & ~issparse(M);
            x = mexsvec(blk,sparse(M),1);
         else
            x = mexsvec(blk,sparse(M));
         end
      else
         x = M;
      end
   end
%%**********************************************************


function [] = main(training_file, test_file, layers, units_per_layer, rounds)
    obj = neural_network(training_file, test_file, layers, units_per_layer, rounds);
    obj = obj.initialise(obj);
    for i = 1:obj.rounds
        obj = obj.feed_forward(obj, i-1);
    end
    obj = obj.testing(obj);
end


function audio = mergeAudio(data, window)

pad = [];
if window(1) <= 1
    pad=zeros(abs(window(1)), 1);
    window(1) = 1;
end
if isstring(data) |ischar(data)
    audio = audioread(data, window);
else
   audio = data;
end
audio = [mean(audio - mean(audio,1) ,2)]; % Take the mean of the audio channels
audio = int16(audio * 32767); % Convert to int16


function [pX,gX,pY,gY,X,Y,U] = get_MCMC_predictiveDensity_fb(f_fname,g_fname,u,n_t,options,dim,fb,N,np,lx,ly)

% legacy code
s = warning ('on');
warning ('*** The function `get_MCMC_predictiveDensity_fb` is now deprecated. Please use `VBA_MCMC_predictiveDensity_fb` instead (same syntax).')
warning (s);

% fallback
switch nargin
    case 11
        [pX,gX,pY,gY,X,Y] = VBA_MCMC_predictiveDensity_fb(f_fname,g_fname,u,n_t,options,dim,fb,N,np,lx,ly);
    case 10
        [pX,gX,pY,gY,X,Y] = VBA_MCMC_predictiveDensity_fb(f_fname,g_fname,u,n_t,options,dim,fb,N,np,lx);
    case 9
        [pX,gX,pY,gY,X,Y] = VBA_MCMC_predictiveDensity_fb(f_fname,g_fname,u,n_t,options,dim,fb,N,np);
    case 8
        [pX,gX,pY,gY,X,Y] = VBA_MCMC_predictiveDensity_fb(f_fname,g_fname,u,n_t,options,dim,fb,N);
    case 7
        [pX,gX,pY,gY,X,Y] = VBA_MCMC_predictiveDensity_fb(f_fname,g_fname,u,n_t,options,dim,fb);
    otherwise
        error('VBA_MCMC_predictiveDensity_fb: wrong number of arguments');
end


function main()
    addpath('../include/vlfeat-0.9.20/toolbox/');
    vl_setup();
    addpath(genpath('../include/eccv14text'));
    %data_infos
    data_infos.img_path = '../data/msra_torch/im/';
    data_infos.map_path = '../data/msra_torch/multiscale/';
    data_infos.res_path = '../data/msra_torch/proposal_res/';

    %global variable
    global globalVar

    %gen_proposals param
    global param
    param.workPath = '/Users/zhangzheng/Documents/FCN_FULL/genProposal';
    param.reuse_mser = false;
    param.debug = false;
    param.minRegionProb = 0.2;
    param.minRegionCompCoveredArea = 0.7;
    param.maxRegionCompArea = 1;
    param.secondaryMinRegionCompArea = 0.5;
    param.orient_param.orientationInterval = 2;
    param.orient_param.minOrientation = -90;
    param.orient_param.maxOrientation = 90;
    param.minCompHeightSimilarity = 0.7;
    param.maxCompOrientationDiff = 3;
    param.minIoUDiff = 0.85;
    param.maxDistRatio = 2;
    %minmaxRFs = [137, 68, 5];
    minmaxRFs = [137, 32, 5];

    %% Only used for normal_mser3
    param.mser_info.delta = 1;
    param.mser_info.minArea = 0.002;
    param.mser_info.maxArea = 1;
    param.mser_info.minDiversity = 0.8;
    param.mser_info.maxVariation = 0.15;

    mkdir(data_infos.res_path);

    extension = '.jpg';
    imgData = dir([data_infos.img_path,'*.jpg']);% original image.
    if(length(imgData) == 0)
       imgData = dir([data_infos.img_path,'*.JPG']);% original image.
       extension = '.JPG';
    end
    nImg = length(imgData);
    for ii= 1:nImg
        disp(ii);
        [~, name, ~] = fileparts(imgData(ii).name);
%         if(~strcmp(name, 'img_11'))
%             continue;
%         end

        globalVar.imgName = name;
        img_path = [data_infos.img_path, imgData(ii).name];%the original image.

        proposalsSavePath = [data_infos.res_path, name, '.txt'];
        nMap = 3;
        proposals = cell(nMap, 1);
        for jj = 1 : nMap
            map_path = [data_infos.map_path, name, '_', num2str(jj), extension];%the res image from last phase.
            img = imread(img_path);
            map = imread(map_path);
            map = double(map) / 255;

            [map_h,map_w,~]=size(map);
            resizeRatio = size(img, 1) / map_h;
            img = imresize(img, [map_h, map_w], 'bilinear');

            proposals_tmp = gen_proposals(img, map, resizeRatio, minmaxRFs(jj));
            if(isempty(proposals_tmp) == false)
                proposals_tmp(:, 1 : 8) = proposals_tmp(:, 1 : 8) * resizeRatio;
            end
            proposals{jj} = proposals_tmp;

            if(false)
                imshow(img);
                hold on;
                for nProposal = 1 : size(proposals_tmp, 1)
                    x_arr = proposals_tmp(nProposal, 1 : 2 : 8);
                    y_arr = proposals_tmp(nProposal, 2 : 2 : 8);
                    plot([x_arr, x_arr(1)], [y_arr, y_arr(1)], 'color', rand(3,1));
                end
                hold off;
                saveas(gcf, [data_infos.res_path, name, '_', num2str(jj), '.jpg'], 'jpg');
            end
        end
        proposals = cell2mat(proposals);

        if(true)
            img = imread(img_path);
            imshow(img);
            hold on;
            for nProposal = 1 : size(proposals, 1)
                x_arr = proposals(nProposal, 1 : 2 : 8);
                y_arr = proposals(nProposal, 2 : 2 : 8);
                plot([x_arr, x_arr(1)], [y_arr, y_arr(1)], 'color', rand(3,1));
            end
            hold off;
            saveas(gcf, [data_infos.res_path, name, '_all.jpg'], 'jpg');
        end

        %% to adapter old code
        proposalsToSave = zeros(10, size(proposals, 1));
        if(size(proposalsToSave, 2) ~= 0)
            proposalsToSave(1 : 8, :) = round(proposals(:, 1 : 8))';
            proposalsToSave(10, :) = proposals(:, 9);
        end
        fid = fopen(proposalsSavePath, 'w');
        fprintf(fid, '%d %d %d %d %d %d %d %d %d %f\n', proposalsToSave);
        fclose(fid);
    end
end


function headmodel = ft_headmodel_fns(seg, varargin)

% FT_HEADMODEL_FNS creates the volume conduction structure to be used
% in the FNS forward solver.
%
% Use as
%   headmodel = ft_headmodel_fns(seg, ...)
%
% Optional input arguments should be specified in key-value pairs and
% can include
%   tissuecond       = matrix C [9XN tissue types]; where N is the number of
%                      tissues and a 3x3 tensor conductivity matrix is stored
%                      in each column.
%   tissue           = see fns_contable_write
%   tissueval        = match tissues of segmentation input
%   transform        = 4x4 transformation matrix (default eye(4))
%   sens             = sensor information (for which ft_datatype(sens,'sens')==1)
%   deepelec         = used in the case of deep voxel solution
%   tolerance        = scalar (default 1e-8)
%
% Standard default values for conductivity matrix C are derived from
% Saleheen HI, Ng KT. New finite difference formulations for general
% inhomogeneous anisotropic bioelectric problems. IEEE Trans Biomed Eng.
% 1997
%
% Additional documentation available at:
% http://hunghienvn.nmsu.edu/wiki/index.php/FNS
%
% See also FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD

% Copyright (C) 2011, Cristiano Micheli and Hung Dang
%
% 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$

ft_hastoolbox('fns', 1);

% get the optional arguments
tissue       = ft_getopt(varargin, 'tissue', []);
tissueval    = ft_getopt(varargin, 'tissueval', []);
tissuecond   = ft_getopt(varargin, 'tissuecond', []);
transform    = ft_getopt(varargin, 'transform', eye(4));
unit         = ft_getopt(varargin, 'unit', 'mm');
sens         = ft_getopt(varargin, 'sens', []);
deepelec     = ft_getopt(varargin, 'deepelec', []); % used in the case of deep voxel solution
tolerance    = ft_getopt(varargin, 'tolerance', 1e-8);

if isempty(sens)
  ft_error('A set of sensors is required')
end

if ispc
  ft_error('FNS only works on Linux and OS X')
end

% check the consistency between tissue values and the segmentation
vecval = ismember(tissueval,unique(seg(:)));
if any(vecval)==0
  ft_warning('Some of the tissue values are not in the segmentation')
end

% create the files to be written
try
  tmpfolder = pwd;

  cd(tempdir)
  [tmp,tname] = fileparts(tempname);
  segfile   = [tname];
  [tmp,tname] = fileparts(tempname);
  confile   = [tname '.csv'];
  [tmp,tname] = fileparts(tempname);
  elecfile = [tname '.h5'];
  [tmp,tname] = fileparts(tempname);
  exefile   = [tname '.sh'];
  [tmp,tname] = fileparts(tempname);
  datafile  = [tname '.h5'];

  % this requires the fieldtrip/fileio toolbox
  ft_hastoolbox('fileio', 1);

  % create a fake mri structure and write the segmentation on disk
  disp('writing the segmentation file...')
  mri = [];
  mri.dim = size(seg);
  mri.transform = eye(4);
  mri.seg = uint8(seg);

  cfg = [];
  cfg.datatype = 'uint8';
  cfg.coordsys  = 'ctf';
  cfg.parameter = 'seg';
  cfg.filename  = segfile;
  cfg.filetype  = 'analyze';
  ft_volumewrite(cfg, mri);

  % write the cond matrix on disk, load the default cond matrix in case not specified
  disp('writing the conductivity file...')
  condmatrix = fns_contable_write('tissue',tissue,'tissueval',tissueval,'tissuecond',tissuecond);
  csvwrite(confile,condmatrix);

  % write the positions of the electrodes on disk
  disp('writing the electrodes file...')
  pos = ft_warp_apply(inv(transform),sens.elecpos); % in voxel coordinates!

  % convert pos into int32 datatype.
  hdf5write(elecfile, '/electrodes/gridlocs', int32(pos));

  % Exe file
  efid = fopen(exefile, 'w');
  if ~ispc
    fprintf(efid,'#!/usr/bin/env bash\n');
    fprintf(efid,['elecsfwd1 -img ' segfile ' -electrodes ./' elecfile ' -data ./', ...
      datafile ' -contable ./' confile ' -TOL ' num2str(tolerance) ' \n']); %2>&1 > /dev/null
  end
  fclose(efid);

  % run the shell instructions
  dos(sprintf('chmod +x %s', exefile));
  dos(['./' exefile]);

  % FIXME: find a cleverer way to store the huge transfer matrix (vista?)
  [transfer,status] = fns_read_transfer(datafile);

  cleaner(segfile,confile,elecfile,exefile,datafile)

catch ME
  disp('The transfer matrix was not written')
  cleaner(segfile,confile,elecfile,exefile,datafile)
  cd(tmpfolder)
  rethrow(ME)
end

% start with an empty volume conductor
headmodel = [];
headmodel.tissue     = tissue;
headmodel.tissueval  = tissueval;
headmodel.transform  = transform;
headmodel.unit       = unit;
headmodel.segdim     = size(seg);
headmodel.type       = 'fns';
headmodel.transfer   = transfer;

if ~isempty(deepelec)
  headmodel.deepelec  = deepelec;
end

function cleaner(segfile,confile,elecfile,exefile,datafile)
delete([segfile '.hdr']);
delete([segfile '.img']);
delete(confile);
delete(elecfile);
delete(exefile);
delete(datafile);


function trl = sampleinfo2trl(data)

% SAMPLEINFO2TRL constructs the trial definition from the sampleinfo, the time axes
% and optionally from the trialinfo
%
% Use as
%   trl = sampleinfo2trl(data)
%
% See also ARTIFACT2BOOLVEC, ARTIFACT2EVENT, ARTIFACT2TRL, BOOLVEC2ARTIFACT, BOOLVEC2EVENT, BOOLVEC2TRL, EVENT2ARTIFACT, EVENT2BOOLVEC, EVENT2TRL, TRL2ARTIFACT, TRL2BOOLVEC, TRL2EVENT

% get the begin and end sample of each trial
begsample = data.sampleinfo(:,1);
endsample = data.sampleinfo(:,2);

% recreate the offset
offset = zeros(numel(data.trial), 1);
for i=1:numel(data.trial)
  offset(i) = time2offset(data.time{i}, data.fsample);
end

if isfield(data, 'trialinfo') && istable(data.trialinfo)
  trl = table(begsample, endsample, offset);
  trl = horzcat(trl, data.trialinfo);
elseif isfield(data, 'trialinfo') && isnumeric(data.trialinfo)
  trl = [begsample endsample offset data.trialinfo];
else
  trl = [begsample endsample offset];
end


function model = presolve_bounds_from_domains(model);

% Sigmonial with non-integer powers must be positive
sigmonials = find((model.variabletype == 4));
for i = 1:length(sigmonials)
    j = sigmonials(i);
    involved = find(model.monomtable(j,:));
    fractional = involved(model.monomtable(j,involved)~=fix(model.monomtable(j,involved)));
    for k = 1:length(fractional)
        model.lb(fractional(k)) = max([1e-9  model.lb(fractional(k))]);
    end
end

% The evaluation based operators can communicate the domain (although
% this really should be available via domain constraints anyway)
% The operator model can however also include range constraints, which we
% just as well might extract and add to the model
for i = 1:length(model.evalVariables)
    j = model.evalVariables(i);
    model.lb(j) = max([model.lb(j) model.evalMap{i}.properties.range(1)]);
    model.ub(j) = min([model.ub(j) model.evalMap{i}.properties.range(2)]);
    j = model.evalMap{i}.variableIndex;
    model.lb(j) = max([model.lb(j) repmat(model.evalMap{i}.properties.domain(1),length(j),1)],[],2);
    model.ub(j) = min([model.ub(j) repmat(model.evalMap{i}.properties.domain(2),length(j),1)],[],2);
end


function [HDR]=scpopen(arg1,CHAN,arg4,arg5,arg6)
% SCPOPEN reads and writes SCP-ECG files
%
% SCPOPEN is an auxillary function to SOPEN for
% opening of SCP-ECG files for reading ECG waveform data
%
% Use SOPEN instead of SCPOPEN
%
% See also: fopen, SOPEN,

%	$Id: scpopen.m 2205 2009-10-27 12:18:15Z schloegl $
%	(C) 2004,2006,2007,2008 by Alois Schloegl <a.schloegl@ieee.org>
%    	This is part of the BioSig-toolbox http://biosig.sf.net/
%
%    BioSig 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.

if nargin<2, CHAN=0; end;

if isstruct(arg1)
        HDR=arg1;
        FILENAME=HDR.FileName;
elseif ischar(arg1);
        HDR.FileName=arg1;
        fprintf(2,'Warning SCPOPEN: the use of SCPOPEN is discouraged; please use SOPEN instead.\n');
end;

VER = version;

fid = fopen(HDR.FileName,HDR.FILE.PERMISSION,'ieee-le');
HDR.FILE.FID = fid;
if ~isempty(findstr(HDR.FILE.PERMISSION,'r')),		%%%%% READ
	tmpbytes = fread(fid,inf,'uchar');
        tmpcrc   = crc16eval(tmpbytes(3:end));
	fseek(fid, 0, 'bof');
        HDR.FILE.CRC = fread(fid,1,'uint16');
	if (HDR.FILE.CRC ~= tmpcrc);
		fprintf(HDR.FILE.stderr,'Warning: CRC check failed (%x vs %x)\n',tmpcrc,HDR.FILE.CRC);
        end;

        HDR.FILE.Length = fread(fid,1,'uint32');
        if HDR.FILE.Length~=HDR.FILE.size,
                fprintf(HDR.FILE.stderr,'Warning SCPOPEN: header information contains incorrect file size %i %i \n',HDR.FILE.Length,HDR.FILE.size);
        end;
	HDR.data = [];

        DHT = [0,1,-1,2,-2,3,-3,4,-4,5,-5,6,-6,7,-7,8,-8,9,-9;0,1,5,3,11,7,23,15,47,31,95,63,191,127,383,255,767,511,1023]';
        prefix  = [1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,10,10];
	PrefixLength = prefix;

        %PREFIX = [0,4,5,12,13,28,29,60,61,124,125,252,253,508,509,1020,1021,1022,1023];
        PREFIX  = [0,4,5,12,13,28,29,60,61,124,125,252,253,508,509,1020,1021,1022,1023]'.*2.^[32-prefix]';
        codelength = [1,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,18,26];
        mask    = [1,7,7,15,15,31,31,63,63,127,127,255,255,511,511,1023,1023,1023,1023]'.*2.^[32-prefix]';
        %MASK    = dec2bin(mask);
        %mask   = [1,7,7,15,15,31,31,63,63,127,127,255,255,511,511,1023,1023,1023,1023]';

        mask2    = [1,7,7,15,15,31,31,63,63,127,127,255,255,511,511,1023,1023,1023,1023]';
        PREFIX2  = DHT(:,2);

	HT19999 = [prefix',codelength',ones(length(prefix),1),DHT];
	HT = [prefix',codelength',ones(length(prefix),1),DHT];

        dd = [0:255]';
        ACC = zeros(size(dd));
        c = 0;
        for k2 = 1:8,
                ACC = ACC + (dd>127).*(2^c);
                dd  = mod(dd*2, 256);
                c   = c + 1;
        end;

        section.CRC     = fread(fid,1,'uint16');
        section.ID      = fread(fid,1,'uint16');
        section.Length  = fread(fid,1,'uint32');
        section.Version = fread(fid,[1,2],'uint8');
        section.tmp     = fread(fid,[1,6],'uint8');

        NSections = min(11,(section.Length-16)/10);
        for k = 1:NSections,
                HDR.Block(k).id = k;
                HDR.Block(k).length = 0;
                HDR.Block(k).startpos = -1;
        end;
	for K = 1:NSections,
                k = fread(fid,1,'uint16');
		len = fread(fid,1,'uint32');
		pos = fread(fid,1,'uint32');
		if ((k > 0) && (k < NSections))
	                HDR.Block(k).id = k;
        	        HDR.Block(k).length = len;
                	HDR.Block(k).startpos = pos-1;

%% [HDR.Block(k).id ,length(tmpbytes), HDR.Block(k).length, HDR.Block(k).length+HDR.Block(k).startpos]
		%% FIXME: instead of min(...,FileSize) a warning or error message should be reported
	                tmpcrc = crc16eval(tmpbytes(HDR.Block(k).startpos+3:min(HDR.Block(k).startpos+HDR.Block(k).length,HDR.FILE.size)));

	                if (HDR.Block(k).length>0)
        	        if (tmpcrc~=(tmpbytes(HDR.Block(k).startpos+(1:2))'*[1;256]))
                		fprintf(HDR.FILE.stderr,'Warning SCPOPEN: faulty CRC %04x in section %i\n',tmpcrc,k-1);
	                end;
        	        end;
		end;
        end;

%%[[HDR.Block.id];[HDR.Block.length];[HDR.Block.startpos]]'

	% default values - in case Section 6 is missing
	HDR.NS = 0; HDR.SPR = 0; HDR.NRec = 0; HDR.Calib = zeros(1,0);
        secList = find([HDR.Block.length]);
        for K = secList(1:end),
                if fseek(fid,HDR.Block(K).startpos,'bof');
                        fprintf(HDR.FILE.stderr,'Warning SCPOPEN: section %i not available, although it is listed in Section 0\n',secList(K+1));
                end;
                section.CRC     = fread(fid,1,'uint16');
                section.ID      = fread(fid,1,'uint16');
                section.Length  = fread(fid,1,'uint32');
                section.Version = fread(fid,[1,2],'uint8');
                section.tmp     = fread(fid,[1,6],'uint8');

                HDR.SCP.Section{find(K==secList)} = section;
                if (section.Length==0),
                elseif section.ID==0,
                        NSections = (section.Length-16)/10;
                        for k = 1:NSections,
                                HDR.Block(k).id = fread(fid,1,'uint16');
                                HDR.Block(k).length = fread(fid,1,'uint32');
                                HDR.Block(k).startpos = fread(fid,1,'uint32')-1;
                        end;

                elseif section.ID==1,
                        tag = 0;
                        k1  = 0;
                        Sect1Len = section.Length-16;
                        ListOfRequiredTags = [2,14,25,26];
                        ListOfRecommendedTags = [0,1,5,8,15,34];
                        while (tag~=255) & (Sect1Len>2),
                                tag = fread(fid,1,'uint8');
                                len = fread(fid,1,'uint16');
                                Sect1Len = Sect1Len - 3 - len;
%% [tag,len,Sect1Len],          %% DEBUGGING information
                                field = fread(fid,[1,len],'uchar');
                                if tag == 0,
                                        ListOfRecommendedTags(ListOfRecommendedTags==tag)=[];
                                        HDR.Patient.Name = char(field);  %% LastName
                                elseif tag == 1,
                                        ListOfRecommendedTags(ListOfRecommendedTags==tag)=[];
                                        HDR.Patient.FirstName = char(field);
                                elseif tag == 2,
                                        ListOfRequiredTags(find(ListOfRequiredTags==2))=[];
                                        HDR.Patient.Id = char(field);
                                elseif tag == 3,
                                        HDR.Patient.LastName2 = char(field);
                                elseif tag == 4,
                                        HDR.Patient.Age = field(1:2)*[1;256];
                                        tmp = field(3);
                                        if     tmp==1, HDR.Patient.Age = HDR.Patient.Age; % unit='Y';
                                        elseif tmp==2, HDR.Patient.Age = HDR.Patient.Age/12; % unit='M';
                                        elseif tmp==3, HDR.Patient.Age = HDR.Patient.Age/52; % unit='W';
                                        elseif tmp==4, HDR.Patient.Age = HDR.Patient.Age/365.25; % unit='d';
                                        elseif tmp==5, HDR.Patient.Age = HDR.Patient.Age/(365.25*24); %unit='h';
                                        else warning('units of age not specified');
                                        end;
                                elseif (tag == 5)
                                        ListOfRecommendedTags(ListOfRecommendedTags==tag)=[];
                                	if any(field(1:4)~=0)
                                        	HDR.Patient.Birthday = [field(1:2)*[1;256],field(3:4),12,0,0];
                                        end;
                                elseif (tag == 6)
                                	if any(field(1:3)),
                                        HDR.Patient.Height = field(1:2)*[1;256];
                                        tmp = field(3);
                                        if tmp==1, % unit='cm';
                                        elseif tmp==2, HDR.Patient.Height = HDR.Patient.Height*2.54; %unit='inches';
                                        elseif tmp==3, HDR.Patient.Height = HDR.Patient.Height*0.1; %unit='mm';
                                        else warning('units of height not specified');
                                        end;
                                        end;
                                elseif (tag == 7)
                                	if any(field(1:3)),
                                        HDR.Patient.Weight = field(1:2)*[1;256];
                                        tmp = field(3);
                                        if tmp==1, % unit='kg';
                                        elseif tmp==2, HDR.Patient.Weight = HDR.Patient.Weight/1000; %unit='g';
                                        elseif tmp==3, HDR.Patient.Weight = HDR.Patient.Weight/2.2; %unit='pound';
                                        elseif tmp==4, HDR.Patient.Weight = HDR.Patient.Weight*0.0284; %unit='ounce';
                                        else warning('units of weight not specified');
                                        end;
                                        end;
                                elseif tag == 8,
                                        ListOfRecommendedTags(ListOfRecommendedTags==tag)=[];
                                        HDR.Patient.Sex = field;
                                elseif tag == 9,
                                        HDR.Patient.Race = field;
                                elseif tag == 10,
					if (field(1)~=0)
	                                        HDR.Patient.Medication = field;
					else
	                                        HDR.Patient.Medication.Code = field(2:3);
						HDR.Patient.Medication = field(4:end);
					end;
                                elseif tag == 11,
                                        HDR.Patient.BloodPressure.Systolic = field*[1;256];
                                elseif tag == 12,
                                        HDR.Patient.BloodPressure.Diastolic = field*[1;256];
                                elseif tag == 13,
                                        HDR.Patient.Diagnosis = char(field);
                                elseif tag == 14,
                                        ListOfRequiredTags(ListOfRequiredTags==tag)=[];
                                        HDR.SCP1.AcquiringDeviceID = char(field);
                                        HDR.VERSION = field(15)/10;
                                elseif tag == 15,
                                        ListOfRecommendedTags(ListOfRecommendedTags==tag)=[];
                                        HDR.SCP1.AnalyisingDeviceID = char(field);
                                elseif tag == 16,
                                        HDR.SCP1.AcquiringInstitution = char(field);
                                elseif tag == 17,
                                        HDR.SCP1.AnalyzingInstitution = char(field);
                                elseif tag == 18,
                                        HDR.SCP1.AcquiringDepartment = char(field);
                                elseif tag == 19,
                                        HDR.SCP1.AnalyisingDepartment = char(field);
                                elseif tag == 20,
                                        HDR.SCP1.Physician = char(field);
                                elseif tag == 21,
                                        HDR.SCP1.LatestComfirmingPhysician = char(field);
                                elseif tag == 22,
                                        HDR.SCP1.Technician = char(field);
                                elseif tag == 23,
                                        HDR.SCP1.Room = char(field);
                                elseif tag == 24,
                                        HDR.SCP1.Emergency = field;
                                elseif tag == 25,
                                        ListOfRequiredTags(ListOfRequiredTags==tag)=[];
                                        HDR.T0(1,1:3) = [field(1:2)*[1;256],field(3:4)];
                                elseif tag == 26,
                                        ListOfRequiredTags(ListOfRequiredTags==tag)=[];
                                        HDR.T0(1,4:6) = field(1:3);
                                elseif tag == 27,
                                        HDR.Filter.HighPass = field(1:2)*[1;256]/100;
                                elseif tag == 28,
                                        HDR.Filter.LowPass = field(1:2)*[1;256]/100;
                                elseif tag == 29,
                                        if (field==0)
                                                HDR.FILTER.Notch = NaN;
                                        elseif bitand(field,1)
                                                HDR.FILTER.Notch = 60; % 60Hz Notch
                                        elseif bitand(field,2)
                                                HDR.FILTER.Notch = 50; % 50Hz Notch
                                        elseif bitand(field,3)==0
                                                HDR.FILTER.Notch = -1; % Notch Off
                                        end;
                                        HDR.SCP1.Filter.BitMap = field;
                                elseif tag == 30,
                                        HDR.SCP1.FreeText = char(field);
                                elseif tag == 31,
                                        HDR.SCP1.ECGSequenceNumber = char(field);
                                elseif tag == 32,
                                        HDR.SCP1.MedicalHistoryCodes = char(field);
                                elseif tag == 33,
                                        HDR.SCP1.ElectrodeConfigurationCodes = field;
                                elseif tag == 34,
                                        ListOfRecommendedTags(ListOfRecommendedTags==tag)=[];
                                        HDR.SCP1.Timezone = field;
                                elseif tag == 35,
                                        HDR.SCP1.MedicalHistory = char(field);
                                elseif tag == 255,
                                        % section terminator
                                elseif tag >= 200,
                                	% manufacturer specific - not standardized
                                else
                                        fprintf(HDR.FILE.stderr,'Warning SCOPEN: unknown tag %i (section 1)\n',tag);
                                end;
                        end;
                        if ~isempty(ListOfRequiredTags)
                                fprintf(HDR.FILE.stderr,'Warning SCPOPEN: the following tags are required but missing in file %s\n',HDR.FileName);
                                disp(ListOfRequiredTags);
                        end;
                        if ~isempty(ListOfRecommendedTags)
                                fprintf(HDR.FILE.stderr,'Warning SCPOPEN: the following tags are recommended but missing in file %s\n',HDR.FileName);
                                disp(ListOfRecommendedTags);
                        end;

                elseif section.ID==2, 	% Huffman tables
                        HDR.SCP2.NHT = fread(fid,1,'uint16');
                        HDR.SCP2.NCT = fread(fid,1,'uint16');
			if HDR.SCP2.NHT~=19999,
				NHT = HDR.SCP2.NHT;
			else
				NHT = 0;
			end;
                        k3 = 0;
                        for k1 = 1:NHT,
                        	HT1 = zeros(HDR.SCP2.NCT,5);
                                for k2 = 1:HDR.SCP2.NCT,
                                	tmp = fread(fid,3,'uint8') ;
                                        HDR.SCP2.prefix = tmp(1);	% PrefixLength
                                        HDR.SCP2.codelength = tmp(2);	% CodeLength
                                        HDR.SCP2.TableModeSwitch = tmp(3);
                                        tmp(4) = fread(fid,1,'int16');  % BaseValue
                                        tmp(5) = fread(fid,1,'uint32'); % BaseCode
                                	k3 = k3   + 1;
				        HT (k3,:) = [tmp'];
				        HT1(k2,:) = [tmp'];
                                end;
                                HDR.SCP2.HTree{k1} = makeTree(HT1);
                                HDR.SCP2.HTs{k1} = HT1;
			end;
			if HDR.SCP2.NHT~=19999,
				HDR.SCP2.HT = HT;
			else
				tmp = size(HT19999,1);
				HDR.SCP2.HT = [ones(tmp,1),[1:tmp]',HT19999];
                                HDR.SCP2.HTree{1} = makeTree(HT19999);
                                HDR.SCP2.HTs{1} = HT19999;
			end;

                elseif section.ID==3,
                        HDR.NS = fread(fid,1,'uint8');
                        HDR.FLAG.Byte = fread(fid,1,'uint8');
                        if ~bitand(HDR.FLAG.Byte,4)
                                fprintf(HDR.FILE.stdout,'Warning SCPOPEN: not all leads simultaneously recorded - this mode is not supported.\n');
                        end;

                        HDR.FLAG.ReferenceBeat = mod(HDR.FLAG.Byte,2);
                        %HDR.NS = floor(mod(HDR.FLAG.Byte,128)/8);
                        for k = 1:HDR.NS,
                                HDR.LeadPos(k,1:2) = fread(fid,[1,2],'uint32');
                                HDR.LeadIdCode(k,1) = fread(fid,1,'uint8');
                        end;
                        HDR.N = max(HDR.LeadPos(:))-min(HDR.LeadPos(:))+1;
                        HDR.AS.SPR = HDR.LeadPos(:,2)-HDR.LeadPos(:,1)+1;
                        HDR.SPR = HDR.AS.SPR(1);
			for k = 2:HDR.NS
				HDR.SPR = lcm(HDR.SPR,HDR.AS.SPR(k));
			end;

                        HDR = leadidcodexyz(HDR);
                        for k = 1:HDR.NS,
                                if 0,
                                elseif (HDR.LeadIdCode(k)==0),
                                        HDR.Label{k} = 'unspecified lead';
                                elseif (HDR.VERSION <= 1.3) & (HDR.LeadIdCode(k) < 86),
                                %        HDR.Label{k} = H.Label(H.LeadIdCode==HDR.LeadIdCode(k));
                                elseif (HDR.VERSION <= 1.3) & (HDR.LeadIdCode(k) > 99),
                                        HDR.Label{k} = 'manufacturer specific';
                                elseif (HDR.VERSION >= 2.0) & (HDR.LeadIdCode(k) < 151),
                                %        HDR.Label{k} = H.Label(H.LeadIdCode==HDR.LeadIdCode(k));
                                elseif (HDR.VERSION >= 2.0) & (HDR.LeadIdCode(k) > 199),
                                        HDR.Label{k} = 'manufacturer specific';
                                else
                                        HDR.Label{k} = 'reserved';
                                end;
                        end;
                        HDR.Label = strvcat(HDR.Label);

                elseif section.ID==4,
                        HDR.SCP4.L = fread(fid,1,'int16');
                        HDR.SCP4.fc0 = fread(fid,1,'int16');
                        HDR.SCP4.N = fread(fid,1,'int16');
                        HDR.SCP4.type = fread(fid,[7,HDR.SCP4.N],'uint16')'*[1,0,0,0; 0,1,0,0;0,2^16,0,0; 0,0,1,0;0,0,2^16,0; 0,0,0,1;0,0,0,2^16];

                        tmp = fread(fid,[2*HDR.SCP4.N],'uint32');
                        HDR.SCP4.PA = reshape(tmp,2,HDR.SCP4.N)';
                        HDR.SCP4.pa = [0;tmp;HDR.N];

                elseif any(section.ID==[5,6]),

                        SCP = [];
                        SCP.Cal = fread(fid,1,'int16')/1e6;    % quant in nV, converted into mV
                        SCP.PhysDim = 'mV';
                        SCP.Dur = fread(fid,1,'int16');
                        SCP.SampleRate = 1e6/SCP.Dur;
                        SCP.FLAG.DIFF  = fread(fid,1,'uint8');
                        SCP.FLAG.bimodal_compression = fread(fid,1,'uint8');

                        if isnan(HDR.NS),
				HDR.ERROR.status = -1;
				HDR.ERROR.message = sprintf('Error SCPOPEN: could not read %s\n',HDR.FileName);
				fprintf(HDR.FILE.stderr,'Error SCPOPEN: could not read %s\n',HDR.FileName);
				return;
			end;

			if CHAN==0, CHAN = 1:HDR.NS; end;
                        SCP.SPR = fread(fid,HDR.NS,'uint16');
			HDR.InChanSelect = CHAN;

                        if section.ID==6,
                                HDR.HeadLen = ftell(fid);
                                HDR.FLAG.DIFF = SCP.FLAG.DIFF;
                                HDR.FLAG.bimodal_compression = SCP.FLAG.bimodal_compression;
                                HDR.data = [];
                                outlen = HDR.SPR;
			        HDR.Calib = sparse(2:HDR.NS+1, 1:HDR.NS, SCP.Cal);
                        elseif isfield(HDR,'SCP4') %% HACK: do no know whether it is correct
                        	outlen = floor(1000*HDR.SCP4.L/SCP.Dur);
                        else
                        	outlen = inf;
                        end;

                        if ~isfield(HDR,'SCP2'),
                                if any(SCP.SPR(1)~=SCP.SPR),
                                        error('SCPOPEN: SPR do not fit');
                                else
                                        S2 = fread(fid,[SCP.SPR(1)/2,HDR.NS],'int16');
                                end;
				%S2 = S2(:,CHAN);

                        elseif (HDR.SCP2.NHT==1) && (HDR.SCP2.NCT==1) && (HDR.SCP2.prefix==0),
				codelength = HDR.SCP2.HT(1,4);
                                if (codelength==16)
                                        S2 = fread(fid,[HDR.N,HDR.NS],'int16');
                                elseif (codelength==8)
                                        S2 = fread(fid,[HDR.N,HDR.NS],'int8');
                                else
                                        fprintf(HDR.FILE.stderr,'Warning SCPOPEN: codelength %i is not supported yet.',codelength);
                                        fprintf(HDR.FILE.stderr,' Contact <a.schloegl@ieee.org>\n');
                                        return;
                                end;
				%S2 = S2(:,CHAN);

                        elseif 1, HDR.SCP2.NHT~=19999;
                        	%% User specific Huffman table
                        	%% a more elegant Huffman decoder is used here %%
                                for k = 1:HDR.NS,
                                        SCP.data{k} = fread(fid,SCP.SPR(k),'uint8');
                                end;
%                                S2 = repmat(NaN,outlen,length(HDR.InChanSelect));
                                clear S2;
                                sz = inf;
                                for k3 = 1:length(HDR.InChanSelect), k = HDR.InChanSelect(k3); %HDR.NS,
					outdata{k3} = DecodeHuffman(HDR.SCP2.HTree,HDR.SCP2.HTs,SCP.data{k},outlen);
					sz = min(sz,length(outdata{k3}));
				end;

                                for k3 = 1:length(HDR.InChanSelect), k = HDR.InChanSelect(k3); %HDR.NS,
                                	S2(:,k) = outdata{k3}(1:sz);
                                end;
				accu=0;

                        elseif HDR.SCP2.NHT==19999,
                                HuffTab = DHT;
                                for k = 1:HDR.NS,
                                        SCP.data{k} = fread(fid,SCP.SPR(k),'uint8');
                                end;
                                %for k = 1:HDR.NS,
                                for k3 = 1:length(HDR.InChanSelect), k = HDR.InChanSelect(k3); %HDR.NS,
                                %for k = CHAN(:)',
                                        s2 = SCP.data{k};
                                        s2 = [s2; repmat(0,ceil(max(HDR.SCP2.HT(:,4))/8),1)];
					k1 = 0;
					l2 = 0;
					accu = 0;
					c  = 0;
					x  = [];
					HT = HDR.SCP2.HT(find(HDR.SCP2.HT(:,1)==1),3:7);
					while (l2 < HDR.LeadPos(k,2)),
						while ((c < max(HT(:,2))) & (k1<length(s2)-1));
							k1 = k1 + 1;
							dd = s2(k1);
							accu = accu + ACC(dd+1)*(2^c);
							c = c + 8;

							if 0, %for k2 = 1:8,
								accu = accu + (dd>127)*(2^c);
								dd = mod(dd*2,256);
								c = c + 1;
							end;
						end;

                                                ixx = 1;
                                                %acc = mod(accu,2^32);   % bitand returns NaN if accu >= 2^32
						acc = accu - 2^32*fix(accu*(2^(-32)));   % bitand returns NaN if accu >= 2^32
						while (bitand(acc,2^HT(ixx,1)-1) ~= HT(ixx,5)),
							ixx = ixx + 1;
						end;

                                                dd = HT(ixx,2) - HT(ixx,1);
						if HT(ixx,3)==0,
							HT = HDR.SCP2.HT(find(HDR.SCP2.HT(:,1)==HT(ixx,5)),3:7);
							fprintf(HDR.FILE.stderr,'Warning SCPOPEN: Switching Huffman Tables is not tested yet.\n');
						elseif (dd==0),
							l2 = l2 + 1;
							x(l2) = HT(ixx,4);
						else %if (HT(ixx,3)>0),
							l2 = l2 + 1;
							%acc2  = fix(accu*(2^(-HT(ixx,1))));
							%tmp = mod(fix(accu*(2^(-HT(ixx,1)))),2^dd);

                                                        tmp = fix(accu*(2^(-HT(ixx,1))));       % bitshift(accu,-HT(ixx,1))
                                                        tmp = tmp - (2^dd)*fix(tmp*(2^(-dd)));  % bitand(...,2^dd)

                                                        %tmp = bitand(accu,(2^dd-1)*(2^HT(ixx,1)))*(2^-HT(ixx,1));
                                                        % reverse bit-pattern
                                                        if dd==8,
                                                                tmp = ACC(tmp+1);
                                                        else
                                                                tmp = dec2bin(tmp);
                                                                tmp = [char(repmat('0',1,dd-length(tmp))),tmp];
                                                                tmp = bin2dec(tmp(length(tmp):-1:1));
                                                        end
                                                        x(l2) = tmp-(tmp>=(2^(dd-1)))*(2^dd);
						end;
						accu = fix(accu*2^(-HT(ixx,2)));
						c = c - HT(ixx,2);
					end;
					x = x(:);
                                        if k3==1,
                                                S2=x(:,ones(1,k));
                                        elseif size(x,1)==size(S2,1),
                                                S2(:,k) = x;
					else
	                                        fprintf(HDR.FILE.stderr,'Error SCPOPEN: Huffman decoding failed (%i) \n',size(x,1));
	    					HDR.data = S2;
						return;
                                        end;
				end;

                        elseif (HDR.SCP2.NHT==19999), % alternative decoding algorithm.
                                warning('this branch is experimental - it might be broken')
                                HuffTab = DHT;
                                for k = 1:HDR.NS,
                                        SCP.data{k} = fread(fid,SCP.SPR(k),'uint8');
                                end;
                                %for k = 1:HDR.NS,
                                for k3 = 1:length(HDR.InChanSelect), k = HDR.InChanSelect(k3); %HDR.NS,
                                %for k = CHAN(:)',
				        tmp = SCP.data{k};
                                        accu = [tmp(4)+256*tmp(3)+65536*tmp(2)+2^24*tmp(1)];
                                        %accu = bitshift(accu,HDR.SCP2.prefix,32);
                                        c  = 0; %HDR.SCP2.prefix;
                                        l  = 4;
                                        l2 = 0;
                                        clear x;
                                        Ntmp = length(tmp);
                                        tmp = [tmp; zeros(4,1)];
                                        while c <= 32, %1:HDR.SPR(k),
                                                ixx = 1;
                                                while (bitand(accu,mask(ixx)) ~= PREFIX(ixx)),
                                                        ixx = ixx + 1;
                                                end;

                                                if ixx < 18,
                                                        c = c + prefix(ixx);
                                                        %accu  = bitshift(accu, prefix(ixx),32);
                                                        accu  = mod(accu.*(2^prefix(ixx)),2^32);
                                                        l2    = l2 + 1;
                                                        x(l2) = HuffTab(ixx,1);

                                                elseif ixx == 18,
                                                        c = c + prefix(ixx) + 8;
                                                        %accu = bitshift(accu, prefix(ixx),32);
                                                        accu  = mod(accu.*(2^prefix(ixx)),2^32);
                                                        l2    = l2 + 1;

                                                        acc1  = mod(floor(accu*2^(-24)),256);
                                                        %accu = bitshift(accu, 8, 32);
                                                        accu  = mod(accu*256, 2^32);

                                                        x(l2) = acc1-(acc1>=2^7)*2^8;
                                                        acc2  = 0;
                                                        for kk = 1:8,
                                                                acc2 = acc2*2 + mod(acc1,2);
                                                                acc1 = floor(acc1/2);
                                                        end;

                                                elseif ixx == 19,
                                                        c = c + prefix(ixx);
                                                        %accu = bitshift(accu, prefix(ixx),32);
                                                        accu  = mod(accu.*(2^prefix(ixx)),2^32);
                                                        l2    = l2 + 1;
                                                        while (c > 7) & (l < Ntmp),
                                                                l = l+1;
                                                                c = c-8;
                                                                accu = accu + tmp(l)*2^c;
                                                        end;

                                                        acc1 = mod(floor(accu*2^(-16)),2^16);
                                                        %accu = bitshift(accu, 16, 32);
                                                        accu = mod(accu.*(2^16), 2^32);

                                                        x(l2) = acc1-(acc1>=2^15)*2^16;
                                                        acc2 = 0;
                                                        for kk=1:16,
                                                                acc2 = acc2*2+mod(acc1,2);
                                                                acc1 = floor(acc1/2);
                                                        end;
                                                        %x(l2) = acc2;
                                                        c = c + 16;
                                                end;

                                                while (c > 7) & (l < Ntmp),
                                                        l = l+1;
                                                        c = c-8;
                                                        accu = accu + tmp(l)*(2^c);
                                                end;
                                        end;

                                        x = x(1:end-1)';
                                        if k3==1,
                                                S2=x(:,ones(1,k));
                                        elseif size(x,1)==size(S2,1),
                                                S2(:,k) = x;
                                        elseif 1,
	                                        fprintf(HDR.FILE.stderr,'Error SCPOPEN: length=%i of channel %i different to length=%i of channel 1 \n',size(x,1),k,size(S2,1));
                                                return;
                                        else
	                                        fprintf(HDR.FILE.stderr,'Error SCPOPEN: Huffman decoding failed (%i) \n',size(x,1));
	    					HDR.data=S2;
						return;
                                        end;
                                end;

                        elseif HDR.SCP2.NHT~=19999,
                        	%% OBSOLETE %%
                                fprintf(HDR.FILE.stderr,'Error SOPEN SCP-ECG: user specified Huffman Table not supported\n');
                                HDR.SCP = SCP;
                                return;

                        else
                                HDR.SCP2,
                        end;

                        % Decoding of Difference encoding
                        if SCP.FLAG.DIFF==2,
                                for k1 = 3:size(S2,1);
                                        S2(k1,:) = S2(k1,:) + [2,-1] * S2(k1-(1:2),:);
                                end;
                        elseif SCP.FLAG.DIFF==1,
                                S2 = cumsum(S2);
                        end;

                        if section.ID==5,
                                HDR.SCP5 = SCP;
                                HDR.SCP5.data = S2;
                                HDR.SampleRate = SCP.SampleRate;

                        elseif section.ID==6,
                                HDR.SCP6 = SCP;
                                HDR.SampleRate = SCP.SampleRate;
                                HDR.PhysDim = repmat({HDR.SCP6.PhysDim},HDR.NS,1);
                                HDR.data = S2;

                                if HDR.FLAG.bimodal_compression,
                                	%% FIXME: THIS IS A HACK - DO NOT KNOW WHETHER IT IS CORRECT.
                                %	HDR.FLAG.bimodal_compression = isfield(HDR,'SCP5') & isfield(HDR,'SCP4');
                                	HDR.FLAG.bimodal_compression = isfield(HDR,'SCP4');
				end;
                                if HDR.FLAG.bimodal_compression,
					if isfield(HDR,'SCP5')
	                                        F = HDR.SCP5.SampleRate/HDR.SCP6.SampleRate;
        	                                HDR.SampleRate = HDR.SCP5.SampleRate;
                	                        HDR.FLAG.F = F;
					else
                	                        HDR.FLAG.F = 1;
					end;

                                        tmp=[HDR.SCP4.PA(:,1);HDR.LeadPos(1,2)]-[1;HDR.SCP4.PA(:,2)+1];
                                        if ~all(tmp==floor(tmp))
                                                tmp,
                                        end;
                                        t  = (1:HDR.N) / HDR.SampleRate;
                                        S1 = zeros(HDR.N, HDR.NS);

                                        p  = 1;
                                        k2 = 1;
                                        pa = [HDR.SCP4.PA;NaN,NaN];
                                        flag = 1;
                                        %% FIXME: accu undefined ##
					accu = 0;
                                        for k1 = 1:HDR.N,
                                                if k1 == pa(p,2)+1,
                                                        flag = 1;
                                                        p    = p+1;
                                                        accu = S2(k2,:);
                                                elseif k1 == pa(p,1),
                                                        flag = 0;
                                                        k2 = ceil(k2);
                                                end;

                                                if flag,
                                                        S1(k1,:) = ((F-1)*accu + S2(fix(k2),:)) / F;
                                                        k2 = k2 + 1/F;
                                                else
                                                        S1(k1,:) = S2(k2,:);
                                                        k2 = k2 + 1;
                                                end;
                                        end;

                                        HDR.SCP.S2 = S2;
                                        HDR.SCP.S1 = S1;
                                        S2 = S1;
                                end;

                                if HDR.FLAG.ReferenceBeat & ~isfield(HDR,'SCP5')
	                                fprintf(HDR.FILE.stderr,'Warning SOPEN SCP-ECG: Flag ReferenceBeat set, but no section 5 (containing the reference beat) is available\n');
                                elseif HDR.FLAG.ReferenceBeat,

                                	tmp_data = HDR.SCP5.data*(HDR.SCP5.Cal/HDR.SCP6.Cal);
                                        for k = find(~HDR.SCP4.type(:,1)'),
                                                t1 = (HDR.SCP4.type(k,2):HDR.SCP4.type(k,4));
                                                t0 = t1 - HDR.SCP4.type(k,3) + HDR.SCP4.fc0;
                                                S2(t1,:) = S2(t1,:) + tmp_data(t0,:);
                                        end;
                                end;
	                        HDR.data  = S2;
                        end;

                elseif section.ID==7,
                        HDR.SCP7.byte1   = fread(fid,1,'uint8');
                        HDR.SCP7.Nspikes = fread(fid,1,'uint8');
                        HDR.SCP7.meanPPI = fread(fid,1,'uint16');
                        HDR.SCP7.avePPI  = fread(fid,1,'uint16');

                        for k=1:HDR.SCP7.byte1,
                                HDR.SCP7.RefBeat{k} = fread(fid,16,'uint8');
                                %HDR.SCP7.RefBeat1 = fread(fid,16,'uint8');
                        end;

                        for k=1:HDR.SCP7.Nspikes,
                                tmp = fread(fid,16,'uint16');
                                tmp(1,2) = fread(fid,16,'int16');
                                tmp(1,3) = fread(fid,16,'uint16');
                                tmp(1,4) = fread(fid,16,'int16');
                                HDR.SCP7.ST(k,:) = tmp;
                        end;
                        for k=1:HDR.SCP7.Nspikes,
                                tmp = fread(fid,6,'uint8');
                                HDR.SCP7.ST2(k,:) = tmp;
                        end;
                        HDR.SCP7.Nqrs = fread(fid,1,'uint16');
                        HDR.SCP7.beattype = fread(fid,HDR.SCP7.Nqrs,'uint8');

                        HDR.SCP7.VentricularRate = fread(fid,1,'uint16');
                        HDR.SCP7.AterialRate = fread(fid,1,'uint16');
                        HDR.SCP7.QTcorrected = fread(fid,1,'uint16');
                        HDR.SCP7.TypeHRcorr = fread(fid,1,'uint8');

                        len = fread(fid,1,'uint16');
                        tag = 255*(len==0);
                        k1 = 0;
                        while tag~=255,
                                tag = fread(fid,1,'uchar');
                                len = fread(fid,1,'uint16');
                                field = fread(fid,[1,len],'uchar');

                                if tag == 0,
                                        HDR.Patient.LastName = char(field);
                                elseif tag == 1,

                                end;
                        end;
                        HDR.SCP7.P_onset = fread(fid,1,'uint16');
                        HDR.SCP7.P_offset = fread(fid,1,'uint16');
                        HDR.SCP7.QRS_onset = fread(fid,1,'uint16');
                        HDR.SCP7.QRS_offset = fread(fid,1,'uint16');
                        HDR.SCP7.T_offset = fread(fid,1,'uint16');
                        HDR.SCP7.P_axis = fread(fid,1,'uint16');
                        HDR.SCP7.QRS_axis = fread(fid,1,'uint16');
                        HDR.SCP7.T_axis = fread(fid,1,'uint16');

                elseif section.ID==8,
                        tmp = fread(fid,9,'uint8');
                        HDR.SCP8.Report = tmp(1);
                        HDR.SCP8.Time = [[1,256]*tmp(2:3),tmp(4:8)'];
                        HDR.SCP8.N = tmp(9);
                        for k = 1:HDR.SCP8.N,
                                ix  = fread(fid,1,'uint8');
                                len = fread(fid,1,'uint16');
                                tmp = fread(fid,[1,len],'uchar');
                                HDR.SCP8.Statement{k,1} = char(tmp);
                        end

                %elseif section.ID==9,
                %        HDR.SCP9.byte1 = fread(fid,1,'uint8');

                elseif section.ID==10,
                        tmp = fread(fid,2,'uint16');
                        HDR.SCP10.NumberOfLeads = tmp(1);
                        HDR.SCP10.ManufacturerCode = tmp(2);
                        for k = []; 1:HDR.SCP10.NumberOfLeads,
                                tmp = fread(fid,2,'uint16')
                                LeadId = tmp(1);
                                LeadLen = tmp(2);
                                tmp = fread(fid,LeadLen/2,'uint16');
                                HDR.SCP10.LeadId(k)=LeadId;
                                HDR.SCP10.LeadLen(k)=LeadLen;
                                HDR.SCP10.Measurements{k}=tmp;
                        end;

                elseif section.ID==11,
                        bytes = fread(fid,11,'uint8');
                        HDR.SCP11.T0 = [bytes(2)*256+bytes(3), bytes(4:8)];
                        HDR.SCP11.Confirmed = bytes(1);
                        HDR.SCP11.NumberOfStatements = bytes(9);
                        for k = 1:HDR.SCP11.NumberOfStatements,
                                SeqNo = fread(fid,1,'uint8');
                                len11 = fread(fid,1,'uint16');
                                typeID = fread(fid,1,'uint8');
                                Statement = fread(fid,len11-1,'uint8');
                                HDR.SCP11.Statement.SeqNo(k) = SeqNo;
                                HDR.SCP11.Statement.len11(k) = len11;
                                HDR.SCP11.Statement.typeID(k) = typeID;
                                HDR.SCP11.Statement.Statement{k} = Statement;
                        end;
                end;

		if ~section.Length,
			HDR.ERROR.status  = -1;
			HDR.ERROR.message = 'Error SCPOPEN: \n';
			return;
		end;
        end;

        HDR.SPR  = size(HDR.data,1);
        HDR.NRec = 1;
        HDR.AS.endpos = HDR.SPR;

        HDR.FILE.OPEN = 0;
        HDR.FILE.POS  = 0;
        HDR.TYPE = 'native';
        fclose(HDR.FILE.FID);

else    % writing SCP file

	NSections = 12;
        SectIdHdr = zeros(1,16);
        VERSION = round(HDR.VERSION*10);
        if ~any(VERSION==[10,13,20])
                fprintf(HDR.FILE.stderr,'Warning SCPOPEN(WRITE): unknown Version number %4.2f\n',HDR.VERSION);
              	VERSION = 20;
        end;
	SectIdHdr(9:10) = VERSION; % Section and Protocol version number

        POS = 6; B = zeros(1,POS);
        for K = 0:NSections-1;
                b = [];
                if K==0,
                        % SECTION 0
                        b = [SectIdHdr(1:10),'SCPECG', zeros(1,NSections*10)];
	                b(16+(7:10)) = s4b(POS+1);

                elseif K==1,
                        % SECTION 1
                        b = SectIdHdr;
                        % tag(1),len(1:2),field(1:len)
                        if isfield(HDR.Patient,'Name'),
				b = [b, 0, s2b(length(HDR.Patient.Name)), HDR.Patient.Name];
			end;
                        if isfield(HDR.Patient,'Id'),
				b = [b, 2, s2b(length(HDR.Patient.Id)), HDR.Patient.Id];
			end;
                        if isfield(HDR.Patient,'Age'),
				%b = [b, 4, s2b(3), s2b(HDR.Patient.Age),1];  %% use birthday instead
			end;
                        if isfield(HDR.Patient,'Birthday'),
	                        b = [b, 5, s2b(4), s2b(HDR.Patient.Birthday(1)),HDR.Patient.Birthday(2:3)];
			end;
                        if isfield(HDR.Patient,'Height'),
                        if ~isnan(HDR.Patient.Height),
                        	b = [b, 6, s2b(3), s2b(HDR.Patient.Height),1];
                        end;
                        end;
                        if isfield(HDR.Patient,'Weight'),
                        if ~isnan(HDR.Patient.Weight),
                        	b = [b, 7, s2b(3), s2b(HDR.Patient.Weight),1];
                        end;
                        end;
	                if isfield(HDR.Patient,'Sex'),
	                if ~isempty(HDR.Patient.Sex)
                        	sex = HDR.Patient.Sex;
                        	if 0,
                        	elseif isnumeric(sex),           sex = sex(1);
                        	elseif strncmpi(sex,'male',1);   sex = 1;
                        	elseif strncmpi(sex,'female',1); sex = 2;
                        	else sex = 9; % unspecified
                        	end;
	                      	b = [b, 8, s2b(1), sex];
	                end;
                        end;
                        if isfield(HDR.Patient,'Race'),
 	                       	b = [b, 9, s2b(1), HDR.Patient.Race(1)];
                        end;
                        if isfield(HDR.Patient,'BloodPressure'),
                        	b = [b, 11, s2b(2), s2b(HDR.Patient.BloodPressure.Systolic)];
                        	b = [b, 12, s2b(2), s2b(HDR.Patient.BloodPressure.Diastolic)];
                        end;

                        %% Tag 14
                        tag14.AnalyzingProgramRevisionNumber = ['',char(0)];
                        tag14.SerialNumberAcqDevice = ['',char(0)];
                        tag14.AcqDeviceSystemSoftware = ['',char(0)];
                        tag14.SCPImplementationSoftware = ['BioSig4OctMat v 1.76+',char(0)];
                        tag14.ManufactureAcqDevice = ['',char(0)];
                        t14 = [zeros(1,35), length(tag14.AnalyzingProgramRevisionNumber),tag14.AnalyzingProgramRevisionNumber,tag14.SerialNumberAcqDevice,tag14.AcqDeviceSystemSoftware,tag14.SCPImplementationSoftware,tag14.ManufactureAcqDevice];
                        t14(8)  = 255;  % Manufacturer
                        %%% ### FIXME ###  t14(9:14) = % cardiograph model
                        t14(15) = VERSION;        % Version
                        t14(16) = hex2dec('A0');  % Demographics and ECG rhythm data" (if we had also the reference beats we should change it in 0xC0).
                        t14(18) = hex2dec('D0');  % Capabilities of the ECG Device: 0xD0 (acquire, print and store).
                        b   = [b, 14, s2b(length(t14)), t14];

                        b = [b, 25, s2b(4), s2b(HDR.T0(1)),HDR.T0(2:3)];
                        b = [b, 26, s2b(3), HDR.T0(4:6)];
                        if ~any(isnan(HDR.Filter.HighPass))
	                        b = [b, 27, s2b(2), s2b(round(HDR.Filter.HighPass(1)*100))];
                        end;
                        if ~any(isnan(HDR.Filter.LowPass))
	                	b = [b, 28, s2b(2), s2b(round(HDR.Filter.LowPass(1)))];
			end;
                        b = [b, 255, 0, 0];	% terminator
			b = b + (b<0)*256;

                elseif K==3,
                        % SECTION 3
                        b = [SectIdHdr,HDR.NS,4+HDR.NS*8];
                        if ~isfield(HDR,'LeadIdCode'), HDR.LeadIdCode = zeros(1,HDR.NS); end;
                        if (numel(HDR.LeadIdCode)~=HDR.NS); warning('HDR.LeadIdCode does not have HDR.NS elements'); end;
                        if any(HDR.LeadIdCode>255), warning('invalid LeadIdCode'); end;
                        for k = 1:HDR.NS,
                                b = [b, s4b(1), s4b(HDR.SPR*HDR.NRec), mod(HDR.LeadIdCode(k),256)];
                        end;

                elseif K==6,
                        % SECTION 6
                        Cal = full(HDR.Calib(2:end,:)); Cal = Cal - diag(diag(Cal));
                        if any(Cal(:))
                                fprintf(HDR.FILE.stderr,'Calibration is not a diagonal matrix.\n\tThis can result in incorrect scalings.\n');
                        end;
                        Cal = full(diag(HDR.Calib(2:end,:)));
                        if any(Cal~=Cal(1)),
                                fprintf(HDR.FILE.stderr,'scaling information is not equal for all channels; \n\tThis is not supported by SCP and can result in incorrect scalings.\n');
                        end;
                        [tmp,scale1] = physicalunits(HDR.PhysDim{1});
                        [tmp,scale2] = physicalunits('nV');
                        b = [SectIdHdr, s2b(round(Cal(1)*scale1/scale2)), s2b(round(1e6/HDR.SampleRate)), 0, 0];
                        for k = 1:HDR.NS,
                                b = [b, s2b(HDR.SPR*HDR.NRec*2)];
                        end;
                        data = HDR.data(:);
                        data = data + (data<0)*2^16;
                        tmp  = s2b(round(data))';
                        b = [b,tmp(:)'];
                else
                        b = [];
                end;
                if (length(b)>0)
                        if mod(length(b),2), % align to multiple of 2-byte blocks
                                b = [b,0];
                        end;
                        if (length(b)<16), fprintf(HDR.FILE.stderr,'section header %i less then 16 bytes %i', K,length(b)); end;
                        b(3:4) = s2b(K);
                        b(5:8) = s4b(length(b));
                        %b(1:2)= s4b(crc);
                        b(1:2) = s2b(crc16eval(b(3:end)));
                        % section 0: startpos in pointer field
	                B(22+K*10+(7:10)) = s4b(POS+1);
                end;
                B = [B(1:POS),b];
                % section 0 pointer field
                B(22+K*10+(1:2)) = s2b(K);
                B(22+K*10+(3:6)) = s4b(length(b)); % length
                POS = POS + length(b);
        end
        B(3:6) = s4b(POS);      % length of file
        B(7:8) = s2b(crc16eval(B(9:22+NSections*10)));  % CRC of Section 0

        B(1:2) = s2b(crc16eval(B(3:end)));

        %        fwrite(fid,crc,'int16');
        count = fwrite(fid,B,'uchar');
        fclose(fid);
end
end  %% scpopen

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  Auxillary functions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function b2 = s2b(i)
	% converts 16bit into 2 bytes
	b2 = [bitand(i,255),bitand(bitshift(i,-8),255)];
	return;
end	%%%%% s2b %%%%%

function b4 = s4b(i)
	% converts 32 bit into 4 bytes
	b4 = [s2b(bitand(i,2^16-1)),s2b(bitand(bitshift(i,-16),2^16-1)) ];
	return;
end	%%%%% s4b %%%%%

function T = makeSubTree(T,bc,len,val)
	if (len==0)
		T.idxTable = val;
		return
	end;
if 1,
	b = bitand(bc,1)+1;
	if ~isfield(T,'branch') T.branch = {[],[]}; end;
	T.branch{b} = makeSubTree(T.branch{b},bitshift(bc,-1),len-1,val);
else
	if bitand(bc,1)
		if ~isfield(T,'node1'),T.node1 = []; end;
		T.node1 = makeSubTree(T.node1,bitshift(bc,-1),len-1,val);
	else
		if ~isfield(T,'node0'),T.node0 = []; end;
		T.node0 = makeSubTree(T.node0,bitshift(bc,-1),len-1,val);
	end;
end,
	return;
end	%%%%% makeSubTree %%%%%

function T = makeTree(HT)
	T = [];
	for k1 = 1:size(HT,1)
		for k2 = 1:HT(k1,1) % CodeLength
			T = makeSubTree(T,HT(k1,5),HT(k1,1),k1);
		end;
	end;
%save matlab T,pause
	return;
end	%%%%% makeTree %%%%%

function outdata = DecodeHuffman(HTrees,HTs,indata,outlen)
	ActualTable = 1;
	k1 = 1; r=0;
	k2 = 0;

	if ((outlen>0) && isfinite(outlen))
		outdata = repmat(NaN,outlen,1);
	else
		outdata = [0;0];  %% make it a column vector
	end;
	Node    = HTrees{ActualTable};
	while ((k1*8+r <= 8*length(indata)) && (k2<outlen))
		if ~isfield(Node,'idxTable')
			r = r+1; if (r>8), k1=k1+1; r=1; end;
if 1,
			b = bitand(bitshift(indata(k1),r-8),1)+1;
			if ~isempty(Node.branch{b})
				Node = Node.branch{b};
			else
				fprintf(2,'Warning SCPOPEN: empty node in Huffman table\n');
			end;
else
			if bitand(bitshift(indata(k1),r-8),1)
				if isfield(Node,'node1')
					Node = Node.node1;
				else
					fprintf(2,'Warning SCPOPEN: empty node in Huffman table\n');
				end;
			else
				if isfield(Node,'node0')
					Node = Node.node0;
				else
					fprintf(2,'Warning SCPOPEN: empty node in Huffman table\n');
				end;
			end;
end;
		end;

		if isfield(Node,'idxTable')
			TableEntry = HTs{ActualTable}(Node.idxTable,:);
			dlen = TableEntry(2)-TableEntry(1);
			if (~TableEntry(3))
				ActualTable = TableEntry(4);
			elseif (dlen~=0)
				acc = 0;
				for k3 = 1:dlen,
					r = r+1; if (r>8), k1=k1+1; r=1; end;
					acc = 2*acc + bitand(bitshift(indata(k1),r-8), 1);
				end;
				if (acc>=bitshift(1,dlen-1))
					acc = acc - bitshift(1,dlen);
				end;
				k2 = k2+1;
				outdata(k2) = acc;
			else
				k2 = k2+1;
				outdata(k2)=TableEntry(4);
			end;
			Node = HTrees{ActualTable};
		end;
	end;
	return;
end %%%%%%%% DecodeHuffman %%%%%%%%%

function crc16 = crc16eval(D)
% CRC16EVAL cyclic redundancy check with the polynomiaL x^16+x^12+x^5+1
% i.e. CRC-CCITT http://en.wikipedia.org/wiki/Crc16

	D = uint16(D);

	crchi = 255;
	crclo = 255;

	t = '00102030405060708191a1b1c1d1e1f112023222524272629383b3a3d3c3f3e32434041464744454a5b58595e5f5c5d53626160676665646b7a79787f7e7d7c74858687808182838c9d9e9f98999a9b95a4a7a6a1a0a3a2adbcbfbeb9b8bbbab6c7c4c5c2c3c0c1cedfdcdddadbd8d9d7e6e5e4e3e2e1e0effefdfcfbfaf9f8f9181b1a1d1c1f1e110003020504070608393a3b3c3d3e3f30212223242526272b5a59585f5e5d5c53424140474645444a7b78797e7f7c7d72636061666764656d9c9f9e99989b9a95848786818083828cbdbebfb8b9babbb4a5a6a7a0a1a2a3afdedddcdbdad9d8d7c6c5c4c3c2c1c0cefffcfdfafbf8f9f6e7e4e5e2e3e0e1e';
	crc16htab = hex2dec(reshape(t,2,length(t)/2)');

	t = '0021426384a5c6e708294a6b8cadceef31107352b594f7d639187b5abd9cffde62432001e6c7a4856a4b2809eecfac8d53721130d7f695b45b7a1938dffe9dbcc4e586a740610223cced8eaf48690a2bf5d4b79671503312fddcbf9e79583b1aa687e4c522036041ae8feccd2a0b684997b6d5f4133251709fbeddfc1b3a597888a9caeb0c2d4e6f80a1c2e304254667b998fbda3d1c7f5eb190f3d235147756eacba8896e4f2c0de2c3a08166472405dbfa99b85f7e1d3cd3f291b0577615344c6d0e2fc8e98aab44650627c0e182a37d5c3f1ef9d8bb9a75543716f1d0b3922e0f6c4daa8be8c926076445a283e0c11f3e5d7c9bbad9f81736557493b2d1f0';
	crc16ltab = hex2dec(reshape(t,2,length(t)/2)');

	for k = 1:length(D),
		ix = double(bitxor(crchi,D(k)))+1;
		crchi = bitxor(crclo,crc16htab(ix));
		crclo = crc16ltab(ix);
	end;
	crc16 = crchi*256+crclo;

end	%%%%% crc16eval %%%%%


function [result,croppedImage] = isGoodPhoto(img)

minWidth = 480;
minHeight = 480;

%minWidth = 128;
%minHeight = 128;

croppedImage = [];

if isa(img,'char')
    img = imread(img);
end

if isa(img,'logical')
    result = 'binary';
    return;
end

if ndims(img)>3
    result = 'animation';
    return;
end

if size(img,1)<minHeight || size(img,2)<minWidth
    result = 'too small';
    return;
end

persistent emptyFlickrGIF;
persistent emptyFlickrPNG;
if isempty(emptyFlickrGIF)
    [emptyFlickrGIF,map]= imread('emptyFlickr.gif');
    if ~isempty( map )
        emptyFlickrGIF = ind2rgb( emptyFlickrGIF, map );
    end
end
if isempty(emptyFlickrPNG)
    emptyFlickrPNG = imread('emptyFlickr.png');
end

% check if it is flickr empty image
imgT = imresize(img,[size(emptyFlickrGIF,1), size(emptyFlickrGIF,2)]);
if size(imgT,3)==1
    diff= sum(reshape(abs(im2double(imgT)-rgb2gray(emptyFlickrGIF)),1,[]));
else
    diff= sum(reshape(abs(im2double(imgT)-emptyFlickrGIF),1,[]));
end
if diff<50
    result = 'flickr empty';
    return;
end

imgT = imresize(img,[size(emptyFlickrPNG,1), size(emptyFlickrPNG,2)]);
if size(imgT,3)==1
    diff= sum(reshape(abs(imgT-rgb2gray(emptyFlickrPNG)),1,[]));
else
    diff= sum(reshape(abs(imgT-emptyFlickrPNG),1,[]));
end
if diff<50
    result = 'flickr empty';
    return;
end

% check if it is line drawing
if size(img,3)==1
    h = imhist(img);
    if sum(h(240:end))/sum(h) > 0.5
        result = 'too white';
        return;
    elseif sum(h(1:10))/sum(h) > 0.5
        result = 'too black';
        return;
    end
else
    h = imhist(img(:,:,1)) + imhist(img(:,:,2)) + imhist(img(:,:,3));
    if sum(h(240:end))/sum(h) > 0.6
        result = 'too white';
        return;
    elseif sum(h(1:10))/sum(h) > 0.8
        result = 'too black';
        return;
    end
end

imgG = img;

G = fspecial('gaussian',[5 5],2);
%# Filter it
imgG = imfilter(imgG,G,'symmetric','same');

if size(imgG,3)>1
    imgG = rgb2gray(imgG);
end

tto = edge(imgG,'canny');

if sum(tto(:))/numel(tto) < 0.01
    result = 'too pure';
    return;
end

%{
% check if it is line drawing
imgG = img;
if size(imgG,3)>1
    imgG = rgb2gray(imgG);
end
h = imhist(imgG);
if sum(h(240:end))/sum(h) > 0.5
    result = 'too white';
    return;
elseif sum(h(1:10))/sum(h) > 0.5
    result = 'too black';
    return;
else
    [~,ind]=max(h);
    if sum(h(max(1,ind-7):min(256,ind+7)))/sum(h) > 0.7
        result = 'too pure';
        return;
    end
end
%}

% maybe useful http://www.mathworks.com/matlabcentral/fileexchange/25354-cropmat

if size(img,3)==1
    ttWhite = img>252;
else
    ttWhite = img(:,:,1)>252 & img(:,:,2)>252 & img(:,:,3)>252;
end

if size(img,3)==1
    ttBlack = img<5;
else
    ttBlack = img(:,:,1)<5 & img(:,:,2)<5 & img(:,:,3)<5;
end

wWhite=sum(ttWhite,1) > size(tto,1)*0.7;
hWhite=sum(ttWhite,2) > size(tto,2)*0.7;
wBlack=sum(ttBlack,1) > size(tto,1)*0.7;
hBlack=sum(ttBlack,2) > size(tto,2)*0.7;

anyEdgeW = sum(tto,1) > size(tto,1)*0.01;
anyEdgeH = sum(tto,2) > size(tto,2)*0.01;

minW=find(anyEdgeW, 1 )-1+2;   maxW=size(imgG,2)-find(anyEdgeW, 1, 'last' );
minH=find(anyEdgeH, 1 )-1+2;   maxH=size(imgG,1)-find(anyEdgeH, 1, 'last' );

try
    if minW<3 || maxW<3
        minW = 0;
        maxW = 0;
    else
        wMargin = wWhite | wBlack;
        if ~(any(wMargin(1:minW)) && any(wMargin(end-maxW:end)))
            minW = 0;
            maxW = 0;
        end
    end

    if minH<3 || maxH<3
        minH = 0;
        maxH = 0;
    else
        hMargin = hWhite | hBlack;
        if ~(any(hMargin(1:minH)) && any(hMargin(end-maxH:end)))
            minH = 0;
            maxH = 0;
        end
    end

    if minW>0 || maxW>0 || minH>0 || maxH>0
        croppedImage = img(minH+1:size(imgG,1)-maxH,minW+1:size(imgG,2)-maxW,:);
        result = 'crop';

        if size(croppedImage,1)<minHeight || size(croppedImage,2)<minWidth
            result = 'too small';
        end

        return;
    end
catch
end

%{

% automatic image cropping
% http://stackoverflow.com/questions/11121657/find-the-edges-of-image-and-crop-it-in-matlab
%# instead of "==" you can check for similarity within a tolerance
%tt=img(:,:,1)==img(:,:,2) & img(:,:,2) == img(:,:,3);

if size(img,3)==1
    tt = img>252;
else
    tt = img(:,:,1)>252 & img(:,:,2)>252 & img(:,:,3)>252;
end

%# invert tt so that it's 1 where there is signal
tt = ~tt;

%# clean up some of the smaller artifacts
tto = imopen(tt,strel('square',10));

%# get the areas and bounding box of the areas above threshold
%# as an additional criterion, you could also use excentricity

stats = regionprops(tto,'BoundingBox','Area');
if ~isempty(stats)
    area = cat(1,stats.Area);
    [~,maxAreaIdx] = max(area);
    bb = round(stats(maxAreaIdx).BoundingBox);

    %# note that regionprops switches x and y (it's a long story)
    croppedImage = img(bb(2):bb(2)+bb(4)-1,bb(1):bb(1)+bb(3)-1,:);

    if size(croppedImage,1)<size(img,1)-5 || size(croppedImage,2)<size(img,2)-5
        result = 'crop';
        return;
    end
end

%% black
if size(img,3)==1
    tt = img<5;
else
    tt = img(:,:,1)<5 & img(:,:,2)<5 & img(:,:,3)<5;
end

%# invert tt so that it's 1 where there is signal
tt = ~tt;

%# clean up some of the smaller artifacts
tto = imopen(tt,strel('square',10));

%# get the areas and bounding box of the areas above threshold
%# as an additional criterion, you could also use excentricity

stats = regionprops(tto,'BoundingBox','Area');
if ~isempty(stats)
    area = cat(1,stats.Area);
    [~,maxAreaIdx] = max(area);
    bb = round(stats(maxAreaIdx).BoundingBox);

    %# note that regionprops switches x and y (it's a long story)
    croppedImage = img(bb(2):bb(2)+bb(4)-1,bb(1):bb(1)+bb(3)-1,:);

    if size(croppedImage,1)<size(img,1)-5 || size(croppedImage,2)<size(img,2)-5
        result = 'crop';
        return;
    end
end

%}

result = 'good';
return;


function calcAllFusedTextures_batchHN(pathData,pathText,namePT,nameCT,nameROI,outcomes,featType,CTweight_mat,scale_mat,algo_cell,Ng_mat,nBatch,matlabPATH)
% -------------------------------------------------------------------------
% function calcAllFusedTextures_batchHN(pathData,pathText,namePT,nameCT,nameROI,outcomes,featType,CTweight_mat,scale_mat,Ng_mat,nBatch,matlabPATH)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function computes FUSED texture features for all patients, for all
% different combinations of the following texture extraction parameters:
% - CT weight: Weight given to MRI wavelet low-pass sub-bands in the
%               PET/CT fusion process.
% - Scale: Resolution at which the ROI is isotropically resampled.
% - Ng: Number of gray-levels in the quantization process.
%
% Different extraction parameters are passed as arrays or cells in the
% function in order to test all possible combinations. This function is
% used for FUSED scans specifically. See Ref. [1,2] and 'prepareVolume.m'
% for more details.
%
% Texture features are computed for all head and neck (HN) DICOM  imaging
% data downloaded from The Cancer Imaging Archive (TCIA) website at:
% <http://dx.doi.org/10.7937/K9/xxxxxxxxxxxxxxxxxxx, and first organized
% in a 'DATA' directory using the function readAllDICOM_HN.m.  Results are
% then saved in a folder 'TEXTURES' in the HN WORKSPACE.
% -------------------------------------------------------------------------
% REFERENCE:
% [1] Vallieres, M. et al. (2015). FDG-PET/CT radiomics models for the
%     early prediction of different tumour outcomes in head and neck cancer.
%     The Journal of Nuclear Medicine, aa(bb), xxx-yyy.
%     doi:
% [2] Vallieres, M. et al. (2015). A radiomics model from joint FDG-PET and
%     MRI texture features for the prediction of lung metastases in soft-tissue
%     sarcomas of the extremities. Physics in Medicine and Biology, 60(14),
%     5471-5496. doi:10.1088/0031-9155/60/14/5471
% -------------------------------------------------------------------------
% INPUTS:
% 1. pathData: Full path to the HN sData files directory.
%              --> Ex: '/myProject/WORKSPACE/DATA'
% 2. pathText: Full path to the HN non texture features directory.
%              --> Ex: '/myProject/WORKSPACE/FEATURES/TEXTURES'
% 3. namePT: Cell of strings of all PET sData files to read
%            --> Ex: {'HGJ_001_PT.PTscan.mat';'HGJ_022_PT.PTscan.mat'}
% 4. namePT: Cell of strings of all CT sData files to read
%            --> Ex: {'HGJ_001_CT.CTscan.mat';'HGJ_022_CT.CTscan.mat'}
% 5. nameROI: Cell of strings specifying the ROI names to analyze for the
%             patients defined by "namePT" and "nameCT"
%             --> Ex: {'GTV';'GTV-P'}
% 6. outcomes: Structure specifying the status (1 or 0) for different
%              outcomes in HN cancer. Contains: outcomes.Failure,
%              outcomes.Locoregional, outcomes.Distant. See ref.[1] for
%              more details.
% 7. featType: Either 'GTVp' for primary GTV, or 'GTVtot' for primaty GTV +
%              nodal GTVs
%              --> Ex: 'GTVp'
% 8. CTweight_mat: Array vector specifying the different CT weights to test
%                  --> Ex: [1/4,1/3,1/2,2/3,3/4]
% 9. scale_mat: Array vector specifying the different 'Scale' values to test.
%               --> Ex: [1,2,3,4,5]
% 10. Ng_mat: Array vector specifying the different 'Ng' values to test.
%            --> Ex: [8,16,32,64]
% 11. nBatch: Number of parallel batch.
%             --> Ex: 8
% 12.  matlabPATH: Full path to the MATLAB excutable on the system.
%      --> Ex: 'matlab' (symbolic link)
% -------------------------------------------------------------------------
% AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>
% -------------------------------------------------------------------------
% HISTORY:
% - Creation: March 2016
%--------------------------------------------------------------------------
% STATEMENT:
% This file is part of <https://github.com/mvallieres/radiomics/>,
% a package providing MATLAB programming tools for radiomics analysis.
% --> Copyright (C) 2015  Martin Vallieres
%
%    This package 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.
%
%    This package 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 package.  If not, see <http://www.gnu.org/licenses/>.
% -------------------------------------------------------------------------

startpath = pwd;
ind = strfind(namePT{1},'_'); cohortID = namePT{1}(1:ind-1);
cd(pathText), mkdir(['batchLog_',cohortID,'_',featType,'_FusText']), cd(['batchLog_',cohortID,'_',featType,'_FusText']), pathBatch = pwd;
time = 60; % Number of seconds to wait before checking if parallel computations are done
nameOutcomes = fieldnames(outcomes); nOutcomes = numel(nameOutcomes);
scans = {'PTCT'}; nScans = numel(scans);

% PRODUCE BATCH COMPUTATIONS
nPatient = numel(namePT); valid = ones(nPatient,1);
for i = 1:nPatient
    if isempty(nameROI{i})
        valid(i) = 0;
    end
end
indValid = find(valid); nPatient = numel(indValid);
if nPatient < nBatch
    nBatch = nPatient;
end
[patients] = batchPatients(nPatient,nBatch);
save('workspace','pathData','pathText','namePT','nameCT','nameROI','patients','indValid','featType','CTweight_mat','scale_mat','algo_cell','Ng_mat'), pause(5);
for i = 1:nBatch
    nameScript = ['batch',num2str(i),'_script.m'];
    fid = fopen(nameScript,'w');
    fprintf(fid,'load(''workspace'')\n');
    fprintf(fid,['calcAllFusedTextures_HN(pathData,pathText,namePT(indValid(patients{',num2str(i),'})),nameCT(indValid(patients{',num2str(i),'})),nameROI(indValid(patients{',num2str(i),'})),featType,CTweight_mat,scale_mat,algo_cell,Ng_mat)\n']);
    fprintf(fid,['system(''touch batch',num2str(i),'_end'');\n']);
    fprintf(fid,'clear all');
    fclose(fid);
    system([matlabPATH,' -nojvm -nodisplay -nodesktop -nosplash < ',nameScript,' >& ',nameScript(1:end-1),'log &']);
end

% WAITING LOOP
waitBatch(pathBatch,time,nBatch)
delete('workspace.mat')

% GROUPING RESULTS FROM ALL BATCH
nPatient = numel(namePT);
for scan = 1:nScans
    cd(pathText)
    if exist(['HGJ_001_',scans{scan},'_',featType,'_text.mat'],'file')
        temp = load(['HGJ_001_',scans{scan},'_',featType,'_text']); temp = struct2cell(temp); temp = temp{1}; % In order to get the necessary 'nameType' and 'nameFeature' fields
    else
        temp = load(['HGJ_001_',scans{scan},'_','GTVp','_text']); temp = struct2cell(temp); temp = temp{1}; % In order to get the necessary 'nameType' and 'nameFeature' fields
    end
    nameType = fieldnames(temp.Experiment1); nameType(end) = []; nType = numel(nameType); % All texture types are the same
    text = cell(numel(CTweight_mat),numel(scale_mat),numel(algo_cell),numel(Ng_mat));
    tempText = cell(1,nPatient); % Cell used to load patient textures only once
    for p = 1:nPatient
        ind = strfind(namePT{p},'_'); namePatient = namePT{p}(1:ind(2)-1);
        if exist([namePatient,'_',scans{scan},'_',featType,'_text.mat'],'file')
            load([namePatient,'_',scans{scan},'_',featType,'_text.mat']) % Variable 'textures' is now in MATLAB workspace
        else
            load([namePatient,'_',scans{scan},'_','GTVp','_text.mat']) % Variable 'textures' is now in MATLAB workspace
        end
        tempText{p} = textures;
    end
    experiment = 0;
    for c = 1:numel(CTweight_mat)
        for s = 1:numel(scale_mat)
            for a = 1:numel(algo_cell)
                for n = 1:numel(Ng_mat)
                    text{c,s,a,n} = struct;
                    experiment = experiment + 1;
                    strExperiment = ['Experiment',num2str(experiment)];
                    for t = 1:nType
                        nameFeature = fieldnames(temp.(strExperiment).(nameType{t})); nFeature = numel(nameFeature);
                        for f = 1:nFeature
                            data = zeros(nPatient,1);
                            for p = 1:nPatient
                                data(p,1) = tempText{p}.(strExperiment).(nameType{t}).(nameFeature{f});
                            end
                            text{c,s,a,n}.(nameType{t}).(nameFeature{f}).Data = data;
                            for o = 1:nOutcomes
                                [text{c,s,a,n}.(nameType{t}).(nameFeature{f}).Spearman.(nameOutcomes{o}).rs,text{c,s,a,n}.(nameType{t}).(nameFeature{f}).Spearman.(nameOutcomes{o}).p] = corr(data,outcomes.(nameOutcomes{o}),'type','Spearman');
                            end
                        end
                    end
                end
            end
        end
    end
    cd .., save(['text_',cohortID,'_',scans{scan},'_',featType],'text')
end

cd(startpath)
end


function conv_feat_data = extract_image_activation_maps(CNN, image, scales, mean_pix, ...
    Semantic_Aware_CNN, semantic_scales)
% extract_image_activation_maps(CNN, image, scales, mean_pix): extract the
% activation maps of one image (section 3 of technical report) for the
% specified scales using the convolutional neural network CNN.
% extract_image_activation_maps(CNN, image, scales, mean_pix, Semantic_Aware_CNN, semantic_scales):
% In this case the current function it also extracts the semantic
% segmentation aware activation maps (see section 4 of the technical
% report).
%
% INPUTS:
% 1) CNN: the caffe net struct with the convolutional neural network that
% implements the activation mas module (section 3)
% 2) image: a Height x Width x 3 uint8 array that represents the image
% pixels
% 3) scales: NumScales x 1 or 1 x NumScales vector with the images scales
% that will be used. The i-th value should be the size in pixels of the
% smallest dimension of the image in the i-th scale.
% 4) mean_pix: is a 3 x 1 or 1 x 3 vector with the mean pixel value per
% color channel that is subtracted from the scaled image before is being
% fed to the CNN
% 5) Semantic_Aware_CNN (OPTIONAL): the caffe net struct with the
% convolutional neural that implements the activation mas module for the
% semantic segmentation aware CNN features (section 4). The Semantic_Aware_CNN
% network gets as input the convolutional feature maps that the CNN network
% yields and outputs semantic segmentation aware activation maps.
% 6) semantic_scales: a NumScales2 x 1 or 1 x NumScales2 vector with the
% images scales that will be used for the semantic segmentation aware
% features. The elements of this vector should be a subset of the scales vector.
%
% OUTPUTS:
% 1) conv_feat_data: a struct that includes the activation maps of the
% image. Its field is:
%    conv_feat_data.feat:
%    1.a) In case the function is called with the arguments:
%    extract_image_activation_maps(CNN, image, scales, mean_pix)
%    then conv_feat_data.feat is a struct that includes 1) the convolutional
%    feature maps (field rsp) that the CNN network yields, 2) the image scales
%    from which they were extracted (field scale), and 3) the original size
%    of the image (fields im_height and im_width)
%    1.b) In case the function is called with the arguments:
%    extract_image_activation_maps(CNN, image, scales, mean_pix, Semantic_Aware_CNN, semantic_scales):
%    then it is a 1 x 2 cell array where 1st element is a struct with the
%    convolutional feature maps of the CNN network (like in the 1.a case)
%    and the 2nd element is a struct with the convolutional feature maps of
%    the Semantic_Aware_CNN network (like in the 1.a case).
%
% 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
%
%
% 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]
% ---------------------------------------------------------

conv_feat_data = init_feat_data();
conv_feat_data.feat.im_height = size(image,1);
conv_feat_data.feat.im_width  = size(image,2);
% extract the activation maps of an image for a given set of scales
[conv_feat_data.feat.rsp, conv_feat_data.feat.scale] = extract_conv_features(...
    CNN, image, scales, mean_pix);

if exist('Semantic_Aware_CNN','var')>0
    assert(exist('semantic_scales','var')>0)
    % extract the semantic segmentation aware activation maps of an image
    % of a given set of scales and the convolutional feature maps (activation
    % maps) that were previously extracted from the image using the CNN
    % network
    semantic_conv_feat_data      = conv_feat_data;
    semantic_conv_feat_data.feat = pick_scales_if_there(...
        semantic_conv_feat_data.feat, semantic_scales);
    conf.do_interleave        = true; % if set to true then the
    % resolution augmentation technique described on the OverFeat paper: http://arxiv.org/abs/1312.6229
    % (section 3.3 of OverFeat technical report) is being used
    conf.interleave_num_steps = 2; % a scalar value for the number of steps
    % that are being using on the above resolution augmentation technique

    % extract the semantic segmentation aware activation maps
    semantic_conv_feat_data.feat.rsp = extract_semantic_seg_features_from_conv5(...
        Semantic_Aware_CNN, semantic_conv_feat_data.feat.rsp, conf);
    conv_feat_data.feat = {conv_feat_data.feat, semantic_conv_feat_data.feat};
end

end

function d = init_feat_data()
d.feat     = [];
end

function feat = pick_scales_if_there(feat, scales)
num_scales = length(scales);

found_scales = zeros(size(scales));
found_rsp = {};
c = 0;
for s = 1:num_scales
    scale_index = find(feat.scale == scales(s));
    if ~isempty(scale_index)
        assert(numel(scale_index) == 1);
        c = c + 1;
        found_scales(c) = scales(s);
        found_rsp{c}    = feat.rsp{scale_index};
    end
end
feat.scale = found_scales(1:c);
feat.rsp   = found_rsp;

end