Pack using overloaded operator - fortran

I am trying to use the pack function on an array of custom types. I have set up a small module with the type and interface to overload .eq.. If I do a simple comparison the overloaded operator appears to work, however when used in the context of a pack function I get an error.
!The module
module m_types
type :: t_property
character(12) :: key
logical :: value
end type t_property
type(t_property), allocatable, dimension(:) :: properties
public :: operator(.eq.)
interface operator(.eq.)
procedure prop_eq
end interface operator(.eq.)
contains
pure function prop_eq(first, second) result(res)
type(t_property), intent(in) :: first, second
logical :: res
if (first%key .eq. second%key) then
res = .true.
else
res = .false.
end if
end function prop_eq
end module m_types
! The test program
program textadventure
use m_types
implicit none
type(t_property) :: temp
allocate(properties(0))
temp = t_property(key="lit", value=.true.)
properties = [properties, temp]
temp = t_property(key="visited", value=.false.)
properties = [properties, temp]
temp = t_property(key="lit", value=.false.)
properties = [properties, temp]
temp = t_property(key="cold", value=.false.)
properties = [properties, temp]
temp = t_property(key="cold", value=.false.)
properties = [properties, temp]
print *, properties
print *, (properties(4) .eq. temp) ! Succeeds
print *, size(pack(properties, properties .eq. temp)) ! Fails
deallocate(properties)
end program textadventure
GCC error message
Error: Operands of comparison operator ‘.eq.’ at (1) are TYPE(t_property)/TYPE(t_property)
The spec says the mask in PACK should be a logical scalar which I thought I had provided - can someone point out where I am wrong?

In the comparison for the mask, properties .eq. temp you have two objects of type(t_property) and you hope to use the function prop_eq to provide that defined operation.
However, the first operand properties is an array, and the first dummy argument of prop_eq is a scalar. As a result, there is no defined operator .eq. available. You should provide a function to process an array first argument. One way would be to make prop_eq elemental.
Returning an array result from .eq. is appropriate: the mask= argument to PACK should conformable with the array argument (and you don't want it to be scalar).
In the successful comparison properties(4) .eq. temp the first operand is scalar.

The corrected code for those interested, it now packs as expected.
module m_types
type :: t_property
character(12) :: key
logical :: value
end type t_property
type(t_property), allocatable, dimension(:) :: properties
public :: operator(.eq.)
interface operator(.eq.)
procedure prop_eq
end interface operator(.eq.)
contains
function prop_eq(first, second) result(res)
type(t_property), intent(in) :: second
type(t_property), intent(in), dimension(:) :: first
logical, dimension(:), allocatable :: res
integer :: i
allocate(res(0))
do i=1, size(first)
if (first(i)%key .eq. second%key) then
res = [res, .true.]
else
res = [res, .false.]
end if
end do
end function prop_eq
end module m_types
program textadventure
use m_types
implicit none
type(t_property) :: temp
allocate(properties(0))
temp = t_property(key="cold", value=.false.)
properties = [properties, temp]
temp = t_property(key="lit", value=.true.)
properties = [properties, temp]
temp = t_property(key="visited", value=.false.)
properties = [properties, temp]
temp = t_property(key="lit", value=.false.)
properties = [properties, temp]
temp = t_property(key="cold", value=.false.)
properties = [properties, temp]
temp = t_property(key="cold", value=.false.)
properties = [properties, temp]
print *, size(properties)
print *,
print *, pack(properties, mask = properties .eq. temp)
deallocate(properties)
end program textadventure

Related

Errors #6259, #7715, #6355, #6303 in defining Derived Type specific assignment/operator procedures

First of all, Hi to everyone ! Wish you all a good starting week :)
In enhancing (as well as simplifying some syntax) a previously developed Derived Type, I was (and some still) encountering some errors as in the title specification (NOTE: for details about full messages and their cause, please read the comment lines in the code below :) ).
Among them, the ones I was not understand the most (of the why I am getting them) are error #6355: This binary operation is invalid for this data type. for the operator(==), and error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands. for the assignment(=).
Here is an example of source code (NOTE: in order to reproduce those errors, comment out the type-bound procedure part, and uncomment out the two interface bodies):
module MPolicyMod
implicit none
private
public :: MPolicy
type, public :: MPolicy_t
real(kind = 8) :: delta_fI_fct_
real(kind = 8) :: delta_fJ_fct_
real(kind = 8) :: interp_I_fct_
real(kind = 8) :: interp_J_fct_
integer :: id_pol_
contains
generic :: assignment(=) => MPolicy_fromPol_sub, MPolicy_fromID_sub
procedure, private, pass :: MPolicy_fromID_sub, MPolicy_fromPol_sub
generic :: operator(==) => MPolicy_isID
procedure, pass, private :: MPolicy_isID
end type MPolicy_t
integer, public, parameter :: MPolicy_NULL = 0
integer, public, parameter :: MPolicy_DEF = 1
integer, public, parameter :: MPolicy_CONST = 2
interface MPolicy_t
module procedure MPolicy_constructor_integer
module procedure MPolicy_constructor_real
module procedure MPolicy_fromID
end interface
! interface assignment(=)
! module procedure MPolicy_fromPol_sub
! module procedure MPolicy_fromID_sub
! end interface assignment(=)
! interface operator(==)
! module procedure MPolicy_isID
! end interface operator(==)
contains
!> Main default constructor.
function MPolicy_constructor_real(dfi, dfj, interpi, interpj, id) result(pol)
real(kind = 8), intent(in) :: dfi, dfj, interpi, interpj
integer, intent(in) :: id
type(MPolicy_t) :: pol
print *, ' Default constructor (as compiler)...'
pol%delta_fI_fct_ = dfi
pol%delta_fJ_fct_ = dfj
pol%interp_I_fct_ = interpi
pol%interp_J_fct_ = interpj
pol%id_pol_ = id
end function MPolicy_constructor_real
function MPolicy_constructor_integer(dfi, dfj, interpi, interpj, id) result(pol)
integer, intent(in) :: dfi, dfj, interpi, interpj, id
type(MPolicy_t) :: pol
print *, ' Default constructor (integer version)...'
pol = MPolicy_t(real(dfi, 8), &
real(dfj, 8), &
real(interpi, 8), &
real(interpj, 8), id)
end function
function MPolicy_fromID(id) result(pol)
integer, intent(in) :: id
type(MPolicy_t) :: pol
print *, ' Constructor from ID...'
select case (id)
case (MPolicy_NULL)
return
case (MPolicy_DEF)
pol = MPolicy_t(2, 2, 2, 2, MPolicy_DEF)
case (MPolicy_CONST)
pol = MPolicy_t(1, 1, 1, 1, MPolicy_CONST)
case default
block
character(len=3) :: fmt
write(fmt, '(i)'), id
stop ' [ERROR] Unknow policy identifier "id"= '//fmt
end block
end select
end function MPolicy_fromID
subroutine MPolicy_fromPol_sub(lhs, rhs)
class(MPolicy_t), intent(in) :: rhs
class(MPolicy_t), intent(out) :: lhs
lhs%delta_fI_fct_ = rhs%delta_fI_fct_
lhs%delta_fJ_fct_ = rhs%delta_fJ_fct_
lhs%interp_I_fct_ = rhs%interp_I_fct_
lhs%interp_J_fct_ = rhs%interp_J_fct_
lhs%id_pol_ = rhs%id_pol_
end subroutine MPolicy_fromPol_sub
subroutine MPolicy_fromID_sub(lhs, rhs)
class(MPolicy_t), intent(out) :: lhs
integer, intent(in) :: rhs
lhs = MPolicy_t(rhs)
end subroutine MPolicy_fromID_sub
function MPolicy(mpol) result(pol)
integer :: mpol
type(MPolicy_t) :: pol
pol = MPolicy_t(mpol)
end function MPolicy
pure function MPolicy_isID(pol, id) result(isID)
class(MPolicy_t), intent(in) :: pol
integer, intent(in) :: id
logical :: isID
isID = pol%id_pol_ == id
end function MPolicy_isID
end module
Example program:
program test
use MPolicyMod
implicit none
! ! NOTE: generates
! ! error #6259: This array or function or substring is invalid in constant expressions. [MPOLICYMOD^MPOLICY_FROMID]
! ! error #7715: Replacing invalid structure constructor by integer 1. [<NULL_STRING>]
! type(MPolicy_t) :: pol = MPolicy_t(MPolicy_NULL)
type(MPolicy_t) :: pol
! =========================================================================
print *, pol
pol = MPolicy_t(MPolicy_NULL)
print *, pol
! ! NOTE: generates (without TYPE-BOUND specification)
! ! error #6355: This binary operation is invalid for this data type.
print *, pol == MPolicy_NULL
pol = MPolicy_t(1, 1, 1, 1, 2)
print *, pol
! ! NOTE: generates (without TYPE-BOUND specification)
! ! error #6303: The assignment operation or the binary expression operation is invalid for the data types of the two operands.
pol = MPolicy_DEF
print *, pol
end program test
Then, after reading through the ISO Fortran Standard 2003 Draft document, at Section 4.5.4 Type-bound procedures, Rule R452 states that we could specify assignment(=) as a generic-spec, in a generic-binding type of proc-binding-stmt. There I realised I was missing the type-bound specifications for those operator/assignment procedures.
Why, then, only the interface body was not enough to make it work?
After this, I also wonder why the initialisation statement type(MPolicy_t) :: pol = MPolicy_t(MPolicy_NULL) generates errors #6259 and #7715.
For that, I still didn't search by my own. But if you come with an asnwer before I might find (hopefully) an explanation, it's always good to have constructive as well as polite discussions with You all.
I now go back to my searching. Have a nice day :)

