function  [descr,rep] = block_from_cache(spec,cache,randomizep)

%BLOCK_FROM_CACHE -- Expand the wildcards in PLM block specification.
%
%  [descr,rep] = block_from_cache(spec,cache)
%  [descr,rep] = block_from_cache(spec,cache,randomizep)
%
%  CACHE is a structure array produced by CACHE_INPUTS.
%  SPEC is a block specification: a matrix or a cell array of matrices.
%  Wildcards must have been expanded beforehand (see EXPAND_BLOCK_SPEC).
%  Each row in the matrix has at least four columns: the first three
%  specify the Gabor orientation (1 or 2), the Gabor peak contrast (1, 2,
%  or 3), and the background noise orientation (1 or 2), respectively.
%  The fourth element is an integer number of desired replications.
%  RANDOMIZEP specifies whether the sequence of trials within a block
%  should be randomized (1=default) or not (0).
%
%  DESCR is a matrix (or a cell array of matrices) with five columns:
%   1. Gabor orientation: 1=-10 or 2=+10 deg, see PLEXP1_PARAMS
%   2. Gabor peak contrast: 1=0.23, 2=0.15 or 3=0.10, see PLEXP1_PARAMS
%   3. noise orientation: 1=-15 or 2=+15 deg, see PLEXP1_PARAMS
%   4. cache_idx: b/n 1 and 12
%   5. rep_idx: b/n 1 and cache(cache_idx).N
%
%  It is always true (provided DESCR and REP are matrices) that
%   cache(descr(k,4)).arg = descr(k,1:3)             for each k
%   rep(k,:) = cache(descr(k,4)).rep(descr(k,5),:)   for each k
%
%  Example 1:
%    backgr = 1 ;   % make block with negative background orientation
%    spec = expand_block_spec([0 0 backgr 300]) ; load cache ;
%    descr = block_from_cache(spec,cache) ;
%    [output,W,W_hist] = PLM_session(descr,cache,Mdef) ;
%
%  Example 2: (ABAB design, imbalanced blocks)
%  [NOT RECOMMENDED because the 300-trial "blocks" are not]
%  [guaranteed to be balanced; only 2400-trial "days" are.]
%    abab=[1 2 1 2 1 2]';T=[300 2400 2400 2400 1800 300]';bnd=cumsum(T');
%    sched1 = expand_block_spec(num2cell([zeros(6,2),abab,T],2)) ;
%    sched2 = expand_block_spec(num2cell([zeros(6,2),3-abab,T],2)) ;
%    descr = block_from_cache(sched1,cache) ;  % cell {[300x5];[2400x5];..}
%    descr = cat(1,descr{:}) ;                 % [9600x5 double]
%
%  Example 3: (recommended ABAB design, balanced blocks, see PLEXP1)
%    a=1;a6=ones(1,6);a8=ones(1,8); b=2;b6=b.*a6;b8=b.*a8; T=300;
%    s1=[a,b8,a8,b8,a6,b]'; s2=[b,a8,b8,a8,b6,a]'; N=length(s1);  %N=32
%    sched1=expand_block_spec(num2cell([zeros(N,2),s1,repmat(T,N,1)],2));
%    sched2=expand_block_spec(num2cell([zeros(N,2),s2,repmat(T,N,1)],2));
%    descr = block_from_cache(sched1,cache) ;  % 32x1 cell of 300x5 double
%    descr = cat(1,descr{:}) ;                 % [9600x5 double]
%
%  See also CACHE_INPUTS, EXPAND_BLOCK_SPEC, PLMODEL1.

%  Original coding by Alexander Petrov, UC Irvine.
%  $Revision: 2.0 $  $Date: 2002/05/14 15:45 $

if (nargin < 3)  randomizep = 1 ; end   % do randomize by default

%-- Branch depending on the type of SPEC
if (isnumeric(spec))    % matrix
   [N_rows,N_cols] = size(spec) ;
   if (N_cols < 4)
      error('Eeach specification must have at least four elements.') ;
   end
   %- Translate SPEC sequences into CACHE indices
   cache_indices = sub2ind([2,3,2],spec(:,1),spec(:,2),spec(:,3)) ;
   %- Iterate through the sub-blocks
   descr = zeros(0,5) ;
   rep = zeros(0,size(cache(1).rep,2)) ;
   for k = 1:N_rows
      curr_arg = spec(k,1:3) ;
      curr_N = round(spec(k,4)) ;
      % The current cache index I depends on CURR_ARG=SPEC(K,1:3)
      I = cache_indices(k) ;
      if (any(cache(I).arg~=curr_arg))
         error('Illegal CACHE entry (%d) for [%d %d %d].',I,curr_arg) ;
      end
      %- Do the real work -- sample from CACHE(I).REP
      N_cache = cache(I).N ;
      if (curr_N <= N_cache)   % requested N less than cached N
         % Sample without replacement
         idx = randperm(N_cache)' ;           % e.g. [2000x1]
         idx = idx(1:curr_N) ;                % e.g. [300x1]
      else   % requested N greater than cached N --> reuse is necessary
         % Sample with replacement
         idx = ceil(N_cache .* rand(curr_N,1)) ;   % e.g. [10000x1]
      end
      %- Append results
      descr1 = [repmat([curr_arg I],curr_N,1),idx] ;   % [arg123 I idx]
      descr = [descr ; descr1 ] ;
      if (nargout==2)  % REP requested
         rep = [rep ; cache(I).rep(idx,:)] ;   % could become quite big
      end
   end  % for k = 1:N_rows
   %- Randomize if requested
   if randomizep
      total_N = size(descr,1) ;
      idx = randperm(total_N) ;
      descr = descr(idx,:) ;
      if (nargout==2)  rep = rep(idx,:) ; end
   end
elseif (iscell(spec))   % cell array: recurse on the constituent matrices
   sz = size(spec) ; N = prod(sz) ;
   descr = cell(sz) ;
   if (nargout==2)  % REP requested
      rep = cell(sz) ;
      for k = 1:N
         [descr{k},rep{k}] = block_from_cache(spec{k},cache,randomizep) ;
      end
   else  % REP is not requested
      for k = 1:N
         descr{k} = block_from_cache(spec{k},cache,randomizep) ;
      end
   end
else
   error('SPEC must be either a matrix or a cell array of matrices.') ;
end

%--- Return REP and DESCR
%%%%% End of file BLOCK_FROM_CACHE.M
