function [G, angle, Type, kappa] = symMinimization(print_Input,print_statPoint,print_Output,make_FunctionPlot, InputPath) %(Q_hom,B_eff)


syms v1 v2 q1 q2 q3 q12 b1 b2 b3


% -------- Options ----------
% % print_Input = false;  %effective quantities
% print_Input = true;
% % print_statPoint = false;
% print_statPoint = true;
% print_Minimizer = true;
% % make_FunctionPlot = false;
% make_FunctionPlot = true;

print_Uniqueness = false;         
check_StationaryPoints = false;   % could be added to Input-Parameters..
compare_with_Classification = false;  %maybe added later --- 


%fprintf('Functions to be minimized:')
f_plus(v1,v2,q1,q2,q3,q12,b1,b2,b3) = q1*v1^4 + q2*v2^4+2*q3*v1^2*v2^2-2*(q1*b1*v1^2+ q2*b2*v2^2+sqrt(2)*q3*b3*v1*v2)...
                                        + q12*(v1^2*v2^2-b2*v1^2-b1*v2^2+b1*b2);
f_minus(v1,v2,q1,q2,q3,q12,b1,b2,b3) = q1*v1^4 + q2*v2^4+2*q3*v1^2*v2^2+2*(q1*b1*v1^2+ q2*b2*v2^2+sqrt(2)*q3*b3*v1*v2)...
                                        + q12*(v1^2*v2^2+b2*v1^2+b1*v2^2+b1*b2);


% ---- Fix parameters


% Epsilon used:
epsilon = 1e-8;


if ~exist('InputPath','var')
     % third parameter does not exist, so default it to something
      absPath = "/home/klaus/Desktop/DUNE/dune-microstructure/outputs";
 end


% 1. Import effective quantities from CellSolver-Code:
%read as sparse Matrix...
try %absolutePath
    Qmat = spconvert(load(absPath + '' + "/QMatrix.txt"));
    Bmat = spconvert(load(absPath + '' + "/BMatrix.txt"));
%     fprintf('Use absolute Path')
catch ME  % use relativePath
    Qmat = spconvert(load('../outputs/QMatrix.txt'));
    Bmat = spconvert(load('../outputs/BMatrix.txt'));
%     fprintf('Use relative Path')
end
%convert to full matrix... 
Qmat = full(Qmat);
Bmat = full(Bmat);




% --- TODO CHECK: assert if Q is orthotropic ???  check ifq13=q31=q23=q32= 0 ?