Ambiguous reference to function error when compiling

So I have a fortran .f90 file (part of the Monte-Carlo tool called SWEEP) that basically contains two main modules for the implementation of a variable length list using two types. The two modules are as follows:
Module LIST_DP_T
Implicit None
Public
Type LIST_DP
Integer :: Count = 0
Double Precision, Pointer :: Items(:)
End Type
Interface Assignment(=)
Module Procedure SetP
End Interface
Interface Operator(+)
Module Procedure Add
Module Procedure AddItem
Module Procedure AddItems
End Interface
Contains
! -------------------------------------------------------
Subroutine Destroy(list)
! DESCRIPTION
! Clears memory associated with a list type.
! ARGUMENTS.
Type(LIST_DP), Intent(INOUT) :: list
! VARIABLES.
Integer :: err = 0
! EXECUTABLE CODE.
list%Count = 0
Deallocate(list%Items, STAT=err)
Nullify(list%Items)
End Subroutine
! -------------------------------------------------------
Subroutine SetP(new, old)
! DESCRIPTION
! Assigns the list of one list type to another
! by passing the list pointer.
! ARGUMENTS.
Type(LIST_DP), Intent(INOUT) :: new
Type(LIST_DP), Intent(IN) :: old
! EXECUTABLE CODE.
new%Count = old%Count
new%Items => old%Items
End Subroutine
! -------------------------------------------------------
Subroutine Copy(new, old)
! DESCRIPTION
! Copies the list of one list type to another.
! ARGUMENTS.
Type(LIST_DP), Intent(INOUT) :: new
Type(LIST_DP), Intent(IN) :: old
! EXECUTABLE CODE.
new%Count = old%Count
new%Items = old%Items
End Subroutine
! -------------------------------------------------------
Function Add(list1, list2) Result (sumlist)
! DESCRIPTION
! Adds together two list and
! returns a new list.
! ARGUMENTS.
Type(LIST_DP), Intent(IN) :: list1
Type(LIST_DP), Intent(IN) :: list2
Type(LIST_DP) :: sumlist
! VARIABLES.
Integer :: err = 0
Double Precision :: items(list1%Count+list2%Count)
! EXECUTABLE CODE.
! Save current list.
If (list1%Count > 0) items(1:list1%Count) = list1%Items
If (list2%Count > 0) items(list1%Count+1:) = list2%Items
! Resize list.
Deallocate(sumlist%Items, STAT=err)
sumlist%Count = list1%Count + list2%Count
Allocate(sumlist%Items(sumlist%Count), STAT=err)
! Assign back the list.
sumlist%Items = items
End Function
! -------------------------------------------------------
Function AddItem(list, item) Result (newlist)
! DESCRIPTION
! Adds a number to the end of the list and
! returns a new list.
! ARGUMENTS.
Type(LIST_DP), Intent(IN) :: list
Double Precision, Intent(IN) :: item
Type(LIST_DP) :: newlist
! VARIABLES.
Integer :: err = 0
Double Precision :: items(list%Count+1)
! EXECUTABLE CODE.
! Save current list.
If (list%Count > 0) items(1:list%Count) = list%Items
items(list%Count+1) = item
! Resize list.
Deallocate(newlist%Items, STAT=err)
newlist%Count = list%Count + 1
Allocate(newlist%Items(newlist%Count), STAT=err)
! Assign back the list.
newlist%Items = items
End Function
! -------------------------------------------------------
Function AddItems(list, items) Result (newlist)
! DESCRIPTION
! Adds some numbers to the end of the list and
! returns a new list.
! ARGUMENTS.
Type(LIST_DP), Intent(IN) :: list
Double Precision, Intent(IN) :: items(:)
Type(LIST_DP) :: newlist
! VARIABLES.
Integer :: err = 0
Double Precision :: allitems(list%Count+Size(items))
! EXECUTABLE CODE.
! Save current list.
If (list%Count > 0) allitems(1:list%Count) = list%Items
allitems(list%Count+1:) = items
! Resize list.
Deallocate(newlist%Items, STAT=err)
newlist%Count = list%Count + Size(items)
Allocate(newlist%Items(newlist%Count), STAT=err)
! Assign back the list.
newlist%Items = allitems
End Function
! -------------------------------------------------------
Double Precision Function SumList(list)
! DESCRIPTION
! Returns the sum of the list items.
Type(LIST_DP), Intent(IN) :: list
SumList = Sum(list%Items)
End Function
End Module
and the other module which is for an integer list:
Module LIST_INT_T
Implicit None
Public
Type LIST_INT
Integer :: Count = 0
Integer, Pointer :: Items(:)
End Type
Interface Assignment(=)
Module Procedure SetP
End Interface
Interface Operator(+)
Module Procedure Add
Module Procedure AddItem
Module Procedure AddItems
End Interface
Contains
! -------------------------------------------------------
Subroutine Destroy(list)
! DESCRIPTION
! Clears memory associated with a list type.
! ARGUMENTS.
Type(LIST_INT), Intent(INOUT) :: list
! VARIABLES.
Integer :: err = 0
! EXECUTABLE CODE.
list%Count = 0
Deallocate(list%Items, STAT=err)
Nullify(list%Items)
End Subroutine
! -------------------------------------------------------
Subroutine SetP(new, old)
! DESCRIPTION
! Assigns the list of one list type to another
! by passing the list pointer.
! ARGUMENTS.
Type(LIST_INT), Intent(INOUT) :: new
Type(LIST_INT), Intent(IN) :: old
! EXECUTABLE CODE.
new%Count = old%Count
new%Items => old%Items
End Subroutine
! -------------------------------------------------------
Subroutine Copy(new, old)
! DESCRIPTION
! Copies the list of one list type to another.
! ARGUMENTS.
Type(LIST_INT), Intent(INOUT) :: new
Type(LIST_INT), Intent(IN) :: old
! EXECUTABLE CODE.
new%Count = old%Count
new%Items = old%Items
End Subroutine
! -------------------------------------------------------
Function Add(list1, list2) Result (sumlist)
! DESCRIPTION
! Adds together two list and
! returns a new list.
! ARGUMENTS.
Type(LIST_INT), Intent(IN) :: list1
Type(LIST_INT), Intent(IN) :: list2
Type(LIST_INT) :: sumlist
! VARIABLES.
Integer :: err = 0
Integer :: items(list1%Count+list2%Count)
! EXECUTABLE CODE.
! Save current list.
If (list1%Count > 0) items(1:list1%Count) = list1%Items
If (list2%Count > 0) items(list1%Count+1:) = list2%Items
! Resize list.
Deallocate(sumlist%Items, STAT=err)
sumlist%Count = list1%Count + list2%Count
Allocate(sumlist%Items(sumlist%Count), STAT=err)
! Assign back the list.
sumlist%Items = items
End Function
! -------------------------------------------------------
Function AddItem(list, item) Result (newlist)
! DESCRIPTION
! Adds a number to the end of the list and
! returns a new list.
! ARGUMENTS.
Type(LIST_INT), Intent(IN) :: list
Integer, Intent(IN) :: item
Type(LIST_INT) :: newlist
! VARIABLES.
Integer :: err = 0
Integer :: items(list%Count+1)
! EXECUTABLE CODE.
! Save current list.
If (list%Count > 0) items(1:list%Count) = list%Items
items(list%Count+1) = item
! Resize list.
Deallocate(newlist%Items, STAT=err)
newlist%Count = list%Count + 1
Allocate(newlist%Items(newlist%Count), STAT=err)
! Assign back the list.
newlist%Items = items
End Function
! -------------------------------------------------------
Function AddItems(list, items) Result (newlist)
! DESCRIPTION
! Adds some numbers to the end of the list and
! returns a new list.
! ARGUMENTS.
Type(LIST_INT), Intent(IN) :: list
Integer, Intent(IN) :: items(:)
Type(LIST_INT) :: newlist
! VARIABLES.
Integer :: err = 0
Integer :: allitems(list%Count+Size(items))
! EXECUTABLE CODE.
! Save current list.
If (list%Count > 0) allitems(1:list%Count) = list%Items
allitems(list%Count+1:) = items
! Resize list.
Deallocate(newlist%Items, STAT=err)
newlist%Count = list%Count + Size(items)
Allocate(newlist%Items(newlist%Count), STAT=err)
! Assign back the list.
newlist%Items = allitems
End Function
! -------------------------------------------------------
Integer Function SumList(list)
! DESCRIPTION
! Returns the sum of the list items.
Type(LIST_INT), Intent(IN) :: list
SumList = Sum(list%Items)
End Function
End Module
and a third module to make things easier when using the modules:
Module LIST_T
Use LIST_DP_T
Use LIST_INT_T
Implicit None
Public
End Module
The module LIST_T is then called from in a different module called Profile_Driver_Input located a different .f90 file (profile_driver_input.f90) as follows:
Module Profile_Driver_Input
Use LIST_T
Use Sweep
Implicit None
Public
Integer, Parameter :: UIN = 102
Integer, Parameter :: MAX_POINTS=1000, MAX_VARS=200
Type Settings
! Control parameters.
Logical :: Solve=.True., PostProcess=.True.
! Input files.
Character(LEN=200) :: MechFile="" ! Particle mechanism file name.
Character(LEN=200) :: ChemFile="" ! Chemistry profile file name.
! Time stepping variables.
Type(LIST_DP) :: Times ! Times between PSL outputs (first entry is start time).
Type(LIST_INT) :: Steps ! Steps between output times.
....
The code then continues to declare more variables and subroutines.
Upon compilation, the following error appears at the end of the Profile_Driver_Input module:
Error: Name 'additem' at (1) is an ambiguous reference to 'additem' from module 'list_dp_t'
Error: Name 'setp' at (1) is an ambiguous reference to 'setp' from module 'list_dp_t'
Error: Name 'setp' at (1) is an ambiguous reference to 'setp' from module 'list_dp_t'
Error: Name 'additem' at (1) is an ambiguous reference to 'additem' from module 'list_dp_t'
Error: Name 'setp' at (1) is an ambiguous reference to 'setp' from module 'list_dp_t'
Error: Name 'setp' at (1) is an ambiguous reference to 'setp' from module 'list_dp_t'
Error: Name 'additem' at (1) is an ambiguous reference to 'additem' from module 'list_dp_t'
Error: Name 'setp' at (1) is an ambiguous reference to 'setp' from module 'list_dp_t'
I cannot seem to figure out how to resolve this issue. Any help/advice would be appreciated.
You have multiple symbols that are named the same way. They come from different modules, but it is still a problem. For example, a subroutine named additem exists in both modules you show.
If you do not actually need to call the subroutines themselves and you only want to call the overloaded operators (+) and assignments (=), you can change the default accessibility from public to private inside the module and only leave public what actually needs to be accessible.
If both additem and destroy and other subroutines will need to be accessible from both modules at the same time, you will have to rename them to be unique. For example: additem_list_int and additem_list_dp.

