stdlib_math_diff.fypp Source File


Source Code

!> Inspired by original code (MIT license) written in 2016 by Keurfon Luu (keurfonluu@outlook.com)
!> https://github.com/keurfonluu/Forlab

#:include "common.fypp"
#:set RI_KINDS_TYPES = REAL_KINDS_TYPES + INT_KINDS_TYPES
submodule (stdlib_math) stdlib_math_diff

    implicit none

contains

    !> `diff` computes differences of adjacent elements of an array.
    
    #:for k1, t1 in RI_KINDS_TYPES
    pure module function diff_1_${k1}$(x, n, prepend, append) result(y)
        ${t1}$, intent(in) :: x(:)
        integer, intent(in), optional :: n
        ${t1}$, intent(in), optional :: prepend(:), append(:)
        ${t1}$, allocatable :: y(:)
        integer :: size_prepend, size_append, size_x, size_work
        integer :: n_, i

        n_ = optval(n, 1)
        if (n_ <= 0) then
            y = x
            return
        end if
        
        size_prepend = 0
        size_append = 0
        if (present(prepend)) size_prepend = size(prepend) 
        if (present(append)) size_append = size(append)
        size_x = size(x)
        size_work = size_x + size_prepend + size_append
        
        if (size_work <= n_) then
            allocate(y(0))
            return
        end if

        !> Use a quick exit for the common case, to avoid memory allocation.
        if (size_prepend == 0 .and. size_append == 0 .and. n_ == 1) then
            y = x(2:) - x(1:size_x-1)
            return
        end if

        block
        ${t1}$ :: work(size_work)
        if (size_prepend > 0) work(:size_prepend) = prepend
        work(size_prepend+1:size_prepend+size_x) = x
        if (size_append > 0) work(size_prepend+size_x+1:) = append
        
        do i = 1, n_
            work(1:size_work-i) = work(2:size_work-i+1) - work(1:size_work-i)
        end do

        y = work(1:size_work-n_)
        end block

    end function diff_1_${k1}$

    pure module function diff_2_${k1}$(x, n, dim, prepend, append) result(y)
        ${t1}$, intent(in) :: x(:, :)
        integer, intent(in), optional :: n, dim
        ${t1}$, intent(in), optional :: prepend(:, :), append(:, :)
        ${t1}$, allocatable :: y(:, :)
        integer :: size_prepend, size_append, size_x, size_work
        integer :: n_, dim_, i

        n_ = optval(n, 1)
        if (n_ <= 0) then
            y = x
            return
        end if
        
        size_prepend = 0
        size_append = 0
        if (present(dim)) then
            if (dim == 1 .or. dim == 2) then
                dim_ = dim
            else
                dim_ = 1
            end if
        else
            dim_ = 1
        end if
        
        if (present(prepend)) size_prepend = size(prepend, dim_)
        if (present(append)) size_append = size(append, dim_)
        size_x = size(x, dim_)
        size_work = size_x + size_prepend + size_append
        
        if (size_work <= n_) then
            allocate(y(0, 0))
            return
        end if
        
        !> Use a quick exit for the common case, to avoid memory allocation.
        if (size_prepend == 0 .and. size_append == 0 .and. n_ == 1) then
            if (dim_ == 1) then
                y = x(2:, :) - x(1:size_x-1, :)
            elseif (dim_ == 2) then
                y = x(:, 2:) - x(:, 1:size_x-1)
            end if
            return
        end if
        
        if (dim_ == 1) then
            block
            ${t1}$ :: work(size_work, size(x, 2))
            if (size_prepend > 0) work(1:size_prepend, :) = prepend
            work(size_prepend+1:size_x+size_prepend, :) = x
            if (size_append > 0) work(size_x+size_prepend+1:, :) = append
            do i = 1, n_
                work(1:size_work-i, :) = work(2:size_work-i+1, :) - work(1:size_work-i, :)
            end do

            y = work(1:size_work-n_, :)
            end block
            
        elseif (dim_ == 2) then
            block
            ${t1}$ :: work(size(x, 1), size_work)
            if (size_prepend > 0) work(:, 1:size_prepend) = prepend
            work(:, size_prepend+1:size_x+size_prepend) = x
            if (size_append > 0) work(:, size_x+size_prepend+1:) = append
            do i = 1, n_
                work(:, 1:size_work-i) = work(:, 2:size_work-i+1) - work(:, 1:size_work-i)
            end do
            
            y = work(:, 1:size_work-n_)
            end block
            
        end if

    end function diff_2_${k1}$
    #:endfor

end submodule stdlib_math_diff