module disp_m

    use, intrinsic :: iso_fortran_env, only: output_unit, sp => real32, dp => real64
    implicit none
    private

    public :: disp

contains

    !> Displays dataset x
    subroutine disp(x, header, fmt, unit)
        class(*), intent(in) :: x(..)                       !! Dataset to be displayed
        character(len=*), intent(in), optional :: header    !! Header of the dataset
        character(len=*), intent(in), optional :: fmt       !! Format of the dataset
        integer, intent(in), optional :: unit               !! File unit to outputted
        character(:), allocatable :: fmt_
        integer :: unit_, i

        if (present(fmt)) then
            fmt_ = '(*('//fmt//',:,", "))'
        else
            fmt_ = '(*(g0.4,:,", "))'
        end if

        if (present(unit)) then
            unit_ = unit
        else
            unit_ = output_unit
        end if

        if (present(header)) write (unit_, '(a)') header

        select rank (x)
        rank (0)
            select type (x)
            type is (real(sp)); write (unit_, fmt_) x
            type is (real(dp)); write (unit_, fmt_) x
            type is (integer); write (unit_, fmt_) x
            type is (logical); write (unit_, fmt_) x
            end select

        rank (1)
            select type (x)
            type is (real(sp)); write (unit_, fmt_) x(:)
            type is (real(dp)); write (unit_, fmt_) x(:)
            type is (integer); write (unit_, fmt_) x(:)
            type is (logical); write (unit_, fmt_) x(:)
            end select

        rank (2)
            do i = 1, size(x, 2)
                select type (x)
                type is (real(sp)); write (unit_, fmt_) x(:, i)
                type is (real(dp)); write (unit_, fmt_) x(:, i)
                type is (integer); write (unit_, fmt_) x(:, i)
                type is (logical); write (unit_, fmt_) x(:, i)
                end select
            end do
        end select

    end subroutine disp

end module disp_m

program main

    use disp_m, only: disp
    
    integer :: i(5) = [110, 2, 45678910, 10, 45]
    logical :: l = .true.
    call disp(i, "i = ", "i10")
    call disp(spread(i+10, dim=1, ncopies=3), "spread(i, dim=1, ncopies=3) = ", "i0")
    call disp(real(spread(i-11, dim=1, ncopies=3)), "real(spread(i, dim=1, ncopies=3)) = ", "g11.4")
    call disp(l, ".true. = ")

end program main
