% Input:
% L: Matrix (directions)
% states: cell array of structs
% transitions: cell array of structures
% additionaldirections: matrix
% q: vector of integers (state indexes)

% Output:
% L: Matrix (contains unique directions)
% states: cell array of structs
% transitions: cell array of structs
% additionaldirections_in_L: matrix (row-wise directions)

% Description:
% adds to direction matrix L the directions of equality guards from dqq,
% inequalityguards from diqq and the directions of the invariants for each
% state from states and the directions from additionaldirections_in_L

function [L,states,transitions,additionaldirections_in_L] = extendDirections(L,states,transitions,additionaldirections,q)

% Important note:
% Before any directions can be added to L those have to be normalized (as L
% contains only normalized directions with length 1). In order to be able
% to detect if a direction is already contained in L, it is necessary to
% round the potential new directions the same way (or with less precision) 
% directions in L have been produced.

% if parameter exists, add specific additional directions (if not already contained) to L and return
% the correspondent mapping to L
additionaldirections_in_L = [];
if ~isempty(additionaldirections)
    [additionaldirections_in_L,L] = addDirections(additionaldirections,L);
end;

dimensionality = size(L,2);
% add invariant directions to L
for i=1:length(states)
    if ~isempty(states{i}.C)
        [states{i}.C,states{i}.d] = normalizeDirections(states{i}.C,states{i}.d,dimensionality);
        
        [indexes,L] = addDirections(states{i}.C,L);
        
        states{i}.invariant_constraints_in_L = indexes; % store for each invariant constraint to which direction in L it corresponds
        
        % add mirrored directions
        [indexes,L] = addDirections(-states{i}.C,L);
        states{i}.mirrored_invariant_constraints_in_L = indexes;
        
    else
        states{i}.invariant_constraints_in_L = [];
        states{i}.mirrored_invariant_constraints_in_L = [];
    end;
end;

% add source and destination invariant indices to transition
for i=1:length(transitions)
    transitions{i}.iminus_constraints_in_L = states{q == transitions{i}.from}.invariant_constraints_in_L;
    transitions{i}.iplus_constraints_in_L = states{q == transitions{i}.to}.invariant_constraints_in_L;
    
    % adapt mirrored values
    transitions{i}.mirrored_iminus_constraints_in_L = states{q == transitions{i}.from}.mirrored_invariant_constraints_in_L;
    transitions{i}.mirrored_iplus_constraints_in_L = states{q == transitions{i}.to}.mirrored_invariant_constraints_in_L;
end;

% add inequlity guard directions to L
for i=1:length(transitions)
    if ~isempty(transitions{i}.iguards_dir)
        [transitions{i}.iguards_dir,transitions{i}.iguards_val] = normalizeDirections(transitions{i}.iguards_dir,transitions{i}.iguards_val,dimensionality);
        
        [indexes,L] = addDirections(transitions{i}.iguards_dir,L);
        
        transitions{i}.iguard_constraints_in_L = indexes; % store for each invariant constraint to which direction in L it corresponds
        
        % mirrored directions
        [indexes,L] = addDirections(-transitions{i}.iguards_dir,L);
        transitions{i}.mirrored_iguard_constraints_in_L = indexes;
        
    else
        transitions{i}.iguard_constraints_in_L = [];
        transitions{i}.mirrored_iguard_constraints_in_L = [];
    end;
end;

% add equality guard directions to L (2 inequalities)
for i=1:length(transitions)
    if ~isempty(transitions{i}.eguards_dir)
        [transitions{i}.eguards_dir,transitions{i}.eguards_val] = normalizeDirections([transitions{i}.eguards_dir;-transitions{i}.eguards_dir],[transitions{i}.eguards_val;-transitions{i}.eguards_val],dimensionality);
        
        [indexes,L] = addDirections(transitions{i}.eguards_dir,L);
        
        transitions{i}.eguards_dir = transitions{i}.eguards_dir(1:size(transitions{i}.eguards_dir,1)/2,:);
        transitions{i}.eguards_val = transitions{i}.eguards_val(1:size(transitions{i}.eguards_val,1)/2,:);
        
        transitions{i}.eguard_constraints_in_L = indexes; % store for each invariant constraint to which direction in L it corresponds - lower half corresponds to mirrored directions
        
        % Remark:
        % mirrored_directions are always already contained as the lower
        % half from transitions{i}.eguard_constraints_in_L thus no seperate field is needed
        
    else
        transitions{i}.eguard_constraints_in_L = [];
    end;
end;

% add I_star directions to L
for i=1:length(transitions)
    if ~isempty(transitions{i}.I_star_constraints)
        [transitions{i}.I_star_constraints,transitions{i}.I_star_values] = normalizeDirections(transitions{i}.I_star_constraints,transitions{i}.I_star_values,dimensionality);
        
        %temp(any(isnan(temp),2),:) = [];
        
        [indexes,L] = addDirections(transitions{i}.I_star_constraints,L);
        
        transitions{i}.istar_constraints_in_L = indexes; % store for each constraint to which direction in L it corresponds
        
        % mirrored directions
        [indexes,L] = addDirections(-transitions{i}.I_star_constraints,L);
        transitions{i}.mirrored_istar_constraints_in_L = indexes;
    else
        transitions{i}.istar_constraints_in_L = [];
        transitions{i}.mirrored_istar_constraints_in_L = [];
    end;
end;

% Input:
% temp: matrix (row-wise directions)
% L: matrix (row-wise directions)

% Output:
% indexes: vector
% L: matrix (row-wise directions)

% Description:
% Adds the directions of temp to L without producing redundant entries and
% returns the mapping of each direction from temp to L
function [indexes,L] = addDirections(temp,L)
        [~,indexes] = ismember(temp,L,'rows');
        
        for j=1:length(indexes)
            if indexes(j) == 0
                indexes(j) = size(L,1)+1;
                L = [L; temp(j,:)];
            end;
        end;