clear; clc; close;  % Clear workspace, command window, and close all figures

%% 🌟 Geometry-Inspired Tensor Modeling Banner 🌟
fprintf('\n');
fprintf('╔════════════════════════════════════════════════════════════════════════════╗\n');
fprintf('║                   🌐 t-Product Geometry-Inspired Tensor Modeling            \n');
fprintf('║                                                                             \n');
fprintf('║   📐 The method is directly inspired by  t-product geometry:             \n');
fprintf('║      - Each image tensor is treated as an element of a structured           \n');
fprintf('║        t-module under the t-product algebra.                                \n');
fprintf('║      - The model imposes low-rankness along both mode-1 (rows) and          \n');
fprintf('║        mode-2 (columns), corresponding to dual t-module structures.         \n');
fprintf('║      - This bidirectional low-rank coupling induces non-flat geometric      \n');
fprintf('║        curvature in the tensor manifold, enriching the expressive power.    \n');
fprintf('║                                                                             \n');
fprintf('╚════════════════════════════════════════════════════════════════════════════╝\n\n');


% Load hyperspectral tensor data
load('Indian_pines_corrected_145x145x200.mat');
addpath("libs\");  % Add auxiliary function path

% Limit the number of spectral frames to 30 for computational issue
maxFrames = 30;
if  (size(T, 3)>maxFrames)
    T = T(:, :, 1:maxFrames);
end

%% ----------Global Settings----------
obsRatio = 0.2;       % Observation ratio (sampling rate)
infBound = 100;       % Maximum peak value of signal
backGround = 5;       % Constant background added to the signal

%% ---Signal and Noise Generation---
T = double(T);                        % Convert tensor to double precision
T = T / max(T(:));                    % Normalize the tensor to [0,1]
T(T < 0) = 0;                         % Clip any negative values to zero

% ----------Process Tensor------------
sz = size(T);                         % Get tensor size
nMode = length(sz);                  % Number of modes (should be 3)

L = T * infBound;                     % Ground-truth signal scaled to peak value
Xc = L + backGround .* ones(sz);     % Add background to signal
Y0 = poissrnd(Xc);                   % Generate Poisson noisy observations

% Generate sampling mask with uniform dropout per mode
vP = (1 - power(obsRatio, 1/nMode)) * ones(1, nMode);
B = f_generateMaskByMode(sz, vP);    % Sampling mask (binary tensor)

Y = Y0 .* B;                          % Apply sampling mask to noisy observations

% Extract observed entries
vIdx = find(B > 0);                  % Linear indices of observed entries
obs.tsize = sz;                      % Store tensor size in observation struct
obs.y = Y(vIdx);                     % Store observed values
obs.idx = vIdx;                      % Store observed indices

%% Optimization Parameters
lambda = 1;                          % Regularization parameter
rho = 5e-8;                          % Initial penalty parameter
nu = 1.1;                            % Update rate for rho

% Pack options into a struct
optsBiTNNdct.para.lambda = lambda;
optsBiTNNdct.para.infBound = infBound;
optsBiTNNdct.para.backGround = backGround;
optsBiTNNdct.para.rho = rho;
optsBiTNNdct.para.nu = nu;

% Outer loop control
optsBiTNNdct.MAX_ITER_OUT = 120;
optsBiTNNdct.MAX_RHO = 1e8;
optsBiTNNdct.MAX_EPS = 1e-4;
optsBiTNNdct.verbose = 1;

% -------------Memo Structure---------------
% Create a log/memo structure for tracking optimization
memoBiTNNdct = h_construct_memo(optsBiTNNdct);
memoBiTNNdct.truth = L;             % Store ground-truth for later evaluation
memoBiTNNdct.printerInterval = 20;   % Frequency of printing optimization progress

% -------------Run the Algorithm------------
memoBiTNNdct = f_poisson_tc_BiTNN_dct_ADMM(obs, optsBiTNNdct, memoBiTNNdct);
