function rv=binread(fn, precision)
% rv = binread(fn, [precision]);
% Read binary data
% fn may end in .bin, .spbin, .bin.gz, .spbin.gz, or .mat
% If fn is not found this routine will also try to read, in this order:
%   fn.bin; fn.spbin; fn.gz; fn.mat
%
% Written by Dan Moran and Keiichiro Kanemoto. (c) 2013
%

rv = NaN;

[path,name, ext]=fileparts(fn);


% set precision
if nargin<2, precision = 'double'; end;

% If the originally requested file does not exist, define an order in which
% we shall look for alternatives (.gz and .mat versions)
tries = {fn,  [path '/' name '.bin'],  [path '/' name '.spbin'],  [fn '.gz'],  [path '/' name '.mat']};

READINGORIGFILE = 1; % This flag gets set to FALSE if we end up reading a file other than the originally requested fn

for fntry=tries
    f = fntry{1};
    if exist(f,'file'),
        [~,~, ext]=fileparts(f);
        switch ext
            case '.bin'
                rv = binread_bin(f, precision);
            case '.spbin'
                rv = binread_spbin(f, precision);
            case '.mat'
                rv = binread_mat(f, precision);
            case '.gz'
                rv = binread_gz(f, precision);
            otherwise
                error(['binread does not know how to handle this file type: ' f]);
        end
        
        % If we found it, we can stop looking
        break;
    end
    READINGORIGFILE = 0;
end %forech file to look for

% Alert the user if we read something other than the originally requested filename
if ~READINGORIGFILE, disp(['Note: ' fn ' does not exist. binread.m is instead reading ' f]); end;

if isnan(rv)
    error(['binread coud not locate or read ' fn]);
end

end % binread








function rv = binread_mat(fn, precision)
% Loads fn.mat and returns the first item in the struct contained in the
% .mat file
    [path,name, ext]=fileparts(fn);
    assert(strcmpi(ext,'.mat'), ['binread::binread_mat can only handle .mat files, not ' fn]);
    Z = load(fn);
    ToC = fieldnames(Z);
    assert(length(ToC)==1, 'binread::binread_mat cannot handle .mat files with more than 1 variable');
    rv = Z.(ToC{1});
end % function binread_mat




% Unzips a file and reads the contents (can be .bin or .spbin)
function rv = binread_gz(fn, precision)

    [path,name, ext]=fileparts(fn);
    assert(strcmpi(ext,'.gz'), ['binread::binread_gz can only handle .gz files, not ' fn]);

    % If an uncompressed version already exists, use that
    if exist(fn(1:end-3),'file')
        disp(['binread found an uncompressed version of ' fn '. Using it.']);
        rv = binread(fn(1:end-3));
        return;
    end
    
    % Unzip and read the resulting file
    gunzipfn = gunzip(fn);gunzipfn=gunzipfn{1};
    rv = binread(gunzipfn);
    
    % If MATLAB has deleted the original .gz during gunzip  we must recompress
    % Note this should rarely be done, since the default as of R2013a is to
    % preserve the .gz during gunzip
    if ~exist([fn '.gz'], 'file'), gzip(gunzipfn); end;

    % Delete the unzipped data to clean up behind ourselves
    delete(gunzipfn);
    
    return;
end % function binread_gz



% Reads a .bin file
function rv = binread_bin(fn, precision)

    rv = NaN;
    
    [path,name, ext]=fileparts(fn);
    assert(strcmpi(ext,'.bin'), ['binread::binread_bin can only handle .bin files, not ' fn]);

    % Read the 'count_[fn].txt' file that tells us the dimensions of the file
    % contents
    if exist([addsep(path) 'count_' name '.txt'], 'file'),
        size=dlmread([addsep(path) 'count_' name '.txt'],'\t');
    else
        if exist('OCTAVE_VERSION'),
            if ~strfind(fn,'sparse') && ~strfind(fn,'full'), warning(['No count_ file found for ' name]); end;
        end
        size = Inf;
    end;

    fid = fopen(fn, 'r');
    if length(size) <= 2, %vector
        rv = fread(fid,size,precision,0,'ieee-le');
    else %matrix
        rv = fread(fid,Inf,precision,0,'ieee-le');
        rv = reshape(rv, size);
    end
    fclose(fid);
    
end % function binread_bin




% Reads a .spbin file
function rv = binread_spbin(fn, precision)
    rv = NaN;
    
    [path,name, ext]=fileparts(fn);
    assert(strcmpi(ext,'.spbin'), ['binread::binread_spbin can only handle .bin files, not ' fn]);
    
    fid = fopen(fn, 'r');
    
    ndim = fread(fid,1,precision,0,'ieee-le');
    issp = fread(fid,1,precision,0,'ieee-le');
    precis = fread(fid,1,precision,0,'ieee-le'); % precision. 8, 4, or 1 bytes. Currently always 8 (double)
    if ndim==1, %vector
        if issp,
            szdim1 = fread(fid,1,precision,0,'ieee-le'); % get the size of dimension 1, so we can osrt out whether to retun a row or column vector
            len = fread(fid,1,precision,0,'ieee-le');
            lennz = fread(fid,1,precision,0,'ieee-le');
            nzinds = fread(fid,lennz,'uint32',0,'ieee-le');
            nzvals = fread(fid,lennz,'double',0,'ieee-le');
            rv = zeros(len,1);
            rv(nzinds) = nzvals;
            if szdim1==1, rv = reshape(rv, 1,len); end;
        else
            szdim1 = fread(fid,1,precision,0,'ieee-le'); % get the size of dimension 1, so we can osrt out whether to retun a row or column vector
            len = fread(fid,1,precision,0,'ieee-le');
            rv = fread(fid,len,precision,0,'ieee-le');
            if szdim1==1, rv = reshape(rv, 1,len); end;
        end;
    elseif ndim==2, %matrix
        sz = fread(fid,2,'double',0,'ieee-le');
        if issp,
            spvec = fread(fid,Inf,precision,0,'ieee-le');
            spvec = reshape(spvec, length(spvec)/3,3);
            rv = vec2sp(spvec);
        else
            rv = fread(fid,Inf,precision,0,'ieee-le');
            rv = reshape(rv, sz');
        end;
            
    else % tensor
        fclose(fid);
        error([fn ' is corrupt. First byte should be 1 or 2, depending on whether data is vector or matrix']);
    end
    
    fclose(fid);
end % function binread_spbin


