From f0b10ca9df1be2a185a71c7f033a4d7271f47a97 Mon Sep 17 00:00:00 2001
From: Johannes Pfeifer <jpfeifer@gmx.de>
Date: Sun, 8 Oct 2023 18:18:43 +0200
Subject: [PATCH] Octave: workaround for intersect with stable flag providing
 wrong third output (Octave bug #60347)

Uses function removed in 47477e152a3575b0108f5845ba76ec2ca3c54330
---
 matlab/compute_moments_varendo.m              |  6 +-
 matlab/conditional_variance_decomposition.m   |  7 +-
 ...al_variance_decomposition_ME_mc_analysis.m | 10 +-
 matlab/disp_moments.m                         |  8 +-
 matlab/dynare_config.m                        |  5 +
 .../intersect_stable/intersect_stable.m       | 91 +++++++++++++++++++
 .../check_measurement_error_requested_vars.m  |  6 +-
 ...lay_unconditional_variance_decomposition.m |  6 +-
 matlab/posterior_analysis.m                   |  6 +-
 9 files changed, 136 insertions(+), 9 deletions(-)
 create mode 100644 matlab/missing/intersect_stable/intersect_stable.m

diff --git a/matlab/compute_moments_varendo.m b/matlab/compute_moments_varendo.m
index e50f1adae8..5d38ce71f9 100644
--- a/matlab/compute_moments_varendo.m
+++ b/matlab/compute_moments_varendo.m
@@ -171,7 +171,11 @@ if options_.order==1
             skipline();
         end
         if ~all(diag(M_.H)==0)
-            [observable_name_requested_vars, varlist_pos] = intersect(var_list_, options_.varobs, 'stable');
+            if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+                [observable_name_requested_vars, varlist_pos] = intersect_stable(var_list_, options_.varobs);
+            else
+                [observable_name_requested_vars, varlist_pos] = intersect(var_list_, options_.varobs, 'stable');
+            end
             if ~isempty(observable_name_requested_vars)
                 NumberOfObservedEndogenousVariables = length(observable_name_requested_vars);
                 temp = NaN(NumberOfObservedEndogenousVariables, NumberOfExogenousVariables+1);
diff --git a/matlab/conditional_variance_decomposition.m b/matlab/conditional_variance_decomposition.m
index c9d92e406a..3ece520421 100644
--- a/matlab/conditional_variance_decomposition.m
+++ b/matlab/conditional_variance_decomposition.m
@@ -1,4 +1,5 @@
 function [ConditionalVarianceDecomposition, ConditionalVarianceDecomposition_ME]= conditional_variance_decomposition(M_,options_,dr, Steps, SubsetOfVariables)
+% [ConditionalVarianceDecomposition, ConditionalVarianceDecomposition_ME]= conditional_variance_decomposition(M_,options_,dr, Steps, SubsetOfVariables)
 % This function computes the conditional variance decomposition of a given state space model
 % for a subset of endogenous variables.
 %
@@ -88,7 +89,11 @@ end
 % Measurement error
 
 if ~all(diag(M_.H)==0)
-    [observable_pos,index_subset,index_observables]=intersect(SubsetOfVariables,options_.varobs_id,'stable');
+    if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+        [observable_pos,index_subset,index_observables]=intersect_stable(SubsetOfVariables,options_.varobs_id);
+    else
+        [observable_pos,index_subset,index_observables]=intersect(SubsetOfVariables,options_.varobs_id,'stable');
+    end
     ME_Variance=diag(M_.H);
 
     ConditionalVarianceDecomposition_ME = zeros(length(observable_pos),length(Steps),number_of_state_innovations+1);
diff --git a/matlab/conditional_variance_decomposition_ME_mc_analysis.m b/matlab/conditional_variance_decomposition_ME_mc_analysis.m
index 52e3ff344e..c4d4e34969 100644
--- a/matlab/conditional_variance_decomposition_ME_mc_analysis.m
+++ b/matlab/conditional_variance_decomposition_ME_mc_analysis.m
@@ -1,5 +1,6 @@
 function oo_ = ...
     conditional_variance_decomposition_ME_mc_analysis(NumberOfSimulations, type, dname, fname, Steps, exonames, exo, var_list, endo, mh_conf_sig, oo_,options_)
+%oo_ = conditional_variance_decomposition_ME_mc_analysis(NumberOfSimulations, type, dname, fname, Steps, exonames, exo, var_list, endo, mh_conf_sig, oo_,options_)
 % This function analyses the (posterior or prior) distribution of the
 % endogenous variables' conditional variance decomposition with measurement error.
 %
@@ -22,7 +23,7 @@ function oo_ = ...
 % OUTPUTS
 %   oo_          [structure]        Dynare structure where the results are saved.
 
-% Copyright © 2017-2020 Dynare Team
+% Copyright © 2017-2023 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -63,7 +64,12 @@ if isempty(exogenous_variable_index)
     end
 end
 
-[observable_pos_requested_vars,index_subset,index_observables]=intersect(var_list,options_.varobs,'stable');
+if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+    [~,index_subset]=intersect_stable(var_list,options_.varobs);
+else
+    [~,index_subset]=intersect(var_list,options_.varobs,'stable');
+end
+
 matrix_pos=strmatch(endo, var_list(index_subset),'exact');
 name_1 = endo;
 name_2 = exo;
diff --git a/matlab/disp_moments.m b/matlab/disp_moments.m
index 0dd5953269..b9759cd51d 100644
--- a/matlab/disp_moments.m
+++ b/matlab/disp_moments.m
@@ -11,7 +11,7 @@ function oo_=disp_moments(y,var_list,M_,options_,oo_)
 % OUTPUTS
 %   oo_                 [structure]    Dynare's results structure,
 
-% Copyright © 2001-2021 Dynare Team
+% Copyright © 2001-2023 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -50,7 +50,11 @@ y = y(ivar,options_.drop+1:end)';
 
 ME_present=0;
 if ~all(M_.H==0)
-    [observable_pos_requested_vars, index_subset, index_observables] = intersect(ivar, options_.varobs_id, 'stable');
+    if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+        [observable_pos_requested_vars, index_subset, index_observables] = intersect_stable(ivar, options_.varobs_id);
+    else
+        [observable_pos_requested_vars, index_subset, index_observables] = intersect(ivar, options_.varobs_id, 'stable');
+    end
     if ~isempty(observable_pos_requested_vars)
         ME_present=1;
         i_ME = setdiff([1:size(M_.H,1)],find(diag(M_.H) == 0)); % find ME with 0 variance
diff --git a/matlab/dynare_config.m b/matlab/dynare_config.m
index 31e7dce80b..2dd275572c 100644
--- a/matlab/dynare_config.m
+++ b/matlab/dynare_config.m
@@ -122,6 +122,11 @@ if isoctave || matlab_ver_less_than('8.4')
     p{end+1} = '/missing/datetime';
 end
 
+% intersect with 'stable' flag is broken before Octave 8.4, bug #60347
+if isoctave && octave_ver_less_than('8.4')
+    p{end+1} = '/missing/intersect_stable';
+end
+
 P = cellfun(@(c)[dynareroot(1:end-1) c], p, 'uni',false);
 
 % Get mex files folder(s)
diff --git a/matlab/missing/intersect_stable/intersect_stable.m b/matlab/missing/intersect_stable/intersect_stable.m
new file mode 100644
index 0000000000..155e154010
--- /dev/null
+++ b/matlab/missing/intersect_stable/intersect_stable.m
@@ -0,0 +1,91 @@
+function [c, ia, ib] = intersect_stable(a, b)
+% Crude implementation of intersect(…, 'stable'), which is missing before
+% Octave 6.2 and buggy before 8.4 (#60347)
+
+% Copyright (C) 2019-2023 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+if nargin ~= 2
+    error('intersect_stable: needs exactly 2 input arguments');
+end
+
+if isnumeric (a) && isnumeric (b)
+    c = [];
+elseif iscell (b)
+    c = {};
+else
+    c = '';
+end
+ia = [];
+ib = [];
+
+if isempty (a) || isempty (b)
+    return
+else
+    isrowvec = isrow (a) && isrow (b);
+
+    for i = 1:numel(a)
+        if iscellstr(c)
+            idx = strcmp(a(i), b);
+        else
+            idx = a(i) == b;
+        end
+        if any(idx) && ~ismember(a(i), c)
+            c = [c(:); a(i)];
+            if nargout > 1
+                ia = [ia, i];
+                ib = [ib, find(idx)];
+            end
+        end
+    end
+
+    %% Adjust output orientation for MATLAB compatibility
+    if isrowvec
+        c = c.';
+    end
+end
+end
+
+%!test
+%! a = [3 4 1 5];
+%! b = [2 4 9 1 6];
+%! [c,ia,ib]=intersect_stable(a,b);
+%! assert(c, [4 1])
+%! assert(ia, [2 3])
+%! assert(ib, [2 4])
+%! assert(a(ia), c)
+%! assert(b(ib), c)
+
+%!test
+%! a = [3 4 1 5]';
+%! b = [2 4 9 1 6]';
+%! [c,ia,ib]=intersect_stable(a,b);
+%! assert(c, [4 1]')
+%! assert(ia, [2 3])
+%! assert(ib, [2 4])
+%! assert(a(ia), c)
+%! assert(b(ib), c)
+
+%!test
+%! a = { 'defun', 'mapcar', 'let', 'eval-when'};
+%! b = { 'setf', 'let', 'list', 'cdr', 'defun'};
+%! [c,ia,ib]=intersect_stable(a,b);
+%! assert(c, { 'defun', 'let' })
+%! assert(ia, [1 3])
+%! assert(ib, [5 2])
+%! assert(a(ia), c)
+%! assert(b(ib), c)
diff --git a/matlab/moments/check_measurement_error_requested_vars.m b/matlab/moments/check_measurement_error_requested_vars.m
index 76bee009d9..6f7419b1fd 100644
--- a/matlab/moments/check_measurement_error_requested_vars.m
+++ b/matlab/moments/check_measurement_error_requested_vars.m
@@ -38,7 +38,11 @@ observable_pos_requested_vars=[];
 
 ME_present=false;
 if ~all(diag(M_.H)==0)
-    [observable_pos_requested_vars,index_subset,index_observables]=intersect(ivar,options_.varobs_id,'stable');
+    if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+        [observable_pos_requested_vars,index_subset,index_observables]=intersect_stable(ivar,options_.varobs_id);
+    else
+        [observable_pos_requested_vars,index_subset,index_observables]=intersect(ivar,options_.varobs_id,'stable');
+    end
     if ~isempty(observable_pos_requested_vars)
         ME_present=true;
     end
diff --git a/matlab/moments/display_unconditional_variance_decomposition.m b/matlab/moments/display_unconditional_variance_decomposition.m
index df547d02cf..f07ecb91b6 100644
--- a/matlab/moments/display_unconditional_variance_decomposition.m
+++ b/matlab/moments/display_unconditional_variance_decomposition.m
@@ -45,7 +45,11 @@ if M_.exo_nbr > 1
     lh = cellofchararraymaxlength(labels)+2;
     dyntable(options_, title, headers, labels, 100*oo_.gamma_y{options_.ar+2}(stationary_vars,:), lh, 8, 2);
     if ME_present
-        [stationary_observables, pos_index_subset] = intersect(index_subset, stationary_vars, 'stable');
+        if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+            [stationary_observables, pos_index_subset] = intersect_stable(index_subset, stationary_vars);
+        else
+            [stationary_observables, pos_index_subset] = intersect(index_subset, stationary_vars, 'stable');
+        end
         headers_ME = vertcat(headers, 'ME');
         labels=get_labels_transformed_vars(M_.endo_names,ivar(stationary_observables),options_,false);
         dyntable(options_, [title,' WITH MEASUREMENT ERROR'], headers_ME, labels, ...
diff --git a/matlab/posterior_analysis.m b/matlab/posterior_analysis.m
index 3b523c0a7c..2bc14e27c8 100644
--- a/matlab/posterior_analysis.m
+++ b/matlab/posterior_analysis.m
@@ -66,7 +66,11 @@ switch type
                                              M_.exo_names,arg2,vartan,arg1,options_.mh_conf_sig,oo_,options_);
     if ~all(diag(M_.H)==0)
         if strmatch(arg1,options_.varobs,'exact')
-            [observable_name_requested_vars,index_subset,index_observables]=intersect(vartan,options_.varobs,'stable');
+            if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+                observable_name_requested_vars=intersect_stable(vartan,options_.varobs);
+            else
+                observable_name_requested_vars=intersect(vartan,options_.varobs,'stable');
+            end
             oo_ = variance_decomposition_ME_mc_analysis(SampleSize,'posterior',M_.dname,M_.fname,...
                                                         [M_.exo_names;'ME'],arg2,observable_name_requested_vars,arg1,options_.mh_conf_sig,oo_,options_);
         end
-- 
GitLab