How to choose a desired code for a Subroutine among various options during the start of a run

Let's say I want to perform an operation in my main program (in Fortran). And lets say that operation is finding minimum number in a 1D array. I wish to do so by passing the array into the call subroutine and the subroutine will print the minimum value on the screen. There are different ways or algorithms to find minimum value in an array. Lets say I have 100 different methods: Method1, Method2..... Method100. Now I want to try using each one of these methods separately (I don't want to try all of them at once, but one method in each run). I don't want to create 100 different subroutines and change the code every time to decide which one to call, rather I want to mention in the input file which one I want to choose. So basically, the computer has to read the input file (to know which method to use) and perform the task using the specified method amongst different methods available.
I can write a Subroutine dump all the methods into that subroutine and put an IF condition to choose among various methods. But IF conditions are in efficient particularly on GPUs, I want to know the most efficient way of doing this.
MAIN PROGRAM
INTEGER Method !will be read from input file
Array = [12,5,3,4,1,7,4,3]
call print_Minimum(Array)
END PROGRAM
SUBROUTINE print_Minimum(Array)
IF (METHOD == 1)
<method 1 code>
ELSE IF (METHOD == 2)
<method 2 code>
:
:
:
:
ELSE IF (METHOD == 100)
<method100 code>
END IF
END SUBROUTINE
Thanks in advance.
This is probably best done using a function pointer and/or functions as arguments.
You can set a function pointer to a certain function and do this in your nested ifs and you can pass functions as arguments.
Both methods are implemented in the following example.
module minimum_mod
implicit none
private
public :: get_min_t, naive_min, time_min_function
abstract interface
integer pure function get_min_t(X)
integer, intent(in) :: X(:)
end function
end interface
contains
subroutine time_min_function(f, X)
procedure(get_min_t) :: f
integer, intent(in) :: X(:)
integer :: res
res = f(X)
write(*, *) res
end subroutine
integer pure function naive_min(X)
integer, intent(in) :: X(:)
integer :: i
naive_min = huge(naive_min)
do i = 1, size(X)
naive_min = min(naive_min, X(i))
end do
end function
end module
program time_min_finders
use minimum_mod, only: get_min_t, naive_min, time_min_function
implicit none
integer, parameter :: test_set(5) = [1, 10, 3, 5, 7]
procedure(get_min_t), pointer :: f
f => naive_min
call time_min_function(f, test_set)
end program
PS: Note that you can now do all the timinig logic inside time_min_function.
You can create an array of a derived type that contains a function pointer, effectively an array of function pointers. Then in principle you could initialize the function pointers to point at all of your test functions so that you could refer to each function by its index without having to test with a SELECT CASE of IF block: this is the typical Fortran way. However, either I've got the syntax for initialization wrong or my old version of gfortran just isn't capable, so I had to initialize one at a time. Sigh.
module minfuncs
implicit none
abstract interface
function func(array)
integer, intent(in) :: array(:)
integer func
end function func
end interface
type func_node
procedure(func), NOPASS, pointer :: f
end type func_node
! type(func_node) :: method(5) = [func_node(min_1),func_node(min_2), &
! func_node(min_3),func_node(min_4),func_node(min_5)]
contains
function min_1(array)
integer, intent(in) :: array(:)
integer min_1
integer i
min_1 = array(1)
do i = 2, size(array)
min_1 = min(min_1,array(i))
end do
end function min_1
function min_2(array)
integer, intent(in) :: array(:)
integer min_2
integer i
min_2 = array(1)
do i = 8, size(array), 7
min_2 = min(min_2,array(i-6),array(i-5),array(i-4), &
array(i-3),array(i-2),array(i-1),array(i))
end do
do i = i-6, size(array)
min_2 = min(min_2, array(i))
end do
end function min_2
function min_3(array)
integer, intent(in) :: array(:)
integer min_3
integer i
min_3 = array(1)
do i = 2, size(array)
min_3 = min_3-dim(min_3,array(i))
end do
end function min_3
function min_4(array)
integer, intent(in) :: array(:)
integer min_4
integer ymm(8)
integer i
if(size(array) >= 8) then
ymm = array(1:8)
do i = 16, size(array), 8
ymm = min(ymm,array(i-7:i))
end do
min_4 = minval([ymm,array(i-7:size(array))])
else
min_4 = minval(array)
end if
end function min_4
function min_5(array)
integer, intent(in) :: array(:)
integer min_5
min_5 = minval(array)
end function min_5
end module minfuncs
program test
use minfuncs
implicit none
integer, parameter :: N = 75
integer i
integer :: A(N) = modulo(5*[(i,i=1,N)]**2,163)
! type(func_node) :: method(5) = [func_node(min_1),func_node(min_2), &
! func_node(min_3),func_node(min_4),func_node(min_5)]
type(func_node) method(5)
method(1)%f => min_1
method(2)%f => min_2
method(3)%f => min_3
method(4)%f => min_4
method(5)%f => min_5
do i = 1, size(method)
write(*,*) method(i)%f(A)
end do
end program test
Output:
2
2
2
2
2

