From 8bc346f87cdc856d9d019f6004ba3e31b2d9edb8 Mon Sep 17 00:00:00 2001
From: dia <denisilie94@gmail.com>
Date: Sun, 26 Nov 2023 20:40:42 +0200
Subject: [PATCH] Initial commit

---
 IDB-DL/README.md                        |  19 +
 IDB-DL/aksvd.m                          |  47 ++
 IDB-DL/idb_dl.m                         |  69 +++
 IDB-DL/normcol_equal.m                  |   7 +
 IDB-DL/omp.m                            |  47 ++
 IDB-DL/omp_err.m                        | 205 ++++++++
 IDB-DL/omp_ker.m                        |  41 ++
 IDB-DL/omp_mask.m                       |  45 ++
 IDB-DL/omp_sparse.m                     | 180 +++++++
 IDB-DL/private/make.m                   |  40 ++
 IDB-DL/private/mexutils.c               |  79 +++
 IDB-DL/private/mexutils.h               | 103 ++++
 IDB-DL/private/mexutils.mexw64.manifest |  10 +
 IDB-DL/private/mexutils.mexw64.map      |   0
 IDB-DL/private/myblas.c                 | 663 ++++++++++++++++++++++++
 IDB-DL/private/myblas.h                 | 492 ++++++++++++++++++
 IDB-DL/private/omp2mex.c                | 156 ++++++
 IDB-DL/private/omp2mex.m                |  23 +
 IDB-DL/private/omp2mex.mexa64           | Bin 0 -> 35784 bytes
 IDB-DL/private/omp2mex.mexmaci64        | Bin 0 -> 68760 bytes
 IDB-DL/private/omp2mex.mexw64           | Bin 0 -> 22528 bytes
 IDB-DL/private/ompcore.c                | 409 +++++++++++++++
 IDB-DL/private/ompcore.h                |  80 +++
 IDB-DL/private/ompmex.c                 | 133 +++++
 IDB-DL/private/ompmex.m                 |  22 +
 IDB-DL/private/ompmex.mexa64            | Bin 0 -> 31688 bytes
 IDB-DL/private/ompmex.mexmaci64         | Bin 0 -> 68760 bytes
 IDB-DL/private/ompmex.mexw64            | Bin 0 -> 22528 bytes
 IDB-DL/private/ompprof.c                | 113 ++++
 IDB-DL/private/ompprof.h                | 106 ++++
 IDB-DL/private/omputils.c               |  89 ++++
 IDB-DL/private/omputils.h               |  77 +++
 IDB-DL/private/printf.m                 |  26 +
 IDB-DL/stdshade.m                       |  67 +++
 IDB-DL/test_idb_dl.m                    |  78 +++
 IDB/ISPM_cor_tol.m                      |  61 +++
 IDB/README.md                           |  19 +
 IDB/bisection_idb.m                     |  52 ++
 IDB/bisection_ispm.m                    |  36 ++
 IDB/gen_rand_untf.m                     |  33 ++
 IDB/getBound.m                          |  25 +
 IDB/idb.m                               |  82 +++
 IDB/test_idb.m                          |  38 ++
 README.md                               |  97 +---
 44 files changed, 3786 insertions(+), 83 deletions(-)
 create mode 100644 IDB-DL/README.md
 create mode 100644 IDB-DL/aksvd.m
 create mode 100644 IDB-DL/idb_dl.m
 create mode 100644 IDB-DL/normcol_equal.m
 create mode 100644 IDB-DL/omp.m
 create mode 100644 IDB-DL/omp_err.m
 create mode 100644 IDB-DL/omp_ker.m
 create mode 100644 IDB-DL/omp_mask.m
 create mode 100644 IDB-DL/omp_sparse.m
 create mode 100644 IDB-DL/private/make.m
 create mode 100644 IDB-DL/private/mexutils.c
 create mode 100644 IDB-DL/private/mexutils.h
 create mode 100644 IDB-DL/private/mexutils.mexw64.manifest
 create mode 100644 IDB-DL/private/mexutils.mexw64.map
 create mode 100644 IDB-DL/private/myblas.c
 create mode 100644 IDB-DL/private/myblas.h
 create mode 100644 IDB-DL/private/omp2mex.c
 create mode 100644 IDB-DL/private/omp2mex.m
 create mode 100644 IDB-DL/private/omp2mex.mexa64
 create mode 100644 IDB-DL/private/omp2mex.mexmaci64
 create mode 100644 IDB-DL/private/omp2mex.mexw64
 create mode 100644 IDB-DL/private/ompcore.c
 create mode 100644 IDB-DL/private/ompcore.h
 create mode 100644 IDB-DL/private/ompmex.c
 create mode 100644 IDB-DL/private/ompmex.m
 create mode 100644 IDB-DL/private/ompmex.mexa64
 create mode 100644 IDB-DL/private/ompmex.mexmaci64
 create mode 100644 IDB-DL/private/ompmex.mexw64
 create mode 100644 IDB-DL/private/ompprof.c
 create mode 100644 IDB-DL/private/ompprof.h
 create mode 100644 IDB-DL/private/omputils.c
 create mode 100644 IDB-DL/private/omputils.h
 create mode 100644 IDB-DL/private/printf.m
 create mode 100644 IDB-DL/stdshade.m
 create mode 100644 IDB-DL/test_idb_dl.m
 create mode 100644 IDB/ISPM_cor_tol.m
 create mode 100644 IDB/README.md
 create mode 100644 IDB/bisection_idb.m
 create mode 100644 IDB/bisection_ispm.m
 create mode 100644 IDB/gen_rand_untf.m
 create mode 100644 IDB/getBound.m
 create mode 100644 IDB/idb.m
 create mode 100644 IDB/test_idb.m