if print_Input
    fprintf('effective quadratic form:')
    Qmat
    fprintf('effective prestrain')
    Bmat
    
    % check if Q is (close to..) symmetric
    % könnte Anti-symmetric part berechnen und schauen dass dieser klein?
    % Test: issymmetric(Qmat) does not work for float matrices?
    % symmetric part 0.5*(Qmat+Qmat')
    % anti-symmetric part 0.5*(Qmat-Qmat')
    if norm(0.5*(Qmat-Qmat'),'fro') < 1e-8
        fprintf('Qmat (close to) symmetric \n')
        norm(0.5*(Qmat-Qmat'),'fro') % TEST
    else
        fprintf('Qmat not symmetric \n')
    end
    
    
    % Check if B_eff is diagonal this is equivalent to b3 == 0
    if abs(Bmat(3)) < 1e-8
        fprintf('B_eff is diagonal (b3 == 0) \n')
    else
        fprintf('B_eff is  NOT diagonal (b3 != 0)  \n')
    end
end




% CAST VALUES TO SYM FIRST? This is done anyway..
% % Substitute effective quantitites      
f_plus = subs(f_plus,{q1, q2, q3, q12, b1, b2, b3}, {Qmat(1,1), Qmat(2,2), Qmat(3,3), Qmat(1,2), ...
                                                     Bmat(1), Bmat(2), Bmat(3)});

f_minus = subs(f_minus,{q1, q2, q3, q12, b1, b2, b3}, {Qmat(1,1), Qmat(2,2), Qmat(3,3), Qmat(1,2), ...
                                                     Bmat(1), Bmat(2), Bmat(3)});


% Compute the Gradients 
df_plusx = diff(f_plus,v1);
df_plusy = diff(f_plus,v2);
df_minusx = diff(f_minus,v1);
df_minusy = diff(f_minus,v2);

% Setup Equations Grad(f) = 0 
eq1 = df_plusx == 0;
eq2 = df_plusy == 0;
eqns_plus = [eq1, eq2];
eq3 = df_minusx == 0;
eq4 = df_minusy == 0;
eqns_minus = [eq3, eq4];

% -------  Symbolically Solve Equations: 
% More robust (works even for values b_3 ~ 1e-08 ): 
S_plus = solve(eqns_plus,v1,v2,'MaxDegree' , 5);  
S_minus = solve(eqns_minus,v1,v2,'MaxDegree' , 5);

A_plus = S_plus.v1;
B_plus = S_plus.v2;
A_minus = S_minus.v1;
B_minus = S_minus.v2;

if check_StationaryPoints
    %---------- TEST if Grad(f) = 0 ---------------------
    fprintf('Testing equation grad(f) = 0  with stationary points')
    for i = 1:size(A_plus,1)
        fprintf('Testing %d.point (f_plus): ',i )
        [ double(subs(subs(df_plusx,v1,A_plus(i)),v2,B_plus(i))), double(subs(subs(df_plusy,v1,A_plus(i)),v2,B_plus(i))) ]
    end
    for i = 1:size(A_minus,1)
        fprintf('Testing %d.point (f_minus): ',i )
        [double(subs(subs(df_minusx,v1,A_minus(i)),v2,B_minus(i))), double(subs(subs(df_minusy,v1,A_minus(i)),v2,B_minus(i)))]
    end
    % ------------------------------------
end

% --- Extract only Real-Solutions
% fprintf('real stationary points of f_plus:')
tmp1 = A_plus(imag(double(A_plus))==0 & imag(double(B_plus)) == 0);
tmp2 = B_plus(imag(double(A_plus))==0 & imag(double(B_plus)) == 0);
A_plus = tmp1;
B_plus = tmp2;
SP_plus = [A_plus,B_plus];

% fprintf('real stationary points of f_minus:')
tmp1 = A_minus(imag(double(A_minus))==0 & imag(double(B_minus)) == 0);
tmp2 = B_minus(imag(double(A_minus))==0 & imag(double(B_minus)) == 0);
A_minus = tmp1;
B_minus = tmp2;
SP_minus = [A_minus,B_minus];

% TODO one should use f_plus.subs(A_plus..) to compute function value symbolically?
% in the end only the stationaryPoints are used.. should be ok to compare function values numerically

% Determine global Minimizer from stationary points:
% fprintf('function values at stationary points (f_plus):')
T_plus = arrayfun(@(v1,v2) double(f_plus(v1,v2,q1,q2,q3,q12,b1,b2,b3)),A_plus,B_plus,'UniformOutput', false);
T_plus = cell2mat(T_plus);

%Test: use Substitution
% subs(f_plus,{v1, v2}, {A_plus,B_plus})

% fprintf('function values at stationary points (f_minus):')
T_minus = arrayfun(@(v1,v2) double(f_minus(v1,v2,q1,q2,q3,q12,b1,b2,b3)),A_minus,B_minus,'UniformOutput', false);
T_minus = cell2mat(T_minus);

%Test: use Substitution
% T_minus = subs(f_minus,{v1, v2}, {A_minus,B_minus})
% double(T_minus)

if print_statPoint
    fprintf('real stationary points of f_plus: ')
%     SP_Plus     %alternative: output as symbolic (can be unwieldy)
    double(SP_plus)
    fprintf('real stationary points of f_minus:')
%     SP_Minus     %alternative: output as symbolic (can be unwieldy)
    double(SP_minus)
    fprintf('function values at stationary points (f_plus):')
    T_plus
    fprintf('function values at stationary points (f_minus):')
    T_minus
end

% --- Find Stationary Point(s) with minimum Value 
[Min_plus,MinIdx_plus] = min(T_plus, [], 'all', 'linear');    %find one min...
[Min_minus,MinIdx_minus] = min(T_minus, [], 'all', 'linear');
% [Min_minus,MinIdx_minus] = min(T_minus)                     % works with symbolic too

% Compare Minimizers of f_plus & f_minus
[globalMinimizerValue,GlobalIdx] = min([Min_plus,Min_minus]); 

if GlobalIdx == 1     %Min_plus   % i.e. Global Minimizer is given by f_plus
    GlobalMinimizer = SP_plus(MinIdx_plus,:);
    sign = 1.0;
elseif GlobalIdx == 2  %Min_minus % i.e. Global Minimizer is given by f_minus
    GlobalMinimizer = SP_minus(MinIdx_minus,:);
    sign = -1.0; 
end

% ------ Check if there are more SP with the same value...
 MinIndices_minus = find(T_minus(:) == globalMinimizerValue);    % Find indices of All Minima
 MinIndices_plus  = find(T_plus(:) == globalMinimizerValue);    % Find indices of All Minima                   
 numMinSP_minus = size(MinIndices_minus,1);   % One of these is always >= 2 due to the structure of the roots..
 numMinSP_plus  = size(MinIndices_plus,1);
    
%     AllMinSP_minus = SP_minus(MinIndices_minus,:)
%     AllMinSP_minus = double(SP_minus(MinIndices_minus,:))
%     AllMin = T_minus(MinIndices)  %bereits klar dass diese selben funktionswert haben..
      
    Minimizer = sign*(GlobalMinimizer'*GlobalMinimizer);  % global minimizing Matrix G*
    MinimizerCount = 1;
    % different Stationary Points might correspond to the same minimizing
    % Matrix G*... check this: 
    
    % Compare only with other StationaryPoints/Minimizers
    % remove Index of Minimizer
    if GlobalIdx == 1     
        MinIndices_plus = MinIndices_plus(MinIndices_plus~=MinIdx_plus);
    elseif GlobalIdx == 2  
        MinIndices_minus = MinIndices_minus(MinIndices_minus~=MinIdx_minus);
    end
    
    MinIndices = cat(1,MinIndices_plus,MinIndices_minus);  %[Minimizers-Indices f_plus, Minimizer-Indices f_minus]
    
    for i = 1:(numMinSP_minus+numMinSP_plus-1)  % -1: dont count Minimizer itself.. 
        idx = MinIndices(i);   
        
        if i > numMinSP_plus
            SP = SP_minus(idx,:);
        else
            SP = SP_plus(idx,:); 
        end
        
%         SP_value = T_minus(idx) % not needed? 
        Matrix = sign*(SP'*SP);

        if norm(double(Matrix-Minimizer),'fro') < 1e-8   %check is this sufficient here?
%             fprintf('both StationaryPoints correspond to the same(Matrix-)Minimizer')
        else
%             fprintf('StationaryPoint corresponds to a different (Matrix-)Minimizer')
            MinimizerCount = MinimizerCount + 1;
        end
    end
% ----------------------------------------------------------------------------------------------------------------



% Output Uniqueness of Minimizers:
if print_Uniqueness
    if MinimizerCount == 1
        fprintf('Unique Minimzier')
    elseif MinimizerCount == 2
        fprintf('Two Minimziers')
    else
        fprintf('1-Parameter family of Minimziers')
    end
end





% --- determine the angle of the Minimizer
% a1 = Minimizer(1,1)
% a2 = Minimizer(2,2)
a1 = double(Minimizer(1,1));
a2 = double(Minimizer(2,2));

% compute the angle <(e,e_1) where Minimizer = kappa* (e (x) e)
e = [sqrt((a1/(a1+a2))), sqrt((a2/(a1+a2)))];    % always positive under sqrt here .. basically takes absolute value here
angle = atan2(e(2), e(1)); 

% compute curvature kappa
kappa = (a1 + a2);

% % CHeck off diagonal entries:
% sqrt(a1*a2);
% double(Minimizer);

G = double(Minimizer);



% --- "Classification" / Determine the TYPE of Minimizer by using 
% the number of solutions (Uniqueness?)
% the angle (axial- or non-axial Minimizer)

% (Alternative compute det[GlobalMinimizer' e1']  where e1 = [1 0] ?)

% Check Uniqueness -- Options: unique/twoMinimizers/1-ParameterFamily
if MinimizerCount == 1
%     fprintf('Unique Minimzier')
    % Check if Minimizer is axial or non-axial:
    if (abs(angle-pi/2) < 1e-8 || abs(angle) < 1e-8) % axial Minimizer
     Type = 3;
    else % non-axial Minimizer   
     Type = 1;
    end
elseif MinimizerCount == 2
%     fprintf('Two Minimziers')
    % Check if Minimizer is axial or non-axial:
    if (abs(angle-pi/2) < 1e-8 || abs(angle) < 1e-8) % axial Minimizer
        Type = 3;
    else % non-axial Minimizer   
        fprintf('ERROR: Two non-axial Minimizers cannot happen!')
    end
else
%     fprintf('1-Parameter family of Minimziers')
    % Check if Minimizer is axial or non-axial:
    if (abs(angle-pi/2) < 1e-8 || abs(angle) < 1e-8) % axial Minimizer
%         fprintf('ERROR: axial Minimizers cannot happen for 1-Parameter Family!')
    else % non-axial Minimizer   
        Type = 2;
    end
end
% ------------------------------------------------------------------------------------------------------




if print_Output
    fprintf(' --------- Output symMinimization --------')
    fprintf('Global Minimizer v: (%d,%d) \n', GlobalMinimizer(1),GlobalMinimizer(2) )
    fprintf('Global Minimizer Value f(v): %d \n', sym(globalMinimizerValue) ) %cast to symbolic
    %     fprintf('Global Minimizer Value : %d', globalMinimizerValue )
    
    fprintf('Global Minimizer G: \n' )
    G 
    fprintf("Angle = %d \n", angle)
    fprintf("Curvature = %d \n", kappa)
    fprintf("Type = %i \n", Type)
    fprintf(' --------- -------------------- --------')
end



if make_FunctionPlot  
    fsurf(@(x,y) f_plus(x,y,q1,q2,q3,q12,b1,b2,b3)) % Plot functions
    hold on
    plot3(double(A_plus),double(B_plus),T_plus,'g*')
    %Plot GlobalMinimizer:
    hold on
    plot3(double(GlobalMinimizer(1)),double(GlobalMinimizer(2)),globalMinimizerValue, 'o', 'Color','c')
    % view(90,0)
    % view(2)

    figure
    fsurf(@(x,y) f_minus(x,y,q1,q2,q3,q12,b1,b2,b3))
    hold on
    plot3(double(A_minus),double(B_minus),T_minus,'g*')
    hold on
    plot3(double(GlobalMinimizer(1)), double(GlobalMinimizer(2)),globalMinimizerValue, 'o', 'Color','c')
end


return 


% Write symbolic solution to txt-File in Latex format
% fileID = fopen('txt.txt','w');
% fprintf(fileID,'%s' , latex(S_plus.v1));
% fclose(fileID);