How to call Numerical Recipes svdcmp from Julia

First of all, I know Julia does have an svd intrinsic function, but it does not exactly do what I need. Instead, svdcmp from Numerical Recipes does.
So, the subroutine is this:
MODULE nrtype
INTEGER, PARAMETER :: I4B = SELECTED_INT_KIND(9)
INTEGER, PARAMETER :: I2B = SELECTED_INT_KIND(4)
INTEGER, PARAMETER :: I1B = SELECTED_INT_KIND(2)
INTEGER, PARAMETER :: SP = KIND(1.0)
INTEGER, PARAMETER :: DP = KIND(1.0D0)
INTEGER, PARAMETER :: SPC = KIND((1.0,1.0))
INTEGER, PARAMETER :: DPC = KIND((1.0D0,1.0D0))
INTEGER, PARAMETER :: LGT = KIND(.true.)
REAL(SP), PARAMETER :: PI=3.141592653589793238462643383279502884197_sp
REAL(SP), PARAMETER :: PIO2=1.57079632679489661923132169163975144209858_sp
REAL(SP), PARAMETER :: TWOPI=6.283185307179586476925286766559005768394_sp
REAL(SP), PARAMETER :: SQRT2=1.41421356237309504880168872420969807856967_sp
REAL(SP), PARAMETER :: EULER=0.5772156649015328606065120900824024310422_sp
REAL(DP), PARAMETER :: PI_D=3.141592653589793238462643383279502884197_dp
REAL(DP), PARAMETER :: PIO2_D=1.57079632679489661923132169163975144209858_dp
REAL(DP), PARAMETER :: TWOPI_D=6.283185307179586476925286766559005768394_dp
TYPE sprs2_sp
INTEGER(I4B) :: n,len
REAL(SP), DIMENSION(:), POINTER :: val
INTEGER(I4B), DIMENSION(:), POINTER :: irow
INTEGER(I4B), DIMENSION(:), POINTER :: jcol
END TYPE sprs2_sp
TYPE sprs2_dp
INTEGER(I4B) :: n,len
REAL(DP), DIMENSION(:), POINTER :: val
INTEGER(I4B), DIMENSION(:), POINTER :: irow
INTEGER(I4B), DIMENSION(:), POINTER :: jcol
END TYPE sprs2_dp
END MODULE nrtype
MODULE nrutil
USE nrtype
IMPLICIT NONE
INTEGER(I4B), PARAMETER :: NPAR_ARTH=16,NPAR2_ARTH=8
INTEGER(I4B), PARAMETER :: NPAR_GEOP=4,NPAR2_GEOP=2
INTEGER(I4B), PARAMETER :: NPAR_CUMSUM=16
INTEGER(I4B), PARAMETER :: NPAR_CUMPROD=8
INTEGER(I4B), PARAMETER :: NPAR_POLY=8
INTEGER(I4B), PARAMETER :: NPAR_POLYTERM=8
INTERFACE assert_eq
MODULE PROCEDURE assert_eq2,assert_eq3,assert_eq4,assert_eqn
END INTERFACE
INTERFACE outerprod
MODULE PROCEDURE outerprod_r,outerprod_d
END INTERFACE
CONTAINS
FUNCTION assert_eq2(n1,n2,string)
CHARACTER(LEN=*), INTENT(IN) :: string
INTEGER, INTENT(IN) :: n1,n2
INTEGER :: assert_eq2
if (n1 == n2) then
assert_eq2=n1
else
write (*,*) 'nrerror: an assert_eq failed with this tag:', &
string
STOP 'program terminated by assert_eq2'
end if
END FUNCTION assert_eq2
!BL
FUNCTION assert_eq3(n1,n2,n3,string)
CHARACTER(LEN=*), INTENT(IN) :: string
INTEGER, INTENT(IN) :: n1,n2,n3
INTEGER :: assert_eq3
if (n1 == n2 .and. n2 == n3) then
assert_eq3=n1
else
write (*,*) 'nrerror: an assert_eq failed with this tag:', &
string
STOP 'program terminated by assert_eq3'
end if
END FUNCTION assert_eq3
!BL
FUNCTION assert_eq4(n1,n2,n3,n4,string)
CHARACTER(LEN=*), INTENT(IN) :: string
INTEGER, INTENT(IN) :: n1,n2,n3,n4
INTEGER :: assert_eq4
if (n1 == n2 .and. n2 == n3 .and. n3 == n4) then
assert_eq4=n1
else
write (*,*) 'nrerror: an assert_eq failed with this tag:', &
string
STOP 'program terminated by assert_eq4'
end if
END FUNCTION assert_eq4
!BL
FUNCTION assert_eqn(nn,string)
CHARACTER(LEN=*), INTENT(IN) :: string
INTEGER, DIMENSION(:), INTENT(IN) :: nn
INTEGER :: assert_eqn
if (all(nn(2:) == nn(1))) then
assert_eqn=nn(1)
else
write (*,*) 'nrerror: an assert_eq failed with this tag:', &
string
STOP 'program terminated by assert_eqn'
end if
END FUNCTION assert_eqn
!BL
SUBROUTINE nrerror(string)
CHARACTER(LEN=*), INTENT(IN) :: string
write (*,*) 'nrerror: ',string
STOP 'program terminated by nrerror'
END SUBROUTINE nrerror
!BL
FUNCTION outerprod_r(a,b)
REAL(SP), DIMENSION(:), INTENT(IN) :: a,b
REAL(SP), DIMENSION(size(a),size(b)) :: outerprod_r
outerprod_r = spread(a,dim=2,ncopies=size(b)) * &
spread(b,dim=1,ncopies=size(a))
END FUNCTION outerprod_r
!BL
FUNCTION outerprod_d(a,b)
REAL(DP), DIMENSION(:), INTENT(IN) :: a,b
REAL(DP), DIMENSION(size(a),size(b)) :: outerprod_d
outerprod_d = spread(a,dim=2,ncopies=size(b)) * &
spread(b,dim=1,ncopies=size(a))
END FUNCTION outerprod_d
!BL
END MODULE nrutil
MODULE nr
INTERFACE pythag
FUNCTION pythag_dp(a,b)
USE nrtype
REAL(DP), INTENT(IN) :: a,b
REAL(DP) :: pythag_dp
END FUNCTION pythag_dp
!BL
FUNCTION pythag_sp(a,b)
USE nrtype
REAL(SP), INTENT(IN) :: a,b
REAL(SP) :: pythag_sp
END FUNCTION pythag_sp
END INTERFACE
END MODULE nr
SUBROUTINE svdcmp_dp(a,w,v)
USE nrtype; USE nrutil, ONLY : assert_eq,nrerror,outerprod
USE nr, ONLY : pythag
IMPLICIT NONE
REAL(DP), DIMENSION(:,:), INTENT(INOUT) :: a
REAL(DP), DIMENSION(:), INTENT(OUT) :: w
REAL(DP), DIMENSION(:,:), INTENT(OUT) :: v
INTEGER(I4B) :: i,its,j,k,l,m,n,nm
REAL(DP) :: anorm,c,f,g,h,s,scale,x,y,z
REAL(DP), DIMENSION(size(a,1)) :: tempm
REAL(DP), DIMENSION(size(a,2)) :: rv1,tempn
m=size(a,1)
write(*,*)"size(a,1)= ",size(a,1)
write(*,*)"size(a,2)= ",size(a,2)
write(*,*)"size(v,1)= ",size(v,1)
write(*,*)"size(v,2)= ",size(v,2)
write(*,*)"size(w) = ",size(w)
n=assert_eq(size(a,2),size(v,1),size(v,2),size(w),'svdcmp_dp')
g=0.0
scale=0.0
do i=1,n
l=i+1
rv1(i)=scale*g
g=0.0
scale=0.0
if (i <= m) then
scale=sum(abs(a(i:m,i)))
if (scale /= 0.0) then
a(i:m,i)=a(i:m,i)/scale
s=dot_product(a(i:m,i),a(i:m,i))
f=a(i,i)
g=-sign(sqrt(s),f)
h=f*g-s
a(i,i)=f-g
tempn(l:n)=matmul(a(i:m,i),a(i:m,l:n))/h
a(i:m,l:n)=a(i:m,l:n)+outerprod(a(i:m,i),tempn(l:n))
a(i:m,i)=scale*a(i:m,i)
end if
end if
w(i)=scale*g
g=0.0
scale=0.0
if ((i <= m) .and. (i /= n)) then
scale=sum(abs(a(i,l:n)))
if (scale /= 0.0) then
a(i,l:n)=a(i,l:n)/scale
s=dot_product(a(i,l:n),a(i,l:n))
f=a(i,l)
g=-sign(sqrt(s),f)
h=f*g-s
a(i,l)=f-g
rv1(l:n)=a(i,l:n)/h
tempm(l:m)=matmul(a(l:m,l:n),a(i,l:n))
a(l:m,l:n)=a(l:m,l:n)+outerprod(tempm(l:m),rv1(l:n))
a(i,l:n)=scale*a(i,l:n)
end if
end if
end do
anorm=maxval(abs(w)+abs(rv1))
do i=n,1,-1
if (i < n) then
if (g /= 0.0) then
v(l:n,i)=(a(i,l:n)/a(i,l))/g
tempn(l:n)=matmul(a(i,l:n),v(l:n,l:n))
v(l:n,l:n)=v(l:n,l:n)+outerprod(v(l:n,i),tempn(l:n))
end if
v(i,l:n)=0.0
v(l:n,i)=0.0
end if
v(i,i)=1.0
g=rv1(i)
l=i
end do
do i=min(m,n),1,-1
l=i+1
g=w(i)
a(i,l:n)=0.0
if (g /= 0.0) then
g=1.0_dp/g
tempn(l:n)=(matmul(a(l:m,i),a(l:m,l:n))/a(i,i))*g
a(i:m,l:n)=a(i:m,l:n)+outerprod(a(i:m,i),tempn(l:n))
a(i:m,i)=a(i:m,i)*g
else
a(i:m,i)=0.0
end if
a(i,i)=a(i,i)+1.0_dp
end do
do k=n,1,-1
do its=1,30
do l=k,1,-1
nm=l-1
if ((abs(rv1(l))+anorm) == anorm) exit
if ((abs(w(nm))+anorm) == anorm) then
c=0.0
s=1.0
do i=l,k
f=s*rv1(i)
rv1(i)=c*rv1(i)
if ((abs(f)+anorm) == anorm) exit
g=w(i)
h=pythag(f,g)
w(i)=h
h=1.0_dp/h
c= (g*h)
s=-(f*h)
tempm(1:m)=a(1:m,nm)
a(1:m,nm)=a(1:m,nm)*c+a(1:m,i)*s
a(1:m,i)=-tempm(1:m)*s+a(1:m,i)*c
end do
exit
end if
end do
z=w(k)
if (l == k) then
if (z < 0.0) then
w(k)=-z
v(1:n,k)=-v(1:n,k)
end if
exit
end if
if (its == 30) call nrerror('svdcmp_dp: no convergence in svdcmp')
x=w(l)
nm=k-1
y=w(nm)
g=rv1(nm)
h=rv1(k)
f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0_dp*h*y)
g=pythag(f,1.0_dp)
f=((x-z)*(x+z)+h*((y/(f+sign(g,f)))-h))/x
c=1.0
s=1.0
do j=l,nm
i=j+1
g=rv1(i)
y=w(i)
h=s*g
g=c*g
z=pythag(f,h)
rv1(j)=z
c=f/z
s=h/z
f= (x*c)+(g*s)
g=-(x*s)+(g*c)
h=y*s
y=y*c
tempn(1:n)=v(1:n,j)
v(1:n,j)=v(1:n,j)*c+v(1:n,i)*s
v(1:n,i)=-tempn(1:n)*s+v(1:n,i)*c
z=pythag(f,h)
w(j)=z
if (z /= 0.0) then
z=1.0_dp/z
c=f*z
s=h*z
end if
f= (c*g)+(s*y)
x=-(s*g)+(c*y)
tempm(1:m)=a(1:m,j)
a(1:m,j)=a(1:m,j)*c+a(1:m,i)*s
a(1:m,i)=-tempm(1:m)*s+a(1:m,i)*c
end do
rv1(l)=0.0
rv1(k)=f
w(k)=x
end do
end do
END SUBROUTINE svdcmp_dp
Note that I include only the portions of the modules that I need (just for this case). then, I compile this into a shared library like:
gfortran -shared -fPIC svdcmp_dp.f90 -o svdcmp_dp.so
so far, so good.
The next thing I do is in Julia:
julia> M=5
julia> a=rand(M,M) #just to see if it works
julia> v=zeros(M,M)
julia> w=zeros(M)
julia> t=ccall((:svdcmp_dp_, "./svdcmp_dp.so")
, Void
, ( Ref{Float64} # array a(mp,np)
, Ref{Float64} # array w
, Ref{Float64} # array v
)
,a,w,v)
and I get:
julia> t=ccall((:svdcmp_dp_, "./svdcmp_dp.so")
, Void
, ( Ref{Float64} # array a(mp,np)
, Ref{Float64} # array w
, Ref{Float64} # array v
)
,a,w,v)
size(a,1)= 0
size(a,2)= 0
size(v,1)= 1
size(v,2)= 1
size(w) = 1
nrerror: an assert_eq failed with this tag:svdcmp_dp
STOP program terminated by assert_eq4
So, actually, my calling is OK, but apparently, the size intrinsic from Fortran 90 is NOT returning what I would expect. I say this because the first line in svdcmp_dp.f90 is calling the function assert_eq4 and determine that the dimensions are not compatible. This is not supposed to happen as I chose a[5 X 5], w[5], v[5,5], right?
I search about size in F90, and find out this:
Description:
Determine the extent of ARRAY along a specified dimension DIM, or the total number of elements in ARRAY if DIM is absent.
Standard:
Fortran 95 and later, with KIND argument Fortran 2003 and later
Class:
Inquiry function
Syntax:
RESULT = SIZE(ARRAY[, DIM [, KIND]])
Arguments:
ARRAY Shall be an array of any type. If ARRAY is a pointer
it must be associated and allocatable arrays must be allocated.
DIM (Optional) shall be a scalar of type INTEGER and its value shall
be in the range from 1 to n, where n equals the rank of ARRAY.
KIND (Optional) An INTEGER initialization expression indicating the
kind parameter of the result.
So, my guess is that the problem is related with the allocable property of a,v & w. Or the pointer issue (zero experience with pointers!)
I have actually solve this issue by replacing the declarations from:
SUBROUTINE svdcmp_dp(a,w,v)
USE nrtype; USE nrutil, ONLY : assert_eq,nrerror,outerprod
USE nr, ONLY : pythag
IMPLICIT NONE
REAL(DP), DIMENSION(:,:), INTENT(INOUT) :: a
REAL(DP), DIMENSION(:), INTENT(OUT) :: w
REAL(DP), DIMENSION(:,:), INTENT(OUT) :: v
INTEGER(I4B) :: i,its,j,k,l,m,n,nm
REAL(DP) :: anorm,c,f,g,h,s,scale,x,y,z
REAL(DP), DIMENSION(size(a,1)) :: tempm
REAL(DP), DIMENSION(size(a,2)) :: rv1,tempn
m=size(a,1)
to :
SUBROUTINE svdcmp_dp(Ma,Na,a,w,v)
USE nrtype; USE nrutil, ONLY : assert_eq,nrerror,outerprod
USE nr, ONLY : pythag
IMPLICIT NONE
INTEGER(I4B) :: i,its,j,k,l,Ma,Na,m,n,nm
REAL(DP), DIMENSION(Ma,Na), INTENT(INOUT) :: a
REAL(DP), DIMENSION(Na), INTENT(INOUT) :: w
REAL(DP), DIMENSION(Na,Na), INTENT(INOUT) :: v
REAL(DP) :: anorm,c,f,g,h,s,scale,x,y,z
REAL(DP), DIMENSION(size(a,1)) :: tempm
REAL(DP), DIMENSION(size(a,2)) :: rv1,tempn
Note that the last one also incudes the dimentions of the input arrays!
PD:
Also, the code need the module(it was incomplete):
MODULE nr
INTERFACE pythag
MODULE PROCEDURE pythag_dp, pythag_sp
END INTERFACE
CONTAINS
FUNCTION pythag_dp(a,b)
USE nrtype
IMPLICIT NONE
REAL(DP), INTENT(IN) :: a,b
REAL(DP) :: pythag_dp
REAL(DP) :: absa,absb
absa=abs(a)
absb=abs(b)
if (absa > absb) then
pythag_dp=absa*sqrt(1.0_dp+(absb/absa)**2)
else
if (absb == 0.0) then
pythag_dp=0.0
else
pythag_dp=absb*sqrt(1.0_dp+(absa/absb)**2)
end if
end if
END FUNCTION pythag_dp
!BL
FUNCTION pythag_sp(a,b)
USE nrtype
IMPLICIT NONE
REAL(SP), INTENT(IN) :: a,b
REAL(SP) :: pythag_sp
REAL(SP) :: absa,absb
absa=abs(a)
absb=abs(b)
if (absa > absb) then
pythag_sp=absa*sqrt(1.0_sp+(absb/absa)**2)
else
if (absb == 0.0) then
pythag_sp=0.0
else
pythag_sp=absb*sqrt(1.0_sp+(absa/absb)**2)
end if
end if
END FUNCTION pythag_sp
END MODULE nr
to run it(first, compile as a library):
julia> Na = 10;
julia> Ma = 10;
julia> w = zeros(Na);
julia> v = zeros(Na,Na);
julia> a = rand(Ma,Na);
julia> t = ccall((:svdcmp_dp_, "./svdcmp_dp.so")
, Void
, ( Ref{Int64} # dim Ma
, Ref{Int64} # dim Na
, Ref{Float64} # array a(Ma,Na)
, Ref{Float64} # array w(Na)
, Ref{Float64} # array v(Na,Na)
)
,Ma,Na,a,w,v)
size(a,1)= 10
size(a,2)= 10
size(v,1)= 10
size(v,2)= 10
size(w) = 10
julia> a
10×10 Array{Float64,2}:
-0.345725 -0.152634 -0.308378 0.16358 -0.0320809 … -0.47387 0.429124 -0.45121
-0.262689 0.337605 -0.0870571 0.409442 -0.160302 -0.0551756 0.16718 0.612903
-0.269915 0.410518 -0.0546271 -0.251295 -0.465747 0.328763 -0.109375 -0.476041
-0.33862 -0.238028 0.3538 -0.110374 0.294611 0.052966 0.44796 -0.0296113
-0.327258 -0.432601 -0.250865 0.478916 -0.0284979 0.0839667 -0.557761 -0.0956028
-0.265429 -0.199584 -0.178273 -0.300575 -0.578186 … -0.0561654 0.164844 0.35431
-0.333577 0.588873 -0.0587738 0.213815 0.349599 0.0573156 0.00210332 -0.0764212
-0.358586 -0.246824 0.211746 0.0193308 0.0844788 0.64333 0.105043 0.0645999
-0.340235 0.0145761 -0.344321 -0.602982 0.422866 -0.15449 -0.309766 0.220315
-0.301303 0.051581 0.712463 -0.0297202 -0.162096 -0.458565 -0.360566 -0.00623828
julia> w
10-element Array{Float64,1}:
4.71084
1.47765
1.06096
0.911895
0.123196
0.235218
0.418629
0.611456
0.722386
0.688394
julia> v
10×10 Array{Float64,2}:
-0.252394 0.128972 -0.0839656 0.6905 … 0.357651 0.0759095 -0.0858018 -0.111576
-0.222082 -0.202181 -0.0485353 -0.217066 0.11651 -0.223779 0.780065 -0.288588
-0.237793 0.109989 0.473947 0.155364 0.0821913 -0.61879 0.119753 0.33927
-0.343341 -0.439985 -0.459649 -0.233768 0.0948844 -0.155143 -0.233945 0.53929
-0.24665 0.0670331 -0.108927 0.119793 -0.520865 0.454486 0.375191 0.226854
-0.194316 0.301428 0.236947 -0.118114 … -0.579563 -0.183961 -0.19942 0.0545692
-0.349481 -0.61546 0.475366 0.227209 -0.0975147 0.274104 -0.0994582 -0.0834197
-0.457956 0.349558 0.263727 -0.506634 0.418154 0.378996 -0.113577 -0.0262257
-0.451763 0.0283005 -0.328583 -0.0121005 -0.219985 -0.276867 -0.269783 -0.604697
-0.27929 0.373724 -0.288427 0.246083 0.0529508 0.0369404 0.197368 0.265678
cheers!

