This commit is contained in:
Tommy Parnell
2017-02-20 16:06:45 -05:00
commit a7037b9e92
625 changed files with 122711 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
*.o
*.mod
.DS_Store
fortran_fcgi
*.c
*.a

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM ubuntu:trusty
# update Ubuntu
RUN apt-get update
RUN apt-get install -y gfortran make sqlite3 libsqlite3-dev nginx libfcgi-dev spawn-fcgi make
WORKDIR /fortran-machine
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
ADD . .
RUN make
ADD nginx.conf /etc/nginx/sites-enabled/default
CMD sh start.sh

25
LICENSE Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2016
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the author nor the names of the contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

13
TODO.md Normal file
View File

@@ -0,0 +1,13 @@
models
- search is more flexible (LOWER function)
views
- spacing, commas, multiple params
- HTML escaping
- cool images for marsupial search
- spaces, commas, dots, hashes in attributes
docs
- testing?
- refresher / update
- figure out how to get the sqlite c file in place

1766
flibs-0.9/aa Normal file

File diff suppressed because it is too large Load Diff

55
flibs-0.9/flibs/ChangeLog Normal file
View File

@@ -0,0 +1,55 @@
2008-08-10 arjenmarkus@sourceforge.net
* chksys/chksysff.f90: added a test for the type of hexdecimal and binary constants/literals
2008-07-09 arjenmarkus@sourceforge.net
* tests/tools/check_reals.f90: drop the elemental keyword
2008-07-04 arjenmarkus@sourceforge.net
* src/tools: added a new program (editcode): specific preprocessing tasks
* tests/tools: small test for the preprocessing program
2008-05-04 arjenmarkus@sourceforge.net
* index.html: added the vstring and vstringlist modules, as well as finite_state and messages
* finite_state: updated the test program, wrote the documentation
* messages: updated the module and the test program
2008-03-16 arjenmarkus@sourceforge.net
* testmake: a complete application to create test programs
* chksys: a set of programs to check properties of the compiler
2008-03-13 arjenmarkus@sourceforge.net
* filedir: module for manipulating files and directories added (written by Michael Baudin)
* platform: module for basic identification of platform properties (written by Michael Baudin)
2008-01-27 arjenmarkus@sourceforge.net
* ftnunit.f90: added init and final routines, added assertions
* test_ftnunit.f90: added init and final routines
2008-01-26 arjenmarkus@sourceforge.net
* ftnunit.f90: moved the test subroutine
* ftnunit.man: updated documentation
* gentabletest.tcl: added facility
(ChangeLog was not kept up-to-date)
2006-06-25 arjenmarkus@sourceforge.net
* added pointsets.f90
* started with simple interval arithmetic
* added an implementation of text streams
* simple string manipulations
* experiment with integer programming
2006-04-11 arjenmarkus@sourceforge.net
* integer_set.f90: correction of one inconsistency (intent)
* func_qsort.f90: added to repository
* quantum.f90: added to repository
* quantum_computing.pdf: added to site docs
* index.html: description of experiments directory
* backtracking facilities
Note: no detailed record maintained before 11 april 2006

27
flibs-0.9/flibs/Copyright Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2008, Arjen Markus
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
Neither the name of the author nor the names of the contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

108
flibs-0.9/flibs/README Normal file
View File

@@ -0,0 +1,108 @@
Flibs, version 0.9, december 2008
---------------------------------
What is Flibs
-------------
Flibs is a collection of Fortran modules for various tasks:
- [cgi] facilitate web programming via CGI
- [checking] checking various aspects of the code via instrumentation
and static analysis
- [computing] computational tasks, such as automatic differentiation
- [controlstructures] flow control structures such as finite state machines
- [datastructures] support for implementing linked lists, dictionaries
and the like
- [filedir] OS-level tasks regarding files and directories
- [funit] a framework for unit testing, inspired by JUnit
- [ipc] inter-process communication
- [lemon] Lex/Yacc-like parser generation
- [platform] utilities to query the OS and the platform running the
program
- [reporting] tools for generating reports in various formats
(notably LaTex and HTML)
- [specs] tool to generate a robust reading routine from specifications,
geared to tabular input
- [sqlite] interface to the SQLite database management system (http//www.sqlite.org)
- [streams] modules to treat files as "streams" rather than record-oriented
- [strings] modules to manipulate strings (tokenizing, varying-length strings)
- [tools] preprocessor tool to manipulate the source code
- [wrapper] tool to generate Fortran 90 and Fortran 2003 interfaces to C
routines from the C header files
Furthermore:
- [app] a tool for generating makefile dependencies from the Fortran
source code and an experiment at "literate programming".
- [chksys] a collection of programs to probe the properties of the
Fortran compiler (with a similar program for C)
- [doc] documentation for the various modules (in HTML form) and several
articles.
- [experiments] various experiments with different programming
paradigms.
- [testmake] a program suite to generate test programs from
simple specifications.
Documentation is not complete, but most if not all modules and utilities
come with a comprehensive example/test case.
The Flibs project is located on SourceForge: http://flibs.sf.net
Building the modules
--------------------
Each directory under the "src" directory contains one or more modules or
tools. The corresponding directory under "tests" contains the
test/example programs and makefiles (or in some case project files for
MS Developer Studio with Compaq Visual Fortran as the Fortran compiler).
Each set can be built on its own.
The makefiles are set up using macros to take care of differences in
compilers and compiler options. In principle it should not be necessary
to adapt the makefiles to your particular compiler, unless it is doing
things in a completely different way than:
- Compile the individual sources
- Link the resulting object files into an executable
The simple configuration system (configure.sh and configure.bat) helps
to set up the macros for the makefiles:
- configure.sh probes various compilers under Linux, Cygwin or MinGW
- configure.bat is meant for Windows
Status of the modules
---------------------
Not all modules and utilities included in the 0.9 release are fully
functional yet. They have been included mainly for completeness and
as a reminder:
- the genetic_algorithms module typically converges too fast, so that
a suboptimal solution is returned.
- the tupleserver program is not quite finished yet, but as tuple spaces
are a very interesting construct to achieve concurrency, it is
included here.
- the fwrapper program, meant to generate C/C++ interfaces from Fortran
code is still in its infancy. The cwrap program (written in Tcl) is
functional though.
Tcl utilities
-------------
While most of the code is written in standard Fortran 90, as far as the
authors are aware, the collection also contains several utilities
written in Tcl. If you do not have a Tcl installation, you can either
get a full installation from www.activestate.com or you can use a
standalone runtime executable from www.equi4.com.
Copyright
---------
Most of the source files have been written by Arjen Markus. The modules
under "filedir", "platform" and several modules in other directories
have been written by Michael Baudin. The date/time module under
"computing" has been supplied by Arjan van Dijk.
All source code in this project is licensed via the BSD license (see
the "Copyright" file). Basically this means:
Do what you want with it, but do not claim it is your original work.

49
flibs-0.9/flibs/TODO Normal file
View File

@@ -0,0 +1,49 @@
TODO list
---------
dd. 16 december 2008
Add a simple module to interpret commands - Fibonaci-like
dd. 8 september 2008
Document the extensions to the automatic differentiation module
dd. 4 may 2008
Change the messages.f90 file: the definitions must be separated out,
like with the "finite state" module
Correct the documentation and interface for the ipc_file module - LUN
dd. 29 april 2008
Idea for exception handling - via a simple preprocessing
program (this can be used for preconditions and postconditions as well)
Done
dd. 21 april 2008
Add COPYRIGHT file
dd. 20 april 2008
Add module for simulated annealing
dd. 22 march 2008
Add report facilities to CVS + logging
dd. 1 february 2007
Add "indexsets"
Add utilities:
- find out the number of bytes for a record length unit
- find a list of files
Add applications and checks:
- varying-length strings
- merge and the like: different string lengths
- very long source lines
dd. 23 december 2005
linkedlist.f90:
- Support for empty lists
- Support for derived type with pointer components
globmatch.f90:
- Expand the test program with more cases

View File

@@ -0,0 +1,165 @@
# chktype.tcl --
# An attempt at literate programming
# See the generated file for the motivation
#
proc comment {text} {
global outfile
puts $outfile "! [join [split $text \n] "\n !"]"
}
proc code {text} {
global outfile
puts $outfile $text
}
# Hm, gencode is superfluous probably
proc gencode {name text} {
global outfile
puts $outfile "[string map [list NAME $name] $text]"
}
# main --
#
set outfile [open "chktype.f90" w]
comment {\
chktype.f90 --
The module chktype is meant to have the compiler check
as much as possible if the terms in an expression all
have the same precision. This can be used to find out
if there are expressions like "2.1*x" with x a double
precision value or "2.0*i/9" with i an integer.
Such statements cause serious problems:
- the constant 2.1 in the first example is used as
single precision whereas the expression as a whole
will be of double precision. The value is, however,
_not_ the same as 2.1d0*x.
- Evaluating 2.0*i/9 may result in a wrong answer:
set i to 1. If i/9 is computed first, then the result
is 0, not 2.0/9= 0.2222...
The module is not intended for use in an actual program,
it is merely meant for checking at run time
}
code {
module chktype
type real_
private
real :: v
endtype
type double_
private
real(kind=kind(1.0d0)) :: v
endtype
type integer_
private
integer :: v
endtype
}
foreach {op name} {+ add - sub * mult / div ** expon > gt < lt >= ge <= le
== eq /= ne} {
gencode $op "
interface operator(NAME)
module procedure ${name}_real
module procedure ${name}_double
module procedure ${name}_integer
module procedure ${name}_real_tc
module procedure ${name}_double_tc
module procedure ${name}_integer_tc
module procedure ${name}_real_ct
module procedure ${name}_double_ct
module procedure ${name}_integer_ct
end interface"
}
code {
interface assignment(=)
module procedure assign_real
module procedure assign_double
module procedure assign_integer
module procedure assign_real_const
module procedure assign_double_const
module procedure assign_integer_const
end interface
}
code {
contains
}
array set T {real real double "real(kind=kind(1.0d0))" integer integer}
foreach type {real double integer} {
code "
elemental subroutine assign_${type}(x,y)
type(${type}_), intent(in) :: y
type(${type}_), intent(out) :: x
x%v = y%v
end subroutine"
code "
elemental subroutine assign_${type}_const(x,y)
$T($type), intent(in) :: y
type(${type}_), intent(out) :: x
x%v = y
end subroutine"
}
foreach {op name} {+ add - sub * mult / div ** expon } {
foreach type {real double integer} {
code "
elemental type(${type}_) function ${name}_${type}(x,y)
type(${type}_), intent(in) :: x,y
${name}_${type}%v = x%v $op y%v
end function
elemental type(${type}_) function ${name}_${type}_tc(x,y)
type(${type}_), intent(in) :: x
$T($type), intent(in) :: y
${name}_${type}_tc%v = x%v $op y
end function
elemental type(${type}_) function ${name}_${type}_ct(x,y)
$T($type), intent(in) :: x
type(${type}_), intent(in) :: y
${name}_${type}_ct%v = x $op y%v
end function"
}
}
foreach {op name} {> gt < lt >= ge <= le == eq /= ne} {
foreach type {real double integer} {
code "
elemental logical function ${name}_${type}(x,y)
type(${type}_), intent(in) :: x,y
${name}_${type} = x%v $op y%v
end function
elemental logical function ${name}_${type}_tc(x,y)
type(${type}_), intent(in) :: x
$T($type), intent(in) :: y
${name}_${type}_tc = x%v $op y
end function
elemental logical function ${name}_${type}_ct(x,y)
$T($type), intent(in) :: x
type(${type}_), intent(in) :: y
${name}_${type}_ct = x $op y%v
end function"
}
}
# TODO: sin, cos, ...
# TODO: max, min
# Do we want minval and friends as well?
# Also: huge(), tiny() ...?
code {
end module chktype}
# Add a small test program
code {
program aha
use chktype
type(real_) :: x
type(real_) :: y
y = 1.0d0
x = 11 * y
end program
}
close $outfile

View File

@@ -0,0 +1,141 @@
# dependencies.tcl
# Examine the available Fortran sources and set up a
# list of dependencies for use in make for instance
#
# Limitations:
# - include files are not examined
# - no directory information about where to find the include file
# is taken into account (that is: no -I options!)
# - include files with a Fortran-type extensions are examined
# as if they were ordinary source files, thus leading to
# too many source files in the dependency list
#
# getDependencyItems --
# Analyse the file and return a list of items
# Arguments:
# filename Name of the file to examine
# Result:
# A list of lists:
# - Element 1 is a list of modules defined in the file
# - Element 2 is a list of external modules referred to in the file
# - Element 3 is a list of include files
#
proc getDependencyItems {filename} {
set infile [open $filename r]
set provided {}
set required {}
set include {}
while { [gets $infile line] >= 0 } {
switch -re -- [string tolower $line] {
{ *include +['"]} {
lappend include [GetInclude $line]
}
{ *use +[a-z]} {
lappend required [GetUse $line]
}
{ *module +[a-z]} {
lappend provided [GetModule $line]
}
}
}
return [list $provided $required $include]
}
# GetInclude, GetUse, GetModule --
# Extract the relevant information from the list
# Arguments:
# line Line of code
# Result:
# Name of a file or a module
#
proc GetInclude {line} {
return [lindex [split $line {'"}] 1]
}
proc GetUse {line} {
return [lindex $line 1]
}
proc GetModule {line} {
return [lindex $line 1]
}
# ObjectFile, ModuleFile --
# Transform the file name
# Arguments:
# name Source file/module name
# Result:
# Name of a file or a module
#
proc ObjectFile {name} {
return "[file root $name].o"
}
proc ModuleFile {name} {
return "[string toupper $name].mod"
}
# analyseAllFiles --
# Analyse all files in the current directory
# Arguments:
# filename Name of the file for dependencies
# type Type of output ("make" or "ordered")
# Result:
# None
#
proc analyseAllFiles {filename type} {
set outfile [open $filename w]
if { $::tcl_platform(platform) == "windows" } {
set mask "*.f *.for *.f90"
} else {
set mask "*.f *.for *.f90 *.F *.FOR *.F90"
}
set files {}
foreach f [eval glob -type f $mask] {
foreach {provided required include} [getDependencyItems $f] {break}
set deps($f) {}
foreach i $include {
lappend deps($f) $i
}
foreach m $provided {
set mod [ModuleFile $m]
lappend files $mod
set deps($mod) $f
}
foreach m $required {
set mod [ModuleFile $m]
set deps($f) $mod
}
lappend files $f
}
switch -- $type {
"make" {
foreach f $files {
if { [lsearch {.f .for .f90 .F .FOR .F90} [file extension $f]] >= 0 } {
puts $outfile [ObjectFile $f]\t:\t$f\n\t[join $deps($f) \\\n\t]
} else {
puts $outfile $f\t:\t[join $deps($f) \\\n\t]
}
}
}
"ordered" {
puts "TO BE DONE!"
}
}
close $outfile
}
# main --
# Get the thing going
#
analyseAllFiles test.mk make

View File

@@ -0,0 +1,11 @@
A.mod : tsta.f90
B.mod : tsta.f90
tsta.o : tsta.f90
D.mod
C.mod : tstc.f90
tstc.o : tstc.f90
D.mod : tstd.f90
tstd.o : tstd.f90
f.inc\
g.inc

View File

@@ -0,0 +1,10 @@
! Testing dependencies
!
module a
use b
use c
end module
module b
use d
end module

View File

@@ -0,0 +1,4 @@
! Testing dependencies
!
module c
end module

View File

@@ -0,0 +1,6 @@
! Testing dependencies
!
module d
include "f.inc"
include "g.inc"
end module

View File

@@ -0,0 +1,10 @@
! Testing dependencies
!
module a
use b
use c
end module
module b
use d
end module

View File

@@ -0,0 +1,10 @@
! Testing dependencies
!
module a
use b
use c
end module
module b
use d
end module

View File

@@ -0,0 +1,207 @@
* @begin@ */
*
* chkcomp.f(or) - source code to check certain fetaures of the FORTRAN
* compiler
*
* Copyright (C) 1998 Arjen Markus
*
* Arjen Markus
*
*
* General information:
* This file contains source code that uses various extensions to the
* FORTRAN standard and intentionally bad programming fragments.
* Comments explain each of them.
* Its purpose is to check if the compiler (in combination with the
* options) will flag the features.
*
* @end@
*
* $Author: arjenmarkus $
* $Date: 2008/03/17 17:57:56 $
* $Source: /cvsroot/flibs/chksys/chkcomp.f,v $
* $Log: chkcomp.f,v $
* Revision 1.1 2008/03/17 17:57:56 arjenmarkus
* Added directory "chksys" - programs to probe compiler properties
*
*
* @@------------------------------------------------------------------
* Routine: CHKSYS
* Author: Arjen Markus
* Purpose: Main program
* Context: -
* Summary:
* Show the properties of the compiler by expressly
* introducing errors and extensions
* --------------------------------------------------------------------
*
PROGRAM CHKCMP
*
* -------- A very useful feature: make sure that all variables
* have to be declared. Unfortunately, it is an extension
* to the FORTRAN standard
*
IMPLICIT NONE
*
CHARACTER*40 STRING
INTEGER INTVAL , INTV2 , I
REAL REAVAL
DOUBLE PRECISION DBLVAL
*
* -------- Does the compiler accept:
* - lowercase letters
* - underscores in variable names
* - long variable names
* - length of data type
*
* Note:
* None of these variables are actually used, so it may
* flag that!
*
INTEGER lowcas
INTEGER UND_SC
INTEGER LONGVARIABLENAME
INTEGER*4 INT4
INTEGER*2 INT2
*
* -------- Does the compiler support the INCLUDE-statement?
*
INCLUDE 'chkcomp.inc'
*
* -------- Some compilers accept:
* - double quotes (") to delimit strings
* - C-like escape-sequences
*
STRING = "Double quote"
STRING = '\'
*
* -------- Does the compiler register the fact that
* we are assigning too large a value? Or that
* we may loose precision?
*
INTVAL = INT( 1.0E20 )
INTVAL = 1.0E20
REAVAL = 1.0E100
REAVAL = 1.0D100
DBLVAL = 1.12345678901234567890D00
REAVAL = DBLVAL
*
* -------- Some compilers do not accept invalid substrings
* and can quite smart about it
*
WRITE(*,*) STRING(1:0)
INTVAL = LEN(STRING) + 1
WRITE(*,*) STRING(1:INTVAL)
*
* -------- Some compilers accept very long statements
* (though the number of spaces may be important as well)
*
STRING = '1' //
1 '2' //
2 '3' //
3 '4' //
4 '5' //
5 '6' //
6 '7' //
7 '8' //
8 '9' //
9 '0' //
& 'A' //
1 'B' //
2 'C' //
3 'D' //
4 'E' //
5 'F' //
6 'G' //
7 'H' //
8 'I' //
9 'J' //
& 'K' //
1 'L'
*
* -------- Use a variable before it is set
*
INTVAL = INTV2
*
* -------- Check DO-loops and IF-statements:
* - Can we change the variable?
* - Can we jump into a DO-loop or an IF block?
*
DO 100 I = 1,10
IF ( I .EQ. 5 ) I = 6
WRITE(*,*) I
100 CONTINUE
*
IF ( INTVAL .GT. 1 ) GOTO 120
IF ( INTVAL .LT. -1 ) GOTO 130
*
DO 110 I = 1,10
120 CONTINUE
WRITE(*,*) I
110 CONTINUE
*
IF ( INTVAL .GT. 2 ) THEN
130 CONTINUE
INTVAL = 10
ENDIF
*
* -------- Statements not reachable?
*
GOTO 210
WRITE(*,*) 'This statement is not reachable'
*
210 CONTINUE
IF ( INTVAL .GT. 10 ) THEN
GOTO 220
ELSE
GOTO 230
ENDIF
WRITE(*,*) 'Neither is this'
*
220 CONTINUE
230 CONTINUE
*
* -------- Check calls to routines (wrong arguments)
*
CALL SUBR1( INTVAL )
CALL SUBR2( INTVAL , INTV2 )
*
* -------- Check special comments
*
CALL PDEBUG
*
STOP
END
SUBROUTINE SUBR1( REAVAL )
*
* -------- Subroutine has one argument: a real
*
REAL REAVAL
WRITE(*,*) 'Real argument:' , REAVAL
RETURN
END
SUBROUTINE SUBR2( INTVAL )
*
* -------- Subroutine has one argument: an integer
*
INTEGER INTVAL
WRITE(*,*) 'Integer argument:' , INTVAL
RETURN
END
SUBROUTINE PDEBUG
*
* -------- Some compilers support the D and ! comments:
* D: hide/show debug-statements
* !: in-line comments
*
* (This code is put at the end because at least one
* compiler aborts on the D in the first column)
*
WRITE(*,*) 'PDEBUG' ! Dummy routine
D WRITE(*,*) 'In DEBUG mode'
*
RETURN
END

View File

@@ -0,0 +1,408 @@
! DOC
!
! chkcomff.f90 - source code to check certain features of the
! FORTRAN 90 compiler
!
! Copyright (C) 1998 Arjen Markus
!
! Arjen Markus
!
!
! General information:
! This file contains source code that uses various violations of the
! FORTRAN 90 standard and intentionally bad programming fragments.
! Comments explain each of them.
! Its purpose is to check if the compiler (in combination with the
! options) will flag the features.
!
! ENDDOC
!
! $Author: arjenmarkus $
! $Date: 2008/03/17 17:57:56 $
! $Source: /cvsroot/flibs/chksys/chkcompff.f90,v $
! $Log: chkcompff.f90,v $
! Revision 1.1 2008/03/17 17:57:56 arjenmarkus
! Added directory "chksys" - programs to probe compiler properties
!
!
! --------------------------------------------------------------------
!
! --------------------------------------------------------------------
! Module: SCOPE_MOD
! Author: Arjen Markus
! Purpose: Introduce scope problems
! Context: Used by main
! Summary:
! Define some variables to check scoping issues:
! - Variable "int_var" is also defined in the
! main program.
! - Variable "real_mod" is passed as an actual argument
! in the main program to subroutine "double_access"
! - The module uses module "sub_module" to see what happens
! to variables that are defined in sub modules: do they
! automatically become visible?
! --------------------------------------------------------------------
!
module sub_module
integer :: int_submodule
end module
module scope_mod
integer , dimension(1:10) :: int_var
real :: real_mod
end module
! --------------------------------------------------------------------
! Routine: arglist_extern
! Author: Arjen Markus
! Purpose: Show the compiler's ability to detect interface problems
! Context: Used by main program
! Summary:
! Simple subroutine, called with the wrong type of
! actual arguments
! --------------------------------------------------------------------
!
subroutine arglist_extern( intv )
!
implicit none
!
integer :: intv
write(*,*) 'Value:' , intv
return
end subroutine
! --------------------------------------------------------------------
! Program: CHKCOMP
! Author: Arjen Markus
! Purpose: Main program
! Context: -
! Summary:
! Show the properties of the compiler by expressly
! introducing (semantical) errors and extensions
! --------------------------------------------------------------------
!
program chkcmp
!
! -------- Use module "scope_mod": two levels of scope problems
! The module defines variables "int_var", "real_mod" and
! via a submodule "int_submodule".
! The scope problems are:
! - Program CHKCOMP defines a local variable "int_var"
! - Program CHKCOMP assumes "int_submodule" is available
! via "scope_mod"
! - Program CHKCOMP uses "real_mod" as an actual parameter:
! to the subroutine "double_access"
! - once for a dummy parameter with a different name
! - once for a dummy parameter with the same name
!
use scope_mod
!
! -------- A very useful feature: make sure that all variables
! have to be declared. It should be placed before any
! declarations
!
implicit none
!
! -------- FORTRAN 90 introduced the problem of scope into the
! FORTRAN world. Does the compiler flag such problems?
! The variables string_var and int_var are also used in
! subroutine scope_problem.
!
character :: string_var
integer :: int_var ! Also in "scope_mod"
real :: real_var
real , pointer , dimension(:) :: ptor
!
! -------- Does the compiler check for:
! - scoping problems
! - problems with actual argument lists (internal and external)
! - problems with double access
! - problems with incomplete logic in function
! - problems with deallocation via a pointer
! - missing interface (required when working with pointers)
! - use of variables before they are set
!
call scope_problem
call arglist_problem( real_var )
call arglist_extern( real_var )
call double_access( real_var , real_var , real_mod )
int_var = -1
write( * , * ) 'Result: ' , func_incomplete( int_var )
write( * , * ) 'Result: ' , func_no_return ( int_var )
call alloc_dealloc
call set_null( ptor )
call undefined_vars
!
! end of program
!
stop
! --------------------------------------------------------------------
! Routines: internal routines
! Author: Arjen Markus
! Contains:
! scope_problem - Show scoping problems
! arglist_problem - Show that the calls to internal routines are checked
! double_access - Show problems with double access
! func_incomplete - Show that incomplete logic brings trouble to
! functions
! --------------------------------------------------------------------
contains
! @@------------------------------------------------------------------
! Routine: scope_problem
! Author: Arjen Markus
! Purpose: Show scoping problems
! Context: Used by main program
! Summary:
! Redefine two variables that have global scope
! --------------------------------------------------------------------
!
subroutine scope_problem( )
!
implicit none
!
! -------- These variables are also defined in CHKCOMP
!
character string_var
complex int_var ! This is an integer in CHKCOMP and in
! scope_mod
int_var = (1.0,1.0)
write(*,*) 'Complex:' , int_var
return
end subroutine
! --------------------------------------------------------------------
! Routine: arglist_problem
! Author: Arjen Markus
! Purpose: Show that the calls to internal routines are checked
! Context: Used by main program
! Summary:
! Define a subroutine with a different list than used
! in the main program
! --------------------------------------------------------------------
!
subroutine arglist_problem( one_arg )
!
implicit none
!
! -------- The one argument in main is a real
!
character :: one_arg
write(*,*) 'One arg' , one_arg
return
end subroutine
! --------------------------------------------------------------------
! Routine: double_access
! Author: Arjen Markus
! Purpose: Show problems with double access
! Context: Used by main program
! Summary:
! The first dummy argument has the same name as a variable
! in the main routine. The second dummy argument has a
! different name, but the actual one substituted is
! the same. The third argument has the same name as a
! module variable.
!
! This is trouble according to the standard.
! --------------------------------------------------------------------
!
subroutine double_access( real_var , second_arg , real_mod )
!
implicit none
real :: real_var , second_arg , real_mod
real_var = second_arg + real_mod
return
end subroutine
! --------------------------------------------------------------------
! Routine: func_incomplete
! Author: Arjen Markus
! Purpose: Show that incomplete logic brings trouble to functions
! Context: Used by main program
! Summary:
! Set the function's return value ONLY if the argument is
! positive. Forget about the other possibilities. Is this
! flagged? (The function does not always return an
! explicitly set value!)
! --------------------------------------------------------------------
!
integer function func_incomplete( one_arg )
!
implicit none
!
integer :: one_arg
if ( one_arg > 0 ) then
func_incomplete = 1
endif
!
return
end function
! --------------------------------------------------------------------
! Routine: func_no_return
! Author: Arjen Markus
! Purpose: Show that functions that do not set a return value are
! flagged
! Context: Used by main program
! Summary:
! Do not set the function's return value
! --------------------------------------------------------------------
!
integer function func_no_return( one_arg )
!
implicit none
!
integer :: one_arg
integer :: return_val
if ( one_arg > 0 ) then
return_val = 1 ! Just a local!
endif
!
return
end function
! --------------------------------------------------------------------
! Routines: end of internal procedures
! --------------------------------------------------------------------
end program
! --------------------------------------------------------------------
! Routine: set_null
! Author: Arjen Markus
! Purpose: Show whether the compiler complains about lacking interfaces
! Context: Used by main program
! Summary:
! Set the one argument to NULL. As this is a pointer,
! an interface is required. Does the compiler complain?
! --------------------------------------------------------------------
!
subroutine set_null( ptor )
!
implicit none
!
real, pointer, dimension(:) :: ptor
nullify( ptor )
return
end subroutine
! --------------------------------------------------------------------
! Routine: use_interface
! Author: Arjen Markus
! Purpose: Show the strange error messages when using interfaces wrongly
! Context: Used by main program
! Summary:
! Define an interface with a name at the end
! --------------------------------------------------------------------
!
subroutine use_interface
!
implicit none
!
real, pointer, dimension(:) :: ptor
real, pointer, dimension(:) :: ptor2
!
! The end interface statement should not contain the name!
! Note:
! Some compilers will accept this. Others do not.
!
interface null_pointer
subroutine set_null( ptor )
real, pointer, dimension(:) :: ptor
end subroutine
end interface null_pointers
call null_pointer( ptor )
call null_pointer( ptor2 )
return
end subroutine
! --------------------------------------------------------------------
! Routine: alloc_dealloc
! Author: Arjen Markus
! Purpose: Show that the compiler can detect obvious deallocation
! problems
! Context: Used by main program
! Summary:
! Allocate an allocatable array, set a pointer to it
! and deallocate via the pointer - this violates the
! standard's access rules.
! --------------------------------------------------------------------
!
subroutine alloc_dealloc
!
implicit none
!
real, pointer, dimension(:) :: ptor
real, allocatable, dimension(:) :: rarray
!
! Allocate the array and deallocate via the pointer
!
allocate( rarray(1:10) )
ptor => rarray
deallocate( ptor )
return
end subroutine
! --------------------------------------------------------------------
! Routine: undefined_vars
! Author: Arjen Markus
! Purpose: Show that the compiler can detect assignments that
! use undefined variables
! Context: Used by main program
! Summary:
! Use unset local variables in an assignment.
! Compare reals (another obvious problem).
! Convert double to real (yet another obvious problem).
! --------------------------------------------------------------------
!
subroutine undefined_vars
!
implicit none
!
real :: result
real :: operand_a
real :: operand_b
real , pointer :: ptor
double precision :: dbl_value
!
! Assignment with undefined variables
!
result = operand_a + operand_b
!
! Silently convert double precision to single
!
dbl_value = atan( 1.0d+00 )
result = dbl_value
!
! Compare reals
!
if ( result .ne. dbl_value ) then
write( * , * ) 'Some truncation occurred!'
endif
!
! Write unassigned pointer to screen
!
write( * , * ) ptor
return
end subroutine

View File

@@ -0,0 +1,40 @@
! Extra checks on Fortran 95 compiler:
!
! character(len=lenarg) - in subroutines, lenarg is an argument
!
! allocate( array(0) ) - is it allocated or not?
!
! allocatable components in derived type
!
program chkf95
integer, dimension(:), allocatable, target :: array
integer, dimension(:), pointer :: parray
type pointer
integer, dimension(:), pointer :: parray
end type
type(pointer) :: p
allocate( array(0) )
parray => array
p%parray => array
if ( allocated(array) ) then
write(*,*) 'Array is allocated - size: ', size(array)
else
write(*,*) 'Array is reported as NOT allocated'
endif
if ( associated(parray) ) then
write(*,*) 'Pointer to array is associated - size: ', size(parray)
else
write(*,*) 'Pointer to array is reported as NOT associated'
endif
if ( associated(p%parray) ) then
write(*,*) '(Component) Pointer to array is associated - size: ', size(p%parray)
else
write(*,*) '(Component) Pointer to array is reported as NOT associated'
endif
end program

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,365 @@
<html>
<head>
<title>CHKSYS: Check compiler and run-time environment</title>
</head>
<body>
<h1>
Checking properties of the compiler and the run-time
environment
</h1>
<table>
<tr>
<td>chksys.f</td>
<td>A program to determine the properties of the
run-time environment for FORTRAN 77 programs</td>
</tr><tr>
<td>chkcomp.f</td>
<td>A FORTRAN 77 source file containing deliberate deviations
from the standard and deliberate bad statements for testing the
compiler's accuracy</td>
</tr><tr>
<td>chksysff.f90</td>
<td>A program to determine the properties of the
run-time environment for Fortran 90 programs</td>
</tr><tr>
<td>chkcomff.f90</td>
<td>A Fortran 90 source file containing deliberate deviations
from the standard and deliberate bad statements for testing the
compiler's accuracy</td>
</tr><tr>
<td>chksysc.c</td>
<td>A program to determine the properties of the run-time
environment for C programs</td>
</tr><tr>
<td>chkcompc.c</td>
<td>A C source file containing deliberate deviations from
the standard and deliberate bad statements for testing the
compiler's accuracy</td>
</tr>
</tr><tr>
<td>fp_special.f90</td>
<td>A Fortran 90 program to test the behaviour of a program in the
presence of floating-point exceptions and special IEEE numbers
(notably negative zeros)</td>
</tr>
</table>
<h3>Introduction</h3>
<p>
The programs CHKSYS and CHKSYSFF are meant to help understand the
run-time environment of a FORTRAN program. For C there is a similar
program, CHKSYSC.
<p>
In many cases the actual behaviour of a program depends on the compiler
you use and the options you included during the compile and link steps.
This is especially true if errors occur or if you are using certain
open-ended language features.
<p>
Many programmers are not aware that such features exist and it is
sometimes difficult or impossible to find them in the documentation.
<p>
That is where the CHKSYS offer some assistence:
<ul>
<li>It checks areas that are open-ended, such as whether local variables
are automatically saved between subroutine calls and various odd
errors that can occur when using external files.
<li>It checks how (deliberate) run-time errors are handled, such as an
overflow or an exceedance of array bounds.
</ul>
<p>
The program source CHKCOMP (in chkcomp.f(or) and its Fortran 90
equivalent chkcomff.f90) is meant to test the accuracy of the compiler.
It is not meant to validate the compiler. It simply contains a fair
number of very common extensions to the standard
and some deliberate program errors (like jumping into a DO-loop and
inaccessible statements). The errors are documented in the source code
via comments.
<p>
The rest of this document describes:
<br><a href="#usage">How to use the programs</a>
<br><a href="#tests">Description of the tests</a>
<br><a href="#results">Presentation of some results</a>
<p>
<p>
<a name="usage"><h3>Usage of the programs</h3></a>
<b>CHKCOMP:</b>
<ul>
<li>First run the compiler of choice with standard options
<li>Then try to find out what extra options are needed to get the
maximum set of messages. Such options include:
<ul>
<li>Highest warning level
<li>Check strict conformance to standard
<li>Check interfaces
<li>etc.
</ul>
<li>In general, it is a good idea to ALWAYS ask for the maximum number of
checks, as this can prevent programming errors at an early stage.
<li>Some (or many) compilers are lazy: they will offer very little checks
beyond basic syntax. Shun such compilers - or at least use appropriate
program checkers beside them.
</ul>
<p>
<b>CHKSYS:</b>
<ul>
<li>Compile the program with the same options as the CHKCOMP source. If
this would result in errors, remove the strict conformance to the
standard, as it uses one, common, extension: IMPLICIT NONE.
<li>Run the program once to get the general features of the run-time
environment
<li>Edit the file "chksys.set" to select the more disruptive tests.
<li>Study the source code and the file "chksys.msg" to see how things
are done. You might learn something from it or you might have
suggestions.
<li>Use various compiler options:
<ul>
<li>With and without optimisations
<li>Array bounds checking
<li>Strict conformance to the standard
<li>Numerical options (like treatment of overflows and underflows)
</ul>
</ul>
<p>
<p>
<a name="tests"><h3>Tests that are performed</h3></a>
<b>CHKSYS</b> performs the following tests:
<ul>
<li>Operating system:
<ul>
<li>What type of system (UNIX, MS-DOS/Windows 3.x or Windows 95/NT)?
<li>Support for long file names?
<li>Distinction between upper and lower case in file names?
</ul>
<li>Memory model:
<ul>
<li>Static (local variables are automatically saved) or
dynamic (you must use SAVE to save the values)
<li>Length of an integer:
<ul>
<li>2 or 4 bytes? Often selectable via compiler switch. Determines
range
</ul>
<li>Precision of real variables and numerical operations:
<ul>
<li>How many decimals are significant?
<li>Any obvious problems with multiplications
</ul>
<li>What is the unit for the length of a direct-access record?
<ul>
<li>Often a byte, sometimes a word (e.g. 4 bytes!)
</ul>
<li>Are binary files allowed? And what keywords do you use?
<li>How many files can be opened at the same time?
<li>File handling:
<ul>
<li>Are the parameters to the OPEN statement checked immediately?
<li>Using unformatted READs on a formatted file
<li>Writing to an unopened file
<li>Reading too many data from a record of an unformatted file
<li>Writing too many data to a record of a direct-access file
<li>Opening the same file twice
<li>Closing an unopened file
<li>Opening a second file to the same unit
<li>Reading files of which the last line is incomplete
</ul>
<li>Check the behaviour of DO-loops:
<ul>
<li>Can you change the value of a DO-variable?
<li>What happens if you change the upper limit?
<li>Are DO-loops performed at least once? (They should not!)
</ul>
<li>Check the behaviour of overlapping substrings:
<ul>
<li>Shifting characters to the left and right
</ul>
<li>Miscellaneous stuff:
<ul>
<li>Treatment of a backslash (C-like espace character?)
<li>Formatting in list-directed output
<li>The first character when writing to the screen
</ul>
</ul>
<p>
<p>
<a name="results"><h3>Some results</h3></a>
<i>Compiling CHKCOMP:</i>
<p>
The first listing is the result when using MicroSoft FORTRAN version 5.1,
with the maximum level of warnings (which turns out to be the default)
but not the switch to force checking against the standard:
<pre>
chkcomp.for(79) : error F2566: INTEGER : REAL : type conversion error
chkcomp.for(80) : error F2566: INTEGER : REAL : type conversion error
chkcomp.for(81) : error F2005: illegal REAL constant
chkcomp.for(82) : error F2566: REAL : REAL : type conversion error
chkcomp.for(128) : error F2516: I : assignment using active DO variable illegal
chkcomp.for(136) : warning F4801: label 120 : used across blocks
chkcomp.for(141) : warning F4801: label 130 : used across blocks
chkcomp.for(171) : warning F4999: LOWCAS : variable declared but not used
chkcomp.for(171) : warning F4999: LONGVARIABLENAME : variable declared but not used
chkcomp.for(171) : warning F4999: UND_SC : variable declared but not used
chkcomp.for(171) : warning F4999: INT2 : variable declared but not used
chkcomp.for(171) : warning F4999: INT4 : variable declared but not used
chkcomp.for(171) : warning F4999: INTV2 : variable declared but not used
chkcomp.for(171) : warning F4999: DBLVAL : variable declared but not used
chkcomp.for(171) : warning F4999: REAVAL : variable declared but not used
chkcomp.for(178) : error F3606: SUBR1 : formal argument REAVAL : type mismatch
chkcomp.for(187) : error F2202: SUBR2 : defined with different number of arguments
</pre>
<p>
Below is a listing from the Lahey FORTRAN 77 compiler, version 5.20,
using no particular switches:
<pre>
Compiling chkcomp.for, a Standard Format Source File
OPTION DESCRIPTION OPTION DESCRIPTION
/n0 - Standard FORTRAN 77 IMPLICIT /nL - No Line-number table
/n2 - Generate 387 constants and code / P - Protect constant arguments
/n4 - No 80486 optimizations /nQ1 - "Unlimited" NDP stack
/n7 - Optimize inter-statement /nQ2 - No protected-mode RPC
/nA2 - No allocatable array checking /nQ3 - No real-mode RPC
/nB - No Bounds checking / R - Remember local variables
/nC - Ignore nonstandard usage / S - Create filename.SLD for SOLD
/nC1 - INTEGER constants 4 bytes /nT - INTEGER*4, LOGICAL*4 default
/nD - DIRECT files with headers /nV - Not VAX interpretation
/nH - No Hardcopy source listing / W - Display Warning messages
/nI - No Interface checking /nX - No Xref listing
/nK - Generate 80x87 code /nZ1 - Better SOLD debugging
Compiling line 35: PROGRAM CHKCMP
File chkcomp.for, line 80:
INTVAL = 1.0E20
^
Warning - INTEGER variable (INTVAL) possibly assigned new value before
former value used.
File chkcomp.for, line 81:
REAVAL = 1.0E100
^
Fatal - Expression is not within numeric limits of REAL data type
(1.18E-38 : 3.40E+38) (See Section 6.3 in Lahey Programmer's Reference).
File chkcomp.for, line 82:
REAVAL = 1.0D100
^
Warning - REAL variable (REAVAL) possibly assigned new value before former
value used.
File chkcomp.for, line 84:
REAVAL = DBLVAL
^
Warning - REAL variable (REAVAL) possibly assigned new value before former
value used.
File chkcomp.for, line 90:
INTVAL = LEN(STRING) + 1
^
Warning - INTEGER variable (INTVAL) possibly assigned new value before
former value used.
File chkcomp.for, line 121:
INTVAL = INTV2
^
Warning - INTEGER variable (INTV2) is used before being assigned a value.
File chkcomp.for, line 128:
IF ( I .EQ. 5 ) I = 6
^^
Fatal - INTEGER variable (I) is a DO index, cannot assign a value (See
Section 8.4.1 in Lahey Language Reference).
Fatal - Statement illegal as IF suffix (See Section 8.5.2.2 in Lahey
Language Reference).
File chkcomp.for, line 148:
WRITE(*,*) 'This statement is not reachable'
^
Warning - No execution path reaches this statement.
File chkcomp.for, line 156:
WRITE(*,*) 'Neither is this'
^
Warning - No execution path reaches this statement.
Warning - INTEGER variable (LOWCAS) is declared but never used,
File chkcomp.for, line 58.
Warning - INTEGER variable (UND_SC) is declared but never used,
File chkcomp.for, line 59.
Warning - INTEGER variable (LONGVARIABLENAME) is declared but never used,
File chkcomp.for, line 60.
Warning - INTEGER variable (INT4) is declared but never used,
File chkcomp.for, line 61.
Warning - INTEGER*2 variable (INT2) is declared but never used,
File chkcomp.for, line 62.
Fatal - Expression is not within numeric limits of INTEGER data type
(-2,147,483,648 : 2,147,483,647) (See Section 6.3 in Lahey Programmer's
Reference), File chkcomp.for, line 79.
Fatal - Expression is not within numeric limits of INTEGER data type
(-2,147,483,648 : 2,147,483,647) (See Section 6.3 in Lahey Programmer's
Reference), File chkcomp.for, line 80.
Warning - REAL variable (REAVAL) assigned a value, never used,
File chkcomp.for, lines 81, 82, 84.
Fatal - Expression is not within numeric limits of REAL data type
(1.18E-38 : 3.40E+38) (See Section 6.3 in Lahey Programmer's Reference),
File chkcomp.for, line 82.
Fatal - Substring specifier must be > 0 and <= string length (See Section
4.4.4.1 in Lahey Language Reference), File chkcomp.for, line 89.
Fatal - Statement label (120) appears in transfer context outside DO range
or block IF (See Chapter 8 in Lahey Language Reference), File chkcomp.for,
line 132.
Fatal - Statement label (130) appears in transfer context outside DO range
or block IF (See Chapter 8 in Lahey Language Reference), File chkcomp.for,
line 133.
Compiling line 173: SUBROUTINE SUBR1( REAVAL )
Compiling line 182: SUBROUTINE SUBR2( INTVAL )
File chkcomp.for, line 182:
SUBROUTINE SUBR2( INTVAL )
^
Warning - Argument count conflicts with previous usage (See Section 10.3.1
in Lahey Language Reference).
Compiling line 191: SUBROUTINE PDEBUG
File chkcomp.for, line 201:
D WRITE(*,*) 'In DEBUG mode'
Abort - Standard source file, columns 1-5: blanks and/or digits (See
Section 1.6 in Lahey Language Reference).
</pre>
Both compilers are fairly old (copyrights indicate 1992), but they
do show the capabilities of a good compiler:
<ul>
<li>Interface checks:
<ul>
<li><i>These are a major source of errors</i> and
unfortunately FORTRAN 77 provides no prototyping mechanisms.
The compilers check the consistency <i>within the file</i> only.
</ul>
<li>Use and misuse of variables:
<ul>
<li>Both warn about variables that have been declared, but not been used.
<li>The Lahey compiler does a more thorough job, because it also warns
about <i>new assignments before the previous value was used</i>.
<li>Illegal use of DO-loop variables.
</ul>
<li>Unreachable statements:
<ul>
<li>The MicroSoft compiler does not warn us about them
<li>The Lahey compiler does a pretty thorough job! It even detects the
problem with this construct:
<pre>
151 IF ( INTVAL .GT. 10 ) THEN
152 GOTO 220
153 ELSE
154 GOTO 230
155 ENDIF
156 WRITE(*,*) 'Neither is this'
</pre>
<li>Illegal substrings:
<ul>
<li>Note the <i>fatal</i> error for line 89: we are using a zero-length
substring.
</ul>
</ul>
</body>
</html>

View File

@@ -0,0 +1,635 @@
@INTRODUCTION
CHKSYS: A program to determine the properties of the run-time
environment for FORTRAN programs
The program presents information about:
- What type of system are you running on (e.g. UNIX or MS Windows)?
- What restrictions for file names?
- Are local variables in subroutines SAVEd or not?
- Other matters concerning portability and the detection of
run-time errors.
Note:
Some systems will ask for a file name when a READ/WRITE occurs to an
openend file (unit number) as occurs in the third file-handling test.
The program can not prevent the question but it tries to detect the
result. So:
First run the program without redirection. Then you know what happens.
The recommended file name is "askfile".
Note:
Some tests may cause the program to crash or, in the worst to let
the whole system hang (on PC). So the file "chksys.set" can be used
to control whether such tests are performed.
@FILE-SYSTEM
File system
-----------
The following information about the operating system and the file
system is obtained:
@PROGRAM-ERROR
*** Error:
*** The program has come up with an unknown condition.
*** Please check this!
@OS-UNKNOWN
Operating system: Unknown. The following information might be misleading
@OS-UNIX
Operating system: The program is running under UNIX
@OS-DOS-WINDOWS
Operating system: The program is running under MS DOS or MS Windows 3.x
@OS-WINDOWS-95-NT
Operating system: The program is running under MS Windows 95 or NT
(No further distinction can be made)
@FILE-SYSTEM-SHORT-NAMES
The file system supports short file names only (either the DOS convention
or an old UNIX System V convention)
@FILE-SYSTEM-LONG-NAMES
The file system supports long file names (more than 14 characters)
@FILE-SYSTEM-IGNORE-CASE
File names are insensitive to uppercase and lowercase (e.g. CHKSYS.MSG
and chksys.msg are equivalent)
@FILE-SYSTEM-RESPECT-CASE
The file system distinguishes between uppercase and lowercase in file
names (e.g. CHKSYS.MSG and chksys.msg are not the same files!)
@FILE-DIRECT-ACCESS
Record length for direct access files
-------------------------------------
A widespread misunderstanding of direct-access files is, that the
record length is expressed in bytes (or perhaps characters). Thus a
record that should contain 10 ordinary integers should have at least
length 40:
OPEN( 10 , FILE = 'direct.dat' , RECL = 40 )
WRITE( 10 , REC = 1 ) ( IARRAY(I) , I = 1,10 )
In fact this is compiler-dependent! Some compilers use a "word"
(commonly 32 bits or 4 bytes) as the unit of length. The OPEN
statement would then need to be:
OPEN( 10 , FILE = 'direct.dat' , RECL = 10 )
In this case:
@FILE-BINARY-FILES
Use of binary files
-------------------
Some FORTRAN compilers allow BINARY files as an extension to
the standard. Such files have the advantage of being independent
of the FORTRAN compiler, in contrast to the ordinary UNFORMATTED
files. Binary files can easily be used in a C program for instance.
Unfortunately, each compiler has its own way of specifying such files.
@FILE-BINARY-FILES-ALLOWED
The current compiler supports binary files. Use the following keywords
in the OPEN statement:
@FILE-BINARY-FILES-NOT-ALLOWED
The current compiler does NOT support binary files.
@NUMBER-OPEN-FILES
Maximum number of open files
----------------------------
All systems have a limit to the number of files that can opened at the
same time.
@NUMBER-OPEN-FILES-LIMITED
The current system allows a limited number of open files:
@NUMBER-OPEN-FILES-UNLIMITED
The current system allows at least 90 open files, which for almost all
practical purposes is quite enough.
@FILE-STANDARD
Standard LU-numbers
-------------------
FORTRAN 77 uses several standard LU numbers, such as 5 for reading
input from the keyboard. The list below shows which standard LU numbers
are used an to what files they are connected:
@LENGTH-INTEGER
Number of bytes for integers
----------------------------
Many compilers offer an option to set the default length for integer
variables. Thus the range for variables declared as INTEGER is
sometimes from -32767 to 32767 (the length of an integer is 2 bytes)
and sometimes from -2147483647 to 2147483647 (the length of an
integer is 4 bytes).
In contrast to FORTRAN 90, FORTRAN 77 does not allow these limits to
be easily detected.
According to the test however:
@NUMERICAL-PRECISION
Numerical precision
-------------------
The program tries to obtain some information about the numerical aspects
of the compiler and run-time environment:
- The number of significant decimals in single and double precision reals
- The precision of certain operations: is x*y equal to y*x,
is x*y equal to(-x)*(-y), is -x equal to abs(x) if x < 0
(Reports from several sources show that this need not be the case!)
Note:
A program like "paranoia" is capable of doing a thorough test on
numerical operations. The tests here are quick and dirty, to get
some insight quickly.
Test 1: Number of significant decimals in single and double precision reals
-------
In most cases the number of significant decimals is 7 for single precision
and 15 for double precision. The numbers are established in two ways:
- A simple approach
- A roundabout approach
The latter is necessary because some compilers seem to use caching
techniques, which gives rise to strange results.
@NUMERICAL-COMMUTATIVE
Test 2: Is multiplication commutative (i.e. x*y equal y*x)?
-------
The test is not exhaustive, but if the test shows that multiplication
is NOT commutative, then you are in trouble.
@NUMERICAL-NEGATIVE
Test 3: Is the sign of influence on multiplication?
-------
The test looks for differences in: x*y and (-x)*(-y). The test is NOT
exhaustive, but merely indicates a potential for trouble.
@NUMERICAL-ABSOLUTE
Test 4: Is (-x) equal to abs(x)?
-------
The test looks for differences in: -x and abs(x), with x negative.
The test is NOT exhaustive, but merely indicates a potential
for trouble.
@NUMERICAL-UNWANTED-RESULT-SINGLE
The test showed differences between the two calculation results
for SINGLE precision. This may give trouble in numerical calculations.
@NUMERICAL-UNWANTED-RESULT-DOUBLE
The test showed differences between the two calculation results
for DOUBLE precision. This may give trouble in numerical calculations.
@NUMERICAL-RESULT-ALLRIGHT
The test showed NO differences between the two calculation results.
This is no guarantee, but indicates that there are no obvious problems.
@NUMERICAL-STRANGE-PRECISION
Warning:
The simple approach showed that single and double precision have
a very large, equal, precision. This is probably due to caching of
intermediate results of calculations. Try again without any optimisation.
The simple approach gives the following results:
@FILE-HANDLING
File handling
-------------
A number of tests follow designed to check the behaviour of the
file I/O system.
@FILE-TEST-CHECK-OPEN
Test 1: Are the parameters to the OPEN statement checked immediately?
-------
A scratch file is opened with the statement:
OPEN( 10 , STATUS = 'SCRATCH' , IOSTAT = IERR ,
FORM = 'INVALID!' )
@FILE-TEST-CHECK-NO
Warning:
The parameters in the OPEN statement are NOT checked immediately. This
will probably be delayed until the first READ or WRITE.
@FILE-TEST-CHECK-YES
Note:
The parameters in the OPEN statement are checked immediately. You can
not open the file with invalid parameters.
@FILE-TEST-MIXED-FORMAT
Test 2: Using unformatted READs on a formatted file
-------
Create a formatted file and read it as if it were unformatted.
(The program tries to read the first record only)
Three situations may occur:
- Reopening the file as unformatted causes an error
- Reading data from the file causes an error
- The program reports no error at all
@FILE-TEST-CREATE-ERROR
Warning:
The file that is used in this test (called "chksys.1") could not
be opened. This means the test has been skipped.
Possible causes:
- The file name does not conform to the conventions of this platform
- You have no permission to create files in this directory
@FILE-TEST-MIXED-ERROR-OPEN
In this case: An error occurs when trying to open the file.
@FILE-TEST-MIXED-ERROR-READ
In this case: An error occurs when trying to read data.
@FILE-TEST-MIXED-NO-ERROR-AT-ALL
Warning:
No error occurred AT ALL! This is a very awkward situation.
It may be that the string that was written ("INVALID!")
starts exactly as an unformatted file would. Most likely,
the compiler does not flag any error. That would be serious
indeed!
@FILE-TEST-WRITE-UNOPENED
Test 3: Writing to an unopened file
-------
What happens if you inadvertently write to an unopened file (that
is a LU-number that has not been connected to a file)?
From past experience:
- The program may ask for a file name
- The program opens a file like "fort.10"
- The program produces a run-time error
Note: if the program asks for a file name, type "askfile" (in lowercase
letters). This way the program may be able to detect the fact that
at some low level a question was put on screen.
Note: text appearing after these line that does not start "In this case"
was written by the run-time library, not by this program itself.
@FILE-TEST-UNOPENED-ERROR
In this case: the program will produce a run-time error
@FILE-TEST-UNOPENED-OPEN
In this case: the program seems to use a default file name
@FILE-TEST-UNOPENED-ASK
In this case: the program (probably) asks you for a name
@FILE-TEST-READ-TOO-MANY
Test 4: Reading too many data from a record of an unformatted file
-------
The test is simple: does the program identify a problem reading too
many data from an unformatted record? If not, you have a very faulty
compiler, or you are using the wrong options.
@FILE-TEST-TOO-MANY-ERROR
In this case: The program correctly detects the error.
@FILE-TEST-TOO-MANY-NO-ERROR
Warning:
The program seems to think everything went well! You should not
use this compiler or at least check the set of compiler options.
@FILE-TEST-WRITE-TOO-MANY
Test 5: Writing too many data to a record of a direct-access file
-------
The test consists of opening a scratch file with record length 4 and
writing ten integers to the first record. This should result in a
run-time error.
@FILE-TEST-OPENING-FILE-TWICE
Test 6: Opening the same file twice
-------
The test consists of opening the same file ("chksys.1") twice,
to units 10 and 11. There are four possibilities:
- All attempts fail (the file is not opened at all after the two OPEN
statements)
- The second attempt fails
- The first unit is implicitly closed
- The file remains open for both units
@FILE-TEST-OPEN-TWICE-ERROR
Note:
The second OPEN statement caused a run-time error
@FILE-TEST-OPEN-TWICE-NO-ERROR
Warning:
The second OPEN statement caused NO run-time error at all
@FILE-TEST-OPEN-TWICE-BOTH-OPEN
Warning:
The units are successfully opened to the same file. This may cause
all kinds of trouble (interaction between READs and WRITEs to the
two units).
@FILE-TEST-OPEN-TWICE-ONLY-FIRST
Note:
Only the first OPEN statement is succssful. The file remains opened
to the first unit.
@FILE-TEST-OPEN-TWICE-ONLY-SECOND
Note:
The second OPEN statement caused the first unit to be closed.
The file is opened to the second unit.
@FILE-TEST-OPEN-TWICE-BOTH-CLOSED
Warning:
The second OPEN statement caused the first unit to be closed and it
failed to open the file. So neither unit is opened!
@FILE-TEST-OPEN-TWICE-ERROR-CLOSE
Warning:
An error occurred while closing the first or second unit. So, apparently
the file was not opened to two units independently.
@FILE-TEST-CLOSING-FILE
Test 7: Closing an unopened file
-------
The test consists of closing a unit number that was never connected
to a file. If this produces a run-time error, closing such a
unit number will abort your program.
Special note:
Some systems produce a "Too many open files" error if you attempt to
close all unit numbers without regard to whether they were opened in
the first place, like:
*
* Close all possible files, just for safety
*
DO 110 LUN = 10,99
CLOSE( LUN )
110 CONTINUE
@FILE-TEST-CLOSING-FILE-ERROR
Warning:
A run-time error occurs if you try to close an unopened file.
@FILE-TEST-CLOSING-FILE-NO-ERROR
Note:
No run-time error occurred, though this may not be the case on other
platforms or with a different compiler (or even different options).
@FILE-TEST-OPEN-AGAIN
Test 8: Opening another file to the same LU-number
-------
The test consists of opening a file to unit 10 and then opening yet
another file to the same LU-number. The result might be that the first
file is closed.
@FILE-TEST-OPEN-AGAIN-NO
Warning:
No run-time error occurred. This means that you could inadvertently
loose the connection to an open file.
@FILE-TEST-OPEN-AGAIN-ERROR
Note:
This causes a run-time error, as it probably should.
@FILE-TEST-INCOMPLETE-LINES
Test 9: Are lines without an end-of-line character accepted?
-------
The test consists of creating a simple text file with one record which
does not have a proper end-of-line. Opening the file again and trying
to read that one line might give a problem.
@FILE-TEST-INCOMPLETE-ACCEPTED
Note:
The program accepts the line without a problem. (With some compilers
this would cause an end-of-file or a read error!)
@FILE-TEST-INCOMPLETE-OPEN-ERROR
Warning:
An OPEN error occurred. Can not complete the test.
@FILE-TEST-INCOMPLETE-READ-ERROR
Warning:
This causes a run-time error, not an end-of-file condition.
@FILE-TEST-INCOMPLETE-END-OF-FILE
Warning:
This causes an end-of-file condition, the last line can not be read.
@SHIFTING-STRINGS
Shifting strings
----------------
In this test substrings are shifted one character to the left and one
to the right. As the operations involve overlapping strings, it is
not obvious that the program will handle this correctly.
@SHIFTING-INCORRECT
Warning:
The results of the shifts in at least one direction are incorrect.
Be careful with such shifts!
@MISCELLANEOUS1
Miscellaneous issues
--------------------
Treatment of backslash characters (\):
Some compilers treat backslashes as C-like escape sequences, such
that '\t' becomes a TAB character
@BACKSLASH-ESCAPE
Warning:
This is the case with the current compiler (and the options used)!
@BACKSLASH-ASIS
This is NOT the case with the current compiler (and the options used).
So backslashes are safe to use. Be aware of this behaviour though.
@MISCELLANEOUS2
The next two lines should appear without an empty line. The lines
after that show the treatment of numbers (right-justified or
left-justified):
@TREATMENT-ASTERISK
If there is a line in between, then note that code like:
CHARACTER*75 STR1 , STR2
WRITE(*,*) STR1
WRITE(*,*) STR2
may give different results than you expect! It means that the
output is less than 75 columns wide, hence wrapping occurs.
@MISCELLANEOUS3
Characters in the first column that are written to the screen may be
eliminated or regarded as carriage control. The string '%1234567890'
was written with the statement:
WRITE( * , '(A)' ) '%1234567890'
@MEMORY
Memory model
------------
Contrary to what many programmers believe, variables in subroutines are
not automatically saved. In fact the SAVE statement should be used
to ensure this.
@MEMORY-STATIC
Static memory:
From the test it appears that local variables retain their
values between calls. Thus most likely, local variables and
arrays are stored in static memory.
Consequences:
- The SAVE statement has no other effect than documentation
- Large arrays do not cause a stack overflow
Warnings:
- Use the SAVE statement to document persistent variables
- Use SAVE for any large arrays, as a different environment
might pose stack problems!
@MEMORY-DYNAMIC
Dynamic memory:
From the test it appears that local variables DO NOT retain
their values between calls. Thus most likely, local variables
and arrays are stored in the stack.
Consequences:
- The SAVE statement should be used to preserve the values
- Large arrays may cause stack overflow in the middle of the
program run
Solutions:
- Use the SAVE statement to preserve the values
(This provides documentation as well)
- Use SAVE for any large arrays to force them in static memory
@DO-LOOPS
Test the behaviour of DO-loops
------------------------------
Test 1: Is the number of iterations in a DO-loop protected?
-------
If the variable that controls the DO-loop is changed by mistake,
then various things can happen. It is even possible that the DO-loop
will never end!
In this test, the number of steps should be 10, and the DO-variable
should (probably) become 26, because it is changed to 20 at step 5.
@DO-LOOP-ALTERABLE
Warning:
The number of iterations can be changed! If the variable is changed,
then the DO-variable jumps from 4 to 20. If the variable is 21 at the
end of the loop, then the value is saved.
@DO-LOOP-PROTECTED
The number of iterations can not be changed, though the iteration
variable may be changed. If the variable is changed, then the step
number jumps from 4 to 20. If the variable is 26 at the end of the
loop, then the value is saved.
@DO-LOOP-INFINITE
Warning:
The implementation of DO-loops is such that a change to the DO-variable
leads to INFINITE loops! In our test, the DO-loop is terminated after
20 steps, indicating this somewhat bizarre behaviour.
@DO-LOOP-CHANGE-UPPER
Test 2: What happens if the upper limit in a DO_loop is changed?
-------
The DO-loop looks like this:
NOSTPM = 10
DO 110 I = 1,NOSTPM
IF ( I .EQ. 4 ) NOSTPM = 6
...
110 CONTINUE
Is it still run 10 times or not?
@DO-LOOP-LIMIT-ALTERABLE
Warning:
The number of iterations can be changed! If the upper limit is changed,
then the number of iterations also changes.
@DO-LOOP-LIMIT-PROTECTED
The number of iterations can not be changed.
@DO-LOOP-LIMIT-INFINITE
Warning:
The implementation of DO-loops is such that a change to the upper limit
may lead to INFINITE loops! In our test, the DO-loop is terminated after
20 steps, indicating this somewhat bizarre behaviour.
@DO-LOOP-PASS
Test 3: DO-loops conform to standard?
-------
In older versions of FORTRAN (FORTRAN 66 for instance), a DO-loop was
always executed at least once. This might make quite a difference.
@DO-LOOP-PASS-ONCE
Warning:
DO-loops are always executed at least once!
@DO-LOOP-PASS-ZERO
DO-loops seem to behave correctly.
@OVERFLOW
Test: What happens if an overflow is generated?
-----
The program calculates 1.0E30 / 1.0E-20, which can not be represented
in single precision (at least if it is a REAL*4 number, according to
IEEE guidelines). An overflow should occur.
@UNDERFLOW
Test: What happens if an underflow is generated?
-----
The program calculates 1.0E-20 / 1.0E30, which can not be represented
in single precision (at least if it is a REAL*4 number, according to
IEEE guidelines). It is either truncated to zero, or an underflow occurs.
@DIVISION
Test: What happens if the program divides by zero?
-----
The program calculates 1.0 / 0.0, which is clearly incorrect. It should
result in a "division by zero" error.
@DOMAIN
Test: The program tries to calculate SQRT( -1.0 )
-----
The program calculates SQRT( -1.0 ), which is clearly incorrect.
It should result in a "domain" error.
@ARRAYBOUND
Test: The program tries to access array elements outside the array
-----
The program uses an array of length 10 and accesses element 11.
As a bonus:
If the reported value for this element is 2.0, the array element is
actually a variable defined after the array. Similarly a value
of 3.1415926 indicates that the program used a variable prior to the
array. If neither value is reported, the memory layout is not obvious.
@ARRAY-NO-ERROR
Warning:
No error was generated. This means that we can access elements
outside the actual array without a run-time error. As this can cause
all kinds of nasty errors, which are likely to become apparent in
a totally different place, there are serious risks involved here.
Check if the compiler has an array bound check option and use this
at least during testing!
@NUMERIC-GENERATE
Warning:
The invalid calculation was done without a run-time error.
Now we will multiply the result by 2.0.
@NUMERIC-NO-ERROR
Warning:
No error was generated. This means that we can manipulate the
result of an invalid calculation. The severity of this behaviour
depends of course on the type of calculation.
(Use a program like "paranoia" to find out more about the numerical
properties of your compiler/computer.)
@SUBSTRING
Test: How does the program react to taking illegal substrings?
-----
This test consists of taking a zero-length substring and a substring
beyond the end of the string. It seems unclear what should happen in
these cases, but if range-checking is enabled, the program will abort.
@SUBSTRING-NO-ERROR
Note:
No error occurred, the program can continue. This means such errors may
remain in the program for a long time.
@end-of-file

View File

@@ -0,0 +1,25 @@
# CHKSYS: Which checks to perform
# Possible keywords:
# @GENERAL - all general (well-behaved) tests
# @OVERFLOW - behaviour on overflow
# @UNDERFLOW - behaviour on underflow
# @DIVISION - behaviour on division by zero
# @DOMAIN - behaviour on negative argument for SQRT()
# @ARRAYBOUND - behaviour if array bounds are exceeded
# @SUBSTRING - behaviour if certain substrings are taken
#
# To include the test, the keyword must begin in the first column
# To exclude it, use the word SKIP in front.
#
# If you want concise information about the tests, put the keyword
# SUMMARY in (VERBOSE is just to show the opposite: a full report
# is default)
#
VERBOSE
@GENERAL
SKIP @OVERFLOW
SKIP @UNDERFLOW
SKIP @DIVISION
SKIP @DOMAIN
@SUBSTRING
@ARRAYBOUND

View File

@@ -0,0 +1,487 @@
@INTRODUCTION
CHKSYSC: A program to determine the properties of the run-time
environment for C programs
The program presents information about:
- What type of system are you running on (e.g. UNIX or MS Windows)?
- What restrictions for file names?
- Sizes and ranges for common data types
- Possible alignment issues
- Other matters concerning portability and the detection of
run-time errors.
Note:
Some tests may cause the program to crash or, in the worst to let
the whole system hang (on PC). So the file "chksysc.set" can be used
to control whether such tests are performed.
@ENVIRONMENT
Environment variables
---------------------
Most systems use environment variables to transfer information between
programs. Two such variables that may influence the behaviour of a
program are: PATH (searching executable programs) and LANG (specifying
the locale, which controls such things as a comma instead of a period
as decimal separator).
Furthermore, most systems supply the name of the program as the first
command-line argument, argv[0]. Many systems include the full path
in this variable, some, notably the Korn shell under UNIX, do not.
In this case:
@ENVIRONMENT-NO-COMMAND-ARGS
Note:
The run-time environment provides no arguments!
@ENVIRONMENT-NO-PATH
Note:
The environment variable PATH has not been defined
@ENVIRONMENT-NO-LANG
Note:
The environment variable LANG has not been defined
@ENVIRONMENT-CURR-TIME
Current time:
-------------
According to the system clock and formatting using the "%c" format
of strftime():
@ENVIRONMENT-NO-TIME
Warning:
The run-time environment does not support a system clock
@ENVIRONMENT-SETLOCALE
Locale:
-------
The function setlocale() can be used to set the locale, that is, items
that are specific to a country or a culture. These include:
- The order for day of the month and the name of the month
- The use of a comma instead of a period to separate decimals
- The order of characters in comparisons
Unfortunately, the C standard only defines the locales "C" (the standard
locale) and "" (the locale defined by the run-time environment, in a
system-specific way, such as via LANG).
It is not possible to check which locales are defined in the current
run-time environment. The program simply reports the current locale
(probably "C").
@FILE-SYSTEM
File system
-----------
The following information about the operating system and the file
system is obtained:
@PROGRAM-ERROR
*** Error:
*** The program has come up with an unknown condition.
*** Please check this!
@OS-UNKNOWN
Operating system: Unknown. The following information might be misleading
@OS-UNIX
Operating system: The program is running under UNIX
@OS-DOS-WINDOWS
Operating system: The program is running under MS DOS or MS Windows 3.x
@OS-WINDOWS-95-NT
Operating system: The program is running under MS Windows 95 or NT
(No further distinction can be made)
@FILE-SYSTEM-SHORT-NAMES
The file system supports short file names only (either the DOS convention
or an old UNIX System V convention)
@FILE-SYSTEM-LONG-NAMES
The file system supports long file names (more than 14 characters)
@FILE-SYSTEM-IGNORE-CASE
File names are insensitive to uppercase and lowercase (e.g. CHKSYS.MSG
and chksys.msg are equivalent)
@FILE-SYSTEM-RESPECT-CASE
The file system distinguishes between uppercase and lowercase in file
names (e.g. CHKSYS.MSG and chksys.msg are not the same files!)
@COMPILER-MACROS
Compiler macros
---------------
In general compilers predefine a number of macros that can be used to
identify certain characteristics of the compiler and the run-time
environment.
Standard macros include:
__STDC__ Used by standard-compliant compilers to indicate that the
standard is in effect. Particularly useful for enabling
prototypes.
This macro is not always defined if you are tacitly allowing
extensions.
__cplusplus Macro indicating the compiler is expecting C++.
The presence of this macro often means __STDC__ is NOT
defined.
_POSIX_SOURCE and _XOPEN_SOURCE
These macros indicate compliance to the POSIX and XOPEN
standards, which are mainly used in UNIX.
Almost every compiler also defines one or more usually ill-documented
macros that can tell you something about the computer system or the
run-time environment.
The program will try and find which such macros are defined and what type
of computer you are working on. Note that the test is not exhaustive nor
is it intended to be - it mainly distinguishes between a number of
popular brands.
@COMPILER-MACROS-GENERAL
The following general macros are defined:
@COMPILER-MACROS-SPECIFIC
The following macros are compiler-specific:
@COMPILER-MACROS-COMPILER
These indicate the following:
@COMPILER-MACROS-PREPROCESSOR
Preprocessor macros
-------------------
The C/C++ preprocessor defines several macros that can be useful
for introducing debug messages:
__FILE__ - the name of the source file
__LINE__ - the line in the source file
__DATE__ - the date of compilation
__TIME__ - the time of compilation
For instance:
@COMPILER-MACROS-PREPROC-NOFILE
Warning:
The current preprocessor does NOT work according to the Standard.
It does not define the macro __FILE__
@NUMBER-OPEN-FILES
Maximum number of open files
----------------------------
All systems have a limit to the number of files that can opened at the
same time.
@NUMBER-OPEN-FILES-MACRO
The C header file "stdio.h" provides a macro FOPEN_MAX. It is the
minimum number of files that is guaranteed can be opened.
@NUMBER-OPEN-FILES-NO-MACRO
The C header file "stdio.h" that was used does not provide a
macro FOPEN_MAX, contrary to most compilers.
@NUMBER-OPEN-FILES-LIMITED
The current system allows a limited number of open files:
@NUMBER-OPEN-FILES-UNLIMITED
The current system allows at least 90 open files, which for almost all
practical purposes is quite enough.
@BASIC-DATA-TYPES
Information about data types
----------------------------
The C header files along with the compiler's sizeof() macro
have provided the following information about the basic data types:
@BASIC-DATA-TYPES-MEMORY
Data types concerning memory:
-----------------------------
Three types are important here:
size_t - The type that is used for the size of an object in memory.
The maximum value it can reach defines the maximum amount
of memory you can allocate with one call to malloc().
ptrdiff_t - The type of value returned when taking the difference
between two addresses.
Warning:
The maximum allowable value may be smaller than can actually
occur!
void * - The general pointer type. A "long" variable should be
large enough to hold it.
@BASIC-DATA-TYPES-FORMAT
Data types and print formats:
-----------------------------
In C formatting strings like "%5f" can yield unexpected results:
- If the number to be printed is larger than fits in the space that
is allowed by the format, the resulting string will simply be enlarged.
- This poses the threat of array overflows on any sprintf() call.
To illustrate this, the program prints several examples. Note the use
of the "F" suffix. This is to avoid complaints from the compiler. There
may even be a difference between:
float_var = 1.0e20 ;
and
float_var = 1.0e20F ;
If this is so, it may be due to local optimisations (the numerical
registers retain the value 1.0e20 in double precision and this is used
in the printf() call).
@MEMORY-ALIGNMENT
Memory alignment
----------------
The organisation of the computer's memory sometimes becomes important:
- When determining the size of a structure
- When allocating memory
The fine grain aspects of the memory organisation are known as alignment.
It basically means that the memory that holds a short integer should
start at an even address, reals at an address that is a quadruple and
so on. Of course this all depends on the specific computer system.
Such alignments may cause gaps in structures and between variables.
Though mainly a waste of space, it can also hide array overflows until
you get to a different computer system (see the section on string
overflow).
The program tries to determine the alignment rules by looking at two
structures and by allocating some memory.
The structures are:
Structure 1: Structure 2:
struct _CharDoubleChar struct _DoubleCharChar
{ {
char one_char ; double one_double ;
double one_double ; char one_char ;
char one_char ; char two_char ;
} CharDoubleChar ; } DoubleCharChar ;
@MEMORY-ALIGNMENT-POINTERS
The malloc() function and its companions usually return addresses that
are a multiple of 2, 4, 8 etc. In this case:
@MEMORY-ALIGNMENT-FAILED-MULT
The successive calls to malloc() returned addresses that do not show
any consistent multiple. We failed to determine such a value.
@STRING-OVERFLOW
String overflow
---------------
The test tries to determine what happens to local variables if a
string is filled with another string that is too large. (C provides
no mechanism to adequately prevent this - you will have to program it
yourself)
The local variables are defined as (in this order!):
int int_before ;
char string[10] ;
int int_after ;
The results may be:
- The integer defined before the string is affected
- The integer defined after the string is affected
- There is no noticeable effect:
- The memory alignment makes sure there is enough space
- The stack has been reorganised
@STRING-OVERFLOW-BEFORE
Warning:
The string overflow affected the variable defined BEFORE the string
@STRING-OVERFLOW-AFTER
Warning:
The string overflow affected the variable defined AFTER the string
@STRING-OVERFLOW-NO-EFFECT
Warning:
The string overflow had no effect on either the variable that was
defined BEFORE the string or the one defined AFTER the string.
Caution:
Such errors may remain undetected for a long time! In fact, during the
development of this program it seemed as if the test had no effect, but
after adding enough subroutines, the PC I used ran the program as if
everything was normal and then hang up - or rebooted! Of course, I blamed
these new subroutines at first ...
@STRING-OVERFLOW-MEMORY-ALIGNMENT
The string overflow had no effect due to the memory alignment
for integers.
@STRING-OVERFLOW-STACK-REORGANISED
The string overflow had no effect because the variables in the stack
are not arranged in the order of definition.
@IMPLICIT-CAST
Implicit casting
----------------
The C compiler uses implicit casting in a number of situations. These
may cause very obscure errors, if you are not aware of this feature.
The background is the use of old-style Kernigan and Ritchie prototypes.
The test consists of executing the following code:
int_val = 1 ;
real_val = 1.1F ;
printf( "Real, integer: %d %d\n" , real_val , int_val ) ;
printf( "Real: %d\n" , real_val ) ;
printf( "Integer: %d\n" , int_val ) ;
From the first print statement, you would expect a bizarre integer and
then "1". From the second and third print statements simply a repetition
of each number. The result, however:
@FILE-HANDLING
File handling
-------------
Several tests follow to establish the behaviour of the run-time system
with regard to somewhat weird file handling:
- Opening a file that has an empty string as name
Note:
To get more information out of the system, the global variable "errno"
is used. Unfortunately, this does not always work. So, after the
message "System error:", you may not see a useful text.
Well, this is an indication of the quality of the compiler as well,
of course.
Note:
Be sure to include not only the header file "errno.h" but also
the header file "string.h" - this defines the strerror() function.
If not, some systems will present rubbish.
Note:
With some compilers a run-time error may follow that is handled by
an assert() macro. If this happens the program will not continue.
--- To do: think of a solution for this! ---
@FILE-EMPTY-NAME
Test 1: Opening a file with an empty name
-------
The fopen() is called like this:
pfile = fopen( "" , "r" ) ;
@FILE-EMPTY-NAME-NULL
The call to fopen() returned NULL - as expected.
@FILE-EMPTY-NAME-SUCCESS
The call to fopen() returned a non-NULL pointer. This means the error
was not caught, or at least not in an obvious way!
@FILE-EMPTY-END
Dummy
@LOGICAL-EXPRESSIONS
Logical expressions
-------------------
The C standard states that programs may short-cut logical expressions
like "a && b" and "a || b":
- If after evaluating "a" it becomes clear that the expression "a && b"
is false, "b" need not be evaluated
- Similarly, "a || b" is true if "a" is true, and "b" needs not to be
known.
The program checks how many arguments are evaluated in four different
logical expressions and assumes that short-cuts are the proper way
to evaluate these.
For each expression the program lists the order in which the operands
are used and what value they had.
@LOGICAL-EXPR-NOT-COMPLIANT
The evaluation of the logical expression does not seem compliant
@LOGICAL-EXPR-COMPLIANT
Note:
From the four tests we conclude that the evaluation of logical
expressions is compliant to the standard.
@LOGICAL-EXPR-AND
Test 1: Evaluate a && b, with "a" false
-------
@LOGICAL-EXPR-OR
Test 2: Evaluate a || b, with "a" true
-------
@LOGICAL-EXPR-THREE-OPERANDS
Test 3: Evaluate a && b || c, with "a", "b" and "c" true
-------
@LOGICAL-EXPR-THREE-BRACKETS
Test 4: Evaluate (a && b) || c, with "a", "b" and "c" true
-------
@ARITHMETIC-EVALUATION
Arithmetic evaluations
----------------------
Contrary to logical expressions, the standard does not prescribe any
order to the evaluation of individual terms or the synthesis to one
result.
This means that side effects will depend on the compiler and possibly
compiler options (such as optimisations). Side effects are present
in such expressions as:
for ( i= 0 ; i < 100 ; a[i++] = b[i] )
/* No body */ ;
Or:
a = i + b[i++] ;
The program tests a number of expressions and tries to decide what
order is used. For each expression the program lists the order in
which the operands are used and what value they had.
@ARITHMETIC-TWO-SUM
Test 1: Evaluate a + b
-------
@ARITHMETIC-THREE-SUM
Test 2: Evaluate a + b + c
-------
@ARITHMETIC-FOUR-SUM
Test 3: Evaluate a + b + c + d
-------
@ARITHMETIC-INDECISIVE
Note:
There was no consistent order in the evaluation of these expressions!
@ARITHMETIC-MIXED-TYPE-CALC
The next test consists of a somewhat naive calculation using reals
and integers:
int_val = 10 ;
float_val = 1.4 ;
int_result = 1.7 + int_val + float_val ;
@INPUT-FEATURES
Features of the input routines
------------------------------
The following tests use sscanf() and atof() to check whether these
functions return the proper values and, possibly, error codes.
@INPUT-SSCANF-RETURN-REGULAR
Test 1: Read two separate strings via sscanf()
-------
@INPUT-SSCANF-RETURN-EOF
Test 2: Try to read beyond the end of the string
-------
@INPUT-SSCANF-RETURN-INVALID
Test 3: Try to read a real value, but the string does not contain one
-------
@INPUT-SSCANF-COMPLIANT
Note:
The behaviour of sscanf() seems compliant to the Standard.
@INPUT-SSCANF-NON-COMPLIANT
Warning:
The behaviour of sscanf() is NOT compliant to the Standard. It does
not return the expected numbers.
@INPUT-ATOF-INVALID-INPUT
Test 4: What does atof() return if the string is not a valid real
-------
@INPUT-NO-ERROR
Warning:
There was no indication of an error. That means that the program is
not capable of detecting the fact that the input did not consist of
a valid number - at least not in this naive way.

View File

@@ -0,0 +1,32 @@
# CHKSYSC: Which checks to perform
# Possible keywords:
# @GENERAL - all general (well-behaved) tests
# @OVERFLOW - behaviour on overflow
# @UNDERFLOW - behaviour on underflow
# @DIVISION - behaviour on division by zero
# @DOMAIN - behaviour on negative argument for sqrt()
# @CHANGECONST - behaviour if a constant literal is changed
# @COPYNULL - behaviour if a NULL pointer is used in strcpy()
# @COPYOVERLAP - behaviour if the receiving string overlaps the copied
# string in strcpy()
# @FREENULL - behaviour if a NULL pointer is freed
# @FREELOCAL - behaviour if a pointer to local memory is freed
#
# To include the test, the keyword must begin in the first column
# To exclude it, use the word SKIP in front.
#
# If you want concise information about the tests, put the keyword
# SUMMARY in (VERBOSE is just to show the opposite: a full report
# is default)
#
VERBOSE
@GENERAL
SKIP @OVERFLOW
SKIP @UNDERFLOW
SKIP @DIVISION
SKIP @DOMAIN
@CHANGECONST
SKIP @COPYNULL
@COPYOVERLAP
@FREENULL
@FREELOCAL

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,208 @@
@warning
File size should not exceed 800 lines!
@INTRODUCTION
CHKSYSFF: A program to determine the properties of the run-time
environment for FORTRAN 90 programs
The program presents information about:
- The numerical properties of the processor/compiler
- What character set is used?
- Manipulations with pointers and allocatable arrays
- Whether array assignments are properly implemented in the compiler
- Issues concerning the random number generation
Note:
The program is accompanied by a more comprehensive program written
in FORTRAN 77. As this language allows more variations in the
precise interpretation of the standard, it does concentrate on
various uncertainties in the behaviour of the run-time environment.
However, even a relatively new language like FORTRAN 90 has non-standard
extensions.
@LIST-KINDS
Integer and real kinds
----------------------
FORTRAN 90 has a number of portable features regarding the specification
of integers and reals of various lengths. To use these features you need
to know the "kinds" - numbers that indicates the kind of data you want.
The program tries to determine which integers and reals are supported by
the current run-time environment.
@CHAR-PROPERTIES
Ordinary characters
-------------------
The character set used by FORTRAN 90 consists of most characters you
find on ordinary QWERTY keyboards and none other. The language provides
standard ways of using different types of character sets besides the
ordinary, but there is no way (like for integers and reals) to find
out which are supported in the current environment. Instead, you will
have to consult the compiler's manual for that.
However, the default character set is supposed to have the following
properties:
- The functions ICHAR() and CHAR() are each others' inverse:
CHAR(ICHAR(c) = c for any character
- The functions IACHAR() and ACHAR() that give the ASCII code for the
characters are likewise related:
ACHAR(IACHAR(c) = c for any character
The program checks these relations for the FORTRAN 90 character set.
@RANDOM-NUMBERS
Random number generator
-----------------------
The language standard defines a random number generator that is supposed
to have a uniform distribution. Unfortunately, there are no guarantees
on the quality of the random number generator:
- What is the length of the pseudo-random number sequence?
- Does the generator truly have a uniform distribution?
This program is not capable of answering these and similar questions.
However, it does provide you with information on the quality:
- It draws 10000 samples and determines the distribution over ten bins
of equal size.
- It displays the number of samples in each bin, the average of the
samples and the standard deviation.
- It estimates via a simple test, if the distribution is roughly
uniform.
@RANDOM-NUMBERS-SUCCESS
One or more cases out of range. The random number generator fails the
uniformity test!
@RANDOM-NUMBERS-FAILURE
All classes within range. The random number generator seems uniform enough.
@UNDEFINED-POINTERS
Pointers: uninitialised
-----------------------
Pointers are a special type of data in FORTRAN 90. They can be used and
abused, as pointers in any language. What seems to be a very rare
feature indeed is the ability in FORTRAN 90 to check the status of
the pointers. This would enable you to make robust programs.
There is a slight complication, though, as uninitialised pointers
have an undefined status. This is checked for the present run-time
environment in this part of the program. Other properties and
pecularities are checked in a subsequent test.
@UNDEFINED-POINTERS-STATE
Checking the state of uninitialised pointers:
@UNDEFINED-POINTERS-TRUE
Undefined status for pointers: may be TRUE!
- you have to be careful!
@UNDEFINED-POINTERS-FALSE
Undefined status for pointers: seems systematically FALSE
- you have to be careful though. This test is NOT rigid
@UNDEFINED-ALLOCATED-STATE
Checking the state of uninitialised allocatable arrays:
@UNDEFINED-ALLOCATED-TRUE
Undefined status for allocatable arrays: may be TRUE!
- you have to be careful!
@UNDEFINED-ALLOCATED-FALSE
Undefined status for allocatable arrays: seems systematically FALSE
- you have to be careful though. This test is NOT rigid
@UNDEFINED-POINTERS-ASSOC
Checking the association of pointers to uninitialised allocatable arrays:
@UNDEFINED-ASSOC-SUCCESS
Succeeded in associating a pointer to an uninitialised allocatable array'
- you have to be careful!
@UNDEFINED-ASSOC-FAILURE
Failed to associate a pointer to an uninitialised allocatable array'
- this might be robust!
@ALLOCATED-POINTERS
Manipulating allocated memory
-----------------------------
FORTRAN 90 allows two ways of allocating and deallocating memory:
- Via ALLOCATABLE arrays
- Via POINTER variables
You may combine these methods, but be careful: it may render the
variables useless or you get run-time errors.
Consequently, this test may cause the program to crash.
@ALLOCATED-POINTERS-SKIPPED
Note:
Test is skipped.
@ALLOCATED-POINTERS-STEP1
Step 1:
- Allocate some memory via an allocatable array
- Deallocate it via a pointer to that memory
@ALLOCATED-STEP1-SUCCESS
Memory freed via pointer - no problem reported
Note: ALLOCATED() on allocatable array indicates:
@ALLOCATED-STEP1-WARNING
Memory still allocated according to target
@ALLOCATED-STEP1-NOALLOC
Memory no longer allocated according to target
@ALLOCATED-STEP1-FAILURE
Memory could not be freed via pointer
@ALLOCATED-POINTERS-STEP2
Step 2:
- Allocate some memory via an allocatable array
- Let a pointer point to that memory
- Deallocate the memory.
- What is the status of the pointer?
Note:
Because of the first test, the original allocatable array may have
become useless. This second test uses a second allocatable array.
@ALLOCATED-STEP2-STATUS
Memory to which pointer points has been freed. Check the status of
the pointer:
@ALLOCATED-STEP2-REASSOCIATE
Trying to reassociate the pointer:
@ALLOCATED-POINTERS-STEP3
Step 3:
- Try to free the allocated memory twice
@ALLOCATED-STEP3-SUCCESS
Memory freed twice - no problem reported
@ALLOCATED-STEP3-FAILURE
Memory could not be freed again
@ARRAY-ASSIGNMENT
Array assignments
-----------------
One of the nice features of FORTRAN 90 is that it allows array
manipulations as statements. For instance:
REAL , DIMENSION(1:10) :: A
REAL , DIMENSION(1:10) :: B
A = B
means that each element of B is copied into the corresponding element
of A.
This is just a simple example. You can also shift the elements of the
same array:
A(2:10) = A(1:9)
or turn the array around:
A(10:1:-1) = A
Whether such statements are indeed useful, depends on your application,
but they can be done!
This part of the CHKSYSFF program determines if for a number of cases,
the results are indeed as expected. It is not a definitive test and may
give more insight in what the statements mean than whether the compiler
is capable of handling them, but it seems a good exercise anyway.
@end-of-file

View File

@@ -0,0 +1,18 @@
# CHKSYSFF: Which checks to perform
# Possible keywords:
# @GENERAL - all general (well-behaved) tests
# @ALLOCATED-POINTERS - check pointers to allocated memory
# @INTERACTIVE - interactively select a set of tests
# (all of them disruptive!)
#
# To include the test, the keyword must begin in the first column
# To exclude it, use the word SKIP in front.
#
# If you want concise information about the tests, put the keyword
# SUMMARY in (VERBOSE is just to show the opposite: a full report
# is default)
#
VERBOSE
@GENERAL
@ALLOCATED-POINTERS
SKIP @INTERACTIVE

View File

@@ -0,0 +1,113 @@
! chkrandom.f90 --
! Program to check a few properties of the random_number and
! random_seed subroutines
!
! Note:
! The program must be called twice in succession
!
module random_numbers
contains
!
! Derived from the documentation of gfortran:
! A standard-conforming routine to initialise the RNG
!
! init_random_seed --
! Reset the random number generator via the system clock or via
! a predefined sequence
!
! Arguments:
! use_time (Optional) Use the system time or not
!
subroutine init_random_seed( use_time )
implicit none
logical, intent(in), optional :: use_time
integer :: i
integer :: seed_size
integer, dimension(:), allocatable :: seed
integer :: clock = 305721054
call random_seed( size = seed_size )
allocate( seed(seed_size) )
call random_seed( get = seed )
if ( use_time ) then
call system_clock( count = clock )
endif
seed = clock + 37 * (/ ( i-1, i = 1,seed_size ) /)
call random_seed( put = seed )
deallocate( seed )
end subroutine
end module
program chkrandom
implicit none
real :: r
real :: rsav
integer :: seed_size
integer, allocatable, dimension(:) :: seed_data
integer, allocatable, dimension(:) :: seed_save
logical :: exists
call system_clock( seed_size )
write(*,*) 'Clock: ', seed_size
!
! Check:
! Is the sequence always the same or not?
!
inquire( file = 'chkrandom.sav', exist = exists )
if ( exists ) then
open( 10, file = 'chkrandom.sav' )
read( 10, * ) rsav
close( 10 )
call random_number( r )
if ( abs(r-rsav) < 1.0e-5 ) then
write(*,*) 'The random number generator is initialised to the same sequence'
else
write(*,*) 'The random number generator uses a different sequence at each start-up'
endif
else
write(*,*) 'Simple checks on random number generator'
write(*,*) '----------------------------------------'
call random_number( r )
open( 10, file = 'chkrandom.sav' )
write( 10, '(f12.8)' ) r
close( 10 )
endif
!
! Inspect:
! Seed for the random number generator
!
call random_seed( size = seed_size )
allocate( seed_data(seed_size), seed_save(seed_size) )
call random_seed( get = seed_save )
!
! This call might reset the RNG to a different sequence
!
call random_seed
call random_seed( get = seed_data )
write(*,*) 'Size of the seed to the random number generator:', seed_size
write(*,*) 'Effect of call to RANDOM_SEED without arguments:'
if ( all( seed_data == seed_save ) ) then
write(*,*) ' RNG is reset to standard sequence'
else
write(*,*) ' RNG is reset to a different sequence each time'
endif
end program

View File

@@ -0,0 +1,243 @@
! fp_special.f90 --
! Investigate the behaviour of the program vis-a-vis special
! IEEE numbers and floating-point exceptions
!
! Note:
! To get full insight, the program may need to run several times
! successively skipping tests.
!
program fp_special
implicit none
integer :: test, success
logical :: exists
real :: y = 0.0
real :: z = 1.0e30
! Start afresh? If the file "fp_special.done" exists or the
! file "fp_special.next' does not.
!
test = -1
inquire( file = "fp_special.done", exist = exists )
if ( exists ) then
open( 10, file = "fp_special.done" )
close( 10, status = 'delete' )
test = 1
else
inquire( file = "fp_special.next", exist = exists )
if ( .not. exists ) then
test = 1
endif
endif
open( 10, file = "fp_special.next" )
if ( test == -1 ) then
read( 10, * ) test, success
rewind( 10 )
if ( success /= 1 ) then
write(*,*) 'Previous test failed! The program aborted'
endif
endif
!
! Select the next test
!
if ( test == 1 ) then
write( 10, * ) test, 0
rewind( 10 )
call division_by_zero
test = 2
endif
if ( test == 2 ) then
write( 10, * ) test, 0
rewind( 10 )
call not_a_number
test = 3
endif
if ( test == 3 ) then
write( 10, * ) test, 0
rewind( 10 )
call overflow
test = 4
endif
if ( test == 4 ) then
write( 10, * ) test, 0
rewind( 10 )
call underflow
endif
call negative_zero
close( 10 )
open( 10, file= "fp_special.done" )
write( 10, * ) 'Done'
close( 10 )
contains
! division_by_zero --
! Does the compiler support division by zero? Or does the program abort?
!
! Arguments:
! None
!
subroutine division_by_zero
real :: x
write( 10, * ) test+1, 0
write(*,*) 'Test: division by zero'
x = 1.0 / y
write(*,*) ' Test succeeded, the program continues'
write(*,*) ' Result of 1/0: ', x
if ( x > huge(1.0) ) then
write(*,*) ' Result is "infinity"'
else
write(*,*) ' Result is "huge" - it should be "infinity"'
endif
write( 10, * ) test+1, 1
end subroutine
! not_a_number --
! Does the compiler support not-a-numbers? Or does the program abort?
!
! Arguments:
! None
!
subroutine not_a_number
real :: x
write( 10, * ) test+1, 0
write(*,*) 'Test: creating Not-a-number'
x = 0.0 / y
write(*,*) ' Test succeeded, the program continues'
write(*,*) ' Result of 0/0: ', x
if ( x >= 0.0 .or. x <= 0.0 ) then
write(*,*) ' Result is 0.0 - it should be "NaN"'
else
write(*,*) ' Result is "NaN"'
endif
write( 10, * ) test+1, 1
end subroutine
! overflow --
! Does the compiler support overflow? Or does the program abort?
!
! Arguments:
! None
!
subroutine overflow
real :: x
write( 10, * ) test+1, 0
write(*,*) 'Test: computations with overflow'
x = 1.0e20 * z
write(*,*) ' Test succeeded, the program continues'
write(*,*) ' Result of 1.0e20 * 1.0e30: ', x
if ( x > huge(1.0) ) then
write(*,*) ' Result is "Infinity"'
else
write(*,*) ' Result is "huge"'
endif
write( 10, * ) test+1, 1
end subroutine
! underflow --
! Does the compiler support underflow (denormalised numbers)?
!
! Arguments:
! None
!
subroutine underflow
real :: x
real :: xprev
write( 10, * ) test+1, 0
write(*,*) 'Test: computations with underflow'
x = 1.0
do while ( x > tiny(x) )
xprev = x
x = x / 2.0
enddo
write(*,*) ' Test succeeded, the program continues'
write(*,*) ' Result of repeated halving: ', x
if ( x == 0.0 ) then
write(*,*) ' Note: no underflow - small values are truncated to zero'
else
write(*,*) ' Small values will gradually go to zero'
endif
write( 10, * ) test+1, 1
end subroutine
! negative_zero --
! Does the compiler support negative zeros?
!
! Arguments:
! None
!
subroutine negative_zero
logical :: supported
real :: neg_zero
supported = .false.
neg_zero = -0.0
if ( sign(1.0,neg_zero) < 0.0 ) then
supported = .true.
else
neg_zero = -tiny(1.0)
do while ( neg_zero /= 0 )
neg_zero = neg_zero / 2.0
enddo
if ( sign(1.0,neg_zero) < 0.0 ) then
supported = .true.
endif
endif
if ( supported ) then
write(*,*) 'Negative zeros are supported'
else
write(*,*) 'Negative zeros are NOT supported'
endif
neg_zero = sqrt( -0.0 )
if ( sign(1.0,neg_zero) < 0.0 ) then
write(*,*) 'SQRT(-0) is indeed negative (as required by the IEEE standard)'
else
write(*,*) 'SQRT(-0) is NOT negative! Inconsistent with the IEEE standard'
endif
end subroutine
end program

View File

@@ -0,0 +1,23 @@
@echo off
rem Small check: can a Fortran program return a value via the stop
rem statement?
set f95=g95
rem %f95% -o stopcode stopcode.f90
.\stopcode
if errorlevel 2 goto toohigh
if errorlevel 1 goto correct
echo "Return code was: 0 - should have been 1"
goto end
:correct
echo Correct return code found (1)
goto end
:toohigh
echo Return code was too high - should have been 1
:end

View File

@@ -0,0 +1,16 @@
#!/bin/sh
#
# Small check: can a Fortran program return a value via the stop
# statement?
#
f95=g95
#$f95 -o stopcode stopcode.f90
./stopcode
rc=$?
if [ $rc = 1 ]; then
echo 'Correct return code found (1)'
else
echo "Return code was: $rc - should have been 1"
fi

View File

@@ -0,0 +1,36 @@
# Makefile for "os_interact" test programs
#
# $Id: makefile,v 1.1 2008/08/12 13:26:02 arjenmarkus Exp $
#
include ../../config/config.mk
include ../../config/options.mk
PROGRAMS = dirsep$(EXEEXT) \
stopcode$(EXEEXT) \
reopen_stdin$(EXEEXT) \
all: $(PROGRAMS)
dirsep$(OBJEXT): dirsep.f90
$(FC) $(FFLAGS) dirsep.f90
dirsep$(EXEEXT): dirsep$(OBJEXT)
$(LD) $(LDFLAGS) $(LDOUTPUT) dirsep$(OBJEXT)
stopcode$(OBJEXT): stopcode.f90
$(FC) $(FFLAGS) stopcode.f90
stopcode$(EXEEXT): stopcode$(OBJEXT)
$(LD) $(LDFLAGS) $(LDOUTPUT) stopcode$(OBJEXT)
reopen_stdin$(OBJEXT): reopen_stdin.f90
$(FC) $(FFLAGS) reopen_stdin.f90
reopen_stdin$(EXEEXT): reopen_stdin$(OBJEXT)
$(LD) $(LDFLAGS) $(LDOUTPUT) reopen_stdin$(OBJEXT)
clean:
$(DELETE) *$(OBJEXT)
$(DELETE) $(PROGRAMS)
$(DELETE) *$(MODEXT)

View File

@@ -0,0 +1,26 @@
Interaction with the operating system
-------------------------------------
This directory contains several programs that have the goal of
probing the interaction with the operating system:
* stopcode.f90:
Can the STOP statement be used to return a return code to the
operating system?
Run the program via chkstop.sh (for Linux, OSX and UNIX like systems)
or chkstop.bat (for Windows)
* reopen_stdin.f90:
Can you reopen standard input so that after reading the contents
of a redirected file, you can interact directly with the user?
Run the program via reopen.sh (for Linux, OSX and UNIX like systems)
or reopen.bat (for Windows)
* dirsep.f90:
This program tests the behaviour of a Fortran program where
file names with directories are concerned: do you need to use a
backslash or is a forward slash okay too on Windows. It appears that
most compilers behave as expected with / on Windows. So there would be
no need to distinguish the Windows and the Linux case then. Just use a /.

View File

@@ -0,0 +1,8 @@
@echo off
rem Can a Fortran program reopen standard input?
set f95=g95
%f95% -o reopen_stdin reopen_stdin.f90
.\reopen_stdin < reopen.inp

View File

@@ -0,0 +1,3 @@
Line 1
Line 2
Last line

View File

@@ -0,0 +1,9 @@
#!/bin/sh
# Can a Fortran program reopen standard input?
#f95=gfortran
f95=g95
$f95 -o reopen_stdin reopen_stdin.f90
./reopen_stdin < reopen.inp

View File

@@ -0,0 +1,56 @@
! reopen_stdin --
! Program to read a file via redirection and then open standard
! input for ordinary interaction
!
! Based on an example by Charles Coldwell
! http://groups.google.com/group/comp.lang.fortran/browse_frm/thread/56ca2ee5b9d77453#
!
program stdin
implicit none
logical :: readline
character(len=40) :: line
write(*,*) 'Read from redirected file ...'
do
read (5,*,end=100) line
write (6,*) line
end do
100 close(5)
!
! Open the UNIX/Linux/OSX way
!
readline = .false.
open(unit=5,file="/dev/tty", status='old', err = 110)
write(*,*) 'Reopening standard input ... use ctrl-D to close the read'
do
read (5,*,end=200) line
readline = .true.
write (6,*) line
end do
goto 200
!
! Open the Windows way
!
110 continue
readline = .false.
write(*,*) 'Reopening standard input ... use ctrl-Z to close the read'
open(unit=5,file="con", status='old', err = 120)
write(*,*) 'Read from redirected file ...'
do
read (5,*,end=200) line
readline = .true.
write (6,*) line
end do
goto 200
120 continue
write(*,*) 'Did not succeed in reopening standard input!'
stop
200 continue
if ( .not. readline ) then
write(*,*) 'No line read after reopening standard input!'
endif
end program stdin

View File

@@ -0,0 +1,11 @@
! stopcode.f90 --
! Small program to check if the STOP statement can be used to
! transmit a return code to the operating system
!
! The code is to be received by the calling shell script or
! batch file. The correct value is 1
!
program stopcode
write(*,*) 'Stopping with return code 1'
stop 1
end program

View File

@@ -0,0 +1,392 @@
* @begin@ */
*
* syqrout.f(or) - routines to determine some properties of the
* run-time environment for FORTRAN programs
*
* Copyright (C) 1998 Arjen Markus
*
* Arjen Markus
*
*
* General information:
* This file contains the following routines:
* - SYQOS: Determine some capabilities of the operating
* system
* - SYQBIN: Determine how to open BINARY files (if possible)
* - SYQREC: Determine the length unit for direct access files
*
* @end@
*
* $Author$
* $Date$
* $Source$
* $Log$
*
* @@------------------------------------------------------------------
* Routine: SYQOS
* Author: Arjen Markus
* Purpose: Determine the type of the operating system
* Context: Used by applications
* Summary:
* Open specific files that will present if the OS is
* of some type. The presence of the files indicates the OS.
* Create a few files to check the conventions for file
* names. This further narrows the possibilities.
*
* Arguments:
* Name Type I/O Description
* ITYPE INT O Type of OS:
* -1 - Could not be determined
* 1 - UNIX
* 2 - DOS/Windows 3.x
* 3 - Windows 95/NT
* LNGNAM LOG O Are long file names allowed (> 14 characters)?
* IGNCAS LOG O Are file names case-sensitive?
* DIRSEP CHAR*1 O Character for separating directories
* --------------------------------------------------------------------
*
SUBROUTINE SYQOS( ITYPE , LNGNAM , IGNCAS , DIRSEP )
*
IMPLICIT NONE
*
INTEGER ITYPE
LOGICAL LNGNAM , IGNCAS
CHARACTER*(*) DIRSEP
*
LOGICAL OPEND , EXISTS
INTEGER I , LUN , IERR
CHARACTER*40 FILNAM
CHARACTER*20 FILUX
*
DATA FILUX / '/bin/sh' /
*
* -------- Find a free logical unit number first
*
ITYPE = -2
LNGNAM = .FALSE.
IGNCAS = .FALSE.
*
DO 110 I = 10,99
INQUIRE( I , OPENED = OPEND )
IF ( .NOT. OPEND ) THEN
LUN = I
GOTO 200
ENDIF
110 CONTINUE
*
* -------- If we get to this point, we could not open a file
*
RETURN
*
* -------- If the platform is UNIX, then the file /bin/sh will
* exist and be readable
*
200 CONTINUE
INQUIRE( FILE = FILUX , EXIST = EXISTS )
IF ( EXISTS ) THEN
ITYPE = 1
DIRSEP = '/'
ENDIF
*
* -------- For all other platforms, there is no fixed file to be
* looked for, except perhaps "autoexec.bat" but even then
* the procedure is complicated. So rely on the characteristics
* of the file names instead:
*
* Type long names ignore case in names
* UNIX yes/no no!
* DOS/WIN3.x no yes
* WIN95/NT yes yes (?)
*
* Long file names:
* - if not supported, the file name will be truncated or
* an error occurs
* Ignore case in file names:
* - the file can be opened as SYQOS.TST and syqos.tst
*
* Note:
* Some old versions of UNIX do not support long file names
* (they are truncated to 14 characters)
*
FILNAM = 'syqos_long_file_name'
OPEN( LUN , FILE = FILNAM , STATUS = 'UNKNOWN' ,
& IOSTAT = IERR )
IF ( IERR .EQ. 0 ) THEN
WRITE( LUN , * ) 'Test'
CLOSE( LUN )
*
IF ( ITYPE .EQ. 1 ) THEN
OPEN( LUN , FILE = FILNAM(1:14) , STATUS = 'OLD' ,
& IOSTAT = IERR )
ELSE
OPEN( LUN , FILE = FILNAM(1:8) , STATUS = 'OLD' ,
& IOSTAT = IERR )
ENDIF
*
* -------- Are long names supported?
*
IF ( IERR .EQ. 0 ) THEN
LNGNAM = .FALSE.
CLOSE( LUN , STATUS = 'DELETE' )
ELSE
LNGNAM = .TRUE.
ENDIF
*
* -------- Get rid of the file with the long name as well
*
OPEN( LUN , FILE = FILNAM , STATUS = 'OLD' ,
& IOSTAT = IERR )
IF ( IERR .EQ. 0 ) THEN
CLOSE( LUN , STATUS = 'DELETE' )
ENDIF
*
* -------- An error occurred in the first place
*
ELSE
LNGNAM = .FALSE.
ENDIF
*
* -------- Now test the distinction between uppercase and lowercase
*
FILNAM = 'syqos_xx'
OPEN( LUN , FILE = FILNAM , STATUS = 'UNKNOWN' ,
& IOSTAT = IERR )
IF ( IERR .EQ. 0 ) THEN
WRITE( LUN , * ) 'Test'
CLOSE( LUN )
*
FIlNAM = 'SYQOS_XX'
OPEN( LUN , FILE = FILNAM , STATUS = 'OLD' ,
& IOSTAT = IERR )
*
* -------- Are uppercase and lowercase distinct?
*
IF ( IERR .EQ. 0 ) THEN
IGNCAS = .TRUE.
CLOSE( LUN , STATUS = 'DELETE' )
ELSE
LNGNAM = .TRUE.
ENDIF
*
* -------- Get rid of the file with the lowercase name as well
*
FILNAM = 'syqos_xx'
OPEN( LUN , FILE = FILNAM , STATUS = 'OLD' ,
& IOSTAT = IERR )
IF ( IERR .EQ. 0 ) THEN
CLOSE( LUN , STATUS = 'DELETE' )
ENDIF
*
* -------- An error occurred in the first place:
* this represents an unsupported situation!
*
ELSE
ITYPE = -1
ENDIF
*
* -------- Finally we can make a good guess about the operating system
*
IF ( ITYPE .NE. 1 .AND. ITYPE .NE. -1 ) THEN
DIRSEP = '\\'
IF ( .NOT. LNGNAM .AND. IGNCAS ) ITYPE = 2
IF ( LNGNAM ) ITYPE = 3
ENDIF
*
RETURN
END
* @@------------------------------------------------------------------
* Routine: SYQBIN
* Author: Arjen Markus
* Purpose: Determine if and how a BINARY file can be opened
* Context: Used by applications
* Summary:
* Open a scratch-file, with various possible keywords
* Write a single number to it to check if that works.
* If so, binary files can be opened that way.
*
* Arguments:
* Name Type I/O Description
* ALLOWD LOG O If true, binary files are allowed. If false,
* you need to use unformatted sequential files.
* FORM CHAR*20 O Value for keyword FORM=
* ACCESS CHAR*20 O Value for keyword ACCESS=
* --------------------------------------------------------------------
*
SUBROUTINE SYQBIN( ALLOWD , FORM , ACCESS )
*
* -------- One common extension to the standard
*
IMPLICIT NONE
*
LOGICAL ALLOWD
CHARACTER*(*) FORM , ACCESS
*
INTEGER NOTYPE
PARAMETER ( NOTYPE = 2 )
*
INTEGER IERR , LUN , I , ITYPE , IDUMMY
CHARACTER*20 FRMTYP(NOTYPE) , ACCTYP(NOTYPE)
LOGICAL OPEND
*
SAVE FRMTYP , ACCTYP , ITYPE
DATA ITYPE / -1 /
DATA ( FRMTYP(I) , ACCTYP(I) , I = 1,NOTYPE ) /
& 'BINARY' , 'SEQUENTIAL' ,
& 'UNFORMATTED' , 'TRANSPARENT' /
*
* -------- Open the scratch file
*
DO 110 I = 10,99
INQUIRE( I , OPENED = OPEND )
IF ( .NOT. OPEND ) THEN
LUN = I
GOTO 200
ENDIF
110 CONTINUE
*
* -------- If we end up here, then we did not find a free unit
*
ALLOWD = .FALSE.
FORM = 'UNFORMATTED'
ACCESS = 'SEQUENTIAL'
RETURN
*
* -------- Try to open a scratch-file and write to it
*
200 CONTINUE
IDUMMY = 1
ITYPE = -1
*
DO 210 I = 1,NOTYPE
OPEN( LUN , IOSTAT = IERR , FORM = FRMTYP(I) ,
& ACCESS = ACCTYP(I) )
IF ( IERR .EQ. 0 ) THEN
WRITE( LUN , IOSTAT = IERR ) IDUMMY
CLOSE( LUN )
IF ( IERR .EQ. 0 ) THEN
ITYPE = I
GOTO 300
ENDIF
ENDIF
210 CONTINUE
*
* -------- Conclusion: set the parameters
*
300 CONTINUE
IF ( ITYPE .EQ. -1 ) THEN
ALLOWD = .FALSE.
FORM = 'UNFORMATTED'
ACCESS = 'SEQUENTIAL'
ELSE
ALLOWD = .TRUE.
FORM = FRMTYP(ITYPE)
ACCESS = ACCTYP(I)
ENDIF
*
RETURN
END
* @@------------------------------------------------------------------
* Routine: SYQREC
* Author: Arjen Markus
* Purpose: Determine the length unit for direct access files
* Context: Used by applications
* Summary:
* Open a scratch-file, with record length 1.
* Write a CHARACTER*1 string to eight sequential records.
* Set a CHARACTER*8 string to an empty string.
* Read eight characters from the first record into the
* string.
* Find out how many characters were filled.
*
* Arguments:
* Name Type I/O Description
* NRCBYT INT O Number of characters in a record
* of 1 unit (-1 if the file could not
* be opened)
* --------------------------------------------------------------------
*
SUBROUTINE SYQREC( NRCBYT )
*
* -------- One common extension to the standard
*
IMPLICIT NONE
*
INTEGER NRCBYT
*
INTEGER IERR , LUN , I , J
CHARACTER*1 STROUT
CHARACTER*8 STRIN , STRDEF
LOGICAL OPEND
*
* -------- Open the scratch file
*
DO 110 I = 10,99
INQUIRE( I , OPENED = OPEND )
IF ( .NOT. OPEND ) THEN
LUN = I
OPEN ( LUN , STATUS = 'SCRATCH' , IOSTAT = IERR ,
& FORM = 'UNFORMATTED' ,
& ACCESS = 'DIRECT' , RECL = 1 )
IF ( IERR .NE. 0 ) THEN
GOTO 900
ELSE
GOTO 200
ENDIF
ENDIF
110 CONTINUE
*
* -------- If we end up here, then we did not find a free unit
*
GOTO 900
*
* -------- Write the eight records
*
200 CONTINUE
STROUT = 'A'
DO 210 I = 1,8
WRITE( LUN , IOSTAT = IERR , REC = I ) STROUT
IF ( IERR .NE. 0 ) THEN
CLOSE( LUN )
GOTO 900
ENDIF
210 CONTINUE
*
* -------- Try reading the eight characters
*
NRCBYT = 8
STRDEF = '12345678'
DO 290 I = 1,8
STRIN = STRDEF
READ( LUN , IOSTAT = IERR , REC = I )
& ( STRIN(J:J) , J = 1,I )
*
* -------- What could we read?
* Assumption: if an error occurs, the rest of the input is
* not changed!
*
IF ( IERR .NE. 0 ) THEN
DO 220 J = 1,8
IF ( STRIN(J:J) .EQ. STRDEF(J:J) ) THEN
NRCBYT = J - 1
GOTO 300
ENDIF
220 CONTINUE
ENDIF
290 CONTINUE
*
* -------- We have found something
*
300 CONTINUE
CLOSE( LUN )
RETURN
*
* -------- Some type of error
*
900 CONTINUE
NRCBYT = -1
RETURN
*
END

View File

@@ -0,0 +1,27 @@
# Options for g95
#
# Maybe define LDOUTPUT as LDOUTPUT=-o $@
#
SPACE = \
SEP = /
FC = g95
FFLAGS_NORMAL = -c
FFLAGS_DEBUG = -c -g
FFLAGS_OPTIMISE = -c -O
LD = g95
LDFLAGS_NORMAL =
LDFLAGS_DEBUG = -g
LDFLAGS_OPTIMISE =
#LDOUTPUT = -o$(SPACE)
LDOUTPUT = -o $@
LIB = ar r
OBJEXT = .o
EXEEXT =
MODEXT = .mod
DELETE = rm -f

View File

@@ -0,0 +1,4 @@
# Options for Compaq Visual Fortran
#
Dummy for the moment!

View File

@@ -0,0 +1,5 @@
# Options for a build wih debugging enabled
#
FFLAGS = $(FFLAGS_DEBUG)
LDFLAGS = $(LDFLAGS_DEBUG)

View File

@@ -0,0 +1,27 @@
# Options for g95
#
# Maybe define LDOUTPUT as LDOUTPUT=-o $@
#
SPACE = \
SEP = /
FC = g95
FFLAGS_NORMAL = -c
FFLAGS_DEBUG = -c -g
FFLAGS_OPTIMISE = -c -O
LD = g95
LDFLAGS_NORMAL =
LDFLAGS_DEBUG = -g
LDFLAGS_OPTIMISE =
#LDOUTPUT = -o$(SPACE)
LDOUTPUT = -o $@
LIB = ar r
OBJEXT = .o
EXEEXT =
MODEXT = .mod
DELETE = rm -f

View File

@@ -0,0 +1,27 @@
# Options for gfortran
#
# Maybe define LDOUTPUT as LDOUTPUT=-o $@
#
SPACE = \
SEP = /
FC = gfortran
FFLAGS_NORMAL = -c
FFLAGS_DEBUG = -c -g
FFLAGS_OPTIMISE = -c -O
LD = gfortran
LDFLAGS_NORMAL =
LDFLAGS_DEBUG = -g
LDFLAGS_OPTIMISE =
#LDOUTPUT = -o$(SPACE)
LDOUTPUT = -o $@
LIB = ar r
OBJEXT = .o
EXEEXT =
MODEXT = .mod
DELETE = rm -f

View File

@@ -0,0 +1,3 @@
program idc
write(*,*) 'Hello'
end program

View File

@@ -0,0 +1,20 @@
# Makefile for Ftcl-example "console"
#
# $Id: makefile,v 1.1 2006/08/13 07:04:55 arjenmarkus Exp $
#
include ../../config.mk
PROGRAM = interactive$(EXEEXT)
all: $(PROGRAM)
interactive$(OBJEXT): interactive.f90
$(FC) $(FFLAGS) interactive.f90
$(PROGRAM): interactive$(OBJEXT)
$(LD) $(LDFLAGS) $(LDOUTPUT) interactive$(OBJEXT) $(LIBS)
clean:
(DELETE) *$(OBJEXT)
(DELETE) *$(EXEEXT)
(DELETE) *$(MODEXT)

View File

@@ -0,0 +1,5 @@
# Options for a normal build
#
FFLAGS = $(FFLAGS_NORMAL)
LDFLAGS = $(LDFLAGS_NORMAL)

View File

@@ -0,0 +1,5 @@
# Options for a build with optimisation turned on
#
FFLAGS = $(FFLAGS_OPTIMISE)
LDFLAGS = $(LDFLAGS_OPTIMISE)

View File

@@ -0,0 +1,5 @@
# Options for a normal build
#
FFLAGS = $(FFLAGS_NORMAL)
LDFLAGS = $(LDFLAGS_NORMAL)

View File

@@ -0,0 +1,18 @@
Configuration
-------------
This directory contains include files defining the properties of various
compilers. Via the configure.bat/configure.sh scripts one compiler is
chosen and the corresponding include file is copied to the file
"config.mk".
In a similar way the configuration options are chosen and implemented:
one of normal.mk, debug.mk and optimise.mk is copied to "options.mk"
Note:
This is indeed the nth garden-variety configuration system, but any
general system (autoconf, CMake, Scons to name the best-known) requires
either a Linux/UNIX like OS or at least extra software.
I deemed this project too "small" to warrant such a configuration
system. The method used here is easy to expand and the configuration
options themselves are simple.

View File

@@ -0,0 +1,4 @@
# Options for Salford Fortran
#
Dummy for the moment

View File

@@ -0,0 +1,109 @@
@echo off
echo on
rem configure.bat --
if @%1 == @-help goto help
if @%1 == @/help goto help
if @%1 == @-? goto help
if @%1 == @/? goto help
goto analyse
:help
echo Configure Flibs: identify the compiler and set the build options
echo .
echo To pick a specific compiler, use:
echo c:\> configure name options
echo Available compilers:
echo - cvf - Compaq Visual Fortran
echo - salford - Salford Fortran
echo - f95 - generic Fortran 95 compiler
echo .
echo Available build options:
echo -debug
echo -normal
echo -optimise
echo .
echo Run this batch file from the central directory!
pause
goto veryend
rem
rem Identify the build options ...
rem
:analyse
cd config
if errorlevel 1 goto error
if exist config.mk del config.mk
if exist options.mk del options.mk
copy normal.mk options.mk
if @%1 == @-debug copy debug.mk options.mk
if @%1 == @-optimise copy optimise.mk options.mk
if @%2 == @-debug copy debug.mk options.mk
if @%2 == @-optimise copy optimise.mk options.mk
echo Identifying compiler ... %1
if @%1 == @ goto start
if @%1 == @cvf goto cmp_cvf
if @%1 == @salford goto cmp_salford
if @%1 == @f95 goto cmp_generic
if @%2 == @cvf goto cmp_cvf
if @%2 == @salford goto cmp_salford
if @%2 == @salford goto cmp_generic
echo Unknown compiler: %1 - searching for known compilers instead
:start
rem -----------------------------------------------------------
rem Compaq Visual Fortran
rem -----------------------------------------------------------
rem
:cmp_cvf
df.exe /compile_only idc.f90
if errorlevel 1 goto after_df
echo Compiler: Compaq Visual Fortran
copy cvf.mk config.mk
goto end
:after_df
rem -----------------------------------------------------------
rem Salford Ftn95
rem -----------------------------------------------------------
rem
:cmp_salford
ftn95.exe idc.f90
if errorlevel 1 goto after_salf
echo Compiler: Salford Fortran
copy salford.mk config.mk
goto end
:after_salf
rem -----------------------------------------------------------
rem Generic Fortran 95 compiler
rem -----------------------------------------------------------
rem
:cmp_generic
f95.exe idc.f90
if errorlevel 1 goto after_generic
echo Compiler: Generic Fortran 95 compiler
copy f95.mk config.mk
goto end
:after_generic
rem
rem No suitable compiler found
rem
echo No suitable compiler could be identified
goto end
:error
echo Error:
echo Subdirectory "config" not found. Please start this batch file in
the echo central directory
goto veryend
:end
cd ..
:veryend

View File

@@ -0,0 +1,112 @@
#!/usr/bin/sh
# Configuration for Flibs: find a suitable compiler and set build options
#
if test @$1 = "@-help" -o @$1 = "@-?" ;then
echo Configure Flibs: identify the compiler and set the build options
echo .
echo To pick a specific compiler, use:
echo % configure name options
echo Available compilers:
echo - gfortran - Gnu\'s Fortran 95 compiler
echo - g95 - Alternative Fortran 95 compiler
echo - f95 - generic Fortran 95 compiler
echo ""
echo Available build options:
echo -debug
echo -normal
echo -optimise
echo ""
echo Run this batch file from the central directory!
exit
fi
cd config
#
# Clean up
#
rm -f config.mk
rm -f options.mk
#
# Build options
#
options=normal
if test @$1 == "@-normal" -o @$2 == "@-normal" ;then
options=normal
fi
if test @$1 == "@-debug" -o @$2 == "@-debug" ;then
options=debug
fi
if test @$1 == "@-optimise" -o @$2 == "@-optimise" ;then
options=optimise
fi
cp $options.mk options.mk
#
# Identify the compiler
#
check=""
if test @$1 == "@gfortran" -o @$2 == "@gfortran" ;then
check=gfortran
fi
if test @$1 == "@g95" -o @$2 == "@g95" ;then
check=g95
fi
if test @$1 == "@f95" -o @$2 == "@f95" ;then
check=f95
fi
found=0
# -------------------------------------------------------------------------
# Compiler: gfortran
# -------------------------------------------------------------------------
if test $found -eq 0 -a \( "$check" == "" -o "$check" == "gfortran" \) ;then
echo Checking gfortran ...
gfortran idc.f90
if test $? -eq 0 ;then
found=1
compiler=gfortran
fi
fi
# -------------------------------------------------------------------------
# Compiler: g95
# -------------------------------------------------------------------------
if test $found -eq 0 -a \( "$check" == "" -o "$check" == "g95" \) ;then
echo Checking g95 ...
g95 idc.f90
if test $? -eq 0 ;then
found=1
compiler=g95
fi
fi
# -------------------------------------------------------------------------
# Compiler: f95
# -------------------------------------------------------------------------
if test $found -eq 0 -a \( "$check" == "" -o "$check" == "f95" \) ;then
echo Checking f95 ...
f95 idc.f90
if test $? -eq 0 ;then
found=1
compiler=f95
fi
fi
#
# Now that we have found a compiler, copy the settings
#
if test $found -eq 1 ;then
echo Compiler: $compiler
cp $compiler.mk config.mk
else
echo No suitable compiler found!
fi
cd ..
exit

View File

@@ -0,0 +1,221 @@
Note on array-valued functions
One feature of Fortran that deserves more attention in my opinion
is the use of _array-valued functions_. In particular this feature
can be used to achieve a high-level programming style, much like what
John Backus advocated in his ACM speech (Backus, ...). Many of the
intrinsic functions do and Fortran's array operations are further,
be it implicit, examples. Here is a simple case of what I am talking
about:
Suppose you have an array of data and you want to print only those values
that are larger than some threshold. One way of doing that is:
do i = 1,size(data)
if ( data(i) > threshold ) then
write( *, '(f10.4)' ) data(i)
endif
enddo
If you use te _pack_ intrinsic function, this can be written more
compactly:
write( *, '(f10.4)' ) pack( data, data > threshold )
We can also pass the result of _pack_ to another function or subroutine,
for instance one that determines basic statistical properties:
write( *, '(a,f10.4)') 'Mean of values above mean:', &
mean( pack( data, data > mean(data) ) )
with _mean_ looking like:
real function mean( data )
real, dimension(:) :: data
mean = sum(data)/max(1,size(data))
end function mean
Compare this to an approach with work arrays (so that we can
reuse the _mean_ function):
meanv = mean(data)
count = 0
do i = 1,size(data)
if ( data(i) > meanv ) then
count = count + 1
work(count) = data(i)
endif
enddo
write( *, '(a,f10.4)') 'Mean of values above mean:', &
mean( work(1:count) )
Or, bypassing the _mean_ function, because it is so simple:
meanv = mean(data) ! We do need to determine the threshold
sumv = 0.0
count = 0
do i = 1,size(data)
if ( data(i) > meanv ) then
count = count + 1
sumv = sumv + data(i)
endif
enddo
write( *, '(a,f10.4)') 'Mean of values above mean:', &
sumv/max(1,count)
So, using such functions can lead to more compact, and, in
many cases, clearer code. But they do have drawbacks:
- You can not access individual elements directly, for instance,
to determine the second largest element of an array, you
need to store the result of a sort function to a proper
array first.
- Similarly, if you need the result in more than one place,
you have to copy them to an actual array or pass them
to a function or subroutine.
- The functions produce intermediate arrays that need to be
allocated and deallocated at appropriate times. This may
influence the performance of the program.
The next two examples are more elaborate: a number-theoretical
problem and a compact implementation of the QuickSort algorithm.
The first requires a bit of explanation: it is a number-theoretical
problem regarding the spacing of irrational numbers (book...). The
numbers considered are: n alpha mod 1, n = 1 .. N. If we sort these
numbers, the spacing between successive numbers can take only one
of three distinct values (here the interval [0,1) is wrapped, so
that 0.9 and 0.1 are 0.2 apart, not 0.8). The program below
demonstrates this:
It constructs an array of these numbers, sorts them (using a
simple algorithm):
program nneighbours
implicit none
integer :: i
write(*,'(f10.4)') cdiff( sort( (/ (mod(i**2*sqrt(2.0),1.0) ,i=1,20) /) ) )
contains
function cdiff( array )
real, dimension(:) :: array
real, dimension(size(array)) :: cdiff
cdiff = abs( array - cshift(array,1) )
cdiff = min( cdiff, 1.0 - cdiff )
end function
function sort( array )
real, dimension(:) :: array
real, dimension(size(array)) :: sort
real :: temp
integer :: i, j
sort = array
do i = 1,size(sort)
do j = i+1,size(sort)
if ( sort(i) > sort(j) ) then
temp = sort(i)
sort(i) = sort(j)
sort(j) = temp
endif
enddo
enddo
end function
end program
A more traditional program would look like this:
program nneighbours
implicit none
integer :: i
integer, parameter :: n = 20
real, dimension(n) :: data
real, dimension(n) :: data
do i = 1,n
data(i) = mod(i*sqrt(2.0),1.0)
enddo
call sort( data )
call cdiff( data )
write(*,'(f10.4)') data
contains
subroutine cdiff( array )
real, dimension(:) :: array
real, dimension(size(array)) :: work
integer :: i
do i = 2,size(array)
work(i) = array(i) - array(i-1)
enddo
work(1) = arrry(1) - array(size(array)
do i = 1,size(array)
array(i) = min( abs(work(i)), abs(1.0-work(i))
enddo
end subroutine
subroutine sort( array )
real, dimension(:) :: array
real :: temp
integer :: i, j
do i = 1,size(array)
do j = i+1,size(array)
if ( array(i) > array(j) ) then
temp = sort(i)
array(i) = array(j)
array(j) = temp
endif
enddo
enddo
end function
end program
With array constructors we can do even more surprising things. This
implementation of the well-known QuickSort algorithm may not be fast
(and it consumes more memory than necessary), but it is rather
compact:
recursive function qsort_reals( data ) result( sorted )
real, dimension(:), intent(in) :: data
real, dimension(1:size(data)) :: sorted
if ( size(data) > 1 ) then
sorted = (/ qsort_reals( pack( data(2:), data(2:) > data(1) ) ), &
data(1), &
qsort_reals( pack( data(2:), data(2:) <= data(1) ) ) /)
else
sorted = data
endif
end function
I leave the construction of a more traditional version as an exercise.
You can also experiment with a more robust version that has better
worse-case behaviour.
This style of programming is not always the best way to solve a
problem: the QuickSort routine above creates multiple copies of the
array during the recursion, but if used carefully, it helps create
programs that are easy to understand and easily seen to be correct.
Literature
J. Backus
Number-theory book

View File

@@ -0,0 +1,118 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin automatic_differentiation n 1.0]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Implement the "automatic differentation" technique}]
[description]
The [strong automatic_differentiation] module defines a derived
type that enables you (via overloading common mathematical
functions and operators) to:
[list_begin bullet]
[bullet]
Define a mathematical function in the usual way
[bullet]
Evaluate that function [strong and] its first derivative at the same
time
[list_end]
without having to program that first derivative.
[para]
The module uses real numbers of the kind "wp" as defined in the
auxiliary module [strong select_precision].
[para]
(I was pointed to this technique by Simon Geard, a couple of years ago,
in the context of one of the many language shootouts on the Internet.)
[section "EXAMPLE"]
In numerical methods like Newton-Raphson for finding a root of the
equation "f(x) = 0", you need the first derivative:
[example {
x(k+1) = x(k) - f(x(k)) / f'(x(k))
}]
Rather than implementing the function and its first derivatives as
separate functions, using this module you can dispense with manually
determining the first derivative:
[example {
program root
use automatic_differentation
type(AUTODERIV) :: x
type(AUTODERIV) :: res
integer :: i
!
! First estimate
!
x = derivvar( 1.0_wp )
do i = 1,10
res = f(x)
x = x - res.v / res.dv
enddo
write(*,*) 'Root: ', x.v
contains
type(AUTODERIV) function f(x)
type(AUTODERIV) :: x
f = x + cos(x)
end function f
end program
}]
[section "DATA TYPES AND ROUTINES"]
The module defines a single data type, AUTODERIV, and one specific
function, derivvar().
[list_begin definitions]
[call [cmd "use automatic_differentiation"]]
The name of the module
[call [cmd "type(AUTODERIV)"]]
The type has two fields:
[list_begin arg]
[arg_def "real(wp)" v]
The value of the variable/function (or any intermediate result)
[arg_def "real(wp)" dv]
The first derivative
[list_end]
[nl]
[call [cmd "x = derivvar( value )"]]
Use this function to properly initialise the program
variable x as a "mathematical" variable. (It sets the derivative
of x to 1, because otherwise it would be regarded as a constant).
[list_begin arg]
[arg_def "real(wp)" value]
The value of the "mathematical" variable.
[list_end]
[list_end]
[manpage_end]

View File

@@ -0,0 +1,154 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/backtracking n 1.1]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Backtracking}]
[description]
The module [emph Backtracking] implements in a general way a well-known
algorithm to solve certain combinatorial problems. The module actually
consists of a single routine that forms the framework of the algorithm
and is to be used in conjunction with a small set of user-defined
routines that implement the specific problem.
[para]
The backtracking technique is especially useful if you can build a
solution in stages. The classic example is the "eight queens" problem,
where you must place eight queens on a chess board in such a way that
none can get another in one move. A little thought shows that
each queen must be placed in its own column (or row). Placing the first
queen in the first column gives us eight possibilities. Placing the
second is only possible if it is not within the range of the first one.
[para]
If it is, it makes no sense to continue so we can eliminate in the
second step a whole subset of possible configurations, namely those with
the second queeen within range of the first.
We can continue building up a partial solution in this way until we
finally have eight queens on the board.
[para]
The idea of the module is that all the data that describe a possible
partial solution to the problem are contained in a derived type called
SOLUTION_DATA. Then the user-defined routine [emph generate] generates
a new set of partial solutions based on this.
[para]
The new set is examined to see if any is acceptable within the context
of the problem, which is done via another user-defined routine.
[para]
Each acceptable solution within this new step may give rise to its own
set of further solutions. This way the partial solutions are extended in
each step until finally a complete solution is found.
[section ROUTINES]
The module backtracking contains
[list_begin definitions]
[call [cmd "call runtests( testproc )"]]
Routine to start the unit tests. It checks if the file "funit.run"
exists. If so, it will call the subroutine [emph testproc] that was
passed. Otherwise it will simply return, so that the ordinary program
execution may continue.
[nl]
If the subroutine testproc returns, the program stops.
[list_begin arg]
[arg_def "subroutine" testproc]
Subroutine that calls the individual test routines. It takes no
arguments. It wil generally exist of a series of calls to the
routine [emph test] - see below.
[list_end]
[call [cmd "call test( proc, text )"]]
Routine to run the individual unit test routine (emph proc). It decides
if the test has not run yet and if so, the test routine is called.
Otherwise it is skipped.
[nl]
[emph test] takes care of all administrative details.
[nl]
Note: to make it possible to use [emph private] unit test routines,
the source code of this subroutine is kept in a separate file,
[emph funit_test.f90] that should be included in an appropriate
place in the program's sources. This way, you can make it a private
routine in each module. The only public access to the unit testing
routines is then via the subroutine [emph testproc] that is passed to
[emph runtests].
[list_begin arg]
[arg_def "subroutine" proc]
Subroutine that implements an individual unit test. It takes no
arguments. Within each such subroutine the complete unit test is run.
[arg_def "character(len=*), intent(in)" text]
Text describing the particular unit test. It is printed in the log
file.
[list_end]
[call [cmd "call assert_true( cond, text )"]]
Routine to check that a condition is true. If not, a message is printed
in the log file and the number of failures is increased.
[list_begin arg]
[arg_def "logical" cond]
The condition to be checked
[arg_def "character(len=*), intent(in)" text]
Text describing the condition
[list_end]
[call [cmd "exists = funit_file_exists( filename )"]]
Logical function to check that a particular file exists
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be checked
[list_end]
[call [cmd "call funit_get_lun( lun )"]]
Subroutine to get a free LU-number
[list_begin arg]
[arg_def "integer, intent(out)" lun]
Next free LU-number
[list_end]
[call [cmd "call funit_remove_file( filename )"]]
Subroutine to remove (delete) a file
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be removed
[list_end]
[call [cmd "call funit_make_empty_file( filename )"]]
Subroutine to make a new, empty file
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be created
[list_end]
[list_end]
[section TODO]
The following things are still left to do:
[list_begin bullet]
[bullet]
Proper inclusion of the routine [emph prolog] and [emph epilog]
[bullet]
Extension of the set of assertion routines
[list_end]
[manpage_end]

View File

@@ -0,0 +1,245 @@
<! -- -*- flibs -*- doctools manpage
-->
<html><head>
<title>binary_streams - flibs </title>
</head>
<! -- Generated from file 'binstreams.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id$ binary_streams.n
-->
<body>
<h1> binary_streams(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> binary_streams - Implement binary streams
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#data_types_and_routines">DATA TYPES AND ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#implementation_notes">IMPLEMENTATION NOTES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>use binary_streams</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>type(BINARY_STREAM)</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>call binstream_open( stream, lun, filename, error )</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>call binstream_close( stream )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call binstream_seek( stream, start, offset )</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>pos = binstream_tell( stream )</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>call binstream_read( stream, var, error )</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>call binstream_write( stream, var, error )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>binary_streams</em> module defines a set of subroutines and
functions that allows you to read a file as if it were a &quot;stream&quot;
of bytes, rather than, as is more usual a set of records.
<p>
The module uses direct-access file I/O but hides the fact that
you read from individual records. By providing two routines to
query and set the position of the next read/write action and by
automatically positioning a &quot;file pointer&quot; otherwise, the module offers
the benefits of both direct-access files and sequential files.
<p>
<em>Note:</em> In Fortran 2003, the notion of &quot;streams&quot; is formalised.
This module will be superfluous with compilers supporting Fortran 2003.
Also there are a number of issues that may or may not come into play on
a particular system - see the section on <a href="#implementation_notes">IMPLEMENTATION NOTES</a>.
<h2><a name="data_types_and_routines">DATA TYPES AND ROUTINES</a></h2>
<p>
The module defines a single data type, BINARY_STREAM, and several
functions and subroutines:
<dl>
<dt><a name="1"><b class='cmd'>use binary_streams</b> </a><dd>
The name of the module
<br><br>
<dt><a name="2"><b class='cmd'>type(BINARY_STREAM)</b> </a><dd>
Files are opened and the necessary data are kept in variables of
this type.
<br><br>
<dt><a name="3"><b class='cmd'>call binstream_open( stream, lun, filename, error )</b> </a><dd>
Open the file &quot;filename&quot; using the LU-number &quot;lun&quot;. If some error
occurs, the argument &quot;error&quot; is set to true.
<br><br>
<dl>
<dt>type(binary_stream) <i class='arg'>stream</i><dd>
The variable by which to reference the file
<br><br>
<dt>integer, intent(in) <i class='arg'>lun</i><dd>
The LU-number to connect the file to
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>filename</i><dd>
The name of the file to open
<br><br>
<dt>logical, intent(out) <i class='arg'>error</i><dd>
Argument indicating whether opening the file was successful or not.
Note that the file is opened with read/write access (though not
explicitly) and that it is opened in such a way that the record length
is 4 bytes. If this is not possible (for any number of reasons), an
error is returned.
</dl>
<br><br>
<dt><a name="4"><b class='cmd'>call binstream_close( stream )</b> </a><dd>
Close the file that was opened as a stream.
<br><br>
<dl>
<dt>type(binary_stream) <i class='arg'>stream</i><dd>
The variable by which to reference the file
</dl>
<br><br>
<dt><a name="5"><b class='cmd'>call binstream_seek( stream, start, offset )</b> </a><dd>
Set the position in the file (either from the start or from the current
position).
<br><br>
<dl>
<dt>type(binary_stream) <i class='arg'>stream</i><dd>
The variable by which to reference the file
<br><br>
<dt>logical, intent(in) <i class='arg'>start</i><dd>
If true, the offset is from the start of the file. Otherwise it is an
offset from the current position.
<br><br>
<dt>integer, intent(in) <i class='arg'>offset</i><dd>
The number of bytes to skip from the given position. Zero means either
the start of the file or the same position.
</dl>
<br><br>
<dt><a name="6"><b class='cmd'>pos = binstream_tell( stream )</b> </a><dd>
Return the current position in the file (in bytes, the first byte is
taken as 0).
<br><br>
<dl>
<dt>type(binary_stream) <i class='arg'>stream</i><dd>
The variable by which to reference the file
</dl>
<br><br>
<dt><a name="7"><b class='cmd'>call binstream_read( stream, var, error )</b> </a><dd>
Read a variable &quot;var&quot; from the current position in the file.
<br><br>
<dl>
<dt>type(binary_stream) <i class='arg'>stream</i><dd>
The variable by which to reference the file
<br><br>
<dt>(...), intent(out) <i class='arg'>var</i><dd>
The variable to be read. It can be either a character string, a
(default) integer, a (default) real, a (default) logical or a
double-precision real. Also one- and two-dimensional arrays of these
types are supported.
</dl>
<br><br>
<dt><a name="8"><b class='cmd'>call binstream_write( stream, var, error )</b> </a><dd>
Write a variable &quot;var&quot; at the current position in the file.
<br><br>
<dl>
<dt>type(binary_stream) <i class='arg'>stream</i><dd>
The variable by which to reference the file
<br><br>
<dt>(...), intent(in) <i class='arg'>var</i><dd>
The variable to be written. It can be either a character string, a
(default) integer, a (default) real, a (default) logical or a
double-precision real. Also one- and two-dimensional arrays of these
types are supported.
</dl>
</dl>
<h2><a name="implementation_notes">IMPLEMENTATION NOTES</a></h2>
<p>
The module makes a number of assumptions:
<ul>
<li>
Any file can be opened as a direct-acess file with any
record length
<br><br>
<li>
To avoid complicated code the files are opened with
records of 4 bytes long (ordinary the record length is 4
or 1 - the length unit is system-dependent!). This means
that systems where the unit is not 1 or 4 bytes are not
supported - this could include 64-bits systems.
<br><br>
<li>
The end of a file may not be accurately detected. This
is due to the behaviour of direct-access files: the
last record may not be complete, if the file size is
a multiple of 4 bytes.
<br><br>
<li>
A default integer is assumed to be 4 bytes, as is
a default real and a default logical. A double precision
real is assumed to be 8 bytes long. There is NO provision
for situations where this is not true.
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,256 @@
<! -- -*- flibs -*- doctools manpage
-->
<html><head>
<title>cgi_protocol - flibs </title>
</head>
<! -- Generated from file 'cgi_protocol.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2008 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id$ cgi_protocol.n
-->
<body>
<h1> cgi_protocol(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> cgi_protocol - Module for supporting the Internet CGI protocol
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#data_types_and_routines">DATA TYPES AND ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#notes">NOTES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>dict_struct</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>dict_data</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>call cgi_begin( html, dict, luout )</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>call cgi_header( type )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call cgi_end</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>call cgi_error( msg, template )</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>call cgi_get_session( dict, value )</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>call cgi_get( dict, varname, value )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
When you want to create web applications, the CGI (common gateway
interface) is one of the means to integrate your underlying programs
with the web server. The protocol itself is, at least superficially,
simple enough, but you need to take care of a number of details.
It is these details that the <em>cgi_protocol</em> module seeks to take
care of.
<p>
The global structure of a program that uses this module to
communicate with the web server is basically:
<ul>
<li>
Get the data from the web server, via <em>cgi_begin</em>
<br><br>
<li>
Handle these input data and write the corresponding HTML-file
<br><br>
<li>
Notify the web server the result is available and stop
</ul>
<h2><a name="data_types_and_routines">DATA TYPES AND ROUTINES</a></h2>
<p>
The module defines the following data types:
<dl>
<dt><a name="1"><b class='cmd'>dict_struct</b> </a><dd>
A derived type holding a list of keyword-value pairs. Each value is of the
type &quot;dict_data&quot; (see below). The routine <em>cgi_begin</em> fills such a
structure to hold all form variables and their values.
<br><br>
<dt><a name="2"><b class='cmd'>dict_data</b> </a><dd>
The derived type whose contents is the string value of a particular form
variable. It has one component: value, a string of 200 characters long
(the actual length is set via the parameter &quot;dict_value_length&quot;, so you
can easily change it if this should prove necessary).
</dl>
The module defines the following routines:
<dl>
<dt><a name="3"><b class='cmd'>call cgi_begin( html, dict, luout )</b> </a><dd>
Routine to start the interaction with the web server. It
automatically determines the protocol to be used and fills the
&quot;dictionary&quot; dict with the values of the form variables for easy
retrieval. The last parameter, luout, should be used to write the
results to the web server.
<br><br>
<dl>
<dt>integer, intent(in) <i class='arg'>html</i><dd>
Type of output that will be written. Should be one of the following
parameters:
<br><br>
<ul>
<li>
output_html - the output will be HTML text (the corresponding CGI header
is written by this routine)
<br><br>
<li>
output_text - the output will be plain ASCII text (the corresponding CGI
header is written by this routine)
<br><br>
<li>
output_no_header - it is not known yet what the output type will be -
you can decide this on the basis of the form input. Use <em>cgi_header</em>
to write the appropriate header later.
<br><br>
<li>
output_html_delayed, output_text_delayed - (not implemented yet)
indicate that the computation will take a while, so that a simple
message is written first.
</ul>
<dt>type(dict_struct), pointer <i class='arg'>dict</i><dd>
Variable to hold alll the form variables and their values. You pass
this variable to <em>cgi_get()</em> to retrieve these values. You can also
store new variables and values, if this is useful (via the plain
dictionary routines).
<br><br>
<em>Note:</em>
Initialise it to &quot;null()&quot; before calling the routine.
<br><br>
<dt>integer, intent(out) <i class='arg'>luout</i><dd>
LU-number for writing output to the web server. Always use this
LU-number, not &quot;*&quot; to write the output.
<br><br>
<em>Note:</em>
If &quot;standard output&quot; is used in the protocol, then this LU-number is
set to 6 - this is not completely portable of course.
</dl>
<dt><a name="4"><b class='cmd'>call cgi_header( type )</b> </a><dd>
Write the CGI header information
<br><br>
<dl>
<dt>integer, intent(in) <i class='arg'>type</i><dd>
Type of output that will be written. See above
</dl>
<dt><a name="5"><b class='cmd'>call cgi_end</b> </a><dd>
Indicate to the server that we are done. The routine stops the program.
<br><br>
<em>Note:</em>
If the run-time library produces extra output as a consequence
of finishing the program, then you may want to use compile options to
suppress that output. The <em>g95</em> compiler for instance reports any
memory that has not been freed. Such output may end up in your HTML
output!
<br><br>
<dt><a name="6"><b class='cmd'>call cgi_error( msg, template )</b> </a><dd>
Report a fatal error in the form of HTML text
<br><br>
<dl>
<dt>character(len=*), intent(in) <i class='arg'>msg</i><dd>
Message to be written
<br><br>
<dt>character(len=*), intent(in), optional <i class='arg'>template</i><dd>
Name of a file to be used as a template. If not given, a simple page
will be written instead. The string &quot;MSG&quot; in that template is replaced
by the contents of the variable msg.
</dl>
<dt><a name="7"><b class='cmd'>call cgi_get_session( dict, value )</b> </a><dd>
Get the value of the &quot;sessionid&quot; variable. This variable is
already present in the form or it is set by this routine to a (more or
less) unique value.
<br><br>
To use it as a session identifier (if your application requires a
continued conversation with the user), make sure each subsequent HTML
reply contains a line like:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
&lt;input type=&quot;hidden&quot; name=&quot;sessionid&quot; value=&quot;(contents of the string)&quot;&gt;
</pre></td></tr></table></p>
<br><br>
<dl>
<dt>character(len=*), intent(out) <i class='arg'>value</i><dd>
The unique session ID
</dl>
<dt><a name="8"><b class='cmd'>call cgi_get( dict, varname, value )</b> </a><dd>
Get the value of a variable defined in the HTML form. If the variable is
not actually present, the value is left unchanged.
<br><br>
If you want to check if a form variable by the name of <em>varname</em>
actually exists, you can use the <em>dict_has_key</em> function, defined
by the <em>dictionary</em> module.
<br><br>
<dl>
<dt>type(DICT_STRUCT), pointer <i class='arg'>dict</i><dd>
The dictionary as returned originally by <em>cgi_begin</em> (you may want
to add new values and variables yourself for easy reference).
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>varname</i><dd>
The name of the variable to retrieve
<br><br>
<dt>character(len=*)|real|integer|logical, intent(inout) <i class='arg'>value</i><dd>
The value (if the variable exists) - it can be a string, (single
precision) real, integer or logical.
</dl>
</dl>
<h2><a name="notes">NOTES</a></h2>
<p>
The current implementation assumes that the compiler supports the new
intrinsic routine <em>get_environment_variable</em>. If this is not the
case, then it is possible to mimick this routine (with a bit of
trickery), but that has not been implemented yet in this version.
<p>
There is no support as yet for a delayed response. The idea is that the
program sends a short note to the web server, to inform the user that
the final result takes a few minutes (or longer) and that he/she can
find it at such and such a location.
<p>
The <em>cgi_protocol</em> module uses the dictionary module underneath.
For convenience the source code for that module is contained within the
source directory for the <em>cgi_protocol</em>.
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2008 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,191 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin cgi_protocol n 1.0]
[copyright {2008 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Module for supporting the Internet CGI protocol}]
[description]
When you want to create web applications, the CGI (common gateway
interface) is one of the means to integrate your underlying programs
with the web server. The protocol itself is, at least superficially,
simple enough, but you need to take care of a number of details.
It is these details that the [emph cgi_protocol] module seeks to take
care of.
[para]
The global structure of a program that uses this module to
communicate with the web server is basically:
[list_begin bullet]
[bullet]
Get the data from the web server, via [emph cgi_begin]
[bullet]
Handle these input data and write the corresponding HTML-file
[bullet]
Notify the web server the result is available and stop
[list_end]
[section "DATA TYPES AND ROUTINES"]
The module defines the following data types:
[list_begin definitions]
[call [cmd "dict_struct"]]
A derived type holding a list of keyword-value pairs. Each value is of the
type "dict_data" (see below). The routine [emph cgi_begin] fills such a
structure to hold all form variables and their values.
[call [cmd "dict_data"]]
The derived type whose contents is the string value of a particular form
variable. It has one component: value, a string of 200 characters long
(the actual length is set via the parameter "dict_value_length", so you
can easily change it if this should prove necessary).
[list_end]
The module defines the following routines:
[list_begin definitions]
[call [cmd "call cgi_begin( html, dict, luout )"]]
Routine to start the interaction with the web server. It
automatically determines the protocol to be used and fills the
"dictionary" dict with the values of the form variables for easy
retrieval. The last parameter, luout, should be used to write the
results to the web server.
[list_begin arg]
[arg_def "integer, intent(in)" html]
Type of output that will be written. Should be one of the following
parameters:
[list_begin bullet]
[bullet]
output_html - the output will be HTML text (the corresponding CGI header
is written by this routine)
[bullet]
output_text - the output will be plain ASCII text (the corresponding CGI
header is written by this routine)
[bullet]
output_no_header - it is not known yet what the output type will be -
you can decide this on the basis of the form input. Use [emph cgi_header]
to write the appropriate header later.
[bullet]
output_html_delayed, output_text_delayed - (not implemented yet)
indicate that the computation will take a while, so that a simple
message is written first.
[list_end]
[arg_def "type(dict_struct), pointer" dict]
Variable to hold alll the form variables and their values. You pass
this variable to [emph cgi_get()] to retrieve these values. You can also
store new variables and values, if this is useful (via the plain
dictionary routines).
[nl]
[emph Note:]
Initialise it to "null()" before calling the routine.
[arg_def "integer, intent(out)" luout]
LU-number for writing output to the web server. Always use this
LU-number, not "*" to write the output.
[nl]
[emph Note:]
If "standard output" is used in the protocol, then this LU-number is
set to 6 - this is not completely portable of course.
[list_end]
[call [cmd "call cgi_header( type )"]]
Write the CGI header information
[list_begin arg]
[arg_def "integer, intent(in)" type]
Type of output that will be written. See above
[list_end]
[call [cmd "call cgi_end"]]
Indicate to the server that we are done. The routine stops the program.
[nl]
[emph Note:]
If the run-time library produces extra output as a consequence
of finishing the program, then you may want to use compile options to
suppress that output. The [emph g95] compiler for instance reports any
memory that has not been freed. Such output may end up in your HTML
output!
[call [cmd "call cgi_error( msg, template )"]]
Report a fatal error in the form of HTML text
[list_begin arg]
[arg_def "character(len=*), intent(in)" msg]
Message to be written
[arg_def "character(len=*), intent(in), optional" template]
Name of a file to be used as a template. If not given, a simple page
will be written instead. The string "MSG" in that template is replaced
by the contents of the variable msg.
[list_end]
[call [cmd "call cgi_get_session( dict, value )"]]
Get the value of the "sessionid" variable. This variable is
already present in the form or it is set by this routine to a (more or
less) unique value.
[nl]
To use it as a session identifier (if your application requires a
continued conversation with the user), make sure each subsequent HTML
reply contains a line like:
[example {
<input type="hidden" name="sessionid" value="(contents of the string)">
}]
[list_begin arg]
[arg_def "character(len=*), intent(out)" value]
The unique session ID
[list_end]
[call [cmd "call cgi_get( dict, varname, value )"]]
Get the value of a variable defined in the HTML form. If the variable is
not actually present, the value is left unchanged.
[nl]
If you want to check if a form variable by the name of [term varname]
actually exists, you can use the [term dict_has_key] function, defined
by the [term dictionary] module.
[list_begin arg]
[arg_def "type(DICT_STRUCT), pointer" dict]
The dictionary as returned originally by [term cgi_begin] (you may want
to add new values and variables yourself for easy reference).
[arg_def "character(len=*), intent(in)" varname]
The name of the variable to retrieve
[arg_def "character(len=*)|real|integer|logical, intent(inout)" value]
The value (if the variable exists) - it can be a string, (single
precision) real, integer or logical.
[list_end]
[list_end]
[section NOTES]
The current implementation assumes that the compiler supports the new
intrinsic routine [emph get_environment_variable]. If this is not the
case, then it is possible to mimick this routine (with a bit of
trickery), but that has not been implemented yet in this version.
[para]
There is no support as yet for a delayed response. The idea is that the
program sends a short note to the web server, to inform the user that
the final result takes a few minutes (or longer) and that he/she can
find it at such and such a location.
[para]
The [emph cgi_protocol] module uses the dictionary module underneath.
For convenience the source code for that module is contained within the
source directory for the [emph cgi_protocol].
[manpage_end]

View File

@@ -0,0 +1,157 @@
<html><head>
<title>checking - flibs </title>
</head>
<! -- Generated from file 'checking.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2007 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: checking.html,v 1.1 2008/06/13 10:22:46 relaxmike Exp $ checking.n
-->
<body>
<h1> checking(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> checking - Instrument source code for tracing execution
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#usage">Usage</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#notes">Notes</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
If you need to understand a program written by somebody else,
it is often useful to run it in a debugger to see what it is
doing, but that can be time-consuming. Adding write statements
to the program is an alternative, but again time-consuming.
<p>
The module <em>w_</em> is meant, in conjunction with the Tcl program
<em>instrument.tcl</em> to automate the process of adding such
write statements.
The write statements that are added record:
<ul>
<li>
Calls to a subroutine (which routine, where)
<br><br>
<li>
Returns from the subroutine
<br><br>
<li>
Gotos within the main program or subroutine
<br><br>
<li>
Opening/closing of files
<br><br>
<li>
Stop statements
</ul>
Messages indicating what is happening in the program are written
to the screen (unit * to be more precise), because that way no
extra logical unit is consumed and it is easier to see the
program's actions in conjunction to the output it produces.
<p>
The design goals were:
<ul>
<li>
The code transformations should be simple (because these would
be easier to implement!)
<br><br>
<li>
The code transformations should not change the program's logic
(of course!)
</ul>
The module uses <em>alternative returns</em> to succeed in achieving
these goals, even though they are marked as <em>depricated</em> in the
Fortran 90 standard.
<h2><a name="usage">Usage</a></h2>
<p>
Using the module and the instrumentation program is easy:
<ul>
<li>
Run the Tcl program <em>instrument.tcl</em> in the directory with source
files:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
&gt; tclsh &lt;directory containing the source&gt;/instrument.tcl
</pre></td></tr></table></p>
This will create an instrumented version of all source files in the
directory that have an extension .f, .for or .f90. The original files
are first renamed, so that they can be retrieved. (See the
tests/checking directory for an example).
<br><br>
<li>
Add the file &quot;w_.f90&quot; to the list of source files for building the
program.
<br><br>
<li>
Build the program by the means you ordinarily use.
</ul>
<h2><a name="notes">Notes</a></h2>
<p>
<ul>
<li>
It can not easily log where functions are called, as
calls to functions are much more difficult to identify in the
source code than subroutine calls. Still, the source code for
functions is instrumented in the same way as subroutines
(with the exception of pure and elemental functions and
subroutines!)
<br><br>
<li>
The instrumentation program is not foolproof: the generated
source will not be correct Fortran, if you use variable names
equal to keywords such as &quot;return&quot; or &quot;open&quot;. (It is not a
full parser of Fortran code, it merely detects certain string
patterns and acts accordingly)
<br><br>
<li>
The name &quot;w_&quot; was chosen, because it is unlikely to be used in
any ordinary program as the name of a module or routine.
<br><br>
<li>
Running the Tcl program requires either an installation of Tcl
or a standalone Tcl &quot;runtime program&quot; (tclkit).
<br><br>
For the first: have a look at http://www.activestate.com/tcl
<br><br>
For the second: http://www.equi4.com/tclkit/index.html
<br><br>
The advantage of the tclkit family of programs is that installation is
simply a matter of copying one single file, an executable program and
de-installation is a matter of deleting that program.
<br><br>
The advantage of the complete installation is that it comes with
an extensive set of examples, libraries and documentation on Tcl/Tk.
<br><br>
(Note: I chose Tcl as the implementation language for this program,
because it is very easy to manipulate strings in Tcl. It could be
reprogrammed in Fortran - the string matching via regular expressions is
the hardest part, but even that can be done by standard means)
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2007 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,121 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin checking n 1.0]
[copyright {2007 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Instrument source code for tracing execution}]
[description]
If you need to understand a program written by somebody else,
it is often useful to run it in a debugger to see what it is
doing, but that can be time-consuming. Adding write statements
to the program is an alternative, but again time-consuming.
[para]
The module [emph w_] is meant, in conjunction with the Tcl program
[emph instrument.tcl] to automate the process of adding such
write statements.
The write statements that are added record:
[list_begin bullet]
[bullet]
Calls to a subroutine (which routine, where)
[bullet]
Returns from the subroutine
[bullet]
Gotos within the main program or subroutine
[bullet]
Opening/closing of files
[bullet]
Stop statements
[list_end]
Messages indicating what is happening in the program are written
to the screen (unit * to be more precise), because that way no
extra logical unit is consumed and it is easier to see the
program's actions in conjunction to the output it produces.
[para]
The design goals were:
[list_begin bullet]
[bullet]
The code transformations should be simple (because these would
be easier to implement!)
[bullet]
The code transformations should not change the program's logic
(of course!)
[list_end]
The module uses [emph "alternative returns"] to succeed in achieving
these goals, even though they are marked as [emph depricated] in the
Fortran 90 standard.
[section Usage]
Using the module and the instrumentation program is easy:
[list_begin bullet]
[bullet]
Run the Tcl program [emph instrument.tcl] in the directory with source
files:
[example {
> tclsh <directory containing the source>/instrument.tcl
}]
This will create an instrumented version of all source files in the
directory that have an extension .f, .for or .f90. The original files
are first renamed, so that they can be retrieved. (See the
tests/checking directory for an example).
[bullet]
Add the file "w_.f90" to the list of source files for building the
program.
[bullet]
Build the program by the means you ordinarily use.
[list_end]
[section Notes]
[list_begin bullet]
[bullet]
It can not easily log where functions are called, as
calls to functions are much more difficult to identify in the
source code than subroutine calls. Still, the source code for
functions is instrumented in the same way as subroutines
(with the exception of pure and elemental functions and
subroutines!)
[bullet]
The instrumentation program is not foolproof: the generated
source will not be correct Fortran, if you use variable names
equal to keywords such as "return" or "open". (It is not a
full parser of Fortran code, it merely detects certain string
patterns and acts accordingly)
[bullet]
The name "w_" was chosen, because it is unlikely to be used in
any ordinary program as the name of a module or routine.
[bullet]
Running the Tcl program requires either an installation of Tcl
or a standalone Tcl "runtime program" (tclkit).
[nl]
For the first: have a look at http://www.activestate.com/tcl
[nl]
For the second: http://www.equi4.com/tclkit/index.html
[nl]
The advantage of the tclkit family of programs is that installation is
simply a matter of copying one single file, an executable program and
de-installation is a matter of deleting that program.
[nl]
The advantage of the complete installation is that it comes with
an extensive set of examples, libraries and documentation on Tcl/Tk.
[nl]
(Note: I chose Tcl as the implementation language for this program,
because it is very easy to manipulate strings in Tcl. It could be
reprogrammed in Fortran - the string matching via regular expressions is
the hardest part, but even that can be done by standard means)
[list_end]
[manpage_end]

View File

@@ -0,0 +1,323 @@
<! -- -*- flibs -*- doctools manpage
-->
<html><head>
<title>simulated_annealing - flibs </title>
</head>
<! -- Generated from file 'annealing.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2008 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id$ simulated_annealing.n
-->
<body>
<h1> simulated_annealing(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> simulated_annealing - Implement a &quot;simulated annealing&quot; algorithm
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#data_types_and_routines">DATA TYPES AND ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#interface_issues">INTERFACE ISSUES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>use simulated_annealing</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>type(ANNEALING_PARAMETERS)</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>call set_parameters( params, update, initial_temp, temp_reduction, number_iterations, scale_factor, automatic_scaling, verbose)</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>call get_next_step( params, range, x, value, task</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call find_minimum( params, range, x, func, value )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>simulated_annealing</em> module allows you to find the minimum
of an arbitrary function of N variables using a straightforward
simulated annealing algorithm.
<p>
The idea is that the variables can vary independently each within a
given interval. For each set of values generated in this way, the
function that is to be minimized is evaluated. The new set of values is
accepted as the new estimate of the minimum in two situations:
<ul>
<li>
The value of the function is lower than the current minimum
<br><br>
<li>
A generated random number is low enough, that is the expression
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
r &lt; exp(-(new value - old value)/scaled temperature)
</pre></td></tr></table></p>
is true.
</ul>
The &quot;temperature&quot; is reduced by a constant factor after a given number
of iterations, thus making the second case more and more improbable. If
there are no new estimates, the iteration stops.
<p>
Theoretically, <em>simulated annealing</em> is able to find the global
minimum of a function, but it would require infinite time to actually
achieve it.
<p>
The module implements the basic technique and if the interface to the
function is more complex than the subroutine <em>find_minimum</em>
assumes, then you can use the code for that routine as a template for a
customised version (see below for some ideas regarding such more
general functionality).
<h2><a name="data_types_and_routines">DATA TYPES AND ROUTINES</a></h2>
<p>
The module defines a single data type, ANNEALING_PARAMETERS and several
subroutines:
<dl>
<dt><a name="1"><b class='cmd'>use simulated_annealing</b> </a><dd>
The name of the module. The module itself uses the module
<em>select_precision</em> to select single or double precision reals.
<br><br>
<dt><a name="2"><b class='cmd'>type(ANNEALING_PARAMETERS)</b> </a><dd>
The type holds the parameters and state variables needed for the
iteration. You can set the fields via the subroutine
<em>set_parameters</em>.
<br><br>
<dt><a name="3"><b class='cmd'>call set_parameters( params, update, initial_temp, temp_reduction, number_iterations, scale_factor, automatic_scaling, verbose)</b> </a><dd>
Subroutine to set the individual parameters for the algorithm. (All
arguments are optional, except <em>params</em> and <em>update</em>)
<br><br>
<dl>
<dt>type(ANNEALING_PARAMETERS) <i class='arg'>params</i><dd>
Derived type holding all parameters (and internal state variables) for
the iteration.
<br><br>
<dt>logical <i class='arg'>update</i><dd>
If true, only the arguments that are present in the call are used to
update the fields in <em>params</em>. Otherwise the structure is first
initialised.
<br><br>
Note: this is probably not a very useful feature.
<br><br>
<dt>real(wp) <i class='arg'>initial_temp</i><dd>
Initial &quot;temperature&quot; (defaults to 1). A larger value means it will be
easier for the vector representing the estimated minimum to wander
about.
<br><br>
<dt>real(wp) <i class='arg'>temp_reduction</i><dd>
Factor by which to reduce the temperature (defaults to 0.95). A smaller
value means the iteration will settle quicker, but possibly misses the
global minimum. A value closer to 1 means the process will take longer,
but the result will be more accurate.
<br><br>
<dt>integer <i class='arg'>number_iterations</i><dd>
Number of estimates to be examined before reducing the &quot;temperature&quot;
(defaults to 100).
<br><br>
<dt>real(wp) <i class='arg'>scale_factor</i><dd>
Factor by which to scale the value before. The idea is that with a well
chose scale factor the simulation is more or less independent from the
actual values (defaults to 1).
<br><br>
<dt>logical <i class='arg'>automatic_scaling</i><dd>
Whether to first automatically determine a reasonable scale factor or
not.
<br><br>
<dt>logical <i class='arg'>verbose</i><dd>
Whether to print the intermediate results before reducing the
temperature or not.
</dl>
<br><br>
<dt><a name="4"><b class='cmd'>call get_next_step( params, range, x, value, task</b> </a><dd>
Low-level routine that exmaines the function value and decides what the
next step will be.
<br><br>
<dl>
<dt>type(ANNEALING_PARAMETERS) <i class='arg'>params</i><dd>
Derived type holding all parameters (and internal state variables) for
the iteration.
<br><br>
<dt>real(wp), dimension(2,:) <i class='arg'>range</i><dd>
The minimum and maximum value for each independent variable.
<br><br>
<dt>real(wp), dimension(:) <i class='arg'>x</i><dd>
Current estimate of each independent variable where the minimum is
attained.
<br><br>
<dt>real(wp) <i class='arg'>value</i><dd>
Value of the function at x.
<br><br>
<dt>integer <i class='arg'>task</i><dd>
Task to be performed: anneal_init, anneal_print, anneal_value or
anneal_done.
</dl>
<dt><a name="5"><b class='cmd'>call find_minimum( params, range, x, func, value )</b> </a><dd>
Routine implementing the procedure to find the minimum.
<br><br>
<dl>
<dt>type(ANNEALING_PARAMETERS) <i class='arg'>params</i><dd>
Derived type holding all parameters (and internal state variables) for
the iteration.
<br><br>
<dt>real(wp), dimension(2,:) <i class='arg'>range</i><dd>
The minimum and maximum value for each independent variable.
<br><br>
<dt>real(wp), dimension(:) <i class='arg'>x</i><dd>
Upon return, estimate of each independent variable where the minimum is
attained.
<br><br>
<dt>real(wp) <i class='arg'>value</i><dd>
Estimate of the minimum value of the function (the value at x).
<br><br>
<dt>real(wp) function <i class='arg'>func(x)</i><dd>
The function must have the interface:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
interface
function f(x)
use select_precision
real(wp), dimension(:), intent(in) :: x
real(wp) :: func
end function
end interface
</pre></td></tr></table></p>
</dl>
</dl>
<h2><a name="interface_issues">INTERFACE ISSUES</a></h2>
<p>
The interface to the function to be minimized is fixed. This is an
unfortunate limitation of Fortran 95. But there are at least two ways
around it:
<ul>
<li>
If the function requires one or more parameters, or a set of
measured data, then it can be useful to store these first as module
variables and then call <em>find_minimum</em> with as argument a function
in that module that can access the data:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
module measured_data
use select_precision
real(wp), dimension(:), allocatable, save :: data
contains
subroutine store_data( array )
real(wp), dimension(:) :: data
... copy the data
end subroutine store_data
real(wp) function f(x)
real(wp), dimension(:) :: x
... use x and data to determine the value of f
end function f
end module
</pre></td></tr></table></p>
<br><br>
<li>
Use the code for <em>find_minimum</em> to implement the evaluation of the
function in the way required. The code is fairly straightforward:
{exampe {
subroutine find_minimum( params, range, x, func, value )
type(ANNEALING_PARAMETERS), intent(inout) :: params
real(wp), dimension(:,:), intent(in) :: range
real(wp), dimension(:), intent(inout) :: x
real(wp), intent(out) :: value
interface
function func( x )
use select_precision
real(wp), dimension(:), intent(in) :: x
real(wp) :: func
end function
end interface
integer :: task
task = annealing_init
do
call get_next_step( params, range, x, value, task )
select case ( task )
case ( annealing_value )
!
! Fill in the evaluation of the function
!
! You can put the customised code here
!
value = func(x)
case ( annealing_report )
!
! Fill in the reporting code
!
write(*,'(a,e12.4)') 'Value so far: ', value
write(*,'(a,(5e12.4),/)') ' Vector: ', x
write(*,'(2(a,i5))') ' Accepted: ', &amp;
params%accepted, ' from ', params%number_iterations
case ( annealing_done )
exit
end select
enddo
end subroutine find_minimum
}]
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2008 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,260 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin simulated_annealing n 1.0]
[copyright {2008 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Implement a "simulated annealing" algorithm}]
[description]
The [strong simulated_annealing] module allows you to find the minimum
of an arbitrary function of N variables using a straightforward
simulated annealing algorithm.
[para]
The idea is that the variables can vary independently each within a
given interval. For each set of values generated in this way, the
function that is to be minimized is evaluated. The new set of values is
accepted as the new estimate of the minimum in two situations:
[list_begin bullet]
[bullet]
The value of the function is lower than the current minimum
[bullet]
A generated random number is low enough, that is the expression
[example {
r < exp(-(new value - old value)/scaled temperature)
}]
is true.
[list_end]
The "temperature" is reduced by a constant factor after a given number
of iterations, thus making the second case more and more improbable. If
there are no new estimates, the iteration stops.
[para]
Theoretically, [emph "simulated annealing"] is able to find the global
minimum of a function, but it would require infinite time to actually
achieve it.
[para]
The module implements the basic technique and if the interface to the
function is more complex than the subroutine [term find_minimum]
assumes, then you can use the code for that routine as a template for a
customised version (see below for some ideas regarding such more
general functionality).
[section "DATA TYPES AND ROUTINES"]
The module defines a single data type, ANNEALING_PARAMETERS and several
subroutines:
[list_begin definitions]
[call [cmd "use simulated_annealing"]]
The name of the module. The module itself uses the module
[term select_precision] to select single or double precision reals.
[call [cmd "type(ANNEALING_PARAMETERS)"]]
The type holds the parameters and state variables needed for the
iteration. You can set the fields via the subroutine
[term set_parameters].
[call [cmd "call set_parameters( params, update, initial_temp, \
temp_reduction, number_iterations, scale_factor, automatic_scaling, verbose)"]]
Subroutine to set the individual parameters for the algorithm. (All
arguments are optional, except [term params] and [term update])
[list_begin arg]
[arg_def "type(ANNEALING_PARAMETERS)" params]
Derived type holding all parameters (and internal state variables) for
the iteration.
[arg_def "logical" update]
If true, only the arguments that are present in the call are used to
update the fields in [term params]. Otherwise the structure is first
initialised.
[nl]
Note: this is probably not a very useful feature.
[arg_def "real(wp)" initial_temp]
Initial "temperature" (defaults to 1). A larger value means it will be
easier for the vector representing the estimated minimum to wander
about.
[arg_def "real(wp)" temp_reduction]
Factor by which to reduce the temperature (defaults to 0.95). A smaller
value means the iteration will settle quicker, but possibly misses the
global minimum. A value closer to 1 means the process will take longer,
but the result will be more accurate.
[arg_def "integer" number_iterations]
Number of estimates to be examined before reducing the "temperature"
(defaults to 100).
[arg_def "real(wp)" scale_factor]
Factor by which to scale the value before. The idea is that with a well
chose scale factor the simulation is more or less independent from the
actual values (defaults to 1).
[arg_def "logical" automatic_scaling]
Whether to first automatically determine a reasonable scale factor or
not.
[arg_def "logical" verbose]
Whether to print the intermediate results before reducing the
temperature or not.
[list_end]
[nl]
[call [cmd "call get_next_step( params, range, x, value, task"]]
Low-level routine that exmaines the function value and decides what the
next step will be.
[list_begin arg]
[arg_def "type(ANNEALING_PARAMETERS)" params]
Derived type holding all parameters (and internal state variables) for
the iteration.
[arg_def "real(wp), dimension(2,:)" range]
The minimum and maximum value for each independent variable.
[arg_def "real(wp), dimension(:)" x]
Current estimate of each independent variable where the minimum is
attained.
[arg_def "real(wp)" value]
Value of the function at x.
[arg_def "integer" task]
Task to be performed: anneal_init, anneal_print, anneal_value or
anneal_done.
[list_end]
[call [cmd "call find_minimum( params, range, x, func, value )"]]
Routine implementing the procedure to find the minimum.
[list_begin arg]
[arg_def "type(ANNEALING_PARAMETERS)" params]
Derived type holding all parameters (and internal state variables) for
the iteration.
[arg_def "real(wp), dimension(2,:)" range]
The minimum and maximum value for each independent variable.
[arg_def "real(wp), dimension(:)" x]
Upon return, estimate of each independent variable where the minimum is
attained.
[arg_def "real(wp)" value]
Estimate of the minimum value of the function (the value at x).
[arg_def "real(wp) function" func(x)]
The function must have the interface:
[example {
interface
function f(x)
use select_precision
real(wp), dimension(:), intent(in) :: x
real(wp) :: func
end function
end interface
}]
[list_end]
[list_end]
[section "INTERFACE ISSUES"]
The interface to the function to be minimized is fixed. This is an
unfortunate limitation of Fortran 95. But there are at least two ways
around it:
[list_begin bullet]
[bullet]
If the function requires one or more parameters, or a set of
measured data, then it can be useful to store these first as module
variables and then call [term find_minimum] with as argument a function
in that module that can access the data:
[example {
module measured_data
use select_precision
real(wp), dimension(:), allocatable, save :: data
contains
subroutine store_data( array )
real(wp), dimension(:) :: data
... copy the data
end subroutine store_data
real(wp) function f(x)
real(wp), dimension(:) :: x
... use x and data to determine the value of f
end function f
end module
}]
[bullet]
Use the code for [term find_minimum] to implement the evaluation of the
function in the way required. The code is fairly straightforward:
{exampe {
subroutine find_minimum( params, range, x, func, value )
type(ANNEALING_PARAMETERS), intent(inout) :: params
real(wp), dimension(:,:), intent(in) :: range
real(wp), dimension(:), intent(inout) :: x
real(wp), intent(out) :: value
interface
function func( x )
use select_precision
real(wp), dimension(:), intent(in) :: x
real(wp) :: func
end function
end interface
integer :: task
task = annealing_init
do
call get_next_step( params, range, x, value, task )
select case ( task )
case ( annealing_value )
!
! Fill in the evaluation of the function
!
! You can put the customised code here
!
value = func(x)
case ( annealing_report )
!
! Fill in the reporting code
!
write(*,'(a,e12.4)') 'Value so far: ', value
write(*,'(a,(5e12.4),/)') ' Vector: ', x
write(*,'(2(a,i5))') ' Accepted: ', &
params%accepted, ' from ', params%number_iterations
case ( annealing_done )
exit
end select
enddo
end subroutine find_minimum
}]
[list_end]
[manpage_end]

View File

@@ -0,0 +1,262 @@
<! -- -*- flibs -*- doctools manpage
-->
<html><head>
<title>automatic_differentiation - flibs </title>
</head>
<! -- Generated from file 'automdiff.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: automdiff.html,v 1.2 2008/09/09 04:29:29 arjenmarkus Exp $ automatic_differentiation.n
-->
<body>
<h1> automatic_differentiation(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> automatic_differentiation - Implement the &quot;automatic differentation&quot; technique
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#example">EXAMPLE</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#data_types_and_routines">DATA TYPES AND ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#possible_extensions">POSSIBLE EXTENSIONS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>use automatic_differentiation</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>type(AUTODERIV)</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>x = derivvar( value )</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>call find_root( f, xinit, tolerance, root, found )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call find_root_iter( fvalue, root, tolerance, found )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>automatic_differentiation</em> module defines a derived
type that enables you (via overloading common mathematical
functions and operators) to:
<ul>
<li>
Define a mathematical function in the usual way
<br><br>
<li>
Evaluate that function <em>and</em> its first derivative at the same
time
</ul>
without having to program that first derivative.
<p>
The module uses real numbers of the kind &quot;wp&quot; as defined in the
auxiliary module <em>select_precision</em>.
<p>
(I was pointed to this technique by Simon Geard, a couple of years ago,
in the context of one of the many language shootouts on the Internet.)
<h2><a name="example">EXAMPLE</a></h2>
<p>
In numerical methods like Newton-Raphson for finding a root of the
equation &quot;f(x) = 0&quot;, you need the first derivative:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
x(k+1) = x(k) - f(x(k)) / f'(x(k))
</pre></td></tr></table></p>
Rather than implementing the function and its first derivatives as
separate functions, using this module you can dispense with manually
determining the first derivative:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
program root
use automatic_differentation
type(AUTODERIV) :: x
type(AUTODERIV) :: res
integer :: i
!
! First estimate
!
x = derivvar( 1.0_wp )
do i = 1,10
res = f(x)
x = x - res.v / res.dv
enddo
write(*,*) 'Root: ', x.v
contains
type(AUTODERIV) function f(x)
type(AUTODERIV) :: x
f = x + cos(x)
end function f
end program
</pre></td></tr></table></p>
<h2><a name="data_types_and_routines">DATA TYPES AND ROUTINES</a></h2>
<p>
The module defines a single data type, AUTODERIV, and one specific
function, derivvar().
<dl>
<dt><a name="1"><b class='cmd'>use automatic_differentiation</b> </a><dd>
The name of the module
<br><br>
<dt><a name="2"><b class='cmd'>type(AUTODERIV)</b> </a><dd>
The type has two fields:
<br><br>
<dl>
<dt>real(wp) <i class='arg'>v</i><dd>
The value of the variable/function (or any intermediate result)
<br><br>
<dt>real(wp) <i class='arg'>dv</i><dd>
The first derivative
</dl>
<br><br>
<dt><a name="3"><b class='cmd'>x = derivvar( value )</b> </a><dd>
Use this function to properly initialise the program
variable x as a &quot;mathematical&quot; variable. (It sets the derivative
of x to 1, because otherwise it would be regarded as a constant).
<br><br>
<dl>
<dt>real(wp) <i class='arg'>value</i><dd>
The value of the &quot;mathematical&quot; variable.
</dl>
<dt><a name="4"><b class='cmd'>call find_root( f, xinit, tolerance, root, found )</b> </a><dd>
This subroutine is a simple implementation of the Newton-Raphson
method to find roots. The function f takes one argument, x.
<br><br>
<dl>
<dt>type(AUTODERIV) function <i class='arg'>f(x)</i><dd>
The function must have the interface:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
interface
function f(x)
use automatic_differentiation_type
type(AUTODERIV), intent(in) :: x
type(AUTODERIV) :: f
end function
end interface
</pre></td></tr></table></p>
Its return value is the function value at point x and its first
derivative.
<br><br>
<dt>type(AUTODERIV), intent(in) <i class='arg'>xinit</i><dd>
Initial estimate of the root - the end result may depend on this choice.
<br><br>
<dt>real(wp) <i class='arg'>tolerance</i><dd>
Maximum allowable error in the absolute value of the root. If the
difference between the old and the new estimate is smaller than this
value, the iteration stops.
<br><br>
<dt>type(AUTODERIV), intent(out) <i class='arg'>root</i><dd>
Final estimate of the root.
<br><br>
<dt>logical <i class='arg'>found</i><dd>
Indicator whether a root was found or not. There is a maximum
number of iterations and the root must not grow too large.
</dl>
<dt><a name="5"><b class='cmd'>call find_root_iter( fvalue, root, tolerance, found )</b> </a><dd>
This subroutine performs a single iteration in the Newton-Raphson
method to find roots. It can be used to implement a more general version
of <em>find_root</em>, for instance if the interface to the function
contains one or more asjustable parameters.
<br><br>
<dl>
<dt>type(AUTODERIV) <i class='arg'>fvalue</i><dd>
The value of the function (plus its first derivative)
<br><br>
<dt>type(AUTODERIV), intent(in) <i class='arg'>root</i><dd>
Current estimate of the root
<br><br>
<dt>real(wp) <i class='arg'>tolerance</i><dd>
Maximum allowable error in the absolute value of the root. If the
difference between the old and the new estimate is smaller than this
value, the indicator <em>found</em> is set to true.
<br><br>
<dt>logical <i class='arg'>found</i><dd>
Indicator whether a root was found or not.
</dl>
</dl>
<h2><a name="possible_extensions">POSSIBLE EXTENSIONS</a></h2>
<p>
You can extend the module in at least two ways:
<ul>
<li>
Determine the second derivative, the third and so on. This is
straightforward enough, but the formulae will get fairly complex after
the second derivative.
<br><br>
<li>
Determine the derivative of a function of two or more variables. This
will require a bit more: such functions have a vector-valued
derivative and each independent variable will have to have a
vector-valued derivative as well. For instance:
<br><br>
A function f(x,y) evaluated at (1,2), would take as the actual arguments
x = (1,1,0) (so no variation in the second direction) and y = (2,0,1)
(no variation in the first direction).
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,203 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin automatic_differentiation n 1.0]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Implement the "automatic differentation" technique}]
[description]
The [strong automatic_differentiation] module defines a derived
type that enables you (via overloading common mathematical
functions and operators) to:
[list_begin bullet]
[bullet]
Define a mathematical function in the usual way
[bullet]
Evaluate that function [strong and] its first derivative at the same
time
[list_end]
without having to program that first derivative.
[para]
The module uses real numbers of the kind "wp" as defined in the
auxiliary module [strong select_precision].
[para]
(I was pointed to this technique by Simon Geard, a couple of years ago,
in the context of one of the many language shootouts on the Internet.)
[section "EXAMPLE"]
In numerical methods like Newton-Raphson for finding a root of the
equation "f(x) = 0", you need the first derivative:
[example {
x(k+1) = x(k) - f(x(k)) / f'(x(k))
}]
Rather than implementing the function and its first derivatives as
separate functions, using this module you can dispense with manually
determining the first derivative:
[example {
program root
use automatic_differentation
type(AUTODERIV) :: x
type(AUTODERIV) :: res
integer :: i
!
! First estimate
!
x = derivvar( 1.0_wp )
do i = 1,10
res = f(x)
x = x - res.v / res.dv
enddo
write(*,*) 'Root: ', x.v
contains
type(AUTODERIV) function f(x)
type(AUTODERIV) :: x
f = x + cos(x)
end function f
end program
}]
[section "DATA TYPES AND ROUTINES"]
The module defines a single data type, AUTODERIV, and one specific
function, derivvar().
[list_begin definitions]
[call [cmd "use automatic_differentiation"]]
The name of the module
[call [cmd "type(AUTODERIV)"]]
The type has two fields:
[list_begin arg]
[arg_def "real(wp)" v]
The value of the variable/function (or any intermediate result)
[arg_def "real(wp)" dv]
The first derivative
[list_end]
[nl]
[call [cmd "x = derivvar( value )"]]
Use this function to properly initialise the program
variable x as a "mathematical" variable. (It sets the derivative
of x to 1, because otherwise it would be regarded as a constant).
[list_begin arg]
[arg_def "real(wp)" value]
The value of the "mathematical" variable.
[list_end]
[call [cmd "call find_root( f, xinit, tolerance, root, found )"]]
This subroutine is a simple implementation of the Newton-Raphson
method to find roots. The function f takes one argument, x.
[list_begin arg]
[arg_def "type(AUTODERIV) function" f(x)]
The function must have the interface:
[example {
interface
function f(x)
use automatic_differentiation_type
type(AUTODERIV), intent(in) :: x
type(AUTODERIV) :: f
end function
end interface
}]
Its return value is the function value at point x and its first
derivative.
[arg_def "type(AUTODERIV), intent(in)" xinit]
Initial estimate of the root - the end result may depend on this choice.
[arg_def "real(wp)" tolerance]
Maximum allowable error in the absolute value of the root. If the
difference between the old and the new estimate is smaller than this
value, the iteration stops.
[arg_def "type(AUTODERIV), intent(out)" root]
Final estimate of the root.
[arg_def "logical" found]
Indicator whether a root was found or not. There is a maximum
number of iterations and the root must not grow too large.
[list_end]
[call [cmd "call find_root_iter( fvalue, root, tolerance, found )"]]
This subroutine performs a single iteration in the Newton-Raphson
method to find roots. It can be used to implement a more general version
of [strong find_root], for instance if the interface to the function
contains one or more asjustable parameters.
[list_begin arg]
[arg_def "type(AUTODERIV)" fvalue]
The value of the function (plus its first derivative)
[arg_def "type(AUTODERIV), intent(in)" root]
Current estimate of the root
[arg_def "real(wp)" tolerance]
Maximum allowable error in the absolute value of the root. If the
difference between the old and the new estimate is smaller than this
value, the indicator [strong found] is set to true.
[arg_def "logical" found]
Indicator whether a root was found or not.
[list_end]
[list_end]
[section "POSSIBLE EXTENSIONS"]
You can extend the module in at least two ways:
[list_begin bullet]
[bullet]
Determine the second derivative, the third and so on. This is
straightforward enough, but the formulae will get fairly complex after
the second derivative.
[bullet]
Determine the derivative of a function of two or more variables. This
will require a bit more: such functions have a vector-valued
derivative and each independent variable will have to have a
vector-valued derivative as well. For instance:
[nl]
A function f(x,y) evaluated at (1,2), would take as the actual arguments
x = (1,1,0) (so no variation in the second direction) and y = (2,0,1)
(no variation in the first direction).
[list_end]
[manpage_end]

View File

@@ -0,0 +1,210 @@
<! -- -*- flibs -*- doctools manpage
-->
<html><head>
<title>flibs/backtracking - flibs </title>
</head>
<! -- Generated from file 'backtrack.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: backtrack.html,v 1.1 2008/09/09 04:29:30 arjenmarkus Exp $ flibs/backtracking.n
-->
<body>
<h1> flibs/backtracking(n) 1.1 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> flibs/backtracking - Backtracking
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#routines">ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#todo">TODO</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>call runtests( testproc )</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>call test( proc, text )</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>call assert_true( cond, text )</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>exists = funit_file_exists( filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call funit_get_lun( lun )</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>call funit_remove_file( filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>call funit_make_empty_file( filename )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The module <em>Backtracking</em> implements in a general way a well-known
algorithm to solve certain combinatorial problems. The module actually
consists of a single routine that forms the framework of the algorithm
and is to be used in conjunction with a small set of user-defined
routines that implement the specific problem.
<p>
The backtracking technique is especially useful if you can build a
solution in stages. The classic example is the &quot;eight queens&quot; problem,
where you must place eight queens on a chess board in such a way that
none can get another in one move. A little thought shows that
each queen must be placed in its own column (or row). Placing the first
queen in the first column gives us eight possibilities. Placing the
second is only possible if it is not within the range of the first one.
<p>
If it is, it makes no sense to continue so we can eliminate in the
second step a whole subset of possible configurations, namely those with
the second queeen within range of the first.
We can continue building up a partial solution in this way until we
finally have eight queens on the board.
<p>
The idea of the module is that all the data that describe a possible
partial solution to the problem are contained in a derived type called
SOLUTION_DATA. Then the user-defined routine <em>generate</em> generates
a new set of partial solutions based on this.
<p>
The new set is examined to see if any is acceptable within the context
of the problem, which is done via another user-defined routine.
<p>
Each acceptable solution within this new step may give rise to its own
set of further solutions. This way the partial solutions are extended in
each step until finally a complete solution is found.
<h2><a name="routines">ROUTINES</a></h2>
<p>
The module backtracking contains
<dl>
<dt><a name="1"><b class='cmd'>call runtests( testproc )</b> </a><dd>
Routine to start the unit tests. It checks if the file &quot;funit.run&quot;
exists. If so, it will call the subroutine <em>testproc</em> that was
passed. Otherwise it will simply return, so that the ordinary program
execution may continue.
<br><br>
If the subroutine testproc returns, the program stops.
<br><br>
<dl>
<dt>subroutine <i class='arg'>testproc</i><dd>
Subroutine that calls the individual test routines. It takes no
arguments. It wil generally exist of a series of calls to the
routine <em>test</em> - see below.
</dl>
<dt><a name="2"><b class='cmd'>call test( proc, text )</b> </a><dd>
Routine to run the individual unit test routine (emph proc). It decides
if the test has not run yet and if so, the test routine is called.
Otherwise it is skipped.
<br><br>
<em>test</em> takes care of all administrative details.
<br><br>
Note: to make it possible to use <em>private</em> unit test routines,
the source code of this subroutine is kept in a separate file,
<em>funit_test.f90</em> that should be included in an appropriate
place in the program's sources. This way, you can make it a private
routine in each module. The only public access to the unit testing
routines is then via the subroutine <em>testproc</em> that is passed to
<em>runtests</em>.
<br><br>
<dl>
<dt>subroutine <i class='arg'>proc</i><dd>
Subroutine that implements an individual unit test. It takes no
arguments. Within each such subroutine the complete unit test is run.
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>text</i><dd>
Text describing the particular unit test. It is printed in the log
file.
</dl>
<dt><a name="3"><b class='cmd'>call assert_true( cond, text )</b> </a><dd>
Routine to check that a condition is true. If not, a message is printed
in the log file and the number of failures is increased.
<br><br>
<dl>
<dt>logical <i class='arg'>cond</i><dd>
The condition to be checked
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>text</i><dd>
Text describing the condition
</dl>
<dt><a name="4"><b class='cmd'>exists = funit_file_exists( filename )</b> </a><dd>
Logical function to check that a particular file exists
<br><br>
<dl>
<dt>character(len=*), intent(in) <i class='arg'>filename</i><dd>
Name of the file to be checked
</dl>
<dt><a name="5"><b class='cmd'>call funit_get_lun( lun )</b> </a><dd>
Subroutine to get a free LU-number
<br><br>
<dl>
<dt>integer, intent(out) <i class='arg'>lun</i><dd>
Next free LU-number
</dl>
<dt><a name="6"><b class='cmd'>call funit_remove_file( filename )</b> </a><dd>
Subroutine to remove (delete) a file
<br><br>
<dl>
<dt>character(len=*), intent(in) <i class='arg'>filename</i><dd>
Name of the file to be removed
</dl>
<dt><a name="7"><b class='cmd'>call funit_make_empty_file( filename )</b> </a><dd>
Subroutine to make a new, empty file
<br><br>
<dl>
<dt>character(len=*), intent(in) <i class='arg'>filename</i><dd>
Name of the file to be created
</dl>
</dl>
<h2><a name="todo">TODO</a></h2>
<p>
The following things are still left to do:
<ul>
<li>
Proper inclusion of the routine <em>prolog</em> and <em>epilog</em>
<br><br>
<li>
Extension of the set of assertion routines
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,154 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/backtracking n 1.1]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Backtracking}]
[description]
The module [emph Backtracking] implements in a general way a well-known
algorithm to solve certain combinatorial problems. The module actually
consists of a single routine that forms the framework of the algorithm
and is to be used in conjunction with a small set of user-defined
routines that implement the specific problem.
[para]
The backtracking technique is especially useful if you can build a
solution in stages. The classic example is the "eight queens" problem,
where you must place eight queens on a chess board in such a way that
none can get another in one move. A little thought shows that
each queen must be placed in its own column (or row). Placing the first
queen in the first column gives us eight possibilities. Placing the
second is only possible if it is not within the range of the first one.
[para]
If it is, it makes no sense to continue so we can eliminate in the
second step a whole subset of possible configurations, namely those with
the second queeen within range of the first.
We can continue building up a partial solution in this way until we
finally have eight queens on the board.
[para]
The idea of the module is that all the data that describe a possible
partial solution to the problem are contained in a derived type called
SOLUTION_DATA. Then the user-defined routine [emph generate] generates
a new set of partial solutions based on this.
[para]
The new set is examined to see if any is acceptable within the context
of the problem, which is done via another user-defined routine.
[para]
Each acceptable solution within this new step may give rise to its own
set of further solutions. This way the partial solutions are extended in
each step until finally a complete solution is found.
[section ROUTINES]
The module backtracking contains
[list_begin definitions]
[call [cmd "call runtests( testproc )"]]
Routine to start the unit tests. It checks if the file "funit.run"
exists. If so, it will call the subroutine [emph testproc] that was
passed. Otherwise it will simply return, so that the ordinary program
execution may continue.
[nl]
If the subroutine testproc returns, the program stops.
[list_begin arg]
[arg_def "subroutine" testproc]
Subroutine that calls the individual test routines. It takes no
arguments. It wil generally exist of a series of calls to the
routine [emph test] - see below.
[list_end]
[call [cmd "call test( proc, text )"]]
Routine to run the individual unit test routine (emph proc). It decides
if the test has not run yet and if so, the test routine is called.
Otherwise it is skipped.
[nl]
[emph test] takes care of all administrative details.
[nl]
Note: to make it possible to use [emph private] unit test routines,
the source code of this subroutine is kept in a separate file,
[emph funit_test.f90] that should be included in an appropriate
place in the program's sources. This way, you can make it a private
routine in each module. The only public access to the unit testing
routines is then via the subroutine [emph testproc] that is passed to
[emph runtests].
[list_begin arg]
[arg_def "subroutine" proc]
Subroutine that implements an individual unit test. It takes no
arguments. Within each such subroutine the complete unit test is run.
[arg_def "character(len=*), intent(in)" text]
Text describing the particular unit test. It is printed in the log
file.
[list_end]
[call [cmd "call assert_true( cond, text )"]]
Routine to check that a condition is true. If not, a message is printed
in the log file and the number of failures is increased.
[list_begin arg]
[arg_def "logical" cond]
The condition to be checked
[arg_def "character(len=*), intent(in)" text]
Text describing the condition
[list_end]
[call [cmd "exists = funit_file_exists( filename )"]]
Logical function to check that a particular file exists
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be checked
[list_end]
[call [cmd "call funit_get_lun( lun )"]]
Subroutine to get a free LU-number
[list_begin arg]
[arg_def "integer, intent(out)" lun]
Next free LU-number
[list_end]
[call [cmd "call funit_remove_file( filename )"]]
Subroutine to remove (delete) a file
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be removed
[list_end]
[call [cmd "call funit_make_empty_file( filename )"]]
Subroutine to make a new, empty file
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be created
[list_end]
[list_end]
[section TODO]
The following things are still left to do:
[list_begin bullet]
[bullet]
Proper inclusion of the routine [emph prolog] and [emph epilog]
[bullet]
Extension of the set of assertion routines
[list_end]
[manpage_end]

View File

@@ -0,0 +1,384 @@
<! -- -*- flibs -*- doctools manpage
-->
<html><head>
<title>libdate - flibs </title>
</head>
<! -- Generated from file 'libdate.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2008 Arjan van Dijk &lt;arjan dot van dijk at rivm dot nl&gt; -- Copyright &copy; 2008 Arjen Markus &lt;arjenmarkus at sourceforge dot net&gt;
-->
<! -- CVS: $Id: libdate.html,v 1.1 2008/10/02 09:02:33 arjenmarkus Exp $ libdate.n
-->
<body>
<h1> libdate(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> libdate - Manipulating date/time information
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#derived_types_and_routines">DERIVED TYPES AND ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#acknowledgements">ACKNOWLEDGEMENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>type(DATETYPE) date</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>type(JULIANDATETYPE) julian</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>relational operators</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>newdate = basedate + timestep</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>newdate = basedate - timestep</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>newstep = factor * timestep</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>newstep = timestep * factor</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>timelag_in_days = timelag( date1, date2 )</b> </a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>seconds = delayseconds( timestep )</b> </a></td></tr>
<tr valign=top ><td ><a href="#10"><b class='cmd'>isleap = leapyear( date )</b> </a></td></tr>
<tr valign=top ><td ><a href="#11"><b class='cmd'>daynumber = doy( date )</b> </a></td></tr>
<tr valign=top ><td ><a href="#12"><b class='cmd'>earlier = mindate( date1, date2 )</b> </a></td></tr>
<tr valign=top ><td ><a href="#13"><b class='cmd'>later = maxdate( date1, date2 )</b> </a></td></tr>
<tr valign=top ><td ><a href="#14"><b class='cmd'>call format_date( date, pattern, datestring )</b> </a></td></tr>
<tr valign=top ><td ><a href="#15"><b class='cmd'>julian = date2julian( date )</b> </a></td></tr>
<tr valign=top ><td ><a href="#16"><b class='cmd'>date = julian2date( julian )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>libdate</em> module defines a derived type and several functions
and subroutines to deal with date/time information:
<ul>
<li>
Store date and time in the form of year, month, day, hour, minute
<br><br>
<li>
Compare dates
<br><br>
<li>
Basic arithmetic
<br><br>
<li>
Formatting date and time as a string based on a pattern
</ul>
<em>Note:</em> Timezones and seconds are <em>not</em> taken into account.
Also, there are no provisions to take care of the various historical
introductions of the Gregorian calendar.
<h2><a name="derived_types_and_routines">DERIVED TYPES AND ROUTINES</a></h2>
<p>
The module <em>libdate</em> defines two separate derived types, DATETYPE
and JULIANDATETYPE, though this second type is mainly meant for internal
use:
<dl>
<dt><a name="1"><b class='cmd'>type(DATETYPE) date</b> </a><dd>
This type has the following fields: year, month, day, hour, minute, in
that order, so that <em>thisdate = datetype( 2007, 1, 29, 17, 0)</em>
defines a date 29 january 2007 and a time 17:00.
<br><br>
A duration is expressed in days, hours and minutes:
<em>period = datetype( 0, 0, 2, 1, 0)</em> means a period of 2 days and
and 1 hour. (When adding a duration to a date/time the month and year
fields are ignored, as they are not additive).
<br><br>
<br><br>
<dt><a name="2"><b class='cmd'>type(JULIANDATETYPE) julian</b> </a><dd>
Julian dates are used internally to make the computations easier. You
should not need to use them explicitly, unless you want to implement
new functionality.
</dl>
The following functions, subroutines and operators are available:
<dl>
<dt><a name="3"><b class='cmd'>relational operators</b> </a><dd>
You can compare two dates using the standard operators
<em>.EQ.</em>, <em>.NE.</em>, <em>.GE.</em>, <em>.GT.</em>, <em>.LE.</em>,
<em>.LT.</em>, with conventional meaning
<br><br>
<br><br>
<dt><a name="4"><b class='cmd'>newdate = basedate + timestep</b> </a><dd>
Add a duration to a date. The second date/time is considered to be the
duration.
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>basedate</i><dd>
The base date/time to which the duration is to be added.
<br><br>
<dt>type(DATETYPE) <i class='arg'>timestep</i><dd>
The duration that will be added. Only the day, hour and minute fields
are considered.
</dl>
<br><br>
<dt><a name="5"><b class='cmd'>newdate = basedate - timestep</b> </a><dd>
Subtract a duration from a date. The second date/time is considered to
be the duration.
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>basedate</i><dd>
The base date/time from which the duration is to be subtracted.
<br><br>
<dt>type(DATETYPE) <i class='arg'>timestep</i><dd>
The duration that will be subtracted. Only the day, hour and minute
fields are considered.
</dl>
<br><br>
<dt><a name="6"><b class='cmd'>newstep = factor * timestep</b> </a><dd>
Multiply a timestep by a real or integer factor. For the timestep,
only the day, hour and minute are considered.
<br><br>
<dl>
<dt>integer/real <i class='arg'>factor</i><dd>
Factor to be applied
<br><br>
<dt>type(DATETYPE) <i class='arg'>timestep</i><dd>
The duration that will be scaled.
</dl>
<br><br>
<dt><a name="7"><b class='cmd'>newstep = timestep * factor</b> </a><dd>
Multiply a timestep by a real or integer factor. For the timestep,
only the day, hour and minute are considered. (The order of teh
arguments is reversed).
<br><br>
<br><br>
<dt><a name="8"><b class='cmd'>timelag_in_days = timelag( date1, date2 )</b> </a><dd>
Compute the time difference between two dates. Return the value in days.
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>date1</i><dd>
First date
<br><br>
<dt>type(DATETYPE) <i class='arg'>date2</i><dd>
Second date. If this date is earlier than the first date, the difference
is positive.
</dl>
<br><br>
<dt><a name="9"><b class='cmd'>seconds = delayseconds( timestep )</b> </a><dd>
Compute the number of seconds in a timestep
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>timestep</i><dd>
Timestep to be converted to seconds
</dl>
<br><br>
<dt><a name="10"><b class='cmd'>isleap = leapyear( date )</b> </a><dd>
Determine if the year in the date structure is a leap year or not
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>date</i><dd>
Date/time to be considered (only the year is of interest of course).
</dl>
<br><br>
<dt><a name="11"><b class='cmd'>daynumber = doy( date )</b> </a><dd>
Compute the day of the year
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>date</i><dd>
Date/time to be considered.
</dl>
<br><br>
<dt><a name="12"><b class='cmd'>earlier = mindate( date1, date2 )</b> </a><dd>
Return the earlier of the two dates
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>date1</i><dd>
First date/time to be considered.
<br><br>
<dt>type(DATETYPE) <i class='arg'>date2</i><dd>
Second date/time to be considered.
</dl>
<br><br>
<dt><a name="13"><b class='cmd'>later = maxdate( date1, date2 )</b> </a><dd>
Return the later of the two dates
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>date1</i><dd>
First date/time to be considered.
<br><br>
<dt>type(DATETYPE) <i class='arg'>date2</i><dd>
Second date/time to be considered.
</dl>
<br><br>
<dt><a name="14"><b class='cmd'>call format_date( date, pattern, datestring )</b> </a><dd>
Format a date according to a pattern.
<br><br>
The pattern may contain any of the following format codes:
<br><br>
<ul>
<li>
<em>dd</em> - Day of month (&quot;01&quot; for instance)
<br><br>
<li>
<em>ds</em> - Day of month (&quot;1&quot; for instance, s for space)
<br><br>
<li>
<em>DDD</em> - Day of the year
<br><br>
<li>
<em>HH</em> - Hour (00-23)
<br><br>
<li>
<em>HS</em> - Hour (0-23)
<br><br>
<li>
<em>mm</em> - Month (&quot;01&quot; for january)
<br><br>
<li>
<em>ms</em> - Month (&quot;1&quot; for january, s for space)
<br><br>
<li>
<em>MM</em> - Minutes within the hour (00-59)
<br><br>
<li>
<em>MS</em> - Minutes within the hour (0-59)
<br><br>
<li>
<em>YY</em> - Year with the century
<br><br>
<li>
<em>yyyy</em> - Year with the century
</ul>
<dl>
<dt>type(DATETYPE) <i class='arg'>date</i><dd>
Date to be converted
<br><br>
<dt>character(len=*) <i class='arg'>pattern</i><dd>
String containing the format pattern
<br><br>
<dt>character(len=*) <i class='arg'>datestring</i><dd>
String containing the result. The contents will not be longer
than the pattern.
</dl>
<br><br>
<dt><a name="15"><b class='cmd'>julian = date2julian( date )</b> </a><dd>
Convert a date/time structure to Julian date. Mainly for internal use.
<br><br>
<dl>
<dt>type(DATETYPE) <i class='arg'>date</i><dd>
Date/time structure to be converted.
</dl>
<br><br>
<dt><a name="16"><b class='cmd'>date = julian2date( julian )</b> </a><dd>
Convert a Julian date to a date/time structure. Mainly for internal use.
<br><br>
<dl>
<dt>type(JULIANDATETYPE) <i class='arg'>julian</i><dd>
Julian date to be converted.
</dl>
</dl>
<h2><a name="acknowledgements">ACKNOWLEDGEMENTS</a></h2>
<p>
This module was written and contributed by Arjan van Dijk. Small
modifications and the addition of the <em>format_date</em> routine by
Arjen Markus.
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2008 Arjan van Dijk &lt;arjan dot van dijk at rivm dot nl&gt;<br>
Copyright &copy; 2008 Arjen Markus &lt;arjenmarkus at sourceforge dot net&gt;<br>
</body></html>

View File

@@ -0,0 +1,283 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin libdate n 1.0]
[copyright {2008 Arjan van Dijk <arjan dot van dijk at rivm dot nl>}]
[copyright {2008 Arjen Markus <arjenmarkus at sourceforge dot net>}]
[moddesc flibs]
[titledesc {Manipulating date/time information}]
[description]
The [term libdate] module defines a derived type and several functions
and subroutines to deal with date/time information:
[list_begin bullet]
[bullet]
Store date and time in the form of year, month, day, hour, minute
[bullet]
Compare dates
[bullet]
Basic arithmetic
[bullet]
Formatting date and time as a string based on a pattern
[list_end]
[strong Note:] Timezones and seconds are [strong not] taken into account.
Also, there are no provisions to take care of the various historical
introductions of the Gregorian calendar.
[section "DERIVED TYPES AND ROUTINES"]
The module [term libdate] defines two separate derived types, DATETYPE
and JULIANDATETYPE, though this second type is mainly meant for internal
use:
[list_begin definitions]
[call [cmd "type(DATETYPE) date"]]
This type has the following fields: year, month, day, hour, minute, in
that order, so that [term "thisdate = datetype( 2007, 1, 29, 17, 0)"]
defines a date 29 january 2007 and a time 17:00.
[nl]
A duration is expressed in days, hours and minutes:
[term "period = datetype( 0, 0, 2, 1, 0)"] means a period of 2 days and
and 1 hour. (When adding a duration to a date/time the month and year
fields are ignored, as they are not additive).
[nl]
[call [cmd "type(JULIANDATETYPE) julian"]]
Julian dates are used internally to make the computations easier. You
should not need to use them explicitly, unless you want to implement
new functionality.
[list_end]
The following functions, subroutines and operators are available:
[list_begin definitions]
[call [cmd "relational operators"]]
You can compare two dates using the standard operators
[term .EQ.], [term .NE.], [term .GE.], [term .GT.], [term .LE.],
[term .LT.], with conventional meaning
[nl]
[call [cmd "newdate = basedate + timestep"]]
Add a duration to a date. The second date/time is considered to be the
duration.
[list_begin arg]
[arg_def "type(DATETYPE)" basedate]
The base date/time to which the duration is to be added.
[arg_def "type(DATETYPE)" timestep]
The duration that will be added. Only the day, hour and minute fields
are considered.
[list_end]
[nl]
[call [cmd "newdate = basedate - timestep"]]
Subtract a duration from a date. The second date/time is considered to
be the duration.
[list_begin arg]
[arg_def "type(DATETYPE)" basedate]
The base date/time from which the duration is to be subtracted.
[arg_def "type(DATETYPE)" timestep]
The duration that will be subtracted. Only the day, hour and minute
fields are considered.
[list_end]
[nl]
[call [cmd "newstep = factor * timestep"]]
Multiply a timestep by a real or integer factor. For the timestep,
only the day, hour and minute are considered.
[list_begin arg]
[arg_def "integer/real" factor]
Factor to be applied
[arg_def "type(DATETYPE)" timestep]
The duration that will be scaled.
[list_end]
[nl]
[call [cmd "newstep = timestep * factor"]]
Multiply a timestep by a real or integer factor. For the timestep,
only the day, hour and minute are considered. (The order of teh
arguments is reversed).
[nl]
[call [cmd "timelag_in_days = timelag( date1, date2 )"]]
Compute the time difference between two dates. Return the value in days.
[list_begin arg]
[arg_def "type(DATETYPE)" date1]
First date
[arg_def "type(DATETYPE)" date2]
Second date. If this date is earlier than the first date, the difference
is positive.
[list_end]
[nl]
[call [cmd "seconds = delayseconds( timestep )"]]
Compute the number of seconds in a timestep
[list_begin arg]
[arg_def "type(DATETYPE)" timestep]
Timestep to be converted to seconds
[list_end]
[nl]
[call [cmd "isleap = leapyear( date )"]]
Determine if the year in the date structure is a leap year or not
[list_begin arg]
[arg_def "type(DATETYPE)" date]
Date/time to be considered (only the year is of interest of course).
[list_end]
[nl]
[call [cmd "daynumber = doy( date )"]]
Compute the day of the year
[list_begin arg]
[arg_def "type(DATETYPE)" date]
Date/time to be considered.
[list_end]
[nl]
[call [cmd "earlier = mindate( date1, date2 )"]]
Return the earlier of the two dates
[list_begin arg]
[arg_def "type(DATETYPE)" date1]
First date/time to be considered.
[arg_def "type(DATETYPE)" date2]
Second date/time to be considered.
[list_end]
[nl]
[call [cmd "later = maxdate( date1, date2 )"]]
Return the later of the two dates
[list_begin arg]
[arg_def "type(DATETYPE)" date1]
First date/time to be considered.
[arg_def "type(DATETYPE)" date2]
Second date/time to be considered.
[list_end]
[nl]
[call [cmd "call format_date( date, pattern, datestring )"]]
Format a date according to a pattern.
[nl]
The pattern may contain any of the following format codes:
[list_begin bullet]
[bullet]
[term dd] - Day of month ("01" for instance)
[bullet]
[term ds] - Day of month ("1" for instance, s for space)
[bullet]
[term DDD] - Day of the year
[bullet]
[term HH] - Hour (00-23)
[bullet]
[term HS] - Hour (0-23)
[bullet]
[term mm] - Month ("01" for january)
[bullet]
[term ms] - Month ("1" for january, s for space)
[bullet]
[term MM] - Minutes within the hour (00-59)
[bullet]
[term MS] - Minutes within the hour (0-59)
[bullet]
[term YY] - Year with the century
[bullet]
[term yyyy] - Year with the century
[list_end]
[list_begin arg]
[arg_def "type(DATETYPE)" date]
Date to be converted
[arg_def "character(len=*)" pattern]
String containing the format pattern
[arg_def "character(len=*)" datestring]
String containing the result. The contents will not be longer
than the pattern.
[list_end]
[nl]
[call [cmd "julian = date2julian( date )"]]
Convert a date/time structure to Julian date. Mainly for internal use.
[list_begin arg]
[arg_def "type(DATETYPE)" date]
Date/time structure to be converted.
[list_end]
[nl]
[call [cmd "date = julian2date( julian )"]]
Convert a Julian date to a date/time structure. Mainly for internal use.
[list_begin arg]
[arg_def "type(JULIANDATETYPE)" julian]
Julian date to be converted.
[list_end]
[list_end]
[section "ACKNOWLEDGEMENTS"]
This module was written and contributed by Arjan van Dijk. Small
modifications and the addition of the [term format_date] routine by
Arjen Markus.
[manpage_end]

View File

@@ -0,0 +1,60 @@
<! -- -*- flibs -*- doctools manpage
-->
<html><head>
<title>select_precision - flibs </title>
</head>
<! -- Generated from file 'select_precision.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2008 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: select_precision.html,v 1.1 2008/09/26 04:19:41 arjenmarkus Exp $ select_precision.n
-->
<body>
<h1> select_precision(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> select_precision - Select the precision for real variables
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The auxiliary <em>select_precision</em> module defines three parameters
by which you can select the precision for real variables and constants:
<ul>
<li>
<em>sp</em> - Single precision
<br><br>
<li>
<em>dp</em> - Double precision (the real kind whose precision is larger
than that of single precision)
<br><br>
<li>
<em>wp</em> - Working precision. If you consistently use <em>wp</em> as the
kind for your real variables and constants, then switching between
single and double precision is merely a matter of setting <em>wp</em> to
either <em>sp</em> or <em>dp</em> in this module and recompiling.
</ul>
<em>Note:</em> One of the pitfalls in using double precision is that
literal constants, such as <em>1.2345678901234567890</em> are not
automatically interpreted as double precision. You need to specify the
kind explicitly: <em>1.2345678901234567890_wp</em> will define the
constant in the working precision.
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2008 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,34 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin select_precision n 1.0]
[copyright {2008 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Select the precision for real variables}]
[description]
The auxiliary [term select_precision] module defines three parameters
by which you can select the precision for real variables and constants:
[list_begin bullet]
[bullet]
[term sp] - Single precision
[bullet]
[term dp] - Double precision (the real kind whose precision is larger
than that of single precision)
[bullet]
[term wp] - Working precision. If you consistently use [term wp] as the
kind for your real variables and constants, then switching between
single and double precision is merely a matter of setting [term wp] to
either [term sp] or [term dp] in this module and recompiling.
[list_end]
[strong Note:] One of the pitfalls in using double precision is that
literal constants, such as [term 1.2345678901234567890] are not
automatically interpreted as double precision. You need to specify the
kind explicitly: [term 1.2345678901234567890_wp] will define the
constant in the working precision.
[manpage_end]

View File

@@ -0,0 +1,269 @@
<html><head>
<title>finite_state_machine - flibs </title>
</head>
<! -- Generated from file 'finite_state.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: finite_state.html,v 1.1 2008/06/13 10:31:48 relaxmike Exp $ finite_state_machine.n
-->
<body>
<h1> finite_state_machine(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> finite_state_machine - Support for building finite state machines
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#data_types_and_routines">DATA TYPES AND ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#example">EXAMPLE</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>call fsm_loop( data, machine )</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>call fsm_loop_int( data, machine )</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>call fsm_loop_print( data, machine, print_debug )</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>call fsm_loop_print_int( data, machine, print_debug )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call fsm_get_state( fsm, state )</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>call fsm_set_state( fsm, state )</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>call fsm_set_lurep( fsm, lurep )</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>lurep = fsm_get_lurep( fsm )</b> </a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>call fsm_finish( fsm )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>finite_state.f90</em> source file defines a set of subroutines
that allow you to build a so-called finite state machine. This is
basically a way to structure a program or a part of a program that
takes input (from a file or from some other source) and reacts to
that input depending on the &quot;state&quot; it is in. A simple example could
be a heating device with a thermostat: if the ambient temperature is
high enough, there is no need to heat the room, so the system is in a
rest state. If the temperature is lower than the set temperature, the
heater should be turned on.
<p>
Finite state machines are encountered in many different areas in one
form or other. Lexical analysers are another, more complicated example.
When analysing an arithmetic expression like &quot;1+2*3&quot;, the &quot;+&quot; that
follows the &quot;1&quot; will probably bring the analyser in a different state:
the literal number has terminated, it now needs to deal with an
operator. This type of programming is used in the test/demo program to
show how to use the finite_state.f90 source file to build a non-trivial
FSM.
<h2><a name="data_types_and_routines">DATA TYPES AND ROUTINES</a></h2>
<p>
The source code expects a data type, STATE_DATA, that contains all
information describing the finite state machine. The contents is
entirely up to the application though. The state data are passed to the
subroutine that implements the actual state machine, so that you can use
this argument to prepare the computation.
<p>
The type must be defined in a module called &quot;fsm_data_definitions&quot;:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
module MYDATA_POOL
type POOLDATA
integer :: pool_index ! For private use by pool_acquire/pool_release
real, dimension(100) :: work ! The actual work space
end type
include &quot;mem_pool.f90&quot;
end module MYDATA_POOL
</pre></td></tr></table></p>
The code defines the following routines:
<dl>
<dt><a name="1"><b class='cmd'>call fsm_loop( data, machine )</b> </a><dd>
Run a finite state machine, implemented by the subroutine &quot;machine&quot; that
uses character strings to define the state. The first state is always
set to the parameter FSM_INIT_CHAR (=&quot;INIT&quot;), the initial state, and should be
used to initialise the computation.
<br><br>
<dl>
<dt>type(STATE_DATA) <i class='arg'>data</i><dd>
The data defining the current state of the machine
<br><br>
<dt>subroutine <i class='arg'>machine</i><dd>
Subroutine that does the actual computation. Its interface is:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
subroutine machine( fsm, data, curstate )
use fsm_data_definitions
implicit none
type(FSM_STATE), intent(inout) :: fsm
type(STATE_DATA), intent(inout) :: data
character(len=*), intent(in) :: curstate
end subroutine</pre></td></tr></table></p>
</dl>
<br><br>
<dt><a name="2"><b class='cmd'>call fsm_loop_int( data, machine )</b> </a><dd>
Similar to <em>fsm_loop</em> but the machine's state is an integer now.
The first state is represented by the parameter FSM_INIT (=0).
<br><br>
It has the same interface as <em>fsm_loop</em>
<br><br>
<dt><a name="3"><b class='cmd'>call fsm_loop_print( data, machine, print_debug )</b> </a><dd>
Like <em>fsm_loop</em>, but the third argument is a routine that allows
you to print intermediate results. It's interface is:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
subroutine print_debug( lurep, data, oldstate, curstate )
use fsm_data_definitions
implicit none
integer, intent(in) :: lurep
type(STATE_DATA), intent(inout) :: data
character(len=*), intent(in) :: oldstate
character(len=*), intent(in) :: curstate
end subroutine</pre></td></tr></table></p>
<br><br>
<dt><a name="4"><b class='cmd'>call fsm_loop_print_int( data, machine, print_debug )</b> </a><dd>
Like <em>fsm_loop_int</em>, but the third argument is a routine that
allows you to print intermediate results. It's interface is:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
subroutine print_debug( lurep, data, oldstate, curstate )
use fsm_data_definitions
implicit none
integer, intent(in) :: lurep
type(STATE_DATA), intent(inout) :: data
integer, intent(in) :: oldstate
integer, intent(in) :: curstate
end subroutine</pre></td></tr></table></p>
<br><br>
<dt><a name="5"><b class='cmd'>call fsm_get_state( fsm, state )</b> </a><dd>
Get the current state from the FSM data structure
<br><br>
<dl>
<dt>type(FSM_DATA) <i class='arg'>fsm</i><dd>
The data maintained by the FSM loop
<br><br>
<dt>integer/character(len=*), intent(out) <i class='arg'>state</i><dd>
Current state of the finite state machine - either as integer or as
character string.
</dl>
<dt><a name="6"><b class='cmd'>call fsm_set_state( fsm, state )</b> </a><dd>
Set the current state in the FSM data structure
<br><br>
<dl>
<dt>type(FSM_DATA) <i class='arg'>fsm</i><dd>
The data maintained by the FSM loop
<br><br>
<dt>integer/character(len=*), intent(in) <i class='arg'>state</i><dd>
The new state of the finite state machine - either as integer or as
character string.
</dl>
<dt><a name="7"><b class='cmd'>call fsm_set_lurep( fsm, lurep )</b> </a><dd>
Set the LU-number for the print routine (by default: 0, to be
interpreted as output to screen).
<br><br>
<dt><a name="8"><b class='cmd'>lurep = fsm_get_lurep( fsm )</b> </a><dd>
Return the LU-number for the print routine.
<br><br>
<dt><a name="9"><b class='cmd'>call fsm_finish( fsm )</b> </a><dd>
Instruct the FSM loop to stop.
</dl>
<h2><a name="example">EXAMPLE</a></h2>
<p>
The use of the source code in the two files &quot;finite_state.f90&quot;and
&quot;fsm_state.f90&quot; is illustrated by the following example:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
module fsm_data_definitions
implicit none
include 'fsm_state.f90'
type STATE_DATA
integer :: position ! Current position in the string
integer :: open_parens ! Number of open parentheses
character(len=80) :: string ! String holding the expression
end type STATE_DATA
end module fsm_data_definitions</pre></td></tr></table></p>
This module defines the STATE_DATA derived type and includes the file
with the (private) definitions.
<p>
The module that actually implements the finite state machine looks like
this (the included file &quot;finite_state.f90&quot;contains the <em>contains</em>
keyword):
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
module analyse_string
use fsm_data_definitions
include 'finite_state.f90'
!
! Here is the actual routine that implements the finite state machine
!
! analyse_expression --
! Analyse an arithmetic expression
! Arguments:
! fsm Private data structure for the FSM machinery
! data Evaluation data structure
! state_name Current state of the machine
!
subroutine analyse_expression( fsm, data, state_name )
type(FSM_STATE), intent(inout) :: fsm
type(STATE_DATA), intent(inout) :: data
character(len=*), intent(in) :: state_name
...
end subroutine analyse_expression
end module</pre></td></tr></table></p>
(You can find the complete source code in the file
&quot;tst_finite_state.f90&quot; in the source distribution)
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,210 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin finite_state_machine n 1.0]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Support for building finite state machines}]
[description]
The [strong finite_state.f90] source file defines a set of subroutines
that allow you to build a so-called finite state machine. This is
basically a way to structure a program or a part of a program that
takes input (from a file or from some other source) and reacts to
that input depending on the "state" it is in. A simple example could
be a heating device with a thermostat: if the ambient temperature is
high enough, there is no need to heat the room, so the system is in a
rest state. If the temperature is lower than the set temperature, the
heater should be turned on.
[para]
Finite state machines are encountered in many different areas in one
form or other. Lexical analysers are another, more complicated example.
When analysing an arithmetic expression like "1+2*3", the "+" that
follows the "1" will probably bring the analyser in a different state:
the literal number has terminated, it now needs to deal with an
operator. This type of programming is used in the test/demo program to
show how to use the finite_state.f90 source file to build a non-trivial
FSM.
[section "DATA TYPES AND ROUTINES"]
The source code expects a data type, STATE_DATA, that contains all
information describing the finite state machine. The contents is
entirely up to the application though. The state data are passed to the
subroutine that implements the actual state machine, so that you can use
this argument to prepare the computation.
[para]
The type must be defined in a module called "fsm_data_definitions":
[example {
module MYDATA_POOL
type POOLDATA
integer :: pool_index ! For private use by pool_acquire/pool_release
real, dimension(100) :: work ! The actual work space
end type
include "mem_pool.f90"
end module MYDATA_POOL
}]
The code defines the following routines:
[list_begin definitions]
[call [cmd "call fsm_loop( data, machine )"]]
Run a finite state machine, implemented by the subroutine "machine" that
uses character strings to define the state. The first state is always
set to the parameter FSM_INIT_CHAR (="INIT"), the initial state, and should be
used to initialise the computation.
[list_begin arg]
[arg_def "type(STATE_DATA)" data]
The data defining the current state of the machine
[arg_def "subroutine" machine]
Subroutine that does the actual computation. Its interface is:
[example {
subroutine machine( fsm, data, curstate )
use fsm_data_definitions
implicit none
type(FSM_STATE), intent(inout) :: fsm
type(STATE_DATA), intent(inout) :: data
character(len=*), intent(in) :: curstate
end subroutine}]
[list_end]
[nl]
[call [cmd "call fsm_loop_int( data, machine )"]]
Similar to [emph fsm_loop] but the machine's state is an integer now.
The first state is represented by the parameter FSM_INIT (=0).
[nl]
It has the same interface as [emph fsm_loop]
[call [cmd "call fsm_loop_print( data, machine, print_debug )"]]
Like [emph fsm_loop], but the third argument is a routine that allows
you to print intermediate results. It's interface is:
[example {
subroutine print_debug( lurep, data, oldstate, curstate )
use fsm_data_definitions
implicit none
integer, intent(in) :: lurep
type(STATE_DATA), intent(inout) :: data
character(len=*), intent(in) :: oldstate
character(len=*), intent(in) :: curstate
end subroutine}]
[call [cmd "call fsm_loop_print_int( data, machine, print_debug )"]]
Like [emph fsm_loop_int], but the third argument is a routine that
allows you to print intermediate results. It's interface is:
[example {
subroutine print_debug( lurep, data, oldstate, curstate )
use fsm_data_definitions
implicit none
integer, intent(in) :: lurep
type(STATE_DATA), intent(inout) :: data
integer, intent(in) :: oldstate
integer, intent(in) :: curstate
end subroutine}]
[call [cmd "call fsm_get_state( fsm, state )"]]
Get the current state from the FSM data structure
[list_begin arg]
[arg_def "type(FSM_DATA)" fsm]
The data maintained by the FSM loop
[arg_def "integer/character(len=*), intent(out)" state]
Current state of the finite state machine - either as integer or as
character string.
[list_end]
[call [cmd "call fsm_set_state( fsm, state )"]]
Set the current state in the FSM data structure
[list_begin arg]
[arg_def "type(FSM_DATA)" fsm]
The data maintained by the FSM loop
[arg_def "integer/character(len=*), intent(in)" state]
The new state of the finite state machine - either as integer or as
character string.
[list_end]
[call [cmd "call fsm_set_lurep( fsm, lurep )"]]
Set the LU-number for the print routine (by default: 0, to be
interpreted as output to screen).
[call [cmd "lurep = fsm_get_lurep( fsm )"]]
Return the LU-number for the print routine.
[call [cmd "call fsm_finish( fsm )"]]
Instruct the FSM loop to stop.
[list_end]
[section "EXAMPLE"]
The use of the source code in the two files "finite_state.f90"and
"fsm_state.f90" is illustrated by the following example:
[example {
module fsm_data_definitions
implicit none
include 'fsm_state.f90'
type STATE_DATA
integer :: position ! Current position in the string
integer :: open_parens ! Number of open parentheses
character(len=80) :: string ! String holding the expression
end type STATE_DATA
end module fsm_data_definitions}]
This module defines the STATE_DATA derived type and includes the file
with the (private) definitions.
[para]
The module that actually implements the finite state machine looks like
this (the included file "finite_state.f90"contains the [emph contains]
keyword):
[example {
module analyse_string
use fsm_data_definitions
include 'finite_state.f90'
!
! Here is the actual routine that implements the finite state machine
!
! analyse_expression --
! Analyse an arithmetic expression
! Arguments:
! fsm Private data structure for the FSM machinery
! data Evaluation data structure
! state_name Current state of the machine
!
subroutine analyse_expression( fsm, data, state_name )
type(FSM_STATE), intent(inout) :: fsm
type(STATE_DATA), intent(inout) :: data
character(len=*), intent(in) :: state_name
...
end subroutine analyse_expression
end module}]
(You can find the complete source code in the file
"tst_finite_state.f90" in the source distribution)
[manpage_end]

View File

@@ -0,0 +1,185 @@
<! -- -*- flibs -*- doctools manpage
-->
<html><head>
<title>flibs/strings - flibs </title>
</head>
<! -- Generated from file 'csv_file.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2005 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id$ flibs/strings.n
-->
<body>
<h1> flibs/strings(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> flibs/strings - Writing CSV files
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#routines">ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>use csv_file</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>call csv_next_record( lun )</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>call csv_write( lun, data )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>csv_file</em> module facilitates the writing of
CSV files. Whereas it is very easy to read CSV files with Fortran, using
list-directed read statements:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
integer :: i1, i2
character(len=20) :: string
real :: f3
read(10,*) i1, i2, string, f3
</pre></td></tr></table></p>
for instance, writing them is slightly more complicated. The module
helps with this by two routines, <em>csv_next_record</em> and
<em>csv_write</em>.
<p>
The module supports writing scalar variables, one- and two-dimensional
arrays according to a simple scheme. As CSV files are ordinary formatted
files where each field is separated by commas and strings are possibly
delimited by quotation marks (&quot;), the procedure is this:
<ul>
<li>
Open the file as a formatted file
<br><br>
<li>
Pass the LU-number for the file to the routines mentioned above,
using the second to write the data and the first to force a new record.
</ul>
In more detail, the layout of the CSV-file can be described as follows:
<ul>
<li>
Single items are written to the end of the current record
<br><br>
<li>
One-dimensional items are also written to the end of the current
record
<br><br>
<li>
Two-dimensional items are written to separate records, one for
each row
<br><br>
<li>
Except for the two-dimensional versions, all routines allow
you to suppress advancing to the next record:
<br><br>
<ul>
<li>
for single items you must indicate whether to advance or not
<br><br>
<li>
for one-dimensional items, the argument is optional. Default
is to advance.
</ul>
</ul>
<em>Note on the format:</em>
CSV-files apparently come in different guises (Kernighan and Pike,
The practice of Programming, Addison-Wesley, 1999). This module
uses the following rules:
<ul>
<li>
items are always separated by a single comma (,)
<br><br>
<li>
string items are delimited by double quotes (&quot;)
<br><br>
<li>
embedded double quotes are treated by doubling the quote
<br><br>
<li>
trailing blanks are considered irrelevant
</ul>
<h2><a name="routines">ROUTINES</a></h2>
<p>
The module contains two public subroutines:
<dl>
<dt><a name="1"><b class='cmd'>use csv_file</b> </a><dd>
To import the subroutines for writing a CSV file, use this module.
<br><br>
<dt><a name="2"><b class='cmd'>call csv_next_record( lun )</b> </a><dd>
Writes a new line to the file.
<br><br>
<dl>
<dt>integer <i class='arg'>lun</i><dd>
The LU-number the file is connected to
</dl>
<dt><a name="3"><b class='cmd'>call csv_write( lun, data )</b> </a><dd>
Writes data to the file in the proper format.
<br><br>
<dl>
<dt>integer <i class='arg'>lun</i><dd>
The LU-number the file is connected to
<br><br>
<dt>(...) <i class='arg'>data</i><dd>
The data to be written to the file. The type can be:
<br><br>
<ul>
<li>
an integer or a real (single or double precision) number or a character
string
<br><br>
<li>
an integer or a real (single or double precision)one-dimensional array or a one-dimensional
array of character strings
<br><br>
<li>
an integer or a real (single or double precision)two-dimensional array or a two-dimensional
array of character strings
</ul>
</dl>
</dl>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2005 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,133 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/strings n 1.0]
[copyright {2005 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Writing CSV files}]
[description]
The [strong csv_file] module facilitates the writing of
CSV files. Whereas it is very easy to read CSV files with Fortran, using
list-directed read statements:
[example {
integer :: i1, i2
character(len=20) :: string
real :: f3
read(10,*) i1, i2, string, f3
}]
for instance, writing them is slightly more complicated. The module
helps with this by two routines, [strong csv_next_record] and
[strong csv_write].
[para]
The module supports writing scalar variables, one- and two-dimensional
arrays according to a simple scheme. As CSV files are ordinary formatted
files where each field is separated by commas and strings are possibly
delimited by quotation marks ("), the procedure is this:
[list_begin bullet]
[bullet]
Open the file as a formatted file
[bullet]
Pass the LU-number for the file to the routines mentioned above,
using the second to write the data and the first to force a new record.
[list_end]
In more detail, the layout of the CSV-file can be described as follows:
[list_begin bullet]
[bullet]
Single items are written to the end of the current record
[bullet]
One-dimensional items are also written to the end of the current
record
[bullet]
Two-dimensional items are written to separate records, one for
each row
[bullet]
Except for the two-dimensional versions, all routines allow
you to suppress advancing to the next record:
[list_begin bullet]
[bullet]
for single items you must indicate whether to advance or not
[bullet]
for one-dimensional items, the argument is optional. Default
is to advance.
[list_end]
[list_end]
[strong "Note on the format:"]
CSV-files apparently come in different guises (Kernighan and Pike,
The practice of Programming, Addison-Wesley, 1999). This module
uses the following rules:
[list_begin bullet]
[bullet]
items are always separated by a single comma (,)
[bullet]
string items are delimited by double quotes (")
[bullet]
embedded double quotes are treated by doubling the quote
[bullet]
trailing blanks are considered irrelevant
[list_end]
[section ROUTINES]
The module contains two public subroutines:
[list_begin definitions]
[call [cmd "use csv_file"]]
To import the subroutines for writing a CSV file, use this module.
[call [cmd "call csv_next_record( lun )"]]
Writes a new line to the file.
[list_begin arg]
[arg_def "integer" lun]
The LU-number the file is connected to
[list_end]
[call [cmd "call csv_write( lun, data )"]]
Writes data to the file in the proper format.
[list_begin arg]
[arg_def "integer" lun]
The LU-number the file is connected to
[arg_def "(...)" data]
The data to be written to the file. The type can be:
[list_begin bullet]
[bullet]
an integer or a real (single or double precision) number or a character
string
[bullet]
an integer or a real (single or double precision) one-dimensional array
or a one-dimensional array of character strings
[bullet]
an integer or a real (single or double precision) two-dimensional array
or a two-dimensional array of character strings
[list_end]
[list_end]
[list_end]
[manpage_end]

View File

@@ -0,0 +1,294 @@
<html><head>
<title>flibs/binarytree - flibs </title>
</head>
<! -- Generated from file 'binarytree.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2005 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: binarytree.html,v 1.1 2008/06/13 10:19:49 relaxmike Exp $ flibs/binarytree.n
-->
<body>
<h1> flibs/binarytree(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> flibs/binarytree - Linked lists
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#routines">ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>call btree_create( btree, data)</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>call btree_destroy(btree )</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>count = btree_count( btree )</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>child =&gt; btree_child_node( node, right )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call btree_append_data( node, data, right )</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>call btree_append_subtree( node, subtree, right )</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>call btree_remove_subtree( node, subtree, right )</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>data = btree_get_data( node )</b> </a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>call btree_put_data( node, data )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>binarytree.f90</em> source file allows you to implement
<em>binary trees</em> of any (derived) type without having to edit
the supplied source code. (The resulting binary tree is <em>not</em>
balanced, that would require a method of ordering the data.) To achieve
genericty, a simple technique is used, which is best illustrated by an
example:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
module MYDATA_MODULE
type MYDATA
character(len=20) :: string
end type MYDATA
end module
module MYDATA_BTREES
use MYDATA_MODULE, TREE_DATA =&gt; MYDATA
include &quot;binarytree.f90&quot;
end module MYDATA_BTREES
</pre></td></tr></table></p>
The above code defines a module <em>MYDATA_MODULE</em> with the derived
type that is to be stored in the binary trees. The name of that
derived type can be anything.
<p>
It also defines a module <em>MYDATA_BTREES</em> which will be the module
that holds the functionality to use binary trees:
<ul>
<li>
The module <em>MYDATA_MODULE</em> is <em>used</em>, but the derived type
<em>MYDATA</em> is renamed to the (fixed) name <em>LIST_DATA</em>. (This
is the name used in the generic source file.)
<br><br>
<li>
The source code for the actual routines is simply included via the
INCLUDE statement.
<br><br>
<li>
Nothing more is required, we can close the source text for the module.
</ul>
To use a single type of binary trees in a program, we can just use the
MYDATA_BTREES module. If you need more than one type of data in binary
trees, then apply the same renaming trick on using the specific binary
trees modules.
<p>
In fact the example in the source file &quot;two_lists.f90&quot; shows the general
technique of how to accomplish this for linked lists. The same applies
to binary trees.
<h2><a name="routines">ROUTINES</a></h2>
<p>
The source file <em>binarytree.f90</em> provides the following
routines:
<dl>
<dt><a name="1"><b class='cmd'>call btree_create( btree, data)</b> </a><dd>
Create a new tree with the given data associated to the root.
The data are copied and stored in that root.
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>btree</i><dd>
The variable that will be used for accessing the tree's root
<br><br>
<dt>type(TREE_DATA), intent(in) <i class='arg'>data</i><dd>
The data to be stored in the root
</dl>
<br><br>
<dt><a name="2"><b class='cmd'>call btree_destroy(btree )</b> </a><dd>
Destroy the tree. All nodes contained in it will be destroyed as
well.
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>btree</i><dd>
The list to be destroyed
</dl>
<br><br>
<dt><a name="3"><b class='cmd'>count = btree_count( btree )</b> </a><dd>
Function to return the number of nodes in the tree.
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>btree</i><dd>
The tree in question
</dl>
<br><br>
<dt><a name="4"><b class='cmd'>child =&gt; btree_child_node( node, right )</b> </a><dd>
Function to return the left or right child node of a given node in the
tree. As each node is itself a tree, you can traverse the tree by
repeatedly using this function on the result.
<br><br>
Note: it returns a <em>pointer</em> to the child node,
so you must use <em>=&gt;</em>.
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>node</i><dd>
The (parent) node in a tree or the root of a tree
<br><br>
<dt>logical <i class='arg'>right</i><dd>
Whether to return the right (.true.) or the left (.false.) child node.
</dl>
<br><br>
<dt><a name="5"><b class='cmd'>call btree_append_data( node, data, right )</b> </a><dd>
Append a new node to the left or right to the given node. (Note that no
balancing is taken care of). If the node already has a child node,
nothing is done.
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>node</i><dd>
The node in the tree that should get a child node
<br><br>
<dt>type(TREE_DATA), intent(in) <i class='arg'>data</i><dd>
The data to be stored in the child node
<br><br>
<dt>logical <i class='arg'>right</i><dd>
Whether to append on the right (.true.) or the left (.false.) side.
</dl>
<br><br>
<dt><a name="6"><b class='cmd'>call btree_append_subtree( node, subtree, right )</b> </a><dd>
Append a subtree to the left or right to the given node.
If the node already has a child node, nothing is done. (Note: the
subtree is referred to, not copied!)
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>node</i><dd>
The node in the tree that should get a child node
<br><br>
<dt>type(BINARY_TREE), pointer <i class='arg'>subtree</i><dd>
The tree to be appended as the child node
<br><br>
<dt>logical <i class='arg'>right</i><dd>
Whether to append on the right (.true.) or the left (.false.) side.
</dl>
<br><br>
<dt><a name="7"><b class='cmd'>call btree_remove_subtree( node, subtree, right )</b> </a><dd>
Remove a subtree to the left or right to the given node.
A pointer to the subtree is returned in the &quot;subtree&quot; argument, so that
this can be destroyed or used independently of the original tree.
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>node</i><dd>
The element in the list after which to insert a new one.
<br><br>
<dt>type(BINARY_TREE), pointer <i class='arg'>subtree</i><dd>
The subtree that was removeds the child node
<br><br>
<dt>logical <i class='arg'>right</i><dd>
Whether to remove on the right (.true.) or the left (.false.) side.
</dl>
<br><br>
<dt><a name="8"><b class='cmd'>data = btree_get_data( node )</b> </a><dd>
Return the data belonging to a node
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>node</i><dd>
The node of the tree in question
</dl>
<br><br>
<dt><a name="9"><b class='cmd'>call btree_put_data( node, data )</b> </a><dd>
Put new data at a given node
<br><br>
<dl>
<dt>type(BINARY_TREE), pointer <i class='arg'>node</i><dd>
The node of the tree in question
<br><br>
<dt>type(TREE_DATA), intent(in) <i class='arg'>data</i><dd>
The data to be stored in the node
</dl>
</dl>
Notes:
<ul>
<li>
The binary trees can only store data of the same derived type. In
that sense the code is not generic.
<br><br>
<li>
Currently, the trees can only store derived types that do not require
an explicit destruction. If you want to store a derived type with
pointers to allocated memory, you can do that however, by supplying an
assignment operator. This would lead to a memory leak though. It is best
to wait for a next version that will allow such derived types to be
stored.
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2005 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,227 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/binarytree n 1.0]
[copyright {2005 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Linked lists}]
[description]
The [strong binarytree.f90] source file allows you to implement
[strong "binary trees"] of any (derived) type without having to edit
the supplied source code. (The resulting binary tree is [strong not]
balanced, that would require a method of ordering the data.) To achieve
genericty, a simple technique is used, which is best illustrated by an
example:
[example {
module MYDATA_MODULE
type MYDATA
character(len=20) :: string
end type MYDATA
end module
module MYDATA_BTREES
use MYDATA_MODULE, TREE_DATA => MYDATA
include "binarytree.f90"
end module MYDATA_BTREES
}]
The above code defines a module [strong MYDATA_MODULE] with the derived
type that is to be stored in the binary trees. The name of that
derived type can be anything.
[para]
It also defines a module [strong MYDATA_BTREES] which will be the module
that holds the functionality to use binary trees:
[list_begin bullet]
[bullet]
The module [strong MYDATA_MODULE] is [strong used], but the derived type
[strong MYDATA] is renamed to the (fixed) name [strong LIST_DATA]. (This
is the name used in the generic source file.)
[bullet]
The source code for the actual routines is simply included via the
INCLUDE statement.
[bullet]
Nothing more is required, we can close the source text for the module.
[list_end]
To use a single type of binary trees in a program, we can just use the
MYDATA_BTREES module. If you need more than one type of data in binary
trees, then apply the same renaming trick on using the specific binary
trees modules.
[para]
In fact the example in the source file "two_lists.f90" shows the general
technique of how to accomplish this for linked lists. The same applies
to binary trees.
[section ROUTINES]
The source file [strong "binarytree.f90"] provides the following
routines:
[list_begin definitions]
[call [cmd "call btree_create( btree, data)"]]
Create a new tree with the given data associated to the root.
The data are copied and stored in that root.
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" btree]
The variable that will be used for accessing the tree's root
[arg_def "type(TREE_DATA), intent(in)" data]
The data to be stored in the root
[list_end]
[nl]
[call [cmd "call btree_destroy(btree )"]]
Destroy the tree. All nodes contained in it will be destroyed as
well.
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" btree]
The list to be destroyed
[list_end]
[nl]
[call [cmd "count = btree_count( btree )"]]
Function to return the number of nodes in the tree.
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" btree]
The tree in question
[list_end]
[nl]
[call [cmd "child => btree_child_node( node, right )"]]
Function to return the left or right child node of a given node in the
tree. As each node is itself a tree, you can traverse the tree by
repeatedly using this function on the result.
[nl]
Note: it returns a [strong pointer] to the child node,
so you must use [strong =>].
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" node]
The (parent) node in a tree or the root of a tree
[arg_def "logical" right]
Whether to return the right (.true.) or the left (.false.) child node.
[list_end]
[nl]
[call [cmd "call btree_append_data( node, data, right )"]]
Append a new node to the left or right to the given node. (Note that no
balancing is taken care of). If the node already has a child node,
nothing is done.
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" node]
The node in the tree that should get a child node
[arg_def "type(TREE_DATA), intent(in)" data]
The data to be stored in the child node
[arg_def "logical" right]
Whether to append on the right (.true.) or the left (.false.) side.
[list_end]
[nl]
[call [cmd "call btree_append_subtree( node, subtree, right )"]]
Append a subtree to the left or right to the given node.
If the node already has a child node, nothing is done. (Note: the
subtree is referred to, not copied!)
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" node]
The node in the tree that should get a child node
[arg_def "type(BINARY_TREE), pointer" subtree]
The tree to be appended as the child node
[arg_def "logical" right]
Whether to append on the right (.true.) or the left (.false.) side.
[list_end]
[nl]
[call [cmd "call btree_remove_subtree( node, subtree, right )"]]
Remove a subtree to the left or right to the given node.
A pointer to the subtree is returned in the "subtree" argument, so that
this can be destroyed or used independently of the original tree.
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" node]
The element in the list after which to insert a new one.
[arg_def "type(BINARY_TREE), pointer" subtree]
The subtree that was removeds the child node
[arg_def "logical" right]
Whether to remove on the right (.true.) or the left (.false.) side.
[list_end]
[nl]
[call [cmd "data = btree_get_data( node )"]]
Return the data belonging to a node
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" node]
The node of the tree in question
[list_end]
[nl]
[call [cmd "call btree_put_data( node, data )"]]
Put new data at a given node
[list_begin arg]
[arg_def "type(BINARY_TREE), pointer" node]
The node of the tree in question
[arg_def "type(TREE_DATA), intent(in)" data]
The data to be stored in the node
[list_end]
[list_end]
Notes:
[list_begin bullet]
[bullet]
The binary trees can only store data of the same derived type. In
that sense the code is not generic.
[bullet]
Currently, the trees can only store derived types that do not require
an explicit destruction. If you want to store a derived type with
pointers to allocated memory, you can do that however, by supplying an
assignment operator. This would lead to a memory leak though. It is best
to wait for a next version that will allow such derived types to be
stored.
[list_end]
[manpage_end]

View File

@@ -0,0 +1,275 @@
<html><head>
<title>flibs/datastructures - flibs </title>
</head>
<! -- Generated from file 'linked_list.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2005 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: linked_list.html,v 1.1 2008/06/13 10:27:02 relaxmike Exp $ flibs/datastructures.n
-->
<body>
<h1> flibs/datastructures(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> flibs/datastructures - Linked lists
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#routines">ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>call list_create( list, data)</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>call list_destroy( list)</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>count = list_count( list)</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>next =&gt; list_next( elem )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call list_insert( elem, data )</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>call list_insert_head( list, data )</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>call list_delete_element( list, elem )</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>call list_get_data( elem, data )</b> </a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>call list_put_data( elem, data )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>linkedlist.f90</em> source file allows you to implement
<em>linked lists</em> of any (derived) type without having to edit
the supplied source code. To this end a simple technique is used,
which is best illustrated by an example:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
module MYDATA_MODULE
type MYDATA
character(len=20) :: string
end type MYDATA
end module
module MYDATA_LISTS
use MYDATA_MODULE, LIST_DATA =&gt; MYDATA
include &quot;linkedlist.f90&quot;
end module MYDATA_LISTS
</pre></td></tr></table></p>
The above code defines a module <em>MYDATA_MODULE</em> with the derived
type that is to be stored in the linked lists. The name of that derived
type can be anything.
<p>
It also defines a module <em>MYDATA_LISTS</em> which will be the module
that holds the functionality to use linked lists:
<ul>
<li>
The module <em>MYDATA_MODULE</em> is <em>used</em>, but the derived type
<em>MYDATA</em> is renamed to the (fixed) name <em>LIST_DATA</em>. (This
is the name used in the generic source file.)
<br><br>
<li>
The source code for the actual routines is simply included via the
INCLUDE statement.
<br><br>
<li>
Nothing more is required, we can close the source text for the module.
</ul>
To use a single type of linked lists in a program, we can just use the
MYDATA_LISTS module. If you need more than one type of data in linked
lists, then apply the same renaming trick on using the specific linked
lists modules.
<p>
In fact the example in the source file &quot;two_lists.f90&quot; shows the general
technique of how to accomplish this.
<h2><a name="routines">ROUTINES</a></h2>
<p>
The source file <em>linkedlist.f90</em> provides the following
routines:
<dl>
<dt><a name="1"><b class='cmd'>call list_create( list, data)</b> </a><dd>
Create a new list with one element. The given data are copied and stored
in that element.
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>list</i><dd>
The variable that will be used for accessing the list
<br><br>
<dt>type(LIST_DATA), intent(in) <i class='arg'>data</i><dd>
The data to be stored in the first element
</dl>
<br><br>
<dt><a name="2"><b class='cmd'>call list_destroy( list)</b> </a><dd>
Destroy the list. All elements contained in it will be destroyed as
well.
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>list</i><dd>
The list to be destroyed
</dl>
<br><br>
<dt><a name="3"><b class='cmd'>count = list_count( list)</b> </a><dd>
Function to return the number of elements in the list.
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>list</i><dd>
The list in question
</dl>
<br><br>
<dt><a name="4"><b class='cmd'>next =&gt; list_next( elem )</b> </a><dd>
Function to return the next element in the list. Note: it returns a
<em>pointer</em> to the next element, so you must use <em>=&gt;</em>.
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>elem</i><dd>
The element in the list
</dl>
<br><br>
<dt><a name="5"><b class='cmd'>call list_insert( elem, data )</b> </a><dd>
Insert a new element (with the given data) into the list, <em>after</em>
the given element.
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>elem</i><dd>
The element in the list after which to insert a new one.
<br><br>
<dt>type(LIST_DATA), intent(in) <i class='arg'>data</i><dd>
The data to be stored in the new element
</dl>
<br><br>
<dt><a name="6"><b class='cmd'>call list_insert_head( list, data )</b> </a><dd>
Insert a new element (with the given data) before the head of list.
The argument &quot;list&quot; will point to the new head.
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>list</i><dd>
The list in question
<br><br>
<dt>type(LIST_DATA), intent(in) <i class='arg'>data</i><dd>
The data to be stored at the new head
</dl>
<br><br>
<dt><a name="7"><b class='cmd'>call list_delete_element( list, elem )</b> </a><dd>
Delete the given element from the list. The associated data
will disappear.
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>list</i><dd>
The list in question
<br><br>
<dt>type(LINKED_LIST), pointer <i class='arg'>elem</i><dd>
The element to be deleted
</dl>
<br><br>
<dt><a name="8"><b class='cmd'>call list_get_data( elem, data )</b> </a><dd>
Copy the data belonging to the given element into the argument &quot;data&quot;.
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>list</i><dd>
The element in question
<br><br>
<dt>type(LIST_DATA), intent(out) <i class='arg'>data</i><dd>
The variable to hold the data
</dl>
<br><br>
<dt><a name="9"><b class='cmd'>call list_put_data( elem, data )</b> </a><dd>
Copy the given data into the given element (overwriting the previous)
<br><br>
<dl>
<dt>type(LINKED_LIST), pointer <i class='arg'>list</i><dd>
The element in question
<br><br>
<br><br>
<dt>type(LIST_DATA), intent(in) <i class='arg'>data</i><dd>
The new data
</dl>
</dl>
Notes:
<ul>
<li>
The lists can only store data of the same derived type. In that sense
the code is not generic.
<br><br>
<li>
Currently, the lists can only store derived types that do not require
an explicit destruction. If you want to store a derived type with
pointers to allocated memory, you can do that however, by supplying an
assignment operator. This would lead to a memory leak though. It is best
to wait for the next version which will allow such derived types to be
stored.
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2005 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,211 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/datastructures n 1.0]
[copyright {2005 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Linked lists}]
[description]
The [strong linkedlist.f90] source file allows you to implement
[strong "linked lists"] of any (derived) type without having to edit
the supplied source code. To this end a simple technique is used,
which is best illustrated by an example:
[example {
module MYDATA_MODULE
type MYDATA
character(len=20) :: string
end type MYDATA
end module
module MYDATA_LISTS
use MYDATA_MODULE, LIST_DATA => MYDATA
include "linkedlist.f90"
end module MYDATA_LISTS
}]
The above code defines a module [strong MYDATA_MODULE] with the derived
type that is to be stored in the linked lists. The name of that derived
type can be anything.
[para]
It also defines a module [strong MYDATA_LISTS] which will be the module
that holds the functionality to use linked lists:
[list_begin bullet]
[bullet]
The module [strong MYDATA_MODULE] is [strong used], but the derived type
[strong MYDATA] is renamed to the (fixed) name [strong LIST_DATA]. (This
is the name used in the generic source file.)
[bullet]
The source code for the actual routines is simply included via the
INCLUDE statement.
[bullet]
Nothing more is required, we can close the source text for the module.
[list_end]
To use a single type of linked lists in a program, we can just use the
MYDATA_LISTS module. If you need more than one type of data in linked
lists, then apply the same renaming trick on using the specific linked
lists modules.
[para]
In fact the example in the source file "two_lists.f90" shows the general
technique of how to accomplish this.
[section ROUTINES]
The source file [strong "linkedlist.f90"] provides the following
routines:
[list_begin definitions]
[call [cmd "call list_create( list, data)"]]
Create a new list with one element. The given data are copied and stored
in that element.
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" list]
The variable that will be used for accessing the list
[arg_def "type(LIST_DATA), intent(in)" data]
The data to be stored in the first element
[list_end]
[nl]
[call [cmd "call list_destroy( list)"]]
Destroy the list. All elements contained in it will be destroyed as
well.
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" list]
The list to be destroyed
[list_end]
[nl]
[call [cmd "count = list_count( list)"]]
Function to return the number of elements in the list.
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" list]
The list in question
[list_end]
[nl]
[call [cmd "next => list_next( elem )"]]
Function to return the next element in the list. Note: it returns a
[strong pointer] to the next element, so you must use [strong =>].
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" elem]
The element in the list
[list_end]
[nl]
[call [cmd "call list_insert( elem, data )"]]
Insert a new element (with the given data) into the list, [strong after]
the given element.
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" elem]
The element in the list after which to insert a new one.
[arg_def "type(LIST_DATA), intent(in)" data]
The data to be stored in the new element
[list_end]
[nl]
[call [cmd "call list_insert_head( list, data )"]]
Insert a new element (with the given data) before the head of list.
The argument "list" will point to the new head.
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" list]
The list in question
[arg_def "type(LIST_DATA), intent(in)" data]
The data to be stored at the new head
[list_end]
[nl]
[call [cmd "call list_delete_element( list, elem )"]]
Delete the given element from the list. The associated data
will disappear.
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" list]
The list in question
[arg_def "type(LINKED_LIST), pointer" elem]
The element to be deleted
[list_end]
[nl]
[call [cmd "call list_get_data( elem, data )"]]
Copy the data belonging to the given element into the argument "data".
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" list]
The element in question
[arg_def "type(LIST_DATA), intent(out)" data]
The variable to hold the data
[list_end]
[nl]
[call [cmd "call list_put_data( elem, data )"]]
Copy the given data into the given element (overwriting the previous)
[list_begin arg]
[arg_def "type(LINKED_LIST), pointer" list]
The element in question
[nl]
[arg_def "type(LIST_DATA), intent(in)" data]
The new data
[list_end]
[list_end]
Notes:
[list_begin bullet]
[bullet]
The lists can only store data of the same derived type. In that sense
the code is not generic.
[bullet]
Currently, the lists can only store derived types that do not require
an explicit destruction. If you want to store a derived type with
pointers to allocated memory, you can do that however, by supplying an
assignment operator. This would lead to a memory leak though. It is best
to wait for the next version which will allow such derived types to be
stored.
[list_end]
[manpage_end]

View File

@@ -0,0 +1,111 @@
<html><head>
<title>mem_pool - flibs </title>
</head>
<! -- Generated from file 'mem_pool.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: mem_pool.html,v 1.1 2008/06/13 10:33:28 relaxmike Exp $ mem_pool.n
-->
<body>
<h1> mem_pool(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> mem_pool - Implement a straightforward memory pool
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#data_types_and_routines">DATA TYPES AND ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#implementation_notes">IMPLEMENTATION NOTES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>call pool_acquire( pdata )</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>call pool_release( pdata )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>mem_pool.f90</em> source file defines a set of subroutines
that allow you to acquire and release memory of a particular derived
type, thereby reducing the number of allocations and deallocations.
Such a memory pool is useful for instance when you need temporary memory
of fixed size.
<h2><a name="data_types_and_routines">DATA TYPES AND ROUTINES</a></h2>
<p>
The source code expects a data type, POOL_DATA, that contains an integer
field &quot;pool_index&quot; for private use by the subroutines. All other fields
can be used by the application itself:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
module MYDATA_POOL
type POOLDATA
integer :: pool_index ! For private use by pool_acquire/pool_release
real, dimension(100) :: work ! The actual work space
end type
include &quot;mem_pool.f90&quot;
end module MYDATA_POOL
</pre></td></tr></table></p>
The code defines the following routines:
<dl>
<dt><a name="1"><b class='cmd'>call pool_acquire( pdata )</b> </a><dd>
Get a pointer to available data in the memory pool. The memory pool is
expanded automatically, by allocating an array of 100 items. If there
is an error (no more memory can be allocated), the pointer will not be
associated.
<br><br>
<dl>
<dt>type(pool_data), pointer <i class='arg'>pdata</i><dd>
The pointer variable that will be associated with valid memory
</dl>
<br><br>
<dt><a name="2"><b class='cmd'>call pool_release( pdata )</b> </a><dd>
Release the data pointed to by pdata to the memory pool. This data will
become available again for further acquiring.
<br><br>
<dl>
<dt>type(pool_data), pointer <i class='arg'>pdata</i><dd>
The pointer variable pointing to data to be released into the pool
</dl>
</dl>
(Note: two more subroutines are envisioned: setting two parameters that
control the allocation and deallocation of memory, and a routine to
print statistical information. These have not been implemented yet.)
<h2><a name="implementation_notes">IMPLEMENTATION NOTES</a></h2>
<p>
The subroutines do not change the fields of the POOL_DATA structure
(except for pool_index). This means that allocatable (pointer)
components in this structure are not influenced. You could use this to
store dynamically sized arrays in the memory pool. It is especially
useful if all such arrays have the same size.
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,75 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin mem_pool n 1.0]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Implement a straightforward memory pool}]
[description]
The [strong mem_pool.f90] source file defines a set of subroutines
that allow you to acquire and release memory of a particular derived
type, thereby reducing the number of allocations and deallocations.
Such a memory pool is useful for instance when you need temporary memory
of fixed size.
[section "DATA TYPES AND ROUTINES"]
The source code expects a data type, POOL_DATA, that contains an integer
field "pool_index" for private use by the subroutines. All other fields
can be used by the application itself:
[example {
module MYDATA_POOL
type POOLDATA
integer :: pool_index ! For private use by pool_acquire/pool_release
real, dimension(100) :: work ! The actual work space
end type
include "mem_pool.f90"
end module MYDATA_POOL
}]
The code defines the following routines:
[list_begin definitions]
[call [cmd "call pool_acquire( pdata )"]]
Get a pointer to available data in the memory pool. The memory pool is
expanded automatically, by allocating an array of 100 items. If there
is an error (no more memory can be allocated), the pointer will not be
associated.
[list_begin arg]
[arg_def "type(pool_data), pointer" pdata]
The pointer variable that will be associated with valid memory
[list_end]
[nl]
[call [cmd "call pool_release( pdata )"]]
Release the data pointed to by pdata to the memory pool. This data will
become available again for further acquiring.
[list_begin arg]
[arg_def "type(pool_data), pointer" pdata]
The pointer variable pointing to data to be released into the pool
[list_end]
[list_end]
(Note: two more subroutines are envisioned: setting two parameters that
control the allocation and deallocation of memory, and a routine to
print statistical information. These have not been implemented yet.)
[section "IMPLEMENTATION NOTES"]
The subroutines do not change the fields of the POOL_DATA structure
(except for pool_index). This means that allocatable (pointer)
components in this structure are not influenced. You could use this to
store dynamically sized arrays in the memory pool. It is especially
useful if all such arrays have the same size.
[manpage_end]

View File

@@ -0,0 +1,430 @@
<html><head>
<title>flibs/datastructures - flibs </title>
</head>
<! -- Generated from file 'sets.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: sets.html,v 1.1 2008/06/13 10:25:55 relaxmike Exp $ flibs/datastructures.n
-->
<body>
<h1> flibs/datastructures(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> flibs/datastructures - Unordered sets
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#routines">ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>call set_create( dataset )</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>call set_destroy( dataset )</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>size = set_size( dataset )</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>call set_add( dataset, elem )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call set_delete_element( dataset, elem )</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>has = set_haselement( dataset, elem )</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>has = elem .elementof. dataset</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>equal = set_equal( set1, set2 )</b> </a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>equal = set1 .eq. set2</b> </a></td></tr>
<tr valign=top ><td ><a href="#10"><b class='cmd'>notequal = set_notequal( set1, set2 )</b> </a></td></tr>
<tr valign=top ><td ><a href="#11"><b class='cmd'>notequal = set1 .ne. set2</b> </a></td></tr>
<tr valign=top ><td ><a href="#12"><b class='cmd'>has = set_hassubset( dataset, subset )</b> </a></td></tr>
<tr valign=top ><td ><a href="#13"><b class='cmd'>has = subset .subsetof. dataset</b> </a></td></tr>
<tr valign=top ><td ><a href="#14"><b class='cmd'>newset = set_union( set1, set2 )</b> </a></td></tr>
<tr valign=top ><td ><a href="#15"><b class='cmd'>newset = set1 .union. set2</b> </a></td></tr>
<tr valign=top ><td ><a href="#16"><b class='cmd'>newset = set_intersection( set1, set2 )</b> </a></td></tr>
<tr valign=top ><td ><a href="#17"><b class='cmd'>newset = set1 .intersect. set2</b> </a></td></tr>
<tr valign=top ><td ><a href="#18"><b class='cmd'>newset = set_exclusion( set1, set2 )</b> </a></td></tr>
<tr valign=top ><td ><a href="#19"><b class='cmd'>newset = set1 .intersect. set2</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>sets.f90</em> source file allows you to implement
<em>unordered sets</em> of any (derived) type without having to
edit the supplied source code. To this end a simple technique is used,
which is best illustrated by an example:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
!
! The data that will be stored in the sets
!
type MYDATA
integer :: value
end type MYDATA
!
! As derived types are compared, we need to define
! how to compare them
!
interface operator(.eq.)
module procedure mydata_isequal
end interface
contains
logical function mydata_isequal( v1, v2 )
type(MYDATA), intent(in) :: v1
type(MYDATA), intent(in) :: v2
mydata_isequal = v1%value .eq. v2%value
end function mydata_isequal
end module MYDATA_MODULE
module MYDATA_SET_STRUCTS
use MYDATA_MODULE, SET_DATA =&gt; MYDATA
include &quot;data_for_sets.f90&quot;
end module MYDATA_SET_STRUCTS
module MYDATA_SETS
use MYDATA_SET_STRUCTS
include &quot;sets.f90&quot;
end module MYDATA_SETS
</pre></td></tr></table></p>
The above code defines a module <em>MYDATA_MODULE</em> with the derived
type that is to be stored in the sets. The name of that derived
type can be anything.
<p>
It also defines a module <em>MYDATA_SET_STRUCTS</em> which prepares the
underlying data structure. The reason for this two-layer process is that
you need to be able to define the name of the modules that are involved
yourself. (Otherwise it would be impossible to use two or more
<em>sets</em> holding different types of data in one program.
<p>
It finally defines a module <em>MYDATA_SETS</em> which will be the
module that holds the functionality to use unordered sets:
<ul>
<li>
The module <em>MYDATA_MODULE</em> is <em>used</em>, but the derived type
<em>MYDATA</em> is renamed to the (fixed) name <em>SET_DATA</em>. (This
is the name used in the generic source file.)
<br><br>
<li>
The source code for the actual routines is simply included via the
INCLUDE statement.
<br><br>
<li>
Nothing more is required, we can close the source text for the module.
</ul>
To use a single type of sets in a program, we can just use the
MYDATA_SETS module. If you need more than one type of data in sets,
then apply the same renaming trick on using the specific set
modules.
<p>
In fact the example in the source file &quot;two_lists.f90&quot; shows the general
technique of how to accomplish this.
<h2><a name="routines">ROUTINES</a></h2>
<p>
The source file <em>sets.f90</em> provides the following
routines:
<dl>
<dt><a name="1"><b class='cmd'>call set_create( dataset )</b> </a><dd>
Create a new empty set.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The variable that will be used for accessing the set
</dl>
<br><br>
<dt><a name="2"><b class='cmd'>call set_destroy( dataset )</b> </a><dd>
Destroy the set. All elements contained in it will be destroyed as
well.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The set to be destroyed
</dl>
<br><br>
<dt><a name="3"><b class='cmd'>size = set_size( dataset )</b> </a><dd>
Function to return the number of elements in the set.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The set in question
</dl>
<br><br>
<dt><a name="4"><b class='cmd'>call set_add( dataset, elem )</b> </a><dd>
Insert a new element in the set. If the element is already present,
nothing is done.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The dataset to add the element to.
<br><br>
<dt>type(SET_DATA), intent(in) <i class='arg'>elem</i><dd>
The element to be stored
</dl>
<br><br>
<dt><a name="5"><b class='cmd'>call set_delete_element( dataset, elem )</b> </a><dd>
Delete the given element from the set.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The list in question
<br><br>
<dt>type(SET_DATA) <i class='arg'>elem</i><dd>
The element to be deleted
</dl>
<br><br>
<dt><a name="6"><b class='cmd'>has = set_haselement( dataset, elem )</b> </a><dd>
Returns whether or not the given element is in the set.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The set in question
<br><br>
<dt>type(SET_DATA) <i class='arg'>elem</i><dd>
The element to be checked
</dl>
<br><br>
<dt><a name="7"><b class='cmd'>has = elem .elementof. dataset</b> </a><dd>
Returns whether or not the given element is in the set.
(The operator version)
<br><br>
<dl>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The set in question
<br><br>
<dt>type(SET_DATA) <i class='arg'>elem</i><dd>
The element to be checked
</dl>
<br><br>
<dt><a name="8"><b class='cmd'>equal = set_equal( set1, set2 )</b> </a><dd>
Returns whether or not the two sets contain the same elements.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>set1</i><dd>
The first set
<br><br>
<dt>type(SET) <i class='arg'>set2</i><dd>
The second set
</dl>
<br><br>
<dt><a name="9"><b class='cmd'>equal = set1 .eq. set2</b> </a><dd>
Returns whether or not the two sets contain the same elements.
(The operator version)
<br><br>
<dt><a name="10"><b class='cmd'>notequal = set_notequal( set1, set2 )</b> </a><dd>
Returns whether or not the two sets do not contain the same elements.
(The operator version)
<br><br>
<dl>
<dt>type(SET) <i class='arg'>set1</i><dd>
The first set
<br><br>
<dt>type(SET) <i class='arg'>set2</i><dd>
The second set
</dl>
<br><br>
<dt><a name="11"><b class='cmd'>notequal = set1 .ne. set2</b> </a><dd>
Returns whether or not the two sets do not contain the same elements.
(The operator version)
<br><br>
<dt><a name="12"><b class='cmd'>has = set_hassubset( dataset, subset )</b> </a><dd>
Returns whether or not one set is contained in the other set.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The set that may hold the second one
<br><br>
<dt>type(SET) <i class='arg'>subset</i><dd>
The set that may be a subset of the fist
</dl>
<br><br>
<dt><a name="13"><b class='cmd'>has = subset .subsetof. dataset</b> </a><dd>
Returns whether or not one set is contained in the other set.
(The operator version)
<br><br>
<dl>
<dt>type(SET) <i class='arg'>subset</i><dd>
The set that may be a subset of the other
<br><br>
<dt>type(SET) <i class='arg'>dataset</i><dd>
The set that may hold the first one
</dl>
<br><br>
<dt><a name="14"><b class='cmd'>newset = set_union( set1, set2 )</b> </a><dd>
Returns the union of two sets.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>set1</i><dd>
The first set
<br><br>
<dt>type(SET) <i class='arg'>set2</i><dd>
The second set
</dl>
<br><br>
<dt><a name="15"><b class='cmd'>newset = set1 .union. set2</b> </a><dd>
Returns the union of two sets - operator version.
<br><br>
<dt><a name="16"><b class='cmd'>newset = set_intersection( set1, set2 )</b> </a><dd>
Returns the intersection of two sets.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>set1</i><dd>
The first set
<br><br>
<dt>type(SET) <i class='arg'>set2</i><dd>
The second set
</dl>
<br><br>
<dt><a name="17"><b class='cmd'>newset = set1 .intersect. set2</b> </a><dd>
Returns the intersection of two sets - operator version.
<br><br>
<dt><a name="18"><b class='cmd'>newset = set_exclusion( set1, set2 )</b> </a><dd>
Returns a copy of the first set with the elements of the second set
excluded.
<br><br>
<dl>
<dt>type(SET) <i class='arg'>set1</i><dd>
The first set
<br><br>
<dt>type(SET) <i class='arg'>set2</i><dd>
The second set
</dl>
<br><br>
<dt><a name="19"><b class='cmd'>newset = set1 .intersect. set2</b> </a><dd>
Returns a copy of the first set with the elements of the second set
excluded - operator version.
</dl>
Notes:
<ul>
<li>
The sets can only store data of the same derived type. In that sense
the code is not generic.
<br><br>
<li>
Currently, the sets can only store derived types that do not require
an explicit destruction. If you want to store a derived type with
pointers to allocated memory, you can do that however, by supplying an
assignment operator. This would lead to a memory leak though. It is best
to wait for the next version which will allow such derived types to be
stored.
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,332 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/datastructures n 1.0]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Unordered sets}]
[description]
The [strong sets.f90] source file allows you to implement
[strong "unordered sets"] of any (derived) type without having to
edit the supplied source code. To this end a simple technique is used,
which is best illustrated by an example:
[example {
!
! The data that will be stored in the sets
!
type MYDATA
integer :: value
end type MYDATA
!
! As derived types are compared, we need to define
! how to compare them
!
interface operator(.eq.)
module procedure mydata_isequal
end interface
contains
logical function mydata_isequal( v1, v2 )
type(MYDATA), intent(in) :: v1
type(MYDATA), intent(in) :: v2
mydata_isequal = v1%value .eq. v2%value
end function mydata_isequal
end module MYDATA_MODULE
module MYDATA_SET_STRUCTS
use MYDATA_MODULE, SET_DATA => MYDATA
include "data_for_sets.f90"
end module MYDATA_SET_STRUCTS
module MYDATA_SETS
use MYDATA_SET_STRUCTS
include "sets.f90"
end module MYDATA_SETS
}]
The above code defines a module [strong MYDATA_MODULE] with the derived
type that is to be stored in the sets. The name of that derived
type can be anything.
[para]
It also defines a module [strong MYDATA_SET_STRUCTS] which prepares the
underlying data structure. The reason for this two-layer process is that
you need to be able to define the name of the modules that are involved
yourself. (Otherwise it would be impossible to use two or more
[strong sets] holding different types of data in one program.
[para]
It finally defines a module [strong MYDATA_SETS] which will be the
module that holds the functionality to use unordered sets:
[list_begin bullet]
[bullet]
The module [strong MYDATA_MODULE] is [strong used], but the derived type
[strong MYDATA] is renamed to the (fixed) name [strong SET_DATA]. (This
is the name used in the generic source file.)
[bullet]
The source code for the actual routines is simply included via the
INCLUDE statement.
[bullet]
Nothing more is required, we can close the source text for the module.
[list_end]
To use a single type of sets in a program, we can just use the
MYDATA_SETS module. If you need more than one type of data in sets,
then apply the same renaming trick on using the specific set
modules.
[para]
In fact the example in the source file "two_lists.f90" shows the general
technique of how to accomplish this.
[section ROUTINES]
The source file [strong "sets.f90"] provides the following
routines:
[list_begin definitions]
[call [cmd "call set_create( dataset )"]]
Create a new empty set.
[list_begin arg]
[arg_def "type(SET)" dataset]
The variable that will be used for accessing the set
[list_end]
[nl]
[call [cmd "call set_destroy( dataset )"]]
Destroy the set. All elements contained in it will be destroyed as
well.
[list_begin arg]
[arg_def "type(SET)" dataset]
The set to be destroyed
[list_end]
[nl]
[call [cmd "size = set_size( dataset )"]]
Function to return the number of elements in the set.
[list_begin arg]
[arg_def "type(SET)" dataset]
The set in question
[list_end]
[nl]
[call [cmd "call set_add( dataset, elem )"]]
Insert a new element in the set. If the element is already present,
nothing is done.
[list_begin arg]
[arg_def "type(SET)" dataset]
The dataset to add the element to.
[arg_def "type(SET_DATA), intent(in)" elem]
The element to be stored
[list_end]
[nl]
[call [cmd "call set_delete_element( dataset, elem )"]]
Delete the given element from the set.
[list_begin arg]
[arg_def "type(SET)" dataset]
The list in question
[arg_def "type(SET_DATA)" elem]
The element to be deleted
[list_end]
[nl]
[call [cmd "has = set_haselement( dataset, elem )"]]
Returns whether or not the given element is in the set.
[list_begin arg]
[arg_def "type(SET)" dataset]
The set in question
[arg_def "type(SET_DATA)" elem]
The element to be checked
[list_end]
[nl]
[call [cmd "has = elem .elementof. dataset"]]
Returns whether or not the given element is in the set.
(The operator version)
[list_begin arg]
[arg_def "type(SET)" dataset]
The set in question
[arg_def "type(SET_DATA)" elem]
The element to be checked
[list_end]
[nl]
[call [cmd "equal = set_equal( set1, set2 )"]]
Returns whether or not the two sets contain the same elements.
[list_begin arg]
[arg_def "type(SET)" set1]
The first set
[arg_def "type(SET)" set2]
The second set
[list_end]
[nl]
[call [cmd "equal = set1 .eq. set2"]]
Returns whether or not the two sets contain the same elements.
(The operator version)
[call [cmd "notequal = set_notequal( set1, set2 )"]]
Returns whether or not the two sets do not contain the same elements.
(The operator version)
[list_begin arg]
[arg_def "type(SET)" set1]
The first set
[arg_def "type(SET)" set2]
The second set
[list_end]
[nl]
[call [cmd "notequal = set1 .ne. set2"]]
Returns whether or not the two sets do not contain the same elements.
(The operator version)
[call [cmd "has = set_hassubset( dataset, subset )"]]
Returns whether or not one set is contained in the other set.
[list_begin arg]
[arg_def "type(SET)" dataset]
The set that may hold the second one
[arg_def "type(SET)" subset]
The set that may be a subset of the fist
[list_end]
[nl]
[call [cmd "has = subset .subsetof. dataset"]]
Returns whether or not one set is contained in the other set.
(The operator version)
[list_begin arg]
[arg_def "type(SET)" subset]
The set that may be a subset of the other
[arg_def "type(SET)" dataset]
The set that may hold the first one
[list_end]
[nl]
[call [cmd "newset = set_union( set1, set2 )"]]
Returns the union of two sets.
[list_begin arg]
[arg_def "type(SET)" set1]
The first set
[arg_def "type(SET)" set2]
The second set
[list_end]
[nl]
[call [cmd "newset = set1 .union. set2"]]
Returns the union of two sets - operator version.
[call [cmd "newset = set_intersection( set1, set2 )"]]
Returns the intersection of two sets.
[list_begin arg]
[arg_def "type(SET)" set1]
The first set
[arg_def "type(SET)" set2]
The second set
[list_end]
[nl]
[call [cmd "newset = set1 .intersect. set2"]]
Returns the intersection of two sets - operator version.
[call [cmd "newset = set_exclusion( set1, set2 )"]]
Returns a copy of the first set with the elements of the second set
excluded.
[list_begin arg]
[arg_def "type(SET)" set1]
The first set
[arg_def "type(SET)" set2]
The second set
[list_end]
[nl]
[call [cmd "newset = set1 .intersect. set2"]]
Returns a copy of the first set with the elements of the second set
excluded - operator version.
[list_end]
Notes:
[list_begin bullet]
[bullet]
The sets can only store data of the same derived type. In that sense
the code is not generic.
[bullet]
Currently, the sets can only store derived types that do not require
an explicit destruction. If you want to store a derived type with
pointers to allocated memory, you can do that however, by supplying an
assignment operator. This would lead to a memory leak though. It is best
to wait for the next version which will allow such derived types to be
stored.
[list_end]
[manpage_end]

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,187 @@
<html><head>
<title>flibs/strings - flibs </title>
</head>
<! -- Generated from file 'filedir.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2005 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: filedir.html,v 1.1 2008/06/13 10:22:48 relaxmike Exp $ flibs/strings.n
-->
<body>
<h1> flibs/strings(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> flibs/strings - Manipulate file and directory names
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#routines">ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><b class='cmd'>use filedir</b> </a></td></tr>
<tr valign=top ><td ><a href="#2"><b class='cmd'>rootname = filedir_rootname( filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#3"><b class='cmd'>extension = filedir_extension( filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>basename = filedir_basename( filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>dirname = filedir_dirname( filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>dirname = filedir_concat( directory, filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>newname = filedir_add_extension( filename, extension )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The <em>filedir</em> contains a number of routines to manipulate file
and directory names:
<ul>
<li>
Strip the extension from a file name and return the &quot;root name&quot;.
<br><br>
<li>
Determine instead the extension from a file name and return that.
<br><br>
<li>
Strip the directory name from the path to the file and return the &quot;base
name&quot;.
<br><br>
<li>
Strip the file name from the path to the file and return the
directory.
<br><br>
<li>
Concatenate file and directory names.
<br><br>
<li>
Add an extension to a file name.
</ul>
These are all relatively simple routines, but they are fairly common,
hence the module.
<h2><a name="routines">ROUTINES</a></h2>
<p>
The module contains the following routines:
<dl>
<dt><a name="1"><b class='cmd'>use filedir</b> </a><dd>
To import the subroutines, use this module.
<br><br>
<dt><a name="2"><b class='cmd'>rootname = filedir_rootname( filename )</b> </a><dd>
Strips the extension if any off the file name and returns the result.
<br><br>
<dl>
<dt>character(len=*) <i class='arg'>filename</i><dd>
Name of the file
</dl>
<br><br>
<dt><a name="3"><b class='cmd'>extension = filedir_extension( filename )</b> </a><dd>
Returns the extension if any found in a file name
<br><br>
<dl>
<dt>character(len=*) <i class='arg'>filename</i><dd>
Name of the file
</dl>
<br><br>
<dt><a name="4"><b class='cmd'>basename = filedir_basename( filename )</b> </a><dd>
Returns the base name of a file (so, without the directory part)
<br><br>
<dl>
<dt>character(len=*) <i class='arg'>filename</i><dd>
Name of the file
</dl>
<br><br>
<dt><a name="5"><b class='cmd'>dirname = filedir_dirname( filename )</b> </a><dd>
Returns the directory part of a file name
<br><br>
<dl>
<dt>character(len=*) <i class='arg'>filename</i><dd>
Name of the file
</dl>
<br><br>
<dt><a name="6"><b class='cmd'>dirname = filedir_concat( directory, filename )</b> </a><dd>
Prepend the directory to the file name
<br><br>
<dl>
<dt>character(len=*) <i class='arg'>directory</i><dd>
Directory to prepend
<br><br>
<dt>character(len=*) <i class='arg'>filename</i><dd>
Name of the file
</dl>
<br><br>
<dt><a name="7"><b class='cmd'>newname = filedir_add_extension( filename, extension )</b> </a><dd>
Append an extension to the file name
<br><br>
<dl>
<dt>character(len=*) <i class='arg'>directory</i><dd>
Directory to prepend
<br><br>
<dt>character(len=*) <i class='arg'>filename</i><dd>
Name of the file
</dl>
</dl>
<em>Note:</em>
The functions all return strings that are large enough to guarantee
that the entire result can be hold. In many cases, the length is the
same as that of the input argument, but for instance with
<em>file_add_extension</em> it is the sum of the lengths of the
two arguments plus 1 (for the dot).
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2005 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,131 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/strings n 1.0]
[copyright {2005 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Manipulate file and directory names}]
[description]
The [strong filedir] contains a number of routines to manipulate file
and directory names:
[list_begin bullet]
[bullet]
Strip the extension from a file name and return the "root name".
[bullet]
Determine instead the extension from a file name and return that.
[bullet]
Strip the directory name from the path to the file and return the "base
name".
[bullet]
Strip the file name from the path to the file and return the
directory.
[bullet]
Concatenate file and directory names.
[bullet]
Add an extension to a file name.
[list_end]
These are all relatively simple routines, but they are fairly common,
hence the module.
[section ROUTINES]
The module contains the following routines:
[list_begin definitions]
[call [cmd "use filedir"]]
To import the subroutines, use this module.
[call [cmd "rootname = filedir_rootname( filename )"]]
Strips the extension if any off the file name and returns the result.
[list_begin arg]
[arg_def "character(len=*)" filename]
Name of the file
[list_end]
[nl]
[call [cmd "extension = filedir_extension( filename )"]]
Returns the extension if any found in a file name
[list_begin arg]
[arg_def "character(len=*)" filename]
Name of the file
[list_end]
[nl]
[call [cmd "basename = filedir_basename( filename )"]]
Returns the base name of a file (so, without the directory part)
[list_begin arg]
[arg_def "character(len=*)" filename]
Name of the file
[list_end]
[nl]
[call [cmd "dirname = filedir_dirname( filename )"]]
Returns the directory part of a file name
[list_begin arg]
[arg_def "character(len=*)" filename]
Name of the file
[list_end]
[nl]
[call [cmd "dirname = filedir_concat( directory, filename )"]]
Prepend the directory to the file name
[list_begin arg]
[arg_def "character(len=*)" directory]
Directory to prepend
[arg_def "character(len=*)" filename]
Name of the file
[list_end]
[nl]
[call [cmd "newname = filedir_add_extension( filename, extension )"]]
Append an extension to the file name
[list_begin arg]
[arg_def "character(len=*)" directory]
Directory to prepend
[arg_def "character(len=*)" filename]
Name of the file
[list_end]
[list_end]
[strong Note:]
The functions all return strings that are large enough to guarantee
that the entire result can be hold. In many cases, the length is the
same as that of the input argument, but for instance with
[strong file_add_extension] it is the sum of the lengths of the
two arguments plus 1 (for the dot).
[manpage_end]

View File

@@ -0,0 +1,179 @@
<html><head>
<title>flibs/m_fileunit - flibs </title>
</head>
<! -- Generated from file 'filedir/m_fileunit.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2008 Michael Baudin michael.baudin@gmail.com
-->
<! -- CVS: $Id: m_fileunit.html,v 1.2 2008/06/17 12:44:21 relaxmike Exp $ flibs/m_fileunit.n
-->
<body>
<h1> flibs/m_fileunit(n) 1.0 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> flibs/m_fileunit - Manage file units
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#overview">OVERVIEW</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#routines">ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#todo">TODO</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1"><strong>fileunit_getallopen</strong> ( <i class='arg'>nbunits</i> <i class='arg'>, units</i> )</a></td></tr>
<tr valign=top ><td ><a href="#2"><strong>fileunit_displayopen</strong> ( <i class='arg'>reportunitnumber</i> )</a></td></tr>
<tr valign=top ><td ><a href="#3"><strong>fileunit_report</strong> ( <i class='arg'>reportunitnumber</i> <i class='arg'>iunit</i>)</a></td></tr>
<tr valign=top ><td ><a href="#4"><strong>fileunit_closeallopen</strong> ( )</a></td></tr>
<tr valign=top ><td ><a href="#5"><strong>fileunit_getfreeunit</strong> ( ) result ( freeunit )</a></td></tr>
<tr valign=top ><td ><a href="#6"><strong>fileunit_set_stoponerror</strong> ( <i class='arg'>stoponerror</i>)</a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
The component <em>m_fileunit</em> provides services to manage fortran file units.
<h2><a name="overview">OVERVIEW</a></h2>
<p>
The function fileunit_getfreeunit returns an integer representing
a fortran unit which is available for opening a file.
The typical use of this function is to manage the files dynamically,
without any database of file units in the library/software.
In the following example, one opens a file with a dynamical
file unit.
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
integer :: fileunit
fileunit = fileunit_getfreeunit ()
open ( unit = fileunit , file = &quot;data.txt&quot; )
[lb]etc...[rb]
</pre></td></tr></table></p>
If several files are to be opened, the &quot;fileunit_getfreeunit&quot;
method has to be inserted between the &quot;open&quot; statements.
This is because two consecutive calls to &quot;fileunit_getfreeunit&quot;
will return the same integer, as expected : if a unit is available
the first time, it will also be available the second time.
In the following example, several files are opened and connected
to several files.
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
integer :: fileunit1
integer :: fileunit2
fileunit1 = fileunit_getfreeunit ()
open ( unit = fileunit1 , file = &quot;data.txt&quot; )
fileunit2 = fileunit_getfreeunit ()
open ( unit = fileunit2 , file = &quot;data2.txt&quot; )
[lb]etc...[rb]
</pre></td></tr></table></p>
In a large fortran software, it may be difficult to see if some
bug has been introduced in the file management, especially
when the software is the composition of several libraries.
The subroutines fileunit_getallopen , fileunit_closeallopen ,
fileunit_report , fileunit_displayopen allow to manage for
the units currently used in the software.
The fileunit_getallopen returns an array of integers which
contains all the currently opened units. The fileunit_closeallopen
subroutine close all currently opened units. The fileunit_report
displays a full report about a given unit number by using the
&quot;inquire&quot; fortran intrinsic statement.
<h2><a name="routines">ROUTINES</a></h2>
<p>
<dl>
<dt><a name="1"><strong>fileunit_getallopen</strong> ( <i class='arg'>nbunits</i> <i class='arg'>, units</i> )</a><dd>
<dl>
<dt><strong>integer, intent ( out ) ::</strong> <i class='arg'>nbunits</i><dd>
<dt><strong>integer , dimension(:) , pointer ::</strong> <i class='arg'>units</i><dd>
</dl>
Computes an array of integers made of all currently opened units.
On output, <i class='arg'>nbunits</i> is the number of opened units and
<i class='arg'>units ( iunit )</i> is the unit number for the opened unit #iunit
with 1&lt;= iunit &lt;= nbunits.
<br><br>
<dt><a name="2"><strong>fileunit_displayopen</strong> ( <i class='arg'>reportunitnumber</i> )</a><dd>
<dl>
<dt><strong>integer, intent ( in ) ::</strong> <i class='arg'>reportunitnumber</i><dd>
</dl>
Writes on unit <i class='arg'>unitnumber</i> the full list of opened units and their associated
filenames.
<br><br>
<dt><a name="3"><strong>fileunit_report</strong> ( <i class='arg'>reportunitnumber</i> <i class='arg'>iunit</i>)</a><dd>
<dl>
<dt><strong>integer, intent ( in ) ::</strong> <i class='arg'>reportunitnumber</i><dd>
<dt><strong>integer, intent ( in ) ::</strong> <i class='arg'>iunit</i><dd>
</dl>
Compute report about logical unit <i class='arg'>iunit</i> and write it on
unit <i class='arg'>unitnumber</i>. Note : All possible features of the &quot;inquire&quot; intrinsic are used.
<br><br>
<dt><a name="4"><strong>fileunit_closeallopen</strong> ( )</a><dd>
Close all currently opened units.
<br><br>
<dt><a name="5"><strong>fileunit_getfreeunit</strong> ( ) result ( freeunit )</a><dd>
<dl>
<dt><strong>integer ::</strong> <i class='arg'>freeunit</i><dd>
</dl>
Returns a free fortran unit <i class='arg'>freeunit</i> as an integer between 1 and FILEUNIT_MAX_UNIT_NUMBER,
representing a free FORTRAN logical unit.
If no free unit can be found, generates an error.
Note that fileunit_getfreeunit assumes that units 5 and 6
are special, and will never return those values.
Original Author : John Burkardt
<br><br>
<dt><a name="6"><strong>fileunit_set_stoponerror</strong> ( <i class='arg'>stoponerror</i>)</a><dd>
<dl>
<dt><strong>integer ::</strong> <i class='arg'>freeunit</i><dd>
</dl>
Configure the behaviour of the component whenever an
error is met.
If <i class='arg'>stoponerror</i> is true, then the execution stops if an error is encountered.
If <i class='arg'>stoponerror</i> is false, then the execution continues if an error is encountered.
In both cases, a message is displayed on standard output.
</dl>
<h2><a name="todo">TODO</a></h2>
<p>
<ul>
<li> allow to &quot;lock&quot; a collection of logical units, so that an
external library which may use constant units can be linked.
<br><br>
<li> allow to &quot;unlock&quot; one unit, or all units at once.
</ul>
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2008 Michael Baudin michael.baudin@gmail.com<br>
</body></html>

View File

@@ -0,0 +1,129 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/m_fileunit n 1.0]
[copyright {2008 Michael Baudin michael.baudin@gmail.com}]
[moddesc flibs]
[titledesc {Manage file units}]
[description]
The component [strong m_fileunit] provides services to manage fortran file units.
[section OVERVIEW]
The function fileunit_getfreeunit returns an integer representing
a fortran unit which is available for opening a file.
The typical use of this function is to manage the files dynamically,
without any database of file units in the library/software.
In the following example, one opens a file with a dynamical
file unit.
[example {
integer :: fileunit
fileunit = fileunit_getfreeunit ()
open ( unit = fileunit , file = "data.txt" )
[lb]etc...[rb]
}]
If several files are to be opened, the "fileunit_getfreeunit"
method has to be inserted between the "open" statements.
This is because two consecutive calls to "fileunit_getfreeunit"
will return the same integer, as expected : if a unit is available
the first time, it will also be available the second time.
In the following example, several files are opened and connected
to several files.
[example {
integer :: fileunit1
integer :: fileunit2
fileunit1 = fileunit_getfreeunit ()
open ( unit = fileunit1 , file = "data.txt" )
fileunit2 = fileunit_getfreeunit ()
open ( unit = fileunit2 , file = "data2.txt" )
[lb]etc...[rb]
}]
In a large fortran software, it may be difficult to see if some
bug has been introduced in the file management, especially
when the software is the composition of several libraries.
The subroutines fileunit_getallopen , fileunit_closeallopen ,
fileunit_report , fileunit_displayopen allow to manage for
the units currently used in the software.
The fileunit_getallopen returns an array of integers which
contains all the currently opened units. The fileunit_closeallopen
subroutine close all currently opened units. The fileunit_report
displays a full report about a given unit number by using the
"inquire" fortran intrinsic statement.
[section ROUTINES]
[list_begin definitions]
[call [method "fileunit_getallopen"] ( [arg nbunits] [arg ", units"] )]
[list_begin arg]
[arg_def [type "integer, intent ( out ) ::"] nbunits]
[arg_def [type "integer , dimension(:) , pointer ::"] units]
[list_end]
Computes an array of integers made of all currently opened units.
On output, [arg nbunits] is the number of opened units and
[arg "units ( iunit )"] is the unit number for the opened unit #iunit
with 1<= iunit <= nbunits.
[call [method "fileunit_displayopen"] ( [arg reportunitnumber] )]
[list_begin arg]
[arg_def [type "integer, intent ( in ) ::"] reportunitnumber]
[list_end]
Writes on unit [arg unitnumber] the full list of opened units and their associated
filenames.
[call [method "fileunit_report"] ( [arg reportunitnumber] [arg iunit])]
[list_begin arg]
[arg_def [type "integer, intent ( in ) ::"] reportunitnumber]
[arg_def [type "integer, intent ( in ) ::"] iunit]
[list_end]
Compute report about logical unit [arg iunit] and write it on
unit [arg unitnumber]. Note : All possible features of the "inquire" intrinsic are used.
[call [method "fileunit_closeallopen"] ( )]
Close all currently opened units.
[call [method "fileunit_getfreeunit"] ( ) result ( freeunit )]
[list_begin arg]
[arg_def [type "integer ::"] freeunit]
[list_end]
Returns a free fortran unit [arg freeunit] as an integer between 1 and FILEUNIT_MAX_UNIT_NUMBER,
representing a free FORTRAN logical unit.
If no free unit can be found, generates an error.
Note that fileunit_getfreeunit assumes that units 5 and 6
are special, and will never return those values.
Original Author : John Burkardt
[call [method "fileunit_set_stoponerror"] ( [arg stoponerror])]
[list_begin arg]
[arg_def [type "integer ::"] freeunit]
[list_end]
Configure the behaviour of the component whenever an
error is met.
If [arg stoponerror] is true, then the execution stops if an error is encountered.
If [arg stoponerror] is false, then the execution continues if an error is encountered.
In both cases, a message is displayed on standard output.
[list_end]
[section TODO]
[list_begin bullet]
[bullet] allow to "lock" a collection of logical units, so that an
external library which may use constant units can be linked.
[bullet] allow to "unlock" one unit, or all units at once.
[list_end]
[manpage_end]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,586 @@
<html><head>
<title>flibs/ftnunit - flibs </title>
</head>
<! -- Generated from file 'ftnunit.man' by tcllib/doctools with format 'html'
-->
<! -- Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;
-->
<! -- CVS: $Id: ftnunit.html,v 1.1 2008/06/13 10:30:53 relaxmike Exp $ flibs/ftnunit.n
-->
<body>
<h1> flibs/ftnunit(n) 1.1 &quot;flibs&quot;</h1>
<h2><a name="name">NAME</a></h2>
<p>
<p> flibs/ftnunit - Unit testing
<h2><a name="table_of_contents">TABLE OF CONTENTS</a></h2>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="#table_of_contents">TABLE OF CONTENTS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#synopsis">SYNOPSIS</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#description">DESCRIPTION</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#routines">ROUTINES</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#generating_tests_from_a_table">GENERATING TESTS FROM A TABLE</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#todo">TODO</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#related_work">RELATED WORK</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#copyright">COPYRIGHT</a><br>
<h2><a name="synopsis">SYNOPSIS</a></h2>
<p>
<table border=1 width=100% cellspacing=0 cellpadding=0><tr bgcolor=lightyellow><td bgcolor=lightyellow><table 0 width=100% cellspacing=0 cellpadding=0><tr valign=top ><td ><a href="#1">runtests.bat </a></td></tr>
<tr valign=top ><td ><a href="#2">runtests.sh </a></td></tr>
<tr valign=top ><td ><a href="#3">runtests.tcl </a></td></tr>
<tr valign=top ><td ><a href="#4"><b class='cmd'>call runtests( testproc )</b> </a></td></tr>
<tr valign=top ><td ><a href="#5"><b class='cmd'>call runtests_init</b> </a></td></tr>
<tr valign=top ><td ><a href="#6"><b class='cmd'>call runtests_final</b> </a></td></tr>
<tr valign=top ><td ><a href="#7"><b class='cmd'>call test( proc, text )</b> </a></td></tr>
<tr valign=top ><td ><a href="#8"><b class='cmd'>call assert_true( cond, text )</b> </a></td></tr>
<tr valign=top ><td ><a href="#9"><b class='cmd'>call assert_false( cond, text )</b> </a></td></tr>
<tr valign=top ><td ><a href="#10"><b class='cmd'>call assert_equal( value1, value2, text )</b> </a></td></tr>
<tr valign=top ><td ><a href="#11"><b class='cmd'>call assert_comparable( value1, value2, margin, text )</b> </a></td></tr>
<tr valign=top ><td ><a href="#12"><b class='cmd'>exists = ftnunit_file_exists( filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#13"><b class='cmd'>call ftnunit_get_lun( lun )</b> </a></td></tr>
<tr valign=top ><td ><a href="#14"><b class='cmd'>call ftnunit_remove_file( filename )</b> </a></td></tr>
<tr valign=top ><td ><a href="#15"><b class='cmd'>call ftnunit_make_empty_file( filename )</b> </a></td></tr>
</table></td></tr></table>
<h2><a name="description">DESCRIPTION</a></h2>
<p>
<em>JUnit</em> is a well-known facility for defining and running unit
tests in Java programs. The <em>ftnunit</em> framework was inspired by
that facility. It is not as good-looking as JUnit, by no means:
<ul>
<li>
It has no graphical user-interface
<br><br>
<li>
As Fortran does not allow introspection, the test routines can not
be detected automatically, instead as a programmer you need to set up a
high-level routine yourself that collects all the unit tests.
<br><br>
<li>
A runtime error, like division by zero, may lead to a termination of
the program. There is no (portable) way to catch these. Instead, the
framework relies on a batch file or shell script to repeatedly start the
program until all tests are run.
</ul>
Despite these limitations, <em>ftnunit</em> can be a great help:
<ul>
<li>
The Tcl program <em>gentabletest.tcl</em> generates a complete test program based
on a simple input file (see <a href="#generating_tests_from_a_table">GENERATING TESTS FROM A TABLE</a>).
<br><br>
<li>
The code to test the various components (subroutines, functions, tasks
consisting of several program units) can be combined with the program
itself, without interfering with the ordinary code.
<br><br>
This is achieved by defining a single routine (test_all, say) that runs
all the unit tests and that is called via the provided routine
<em>runtests</em>:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
program myprog
...
!
! The routine runtests will check if unit tests are requested
! If not, it will return immediately. This way we make sure
! the unit tests remain part of the program.
!
! The routine test_all runs all unit tests
! (see the dataproc_testing module)
!
call runtests( test_all )
!
! Ordinary processing
!
...
end program
</pre></td></tr></table></p>
The routine runtests checks if there is a file &quot;ftnunit.run&quot;. If there is
such a file, it will run the given subroutine. Otherwise it will return
and the rest of the program is executed.
<br><br>
<li>
Because the test code is incorporated in the program itself, it is less
likely that they evolve independently: changes in the argument lists of
the subroutines and functions may lead to compile errors in the test
code.
<br><br>
<li>
There is no need to set up a whole new program for testing portions of
the program.
</ul>
The source file &quot;test_ftnunit.f90&quot; illustrates how to use the ftnunit
framework:
<ul>
<li>
The main program calls the routine &quot;runtests&quot; and passes it the argument
&quot;test_all&quot;, a routine defined in a module called &quot;dataproc_testing&quot;.
<br><br>
<li>
The routine &quot;test_all&quot; consists of nothing but calls to the generic
routine &quot;test&quot;:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
subroutine test_all
call test( test_no_file, &quot;Read non-existent file&quot; )
call test( test_empty_file, &quot;Read an empty file&quot; )
call test( test_invalid_file, &quot;Read an invalid file&quot; )
call test( test_ordinary_file, &quot;Read an ordinary file&quot; )
end subroutine test_all
</pre></td></tr></table></p>
<br><br>
<li>
The module includes a source file &quot;ftnunit_test.f90&quot;. This is a remnant
of a previous version. Please ignore this.
<br><br>
<li>
The generic routine &quot;test&quot; checks whether a particular unit
test needs to be run (via the test number) and then runs the subroutine
that was passed as one of its arguments. One such routine looks like
this:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
subroutine test_no_file
integer :: nodata
real :: vmean, vmin, vmax
call ftnunit_remove_file( 'no_such_file' )
call write_name( 'no_such_file' )
call open_files
call process_data( nodata, vmean, vmax, vmin )
call assert_true( nodata == 0, &quot;No data read&quot; )
end subroutine test_no_file
</pre></td></tr></table></p>
The assertion is used to check that the result is as expected.
<br><br>
<li>
The program contains some deliberate errors and the resulting
log file looks like this&quot;:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
Test: Read non-existent file
Test: Read an empty file
Test: Read an invalid file
forrtl: severe (59): list-directed I/O syntax error, unit 11, file c:\arjen\flibs\tests\ftnunit\invalid_file
Image PC Routine Line Source
test_ftnunit.exe 004151B9 Unknown Unknown Unknown
test_ftnunit.exe 00415017 Unknown Unknown Unknown
test_ftnunit.exe 004141F4 Unknown Unknown Unknown
test_ftnunit.exe 00414629 Unknown Unknown Unknown
test_ftnunit.exe 00409C05 Unknown Unknown Unknown
test_ftnunit.exe 004095FB Unknown Unknown Unknown
test_ftnunit.exe 0040144B Unknown Unknown Unknown
test_ftnunit.exe 00401FE9 Unknown Unknown Unknown
test_ftnunit.exe 00401A2C Unknown Unknown Unknown
test_ftnunit.exe 00401BB3 Unknown Unknown Unknown
test_ftnunit.exe 0040294A Unknown Unknown Unknown
test_ftnunit.exe 0040232E Unknown Unknown Unknown
test_ftnunit.exe 0044A1E9 Unknown Unknown Unknown
test_ftnunit.exe 00433519 Unknown Unknown Unknown
kernel32.dll 7C816D4F Unknown Unknown Unknown
Incrementally linked image--PC correlation disabled.
Test: Read an ordinary file
Number of failed assertions: 0
Number of runs needed to complete the tests: 3
</pre></td></tr></table></p>
</ul>
The program is run via one of the following files:
<dl>
<dt><a name="1">runtests.bat </a><dd>
A batch file for use under MS Windows
<br><br>
<dt><a name="2">runtests.sh </a><dd>
A Bourne shell script for use under UNIX/Linux or similar systems, like
Cygwin or Mingw.
<br><br>
<dt><a name="3">runtests.tcl </a><dd>
A Tcl program that presents a simple graphical user-interface
</dl>
<h2><a name="routines">ROUTINES</a></h2>
<p>
The module ftnunit contains the following subroutines and functions:
<dl>
<dt><a name="4"><b class='cmd'>call runtests( testproc )</b> </a><dd>
Routine to start the unit tests. It checks if the file &quot;ftnunit.run&quot;
exists. If so, it will call the subroutine <em>testproc</em> that was
passed. Otherwise it will simply return, so that the ordinary program
execution may continue.
<br><br>
If the subroutine testproc returns, the program stops, unless you have
called the subroutine <em>runtests_init</em> before <em>runtests</em>.
<br><br>
<dt><a name="5"><b class='cmd'>call runtests_init</b> </a><dd>
Routine to initialise the ftnunit system, so that you call <em>runtests</em>
more than once. To complete the tests, call <em>runtests_final</em>, as
this will print the final statistics and stop the program.
<br><br>
<dt><a name="6"><b class='cmd'>call runtests_final</b> </a><dd>
Routine to finalise the ftnunit system: it will print the final statistics
and stop the program, but only if the file &quot;ftnunit.run&quot; is present.
<br><br>
<dl>
<dt>subroutine <i class='arg'>testproc</i><dd>
Subroutine that calls the individual test routines. It takes no
arguments. It wil generally exist of a series of calls to the
routine <em>test</em> - see below.
</dl>
<dt><a name="7"><b class='cmd'>call test( proc, text )</b> </a><dd>
Routine to run the individual unit test routine (emph proc). It decides
if the test has not run yet and if so, the test routine is called.
Otherwise it is skipped.
<br><br>
<em>test</em> takes care of all administrative details.
<br><br>
Note: to make it possible to use <em>private</em> unit test routines,
the source code of this subroutine is kept in a separate file,
<em>ftnunit_test.f90</em> that should be included in an appropriate
place in the program's sources. This way, you can make it a private
routine in each module. The only public access to the unit testing
routines is then via the subroutine <em>testproc</em> that is passed to
<em>runtests</em>.
<br><br>
<dl>
<dt>subroutine <i class='arg'>proc</i><dd>
Subroutine that implements an individual unit test. It takes no
arguments. Within each such subroutine the complete unit test is run.
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>text</i><dd>
Text describing the particular unit test. It is printed in the log
file.
</dl>
<dt><a name="8"><b class='cmd'>call assert_true( cond, text )</b> </a><dd>
Routine to check that a condition is true. If not, a message is printed
in the log file and the number of failures is increased.
<br><br>
<dl>
<dt>logical <i class='arg'>cond</i><dd>
The condition to be checked
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>text</i><dd>
Text describing the condition
</dl>
<dt><a name="9"><b class='cmd'>call assert_false( cond, text )</b> </a><dd>
Routine to check that a condition is false. If not, a message is
printed in the log file and the number of failures is increased.
<br><br>
<dl>
<dt>logical <i class='arg'>cond</i><dd>
The condition to be checked
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>text</i><dd>
Text describing the condition
</dl>
<dt><a name="10"><b class='cmd'>call assert_equal( value1, value2, text )</b> </a><dd>
Routine to check that two integers are equal or if two one-dimensional
integer arrays are equal. If not, a message is printed, along with the
values that were different.
<br><br>
<dl>
<dt>integer [, dimension(:)] <i class='arg'>value1</i><dd>
The first integer value or array
<br><br>
<dt>integer [, dimension(:)] <i class='arg'>value2</i><dd>
The second integer value or array
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>text</i><dd>
Text describing the condition
</dl>
<dt><a name="11"><b class='cmd'>call assert_comparable( value1, value2, margin, text )</b> </a><dd>
Routine to check that two reals are almost equal or if two one-dimensional
real arrays are almost equal. If not, a message is printed, along with
the values that were different.
<br><br>
The margin is taken as a relative tolerance. Two values are
considered almost equal if:
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
abs( v1 - v2 ) &lt; margin * (abs(v1)+abs(v2)) / 2
</pre></td></tr></table></p>
<br><br>
<dl>
<dt>real [, dimension(:)] <i class='arg'>value1</i><dd>
The first real value or array
<br><br>
<dt>real [, dimension(:)] <i class='arg'>value2</i><dd>
The second real value or array
<br><br>
<dt>character(len=*), intent(in) <i class='arg'>text</i><dd>
Text describing the condition
</dl>
<dt><a name="12"><b class='cmd'>exists = ftnunit_file_exists( filename )</b> </a><dd>
Logical function to check that a particular file exists
<br><br>
<dl>
<dt>character(len=*), intent(in) <i class='arg'>filename</i><dd>
Name of the file to be checked
</dl>
<dt><a name="13"><b class='cmd'>call ftnunit_get_lun( lun )</b> </a><dd>
Subroutine to get a free LU-number
<br><br>
<dl>
<dt>integer, intent(out) <i class='arg'>lun</i><dd>
Next free LU-number
</dl>
<dt><a name="14"><b class='cmd'>call ftnunit_remove_file( filename )</b> </a><dd>
Subroutine to remove (delete) a file
<br><br>
<dl>
<dt>character(len=*), intent(in) <i class='arg'>filename</i><dd>
Name of the file to be removed
</dl>
<dt><a name="15"><b class='cmd'>call ftnunit_make_empty_file( filename )</b> </a><dd>
Subroutine to make a new, empty file
<br><br>
<dl>
<dt>character(len=*), intent(in) <i class='arg'>filename</i><dd>
Name of the file to be created
</dl>
</dl>
<h2><a name="generating_tests_from_a_table">GENERATING TESTS FROM A TABLE</a></h2>
<p>
The Tcl program &quot;gentabletest.tcl&quot; reads the test specifications from an
input file and generates a complete Fortran program. The ideas from
Bil Kleb's &quot;Toward Scientific Numerical Modeling&quot;
<a href="ftp://ftp.rta.nato.int/PubFullText/RTO/MP/RTO-MP-AVT-147/RTO-MP-AVT-147-P-17-Kleb.pdf">ftp://ftp.rta.nato.int/PubFullText/RTO/MP/RTO-MP-AVT-147/RTO-MP-AVT-147-P-17-Kleb.pdf</a>
were used for the set-up.
<p>
To do: provide a detailed description. For the moment: see <em>example.tbl</em>, including below.
<p><table><tr><td bgcolor=black>&nbsp;</td><td><pre class='sample'>
! Example of generating test code via a table
! -------------------------------------------
! The routine to be tested determines the minimum oxygen concentration
! in a river, based on the Streeter-Phelps model:
!
! dBOD/dt = -k * BOD
!
! dO2/dt = -k * BOD + ka * (O2sat-O2) / H
!
! where
! BOD - biological oxygen demand (mg O2/l)
! O2 - oxygen concentration (mg O2/l)
! O2sat - saturation concentration of oxygen (mg O2/l)
! k - decay rate of BOD (1/day)
! ka - reareation rate of oxygen (m/day)
! H - depth of the river
!
! We need boundary (initial) conditions for BOD and oxygen and
! the equations describe the concentrations of BOD and oxygen in a
! packet of water as it flows along the river.
!
! Note:
! It is a very simple model, it is not meant as a realistic
! representation.
!
! The routine simply continues the solution until a minimum is found.
! The results are: oxymin and time
!
!
! The keyword DECLARATIONS introduces the declarations we need for the
! complete generated code
!
DECLARATIONS
use streeter_phelps
real :: bod, oxy
real :: k, ka, h, oxysat, dt, oxymin, time
!
! The keyword CODE introduces the code fragment required to run the
! routine or routines. The results and possible checking of error
! conditions are separated.
!
CODE
call compute_min_oxygen( bod, oxy, k, ka, h, oxysat, dt, oxymin, time )
!
! The keyword RESULT indicates which arguments/variables hold the
! interesting results. Specify one name per line (you can not currently
! use array elements) and the allowed margin (taken as absolute, if
! followed by &quot;%&quot; as a percentage)
!
RESULT
oxymin 0.001 ! Minimum oxygen concentration
time 0.01% ! Time the minimum is reached
!
! The keyword ERROR is used for a code fragment that checks if the
! routine has correctly found an error in the input (that is, some
! parameter value is out of range). The code is invoked when any of
! result variables in a table entry has the keyword ERROR instead of
! a proper value.
! Use the subroutine &quot;error&quot; to indicate the correctly reported error
! condition.
!
ERROR
if ( time == -999.0 ) then
call error
endif
!
! The keyword RANGES specifies that the variables are to be taken
! from a uniform or a normal distribution. The generated program will
! simply select values at random and run the code with them. The report
! consists of the detailed output as well as a summary.
!
RANGES
oxy 10.0 2.0 Uniform ! Name of the variable, the mean and the margin (uniform)
! Normal: mean and standard deviation followed by Normal
! Note: all parameters must be given!
!
! The keyword TABLE indicates the beginning of a table of input data and
! expected values. The first (non-comment) line contains the names of
! the variables as used in the code fragments and all others are the
! values expected.
!
! There are two special values:
! ? - indicating an unknown value for result variables and a &quot;do not
! care&quot; value for input variables
! It is useful to generate a table that does contain the (computed)
! results (see the file table.out) or to indicate situations
! where one or more input variables are out of range and this
! should lead to an error
! ERROR - indicating that the entry should cause the routine to be
! tested to flag an error condition.
!
TABLE
dt oxy bod oxysat h k ka oxymin time
0.1 10 1 10 10 0.1 1.0 10.0 2.0
1.0 10 1 10 10 0.1 1.0 ? ?
!
! This case is unacceptable: time step must be positive
0.0 ? ? ? ? ? ? ? ERROR
1.0 0. 10 10 10 0.1 1.0 ? ?
</pre></td></tr></table></p>
<h2><a name="todo">TODO</a></h2>
<p>
The following things are still left to do:
<ul>
<li>
Proper inclusion of the routine <em>prolog</em> and <em>epilog</em>
<br><br>
<li>
Extension of the set of assertion routines
</ul>
<h2><a name="related_work">RELATED WORK</a></h2>
<p>
There are at least two similar initiatives with regard to a unit testing
framework for Fortran:
<ul>
<li>
<a href="http://nasarb.rubyforge.org">Funit (implemented in Fortran and
Ruby)</a> by Bil Kleb and others
<br><br>
<li>
<a href="http://www.sourceforge.net/projects/pfunit">A framework
implemented in Fortran</a> by Brice Womack and Tom Clune
<br><br>
<li>
<a href="http://www.sourceforge.net/projects/fortranxunit">FRUIT
(implemented in Fortran and Ruby)</a> by Andrew Chen
</ul>
(Note: To avoid confusion, I have renamed my original module &quot;funit&quot; to
<em>ftnunit</em>)
<h2><a name="copyright">COPYRIGHT</a></h2>
<p>
Copyright &copy; 2006 Arjen Markus &lt;arjenmarkus@sourceforge.net&gt;<br>
</body></html>

View File

@@ -0,0 +1,489 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/ftnunit n 1.1]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Unit testing}]
[description]
[emph JUnit] is a well-known facility for defining and running unit
tests in Java programs. The [emph ftnunit] framework was inspired by
that facility. It is not as good-looking as JUnit, by no means:
[list_begin bullet]
[bullet]
It has no graphical user-interface
[bullet]
As Fortran does not allow introspection, the test routines can not
be detected automatically, instead as a programmer you need to set up a
high-level routine yourself that collects all the unit tests.
[bullet]
A runtime error, like division by zero, may lead to a termination of
the program. There is no (portable) way to catch these. Instead, the
framework relies on a batch file or shell script to repeatedly start the
program until all tests are run.
[list_end]
Despite these limitations, [emph ftnunit] can be a great help:
[list_begin bullet]
[bullet]
The Tcl program [emph gentabletest.tcl] generates a complete test program based
on a simple input file (see [sectref "GENERATING TESTS FROM A TABLE"]).
[bullet]
The code to test the various components (subroutines, functions, tasks
consisting of several program units) can be combined with the program
itself, without interfering with the ordinary code.
[nl]
This is achieved by defining a single routine (test_all, say) that runs
all the unit tests and that is called via the provided routine
[emph runtests]:
[example {
program myprog
...
!
! The routine runtests will check if unit tests are requested
! If not, it will return immediately. This way we make sure
! the unit tests remain part of the program.
!
! The routine test_all runs all unit tests
! (see the dataproc_testing module)
!
call runtests( test_all )
!
! Ordinary processing
!
...
end program
}]
The routine runtests checks if there is a file "ftnunit.run". If there is
such a file, it will run the given subroutine. Otherwise it will return
and the rest of the program is executed.
[bullet]
Because the test code is incorporated in the program itself, it is less
likely that they evolve independently: changes in the argument lists of
the subroutines and functions may lead to compile errors in the test
code.
[bullet]
There is no need to set up a whole new program for testing portions of
the program.
[list_end]
The source file "test_ftnunit.f90" illustrates how to use the ftnunit
framework:
[list_begin bullet]
[bullet]
The main program calls the routine "runtests" and passes it the argument
"test_all", a routine defined in a module called "dataproc_testing".
[bullet]
The routine "test_all" consists of nothing but calls to the generic
routine "test":
[example {
subroutine test_all
call test( test_no_file, "Read non-existent file" )
call test( test_empty_file, "Read an empty file" )
call test( test_invalid_file, "Read an invalid file" )
call test( test_ordinary_file, "Read an ordinary file" )
end subroutine test_all
}]
[bullet]
The module includes a source file "ftnunit_test.f90". This is a remnant
of a previous version. Please ignore this.
[bullet]
The generic routine "test" checks whether a particular unit
test needs to be run (via the test number) and then runs the subroutine
that was passed as one of its arguments. One such routine looks like
this:
[example {
subroutine test_no_file
integer :: nodata
real :: vmean, vmin, vmax
call ftnunit_remove_file( 'no_such_file' )
call write_name( 'no_such_file' )
call open_files
call process_data( nodata, vmean, vmax, vmin )
call assert_true( nodata == 0, "No data read" )
end subroutine test_no_file
}]
The assertion is used to check that the result is as expected.
[bullet]
The program contains some deliberate errors and the resulting
log file looks like this":
[example {
Test: Read non-existent file
Test: Read an empty file
Test: Read an invalid file
forrtl: severe (59): list-directed I/O syntax error, unit 11, file c:\arjen\flibs\tests\ftnunit\invalid_file
Image PC Routine Line Source
test_ftnunit.exe 004151B9 Unknown Unknown Unknown
test_ftnunit.exe 00415017 Unknown Unknown Unknown
test_ftnunit.exe 004141F4 Unknown Unknown Unknown
test_ftnunit.exe 00414629 Unknown Unknown Unknown
test_ftnunit.exe 00409C05 Unknown Unknown Unknown
test_ftnunit.exe 004095FB Unknown Unknown Unknown
test_ftnunit.exe 0040144B Unknown Unknown Unknown
test_ftnunit.exe 00401FE9 Unknown Unknown Unknown
test_ftnunit.exe 00401A2C Unknown Unknown Unknown
test_ftnunit.exe 00401BB3 Unknown Unknown Unknown
test_ftnunit.exe 0040294A Unknown Unknown Unknown
test_ftnunit.exe 0040232E Unknown Unknown Unknown
test_ftnunit.exe 0044A1E9 Unknown Unknown Unknown
test_ftnunit.exe 00433519 Unknown Unknown Unknown
kernel32.dll 7C816D4F Unknown Unknown Unknown
Incrementally linked image--PC correlation disabled.
Test: Read an ordinary file
Number of failed assertions: 0
Number of runs needed to complete the tests: 3
}]
[list_end]
The program is run via one of the following files:
[list_begin definitions]
[call runtests.bat]
A batch file for use under MS Windows
[call runtests.sh]
A Bourne shell script for use under UNIX/Linux or similar systems, like
Cygwin or Mingw.
[call runtests.tcl]
A Tcl program that presents a simple graphical user-interface
[list_end]
[section ROUTINES]
The module ftnunit contains the following subroutines and functions:
[list_begin definitions]
[call [cmd "call runtests( testproc )"]]
Routine to start the unit tests. It checks if the file "ftnunit.run"
exists. If so, it will call the subroutine [emph testproc] that was
passed. Otherwise it will simply return, so that the ordinary program
execution may continue.
[nl]
If the subroutine testproc returns, the program stops, unless you have
called the subroutine [emph runtests_init] before [emph runtests].
[call [cmd "call runtests_init"]]
Routine to initialise the ftnunit system, so that you call [emph runtests]
more than once. To complete the tests, call [emph runtests_final], as
this will print the final statistics and stop the program.
[call [cmd "call runtests_final"]]
Routine to finalise the ftnunit system: it will print the final statistics
and stop the program, but only if the file "ftnunit.run" is present.
[list_begin arg]
[arg_def "subroutine" testproc]
Subroutine that calls the individual test routines. It takes no
arguments. It wil generally exist of a series of calls to the
routine [emph test] - see below.
[list_end]
[call [cmd "call test( proc, text )"]]
Routine to run the individual unit test routine (emph proc). It decides
if the test has not run yet and if so, the test routine is called.
Otherwise it is skipped.
[nl]
[emph test] takes care of all administrative details.
[nl]
Note: to make it possible to use [emph private] unit test routines,
the source code of this subroutine is kept in a separate file,
[emph ftnunit_test.f90] that should be included in an appropriate
place in the program's sources. This way, you can make it a private
routine in each module. The only public access to the unit testing
routines is then via the subroutine [emph testproc] that is passed to
[emph runtests].
[list_begin arg]
[arg_def "subroutine" proc]
Subroutine that implements an individual unit test. It takes no
arguments. Within each such subroutine the complete unit test is run.
[arg_def "character(len=*), intent(in)" text]
Text describing the particular unit test. It is printed in the log
file.
[list_end]
[call [cmd "call assert_true( cond, text )"]]
Routine to check that a condition is true. If not, a message is printed
in the log file and the number of failures is increased.
[list_begin arg]
[arg_def "logical" cond]
The condition to be checked
[arg_def "character(len=*), intent(in)" text]
Text describing the condition
[list_end]
[call [cmd "call assert_false( cond, text )"]]
Routine to check that a condition is false. If not, a message is
printed in the log file and the number of failures is increased.
[list_begin arg]
[arg_def "logical" cond]
The condition to be checked
[arg_def "character(len=*), intent(in)" text]
Text describing the condition
[list_end]
[call [cmd "call assert_equal( value1, value2, text )"]]
Routine to check that two integers are equal or if two one-dimensional
integer arrays are equal. If not, a message is printed, along with the
values that were different.
[list_begin arg]
[arg_def "integer \[, dimension(:)\]" value1]
The first integer value or array
[arg_def "integer \[, dimension(:)\]" value2]
The second integer value or array
[arg_def "character(len=*), intent(in)" text]
Text describing the condition
[list_end]
[call [cmd "call assert_comparable( value1, value2, margin, text )"]]
Routine to check that two reals are almost equal or if two one-dimensional
real arrays are almost equal. If not, a message is printed, along with
the values that were different.
[nl]
The margin is taken as a relative tolerance. Two values are
considered almost equal if:
[example {
abs( v1 - v2 ) < margin * (abs(v1)+abs(v2)) / 2
}]
[list_begin arg]
[arg_def "real \[, dimension(:)\]" value1]
The first real value or array
[arg_def "real \[, dimension(:)\]" value2]
The second real value or array
[arg_def "character(len=*), intent(in)" text]
Text describing the condition
[list_end]
[call [cmd "exists = ftnunit_file_exists( filename )"]]
Logical function to check that a particular file exists
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be checked
[list_end]
[call [cmd "call ftnunit_get_lun( lun )"]]
Subroutine to get a free LU-number
[list_begin arg]
[arg_def "integer, intent(out)" lun]
Next free LU-number
[list_end]
[call [cmd "call ftnunit_remove_file( filename )"]]
Subroutine to remove (delete) a file
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be removed
[list_end]
[call [cmd "call ftnunit_make_empty_file( filename )"]]
Subroutine to make a new, empty file
[list_begin arg]
[arg_def "character(len=*), intent(in)" filename]
Name of the file to be created
[list_end]
[list_end]
[section "GENERATING TESTS FROM A TABLE"]
The Tcl program "gentabletest.tcl" reads the test specifications from an
input file and generates a complete Fortran program. The ideas from
Bil Kleb's "Toward Scientific Numerical Modeling"
[uri ftp://ftp.rta.nato.int/PubFullText/RTO/MP/RTO-MP-AVT-147/RTO-MP-AVT-147-P-17-Kleb.pdf]
were used for the set-up.
[para]
To do: provide a detailed description. For the moment: see [emph example.tbl], including below.
[example {
! Example of generating test code via a table
! -------------------------------------------
! The routine to be tested determines the minimum oxygen concentration
! in a river, based on the Streeter-Phelps model:
!
! dBOD/dt = -k * BOD
!
! dO2/dt = -k * BOD + ka * (O2sat-O2) / H
!
! where
! BOD - biological oxygen demand (mg O2/l)
! O2 - oxygen concentration (mg O2/l)
! O2sat - saturation concentration of oxygen (mg O2/l)
! k - decay rate of BOD (1/day)
! ka - reareation rate of oxygen (m/day)
! H - depth of the river
!
! We need boundary (initial) conditions for BOD and oxygen and
! the equations describe the concentrations of BOD and oxygen in a
! packet of water as it flows along the river.
!
! Note:
! It is a very simple model, it is not meant as a realistic
! representation.
!
! The routine simply continues the solution until a minimum is found.
! The results are: oxymin and time
!
!
! The keyword DECLARATIONS introduces the declarations we need for the
! complete generated code
!
DECLARATIONS
use streeter_phelps
real :: bod, oxy
real :: k, ka, h, oxysat, dt, oxymin, time
!
! The keyword CODE introduces the code fragment required to run the
! routine or routines. The results and possible checking of error
! conditions are separated.
!
CODE
call compute_min_oxygen( bod, oxy, k, ka, h, oxysat, dt, oxymin, time )
!
! The keyword RESULT indicates which arguments/variables hold the
! interesting results. Specify one name per line (you can not currently
! use array elements) and the allowed margin (taken as absolute, if
! followed by "%" as a percentage)
!
RESULT
oxymin 0.001 ! Minimum oxygen concentration
time 0.01% ! Time the minimum is reached
!
! The keyword ERROR is used for a code fragment that checks if the
! routine has correctly found an error in the input (that is, some
! parameter value is out of range). The code is invoked when any of
! result variables in a table entry has the keyword ERROR instead of
! a proper value.
! Use the subroutine "error" to indicate the correctly reported error
! condition.
!
ERROR
if ( time == -999.0 ) then
call error
endif
!
! The keyword RANGES specifies that the variables are to be taken
! from a uniform or a normal distribution. The generated program will
! simply select values at random and run the code with them. The report
! consists of the detailed output as well as a summary.
!
RANGES
oxy 10.0 2.0 Uniform ! Name of the variable, the mean and the margin (uniform)
! Normal: mean and standard deviation followed by Normal
! Note: all parameters must be given!
!
! The keyword TABLE indicates the beginning of a table of input data and
! expected values. The first (non-comment) line contains the names of
! the variables as used in the code fragments and all others are the
! values expected.
!
! There are two special values:
! ? - indicating an unknown value for result variables and a "do not
! care" value for input variables
! It is useful to generate a table that does contain the (computed)
! results (see the file table.out) or to indicate situations
! where one or more input variables are out of range and this
! should lead to an error
! ERROR - indicating that the entry should cause the routine to be
! tested to flag an error condition.
!
TABLE
dt oxy bod oxysat h k ka oxymin time
0.1 10 1 10 10 0.1 1.0 10.0 2.0
1.0 10 1 10 10 0.1 1.0 ? ?
!
! This case is unacceptable: time step must be positive
0.0 ? ? ? ? ? ? ? ERROR
1.0 0. 10 10 10 0.1 1.0 ? ?
}]
[section TODO]
The following things are still left to do:
[list_begin bullet]
[bullet]
Proper inclusion of the routine [emph prolog] and [emph epilog]
[bullet]
Extension of the set of assertion routines
[list_end]
[section "RELATED WORK"]
There are at least two similar initiatives with regard to a unit testing
framework for Fortran:
[list_begin bullet]
[bullet]
[uri {http://nasarb.rubyforge.org} {Funit (implemented in Fortran and
Ruby)}] by Bil Kleb and others
[bullet]
[uri {http://www.sourceforge.net/projects/pfunit} {A framework
implemented in Fortran}] by Brice Womack and Tom Clune
[bullet]
[uri {http://www.sourceforge.net/projects/fortranxunit} {FRUIT
(implemented in Fortran and Ruby)}] by Andrew Chen
[list_end]
(Note: To avoid confusion, I have renamed my original module "funit" to
[emph ftnunit])
[manpage_end]

129
flibs-0.9/flibs/doc/ipc.man Normal file
View File

@@ -0,0 +1,129 @@
[comment {-*- flibs -*- doctools manpage}]
[manpage_begin flibs/ipc n 0.1]
[copyright {2006 Arjen Markus <arjenmarkus@sourceforge.net>}]
[moddesc flibs]
[titledesc {Inter-process communication}]
[description]
[emph IPC] or inter-process communication is the name for various
mechanisms by which programs (either multiple instances of the same
program or different programs) can exchange information. There are
numerous mechanisms available, each with its own pros and cons.
[para]
The modules in the [emph ipc] directory are intended to make
inter-process communication possible with a minimum of OS requirements:
[list_begin bullet]
[bullet]
The module [emph ipc_file] uses plain files for this communication and
can therefore only be used if the processes have access to the same
disks.
[nl]
The advantage however is that it can be in standard Fortran, without any
reliance on special system libraries. The sole exception is the use of a
sleep routine, to make sure the program can pause without consuming too
much CPU (like in a long computation).
[bullet]
The module [emph ipc_mmap] uses so-called memory-mapped files. This is
a more sophisticated method that does rely on system libraries
(currently Windows and Linux, including Cygwin, are supported).
[nl]
The advantage is mostly in better performance - data are communicated
via memory and not via the hard disk. The disadvantages are that a C
compiler is required, as part of the code is in C and it is limited to
shared-memory systems.
[list_end]
Both modules use the [emph same] set of routines and the [emph same]
programming methods can be used. In fact, you select either one by
changing the module [emph ipc], not by using [emph ipc_file] or
[emph ipc_mmap] directly.
[section "PROGRAMMING PHILOSOPHY"]
[section ROUTINES]
The set of routines offered by the [emph IPC] modules consists of
the following:
[list_begin definitions]
[call [cmd "call ipc_open( comm, src, dest, dir, maxsize )"]]
Open the connection between sender (src) and receiver (dest). The
connection parameters are stored in the argument [emph comm], which is
then used for all other routines to identify the connection.
[list_begin arg]
[arg_def type(ipc_comm) comm]
Variable of derived type "ipc_comm" which will hold all information.
[arg_def character(len=*) src]
String identifying the sender process. This should be unique among all
the processes involved. (Maximum significant length: 20 characters)
[arg_def character(len=*) dest]
String identifying the receiver process. This should be unique
among all the processes involved. (Maximum significant length: 20
characters)
[arg_def character(len=*) dir]
Directory to be used for the files. This directory must be accessible to
all processes. An empty string signifies a default directory, which
depends on the platform, such as /tmp or c:\temp - but it is not
recommended. (Maximum significant length: 256 characters)
[arg_def integer maxsize]
The maximum size for all the data sent in one message. It is measured in
bytes. (Note: you should not take it too tight, as per call to
ipc_send there will be a small overhead, some 40 bytes, to identify the
type and dimension of the data. It is ignored, however, when using the
[emph ipc_file] module).
[call [cmd "call ipc_send_start( comm, tag, id )"]]
Start the sending of the data. The message as a whole is identified by a
short string and an ID number, so that the receiver can check (if
needed) that the right message is received.
[arg_def type(ipc_comm) comm]
Variable of derived type "ipc_comm" which holds the connection information.
[arg_def character(len=*) tag]
String identifying the type of message (maximum length: 20)
[arg_def integer id]
ID number of the message
[call [cmd "call ipc_send_stop( comm )"]]
Signals that all data have been sent.
[arg_def type(ipc_comm) comm]
Variable of derived type "ipc_comm" which holds the connection information.
...
[call [cmd "call ipc_receive_start( comm, tag, id )"]]
Start receiving of the data. The message as a whole is identified by the
short string and an ID number. so that the receiver can check (if
needed) that the right message is received.
[arg_def type(ipc_comm) comm]
Variable of derived type "ipc_comm" which holds the connection information.
[arg_def character(len=*) tag]
String identifying the type of message (maximum length: 20)
[arg_def integer id]
ID number of the message
[manpage_end]

View File

@@ -0,0 +1,19 @@
OVERVIEW
--------
src/strings:
------------
The source files in this directory manipulate character strings in some
way or another:
- filedir.f90
Module to manipulate the parts of a file name (directory, name, extension)
- textstr.f90
Small collection of modules that can help to store strings of arbitrary length.
It is not meant to implement a general arbitrary-length module: there are only a few
intrinsic functions emulated.
- csv_file.f90
Module to help _write_ CSV files

View File

@@ -0,0 +1,6 @@
@echo off
rem makedoc.tcl --
rem Script for creating HTML-files from the raw documentation files
rem
c:\tcl\bin\tclsh.exe c:\tcl\demos\TclApps\apps\dtp\main.tcl doc -out %1.html html %1.man
copy /n /y %1.html ..\..\site

View File

@@ -0,0 +1,79 @@
#
# A script to generate the html documentation from the
# doctools man files.
#
package require doctools
#
# formatfile --
# Process the given manfile and generate the associated
# html file.
#
proc formatfile {manfile} {
::doctools::new mdt
mdt configure -file $manfile
mdt configure -format html
set handle [open $manfile r]
set content [read -nonewline $handle]
close $handle
set htmlcontent [mdt format $content]
set htmlfile [computefilename $manfile .html]
set handle [open $htmlfile w]
puts -nonewline $handle $htmlcontent
close $handle
mdt destroy
return $htmlfile
}
#
# computefilename --
# Replace the extension in the given filename
# with the given new extension.
#
proc computefilename {filename newextension} {
set lastdot [string last "." $filename]
incr lastdot -1
set newname [string range $filename 0 $lastdot]
append newname $newextension
return $newname
}
#
# processall --
# Process all man files in the current directory.
#
proc processall {} {
set manfiles [glob "*/*.man"]
foreach filename $manfiles {
set isuptodate [isuptodate $filename]
if {$isuptodate==0} then {
puts "> Updating $filename..."
formatfile $filename
} else {
puts "> Up-to-date: $filename "
}
}
return ""
}
#
# isuptodate --
# Returns 1 if the given man file is up-to-date,
# with respect to the associated htmlfile.
#
proc isuptodate {manfile} {
set htmlfile [computefilename $manfile .html]
set fexists [file exists $htmlfile]
if {$fexists==0} then {
set result 0
} else {
set time1 [file mtime $manfile]
set time2 [file mtime $htmlfile]
set result [expr {$time1< $time2}]
}
return $result
}
#
# Executable part of the script.
#
processall

Some files were not shown because too many files have changed in this diff Show More