diff --git a/IDB-DL/README.md b/IDB-DL/README.md
new file mode 100644
index 0000000..2565a85
--- /dev/null
+++ b/IDB-DL/README.md
@@ -0,0 +1,19 @@
+# IDB-DL - Incoherent dictionary learning using a distance barrier
+
+We present a novel method for incoherent dictionary learning.
+The proposed algorithm uses a distance barrier term that promotes incoherence.
+
+## Citing Us
+
+If you use our work, please cite:
+
+Denis C. Ilie Ablachim and Bogdan Dumitrescu, "Incoherent frames design and dictionary learning using a
+distance barrier", submitted, 2023. See [paper](http://asydil.upb.ro/publications/).
+
+## Reproducing the results
+
+To reproduce the article results, run the script "test\_idb\_dl".
+
+## Funding
+
+This work is supported by a grant of the Ministry of Research, Innovation and Digitization, CNCS – UEFISCDI, project number PN-III-P4-PCE-2021-0154, within PNCDI III.
diff --git a/IDB-DL/aksvd.m b/IDB-DL/aksvd.m
new file mode 100644
index 0000000..6d086f0
--- /dev/null
+++ b/IDB-DL/aksvd.m
@@ -0,0 +1,47 @@
+function [D, X, errs, coh, train_time] = aksvd(Y, D, n_nonzero_coefs, max_iter)
+    % start waitbar
+    train_time = 0;
+    % wb = waitbar(0, 'Training AKSVD...');
+
+    errs = zeros(1, max_iter);
+    n_signals = size(Y, 2);
+    [n_features, n_components] = size(D);
+
+    for i_iter = 1:max_iter
+        tmp_time = tic;
+
+        % X coding method
+        X = omp(Y, D, n_nonzero_coefs);
+
+        % optimize dictionary D
+        E = Y - D*X;
+        for j = 1:n_components
+            [~, data_indices, x] = find(X(j,:));
+
+            if (isempty(data_indices))
+                d = randn(n_features, 1);
+                D(:, j) = d / norm(d);
+            else
+                F = E(:, data_indices) + D(:, j)*x;
+                d = F*x';
+                D(:, j) = d / norm(d);
+                X(j, data_indices) = F'*D(:, j);
+                E(:, data_indices) = F - D(:, j)*X(j, data_indices);
+            end
+        end
+
+        errs(i_iter) = norm(Y - D*X, 'fro') / sqrt(n_features*n_signals);
+        train_time = train_time + toc(tmp_time);
+
+        % update waitbar
+        % waitbar(i_iter/max_iter, wb, sprintf('Training AKSVD - Remaining time: %d [sec]',...
+        %        round(train_time/i_iter*(max_iter - i_iter))));
+    end
+
+    % close waitbar
+    % close(wb);
+
+    coh = triu(abs(D'*D), 1);
+    coh = sort(coh(:));
+    coh = coh(n_components * (n_components + 1) / 2 + 1 : end);
+end
diff --git a/IDB-DL/idb_dl.m b/IDB-DL/idb_dl.m
new file mode 100644
index 0000000..55a4b92
--- /dev/null
+++ b/IDB-DL/idb_dl.m
@@ -0,0 +1,69 @@
+function [D, X, errs, coh, train_time] = aksvd_coh_dist(Y, D, n_nonzero_coefs, M, gamma, lambda, max_iter)
+    % start waitbar
+    train_time = 0;
+    % wb = waitbar(0, 'Training AKSVD coh dist...');
+    
+    errs = zeros(1, max_iter);
+    n_signals = size(Y, 2);
+    [n_features, n_components] = size(D);
+    desired_coh = 1 - M/2;
+
+    for i_iter = 1:max_iter
+        tmp_time = tic;
+        
+        % sparse coding
+        X = omp(Y, D, n_nonzero_coefs);
+
+        % dictionary update
+        E = Y - D*X;
+        for j = 1:n_components   
+            [~, data_indices, x] = find(X(j,:));
+
+            if (isempty(data_indices))
+                d = randn(n_features, 1);
+                D(:, j) = d / norm(d);
+            else
+                % scalar products and weights
+                d = D(:,j);
+                v = D'*d;
+                v(j) = 0;
+                w = max(abs(v)/desired_coh, 1);
+
+                % what atoms are too close?
+                i_minus = find(v>desired_coh);
+                i_plus = find(v<-desired_coh);
+
+                % gradients
+                g1 = D*(w.*v);
+                g2 = zeros(n_features, 1);
+                if ~isempty(i_minus)
+                    g2 = g2 + sum(D(:,i_minus) - repmat(d,1,length(i_minus)), 2);
+                end
+                if ~isempty(i_plus)
+                    g2 = g2 - sum(D(:,i_plus) + repmat(d,1,length(i_plus)), 2);
+                end
+
+                F = E(:, data_indices) + d * x;
+                d = F*x' - gamma*(g1 + lambda*g2);
+
+                D(:, j) = d / norm(d);
+                X(j, data_indices) = F'*D(:, j);
+                E(:, data_indices) = F - D(:, j)*X(j, data_indices);
+            end
+        end
+
+        errs(i_iter) = norm(Y - D*X, 'fro') / sqrt(n_features*n_signals);
+        train_time = train_time + toc(tmp_time);
+
+        % update waitbar
+        % waitbar(i_iter/max_iter, wb, sprintf('Training AKSVD coh dist - Remaining time: %d [sec]',...
+        %        round(train_time/i_iter*(max_iter - i_iter))));
+    end
+    
+    % close waitbar
+    % close(wb);
+
+    coh = triu(abs(D'*D), 1);
+    coh = sort(coh(:));
+    coh = coh(n_components * (n_components + 1) / 2 + 1 : end);
+end
diff --git a/IDB-DL/normcol_equal.m b/IDB-DL/normcol_equal.m
new file mode 100644
index 0000000..ada53c7
--- /dev/null
+++ b/IDB-DL/normcol_equal.m
@@ -0,0 +1,7 @@
+function [matout]=normcol_equal(matin)
+% solve the proximal problem 
+% matout = argmin||matout-matin||_F^2, s.t. matout(:,i)=1
+    matout = matin./repmat(sqrt(sum(matin.*matin,1)+eps),size(matin,1),1);
+end
+
+
diff --git a/IDB-DL/omp.m b/IDB-DL/omp.m
new file mode 100644
index 0000000..eb7cb33
--- /dev/null
+++ b/IDB-DL/omp.m
@@ -0,0 +1,47 @@
+% Copyright (c) 2016 Paul Irofti <paul@irofti.net>
+% 
+% Permission to use, copy, modify, and/or distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+% 
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+function [X,shared] = omp(Y,D,s,shared,varargin)
+%% Orthogonal Matching Pursuit algorithm
+% INPUTS:
+%   Y -- training signals set
+%   D -- current dictionary
+%   s -- sparsity constraint
+%
+% OUTPUTS:
+%   X -- sparse representations
+
+    if isempty(varargin)
+        ompparams = {'checkdict', 'off'};
+        do_sparse = true;
+    else
+        if strcmp(varargin{1}, 'error')
+            do_sparse = false;
+            error = varargin{2};
+            if length(varargin) > 2
+                ompparams = {varargin{3:end}};
+            else
+                ompparams = {'checkdict', 'off'};
+            end
+        else
+            do_sparse = true;
+            ompparams = varargin;
+        end
+    end
+    if do_sparse
+        X = omp_sparse(D'*Y, D'*D, s, ompparams{:});
+    else
+        X = omp_err(D'*Y, sum(Y.*Y), D'*D, error, ompparams{:});
+    end
+end
diff --git a/IDB-DL/omp_err.m b/IDB-DL/omp_err.m
new file mode 100644
index 0000000..941a473
--- /dev/null
+++ b/IDB-DL/omp_err.m
@@ -0,0 +1,205 @@
+function gamma = omp_err(varargin)
+%OMP2 Error-constrained Orthogonal Matching Pursuit.
+%  GAMMA = OMP2(D,X,G,EPSILON) solves the optimization problem
+%
+%       min  |GAMMA|_0     s.t.  |X - D*GAMMA|_2 <= EPSILON
+%      gamma
+%
+%  for each of the signals in X, using Batch Orthogonal Matching Pursuit.
+%  Here, D is a dictionary with normalized columns, X is a matrix
+%  containing column signals, EPSILON is the error target for each signal,
+%  and G is the Gramm matrix D'*D. The output GAMMA is a matrix containing
+%  the sparse representations as its columns. 
+%
+%  GAMMA = OMP2(D,X,[],EPSILON) performs the same operation, but without
+%  the matrix G, using OMP-Cholesky. This call produces the same output as
+%  Batch-OMP, but is significantly slower. Using this syntax is only
+%  recommended when available memory is too small to store G.
+%
+%  GAMMA = OMP2(DtX,XtX,G,EPSILON) is the fastest implementation of OMP2,
+%  but also requires the most memory. Here, DtX stores the projections
+%  D'*X, and XtX is a row vector containing the squared norms of the
+%  signals, sum(X.*X). In this case Batch-OMP is used, but without having
+%  to compute D'*X and XtX in advance, which slightly improves runtime.
+%  Note that in general, the call
+%
+%    GAMMA = OMP2(D'*X, sum(X.*X), G, EPSILON);
+%
+%  will be faster than the call
+%
+%    GAMMA = OMP2(D,X,G,EPSILON);
+%
+%  due to optimized matrix multiplications in Matlab. However, when the
+%  entire matrix D'*X cannot be stored in memory, one of the other two
+%  versions can be used. Both compute D'*X for just one signal at a time,
+%  and thus require much less memory.
+%
+%  GAMMA = OMP2(...,PARAM1,VAL1,PARAM2,VAL2,...) specifies additional
+%  parameters for OMP2. Available parameters are:
+%
+%    'gammamode' - Specifies the representation mode for GAMMA. Can be
+%                  either 'full' or 'sparse', corresponding to a full or
+%                  sparse matrix, respectively. By default, GAMMA is
+%                  returned as a sparse matrix.
+%    'maxatoms' -  Limits the number of atoms in the representation of each
+%                  signal. If specified, the number of atoms in each
+%                  representation does not exceed this number, even if the
+%                  error target is not met. Specifying maxatoms<0 implies
+%                  no limit (default).
+%    'messages'  - Specifies whether progress messages should be displayed.
+%                  When positive, this is the number of seconds between
+%                  status prints. When negative, indicates that no messages
+%                  should be displayed (this is the default).
+%    'checkdict' - Specifies whether dictionary normalization should be
+%                  verified. When set to 'on' (default) the dictionary
+%                  atoms are verified to be of unit L2-norm. Setting this
+%                  parameter to 'off' disables verification and accelerates
+%                  function performance. Note that an unnormalized
+%                  dictionary will produce invalid results.
+%    'profile'   - Can be either 'on' or 'off'. When 'on', profiling
+%                  information is displayed at the end of the funciton
+%                  execution.
+%
+%
+%  Summary of OMP2 versions:
+%
+%    version                 |   speed     |   memory
+%  -------------------------------------------------------------
+%   OMP2(DtX,XtX,G,EPSILON)  |  very fast  |  very large
+%   OMP2(D,X,G,EPSILON)      |  fast       |  moderate
+%   OMP2(D,X,[],EPSILON)     |  very slow  |  small
+%  -------------------------------------------------------------
+%
+%
+%  References:
+%  [1] M. Elad, R. Rubinstein, and M. Zibulevsky, "Efficient Implementation
+%      of the K-SVD Algorithm using Batch Orthogonal Matching Pursuit",
+%      Technical Report - CS, Technion, April 2008.
+%
+%  See also OMP.
+
+
+%  Ron Rubinstein
+%  Computer Science Department
+%  Technion, Haifa 32000 Israel
+%  ronrubin@cs
+%
+%  April 2009
+
+
+% default options
+
+sparse_gamma = 1;
+msgdelta = -1;
+maxatoms = -1;
+checkdict = 1;
+profile = 0;
+
+
+% determine number of parameters
+
+paramnum = 1;
+while (paramnum<=nargin && ~ischar(varargin{paramnum}))
+  paramnum = paramnum+1;
+end
+paramnum = paramnum-1;
+
+
+% parse options
+
+for i = paramnum+1:2:length(varargin)
+  paramname = varargin{i};
+  paramval = varargin{i+1};
+
+  switch lower(paramname)
+
+    case 'gammamode'
+      if (strcmpi(paramval,'sparse'))
+        sparse_gamma = 1;
+      elseif (strcmpi(paramval,'full'))
+        sparse_gamma = 0;
+      else
+        error('Invalid GAMMA mode');
+      end
+      
+    case 'maxatoms'
+      maxatoms = paramval;
+      
+    case 'messages'
+      msgdelta = paramval;
+
+    case 'checkdict'
+      if (strcmpi(paramval,'on'))
+        checkdict = 1;
+      elseif (strcmpi(paramval,'off'))
+        checkdict = 0;
+      else
+        error('Invalid checkdict option');
+      end
+
+    case 'profile'
+      if (strcmpi(paramval,'on'))
+        profile = 1;
+      elseif (strcmpi(paramval,'off'))
+        profile = 0;
+      else
+        error('Invalid profile mode');
+      end
+
+    otherwise
+      error(['Unknown option: ' paramname]);
+  end
+  
+end
+
+
+% determine call type
+
+if (paramnum==4)
+  
+  n1 = size(varargin{1},1);
+  n2 = size(varargin{2},1);
+  n3 = size(varargin{3},1);
+  
+  if ( (n1>1 && n2==1) || (n1==1 && n2==1 && n3==1) )  %  DtX,XtX,G,EPSILON
+    
+    DtX = varargin{1};
+    XtX = varargin{2};
+    G = varargin{3};
+    epsilon = varargin{4};
+    D = [];
+    X = [];
+    
+  else  % D,X,G,EPSILON
+    
+    D = varargin{1};
+    X = varargin{2};
+    G = varargin{3};
+    epsilon = varargin{4};
+    DtX = [];
+    XtX = [];
+    
+  end
+  
+else
+  error('Invalid number of parameters');
+end
+
+
+% verify dictionary normalization
+
+if (checkdict)
+  if (isempty(G))
+    atomnorms = sum(D.*D);
+  else
+    atomnorms = diag(G);
+  end
+  if (any(abs(atomnorms-1) > 1e-2))
+    error('Dictionary columns must be normalized to unit length');
+  end
+end
+
+
+% omp
+
+gamma = omp2mex(D,X,DtX,XtX,G,epsilon,sparse_gamma,msgdelta,maxatoms,profile);
diff --git a/IDB-DL/omp_ker.m b/IDB-DL/omp_ker.m
new file mode 100644
index 0000000..f46abad
--- /dev/null
+++ b/IDB-DL/omp_ker.m
@@ -0,0 +1,41 @@
+function [X, shared] = omp_ker(K, Kz, A, s, shared, varargin)
+% Kernel Orthogonal Matching Pursuit algorithm
+% Can be used inside a DL algorithm or on its own
+% Input:
+%   K       - kernel matrix
+%   Kz      - kernel matrix between training and test signals (in DL, Kz==K)
+%             if empty, then Kz = K
+%   A       - current dictionary
+%   s       - sparsity constraint
+%
+% Output:
+%   X       - sparse representations
+
+% BD 22.12.2017
+
+    if isempty(varargin)
+        ompparams = {'checkdict', 'off'};
+        do_sparse = true;
+    else
+        if strcmp(varargin{1}, 'error')
+            do_sparse = false;
+            error = varargin{2};
+            if length(varargin) > 2
+                ompparams = varargin{3:end};
+            else
+                ompparams = {'checkdict', 'off'};
+            end
+        else
+            do_sparse = true;
+            ompparams = varargin;
+        end
+    end
+    if isempty(Kz)      % when OMP applied on training signals
+      Kz = K;
+    end
+    if do_sparse
+        X = omp_sparse(A'*Kz, A'*K*A, s, ompparams{:});
+    else
+        X = omp_err(A'*Kz, sum(K), A'*K*A, error, ompparams{:});
+    end
+end
diff --git a/IDB-DL/omp_mask.m b/IDB-DL/omp_mask.m
new file mode 100644
index 0000000..1c9360c
--- /dev/null
+++ b/IDB-DL/omp_mask.m
@@ -0,0 +1,45 @@
+% Copyright (c) 2018 Bogdan Dumitrescu <bogdan.dumitrescu@acse.pub.ro>
+% 
+% Permission to use, copy, modify, and/or distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+% 
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+function [X, shared] = omp_mask(Y, D, s, shared, varargin)
+
+% Orthogonal Matching Pursuit algorithm for data with missing entries.
+% The missing entries are simply ignored (masked). 
+% Input:
+%   Y     - training signals set
+%   Ymask - varargin{1} = signal mask
+%   D     - current dictionary
+%   s     - sparsity level
+%
+% Output:
+%   X     - sparse representations
+
+% BD 10.04.2018
+
+[~,N] = size(Y);
+
+Ymask = varargin{1};
+Y = Y .* Ymask;    % apply mask, to be sure
+
+ompparams = {'checkdict', 'off'};
+
+for i = 1:N    % represent signals one by one, since the available entries are different
+  suppy = find(Ymask(:,i));
+  Dsmall = D(suppy,:);
+  normDsmall = sqrt( sum( Dsmall.*Dsmall ) );
+  Dsmall = Dsmall ./ repmat(normDsmall, size(Dsmall,1), 1);
+%  X(:,i) = omp(Y(suppy,i), Dsmall, s, shared, varargin);
+  X(:,i) = omp_sparse(Dsmall'*Y(suppy,i), Dsmall'*Dsmall, s, ompparams{:});
+  X(:,i) = X(:,i) ./ normDsmall';
+end
diff --git a/IDB-DL/omp_sparse.m b/IDB-DL/omp_sparse.m
new file mode 100644
index 0000000..f1b65eb
--- /dev/null
+++ b/IDB-DL/omp_sparse.m
@@ -0,0 +1,180 @@
+function gamma = omp_sparse(varargin)
+%OMP Sparsity-constrained Orthogonal Matching Pursuit.
+%  GAMMA = OMP(D,X,G,T) solves the optimization problem
+%
+%       min  |X - D*GAMMA|_2     s.t.  |GAMMA|_0 <= T
+%      gamma
+%
+%  for each of the signals in X, using Batch Orthogonal Matching Pursuit.
+%  Here, D is a dictionary with normalized columns, X is a matrix
+%  containing column signals, T is the # of non-zeros in each signal
+%  representation, and G is the Gramm matrix D'*D. The output GAMMA is a
+%  matrix containing the sparse representations as its columns. 
+%
+%  GAMMA = OMP(D,X,[],T) performs the same operation, but without the
+%  matrix G, using OMP-Cholesky. This call produces the same output as
+%  Batch-OMP, but is significantly slower. Using this syntax is only
+%  recommended when available memory is too small to store G.
+%
+%  GAMMA = OMP(DtX,G,T) is the fastest implementation of OMP, but also
+%  requires the most memory. Here, DtX stores the projections D'*X. In this
+%  case Batch-OMP is used, but without having to compute D'*X in advance,
+%  which slightly improves runtime. Note that in general, the call
+%
+%    GAMMA = OMP(D'*X,G,T);
+%
+%  will be faster than the call
+%
+%    GAMMA = OMP(D,X,G,T);
+%
+%  due to optimized matrix multiplications in Matlab. However, when the
+%  entire matrix D'*X cannot be stored in memory, one of the other two
+%  versions can be used. Both compute D'*X for just one signal at a time,
+%  and thus require much less memory.
+%
+%  GAMMA = OMP(...,PARAM1,VAL1,PARAM2,VAL2,...) specifies additional
+%  parameters for OMP. Available parameters are:
+%
+%    'gammamode' - Specifies the representation mode for GAMMA. Can be
+%                  either 'full' or 'sparse', corresponding to a full or
+%                  sparse matrix, respectively. By default, GAMMA is
+%                  returned as a sparse matrix.
+%    'messages'  - Specifies whether progress messages should be displayed.
+%                  When positive, this is the number of seconds between
+%                  status prints. When negative, indicates that no messages
+%                  should be displayed (this is the default).
+%    'checkdict' - Specifies whether dictionary normalization should be
+%                  verified. When set to 'on' (default) the dictionary
+%                  atoms are verified to be of unit L2-norm. Setting this
+%                  parameter to 'off' disables verification and accelerates
+%                  function performance. Note that an unnormalized
+%                  dictionary will produce invalid results.
+%    'profile'   - Can be either 'on' or 'off'. When 'on', profiling
+%                  information is displayed at the end of the funciton
+%                  execution.
+%
+%
+%  Summary of OMP versions:
+%
+%    version      |   speed     |   memory
+%  --------------------------------------------------
+%   OMP(DtX,G,T)  |  very fast  |  very large
+%   OMP(D,X,G,T)  |  fast       |  moderate
+%   OMP(D,X,[],T) |  very slow  |  small
+%  --------------------------------------------------
+%
+%
+%  References:
+%  [1] M. Elad, R. Rubinstein, and M. Zibulevsky, "Efficient Implementation
+%      of the K-SVD Algorithm using Batch Orthogonal Matching Pursuit",
+%      Technical Report - CS, Technion, April 2008.
+%
+%  See also OMP2.
+
+
+%  Ron Rubinstein
+%  Computer Science Department
+%  Technion, Haifa 32000 Israel
+%  ronrubin@cs
+%
+%  April 2009
+
+
+% default options
+
+sparse_gamma = 0;
+msgdelta = -1;
+checkdict = 1;
+profile = 0;
+
+
+% determine number of parameters
+
+paramnum = 1;
+while (paramnum<=nargin && ~ischar(varargin{paramnum}))
+  paramnum = paramnum+1;
+end
+paramnum = paramnum-1;
+
+
+% parse options
+
+for i = paramnum+1:2:length(varargin)
+  paramname = varargin{i};
+  paramval = varargin{i+1};
+
+  switch lower(paramname)
+
+    case 'gammamode'
+      if (strcmpi(paramval,'sparse'))
+        sparse_gamma = 1;
+      elseif (strcmpi(paramval,'full'))
+        sparse_gamma = 0;
+      else
+        error('Invalid GAMMA mode');
+      end
+      
+    case 'messages'
+      msgdelta = paramval;
+
+    case 'checkdict'
+      if (strcmpi(paramval,'on'))
+        checkdict = 1;
+      elseif (strcmpi(paramval,'off'))
+        checkdict = 0;
+      else
+        error('Invalid checkdict option');
+      end
+
+    case 'profile'
+      if (strcmpi(paramval,'on'))
+        profile = 1;
+      elseif (strcmpi(paramval,'off'))
+        profile = 0;
+      else
+        error('Invalid profile mode');
+      end
+
+    otherwise
+      error(['Unknown option: ' paramname]);
+  end
+  
+end
+
+
+% determine call type
+
+if (paramnum==3)
+  DtX = varargin{1};
+  G = varargin{2};
+  T = varargin{3};
+  D = [];
+  X = [];
+elseif (paramnum==4)
+  D = varargin{1};
+  X = varargin{2};
+  G = varargin{3};
+  T = varargin{4};
+  DtX = [];
+else
+  error('Invalid number of parameters');
+end
+
+
+% verify dictionary normalization
+
+if (checkdict)
+  if (isempty(G))
+    atomnorms = sum(D.*D);
+  else
+    atomnorms = diag(G);
+  end
+  if (any(abs(atomnorms-1) > 1e-2))
+    error('Dictionary columns must be normalized to unit length');
+  end
+end
+
+
+% omp
+
+gamma = ompmex(D,X,DtX,G,T,sparse_gamma,msgdelta,profile);
diff --git a/IDB-DL/private/make.m b/IDB-DL/private/make.m
new file mode 100644
index 0000000..aae72b7
--- /dev/null
+++ b/IDB-DL/private/make.m
@@ -0,0 +1,40 @@
+function make
+%MAKE Build the OMPBox package.
+%  MAKE compiles all OMPBox MEX functions, using Matlab's default MEX
+%  compiler. If the MEX compiler has not been set-up before, please run
+%
+%    mex -setup
+%
+%  before using this MAKE file.
+
+%  Ron Rubinstein
+%  Computer Science Department
+%  Technion, Haifa 32000 Israel
+%  ronrubin@cs
+%
+%  August 2009
+
+
+% detect platform 
+
+compstr = computer;
+is64bit = strcmp(compstr(end-1:end),'64');
+
+
+% compilation parameters
+
+compile_params = cell(0);
+if (is64bit)
+  compile_params{1} = '-largeArrayDims';
+end
+
+% Compile files %
+
+ompsources = {'mexutils.c','ompcore.c','omputils.c','myblas.c','ompprof.c'};
+
+disp('Compiling ompmex...');
+mex('ompmex.c', ompsources{:},compile_params{:});
+
+disp('Compiling omp2mex...');
+mex('omp2mex.c',ompsources{:},compile_params{:});
+
diff --git a/IDB-DL/private/mexutils.c b/IDB-DL/private/mexutils.c
new file mode 100644
index 0000000..169f523
--- /dev/null
+++ b/IDB-DL/private/mexutils.c
@@ -0,0 +1,79 @@
+/**************************************************************************
+ *
+ * File name: mexutils.c
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 15.8.2009
+ *
+ *************************************************************************/
+
+#include "mexutils.h"
+#include <math.h>
+
+
+
+/* verify that the mxArray contains a double matrix */
+
+void checkmatrix(const mxArray *param, char *fname, char *pname)
+{
+  char errmsg[100];
+  sprintf(errmsg, "%.15s requires that %.25s be a double matrix.", fname, pname);
+  if (!mxIsDouble(param) || mxIsComplex(param) || mxGetNumberOfDimensions(param)>2) {
+    mexErrMsgTxt(errmsg);
+  }
+}
+
+
+/* verify that the mxArray contains a 1-D double vector */
+
+void checkvector(const mxArray *param, char *fname, char *pname)
+{
+  char errmsg[100];
+  sprintf(errmsg, "%.15s requires that %.25s be a double vector.", fname, pname);
+  if (!mxIsDouble(param) || mxIsComplex(param) || mxGetNumberOfDimensions(param)>2 || (mxGetM(param)!=1 && mxGetN(param)!=1)) {
+    mexErrMsgTxt(errmsg);
+  }
+}
+
+
+/* verify that the mxArray contains a double scalar */
+
+void checkscalar(const mxArray *param, char *fname, char *pname)
+{
+  char errmsg[100];
+  sprintf(errmsg, "%.15s requires that %.25s be a double scalar.", fname, pname);
+  if (!mxIsDouble(param) || mxIsComplex(param) || mxGetNumberOfDimensions(param)>2 || 
+      mxGetM(param)!=1 || mxGetN(param)!=1) 
+  {
+    mexErrMsgTxt(errmsg);
+  }
+}
+
+
+/* verify that the mxArray contains a sparse matrix */
+
+void checksparse(const mxArray *param, char *fname, char *pname)
+{
+  char errmsg[100];
+  sprintf(errmsg, "%.15s requires that %.25s be sparse.", fname, pname);
+  if (!mxIsSparse(param)) {
+    mexErrMsgTxt(errmsg);
+  }
+}
+
+
+/* verify that the mxArray contains a 1-dimensional cell array */
+
+void checkcell_1d(const mxArray *param, char *fname, char *pname)
+{
+  char errmsg[100];
+  sprintf(errmsg, "%.15s requires that %.25s be a 1-D cell array.", fname, pname);
+  if (!mxIsCell(param) || mxGetNumberOfDimensions(param)>2 || (mxGetM(param)!=1 && mxGetN(param)!=1)) {
+    mexErrMsgTxt(errmsg);
+  }
+}
+
diff --git a/IDB-DL/private/mexutils.h b/IDB-DL/private/mexutils.h
new file mode 100644
index 0000000..c800cbd
--- /dev/null
+++ b/IDB-DL/private/mexutils.h
@@ -0,0 +1,103 @@
+/**************************************************************************
+ *
+ * File name: mexutils.h
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 18.8.2009
+ *
+ * Utility functions for MEX files.
+ *
+ *************************************************************************/
+
+
+#ifndef __MEX_UTILS_H__
+#define __MEX_UTILS_H__
+
+#include "mex.h"
+
+
+
+/**************************************************************************
+ * Function checkmatrix:
+ *
+ * Verify that the specified mxArray is real, of type double, and has 
+ * no more than two dimensions. If not, an error message is printed
+ * and the mex file terminates.
+ * 
+ * Parameters:
+ *   param - the mxArray to be checked
+ *   fname - the name of the function where the error occured (15 characters or less)
+ *   pname - the name of the parameter (25 characters or less)
+ *
+ **************************************************************************/
+void checkmatrix(const mxArray *param, char *fname, char *pname);
+
+
+/**************************************************************************
+ * Function checkvector:
+ *
+ * Verify that the specified mxArray is 1-D, real, and of type double. The
+ * vector may be a column or row vector. Otherwise, an error message is
+ * printed and the mex file terminates.
+ * 
+ * Parameters:
+ *   param - the mxArray to be checked
+ *   fname - the name of the function where the error occured (15 characters or less)
+ *   pname - the name of the parameter (25 characters or less)
+ *
+ **************************************************************************/
+void checkvector(const mxArray *param, char *fname, char *pname);
+
+
+/**************************************************************************
+ * Function checkscalar:
+ *
+ * Verify that the specified mxArray represents a real double scalar value. 
+ * If not, an error message is printed and the mex file terminates.
+ * 
+ * Parameters:
+ *   param - the mxArray to be checked
+ *   fname - the name of the function where the error occured (15 characters or less)
+ *   pname - the name of the parameter (25 characters or less)
+ *
+ **************************************************************************/
+void checkscalar(const mxArray *param, char *fname, char *pname);
+
+
+/**************************************************************************
+ * Function checksparse:
+ *
+ * Verify that the specified mxArray contains a sparse matrix. If not,
+ * an error message is printed and the mex file terminates.
+ * 
+ * Parameters:
+ *   param - the mxArray to be checked
+ *   fname - the name of the function where the error occured (15 characters or less)
+ *   pname - the name of the parameter (25 characters or less)
+ *
+ **************************************************************************/
+void checksparse(const mxArray *param, char *fname, char *pname);
+
+
+/**************************************************************************
+ * Function checkcell_1d:
+ *
+ * Verify that the specified mxArray is a 1-D cell array. The cell array 
+ * may be arranged as either a column or a row. If not, an error message 
+ * is printed and the mex file terminates.
+ * 
+ * Parameters:
+ *   param - the mxArray to be checked
+ *   fname - the name of the function where the error occured (15 characters or less)
+ *   pname - the name of the parameter (25 characters or less)
+ *
+ **************************************************************************/
+void checkcell_1d(const mxArray *param, char *fname, char *pname);
+
+
+#endif
+
diff --git a/IDB-DL/private/mexutils.mexw64.manifest b/IDB-DL/private/mexutils.mexw64.manifest
new file mode 100644
index 0000000..1c06b61
--- /dev/null
+++ b/IDB-DL/private/mexutils.mexw64.manifest
@@ -0,0 +1,10 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level='asInvoker' uiAccess='false' />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+</assembly>
diff --git a/IDB-DL/private/mexutils.mexw64.map b/IDB-DL/private/mexutils.mexw64.map
new file mode 100644
index 0000000..e69de29
diff --git a/IDB-DL/private/myblas.c b/IDB-DL/private/myblas.c
new file mode 100644
index 0000000..06791b0
--- /dev/null
+++ b/IDB-DL/private/myblas.c
@@ -0,0 +1,663 @@
+/**************************************************************************
+ *
+ * File name: myblas.c
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Version: 1.1
+ * Last updated: 13.8.2009
+ *
+ *************************************************************************/
+
+
+#include "myblas.h"
+#include <ctype.h>
+
+
+/* find maximum of absolute values */
+
+mwIndex maxabs(double c[], mwSize m)
+{
+  mwIndex maxid=0, k;
+  double absval, maxval = SQR(*c);   /* use square which is quicker than absolute value */
+
+  for (k=1; k<m; ++k) {
+    absval = SQR(c[k]);
+    if (absval > maxval) {
+      maxval = absval;
+      maxid = k;
+    }
+  }
+  return maxid;
+}
+
+
+/* compute y := alpha*x + y */
+
+void vec_sum(double alpha, double x[], double y[], mwSize n)
+{
+  mwIndex i;
+
+  for (i=0; i<n; ++i) {
+    y[i] += alpha*x[i];
+  }
+}
+
+
+/* compute y := alpha*A*x */
+
+void mat_vec(double alpha, double A[], double x[], double y[], mwSize n, mwSize m)
+{
+  mwIndex i, j, i_n;
+  double *Ax;
+
+  Ax = mxCalloc(n,sizeof(double));
+
+  for (i=0; i<m; ++i) {
+    i_n = i*n;
+    for (j=0; j<n; ++j) {
+      Ax[j] += A[i_n+j] * x[i];
+    }
+  }
+
+  for (j=0; j<n; ++j) {
+    y[j] = alpha*Ax[j];
+  }
+
+  mxFree(Ax);
+}
+
+
+/* compute y := alpha*A'*x */
+
+void matT_vec(double alpha, double A[], double x[], double y[], mwSize n, mwSize m)
+{
+  mwIndex i, j, n_i;
+  double sum0, sum1, sum2, sum3;
+
+  for (j=0; j<m; ++j) {
+    y[j] = 0;
+  }
+
+  /* use loop unrolling to accelerate computation */
+
+  for (i=0; i<m; ++i) {
+    n_i = n*i;
+    sum0 = sum1 = sum2 = sum3 = 0;
+    for (j=0; j+4<n; j+=4) {
+      sum0 += A[n_i+j]*x[j];
+      sum1 += A[n_i+j+1]*x[j+1];
+      sum2 += A[n_i+j+2]*x[j+2];
+      sum3 += A[n_i+j+3]*x[j+3];
+    }
+    y[i] += alpha * ((sum0 + sum1) + (sum2 + sum3));
+    while (j<n) {
+      y[i] += alpha*A[n_i+j]*x[j];
+      j++;
+    }
+  }
+}
+
+
+/* compute y := alpha*A*x */
+
+void mat_sp_vec(double alpha, double pr[], mwIndex ir[], mwIndex jc[], double x[], double y[], mwSize n, mwSize m)
+{
+  
+  mwIndex i, j, j1, j2;
+
+  for (i=0; i<n; ++i) {
+    y[i] = 0;
+  }
+  
+  j2 = jc[0];
+  for (i=0; i<m; ++i) {
+    j1 = j2; j2 = jc[i+1];
+    for (j=j1; j<j2; ++j) {
+      y[ir[j]] += alpha * pr[j] * x[i];
+    }
+  }
+  
+}
+
+
+/* compute y := alpha*A'*x */
+
+void matT_sp_vec(double alpha, double pr[], mwIndex ir[], mwIndex jc[], double x[], double y[], mwSize n, mwSize m)
+{
+  
+  mwIndex i, j, j1, j2;
+  
+  for (i=0; i<m; ++i) {
+    y[i] = 0;
+  }
+  
+  j2 = jc[0];
+  for (i=0; i<m; ++i) {
+    j1 = j2; j2 = jc[i+1];
+    for (j=j1; j<j2; ++j) {
+      y[i] += alpha * pr[j] * x[ir[j]];
+    }
+  }
+  
+}
+
+
+/* compute y := alpha*A*x */
+
+void mat_vec_sp(double alpha, double A[], double pr[], mwIndex ir[], mwIndex jc[], double y[], mwSize n, mwSize m)
+{
+  
+  mwIndex i, j, j_n, k, kend;
+  
+  for (i=0; i<n; ++i) {
+    y[i] = 0;
+  }
+  
+  kend = jc[1];
+  if (kend==0) {   /* x is empty */
+    return;
+  }
+  
+  for (k=0; k<kend; ++k) {
+    j = ir[k];
+    j_n = j*n;
+    for (i=0; i<n; ++i) {
+      y[i] += alpha * A[i+j_n] * pr[k];
+    }
+  }
+
+}
+
+
+/* compute y := alpha*A'*x */
+
+void matT_vec_sp(double alpha, double A[], double pr[], mwIndex ir[], mwIndex jc[], double y[], mwSize n, mwSize m)
+{
+  
+  mwIndex i, j, j_n, k, kend;
+  
+  for (i=0; i<m; ++i) {
+    y[i] = 0;
+  }
+  
+  kend = jc[1];
+  if (kend==0) {   /* x is empty */
+    return;
+  }
+  
+  for (j=0; j<m; ++j) {
+    j_n = j*n;
+    for (k=0; k<kend; ++k) {
+      i = ir[k];
+      y[j] += alpha * A[i+j_n] * pr[k];
+    }
+  }
+  
+}
+
+
+/* compute y := alpha*A*x */
+
+void mat_sp_vec_sp(double alpha, double pr[], mwIndex ir[], mwIndex jc[], double prx[], mwIndex irx[], mwIndex jcx[], double y[], mwSize n, mwSize m)
+{
+  
+  mwIndex i, j, k, kend, j1, j2;
+
+  for (i=0; i<n; ++i) {
+    y[i] = 0;
+  }
+  
+  kend = jcx[1]; 
+  if (kend==0) {   /* x is empty */
+    return;
+  }
+  
+  for (k=0; k<kend; ++k) {
+    i = irx[k];
+    j1 = jc[i]; j2 = jc[i+1];
+    for (j=j1; j<j2; ++j) {
+      y[ir[j]] += alpha * pr[j] * prx[k];
+    }
+  }
+  
+}
+
+
+/* compute y := alpha*A'*x */
+
+void matT_sp_vec_sp(double alpha, double pr[], mwIndex ir[], mwIndex jc[], double prx[], mwIndex irx[], mwIndex jcx[], double y[], mwSize n, mwSize m)
+{
+  
+  mwIndex i, j, k, jend, kend, jadd, kadd, delta;
+  
+  for (i=0; i<m; ++i) {
+    y[i] = 0;
+  }
+  
+  kend = jcx[1];
+  if (kend==0) {   /* x is empty */
+    return;
+  }
+  
+  for (i=0; i<m; ++i) {
+    j = jc[i]; 
+    jend = jc[i+1];
+    k = 0;
+    while (j<jend && k<kend) {
+      
+      delta = ir[j] - irx[k];
+      
+      if (delta) { /* if indices differ - increment the smaller one */
+        jadd = delta<0;
+        kadd = 1-jadd;
+        j += jadd;
+        k += kadd;
+      }
+      
+      else {    /* indices are equal - add to result and increment both */
+        y[i] += alpha * pr[j] * prx[k];
+        j++; k++;
+      }
+    }
+  }
+  
+}
+
+
+/* matrix-matrix multiplication */
+
+void mat_mat(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k)
+{
+  mwIndex i1, i2, i3, iX, iA, i2_n;
+  double b;
+  
+  for (i1=0; i1<n*k; i1++) {
+    X[i1] = 0;
+  }
+
+  for (i2=0; i2<m; ++i2) {
+    i2_n = i2*n;
+    iX = 0;
+    for (i3=0; i3<k; ++i3) {
+      iA = i2_n;
+      b = B[i2+i3*m];
+      for (i1=0; i1<n; ++i1) {
+        X[iX++] += A[iA++]*b;
+      }
+    }
+  }
+  
+  for (i1=0; i1<n*k; i1++) {
+    X[i1] *= alpha;
+  }
+}
+
+
+/* matrix-transpose-matrix multiplication */
+
+void matT_mat(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k)
+{
+  mwIndex i1, i2, i3, iX, iA, i2_n;
+  double *x, sum0, sum1, sum2, sum3;
+
+  for (i2=0; i2<m; ++i2) {
+    for (i3=0; i3<k; ++i3) {
+      sum0 = sum1 = sum2 = sum3 = 0;
+      for (i1=0; i1+4<n; i1+=4) {
+        sum0 += A[i1+0+i2*n]*B[i1+0+i3*n];
+        sum1 += A[i1+1+i2*n]*B[i1+1+i3*n];
+        sum2 += A[i1+2+i2*n]*B[i1+2+i3*n];
+        sum3 += A[i1+3+i2*n]*B[i1+3+i3*n];
+      }
+      X[i2+i3*m] = (sum0+sum1) + (sum2+sum3);
+      while(i1<n) {
+        X[i2+i3*m] += A[i1+i2*n]*B[i1+i3*n];
+        i1++;
+      }
+    }
+  }
+  
+  for (i1=0; i1<m*k; i1++) {
+    X[i1] *= alpha;
+  }
+}
+
+
+/* tensor-matrix product */
+
+void tens_mat(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k, mwSize l)
+{
+  mwIndex i1, i2, i3, i4, i2_n, nml;
+  double b;
+  
+  nml = n*m*l;
+  for (i1=0; i1<nml; ++i1) {
+    X[i1] = 0;
+  }
+
+  for (i2=0; i2<m; ++i2) {
+    i2_n = i2*n;
+    for (i3=0; i3<k; ++i3) {
+      for (i4=0; i4<l; ++i4) {
+        b = B[i4+i3*l];
+        for (i1=0; i1<n; ++i1) {
+          X[i1 + i2_n + i4*n*m] += A[i1 + i2_n + i3*n*m] * b;
+        }
+      }
+    }
+  }
+  
+  for (i1=0; i1<nml; ++i1) {
+    X[i1] *= alpha;
+  }
+}
+
+
+/* tensor-matrix-transpose product */
+
+void tens_matT(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k, mwSize l)
+{
+  mwIndex i1, i2, i3, i4, i2_n, nml;
+  double b;
+  
+  nml = n*m*l;
+  for (i1=0; i1<nml; ++i1) {
+    X[i1] = 0;
+  }
+
+  for (i2=0; i2<m; ++i2) {
+    i2_n = i2*n;
+    for (i4=0; i4<l; ++i4) {
+      for (i3=0; i3<k; ++i3) {
+        b = B[i3+i4*k];
+        for (i1=0; i1<n; ++i1) {
+          X[i1 + i2_n + i4*n*m] += A[i1 + i2_n + i3*n*m] * b;
+        }
+      }
+    }
+  }
+  
+  for (i1=0; i1<nml; ++i1) {
+    X[i1] *= alpha;
+  }
+}
+
+
+/* dot product */
+
+double dotprod(double a[], double b[], mwSize n)
+{
+  double sum = 0;
+  mwIndex i;
+  for (i=0; i<n; ++i)
+    sum += a[i]*b[i];
+  return sum;
+}
+
+
+/* find maximum of vector */
+
+mwIndex maxpos(double c[], mwSize m)
+{
+  mwIndex maxid=0, k;
+  double val, maxval = *c;
+
+  for (k=1; k<m; ++k) {
+    val = c[k];
+    if (val > maxval) {
+      maxval = val;
+      maxid = k;
+    }
+  }
+  return maxid;
+}
+
+
+/* solve L*x = b */
+
+void backsubst_L(double L[], double b[], double x[], mwSize n, mwSize k)
+{
+  mwIndex i, j;
+  double rhs;
+
+  for (i=0; i<k; ++i) {
+    rhs = b[i];
+    for (j=0; j<i; ++j) {
+      rhs -= L[j*n+i]*x[j];
+    }
+    x[i] = rhs/L[i*n+i];
+  }
+}
+
+
+/* solve L'*x = b */
+
+void backsubst_Lt(double L[], double b[], double x[], mwSize n, mwSize k)
+{
+  mwIndex i, j;
+  double rhs;
+
+  for (i=k; i>=1; --i) {
+    rhs = b[i-1];
+    for (j=i; j<k; ++j) {
+      rhs -= L[(i-1)*n+j]*x[j];
+    }
+    x[i-1] = rhs/L[(i-1)*n+i-1];
+  }
+}
+
+
+/* solve U*x = b */
+
+void backsubst_U(double U[], double b[], double x[], mwSize n, mwSize k)
+{
+  mwIndex i, j;
+  double rhs;
+
+  for (i=k; i>=1; --i) {
+    rhs = b[i-1];
+    for (j=i; j<k; ++j) {
+      rhs -= U[j*n+i-1]*x[j];
+    }
+    x[i-1] = rhs/U[(i-1)*n+i-1];
+  }
+}
+
+
+/* solve U'*x = b */
+
+void backsubst_Ut(double U[], double b[], double x[], mwSize n, mwSize k)
+{
+  mwIndex i, j;
+  double rhs;
+
+  for (i=0; i<k; ++i) {
+    rhs = b[i];
+    for (j=0; j<i; ++j) {
+      rhs -= U[i*n+j]*x[j];
+    }
+    x[i] = rhs/U[i*n+i];
+  }
+}
+
+
+/* back substitution solver */
+
+void backsubst(char ul, double A[], double b[], double x[], mwSize n, mwSize k)
+{
+  if (tolower(ul) == 'u') {
+    backsubst_U(A, b, x, n, k);
+  }
+  else if (tolower(ul) == 'l') {
+    backsubst_L(A, b, x, n, k);
+  }
+  else {
+    mexErrMsgTxt("Invalid triangular matrix type: must be ''U'' or ''L''");
+  }
+}
+
+
+/* solve equation set using cholesky decomposition */
+
+void cholsolve(char ul, double A[], double b[], double x[], mwSize n, mwSize k)
+{
+  double *tmp;
+
+  tmp = mxMalloc(k*sizeof(double));
+
+  if (tolower(ul) == 'l') {
+    backsubst_L(A, b, tmp, n, k);
+    backsubst_Lt(A, tmp, x, n, k);
+  }
+  else if (tolower(ul) == 'u') {
+    backsubst_Ut(A, b, tmp, n, k);
+    backsubst_U(A, tmp, x, n, k);
+  }
+  else {
+    mexErrMsgTxt("Invalid triangular matrix type: must be either ''U'' or ''L''");
+  }
+
+  mxFree(tmp);
+}
+
+
+/* perform a permutation assignment y := x(ind(1:k)) */
+
+void vec_assign(double y[], double x[], mwIndex ind[], mwSize k)
+{
+  mwIndex i;
+
+  for (i=0; i<k; ++i)
+    y[i] = x[ind[i]];
+}
+
+
+/* matrix transpose */
+
+void transpose(double X[], double Y[], mwSize n, mwSize m)
+{
+  mwIndex i, j, i_m, j_n;
+  
+  if (n<m) {
+    for (j=0; j<m; ++j) {
+      j_n = j*n;
+      for (i=0; i<n; ++i) {
+        Y[j+i*m] = X[i+j_n];
+      }
+    }
+  }
+  else {
+    for (i=0; i<n; ++i) {
+      i_m = i*m;
+      for (j=0; j<m; ++j) {
+        Y[j+i_m] = X[i+j*n];
+      }
+    }
+  }
+}
+
+
+/* print contents of matrix */
+
+void printmat(double A[], int n, int m, char* matname)
+{
+  int i, j;
+  mexPrintf("\n%s = \n\n", matname);
+
+  if (n*m==0) {
+    mexPrintf("   Empty matrix: %d-by-%d\n\n", n, m);
+    return;
+  }
+
+  for (i=0; i<n; ++i) {
+    for (j=0; j<m; ++j)
+      mexPrintf("   %lf", A[j*n+i]);
+    mexPrintf("\n");
+  }
+  mexPrintf("\n");
+}
+
+
+/* print contents of sparse matrix */
+
+void printspmat(mxArray *a, char* matname)
+{
+  mwIndex *aJc = mxGetJc(a);
+  mwIndex *aIr = mxGetIr(a);
+  double *aPr = mxGetPr(a);
+
+  int i;
+
+  mexPrintf("\n%s = \n\n", matname);
+
+  for (i=0; i<aJc[1]; ++i)
+    printf("   (%d,1) = %lf\n", aIr[i]+1,aPr[i]);
+
+  mexPrintf("\n");
+}
+
+
+
+/* matrix multiplication using Winograd's algorithm */
+
+/*
+void mat_mat2(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k)
+{
+  
+  mwIndex i1, i2, i3, iX, iA, i2_n;
+  double b, *AA, *BB;
+  
+  AA = mxCalloc(n,sizeof(double));
+  BB = mxCalloc(k,sizeof(double));
+  
+  for (i1=0; i1<n*k; i1++) {
+    X[i1] = 0;
+  }
+  
+  for (i1=0; i1<n; ++i1) {
+    for (i2=0; i2<m/2; ++i2) {
+      AA[i1] += A[i1+2*i2*n]*A[i1+(2*i2+1)*n];
+    }
+  }
+
+  for (i2=0; i2<k; ++i2) {
+    for (i1=0; i1<m/2; ++i1) {
+      BB[i2] += B[2*i1+i2*m]*B[2*i1+1+i2*m];
+    }
+  }
+
+  for (i2=0; i2<k; ++i2) {
+    for (i3=0; i3<m/2; ++i3) {
+      for (i1=0; i1<n; ++i1) {
+        X[i1+i2*n] += (A[i1+(2*i3)*n]+B[2*i3+1+i2*m])*(A[i1+(2*i3+1)*n]+B[2*i3+i2*m]);
+      }
+    }
+  }
+  
+  if (m%2) {
+    for (i2=0; i2<k; ++i2) {
+      for (i1=0; i1<n; ++i1) {
+        X[i1+i2*n] += A[i1+(m-1)*n]*B[m-1+i2*m];
+      }
+    }
+  }
+  
+  for (i2=0; i2<k; ++i2) {
+    for (i1=0; i1<n; ++i1) {
+      X[i1+i2*n] -= (AA[i1] + BB[i2]);
+      X[i1+i2*n] *= alpha;
+    }
+  }
+  
+  mxFree(AA);
+  mxFree(BB);
+}
+*/
+
+
+
+
diff --git a/IDB-DL/private/myblas.h b/IDB-DL/private/myblas.h
new file mode 100644
index 0000000..17df5ec
--- /dev/null
+++ b/IDB-DL/private/myblas.h
@@ -0,0 +1,492 @@
+/**************************************************************************
+ *
+ * File name: myblas.h
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Version: 1.1
+ * Last updated: 17.8.2009
+ *
+ * A collection of basic linear algebra functions, in the spirit of the
+ * BLAS/LAPACK libraries.
+ *
+ *************************************************************************/
+
+
+
+#ifndef __MY_BLAS_H__
+#define __MY_BLAS_H__
+
+
+#include "mex.h"
+#include <math.h>
+
+
+
+/**************************************************************************
+ * Squared value.
+ **************************************************************************/
+#define SQR(X) ((X)*(X))
+
+
+
+/**************************************************************************
+ * Matrix-vector multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*A*x
+ *
+ * Parameters:
+ *   A - matrix of size n X m
+ *   x - vector of length m
+ *   y - output vector of length n
+ *   alpha - real constant
+ *   n, m - dimensions of A
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void mat_vec(double alpha, double A[], double x[], double y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Matrix-transpose-vector multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*A'*x
+ *
+ * Parameters:
+ *   A - matrix of size n X m
+ *   x - vector of length n
+ *   y - output vector of length m
+ *   alpha - real constant
+ *   n, m - dimensions of A
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void matT_vec(double alpha, double A[], double x[], double y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Sparse-matrix-vector multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*A*x
+ *
+ * where A is a sparse matrix.
+ *
+ * Parameters:
+ *   pr,ir,jc - sparse representation of the matrix A, of size n x m
+ *   x - vector of length m
+ *   y - output vector of length n
+ *   alpha - real constant
+ *   n, m - dimensions of A
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void mat_sp_vec(double alpha, double pr[], mwIndex ir[], mwIndex jc[], double x[], double y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Sparse-matrix-transpose-vector multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*A'*x
+ *
+ * where A is a sparse matrix.
+ *
+ * Parameters:
+ *   pr,ir,jc - sparse representation of the matrix A, of size n x m
+ *   x - vector of length m
+ *   y - output vector of length n
+ *   alpha - real constant
+ *   n, m - dimensions of A
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void matT_sp_vec(double alpha, double pr[], mwIndex ir[], mwIndex jc[], double x[], double y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Matrix-sparse-vector multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*A*x
+ *
+ * where A is a matrix and x is a sparse vector.
+ *
+ * Parameters:
+ *   A - matrix of size n X m
+ *   pr,ir,jc - sparse representation of the vector x, of length m
+ *   y - output vector of length n
+ *   alpha - real constant
+ *   n, m - dimensions of A
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void mat_vec_sp(double alpha, double A[], double pr[], mwIndex ir[], mwIndex jc[], double y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Matrix-transpose-sparse-vector multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*A'*x
+ *
+ * where A is a matrix and x is a sparse vector.
+ *
+ * Parameters:
+ *   A - matrix of size n X m
+ *   pr,ir,jc - sparse representation of the vector x, of length n
+ *   y - output vector of length m
+ *   alpha - real constant
+ *   n, m - dimensions of A
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void matT_vec_sp(double alpha, double A[], double pr[], mwIndex ir[], mwIndex jc[], double y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Sparse-matrix-sparse-vector multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*A*x
+ *
+ * where A is a sparse matrix and x is a sparse vector.
+ *
+ * Parameters:
+ *   pr,ir,jc - sparse representation of the matrix A, of size n x m
+ *   prx,irx,jcx - sparse representation of the vector x (of length m)
+ *   y - output vector of length n
+ *   alpha - real constant
+ *   n, m - dimensions of A
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void mat_sp_vec_sp(double alpha, double pr[], mwIndex ir[], mwIndex jc[], double prx[], mwIndex irx[], mwIndex jcx[], double y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Sparse-matrix-transpose-sparse-vector multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*A'*x
+ *
+ * where A is a sparse matrix and x is a sparse vector.
+ *
+ * Importnant note: this function is provided for completeness, but is NOT efficient.
+ * If possible, convert x to non-sparse representation and use matT_vec_sp instead.
+ *
+ * Parameters:
+ *   pr,ir,jc - sparse representation of the matrix A, of size n x m
+ *   prx,irx,jcx - sparse representation of the vector x (of length n)
+ *   y - output vector of length n
+ *   alpha - real constant
+ *   n, m - dimensions of A
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void matT_sp_vec_sp(double alpha, double pr[], mwIndex ir[], mwIndex jc[], double prx[], mwIndex irx[], mwIndex jcx[], double y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Matrix-matrix multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   X := alpha*A*B
+ *
+ * Parameters:
+ *   A - matrix of size n X m
+ *   B - matrix of size m X k
+ *   X - output matrix of size n X k
+ *   alpha - real constant
+ *   n, m, k - dimensions of A, B
+ *
+ * Note: This function re-writes the contents of X.
+ *
+ **************************************************************************/
+void mat_mat(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k);
+
+
+
+/**************************************************************************
+ * Matrix-transpose-matrix multiplication. 
+ *
+ * Computes an operation of the form:
+ *
+ *   X := alpha*A*B
+ *
+ * Parameters:
+ *   A - matrix of size n X m
+ *   B - matrix of size m X k
+ *   X - output matrix of size n X k
+ *   alpha - real constant
+ *   n, m, k - dimensions of A, B
+ *
+ * Note: This function re-writes the contents of X.
+ *
+ **************************************************************************/
+void matT_mat(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k);
+
+
+
+/**************************************************************************
+ * Tensor-matrix multiplication. 
+ *
+ * This function accepts a 3-D tensor A of size n X m X k
+ * and a 2-D matrix B of size l X k.
+ * The function computes the 3-D tensor X of size n X m X l, where
+ *
+ *   X(i,j,:) = B*A(i,j,:)
+ *
+ * for all i,j.
+ *
+ * Parameters:
+ *   A - tensor of size n X m X k
+ *   B - matrix of size l X k
+ *   X - output tensor of size n X m X l
+ *   alpha - real constant
+ *   n, m, k, l - dimensions of A, B
+ *
+ * Note: This function re-writes the contents of X.
+ *
+ **************************************************************************/
+void tens_mat(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k, mwSize l);
+
+
+
+/**************************************************************************
+ * Tensor-matrix-transpose multiplication. 
+ *
+ * This function accepts a 3-D tensor A of size n X m X k
+ * and a 2-D matrix B of size k X l.
+ * The function computes the 3-D tensor X of size n X m X l, where
+ *
+ *   X(i,j,:) = B'*A(i,j,:)
+ *
+ * for all i,j.
+ *
+ * Parameters:
+ *   A - tensor of size n X m X k
+ *   B - matrix of size k X l
+ *   X - output tensor of size n X m X l
+ *   alpha - real constant
+ *   n, m, k, l - dimensions of A, B
+ *
+ * Note: This function re-writes the contents of X.
+ *
+ **************************************************************************/
+void tens_matT(double alpha, double A[], double B[], double X[], mwSize n, mwSize m, mwSize k, mwSize l);
+
+
+
+/**************************************************************************
+ * Vector-vector sum.
+ *
+ * Computes an operation of the form:
+ *
+ *   y := alpha*x + y
+ *
+ * Parameters:
+ *   x - vector of length n
+ *   y - output vector of length n
+ *   alpha - real constant
+ *   n - length of x,y
+ *
+ * Note: This function re-writes the contents of y.
+ *
+ **************************************************************************/
+void vec_sum(double alpha, double x[], double y[], mwSize n);
+
+
+
+/**************************************************************************
+ * Triangular back substitution.
+ *
+ * Solve the set of linear equations
+ *
+ *   T*x = b
+ *
+ * where T is lower or upper triangular.
+ *
+ * Parameters:
+ *   ul - 'U' for upper triangular, 'L' for lower triangular
+ *   A  - matrix of size n x m containing T
+ *   b  - vector of length k
+ *   x  - output vector of length k
+ *   n  - size of first dimension of A
+ *   k  - the size of the equation set, k<=n,m
+ *
+ * Note:
+ *   The matrix A can be of any size n X m, as long as n,m >= k. 
+ *   Only the lower/upper triangle of the submatrix A(1:k,1:k) defines the
+ *   matrix T (depending on the parameter ul).
+ *
+ **************************************************************************/
+void backsubst(char ul, double A[], double b[], double x[], mwSize n, mwSize k);
+
+
+
+/**************************************************************************
+ * Solve a set of equations using a Cholesky decomposition.
+ *
+ * Solve the set of linear equations
+ *
+ *   M*x = b
+ *
+ * where M is positive definite with a known Cholesky decomposition:
+ * either M=L*L' (L lower triangular) or M=U'*U (U upper triangular).
+ *
+ * Parameters:
+ *   ul - 'U' for upper triangular, 'L' for lower triangular decomposition
+ *   A  - matrix of size n x m with the Cholesky decomposition of M
+ *   b  - vector of length k
+ *   x  - output vector of length k
+ *   n  - size of first dimension of A
+ *   k  - the size of the equation set, k<=n,m
+ *
+ * Note:
+ *   The matrix A can be of any size n X m, as long as n,m >= k. 
+ *   Only the lower/upper triangle of the submatrix A(1:k,1:k) is used as
+ *   the Cholesky decomposition of M (depending on the parameter ul).
+ *
+ **************************************************************************/
+void cholsolve(char ul, double A[], double b[], double x[], mwSize n, mwSize k);
+
+
+
+/**************************************************************************
+ * Maximum absolute value.
+ *
+ * Returns the index of the coefficient with maximal absolute value in a vector.
+ *
+ * Parameters:
+ *   x - vector of length n
+ *   n - length of x
+ *
+ **************************************************************************/
+mwIndex maxabs(double x[], mwSize n);
+
+
+
+/**************************************************************************
+ * Maximum vector element.
+ *
+ * Returns the index of the maximal coefficient in a vector.
+ *
+ * Parameters:
+ *   x - vector of length n
+ *   n - length of x
+ *
+ **************************************************************************/
+mwIndex maxpos(double x[], mwSize n);
+
+
+
+/**************************************************************************
+ * Vector-vector dot product.
+ *
+ * Computes an operation of the form:
+ *
+ *   c = a'*b
+ *
+ * Parameters:
+ *   a, b - vectors of length n
+ *   n - length of a,b
+ *
+ * Returns: The dot product c.
+ *
+ **************************************************************************/
+double dotprod(double a[], double b[], mwSize n);
+
+
+
+/**************************************************************************
+ * Indexed vector assignment.
+ *
+ * Perform a permutation assignment of the form
+ *
+ *   y = x(ind)
+ *
+ * where ind is an array of indices to x.
+ *
+ * Parameters:
+ *   y - output vector of length k
+ *   x - input vector of arbitrary length
+ *   ind - array of indices into x (indices begin at 0)
+ *   k - length of the array ind
+ *
+ **************************************************************************/
+void vec_assign(double y[], double x[], mwIndex ind[], mwSize k);
+
+
+
+/**************************************************************************
+ * Matrix transpose.
+ *
+ * Computes Y := X'
+ *
+ * Parameters:
+ *   X - input matrix of size n X m
+ *   Y - output matrix of size m X n
+ *   n, m - dimensions of X
+ *
+ **************************************************************************/
+void transpose(double X[], double Y[], mwSize n, mwSize m);
+
+
+
+/**************************************************************************
+ * Print a matrix.
+ *
+ * Parameters:
+ *   A - matrix of size n X m
+ *   n, m - dimensions of A
+ *   matname - name of matrix to display
+ *
+ **************************************************************************/
+void printmat(double A[], int n, int m, char* matname);
+
+
+
+/**************************************************************************
+ * Print a sparse matrix.
+ *
+ * Parameters:
+ *   A - sparse matrix of type double
+ *   matname - name of matrix to display
+ *
+ **************************************************************************/
+void printspmat(mxArray *A, char* matname);
+
+
+#endif
+
diff --git a/IDB-DL/private/omp2mex.c b/IDB-DL/private/omp2mex.c
new file mode 100644
index 0000000..2b16f46
--- /dev/null
+++ b/IDB-DL/private/omp2mex.c
@@ -0,0 +1,156 @@
+/**************************************************************************
+ *
+ * File name: omp2mex.c
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 18.8.2009
+ *
+ *************************************************************************/
+
+#include "ompcore.h"
+#include "omputils.h"
+#include "mexutils.h"
+
+
+/* Input Arguments */
+
+#define	IN_D	        prhs[0]
+#define IN_X          prhs[1]
+#define IN_DtX        prhs[2]
+#define IN_XtX        prhs[3]
+#define IN_G          prhs[4]
+#define IN_EPS        prhs[5]
+#define IN_SPARSE_G   prhs[6]
+#define IN_MSGDELTA   prhs[7]
+#define IN_MAXATOMS   prhs[8]
+#define IN_PROFILE    prhs[9]
+
+
+/* Output Arguments */
+
+#define	GAMMA_OUT     plhs[0]
+
+
+/***************************************************************************************/
+
+
+void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray*prhs[])
+
+{
+  double *D, *x, *DtX, *XtX, *G, eps, msgdelta;
+  int gmode, maxatoms, profile;
+  mwSize m, n, L;    /* D is n x m , X is n x L, DtX is m x L */
+
+  
+  /* check parameters */
+  
+  checkmatrix(IN_D, "OMP2", "D");
+  checkmatrix(IN_X, "OMP2", "X");
+  checkmatrix(IN_DtX, "OMP2", "DtX");
+  checkmatrix(IN_XtX, "OMP2", "XtX");
+  checkmatrix(IN_G, "OMP2", "G");
+  
+  checkscalar(IN_EPS, "OMP2", "EPSILON");
+  checkscalar(IN_SPARSE_G, "OMP2", "sparse_g");
+  checkscalar(IN_MSGDELTA, "OMP2", "msgdelta");
+  checkscalar(IN_MAXATOMS, "OMP2", "maxatoms");
+  checkscalar(IN_PROFILE, "OMP2", "profile");
+  
+  
+  /* get parameters */
+  
+  x = D = DtX = XtX = G = 0;
+  
+  if (!mxIsEmpty(IN_D))
+    D = mxGetPr(IN_D);
+  
+  if (!mxIsEmpty(IN_X))
+    x = mxGetPr(IN_X);
+  
+  if (!mxIsEmpty(IN_DtX))
+    DtX = mxGetPr(IN_DtX);
+  
+  if (!mxIsEmpty(IN_XtX))
+    XtX = mxGetPr(IN_XtX);
+  
+  if (!mxIsEmpty(IN_G))
+    G = mxGetPr(IN_G);
+  
+  eps = mxGetScalar(IN_EPS);
+  if ((int)(mxGetScalar(IN_SPARSE_G)+1e-2)) {
+    gmode = SPARSE_GAMMA;
+  }
+  else {
+    gmode = FULL_GAMMA;
+  }
+  msgdelta = mxGetScalar(IN_MSGDELTA);
+  if (mxGetScalar(IN_MAXATOMS) < -1e-5) {
+    maxatoms = -1;
+  }
+  else {
+    maxatoms = (int)(mxGetScalar(IN_MAXATOMS)+1e-2);
+  }
+  profile = (int)(mxGetScalar(IN_PROFILE)+1e-2);
+  
+  
+  /* check sizes */
+  
+  if (D && x) {
+    n = mxGetM(IN_D);
+    m = mxGetN(IN_D);
+    L = mxGetN(IN_X);
+    
+    if (mxGetM(IN_X) != n) {
+      mexErrMsgTxt("D and X have incompatible sizes.");
+    }
+    
+    if (G) {
+      if (mxGetN(IN_G)!=mxGetM(IN_G)) {
+        mexErrMsgTxt("G must be a square matrix.");
+      }
+      if (mxGetN(IN_G) != m) {
+        mexErrMsgTxt("D and G have incompatible sizes.");
+      }
+    }
+  }
+  
+  else if (DtX && XtX) {
+    m = mxGetM(IN_DtX);
+    L = mxGetN(IN_DtX);
+    
+    /* set n to an arbitrary value that is at least the max possible number of selected atoms */
+    
+    if (maxatoms>0) {
+      n = maxatoms;
+    }
+    else {
+      n = m;
+    }
+    
+    if ( !(mxGetM(IN_XtX)==L && mxGetN(IN_XtX)==1) && !(mxGetM(IN_XtX)==1 && mxGetN(IN_XtX)==L) ) {
+      mexErrMsgTxt("DtX and XtX have incompatible sizes.");
+    }
+    
+    if (mxGetN(IN_G)!=mxGetM(IN_G)) {
+      mexErrMsgTxt("G must be a square matrix.");
+    }
+    if (mxGetN(IN_G) != m) {
+      mexErrMsgTxt("DtX and G have incompatible sizes.");
+    }
+  }
+  
+  else {
+    mexErrMsgTxt("Either D and X, or DtX and XtX, must be specified.");
+  }
+  
+  
+  /* Do OMP! */
+  
+  GAMMA_OUT = ompcore(D, x, DtX, XtX, G, n, m, L, maxatoms, eps, gmode, profile, msgdelta, 1);
+  
+  return;
+}
diff --git a/IDB-DL/private/omp2mex.m b/IDB-DL/private/omp2mex.m
new file mode 100644
index 0000000..f6f4403
--- /dev/null
+++ b/IDB-DL/private/omp2mex.m
@@ -0,0 +1,23 @@
+%This is the Matlab interface to the OMP2 MEX implementation.
+%The function is not for independent use, only through omp2.m.
+
+
+%OMP2MEX Matlab interface to the OMP2 MEX implementation.
+%  GAMMA = OMP2MEX(D,X,DtX,XtX,G,EPSILON,SPARSE_G,MSGDELTA,MAXATOMS,PROFILE)
+%  invokes the OMP2 MEX function according to the specified parameters. Not
+%  all the parameters are required. Those among D, X, DtX, XtX and G which
+%  are not specified should be passed as [].
+%
+%  EPSILON - the target error.
+%  SPARSE_G - returns a sparse GAMMA when nonzero, full GAMMA when zero.
+%  MSGDELTA - the delay in secs between messages. Zero means no messages.
+%  MAXATOMS - the max number of atoms per signal, negative for no max.
+%  PROFILE - nonzero means that profiling information should be printed.
+
+
+%  Ron Rubinstein
+%  Computer Science Department
+%  Technion, Haifa 32000 Israel
+%  ronrubin@cs
+%
+%  April 2009
diff --git a/IDB-DL/private/omp2mex.mexa64 b/IDB-DL/private/omp2mex.mexa64
new file mode 100644
index 0000000000000000000000000000000000000000..c27d7fbe9a04f6eb6bb569d46a605ce6a5c9eb1d
GIT binary patch
literal 35784
zcmeHw3wTu3+3ucPAV6?}g2wx3p+qh*+?0sage0;DX8?iN&;nyfCXkvNnHdNwZFDlq
z?l_8`((?a3{KfuqN>8<L`is*34NeG<2#Nu`a1o7o+v9kNu|m+6Iq$bFJ3Et%+H=lx
z&U2oJ&9n2Z^{#Jy>s#Mi>sxEDy_xm1BFF7X7K<XUWaS46i;d(+o=8Y*k{pVXr%YFd
z;qSG|HH<f<H>qE!aX5!0RWf*-%1EW4h{w}srgJ_?Wjf_!<;~~mN*qnQh~tu)^9@2$
zxSFSj*Y&0IlX<*EP?;|YFy-qI`8q^CNi&E6m!xSt%5)?>w~6$4Y7=~t#^-An`Qqtl
zkxx=)3t$W1_m=;!ls8}GYq*OGj;BjR*N{|}cPH|ZUB2rjn|XmKFHt+>iF!*aif8sD
z8PsQH&n;HK7A9R}b5WSOEX66ku4&MRuND7W!I()Mv8}txw?FZRfBXtbEd6_+B$xEh
z$U~^n)`*}&I9-2CUO%kkv9UK#d*EN5<4FI%PXM~OKdk&T0e%UJ8HoRE0)EoZhCBZH
z4*&znX-FV{TLS(^67Z8@29i&<x8aVzUH~u<{{-N+cxJ?J$9??(`1l0n{viSGN`POR
z0RLqId};#xBy=38ULPmGpG_dAGy(sb1o%@4<os&_{#z38S0=y%3FI^;;J+sU|H1_P
zB?<6M4A>FM=P`)ubG0*mtMZu2;SnOW81!Prs{C2ZLo1}?ak*Ah*Vec^-qL!n%cZ#N
zi=3`<cfET>rN`^8U*s&Ps;zM^DqUXX=4k_`xXKzzT@{rzrB#*px|M2o!|lGBGH+#V
zjZ)oU_Y_svdDkK|+wEOgR$5hB&%;hm<`REVeZAAOVo`&aWAp13m&;REUs>a=aFwmR
z2dLdsSnDG-C|FQiU03C95CVMF%iZ<!DheyB-8CK*>T%sXC5HrfOUv$|?5>K^$|{y|
zVO?pxN62tj5mr#|F7>)O&hjxm?e)FkIb|a3EUl`lEmO+yhcx0jxmM0nZ+&G0<MXVl
zM=jjdWpz~N1#X_Hx?!Q)JNMq|QqIJps3oJaFn7azR%2$6)umMnA+u%$cy6zElg<TP
zl*?7-U0dgNd26d`*SPClq!Ub7QCWp{Tji^)cbAveRjzi|lP#30%H`D!*`8V!a2p{Y
z**6hZ#zQdZWC+Q|U3$}0#aUFM%y!sk6}Tp6PtKlZgeOfkf>W|hVFR7or#tqG$oYj5
zlG!Z8RxOB`XTQNp8fGN=XA%Dj-%hnq*S!k&5HY8rt22&mck;OkQH90@U6k)jFi1b^
zyP|T4q6}89GSX9(m`Er7Up)RxD3GRPA)WGgFDL#%$`ld5q_Pq#uT<qW5g*CoDT-ah
zx%^~9zFZH<b;u4e?%KtAN8)t?PkVs0APx8><DEa_ak7rI6d}3^AI4eO-!2m_mWmAT
zG2voK$MDQXMqn8}hx3@C@EM#eMmFxfvKo1qaC2Mcm~iP@h;zCLH@9$}2`5?dQX6@Q
zFlh@y<~Pz`t`8c<xJnv%kobohsG{6$!qZLoN)v81;dLgQ+FxD`jXXs1r7tC<v623A
zea!RP`bGvbyIsPA?C%B>KGKA5GU204_!bj>sR`d^!Y?!7J4|?{3EySHFE`;~6MlsW
zZ#Usrn(z)2ew7J7X2P#F;hiS@8WXOY@M}$YmkGC-@E#L>oe5XOc%gQ=-i%)eU$DUc
zLkoOiz2+;`|8bfcOg&tyC~6?=O^S7>{=I3fOsm+m(}25UW8MX^UaJuQN=oUDyc3JX
zHn2G6CBzRKadN=jkrpFPj<-AVKa6+^i$7_^X`<+kJZ{9v@peZZFyb_5x+ANMI62zx
zNU0Gg2iqOF(}<H}?T+Ldahh<tBa@7HI*VUt#A)K`j*KwkG~skdEJmChZ+GPL^Rm9=
za8Z9FPL3A!H{#@AQGX*&jurJc;^a`fBTpJ}a-^ug5hn+V`Wta_oT$GMCx?mp8*y@!
zsJ{^>2Z{O{adM2PzY!;g*d4jfh?65k{bd}jeG1zh)@utD*4~JJ5*L3zE*{VST3q_e
zaq)d|@#eVr&bat9aq+Ej@y&7ZjdAf%Ts#mLUl$km#l@@R;_kTk(ztkWT-+HKFN%xL
zjEhf;i%*P;kBN(49Ty)J7atxMPnB^sG;;{TYA7{{NN$gJ6kNd}Job?*7!vEWUfYP?
zp|lDL{^M1|KkW#S8_ug*m)i2_ZE8zTvTE6{9zO3K2>~00fV5a=1?xvLKlzKsnH!N;
z@r^H5{WI&qsi@lf-gGrMb2V^%?LT5My&QvYe`+=2mZivRlu!91Ymi8RV&u#JVg6nD
z#rcaC!i%j`HQ#zwJE(?6jh?}jyp~8XSDyx#qXzFwL&_yQB^5-!hQk?vzq*BG_!LAR
z%;8MHA9DB&g1g|yAjAgvRSt&<KF;ARz%Ow4_XPiq3&{bzgTqe{e2T-<0sof6_Yu6G
z!+C&z!Qpa(w{utp{1Ato1aD&S4E<4<D*pv)pQf<2X&d}7RZ?FASZyl`%Y@(XgbaNd
zPiU73n|MN|Uc?i2$%Mywf=!>m6L!dipYwz){l6fg+d86X6H3g{11wgOdJ5WgTPK7A
z;e6}H{YBR2&gA>|Tfj11UoJ9iKrByRgjjwv+7UuBhW`=SDy%oQTOYcW%UAU&LSm34
z7Fs8CIs-B9Fx9^<tXKn+h|9mOU9mP<Fxeou3&AryZ-)Nx-%)?9Xd6n((C2*3B3tZb
z`-%des9K9^o$K7DYR*O^XX-yD-rS-s)~3}wd#0`u<nOUqo9-hhH1lV<u=#_8qV@H2
zUy;B-19WHc%b5fgw+2eN5SxCKxN?iOS)1<RLY9+|nc$85i1EzOw}8d2wMA4oUPwyQ
zA7he>!jX9#$<Wtvq&;#MM>6$$II=6Ulp{8MF-LYp$~ls!pGB3sttF}RP$bz-kNln`
z&Csj4F;Mjx`Xru@tdypY=8-KdGMl(^iyD1%VA&n8>^3w%tLs0`A;nbps~IP&yKg+>
zI7u9+w|5N;cMW#&bvt;Xm5Av#BQ}F#+YsALv56FGL&i+~x0I2r9#{iq*=)%0pL2m*
ztbuyM*HTSFGaD)QXHYuwC<n$<QEvhGi6ws#xrCT+OU$<oz00`)e~LCag3d-quxN)G
zbna4v#qDa)7j|edJw_cU=!D;}VA+G=>OS`4{3ZEI^OxnjmbOYaL++(P)xNfCU3Tqo
zxf)CvlSM|+iu6fgHMFi#4Gr&tPJbx}x*H9w=+`lmzO7%53N(e`-?X>&PkLDRTFRm4
z5JRv?FIT5<X}-&#LBo%iwi4^b+#X4*-;5sCeTe)ss%-45$}dS4WIoD^XR0hWtFo0C
z`lvF+q{_o!Mbjj$IZ8rq;EGwihbR_3%hD{q=cBvzLty-8)V!vznx9cQkoh5GN{js`
z4|9v1Cx(P-c5+pOnh$cNExvsuve2yN*nd*Z-`&)wVO61K7gz1lz8YOD4M$Dv1Z(td
z{W);{Gj>_lx4<_@c~s&zXP8|sqVN|8Q=6>jmU)j@`dDU+)C0_`DHUVO`zB?b&a=WY
z(U<fqO{(?Ir4M|ZM#DmDZluwr?d)V^V;7vVs)ZE999kd~9?*dpbK)Zc9Y8T7gyw^q
z2S_^xNZ^QdV?alOe_a;*>O2VdubU1}zMhIHE3&E9?M0dJEH?1`8KVb7awCN?N;B2q
zx(xk`FR5sZj6f#ATAN<VkU$QLzOC2uXoiShLY344J*06P353L=47H^*8E6LiZiv{8
zJn+D3FhG@XgeL4GMu+y9{uGy%MvTbk2u%&}q_eulleVEC;=Gqfd#GHhh!!}8Ag|qd
z=36!sV7G2RMaI8_v)ib=;8s$^p?#=N<7gI(8tAn^?b=?|-%O^4AN~_tYBV$Tt=pSP
zuKv~Es4jsnDnA(DEfmnH+JUgO>EDrqbn)iE2|m*Srf%!vPtZ)y>_XVT&ZaaBXKK%2
zCRja$n?gfHLd|(7Rexy=eA)Y~5VC#;N%pVHgf?e~lTib_Zv^g!JnM6;k<pmK#DOfX
z7Sl%yl(5)NG95bAF^IBT&N$G$C=~Lg0o=sW0@E4UoJOQApn{P35nPENxmN`91RB|j
z2eiMiblF>Wa-(EYcC_<NUl8S8rH%CoA&q(q1tV`8)mncT@e1o_C%aYaxTTpe)n;lI
z(MQOr>rZfwFmZ${tXappt#_UzMru`LeX+ut-EMvO7}#L-5M_teLzD_S+%|;@$ZA2_
z)k}EzaD{czQN+ijvv~ID)#D+2LJNc)p;9`!t$X$iB8HQbDf6PenBVtQSc{KWSnq13
zxt`-UGU~a9GKW!P8bXn$*~$gO>#&ZAFVlb8q7LbRWUfClhoO6bw2WMeKWcEZLPR@;
z4E}e)e5?<1WGGVge}_pU*C5nwU6NV>rFfCnzx3QD%bm)~W&Mho?RRlz>x9|fL(yBQ
z=w7pJ?Q6E-pHJ#F*F;wPEW=!<SlTz3>uJW&-&`EOF_ow%Pnzo(nd=Uw_P5wzCfVRO
z-C(C}2eQF(Na|w)ChR;dkTC2QeNHs69tDuoDB{sARNn2ZJT|EHi5#cl5-DQ%M%EU=
zoiw~u?X3RLmDH)Yw-rN_FE@@}k8XS*UG!aa#s<paZzhkRcuV0=ncE2NoX&KJ>7P?s
z{&g7`$)94!1Rb0PB=~4F3YX~*r>MDme4j^N0ta=!B{{74t>j72WM`nt&RqycH)3s-
zp$6G(02yq+3$24}7D4XRYmi&tjO`PtIhtD0FDK2Hz>LUFMpLzR>Ur;==e1L}fK|t}
zqno}Mc^>|hY<=cNP}apof1_-X4CWnoZXglCW-_(@kIzUPGrQBe{Yyvk&c@zUHMzMR
zF^Be%Lp!6N?gDGzq_yb*=wwIdH|j#&6C6S&)#EOdufGPbBkO_ZF-5=gI@BYTln@Tv
z8$_NiDloaZlZe*Lm19leux{^$(aE*X2~Ew>ON9b~t!&l^kh@TW!#~P~KrPJ?+L;Gn
zcPdJ9pdY+vqjFj;s&*XaPGh_~bzV7ASZ~Cxj$J$M;0_(N>0yZ*DNzmXB<U|WNbMlD
zgLc!F8;K1uv2wmiLS>jKoEeVbPHqS2ypxM!RU>oEhKl-N?3(p<GSY-r*N;#G1dB5q
z+Ifg4<^{yOou&>x!R)5pJ$)3)<#T2Y$^_p|-otKWz03O0Vb->xO;iU<D9ox7VjbEN
zBGWo7&rn;cGdAx4u{L?IM~Jlp%8lvgFevQWepwzQDf(7y=EL~gz&t0Evcpenk!mWr
zz?zW)Jr{?e!^B@sq~Ua<ZhiPpUOw5`p-nyBhSzmM;iikITnru}xvka*&(kQPX<{M*
zrvAHl_&g?#JN_~rHayuSEdC9i?DT|z9Y4o!OeJddzXhjZ_GUsctUj;|-jAWcJ76<4
zzy9{e&=ivd^$25)@fYTHb~4*qx9?=t;=Nek&iT7g)5!HyOLN~7f?3~7fs^X(dk*a#
zbPU#ec)!D7;$83I%fS~K`If33W?K*4`LI5wwh?#~3tJqPh?;w>{v)y>Pqt<x@un$h
z=L{bagHGM5bv$zoY*Kw2VKmz@tN#Fo3wkcq5M!UZ4JO`BIq#6lB%G$}H5XH-BLqr}
zxGC}o%%JZ^g`*$#Is=H>0uF~}8U*Vp3lz8)h3ZMe*dS0?Hxaz#IO@QJZLw=eG1XcV
z*8d29Du*;}U(?tdfe~x+q7LmV3^ghb0vfr1Q{$w7AEKn)a1<=(8yLcD^fc{ZttTf9
zmKl=o#16BZG*HrR3=T#AEiy%(AbxW*@<INgo{y$cwG$5Q1C&nVT~B91GB$T0Lc*A>
ztV^&s$a)vvd20j5+0-D%8Y?Il1;Iih!-xPeMrhZBJq!fiq|GdHya6;8YiBqs6+EbY
z4kOKM*aZWm2J0eN+8j`mN47zYl-H>zP&ar#QV0PkXy(})7@hV4r&qIhA@-U6jRO<^
z$mtlO6@}KUy()X=fK?hXU~DKRI#Jd3sE0rDcVZU8LW|Er2WS?G+MvUdFdt=;+U>df
z>z87@Hyn~&n5!1QLJK_HY<#1Cb+68%3~>v(ihS!L^jKQSWgW$84%~O`L3|&2?_SR)
zRz&pQXo!*>Fn)v0nF#40M94ZE<xe!a!9XU{Q;DIM{~?`OlQ9LGGl=6|f_gcgWfHmc
zG?v6y%c8(h9HN|&d*BK&5b`MeEW&y_C5{iI0e}g}Fyq4A)@7*;*dakh0`rkzeXyBW
zH*-0}s)jaBC&v@onZro)R3bqvQ^&JKzIFWuw1*)O)m=%s$gytZ2`^GfF!r(*Br=iX
z+2e_+9hMFMVl4HA#Vz{9=w{RlsnEh;fC>$Uc$vj`#N5fGhh|;^{$1qU^;I8|b~6hA
z=&vCh`I_cs>>nPEuwFrJ$tp`N7>@jyslS<t>AQ56dSU@Hk1_f1RFScn%mVXVZ9xIn
zhyMdb`q$-P8fryIuG_3l^sLeAoKI0qc;o*8o#@%|_oQdAISZoo%{<J!`{O)JgGLYV
z@HPss<>55quSS@rNH@cZ+KsI!Yv2@Lg;0M}AJ|LV0j6y+dMuCJ%qj!pg=UWZz8Jdt
zj1OR`DU>2-l#P3=&m9pCA?VBu`ljnM;anoL@PXd6Qq<q4T%$NZPD_6k020?zxSbL&
zMk3!lIF2QC<af+mY%c+gg-4bh;EX)ZP@7_XY(5^HVdRNVkD*o{!Jd54UhHvW7gdQr
zs#7j>DetrBK_wK{_ycT^J<cLlM_^l^9R#xJsk5^;7#6|*q@+lQZNUVXNUfFYO8cAe
zqP<HdX7$I&v|{yxc|g%GyNv3<9%!-Ckm;DQ(+1I2|BaNLTqDg!@QQgr)s}aX!J9+D
z(9|?FIh28*@SFWTCAmoWjgf7tHnK&yO5b}Bz;;H0A=Y1$m$dh&+Ewj%6sIC!xM${r
zqIXU#8%@14la<dqcc=gx`o0X+vQq^Q7G8IQK)Ea;mJl<eB{ZF4GnQsro6^}zfsI~1
zNBxNL2NWW-M{yZFod=n4lw5VJM>ZI3PyF;!V0{;Z4_|={XiT<^(EusnXzl=ozRkNp
ztq)(#V!XMDgSW<L^da6>RBPS#9^aLN^TqS^vQb^%BY%?xC!aB9FAtx<qxPGKPxP{Q
zc69X=a>_Izu>Ap={0`#T`~W?hPrQvWEqWP`D0nW<ItY(`8Xo;6_=n-GcsRhDkrr)m
zWjF&%Wk`6jo4KzgG*2)E)|eD1h*RK;>nYE-D6pF`4XnT{slcFn!nc}BhqaT8eQtV@
z_Q1#VNtx<TAFUwVfzE2z&e^pu%$s_&>)jTxeIka1wy_cWZQQo`7?Aehtt~WDr?*DO
zsJR{15Y4Z4ZK&%1$iimuDOhNaNJ+-SrS;LxNOOczUIS6)g+^NJ!O6(8ZrmLqhowi&
zJ-fz6*(8S@Q-5%h>W`!$^G{QezPgjxf24-e_mK#@HkEaBwv<msX(LjGsi9SA_Ml@3
z@<5rNkfX_c#rhz9LcqpEXe!SdOmATvD(CQ=j{E>2yT)oWBE3}&Ewrd16;EA{yhfFG
zXivRHHCMH!Q;Y_m<?-NOPf@yE>$cx;z^;YuH}p86u0z%S$}&O#V?ahB;8`v}JBk6{
zaDy6bV(E_HQw+C<7LA6^9o9#lMAudQQA_j<RXZ9z(s$E09j>|qpC7<gFKyLtdxv*$
z^i3<~z!z2jdCTe?HoLLyGT<p#LoE(t;9wGSQAcc7t+Vm{2Q8`g`>-(YXl1O?FKPc8
z`?%ign5!X9<V;8QlJx!PLh1W?raiv*dOMTp-7IaS6}8(nnB<IIdkt@Co!VZu5LLBT
z^rB9(VVg7Xp4X0cq-l;X1)~`x)^2&l?%$tg&)w@C2lIHNTzz_4cmoZ*7^ANQA++=x
zs(%kuKk7Rj-A|m?$_IxcQWv#FcPQM4&P^3;^?jh<LnZt7W|}59s;QG46MY=c-ZAqx
z%y{uLH+0n=wGAVJ#>bvCNA3w<mm@U#Wd?*6<Mq%fyZ`;a+qIs2rlRU^vpKYA)JYBl
zHhUL$6!%f4R^&Ix$1I6Dq=(6#FlEV5R0Jc1G;Hy8+WjqQcFO^$rPG;v+}iXjR`j`)
z)z7F}_Z^|aQ5ZT?+Z-C&ux|-y+%!mpu#&?Fs}z}oKP3^0Wl#)%O5ULu8fWnw{3$t(
zVlmqG*c`cCzGgL4kmAs2(fb;1_`wqFQ7-O)4NFFlAxA(#>%%iJLLztJk1*lVR-*$f
zRD(-1oPig;mqEdKq4ajS74HwjcI~iTa45CXTu10fX*94y>BpnP?f%v@JG1U#?^K7@
z?hFpI=eDfI=H<x!cv{5PrR;k!8`aM+7}$Krqc7kMnta>BHvKWdyPR5!Gjvth-bPKO
z*r9+k_ZTUV<OnTDqeAzf&=#P_y_2D>Gni!04f8^K3q%VYrs@x&>JMREGdJuz8ExSu
z+O;FHws(X^nrn-p>+4d3`DwJ;n1`mr+`XTdN*zhf?e^x{gGrP-iih<USfZ`ZK*RV%
zT~Rdp3Cd0n^TJzvZ$<xN)E4%}CLBumzEj&{?^|6vukPZsd?wc!IO(&(P*iWH1y3<2
zyq~iK2UfVr>GVWGBPRV7`m>)3rqX%8v}5EPi|&&)UWmc$z#b3YqGqbRu{-Rc^aG~G
zR;jV=qWTM+S}Q$MhpuXsjUC3=KIXl}9vp@@s|OrG#{udep3`bD9ZjBoKx#wdcQJM1
zAs8dV{?z1uHZ{5U9>lJ_&a4qEwvpTVo?Uwhi+8-^=`iW#$kj=&#b}n3zPpSLY|lNK
zZ~fICcnA<SnG`i0@ZR7IE(-TJa!0=IdrK<B+>c`~mMl`GJ(g&goRx~s<j9S9|GPbS
zsWZ5Ye1+#j=6j>hOUoHA+#s&Qu6>OSUW@{#w%@M3g8j;K_~yad^d60Ptz%9o{VS8^
z%%aY~8EX?wpV-4<(<k0Xcgziq{*wAQn~n3EDd^DFk?W2A5dO#lPh!tK=luapnUUs%
zX+E-trY6yY&DrDVb_X36TD(wyOX2gVwWM$lDmVgvX;L_iPky4$+5P)0YVy(!XYQw~
zM^U%^bY3XsGuDG%pdzDN9Km(%^wGvuvLEBgc5w>+j7ZtTJ8#>Xh{^4e%MH+zL-eD5
z_EELsutO`_#oY1}aLdr}n5tpX`h}_;a-wNN>D&ox&i1+TJzi+_*Vye){U82a)z<C6
z{DO~SI!HPG%?$H(_)Ns^|KRVD4*<gzvKeA=3Vd-1wwgvHk)l-T_u!OZxrp7;?aY1C
z+O!qym`+oy4>ch)Co~eT#(Us`k3cc3`LeKS#TJr~xfp^<FqSg%&~X>fqTZ`hj9l<6
za>1PxBNsf2Trf?(%mq^mH`_zE3m1%4Wy!0!5700i0-dZ6t%R}WhEk433J{1doLN1J
z@!e)oExryld0o3Bcik>;Hche-42|&IP<jvO4mG)`17jB-Wu&+8X)B+v<gSERbi)rR
zhYhYA+b+jYRhq=`&S^2~HUdTn(?ZM_HML~F=0k=KG{GkNSWGM4ftKu`HpAqAP4HF+
z8U~#m40utD4nG?03-^fLZcMcBk8q#<bsczFahyyax>YpyI`p&Z9hd{zngK6!9Tt>P
zv<;tobU^rvc6wZDD`M6FZAWG$T8nXz=CB-a<hFSy!1`8lTT$6hS=XnD*wk5RDt<58
z4sVHG(B-fkvRh6%b6@pdbVumcxA6vn{Ks?APSs!3j;EudcGcqC<w&mHf=^*OR)3ES
zh-WVPSZduaRILZweA~E@xxu5mP`R)o7FXCX-vN`wjTeXtuhC6r=JsH3exv$;Hn(f&
z`OM@PC1iC6dFtv6H0r*1tJ|2>$;X(ie!#FgxffWSc^0(PmV_2R^i39j(PZ&6aTbT!
z`&t~M&Sdce%;K*}i@(#~;yzg1ZCLyhX7L@|;v@f$TKsQVf^&=0%rZvw8+xAO>**Bg
zJZy)WMn(ip+0u&P`66FLgKIEbiBIUW5>Ic#DjaUYg?C<A_(-jH6%L2nXB7@7=$**c
zrHd9q*hQE~U#5*#OdyWX53!DVRSn&nM*9T$Y+Ey$-U{2%L{Hrw?qk_7jNExWSngPy
zx$WNZ-Lvqdc8Hw#8xF10p&fx#I4v6BOVE=K@G;)w)1%L$`|+g?DKUdGI`;S2wS)Gv
zN9`@|Cfh@KF^uIFhb8R53I>yz{p<;Q%V|nDkA!ykm{-vAqJvwVmg5e~Vb%W`JrFLl
z(*%?Lm3J(PcAUo~+vdT;2f&OJHXJb)pb9+-V7(QI?qN$F(xI-;0v%4|3(SA)VU8*H
zeJFRxe)e^H%Lhz3N2t;=Cv@v4Fc&6OQmM-sS~97}5gNIN^uwDt3wv2|!+xi>&wlol
zz2*I6D4B-o_j;@$qiIn53%mcLv^zp6<4E)LFHq2kber8DvA~&-MGoRwuf+pfV!WN`
zYfF4=PyVu9*pfao>$T?u(Ws~da$Q6Z7NT9@Gnl`FQE%qc>rm+NuHEvvL!%`KYti(t
z(EMo84(~)#BmF2%?#Eg01WO-7VzOrw+bh2JO>N4GH$SuCds?g;UbSm4V@=}JUeUhR
zhaJW9l6Ch<HK+trHu-yeUYozC$~zgGfMGm%>OZFRzRy!mt;eB3QM_iw%Y=39I_)t<
zPxOOr*eHq`YguD$gfH(h@z98gqa9yuhMjm!)Lz!UMk_Lt=;sd8bM{_+)seoMRe7^W
zy?M}V8EMvnblNXnOMLMf>JIIkL&JL0hfT1v`bO+`IW+>|i?Q<ffWTDJubIOp-XEYg
z{dkuW@8jf@X==HeHG(6E2?Y&9?qNhq8*hOYpKernp_+T3{zm#(s)@FP9T?ZtPcg+`
zgN7Fiyo2i5lPZRqm^gj{`PL>{gn*r~fc;u*4G1=5WI_acI-kLgIdTts7m)Y*%pOX~
zge_l3kE014t_ja(mRB4YXCFro8|+v?Q%yd@0C|J5fPE0y6-<RJ2WK}g!%ZD2i{^Ii
zi+p<SKZN#3A0fAk(>r1@?N~nMBaG$o*y?=r7pO|80J{N2yVSCia+;A-w<9$3Z`hoQ
zE-utQ(%yoo1Bfx}>MgIp<Ue6OeKhv8`6m9;0NKjzZ~3{WuyhU0{FRYm1JbbWfltb}
zs56|qyjP)6d>2|2X5An(x{ED?@YV3oejnY3tV@in#gx^*H_J2|_nUy(f2*M{M)A=G
z*y%$qd<b&z5Cz#ciCb83KVFSP_Bnh_!0U02VE_H2(-iL%rU2?Z7$visy=4y;p29?2
zXECbyBKYY`0DJC|(P?Y2grE7WA?q2Iwl{46J?V>9d~o%(+BTb<47RpsqYy?j1+@v9
zC1{SI(*?~FR26i-pe01xX5Y<8IVUSQsp6!L6E7zXoZL@DUrRb`aJo>t_d#B&>pwqq
z&G+FALZedY`-`%aKdF|EAH+KKS(I%&{u4q$T3f;0Sj;H;jeR8PHiILht)QGZqHpTM
z3^22;U?l_F^fNC@Sy^obRSf)-{*D38Z!2hE;AZ_L1I%kHsAJ%I{doga+X}n{M(VJX
z)_-H*C2a-wGrWZGhYj2onN7fI1IUToM1b1>rbosAFgly~zYp5i`P%FG+RKjMiVR2c
ziZtArxU+E2$8E!%gL^veJlrbok^qc2?feiJF_an|4q`3rLg%?2o720XvZB|$6pIxx
zuKko8N`0}H>mY)hYYw;ywNH_gxHxtbF#mmb<7HM9g8cWDBQyl{#UhBkF)Ogq`@s@?
zmw{}Ps6aA?9y>_cs=%CLX3inz@tk>)!F&YFhP>&-lEzsU8!X$Iyavd7lc}*1p|4n|
z4xvnx;lIy|&`m6KKSGNTf=Yvo0aPVRv0KOC!-Qq2|4wiCLo-Jb2bdLo4miZ{T1u-t
zUO_zNElXX)c@?q?<GlvFkxwvR7<_pCTb4R6p6|>7@V#Q@%Obw9oG;6ecZm32HS^6U
zz8GFALB|Gz@4pe>-_3mR-^)_pi0At$@x5i{%Ok$+@qBj?U)ap265qq|eA&eJTQgq?
z@wwvp1`%Hq_^4mE4gNX)Q%4)VNXi+^rbgzdjQ^1cn_svtSm1&ME?D4#1uj_Nf(8D!
zEKsP-bIzZv6naaP65O+uqWKH$j(Kwx5BuRE7k<shv!dKx<t<h4Q$VHO+G>wdS6@rN
z-J{I5Rr@?%+j6(9)aF^`E3J3is@cy9Wh+Xdt+b}xR$^OOy4r24tSPIlt}FFc;^&ZT
z9{l2x2ORw3e6zou&s)MJf_{@sRD|(;o5Z3@?@D*QO{h7+R*P_N5fhA>dFtF{l@*ol
za-s2kBmX%2oqL;Z`+ViQ>v#6Nw|h+Xq-h?T_!%RQ&AYPHYa5e285lE5Ir|ByUb86Q
zjeoVf%v)PeMSeGaPZ|A6)wlBVHf1&W7P*rq7TU`2dsDX3`uftf$bt5|1x>dCznEnk
zQ*OHve<s-69&aUD++7X>RF_uP;K!eAK-{<3#^g*czeW6+mOZ&*s8U{Ex~8Uf&5VI0
zVL90M>bi}`I|a=txM9g-d%nyv@Pc2DfAXPUHw~Kkum3{VIl<TULIu0duy|urc0Qh!
z==zzI&rP6I`0!g=#$|gtkB8^x*=z7SR+Z&8{E}5^%?cl^DjLs5KVfx?(J#i1Egn0T
zdd=9ej<NWF@4M!6^L`hXedrjEZKiGLP{n4mu^%53g~E{K6PK@@IHsJZjj5_2mMj>5
z(hbOgIBb`S|Hv^G_pP|YxbMV$824%1_u<}vy8`z%+_Or(Wh*CQ%+t7^Sg^9T%I&#l
zEk$hAwdK@u9u&2x){A!L&F!&ayqCE>9(OsZgH*bP4jo6A%{HGo18Vuonu^+bbO-#1
zmkl9Eve_6DWuYHH^LgEDknt{pdf04ZZqA-kRblhE%W7-NJtjUlBhi%x=5v-d*h-gU
zz_ISec)n3yp|{Y?+pk<l8BAgG)s>?=n;84?$$FI4x+}g5-vSfUH`TY$+lRAXxy4+w
zIJW-w6MXe<PYoKwD7>FMdri5!!RD#0_ZmC{+Hc}_yLhKSzu-M5u2uTA^H9ZdW%3OA
zP0?}4v+d8ZSOdbF{xcTa0=f;9o|p4?$6_56--EBr@KS0@Yb-Vz^aQ91`oP{;tO~Rn
zbOUH;AM$~&KZYF>&<)39vA=^>o{Yt^k`?7optpja0;S)3{jYDvV)ui-bt)En8uaeB
zP#)+JQ2MQhxo^i}DJhDw7c>iWAwCsyfSw0!0KMik#uVtOcVn?vLGOGY{Hge@41BLT
z74$59!wNbbAJ;AcZ3f){ny<%VPk|1{H`nihJ`S3R$K5MF!cGe42cUJJ!=w1`zMv;S
zw}BqP|3-Kf^xjWkkHLy^!>6zZ=xv{&9MH?Up%*A!^b<35S?*n+SQ;`cSEdh6+W=dR
z28Pe^m8@N;BjU<M8Mj}QImbF|O<JRJ+ZDHrn=<-(pe%nOXcyXsZ3_~Ji{viBtpo3*
z{NkDbN}G9=e~rc1Z#5QOl<`nf!SF%O$C4jS38g+V$Uivw&J<|DGuS|$!o4Mo&#(!S
z{6+rcq?JR#mp{w~xE=T4mRRg}pow@sM$*r}g(m~L4o5#)6)$HYFN^F#pL_iXJim_T
z`3dJCnCeOW_GR#WjkgMhPV-Imygb9mK(?dbTHW+oES3gJR~ByS=a0M|iybFS*8Q$W
zlKm;kJ1s_T{@Z>pfhQMz+D@5qkxa6E4Y2VH;WGaQECB2U!lX=-{gZyr%};f(VO(Tl
ze9WTq=ZdzP{b+J1<&jkXpyVtIwLt6smtH;uUa-su|F{#e*n<cgvh&ea4O}MmDKB`|
zgQwoilT^tVNatT5{RGnQr*vF4+}nW7!gyRwfYcdsR|y*s&r9I>1$Z8d<4FqevdNxZ
z;E93f4`v>tKKB_GrMh2*vHeqweH+zXmT5P&Uvh5?kZcF|Yv3bZNW}jPS&R3L2Jl+o
zFW9c{K(dQ_Ws@%3z)v4P)+ds^u$P}~cntgx<Fn4G2-8K_$H0CEET19RHY1v%D6aww
zP<W1L-va7i70?w8o1Z=?!=Lg{(j&=9P9P*_D)NkhPivw)xNgO*0^11eB?6=z!>&ng
zF6`w)Oxz%dOAVzwnw)ee8HDs$kNnrcKQfszKhr~Gjnjkd{a55!g*+OSDQ#%($3?t3
ziB|{jA@DN&2I5`Dd8vLQ;cIK~&G%T6i_3;P3)lm|UM4{1r+(c^HQ?jQ0iJR2?LB55
zvp%PLc^klc2E5x6@jk(MZAhm%pdNF?b~7*ezbAWn+QIV=@LX!{AK<xP^l0isAA{#J
z%q_<-28=owZIryew+He5auw!~Utx~HOSlWy1q)oTzy%9ju)qZiT(H0e3tX_k1q)oT
zzy%9ju)qZiT(H3Z2Nsa$bjWi#Xq$kp(;^__(YQFBHA0t6XTPb3csfJzhc--b;S(};
zZBaSJ4>zz&o)>bZh|^vPT~~0T$a6zzUyv@D{^j{tEyZcSjxKCnuxkhQB<K=Ev0+b(
zF8N|%q2R|CaO}zyRIuTPTR0%tlnxP)``}_*T)_`5uxp19Eb%lEmwIj!eE2+)U9voU
z7tZvbFB0&XH;c=1@eLb`uNUcf_t+=BOT<$|`Z*Cd>LJSezeVIZcJbxqi1yDDbiSZ>
z3tA^=qo5lE-6H4?LBoP}2-+!Vm!R^Hk_@pKWfL?<&^$rs3wpPpb%Hhux<Sw_g6<GB
zENF+Ioq~1=Di8UR=hw_GD7eLzRlM9+<Mr8cv!`U|Oq_JHk3}c_baGC1&eR(?9B;4z
z;>j3Yw7(sHCF5mQ`+#_|F)qw`=-eSW9{S>`iX1n6@j;60Uw!exitM+2@iayD*S`1=
zMUIQU_)tZTpT0Oor)*bKLNdIHZ9sgul0olK;;v-nBE>c!ZtZ=sWy+I`LZyE)!Ai1%
zMxJj*csLo2F3&kMrzR_xD93(iMkN18Mfy8)YO*rQ@N<3fOO=7fRWiJfJRhkqKfFt$
z8R6k%<#I*#OLJ;6ylln;W<>H!Z`_+k{yk-YbJxhvC*gcG<3*v!a*@Duq~jF$bpn_3
zjDVG!5wMu_k@Lyz4DWBBY9U9?TT;$i#^2x0n}83b|8EoE&m_Q~Pk`@9fFDeN)Bms;
zNS{P@*1>N<ze-t#K-@KuoHO8|c3Dk^#bpx)J&!Qq%kmkl$afg@RXyQP<Z+z62SJd|
zn<Tt;CdWlK<qClx6EBe1yE?FZU*PXd;{0|_l!*dACnok)0?!rr{%IUw@9z*S0!}CQ
z#gDHN#@~OOE)#M_;&*T9V(%kCR|xzkqD!-PvcT!R8r7@2fVdT9J15Emz%5Guar7&}
z|KbeJ$lkLd_@uy}FXA|RcY@${fzJ{0<vQRmO#TR^YXRq%>xP4Z{|{ne^gEucydm%f
z!fq0OSKz5)VZ`1cBM+_5EEw<c<NB=NA2*o`9xvq6dX4zw{X;tD6|&DSr*lU3&Jw{<
z0#D1~ID7Am;I#r@FpJ|ooUn7{NY1VJC33nR<3zbh@CWiaz}}l7m?!Wdc^ua`QRWDI
zj_4=y-EXnLXNdkO-)AlbPOHdxJ5&n(y9DEdLO#87CO!ZB0}jak-w50S|9@7rmt1Gk
zPj3-_@hzO6`V(E71g_l3<MLholLEJi_6_o6<r#rLS;ztQeh$Gu11J46AK<wBOFz{}
z<xUfNHi?9TjK6=o9})Zqh5i2^_}>wDvFH!%T|d$y0uQh+-*6s?;6Eeq9m385fzvO$
zT9p3t)G**lINdpZ9v=bxVv*NOE>D2ddSxK~2?_9Nz-?y1B3uZ3pmRgj1acN7z?USz
zS0un!CBU1252WXo1pI$Z!2de%>j$WBcLF(IGra#fsiRT`wlke<Gm!nW65#aie<1!@
z3Gm_s_^Jf>{R!}2C&0HRz@JWl{|CeSpBMW=0{#;T@GlbJNpK(o*~gjyzcK+nHUXZS
z09VTDy&kWxq9VIY={@Gw<*jz%RM#4}2dBoC*Sc0z)h;isa+TwwVUMfS*Pzh%)>UqN
znVUU5=O#K@b^s<i|F+V_KJaz9YrOSqm5Tb(YPYN0S6#gpSxhk(IK6#XA)g(;%1(0i
zc`K_tP!M2QZM~a^Bv`$6c~z-Mr7y>kTIQltTk9%a;>=e-F4ye~@|{JlqPc}Q)fZop
zx^OG_4%$`fq3=)?`aaWBTeTYJ%9b|N)q03$5suu24CsJQ)0ca|<1V{LeBsNIDZi%<
z!f_<Ht4I}PsD%p@R(URi1hAa+!Lw^pxk8mCP4O*gsp6XLm^Ulm;hJ~*?F)+*xfbQm
zaum4~FAnY{4Hw~Kc%1o5-1PY?k9q1y_l5KG7c4At&CYi^^IZ!UE#PIKFf@|Tlzp`=
ztp+V|da*D7Qyhnoq2WBWu9f(f9^c!uDv@ZMnTvYj+f7~`LAZr6sKVa*(i#tp<W`pB
zL}8C_xrcKIdz9Cb#^sQ{+~eU#DVqfJ=5ZA(w--AcraH)aEBIp9GkGQcGXS$3O2=u!
zN@;mHS%SX)RM4f$O_Cg5Ak3iPmF-5VlH$|?hN7-wuPNeCT!oA0<~!{L3hRV8RT!r-
zvxA61aTGH}<$1((&@vs89Ct=EoekXg5M~^0%tZ8_(cF8KasLCBzv+x=R{q=s2RhT)
z)ZgL!>AXDi$;H(T;@D=4kpxF18z(E9k86e<<iXE4p1SumW_J2>-y-5qn(j@V$_%xT
zA38n2xy`+tQy8b|q-;7Jo1gn^I!(LJq1dL(IEp#pdDQVIPxn1+8%<|4H>xbo=cY5P
zd&ScM(ELE_UThK;j@h2I)!x$Opx$~;SISULt=FBsqQ;kv4qWH1$MN6U2DIE)Syeu<
zvRq*dD@#2qmF)7hH4w(Bx1OiS6Ra7Vh`2;983k*-yQ-A(i%?yaSIH)qqhuqPy`okG
zFv65<uNx<fXOp7_cP$$~+3uCXHLom(G#TSExR2&JWEe%2R#!q8L{kw^njBj;#=eq`
zi2{d^dk0vF{BsaJrqMGImYeL7`(BdT1TKl(XO(gM7n45u<^GtYqa~xDn5O$&`H10n
zIBRA3a(`J;@z{fBcYei}e+OW!dJX;Mew(Blu>PV8(=^u2z5CgC;$+uC{3RAyzTB6S
zRPJAMZZ1QHiT)Tj?G?-Xa{o?JhhUcaOFl_gBAxc4WnAv_Nh%%(nf}BRU;j10XzyC)
zm-~T|HX@xas{cUw9|BIg$^3HPP||Hkqf6>9%a``sAkt<rF2&eil>3ZwKcDhbp7{L#
z3XIxCzCSRYUorsVqW)>Vq#hF6h=4i2+^>|BeJd}SL4{a@Zi&k;_caY_%1goc{J)RO
zZxf46Njph7(-oipPe?QCZ|v)h<e9dJ=VmEF=9lqZarxzbs-*HgkmQfI-)@m#w!g9O
zI-logFdBx(<ICS4mtXE5NlM?`o3HrtUyaKz_i-i7i7Q|BA1U)K1ZeM540B_@H%uFr
zxZ?R`T+)xggWXgkzkL7I{vGqvz8|S5%a{Ad9U^~*Ff5Hfx@5W1j!yw26UqE?pSiP^
zxE005{`UTn`6Z1Z$(&zl6ufnu%KZ?58s*FUly1&1_odRrhN0w(&oAQ_Av5tv{pJ2o
zMuPlQmieOnPQr4`Ai^^LxHw$qH&dhUnt1a>`FGQXIj*a5yKu|;NdHBz^Q7(w8rPO}
dJV@V=n=e_e#6}=6Q2vh}<Q!MUWr&L^{}WW~?R5YE

literal 0
HcmV?d00001

diff --git a/IDB-DL/private/omp2mex.mexmaci64 b/IDB-DL/private/omp2mex.mexmaci64
new file mode 100644
index 0000000000000000000000000000000000000000..7dbe5490f52ac584acac305c256b641ccc7617e1
GIT binary patch
literal 68760
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
H9WMMI{{TdX

literal 0
HcmV?d00001

diff --git a/IDB-DL/private/omp2mex.mexw64 b/IDB-DL/private/omp2mex.mexw64
new file mode 100644
index 0000000000000000000000000000000000000000..7fddcd587bf09dc698484f9be745622a4d07769b
GIT binary patch
literal 22528
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
TIe$L*#LyGS{PXt@C4v77ux9%{

literal 0
HcmV?d00001

diff --git a/IDB-DL/private/ompcore.c b/IDB-DL/private/ompcore.c
new file mode 100644
index 0000000..b4fe24a
--- /dev/null
+++ b/IDB-DL/private/ompcore.c
@@ -0,0 +1,409 @@
+/**************************************************************************
+ *
+ * File name: ompcore.c
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 25.8.2009
+ *
+ *************************************************************************/
+
+
+#include "ompcore.h"
+#include "omputils.h"
+#include "ompprof.h"
+#include "myblas.h"
+#include <math.h>
+#include <string.h>
+
+
+
+/******************************************************************************
+ *                                                                            *
+ *                           Batch-OMP Implementation                         *
+ *                                                                            *
+ ******************************************************************************/  
+
+mxArray* ompcore(double D[], double x[], double DtX[], double XtX[], double G[], mwSize n, mwSize m, mwSize L,
+                 int T, double eps, int gamma_mode, int profile, double msg_delta, int erroromp)
+{
+  
+  profdata pd;
+  mxArray *Gamma;
+  mwIndex i, j, signum, pos, *ind, *gammaIr, *gammaJc, gamma_count;
+  mwSize allocated_coefs, allocated_cols;
+  int DtX_specified, XtX_specified, batchomp, standardomp, *selected_atoms;
+  double *alpha, *r, *Lchol, *c, *Gsub, *Dsub, sum, *gammaPr, *tempvec1, *tempvec2; 
+  double eps2, resnorm, delta, deltaprev, secs_remain;
+  int mins_remain, hrs_remain;
+  clock_t lastprint_time, starttime;
+ 
+  
+  
+  /*** status flags ***/
+  
+  DtX_specified = (DtX!=0);   /* indicates whether D'*x was provided */
+  XtX_specified = (XtX!=0);   /* indicates whether sum(x.*x) was provided */
+  
+  standardomp = (G==0);       /* batch-omp or standard omp are selected depending on availability of G */
+  batchomp = !standardomp;
+  
+  
+  
+  /*** allocate output matrix ***/
+  
+  
+  if (gamma_mode == FULL_GAMMA) {
+    
+    /* allocate full matrix of size m X L */
+    
+    Gamma = mxCreateDoubleMatrix(m, L, mxREAL);
+    gammaPr = mxGetPr(Gamma);
+    gammaIr = 0;
+    gammaJc = 0;
+  }
+  else {
+    
+    /* allocate sparse matrix with room for allocated_coefs nonzeros */
+    
+    /* for error-omp, begin with L*sqrt(n)/2 allocated nonzeros, otherwise allocate L*T nonzeros */
+    allocated_coefs = erroromp ? (mwSize)(ceil(L*sqrt((double)n)/2.0) + 1.01) : L*T;
+    Gamma = mxCreateSparse(m, L, allocated_coefs, mxREAL);
+    gammaPr = mxGetPr(Gamma);
+    gammaIr = mxGetIr(Gamma);
+    gammaJc = mxGetJc(Gamma);
+    gamma_count = 0;
+    gammaJc[0] = 0;
+  }
+  
+  
+  /*** helper arrays ***/
+  
+  alpha = (double*)mxMalloc(m*sizeof(double));        /* contains D'*residual */
+  ind = (mwIndex*)mxMalloc(n*sizeof(mwIndex));        /* indices of selected atoms */
+  selected_atoms = (int*)mxMalloc(m*sizeof(int));     /* binary array with 1's for selected atoms */
+  c = (double*)mxMalloc(n*sizeof(double));            /* orthogonal projection result */
+  
+  /* current number of columns in Dsub / Gsub / Lchol */
+  allocated_cols = erroromp ? (mwSize)(ceil(sqrt((double)n)/2.0) + 1.01) : T;
+  
+  /* Cholesky decomposition of D_I'*D_I */
+  Lchol = (double*)mxMalloc(n*allocated_cols*sizeof(double));
+
+  /* temporary vectors for various computations */
+  tempvec1 = (double*)mxMalloc(m*sizeof(double));
+  tempvec2 = (double*)mxMalloc(m*sizeof(double));
+  
+  if (batchomp) {
+    /* matrix containing G(:,ind) - the columns of G corresponding to the selected atoms, in order of selection */
+    Gsub = (double*)mxMalloc(m*allocated_cols*sizeof(double));
+  }
+  else {
+    /* matrix containing D(:,ind) - the selected atoms from D, in order of selection */
+    Dsub = (double*)mxMalloc(n*allocated_cols*sizeof(double));
+    
+    /* stores the residual */
+    r = (double*)mxMalloc(n*sizeof(double));        
+  }
+  
+  if (!DtX_specified) {
+    /* contains D'*x for the current signal */
+    DtX = (double*)mxMalloc(m*sizeof(double));  
+  }
+  
+  
+  
+  /*** initializations for error omp ***/
+  
+  if (erroromp) {
+    eps2 = eps*eps;        /* compute eps^2 */
+    if (T<0 || T>n) {      /* unspecified max atom num - set max atoms to n */
+      T = n;
+    }
+  }
+  
+  
+  
+  /*** initialize timers ***/
+  
+  initprofdata(&pd);             /* initialize profiling counters */
+  starttime = clock();           /* record starting time for eta computations */
+  lastprint_time = starttime;    /* time of last status display */
+  
+  
+  
+  /**********************   perform omp for each signal   **********************/
+  
+  
+  
+  for (signum=0; signum<L; ++signum) {
+    
+    
+    /* initialize residual norm and deltaprev for error-omp */
+    
+    if (erroromp) {
+      if (XtX_specified) {
+        resnorm = XtX[signum];
+      }
+      else {
+        resnorm = dotprod(x+n*signum, x+n*signum, n);
+        addproftime(&pd, XtX_TIME);
+      }
+      deltaprev = 0;     /* delta tracks the value of gamma'*G*gamma */
+    }
+    else {
+      /* ignore residual norm stopping criterion */
+      eps2 = 0;
+      resnorm = 1;
+    }
+    
+    
+    if (resnorm>eps2 && T>0) {
+      
+      /* compute DtX */
+      
+      if (!DtX_specified) {
+        matT_vec(1, D, x+n*signum, DtX, n, m);
+        addproftime(&pd, DtX_TIME);
+      }
+      
+      
+      /* initialize alpha := DtX */
+      
+      memcpy(alpha, DtX + m*signum*DtX_specified, m*sizeof(double));
+      
+      
+      /* mark all atoms as unselected */
+      
+      for (i=0; i<m; ++i) {
+        selected_atoms[i] = 0;
+      }
+      
+    }
+    
+
+    /* main loop */
+    
+    i=0;
+    while (resnorm>eps2 && i<T) {
+
+      /* index of next atom */
+      
+      pos = maxabs(alpha, m);
+      addproftime(&pd, MAXABS_TIME);
+      
+      
+      /* stop criterion: selected same atom twice, or inner product too small */
+      
+      if (selected_atoms[pos] || alpha[pos]*alpha[pos]<1e-14) {
+        break;
+      }
+      
+      
+      /* mark selected atom */
+      
+      ind[i] = pos;
+      selected_atoms[pos] = 1;
+      
+      
+      /* matrix reallocation */
+      
+      if (erroromp && i>=allocated_cols) {
+        
+        allocated_cols = (mwSize)(ceil(allocated_cols*MAT_INC_FACTOR) + 1.01);
+        
+        Lchol = (double*)mxRealloc(Lchol,n*allocated_cols*sizeof(double));
+        
+        batchomp ? (Gsub = (double*)mxRealloc(Gsub,m*allocated_cols*sizeof(double))) :
+                   (Dsub = (double*)mxRealloc(Dsub,n*allocated_cols*sizeof(double))) ;
+      }
+      
+      
+      /* append column to Gsub or Dsub */
+      
+      if (batchomp) {
+        memcpy(Gsub+i*m, G+pos*m, m*sizeof(double));
+      }
+      else {
+        memcpy(Dsub+i*n, D+pos*n, n*sizeof(double));
+      }
+      
+      
+      /*** Cholesky update ***/
+      
+      if (i==0) {
+        *Lchol = 1;
+      }
+      else {
+        
+        /* incremental Cholesky decomposition: compute next row of Lchol */
+        
+        if (standardomp) {
+          matT_vec(1, Dsub, D+n*pos, tempvec1, n, i);      /* compute tempvec1 := Dsub'*d where d is new atom */
+          addproftime(&pd, DtD_TIME);
+        }
+        else {
+          vec_assign(tempvec1, Gsub+i*m, ind, i);          /* extract tempvec1 := Gsub(ind,i) */
+        }
+        backsubst('L', Lchol, tempvec1, tempvec2, n, i);   /* compute tempvec2 = Lchol \ tempvec1 */
+        for (j=0; j<i; ++j) {                              /* write tempvec2 to end of Lchol */
+          Lchol[j*n+i] = tempvec2[j];
+        }
+        
+        /* compute Lchol(i,i) */
+        sum = 0;
+        for (j=0; j<i; ++j) {         /* compute sum of squares of last row without Lchol(i,i) */
+          sum += SQR(Lchol[j*n+i]);
+        }
+        if ( (1-sum) <= 1e-14 ) {     /* Lchol(i,i) is zero => selected atoms are dependent */
+          break;
+        }
+        Lchol[i*n+i] = sqrt(1-sum);
+      }
+      
+      addproftime(&pd, LCHOL_TIME);
+
+      i++;
+      
+      
+      /* perform orthogonal projection and compute sparse coefficients */
+      
+      vec_assign(tempvec1, DtX + m*signum*DtX_specified, ind, i);   /* extract tempvec1 = DtX(ind) */
+      cholsolve('L', Lchol, tempvec1, c, n, i);                     /* solve LL'c = tempvec1 for c */
+      addproftime(&pd, COMPCOEF_TIME);
+      
+
+      /* update alpha = D'*residual */
+      
+      if (standardomp) {
+        mat_vec(-1, Dsub, c, r, n, i);             /* compute r := -Dsub*c */
+        vec_sum(1, x+n*signum, r, n);              /* compute r := x+r */
+        
+        
+        /*memcpy(r, x+n*signum, n*sizeof(double));   /* assign r := x */
+        /*mat_vec1(-1, Dsub, c, 1, r, n, i);         /* compute r := r-Dsub*c */
+        
+        addproftime(&pd, COMPRES_TIME);
+        matT_vec(1, D, r, alpha, n, m);            /* compute alpha := D'*r */
+        addproftime(&pd, DtR_TIME);
+        
+        /* update residual norm */
+        if (erroromp) {
+          resnorm = dotprod(r, r, n);
+          addproftime(&pd, UPDATE_RESNORM_TIME);
+        }
+      }
+      else {
+        mat_vec(1, Gsub, c, tempvec1, m, i);                              /* compute tempvec1 := Gsub*c */
+        memcpy(alpha, DtX + m*signum*DtX_specified, m*sizeof(double));    /* set alpha = D'*x */
+        vec_sum(-1, tempvec1, alpha, m);                                  /* compute alpha := alpha - tempvec1 */
+        addproftime(&pd, UPDATE_DtR_TIME);
+        
+        /* update residual norm */
+        if (erroromp) {
+          vec_assign(tempvec2, tempvec1, ind, i);      /* assign tempvec2 := tempvec1(ind) */
+          delta = dotprod(c,tempvec2,i);               /* compute c'*tempvec2 */
+          resnorm = resnorm - delta + deltaprev;       /* residual norm update */
+          deltaprev = delta;
+          addproftime(&pd, UPDATE_RESNORM_TIME);
+        }
+      }
+    }
+    
+    
+    /*** generate output vector gamma ***/
+
+    if (gamma_mode == FULL_GAMMA) {    /* write the coefs in c to their correct positions in gamma */
+      for (j=0; j<i; ++j) {
+        gammaPr[m*signum + ind[j]] = c[j];
+      }
+    }
+    else {
+      /* sort the coefs by index before writing them to gamma */
+      quicksort(ind,c,i);
+      addproftime(&pd, INDEXSORT_TIME);
+      
+      /* gamma is full - reallocate */
+      if (gamma_count+i >= allocated_coefs) {
+        
+        while(gamma_count+i >= allocated_coefs) {
+          allocated_coefs = (mwSize)(ceil(GAMMA_INC_FACTOR*allocated_coefs) + 1.01);
+        }
+        
+        mxSetNzmax(Gamma, allocated_coefs);
+        mxSetPr(Gamma, mxRealloc(gammaPr, allocated_coefs*sizeof(double)));
+        mxSetIr(Gamma, mxRealloc(gammaIr, allocated_coefs*sizeof(mwIndex)));
+        
+        gammaPr = mxGetPr(Gamma);
+        gammaIr = mxGetIr(Gamma);
+      }
+      
+      /* append coefs to gamma and update the indices */
+      for (j=0; j<i; ++j) {
+        gammaPr[gamma_count] = c[j];
+        gammaIr[gamma_count] = ind[j];
+        gamma_count++;
+      }
+      gammaJc[signum+1] = gammaJc[signum] + i;
+    }
+    
+    
+    
+    /*** display status messages ***/
+    
+    if (msg_delta>0 && (clock()-lastprint_time)/(double)CLOCKS_PER_SEC >= msg_delta)
+    {
+      lastprint_time = clock();
+      
+      /* estimated remainig time */
+      secs2hms( ((L-signum-1)/(double)(signum+1)) * ((lastprint_time-starttime)/(double)CLOCKS_PER_SEC) ,
+        &hrs_remain, &mins_remain, &secs_remain);
+      
+      mexPrintf("omp: signal %d / %d, estimated remaining time: %02d:%02d:%05.2f\n",        
+        signum+1, L, hrs_remain, mins_remain, secs_remain);
+      mexEvalString("drawnow;");
+    }
+    
+  }
+  
+  /* end omp */
+  
+  
+  
+  /*** print final messages ***/
+  
+  if (msg_delta>0) {
+    mexPrintf("omp: signal %d / %d\n", signum, L);
+  }
+  
+  if (profile) {
+    printprofinfo(&pd, erroromp, batchomp, L);
+  }
+  
+  
+  
+  /* free memory */
+  
+  if (!DtX_specified) {
+    mxFree(DtX);
+  }
+  if (standardomp) {
+    mxFree(r);
+    mxFree(Dsub);
+  }
+  else {
+    mxFree(Gsub);
+  }  
+  mxFree(tempvec2);
+  mxFree(tempvec1);
+  mxFree(Lchol);
+  mxFree(c);
+  mxFree(selected_atoms);
+  mxFree(ind);
+  mxFree(alpha);
+  
+  return Gamma;
+}
diff --git a/IDB-DL/private/ompcore.h b/IDB-DL/private/ompcore.h
new file mode 100644
index 0000000..6fb03a8
--- /dev/null
+++ b/IDB-DL/private/ompcore.h
@@ -0,0 +1,80 @@
+/**************************************************************************
+ *
+ * File name: ompcore.h
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 18.8.2009
+ *
+ * Contains the core implementation of Batch-OMP / OMP-Cholesky.
+ *
+ *************************************************************************/
+
+
+#ifndef __OMP_CORE_H__
+#define __OMP_CORE_H__
+
+
+#include "mex.h"
+
+
+
+/**************************************************************************
+ * Perform Batch-OMP or OMP-Cholesky on a specified set of signals, using
+ * either a fixed number of atoms or an error bound.
+ *
+ * Parameters (not all required):
+ *
+ *   D - the dictionary, of size n X m
+ *   x - the signals, of size n X L
+ *   DtX - D'*x, of size m X L
+ *   XtX - squared norms of the signals in x, sum(x.*x), of length L
+ *   G - D'*D, of size m X m
+ *   T - target sparsity, or maximal number of atoms for error-based OMP
+ *   eps - target residual norm for error-based OMP
+ *   gamma_mode - one of the constants FULL_GAMMA or SPARSE_GAMMA
+ *   profile - if non-zero, profiling info is printed
+ *   msg_delta - positive: the # of seconds between status prints, otherwise: nothing is printed
+ *   erroromp - if nonzero indicates error-based OMP, otherwise fixed sparsity OMP
+ *
+ * Usage:
+ *
+ *   The function can be called using different parameters, and will have
+ *   different complexity depending on the parameters specified. Arrays which
+ *   are not specified should be passed as null (0). When G is specified, 
+ *   Batch-OMP is performed. Otherwise, OMP-Cholesky is performed.
+ *
+ *   Fixed-sparsity usage:
+ *   ---------------------
+ *   Either DtX, or D and x, must be specified. Specifying DtX is more efficient.
+ *   XtX does not need to be specified.
+ *   When D and x are specified, G is not required. However, not providing G
+ *   will significantly degrade efficiency.
+ *   The number of atoms must be specified in T. The value of eps is ignored.
+ *   Finally, set erroromp to 0.
+ *
+ *   Error-OMP usage:
+ *   ----------------
+ *   Either DtX and Xtx, or D and x, must be specified. Specifying DtX and XtX
+ *   is more efficient.
+ *   When D and x are specified, G is not required. However, not providing G
+ *   will significantly degrade efficiency.
+ *   The target error must be specified in eps. A hard limit on the number
+ *   of atoms can also be specified via the parameter T. Otherwise, T should 
+ *   be negative. Finally, set erroromp to nonzero.
+ *
+ *
+ * Returns: 
+ *   An mxArray containing the sparse representations of the signals in x
+ *   (allocated using the appropriate mxCreateXXX() function).
+ *   The array is either full or sparse, depending on gamma_mode.
+ *
+ **************************************************************************/
+mxArray* ompcore(double D[], double x[], double DtX[], double XtX[], double G[], mwSize n, mwSize m, mwSize L,
+                 int T, double eps, int gamma_mode, int profile, double msg_delta, int erroromp);
+
+
+#endif
diff --git a/IDB-DL/private/ompmex.c b/IDB-DL/private/ompmex.c
new file mode 100644
index 0000000..d8d3e9f
--- /dev/null
+++ b/IDB-DL/private/ompmex.c
@@ -0,0 +1,133 @@
+/**************************************************************************
+ *
+ * File name: ompmex.c
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 18.8.2009
+ *
+ *************************************************************************/
+
+#include "ompcore.h"
+#include "omputils.h"
+#include "mexutils.h"
+
+
+/* Input Arguments */
+
+#define IN_D          prhs[0]
+#define IN_X          prhs[1]
+#define IN_DtX        prhs[2]
+#define IN_G          prhs[3]
+#define IN_T          prhs[4]
+#define IN_SPARSE_G   prhs[5]
+#define IN_MSGDELTA   prhs[6]
+#define IN_PROFILE    prhs[7]
+
+
+/* Output Arguments */
+
+#define	GAMMA_OUT     plhs[0]
+
+
+/***************************************************************************************/
+
+
+void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray*prhs[])
+
+{
+  double *D, *x, *DtX, *G, msgdelta;
+  int gmode, profile, T;
+  mwSize m, n, L;   /* D is n x m , X is n x L, DtX is m x L */
+  
+  
+  /* check parameters */
+  
+  checkmatrix(IN_D, "OMP", "D");
+  checkmatrix(IN_X, "OMP", "X");
+  checkmatrix(IN_DtX, "OMP", "DtX");
+  checkmatrix(IN_G, "OMP", "G");
+  
+  checkscalar(IN_T, "OMP", "T");
+  checkscalar(IN_SPARSE_G, "OMP", "sparse_g");
+  checkscalar(IN_MSGDELTA, "OMP", "msgdelta");
+  checkscalar(IN_PROFILE, "OMP", "profile");
+
+  
+  /* get parameters */
+  
+  x = D = DtX = G = 0;
+  
+  if (!mxIsEmpty(IN_D))
+    D = mxGetPr(IN_D);
+  
+  if (!mxIsEmpty(IN_X))
+    x = mxGetPr(IN_X);
+  
+  if (!mxIsEmpty(IN_DtX))
+    DtX = mxGetPr(IN_DtX);
+  
+  if (!mxIsEmpty(IN_G))
+    G = mxGetPr(IN_G);
+  
+  T = (int)(mxGetScalar(IN_T)+1e-2);
+  if ((int)(mxGetScalar(IN_SPARSE_G)+1e-2)) {
+    gmode = SPARSE_GAMMA;
+  }
+  else {
+    gmode = FULL_GAMMA;
+  }
+  msgdelta = mxGetScalar(IN_MSGDELTA);
+  profile = (int)(mxGetScalar(IN_PROFILE)+1e-2);
+  
+  
+  /* check sizes */
+  
+  if (D && x) {
+    n = mxGetM(IN_D);
+    m = mxGetN(IN_D);
+    L = mxGetN(IN_X);
+    
+    if (mxGetM(IN_X) != n) {
+      mexErrMsgTxt("D and X have incompatible sizes.");
+    }
+    
+    if (G) {
+      if (mxGetN(IN_G)!=mxGetM(IN_G)) {
+        mexErrMsgTxt("G must be a square matrix.");
+      }
+      if (mxGetN(IN_G) != m) {
+        mexErrMsgTxt("D and G have incompatible sizes.");
+      }
+    }
+  }
+  
+  else if (DtX) {
+    m = mxGetM(IN_DtX);
+    L = mxGetN(IN_DtX);
+    
+    n = T;  /* arbitrary - it is enough to assume signal length is T */
+    
+    if (mxGetN(IN_G)!=mxGetM(IN_G)) {
+      mexErrMsgTxt("G must be a square matrix.");
+    }
+    if (mxGetN(IN_G) != m) {
+      mexErrMsgTxt("DtX and G have incompatible sizes.");
+    }
+  }
+  
+  else {
+    mexErrMsgTxt("Either D and X, or DtX, must be specified.");
+  }
+  
+  
+  /* Do OMP! */
+  
+  GAMMA_OUT = ompcore(D, x, DtX, 0, G, n, m, L, T, 0, gmode, profile, msgdelta, 0);
+  
+  return;
+}
+
diff --git a/IDB-DL/private/ompmex.m b/IDB-DL/private/ompmex.m
new file mode 100644
index 0000000..6026dd0
--- /dev/null
+++ b/IDB-DL/private/ompmex.m
@@ -0,0 +1,22 @@
+%This is the Matlab interface to the OMP MEX implementation.
+%The function is not for independent use, only through omp.m.
+
+
+%OMPMEX Matlab interface to the OMP MEX implementation.
+%  GAMMA = OMPMEX(D,X,DtX,G,L,SPARSE_G,MSGDELTA,PROFILE) invokes the OMP
+%  MEX function according to the specified parameters. Not all the
+%  parameters are required. Those among D, X, DtX and G which are not
+%  specified should be passed as [].
+%
+%  L - the target sparsity.
+%  SPARSE_G - returns a sparse GAMMA when nonzero, full GAMMA when zero.
+%  MSGDELTA - the delay in secs between messages. Zero means no messages.
+%  PROFILE - nonzero means that profiling information should be printed.
+
+
+%  Ron Rubinstein
+%  Computer Science Department
+%  Technion, Haifa 32000 Israel
+%  ronrubin@cs
+%
+%  April 2009
diff --git a/IDB-DL/private/ompmex.mexa64 b/IDB-DL/private/ompmex.mexa64
new file mode 100644
index 0000000000000000000000000000000000000000..bcabb639e9d7fd527a4fc18b11cbc9565981adff
GIT binary patch
literal 31688
zcmeHwd3+RA_HT6-3=rs!qQ<?)fgviyuqhF75|c<Zc0*)w!U(oa(t&6;)7=n9QJhXI
zHEk=-Z<uiweuFdelNmSkCl2En?GPjpMS{2laf!H9(=IVC#N|i7@3~8*(n+iH-ut}w
zkHx2R?mhRMbI(2Z+;f*Is%|QCUXo_9DDp~IE>c)-9LE%if-JwJP?RENrZO6T&rnWh
zv>5|MLsDzUaFm3KjpwP3OiGG;GW^^5oK8YnPJFDsa$c?^!KkQDLNnh8utb+}zG&TG
zSf0-F6#`1WG@yyEOYn6GJ_&6^fJ?$Go@F|co?AqDGPDai36uFc1z$28FZd*6wg7J7
zyZ);Go%+fJU%};Ea57vfTth-x-(}z<yL{hEcJl&JU#fm6677~yRL|^5GRSA<%v-F0
zEK0h_?xHlgEaj<u!h?T5<G1(S^Ox>++ZD0+#G2!NKk9T8vGN}Sl3dc?R)o}A`&uLw
z!qfF&`nu6wcT7Bg#?8NfjAMrW4FPrRP_%Mm3jA@XW;psIDd<T*JATR6e*-a`oaPkr
zx1^xIEd@OpW;pp|dpmx~*Hb_YM?VR8donTdm*98iF!*y))O%kFygLQ{j1>6aroc~6
zfj<ZxhilgdDe#Y`kmE@~zd8l}o)mKakb?e#6!bMI@R1a9)~2ApCI$V%6!aA-@F!xx
z<|v;eAa2mr$>^=hujX)cjwoFWc&uVo{we06<#M3A-7D%E>fJuSr^)YjD{jXkm%GZ_
z<XutY^Lv{Xxk_pq>b;9R%WJ*7Z1@s)WwXa!T~qI=t@(vlsq;2p5~#2A*EG~Cb<GZ6
zSzV+5I;7@!{R=BSwVo!Pc5yI|gK}WK&1FqZF5ikp&3?u0_BA%u)cdR5l`F3SiNjag
z5Fj-uS<+C~SnF*T0s?i*y-oA0OKa-9^*&VUbDux0fCTtGmDdovyV_Gz%NQ3ndYXJf
zhPRe*B~4zB-^=lgkLl@X8c5Hr6ls^Iwzi>Csl*@Bh;wqSTpoW@O*5nOUEAbGQ|c-k
zsm%+#oT;vPq1Qj}7j+&^#In?6Ld(j$&E>4c%n++QwF@D$eg$YQY4VcJC0vx-UFpBB
z(d+g%)Hba4Hn~YBn6A2}7X5Z@pr*-N<!P*0<!vHcD77`q>zea@4J_d`Qo!V&N4QF!
zf<Yl8A3x7|(-l`)g)+zKxVXeUHGgXU3?n^dx{;igZ%P~3>4Us6IU}8p@ab$0Vrv%U
z%yZu{N*3lL`bYB$|L5<%KAl|mRQyI#0WP>YXe`Q>%lKSHI2spp5!dG+ka0F}a?MCZ
zIYv3fD9=<9qMYb|bLVfNLY6WS<;3GZiRectQ$_x`ni{NxGL?%&ejLkZD5}VF`RRsy
zxgL`1kga0eb&B<l#5W3j*0rPsX}~X;?`hz9vX8VCVRRFIG$&zydrf#KY+U^&{8$64
zD7lS1a|{64Q(krx9!)f^JQLpBn*}DkT+<NKOcUPR$3-SQ)h#d8gqOCUvT_q19b;S-
zCj2M^t0-5Q@YyE(N)z5{!Z(`mHWR+tgqN|Dir1R(=6P+M34feP&UzDGbU7=%%Y;AP
zM8C;|Kf#3GV#1$j!f!R<b4~c|Cj3bzeAI+L*@W*j;ZHH)yG(c*gYxP&;ZKuDz#bEx
zEfhtWZo;2oqVF}~?IwJ`3I8J#UJ>JkeCJFv{)hAr7Wn^YflsZcf29UL$Wp_Z``n76
zhNAwoM3)-enbpp;O3XL}bYEh^+W^)x6rx{AC4I5C5{blmmZw167u#p#DFFAy+KfB}
z-oDtwMxFw0U+iupPZLF7>`o(3fwwPqvyrDk(-*te$Wx&0i+PMZ1=zmWWk#L?YhSF`
z$kT+=7n@?_DZuu{eq`in;^~X!7<rm-`eGI%Pl2~D_Q_G%UK`85W#lQ)qWwmm0xa5Z
z<SDSC{YIVwD%x-4DUhQ5MxFvF+Hd43aH9Q2o&qM?Z{#UZqWwmm0wmgR<S8(s{YIVw
zBHC}{DG;LlGLPPV9mgHkGfEZK-^hQMlz%rVpG^N!Qu&^w{L@MK)};Klr2GR(`OQiB
zjY;_pN%=@pK9rPSlavo6<?E92-lY7pr2OKfyelbRmXx2Jl%J85pPZDRkd!|yDSv!Y
zeoRt6Q|8sk?2$;Tk<2s#h5i2H5elBaf`@{Ui5}}2YvCPAyMUlSu$1U$yg=ZrqpH@c
zwte(dwXHv0wd_*&9rcfcfDJ-GR-&hx`H|$Oc(Hc&2Jk9@a~7+?*-fBSRP9}Vwi=$j
z3V8jx|4AhDDh$3|nRUopmVwu(pZH^|QAml!;4A)F@#V#fix(|K6su6Rz+I~LtQt9f
z{4Azq=S2YFLcIVXNDW_~g_5zn<Uy3^Cvmh5=+7@;48J3E8b{{>eTbvi5;~5!1|fE!
z_i%I#p+_K`0i6f*c8*>^=#ROO0-*oS(X$BM&CxS~{u4)!Cv+P_&(fbj<BOjni?>9r
zEnY-cQd{2)wAxV?l?8MhhXR{^8!zaT1&et>uI}Rn+hxH#USQW>MM0mnBJ(h6?<=)V
z>LK0rodWwJVXc#*p=hyn!>%&xV_y^pcUeGEpx@0%a>_O#H&g!)!SGihMt6l0{?Tf1
zO;oXlK1Hoab}H7E%~I38Z_os-Y%5cH+t)0!-ch*|xGlit>UVS8I+oc<)P-g1tf7l#
zi*Ba!!m_Q_P!Uj9E9mvhix(vV>l1;keO8-mQ?>Qus59)^;tZE<|0lIu4KMCg!-1$%
zOX$b3UUI=bESTNUE8gxaikB8IE55SWy{ugg&(d=s-=THH!ia;=Axl@dLebc-ImV`c
z^c7RPGxmFq$<<%unC-Dm3^Pj~&6|lP&C>VsLTYlBzJq7hv&_4^En5PY!c5y?rpa(=
z*6=@aGH!(@IoZ0vR91Eqm5~Knj#6!r+5a^Yt`h@M>_d*IBSbe4@q1BRLb=_Piy}9J
za!(NP1Dtq0A?_o@LmY80A^t#!{{%vgFdA9Ss`j-*>vd@Rs?>1Cggi2?R;EvZ+%;>}
z$e7LGe7p+SC($*E{u9VhwKw(Cp>RtSLmTP7FIoBw;?QRhLAXq>Qm1igffLay%~zl&
zKzbszGuWTD>b3Yj{TImmJF3hbtO~d1oBDt7>X|Al%&PpIDjTHAG?OZ~fs|BPy`L1z
z=ZabU&r`0qpOskx|A;@S?+4+(qvrne2U+Yhss}PZfJ|wzC{J^XrGKlM?{ZaynzwSL
zErF*=q{FP{q<>S*rGwRU2{k|Gs+};{aK}o+Q5P=&Y5Yz7U!eSV>~h@T3ST4T(TIDF
zFuRPUbU)I}J-B6JL^7xYCrCX&d_9#0`z`+0h;=q+g=ON;>2{N91JlUOA7oLS*IL)o
zpwPDUFtDK)@lMqu3UW>@l#Ai&L=K?OniJ{*7BEr{XdWP)7$Bh+tQ$f)3W96$EY_C!
z5FT7J6G3tvRa04JSFI0~<*Hi94jK`*Hc%RoD_0G#vFZI^P}LY2p<F_19eOpxgbG;p
zO?@@b+C(;&8mWc)N#hO-M+$^xHnpuM9atL$8i;5I52BqK4pAeVkx5Szp;P-<{~MQ<
zMTFpUMy7{&(GmT2UbF=j5#>!h+fVgUL$pvglDu_C*<`tq5Dx1@uaoi1Ik}zc3vVVx
zoZ9>P1st2lvIcfMunujf8f+y~BS1dFCdgK%zV)G2lB*{^r?!N8ss3<?_fSZuW`{6&
z{uLagi@yM~!vkF)>a#BS5Z&~^cBF%A>`L<(ruHmmf>k5ADKs=B(prRC_2(uamK|o5
zkoCJra&S#9v^jz;1FGb{5xNTUtdFryMrR5Whw`{uOdl;&!E)Qkba1L}h;mrIaKgPP
z6$xYkc^4}S&17I>7J-hC3P9#ZawU=!UXd&k*w|+5O&(|E(pw(qM#&|1^z*FG2y$2H
zU_K$Fk+)DX_NLLSb^DOVEPAj{wVu5!7pB@s-6DL1jJobsP7x)FXtg!3yU%*rK_aAH
z1?w}_*8EQEt=%Al)g#0Xt4F95aLh~7sDivUl%19{ou&6xTNmv|enJ~7%RjX090;G(
z24OEyEnR)q9Xm!4!FoopXeU-LJF2aV4^&$(Z>N<E$Dhlf?;2u`qQx|XV)xT*9LDh4
zr(-dY8$4uDM|ROVf$0zCD0B~zma!A?M-6XO2x#442)+&C?m@({Q7F~7z@)L$k?ON9
z%{&E4@hYv4_y1JZJDt_b{EC_F-Xyd2z-;fJ>djR3fY~+=Hrts0nmS;vLp*Jm>(?y*
zE#~qtf}!T(_;WJ}`ii8vy2)IZGPS?M2D8Zq_k0R++HsQ&PK2aEHekYzibcSZKC)<N
z9V(!pF_&jIQGJ)N`q-e>r*b?EmslCYZ(w~9-bTYq)sE=5oI*~;qpcXCe7UjzOt|rG
z?CN1j=8Vn65p1Q1p!hv%cmoR?;cYXS4hj7Wsw=q0hLQXc4h!JmG$27oqfvy+U^GK5
z+!6RB_8ch4{c@(T>Nis)K}BCcm2KORj&Hz%)24>mYycT-zzeOzY!(4`=Kcc2l8rby
zkeZ{bu>m2?m%@x-C!?uaCwbmm@VriP3v$y=xal*oe;}Tct?z@Jp|-ZM=(ue?i3qon
zsr9r^NE|b}%lgpg&h%|-2TIlS)=uP{+F_^mh5kN~h7MX=ZiY?{IKR;ra!*hQnbeNa
z)Q*=Bb!0oRu2%HReuQ>pk`f|d`@`Vrr3%wqdkAQ)TshViPU}N`Fgk_yxsmAwdbLm>
zw3*EsAqp32c+8*Ag+MLK8QE3@#FLq*$q7Gr$4>RMTU6};%$>z(x9Plbq_DpBG-}2H
zClBaoO+PE#K!s{}8%cl4Not2r#fp_qn@DVgiIwvW?E#r7TsCKT8@B^=-o{0-rja?m
z$%mL@a9}jxWTXl0IPUDF+7{cK+EIun;$zSr{UMq<_yqGL9c<~xqh3B|)}v0)ZQ~wx
zF7qzyE&EvCM((0ESRzr@ln8TZON318v^+q4sm|KC6~Nl!!|@o_4yZSwe~Cfi(00lC
zAW6|TpF!oJEg+s7$=DjCwMZS+Tw=9lK+h#n=rH+GG_2Zsu5P{cGG0H~*{MxG(82Dz
zFoj#jQoR^F1Pa@&zdTB#h^C3jNSOS0$(Rf#jtBl+o;D&`=VYqtTO!%-7{Sm;#_{K7
z60`>2K+rHpGoct(A6N$O!cgE2*h<~6zw;q9#Uw!<VXQG8XJKcXSYvEs&Ej6HKg#KQ
z(bCwN)Jn7O3Bk<wG7zK&e9x)91;=3C!~G6}iM!sdIJ9#_#=W6x`?jN3l)hqEA5&Wn
zwx>~x(-Kn)yPFP^4SBKEmd3lLqLULGCIX$@s(BK*%~Ce0{sw7uTemg18^Z;jOD)9M
zC%3`G+au>4QkjI)biMjmayr65iJbFdx4{g0I~p8+e;^n@)Fx0kHPawiM=Vg_TvV#3
z;gl6^RhXLyS_&MEAi{~z{&S$1YORmz4<eq*Ax$T@H1={ZVog!hseOf^M)g6!S}x$%
zhJc@;rY8|780RY(!rU2mu-=oC24jZg%W$|VCk@o}B%K}We*shM*F<mbMn1^j*Nf3L
zs`jc=dk?kKc-OO;5ZlHsWJnmZm31i=2YGKJI&W^~cs4c2vBoMYL`ATWV8{s(VeBMS
z%r*m|*XU4!0&fVN#rhe+N(BvCU%)^s8+PFksli+XOPk$l`nV3Lk?}Hl0=dDvu~G;?
zMYHceo52}=4o_qGQmbvuLXMbR$>9Wo)uq<FohsXNz$$AoVC<+SK3UavsQV5FdoT-O
zp~YvR-82iu?a*Oql#jA0osPm?P0KLeo1d3dn5&lTr3D^-Y<$DN`c~x;L(+n-y4bo1
z9!o2^y!}|sf%@_t$UhD5-RZlFRT2MB8lq$ejNfo;E>e2@Fj<FV`Hn_62*_mmC?Xi3
z5674_8Bw^^Mie@s1}Of?By#CvSP@?>iwaNXDB_G=gHVWpP(<lRk=FN6;W?o!AYcMA
z%-PXC>y??4&tk(cRE`4cFI$Q9FI*0hs*w#dDey$L6)?~`od7LU&tZ#v>$>&m4?`fD
zyOOvlux{W5&rnS;_LXfYWFpUD+Y?hiTsdY7j%m;zZThirGx9>Jv@{%|O2ZLe=VELz
zw=wCF*_{*NgzWrgl}_5t?gv7D73tX5G%ssy>b@BB3hGPNSn9!O><Xs-MkZ$P(pl<>
z1<ZU#6u_>+wvo(&em?CnRA9aJztN`PngUEi?MTUWo3(}Z8Uw-kb;^lo{1|kiz2k$V
zXSg*FqV>P<G>h)P<!Kr;`mcC;3#D)5=`5meLYk(?s~N7W(>RK<hF<5Z5b`(m=ACpL
zFzBc-kQ+OnH3r6u%zo`BV(98c@555ls6@^v8+KS9dqD(*uq!tln5oZ3aEZ~v2YS;=
zQ9n#v$8!V)E&TuxC|pPBJydus3i;u|0W7Iw_cC*_vjlV&wk%sg8M~8V?TYn|a%=!$
z<jEc=hA6>yzUVKu-Ple|;#+m%f=l@yg$GqoTH_noFx$=|*GR~YP$vLn)2_2;AQ=_O
ze^F5^LMIO45EH4jb6qF1<^QV#OD1OZJIJ(R^)m{r`iZ!?1y&kTIBCdr&e~>2(i%LM
z*eNv9Y=o#t1Zvw`>7Xs3WMq1lnjWzsDdOhPNJ$|QabsMEs*P(Cp)&9e1hA8laD@45
z`qIvRRXep4TXCuahI^nK5Z*aCcLI55E~}qAcccVI;Q^a!*`|U93$LpHpj;jSON5!x
z5}8T4S<7;*E!k|Pz(y~hqkhilLka;pqc{=UDD)s{f|_f)`(=mG@x+ZM05^Csc<aev
zKxeXJjAqW&+64%GTekyRZ#|9Wcy|*8?~U>BA>LP1mp<WLekzCHi~aSL$8R1_@g@&J
z-ZtSno_-bk^4E}`d=|^+$5&0GpiC12J0GCQ?^*24chlZ{@@GFL0gLuv9fEy%-m{44
zhY-=9Lwp#s_DpibNp0FOHBlRu%8>9(9}8bgX`WyTOfo6Z&t&Bp3T$Ed?@++^Eea&C
zJ7EfpxF&j`xpr7PtzDi@(mkY`KcJ_n)L`~_1?et0t3&(Jp?zvT)T2}Hj*uPeBl3M!
z+prdARou447?6(eg>5ubXSc^EsD)kD2+gk!ZIl{3Y+*C_G%U1pGSacRwBEiEWzI;(
zOC)K2WSqqjo(i5dXI~m|TKd((BdhJiCMg`4`omMyU@QyFH)f)IRS%K>T#aNuO(GoH
zbmr)6DW8toaxzA%k!!OYVdqHjK$)v4&=l^q{*vyVvoR5w&RN6RZHz+Y6uv{Ti;!|?
ztTj2=?P_G9MUAM~b=~$7HQuS+^AfdO)mmO>F#IUbho5+z${kvt<E-5dE$TR{-vxD@
zs`dn9gaAeWMj_x)E<oFl0pEO<8g60b&hR}9?}#iK51qTLx7`ibRfBO${1sK(AAbQe
zrs)PkHbQmRu`E<CYu8H;aR-NQS}_McqXv&!Ru!<>jUAT(pMf>hk|+iaCNVcTVy9}I
zgGV1|Nwq70g?U#yBaMGf9)fdR|BqOxAxdy&gS{eq7hEWN7iZcLcxS+wOt&TJAg!#^
zp}{0yIJB2=gVLq#WD8MM+pEugi)`583ccfZpdV?P<4eJ~jl?=EdmX`DS&qV;{<C2o
zf1ImNyM<TK!AmguDv&}?zoG_rK=u8BL-Acid4}9Ll##k<E!?5>X*f4Eus!ge-bghE
zcjlTVH)^Si0uwzK&2F#qo2$6-GcR)L4z&X#g2u;=EN9`XfnH~1{2qpgEWz!e*B!xk
zzj0{&#Y{yt*kN~S@wkfu25j~=esTQvGqqxW1|PE|+K?S3d%~0zqtFnH6w<IQ(BlZU
zWjQRnU6vkK;Q?#Q?^xAeW~_QZ)%q@tlpc?vGrhy9p$`YwfX>Z=L<p<chqOwWx%g8N
zqnwR$_*3x~<<L1x=HgGq0aT08w!`i$><zT4k&+CjMvLB;@IxG|z!~L|F4(XlhYXnm
z1+BNv!U&07jz7YL%i0YGSg3}V*<7J#{3k-e`H}2Sc@!Uv!gigoU3e7r(mZG6=UFtc
zBiRSyV;sTuEC;jhKL2#5*69k5b`-X)!r|q(UDz$+=u-L~%tq}q3<k2mf%sFngC^q{
z9p2Lf?{;ZzuE?oTM+bG4;(!9K!fsL^%^6vcMV0PArES0-@K1%du5g;8Fv=?(s1QB0
zkD5P{nm>}cW??jNFy6*%bZ9Tg)?OMJXKpQqZlG5U7iW<&^U-yfyLa(g$&u8;K7XMj
zoJQPnY}VUgiH<=74dWASMb+>V)SVsWmA3`nh(B($7WU@N{fSH4;TYUp2XF3@tYRkD
z6*?HO!cf$1mj%0+SN)%`0w-3u>Dja+p%Ih4m;UUcim7()mkx}a?)cNv#tSi+ojBuB
zvAR=vXLmUw*}F}htx{(@MDrKAw07E4M^0^*ogKy4?)G2c2#>~{)!ojpb2s^i?~oeK
zMwe&rmfFzxT|!Pg5@STzpSt{?rY;wcLO8USnKi<T?G(0t?9iUW;vM&Rx=eaG3w6?K
z3A*KA;Bv!(9fkXgt-srW2m!z*ld_iG{<B=+MbUm|;kcIrZ%Bn$_;K#Ul0~Yt!xE2D
zuu|bn&cc}g?~d>ZuJDx<D}3*>*c<<cw48Cn4dS{S+SfSX#VBxTyBykHoL7E{#|f-0
z@6d?Xy5>f*zcOjgEb0n<VQr!56K7a#`o#U{u6dF1pOcTX*|@lsl1^<6h2Hqj5RWW~
zB#y!_{TIQMwk#J+bJ!7?o<<v+BM0Di&pIu%cp-nw;Pa@pqI3run1jDGDICJnF7d}4
z!KW>1`m!!p;YX{EC%64*ek9{#=0Q(UmGMo^@S09~9^zE#$Jp5}$-tkSj2+y0J66X`
zVV6Q~h;|O~!$a(&YKx;zt!z6B%fCighKAj$hDGbAs`k7KT^q^fL0EHj&Xf1?BCEc}
zX@?qo{~J|Xvla6To*wBU<@m!TEY{({3rFz1Z({EOjZnyDh$R__#T7Ve%1I+dsnPEs
zD8X_uho#R|_?oq4GsrQWW>{}&L27Pf9Bz&GBLu$y#jxhf!=V*hNJ8ck2&%wXvK7H`
zmt0KVt5c3b@Wm8@dniXC_+kpdH2Ja+Og-G{h+HB<FjkcnFXFeGhT%x)WW8l2j5RNk
zu|HOVL~`ZK>T!(k4vT6DbgAiUI-P}Uw)^vGl8s?#MCV1a`vJGA>1ACQyIWwxHa=|?
z^Of8w5DPbapE&FY<v4cfMpJ1L!#$@ZXj=}95T%7!Aa3f(A>9XtE_A_N^qiEocq@9c
zi~0<c0}jF4o#+@iI|y*27!E%k{fqDjZ#O1d#7BhB;F>PntT;fXk6b9adky@oZY$<M
zwr0T1T&D$fl;QaoR~Ljo<Dl(QM;WsQU?-TBcmu{kmeaD^S=iy91nXNVY{jLYGS{bx
z*yOA<6~7bjM6`q#^g1oiJ1hrXg)jQYUK+XZP254C`0-f0M-7&BVs}*5sajmyo#}O(
z@T5xDsvna9vF9qgOAW8tj;8hFm~RU=GB<d9I~o^N#NrAE=38O1r11h#(bc-i%-kOA
z&Tm{F*5(ckp3h8<Q9@RCQlzf4p;Mnuwz{2Jonnm1>bniAQ+R>ZS!6*^ZAxkJ=fBP3
z&zLO!MUur~_Q4j%s54o7H?#Ol(&BFowRiv)_Zk-ekXd{yxA?gKs}}zZOK@&+npq|Y
zzoGpcUr%R{^RN?Y8W}NkWm`Lj=QDf}4XR_<N_^6wm3VdsR^bQ}Zrt<AYcsFH5pV~s
z!Vv`hli9j-(Lx9ti-~j(9kgNsaYlZIb<~S$<QG|VPEgE_HRIXsupLeG<n{<3SB}QW
zo!^h;j>T2j=|89MV(iqOry%}{Q|obRFF-1S77g&F@Z{ZmjQ0oh_&?x&e5pf9%pykT
zu6~F1tmDXjN88)!j!01gW4X;~i8`@@!DQw*@~Wfl5EUFnK__C&UU**on0A-tfYY*1
z4Sq};!Yds#!DN5spNOiRM={BE_^|l^(w4!7BgO($p{)ScTe0{Kw&Wol8V4=V5kx-4
z{KqzPOu3&xx#t~6UUsy-$CPtMYAkaj7k&tHVNxZPdR>vFQ~I5eaXUyq+=;WWn<Z!M
za%oRHj=b(@dp8|QW?}k06KlwL78L)~5j>oAX(Z!p(meZ9RFso#cLZY=1QW8zv)Jpk
z`Cv<ow^s+-63>}ayzCUVq(|=t>^VtvDjET<v9z%e{fd~u;vI~7KA&DkL65f`mQS1-
zEkRh1W`Bk5M~`;-CzBf4`)P7Nz`PSAgA9quo=t3f1MiyplvQuuv*A%vtQ%f*XnU|G
zacO(CuXWpt*e_Y1JgA11aK>H1{(#>e?637t#UWteI2>hNMdgF{Q!cIFsX<ZPX2s2f
zHJv)0F~wgUg0|zJC~mA}jkOV;yvxO=5fewJs=1;rY!kIT+SlksW)l7A3$)MPsju2U
zShHGx9;r7UnjJ@)?Ls-7m)=Np$r|cT?MtVI^=1HvU`O-~IPY?4ghVXH%HuskW|Drb
z9Brb#2(1}HyNqb>q@YYw%W13=oMB8T=oktQIT;<i2igL<(d30{;qIn$>0zlBIu3SX
zT$7(-ia#A4FBW*usz(m07;0kTxEk`UEwl&$IU@o288{jcWME`M1bH@}!MdG=`}_+i
zdVTDOWaPq@FT>+#LPu!A-psPsiE;Kpe4jy%6*RTvV+@d2hy~;$K(1gaWE`B_ybL!v
zQXb9i+NZ^|?|&Zs(T4vlz)Yg&>`NrH?qbYG7|ZA2sPp#Uped0OoCcI_S1S+7X+}=n
z&dBV)fjPdUR6DG_0Z}&@qIPw`<X1CKACEI_eu)1tkZk4lS#ja(Sh_}LI}L^nD8sr3
zF{#+1&T?({pNdL5F-=FA8$`zUvSm<N=);@-7JnM7IR@*`i8Z)0&omnknSj~<D4{1t
zkLY2L(}P@i{;O**CD}8Hn^|%fZjD3sgE(^ICOJyz;0@!m6#q1)0NVTnN?FU^aD+==
z$3)y{F`D>1=;=uSN8!@(S*x*xpIu|fdW4nj%o;{ddZHB%Tz#!}%%LEIqwP6hV=z}h
zyMTED76>>~z#;)v0m}ufAlNbIDh{eRSjj;x2aO!~IcVnK1_Jtxq_c*g3$+I}@>1R4
z(NU{^f@lyq{&9N#qB7$V)zWoQqDP-YZ0F$l`;x4VlB=+oQS|A9B<T|j3R_1>6;Z@r
z(`^Pax1(ewLw4w2KQCqFb(GXH<Rkif2C}@Pq?sW%>iZ02QAbH5L$1@GFpz3TiJy?M
zMl7ZEdklO<N68HgUqSfW47@!yhY;5rh=SO8gt*#3%#2L{!f-b6*uVC5vG#JYw#OM>
zVRNRh$igodzdZcP@w4MsfZt5~ittnMs|dk}Gw#Mwv>M6WOu$;&3+H(g$?Z^C(F1!D
zi84mDmQw`>s6qzSd{C8YAAysoIOi<jg4bV#n^|!P3SM7@)JU`!iy(H#ti(?D2gi(|
zx@UhxY-9{QgR#|uIKxa_K*T-Sj5r6K&WT@nj_rB4yqQGO%1KTzNFD`A@cL%RdyT2F
z5~;6Psu8JN)DgVikJNcAbpuk1kb+9>Bm#treyyy<VLclUCR~|W!7JHkR5BkFVt6g1
zRUTIeySytiCvn<Vw3F-nW6-jv)D1f9|E|o;NT&PxS<vmpykgMh5#4LJ=MU}PHRSCl
zx);rK<wW-=r<-9I_CBKf#!QF!eP!lt$#g-Yd&5juM08cjbXO2v)J&%m-G#|?(}?ab
zX1WTZ8<$Ksn&?_UM}FOL%&#nIN(Y`uDmbQ{{ogAVsf_=PNB#1{^@9a|u)q%%_`w1{
zSl|Z>{9u9qGYga|rGC6RM_Hu!*qesjczus=MU}VK?@=0?8t5H8${c%Lz~{Fw_u4&n
z-?afxlh<CyULTaND5Z8!eU-h!zS6VGYp<!VY^ZDW_-pXmBfAf8R`P*@Uz~2vchmVR
zn8fd*EUWRa^fuW=^^@!kNc$@$8JhYUy_Gf9HQp+r!S&-Fob%Q%T7Ehqf65G>UA%V4
zXZNr4`0W$&rvk^CSH)i0G|)Wd`_ZrRR{9&7sH*Qr@2jM@N_{6i?-JI6?~prXa;d!%
z?}xH`nwmV<fdl<;0lHuX-f(4~P-Q<Ce<s<zK7S2*&RYco)Ol*^@v<g6Fy0I76AGqQ
zT_FC<$e&t0N~vn{tgdfZJ!?2g^x)Q@tM{io+aq8>$yrP9*ztLufiL;vId|Xk$CeSZ
z|L}Xdz7%x5PgS$)3zlEol3$Ds9$hy{@!SMTbpY?aGA{f5MLa#P$Wf2?PSsS|@#ZN{
z{fYprDmu?jubjHT@P~;L7f+l>UNUi_b0VHP{Jwl%?q^BtqbB(5v+bisDR#S^y(~;r
z3PV;+UVh!=301soLTxpX<iYq;&H@MWuw8j?BGH83t@w?`|9top;HiL<@!N&;LHr)Z
z?^FEdyUM337km7bD<>nV;5WHsWkapkcg=N_vDY<JQTO>!-=YRTI-B>o&yK-c>Gk=%
zRiqP2#WiZw*>u_M<!o@12h`M8H#EU5@bWD?QV?dhGa}-m7ncS6UK(7&RnQ*0eZu+q
z(`u{jK5u11eU;BdSMeQmE>E-Fvm67DxgC@Bt@29!rDob8^*SqI4SS%m3T|y896~4C
zQQ6?F{yuaIOhn(--ctV{$|3bG=9(pu4Yi-3Yx4T)(HTbNL*zN?tGvy2Uqh4Mpc&SF
zlfU0(_yu>Kq+S`)&!ZH}De1H5<pXEKpL+k9NHine^RGl=6JReOZT7V%6NxUscpDy(
z!yUC%I}(ZUfTy+NXbEs9U@hQ`op_-NU>D$az-M2<aS!1B?nL4n!24fKB=XV~<#WIb
z0qqA933^ZKn}9a}{^<2Y;(oxt0(Jo&|3)Iw3wS4BMh4!V_9plNe+%dYEbf86fF3;e
zv<1-qb|Ud2;BVdmeJ0+X_Ff_}9q<A`74Uk%rGN?^!d(y80(cK#Ii47P2k=F}T<q1C
z;mPh&z_aimc_UysU<=@Banu8N{0FE9uoV!mbW>_SggpR%`4Q>?{5i&bGay~#I8ZFV
zSfE&%ZI)BAkI7mOTaE`F|Cc~%+>SOPuZ*=_GB$Uvb@b}2waQOVzToU><If~k;xEN-
z=HrP3JJTd2F2XOxuK@TS;uqH>Kst$h_=!Y<y<@R#tnHSxk})G(cckB*5y`x5MDUpO
znQ5b-Mlp^q6gz(R;P>`Zc!nHMk@RK3^t6?n%no!XesiOV#Jzy2Xg)yEp6{TsLDzEl
z(Y48P7V^5tF7&M0M$r5*ndWLkZH3xNertm-evPLD4V}tO?L5h5Fp%x&9jOO#PbUkI
zE;<X|g!JB*5{UzZlWo8Jw)9{|`ZkNf&ELuQ9B6KUPdkVi7s({sKMdSC48vu923#j_
zPZ3VaG}%AxZ?Ha!s10_Eiz_fb=&cyaJkeKkZcmS7+?E*}k)CIvj%wfa+@9xoUzLNt
z?m!~(OOP9~i_urjTqgOHA2iQ`rpZi`R>KHL=ii|GG>pL;s2rCazb(LpfLlcfsWaqW
zD{MeC&w=I@(A<$klNRE2lRbMub1}yAeP$Y?J=Yr+rM91nvE2q*JGEWb=`i(Q`alnm
zY$xa+ehqd_MgIU<i`%ssw55m}?8tgJ*^38clP+68zYcMwDV6Mn1N3A=dhN?@#GmO%
z(?z%sfIAFaF~eYN63oCmArYTKl%6a4w}kww8oHumi?c`Af*H4@-Iku_0*2&F2hU3I
zv=9%j3-MEd>jv&QLP$A=UDLc=*q-N^xDgPS8OgXkJ?%0w2<fp7{8e~{mdTX-Ob@}D
zqzBpi3Gh4$9*ydhHZ=Qj8ShS_)j^ws_{;Pgj`m7UOYIwnSo<(&Cz4!TcKq^y+X)=I
z!6*62uiL2wd|Wv}Q-|2zkNmLu92%f)25li8ir<oo_Sc-&j&hm<wt@B`GcCoxy9a1G
zK~seJ;{>yRfaV6_(d0uPfMzP@lx~axLqDUB($@`m5cii;F^9a0xdyl5j7z?!Q@)pz
zs-x?WNXUFVDNpYjrAwBx7YiVt&9L}GhR22Tc6R+~4u|;PE9{c*3q3{T$>wyO%z+}`
z8%ptoE?NHh(L@8~Y24EV6R>M5+D4ZEiXCHvF1bKiAn0ihqAQmJ_C9ZxY~%<*rgVt}
z&3kl-=~co1HeuIRAz0$GL|*F2`~Z~n9~9}5_2K3?)4yC4;9fV&%X*KB{5nydNGivY
zp3{r+FGXH7msj@x@UMK2eR7orqW_BoEEn)90UHHeE8uzoHwm~^z^H&-0`>^lD<ED9
z%`TgOb^!|nEE2F>z^epo6mYG8>jm5-;8p>n0(J@5BVez9c*!)o<oj#ql$2ax&s)4a
zQ11`e3-hPt7fhaVet>1C+&Hx$zhL@V9L?Fj^=~>x7tNE&S2`BOox|eOjd5Y-p*Ii9
z@h}*lsmO6N7(YTula6aDNXICUerrNw3}q?OUkBqyDso&5#*b3u_!*4H=#>3xDo96E
zu@8$Mqu7M~P2|vLtYRM)Zyi`5n;6njsf<r1G~}R@%gq>`PDiK9?;My*)0N|t^*=LX
zB>gx=#yfLqx^lb`=LX|XP=*^<>4-k^{m6sq5nUS17@kg7PEw>_noH9WWo_4*F_K<J
z<AE}Y?-|3qcb(#V8s4vNY&ZnVSb;B)fm7grB=B;c5oqOnBrGO<<a}}o!w<Djosc8v
zEh*<ZMnBZfcL6_~{&%Oq-=6|cA95H@PFo86?iBbJQ{buH!^wXicnkdMiDgJ6UBl7S
zL#EU(*Ph3T?ZTk{Bk)U#IDR4r3hhmZJ|a%ko)h?E1^#MU4B{&0Ksia^_fO*pw&q82
zg22C4!0`@2KS|&}6BEC5yO{#tCKj}89{`?G;OWi%$>Zy1jDF}iT_)sMr}7Hfz6Qx^
zf%l391>4&J?-TeBFXr?QaiFXP-a@l<(m0~;K9GHOUcd?2dL7A40{=)U$FnsUk`D@e
znUF8n0smz3bCgXBIK5mq>=yL@B^C(x@?zy>fp-bJNj$B~*tiozmF-2rbC}6LX~?+#
zLeNi`!UfBH<55AM93MtwULpB+(!j*U_CA2)1U~&-hE~}A70J^D-c`);{T#6O%9EV)
zXK=(F0zX60uPx#Tw*E%)rvjfL{7@704uLNdej@jfiv<2W;h&;dSqeO@B9r6WN<n{_
z@Y`Ps`96VvbT&sw|Gx=%3*!I1qQB%i^L9aBUdZX$eh<mt3H;ZSIbQDhHwpYHqJP7@
zSh-)||5U;eY@LrJonw*yxpJWJVx?8k7YIGsz6xc#8U4_H-zVsI3H#qC<n#!9x$p;e
zW`HtX;ORxBbV>Q23;cb;&LLi`Sg_!<C`0F|QNX9+>rTn@IK5!#Six%sxhe2xq`;q(
z0zVyiyIHVEmjFN9cSGi+kh3ra{)!a%>J<3K6!;+U!|D0w6!iZ}LH`o)XAaZePg2PF
zis6TTCuJP=(Zlu2X({j%Q{bniz!#^$FG_)LOo6{X1^y2y@b{#^-<Jaa2*VHkUd)pz
z=ntg8_ocvF5I}~r^Vk&llT+Y-k^(<71zxFa^85UO>gxPTW#BV6ZhxH{Uv;VX`ta45
zss{Ip+J@zxT6Yz0Li*gEK(j*kVQam()0aQ9;5_=Y%rHds{hJy$yJ6_|*87{TQ>vRh
zbzXN>pswyZu$XdgQ2GavLOy+dg?-2+;IFCmAqiY%Lz9=MB(m<h<+UDBN;i~ITIr^*
zwlvnb#V22Ya=R~CQ0yvmm(451M}2TJ%8j3b+p2DlkM3wGbeqT5P`e7BmGLw;Hu#8U
z5x%$s8PLJgjC)F;@m5|VZWyv+;`cQ|IKBwxE>lGvYUu)nwVlf#0gRLGpSq`1Db!Zd
z6t|Q-ihGW8{>8;k_xwvPSy;Bny{PzNXPH~^<GVei;Ue54$0vV?n(m_UoUf5|Uszte
zU}2eiPO-~X>|VHN0j~p<p^t>7>?W|Z8nnd6i-ZA~;(<Egs(cOZmAJ)@+wiPOBpRR0
zK|65^h}TCb{Dd*6!Tu&sy$?q6D$DUfAzxs*k5dSHR5g&sRgk{i=i^^eG6@*qaW7Ub
zS?qM0+92Dl;KrYC>dHD{bJUKH2`QecDzXIKL{i{URVGPJd>;v4`GG+}EBlR9CB>-+
z3`O0G{icjlahEQcSL||>D9i`(Q6YSkiG7C%P<)9=evXK~vqaxWO8P_;eH&=-H<<7V
zBPL?t6HNnOG8+1QrEmMh6jNtjitjnmr>VZj=ckH_%wI05YZhN^!Wc>Mg(Ty{O6IRM
z!5s3tPxva;z{i-_$DamQk^G^lfzs*BPz(7tr-u1#(*Wf(Mrry`7JVFxfA-1rF|9%0
z#4<7CQ%os8N0t2HDf2hB27P_X^m#7&gw=p}`T!LFp49+u3Kp38zU%7zp5=i4CJtB1
zRDFZro4=wykPioL^fuw^U-<@Vd7!4YYI04L!U$G+d@Ggws_W_@j6;7DFOeUxVq_xY
z7F;q5(k5@MhxkRRvDUBTQ^-;Bk<4GwAQBj1O1|HV4~ykfoCb9R8$bEpl_D~)tb#O|
z<1%=N<{UDOsyuZy5C+jy1(c?7n2)iq<YS(|H^}_MEJXfI6m4f{PlV+ryX3i-gm!_K
zK%TS8JRVsY#NR0dNjP2-3W#ZX&_&x9yo{tl)-TVOB^29Q?9us^T>qs&W7TWuFVEW~
zT#xk^U6`h^ZXP(#7TW@4A#g+@>zC(p63X*ykg$tnn6E34p|fJiFVF8JbP8grzd<MP
zD*@>&TH@t7pM*079qCUr$?aba9GzWDet8}!;aZf_m5Tosl#y<dU!EIExP_Q;N&RL0
z(thhj*~N@XG0qp|Iioz!Cw}5d=Klk5WK((nz}UaofW#%X+dPB6QV)sPfP|S}o>xl9
z?zu~1Kq1zEo09nDxuyY4yp&Al|63BjT`V>w>`8K%Wd28z_>FVDaa5b4{88+id2XP7
znct3tx&87yRYG|mNYW?U?@7Tg``<WsE$18sM#u2H)L+WmmBcU4k0hjf_vS0P{uh(@
z<vFf|$@NSBkuu*vg3dn0FgMP7<#%e6>1AHR!{Ehfs=+Vse|3s;KS>7?cFDYiUjR!c
zlJ(2;WBJ`-n=mYmKe{BJwBtR%k%=U~JZJ846Sbn)+24Ubl3&6Eip>1RIoeg6N1lgB
z36jr%U^es1bEzzk;FpX7N<N7m3ueMe{pI<OEd@W-WxnY9x`Zn*BaxQ;XD8t$zZn|7
zYoaak@XS@&9G(V1CYAcj_(iu{r0$%O|J_6f-ncH69mlgwSCM2O2Y-j-k2i6uQ>CJU
IDJiS`FBoA>>;M1&

literal 0
HcmV?d00001

diff --git a/IDB-DL/private/ompmex.mexmaci64 b/IDB-DL/private/ompmex.mexmaci64
new file mode 100644
index 0000000000000000000000000000000000000000..46db76daef479eafb40adb27806e3ba01fe0c2ec
GIT binary patch
literal 68760
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
NSY@=n9(K6x{{dqr)-V77

literal 0
HcmV?d00001

diff --git a/IDB-DL/private/ompmex.mexw64 b/IDB-DL/private/ompmex.mexw64
new file mode 100644
index 0000000000000000000000000000000000000000..3547dfaeffc53253a09ca1ef8a1d2f5e38a088b5
GIT binary patch
literal 22528
zcmeHv3v?7!w)W|CH#8x!I+B*CAPqDU3_>J90}0v<-CBj!7<mQ-l8|&r#=PtYMnxwQ
zqPAtvu)I1lI?m$Gb*{`EN13@egRg7n0f8VGKqbhi@ikVAXhcWiv;J?Nst%2eb6xBI
z*ZSAG*Jf4iQ+x08+Gp>5_C8g?f8AzgW{g>IX&PfY0qN)F_kVut1Np28&z!|}jM_hO
zr%B#FacOl;$k`ZdSQjj>b5@ks*EfWnYXi<;q~2Lm@AO`?#97x+8JLltp6W81Ua;)d
z`$y*Q>`GkK^Ict5JXg=ZuxkW|WBBt~a!wcHd{1{3aQxX_=WsaL;G4qX;+l$TYAZ4F
zVjp9bcaC7{?t#S#+#s9e9G#NN*rmW+pkte^0JQURvxxweQy8<-4Zj3r+wlNl&3u5!
zZm{Z=65z~d9aFZJF&R}qydLAB1~)UdgF0$vtW)oTu#9aTZh=4k;9D_cw&ZrcQ*lN(
z&=dylP(E%nWSTqXk^FNqRyHG8SspHD?D4ZfL7OZS*IZmlKR484hR()RD+rCasP9F%
zl74Q+;xig`Mn1=85UEdUH|gi5_JW~c1#ny@n^B<lmf}kKxjA2;wgHST!f(Yze9Lhq
z{oIVrPkQ{Z_)jDtMpwCPVsxp?zHIpl&r;7a&vMTS@s1C-qK>4Dl4I7h7ceHq93ugQ
zY0tT6Z6xIjb%7aZ<r*6}R7e{f?*XXOb+jGm{W|&pp&dFp3+O-T=)Vy9+Z5i26X*wZ
z^j1Qz)48*O4(sSBp^J5NF3^4*9VGM|9bEwQ3>|$vp(k;)koS4sF<+kM%lll6?r=3@
z^fcurb-^^oHpL@jL`6_9L0MD=#pouT0n<_^>+}qoqiW|2y<*hsY68NpT6Jm~e>jm1
z+eUTenk@B0l+i2^vecuTFod#G?Lrxa>=QzX^`{=jP6*T0XsxSJeF6ku-f_aO)A4&+
z_kuImBRsInqwP{-dd-3=A$v$|#sjey>8x=Kd5GrJ`nj;_L0&i}Z2B1$FfTu^DTs=y
z7U_-HFikbrD9cKpdJ&ZicZJi$SbE$Iv9$u>Z`FiNFY!h&3zN>CB`Uq@S5t|-m$TnU
z?8^<JdW=(c8>5J&=Mc33)c!lskiYfw@M=uTu0BnCD9}J2)5}>Hh*)u4-C{gaNB8mO
z^p0%Q)NzBT203MScos(dYj|z-Fc1SSJeASYF3$J3OGU4uvV%ied6Yv%7gm+9A}%UR
zX({h@HG{9q>#7F8GVDDeWNdJiVVz49udC6E6)FbG>!PK1%h5_#ffx<Da{bC~5wvVM
zy20gCSD2`;dyPJS<?2IQLbh%zV$>ww(YX~EPpM~(XSHWl>6+CYFD8EOC5EwyinVDK
zn#t=VH6$zZD|<xcsI0WZwjRLL#F*@IYL2t#QD^(q`!6I-3!g{jjl3K=i^`2Ci!DPb
z>r;6Jbq76TaMYxj_oBLx1f-AX`i>Wq`yayI^gaFiPW69Z5w*8Z&D8t9l*%c3{})kt
za0=+?|E6*D)U7|6^s)WfcnTY1%lgjPo}lO9v5|~q<*&<@dzMSF-#2}$X^>T83UzOl
z6Ju@NO7l{lien9===DzJks)5XwGSrmk5=@`%5V1o<B#6ZBP)N{$&0t{@wdjss9*I6
z(?UB>2=>`aF+n`sx=mDK+rb0eyaK{0yM4m6tM5MH73}E~fWXtk)taUW55)bj)2QDK
z%<n~Dh~?tXE@up?Qh<6M<y*J#K$qCMSGYg!Q4Yvaf4ns=M}0fR=(6p?{cSDJ_n{{f
zDO7P(b`{CdKp*N>OD|wb^lOe>=F5vq%FCj1K<&epCqgBnzPr_Clx5|%9yuD>f@_ag
zd8!9ZasSlcj+(8<!WC7*$MF-AaM3P*>+zedCp<z)n;OCb0Kb>;FZx^G)$!2!m2J3o
ztEHgl#ff)Ul`ykwefkMuRTq^)W=ZKXx;Y^W(}!g9(7;)81@G6deCrpU{2J!tSGx4!
z%F)|;)T#QA$-HUQ-IHnSc#)4%wSvYUd|T8{LC~kRClAZw+e}6iv;3W8pyP+sz2I3C
za~&s~(MYbkgDS?-p5lxUov8jM(T9(zzaT2GC6LB7|414EjDb(P8inL(j~$+Ny7)9T
zj$gB=w4D%z>4TzqaKJ28d@d=ki_u87C_MRw*!GT@wB1ELD*hqb4bX9!I>tcIQ=@9d
zQ_BWd5k}>+hsTP_DpxV8p@jot+goP!y-Dz76RDwNDCyE^R$oF{XN~%LMa7q;gvkrR
zAGLlS$-wkqr{SsFhz-is@;n&;^9s}McTsc{o+?(fl6H`OKF~`ApRn>!YrM%S74BLu
zc%y$H>c9x1FUt~R{$lmj@yG@o+Y8WQ#gQtXu;Q4g+}0QM<wkwmXobKQ^=%btoA?NE
zw5&wpnD%z`xyj{3pVXA~twK<yK{<tKkGe?H2|J|&SYURz$Y4=;&c)3Qq}f|RBA$6a
zM*$rS4p=wST#XqCo9a+SY&{en2?e!ZIxya^>=KoO>d+XVG3;kRhIVHUs>?C{DvvPp
z1e(iOT_D^wk7}Z=T!o7wPvcbK&Pzw=UEWpzbIO)02BpGpg_cpkippWKx((?@+h(*)
zd)8Ou2>Mw|(I1)6`lgkhysljM3(FvMDPH9jX7&kp?FPXZH_=5gnm^QK`6G=`*mNI&
zRHz9XqbMX4vkSYfVWw!u+Jc~4?V0$)^iou0R~a`;C?zaY5}tfh*U7``Jx<0zF5(Ia
zU$p6YJ`=8bvdfKmg8PWr_O@9q1uM5V=vMy;uwm)54?|ShgSy;W<F_|K>2hSnzf)Fx
z+eKxWN@S?SD%uQdu^Dy?XauW~cSwEdd@ie<V#RJQd?<Q{9J6qJMcKDijAgcwl;5U~
zTM@aeCMOT+%1TGxA*rn|MST{e{wp~e%hx|&XK2Q-)$J2dE9yhpuS&`;ztV|Dck6BX
zn}NY&7wn@=-N+|KQ*PBFep-;Sgk29_Ax3TebD^{(L^r~MB52s-n3-Etl1EDow*xiY
z=*4Js;0aW!ZrvL$qCv=bXhh9sE)yC6!UJ6_G%`%RP5u2@=!ulo!mz-9I1g=1;yue;
zO%m;sT+o=Tyrll}TpC*h5z{JH8F2x70Q3K&e5!f%-LVwD91~kK%Z3{k#w-nT?5eN*
z%6<&GLppiXue<`yklNG~sqJm6-@I2mc}&vci(=ziF$@f7k-bWo;H8c{gJMgYb79od
zjdHITwY&z{Cq`2MRG=qG=0lRUC-RCup29anPRtZK)FGNl+8qlO%Nvr`Ax2G-`9-<#
z&EQAAyh9|B!+dnN)957S6+XDM4)Nq`A|L85k&o^zt8}tgG#?VRxE#ymqpQMCn6QZk
zO8au!!4vagZM0$0#uZ06+zqQg2+}B&dpLC>Q6<b%+hg#6UeXhJK`uNJ%GL+mZVXt&
zrj^r<>NMa}6?!5c@^!>s(5_o`%OLDkorxm@Ddt7m1oc92XyHEd85lfMU(X^D`l0cN
z0Z8YI^<~w%u#%($xO`Ciar*L-7C(cPBgHA<OG|NS*=Z~LALr2eKw;pNn7RQSkjfEr
z315FQ>Bm44mDSiCblux6EBkb7EP{Imb~~iaCj-M$t4A3v0y6a?v<O?Kx`r()Z*XfR
zEjwk_pN=*v!BUv|cMN*iE$f+rua~>y9yk!SDxu^yFmsRGi3<Jy&WE`aP6r{6lR9ZM
z26aIF7Yqi&7$UXLCAl2njutjcQ_Uqlnw6-$s%}H8Tp}ZBC_gzhl<)#w+RCfCy&NF(
zxsK$g%eRYacPBhLjSXu%NYTzoEHJXG3(yvyJGz^BLb&UrFL+p&aa#f3mrmmF1c6O9
zg<gr^4U-UM+)9Mjd!ZdNjI;?$F$kU4jZrtm17d8sjqFd(Ilv81SMANBGJi;HX?Mb+
zU_0i8iZ``aidrzw`F(_t6mLeKflw0&)j$j;stgh$@}H#J7a}!T7|VQB&e<*Hd?_j^
zXx%1iFBx_1L!9+}+OnbXPmxB`WIq+|Y{SZGj%RZj%kgci66)ih3V#F78tY&lE#l1G
z!p2{7mMvhRctGhvIDmj;D{WyS^gC8!5_9&5+Fry-7quCi8HoIO9~}H}xVffHX_sSu
zF~^5&-sco!%WUXz4|;@2uSvfhJ;H3;)cx3YG0Zyj;`Azh8_W$^7jnH!aL5W4^e_S;
zr+SQhWGww}v!D=rQSPT02y}^5;edJ}oGsTFSOc<B2hseS7skd3QsJAz##exqp2LlP
z&E#%|{mnnxojt<JE_F6McXG&hTBO{W2$9iz=s7rqyCvla4~akL4nlS{q4RI3J5u%c
zb+2)^sPw2W>E$hi*UPJchd%VEPjC{ly<v8^XPii=PXj>{O2o&q(t~|*%fKjd(M__l
zY)fKmxJ`_$z_x(BY>`cD`vBUwThy=;;~t=~2oo3-&@?2qv{`5q5Vh`wQSZJ!+6b<W
zdb|51#cS&$luCO7p|J6KZP*sP`vz%$Ak-kWF-RyFvuBV{Ly^aj{cOf&5b|OvH(wn~
z>&EK%krXM1{W%?S&ay3J7-j?#KCDaT8<Jwlzz}Sty|4`_>P<y!u$8E1DDt)*a4y@k
zP_f_`AZ_Hrs65NoKm8jD9&W>MeOn>(?RwB`T8)t*VAbOA?o!KcoOXd6^>0Vw6c-D-
zgpFJNu4!b#W$*!dIEq3dK+V=;S77web^r^ln+-V%5%|Yl6hEV&$J;2}>$1a8WaT+m
zF^7-4N;rJfRR+k-O;mp6+J*;k|H?&1NIklNpdsV}u0I?4{OwGx#kBZez>T#$uY!|>
z$9mxsi<AdQ$w{rfaC2rH)FQVifI>azszw75tiFGz8lrj|aJ~`7Igr0st>Es^-#lRD
zQHQ!jf3~X&c>ytp>PAU|)f5<*5_N{o#MCLga6lbzkaE>AyySz9#<pT?bi$w#>W)sz
z)IDpG4?B?08|RH-mwkoU@?owp4d*9xu*LlhW-BS5ND5Z^HT5;LLML5|XAqnokD6=Z
zcI+#s{KTp+P!l`w8tW6Nss4_SWR=TJwZ<iCjl3aVbjq2nLPr^w^OX~_(Y4spnOl15
zH0DK|hZyIeIBICZmLw|Hw-(a0__0hGFnPx@I$7E4jaqQ%RD^4XYdic)^l=X4Sf$G%
z#~yd>08Yk{V<YOE@+V$pf?RQqC$>QI7WN1+@;uj6<m(a5VX>k|Qo7~BL$}rV=j0y@
z+N(B;h26JZ2s@<b&<T3Vf@vJ|Xzilb2CLW$Sy>Ll4eT2N%ifzZh$RO!a1dhs$AmZ!
zbr1@5qryQOD(pjr!!}ShJSbIk`=aAq-so6mtk}|J@x<nvyvkTl)W^Ka2(PjRo?y4d
zAI*nj&amuLx;;v}w#$>V$CJ|`wd}G;EuEIaw%aFqqa(c0HD5=^dZR189ynXuFSQ&8
z@k3Pq2-nA!*5g9-TSV_N4&_KDbS6deB#rqqlouL90yj>`a5Tk#6qR{)lGfvNK~@mN
zt&B^M+?~HBWsA+%aMWbk&?v?hej~-^XKe<*NO{;^Y*>5NkA#A>;T(;ySa}1Btp?bb
zXaX8L%6G6S#C#^&EVaZjkGNAVYzzKUE^H4?II#f1DB{NNbcXF6<ltM9($?`Jg}y>|
zkJR#oMYwkZ#w^98uVv*mDdvU)?oaXO^kT6P>Bc}~Rhz-wj;ot`qKy=Ljn~`@jdtUf
z;qE0mA!TzPkRomwVjoq|IaT7sEdw{s1zD7kKr+O^$iMju4~C{nIXzd$EIs}l3Yy!|
z5Bw{|(Ox?wRN}@h!#xyv=Tm(FU=N<<WU1s#8--~Ux6yeOg`Oo8Vj>d#01d&%?t6wJ
zDnB=1wE*vr$jqAZj>}3fKc4=WQ}@#$YMEDgMb6nH$6P!Y#C~HFCNzT%r@2dP`O2~(
zEXEdmEyb>ekkk~O3s`tAK*7Ct6i-p~NPQ#*?IGFV2AVty<&^@E63GTPUIFEmoRehZ
zQFTs~bxx9x1o_B?9ie>eK4g^`eFwIw4k_obWPU};i9;=nEs3WTyfqRQ_M>-Y^I<<y
zhn6lxS_g#I%WxVfE10INKtQ4bQIRZ)`OMaeu_a$i%04M}CEtRjoCDZ`#n^Pd1w)j(
zaUIatznj*@M6;viUn!etsdwX%79JFpScytgkxj-rKPwgvgr@K=EFV$xIj>VST53_-
zM-^=}aI7+R?|nixtttGVXU*#C`@MF?bj#E4{MFEs<#;nh{u+_)qpmFw4k<#eq#&<^
z|2cuy2XUAvY+44F?^ikz`eJ@<eP;C=G{-50pvZ|+XsAu27=6ywjFx2h&AT}ayW9(7
zMf*`k^L$%a0V$sJD@XE<ODErub6%B|kNmOgvXbL7oR6aSBe>qj^(L;5$=24d@Wez5
z(mYs!-I79gIR(K)8(flZ#t?&>geXFEC1fmi-Oi2@fpP-7ZepAV;wm{P748jgN8x2$
zuj4ul&Q}4)K$c_tg>6F11@Kd_dMFEdCE?zKn2Hp$WQ)pvNzsss?-oxU#lhxrnxmLA
zgyMb(2!1SWKSDU55EzOG$#TA37|So9rpzz#)>MmO2mY9a2dIgB7!x-1r5*la4~8_K
ztHxN|OL~Y(qXT*&SCO$a+k-%p))0C=bY`XZ9L}BK#B5i}8H9qn%@`ATm>Y6pZ7|bN
z0p`Ldfg!?3kUk#~&97pl)FBVs_)xqV)Rc#$*TY-tlg#~o^Bc1HW54-Tv84m9=S88l
z0J{R;jVTCo%ZjcL6vs4^P+&uiq@=k}QV}mvR;@{F4p33N%Q=LdL84tC1g8q$dQ^@r
z=59mId5wFHH2oBTpKp-Qmd6Ycu#AzAc#+WHDHrzAon&VmCU8EIRuA!d$B;^p5Qz&i
zBhlbEVY|O@PiUf;(~U!oZhsEWB<yKWGxDjl7PRS<9E|isEbR$@YP@2FI~B1x_?3QH
zIUy@w@Z+y(6wuLoCH|5Lr!(9d$k!I|w?Ussv0H8AK;8bF!QuI2o<#!q5(;-O<R!(f
zgM0Z#QjUu!-}Ec*iERTZaQHtF+up;`%3npZCTbm0tk@2%dj$NXp*Z3{b-rR5BKMfq
zM`<bMFds`t;t^J%U}uDZlveSOB`r%d@6vq==@)UVIv9DD_6m`PDiwBzrsCWjr$f6W
zbGsDF?1BL87UYO@e3K%XVMMEXz!rQTaojnQ)=qnYXg&$HjBAJF!o$MGa}96mCP!L8
zx`@pPGSc6tCFufrEcX()Dtd+8@?jR`;$^y%;xc~AkF(B}uS~+mYLYXK+8}B-iCPp-
z_AWFI(n;Fb-Vod=5K~A4NsM7A;I-rQ=2H=VO^!|H8y8|h;Yo_V&`(a!PTK#(92o7(
zi(;~A2((@)=dY5v7po=4Zp}h|Maq<UmS1W2V`?qrO2w9MEE{T}Kj%oXv`z_~=3rM&
zHu#N6`6%r|qj8f*lQ%O=-h%~MDH#i3_X=37l^!T5773gJr$Q5r<$2DJZEhkwKePgt
z3q6I0)Her;11JJm)f4F#%{YmoVTtB^7nWnq(#|0XA!JLxO)`HYnO_$*@~#=z+Ms(T
zp=A<ZCK3{sshE~Yq-Ao$YP0xKt)it8@uJJNgs%^dQLsLK<qHV`0*(U|ygf43XNzG6
zSO*?(Y;a|f*4t@4{L1@KV}uo05E~Thq=@`6n(yLq1ucbmb!<bg-H0bJxupfz=DNAO
zPa5(T?xnXni(>iT0KjFxgv|LHT0IG;G@=;;`B=_*8-qgHjsr>wrbCAb!jtWI&-J!s
zZu95-4QG^cOx`ne#)@GXH=eR5Qul+j>S<}%dYq8hN_P+4Mct%8H*utHRK$3e-@M;&
zOSC%~%k~r<62dzotdI)#g=WI67R4<31aJ_^&1-2J7SE3GK4D&DkT9=(L6s|*hPd)$
zEZ=AP+N}XUe>(x^ykbya#(g|Qhd8lzHtLP9XBMrelhzYKOeah)VRmi`4G<y{yYA2W
zU~8iC30^u1o96O$#H#_av|R4T=#@4s%)Q9i>itTuq<jqTCg&WL@Oty)>)0b<FH-C(
z8(c^hDU5h3f70!T-ipHEZD>Ty|23VCoE{`5+Y4No<y;=`o%{V3GY4ytWHEETV=>QA
zstkvAmCcX1e;PKwbD>c%pH9Pk>}Q$}ag$5at0w#!?B-1wremh<1P;z$_=>PGn_CWK
zVmLSw6-*;r|6E{=kqIT@G5);{nHfwYOHZQzs~=ttX)})dl@ppHdpuJ9p-5x`tu5kn
zyc@!?uLxD96J0tkTmQAD4HPK*HOJRDWo;R%{U^K&aRxd-pNFh;BFV1euf~vMlP<u(
zc~qN}LmT=1@c2kO^z;F_fbd@k7~jIa^LH{kzoLrD2kM{|C)zmQ8qypM#$fVzvcZq<
z@Rnge9s80WP+u6LxO$&o>Ex5yuVY7ji2(wwIcD<uk;wPezXk_HXV$Oz%9tR%Pf}Yk
z96HOy0>FvY>XCaHkB{lwjsD4?>r27!G++DjykoM0nD-M3JvY>op4<JgmDq-dB<#Lo
z&KKB05E69Y(C-nN`YC<J5{)$ufcHsCTDurs2xsUW+h;uV(F6P!r$Aobv4h6LU<6Bw
zj-<k)p$T-3hf^~-XCIEl_>nJ?I)ia2GC;G*;);atJz73=;vKGX6h~<mTxqy6agD=;
z&s;pyapmK}3qy}L>X9|aHXL<E%@Bd}`VPH5YPWouD%`b%>^qkJm%TW6t7up5zvI!%
zfG{7_-qsvJ)aqK03l5fJHZ8Ddj<Te7G{<62&ri^8n!|k>eHNz|B<N|H!+9G0Y)&sq
z&@(j$JB@w<$G}|+!~{KCbMyg|n1|*##Odw?9beYOPow{d)4d70OLJ^JjeZ}e%L)2)
z&Cz@sJ%}foxLtFsJB_f?AY^HdMW+$m24S4$n4Lt}TyiO6O}Mt;DuIA^0^SIC53V7U
zvjBSm3jn`se`I?~jSC+UDW8~YocNUJa=et~dAzx1oJaY>qx70<vdlG^<{G=XCe2)9
zGuK$mHQDBx=~&r1m;}C*5qt;nkW2P)BN90=X3eB`uQA6rco)`s477Jq09%iGEvKjN
zz_W5hU4W;S+jALqrP;qj$KeZb7PrWP3O9hkIPO1CB?`B9t7AEDzQJPy&xA90K83ic
z1q83@N8#p9Z{ZbCTW&8hTF3-X><k`y1y3#DrCs_}XHfseppFC8bOtrT+ZT=Y={3Yt
z@HnTB;abj9VDOONtT}_{V$S0>c(5qx<Imt3#d*924}IQt5b`^{ulHbEEw{@C53N`0
z89Z&^0ig&4^(nn<Lm492e_CVYB6O#&9|#P4_Wqxu){EMd?y-11H03#de}dn)^80=K
zzKP%0^LsVFU&rq<zhBPpx%@tf-!u7r1iufRLoI*6?|<d@1N^>|-|79QHsx2i>%PaQ
zAQ+(oSWnA4L!Qr>1*d}HF&@Qj^H&sGPWH;?gJQ)VdGbN20<WSo5xy+2D<Y!0t8Jfq
z%tAO#__N2{(l;n>k<EMLylzR^%UxUlB{WaiOSWI}6o?842h}LHW`6oeQO~K3ILUt(
zrnlj(Ail$CYqM9KY}2$z%BLM-+g^MBN*n+~Zb1i)Kg{pz`MrtXZ^51N74pYVGpx4e
zxCtow{zB>19PiMPXw;fNmxCz;T8~Anu?41q$zsc7rk+PTaV-660QGa)v|{Ob0O|+0
z!M}5Fjd&Y-WwZqEJgN~eG$D#Ea~cUKZ376V5w7*O9zqh15h&L2m`iJ1_}OF{4?ZjV
zIBHD=Dj+JBDR}FL(UsT+>|$X@_`Ii8yqnV;IT&YNd{7krIKF8P1o6(m2+#AVjmFCG
zj1K|B!qadTOnnj~?f+MhL?wMSNSfm$KHQ;?!_ecVU+~t`i#Xv85c~>q`9XZUDz+R)
zBKcwPI&sczEc}V)Xe80h@v-oGc%y-4wu76?Yy}Z;aIN9fo%AmYsEhkKE&V+_aF$Kf
zW#Ly5t6Vr3{;jNhB`fa?Jcv)NG)D$%YmPCz4eJi75Vbx95S{lR?mon`t?$qv@0#~>
zJog_#IWOM-3IOFltz9S#%Srk8Oi7v0wIQiRXaJJ38iKkEkFD`=nl7iiuz&-X!@1r~
zLy4u|2+(B}z(ZnH*Mi&{k8A?NH9CVEK=lFLW$nLA|02HicqA2{HHddv-vLIPGw(Bi
z@I<sWfP(reR1+O$Ad-3pUKbe<LcxQeXx8)b3RTzmVTZsII!XmfNUQX;n`~iNw;H)_
zd6v><BPoN*m$LHKvozRfi5hi04Z~_Z6ghj2;{}u=sd>kM?mqxB<QBHJe3=rt46j*I
z&&5D7D4W)!^iq^c-vey}m#pkoj$nn<T!e212?`TSCIv7doz$Y4BC{c%OHd7;5TTkx
zFJcl8A0qOVmGO^c(YfTY@aP1=qaAM5Fc{4oxoo(@7r=b7_x!z+_bEq_hB(9cq}93>
zRcV~|1Y2Uf10yiARW4_jwF9OC$DvWs&Alqmg()khW7bt%cHo3n8t=iJ6ujd*n@=E-
zh*f<SmWAk-e}P6VARVAj{viC){=1N$k?bjt#ycuHI>o9wAX&{(io-;VkYNca&M!fl
zB*u`%^UojDd5{C4F$xTYg*_s^dxlm~1IU_*luIQym9V*4<Cq4Oos>fG&oD~(=P7x6
z)d7r<zFXSF&u(ET_hJBI%y^`O=BV{tLMhfDfvEKzyrLui&D3Ai`Yzh;vOWXkC<}{s
zS$CjB8iNWq5+}N?M!C)?H)@WD1n861g?dkOJ6MPz<n7fQ_W%zG9Hwo;(TazbdGy*8
zil+0?-vMZjFbL03SMaZzqn_&A(MiI?VxabK^I_5S*9gP$Ej-9hAQ7h$FNOp84{4Fn
z$Tl^{a^MDTf0|AV)YnjJGq~^J+^%76CB2bj&NeV^UNeoJknD0fV{Ho5`%xdRiPoq<
zb9{(5oDk1ER3(gCbG)WwCIh2)xSCh!&^<iuSL#*JH5^l&A3xcP&q(-A#p6KA4xM6b
z{7ba2#OCb*7{E8;W>Vi2t%nX^MSP!y7n-DFq+U1@gI*@;J*L;&`UC1EQX-s6Jng>@
z@x>fl*qj&d5G%wY(TvY}@0de>iWNzFW;BLhHRswad=!m$Omc;%h`-r8B%9kj!e88(
zKH)DVBNoPFmgR$iUZ7l?h8Lp2RK;U6x66e+LBXT7;pi*dhf|HWsRw$^HvA)b$O>Ml
zrB8ZZ)3CRzKY?&C-${NP%qe)kx>voM#08X~xLs}Pgp)ljeWrmFzGq^O*QN}mW8J5G
z$?s{9zBc77e%}lXwNFNB7hC4hcEchgG{>8137<JEd!Cs)p~3$)ohnsAZ^aj8{pQV!
zf0={l!Y=#;#mOEl^t~Vttfw#abCLg|9ljQtqZUOkjz;p1X^!foawV~VyC6Zl9tb)}
zE0FQ^-%iX7KVT;JiaYa+?(o@#EO>k6==gJ{!`GiYj8Y~_x^v>oj#>W)8bX}K??2=B
zW_}OydnUh+z<pTMyx^wn9`pPFAL5Epf3~QY6kF8qQuo78Z6=*c`eO&w80PP?J_1S5
zXbq-eimv51f%4DlqocgNcNAdPi{n#z1^<k8uqHqLey{{CU8}P?4VY@cFUxg&p8-1!
zxZQxiFyKZ5)*G<cfcXY=8Zga(76X1(rnmpD0re-wUNfE#8L-oU&lvC*25dH9y#fDJ
z%J}a&19lqlfB|1MV4ne10}dL{X0&sT0Vf$S*MJ2E6b-oCfXw(kWVCaS0dFy2f?jDn
ziw2x!K$ii}F<`0zzg?r({Lp}J8}MZVb{p_H13qEE%?8|Lz*`JhYrwSzEHPkWeBTF~
z3t6)%iRh~dR|kSlud}?q(pfUy*?@8wur3nff9e}*3{=!q)dVVM=#@a^tXDa!%hw0+
zSHKkwb&ciWnzgk7XQ<|uKq$fYgSZ&GYPhFzXXwUAc`)FtD-Q>2noet{<iF{Sv0F2r
zxazH2HeG=~R<5e44Y0b<y2?N;{pG9v3pJJpLxIwD^t2S$Rk$!0Rswj9znFOe={IG@
zC9^}$VBp3`O)wC0hO5iN&M7ly0f)bPE_YToL};d`hWNwyLlxz<<w2Sfq%{|UT326Q
z>zq>QoQWHK!^kRw<u}zg+*C;A<ljHwpY9BV!ZjE|pb`VEE3c`qsbA*=CNS4IC3jZk
zT;n!-#;hvd_HcXdpSvIWCH=m*g5J4TRegSiP8)pT;hcZH^WjY+<{8gdOk4Hy_P=Kj
zCg=-vd|v`yQ5C7JC7do-or*UwZeDUe?w0D;m)F)*I<b`H_3I*-y&*Jb_~u3oG@)Np
zr!Jd1m9%c^RC(%D>}Ef(UO?B|?^b6^8^VyRE)3_X-;~Q{<keO=LxGBh`pQshD!cfH
z{f6Zy)mH|ZoS}wb__W5q(=KDn=<lQP7xIBneM7KrnD_hmyy3;eJT%%f_!d9`B53eC
zjNi+5dUN03UPVKoDyio)+LJ4)8)}^qeKx1Wc?RF9@p+S)JF{PZd6TnzEewjQ>F~mQ
zZ+j*1GfBMP%SZm`pYiGXpc@?juM8>zp-`X_mQo2V;I_~I4+9`bEyx#KyZ}=Vgl@Q*
zba{SxxT5-E@(}tjHT5F;Ii1Da(U9fU)K_7t;R74$ohYFpr;{@o?IC+R^>a7s{<qJ7
zrzropWSX7MX5%}$arxueI8!Eb436o`=n>-SjcH}0F{pFTH?vW|ry!5A!+UH-GZS%b
zMJ&4?*Q0Q76B$DEu>U(a#*U6f(v0l4gm~uK*xaG>*(K^YHet^NY}DP8PN$DbVWYM=
zd7D(Pmg?M|$!_l*&uVv0VApS%$h=LH*=5x(mh;&{=6v~T=IE5!S?Jf2zwETW&%Myf
z9HvEVWS_Onf;rH*w_;N=W!hL~QwkF*M=>Ga#)QSA8_!*y%9g)7!(=zjG>z&R8MiiC
z%FM+nxm1U~Hl(>avynOCa<9~%sT{=RqGxLgvv#I&`Y=uy!GxF7S!#GRv%>u**EfwB
z!N%Zo+fj?Hebf(c|00MF*CuLjYATzGF`oCXjX7S<VCi8yvo1Z0Ig@xy<3_S^)n+Da
z96dNHKC)~C)`r??yqK~3aP6UXF0`==J8f)icqB`$9Dz2hr_oI|3$qQ5=(F_bW9Y%=
z@eM8ua<Pe**w`g6V+`R`)E$MoHkM2`rCC|pBwjmCdcpe!d<d6J^>fo$?z`CQUN*7J
zPRKiKMmrX?lgb*C_)KYLmNp;l7;U(r$!l@lO>K<D*gwM>(fF5Q{P|XvOy}e88_{El
zr<Cz^g`6{&j$)Zu*UZKCMpITQ%R=&#wRAMg%13!|X3yDidn1k2JsW#8u6L;)TE8&<
zXO1MCX@r@L$j4O%of(nBM$AgdHRYtT9IRXBcl$wmrtxWPeCKF39`YKGF^tC;#?Lx;
zaBQEW$EaU?t^?}{x&!ibfbLkF5qDv?gXOSwm%~o56S@&35G=>@62L|yXfTmJRgQ$t
z0?r!UXi7_AX{HR8VT_I1F`=ESJdFJT*FA}PsTP)sI;pcpG>)>Em}Ne6*5EyYaw^K(
z!AsC&&`rFKjn~<^8jTvi#!|+ruf;p1B;3CPCj!^ED#69rm%wc<#(&w7gezVPI&lB!
z_hUYxOXYuiKC#YR*K}1Nya;DKddbh&PxaCg9^A5<^{3(>djusZ<g058-^?0x?4n5B
z+CcD{DlfMG`Ve-?Q0ZlPx$HsCNijripou-qpS(Q8W-K31R|W$C{FgPBx~3(8uoUEV
zrs_}VXQEzObW2@%6DP6vQD<?Wyq5k;nWHHC^&%@n8KYlV!P!>pPeyy0cv=t)AZlBp
z=Lj?~_GwB2tB=RelM=m`{X{cbuB};H*EFLN!SF0r7ijVYgZ|LErSxC(<nd=j!AlSW
z*RMl{0eUft;VMS80(v!ez#3}QvGc7J82AnB57xRsT_}JydaR)vgMhyyOhx0(j9qT6
z2-G0*US_STZD<IxZ>-^l+J>6~LFQkwe8J*Na&!4$*hq6}9Uo$81sb?Br4(60I2;Jp
zvAa%{O9QOJTE%62!c-bSE>{_-EUl-|{@13`a^z5@fu<Vte}SpAbU~?+3s#g?BU!9P
zmuv#z8yib18pBuC)S`KIB{1l*v@)<ZvTj`<SX$lCa0C8dC17g<<@Kc${bMXu4W;$v
zb%79ysD)&RvFk0Rk$OIV5!{eyLp|^O^ORDKeZ*X9U_N3#KGfm{UYqS>Uc~j4fy8s%
zvZOW;Xk-U0OEKY^dMquXH46L~+iij#EQkbyf%<R)`L<<Qy*{N%UsFY(ks>)`I_y6!
z(7f*@r<kRX*O+NBVtzErF0?ET*DkLKh9l**%j$2csjq~b2q!}lk;W?{^%c~CuRa{a
zdS(%2L3v|15)3S8fH0ssZ!>RAs2q9b(rV~srChT%SRTBYG%mz`XTCNP2;N*A2=bt_
zz5=x(Bt!O43TC#nrs4*UU~FH~O!bB&jHD|GYs??$W~#g7=1@3Lw-grY31RvHDzU45
zix>Iiyji+d>Ao=We{bD~eS`M3#C12G@5Qy5_7gi|I)aq(lCI;r>C$|%frJEz_~Y;Y
zu>>Mp^ac}0)B9ID4^_RaX^C#=fR=uV=O5w!FbTl!Zo-S{o)!4ZS3nKdAj<UrzCDbK
z%E`GQ<%o%VE;%2hyfAMvzCl8sNAO`>*(ekIE3QnG30At0lc3xPXr02?b12&ZAG#1@
zKsh<LO3uqD7h5w8yvT(JhHx>I31%Xf8l<{OITht!k4;C;2Offd!BvcMa!y6L)w9Tv
zwgFGD1J@RmlXD}=q1Ix5e;0Uy^|)T9b^sIk5y4r=b6m(j2+qZoiE?s&LwV4z@(^<a
zPw-J(B`9wPl=2z7ALU}e*~nYAQQf5cg>svATs^=OJdP`l@*rUDrGO|C?72)Y6P!91
z--ZGIIG}*smv|@#z8F{U^=Kck6qg-kobj?Za1l=*;2h*DnZOqTMy|k`qD=ok;#0W7
zDDMPJL0+;6WjkOT*S#ncJc5hb?*+8aNB_hFC>rG+z>EcG1NdCPCAf%xDd0#i`bU}a
zrU0&7lqqMT{D;Z}e`}N}&pBk2|A$%R|Ci}lc&{dw1&FITF^B{{1qi758F;)>XCi1N
zn;E;#1Uu0yu%dDto7JtYz1i7RS6d&NHz^XVpBt*E#`$yT;<}oOU_+>(Dts~C2FxuF
z)y-Ic$t0Zj*4I?wsCs!IND=?MNtevXopgD6s?%8%4&wEKgedR_ww9;YLrt9EMuIir
zn|V7<{EFzT9=!xA5nHcE*s?AV8YU)_eNCuJk(V4;AE<TK(tX~f@(@m(8*T^$CpjZE
zo(c*Q=S`|AuMGtzU0yVExb4#`54WGhaYpY&Gfx|9(ahmVqk7TIggh@V_7r=(UTM))
fB_&4T|K^$Ucsth5`J1heZF>xbAAkR=B=CO#+sc>u

literal 0
HcmV?d00001

diff --git a/IDB-DL/private/ompprof.c b/IDB-DL/private/ompprof.c
new file mode 100644
index 0000000..a83fabd
--- /dev/null
+++ b/IDB-DL/private/ompprof.c
@@ -0,0 +1,113 @@
+/**************************************************************************
+ *
+ * File name: ompprof.c
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 11.4.2009
+ *
+ *************************************************************************/
+
+
+#include "ompprof.h"
+
+
+/* initialize profiling information */
+
+void initprofdata(profdata *pd)
+{
+  pd->DtX_time = 0;
+  pd->XtX_time = 0;
+  pd->DtR_time = 0;
+  pd->maxabs_time = 0;
+  pd->DtD_time = 0;
+  pd->Lchol_time = 0;
+  pd->compcoef_time = 0;
+  pd->update_DtR_time = 0;
+  pd->update_resnorm_time = 0;
+  pd->compres_time = 0;
+  pd->indexsort_time = 0;
+  
+  pd->DtX_time_counted = 0;
+  pd->XtX_time_counted = 0;
+  pd->DtR_time_counted = 0;
+  pd->DtD_time_counted = 0;
+  pd->update_DtR_time_counted = 0;
+  pd->resnorm_time_counted = 0;
+  pd->compres_time_counted = 0;
+  pd->indexsort_time_counted = 0;
+  
+  pd->prevtime = clock();
+}
+
+
+/* add elapsed time to profiling data according to specified computation */
+
+void addproftime(profdata *pd, int comptype)
+{
+  switch(comptype) {
+    case DtX_TIME:            pd->DtX_time            += clock()-pd->prevtime; pd->DtX_time_counted = 1; break;
+    case XtX_TIME:            pd->XtX_time            += clock()-pd->prevtime; pd->XtX_time_counted = 1; break;
+    case DtR_TIME:            pd->DtR_time            += clock()-pd->prevtime; pd->DtR_time_counted = 1; break;
+    case DtD_TIME:            pd->DtD_time            += clock()-pd->prevtime; pd->DtD_time_counted = 1; break;
+    case COMPRES_TIME:        pd->compres_time        += clock()-pd->prevtime; pd->compres_time_counted = 1; break;
+    case UPDATE_DtR_TIME:     pd->update_DtR_time     += clock()-pd->prevtime; pd->update_DtR_time_counted = 1; break;
+    case UPDATE_RESNORM_TIME: pd->update_resnorm_time += clock()-pd->prevtime; pd->resnorm_time_counted = 1; break;
+    case INDEXSORT_TIME:      pd->indexsort_time      += clock()-pd->prevtime; pd->indexsort_time_counted = 1; break;
+    case MAXABS_TIME:         pd->maxabs_time         += clock()-pd->prevtime; break;
+    case LCHOL_TIME:          pd->Lchol_time          += clock()-pd->prevtime; break;
+    case COMPCOEF_TIME:       pd->compcoef_time       += clock()-pd->prevtime; break;
+  }
+  pd->prevtime = clock();
+}
+
+
+/* print profiling info */
+
+void printprofinfo(profdata *pd, int erroromp, int batchomp, int signum)
+{
+  clock_t tottime;
+  
+  tottime = pd->DtX_time + pd->XtX_time + pd->DtR_time + pd->DtD_time + pd->compres_time + pd->maxabs_time + 
+            pd->Lchol_time + pd->compcoef_time + pd->update_DtR_time + pd->update_resnorm_time + pd->indexsort_time;
+  
+  mexPrintf("\n\n*****  Profiling information for %s  *****\n\n", erroromp? "OMP2" : "OMP");
+  
+  mexPrintf("OMP mode: %s\n\n", batchomp? "Batch-OMP" : "OMP-Cholesky");
+  
+  mexPrintf("Total signals processed: %d\n\n", signum);
+  
+  if (pd->DtX_time_counted) {
+    mexPrintf("Compute DtX time:      %7.3lf seconds\n", pd->DtX_time/(double)CLOCKS_PER_SEC);
+  }
+  if (pd->XtX_time_counted) {
+    mexPrintf("Compute XtX time:      %7.3lf seconds\n", pd->XtX_time/(double)CLOCKS_PER_SEC);
+  }
+  mexPrintf("Max abs time:          %7.3lf seconds\n", pd->maxabs_time/(double)CLOCKS_PER_SEC);
+  if (pd->DtD_time_counted) {
+    mexPrintf("Compute DtD time:      %7.3lf seconds\n", pd->DtD_time/(double)CLOCKS_PER_SEC);
+  }
+  mexPrintf("Lchol update time:     %7.3lf seconds\n", pd->Lchol_time/(double)CLOCKS_PER_SEC);
+  mexPrintf("Compute coef time:     %7.3lf seconds\n", pd->compcoef_time/(double)CLOCKS_PER_SEC);
+  if (pd->compres_time_counted) {
+    mexPrintf("Compute R time:        %7.3lf seconds\n", pd->compres_time/(double)CLOCKS_PER_SEC);
+  }
+  if (pd->DtR_time_counted) {
+    mexPrintf("Compute DtR time:      %7.3lf seconds\n", pd->DtR_time/(double)CLOCKS_PER_SEC);
+  }
+  if (pd->update_DtR_time_counted) {
+    mexPrintf("Update DtR time:       %7.3lf seconds\n", pd->update_DtR_time/(double)CLOCKS_PER_SEC);
+  }
+  if (pd->resnorm_time_counted) {
+    mexPrintf("Update resnorm time:   %7.3lf seconds\n", pd->update_resnorm_time/(double)CLOCKS_PER_SEC);
+  }
+  if (pd->indexsort_time_counted) {
+    mexPrintf("Index sort time:       %7.3lf seconds\n", pd->indexsort_time/(double)CLOCKS_PER_SEC);
+  }
+  mexPrintf("---------------------------------------\n");
+  mexPrintf("Total time:            %7.3lf seconds\n\n", tottime/(double)CLOCKS_PER_SEC);
+}
+
diff --git a/IDB-DL/private/ompprof.h b/IDB-DL/private/ompprof.h
new file mode 100644
index 0000000..e71dbe1
--- /dev/null
+++ b/IDB-DL/private/ompprof.h
@@ -0,0 +1,106 @@
+/**************************************************************************
+ *
+ * File name: ompprof.h
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 18.8.2009
+ *
+ * Collection of definitions and functions for profiling the OMP method.
+ *
+ *************************************************************************/
+
+
+#ifndef __OMP_PROF_H__
+#define __OMP_PROF_H__
+
+#include "mex.h"
+#include <time.h>
+
+
+
+/**************************************************************************
+ *
+ * Constants and data types.
+ *
+ **************************************************************************/
+
+
+/* constants denoting the various parts of the algorithm */
+
+enum { DtX_TIME, XtX_TIME, DtR_TIME, MAXABS_TIME, DtD_TIME, LCHOL_TIME, COMPCOEF_TIME, 
+       UPDATE_DtR_TIME, UPDATE_RESNORM_TIME, COMPRES_TIME, INDEXSORT_TIME };
+
+       
+       
+/* profiling data container with counters for each part of the algorithm */
+       
+typedef struct profdata 
+{
+  clock_t prevtime;  /* the time when last initialization/call to addproftime() was performed */
+  
+  clock_t DtX_time;
+  clock_t XtX_time;
+  clock_t DtR_time;
+  clock_t maxabs_time;
+  clock_t DtD_time;
+  clock_t Lchol_time;
+  clock_t compcoef_time;
+  clock_t update_DtR_time;
+  clock_t update_resnorm_time;
+  clock_t compres_time;
+  clock_t indexsort_time;
+  
+  /* flags indicating whether profiling data was gathered */
+  int DtX_time_counted;
+  int XtX_time_counted;
+  int DtR_time_counted;
+  int DtD_time_counted;
+  int update_DtR_time_counted;
+  int resnorm_time_counted;
+  int compres_time_counted;
+  int indexsort_time_counted;
+  
+} profdata;
+
+
+
+/**************************************************************************
+ *
+ * Initialize a profdata structure, zero all counters, and start its timer.
+ *
+ **************************************************************************/
+void initprofdata(profdata *pd);
+
+
+/**************************************************************************
+ *
+ * Add elapsed time from last call to addproftime(), or from initialization
+ * of profdata, to the counter specified by comptype. comptype must be one
+ * of the constants in the enumeration above.
+ *
+ **************************************************************************/
+void addproftime(profdata *pd, int comptype);
+
+
+/**************************************************************************
+ *
+ * Print the current contents of the counters in profdata.
+ *
+ * Parameters:
+ *   pd - the profdata to print
+ *   erroromp - indicates whether error-based (nonzero) or sparsity-based (zero)
+ *              omp was performed.
+ *   batchomp - indicates whether batch-omp (nonzero) or omp-cholesky (zero)
+ *              omp was performed.
+ *   signum   - number of signals processed by omp
+ *
+ **************************************************************************/
+void printprofinfo(profdata *pd, int erroromp, int batchomp, int signum);
+
+
+#endif
+
diff --git a/IDB-DL/private/omputils.c b/IDB-DL/private/omputils.c
new file mode 100644
index 0000000..d70ffe0
--- /dev/null
+++ b/IDB-DL/private/omputils.c
@@ -0,0 +1,89 @@
+/**************************************************************************
+ *
+ * File name: omputils.c
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 18.8.2009
+ *
+ *************************************************************************/
+
+#include "omputils.h"
+#include <math.h>
+
+
+const char FULL_GAMMA_STR[] = "full";
+const char SPARSE_GAMMA_STR[] = "sparse";
+
+
+/* convert seconds to hours, minutes and seconds */
+
+void secs2hms(double sectot, int *hrs, int *mins, double *secs)
+{
+  *hrs = (int)(floor(sectot/3600)+1e-2);
+  sectot = sectot - 3600*(*hrs);
+  *mins = (int)(floor(sectot/60)+1e-2);
+  *secs = sectot - 60*(*mins);
+}
+
+
+/* quicksort, public-domain C implementation by Darel Rex Finley. */
+/* modification: sorts the array data[] as well, according to the values in the array vals[] */
+
+#define  MAX_LEVELS  300
+
+void quicksort(mwIndex vals[], double data[], mwIndex n) {
+  
+  long piv, beg[MAX_LEVELS], end[MAX_LEVELS], i=0, L, R, swap ;
+  double datapiv;
+  
+  beg[0]=0;
+  end[0]=n;
+  
+  while (i>=0) {
+    
+    L=beg[i]; 
+    R=end[i]-1;
+    
+    if (L<R) {
+      
+      piv=vals[L];
+      datapiv=data[L];
+      
+      while (L<R) 
+      {
+        while (vals[R]>=piv && L<R) 
+          R--;
+        if (L<R) {
+          vals[L]=vals[R];
+          data[L++]=data[R];
+        }
+        
+        while (vals[L]<=piv && L<R) 
+          L++;
+        if (L<R) { 
+          vals[R]=vals[L];
+          data[R--]=data[L];
+        }
+      }
+      
+      vals[L]=piv;
+      data[L]=datapiv;
+      
+      beg[i+1]=L+1;
+      end[i+1]=end[i];
+      end[i++]=L;
+      
+      if (end[i]-beg[i] > end[i-1]-beg[i-1]) {
+        swap=beg[i]; beg[i]=beg[i-1]; beg[i-1]=swap;
+        swap=end[i]; end[i]=end[i-1]; end[i-1]=swap;
+      }
+    }
+    else {
+      i--;
+    }
+  }
+}
diff --git a/IDB-DL/private/omputils.h b/IDB-DL/private/omputils.h
new file mode 100644
index 0000000..32839cd
--- /dev/null
+++ b/IDB-DL/private/omputils.h
@@ -0,0 +1,77 @@
+/**************************************************************************
+ *
+ * File name: omputils.h
+ *
+ * Ron Rubinstein
+ * Computer Science Department
+ * Technion, Haifa 32000 Israel
+ * ronrubin@cs
+ *
+ * Last Updated: 18.8.2009
+ *
+ * Utility definitions and functions for the OMP library.
+ *
+ *************************************************************************/
+
+
+#ifndef __OMP_UTILS_H__
+#define __OMP_UTILS_H__
+
+#include "mex.h"
+
+
+/* constants for the representation mode of gamma */
+
+extern const char FULL_GAMMA_STR[];      /* "full" */
+extern const char SPARSE_GAMMA_STR[];    /* "sparse" */
+
+
+#define FULL_GAMMA 1
+#define SPARSE_GAMMA 2
+#define INVALID_MODE 3
+
+
+
+/**************************************************************************
+ * Memory management for OMP2.
+ *
+ * GAMMA_INC_FACTOR:
+ * The matrix GAMMA is allocated with sqrt(n)/2 coefficients per signal,
+ * for a total of nzmax = L*sqrt(n)/2 nonzeros. Whenever GAMMA needs to be
+ * increased, it is increased by a factor of GAMMA_INC_FACTOR.
+ *
+ * MAT_INC_FACTOR:
+ * The matrices Lchol, Gsub and Dsub are allocated with sqrt(n)/2
+ * columns each. If additional columns are needed, this number is 
+ * increased by a factor of MAT_INC_FACTOR.
+ **************************************************************************/
+
+#define GAMMA_INC_FACTOR (1.4)
+#define MAT_INC_FACTOR (1.6)
+
+
+
+/**************************************************************************
+ * Convert number of seconds to hour, minute and second representation.
+ *
+ * Parameters:
+ *   sectot - total number of seconds
+ *   hrs, mins, secs - output hours (whole) and minutes (whole) and seconds
+ *
+ **************************************************************************/
+void secs2hms(double sectot, int *hrs, int *mins, double *secs);
+
+
+
+/**************************************************************************
+ * QuickSort - public-domain C implementation by Darel Rex Finley.
+ *
+ * Modified to sort both the array vals[] and the array data[] according 
+ * to the values in the array vals[].
+ *
+ **************************************************************************/
+void quicksort(mwIndex vals[], double data[], mwIndex n);
+
+
+#endif
+
diff --git a/IDB-DL/private/printf.m b/IDB-DL/private/printf.m
new file mode 100644
index 0000000..402ac26
--- /dev/null
+++ b/IDB-DL/private/printf.m
@@ -0,0 +1,26 @@
+function str = printf(varargin)
+%PRINTF Print formatted text to screen.
+%  PRINTF(FMT,VAL1,VAL2,...) formats the data in VAL1,VAL2,... according to
+%  the format string FMT, and prints the result to the screen.
+%
+%  The call to PRINTF(FMT,VAL1,VAL2,...) simply invokes the call
+%  DISP(SPRINTF(FMT,VAL1,VAL2,...)). For a complete description of the
+%  format string options see function SPRINTF.
+%
+%  STR = PRINTF(...) also returns the formatted string.
+
+
+%  Ron Rubinstein
+%  Computer Science Department
+%  Technion, Haifa 32000 Israel
+%  ronrubin@cs
+%
+%  April 2008
+
+
+if (nargout>0)
+  str = sprintf(varargin{:});
+  disp(str);
+else
+  disp(sprintf(varargin{:}));
+end
diff --git a/IDB-DL/stdshade.m b/IDB-DL/stdshade.m
new file mode 100644
index 0000000..54a4900
--- /dev/null
+++ b/IDB-DL/stdshade.m
@@ -0,0 +1,67 @@
+function [lineOut, fillOut] = stdshade(amatrix,alpha,acolor,F,smth)
+% usage: stdshading(amatrix,alpha,acolor,F,smth)
+% plot mean and sem/std coming from a matrix of data, at which each row is an
+% observation. sem/std is shown as shading.
+% - acolor defines the used color (default is red) 
+% - F assignes the used x axis (default is steps of 1).
+% - alpha defines transparency of the shading (default is no shading and black mean line)
+% - smth defines the smoothing factor (default is no smooth)
+% smusall 2010/4/23
+
+if exist('acolor','var')==0 || isempty(acolor)
+    acolor='r'; 
+end
+
+if exist('F','var')==0 || isempty(F)
+    F=1:size(amatrix,2);
+end
+
+if exist('smth','var'); if isempty(smth); smth=1; end
+else smth=1; %no smoothing by default
+end  
+
+if ne(size(F,1),1)
+    F=F';
+end
+
+amean = nanmean(amatrix,1); %get man over first dimension
+if smth > 1
+    amean = boxFilter(nanmean(amatrix,1),smth); %use boxfilter to smooth data
+end
+astd = nanstd(amatrix,[],1); % to get std shading
+% astd = nanstd(amatrix,[],1)/sqrt(size(amatrix,1)); % to get sem shading
+
+if exist('alpha','var')==0 || isempty(alpha) 
+    fillOut = fill([F fliplr(F)],[amean+astd fliplr(amean-astd)],acolor,'linestyle','none');
+    acolor='k';
+else
+    fillOut = fill([F fliplr(F)],[amean+astd fliplr(amean-astd)],acolor, 'FaceAlpha', alpha,'linestyle','none');
+end
+
+if ishold==0
+    check=true; else check=false;
+end
+
+hold on;
+lineOut = plot(F,amean, 'color', acolor,'linewidth',1.5); %% change color or linewidth to adjust mean line
+
+if check
+    hold off;
+end
+
+end
+
+
+function dataOut = boxFilter(dataIn, fWidth)
+% apply 1-D boxcar filter for smoothing
+
+fWidth = fWidth - 1 + mod(fWidth,2); %make sure filter length is odd
+dataStart = cumsum(dataIn(1:fWidth-2),2);
+dataStart = dataStart(1:2:end) ./ (1:2:(fWidth-2));
+dataEnd = cumsum(dataIn(length(dataIn):-1:length(dataIn)-fWidth+3),2);
+dataEnd = dataEnd(end:-2:1) ./ (fWidth-2:-2:1);
+dataOut = conv(dataIn,ones(fWidth,1)/fWidth,'full');
+dataOut = [dataStart,dataOut(fWidth:end-fWidth+1),dataEnd];
+
+end
+
diff --git a/IDB-DL/test_idb_dl.m b/IDB-DL/test_idb_dl.m
new file mode 100644
index 0000000..386c862
--- /dev/null
+++ b/IDB-DL/test_idb_dl.m
@@ -0,0 +1,78 @@
+clc;
+clear;
+close all;
+
+% DL parameters
+n_samples = 1000;       % number of signals
+n_features = 64;        % size of signals
+n_components = 100;     % number of atoms
+n_nonzero_coefs = 5;    % sparsity level
+
+M = 1.2;                % barrier margin
+gamma = 0.5;            % trade-off factors
+lambda = 25;
+
+noise_var = 0.01;
+x_threshold = 0.2;
+max_iter = 100;
+n_rounds = 10;
+
+errs_aksvd = zeros(n_rounds, max_iter);
+coh_aksvd = zeros(n_rounds, (n_components - 1)*n_components/2);
+
+errs_idb = zeros(n_rounds, max_iter);
+coh_idb = zeros(n_rounds, (n_components - 1)*n_components/2);
+
+for i_round = 1:n_rounds
+    rng(i_round);
+
+    % Prepare the true dictionary
+    Dt = randn(n_features, n_components);
+    Dt = normc(Dt);
+    
+    % Prepare the set of samples
+    Y = zeros(n_features, n_samples);
+    for i = 1:n_samples
+        support = randperm(n_components);
+        support = support(1:n_nonzero_coefs);
+
+        x = randn(n_nonzero_coefs, 1);
+        x = x + sign(x)*x_threshold;
+
+        Y(:,i) = Dt(:, support)*x + noise_var*randn(n_features, 1);
+    end
+
+    % Init D0
+    D0 = normcol_equal(randn(n_features, n_components));
+
+    % AK-SVD method
+    [~, ~, errs, coh, ~] = aksvd(Y, D0, n_nonzero_coefs, max_iter);
+    coh_aksvd(i_round, :) = coh;
+    errs_aksvd(i_round, :) = errs;
+    
+    % IDB method
+    [~, ~, errs, coh, ~] = idb_dl(Y, D0, n_nonzero_coefs, M, gamma, lambda, max_iter);
+    errs_idb(i_round, :) = errs;
+    coh_idb(i_round, :) = coh;
+end
+
+csize = 14;
+figure();
+stdshade(errs_aksvd, 0.2, 'red');
+hold on; grid on;
+stdshade(errs_idb, 0.2, 'blue');
+xlabel('iteration', 'interpreter', 'latex', 'FontSize', csize)
+ylabel('error', 'interpreter', 'latex', 'FontSize', csize)
+h = legend('', 'AK-SVD', '', 'IDB-DL');
+set(h, 'interpreter', 'latex', 'FontSize', csize);
+pbaspect([1, 0.5, 1]);
+                
+figure();
+plot(mean(coh_aksvd), 'red')
+hold on; grid on;
+plot(mean(coh_idb), 'blue')
+xlabel('\# product', 'interpreter', 'latex', 'FontSize', csize)
+ylabel('atom scalar products', 'interpreter', 'latex', 'FontSize', csize)
+h = legend('AK-SVD', 'IDB-DL');
+set(h, 'interpreter', 'latex', 'FontSize', csize);
+pbaspect([1, 0.5, 1]);
diff --git a/IDB/ISPM_cor_tol.m b/IDB/ISPM_cor_tol.m
new file mode 100644
index 0000000..e26ac68
--- /dev/null
+++ b/IDB/ISPM_cor_tol.m
@@ -0,0 +1,61 @@
+function [F, cohv] = ISPM_cor_tol(m, n, desired_coh, alpha, Nit, tol_stop)
+
+% Incoherence via Shifted Power Method
+% Corrected version of the algorithm appeared in IEEE SPL, vol.24, Sept.2017
+% "Designing Incoherent Frames With Only Matrix–Vector Multiplications".
+% This version includes a stopping criterion, to take advantage of quick convergence.
+%
+% Input:
+%   m,n         - frame size
+%   desired_coh - desired coherence, used as target (if too small, the
+%                 algorithm does not converge)
+%   alpha       - trade-off parameter; should be 0.5 or larger
+%   Nit         - number of iterations (the algorithm does not stop bases
+%                 on an error criterion
+%   tol_stop    - stopping tolerance
+% Output:
+%   F           - the frame
+%   cohv        - vector of mutual coherences at each iteration
+
+% Note: the correction regards eq.(6): a square root should be applied on
+% the right hand side
+
+% BD 3.05.2018, 11.11.2022
+
+% start with UNTF
+F = gen_rand_untf(m,n);
+
+cohv = zeros(1,Nit);
+coh_min = 1;
+
+% main loop
+for k = 1 : Nit
+  perm = randperm(n-1)+1;
+  for j = perm  % a round of the atoms in random order
+    d = F(:,j);
+    v = F'*d;
+    v(j) = 0;
+    w = max(abs(v)/desired_coh, 1);
+    w_max = max(w);
+    eig_shift = (n/m)*alpha*w_max + (1-alpha)*sum(w)/m; % compute shift
+    d = F*(w.*v) - eig_shift*d; % power method iteration
+    d = d / norm(d);
+    F(:,j) = d;
+  end
+  cohv(k) = max(max(abs(F'*F - eye(n))));
+  if cohv(k) < coh_min
+    coh_min = cohv(k);
+    F_best = F;
+  end
+
+  if coh_min - desired_coh < tol_stop  % if coherence is near enough the target, stop
+    cohv = cohv(1:k);
+    break
+  end
+
+end
+
+% make sure that it is UNTF (most likely useless)
+F = F_best;
+%[U,~,V] = svd(F);
+%F = U*V(:,1:m)'*sqrt(n/m);
diff --git a/IDB/README.md b/IDB/README.md
new file mode 100644
index 0000000..d9e7b1d
--- /dev/null
+++ b/IDB/README.md
@@ -0,0 +1,19 @@
+# IDB - Incoherent frames design using a distance barrier
+
+We present a novel method for incoherent frame design.
+The proposed algorithm uses a distance barrier term that promotes incoherence.
+
+## Citing Us
+
+If you use our work, please cite:
+
+Denis C. Ilie Ablachim and Bogdan Dumitrescu, "Incoherent frames design and dictionary learning using a
+distance barrier", submitted, 2023. See [paper](http://asydil.upb.ro/publications/).
+
+## Reproducing the results
+
+To reproduce the article results, run the script "test\_idb".
+
+## Funding
+
+This work is supported by a grant of the Ministry of Research, Innovation and Digitization, CNCS – UEFISCDI, project number PN-III-P4-PCE-2021-0154, within PNCDI III.
diff --git a/IDB/bisection_idb.m b/IDB/bisection_idb.m
new file mode 100644
index 0000000..6c1e893
--- /dev/null
+++ b/IDB/bisection_idb.m
@@ -0,0 +1,52 @@
+function [Fbest, coh_best, iter_total_count] = bisection_idb(mu_min, mu_max, nr_it_bis, m, n, ...
+                                             K, lambda, gamma0, rho, search_it, tol_stop)
+
+% Compute low coherence frames by bisection and the IDB algorithm
+% Input:
+%   mu_min          - lower limit of the search interval
+%   mu_max          - upper limit of the search interval
+%   nr_it_bis       - number of bisection iterations
+%   m
+%   n               - frame size
+%   K               - number of IDB iterations
+%   lambda          - trade-off factor
+%   gamma0          - initial step size
+%   rho             - step size decrease factor
+%   search_it       - number of halving steps in gradient search
+%   tol_stop        - stopping tolerance
+% Output:
+%   Fbest           - frame with best coherence
+%   coh_best        - coherence value
+%   iter_total_count- total number of IDB iterations in the bisection algorithm
+
+% BD 11.11.2022
+
+iter_total_count = 0;
+coh_best = 1;
+for i_bis = 1:nr_it_bis
+  i_bis
+  mu = (mu_min + mu_max)/2
+  [F, coh] = idb(m, n, mu, lambda, K, gamma0, rho, search_it, tol_stop);
+  iter_total_count = iter_total_count + length(coh);
+  coh_crt = min(coh)
+  if coh_crt < coh_best
+    coh_best = coh_crt;
+    Fbest = F;
+  end
+  if coh_crt < mu + 10*tol_stop   % successful design
+    if coh_crt > mu_max  % if current coherence is larger than current best, stop
+      break              % further improvement is impossible, since the interval stays the same
+    end
+    mu_max = coh_best;
+  else
+    mu_min = mu;
+    mu_max = min(coh_crt, mu_max); % maybe the upper bound can be also improved
+  end
+  
+  coh_best
+  fprintf("Interval [%f,%f]\n", mu_min, mu_max);
+
+  if mu_max - mu_min < tol_stop  % stop if search interval is very small
+    break
+  end
+end
diff --git a/IDB/bisection_ispm.m b/IDB/bisection_ispm.m
new file mode 100644
index 0000000..5f7f618
--- /dev/null
+++ b/IDB/bisection_ispm.m
@@ -0,0 +1,36 @@
+function [Fbest, coh_best, iter_total_count] = bisection_ISPM(mu_min, mu_max, nr_it_bis, m, n, ...
+                                             Nit, alpha, tol_stop)
+
+% Compute low coherence frames by bisection and the ISPM algorithm
+
+% BD 11.11.2022
+
+iter_total_count = 0;
+coh_best = 1;
+for i_bis = 1:nr_it_bis
+  i_bis
+  mu = (mu_min + mu_max)/2
+  [F, coh] = ISPM_cor_tol(m, n, mu, alpha, Nit, tol_stop);
+  iter_total_count = iter_total_count + length(coh);
+  coh_crt = min(coh)
+  if coh_crt < coh_best
+    coh_best = coh_crt;
+    Fbest = F;
+  end
+  if coh_crt < mu + 5*tol_stop   % successful design
+    if coh_crt > mu_max  % if current coherence is larger than current best, stop
+      break              % further improvement is impossible, since the interval stays the same
+    end
+    mu_max = coh_best;
+  else
+    mu_min = mu;
+    mu_max = min(coh_crt, mu_max); % maybe the upper bound can be also improved
+  end
+  
+  coh_best
+  fprintf("Interval [%f,%f]\n", mu_min, mu_max);
+
+  if mu_max - mu_min < tol_stop  % stop if search interval is very small
+    break
+  end
+end
diff --git a/IDB/gen_rand_untf.m b/IDB/gen_rand_untf.m
new file mode 100644
index 0000000..04d677b
--- /dev/null
+++ b/IDB/gen_rand_untf.m
@@ -0,0 +1,33 @@
+function D = gen_rand_untf(m,n)
+
+% Generate random unit norm tight frame of size mxn
+
+% BD 10.4.2017
+
+% generate random tight frame
+D = randn(m,n);
+[Q,~] = qr(D',0);
+D = Q' * sqrt(n/m);  % rows have correct norm
+
+% force atoms to unit norm
+atom_norms = sum(D.*D);
+for i = 1 : n-1
+  if atom_norms(i) ~= 1  % do nothing if it happens that the norm is 1
+    s1 = sign(atom_norms(i)-1);
+    j = i+1;
+    while sign(atom_norms(j)-1) == s1  % find atom with norm on the other side of 1
+      j = j+1;
+    end
+    % compute tangent of rotation angle
+    an1 = atom_norms(i);
+    an2 = atom_norms(j);
+    cp = D(:,i)'*D(:,j);
+    t = (cp + sign(cp)*sqrt(cp*cp - (an1-1)*(an2-1))) / (an2-1);
+    % compute rotation
+    c = 1 / sqrt(1+t*t);
+    s = c*t;
+    % new atoms and updated norm
+    D(:,[i j]) = D(:,[i j]) * [c s; -s c];
+    atom_norms(j) = an1+an2-1;
+  end
+end
diff --git a/IDB/getBound.m b/IDB/getBound.m
new file mode 100644
index 0000000..d664bd2
--- /dev/null
+++ b/IDB/getBound.m
@@ -0,0 +1,25 @@
+%* ------------------------------------------------------------------------
+%*   Bashar Tahir, bashar.tahir@tuwien.ac.at
+%*   (c) 2019 Institute of Telecommunications, TU Wien.
+%*   www.nt.tuwien.ac.at 
+%*
+%*   The algorithm is published under
+%*   B. Tahir, S. Schwarz, and M. Rupp, "Constructing Grassmannian Frames by
+%*   an Iterative Collision-Based Packing," in IEEE Signal Processing
+%*   Letters, 2019. DOI: 10.1109/LSP.2019.2919391 .
+%* -----------------------------------------------------------------------
+%*   The following function calculates a lower bound on the frame coherence, 
+%*   which is described in the publication above.
+%*
+%%
+function bound = getBound(N,M)
+    if M <= N^2
+            bound = sqrt((M-N)/(N*(M-1)));
+    elseif N^2 < M && M <= 2*(N^2-1)
+            bound = max(max(sqrt(1/N), sqrt((2*M-N^2-N)/((N+1)*(M-N)))), 1 - 2*M^(-1/(N-1)));
+    elseif M > 2*(N^2-1)
+            bound = max(sqrt((2*M-N^2-N)/((N+1)*(M-N))), 1 - 2*M^(-1/(N-1)));
+    end
+end
+
+%</
\ No newline at end of file
diff --git a/IDB/idb.m b/IDB/idb.m
new file mode 100644
index 0000000..d4fb481
--- /dev/null
+++ b/IDB/idb.m
@@ -0,0 +1,82 @@
+function [F, cohv] = idb(m, n, desired_coh, lambda, K, gamma0, rho, search_it, tol_stop)
+
+% Minimize frame coherence using a distance barrier function
+% Input:
+%   m
+%   n               - frame size
+%   desired_coh     - target coherence
+%   K               - number of IDB iterations
+%   lambda          - trade-off factor
+%   gamma0          - initial step size
+%   rho             - step size decrease factor
+%   search_it       - number of halving steps in gradient search
+%   tol_stop        - stopping tolerance
+% Output:
+%   F               - frame with best coherence
+%   cohv            - vector with the coherence at each iteration
+
+% BD 8.08.2022, 11.11.2022
+
+if nargin < 9
+  tol_stop = 0.0001;
+end
+
+%F = gen_rand_untf(m,n);
+F = normc(randn(m,n));
+
+cohv = zeros(1,K);
+coh_min = 1;
+
+for k = 1 : K
+  perm = randperm(n-1)+1;
+  for j = perm  % a round of the atoms in random order
+    d = F(:,j);
+    v = F'*d;
+    v(j) = 0;
+    % what atoms are too close?
+    i_minus = find(v>desired_coh);
+    i_plus = find(v<-desired_coh);
+    % weighted least squares
+    w = max(abs(v)/desired_coh, 1);
+    % gradient
+    %g = F*v;
+    g = F*(w.*v);
+    %g = zeros(m,1);
+    if ~isempty(i_minus)
+      g = g + lambda*sum(F(:,i_minus) - repmat(d,1,length(i_minus)), 2);
+    end
+    if ~isempty(i_plus)
+      g = g - lambda*sum(F(:,i_plus) + repmat(d,1,length(i_plus)), 2);
+    end
+    
+    % loop trying to find better atom
+    g_now = gamma0;
+    c_max = max(abs(v));
+    for i = 1 : search_it   % a number of iterations to avoid getting stuck
+      dn = d - g_now*g;
+      dn = dn / norm(dn);
+      vn = F'*dn;
+      vn(j)=0;
+      if max(abs(vn)) < c_max
+        i = i-1;
+        break;
+      end
+      g_now = g_now/2;
+    end
+    F(:,j) = dn;
+  end
+  gamma0 = gamma0*rho;
+  
+  cohv(k) = max(max(abs(F'*F - eye(n))));
+  if cohv(k) < coh_min
+    coh_min = cohv(k);
+    F_best = F;
+  end
+  if coh_min - desired_coh < tol_stop  % if coherence is near enough the target, stop
+    cohv = cohv(1:k);
+    break
+  end
+  %coh_min
+end
+
+F = F_best;
diff --git a/IDB/test_idb.m b/IDB/test_idb.m
new file mode 100644
index 0000000..3cb46c5
--- /dev/null
+++ b/IDB/test_idb.m
@@ -0,0 +1,38 @@
+% Design incoherent frames using a distance barrier.
+% Bisection is used to find the best coherence value.
+
+m = 90;
+n = 100;         % frame size
+K = 2000;        % number of iterations
+run_also_ISPM = 0; % run also ISPM with the same frame size
+
+%-------------------------------------------
+% IDB parameters
+lambda = 0.2;    % trade-off factor
+gamma0 = 0.1;    % initial step size
+rho = 0.999;     % step size decrease factor
+search_it = 5;   % number of halving steps in gradient search
+tol_stop = 1e-4; % stopping tolerance
+K_bis = 15;      % number of bisection iterations
+%-------------------------------------------
+
+mu_min = getBound(m,n); % lower coherence bound
+Fr = normc(randn(m,n));
+mu_max = max(max(abs(Fr'*Fr - eye(n)))); % coherence of a random frame
+fprintf("Interval [%f,%f]\n", mu_min, mu_max);
+[F_IDB, coh_best, iter_total_count] = bisection_idb(mu_min, mu_max, K_bis, m, n, K, lambda,...
+                                      gamma0, rho, search_it, tol_stop);
+fprintf("Total number of IDB iterations: %d\n", iter_total_count)
+coh_IDB = max(max(abs(F_IDB'*F_IDB - eye(n))));
+fprintf("Final IDB coherence: %f\n", coh_IDB)
+
+%-------------------------------------------
+% ISPM parameters
+alpha = 0.5;
+%-------------------------------------------
+if run_also_ISPM
+    [F_ISPM, coh_best, iter_total_count] = bisection_ispm(mu_min, mu_max, K_bis, m, n, K, alpha, tol_stop);
+    fprintf("Total number of ISPM iterations: %d\n", iter_total_count)
+    coh_ISPM = max(max(abs(F_ISPM'*F_ISPM - eye(n))));
+    fprintf("Final ISPM coherence: %f\n", coh_ISPM)
+end
\ No newline at end of file
diff --git a/README.md b/README.md
index ad3a034..7079a0d 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,23 @@
 # Incoherent frames design and dictionary learning using a distance barrier
 
+This repository contains the two implementations presented in our paper "Incoherent frames design and dictionary learning using a
+distance barrier": Incoherent frames design using a distance barrier (IDB) and Incoherent dictionary learning using a distance barrier (IDB-DL).
 
+## Citing Us
 
-## Getting started
-
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
-
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
-
-## Add your files
-
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
+If you use our work, please cite:
 
 ```
-cd existing_repo
-git remote add origin https://gitlab.cs.pub.ro/asydil/incoherent-frames-design-and-dictionary-learning-using-a-distance-barrier.git
-git branch -M main
-git push -uf origin main
+@inproceedings{ilie2023classification,
+  title={Classification With Dictionary Learning and A Distance Barrier Promoting Incoherence},
+  author={Ilie-Ablachim, Denis C and Dumitrescu, Bogdan},
+  booktitle={2023 IEEE 33rd International Workshop on Machine Learning for Signal Processing (MLSP)},
+  pages={1--6},
+  year={2023},
+  organization={IEEE}
+}
 ```
 
-## Integrate with your tools
-
-- [ ] [Set up project integrations](https://gitlab.cs.pub.ro/asydil/incoherent-frames-design-and-dictionary-learning-using-a-distance-barrier/-/settings/integrations)
-
-## Collaborate with your team
-
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
-
-## Test and Deploy
-
-Use the built-in continuous integration in GitLab.
-
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
-
-***
-
-# Editing this README
-
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
-
-## Suggestions for a good README
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
-
-## Name
-Choose a self-explaining name for your project.
-
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
-
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
-
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
-
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
-
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
-
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
-
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
-
-## License
-For open source projects, say how it is licensed.
+## Funding
 
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+This work is supported by a grant of the Ministry of Research, Innovation and Digitization, CNCS – UEFISCDI, project number PN-III-P4-PCE-2021-0154, within PNCDI III.
-- 
GitLab