Pointers in pure functions

In order to traverse a linked list in Fortran, I use a pointer to the current element that is moved to the next one inside a loop. Trying to apply this inside a pure function that operates on said linked list results in an error.
Example:
module list
implicit none
! Node
type n_list
integer :: val
type(n_list),pointer :: next => NULL()
end type
! Linked list
type t_list
type(n_list),pointer :: head
end type
contains
pure function in_list( list, val ) result(res)
implicit none
class(t_list),intent(in) :: list
integer,intent(in) :: val
logical :: res
type(n_list),pointer :: cur
res = .true.
! Traverse the list
cur => list%head
do while ( associated(cur) )
if ( cur%val == val ) return
cur => cur%next
enddo
! Not found
res = .false.
end function
end module
Results in
cur => list%head
1
Error: Bad target in pointer assignment in PURE procedure at (1)
I am aware of the rationale behind the error/warning, and that it is difficult to ensure that the arguments of the function are not changed when using pointers (Fortran 2008, ch. 12.7 "Pure procedures", esp. C1283). In this case, though, list is never changed.
Is it possible to tell the compiler (ifort and gfortran) that intent(in) is not violated?
The relevant part of the constraint you come up against (C12831) is
In a pure subprogram any designator with a base object that is .. a dummy argument with the INTENT (IN) attribute .. shall not be used
..
as the data-target in a pointer-assignment-stmt
The note below that constraint description motivates it
The above constraints are designed to guarantee that a pure procedure is free from side effects
What you want to say is "I guarantee that there are no side effects, we don't need the constraints for that". The constraints are sufficient but not necessary for this guarantee and you can analyse your code well.
However, a conforming processor/compiler must be able to detect breaches of constraints not just the overall goal of the constraints, so you don't just need to say "it's pure really", but also "and I don't need to be told of violations of C1283". That seems like a lot of effort for the compiler vendor to go to for very little benefit.
I guess, then, that the answer is "no": there isn't a way to compile your code. This isn't definitive, as we're really into implementation-specific areas. You asked about gfortran and ifort in particular, so a "use -nocheck c1283" refutes my "answer".
Now, if there is an option you're in the realms of "trust me" (and non-standard Fortran). So, let's go there anyway. It's just that we're going to lie. As usual, interface blocks will be our means.
module list_mod
implicit none
! Node
type n_list
integer :: val
type(n_list),pointer :: next => NULL()
end type
! Linked list
type t_list
type(n_list),pointer :: head
end type
interface
pure logical function in_list(list, val)
import t_list
class(t_list), intent(in) :: list
integer, intent(in) :: val
end function
end interface
end module
! Interface mismatch in the external function
function in_list(list, val) result(res)
use list_mod, only : t_list, n_list
implicit none
class(t_list),intent(in) :: list
integer,intent(in) :: val
logical :: res
type(n_list),pointer :: cur
res = .true.
! Traverse the list
cur => list%head
do while ( associated(cur) )
if ( cur%val == val ) return
cur => cur%next
enddo
! Not found
res = .false.
end function
use list_mod
type(t_list) testlist
type(n_list), pointer :: ptr
integer i
logical :: res(5) = .FALSE.
allocate(testlist%head)
ptr => testlist%head
do i=1,5
allocate(ptr%next)
ptr => ptr%next
ptr%val = i
end do
! in_list is pure, isn't it?
forall(i=1:5:2) res(i)=in_list(testlist,i)
print*, res
end
This is pure nastiness and is limiting: you no longer have a module procedure; you're not standard conforming; the compiler may be clever and check interfaces (even though it needn't). If the compiler hates you as a result you have only yourself to blame.
Finally, it's all rather a lot of effort to get the procedure pure.
1 This is in Fortran 2008 corresponding to the language revision at the time of asking. In Fortran 2018 the corresponding constraint is C1594.
I have found a solution using recursive functions that is at least Standard conforming. It is neither elegant nor fast, and is limited be the stack depth, but it is working. I'll post it as an answer, although I hope some-one has a better solution...
module list
implicit none
! Node
type n_list
integer :: val
type(n_list),pointer :: next => NULL()
end type
! Linked list
type t_list
type(n_list),pointer :: head
end type
contains
pure function in_list( list, val ) result(res)
implicit none
class(t_list),intent(in) :: list
integer,intent(in) :: val
logical :: res
if ( associated(list%head) ) then
res = in_list_node( list%head, val )
else
res = .false.
endif
end function
recursive pure function in_list_node( node, val ) result(res)
implicit none
class(n_list),intent(in) :: node
integer,intent(in) :: val
logical :: res
if ( node%val == val ) then
res = .true.
elseif ( associated(node%next) ) then
! Recurse
res = in_list_node( node%next, val )
else
res = .false.
endif
end function
end module
program test
use list
implicit none
integer,parameter :: MAXELEM = 100000
integer :: i
type(t_list) :: lst
type(n_list),pointer :: cur
! Fill list
lst%head => NULL()
allocate( lst%head )
lst%head%val = 1
cur => lst%head
do i=2,MAXELEM
allocate( cur%next )
cur%next%val = i
cur => cur%next
enddo !i
print *,'is MAXELEM/2 in list? ', in_list( lst, MAXELEM/2 )
print *,'is MAXELEM+1 in list? ', in_list( lst, MAXELEM+1 )
end program
OK, I found a solution using the transfer intrinsic. The main idea is to clone the list struct (without the data, I checked), and use the pointer to the first node (unchanged) as a start value. Yeah, it is a loop-hole, but both ifort and gfortran accept this without warnings.
module list_mod
implicit none
! Node
type n_list
integer :: val
type(n_list),pointer :: next => NULL()
end type
! Linked list
type t_list
type(n_list),pointer :: head
end type
contains
pure function getHead(list) result(res)
implicit none
class(t_list),intent(in) :: list
type(n_list),pointer :: res
type(t_list),pointer :: listPtr
! Create a copy of pointer to the list struct
allocate( listPtr )
listPtr = transfer( list, listPtr )
! Set the pointer
res => listPtr%head
! Free memory
deallocate( listPtr )
end function
pure function in_list( list, val ) result(res)
implicit none
class(t_list),intent(in) :: list
integer,intent(in) :: val
logical :: res
type(n_list),pointer :: cur
res = .true.
! Traverse the list
cur => getHead(list)
do while ( associated(cur) )
if ( cur%val == val ) return
cur => cur%next
enddo
! Not found
res = .false.
end function
end module
program test
use list_mod
implicit none
integer,parameter :: MAXELEM = 10000000
integer :: i
type(t_list) :: list
type(n_list),pointer :: cur
! Fill list
list%head => NULL()
allocate( list%head )
list%head%val = 1
cur => list%head
do i=2,MAXELEM
allocate( cur%next )
cur%next%val = i
cur => cur%next
enddo !i
print *,'is MAXELEM/2 in list? ', in_list( list, MAXELEM/2 )
print *,'is MAXELEM+1 in list? ', in_list( list, MAXELEM+1 )
end program