classdef MimImageCoordinateUtilities
    % MimImageCoordinateUtilities. Utility functions related to processing 3D
    % image coordinates
    %
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    methods (Static)

        % In Matlab, matrices can be represented as a linear vector. So every
        % point in a 3D matrix has a linear index as well as an i-j-k
        % coordinate. This function returns the 'offset' values which can be
        % added to the linear index of any point to return the linear indices of
        % its nearest neighbours.
        function [linear_offsets, linear_offsets27] = GetLinearOffsets(image_size)
            % Compute linear index offsets for diretion vectors
            dirs = [5, 23, 11, 17, 13, 15];
            linear_offsets = MimImageCoordinateUtilities.GetLinearOffsetsForDirections(dirs, image_size);

            dirs = 1:27;
            linear_offsets27 = MimImageCoordinateUtilities.GetLinearOffsetsForDirections(dirs, image_size);
        end

        function linear_offsets = GetLinearOffsetsForDirections(dirs, image_size)
            direction_vectors = MimImageCoordinateUtilities.CalculateDirectionVectors;
            linear_offsets = zeros(1, numel(dirs));
            for n = 1 : length(dirs)
                direction = dirs(n);
                direction_vector = direction_vectors(direction, :);
                start_point = [2 2 2];
                i_end_point = start_point + direction_vector;
                i = [start_point(1); i_end_point(1)];
                j = [start_point(2); i_end_point(2)];
                k = [start_point(3); i_end_point(3)];
                linear_indices = sub2ind(image_size, i, j, k);
                linear_offsets(n) = linear_indices(2) - linear_indices(1);
            end
        end

        % Returns the coordinates of each point in a 3x3x3 matrix relative to
        % its centre
        function direction_vectors = CalculateDirectionVectors
            [i, j, k] = ind2sub([3 3 3], 1:27);
            direction_vectors = [i' - 2, j' - 2, k' - 2];
        end

        % This function alters matrix indices to transform from a smaller matrix to
        % a bigger one
        function new_indices = OffsetIndices(indices, offset, size_small, size_big)
            indices_i = (indices - 1);
            div1 = (size_small(1));
            div2 = (size_small(1)*size_small(2));

            k_mod = rem(indices_i, div2);

            k = (indices_i - k_mod)/div2; % Equivalent to idivide but quicker

            i = mod(k_mod, div1);

            j = (k_mod - i)/div1; % Equivalent to idivide but quicker

            i = i + offset(1);
            j = j + offset(2);
            k = k + offset(3);

            % Note that i,j,k can be negative, in the case where a border has
            % been added to the image so its coordinates extend beyond the
            % boundaries of the original image

            new_indices = 1 + (i) + (j)*size_big(1) + (k)*size_big(1)*size_big(2);
        end

        % Creates an image cropped to the smallest box size that encloses all
        % the points specified by their linear indices.
        function [offset reduced_image reduced_image_size] = GetMinimalImageForIndices(indices, image_size)
            if size(indices, 1) > 1
                error('GetMinimalImageForIndices requires indices to be in a row vector');
            end
            indices = int32(indices);
            [i, j, k] = MimImageCoordinateUtilities.FastInd2sub(image_size, indices);

            voxel_coordinates = [i' j' k'];
            mins = min(voxel_coordinates, [], 1);
            maxs = max(voxel_coordinates, [], 1);
            reduced_image_size = maxs - mins + int32([1 1 1]);
            reduced_image = false(reduced_image_size);
            offset = mins - 1;
            i = MimImageCoordinateUtilities.FastSub2ind(reduced_image_size, voxel_coordinates(:,1)-offset(1), voxel_coordinates(:,2)-offset(2), voxel_coordinates(:,3)-offset(3));

            reduced_image(i) = true;
        end

        % A faster alternative to Ind2sub
        function [i, j, k] = FastInd2sub(im_size, indices)
            indices = indices - 1;
            div1 = (im_size(1));
            div2 = ((im_size(1)*im_size(2)));

            k_mod = rem(indices, div2);

            k = 1 + (indices - k_mod)/div2; % Equivalent to idivide but quicker
            i = 1 + mod(k_mod, im_size(1));
            j = 1 + (k_mod - i + 1)/div1; % Equivalent to idivide but quicker
        end

        function indices = FastSub2ind(im_size, i, j, k)
            indices = i + (j-1)*im_size(1) + (k-1)*im_size(1)*im_size(2);
        end

        function rot_matrix = GetEulerRotationMatrix(phi, theta, psi)
            rot_matrix = zeros(3,3);

            rot_matrix(1, 1) = cos(psi)*cos(phi) - cos(theta)*sin(phi)*sin(psi);
            rot_matrix(1, 2) = cos(psi)*sin(phi) + cos(theta)*cos(phi)*sin(psi);
            rot_matrix(1, 3) = sin(psi)*sin(theta);

            rot_matrix(2, 1) = -sin(psi)*cos(phi) - cos(theta)*sin(phi)*cos(psi);
            rot_matrix(2, 2) = -sin(psi)*sin(phi) + cos(theta)*cos(phi)*cos(psi);
            rot_matrix(2, 3) = cos(psi)*sin(theta);

            rot_matrix(3, 1) =  sin(theta)*sin(phi);
            rot_matrix(3, 2) = -sin(theta)*cos(psi);
            rot_matrix(3, 3) = cos(theta);
        end

        function affine_matrix = CreateAffineMatrix(x)
            affine_matrix = zeros(3, 4, 'single');
            affine_matrix(:) = x(:);
            affine_matrix = [affine_matrix; [0 0 0 1]];
        end

        function affine_matrix = CreateAffineTranslationMatrix(x)
            affine_matrix = zeros(3, 4, 'single');
            affine_matrix(1, 1) = 1;
            affine_matrix(2, 2) = 1;
            affine_matrix(3, 3) = 1;
            affine_matrix(4, 4) = 1;
            affine_matrix(1:3, 4) = x;
        end

        function affine_matrix = CreateRigidAffineMatrix(x)
            affine_matrix = zeros(3, 4, 'single');

            euler_rot_matrix = MimImageCoordinateUtilities.GetEulerRotationMatrix(x(1), x(2), x(3));
            affine_matrix(1:3, 1:3) = euler_rot_matrix;
            affine_matrix(1:3, 4) = x(4:6);

            affine_matrix = [affine_matrix; [0 0 0 1]];
        end

        function [i, j, k] = TransformCoordsAffine(i, j, k, augmented_matrix)
            [j, i, k] = MimImageCoordinateUtilities.TranslateAndRotateMeshGrid(j, i, k, augmented_matrix(1:3,1:3), augmented_matrix(1:3,4));
        end

        function [i, j, k] = TransformCoordsFluid(i, j, k, deformation_field)
            i = i - deformation_field.RawImage(:,:,:,1);
            j = j - deformation_field.RawImage(:,:,:,2);
            k = k - deformation_field.RawImage(:,:,:,3);
        end

        function [X, Y, Z] = TranslateAndRotateMeshGrid(X, Y, Z, rot_matrix, trans_matrix)
            % Rotates and translates meshgrid generated coordinates in 3D
            % Note coordinates are [XYZ] NOT [IJK]
            [X, Y, Z] = MimImageCoordinateUtilities.RotateMeshGrid(X + trans_matrix(1), Y + trans_matrix(2), Z + trans_matrix(3), rot_matrix);
        end

        function [X, Y, Z] = RotateMeshGrid(X, Y, Z, rot_matrix)
            % Rotates coordinates that are given in 3D meshgrid matrices
            coords = rot_matrix * [ ...
                reshape(X, 1, numel(X)); ...
                reshape(Y, 1, numel(Y)); ...
                reshape(Z, 1, numel(Z)) ...
                ];

            X = reshape(coords(1, :), size(X));
            Y = reshape(coords(2, :), size(Y));
            Z = reshape(coords(3, :), size(Z));
        end

        function affine_matrix = GetAffineTranslationFromPatientPosition(image_1, image_2)

            % Get the coordinates of the centre of the first voxel in image1,
            % relative to the centre of image1
            [i1, j1, k1] = image_1.GlobalCoordinatesToCoordinatesMm([1, 1, image_1.OriginalImageSize(3)]);
            [i1, j1, k1] = image_1.GlobalCoordinatesMmToCentredGlobalCoordinatesMm(i1, j1, k1);
            image_1_origin_coordinates = [i1, j1, k1];
            image_1_centre_coordinates = image_1.GlobalOrigin - image_1_origin_coordinates;

            % Get the coordinates of the centre of the first voxel in image2,
            % relative to centre of image2
            [i2, j2, k2] = image_2.GlobalCoordinatesToCoordinatesMm([1, 1, image_2.OriginalImageSize(3)]);
            [i2, j2, k2] = image_2.GlobalCoordinatesMmToCentredGlobalCoordinatesMm(i2, j2, k2);
            image_2_origin_coordinates = [i2, j2, k2];
            image_2_centre_coordinates = image_2.GlobalOrigin - image_2_origin_coordinates;

            translation = image_1_centre_coordinates - image_2_centre_coordinates;

            translation = translation([2 1 3]);
            translation(3) = - translation(3);
            affine_matrix = MimImageCoordinateUtilities.CreateAffineTranslationMatrix(translation);
        end

        function deformation_field = AdjustDeformationFieldForInitialAffineTransformation(deformation_field, affine_initial_matrix)
            % To combine a rigid transformation with a nonrigid deformation field,
            % compute the change in image coordinates after applying the
            % deformation field and then the rigid affine transformation.

            [df_i, df_j, df_k] = deformation_field.GetCentredGlobalCoordinatesMm;
            [df_i, df_j, df_k] = ndgrid(df_i, df_j, df_k);
            [df_i_t, df_j_t, df_k_t] = MimImageCoordinateUtilities.TransformCoordsFluid(df_i, df_j, df_k, deformation_field);
            [df_i_t, df_j_t, df_k_t] = MimImageCoordinateUtilities.TransformCoordsAffine(df_i_t, df_j_t, df_k_t, affine_initial_matrix);

            deformation_field_raw = zeros(deformation_field.ImageSize);
            deformation_field_raw(:,:,:,1) = df_i - df_i_t;
            deformation_field_raw(:,:,:,2) = df_j - df_j_t;
            deformation_field_raw(:,:,:,3) = df_k - df_k_t;
            deformation_field2 = deformation_field.BlankCopy;
            deformation_field2.ChangeRawImage(deformation_field_raw);
            deformation_field = deformation_field2;
        end

        function [permutation_vector, flip] = GetDimensionPermutationVectorFromDicomOrientation(orientation, reporting)
            % Returns a vector which defines the order in which the dimensions of an
            % DICOM image volume should be permuted in order to align it with the
            % PTK coordinate system

            % DICOM coordinates are XYZ but Matlab's coordinates are YXZ. We
            % convert the orientation to Matlab coordinates, which we refer to as IJK.
            orientation_1 = orientation([5, 4, 6])'; % Direction of first image axis in ijk (=yxz) coordinates
            orientation_2 = orientation([2, 1, 3])'; % Direction of second image axis in ijk (=yxz) coordinates

            % By switching the i and j axes we have inverted the coordinate
            % system, so we need to flip the k dimension
            orientation_1(3) = - orientation_1(3);
            orientation_2(3) = - orientation_2(3);

            % Determine the PTK dimensions to which each of these vectors correspond
            [permutation_vector, dimension_1, dimension_2, dimension_3] = MimImageCoordinateUtilities.GetPermutationFromOrientations(orientation_1, orientation_2, reporting);

            % Calculate flip for each dimension, based on whether the
            % dimension axis lies in the same or opposite direction to the
            % image axis
            orientation_3 = cross(orientation_2, orientation_1);
            flip = MimImageCoordinateUtilities.GetFlipFromOrientations(orientation_1, orientation_2, orientation_3, dimension_1, dimension_2, dimension_3);

            % Check the resulting vector is valid
            if (sum(permutation_vector == 1) ~= 1) || (sum(permutation_vector == 2) ~= 1) || (sum(permutation_vector == 3) ~= 1) || ...
                    ~isempty(setdiff(permutation_vector, [1,2,3]))
                reporting.Error('MimImageCoordinateUtilities:InvalidPermutationVector', 'GetDimensionPermutationVectorFromDicomOrientation() resulted in an invalid permutation vector');
            end
        end

        function [permutation_vector, flip] = GetDimensionPermutationVectorFromNiiOrientation(header, reporting)
            % Returns a vector which defines the order in which the dimensions of a
            % nii image volume should be permuted in order to align it with the
            % PTK coordinate system

            if header.SformCode > 0
                d1 = [-1; -1; 1].*header.SrowX(1:3);
                d2 = [-1; -1; 1].*header.SrowY(1:3);
                [permutation_vector, flip] = MimImageCoordinateUtilities.GetDimensionPermutationVectorFromDicomOrientation([d1; d2], reporting);
            elseif header.QformCode > 0
                B = header.QuaternB;
                C = header.QuaternC;
                D = header.QuaternD;
                A = sqrt(1 - B^2 - C^2 - D^2);
                d1 = [-1; -1; 1].*[A^2+B^2-C^2-D^2; 2*(B*C - A*D); 2*(B*D+A*C)];
                d2 = [-1; -1; 1].*[2*(B*C + A*D); A^2 + C^2 - B^2 - D^2; 2*(C*D - A*B)];
                d3 = [-1; -1; 1].*[2*(B*D - A*C); 2*(C*D + A*B); A^2 + D^2 - B^2 - C^2];
                [permutation_vector, flip] = MimImageCoordinateUtilities.GetDimensionPermutationVectorFromDicomOrientation([d1; d2], reporting);
            else
                d1 = [-1; -1; 1].*[1; 0; 0];
                d2 = [-1; -1; 1].*[0; 1; 0];
                [permutation_vector, flip] = MimImageCoordinateUtilities.GetDimensionPermutationVectorFromDicomOrientation([d1; d2], reporting);
            end
        end

        function [permutation_vector, flip] = GetDimensionPermutationVectorForAnalyze(orientation, reporting)
            permutation_vector = [2, 1, 3];
            flip = [false, false, true];
        end

        function [permutation_vector, dimension_1, dimension_2, dimension_3] = GetPermutationFromOrientations(orientation_1, orientation_2, reporting)
            [dimension_1, dimension_2, dimension_3] = MimImageCoordinateUtilities.GetDimensionIndicesFromOrientations(orientation_1, orientation_2, reporting);

            permutation_vector = [3, 3, 3];
            permutation_vector(dimension_1) = 1;
            permutation_vector(dimension_2) = 2;
        end

        function flip = GetFlipFromOrientations(orientation_1, orientation_2, orientation_3, dimension_1, dimension_2, dimension_3)
            flip = [false, false, false];
            flip(dimension_1) = orientation_1(dimension_1) < 0;
            flip(dimension_2) = orientation_2(dimension_2) < 0;
            flip(dimension_3) = orientation_3(dimension_3) < 0;
        end

        function dicom_cosine = AnatomicalOrientationToDicomCosine(anatomical_orientation_char, reporting)
            switch anatomical_orientation_char
                case 'R'
                    dicom_cosine = [1, 0, 0];
                case 'L'
                    dicom_cosine = [-1, 0, 0];
                case 'A'
                    dicom_cosine = [0, 1, 0];
                case 'P'
                    dicom_cosine = [0, -1, 0];
                case 'I'
                    dicom_cosine = [0, 0, 1];
                case 'S'
                    dicom_cosine = [0, 0, -1];
                otherwise
                    reporting.Error('MimImageCoordinateUtilities:UnknownAnatomicalOrientation', ['MimImageCoordinateUtilities: WARNING: no implementation yet for anatomical orientation ' anatomical_orientation_char '.']);
            end
        end

        function [permutation_vector, flip_orientation] = GetDimensionPermutationVectorFromAnatomicalOrientation(anatomical_orientation_string, reporting)
            direction_cosine_1 = MimImageCoordinateUtilities.AnatomicalOrientationToDicomCosine(anatomical_orientation_string(1), reporting);
            direction_cosine_2 = MimImageCoordinateUtilities.AnatomicalOrientationToDicomCosine(anatomical_orientation_string(2), reporting);
            direction_cosine_3 = MimImageCoordinateUtilities.AnatomicalOrientationToDicomCosine(anatomical_orientation_string(3), reporting);

            [permutation_vector, flip_orientation] = MimImageCoordinateUtilities.GetDimensionPermutationVectorFromMhdCosines(direction_cosine_1, direction_cosine_2, direction_cosine_3, reporting);
        end

        function [permutation_vector, flip_orientation] = GetDimensionPermutationVectorFromMhdCosines(direction_cosine_1, direction_cosine_2, direction_cosine_3, reporting)
            % Determines the permutation and flip transformations required
            % to convert MetaIO data to PTK data

            % DICOM coordinates are XYZ but Matlab's coordinates are YXZ. We
            % convert the orientation to Matlab coordinates, which we refer to as IJK.
            orientation_1 = direction_cosine_2([2, 1, 3])'; % Direction of first image axis in ijk (=yxz) coordinates
            orientation_2 = direction_cosine_1([2, 1, 3])'; % Direction of second image axis in ijk (=yxz) coordinates
            orientation_3 = direction_cosine_3([2, 1, 3])'; % Direction of third image axis in ijk (=yxz) coordinates

            % By switching the i and j axes we have inverted the coordinate
            % system, so we need to flip the k dimension
            orientation_1(3) = - orientation_1(3);
            orientation_2(3) = - orientation_2(3);
            orientation_3(3) = - orientation_3(3);

            % Determine the PTK dimensions to which each of these vectors correspond
            [permutation_vector, dimension_1, dimension_2, dimension_3] = MimImageCoordinateUtilities.GetPermutationFromOrientations(orientation_1, orientation_2, reporting);

            % Calculate flip for each dimension, based on whether the
            % dimension axis lies in the same or opposite direction to the
            % image axis
            flip_orientation = MimImageCoordinateUtilities.GetFlipFromOrientations(orientation_1, orientation_2, orientation_3, dimension_1, dimension_2, dimension_3);

            % Check the resulting vector is valid
            if (sum(permutation_vector == 1) ~= 1) || (sum(permutation_vector == 2) ~= 1) || (sum(permutation_vector == 3) ~= 1) || ...
                    ~isempty(setdiff(permutation_vector, [1,2,3]))
                reporting.Error('MimImageCoordinateUtilities:InvalidPermutationVector', 'GetDimensionPermutationVectorFromAnatomicalOrientation() resulted in an invalid permutation vector');
            end
        end

        function [dimension_number_1, dimension_number_2, dimension_number_3] = GetDimensionIndicesFromOrientations(orientation_vector_1, orientation_vector_2, reporting)
            % The orientation vector is formed of cosines. Typically these will
            % be 1s and 0s but we allow for small variations in the angles.

            [~, dimension_number_1] = max(abs(orientation_vector_1(:)));
            remaining_dimensions = setdiff([1,2,3], dimension_number_1);
            reduced_orientation_vector_2 = orientation_vector_2(remaining_dimensions);
            [~, dimension_number_2_from_reduced_set] = max(abs(reduced_orientation_vector_2(:)));
            dimension_number_2 = remaining_dimensions(dimension_number_2_from_reduced_set);
            dimension_number_3 = setdiff([1, 2, 3], [dimension_number_1, dimension_number_2]);
        end

        function spline_points = CreateSplineCurve(points, num_points)
            number_of_points = size(points, 1);
            number_of_coordinates = size(points, 2);
            extended_points = zeros(number_of_points + 2, number_of_coordinates);
            extended_points(2 : number_of_points + 1, :) = points;
            extended_points(1, :) = extended_points(2, :) - (extended_points(3, :) - extended_points(2, :));
            extended_points(end, :) = extended_points(end - 1, :) - (extended_points(end - 2, : ) - extended_points(end - 1, :));

            extended_number_of_points = size(extended_points, 1);
            interval_values = linspace(0, 1, num_points + 1);
            interval_values2 = interval_values.^2;
            interval_values3 = interval_values.^3;

            for index = 2 : extended_number_of_points - 2
                coeffs = (1/6).*[...
                        extended_points(index - 1,:)  + 4*extended_points(index, :) + extended_points(index + 1, :); ...
                    - 3*extended_points(index - 1, :) + 3*extended_points(index + 1, :); ...
                      3*extended_points(index - 1, :) - 6*extended_points(index, :) + 3*extended_points(index + 1, :); ...
                    -   extended_points(index - 1, :) + 3*extended_points(index, :) - 3*extended_points(index + 1, :) + extended_points(index+2, :) ...
                ]';

                interval = [ones(size(interval_values)); interval_values; interval_values2; interval_values3];
                spline_points(:, (index - 2)*num_points + 1 : (index - 1)*num_points + 1) = coeffs*interval;
            end
        end

        function dicom_coordinates = PTKToDicomCoordinates(ptk_coordinates, template_image)
            offset = template_image.GetDicomOffset;
            dicom_coordinates = ptk_coordinates + repmat(offset, size(ptk_coordinates, 1), 1);
        end

        function [d_x, d_y, d_z] = PTKToDicomCoordinatesCoordwise(p_x, p_y, p_z, template_image)
            offset = template_image.GetDicomOffset;
            d_x = p_x + offset(1);
            d_y = p_y + offset(2);
            d_z = p_z + offset(3);
        end

        function ptk_coordinates = DicomToPTKCoordinates(dicom_coordinates, template_image)
            offset = template_image.GetDicomOffset;
            ptk_coordinates = dicom_coordinates - repmat(offset, size(dicom_coordinates, 1), 1);
        end

        function dicom_coordinates = PTKToCornerCoordinates(ptk_coordinates, template_image)
            offset = template_image.GetCornerOffset;
            dicom_coordinates = ptk_coordinates + repmat(offset, size(ptk_coordinates, 1), 1);
        end

        function [d_x, d_y, d_z] = PTKToCornerCoordinatesCoordwise(p_x, p_y, p_z, template_image)
            offset = template_image.GetCornerOffset;
            d_x = p_x + offset(1);
            d_y = p_y + offset(2);
            d_z = p_z + offset(3);
        end

        function ptk_coordinates = CornerToPTKCoordinates(corner_coordinates, template_image)
            offset = template_image.GetCornerOffset;
            ptk_coordinates = corner_coordinates - repmat(offset, size(corner_coordinates, 1), 1);
        end

        function [ptk_x, ptk_y, ptk_z] = CoordinatesMmToPTKCoordinates(ic, jc, kc)
            ptk_x = jc;
            ptk_y = ic;
            ptk_z = -kc;
        end

        function coordinates_mm = PTKCoordinatesToCoordinatesMm(ptk_coordinates)
            coordinates_mm = [ptk_coordinates(:, 2), ptk_coordinates(:, 1), - ptk_coordinates(:, 3)];
        end

        function dicom_coordinates = PTKCoordinatesToCornerCoordinates(ptk_coordinates, template_image)
            offset = template_image.GetCornerOffset;
            dicom_coordinates = ptk_coordinates + repmat(offset, size(ptk_coordinates, 1), 1);
        end

        function dicom_coordinates = CoordinatesMmToCornerCoordinates(ptk_coordinates, template_image)
            voxel_size = template_image.VoxelSize;

            % Adjust to coordinates at centre of first voxel
            offset = -voxel_size/2;
            offset = [offset(2), offset(1), -offset(3)];

            % Shift the global origin to the first slice of the image
            global_origin = [0, 0, 0];

            % Adjust to Dicom origin
            offset = offset + global_origin;

            dicom_coordinates = [ptk_coordinates(:, 2), ptk_coordinates(:, 1), - ptk_coordinates(:, 3)];
            dicom_coordinates = dicom_coordinates + repmat(offset, size(ptk_coordinates, 1), 1);
        end

        function ptk_coordinates = CornerToCoordinatesMm(dicom_coordinates, template_image)
            voxel_size = template_image.VoxelSize;

            offset = -voxel_size/2;
            offset = [offset(2), offset(1), -offset(3)];

            dicom_coordinates = dicom_coordinates - repmat(offset, size(dicom_coordinates, 1), 1);

            ptk_coordinates = [dicom_coordinates(:, 2), dicom_coordinates(:, 1), - dicom_coordinates(:, 3)];
        end

        function ptk_coordinates = ConvertToPTKCoordinates(coordinates, coordinate_system, template_image)
            switch coordinate_system
                case MimCoordinateSystem.PTK
                    ptk_coordinates = coordinates;
                case MimCoordinateSystem.Dicom
                    ptk_coordinates = MimImageCoordinateUtilities.DicomToPTKCoordinates(coordinates, template_image);
                case MimCoordinateSystem.DicomUntranslated
                    ptk_coordinates = MimImageCoordinateUtilities.CornerToPTKCoordinates(coordinates, template_image);
                otherwise
                    reporting.Error('MimImageCoordinateUtilities:UnsupportedCoordinateSystem', 'The coordinate system specified by parameter coordinate_system is not supported');
            end
        end

        function coordinates = ConvertFromPTKCoordinates(ptk_coordinates, coordinate_system, template_image)
            switch coordinate_system
                case MimCoordinateSystem.PTK
                    coordinates = ptk_coordinates;
                case MimCoordinateSystem.Dicom
                    coordinates = MimImageCoordinateUtilities.PTKToDicomCoordinates(ptk_coordinates, template_image);
                case MimCoordinateSystem.DicomUntranslated
                    coordinates = MimImageCoordinateUtilities.PTKToCornerCoordinates(ptk_coordinates, template_image);
                otherwise
                    reporting.Error('MimImageCoordinateUtilities:UnsupportedCoordinateSystem', 'The coordinate system specified by parameter coordinate_system is not supported');
            end
        end

        function [c_x, c_y, c_z] = ConvertFromPTKCoordinatesCoordwise(p_x, p_y, p_z, coordinate_system, template_image)
            switch coordinate_system
                case MimCoordinateSystem.PTK
                    c_x = p_x;
                    c_y = p_y;
                    c_z = p_z;
                case MimCoordinateSystem.Dicom
                    [c_x, c_y, c_z] = MimImageCoordinateUtilities.PTKToDicomCoordinatesCoordwise(p_x, p_y, p_z, template_image);
                case MimCoordinateSystem.DicomUntranslated
                    [c_x, c_y, c_z] = MimImageCoordinateUtilities.PTKToCornerCoordinatesCoordwise(p_x, p_y, p_z, template_image);
                otherwise
                    reporting.Error('MimImageCoordinateUtilities:UnsupportedCoordinateSystem', 'The coordinate system specified by parameter coordinate_system is not supported');
            end
        end

        function voxel_indices = AddNearestNeighbours(voxel_indices, template_image)
            if isempty(voxel_indices)
                return;
            end
            [~, linear_offsets27] = MimImageCoordinateUtilities.GetLinearOffsets(template_image.ImageSize);
            voxel_indices = repmat(int32(voxel_indices), 27, 1) + repmat(int32(linear_offsets27'), 1, length(voxel_indices));
            voxel_indices = unique(voxel_indices(:));
        end

        function global_coordinates = GetGlobalCoordinatesForPoints(point_list, template_image)
            xc = [point_list.CoordX];
            yc = [point_list.CoordY];
            zc = [point_list.CoordZ];
            ptk_coords = [xc', yc', zc'];
            coordinates_mm = MimImageCoordinateUtilities.PTKCoordinatesToCoordinatesMm(ptk_coords);
            global_coordinates = round(template_image.CoordinatesMmToGlobalCoordinates(coordinates_mm));
        end

        function global_indices = GetGlobalIndicesForPoints(point_list, template_image)
            global_coordinates = MimImageCoordinateUtilities.GetGlobalCoordinatesForPoints(point_list, template_image);
            global_indices = template_image.GlobalCoordinatesToGlobalIndices(global_coordinates);
        end

        function dist = DistanceBetweenPoints(point_1, point_2)
            dist = norm([point_1.CoordX - point_2.CoordX, point_1.CoordY - point_2.CoordY, point_1.CoordZ - point_2.CoordZ]);
        end

        % Select an appropriate image orientation for exporting an image
        function orientation = ChooseOrientation(voxel_size)
            orientation = GemImageOrientation.XY;
            [sorted_voxel_size, sorted_voxel_size_index] = sort(voxel_size, 'descend');
            if abs(sorted_voxel_size(1) - sorted_voxel_size(2)) > abs(sorted_voxel_size(2) - sorted_voxel_size(3))
                if sorted_voxel_size_index(1) == 1
                    orientation = GemImageOrientation.XZ;
                end
            end
        end

        function offset_voxels = GetOriginOffsetVoxels(from_image, to_image)
            if ~isempty(to_image.GlobalOrigin) && ~isempty(from_image.GlobalOrigin)
                offset_mm = to_image.GlobalOrigin - from_image.GlobalOrigin;
                offset_voxels = offset_mm./from_image.VoxelSize;
            else
                offset_voxels = [0, 0, 0];
            end
        end

        function [Y, X] = GetSliceCoordinates(coordinates_3d, dimension)
            % Returns the 2D [Y, X] coordinates for a 2D slice in the
            % specified orientation given a set of 3D [i,j,k] coordinates
           switch dimension
               case GemImageOrientation.XZ
                   Y = coordinates_3d(:, 3);
                   X = coordinates_3d(:, 2);
               case GemImageOrientation.YZ
                   Y = coordinates_3d(:, 3);
                   X = coordinates_3d(:, 1);
               case GemImageOrientation.XY
                   Y = coordinates_3d(:, 1);
                   X = coordinates_3d(:, 2);
               otherwise
                   error('Unsupported dimension');
           end
        end
    end
end


function gr=bvalue(catalogObject, mcType, manual_on)
    %BVALUE evaluate b-value, a-value and magnitude of completeness
    % of an earthquake catalog stored in a Catalog object.
    %
    % gr = BVALUE(catalogObject, MCTYPE) produces a Gutenberg-Richter type plot
    %    with the best fit line and display of b-,a-values and Mc
    %    for catalogObject. These values are also returned in a structure.
    %    MCTYPE is a number from 1-5
    %    to select the algorithm used for calculation of the
    %    magnitude of completeness. Options are:
    %
    %    1: Maximum curvature
    %    2: Fixed Mc = minimum magnitude (Mmin)
    %    3: Mc90 (90% probability)
    %    4: Mc95 (95% probability)
    %    5: Best combination (Mc95 - Mc90 - maximum curvature)
    %
    % * Note: it seems only 1 only really works, and 5 is same as 1 *'
    %
    % gr = BVALUE(catalogObject, MCTYPE, manual_on) where the value of manual_on
    %    computes as true will give the user the ability to manually pick a
    %    linear segment on the graph too (which is then plotted with a
    %    green line). The manual Mc and bvalue are returned in the gr
    %    structure as gr.Mc_manual and gr.bvalue_manual

    % Liberally adapted from original code in ZMAP.
    % Author: Silvio De Angelis, 27/07/2012 00:00:00
    % Modified and included in Catalog by Glenn Thompson,
    % 14/06/2014

    % This program 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 2 of the License, or
    % (at your option) any later version.
    %
    % This program 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 this program; if not, write to the
    % Free Software Foundation, Inc.,
    % 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

    if nargin < 2
        disp('* Note: it seems only 1 only really works, and 5 is same as 1 *')
        mcType = menu('mcType can be:','Maximum curvature','Fixed Mc = minimum magnitude (Mmin)', ...
            'Mc90 (90% probability)', 'Mc95 (95% probability)', ...
            'Best combination (Mc95 - Mc90 - maximum curvature)')
    end
    if ~exist('manual_on','var')
        manual_on = false;
    end

    % form magnitude vector - removing any NaN values with find
    good_magnitude_indices = find(catalogObject.mag > -3.0);
    mag = catalogObject.mag(good_magnitude_indices);
    %MIN AND MAX MAGNITUDE IN catalogObject
    minimum_mag = min(mag);
    maximum_mag = max(mag);

    %COUNT EVENTS IN EACH MAGNITUDE BIN
    [bval, xt2] = hist(mag, (minimum_mag:0.1:maximum_mag));

    %CUMULATIVE NUMBER OF EVENTS IN EACH MAGNITUDE BIN
    bvalsum = cumsum(bval);

    %NUMBER OF EVENTS IN EACH BIN IN REVERSE ORDER
    bval2 = bval(length(bval):-1:1);

    %NUMBER OF EVENTS IN EACH MAGNITUDE BIN IN REVERSE ORDER
    bvalsum3 = cumsum(bval(length(bval):-1:1));

    %BINS IN REVERSE ORDER
    xt3 = (maximum_mag:-0.1:minimum_mag);
    backg_ab = log10(bvalsum3);

    %CREATE FIGURE WINDOW AND MAKE FREQUENCY-MAGNITUDE PLOT
    figure('Color','w','Position',[0 0 600 600])

    pl = semilogy(xt3,bvalsum3,'sb'); % semilogy is same as plot, except a log (base10) scale is used for Y-axis
    set(pl, 'LineWidth', [1.0],'MarkerSize', [10],'MarkerFaceColor','r','MarkerEdgeColor','k');
    axis square
    hold on

    pl1 = semilogy(xt3,bval2,'^b');
    set(pl1, 'LineWidth',[1.0],'MarkerSize',[10],'MarkerFaceColor','w','MarkerEdgeColor','k');
    xlabel('Magnitude','Fontsize', 12)
    ylabel('Cumulative Number','Fontsize',12)
    set(gca,'visible','on','FontSize',12,'FontWeight','normal',...
        'FontWeight','bold','LineWidth',[1.0],'TickDir','in','Ticklength',[0.01 0.01],...
        'Box','on','Tag','cufi','color','w')

    %ESTIMATE B-VALUE (MAX LIKELIHOOD ESTIMATE)
    Nmin = 10;
    fMccorr = 0;
    fBinning = 0.1;

    if length(mag) >= Nmin

        %GOODNESS-OF-FIT TO POWER LAW
        %%%%%%%%%%%%%%%%%% mcperc_ca3.m start %%%%%%%%%%%%%%%%%%%%
        % This is a completeness determination test

        [bval,xt2] = hist(mag,-2:0.1:6);
        l = max(find(bval == max(bval)));
        magco0 =  xt2(l);

        dat = [];

        %for i = magco0-0.6:0.1:magco0+0.2
        for i = magco0-0.5:0.1:magco0+0.7
            l = mag >= i - 0.0499;
            nu = length(mag(l));
            if length(mag(l)) >= 25;
                %[bv magco stan av] =  bvalca3(catZmap(l,:),2,2);
                [mw bv2 stan2 av] =  Catalog.bvalue_lib.bmemag(mag(l));
                Catalog.bvalue_lib.synthb_aut;
                dat = [ dat ; i res2];
            else
                dat = [ dat ; i nan];
            end

        end

        j =  min(find(dat(:,2) < 10 ));
        if isempty(j) == 1; Mc90 = nan ;
        else;
            Mc90 = dat(j,1);
        end

        j =  min(find(dat(:,2) < 5 ));
        if isempty(j) == 1; Mc95 = nan ;
        else;
            Mc95 = dat(j,1);
        end

        j =  min(find(dat(:,2) < 10 ));
        if isempty(j) == 1; j =  min(find(dat(:,2) < 15 )); end
        if isempty(j) == 1; j =  min(find(dat(:,2) < 20 )); end
        if isempty(j) == 1; j =  min(find(dat(:,2) < 25 )); end
        j2 =  min(find(dat(:,2) == min(dat(:,2)) ));
        %j = min([j j2]);

        Mc = dat(j,1);
        magco = Mc;
        prf = 100 - dat(j2,2);
        if isempty(magco) == 1; magco = nan; prf = 100 -min(dat(:,2)); end
        %display(['Completeness Mc: ' num2str(Mc) ]);
        %%%%%%%%%%%%%%%%%% mcperc_ca3.m end %%%%%%%%%%%%%%%%%%%%%%

        %CALCULATE MC
        [fMc] = Catalog.bvalue_lib.calc_Mc(mag, mcType, fBinning, fMccorr);
        l = mag >= fMc-(fBinning/2);
        if length(mag(l)) >= Nmin
            [fMeanMag, fBValue, fStd_B, fAValue] =  Catalog.bvalue_lib.calc_bmemag(mag(l), fBinning);
        else
            [fMc, fBValue, fStd_B, fAValue] = deal(NaN);
        end

        %STANDARD DEV OF a-value SET TO NAN;
        [fStd_A, fStd_Mc] = deal(NaN);

    else
        [fMc, fStd_Mc, fBValue, fStd_B, fAValue, fStd_A, ...
            fStdDevB, fStdDevMc] = deal(NaN);
    end

    magco = fMc;
    index_low=find(xt3 < magco+.05 & xt3 > magco-.05);
try
    mag_hi = xt3(1);
    index_hi = 1;
    mz = xt3 <= mag_hi & xt3 >= magco-.0001;
    mag_zone=xt3(mz);
    y = backg_ab(mz);

    %PLOT MC IN FIGURE
    Mc = semilogy(xt3(index_low),bvalsum3(index_low)*1.5,'vk');
    set(Mc,'LineWidth',[1.0],'MarkerSize',7)
    Mc = text(xt3(index_low)+0.2,bvalsum3(index_low)*1.5,'Mc');
    set(Mc,'FontWeight','normal','FontSize',12,'Color','k')

    %CREATE AND PLOT FIT LINE
    sol_type = 'Maximum Likelihood Solution';
    bw=fBValue;
    aw=fAValue;
    ew=fStd_B;
    p = [ -1*bw aw];
    f = polyval(p,mag_zone);
    f = 10.^f;
    hold on
    ttm= semilogy(mag_zone,f,'k');
    set(ttm,'LineWidth',[2.0])
    std_backg = ew;

    %ERROR CALCULATIONS
    %b = mag;
    bv = [];
    si = [];

    set(gca,'XLim',[min(mag)-0.5  max(mag+0.5)])
    %set(gca,'YLim',[0.9 length(mag+30)*2.5]);

    p=-p(1,1);
    p=fix(100*p)/100;
    tt1=num2str(bw,3);
    tt2=num2str(std_backg,1);
    tt4=num2str(bv,3);
    tt5=num2str(si,2);
    tmc=num2str(magco,2);
    rect=[0 0 1 1];
    h2=axes('position',rect);
    set(h2,'visible','off');
    t=catalogObject.gettimerange();
    a0 = aw-log10((t(2)-t(1))/365);

    text(.53,.88, ['b-value = ',tt1,' +/- ',tt2,',  a value = ',num2str(aw,3)],'FontSize',12);
    text(.53,.85,sol_type,'FontSize',12 );
    text(.53,.82,['Magnitude of Completeness = ',tmc],'FontSize',12);

    %% Added by Glenn 2018-05-01 to return a structure
    gr.bvalue = str2num(tt1);
    gr.bvalue_error = str2num(tt2);
    gr.avalue = aw;
    gr.Mc = str2num(tmc);

    %% Manually fit a line (added by Glenn 2018-05-01)
    if manual_on
        [xmag, yN] = ginput(2);
        slope = (log10(yN(1)) - log10(yN(2) )) / (xmag(2) - xmag(1) )
        plot(xmag, yN, 'g');
        gr.Mc_manual = xmag(1);
        gr.bvalue_manual = slope;
        text(xmag(1) + (xmag(2)-xmag(1))*0.5, yN(1) + (yN(2)-yN(1))*0.5, sprintf('manual b=%.2f',slope),'Color','g');
    end
catch
    gr.bvalue = NaN;
    gr.Mc = NaN;
    gr.avalue = NaN;
    gr.bvalue_error = NaN;
end

end


%RANDOMFORESTC Breiman's random forest
%
%   W = RANDOMFORESTC(A,L,N)
%   W = A*RANDOMFORESTC([],L,N)
%
% INPUT
%   A       Dateset used for training
%   L       Number of decision trees to be generated (default 50)
%   N       Size of feature subsets to be used (default 1)
%
% OUTPUT
%   W       Resulting, trained feature space classifier
%
% DESCRIPTION
% Train a decision forest on A, using L decision trees, each trained on
% a bootstrapped version of dataset A. Each decison tree is using random
% feature subsets of size N in each node.  When N=0, no feature subsets
% are used.
%
% REFERENCES
% [1] L. Breiman, Random Forests, Machine learning, vol. 45 (1), 5-32, 2001
%
% SEE ALSO (PRTools Guide)
% DATASETS, MAPPINGS, DTC

% Copyright: D.M.J. Tax, D.M.J.Tax@37steps.com
% Faculty EWI, Delft University of Technology
% P.O. Box 5031, 2600 GA Delft, The Netherlands

function out = randomforestc(varargin)

argin = setdefaults(varargin,[],50,1);
if mapping_task(argin,'definition')

  out = define_mapping(argin,'untrained',['RandForest' int2str(argin{2})]);

elseif mapping_task(argin,'training')

  [a,L,featsubset] = deal(argin{:});
	isvaldfile(a,2,2); % at least 2 obj/class, 2 classes
	opt = [];
	[n,dim,opt.K] = getsize(a);
	opt.featsubset = featsubset;
	v = cell(L,1);
	for i=1:L
		[x,z] = gendat(a);
    if exist('decisiontree','file')==3
      v{i} = decisiontree(+x,getnlab(x),opt.K,opt.featsubset);
    else
		 prwarning(2,'No compiled decisiontree found, using the slower Matlab implementation.');
    	v{i} = tree_train(+x,getnlab(x),opt);
    end
  end
  out = trained_classifier(a,v);

elseif mapping_task(argin,'execution')

  [a,w] = deal(argin{1:2});
	v = getdata(w);
	n = size(a,1);  % nr objects
	K = size(w,2);  % nr of classes
	nrv = length(v); % nr of trees
    out = zeros(n,K);
    if exist('decisiontree')==3
      for j=1:nrv
        I = decisiontree(v{j},+a);
        out = out + accumarray([(1:n)' I],ones(n,1),[n K]);
      end
    else
      % the old fashioned slow Matlab code
      for i=1:n
        x = +a(i,:);
        for j=1:nrv
          I = tree_eval(v{j},x);
          out(i,I) = out(i,I)+1;
        end
      end
      out = out./repmat(sum(out,2),1,K);
    end
    out = setdat(a,out,w);

else
  error('Illegal call')
end

return

%    out = tree_eval(w,x)
%
function out = tree_eval(w,x)

n = size(x,1);
out = zeros(n,1);

for i=1:n

	v=w;
	% if the first split is already solving everything (1 obj. per class)
	if isa(v,'double')
		out(i,1) = v;
	end
	while (out(i,1)==0)
		if (x(i,v.bestf)<v.bestt)
			v = v.l;
		else
			v = v.r;
		end
		if isa(v,'double')
			out(i,1) = v;
		end
	end
end

%
%    w = tree_train(x,y,opt)
%
function w = tree_train(x,y,opt)

% how good are we in this node?
err = tree_gini(y,opt.K);
if (err==0)

	w = y(1); % just predict this label

else
	% we split further
	n = size(x,1);

	% optionally, choose only from a subset
	if (opt.featsubset>0)
		fss = randperm(size(x,2));
		fss = fss(1:opt.featsubset);
	else
		fss = 1:size(x,2);
	end

	% check each feature separately:
	besterr = inf; bestf = []; bestt = []; bestj = []; bestI = [];
	for i=fss
		% sort the data along feature i:
		[xi,I] = sort(x(:,i)); yi = y(I);
		% run over all possible splits:
		for j=1:n-1
			% compute the gini
			err = j*tree_gini(yi(1:j),opt.K) + (n-j)*tree_gini(yi(j+1:n),opt.K);
			% and see if it is better than before.
			if (err<besterr)
				besterr = err;
				bestf = i;
				bestj = j;
				bestt = mean(xi(j:j+1));
				bestI = I;
			end
		end
	end

	% store
	w.bestf = bestf;
	w.bestt = bestt;
	%  now find the children:
	w.l = tree_train(x(bestI(1:bestj),:),y(bestI(1:bestj)),opt);
	w.r = tree_train(x(bestI(bestj+1:end),:),y(bestI(bestj+1:end)),opt);
end

function g = tree_gini(y,K)

out = zeros(1,K);
for k=1:K
	out(k) = mean(y==k);
end

g = out*(1-out)';


function [meshStruct]=hexMeshCylinder(varargin)

% function [meshStruct]=hexMeshCylinder(cylRadius,cylLength,pointSpacing)
% ------------------------------------------------------------------------
%
%
% Kevin Mattheus Moerman
% gibbon.toolbox@gmail.com
%
% 2021/02/26 Created
% 2021/05/08 @Fireedman (Luis Antonio Aguilar) added "end" to function
%------------------------------------------------------------------------

%% Parse input

switch nargin
    case 0
        cylRadius=[];
        cylLength=[];
        pointSpacing=[];
    case 1
        cylRadius=varargin{1};
        cylLength=[];
        pointSpacing=[];
    case 2
        cylRadius=varargin{1};
        cylLength=varargin{2};
        pointSpacing=[];
    case 3
        cylRadius=varargin{1};
        cylLength=varargin{2};
        pointSpacing=varargin{3};
end

%Check for empty variables
if isempty(cylRadius)
    cylRadius=1; %Unit radius
end

if isempty(cylLength)
    cylLength=cylRadius; %Height the same as radius
end

if isempty(pointSpacing)
    pointSpacing=(cylRadius*2*pi)/10; %One tenth of circumference
end

if numel(pointSpacing)==1
    pointSpacing=pointSpacing.*ones(1,2);
end

%%
%Set number of elements for core
numElementsCore=ceil(((cylRadius*2*pi)./pointSpacing(1))/4);

if numElementsCore<1
    numElementsCore=1;
end

%Set number of nodes allong circumference
numElementsHeight=ceil(cylLength./pointSpacing(2));
if numElementsHeight<1
    numElementsHeight=1;
end

%% Create quad mesh for disc

%Raw quad mesh
[Fs,Vs]=discQuadMesh(numElementsCore,cylRadius,0.6);

%Smoothen
Eb=patchBoundary(Fs);
controlParSmooth.n=25;
controlParSmooth.Method='LAP';
controlParSmooth.RigidConstraints=unique(Eb(:));
[Vs]=patchSmooth(Fs,Vs,[],controlParSmooth);

%% Thicken to elements

%Thicken
[E,V,Fp1,Fp2]=patchThick(Fs,Vs,1,cylLength,numElementsHeight);
V(:,3)=V(:,3)-cylLength/2;

%Get element faces
[F,~]=element2patch(E,[],'hex8');

%Find boundary faces
[indFree]=freeBoundaryPatch(F);
Fb=F(indFree,:);

%Assign boundary labels (or colors)
faceBoundaryMarker=zeros(size(Fb,1),1); %Side of cylinder
faceBoundaryMarker(all(ismember(Fb,Fp1),2))=1; %Original (e.g. top)
faceBoundaryMarker(all(ismember(Fb,Fp2),2))=2; %Thickened (e.g. bottom)

%% Collect output

meshStruct.nodes=V;
meshStruct.facesBoundary=Fb;
meshStruct.boundaryMarker=faceBoundaryMarker;
meshStruct.faces=F;
meshStruct.elements=E;
meshStruct.elementMaterialID=ones(size(E,1),1);
meshStruct.faceMaterialID=ones(size(F,1),1);

end

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2021 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program 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.
%
% This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program 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.
%
% This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.


  function atten = xray_filters(mtype, thickness, energy, varargin)
%|function atten = xray_filters(mtype, thickness, energy, [options])
%|
%| Compute X-ray photon survival probability as a function of energy
%| for various materials.
%| in
%|	mtype			'aluminum', 'copper', ...
%|				can be a cell array {L} for multiple filters
%|	thickness		in cm (can be an array [L] if mtype is cell)
%|	energy	[N,1]		in keV (vector)
%| option
%|	'units'	cm | mm		default: cm
%| out
%|	atten	[N,L]		unitless survival probabilities
%|
%| Copyright 2001-04-27, Jeff Fessler, University of Michigan

if nargin == 1 && streq(mtype, 'test'), xray_filters_test, return, end
if nargin < 3, ir_usage, end

arg.units = 'cm';
arg = vararg_pair(arg, varargin);

if iscell(mtype)
	LL = length(mtype);
	if length(thickness) ~= LL, error 'thickness / material mismatch', end
	atten = zeros(length(energy), LL);
	for ll=1:LL
		atten(:,ll) = xray_filters(mtype{ll}, thickness(ll), ...
			energy(:), 'units', arg.units);
	end
return
end

mass_atten = xray_read_atten(mtype, energy, 'units', arg.units); % [N,L]
density = xray_read_dens(mtype, 'units', arg.units); % [L,1]
atten = exp(-mass_atten .* thickness .* density);

function xray_filters_test
kev = [20:200]';
mtype = {'lead', 'copper', 'aluminum'};
t = xray_filters(mtype, [0.01 0.2 0.1], kev);
if im
	clf, semilogy(kev, t, '-o'), axis([20 200 10^-2 1]),
	ir_legend(mtype)
end


%function f = s6990029.m
%This function was completed on Tuesday, 16 November 2002
%as a part of a course, in order to form a good-interfaced program
%for producing random variables from the negative binomial distribution.
%
%Made by P.B.(6990029)
function s6990029

fprintf('In a few seconds a pop-up screen will prompt you to choose from one\nof 4 methods, in order to generate random variables from the Negative Binomial Distribution.');
fprintf('\n\nLOADING('),
for j=1:30
fprintf('---')
pause(0.06990029)
end
fprintf(')')
fprintf('\n')

K=menu('Choose a method for generating n random variables from the Neg.Binomial :','1) Inverse transform Method','2) Convolution Method (using Bernoulli D.)','3) Convolution Method (using Geometrical D.)','4) Acceptance Rejection Method','**************************Which is faster=better***********************','Exit');

%Method 1

if (K==1)

disp('You have selected the Inverse Transform method. Please wait...');
pause(2);
clc;clear;
N = input('Please give me the size of the sample : ');
R = input('Type the value of the parameter of successes R : ');
P = input('Now give the probability of success P : ');
disp('Please standby...');

%set up the storage space and the clock
tic
X = zeros(1,N);
x =1:N;

for x =1:N
pr(x)  = NBINCDF(x,R,P);
end

pr(N)=1;    %epeidh en vgainei panta 1 to teleutaio stoixeio tou pinaka ths a8roistikhs ...kammia fora... dhmiourgei provlhmata!!!

for i=1:N
 s=1;
u= rand;
while u > pr(s)
    s=s+1;
end
X(i) = s;
end

%stop the clock
t=toc;
fp=fopen('c:\data1.txt','w');
fprintf(fp,' %3.0f \n',X);
fclose(fp);
disp('Your sample has been saved in data1.txt');
fprintf('The time needed, depending on your machine'' speed \n and the complexity of the algorithm, was: ');disp(t)
pause(2);

load handel;
sound(y,Fs);

%the empirical histogram

[n,h]= hist(X,10);
n=n/(h(2)-h(1))/N;
bar(h,n,1,'w')
hold on

%theoritical curve

for x =1:N
pr(x)  = NBINPDF(x,R,P);
end
x =1:N;
plot(x,pr,'k');
hold off
title('Empirical & Theoritical pdf of the Neg. Binomial Distribution');
xlabel('X');
ylabel('P(X)');

%Method 2

elseif (K==2)

fprintf('You have selected the Convolution method \n(using as a prime several Bernoulli variables). \n\nPlease wait...');
pause(4);
clc;clear;
N = input('Please give me the size of the sample : ');
R = input('Type the value of the parameter of successes R : ');
P = input('Now give the probability of the bernoulli trials P : ');
disp('Please standby...');

%set up the storage space and the clock
tic

X = zeros(1,N);
for j=1:N
h=0;
i=0;
while h < R
u=rand;
i=i+1;
if  u <= P
h = h+1;
end
end
X(j)=i-R;               % duskolo shmeio...orismos:...o arithmos twn dokimwn mexri R epituxies!
end

t=toc;
fp=fopen('c:\data2.txt','w');
fprintf(fp,' %3.0f \n',X);
fclose(fp);
disp('Your sample has been saved in data2.txt');
fprintf('The time needed, depending on your machine'' speed \n and the complexity of the algorithm, was: ');disp(t)
pause(2);

load handel;
sound(y,Fs);

%the empirical histogram

[n,h]= hist(X,10);
n=n/(h(2)-h(1))/N;
bar(h,n,1,'w')
hold on

%theoritical curve

for x =1:N
pr(x)  = NBINPDF(x,R,P);
end
x =1:N;
plot(x,pr,'k');
hold off
title('Empirical & Theoritical pdf of the Neg. Binomial Distribution');
xlabel('X');
ylabel('P(X)');

%METHOD 3

elseif (K==3)

fprintf('You have selected the Convolution method \n(using as a prime several Geometrical variables). \n\nPlease wait...');
pause(4);
clc;clear;
N = input('Please give me the size of the sample : ');
R = input('Type the value of the parameter of successes R : ');
P = input('Now give the probability of the bernoulli trials P : ');
disp('Please standby...');

%set up the storage space and the clock
tic

X = zeros(1,N);

G=geornd(P,N,R);

X=G*ones(R,1);

%stop the clock
t=toc;
fp=fopen('c:\data3.txt','w');
fprintf(fp,' %3.0f \n',X);
fclose(fp);
disp('Your sample has been saved in data3.txt');
fprintf('The time needed, depending on your machine'' speed \n and the complexity of the algorithm, was: ');disp(t)
pause(2);

load handel;
sound(y,Fs);

%the empirical histogram

[n,h]= hist(X,10);
n=n/(h(2)-h(1))/N;
bar(h,n,1,'w')
hold on

%theoritical curve

for x =1:N
pr(x)  = NBINPDF(x,R,P);
end
x =1:N;
plot(x,pr,'k');
hold off
title('Empirical & Theoritical pdf of the Neg. Binomial Distribution');
xlabel('X');
ylabel('P(X)');

%METHOD 4

elseif (K==4)

fprintf('You have selected the Acceptance-Rejection method). \n\nPlease wait...');
pause(4);
clc;clear;
N = input('Please give me the size of the sample : ');
R = input('Type the value of the parameter of successes R : ');
P = input('Now give the probability of the bernoulli trials P : ');
disp('Please standby...');

%set up the storage space and the clock
tic

for x =1:N
pr(x)  = NBINPDF(x,R,P);
end

c=max(pr)*N;

irv=1;
while irv<=N
    y = unidrnd(N);
    u = rand(1);
    if u <= (pr(y))/c ;
        X(irv) =  y;
        irv = irv +1;
    end
end

%stop the clock
t=toc;
%save to disk
fp=fopen('c:\data4.txt','w');
fprintf(fp,' %3.0f \n',X);
fclose(fp);
disp('Your sample has been saved in data4.txt');
fprintf('The time needed, depending on your machine'' speed \n and the complexity of the algorithm, was: ');disp(t)
pause(2);

load handel;
sound(y,Fs);

%the empirical histogram

[n,h]= hist(X,10);
n=n/(h(2)-h(1))/N;
bar(h,n,1,'w')
hold on

%theoritical curve

for x =1:N
pr(x)  = NBINPDF(x,R,P);
end
x =1:N;
plot(x,pr,'k');
hold off
title('Empirical & Theoritical pdf of the Neg. Binomial Distribution');
xlabel('X');
ylabel('P(X)');

%Test for overall performance within the range of the two parameters (R,P)

%#########################################################################################

elseif (K==5)

    clc;
    fprintf('You have selected to test between the three first  methods.\nFor the 4th one the envelope function selected was rather unsuitable since it was\nthe discrete uniform.\nWith an other function as an envelope it would be very hard to test\nthrough a wide range of (R,P)\nsince the Neg. Binomial''s shape would change.\n\nPlease wait. It will take some time ...\n');
pause(5);

clear;
N = input('Please give me the size of the sample. It would be most preferable to choose sample bigger than 10000! : ');

fprintf('Please go drink a coffee...\n')

    q=1;

    for R=10:N
    for P=0.3:0.2:0.7
        %1st
tic
X = zeros(1,N);
x =1:N;

for x =1:N
pr(x)  = NBINCDF(x,R,P);
end

pr(N)=1;    %epeidh en vgainei panta 1 to teleutaio stoixeio tou pinaka ths a8roistikhs ...kammia fora... dhmiourgei provlhmata!!!

for i=1:N
 s=1;
u= rand;
while u> pr(s)
    s=s+1;
end
    X(i) = s;
end
%stop the clock
t1=toc;

%2nd

tic

X = zeros(1,N);
for j=1:N
h=0;
i=0;
while h < R
u=rand;
i=i+1;
if  u <= P
h = h+1;
end
end
X(j)=i-R;               % duskolo shmeio...orismos:...o arithmos twn dokimwn mexri R epituxies!
end

t2=toc;

%3rd

tic

X = zeros(1,N);

G=geornd(P,N,R);

X=G*ones(R,1);

%stop the clock
t3=toc;

q=q+1;

T(q,:)=[t1 t2 t3];

end
end

boxplot(T);
anova1(T);

fprintf('\n\n\n\n\nOne would choose the method which uses the sum of the geometrical variables\nsince the Anova table also recommends this method!\nHave a nice day.')

else
 disp('Have a nice Day!!!');

%break

end


function [node,elem,bdFlag,HB] = uniformrefine3(node,elem,bdFlag,HB)
%% UNIFORMREFINE3 uniformly refine a 3-D triangulation.
%
% [node,elem] = uniformrefine3(node,elem) divides each tetrahedra into
% eight small similar sub-tetrahedrons.
%
% [node,elem,bdFlag] = uniformrefine3(node,elem,bdFlag) also update boundary
% conditions represented by bdFlag.
%
% [node,elem,~,HB] = uniformrefine3(node,elem) outpus HB array which is
% useful for nodal interpolation. Unlike bisect3, HB is not useful for the
% coarsening. See uniformcoarsen3red.
%
% Warning: uniform refine in 3D is not orientation presereved. Some volumes
% of the sub-tetrahedron are negative. The ordering is special such that
% the refinement can be easy. See uniformrefine3doc for details.
%
% Example
%
%     [node,elem] = cubemesh([-1,1,-1,1,-1,1]);
%     figure(1); subplot(1,3,1);
%     set(gcf,'Units','normal'); set(gcf,'Position',[0.25,0.25,0.5,0.3]);
%     showmesh3(node,elem,[],'FaceAlpha',0.35); view([210 8]);
%     [node,elem] = uniformrefine3(node,elem);
%     figure(1); subplot(1,3,2);
%     showmesh3(node,elem,[],'FaceAlpha',0.35); view([210 8]);
%     bdFlag = setboundary3(node,elem,'Dirichlet');
%     [node,elem,~,bdFlag] = uniformrefine3(node,elem,[],bdFlag);
%     figure(1); subplot(1,3,3);
%     showmesh3(node,elem,[],'FaceAlpha',0.35); view([210 8]);
%
% See also uniformbisect, uniformrefine, bisect, bisect3, uniformcoarsen3red
%
% Reference page in Help browser
%  ifem uniformrefine3doc
%
% Reference: J. Bey. Simplicial grid refinement: on Freudenthal's algorithm
% and the optimal number of congruence classes. Numer. Math.. 85(1):1--29,
% 2000. p11 Algorithm: RedRefinement3D.
%
% Copyright (C) Long Chen. See COPYRIGHT.txt for details.

if ~exist('bdFlag','var'), bdFlag =[]; end
if ~exist('HB','var'),    HB = [];     end

%% Construct data structure
[elem2dof,edge] = dof3P2(elem);
N = size(node,1); NT = size(elem,1); NE = size(edge,1);

%% Add new nodes
node(N+1:N+NE,:) = (node(edge(:,1),:)+node(edge(:,2),:))/2;
if ~isempty(HB)
    maxgeneration = max(HB(:,4));
    HB(N+1:N+NE,[1 2 3]) = [(N+1:N+NE)', edge(:,1:2)];
    HB(N+1:N+NE,4) = maxgeneration + 1;
end

%% Refine each tetrahedron into 8 tetrahedrons
t = 1:NT;
p(t,1:10) = elem2dof;
elem(8*NT,:) = [0 0 0 0]; % enlarge the elem array
elem(t,:) = [p(t,1), p(t,5), p(t,6), p(t,7)];
elem(NT+1:2*NT,:) = [p(t,5), p(t,2), p(t,8), p(t,9)];
elem(2*NT+1:3*NT,:) = [p(t,6), p(t,8), p(t,3), p(t,10)];
elem(3*NT+1:4*NT,:) = [p(t,7), p(t,9), p(t,10), p(t,4)];
% always use diagonal 6-9. The ordering is important. See the reference.
elem(4*NT+1:5*NT,:) = [p(t,5), p(t,6), p(t,7), p(t,9)];
elem(5*NT+1:6*NT,:) = [p(t,5), p(t,6), p(t,8), p(t,9)];
elem(6*NT+1:7*NT,:) = [p(t,6), p(t,7), p(t,9), p(t,10)];
elem(7*NT+1:8*NT,:) = [p(t,6), p(t,8), p(t,9), p(t,10)];

%% Update boundary edges
if ~isempty(bdFlag)
    bdFlag(8*NT,:) = [0 0 0 0]; % enlarge the bdFlag array
    bdFlag(NT+1:2*NT,[1 3 4]) = bdFlag(t,[1 3 4]);
    bdFlag(2*NT+1:3*NT,[1 2 4]) = bdFlag(t,[1 2 4]);
    bdFlag(3*NT+1:4*NT,[1 2 3]) = bdFlag(t,[1 2 3]);
    % always use diagonal 6-9
    bdFlag(4*NT+1:5*NT,2) = bdFlag(t,3);
    bdFlag(5*NT+1:6*NT,4) = bdFlag(t,4);
    bdFlag(6*NT+1:7*NT,3) = bdFlag(t,2);
    bdFlag(7*NT+1:8*NT,1) = bdFlag(t,1);
    % change t in the last
    bdFlag(t,1) = 0;
else
    bdFlag = [];
end


%%
col = {[0 114 178],[0 158 115], [213 94 0],[230 159 0],...
    [86 180 233], [204 121 167], [64 224 208], [240 228 66]};

%% AR(1)
g = .95;
[Y, trueC, trueSpikes] = gen_data();
N = size(Y, 1);

% run deconvolution
lam = 2.4;
[c_oasis, s_oasis] = oasisAR1(Y(1,:), g, lam);
[c_foopsi, s_foopsi] = foopsi(Y(1,:), g, lam);

% plot results
figure('papersize', [15, 4]);
init_fig;

% c
axes('position', [.05, .57, .95, .37]);
hold on;
plot(Y(1,:), 'color', col{8}/255);
alpha(.7);
plot(trueC(1,:), 'color', col{3}/255, 'linewidth', 1.5);
plot(c_oasis, 'color', col{1}/255);
plot(c_foopsi, '--', 'color', col{7}/255);
axis tight;
xlim([0, 2000]);
set(gca, 'xtick', [0, 25, 50, 75]*30);
set(gca, 'xticklabel', []);
set(gca, 'ytick', 0:2);
ylabel('Fluor.');
box off;
legend('Data', 'Truth', 'OASIS', 'CVX', 'location', 'northeast', 'orientation', 'horizontal');
% s
axes('position', [.05, .18, .95, .37]);
hold on;
plot(trueSpikes(1,:), 'color', col{3}/255, 'linewidth', 1.5);
plot(s_oasis, 'color', col{1}/255);
plot(s_foopsi, '--', 'color', col{7}/255);
axis tight;
xlim([0, 2000]);
set(gca, 'xtick', [0, 25, 50, 75]*30);
set(gca, 'xticklabel', get(gca, 'xtick')/30);
set(gca, 'ytick', [0,1]);
xlabel('Time [s]');
ylabel('Activity.');

saveas(gcf, 'fig/traceAR1.pdf');

%% time it
fprintf('\n**************AR 1**************\n');
tic;
for m=1:N
    [c_oasis, s_oasis] = oasisAR1(Y(m,:), g, lam);
end
fprintf('OASIS: %.3f seconds\n', toc);

tic;
for m=1:N
    [c_foopsi, s_foopsi] = foopsi(Y(1,:), g, lam); %#ok<ASGLU>
end
fprintf('FOOPSI: %.3f seconds\n', toc);
fprintf('\n**************AR 1**************\n');

%% AR(2)
% simulation
g = [1.7, -0.712];
sn = 1;
seed = 3;
[Y, trueC, trueSpikes] = gen_data(g, sn, [], [], [], [], [], seed);
[N, T] = size(Y);

% deconvolution
lam = 25;
[c_onnls, s_onnls] = onnls(Y(1,:), g, lam);
[c_foopsi, s_foopsi] = foopsi(Y(1,:), g, lam);
figure('papersize', [15, 4]);
init_fig;

% plot results
figure('papersize', [15, 4]);
init_fig;

% c
axes('position', [.05, .57, .46, .37]);
hold on;
plot(Y(1,:), 'color', col{8}/255, 'linewidth', 0.5);
alpha(.7);
plot(trueC(1,:), 'color', col{3}/255, 'linewidth', 1.5);
plot(c_onnls, 'color', col{1}/255);
plot(c_foopsi, '--', 'color', col{7}/255);
axis tight;
xlim([0, 1200]);
set(gca, 'xtick', 0:300:1500);
set(gca, 'xticklabel', []);
ylim(round([1+min(Y(1,:)), max(Y(1,:))-0.5]));
ylabel('Fluor.');
box off;
% s
axes('position', [.05, .18, .46, .37]);
hold on;
plot(trueSpikes(1,:), 'color', col{3}/255, 'linewidth', 1.5);
plot(s_onnls, 'color', col{1}/255);
plot(s_foopsi, '--', 'color', col{7}/255);
axis tight;
xlim([0, 1200]);
set(gca, 'xtick', 0:300:1500);
set(gca, 'xticklabel', get(gca, 'xtick')/30);
set(gca, 'ytick', [0,1]);
xlabel('Time [s]');
ylabel('Activity.');

%% timeit
fprintf('\n**************AR 2**************\n');
tic;
for m=1:N
    [c_onnls, s_onnls] = onnls(Y(m,:), g, lam);
end
fprintf('online NNLS: %.3f seconds\n', toc);

tic;
for m=1:N
    [c_foopsi, s_foopsi] = foopsi(Y(m,:), g, lam);
end
fprintf('FOOPSI: %.3f seconds\n', toc);
fprintf('\n**************AR 2**************\n');


function [price] = PROJ_Double_Barrier(N,alph,call,L,U, S_0,W,M,T,r,rnCHF)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% About: Pricing Function for Discrete Double Barrier Options using PROJ method
% Models Supported: Levy Processes, including jump diffusions and Black-Scholes model
% Returns: price of contract
% Author: Justin Lars Kirkby
%
% ----------------------
% Contract/Model Params
% ----------------------
% S_0 = initial stock price (e.g. 100)
% W   = strike  (e.g. 100)
% r   = interest rate (e.g. 0.05)
% q   = dividend yield (e.g. 0.05)
% T   = time remaining until maturity (in years, e.g. T=1)
% M   = number of subintervals of [0,T] (total of M+1 monitoring points in time grid, including S_0)
% call = 1 for call (else put)
% [L,U] = barriers
% rnCHF = risk netural characteristic function (function handle with single argument)
%
% ----------------------
% Numerical (PROJ) Params
% ----------------------
% N     = number of grid/basis points (power of 2, e.g. 2^12), resolution = 2*alph/(N-1)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

l = log(L/S_0);  u = log(U/S_0);  dt = T/M;

K = N/2;
dx = (u-l)/(K-1);
a  = 1/dx;

E  = ceil(2*alph/(u-l));
if M<= 12
    E = min(E,4);
else
    E = min(E,3);
end

a2    = a^2;
N_Ee  = E*N;
Cons2 = 24*a2*exp(-r*dt)/N_Ee ;

grand = @(w)rnCHF(w).*(sin(w/(2*a))./w).^2./(2+cos(w/a));
dw    = 2*pi*a/N_Ee ;
omega = (dw: dw: (N_Ee -1)*dw);      %We calcuate coefficient of w=0 explicitly
zmin  = (1 - E*K)*dx;           %K corresponds to zero
beta  = Cons2*real(fft([1/(24*a^2) exp(-1i*zmin*omega).*feval(grand,omega)]));

toepM = [beta(E*K:-1:(E-1)*K+1)';0; beta((E+1)*K-1:-1:E*K+1)'];
toepM = fft(toepM);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%STEP 1: Payoff Coefficients
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
xmin = l;
nnot = floor(1-xmin*a);   %index to left of x_0=0
lws = log(W/S_0);
nbar = floor(a*(lws - xmin)+1);
rho   = lws - (xmin+(nbar - 1)*dx);
zeta  = a*rho;
xnbar = xmin + (nbar - 1)*dx;

Thet  = zeros(K,1);
Cons3 = 1/48;
Cons4 = 1/12;

%%%%%%  Gaussian Quad Constants
q_plus = (1 + sqrt(3/5))/2;  q_minus = (1 - sqrt(3/5))/2;
b3  = sqrt(15); b4 = b3/10;

%%%% PAYOFF CONSTANTS-----------------------------------
varthet_01 = exp(.5*dx)*(5*cosh(b4*dx) - b3*sinh(b4*dx) + 4)/18;
varthet_m10 = exp(-.5*dx)*(5*cosh(b4*dx) + b3*sinh(b4*dx) + 4)/18;
varthet_star = varthet_01 + varthet_m10;

if call==1  %DBC
    sigma = 1 - zeta; sigma_plus = (q_plus-.5)*sigma; sigma_minus = (q_minus-.5)*sigma;
    es1 = exp(dx*sigma_plus); es2 = exp(dx*sigma_minus);
    dbar_0 = .5 + zeta*(.5*zeta-1);
    dbar_1 = sigma*(1 - .5*sigma);

    d_0 = exp((rho+dx)*.5)*sigma^2/18*(5*((1-q_minus)*es2 +(1-q_plus)*es1) + 4);
    d_1 = exp((rho-dx)*.5)*sigma/18*(5*( (.5*(zeta+1) +sigma_minus)*es2 + (.5*(zeta+1) +sigma_plus)*es1 ) + 4*(zeta+1) );

    Thet(nbar)         = W*(exp(-rho)*d_0 - dbar_0);
    Thet(nbar + 1)     = W*(exp(dx-rho)*(varthet_01 +d_1) -(.5 + dbar_1) );
    Thet(nbar + 2:K-1) = exp(xmin +dx*(nbar+1:K-2))*S_0*varthet_star - W;
    Thet(K)            = U*varthet_m10 - W/2;
    %%%%%%%
    p = ifft(toepM.*fft([Thet(1:K);zeros(K,1)]));
    Val = p(1:K);
    %%%%%%%
    for m=M-2:-1:0
        Thet(1)      = Cons3*(13*Val(1)+15*Val(2)-5*Val(3)+Val(4));
        Thet(K)      = Cons3*(13*Val(K)+15*Val(K-1)-5*Val(K-2)+Val(K-3));
        Thet(2:K -1) = Cons4*(Val(1:K-2)+10*Val(2:K-1)+Val(3:K));
        %%%%%%%
        p        = ifft(toepM.*fft([Thet(1:K);zeros(K,1)]));
        Val = p(1:K);
    end

else  %DBP
    zeta_plus = zeta*q_plus; zeta_minus = zeta*q_minus;
    rho_plus = rho*q_plus; rho_minus = rho*q_minus;

    ed1 = exp(rho_minus); ed2 = exp(rho/2); ed3 = exp(rho_plus);

    dbar_1 = zeta^2/2;
    dbar_0 = zeta - dbar_1;         %  dbar_1 = zeta + .5*((zeta - 1)^2 - 1);
    d_0    = zeta*(5*( (1-zeta_minus)*ed1 + (1-zeta_plus)*ed3 ) + 4*(2-zeta)*ed2)/18;
    d_1    = zeta*( 5*(zeta_minus*ed1 + zeta_plus*ed3) + 4*zeta*ed2 )/18;

    Thet(1)        =  W/2 - L*varthet_01;
    Thet(2:nbar-1) =  W - exp(xmin +dx*(1:nbar-2))*S_0*varthet_star;
    Thet(nbar)     =  W*(.5 + dbar_0 - exp(-rho)*(varthet_m10 + d_0));
    Thet(nbar + 1) =  W*(dbar_1 - exp(- rho)*d_1);
    %%%%%%%
    p   = ifft(toepM.*fft([Thet(1:K);zeros(K,1)]));
    Val = p(1:K);
    %%%%%%%
    for m=M-2:-1:0
        Thet(1)      = Cons3*(13*Val(1)+15*Val(2)-5*Val(3)+Val(4));
        Thet(K)      = Cons3*(13*Val(K)+15*Val(K-1)-5*Val(K-2)+Val(K-3));
        Thet(2:K -1) = Cons4*(Val(1:K-2)+10*Val(2:K-1)+Val(3:K));
        %%%%%%%
        p    = ifft(toepM.*fft([Thet(1:K);zeros(K,1)]));
        Val  = p(1:K);
    end
end

xnot = l+(nnot-1)*dx;
xs = [xnot-2*dx,xnot-dx,xnot,xnot+dx,xnot+2*dx];
ys = [Val(nnot-2),Val(nnot-1),Val(nnot),Val(nnot+1),Val(nnot+2)];
price = spline(xs,ys,0);

end


% SYNTAX:
% dc = hmrR_OD2Conc_new_Nirs( dod, SD, ppf )
%
% UI NAME:
% OD_to_Conc
%
% DESCRIPTION:
% Convert OD to concentrations
%
% INPUTS:
% dod: the change in OD (#time points x #channels)
% SD:  the SD structure
% ppf: Partial path length factors for each wavelength. If there are 2 wavelengths
%      of data, then this is a vector of 2 elements.  Typical value is ~6 for each
%      wavelength if the absorption change is uniform over the volume of tissue measured.
%      To approximate the partial volume effect of a small localized absorption change
%      within an adult human head, this value could be as small as 0.1. Convention is
%      becoming to set ppf=1 and to not divide by the source-detector separation such that
%      the resultant "concentration" is in units of Molar mm (or Molar cm if those are the
%      spatial units). This is becoming wide spread in the literature but there is no
%      fixed citation. Use a value of 1 to choose this option.
%
% OUTPUTS:
% dc: the concentration data (#time points x 3 x #SD pairs
%     3 concentrations are returned (HbO, HbR, HbT)
%
% USAGE OPTIONS:
% Delta_OD_to_Conc: dc = hmrR_OD2Conc_new_Nirs( dod, SD, ppf )
%
% PARAMETERS:
% ppf: [1.0, 1.0, 1.0]
%
function dc = hmrR_OD2Conc_new_Nirs( dod, SD, ppf )

dc = [];
nWav = length(SD.Lambda);
ml = SD.MeasList;

if length(ppf) < nWav
    errordlg('WARNING: Data contains more than 3 wavelengths. Using PPF value of 1 for all wavelengths.');
    ppf = ones(1, nWav);
elseif length(ppf) > nWav
    d = length(ppf)-nWav;
    ppf(end-d+1:end) = [];
end

if ~isempty(find(ppf==1))
    ppf = ones(size(ppf));
end

nTpts = size(dod,1);

e = GetExtinctions( SD.Lambda );
if ~isfield(SD,'SpatialUnit')
    e = e(:,1:2) / 10; % convert from /cm to /mm
elseif strcmpi(SD.SpatialUnit,'mm')
    e = e(:,1:2) / 10; % convert from /cm to /mm
elseif strcmpi(SD.SpatialUnit,'cm')
    e = e(:,1:2) ;
end
einv = inv( e'*e )*e';

lst = find( ml(:,4)==1 );
for idx=1:length(lst)
    idx1 = lst(idx);
    idx2 = find( ml(:,4)>1 & ml(:,1)==ml(idx1,1) & ml(:,2)==ml(idx1,2) );
    rho = norm(SD.SrcPos(ml(idx1,1),:)-SD.DetPos(ml(idx1,2),:));
    if ppf(1)~=1
        dc(:,:,idx) = ( einv * (dod(:,[idx1 idx2'])./(ones(nTpts,1)*rho*ppf))' )';
    else
        dc(:,:,idx) = ( einv * (dod(:,[idx1 idx2'])./(ones(nTpts,1)))' )';
    end
end
dc(:,3,:) = dc(:,1,:) + dc(:,2,:);


function [inside] = bounding_mesh(pos, pnt, tri);

% BOUNDING_MESH determines if a point is inside/outside a triangle mesh
% whereby the bounding triangle mesh should be closed.
%
% [inside] = bounding_mesh(pos, pnt, tri)
%
% where
%   pos     position of point of interest (can be 1x3 or Nx3)
%   pnt     bounding mesh vertices
%   tri     bounding mesh triangles
%
% See also SOLID_ANGLE

% Copyright (C) 2003, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id: bounding_mesh.m 2885 2011-02-16 09:41:58Z roboos $

global fb;
if isempty(fb)
  fb = 0;
end

npos = size(pos, 1);
npnt = size(pnt, 1);
ntri = size(tri, 1);

% determine a cube that encompases the boundary triangulation
bound_min = min(pnt);
bound_max = max(pnt);

% determine a sphere that is completely inside the boundary triangulation
bound_org = mean(pnt);
bound_rad = sqrt(min(sum((pnt - repmat(bound_org, size(pnt,1), 1)).^2, 2)));

inside = zeros(npos, 1);
for i=1:npos
  if fb
    fprintf('%6.2f%%', 100*i/npos);
  end
  if any(pos(i,:)<bound_min) || any(pos(i,:)>bound_max)
    % the point is outside the bounding cube
    inside(i) = 0;
    if fb, fprintf(' outside the bounding cube\n'); end
  elseif sqrt(sum((pos(i,:)-bound_org).^2, 2))<bound_rad
    % the point is inside the interior sphere
    inside(i) = 1;
    if fb, fprintf(' inside the interior sphere\n'); end
  else
    % the point is inside the bounding cube but outside the interior sphere
    % compute the total solid angle of the surface, which is zero for a point outside
    % the triangulation and 4*pi or -4*pi for a point inside (depending on the triangle
    % orientation)
    tmp = pnt - repmat(pos(i,:), npnt, 1);
    solang = solid_angle(tmp, tri);
    if any(isnan(solang))
      inside(i) = nan;
    elseif (abs(sum(solang))-2*pi)<0
      % total solid angle is (approximately) zero
      inside(i) = 0;
    elseif (abs(sum(solang))-2*pi)>0
      % total solid angle is (approximately) plus or minus 4*pi
      inside(i) = 1;
    end
    if fb, fprintf(' solid angle\n'); end
  end
end


function linpack_c_test33 ( )

%*****************************************************************************80
%
%% TEST33 tests CSPFA and CSPDI.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    25 June 2009
%
%  Author:
%
%    John Burkardt
%
  n = 3;

  fprintf ( 1, '\n' )
  fprintf ( 1, 'TEST33\n' );
  fprintf ( 1, '  For a single precision complex (C)\n' );
  fprintf ( 1, '  symmetric matrix in packed storage (SP)\n' );
  fprintf ( 1, '  CSPFA factors the matrix.\n' );
  fprintf ( 1, '  CSPDI computes the determinant or inverse.\n' );
  fprintf ( 1, '\n' )
  fprintf ( 1, '  The matrix order is N = %d\n', n );
%
%  Set the values of the packed matrix A.
%
  k = 0;
  seed = 123456789;

  for j = 1 : n

    for i = 1 : j-1
      k = k + 1;
      [ a(k), seed ] = c4_uniform_01 ( seed );
    end

    k = k + 1;
    [ a(k), seed ] = c4_uniform_01 ( seed );

  end
%
%  Copy the packed matrix into a "normal" matrix.
%
  k = 0;
  for j = 1 : n
    for i = 1 : j
      k = k + 1;
      a_save(i,j) = a(k);
    end
  end

  for j = 1 : n
    a_save(j+1:n,j) = transpose ( a_save(j,j+1:n) );
  end

  fprintf ( 1, '\n' )
  fprintf ( 1, '  The matrix A is\n' );
  fprintf ( 1, '\n' )

  for i = 1 : n
    for j = 1 : n
      fprintf ( 1, '  (%8f  %8f)', real ( a_save(i,j) ), imag ( a_save(i,j) ) );
    end
    fprintf ( 1, '\n' );
  end
%
%  Factor the matrix A.
%
  [ a, ipvt, info ] = cspfa ( a, n );

  if ( info ~= 0 )
    fprintf ( 1, '\n' )
    fprintf ( 1, '  CSPFA returned an error flag INFO = %d\n', info );
    return
  end
%
%  Get the determinant.
%
  job = 10;
  [ a, det ] = cspdi ( a, n, ipvt, job );

  fprintf ( 1, '\n' )
  fprintf ( 1, '  Determinant = (%8f  %8f)*10^(%8f)\n', ...
    real ( det(1) ), imag ( det(1) ), real ( det(2) ) );
%
%  Get the inverse.
%
  job = 01;
  [ a, det ] = cspdi ( a, n, ipvt, job );
%
%  Copy the packed matrix into a "normal" matrix.
%
  k = 0;
  for j = 1 : n
    for i = 1 : j
      k = k + 1;
      b_save(i,j) = a(k);
    end
  end

  for j = 1 : n
    b_save(j+1:n,j) = transpose ( b_save(j,j+1:n) );
  end

  c(1:n,1:n) = b_save(1:n,1:n) * a_save(1:n,1:n);

  fprintf ( 1, '\n' )
  fprintf ( 1, '  The product inv(A) * A is\n' );
  fprintf ( 1, '\n' )

  for i = 1 : n
    for j = 1 : n
      fprintf ( 1, '  (%8f  %8f)', real ( c(i,j) ), imag ( c(i,j) ) );
    end
    fprintf ( 1, '\n' );
  end

  return
end


%% FEM2D_SCRIPT carries out the finite element assembly and solution procedures.
%
%  Discussion:
%
%    The BATCH command runs scripts, not functions.  So we have to write
%    this short script if we want to work with BATCH!
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    29 March 2010
%
%  Author:
%
%    John Burkardt
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'FEM2D_SCRIPT\n' );
  fprintf ( 1, '  Call FEM2D_FUN to set up and solve the system.\n' );
%
%  Get the problem parameters.
%
  param = p_data ( );
%
%  Set up the mesh, the linear system, and solve.
%
  [ x, z_ss ] = fem2d_fun ( param );


function tv = Fval(u, img, alpha, huber)
% total variation part of the criterion

[H W] = size(img);
N = W * H;

nabla = make_derivatives_mine(H, W);

if huber
    tv = nabla * u(:);
    tv_size = sqrt(tv(1:N).^2 + tv(N+1:end).^2);
    idx1 = tv_size <= alpha;
    idx2 = tv_size > alpha;
    tv = sum(tv_size(idx1).^2/(2*alpha)) + sum(tv_size(idx2) - alpha/2);
else
    tv = nabla * u(:);
    tv = sum(sqrt(tv(1:N).^2 + tv(N+1:end).^2));
end


function [Gc,Kp,Ti,Td,H]=optPID(key,typ,vars)
k=vars(1); L=vars(2); T=vars(3); N=vars(4);
Td=[];H=1;
if length(vars)==5, iC=vars(5); tt=0;
else,
   Kc=vars(5); Tc=vars(6); kappa=vars(7); tt=1;
end
if tt==0
   if key==2
PIDtab=[0.980, 0.712, 0.569, 1.072, 0.786, 0.628;
       -0.892,-0.921,-0.951,-0.560,-0.559,-0.583;
        0.690, 0.968, 1.023, 0.648, 0.883, 1.007;
       -0.155,-0.247,-0.179,-0.114,-0.158,-0.167];
   elseif key==3
PIDtab=[1.048, 1.042, 0.968, 1.154, 1.142, 1.061;
       -0.897,-0.897,-0.904,-0.567,-0.579,-0.583;
        1.195, 0.987, 0.977, 1.047, 0.919, 0.892;
       -0.368,-0.238,-0.253,-0.220,-0.172,-0.165;
        0.489, 0.385, 0.316, 0.490, 0.384, 0.315;
        0.888, 0.906, 0.892, 0.708, 0.839, 0.832];
   elseif key==4
PIDtab=[1.260, 1.053, 0.942, 1.295, 1.120, 1.001;
       -0.887,-0.930,-0.933,-0.619,-0.625,-0.624;
        0.701, 0.736, 0.770, 0.661, 0.720, 0.754;
       -0.147,-0.126,-0.130,-0.110,-0.114,-0.116;
        0.375, 0.349, 0.308, 0.378, 0.350, 0.308;
        0.886, 0.907, 0.897, 0.756, 0.811, 0.813];
   end
   ii=0; if (L/T>1) ii=3; end; tt=L/T;
   a1=PIDtab(1,ii+iC); b1=PIDtab(2,ii+iC);
   a2=PIDtab(3,ii+iC); b2=PIDtab(4,ii+iC);
   Kp=a1/k*tt^b1; Ti=T/(a2+b2*tt);
   if key==3| key==4
      a3=PIDtab(5,ii+iC); b3=PIDtab(6,ii+iC);
      Td=a3*T*tt^b3;
   end
else
   if key==2,
      Kp=0.361*Kc; Ti=0.083*(1.935*kappa+1)*Tc;
   elseif key==3,
      Kp=0.509*Kc; Td=0.125*Tc;
      Ti=0.051*(3.302*kappa+1)*Tc;
   elseif key==4,
      Kp=(4.437*kappa-1.587)...
         /(8.024*kappa-1.435)*Kc;
      Ti=0.037*(5.89*kappa+1)*Tc;
      Td=0.112*Tc;
   end
end
if key==2, Gc=tf(Kp*[Ti,1],[Ti,0]);
elseif key==3
   nn=[Kp*Ti*Td*(N+1)/N, Kp*(Ti+Td/N), Kp];
   dd=Ti*[Td/N,1,0]; Gc=tf(nn,dd);
elseif key==4
   Gc=tf(Kp*[Ti,1],[Ti,0]);
   nH=[(1+Kp/N)*Ti*Td, Kp*(Ti+Td/N), Kp];
   dH=Kp*conv([Ti,1],[Td/N,1]); H=tf(nH,dH);
end


function [R,eff] = randmio_und_connected(R, ITER)
%RANDMIO_UND_CONNECTED     Random graph with preserved degree distribution
%
%   R = randmio_und_connected(W,ITER);
%   [R eff] = randmio_und_connected(W, ITER);
%
%   This function randomizes an undirected network, while preserving the
%   degree distribution. The function does not preserve the strength
%   distribution in weighted networks. The function also ensures that the
%   randomized network maintains connectedness, the ability for every node
%   to reach every other node in the network. The input network for this
%   function must be connected.
%
%   Input:      W,      undirected (binary/weighted) connection matrix
%               ITER,   rewiring parameter
%                       (each edge is rewired approximately ITER times)
%
%   Output:     R,      randomized network
%               eff,    number of actual rewirings carried out
%
%   References: Maslov and Sneppen (2002) Science 296:910
%
%
%   2007-2012
%   Mika Rubinov, UNSW
%   Jonathan Power, WUSTL
%   Olaf Sporns, IU

%   Modification History:
%   Jun 2007: Original (Mika Rubinov)
%   Apr 2008: Edge c-d is flipped with 50% probability, allowing to explore
%             all potential rewirings (Jonathan Power)
%   Mar 2012: Limit number of rewiring attempts, count number of successful
%             rewirings (Olaf Sporns)

n=size(R,1);
[i,j]=find(tril(R));
K=length(i);
ITER=K*ITER;

% maximal number of rewiring attempts per 'iter'
maxAttempts= round(n*K/(n*(n-1)));
% actual number of successful rewirings
eff = 0;

for iter=1:ITER
    att=0;
    while (att<=maxAttempts)                                     %while not rewired
        rewire=1;
        while 1
            e1=ceil(K*rand);
            e2=ceil(K*rand);
            while (e2==e1)
                e2=ceil(K*rand);
            end
            a=i(e1); b=j(e1);
            c=i(e2); d=j(e2);

            if all(a~=[c d]) && all(b~=[c d])
                break           %all four vertices must be different
            end
        end

        if rand>0.5
            i(e2)=d; j(e2)=c; 	%flip edge c-d with 50% probability
            c=i(e2); d=j(e2); 	%to explore all potential rewirings
        end

        %rewiring condition
        if ~(R(a,d) || R(c,b))
            %connectedness condition
            if ~(R(a,c) || R(b,d))
                P=R([a d],:);
                P(1,b)=0; P(2,c)=0;
                PN=P;
                PN(:,d)=1; PN(:,a)=1;

                while 1
                    P(1,:)=any(R(P(1,:)~=0,:),1);
                    P(2,:)=any(R(P(2,:)~=0,:),1);
                    P=P.*(~PN);
                    if ~all(any(P,2))
                        rewire=0;
                        break
                    elseif any(any(P(:,[b c])))
                        break
                    end
                    PN=PN+P;
                end
            end %connectedness testing

            if rewire               %reassign edges
                R(a,d)=R(a,b); R(a,b)=0;
                R(d,a)=R(b,a); R(b,a)=0;
                R(c,b)=R(c,d); R(c,d)=0;
                R(b,c)=R(d,c); R(d,c)=0;

                j(e1) = d;          %reassign edge indices
                j(e2) = b;
                eff = eff+1;
                break;
            end %edge reassignment
        end %rewiring condition
        att=att+1;
    end %while not rewired
end %iterations


function [vol, labels] = imVolume(img, varargin)
% Volume of regions within a 3D binary or label image.
%
%   V = imVolume(IMG);
%   Compute volume of the image. IMG is either a binary image, or a label
%   image. In the case of a label image, the area of each labeled area is
%   returned in a column vector with as many elements as the number of
%   labels.
%
%   V = imVolume(IMG, SCALE);
%   Also specify scale of image tile. SCALE si a 1-by-3 array, containing
%   voxel size in each direction.
%
%   See Also
%   imVolumeDensity, imSurfaceArea, imMeanBreadth
%

% ------
% Author: David Legland
% e-mail: david.legland@inrae.fr
% Created: 2010-01-15,    using Matlab 7.9.0.529 (R2009b)
% Copyright 2010 INRAE - Cepia Software Platform.

%% Parse input arguments

% check image dimension
if ndims(img) ~= 3
    error('first argument should be a 3D binary or label image');
end

% the labels to compute
labels = [];

% default spatial calibration
delta = [1 1 1];

% parse parameter name-value pairs
while ~isempty(varargin)
    var1 = varargin{1};
    varargin(1) = [];

    if isnumeric(var1)
        % option can be number of directions, spatial calibration, or list
        % of labels
        if all(size(var1) == [1 3])
            % spatial calibration
            delta = var1;
        elseif ~isscalar(var1) && size(var1, 2) == 1
            % list of labels to compute
            labels = var1;
        else
            error('Unable to parse input argument');
        end
    else
        error('Expect numeric input only');
    end
end

%% Process label images

% in case of a label image, return a vector with a set of results
if ~islogical(img)
    % extract labels if necessary (considers 0 as background)
    if isempty(labels)
        labels = imFindLabels(img);
    end

    % allocate memory
    vol = zeros(length(labels), 1);

    % iterate over labels
    for i = 1:length(labels)
        vol(i) = imVolume(img==labels(i), delta);
    end
    return;
end

%% Process binary images

% in case of binary image, compute only one label...
labels = 1;

% compute area, multiplied by image resolution
vol = sum(img(:)) * prod(delta);


function b = isint(x)
%ISINT  True for integers.
%
%   ISINT(X) returns 1's where the elements of X are integers and 0's where
%   they are not.  For example, ISINT([ 2e9 pi 3+5i NaN ]) is [ 1 0 1 0 ].
%
%   See also ISEVEN, ISODD.

%   Author:      Peter J. Acklam
%   Time-stamp:  2003-04-12 14:30:59 +0200
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam

   error(nargchk(1, 1, nargin));
   if ~isnumeric(x)
      error('Argument must be a numeric array.');
   end

   cls = class(x);                      % class of input argument
   if isempty(x)
      b = feval(cls, x);                % return empty array of same class
   else
      switch cls
         case 'double'
            b = x == round(x);
         case 'single'
            % "mod" is not defined for class "single"; so convert input to
            % double, compare, and convert back
            d = double(x);
            b = single(d == round(d));
         case {'int8', 'int16', 'int32', 'int64', ...
               'uint8', 'uint16', 'uint32', 'uint64'}
            % return an array of ones of the same class as input argument
            b = logical(repmat(feval(cls, 1), size(x)));
         otherwise
            error('Argument is of unrecognized class.');
      end
   end


function a = cosh(a)
%COSH         Gradient hyperbolic cosine cosh(a)
%

% written  10/16/98     S.M. Rump
% modified 10/14/00     S.M. Rump  use Tony's trick
% modified 04/04/04     S.M. Rump  set round to nearest for safety
%                                    accelaration for sparse input
% modified 04/06/05     S.M. Rump  rounding unchanged
% modified 09/28/08     S.M. Rump  check for rounding to nearest improved
% modified 10/08/08     S.M. Rump  improved sparse multiplication: not using intval data type
% modified 08/26/12     S.M. Rump  global variables removed
%

  e = 1e-30;
  if 1+e==1-e                           % fast check for rounding to nearest
    rndold = 0;
  else
    rndold = getround;
    setround(0)
  end

  N = getappdata(0,'INTLAB_GRADIENT_NUMVAR');

  ax = sinh(a.x(:));
  a.x = cosh(a.x);
  if issparse(a.dx)
    sizeax = size(a.dx,1);
    [ia,ja,sa] = find(a.dx);
    % take care of scalar a.x: cures Matlab V6.0 bug
    % a=7; i=[1 1]; x=a(i), b=sparse(a); y=b(i)  yields row vector x but column vector y
    ax = ax(ia);
    if isa(a.x,'intval')
      adx = times(ax(:),sa(:),0);
      if adx.complex
        a.dx = intval( sparse(ia,ja,adx.mid,sizeax,N) , sparse(ia,ja,adx.rad,sizeax,N) , 'midrad' );
      else
        a.dx = intval( sparse(ia,ja,adx.inf,sizeax,N) , sparse(ia,ja,adx.sup,sizeax,N) , 'infsup' );
      end
    else
      a.dx = sparse(ia,ja,ax(:).*sa(:),sizeax,N);
    end
  else
    a.dx = a.dx .* ax(:,ones(1,N));
  end

  if rndold
    setround(rndold)
  end


function C = addlongerror(C,err,exponent)
%ADDLONGERROR Add error to long number
%
%  C = addlongerror(A,err,exponent)
%
%Error term of C is updated by err. Third argument optional, default 0.
%If exponent is specified, error term is updated by err*10^exponent
%
%Input C, err and exponent may be column vectors
%

% written  12/30/98     S.M. Rump
% modfied  02/09/00     S.M. Rump  rounding unchanged after use
% modified 04/04/04     S.M. Rump  set round to nearest for safety
% modified 04/06/05     S.M. Rump  rounding unchanged
% modified 11/20/05     S.M. Rump  fast check for rounding to nearest
% modified 01/29/10     S.M. Rump  only warning if no interval arithmetic
% modified 08/26/12     S.M. Rump  global variables removed
%

  INTLAB_LONG_BETA = getappdata(0,'INTLAB_LONG_BETA');
  INTLAB_LONG_ERROR = getappdata(0,'INTLAB_LONG_ERROR');

  if ~INTLAB_LONG_ERROR
    warning('long arithmetic changed to interval arithmetic, see longinit')
    INTLAB_LONG_ERROR = 1;
  end

  if any( err<0 )
    error('negative error specified in addlongerror')
  end

  e = 1e-30;
  if 1+e==1-e                           % fast check for rounding to nearest
    rndold = 0;
  else
    rndold = getround;
    setround(0)
  end

  if nargin==3
    setround(1)
    % 10^exponent = beta^(Ernd+Efrac)
    E = exponent / log10(INTLAB_LONG_BETA);
    Ernd = round(E);
    Efrac = E - Ernd;
    err = err .* INTLAB_LONG_BETA.^Efrac;
    setround(0)                         % set rounding to nearest
  else
    Ernd = 0;
  end
  C.error = errorupdate( 1 , C.error , 0  , 1 , err , Ernd );
  C.error = errornormalize(C.error);

  C = normalize(C);

  setround(rndold)


function z = s2z4(S);

% Z = s2z4(S)
%
% Scattering to Impedance transformation
% only for N-by-4 matrix

%d = (1 - S(:, 1)).*(1 - S(:, 4)) - S(:,2).* S(:, 3);

for i = 1:size(S,1)
  d(i) = (1 - S(i, 1)).*(1 - S(i, 4)) - S(i,2).* S(i, 3);
  while abs(d(i)) < 1e-8
    fckindex = 1+round(rand*3);
    S(i, fckindex) = S(i, fckindex)*(1+rand*1e-8);
    d(i) = (1 - S(i, 1)).*(1 - S(i, 4)) - S(i,2).* S(i, 3);
  end;
end;

d = rot90(d, 3);

% at this point the I-S matrix should be non-singular

z(:, 1) = ((1 + S(:, 1)).*(1 - S(:, 4)) + S(:,2).* S(:, 3))./d;
z(:, 2) = 2* S(:,2)./d;
z(:, 3) = 2* S(:,3)./d;
z(:, 4) = ((1 - S(:, 1)).*(1 + S(:, 4)) + S(:,2).* S(:, 3))./d;


c = 340;                    % Sound velocity (m/s)
fs = 16000;                 % Sample frequency (samples/s)
r = [2 1.5 3 ; 1 1.5 2];    % Receiver positions [x_1 y_1 z_1 ; x_2 y_2 z_2] (m)
s = [2 3.5 2];              % Source position [x y z] (m)
L = [5 4 6];                % Room dimensions [x y z] (m)
beta = 0.4;                 % Reverberation time (s)
n = 4096;                   % Number of samples
mtype = 'omnidirectional';  % Type of microphone
order = -1;                 % -1 equals maximum reflection order!
dim = 3;                    % Room dimension
orientation = 0;            % Microphone orientation (rad)
hp_filter = 1;              % Enable high-pass filter

h = rir_generator(c, fs, r, s, L, beta, n, mtype, order, dim, orientation, hp_filter);


%% brute_force_tune
% Code to test the performance of various tuning parameters
% Works sorta like RANSAC I guess?
% Adam Werries 2016, see Apache 2.0 license.

k_max = 40;
% Specify ranges
init_b_a_unc = linspace(10,3000,100);
init_b_g_unc = linspace(0,500,100);
% Repeat arrays
init_b_a_unc = repmat(init_b_a_unc, [1 k_max]);
init_b_g_unc = repmat(init_b_g_unc, [1 k_max]);
% Generate random selections of each vector
num_items = length(init_b_a_unc);
ba_i = randperm(num_items);
bg_i = randperm(num_items);
rms_error_filter = zeros(1,num_items);
max_error_filter = zeros(1,num_items);
parfor i = 1:num_items
    fprintf('Iteration: %d, aBiasUnc: %08.3f, gBiasUnc: %08.3f\n', i, init_b_a_unc(ba_i(i)), init_b_g_unc(bg_i(i)));
    temp_conf = LC_KF_config;
    temp_conf.init_b_a_unc = init_b_a_unc(ba_i(i)) * mug_to_mps2;
    temp_conf.init_b_g_unc = init_b_g_unc(bg_i(i)) * deg_to_rad / 3600;
    [out_profile,out_IMU_bias_est,out_KF_SD] = Loosely_coupled_INS_GNSS(init_cond, filter_time, epoch, lla, gps, imu, temp_conf, est_IMU_bias);
    xyz = out_profile(:,2:4);
    llh = ecef2lla(xyz);
    [x,y] = deg2utm(llh(:,1),llh(:,2));
    x = x-min_x;
    y = y-min_y;

    distance = ((ground_truth_full(:,1)-x).^2 + (ground_truth_full(:,2)-y).^2).^0.5;
    rms_error_filter(i) = rms(distance);
    max_error_filter(i) = max(distance);
end

[minmax, i] = min(max_error_filter);
fprintf('\nBest max: %08.3f, rms is %08.3f\n', minmax, rms_error_filter(i));
fprintf('Best iteration for max: %d, aBiasUnc: %08.3f, gBiasUnc: %08.3f\n', i, init_b_a_unc(ba_i(i)), init_b_g_unc(bg_i(i)));
[minrms, i] = min(rms_error_filter);
fprintf('Best rms: %08.3f, max is %08.3f\n', minrms, max_error_filter(i));
fprintf('Best iteration for rms: %d, aBiasUnc: %08.3f, gBiasUnc: %08.3f\n', i, init_b_a_unc(ba_i(i)), init_b_g_unc(bg_i(i)));


%% This file will be used to generate the simulation data of
% the Belousov Zhabotinsky reaction.
% Code By: K
% Last Updated: 2019/06/24
%% Clear all
close all;clear all;clc;
%% Add path
[fld_status, fld_msg, fld_msgID]=mkdir('Datas');
[fld_status, fld_msg, fld_msgID]=mkdir('Figures');
addpath('Datas')
addpath('Figures')
addpath('Functions')
%% Define parameters for the simulation
% Define diffusion rate
Dx=0.01;Dz=0.01;Ds=1;Du=1;

% Others
q=0.1;f=1.5;ksi=0.3;alpha=0.3;beta=0.26;gama=0.4;ksi2=1.5;ksi3=0.003;phi=0;

% Define the time horizon
dt=0.001;T=1;
tspan=0:dt:T;

% Define the spatial domain
L=20; % Total length of each dimension
n=128; % Discretization point of each dimension
N=n*n; % Total points used, n^2

x2=linspace(-L/2,L/2,n+1); x=x2(1:n);
y=x;dx=x(2)-x(1);

kx=(2*pi/L)*[0:(n/2-1) -n/2:-1];
ky=kx;

% Get n-dimensional grid
[X,Y]=meshgrid(x,y);
[KX,KY]=meshgrid(kx,ky);
K2=KX.^2+KY.^2; K22=reshape(K2,N,1);
Kx=reshape(KX,N,1);Ky=reshape(KY,N,1);Kxx=reshape(KX,N,1);Kyy=reshape(KY,N,1);

% Create a time matrix
r_t=zeros(n,n,length(tspan));r=zeros(n,n,length(tspan));r_x=zeros(n,n,length(tspan));r_xx=zeros(n,n,length(tspan));r_y=zeros(n,n,length(tspan));r_yy=zeros(n,n,length(tspan));
z_t=zeros(n,n,length(tspan));z=zeros(n,n,length(tspan));z_x=zeros(n,n,length(tspan));z_xx=zeros(n,n,length(tspan));z_y=zeros(n,n,length(tspan));z_yy=zeros(n,n,length(tspan));
s_t=zeros(n,n,length(tspan));s=zeros(n,n,length(tspan));s_x=zeros(n,n,length(tspan));s_xx=zeros(n,n,length(tspan));s_y=zeros(n,n,length(tspan));s_yy=zeros(n,n,length(tspan));
u_t=zeros(n,n,length(tspan));u=zeros(n,n,length(tspan));u_x=zeros(n,n,length(tspan));u_xx=zeros(n,n,length(tspan));u_y=zeros(n,n,length(tspan));u_yy=zeros(n,n,length(tspan));
r_lap=zeros(n,n,length(tspan));z_lap=zeros(n,n,length(tspan));s_lap=zeros(n,n,length(tspan));u_lap=zeros(n,n,length(tspan));
%% Set a Guassian for the initial condition
r0=cos(sqrt(X.^2+Y.^2)).^2+GaussianFilter(X,Y,0.05,0.05,0,0)+GaussianFilter(X,Y,1,0.01,0,0)+GaussianFilter(X,Y,0.01,1,0,0)+GaussianFilter(X,Y,0.25,0.25,5,5)+GaussianFilter(X,Y,0.25,0.25,5,-5)+GaussianFilter(X,Y,0.25,0.25,-5,5)+GaussianFilter(X,Y,0.25,0.25,-5,-5)+0.1;
z0=sin(0.1*sqrt(X.^2+Y.^2)).^2+GaussianFilter(X,Y,0.05,0.01,0,0)+GaussianFilter(X,Y,0.25,0.25,5,5)+GaussianFilter(X,Y,0.25,0.25,5,-5)+GaussianFilter(X,Y,0.25,0.25,-5,5)+GaussianFilter(X,Y,0.25,0.25,-5,-5)+0.1;
s0=sin(sqrt(X.^2+Y.^2)).^2+GaussianFilter(X,Y,0.5,0.5,0,5)+GaussianFilter(X,Y,0.5,0.5,5,0)+GaussianFilter(X,Y,0.5,0.5,0,-5)+GaussianFilter(X,Y,0.5,0.5,-5,0)+0.1;
u0=cos(sqrt(X.^2+Y.^2)).^2+GaussianFilter(X,Y,0.5,0.01,0,0)+GaussianFilter(X,Y,0.01,0.5,0,0)+GaussianFilter(X,Y,0.25,0.25,5,5)+GaussianFilter(X,Y,0.25,0.25,5,-5)+GaussianFilter(X,Y,0.25,0.25,-5,5)+GaussianFilter(X,Y,0.25,0.25,-5,-5)+0.01;

%% Plot the initial conditions
figure(1)
surf(x,y,r0)
set(gca,'FontSize',18);
set(gcf,'Position',[100 100 600 600]);
set(gcf,'PaperPositionMode','auto');
view(-18,35)
box('off')
axis('off')

figure(2)
surf(x,y,z0)
set(gca,'FontSize',18);
set(gcf,'Position',[100 100 600 600]);
set(gcf,'PaperPositionMode','auto');
view(-18,35)
box('off')
axis('off')

figure(3)
surf(x,y,s0)
set(gca,'FontSize',18);
set(gcf,'Position',[100 100 600 600]);
set(gcf,'PaperPositionMode','auto');
view(-18,35)
box('off')
axis('off')

figure(4)
surf(x,y,u0)
set(gca,'FontSize',18);
set(gcf,'Position',[100 100 600 600]);
set(gcf,'PaperPositionMode','auto');
view(-18,35)
box('off')
axis('off')
%%
close all
% Transfer the initial guess to fourier domain
xzsu0t=[reshape(fft2(r0),1,N) reshape(fft2(z0),1,N) reshape(fft2(s0),1,N) reshape(fft2(u0),1,N)].';

tic
opts = odeset('RelTol',1e-12,'AbsTol',1e-13);
NeedDev=0;
[time,xzsusol]=ode45(@(time,xzsut)BZ_Reaction_PDE(time,xzsut,Kx,Kxx,Ky,Kyy,K22,n,N,Dx,Dz,Ds,Du,q,f,ksi,alpha,beta,gama,ksi2,ksi3,phi,NeedDev),tspan,xzsu0t,opts);
toc

%% Extract values
% After simulation, we extract the derivative
NeedDev=1;

for pin=1:length(tspan)
    % Get the derivative data
    [~,r_t(:,:,pin),z_t(:,:,pin),s_t(:,:,pin),u_t(:,:,pin),r(:,:,pin),z(:,:,pin),s(:,:,pin),u(:,:,pin),r_x(:,:,pin),z_x(:,:,pin),s_x(:,:,pin),u_x(:,:,pin),r_y(:,:,pin),z_y(:,:,pin),s_y(:,:,pin),u_y(:,:,pin),r_xx(:,:,pin),z_xx(:,:,pin),s_xx(:,:,pin),u_xx(:,:,pin),r_yy(:,:,pin),z_yy(:,:,pin),s_yy(:,:,pin),u_yy(:,:,pin),r_lap(:,:,pin),z_lap(:,:,pin),s_lap(:,:,pin),u_lap(:,:,pin)]=...
        BZ_Reaction_PDE(0,xzsusol(pin,:).',Kx,Kxx,Ky,Kyy,K22,n,N,Dx,Dz,Ds,Du,q,f,ksi,alpha,beta,gama,ksi2,ksi3,phi,NeedDev);
end
%% Test out whehter the derivative is correct here
for pin=2:length(tspan)
    % Test out the zt (All time derivative tested, the Fourier derivative result matches the finite differences result)
    %     u_t_dum(:,:,pin)=(u(:,:,pin)-u(:,:,pin-1))/dt;
    %     figure(1)
    %     clf
    %     surf(x,y,u_t_dum(:,:,pin))
    %
    %     figure(2)
    %     clf
    %     surf(x,y,u_t(:,:,pin))
    %
    %     Dum=u_t_dum(:,:,pin)-u_t(:,:,pin);
    %     norm(Dum(:))

    % Test out the r_x, r_y (All tested, the answer matches)
    %     figure(1)
    %     clf
    %     surf(x,y,u_x(:,:,pin))
    %
    %     for kk=1:n
    %        Dum(kk,:)=CalDerivative(u(kk,:,pin),dx,1);
    %     end
    %
    %     figure(2)
    %     clf
    %     surf(x,y,Dum)
    %
    %     figure(3)
    %     clf
    %     surf(x,y,u_y(:,:,pin))
    %
    %     for kk=1:n
    %        Dum(:,kk)=CalDerivative(u(:,kk,pin),dx,1);
    %     end
    %
    %     figure(4)
    %     clf
    %     surf(x,y,Dum)

    % Test r_xx (All tested, the results matches)
    %     figure(1)
    %     clf
    %     surf(x,y,u_xx(:,:,pin))
    %
    %     for kk=1:n
    %         Dum_x(kk,:)=CalDerivative(u(kk,:,pin),dx,1);
    %     end
    %
    %     for kk=1:n
    %         Dum_xx(kk,:)=CalDerivative(Dum_x(kk,:),dx,1);
    %     end
    %
    %     figure(2)
    %     clf
    %     surf(x,y,Dum_xx)
    %
    %     figure(3)
    %     clf
    %     surf(x,y,u_yy(:,:,pin))
    %
    %     for kk=1:n
    %         Dum_y(:,kk)=CalDerivative(u(:,kk,pin),dx,1);
    %     end
    %
    %     for kk=1:n
    %         Dum_yy(:,kk)=CalDerivative(Dum_y(:,kk),dx,1);
    %     end
    %
    %     figure(4)
    %     clf
    %     surf(x,y,Dum_yy)
    %
    %     figure(5)
    %     clf
    %     surf(x,y,u_lap(:,:,pin))
    %
    %     figure(6)
    %     clf
    %     surf(x,y,Dum_xx+Dum_yy)
    %
    %     figure(7)
    %     clf
    %     surf(x,y,u_xx(:,:,pin)+u_yy(:,:,pin))
end

%% Plot the result and save it to file
%Creat Movie
Save_Animation=1;

States=['r','z','s','u'];
for pin=1:4
    if pin==1
        Animate=r;
        limits=[0 1];
    elseif pin==2
        Animate=z;
        limits=[0 1];
    elseif pin==3
        Animate=s;
        limits=[0 1];
    elseif pin==4
        Animate=u;
        limits=[0 1];
    end
    if Save_Animation==1
        name=strcat('BZ_Diffusion_',States(pin),'_',num2str(T),'_Seconds');
        writerObj = VideoWriter(name,'MPEG-4'); % Name it.
        writerObj.FrameRate = 20; % How many frames per second.
        writerObj.Quality = 100;
        open(writerObj);
        for j=1:15:length(tspan)
            clf
            pcolor(x,y,Animate(:,:,j))
            shading interp
            colormap(parula)
            caxis(limits)
            colorbar
            %Make plot propotional
            set(gca,'FontSize',18);
            set(gcf,'Position',[50 50 1000 1000]);
            set(gcf,'PaperPositionMode','auto');
            set(gca,'DataAspectRatio',[1 1 1])
            drawnow limitrate
            Mov=getframe(gcf);
            writeVideo(writerObj,Mov);
        end
        % Close the object or you can't read the final file correctly
        close(writerObj)
    end
end

%% Save files
Save_File=1;
if Save_File==1
    save(strcat('Datas\Simulation_BZ_',num2str(T),'_Seconds_',num2str(n),'_Grid.mat'),'r_t','z_t','s_t','u_t','r','z','s','u',...
        'r_x','z_x','s_x','u_x','r_y','z_y','s_y','u_y','r_xx','z_xx','s_xx','u_xx',...
        'r_yy','z_yy','s_yy','u_yy','dt','T','tspan','dx','x')
end