├── .gitignore ├── LICENCE ├── README.md ├── benchmarks ├── benchmark-array.f90 └── benchmark-ints.f90 ├── data ├── a_e.csv ├── a_f.csv ├── a_z.csv ├── ancestry_comp.csv ├── c_e.csv ├── c_f.csv ├── c_z.csv ├── hg38.bed ├── i.csv ├── i_z.csv ├── k.csv ├── k_z.csv ├── u_e.csv ├── u_f.csv ├── u_z.csv ├── x_e.csv ├── x_f.csv └── x_z.csv ├── doc ├── css │ ├── font-awesome.css │ ├── font-awesome.min.css │ ├── local.css │ └── pygments.css ├── favicon.png ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── index.html ├── interface │ ├── aprint.html │ ├── cast.html │ ├── echo.html │ ├── from_file.html │ ├── join.html │ ├── operator(+).html │ ├── operator(-).html │ ├── operator(==).html │ ├── operator(ASTERISKASTERISK).html │ ├── operator(SLASH=).html │ ├── operator(SLASHSLASH).html │ ├── random_gauss.html │ ├── split.html │ ├── str.html │ ├── string.html │ └── to_file.html ├── js │ ├── MathJax-config │ │ └── .gitignore │ └── svg-pan-zoom.min.js ├── lists │ ├── files.html │ ├── modules.html │ ├── procedures.html │ └── types.html ├── module │ ├── array_printing.html │ ├── binary_io.html │ ├── file_io.html │ ├── gaussian_sampling.html │ ├── internal_io.html │ ├── io_fortran_lib.html │ ├── join_split.html │ ├── operators.html │ ├── randoms.html │ ├── string_methods.html │ └── text_io.html ├── modules.json ├── page │ ├── Examples │ │ ├── benchmark.html │ │ ├── binary.html │ │ ├── csv.html │ │ ├── exome.html │ │ ├── fizzbuzz.html │ │ ├── index.html │ │ ├── logging.html │ │ └── manipulations.html │ ├── Ref │ │ ├── String-methods.html │ │ ├── String.html │ │ ├── aprint.html │ │ ├── cast.html │ │ ├── constants.html │ │ ├── echo.html │ │ ├── from_file.html │ │ ├── index.html │ │ ├── join-split.html │ │ ├── operators.html │ │ ├── str.html │ │ └── to_file.html │ ├── UserInfo │ │ ├── characters.html │ │ ├── compilers.html │ │ ├── error-codes.html │ │ ├── file-ext.html │ │ ├── index.html │ │ ├── locale-fmts.html │ │ ├── text-fmts.html │ │ └── thread-safety.html │ └── index.html ├── search.html ├── sourcefile │ ├── array_printing_impl.f90.html │ ├── binary_io_impl.f90.html │ ├── file_io_impl.f90.html │ ├── gaussian_sampling_impl.f90.html │ ├── internal_io_impl.f90.html │ ├── io_fortran_lib_mod.f90.html │ ├── join_split_impl.f90.html │ ├── operators_impl.f90.html │ ├── randoms_mod.f90.html │ ├── string_methods_impl.f90.html │ └── text_io_impl.f90.html ├── src │ ├── array_printing_impl.f90 │ ├── binary_io_impl.f90 │ ├── file_io_impl.f90 │ ├── gaussian_sampling_impl.f90 │ ├── internal_io_impl.f90 │ ├── io_fortran_lib_mod.f90 │ ├── join_split_impl.f90 │ ├── operators_impl.f90 │ ├── randoms_mod.f90 │ ├── string_methods_impl.f90 │ └── text_io_impl.f90 ├── tipuesearch │ ├── .DS_Store │ ├── img │ │ ├── .DS_Store │ │ ├── loader.gif │ │ └── search.png │ ├── tipuesearch.css │ ├── tipuesearch.js │ ├── tipuesearch.min.js │ ├── tipuesearch_content.js │ └── tipuesearch_set.js └── type │ └── string.html ├── doc_API_design ├── Examples │ ├── benchmark.md │ ├── binary.md │ ├── csv.md │ ├── exome.md │ ├── fizzbuzz.md │ ├── index.md │ ├── logging.md │ └── manipulations.md ├── Ref │ ├── String-methods.md │ ├── String.md │ ├── aprint.md │ ├── cast.md │ ├── constants.md │ ├── echo.md │ ├── from_file.md │ ├── index.md │ ├── join-split.md │ ├── operators.md │ ├── str.md │ └── to_file.md ├── UserInfo │ ├── characters.md │ ├── compilers.md │ ├── error-codes.md │ ├── file-ext.md │ ├── index.md │ ├── locale-fmts.md │ ├── text-fmts.md │ └── thread-safety.md └── index.md ├── ford-API-index.md ├── fpm.toml ├── src ├── io_fortran_lib │ ├── array_printing_impl.f90 │ ├── binary_io_impl.f90 │ ├── file_io_impl.f90 │ ├── internal_io_impl.f90 │ ├── join_split_impl.f90 │ ├── operators_impl.f90 │ ├── string_methods_impl.f90 │ └── text_io_impl.f90 ├── modules │ ├── io_fortran_lib_mod.f90 │ └── randoms_mod.f90 └── randoms │ └── gaussian_sampling_impl.f90 └── test ├── test.ps1 ├── test.sh ├── tests.log └── units ├── array-test-c.f90 ├── array-test-i.f90 ├── array-test-r.f90 ├── file-test-c.f90 ├── file-test-i.f90 ├── file-test-r.f90 ├── kinds_mod.f90 ├── scalar-test-c.f90 ├── scalar-test-i.f90 └── scalar-test-r.f90 /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2024, Austin Bullock 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IO-Fortran-Library 2 | 3 | The [IO-Fortran-Library](https://acbbullock.github.io/IO-Fortran-Library/doc/index.html) 4 | is a Fortran module `io_fortran_lib` which provides high level routines 5 | for doing internal and external I/O. In particular, the module provides 6 | a handful of generic interfaces and a simple derived type for doing 7 | string-based and array-based I/O that are useful for recording program 8 | data, reading data into programs, writing formatted logs and output, 9 | and for doing advanced string manipulations. For instance, one may read 10 | and write data from/to `.csv` and `.dat` files, represent numbers as 11 | strings inside of a string expression, efficiently stream text data to 12 | a `.log` file, and dynamically manipulate strings with a `String` type 13 | (including casting between numeric and string data). 14 | 15 | To use `io_fortran_lib` with your [fpm](https://github.com/fortran-lang/fpm) 16 | project, add the following lines to your `fpm.toml` file and `use` the 17 | module in your program units to access the routines: 18 | 19 | ```toml 20 | [dependencies] 21 | IO-Fortran-Library = { git="https://github.com/acbbullock/IO-Fortran-Library", branch="main" } 22 | ``` 23 | 24 | The module is fully self-contained, with no external dependencies, and 25 | is written to be portable and compliant to the Fortran 2018 standard 26 | such that no special extensions or compiler options should be required. 27 | The public interfaces accept all intrinsic numeric types (`integer`, 28 | `real`, and `complex`) and all standard kinds provided by the intrinsic 29 | `iso_fortran_env` module (`int8`, `int16`, `int32`, `int64`, `real32`, 30 | `real64`, and `real128`). All array-based routines additionally support 31 | up to rank 15. 32 | 33 | ## Documentation 34 | 35 | The API documentation for this project was generated by 36 | [FORD](https://github.com/Fortran-FOSS-Programmers/ford) (the Fortran 37 | documentation generator) and is available at 38 | [IO-Fortran-Library](https://acbbullock.github.io/IO-Fortran-Library/doc/index.html). 39 | To view the documentation locally, clone this repository and view 40 | `/doc/index.html` in your browser. 41 | 42 | For a list of available interfaces and how to call them, see the 43 | [reference guide](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/index.html). 44 | For further information, see the 45 | [important user information](https://acbbullock.github.io/IO-Fortran-Library/doc/page/UserInfo/index.html) 46 | and the 47 | [tutorials](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Examples/index.html) 48 | for complete example programs. 49 | 50 | ## Release Notes v1.3.1 51 | 52 | ### Improved 53 | 54 | * Fixed a "z" format string conversion bug. 55 | * Updated documentation. 56 | * Modified benchmark programs. 57 | 58 | ## Release Notes v1.2.1 59 | 60 | ### Added 61 | 62 | * Added implementations for 63 | [cast](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/cast.html). 64 | 65 | ### Improved 66 | 67 | * Improved performance for 68 | [count](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/String-methods.html#count), 69 | [read_file](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/String-methods.html#read_file), 70 | and [write_file](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/String-methods.html#write_file), 71 | as well as [split](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/join-split.html) 72 | for very large strings. The read/write performance is 2-4x faster than 73 | previous (compiler-dependent). 74 | 75 | ## Release Notes v1.2.0 76 | 77 | ### New features 78 | 79 | * The `String` type has been greatly expanded and can now serve as an 80 | interface for advanced string manipulations and text file I/O. See the 81 | references for 82 | [type-bound procedures](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/String-methods.html) 83 | and the [tutorials](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Examples/index.html) 84 | for more information. 85 | * New [constants](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/constants.html) 86 | have been added for public use to facilitate file I/O consistently on 87 | a variety of platforms. 88 | * New interface [String](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/String.html) 89 | for an elemental version of `str` with a return type of `String`. 90 | * New interface [cast](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/cast.html) 91 | for casting `character` and `String` data into numeric variables. 92 | * New interfaces [join and split](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/join-split.html) 93 | for joining and splitting strings. 94 | * New [operator interfaces](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/operators.html) 95 | for string manipulations involving both `character` and `String` 96 | types. 97 | 98 | ### Added 99 | 100 | * Slightly modified interfaces for 101 | [echo](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/echo.html) 102 | and [from_file](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/from_file.html) 103 | to accept an additional optional argument each. 104 | * Test programs have been added in `/test`, which are all passing in 105 | the following configurations with lowest and highest optimizations 106 | enabled: 107 | * GNU Fortran Compiler v11.3.0 on Linux 108 | * Intel Fortran Compiler v2023.0.0 on Linux 109 | * Intel Fortran Compiler Classic v2021.8.0 on Linux 110 | * GNU Fortran Compiler v11.2.0 on Windows 11 111 | * Intel Fortran Compiler v2023.0.0 on Windows 11 112 | * Intel Fortran Compiler Classic v2021.8.0 on Windows 11 113 | 114 | ### Improved 115 | 116 | * Removed vertical bars in 117 | [aprint](https://acbbullock.github.io/IO-Fortran-Library/doc/page/Ref/aprint.html) 118 | which were displayed incorrectly in terminals with display encoding 119 | different from UTF-8. 120 | * Text file I/O has been optimized for performance and memory usage, 121 | and expanded to support processing of large files. 122 | 123 | ## License 124 | 125 | All source code referenced is distributed under the 126 | [MIT license](https://github.com/acbbullock/IO-Fortran-Library/blob/main/LICENCE). 127 | 128 | ## Contact 129 | 130 | For bug fixes or feature requests, feel free to open an issue at the 131 | [project repository](https://github.com/acbbullock/IO-Fortran-Library/issues) 132 | or contact [acb.bullock@gmail.com](mailto:acb.bullock@gmail.com). 133 | -------------------------------------------------------------------------------- /benchmarks/benchmark-array.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use, intrinsic :: iso_fortran_env, only: int64, rk=>real32, dp=>real64, compiler_version, compiler_options 3 | use randoms, only: random_gauss 4 | use io_fortran_lib, only: String, cast, str, LF, operator(+) 5 | implicit none (type, external) 6 | 7 | type(String) :: csv 8 | type(String), allocatable :: cells(:,:) 9 | 10 | integer(int64) :: t1, t2 11 | real(dp) :: wall_time, rate 12 | 13 | integer, parameter :: n = 15000 14 | real(rk), allocatable :: x(:,:), y(:,:) 15 | 16 | allocate( x(n,n), cells(n,n) ); call random_gauss(x, 0e0_rk, 1.0_rk) 17 | write(*,"(a)") "Compiler version: " + compiler_version() 18 | write(*,"(a)") "Compiler options: " + compiler_options() + LF 19 | 20 | call system_clock(t1) 21 | call cast(x, into=cells, fmt="z") 22 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 23 | 24 | write(*,"(a)") "Wall time for cast: " + str(wall_time, fmt="f", decimals=3) + " s" 25 | write(*,"(a)") "Number of string conversions/second: " + str(nint(size(x)/wall_time)) + LF 26 | 27 | call system_clock(t1) 28 | call csv%write_file(cells, file="bigx.csv") 29 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 30 | 31 | write(*,"(a)") "Wall time for write_file: " + str(wall_time, fmt="f", decimals=3) + " s" 32 | write(*,"(a)") "Estimated file size: " + str(csv%len64()/1e9, fmt="f", decimals=6) + " GB" + LF 33 | 34 | call system_clock(t1) 35 | call csv%read_file("bigx.csv", cell_array=cells) 36 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 37 | 38 | write(*,"(a)") "Wall time for read_file: " + str(wall_time, fmt="f", decimals=3) + " s" + LF 39 | 40 | call csv%empty(); allocate( y(n,n) ) 41 | 42 | call system_clock(t1) 43 | call cast(cells, into=y, fmt="z") 44 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 45 | 46 | write(*,"(a)") "Wall time for cast: " + str(wall_time, fmt="f", decimals=3) + " s" 47 | write(*,"(a)") "Number of string casts/second: " + str(nint(size(x)/wall_time)) 48 | write(*,"(a,l)") "Data is exact match: ", all(x == y) 49 | end program main 50 | -------------------------------------------------------------------------------- /benchmarks/benchmark-ints.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use, intrinsic :: iso_fortran_env, only: i64=>int64, dp=>real64 3 | use io_fortran_lib, only: String, str, operator(+) 4 | implicit none (type, external) 5 | 6 | integer, parameter :: largest = huge(0), smallest = -largest - 1, nsteps = 128 7 | integer, parameter :: step_size = int( (int(largest,i64) - int(smallest,i64) )/int(nsteps,i64)) 8 | 9 | type(String) :: csv 10 | type(String), allocatable, target :: cells(:,:) 11 | type(String), pointer, contiguous :: cells_p(:,:) 12 | 13 | integer :: lower, upper, i, j 14 | 15 | integer(i64) :: t1, t2, total_length 16 | real(dp) :: wall_time, rate 17 | 18 | write(*,"(a)") "Writing integers from " + str(smallest) + " to " + str(largest) 19 | 20 | allocate( cells(step_size, 2) ) 21 | total_length = 0_i64 22 | call system_clock(t1) 23 | 24 | do j = 1, nsteps 25 | lower = smallest + (j-1)*step_size 26 | upper = lower + step_size - 1 27 | 28 | cells_p(lower:upper,1:2) => cells(:,:) 29 | 30 | do concurrent ( i = lower:upper ) 31 | cells_p(i,1) = String(i, fmt="i") 32 | cells_p(i,2) = String(i, fmt="z") 33 | end do 34 | 35 | call csv%write_file(cells_p, file="int32.csv", append=.true.) 36 | total_length = total_length + csv%len64() 37 | 38 | write(*,"(a)") "File length: " + str(total_length/1e9, fmt="f", decimals=3) + " GB in cycle " + str(j) 39 | end do 40 | 41 | lower = upper + 1; upper = largest 42 | nullify(cells_p); deallocate(cells); allocate( cells(lower:upper,2) ) 43 | 44 | do concurrent ( i = lower:upper ) 45 | cells(i,1) = String(i, fmt="i") 46 | cells(i,2) = String(i, fmt="z") 47 | end do 48 | 49 | call csv%write_file(cells, file="int32.csv", append=.true.) 50 | total_length = total_length + csv%len64() 51 | 52 | write(*,"(a)") "File length: " + str(total_length/1e9, fmt="f", decimals=3) + " GB" 53 | 54 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 55 | write(*,"(a)") "Total time for write: " + str(wall_time/60, fmt="f", decimals=3) + " minutes" 56 | end program main 57 | -------------------------------------------------------------------------------- /doc/css/local.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | } 4 | table.nostretch { 5 | width=100% 6 | } 7 | .nostretch td { 8 | class='block' 9 | } 10 | .nostretch tr td{ 11 | width:1%; 12 | white-space:nowrap; 13 | } 14 | 15 | html { 16 | scroll-padding-top: 70px; 17 | } 18 | 19 | ol.hierarchy { 20 | min-height: 40px; 21 | background-color: #f5f5f5; 22 | border: 1px solid #e3e3e3; 23 | border-radius: 3px; 24 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 25 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); 26 | } 27 | 28 | .smallcaps { 29 | font-variant: small-caps; 30 | } 31 | .well .sidebar { 32 | padding: 8px 0 33 | } 34 | .sidebar a { 35 | padding: 0px,0px,0px,0px 36 | } 37 | .varlist>tbody>tr>td { 38 | padding-left: 3px; 39 | padding-right: 3px; 40 | } 41 | .varlist>tbody>tr>td:first-child, .varlist>thead>tr>td:first-child { 42 | padding-left: 8px; 43 | } 44 | .varlist>tbody>td>td:last-child, .varlist>thead>tr>td:last-child { 45 | padding-right: 8px; 46 | } 47 | 48 | .highlight pre { 49 | overflow-x: auto; 50 | overflow-wrap: normal; 51 | white-space: pre 52 | } 53 | 54 | /* .hl is for when line numbers are included, .highlight is for all 55 | other cases. */ 56 | .hl pre { 57 | counter-reset: line-numbering; 58 | overflow-x: auto; 59 | overflow-wrap: normal; 60 | white-space: pre; 61 | padding: 0; 62 | padding-right: 9.5px; 63 | overflow-y: hidden; 64 | padding-bottom: 9.5px; 65 | } 66 | 67 | .hl pre a::before { 68 | content: counter(line-numbering); 69 | counter-increment: line-numbering; 70 | padding-right: 0.7em; /* space after numbers */ 71 | margin-top: 4.5em; 72 | width: 60px; 73 | text-align: right; 74 | opacity: 0.7; 75 | display: inline-block; 76 | color: #aaa; 77 | background: #eee; 78 | margin-right: 10px; 79 | border-right: 1px solid #ccc; 80 | -webkit-touch-callout: none; 81 | -webkit-user-select: none; 82 | -khtml-user-select: none; 83 | -moz-user-select: none; 84 | -ms-user-select: none; 85 | user-select: none; 86 | } 87 | 88 | .hl pre a:first-of-type::before { 89 | padding-top: 9.5px; 90 | } 91 | 92 | .hl pre a:last-of-type::before { 93 | padding-bottom: 9.5px; 94 | } 95 | 96 | .hl pre a:only-of-type::before { 97 | padding: 9.5px; 98 | } 99 | 100 | .hl pre a { 101 | display: inline-block; 102 | height: 4.5em; 103 | margin: -4.5em 0 0; 104 | } 105 | .codesum h3 { 106 | margin-top: 2px; 107 | margin-bottom: 2px; 108 | } 109 | 110 | h1.inline, h2.inline, h3.inline { 111 | display: inline; 112 | } 113 | 114 | .depwarn { 115 | float: right; 116 | } 117 | 118 | .anchor { 119 | position: absolute; 120 | margin: -4.5em; 121 | visibility:hidden; 122 | } 123 | 124 | .alert { 125 | margin-left: 5px; 126 | margin-right: 5px; 127 | margin-top: 5px; 128 | } 129 | 130 | .alert-title { 131 | margin-top: 0; 132 | color: inherit; 133 | } 134 | 135 | div.toc { 136 | font-size: 14.73px; 137 | padding-left: 0px; 138 | padding-right: 0px; 139 | } 140 | 141 | div.toc a { 142 | padding-left: 20px; 143 | padding-right: 20px; 144 | margin-right: 15px; 145 | padding-top: 5px; 146 | padding-bottom: 5px; 147 | } 148 | 149 | div.toc li { 150 | font-size: 0.95em; 151 | padding-left: 15px; 152 | } 153 | 154 | div.toc li.title { 155 | font-size: 1em; 156 | } 157 | 158 | div.toc hr { 159 | margin-top: 12px; 160 | margin-bottom: 10px; 161 | } 162 | 163 | .in-well { 164 | padding: 0px 0px; 165 | margin-bottom: 0px; 166 | float:right; 167 | } 168 | 169 | table tr.submod>td { 170 | border-top: none; 171 | font-size: 13.5px; 172 | } 173 | 174 | .graph-help { 175 | font-size: 10px; 176 | } 177 | 178 | .depgraph { 179 | width: 100%; 180 | max-width: 1140px; 181 | } 182 | 183 | #sidebar a { 184 | white-space: nowrap; 185 | overflow: hidden; 186 | text-overflow: ellipsis; 187 | } 188 | 189 | .highlighttable { 190 | width: auto; 191 | table-layout: fixed; 192 | } 193 | 194 | ul.checklist { 195 | list-style-type: none; 196 | } 197 | 198 | ul.checklist input[type="checkbox"] { 199 | margin-left: -20.8px; 200 | margin-right: 4.55px; 201 | } 202 | 203 | .gitter-chat-embed { 204 | z-index: 100000; 205 | } 206 | 207 | table.graph { 208 | text-align: center; 209 | } 210 | 211 | 212 | .graph td.root { 213 | border:2px solid black; 214 | padding:10px; 215 | } 216 | 217 | .graph td.triangle-right:after { 218 | content: ""; 219 | display: block; 220 | border-top: 7px solid transparent; 221 | border-bottom: 7px solid transparent; 222 | border-left: 7px solid black; 223 | } 224 | 225 | .graph td.triangle-left:after { 226 | content: ""; 227 | display: block; 228 | border-top: 7px solid transparent; 229 | border-bottom: 7px solid transparent; 230 | border-right: 7px solid black; 231 | } 232 | 233 | .graph td.node { 234 | color: white; 235 | padding:10px; 236 | border-style: solid; 237 | border-width: 3px 0px 3px 0px; 238 | border-color: white; 239 | } 240 | 241 | .graph td.node a{ 242 | color: white; 243 | } 244 | 245 | .graph td.dashedText, 246 | .graph td.solidText { 247 | padding: 0px 10px 0px 10px; 248 | min-width: 40px; 249 | color: black; 250 | border-color: black; 251 | } 252 | 253 | .graph td.dashedText { 254 | border-bottom-style: dashed; 255 | } 256 | 257 | .graph td.solidText { 258 | border-bottom-style: solid; 259 | } 260 | 261 | .graph td.dashedBottom, 262 | .graph td.dashedTop, 263 | .graph td.solidTop, 264 | .graph td.solidBottom { 265 | min-width: 40px; 266 | color: transparent; 267 | border-color: black; 268 | } 269 | 270 | .graph td.dashedBottom { 271 | border-bottom-style: dashed; 272 | } 273 | 274 | .graph td.dashedTop { 275 | border-top-style: dashed; 276 | } 277 | 278 | .graph td.solidBottom { 279 | border-bottom-style: solid; 280 | } 281 | 282 | .graph td.solidTop { 283 | border-top-style: solid; 284 | } 285 | 286 | /* Ensure tables in Pages don't collapse horizontally */ 287 | td, th { 288 | padding-right: 10px; 289 | } 290 | 291 | .nav>li>a { 292 | padding-left: 10px; 293 | padding-right: 10px; 294 | } 295 | 296 | .nav > .nav { 297 | margin-left: 16px; 298 | } 299 | -------------------------------------------------------------------------------- /doc/css/pygments.css: -------------------------------------------------------------------------------- 1 | pre { line-height: 125%; } 2 | td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 3 | span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 4 | td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 5 | span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } 6 | .codehilite .hll { background-color: #ffffcc } 7 | .codehilite { background: #f8f8f8; } 8 | .codehilite .c { color: #3D7B7B; font-style: italic } /* Comment */ 9 | .codehilite .err { border: 1px solid #FF0000 } /* Error */ 10 | .codehilite .k { color: #008000; font-weight: bold } /* Keyword */ 11 | .codehilite .o { color: #666666 } /* Operator */ 12 | .codehilite .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ 13 | .codehilite .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ 14 | .codehilite .cp { color: #9C6500 } /* Comment.Preproc */ 15 | .codehilite .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ 16 | .codehilite .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ 17 | .codehilite .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ 18 | .codehilite .gd { color: #A00000 } /* Generic.Deleted */ 19 | .codehilite .ge { font-style: italic } /* Generic.Emph */ 20 | .codehilite .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ 21 | .codehilite .gr { color: #E40000 } /* Generic.Error */ 22 | .codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 23 | .codehilite .gi { color: #008400 } /* Generic.Inserted */ 24 | .codehilite .go { color: #717171 } /* Generic.Output */ 25 | .codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 26 | .codehilite .gs { font-weight: bold } /* Generic.Strong */ 27 | .codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 28 | .codehilite .gt { color: #0044DD } /* Generic.Traceback */ 29 | .codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ 30 | .codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ 31 | .codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ 32 | .codehilite .kp { color: #008000 } /* Keyword.Pseudo */ 33 | .codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ 34 | .codehilite .kt { color: #B00040 } /* Keyword.Type */ 35 | .codehilite .m { color: #666666 } /* Literal.Number */ 36 | .codehilite .s { color: #BA2121 } /* Literal.String */ 37 | .codehilite .na { color: #687822 } /* Name.Attribute */ 38 | .codehilite .nb { color: #008000 } /* Name.Builtin */ 39 | .codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 40 | .codehilite .no { color: #880000 } /* Name.Constant */ 41 | .codehilite .nd { color: #AA22FF } /* Name.Decorator */ 42 | .codehilite .ni { color: #717171; font-weight: bold } /* Name.Entity */ 43 | .codehilite .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ 44 | .codehilite .nf { color: #0000FF } /* Name.Function */ 45 | .codehilite .nl { color: #767600 } /* Name.Label */ 46 | .codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 47 | .codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */ 48 | .codehilite .nv { color: #19177C } /* Name.Variable */ 49 | .codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 50 | .codehilite .w { color: #bbbbbb } /* Text.Whitespace */ 51 | .codehilite .mb { color: #666666 } /* Literal.Number.Bin */ 52 | .codehilite .mf { color: #666666 } /* Literal.Number.Float */ 53 | .codehilite .mh { color: #666666 } /* Literal.Number.Hex */ 54 | .codehilite .mi { color: #666666 } /* Literal.Number.Integer */ 55 | .codehilite .mo { color: #666666 } /* Literal.Number.Oct */ 56 | .codehilite .sa { color: #BA2121 } /* Literal.String.Affix */ 57 | .codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */ 58 | .codehilite .sc { color: #BA2121 } /* Literal.String.Char */ 59 | .codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */ 60 | .codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ 61 | .codehilite .s2 { color: #BA2121 } /* Literal.String.Double */ 62 | .codehilite .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ 63 | .codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */ 64 | .codehilite .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ 65 | .codehilite .sx { color: #008000 } /* Literal.String.Other */ 66 | .codehilite .sr { color: #A45A77 } /* Literal.String.Regex */ 67 | .codehilite .s1 { color: #BA2121 } /* Literal.String.Single */ 68 | .codehilite .ss { color: #19177C } /* Literal.String.Symbol */ 69 | .codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */ 70 | .codehilite .fm { color: #0000FF } /* Name.Function.Magic */ 71 | .codehilite .vc { color: #19177C } /* Name.Variable.Class */ 72 | .codehilite .vg { color: #19177C } /* Name.Variable.Global */ 73 | .codehilite .vi { color: #19177C } /* Name.Variable.Instance */ 74 | .codehilite .vm { color: #19177C } /* Name.Variable.Magic */ 75 | .codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */ 76 | -------------------------------------------------------------------------------- /doc/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/favicon.png -------------------------------------------------------------------------------- /doc/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /doc/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /doc/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /doc/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /doc/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /doc/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /doc/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /doc/js/MathJax-config/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/js/MathJax-config/.gitignore -------------------------------------------------------------------------------- /doc/lists/files.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | All Files – IO-Fortran-Library 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 63 |
64 | 65 |
66 |
67 |
68 |

Source Files

69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 |
FileDescription
array_printing_impl.f90
binary_io_impl.f90
file_io_impl.f90
gaussian_sampling_impl.f90
internal_io_impl.f90
io_fortran_lib_mod.f90
join_split_impl.f90
operators_impl.f90
randoms_mod.f90
string_methods_impl.f90
text_io_impl.f90
117 | 118 |
119 |
120 |
121 |
122 | 138 | 139 | 140 | 141 | 143 | 144 | 146 | 147 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /doc/lists/types.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | All Types – IO-Fortran-Library 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 63 |
64 | 65 |
66 |
67 |
68 |

Derived Types

69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
TypeLocationExtendsDescription
Stringio_fortran_libNone

A growable string type for advanced character handling and text I/O.

Read more…
80 | 81 |
82 |
83 |
84 |
85 | 101 | 102 | 103 | 104 | 106 | 107 | 109 | 110 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /doc/module/array_printing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | array_printing – IO-Fortran-Library 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 62 |
63 | 64 |
65 |
66 |

array_printing 67 | Submodule 68 | 69 |

70 |
71 |
72 |
73 | 87 |
88 |
89 | 95 |
96 |
97 |
98 | 103 | 104 |
105 | 106 |
107 |
108 | 134 | 135 |
136 | 137 |
138 |

This submodule provides module procedure implementations for the public interface aprint.

139 |
140 |

Uses

141 |
142 | 150 |
151 |
152 | 153 | 154 |
155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 |
167 |
168 | 169 |
170 |
171 | 187 | 188 | 189 | 190 | 192 | 193 | 195 | 196 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /doc/module/binary_io.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | binary_io – IO-Fortran-Library 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 62 |
63 | 64 |
65 |
66 |

binary_io 67 | Submodule 68 | 69 |

70 |
71 |
72 |
73 | 87 |
88 |
89 | 95 |
96 |
97 |
98 | 103 | 104 |
105 | 106 |
107 |
108 | 134 | 135 |
136 | 137 |
138 |

This submodule provides module procedure implementations for the private interfaces to_binary and 139 | from_binary.

140 |
141 |

Uses

142 |
143 | 151 |
152 |
153 | 154 | 155 |
156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 |
168 |
169 | 170 |
171 |
172 | 188 | 189 | 190 | 191 | 193 | 194 | 196 | 197 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /doc/module/file_io.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | file_io – IO-Fortran-Library 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 62 |
63 | 64 |
65 |
66 |

file_io 67 | Submodule 68 | 69 |

70 |
71 |
72 |
73 | 87 |
88 |
89 | 95 |
96 |
97 |
98 | 103 | 104 |
105 | 106 |
107 |
108 | 134 | 135 |
136 | 137 |
138 |

This submodule provides module procedure implementations for the public interfaces to_file and 139 | from_file.

140 |
141 |

Uses

142 |
143 | 151 |
152 |
153 | 154 | 155 |
156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 |
168 |
169 | 170 |
171 |
172 | 188 | 189 | 190 | 191 | 193 | 194 | 196 | 197 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /doc/module/join_split.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | join_split – IO-Fortran-Library 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 62 |
63 | 64 |
65 |
66 |

join_split 67 | Submodule 68 | 69 |

70 |
71 |
72 |
73 | 87 |
88 |
89 | 95 |
96 |
97 |
98 | 103 | 104 |
105 | 106 |
107 |
108 | 134 | 135 |
136 | 137 |
138 |

This submodule provides module procedure implementations for the public interfaces join and split.

139 |
140 |

Uses

141 |
142 | 150 |
151 |
152 | 153 | 154 |
155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 |
167 |
168 | 169 |
170 |
171 | 187 | 188 | 189 | 190 | 192 | 193 | 195 | 196 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /doc/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Search Results – IO-Fortran-Library 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 61 |
62 | 63 |
64 |
65 |
66 |

Search Results

67 |
68 |
69 |
70 | 82 | 87 |
88 |
89 | 105 | 106 | 107 | 108 | 110 | 111 | 113 | 114 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /doc/src/join_split_impl.f90: -------------------------------------------------------------------------------- 1 | submodule (io_fortran_lib) join_split 2 | !--------------------------------------------------------------------------------------------------------------------- 3 | !! This submodule provides module procedure implementations for the **public interfaces** `join` and `split`. 4 | !--------------------------------------------------------------------------------------------------------------------- 5 | implicit none (type, external) 6 | 7 | contains ! Procedure bodies for module subprograms <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>< 8 | 9 | module procedure join_char 10 | type(String) :: temp_String 11 | character(len=:), allocatable :: separator_ 12 | 13 | if ( .not. present(separator) ) then 14 | separator_ = SPACE 15 | else 16 | separator_ = separator 17 | end if 18 | 19 | temp_String = join(String(tokens), separator=separator_) 20 | 21 | if ( temp_String%len() < 1 ) then 22 | new = EMPTY_STR 23 | else 24 | new = temp_String%s 25 | end if 26 | end procedure join_char 27 | 28 | module procedure join_string 29 | type(String) :: token_pair(2) 30 | character(len=:), allocatable :: separator_ 31 | integer(i64) :: num_tokens 32 | 33 | num_tokens = size(tokens, kind=i64) 34 | 35 | if ( num_tokens == 1_i64 ) then 36 | if ( tokens(1_i64)%len64() < 1_i64 ) then 37 | new%s = EMPTY_STR; return 38 | else 39 | new%s = tokens(1_i64)%s; return 40 | end if 41 | end if 42 | 43 | if ( .not. present(separator) ) then 44 | separator_ = SPACE 45 | else 46 | separator_ = separator 47 | end if 48 | 49 | if ( num_tokens > 500_i64 ) then 50 | new = join(tokens=[ join(tokens(:num_tokens/2_i64), separator_), & 51 | join(tokens(1_i64+num_tokens/2_i64:), separator_) ], separator=separator_) 52 | else 53 | call new%join_base(tokens=tokens, separator=separator_) 54 | end if 55 | end procedure join_string 56 | 57 | module procedure split_char 58 | character(len=:), allocatable :: separator_ 59 | 60 | if ( .not. present(separator) ) then 61 | separator_ = SPACE 62 | else 63 | separator_ = separator 64 | end if 65 | 66 | tokens = split(String(substring), separator=separator_) 67 | end procedure split_char 68 | 69 | module procedure split_string 70 | character(len=:), allocatable :: separator_ 71 | integer(i64) :: substring_len, l, i 72 | integer :: sep_len, num_seps, sep, token, current 73 | 74 | substring_len=0_i64; l=0_i64; i=0_i64; sep_len=0; num_seps=0; sep=0; token=0; current=0 75 | 76 | substring_len = substring%len64() 77 | 78 | if ( substring_len < 1_i64 ) then 79 | allocate( tokens(1) ); tokens(1)%s = EMPTY_STR; return 80 | end if 81 | 82 | if ( .not. present(separator) ) then 83 | separator_ = SPACE 84 | else 85 | separator_ = separator 86 | end if 87 | 88 | sep_len = len(separator_) 89 | 90 | if ( sep_len == 0 ) then 91 | allocate( tokens(substring_len) ) 92 | do i = 1_i64, substring_len 93 | tokens(i)%s = substring%s(i:i) 94 | end do 95 | return 96 | end if 97 | 98 | num_seps = substring%count(match=separator_) 99 | 100 | if ( num_seps == 0 ) then 101 | allocate( tokens(1) ); tokens(1)%s = substring%s; return 102 | end if 103 | 104 | allocate( tokens(num_seps + 1) ) 105 | 106 | sep = iachar(separator_(1:1)) 107 | 108 | i = 1_i64; l = 1_i64; token = 1; positional_transfers: do 109 | current = iachar(substring%s(i:i)) 110 | 111 | if ( current /= sep ) then 112 | i = i + 1_i64; cycle 113 | end if 114 | 115 | if ( sep_len == 1 ) then 116 | tokens(token)%s = substring%s(l:i-1) 117 | if ( token == num_seps ) then 118 | tokens(num_seps+1)%s = substring%s(i+1:); return 119 | end if 120 | token = token + 1; i = i + 1_i64; l = i; cycle 121 | else 122 | if ( substring%s(i:i+sep_len-1) == separator_ ) then 123 | tokens(token)%s = substring%s(l:i-1) 124 | if ( token == num_seps ) then 125 | tokens(num_seps+1)%s = substring%s(i+sep_len:); return 126 | end if 127 | token = token + 1; i = i + sep_len; l = i; cycle 128 | else 129 | i = i + 1_i64; cycle 130 | end if 131 | end if 132 | end do positional_transfers 133 | end procedure split_string 134 | end submodule join_split 135 | -------------------------------------------------------------------------------- /doc/src/operators_impl.f90: -------------------------------------------------------------------------------- 1 | submodule (io_fortran_lib) operators 2 | !--------------------------------------------------------------------------------------------------------------------- 3 | !! This submodule provides module procedure implementations for the **public interfaces** `operator(//)`, 4 | !! `operator(+)`, `operator(-)`, `operator(**)`, `operator(==)`, and `operator(/=)`. 5 | !--------------------------------------------------------------------------------------------------------------------- 6 | implicit none (type, external) 7 | 8 | contains ! Procedure bodies for module subprograms <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>< 9 | 10 | module procedure string_concatenation 11 | if ( Stringl%len() < 1 ) then 12 | if ( Stringr%len() < 1 ) then 13 | new%s = EMPTY_STR; return 14 | else 15 | new%s = Stringr%s; return 16 | end if 17 | end if 18 | 19 | if ( Stringr%len() < 1 ) then 20 | new%s = Stringl%s; return 21 | end if 22 | 23 | new%s = Stringl%s//Stringr%s 24 | end procedure string_concatenation 25 | 26 | module procedure string_char_concatenation 27 | if ( Stringl%len() < 1 ) then 28 | if ( len(charsr) < 1 ) then 29 | new%s = EMPTY_STR; return 30 | else 31 | new%s = charsr; return 32 | end if 33 | end if 34 | 35 | if ( len(charsr) < 1 ) then 36 | new%s = Stringl%s; return 37 | end if 38 | 39 | new%s = Stringl%s//charsr 40 | end procedure string_char_concatenation 41 | 42 | module procedure char_string_concatenation 43 | if ( len(charsl) < 1 ) then 44 | if ( Stringr%len() < 1 ) then 45 | new%s = EMPTY_STR; return 46 | else 47 | new%s = Stringr%s; return 48 | end if 49 | end if 50 | 51 | if ( Stringr%len() < 1 ) then 52 | new%s = charsl; return 53 | end if 54 | 55 | new%s = charsl//Stringr%s 56 | end procedure char_string_concatenation 57 | 58 | module procedure char_concat_plus 59 | new = charsl//charsr 60 | end procedure char_concat_plus 61 | 62 | module procedure string_concat_plus 63 | if ( Stringl%len() < 1 ) then 64 | if ( Stringr%len() < 1 ) then 65 | new%s = EMPTY_STR; return 66 | else 67 | new%s = Stringr%s; return 68 | end if 69 | end if 70 | 71 | if ( Stringr%len() < 1 ) then 72 | new%s = Stringl%s; return 73 | end if 74 | 75 | new%s = Stringl%s//Stringr%s 76 | end procedure string_concat_plus 77 | 78 | module procedure string_char_concat_plus 79 | if ( Stringl%len() < 1 ) then 80 | if ( len(charsr) < 1 ) then 81 | new%s = EMPTY_STR; return 82 | else 83 | new%s = charsr; return 84 | end if 85 | end if 86 | 87 | if ( len(charsr) < 1 ) then 88 | new%s = Stringl%s; return 89 | end if 90 | 91 | new%s = Stringl%s//charsr 92 | end procedure string_char_concat_plus 93 | 94 | module procedure char_string_concat_plus 95 | if ( len(charsl) < 1 ) then 96 | if ( Stringr%len() < 1 ) then 97 | new%s = EMPTY_STR; return 98 | else 99 | new%s = Stringr%s; return 100 | end if 101 | end if 102 | 103 | if ( Stringr%len() < 1 ) then 104 | new%s = charsl; return 105 | end if 106 | 107 | new%s = charsl//Stringr%s 108 | end procedure char_string_concat_plus 109 | 110 | module procedure char_excision 111 | type(String) :: Stringl 112 | 113 | Stringl%s = charsl 114 | 115 | if ( Stringl%len() < 1 ) then 116 | new%s = EMPTY_STR; return 117 | end if 118 | 119 | if ( len(charsr) < 1 ) then 120 | new%s = Stringl%s; return 121 | end if 122 | 123 | new = Stringl%replace(match=charsr, substring=EMPTY_STR) 124 | end procedure char_excision 125 | 126 | module procedure string_excision 127 | if ( Stringl%len() < 1 ) then 128 | new%s = EMPTY_STR; return 129 | end if 130 | 131 | if ( Stringr%len() < 1 ) then 132 | new%s = Stringl%s; return 133 | end if 134 | 135 | new = Stringl%replace(match=Stringr%s, substring=EMPTY_STR) 136 | end procedure string_excision 137 | 138 | module procedure string_char_excision 139 | if ( Stringl%len() < 1 ) then 140 | new%s = EMPTY_STR; return 141 | end if 142 | 143 | if ( len(charsr) < 1 ) then 144 | new%s = Stringl%s; return 145 | end if 146 | 147 | new = Stringl%replace(match=charsr, substring=EMPTY_STR) 148 | end procedure string_char_excision 149 | 150 | module procedure char_string_excision 151 | type(String) :: Stringl 152 | 153 | Stringl%s = charsl 154 | 155 | if ( Stringl%len() < 1 ) then 156 | new%s = EMPTY_STR; return 157 | end if 158 | 159 | if ( Stringr%len() < 1 ) then 160 | new%s = Stringl%s; return 161 | end if 162 | 163 | new = Stringl%replace(match=Stringr%s, substring=EMPTY_STR) 164 | end procedure char_string_excision 165 | 166 | module procedure repeat_chars 167 | new = repeat(char_base, ncopies=ncopies) 168 | end procedure repeat_chars 169 | 170 | module procedure repeat_String 171 | if ( String_base%len() < 1 ) then 172 | new%s = EMPTY_STR; return 173 | end if 174 | 175 | new%s = repeat(String_base%s, ncopies=ncopies) 176 | end procedure repeat_String 177 | 178 | module procedure string_equivalence 179 | integer :: Stringl_len, Stringr_len 180 | 181 | Stringl_len = Stringl%len() 182 | Stringr_len = Stringr%len() 183 | 184 | if ( Stringl_len /= Stringr_len ) then 185 | equal = .false.; return 186 | end if 187 | 188 | if ( Stringl_len < 1 ) then 189 | equal = .true.; return 190 | end if 191 | 192 | equal = ( Stringl%s == Stringr%s ) 193 | end procedure string_equivalence 194 | 195 | module procedure string_char_equivalence 196 | integer :: Stringl_len, charsr_len 197 | 198 | Stringl_len = Stringl%len() 199 | charsr_len = len(charsr) 200 | 201 | if ( Stringl_len /= charsr_len ) then 202 | equal = .false.; return 203 | end if 204 | 205 | if ( Stringl_len < 1 ) then 206 | equal = .true.; return 207 | end if 208 | 209 | equal = ( Stringl%s == charsr ) 210 | end procedure string_char_equivalence 211 | 212 | module procedure char_string_equivalence 213 | integer :: charsl_len, Stringr_len 214 | 215 | charsl_len = len(charsl) 216 | Stringr_len = Stringr%len() 217 | 218 | if ( charsl_len /= Stringr_len ) then 219 | equal = .false.; return 220 | end if 221 | 222 | if ( charsl_len < 1 ) then 223 | equal = .true.; return 224 | end if 225 | 226 | equal = ( charsl == Stringr%s ) 227 | end procedure char_string_equivalence 228 | 229 | module procedure string_nonequivalence 230 | integer :: Stringl_len, Stringr_len 231 | 232 | Stringl_len = Stringl%len() 233 | Stringr_len = Stringr%len() 234 | 235 | if ( Stringl_len /= Stringr_len ) then 236 | unequal = .true.; return 237 | end if 238 | 239 | if ( Stringl_len < 1 ) then 240 | unequal = .false.; return 241 | end if 242 | 243 | unequal = ( Stringl%s /= Stringr%s ) 244 | end procedure string_nonequivalence 245 | 246 | module procedure string_char_nonequivalence 247 | integer :: Stringl_len, charsr_len 248 | 249 | Stringl_len = Stringl%len() 250 | charsr_len = len(charsr) 251 | 252 | if ( Stringl_len /= charsr_len ) then 253 | unequal = .true.; return 254 | end if 255 | 256 | if ( Stringl_len < 1 ) then 257 | unequal = .false.; return 258 | end if 259 | 260 | unequal = ( Stringl%s /= charsr ) 261 | end procedure string_char_nonequivalence 262 | 263 | module procedure char_string_nonequivalence 264 | integer :: charsl_len, Stringr_len 265 | 266 | charsl_len = len(charsl) 267 | Stringr_len = Stringr%len() 268 | 269 | if ( charsl_len /= Stringr_len ) then 270 | unequal = .true.; return 271 | end if 272 | 273 | if ( charsl_len < 1 ) then 274 | unequal = .false.; return 275 | end if 276 | 277 | unequal = ( charsl /= Stringr%s ) 278 | end procedure char_string_nonequivalence 279 | end submodule operators 280 | -------------------------------------------------------------------------------- /doc/src/randoms_mod.f90: -------------------------------------------------------------------------------- 1 | module randoms 2 | !--------------------------------------------------------------------------------------------------------------------- 3 | !! This module provides Gaussian sampling utility routines for use in unit testing. 4 | !--------------------------------------------------------------------------------------------------------------------- 5 | use, intrinsic :: iso_fortran_env, only: r128=>real128, r64=>real64, r32=>real32 6 | implicit none (type, external) 7 | private 8 | 9 | ! Public API list ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | public :: random_gauss 11 | 12 | ! Definitions and Interfaces ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 13 | 14 | interface gauss ! Submodule gaussian_sampling 15 | !------------------------------------------------------------------------------------------------------------------- 16 | !! Samples random numbers from the standard Normal (Gaussian) Distribution with the given mean and sigma. 17 | !! Uses the Acceptance-complement ratio from W. Hoermann and G. Derflinger. 18 | !! This is one of the fastest existing methods for generating normal random variables. 19 | !! 20 | !! REFERENCE: - W. Hoermann and G. Derflinger (1990): 21 | !! The ACR Method for generating normal random variables, 22 | !! OR Spektrum 12 (1990), 181-185. 23 | !! 24 | !! Implementation taken from 25 | !! UNURAN (c) 2000 W. Hoermann & J. Leydold, Institut f. Statistik, WU Wien 26 | !--------------------------------------------------------------------------------------------------------------- 27 | impure real(r128) module function gauss_r128(mu, sig) result(gauss_res) 28 | real(r128), intent(in) :: mu, sig 29 | end function gauss_r128 30 | impure real(r64) module function gauss_r64(mu, sig) result(gauss_res) 31 | real(r64), intent(in) :: mu, sig 32 | end function gauss_r64 33 | impure real(r32) module function gauss_r32(mu, sig) result(gauss_res) 34 | real(r32), intent(in) :: mu, sig 35 | end function gauss_r32 36 | end interface 37 | 38 | interface random_gauss ! Submodule gaussian_sampling 39 | !------------------------------------------------------------------------------------------------------------------- 40 | !! Applies `gauss` to whole arrays and scalars. 41 | !------------------------------------------------------------------------------------------------------------------- 42 | impure elemental module subroutine random_gauss_r128(x, mu, sig) 43 | real(r128), intent(inout) :: x 44 | real(r128), intent(in) :: mu, sig 45 | end subroutine random_gauss_r128 46 | impure elemental module subroutine random_gauss_r64(x, mu, sig) 47 | real(r64), intent(inout) :: x 48 | real(r64), intent(in) :: mu, sig 49 | end subroutine random_gauss_r64 50 | impure elemental module subroutine random_gauss_r32(x, mu, sig) 51 | real(r32), intent(inout) :: x 52 | real(r32), intent(in) :: mu, sig 53 | end subroutine random_gauss_r32 54 | end interface 55 | end module randoms 56 | -------------------------------------------------------------------------------- /doc/tipuesearch/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/tipuesearch/.DS_Store -------------------------------------------------------------------------------- /doc/tipuesearch/img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/tipuesearch/img/.DS_Store -------------------------------------------------------------------------------- /doc/tipuesearch/img/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/tipuesearch/img/loader.gif -------------------------------------------------------------------------------- /doc/tipuesearch/img/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acbbullock/IO-Fortran-Library/15bc3557c36adc174abbd9a077e0686dc11cc4af/doc/tipuesearch/img/search.png -------------------------------------------------------------------------------- /doc/tipuesearch/tipuesearch.css: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Tipue Search 4.0 4 | Copyright (c) 2014 Tipue 5 | Tipue Search is released under the MIT License 6 | http://www.tipue.com/search 7 | */ 8 | 9 | 10 | /* 11 | #tipue_search_input 12 | { 13 | font: 13px/1.6 'open sans', sans-serif; 14 | color: #333; 15 | padding: 12px 12px 12px 40px; 16 | width: 170px; 17 | border: 1px solid #e2e2e2; 18 | border-radius: 0; 19 | -moz-appearance: none; 20 | -webkit-appearance: none; 21 | box-shadow: none; 22 | outline: 0; 23 | margin: 0; 24 | background: #fff url('img/search.png') no-repeat 15px 15px; 25 | } 26 | */ 27 | 28 | #tipue_search_content 29 | { 30 | max-width: 650px; 31 | padding-top: 15px; 32 | margin: 0; 33 | } 34 | #tipue_search_loading 35 | { 36 | padding-top: 60px; 37 | background: #fff url('img/loader.gif') no-repeat left; 38 | } 39 | 40 | #tipue_search_warning_head 41 | { 42 | font: 300 15px/1.6 'Open Sans', sans-serif; 43 | color: #555; 44 | } 45 | #tipue_search_warning 46 | { 47 | font: 300 13px/1.6 'Open Sans', sans-serif; 48 | color: #333; 49 | margin: 7px 0; 50 | } 51 | #tipue_search_warning a 52 | { 53 | color: #36c; 54 | font-weight: 300; 55 | text-decoration: none; 56 | } 57 | #tipue_search_warning a:hover 58 | { 59 | color: #333; 60 | } 61 | #tipue_search_results_count 62 | { 63 | font: 300 13px/1.6 'Open Sans', sans-serif; 64 | color: #333; 65 | } 66 | .tipue_search_content_title 67 | { 68 | font: 300 25px/1.7 'Open Sans', sans-serif; 69 | text-rendering: optimizelegibility; 70 | margin-top: 23px; 71 | } 72 | .tipue_search_content_title a 73 | { 74 | color: #333; 75 | text-decoration: none; 76 | } 77 | .tipue_search_content_title a:hover 78 | { 79 | color: #555; 80 | } 81 | .tipue_search_content_url 82 | { 83 | font: 300 13px/1.7 'Open Sans', sans-serif; 84 | word-break: break-all; 85 | word-break: break-word; 86 | -webkit-hyphens: auto; 87 | -moz-hyphens: auto; 88 | hyphens: auto; 89 | } 90 | .tipue_search_content_url a 91 | { 92 | color: #06c; 93 | text-decoration: none; 94 | } 95 | .tipue_search_content_url a:hover 96 | { 97 | color: #333; 98 | } 99 | .tipue_search_content_text 100 | { 101 | font: 300 15px/1.6 'Open Sans', sans-serif; 102 | color: #555; 103 | word-break: break-all; 104 | word-break: break-word; 105 | -webkit-hyphens: auto; 106 | -moz-hyphens: auto; 107 | hyphens: auto; 108 | margin-top: 3px; 109 | } 110 | .h01 111 | { 112 | color: #333; 113 | font-weight: 400; 114 | } 115 | 116 | #tipue_search_foot 117 | { 118 | margin: 51px 0 21px 0; 119 | } 120 | #tipue_search_foot_boxes 121 | { 122 | padding: 0; 123 | margin: 0; 124 | font: 12px/1 'Open Sans', sans-serif; 125 | } 126 | #tipue_search_foot_boxes li 127 | { 128 | list-style: none; 129 | margin: 0; 130 | padding: 0; 131 | display: inline; 132 | } 133 | #tipue_search_foot_boxes li a 134 | { 135 | padding: 9px 15px 10px 15px; 136 | background-color: #f1f1f1; 137 | border: 1px solid #dcdcdc; 138 | border-radius: 1px; 139 | color: #333; 140 | margin-right: 7px; 141 | text-decoration: none; 142 | text-align: center; 143 | } 144 | #tipue_search_foot_boxes li.current 145 | { 146 | padding: 9px 15px 10px 15px; 147 | background: #fff; 148 | border: 1px solid #dcdcdc; 149 | border-radius: 1px; 150 | color: #333; 151 | margin-right: 7px; 152 | text-align: center; 153 | } 154 | #tipue_search_foot_boxes li a:hover 155 | { 156 | border: 1px solid #ccc; 157 | background-color: #f3f3f3; 158 | } 159 | -------------------------------------------------------------------------------- /doc/tipuesearch/tipuesearch.min.js: -------------------------------------------------------------------------------- 1 | (function($){$.fn.tipuesearch=function(options){var set=$.extend({"show":7,"newWindow":false,"showURL":true,"minimumLength":3,"descriptiveWords":25,"highlightTerms":true,"highlightEveryTerm":false,"mode":"static","liveDescription":"*","liveContent":"*","contentLocation":"tipuesearch/tipuesearch_content.json"},options);return this.each(function(){var tipuesearch_in={pages:[]};$.ajaxSetup({async:false});if(set.mode=="live")for(var i=0;i");var t_2=html.toLowerCase().indexOf("",t_1+7);if(t_1!=-1&&t_2!=-1)var tit=html.slice(t_1+7,t_2);else var tit="No title";tipuesearch_in.pages.push({"title":tit,"text":desc,"tags":cont,"loc":tipuesearch_pages[i]})});if(set.mode=="json")$.getJSON(set.contentLocation,function(json){tipuesearch_in=$.extend({},json)}); 3 | if(set.mode=="static")tipuesearch_in=$.extend({},tipuesearch);var tipue_search_w="";if(set.newWindow)tipue_search_w=' target="_blank"';function getURLP(name){return decodeURIComponent(((new RegExp("[?|&]"+name+"="+"([^&;]+?)(&|#|;|$)")).exec(location.search)||[,""])[1].replace(/\+/g,"%20"))||null}if(getURLP("q")){$("#tipue_search_input").val(getURLP("q"));getTipueSearch(0,true)}$(this).keyup(function(event){if(event.keyCode=="13")getTipueSearch(0,true)});function getTipueSearch(start,replace){$("#tipue_search_content").hide(); 4 | var out="";var results="";var show_replace=false;var show_stop=false;var standard=true;var c=0;found=new Array;var d=$("#tipue_search_input").val().toLowerCase();d=$.trim(d);if(d.match('^"')&&d.match('"$')||d.match("^'")&&d.match("'$"))standard=false;if(standard){var d_w=d.split(" ");d="";for(var i=0;i=set.minimumLength){if(standard){if(replace){var d_r=d;for(var i=0;i$1')}if(tipuesearch_in.pages[i].tags.search(pat)!= 7 | -1)score-=1E5-i;if(d_w[f].match("^-")){pat=new RegExp(d_w[f].substring(1),"i");if(tipuesearch_in.pages[i].title.search(pat)!=-1||tipuesearch_in.pages[i].text.search(pat)!=-1||tipuesearch_in.pages[i].tags.search(pat)!=-1)score=1E9}}if(score<1E9)found[c++]=score+"^"+tipuesearch_in.pages[i].title+"^"+s_t+"^"+tipuesearch_in.pages[i].loc}}else for(var i=0;i$1')}if(tipuesearch_in.pages[i].tags.search(pat)!=-1)score-=1E5-i;if(score<1E9)found[c++]=score+"^"+tipuesearch_in.pages[i].title+"^"+s_t+"^"+tipuesearch_in.pages[i].loc}if(c!=0){if(show_replace==1){out+='
Showing results for '+ 9 | d+"
";out+='
Search instead for '+d_r+"
"}if(c==1)out+='
1 result
';else{c_c=c.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",");out+='
'+c_c+" results
"}found.sort();var l_o=0;for(var i=0;i=start&&l_o"+fo[1]+"";if(set.showURL)out+='";var t=fo[2];var t_d="";var t_w=t.split(" ");if(t_w.length"}l_o++}if(c>set.show){var pages=Math.ceil(c/set.show);var page=start/set.show; 11 | out+='
    ';if(start>0)out+='
  • Prev
  • ';if(page<=2){var p_b=pages;if(pages>3)p_b=3;for(var f=0;f'+(f+1)+"";else out+='
  • '+(f+1)+"
  • "}else{var p_b=page+2;if(p_b>pages)p_b=pages;for(var f=page- 12 | 1;f'+(f+1)+"";else out+='
  • '+(f+1)+"
  • "}if(page+1!=pages)out+='
  • Next
  • ';out+="
"}}else out+='
Nothing found
'}else if(show_stop)out+='
Nothing found
Common words are largely ignored
'; 13 | else{out+='
Search too short
';if(set.minimumLength==1)out+='
Should be one character or more
';else out+='
Should be '+set.minimumLength+" characters or more
"}$("#tipue_search_content").html(out);$("#tipue_search_content").slideDown(200);$("#tipue_search_replaced").click(function(){getTipueSearch(0,false)});$(".tipue_search_foot_box").click(function(){var id_v=$(this).attr("id");var id_a= 14 | id_v.split("_");getTipueSearch(parseInt(id_a[0]),id_a[1])})}})}})(jQuery); 15 | -------------------------------------------------------------------------------- /doc/tipuesearch/tipuesearch_set.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Tipue Search 4.0 4 | Copyright (c) 2014 Tipue 5 | Tipue Search is released under the MIT License 6 | http://www.tipue.com/search 7 | */ 8 | 9 | 10 | var tipuesearch_stop_words = ["and", "be", "by", "do", "for", "he", "how", "if", "is", "it", "my", "not", "of", "or", "the", "to", "up", "what", "when", "use", "who", "she", "my", "his", "her"]; 11 | 12 | var tipuesearch_replace = {"words": [ 13 | {"word": "tipua", "replace_with": "tipue"}, 14 | {"word": "javscript", "replace_with": "javascript"} 15 | ]}; 16 | 17 | var tipuesearch_stem = {"words": [ 18 | {"word": "e-mail", "stem": "email"}, 19 | {"word": "javascript", "stem": "script"}, 20 | {"word": "procedure", "stem": "subroutine"}, 21 | {"word": "procedure", "stem": "function"} 22 | ]}; 23 | 24 | -------------------------------------------------------------------------------- /doc_API_design/Examples/benchmark.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Benchmarking 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## Big data I/O 7 | 8 | The IO-Fortran-Library is capable of reading and writing very large 9 | text files with efficiency, even those whose data size exceeds the 10 | 2,147,483,647 byte upper limit of the 32-bit signed integer. A program 11 | is provided in `/test/benchmark.f90` for benchmarking the major 12 | internal and external text I/O routines of the IO-Fortran-Library: 13 | 14 | ```fortran 15 | program main 16 | use, intrinsic :: iso_fortran_env, only: int64, rk=>real32, dp=>real64, compiler_version, compiler_options 17 | use randoms, only: random_gauss 18 | use io_fortran_lib, only: String, cast, str, LF, operator(+) 19 | implicit none (type, external) 20 | 21 | type(String) :: csv 22 | type(String), allocatable :: cells(:,:) 23 | 24 | integer(int64) :: t1, t2 25 | real(dp) :: wall_time, rate 26 | 27 | integer, parameter :: n = 15000 28 | real(rk), allocatable :: x(:,:), y(:,:) 29 | 30 | allocate( x(n,n), cells(n,n) ); call random_gauss(x, 0e0_rk, 1.0_rk) 31 | write(*,"(a)") "Compiler version: " + compiler_version() 32 | write(*,"(a)") "Compiler options: " + compiler_options() + LF 33 | 34 | call system_clock(t1) 35 | call cast(x, into=cells, fmt="z") 36 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 37 | 38 | write(*,"(a)") "Wall time for cast: " + str(wall_time, fmt="f", decimals=3) + " s" 39 | write(*,"(a)") "Number of string conversions/second: " + str(nint(size(x)/wall_time)) + LF 40 | 41 | call system_clock(t1) 42 | call csv%write_file(cells, file="bigx.csv") 43 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 44 | 45 | write(*,"(a)") "Wall time for write_file: " + str(wall_time, fmt="f", decimals=3) + " s" 46 | write(*,"(a)") "Estimated file size: " + str(csv%len64()/1e9, fmt="f", decimals=6) + " GB" + LF 47 | 48 | call system_clock(t1) 49 | call csv%read_file("bigx.csv", cell_array=cells) 50 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 51 | 52 | write(*,"(a)") "Wall time for read_file: " + str(wall_time, fmt="f", decimals=3) + " s" + LF 53 | 54 | call csv%empty(); allocate( y(n,n) ) 55 | 56 | call system_clock(t1) 57 | call cast(cells, into=y, fmt="z") 58 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 59 | 60 | write(*,"(a)") "Wall time for cast: " + str(wall_time, fmt="f", decimals=3) + " s" 61 | write(*,"(a)") "Number of string casts/second: " + str(nint(size(x)/wall_time)) 62 | write(*,"(a,l)") "Data is exact match: ", all(x == y) 63 | end program main 64 | ``` 65 | 66 | Here, we populate an `n`-by-`n` single-precision array `x` with samples 67 | from the standard Gaussian distribution and convert each to a 68 | hexadecimal string to populate a cell array, write the cell array to a 69 | text file `"bigx.csv"`, read the file back into the program to 70 | re-populate the cell array, then finally cast the cell data into `y` 71 | and compare with `x` to observe an exact match. For `n = 15000`, the 72 | resulting csv file size is `2.47 GB`. 73 | 74 | @note With the Intel Fortran compiler `ifx`, we must specify 75 | `-heap-arrays 0` to avoid a segmentation fault when reading a file of 76 | this size, as noted in [compiler-dependent 77 | behavior](../UserInfo/compilers.html). 78 | 79 | For a more extreme example, consider the following program to write 80 | every 32-bit integer as a hexadecimal string to a text file `int32.txt`: 81 | 82 | ```fortran 83 | program main 84 | use, intrinsic :: iso_fortran_env, only: i64=>int64, dp=>real64 85 | use io_fortran_lib, only: String, str, operator(+) 86 | implicit none (type, external) 87 | 88 | integer, parameter :: largest = huge(0), smallest = -largest - 1, nsteps = 128 89 | integer, parameter :: step_size = int( (int(largest,i64) - int(smallest,i64) )/int(nsteps,i64)) 90 | 91 | type(String) :: csv 92 | type(String), allocatable, target :: cells(:,:) 93 | type(String), pointer, contiguous :: cells_p(:,:) 94 | 95 | integer :: lower, upper, i, j 96 | 97 | integer(i64) :: t1, t2, total_length 98 | real(dp) :: wall_time, rate 99 | 100 | write(*,"(a)") "Writing integers from " + str(smallest) + " to " + str(largest) 101 | 102 | allocate( cells(step_size, 2) ) 103 | total_length = 0_i64 104 | call system_clock(t1) 105 | 106 | do j = 1, nsteps 107 | lower = smallest + (j-1)*step_size 108 | upper = lower + step_size - 1 109 | 110 | cells_p(lower:upper,1:2) => cells(:,:) 111 | 112 | do concurrent ( i = lower:upper ) 113 | cells_p(i,1) = String(i, fmt="i") 114 | cells_p(i,2) = String(i, fmt="z") 115 | end do 116 | 117 | call csv%write_file(cells_p, file="int32.csv", append=.true.) 118 | total_length = total_length + csv%len64() 119 | 120 | write(*,"(a)") "File length: " + str(total_length/1e9, fmt="f", decimals=3) + " GB in cycle " + str(j) 121 | end do 122 | 123 | lower = upper + 1; upper = largest 124 | nullify(cells_p); deallocate(cells); allocate( cells(lower:upper,2) ) 125 | 126 | do concurrent ( i = lower:upper ) 127 | cells(i,1) = String(i, fmt="i") 128 | cells(i,2) = String(i, fmt="z") 129 | end do 130 | 131 | call csv%write_file(cells, file="int32.csv", append=.true.) 132 | total_length = total_length + csv%len64() 133 | 134 | write(*,"(a)") "File length: " + str(total_length/1e9, fmt="f", decimals=3) + " GB" 135 | 136 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,dp)/rate 137 | write(*,"(a)") "Total time for write: " + str(wall_time/60, fmt="f", decimals=3) + " minutes" 138 | end program main 139 | ``` 140 | 141 | The resulting file size is `94.128 GB`. 142 | -------------------------------------------------------------------------------- /doc_API_design/Examples/binary.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Handling dat files 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## Binary file I/O 7 | 8 | The routines [to_file](../Ref/to_file.html) and 9 | [from_file](../Ref/from_file.html) are used for writing numeric arrays 10 | to binary files with the extension `.dat` or `.bin`. 11 | 12 | The following program demonstrates the use of `to_file` and `from_file` 13 | for writing `real` data of rank `5` to a `.dat` file, reading the file 14 | back into the program, and testing for exact equality to ensure that 15 | there has been no loss in precision: 16 | 17 | ```fortran 18 | program main 19 | use io_fortran_lib, only: to_file, from_file 20 | implicit none (type, external) 21 | 22 | real :: x(20,20,20,20,20) 23 | real, allocatable :: x_dat(:,:,:,:,:) 24 | 25 | call random_number(x) 26 | 27 | call to_file(x, file="x.dat") 28 | 29 | call from_file("x.dat", into=x_dat, data_shape=shape(x)) 30 | 31 | write(*,*) "x == x_dat : ", all(x == x_dat) 32 | end program main 33 | ``` 34 | 35 | @warning Reading into arrays of a different `kind` than the array that 36 | was written will invalidate the data. Always make sure the `kind` is 37 | matching for binary I/O. 38 | 39 | TIP: The shape of an array may be written to a csv file so that the 40 | value of `data_shape` can be read into the program before reading in 41 | the main array with the corresponding value. The following program 42 | demonstrates the above tip: 43 | 44 | ```fortran 45 | program main 46 | use io_fortran_lib, only: to_file, from_file 47 | implicit none (type, external) 48 | 49 | real :: x(20,20,20,20,20) 50 | real, allocatable :: x_dat(:,:,:,:,:) 51 | integer, allocatable :: x_shape(:) 52 | 53 | call random_number(x) 54 | 55 | call to_file(x, file="x.dat") 56 | call to_file(shape(x), file="x_shape.csv") 57 | 58 | call from_file("x_shape.csv", into=x_shape) 59 | call from_file("x.dat", into=x_dat, data_shape=x_shape) 60 | 61 | write(*,*) "x == x_dat : ", all(x == x_dat) 62 | end program main 63 | ``` 64 | -------------------------------------------------------------------------------- /doc_API_design/Examples/csv.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Handling csv files 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## Basic csv file I/O 7 | 8 | The routines [to_file](../Ref/to_file.html) and 9 | [from_file](../Ref/from_file.html) are the preferred method for 10 | handling I/O for numeric data of uniform type and format. Typical use 11 | cases involve writing whole arrays to file and reading files of uniform 12 | type and format directly into an array of numeric type. 13 | 14 | The following program demonstrates the use of `to_file` and `from_file` 15 | for writing an array of `real` data to a csv file in each possible 16 | [text format](../UserInfo/text-fmts.html), reading each file back into 17 | the program, and testing for exact equality to ensure that there has 18 | been no loss in precision: 19 | 20 | ```fortran 21 | program main 22 | use io_fortran_lib, only: to_file, from_file 23 | implicit none (type, external) 24 | 25 | real :: x(1000,20) 26 | real, allocatable :: x_e(:,:), x_f(:,:), x_z(:,:) 27 | 28 | call random_number(x) 29 | 30 | call to_file(x, file="x_e.csv", header=["x"], fmt="e") 31 | call to_file(x, file="x_f.csv", header=["x"], fmt="f") 32 | call to_file(x, file="x_z.csv", header=["x"], fmt="z") 33 | 34 | call from_file("x_e.csv", into=x_e, header=.true., fmt="e") 35 | call from_file("x_f.csv", into=x_f, header=.true., fmt="f") 36 | call from_file("x_z.csv", into=x_z, header=.true., fmt="z") 37 | 38 | write(*,*) "x == x_e : ", all(x == x_e) 39 | write(*,*) "x == x_f : ", all(x == x_f) 40 | write(*,*) "x == x_z : ", all(x == x_z) 41 | end program main 42 | ``` 43 | 44 | Here we use the simple header `header=["x"]`, which produces a header 45 | of the form: 46 | 47 | ```text 48 | x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 49 | ``` 50 | 51 | Note that the default value for `header` when reading is `.false.`. If 52 | a header is actually present, the output array will have an extra row 53 | with default initialized values. 54 | 55 | @warning Default text format for writing and reading is `"e"` for data 56 | of type `real` or `complex`, and `"i"` for data of type `integer`. 57 | Attempting to read data with a format that does not correspond to the 58 | format in the file may result in an I/O syntax error. 59 | 60 | @note Reading into arrays of a different `kind` than the array that was 61 | written is a type conversion, and will fail the equality test. 62 | 63 | ## Advanced csv file I/O 64 | 65 | The routines [write_file](../Ref/String-methods.html#write_file) and 66 | [read_file](../Ref/String-methods.html#read_file) are the preferred 67 | method for handling I/O for general text files. Typical use cases 68 | involve writing cell arrays of type `String` to delimited files, and 69 | reading delimited files into a cell array. For reading and writing 70 | non-delimited text files, one would use `read_file` without the 71 | `cell_array` argument and [echo](../Ref/echo.html). 72 | 73 | The same program as above can be recast in an object-oriented fashion 74 | that is generalizable for processing data of mixed type: 75 | 76 | ```fortran 77 | program main 78 | use io_fortran_lib, only: String, str, cast 79 | implicit none (type, external) 80 | 81 | type(String) :: csv 82 | type(String), allocatable :: cells(:,:) 83 | 84 | real :: x(1000,20) 85 | real, allocatable :: x_e(:,:), x_f(:,:), x_z(:,:) 86 | integer :: i 87 | 88 | call random_number(x); allocate( cells(1001,20) ); cells(1,:) = [(String("x"//str(i)), i = 1, 20)] 89 | 90 | call cast(x, into=cells(2:,:), fmt="e"); call csv%write_file(cells, file="x_e.csv") 91 | call cast(x, into=cells(2:,:), fmt="f"); call csv%write_file(cells, file="x_f.csv") 92 | call cast(x, into=cells(2:,:), fmt="z"); call csv%write_file(cells, file="x_z.csv") 93 | 94 | allocate( x_e, x_f, x_z, mold=x ) 95 | call csv%read_file("x_e.csv", cell_array=cells); call cells(2:,:)%cast(into=x_e, fmt="e") 96 | call csv%read_file("x_f.csv", cell_array=cells); call cells(2:,:)%cast(into=x_f, fmt="f") 97 | call csv%read_file("x_z.csv", cell_array=cells); call cells(2:,:)%cast(into=x_z, fmt="z") 98 | 99 | write(*,*) "x == x_e : ", all(x == x_e) 100 | write(*,*) "x == x_f : ", all(x == x_f) 101 | write(*,*) "x == x_z : ", all(x == x_z) 102 | end program main 103 | ``` 104 | 105 | Here, we construct the same header as before with the implicit loop 106 | 107 | ```fortran 108 | cells(1,:) = [(String("x"//str(i)), i = 1, 20)] 109 | ``` 110 | 111 | and then construct the remainder of the cell array `cells` with an 112 | elemental cast `call cast(x, into=cells(2:,:), fmt)` before writing the 113 | array to a csv file. We then read the files back into `csv` and output 114 | the cells into `cells` (which is reallocated internally). Note that 115 | when casting the cell data into numeric arrays, we must pre-allocate 116 | the output arrays due to the restrictions on `intent(out)` arguments 117 | of `elemental` procedures. 118 | 119 | @note One may optionally specify the arguments of `row_separator` and 120 | `column_separator` when writing and reading text files with 121 | [write_file](../Ref/String-methods.html#write_file) and 122 | [read_file](../Ref/String-methods.html#read_file). The default 123 | `row_separator` is `LF`, and the default `column_separator` is `","`. 124 | 125 | @warning When reading files with `CRLF` line endings, be sure to 126 | specify `row_separator=CR//LF` or pre-process the file to `LF`. Trying 127 | to cast data with a hidden `CR` character may result in an I/O syntax 128 | error. 129 | 130 | For a slightly more advanced example, consider the following program 131 | to read in and cast the data of mixed type contained in the example 132 | data `/data/ancestry_comp.csv`: 133 | 134 | ```fortran 135 | program main 136 | use, intrinsic :: iso_fortran_env, only: int8, int64 137 | use io_fortran_lib, only: String, cast, CR, LF, operator(+), operator(-) 138 | implicit none (type, external) 139 | 140 | type(String) :: csv 141 | type(String), allocatable :: cells(:,:) 142 | 143 | integer(int8), allocatable :: copy(:), chromosome(:) 144 | integer(int64), allocatable :: start_point(:), end_point(:) 145 | integer :: nrows 146 | 147 | call csv%read_file("./data/ancestry_comp.csv", cell_array=cells, row_separator=CR+LF) 148 | write(*,*) csv 149 | 150 | nrows = size(cells, dim=1) - 1 151 | 152 | allocate( copy(nrows), chromosome(nrows), start_point(nrows), end_point(nrows) ) 153 | 154 | call cells(2:,2)%cast(into=copy) 155 | call cast(cells(2:,3)%replace("X","0") - "chr", into=chromosome) 156 | call cells(2:,4)%cast(into=start_point) 157 | call cells(2:,5)%cast(into=end_point) 158 | end program main 159 | ``` 160 | 161 | Here, `file` is a relative path, and we use the extended operator `+` 162 | for [concatenation](../Ref/operators.html#concatenation) in the 163 | `character` expression `CR+LF`. We then allocate data arrays and cast 164 | each column into respective arrays. Note that we must use 165 | [cast](../Ref/cast.html) as a standalone subroutine to accept the 166 | `String`-valued expression 167 | 168 | ```fortran 169 | cells(2:,3)%replace("X","0") - "chr" 170 | ``` 171 | 172 | which first calls [replace](../Ref/String-methods.html#replace) to 173 | return an elemental copy of the given cells in which all instances of 174 | `X` have been replaced with `0`, and then calls the 175 | [excision operator](../Ref/operators.html#excision) `-` to remove all 176 | instances of `"chr"` elementally. The output of the `String` expression 177 | contains numeric characters only, which are then casted to the array 178 | `chromosome`. 179 | 180 | @note In general, for other 181 | [text file extensions](../UserInfo/file-ext.html), one would specify 182 | the `column_separator` associated with the given file. For instance, 183 | one would specify `column_separator=TAB` for the file formats `.bed`, 184 | `.gff`, and `.gtf`. 185 | -------------------------------------------------------------------------------- /doc_API_design/Examples/exome.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: NGS Human Core Exome Panel 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## Handling genomic data 7 | 8 | Fortran can serve as a valuable tool for heavy numerical calculations 9 | in Next Generation Sequencing (NGS) analysis, which may often involve 10 | reading and writing many large `.bed` files in succession. The 11 | IO-Fortran-Library is optimized for both performance and memory 12 | consumption when reading and writing large text files, streamlining 13 | performance for Fortran bioinformatics applications. 14 | 15 | To demonstrate the speed of file I/O, the following program reads the 16 | Twist Human Core Exome target `.bed` file for hg38 obtained from 17 | [Twist Bioscience](https://www.twistbioscience.com/resources/data-files/ngs-human-core-exome-panel-bed-file) 18 | into a cell array and then writes the cell array to a new file in a 19 | round-trip, comparing the two files for an exact match and providing 20 | the total time elapsed: 21 | 22 | ```fortran 23 | program main 24 | use, intrinsic :: iso_fortran_env, only: int64, real64, compiler_version 25 | use io_fortran_lib, only: String, str, TAB, operator(+), operator(==) 26 | implicit none (type, external) 27 | 28 | type(String) :: hg38, hg38_new 29 | type(String), allocatable :: cells(:,:) 30 | 31 | integer(int64) :: t1, t2 32 | real(real64) :: wall_time, rate 33 | 34 | call system_clock(t1) 35 | 36 | call hg38%read_file("./data/hg38.bed", cell_array=cells, column_separator=TAB) 37 | call hg38_new%write_file(cells, "./data/hg38_new.bed", column_separator=TAB) 38 | 39 | call system_clock(t2, count_rate=rate); wall_time = real(t2-t1,real64)/rate 40 | 41 | write(*,"(a,l)") "New file and original are exact match: ", hg38_new == hg38 42 | write(*,"(a)") "Wall time: " + str(wall_time, fmt="f", decimals=3) + " s " + & 43 | 'using compiler: "' + compiler_version() + '".' 44 | end program main 45 | ``` 46 | 47 | The file `hg38.bed` is provided locally in `/data` and contains 48 | `192262` lines of `TAB`-delimited data. 49 | 50 | @note With the Intel Fortran compiler `ifx`, we may need to specify 51 | `-heap-arrays 0` to avoid a segmentation fault when reading a file of 52 | this size, as noted in [compiler-dependent 53 | behavior](../UserInfo/compilers.html). 54 | -------------------------------------------------------------------------------- /doc_API_design/Examples/fizzbuzz.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: FizzBuzz 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## Division game 7 | 8 | The following program demonstrates the use of 9 | [aprint](../Ref/aprint.html) for printing an array of 10 | [Strings](../../type/string.html) containing values of the first 100 11 | [FizzBuzz](https://en.wikipedia.org/wiki/Fizz_buzz) numbers: 12 | 13 | ```fortran 14 | program main 15 | use io_fortran_lib, only: String, aprint 16 | implicit none (type, external) 17 | 18 | integer, allocatable :: nums(:) 19 | integer :: i 20 | 21 | nums = [(i, i = 1, 100)] 22 | 23 | call aprint( FizzBuzz(nums) ) 24 | 25 | contains 26 | pure elemental type(String) function FizzBuzz(number) result(res) 27 | integer, intent(in) :: number 28 | 29 | if ( mod(number,5) /= 0 ) then 30 | if ( mod(number,3) /= 0 ) then 31 | res = String(number) 32 | else 33 | res = String("fizz") 34 | end if 35 | else 36 | if ( mod(number,3) /= 0 ) then 37 | res = String("buzz") 38 | else 39 | res = String("fizzbuzz") 40 | end if 41 | end if 42 | end function FizzBuzz 43 | end program main 44 | ``` 45 | -------------------------------------------------------------------------------- /doc_API_design/Examples/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tutorials 3 | author: Austin C Bullock 4 | ordered_subpage: csv.md 5 | ordered_subpage: binary.md 6 | ordered_subpage: logging.md 7 | ordered_subpage: manipulations.md 8 | ordered_subpage: fizzbuzz.md 9 | ordered_subpage: exome.md 10 | ordered_subpage: benchmark.md 11 | --- 12 | 13 | The following subsections provide example programs for common use cases 14 | of the public [interfaces](../../lists/procedures.html): 15 | 16 | * [Handling csv files](csv.html) 17 | * [Handling dat files](binary.html) 18 | * [Handling log files](logging.html) 19 | * [String manipulation](manipulations.html) 20 | * [FizzBuzz](fizzbuzz.html) 21 | * [NGS Human Core Exome Panel](exome.html) 22 | * [Benchmarking](benchmark.html) 23 | -------------------------------------------------------------------------------- /doc_API_design/Examples/logging.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Handling log files 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## Log file I/O 7 | 8 | The routine [echo](../Ref/echo.html) is the preferred method for 9 | writing general text data to a log file. 10 | 11 | The following program demonstrates a simple use of `echo` for writing 12 | messages to a log file: 13 | 14 | ```fortran 15 | program main 16 | use io_fortran_lib, only: echo, str, LF 17 | implicit none (type, external) 18 | 19 | character(len=:), allocatable :: logfile, logmsg 20 | character(len=10) :: date, time 21 | integer :: errstat 22 | 23 | call date_and_time(date=date, time=time) 24 | logfile = "logfile_main_"//trim(adjustl(date))//"_"//time//".log" 25 | 26 | logmsg = "PROGRAM MAIN - BEGINNING EXECUTION" 27 | call echo(logmsg//LF//repeat("-", ncopies=len(logmsg)), file=logfile) 28 | 29 | ! ... 30 | 31 | logmsg = "All is good so far..." 32 | call echo(logmsg, logfile) 33 | 34 | read(*,*) errstat 35 | 36 | if ( errstat /= 0 ) then 37 | logmsg = "Process has non-zero exit status: "//str(errstat)//LF//"Stopping..." 38 | call echo(logmsg, logfile) 39 | error stop logmsg 40 | end if 41 | 42 | logmsg = "All processes have executed successfully." 43 | call echo(logmsg, logfile) 44 | end program main 45 | ``` 46 | 47 | Depending on style, one may wish to accumulate log messages into a 48 | `String` and then call the type-bound procedure 49 | [echo](../Ref/String-methods.html#echo) conditionally: 50 | 51 | ```fortran 52 | program main 53 | use io_fortran_lib, only: String, str, LF, operator(+), operator(**) 54 | implicit none (type, external) 55 | 56 | type(String) :: logmsg 57 | character(len=:), allocatable :: logfile 58 | character(len=10) :: date, time 59 | integer :: errstat 60 | 61 | call date_and_time(date=date, time=time) 62 | logfile = "logfile_main_" + trim(adjustl(date)) + "_" + time + ".log" 63 | 64 | logmsg = String("PROGRAM MAIN - BEGINNING EXECUTION") 65 | call logmsg%push(LF + "-"**logmsg%len() + LF) 66 | 67 | ! ... 68 | 69 | call logmsg%push("All is good so far..." + LF) 70 | 71 | read(*,*) errstat 72 | 73 | if ( errstat /= 0 ) then 74 | call logmsg%push("Process has non-zero exit status: " + str(errstat) + LF + "Stopping...") 75 | call logmsg%echo(logfile) 76 | error stop logmsg%as_str() 77 | end if 78 | 79 | call logmsg%push("All processes have executed successfully.") 80 | call logmsg%echo(logfile) 81 | end program main 82 | ``` 83 | 84 | Here, the `error stop` will dump the entire contents of `logmsg` to 85 | stdout. We also take advantage of the 86 | [operators](../Ref/operators.html) `+` and `**` for concatenation and 87 | repetition. 88 | -------------------------------------------------------------------------------- /doc_API_design/Examples/manipulations.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: String manipulations 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## String Queries 7 | 8 | Sometimes it is useful or necessary to write execution conditions based 9 | on compiler vendors or version, such as when a certain compiler version 10 | has a known bug or when a piece of code is required for a specific 11 | compiler. The following program shows how one may use the 12 | [count](../Ref/String-methods.html#count) procedure to determine a 13 | compiler and version at runtime: 14 | 15 | ```fortran 16 | program main 17 | use, intrinsic :: iso_fortran_env, only: compiler_version 18 | use io_fortran_lib, only: String 19 | implicit none (type, external) 20 | 21 | type(String) :: compiler 22 | logical :: GCC, GCC_SUPPORTED, INTEL, INTEL_SUPPORTED 23 | 24 | compiler = String(compiler_version()) 25 | 26 | GCC = compiler%count(match="GCC") > 0 27 | GCC_SUPPORTED = GCC .and. ( compiler%count(match="13.2.1") > 0 ) 28 | 29 | INTEL = compiler%count(match="Intel") > 0 30 | INTEL_SUPPORTED = INTEL .and. ( compiler%count(match="2024.0.2") > 0 ) 31 | 32 | write(*,*) compiler 33 | 34 | if ( GCC ) then 35 | if ( GCC_SUPPORTED ) then 36 | write(*,*) "This GNU Fortran Compiler version is known to be supported." 37 | else 38 | write(*,*) "This GNU Fortran Compiler version may not be supported." 39 | end if 40 | else if ( INTEL ) then 41 | if ( INTEL_SUPPORTED ) then 42 | write(*,*) "This Intel Fortran Compiler version is known to be supported." 43 | else 44 | write(*,*) "This Intel Fortran Compiler version may not be supported." 45 | end if 46 | end if 47 | end program main 48 | ``` 49 | 50 | ## Dynamic string manipulation 51 | 52 | Using [operator](../Ref/operators.html) techniques and 53 | [string methods](../Ref/String-methods.html), we may easily perform 54 | complex string manipulations during run time. 55 | 56 | The following program demonstrates the use of the 57 | [String](../../type/string.html) type and some 58 | [type-bound procedures](../Ref/String-methods.html) for manipulating a 59 | timestamp: 60 | 61 | ```fortran 62 | program main 63 | use io_fortran_lib, only: String, join, split, LF, operator(+), operator(-) 64 | implicit none (type, external) 65 | 66 | type(String) :: time_stamp, new_time_stamp 67 | type(String), allocatable :: tokens(:) 68 | 69 | character(len=10) :: date, time 70 | 71 | call date_and_time(date=date, time=time) 72 | 73 | time_stamp = String("Date : " + date + LF + "Time : " + time) 74 | 75 | write(*,"(a)") "ORIGINAL TIME STAMP:" + LF + time_stamp%as_str() + LF 76 | 77 | tokens = split(time_stamp, separator=LF) - "Date : " - "Time : " + [" : Date", " : Time"] 78 | new_time_stamp = join(tokens, separator=" | ") 79 | 80 | write(*,"(a)") "RECONSTRUCTED TIME STAMP:" + LF + new_time_stamp%as_str() 81 | end program main 82 | ``` 83 | 84 | This program produces the following sample output: 85 | 86 | ```text 87 | ORIGINAL TIME STAMP: 88 | Date : 20240316 89 | Time : 163641.569 90 | 91 | RECONSTRUCTED TIME STAMP: 92 | 20240316 : Date | 163641.569 : Time 93 | ``` 94 | -------------------------------------------------------------------------------- /doc_API_design/Ref/String.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: String 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [interface String](../../interface/string.html) 7 | 8 | *Description*: Function for returning a 9 | [String](../../type/string.html) representation of numbers. 10 | 11 | To return the empty `String`, use no arguments: 12 | 13 | ```fortran 14 | result = String() 15 | ``` 16 | 17 | For `x` a scalar or array of any rank and of type `character`: 18 | 19 | ```fortran 20 | result = String(x) 21 | ``` 22 | 23 | This is for `character` to `String` conversion. 24 | 25 | For `x` a scalar or array of any rank and of type `integer`: 26 | 27 | ```fortran 28 | result = String(x [, fmt]) 29 | ``` 30 | 31 | * `fmt` is `optional`, may be one of `INT_FMTS` 32 | 33 | For `x` a scalar or array of any rank and of type `real`: 34 | 35 | ```fortran 36 | result = String(x [, locale, fmt, decimals]) 37 | ``` 38 | 39 | * `locale` is `optional`, may be one of `LOCALES` 40 | * `fmt` is `optional`, may be one of `REAL_FMTS` 41 | * `decimals` is `optional` and of type `integer` 42 | 43 | For `x` a scalar or array of any rank and of type `complex`: 44 | 45 | ```fortran 46 | result = String(x [, locale, fmt, decimals, im]) 47 | ``` 48 | 49 | * `locale` is `optional`, may be one of `LOCALES` 50 | * `fmt` is `optional`, may be one of `REAL_FMTS` 51 | * `decimals` is `optional` and of type `integer` 52 | * `im` is `optional` and of type `character(len=*)` 53 | 54 | @note Unlike [str](str.html), which takes scalar arguments only and 55 | returns a `character`, `String` operates elementally and returns a 56 | [String](../../type/string.html). 57 | 58 | ### Optional Arguments 59 | 60 | Integer formats (default is `"i"`): 61 | 62 | ```fortran 63 | INT_FMTS = [ "i", "z" ] 64 | ``` 65 | 66 | Real formats (default is `"e"`): 67 | 68 | ```fortran 69 | REAL_FMTS = [ "e", "f", "z" ] 70 | ``` 71 | 72 | Locales (default is `"US"`): 73 | 74 | ```fortran 75 | LOCALES = [ "US", "EU" ] 76 | ``` 77 | 78 | Decimals: `decimals` specifies the number of digits on the rhs of the 79 | radix point, with a default determined internally based on the 80 | [text format](../UserInfo/text-fmts.html) and precision. 81 | 82 | Imaginary unit: `im` specifies the form of a complex number. By 83 | default, `complex` numbers will be written as ordered pairs, e.g. 84 | `(2.45,3.45)`. If `im` is specified, then the number will be written as 85 | a sum with the specified imaginary unit, e.g. `2.45+3.45j` for `im="j"` 86 | or `2.45+3.45*1i` for `im="*1i"`. 87 | -------------------------------------------------------------------------------- /doc_API_design/Ref/aprint.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: aprint 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [interface aprint](../../interface/aprint.html) 7 | 8 | *Description*: Subroutine for printing arrays and array sections to 9 | stdout. 10 | 11 | For `x` an array of rank `1` or `2` and of type `character` or `String`: 12 | 13 | ```fortran 14 | call aprint(x) 15 | ``` 16 | 17 | For `x` an array of rank `1` or `2` and of type `integer`: 18 | 19 | ```fortran 20 | call aprint(x [, fmt]) 21 | ``` 22 | 23 | * `fmt` is `optional`, may be one of `INT_FMTS` 24 | 25 | For `x` an array of rank `1` or `2` and of type `real`: 26 | 27 | ```fortran 28 | call aprint(x [, fmt, decimals]) 29 | ``` 30 | 31 | * `fmt` is `optional`, may be one of `REAL_FMTS` 32 | * `decimals` is `optional` and of type `integer` 33 | 34 | For `x` an array of rank `1` or `2` and of type `complex`: 35 | 36 | ```fortran 37 | call aprint(x [, fmt, decimals, im]) 38 | ``` 39 | 40 | * `fmt` is `optional`, may be one of `REAL_FMTS` 41 | * `decimals` is `optional` and of type `integer` 42 | * `im` is `optional` and of type `character(len=*)` 43 | 44 | ### Optional Arguments 45 | 46 | Integer formats (default is `"i"`): 47 | 48 | ```fortran 49 | INT_FMTS = [ "i", "z" ] 50 | ``` 51 | 52 | Real formats (default is `"f"`): 53 | 54 | ```fortran 55 | REAL_FMTS = [ "e", "f", "z" ] 56 | ``` 57 | 58 | Decimals (default is `2`): `decimals` specifies the number of digits on 59 | the rhs of the radix point. 60 | 61 | Imaginary unit (default is `"j"`): `im` specifies the form of a complex 62 | number. 63 | 64 | @note The optional arguments for `aprint` are different than elsewhere, 65 | and better suited for easy viewing of array sections. 66 | -------------------------------------------------------------------------------- /doc_API_design/Ref/cast.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: cast 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [interface cast](../../interface/cast.html) 7 | 8 | *Description*: Subroutine for casting between numeric and string data. 9 | 10 | ### Casting numbers to string variables 11 | 12 | For casting `x` of type `integer` into a variable `into` of type 13 | `character` (scalar only) or `String` (any rank): 14 | 15 | ```fortran 16 | call cast(x, into [, fmt]) 17 | ``` 18 | 19 | * `fmt` is `optional`, may be one of `INT_FMTS` 20 | 21 | For casting `x` of type `real` into a variable `into` of type 22 | `character` (scalar only) or `String` (any rank): 23 | 24 | ```fortran 25 | call cast(x, into [, locale, fmt, decimals]) 26 | ``` 27 | 28 | * `locale` is `optional`, may be one of `LOCALES` 29 | * `fmt` is `optional`, may be one of `REAL_FMTS` 30 | * `decimals` is `optional` and of type `integer` 31 | 32 | For casting `x` of type `complex` into a variable `into` of type 33 | `character` (scalar only) or `String` (any rank): 34 | 35 | ```fortran 36 | call cast(x, into [, locale, fmt, decimals, im]) 37 | ``` 38 | 39 | * `locale` is `optional`, may be one of `LOCALES` 40 | * `fmt` is `optional`, may be one of `REAL_FMTS` 41 | * `decimals` is `optional` and of type `integer` 42 | * `im` is `optional` and of type `character(len=*)` 43 | 44 | @note While [str](str.html) and [String](String.html) return values 45 | which may be used flexibly inside of string expressions, `cast` may be 46 | used as above to write directly to variables. When converting large 47 | amounts of data to strings, `cast` may be up to 2x faster than the 48 | functional alternatives since the total number of string allocations is 49 | reduced by at least half, all else being equal. 50 | 51 | ### Casting strings to numeric variables 52 | 53 | For casting `substring` of type `character` (scalar only) or `String` 54 | (any rank) into a variable `into` of type `integer`: 55 | 56 | ```fortran 57 | call cast(substring, into [, fmt]) 58 | ``` 59 | 60 | ```fortran 61 | call substring%cast(into [, fmt]) 62 | ``` 63 | 64 | * `fmt` is `optional`, may be one of `INT_FMTS` 65 | 66 | For casting `substring` of type `character` (scalar only) or `String` 67 | (any rank) into a variable `into` of type `real`: 68 | 69 | ```fortran 70 | call cast(substring, into [, locale, fmt]) 71 | ``` 72 | 73 | ```fortran 74 | call substring%cast(into [, locale, fmt]) 75 | ``` 76 | 77 | * `locale` is `optional`, may be one of `LOCALES` 78 | * `fmt` is `optional`, may be one of `REAL_FMTS` 79 | 80 | For casting `substring` of type `character` (scalar only) or `String` 81 | (any rank) into a variable `into` of type `complex`: 82 | 83 | ```fortran 84 | call cast(substring, into [, locale, fmt, im]) 85 | ``` 86 | 87 | ```fortran 88 | call substring%cast(into [, locale, fmt, im]) 89 | ``` 90 | 91 | * `locale` is `optional`, may be one of `LOCALES` 92 | * `fmt` is `optional`, may be one of `REAL_FMTS` 93 | * `im` is `optional` and of type `character(len=*)` 94 | 95 | @warning The arguments `x` and `substring` must always be of the same 96 | rank and shape as `into`, which must be pre-allocated prior to calling 97 | `cast` due to the restrictions on `intent(out)` arguments of 98 | `elemental` procedures. 99 | 100 | @note The type-bound procedure access of the form 101 | `call substring%cast()` is valid when `substring` is a `String` 102 | variable. To cast a `String`-valued expression, the expression must be 103 | passed to `cast` by the form `call cast()`. 104 | 105 | ### Optional Arguments 106 | 107 | Integer formats (default is `"i"`): 108 | 109 | ```fortran 110 | INT_FMTS = [ "i", "z" ] 111 | ``` 112 | 113 | Real formats (default is `"e"`): 114 | 115 | ```fortran 116 | REAL_FMTS = [ "e", "f", "z" ] 117 | ``` 118 | 119 | Locales (default is `"US"`): 120 | 121 | ```fortran 122 | LOCALES = [ "US", "EU" ] 123 | ``` 124 | 125 | Decimals: `decimals` specifies the number of digits on the rhs of the 126 | radix point, with a default determined internally based on the 127 | [text format](../UserInfo/text-fmts.html) and precision. 128 | 129 | Imaginary unit: `im` specifies the form of a complex number. If not 130 | present, `complex` numbers will be assumed to be written as ordered 131 | pairs, e.g. `(2.45,3.45)`. 132 | -------------------------------------------------------------------------------- /doc_API_design/Ref/constants.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: CONSTANTS 3 | author: Austin C Bullock 4 | --- 5 | 6 | The [[io_fortran_lib]] module provides access to a handful of 7 | `parameter` [constants](../../module/io_fortran_lib.html#variable-nl) 8 | for general use: 9 | 10 | * `NL`: The newline character (system agnostic). 11 | * `SPACE`: The space character. 12 | * `CR`: The carriage return character. 13 | * `FF`: The form feed character. 14 | * `VT`: The vertical tab character. 15 | * `LF`: The line feed character. 16 | * `TAB`: The horizontal tab character. 17 | * `HT`: The horizontal tab character (alternate name). 18 | * `BELL`: The bell/alert character. 19 | * `NUL`: The null character. 20 | * `CNUL`: The C null character re-exported from `iso_c_binding`. 21 | -------------------------------------------------------------------------------- /doc_API_design/Ref/echo.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: echo 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [interface echo](../../interface/echo.html) 7 | 8 | *Description*: Subroutine for writing a scalar `character` or `String` 9 | to an external text file. 10 | 11 | For `substring` a scalar of type `character` or `String`: 12 | 13 | ```fortran 14 | call echo(substring, file [, append, terminator, stat, errmsg]) 15 | ``` 16 | 17 | * `file` is of type `character(len=*)` 18 | * `append` is `optional` and of type `logical` 19 | * `terminator` is `optional` and of type `character(len=*)` 20 | * `stat` is `optional` and of type `integer` 21 | * `errmsg` is `optional` and of type `character(len=*)` 22 | 23 | For `substring` a scalar variable of type `String`: 24 | 25 | ```fortran 26 | call substring%echo(file [, append, terminator, stat, errmsg]) 27 | ``` 28 | 29 | * `file` is of type `character(len=*)` 30 | * `append` is `optional` and of type `logical` 31 | * `terminator` is `optional` and of type `character(len=*)` 32 | * `stat` is `optional` and of type `integer` 33 | * `errmsg` is `optional` and of type `character(len=*)` 34 | 35 | @note The type-bound procedure access of the form 36 | `call substring%echo()` is valid when `substring` is a `String` 37 | variable. To echo a `String`-valued expression, the expression must be 38 | passed to `echo` by the form `call echo()`. 39 | 40 | @note `file` may be a relative path, but absolute paths are not 41 | guaranteed to work on every platform. 42 | 43 | ### Optional Arguments 44 | 45 | Append (default is `.true.`): `append` specifies whether to append or 46 | to replace the file `file`. Either way, the file will be created if it 47 | does not exist. 48 | 49 | Terminator (default is `LF`): `terminator` is a string terminator 50 | inserted at the end of the input string. 51 | -------------------------------------------------------------------------------- /doc_API_design/Ref/from_file.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: from_file 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [interface from_file](../../interface/from_file.html) 7 | 8 | *Description*: Subroutine for reading an external file of uniform 9 | numeric data type and format into an array. 10 | 11 | For reading textual data into an array `into` of rank `1` or `2` and of 12 | type `integer`: 13 | 14 | ```fortran 15 | call from_file(file, into [, header, delim, fmt, stat, errmsg]) 16 | ``` 17 | 18 | * `file` is of type `character(len=*)` 19 | * `header` is `optional` and of type `logical` 20 | * `delim` is `optional` and of type `character(len=*)` 21 | * `fmt` is `optional`, may be one of `INT_FMTS` 22 | * `stat` is `optional` and of type `integer` 23 | * `errmsg` is `optional` and of type `character(len=*)` 24 | 25 | For reading textual data into an array `into` of rank `1` or `2` and of 26 | type `real`: 27 | 28 | ```fortran 29 | call from_file(file, into [, header, locale, delim, fmt, stat, errmsg]) 30 | ``` 31 | 32 | * `file` is of type `character(len=*)` 33 | * `header` is `optional` and of type `logical` 34 | * `locale` is `optional`, may be one of `LOCALES` 35 | * `delim` is `optional` and of type `character(len=*)` 36 | * `fmt` is `optional`, may be one of `REAL_FMTS` 37 | * `stat` is `optional` and of type `integer` 38 | * `errmsg` is `optional` and of type `character(len=*)` 39 | 40 | For reading textual data into an array `into` of rank `1` or `2` and of 41 | type `complex`: 42 | 43 | ```fortran 44 | call from_file(file, into [, header, locale, delim, fmt, im, stat, errmsg]) 45 | ``` 46 | 47 | * `file` is of type `character(len=*)` 48 | * `header` is `optional` and of type `logical` 49 | * `locale` is `optional`, may be one of `LOCALES` 50 | * `delim` is `optional` and of type `character(len=*)` 51 | * `fmt` is `optional`, may be one of `REAL_FMTS` 52 | * `im` is `optional` and of type `character(len=*)` 53 | * `stat` is `optional` and of type `integer` 54 | * `errmsg` is `optional` and of type `character(len=*)` 55 | 56 | For reading binary data into an array `into` of any rank `1`-`15` and 57 | of type `integer`, `real`, `complex`: 58 | 59 | ```fortran 60 | call from_file(file, into, data_shape [, stat, errmsg]) 61 | ``` 62 | 63 | * `file` is of type `character(len=*)` 64 | * `data_shape` is of type `integer, dimension(:)` 65 | * `stat` is `optional` and of type `integer` 66 | * `errmsg` is `optional` and of type `character(len=*)` 67 | 68 | @note `file` may be a relative path, but absolute paths are not 69 | guaranteed to work on every platform. 70 | 71 | @warning In all cases, `into` must be `allocatable`, and will lose its 72 | allocation status upon passing into `from_file` if already allocated. 73 | As a result, `from_file` does not allow reading into sections of 74 | already allocated arrays. 75 | 76 | @note When reading binary data, `data_shape` must be present and its 77 | size must equal the rank of `into`. 78 | 79 | ### Optional Arguments 80 | 81 | Header (default is `.false.`): specifies whether a header line is 82 | present. 83 | 84 | Locales (default is `"US"`): 85 | 86 | ```fortran 87 | LOCALES = [ "US", "EU" ] 88 | ``` 89 | 90 | Delimiter: data separator. Default is `","` for `integer` data and for 91 | `real`/`complex` data with `"US"` locale, and `";"` for 92 | `real`/`complex` data with `"EU"` locale. It is always recommended to 93 | omit the delimiter argument for default unless a custom delimiter is 94 | really necessary. If `x` has rank `1` and the data is ordered down the 95 | rows, then the `delim` argument is ignored. 96 | 97 | Integer formats (default is `"i"`): 98 | 99 | ```fortran 100 | INT_FMTS = [ "i", "z" ] 101 | ``` 102 | 103 | Real formats (default is `"e"`): 104 | 105 | ```fortran 106 | REAL_FMTS = [ "e", "f", "z" ] 107 | ``` 108 | 109 | Imaginary unit: `im` specifies the form of a complex number. If not 110 | present, `complex` numbers will be assumed to be written as ordered 111 | pairs, e.g. `(2.45,3.45)`. 112 | -------------------------------------------------------------------------------- /doc_API_design/Ref/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Reference Guide 3 | author: Austin C Bullock 4 | ordered_subpage: constants.md 5 | ordered_subpage: String.md 6 | ordered_subpage: str.md 7 | ordered_subpage: cast.md 8 | ordered_subpage: join-split.md 9 | ordered_subpage: to_file.md 10 | ordered_subpage: from_file.md 11 | ordered_subpage: echo.md 12 | ordered_subpage: aprint.md 13 | ordered_subpage: String-methods.md 14 | ordered_subpage: operators.md 15 | --- 16 | 17 | The following subsections provide user instructions for each of the 18 | publicly accessible [interfaces](../../lists/procedures.html): 19 | 20 | * [String](String.html): Function for returning a 21 | [String](../../type/string.html) representation of numbers. 22 | * [str](str.html): Function for returning a `character` representation 23 | of a number. 24 | * [cast](cast.html): Subroutine for casting between numeric and string 25 | data. 26 | * [join and split](join-split.html): Functions for joining and 27 | splitting strings. 28 | * [to_file](to_file.html): Subroutine for writing an array of uniform 29 | numeric data type to an external file. 30 | * [from_file](from_file.html): Subroutine for reading an external file 31 | of uniform numeric data type and format into an array. 32 | * [echo](echo.html): Subroutine for writing a scalar `character` or 33 | `String` to an external text file. 34 | * [aprint](aprint.html): Subroutine for printing arrays and array 35 | sections to stdout. 36 | * [String methods](String-methods.html): Type-bound procedures for 37 | [String](../../type/string.html). 38 | * [Operators](operators.html): Extended operators for convenient string 39 | manipulations. 40 | -------------------------------------------------------------------------------- /doc_API_design/Ref/join-split.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: join and split 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [interface join](../../interface/join.html) 7 | 8 | *Description*: Function for joining a vector of `tokens` into a scalar 9 | `character` or `String`. 10 | 11 | To join a one-dimensional array `tokens` of type `character` or 12 | `String`: 13 | 14 | ```fortran 15 | result = join(tokens [, separator]) 16 | ``` 17 | 18 | * `separator` is `optional` and of type `character(len=*)` 19 | 20 | For a subroutine version of `join`, see 21 | [join](String-methods.html#join). 22 | 23 | @note The return type of `join` is the same as the type of `tokens`. 24 | 25 | ## [interface split](../../interface/split.html) 26 | 27 | *Description*: Function for splitting a scalar `character` or `String` 28 | into a vector of `tokens`. 29 | 30 | For `substring` a scalar `character` or `String`: 31 | 32 | ```fortran 33 | result = split(substring [, separator]) 34 | ``` 35 | 36 | * `separator` is `optional` and of type `character(len=*)` 37 | 38 | For `substring` a scalar variable of type `String`: 39 | 40 | ```fortran 41 | result = substring%split([separator]) 42 | ``` 43 | 44 | * `separator` is `optional` and of type `character(len=*)` 45 | 46 | @note The type-bound procedure access of the form `substring%split()` 47 | is valid when `substring` is a `String` variable. To split a 48 | `String`-valued expression, the expression must be passed to `split` by 49 | the form `split(substring)`. 50 | 51 | @note The return type of `split` is always `String`. 52 | 53 | ### Optional Arguments 54 | 55 | Separator (default is `SPACE`): the separator to use when joining or 56 | splitting. 57 | -------------------------------------------------------------------------------- /doc_API_design/Ref/operators.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Operators 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [Operator interfaces](../../lists/procedures.html) 7 | 8 | *Description*: Extended operators for convenient string manipulations. 9 | 10 | ### Concatenation ([//](../../interface/operator%28SLASHSLASH%29.html) and [+](../../interface/operator%28%2B%29.html)) 11 | 12 | For `x` and `y` scalars or arrays of any compatible rank, and of any 13 | combination of type `character` and `String`: 14 | 15 | ```fortran 16 | result = x // y 17 | ``` 18 | 19 | ```fortran 20 | result = x + y 21 | ``` 22 | 23 | @note Concatenation of mixed type will return a `String`. 24 | 25 | ### Excision ([-](../../interface/operator%28-%29.html)) 26 | 27 | For `x` and `y` scalars or arrays of any compatible rank, and of any 28 | combination of type `character` and `String`: 29 | 30 | ```fortran 31 | result = x - y 32 | ``` 33 | 34 | @note Excision always returns a `String` value even when both arguments 35 | are of type `character`. This ensures that excision can be performed 36 | elementally even for `character` values, which would not be 37 | well-defined with a return type of `character`. For two scalar 38 | `character` values, one may simply perform the conversion 39 | `result = str(x - y)` to return a scalar `character`. 40 | 41 | @note 42 | String arithmetic is not associative, commutative, or distributive in 43 | general: 44 | 45 | * (Associative) `(x + y) + z == x + (y + z)` and `x + (y - z) /= (x + 46 | y) - z` are both `.true.` in general. 47 | * (Commutative) `x + y /= y + x` and `x + y - z /= x - z + y` are both 48 | `.true.` in general. 49 | * (Distributive) `x - (y + z) /= x - y - z` is `.true.` in general. 50 | @endnote 51 | 52 | ### Repetition ([**](../../interface/operator%28ASTERISKASTERISK%29.html)) 53 | 54 | For `x` a scalar or array of any rank, and of type `character` or 55 | `String`: 56 | 57 | ```fortran 58 | result = x**ncopies 59 | ``` 60 | 61 | * `ncopies` is of type `integer` 62 | 63 | @note The `**` operator is a wrapper for the 64 | [repeat](https://gcc.gnu.org/onlinedocs/gfortran/REPEAT.html) 65 | intrinsic, and extended for type `String`. 66 | 67 | ### Equivalence ([==](../../interface/operator%28%3D%3D%29.html)) 68 | 69 | For `x` and `y` scalars or arrays of any compatible rank, and of any 70 | combination of type `character` and `String`: 71 | 72 | ```fortran 73 | result = (x == y) 74 | ``` 75 | 76 | ```fortran 77 | result = (x .eq. y) 78 | ``` 79 | 80 | ### Non-equivalence ([/=](../../interface/operator%28SLASH%3D%29.html)) 81 | 82 | For `x` and `y` scalars or arrays of any compatible rank, and of any 83 | combination of type `character` and `String`: 84 | 85 | ```fortran 86 | result = (x /= y) 87 | ``` 88 | 89 | ```fortran 90 | result = (x .ne. y) 91 | ``` 92 | -------------------------------------------------------------------------------- /doc_API_design/Ref/str.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: str 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [interface str](../../interface/str.html) 7 | 8 | *Description*: Function for returning a `character` representation of a 9 | number. 10 | 11 | To return the 12 | [empty string](../../module/io_fortran_lib.html#variable-empty_str), 13 | use no arguments: 14 | 15 | ```fortran 16 | result = str() 17 | ``` 18 | 19 | For `x` a scalar of type `String`: 20 | 21 | ```fortran 22 | result = str(x) 23 | ``` 24 | 25 | This is for scalar `String` to `character` conversion. 26 | 27 | For `x` a scalar of type `integer`: 28 | 29 | ```fortran 30 | result = str(x [, fmt]) 31 | ``` 32 | 33 | * `fmt` is `optional`, may be one of `INT_FMTS` 34 | 35 | For `x` a scalar of type `real`: 36 | 37 | ```fortran 38 | result = str(x [, locale, fmt, decimals]) 39 | ``` 40 | 41 | * `locale` is `optional`, may be one of `LOCALES` 42 | * `fmt` is `optional`, may be one of `REAL_FMTS` 43 | * `decimals` is `optional` and of type `integer` 44 | 45 | For `x` a scalar of type `complex`: 46 | 47 | ```fortran 48 | result = str(x [, locale, fmt, decimals, im]) 49 | ``` 50 | 51 | * `locale` is `optional`, may be one of `LOCALES` 52 | * `fmt` is `optional`, may be one of `REAL_FMTS` 53 | * `decimals` is `optional` and of type `integer` 54 | * `im` is `optional` and of type `character(len=*)` 55 | 56 | @note Note that `str` operates on scalars only. For elemental 57 | functionality, see [String](String.html). 58 | 59 | ### Optional Arguments 60 | 61 | Integer formats (default is `"i"`): 62 | 63 | ```fortran 64 | INT_FMTS = [ "i", "z" ] 65 | ``` 66 | 67 | Real formats (default is `"e"`): 68 | 69 | ```fortran 70 | REAL_FMTS = [ "e", "f", "z" ] 71 | ``` 72 | 73 | Locales (default is `"US"`): 74 | 75 | ```fortran 76 | LOCALES = [ "US", "EU" ] 77 | ``` 78 | 79 | Decimals: `decimals` specifies the number of digits on the rhs of the 80 | radix point, with a default determined internally based on the 81 | [text format](../UserInfo/text-fmts.html) and precision. 82 | 83 | Imaginary unit: `im` specifies the form of a complex number. By 84 | default, `complex` numbers will be written as ordered pairs, e.g. 85 | `(2.45,3.45)`. If `im` is specified, then the number will be written as 86 | a sum with the specified imaginary unit, e.g. `2.45+3.45j` for `im="j"` 87 | or `2.45+3.45*1i` for `im="*1i"`. 88 | -------------------------------------------------------------------------------- /doc_API_design/Ref/to_file.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: to_file 3 | author: Austin C Bullock 4 | --- 5 | 6 | ## [interface to_file](../../interface/to_file.html) 7 | 8 | *Description*: Subroutine for writing an array of uniform numeric data 9 | type to an external file. 10 | 11 | For writing textual data from an array `x` of rank `1`, `2` and of type 12 | `integer`: 13 | 14 | ```fortran 15 | call to_file(x, file [, header, delim, fmt, stat, errmsg]) 16 | ``` 17 | 18 | * `file` is of type `character(len=*)` 19 | * `header` is `optional` and of type `character(len=*), dimension(:)` 20 | * `delim` is `optional` and of type `character(len=*)` 21 | * `fmt` is `optional`, may be one of `INT_FMTS` 22 | * `stat` is `optional` and of type `integer` 23 | * `errmsg` is `optional` and of type `character(len=*)` 24 | 25 | For writing textual data from an array `x` of rank `1`, `2` and of type 26 | `real`: 27 | 28 | ```fortran 29 | call to_file(x, file [, header, locale, delim, fmt, decimals, stat, errmsg]) 30 | ``` 31 | 32 | * `file` is of type `character(len=*)` 33 | * `header` is `optional` and of type `character(len=*), dimension(:)` 34 | * `locale` is `optional`, may be one of `LOCALES` 35 | * `delim` is `optional` and of type `character(len=*)` 36 | * `fmt` is `optional`, may be one of `REAL_FMTS` 37 | * `decimals` is `optional` and of type `integer` 38 | * `stat` is `optional` and of type `integer` 39 | * `errmsg` is `optional` and of type `character(len=*)` 40 | 41 | For writing textual data from an array `x` of rank `1`, `2` and of type 42 | `complex`: 43 | 44 | ```fortran 45 | call to_file(x, file [, header, locale, delim, fmt, decimals, im, stat, errmsg]) 46 | ``` 47 | 48 | * `file` is of type `character(len=*)` 49 | * `header` is `optional` and of type `character(len=*), dimension(:)` 50 | * `locale` is `optional`, may be one of `LOCALES` 51 | * `delim` is `optional` and of type `character(len=*)` 52 | * `fmt` is `optional`, may be one of `REAL_FMTS` 53 | * `decimals` is `optional` and of type `integer` 54 | * `im` is `optional` and of type `character(len=*)` 55 | * `stat` is `optional` and of type `integer` 56 | * `errmsg` is `optional` and of type `character(len=*)` 57 | 58 | For writing binary data from an array `x` of any rank `1`-`15` and of 59 | type `integer`, `real`, `complex`: 60 | 61 | ```fortran 62 | call to_file(x, file [, stat, errmsg]) 63 | ``` 64 | 65 | * `file` is of type `character(len=*)` 66 | * `stat` is `optional` and of type `integer` 67 | * `errmsg` is `optional` and of type `character(len=*)` 68 | 69 | @note `file` may be a relative path, but absolute paths are not 70 | guaranteed to work on every platform. 71 | 72 | @note `to_file` will always use the `NL` line ending when writing text 73 | files (which on most systems equates to `LF`). 74 | 75 | ### Optional Arguments 76 | 77 | Header (default is none): `header` is a 78 | [character array literal](../UserInfo/compilers.html). For `x` of rank 79 | `1`, `header` may be of size `1` or `size(x)`. For `x` of rank `2`, 80 | `header` may be of size `1` or `size(x, dim=2)`. 81 | 82 | Locales (default is `"US"`): 83 | 84 | ```fortran 85 | LOCALES = [ "US", "EU" ] 86 | ``` 87 | 88 | Delimiter: data separator. Default is `","` for `integer` data and for 89 | `real`/`complex` data with `"US"` locale, and `";"` for 90 | `real`/`complex` data with `"EU"` locale. It is always recommended to 91 | omit the delimiter argument for default unless a custom delimiter is 92 | really necessary. If `x` has rank `1` and `dim=1`, then the `delim` 93 | argument is ignored. 94 | 95 | Integer formats (default is `"i"`): 96 | 97 | ```fortran 98 | INT_FMTS = [ "i", "z" ] 99 | ``` 100 | 101 | Real formats (default is `"e"`): 102 | 103 | ```fortran 104 | REAL_FMTS = [ "e", "f", "z" ] 105 | ``` 106 | 107 | Decimals: `decimals` specifies the number of digits on the rhs of the 108 | radix point, with a default determined internally based on the 109 | [text format](../UserInfo/text-fmts.html) and precision. 110 | 111 | Imaginary unit: `im` specifies the form of a complex number. By 112 | default, `complex` numbers will be written as ordered pairs, e.g. 113 | `(2.45,3.45)`. If `im` is specified, then the number will be written as 114 | a sum with the specified imaginary unit, e.g. `2.45+3.45j` for `im="j"` 115 | or `2.45+3.45*1i` for `im="*1i"`. 116 | -------------------------------------------------------------------------------- /doc_API_design/UserInfo/characters.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Character sets and kinds 3 | author: Austin C Bullock 4 | --- 5 | 6 | The IO-Fortran-Library officially supports the standard Fortran 7 | character set, based on the 8 | [US-ASCII](https://en.wikipedia.org/wiki/ASCII) character set with 9 | default character `kind`. On most systems, the default `kind` will 10 | consist of precisely one byte per character, equivalent to 11 | `selected_char_kind("ascii")`. Since US-ASCII is a 7-bit character set 12 | and most systems use 8-bit characters, it must be noted that only the 13 | first 128 characters are system-independent, i.e. `achar(0)` is 14 | portable but `achar(255)` is not. To maximize portability across 15 | systems and compilers, the IO-Fortran-Library does not reference any 16 | other character sets or kinds other than the 128-character US-ASCII 17 | with default `kind`. 18 | 19 | However, most users should feel free to employ characters outside of 20 | the US-ASCII in strings and string expressions (including many Unicode 21 | symbols) and these will tend to behave as expected as long as the 22 | characters can fit comfortably into one byte and the output unit 23 | supports UTF-8 encoding. For instance, inspect the output of the 24 | following program: 25 | 26 | ```fortran 27 | program main 28 | use io_fortran_lib 29 | implicit none (type, external) 30 | 31 | type(String) :: emojis 32 | 33 | emojis = "😂🙈😊🤣" + "😍" - "😂" + "👌"**5 34 | call emojis%echo("emojis.txt") 35 | write(*,*) emojis 36 | end program main 37 | ``` 38 | 39 | The expected result is `🙈😊🤣😍👌👌👌👌👌`, which will be displayed 40 | properly in any terminal or text file with UTF-8 encoding. 41 | 42 | @note The Fortran standard permits compilers to support character sets 43 | and kinds other than US-ASCII with one-byte `kind`, and acknowledges 44 | the extended four-byte 45 | [UCS-4](https://en.wikipedia.org/wiki/Universal_Coded_Character_Set) 46 | character set defined by ISO 10646, but such support is highly 47 | inconsistent across compilers at the time of writing. 48 | -------------------------------------------------------------------------------- /doc_API_design/UserInfo/compilers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Compiler-dependent behavior 3 | author: Austin C Bullock 4 | --- 5 | 6 | When writing text files, it's important to note that some compilers 7 | implement extensions to the Fortran standard by default with regards 8 | to character array literals. For example, the array literal 9 | 10 | ```fortran 11 | header = [ "firstcol", "secondcol" ] 12 | ``` 13 | 14 | is not standard Fortran 2018 since the strings in the array do not have 15 | identical length. Some compilers will accept this and others will not. 16 | If required, simply add padding spaces to the left or right of each 17 | string to match the length of the longest element. These padding 18 | spaces will not be present in the output file. 19 | 20 | @note Some compilers may allocate strings dynamically on the stack. 21 | When reading large text files, this may result in a stack overflow or 22 | segmentation fault unless the compiler is directed to allocate 23 | everything on the heap. For example, one would specify `-heap-arrays 0` 24 | for the Intel Fortran compiler on Linux (`/heap-arrays:0` on Windows). 25 | -------------------------------------------------------------------------------- /doc_API_design/UserInfo/error-codes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Error Codes 3 | author: Austin C Bullock 4 | --- 5 | 6 | When writing to file or reading from file, the `optional` arguments 7 | `stat` and `errmsg` may be present, which will detail any errors that 8 | may occur during execution of the procedure. The error codes have the 9 | following explanation: 10 | 11 | ```Fortran 12 | integer, parameter :: READ_ERR = 1 ! Read error code 13 | integer, parameter :: WRITE_ERR = 2 ! Write error code 14 | integer, parameter :: ALLOC_ERR = 3 ! Allocation error code 15 | integer, parameter :: ARG_ERR = 4 ! Argument error code 16 | ``` 17 | 18 | A `READ_ERR` code indicates that an error has occured during a `read` 19 | statement, `inquire` statement, or a `close` statement in the execution 20 | of [from_file](../Ref/from_file.html) or 21 | [read_file](../Ref/String-methods.html#read_file). A `WRITE_ERR` code 22 | indicates that an error has occured during a `write` statement or a 23 | `close` statement in the execution of [to_file](../Ref/to_file.html), 24 | [echo](../Ref/echo.html), or 25 | [write_file](../Ref/String-methods.html#write_file). An `ALLOC_ERR` 26 | code indicates that an `allocate` or `deallocate` statement has failed. 27 | An `ARG_ERR` code indicates that the user has provided an incorrect 28 | argument to the procedure. 29 | 30 | In all cases, the `errmsg` will contain more detailed information about 31 | the error that occured. If no error condition occurs, the `stat` will 32 | return `0` and the `errmsg` will be empty. 33 | -------------------------------------------------------------------------------- /doc_API_design/UserInfo/file-ext.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: File extensions 3 | author: Austin C Bullock 4 | --- 5 | 6 | When writing to file or reading from file, a valid file extension must 7 | be present. 8 | 9 | The following are valid text file extensions: 10 | 11 | ```fortran 12 | character(len=3), parameter :: TEXT_EXT(*) = [ "csv", "txt", & ! Allowed text extensions 13 | "log", "rtf", & 14 | "odm", "odt", & 15 | "ods", "odf", & 16 | "xls", "doc", & 17 | "org", "dbf", & 18 | "bed", "gff", & 19 | "gtf" ] 20 | ``` 21 | 22 | The following are valid binary file extensions: 23 | 24 | ```fortran 25 | character(len=3), parameter :: BINARY_EXT(*) = [ "dat", "bin" ] ! Allowed binary extensions 26 | ``` 27 | 28 | The routines [to_file](../Ref/to_file.html) and 29 | [from_file](../Ref/from_file.html) will detect the file extension used 30 | and direct whether to write/read a text file or a binary file. The 31 | routines [echo](../Ref/echo.html), 32 | [write_file](../Ref/String-methods.html#write_file), and 33 | [read_file](../Ref/String-methods.html#read_file) accept only text 34 | extensions. Other file extensions may be eligible for addition. 35 | -------------------------------------------------------------------------------- /doc_API_design/UserInfo/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Important User Information 3 | author: Austin C Bullock 4 | ordered_subpage: text-fmts.md 5 | ordered_subpage: locale-fmts.md 6 | ordered_subpage: file-ext.md 7 | ordered_subpage: error-codes.md 8 | ordered_subpage: compilers.md 9 | ordered_subpage: characters.md 10 | ordered_subpage: thread-safety.md 11 | --- 12 | 13 | The following subsections detail important information for users: 14 | 15 | * [Numeric text formats](text-fmts.html) 16 | * [Locales](locale-fmts.html) 17 | * [File extensions](file-ext.html) 18 | * [Error Codes](error-codes.html) 19 | * [Compiler-dependent behavior](compilers.html) 20 | * [Character sets and kinds](characters.html) 21 | * [Thread safety](thread-safety.html) 22 | -------------------------------------------------------------------------------- /doc_API_design/UserInfo/locale-fmts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Locales 3 | author: Austin C Bullock 4 | --- 5 | 6 | When writing floating point numbers of type `real` or `complex` as 7 | strings with [String](../Ref/String.html), [str](../Ref/str.html), or 8 | [to_file](../Ref/to_file.html), any of the following locales may be 9 | used: 10 | 11 | ```fortran 12 | character(len=2), parameter :: LOCALES(*) = [ "US", "EU" ] ! Allowed locale specifiers 13 | ``` 14 | 15 | * `"US"`: US decimal (default), e.g. `1.23456789` 16 | * `"EU"`: EU decimal, e.g. `1,23456789` 17 | 18 | With `to_file` and `from_file`, the `locale` additionally determines 19 | the default delimiter, e.g. `1.23456789,0.12345678` for `locale="US"` 20 | and `1,23456789;0,12345678` for `locale="EU"`. 21 | 22 | When moving data in the opposite direction with the complementary 23 | procedures [cast](../Ref/cast.html) or 24 | [from_file](../Ref/from_file.html), the same `locale` is required to 25 | properly read the decimals. Specifying a `locale` that is different 26 | from what is actually present may result in an I/O syntax error. 27 | -------------------------------------------------------------------------------- /doc_API_design/UserInfo/text-fmts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Numeric text formats 3 | author: Austin C Bullock 4 | --- 5 | 6 | When writing `integer` data as strings with 7 | [String](../Ref/String.html), [str](../Ref/str.html), or 8 | [to_file](../Ref/to_file.html), any of the following text formats may 9 | be used: 10 | 11 | ```fortran 12 | character(len=1), parameter :: INT_FMTS(*) = [ "i", "z" ] ! Allowed formats for integers 13 | ``` 14 | 15 | * `"i"`: integer format (default), e.g. `123456` 16 | * `"z"`: hexadecimal format, e.g. `0x1e240` 17 | 18 | When writing floating point numbers of type `real` or `complex` as 19 | strings with [String](../Ref/String.html), [str](../Ref/str.html), or 20 | [to_file](../Ref/to_file.html), any of the following text formats may 21 | be used: 22 | 23 | ```fortran 24 | character(len=1), parameter :: REAL_FMTS(*) = [ "e", "f", "z" ] ! Allowed formats for floats 25 | ``` 26 | 27 | * `"e"`: normalized exponential format (default), e.g. 28 | `1.23456789012345675e+005` 29 | * `"f"`: decimal format, e.g. `123456.789012345674` 30 | * `"z"`: hexadecimal format, e.g. `0x40fe240c9fcb68cd` 31 | 32 | @note The `"z"` hexadecimal format is an unsigned integer format and 33 | may be used for `integer`, `real`, or `complex` data. Floating point 34 | numbers are interpreted bit-wise as unsigned integers when written with 35 | the `"z"` format, preventing any loss of precision in a round-trip 36 | conversion. This format is preferred in data transfers for which 37 | precision losses are intolerable. The `"z"` format may also be 38 | preferred for faster read/write times and more compact storage. 39 | 40 | When moving data in the opposite direction with the complementary 41 | procedures [cast](../Ref/cast.html) or 42 | [from_file](../Ref/from_file.html), the same `fmt` is required to 43 | properly cast the data. Specifying a `fmt` that is different from what 44 | is actually present may result in an I/O syntax error. 45 | 46 | @note By default, `real` and `complex` data will be written with a 47 | number of significant digits required for a lossless round-trip 48 | conversion of the form `numeric -> string -> numeric`. In general, one 49 | may expect the `f` format to produce losses in round-trip conversion of 50 | up to a few `epsilon` in as many as a fifth of transfers. However, the 51 | `e` format is not expected to produce any losses, and the `z` format 52 | will never produce losses as it involves direct bit transfer (no 53 | base-10 ⇆ base-2 conversions are involved). 54 | 55 | @note For negative integers, the hexadecimal format `"z"` produces 56 | ambiguities across storage sizes. For instance, the integer `0xff` 57 | equates to `-1` in `int8` storage but equates to `255` in larger 58 | storage sizes. Similarly, the integer `0xffff` equates to `-1` in 59 | `int16` storage but equates to `65535` in larger storage sizes, and so 60 | on. It is the responsibility of the programmer to cast hexadecimal 61 | strings into variables with the proper storage size. 62 | -------------------------------------------------------------------------------- /doc_API_design/UserInfo/thread-safety.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Thread safety 3 | author: Austin C Bullock 4 | --- 5 | 6 | The IO-Fortran-Library promotes thread-safety by explicitly enforcing 7 | recursion with the `recursive` keyword on all module procedures. 8 | However, performing I/O in parallel regions has the tendency to result 9 | in unexpected behavior. For instance, inspect the output of the 10 | following program with multiple coarray images: 11 | 12 | ```fortran 13 | program main 14 | use io_fortran_lib 15 | implicit none (type, external) 16 | 17 | call echo("Hello from image "//str(this_image()), file="hello.txt") 18 | end program main 19 | ``` 20 | 21 | This program will result in conflicts as multiple images attempt to 22 | write to the same file concurrently. The proper way to compose this 23 | program is by nesting `echo` inside a `critical` block to enforce 24 | strict thread-safety in the region: 25 | 26 | ```fortran 27 | program main 28 | use io_fortran_lib 29 | implicit none (type, external) 30 | 31 | critical 32 | call echo("Hello from image "//str(this_image()), file="hello.txt") 33 | end critical 34 | end program main 35 | ``` 36 | 37 | Another common scenario involves performing I/O on a single image, 38 | which is thread-safe: 39 | 40 | ```fortran 41 | program main 42 | use io_fortran_lib 43 | implicit none (type, external) 44 | 45 | if ( this_image() == 1 ) then 46 | call echo("Hello from image "//str(this_image()), file="hello.txt") 47 | end if 48 | end program main 49 | ``` 50 | -------------------------------------------------------------------------------- /doc_API_design/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Design 3 | author: Austin C Bullock 4 | ordered_subpage: UserInfo 5 | ordered_subpage: Ref 6 | ordered_subpage: Examples 7 | --- 8 | 9 | The API is made available for use through the following statement: 10 | 11 | ```fortran 12 | use io_fortran_lib 13 | ``` 14 | 15 | which may be placed at the start of any compilation unit, immediately 16 | following the `program`, `module`, `function`, or `subroutine` 17 | statement, and before any `implicit` statement. 18 | 19 | The functionality provided by the API is distributed via a handful of 20 | [generic interfaces](../lists/procedures.html) and a derived type 21 | [String](../type/string.html). The corresponding routines operate on 22 | numeric and string data, and take a minimal number of arguments with 23 | optional arguments to customize output for particular use cases. This 24 | ensures ease of use and flexibility for the end-user, without the need 25 | for any specific knowledge regarding the internal implementations. For 26 | advanced character handling, the `String` type provides extensive 27 | functionality through [type-bound procedures](Ref/String-methods.html). 28 | For convenience, a list of 29 | [constants](../module/io_fortran_lib.html#variable-nl) are also 30 | provided. 31 | 32 | The design of [[io_fortran_lib]] is dualistic in nature. That is, for 33 | each operation represented by an interface, there exists a transpose 34 | operation with a corresponding interface, such as `String ⇆ cast`, 35 | `str ⇆ cast`, `to_file ⇆ from_file`, `read_file ⇆ write_file`, 36 | `join ⇆ split`, and so on. Each pair of interfaces are designed to 37 | mirror each other in their arguments and assumptions of optional 38 | arguments. 39 | 40 | @note All file I/O (both text and binary) is conducted via unformatted, 41 | stream-access reads and writes as introduced in Fortran 2003. 42 | -------------------------------------------------------------------------------- /ford-API-index.md: -------------------------------------------------------------------------------- 1 | --- 2 | project: IO-Fortran-Library 3 | version: 1.3.1 4 | summary: A portable, standard I/O library for Modern Fortran 5 | author: Austin C Bullock 6 | github: https://github.com/acbbullock 7 | project_github: https://github.com/acbbullock/IO-Fortran-Library 8 | license: MIT 9 | creation_date: %Y-%m-%d %H:%M %z 10 | print_creation_date: true 11 | md_extensions: markdown.extensions.toc 12 | page_dir: doc_API_design 13 | externalize: true 14 | --- 15 | 16 | [TOC] 17 | 18 | # API Documentation 19 | 20 | This API documentation was generated by 21 | [FORD](https://github.com/Fortran-FOSS-Programmers/ford) (the Fortran 22 | documentation generator). 23 | 24 | ## Purpose 25 | 26 | The purpose of this project is to provide a portable, standard I/O 27 | library for Modern Fortran programs supporting the Fortran 2018 28 | standard, with the goal of enabling essential I/O functionality for 29 | Fortran programmers on any system and with any modern compiler. 30 | 31 | ## Scope 32 | 33 | The IO Fortran Library is a Fortran module [[io_fortran_lib]] which 34 | provides high level routines for doing internal and external I/O. In 35 | particular, the module provides a handful of generic interfaces and a 36 | simple derived type for doing string-based and array-based I/O that are 37 | useful for recording program data, reading data into programs, writing 38 | formatted logs and output, and for doing advanced string manipulations. 39 | For instance, one may read and write data from/to `.csv` and `.dat` 40 | files, represent numbers as strings inside of a string expression, 41 | efficiently write text to a `.log` file, and dynamically manipulate 42 | strings with a `String` type (including casting between numeric and 43 | string data). 44 | 45 | @note The module is fully self-contained, with no external 46 | dependencies, and is written to be portable and compliant to the 47 | Fortran 2018 standard such that no special extensions or compiler 48 | options should be required. The public interfaces accept all intrinsic 49 | numeric types (`integer`, `real`, and `complex`) and all standard kinds 50 | provided by the intrinsic `iso_fortran_env` module (`int8`, `int16`, 51 | `int32`, `int64`, `real32`, `real64`, and `real128`). All array-based 52 | routines additionally support up to rank 15. 53 | 54 | ## How to Use 55 | 56 | To use `io_fortran_lib` with your 57 | [fpm](https://github.com/fortran-lang/fpm) project, add the following 58 | lines to your `fpm.toml` file and `use` the module in your program 59 | units to access the routines: 60 | 61 | ```toml 62 | [dependencies] 63 | IO-Fortran-Library = { git="https://github.com/acbbullock/IO-Fortran-Library", branch="main" } 64 | ``` 65 | 66 | See the [important user information](page/UserInfo/index.html) and 67 | [reference guide](page/Ref/index.html) for information about calling 68 | the [routines](lists/procedures.html), and further see the 69 | [tutorials](page/Examples/index.html) for complete example programs. 70 | 71 | ## License 72 | 73 | All source code referenced is distributed under the 74 | [MIT license](https://github.com/acbbullock/IO-Fortran-Library/blob/main/LICENCE) 75 | and available at 76 | [Github](https://github.com/acbbullock/IO-Fortran-Library). 77 | 78 | ## Contact 79 | 80 | For bug fixes or feature requests, feel free to open an issue at the 81 | [project repository](https://github.com/acbbullock/IO-Fortran-Library) 82 | or contact [acb.bullock@gmail.com](mailto:acb.bullock@gmail.com). 83 | -------------------------------------------------------------------------------- /fpm.toml: -------------------------------------------------------------------------------- 1 | name = "IO-Fortran-Library" 2 | version = "1.3.1" 3 | license = "MIT" 4 | author = "Austin C Bullock" 5 | maintainer = "acb.bullock@gmail.com" 6 | copyright = "© 2022, Austin C Bullock" 7 | 8 | executable = [ 9 | { name = "benchmark-array", source-dir = "benchmarks", main = "benchmark-array.f90" }, 10 | { name = "benchmark-ints", source-dir = "benchmarks", main = "benchmark-ints.f90" } 11 | ] 12 | 13 | [build] 14 | auto-executables = false 15 | auto-tests = true 16 | auto-examples = true 17 | 18 | [install] 19 | library = true 20 | -------------------------------------------------------------------------------- /src/io_fortran_lib/join_split_impl.f90: -------------------------------------------------------------------------------- 1 | submodule (io_fortran_lib) join_split 2 | !--------------------------------------------------------------------------------------------------------------------- 3 | !! This submodule provides module procedure implementations for the **public interfaces** `join` and `split`. 4 | !--------------------------------------------------------------------------------------------------------------------- 5 | implicit none (type, external) 6 | 7 | contains ! Procedure bodies for module subprograms <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>< 8 | 9 | module procedure join_char 10 | type(String) :: temp_String 11 | character(len=:), allocatable :: separator_ 12 | 13 | if ( .not. present(separator) ) then 14 | separator_ = SPACE 15 | else 16 | separator_ = separator 17 | end if 18 | 19 | temp_String = join(String(tokens), separator=separator_) 20 | 21 | if ( temp_String%len() < 1 ) then 22 | new = EMPTY_STR 23 | else 24 | new = temp_String%s 25 | end if 26 | end procedure join_char 27 | 28 | module procedure join_string 29 | type(String) :: token_pair(2) 30 | character(len=:), allocatable :: separator_ 31 | integer(i64) :: num_tokens 32 | 33 | num_tokens = size(tokens, kind=i64) 34 | 35 | if ( num_tokens == 1_i64 ) then 36 | if ( tokens(1_i64)%len64() < 1_i64 ) then 37 | new%s = EMPTY_STR; return 38 | else 39 | new%s = tokens(1_i64)%s; return 40 | end if 41 | end if 42 | 43 | if ( .not. present(separator) ) then 44 | separator_ = SPACE 45 | else 46 | separator_ = separator 47 | end if 48 | 49 | if ( num_tokens > 500_i64 ) then 50 | new = join(tokens=[ join(tokens(:num_tokens/2_i64), separator_), & 51 | join(tokens(1_i64+num_tokens/2_i64:), separator_) ], separator=separator_) 52 | else 53 | call new%join_base(tokens=tokens, separator=separator_) 54 | end if 55 | end procedure join_string 56 | 57 | module procedure split_char 58 | character(len=:), allocatable :: separator_ 59 | 60 | if ( .not. present(separator) ) then 61 | separator_ = SPACE 62 | else 63 | separator_ = separator 64 | end if 65 | 66 | tokens = split(String(substring), separator=separator_) 67 | end procedure split_char 68 | 69 | module procedure split_string 70 | character(len=:), allocatable :: separator_ 71 | integer(i64) :: substring_len, l, i 72 | integer :: sep_len, num_seps, sep, token, current 73 | 74 | substring_len=0_i64; l=0_i64; i=0_i64; sep_len=0; num_seps=0; sep=0; token=0; current=0 75 | 76 | substring_len = substring%len64() 77 | 78 | if ( substring_len < 1_i64 ) then 79 | allocate( tokens(1) ); tokens(1)%s = EMPTY_STR; return 80 | end if 81 | 82 | if ( .not. present(separator) ) then 83 | separator_ = SPACE 84 | else 85 | separator_ = separator 86 | end if 87 | 88 | sep_len = len(separator_) 89 | 90 | if ( sep_len == 0 ) then 91 | allocate( tokens(substring_len) ) 92 | do i = 1_i64, substring_len 93 | tokens(i)%s = substring%s(i:i) 94 | end do 95 | return 96 | end if 97 | 98 | num_seps = substring%count(match=separator_) 99 | 100 | if ( num_seps == 0 ) then 101 | allocate( tokens(1) ); tokens(1)%s = substring%s; return 102 | end if 103 | 104 | allocate( tokens(num_seps + 1) ) 105 | 106 | sep = iachar(separator_(1:1)) 107 | 108 | i = 1_i64; l = 1_i64; token = 1; positional_transfers: do 109 | current = iachar(substring%s(i:i)) 110 | 111 | if ( current /= sep ) then 112 | i = i + 1_i64; cycle 113 | end if 114 | 115 | if ( sep_len == 1 ) then 116 | tokens(token)%s = substring%s(l:i-1) 117 | if ( token == num_seps ) then 118 | tokens(num_seps+1)%s = substring%s(i+1:); return 119 | end if 120 | token = token + 1; i = i + 1_i64; l = i; cycle 121 | else 122 | if ( substring%s(i:i+sep_len-1) == separator_ ) then 123 | tokens(token)%s = substring%s(l:i-1) 124 | if ( token == num_seps ) then 125 | tokens(num_seps+1)%s = substring%s(i+sep_len:); return 126 | end if 127 | token = token + 1; i = i + sep_len; l = i; cycle 128 | else 129 | i = i + 1_i64; cycle 130 | end if 131 | end if 132 | end do positional_transfers 133 | end procedure split_string 134 | end submodule join_split 135 | -------------------------------------------------------------------------------- /src/io_fortran_lib/operators_impl.f90: -------------------------------------------------------------------------------- 1 | submodule (io_fortran_lib) operators 2 | !--------------------------------------------------------------------------------------------------------------------- 3 | !! This submodule provides module procedure implementations for the **public interfaces** `operator(//)`, 4 | !! `operator(+)`, `operator(-)`, `operator(**)`, `operator(==)`, and `operator(/=)`. 5 | !--------------------------------------------------------------------------------------------------------------------- 6 | implicit none (type, external) 7 | 8 | contains ! Procedure bodies for module subprograms <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>< 9 | 10 | module procedure string_concatenation 11 | if ( Stringl%len() < 1 ) then 12 | if ( Stringr%len() < 1 ) then 13 | new%s = EMPTY_STR; return 14 | else 15 | new%s = Stringr%s; return 16 | end if 17 | end if 18 | 19 | if ( Stringr%len() < 1 ) then 20 | new%s = Stringl%s; return 21 | end if 22 | 23 | new%s = Stringl%s//Stringr%s 24 | end procedure string_concatenation 25 | 26 | module procedure string_char_concatenation 27 | if ( Stringl%len() < 1 ) then 28 | if ( len(charsr) < 1 ) then 29 | new%s = EMPTY_STR; return 30 | else 31 | new%s = charsr; return 32 | end if 33 | end if 34 | 35 | if ( len(charsr) < 1 ) then 36 | new%s = Stringl%s; return 37 | end if 38 | 39 | new%s = Stringl%s//charsr 40 | end procedure string_char_concatenation 41 | 42 | module procedure char_string_concatenation 43 | if ( len(charsl) < 1 ) then 44 | if ( Stringr%len() < 1 ) then 45 | new%s = EMPTY_STR; return 46 | else 47 | new%s = Stringr%s; return 48 | end if 49 | end if 50 | 51 | if ( Stringr%len() < 1 ) then 52 | new%s = charsl; return 53 | end if 54 | 55 | new%s = charsl//Stringr%s 56 | end procedure char_string_concatenation 57 | 58 | module procedure char_concat_plus 59 | new = charsl//charsr 60 | end procedure char_concat_plus 61 | 62 | module procedure string_concat_plus 63 | if ( Stringl%len() < 1 ) then 64 | if ( Stringr%len() < 1 ) then 65 | new%s = EMPTY_STR; return 66 | else 67 | new%s = Stringr%s; return 68 | end if 69 | end if 70 | 71 | if ( Stringr%len() < 1 ) then 72 | new%s = Stringl%s; return 73 | end if 74 | 75 | new%s = Stringl%s//Stringr%s 76 | end procedure string_concat_plus 77 | 78 | module procedure string_char_concat_plus 79 | if ( Stringl%len() < 1 ) then 80 | if ( len(charsr) < 1 ) then 81 | new%s = EMPTY_STR; return 82 | else 83 | new%s = charsr; return 84 | end if 85 | end if 86 | 87 | if ( len(charsr) < 1 ) then 88 | new%s = Stringl%s; return 89 | end if 90 | 91 | new%s = Stringl%s//charsr 92 | end procedure string_char_concat_plus 93 | 94 | module procedure char_string_concat_plus 95 | if ( len(charsl) < 1 ) then 96 | if ( Stringr%len() < 1 ) then 97 | new%s = EMPTY_STR; return 98 | else 99 | new%s = Stringr%s; return 100 | end if 101 | end if 102 | 103 | if ( Stringr%len() < 1 ) then 104 | new%s = charsl; return 105 | end if 106 | 107 | new%s = charsl//Stringr%s 108 | end procedure char_string_concat_plus 109 | 110 | module procedure char_excision 111 | type(String) :: Stringl 112 | 113 | Stringl%s = charsl 114 | 115 | if ( Stringl%len() < 1 ) then 116 | new%s = EMPTY_STR; return 117 | end if 118 | 119 | if ( len(charsr) < 1 ) then 120 | new%s = Stringl%s; return 121 | end if 122 | 123 | new = Stringl%replace(match=charsr, substring=EMPTY_STR) 124 | end procedure char_excision 125 | 126 | module procedure string_excision 127 | if ( Stringl%len() < 1 ) then 128 | new%s = EMPTY_STR; return 129 | end if 130 | 131 | if ( Stringr%len() < 1 ) then 132 | new%s = Stringl%s; return 133 | end if 134 | 135 | new = Stringl%replace(match=Stringr%s, substring=EMPTY_STR) 136 | end procedure string_excision 137 | 138 | module procedure string_char_excision 139 | if ( Stringl%len() < 1 ) then 140 | new%s = EMPTY_STR; return 141 | end if 142 | 143 | if ( len(charsr) < 1 ) then 144 | new%s = Stringl%s; return 145 | end if 146 | 147 | new = Stringl%replace(match=charsr, substring=EMPTY_STR) 148 | end procedure string_char_excision 149 | 150 | module procedure char_string_excision 151 | type(String) :: Stringl 152 | 153 | Stringl%s = charsl 154 | 155 | if ( Stringl%len() < 1 ) then 156 | new%s = EMPTY_STR; return 157 | end if 158 | 159 | if ( Stringr%len() < 1 ) then 160 | new%s = Stringl%s; return 161 | end if 162 | 163 | new = Stringl%replace(match=Stringr%s, substring=EMPTY_STR) 164 | end procedure char_string_excision 165 | 166 | module procedure repeat_chars 167 | new = repeat(char_base, ncopies=ncopies) 168 | end procedure repeat_chars 169 | 170 | module procedure repeat_String 171 | if ( String_base%len() < 1 ) then 172 | new%s = EMPTY_STR; return 173 | end if 174 | 175 | new%s = repeat(String_base%s, ncopies=ncopies) 176 | end procedure repeat_String 177 | 178 | module procedure string_equivalence 179 | integer :: Stringl_len, Stringr_len 180 | 181 | Stringl_len = Stringl%len() 182 | Stringr_len = Stringr%len() 183 | 184 | if ( Stringl_len /= Stringr_len ) then 185 | equal = .false.; return 186 | end if 187 | 188 | if ( Stringl_len < 1 ) then 189 | equal = .true.; return 190 | end if 191 | 192 | equal = ( Stringl%s == Stringr%s ) 193 | end procedure string_equivalence 194 | 195 | module procedure string_char_equivalence 196 | integer :: Stringl_len, charsr_len 197 | 198 | Stringl_len = Stringl%len() 199 | charsr_len = len(charsr) 200 | 201 | if ( Stringl_len /= charsr_len ) then 202 | equal = .false.; return 203 | end if 204 | 205 | if ( Stringl_len < 1 ) then 206 | equal = .true.; return 207 | end if 208 | 209 | equal = ( Stringl%s == charsr ) 210 | end procedure string_char_equivalence 211 | 212 | module procedure char_string_equivalence 213 | integer :: charsl_len, Stringr_len 214 | 215 | charsl_len = len(charsl) 216 | Stringr_len = Stringr%len() 217 | 218 | if ( charsl_len /= Stringr_len ) then 219 | equal = .false.; return 220 | end if 221 | 222 | if ( charsl_len < 1 ) then 223 | equal = .true.; return 224 | end if 225 | 226 | equal = ( charsl == Stringr%s ) 227 | end procedure char_string_equivalence 228 | 229 | module procedure string_nonequivalence 230 | integer :: Stringl_len, Stringr_len 231 | 232 | Stringl_len = Stringl%len() 233 | Stringr_len = Stringr%len() 234 | 235 | if ( Stringl_len /= Stringr_len ) then 236 | unequal = .true.; return 237 | end if 238 | 239 | if ( Stringl_len < 1 ) then 240 | unequal = .false.; return 241 | end if 242 | 243 | unequal = ( Stringl%s /= Stringr%s ) 244 | end procedure string_nonequivalence 245 | 246 | module procedure string_char_nonequivalence 247 | integer :: Stringl_len, charsr_len 248 | 249 | Stringl_len = Stringl%len() 250 | charsr_len = len(charsr) 251 | 252 | if ( Stringl_len /= charsr_len ) then 253 | unequal = .true.; return 254 | end if 255 | 256 | if ( Stringl_len < 1 ) then 257 | unequal = .false.; return 258 | end if 259 | 260 | unequal = ( Stringl%s /= charsr ) 261 | end procedure string_char_nonequivalence 262 | 263 | module procedure char_string_nonequivalence 264 | integer :: charsl_len, Stringr_len 265 | 266 | charsl_len = len(charsl) 267 | Stringr_len = Stringr%len() 268 | 269 | if ( charsl_len /= Stringr_len ) then 270 | unequal = .true.; return 271 | end if 272 | 273 | if ( charsl_len < 1 ) then 274 | unequal = .false.; return 275 | end if 276 | 277 | unequal = ( charsl /= Stringr%s ) 278 | end procedure char_string_nonequivalence 279 | end submodule operators 280 | -------------------------------------------------------------------------------- /src/modules/randoms_mod.f90: -------------------------------------------------------------------------------- 1 | module randoms 2 | !--------------------------------------------------------------------------------------------------------------------- 3 | !! This module provides Gaussian sampling utility routines for use in unit testing. 4 | !--------------------------------------------------------------------------------------------------------------------- 5 | use, intrinsic :: iso_fortran_env, only: r128=>real128, r64=>real64, r32=>real32 6 | implicit none (type, external) 7 | private 8 | 9 | ! Public API list ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | public :: random_gauss 11 | 12 | ! Definitions and Interfaces ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 13 | 14 | interface gauss ! Submodule gaussian_sampling 15 | !------------------------------------------------------------------------------------------------------------------- 16 | !! Samples random numbers from the standard Normal (Gaussian) Distribution with the given mean and sigma. 17 | !! Uses the Acceptance-complement ratio from W. Hoermann and G. Derflinger. 18 | !! This is one of the fastest existing methods for generating normal random variables. 19 | !! 20 | !! REFERENCE: - W. Hoermann and G. Derflinger (1990): 21 | !! The ACR Method for generating normal random variables, 22 | !! OR Spektrum 12 (1990), 181-185. 23 | !! 24 | !! Implementation taken from 25 | !! UNURAN (c) 2000 W. Hoermann & J. Leydold, Institut f. Statistik, WU Wien 26 | !--------------------------------------------------------------------------------------------------------------- 27 | impure real(r128) module function gauss_r128(mu, sig) result(gauss_res) 28 | real(r128), intent(in) :: mu, sig 29 | end function gauss_r128 30 | impure real(r64) module function gauss_r64(mu, sig) result(gauss_res) 31 | real(r64), intent(in) :: mu, sig 32 | end function gauss_r64 33 | impure real(r32) module function gauss_r32(mu, sig) result(gauss_res) 34 | real(r32), intent(in) :: mu, sig 35 | end function gauss_r32 36 | end interface 37 | 38 | interface random_gauss ! Submodule gaussian_sampling 39 | !------------------------------------------------------------------------------------------------------------------- 40 | !! Applies `gauss` to whole arrays and scalars. 41 | !------------------------------------------------------------------------------------------------------------------- 42 | impure elemental module subroutine random_gauss_r128(x, mu, sig) 43 | real(r128), intent(inout) :: x 44 | real(r128), intent(in) :: mu, sig 45 | end subroutine random_gauss_r128 46 | impure elemental module subroutine random_gauss_r64(x, mu, sig) 47 | real(r64), intent(inout) :: x 48 | real(r64), intent(in) :: mu, sig 49 | end subroutine random_gauss_r64 50 | impure elemental module subroutine random_gauss_r32(x, mu, sig) 51 | real(r32), intent(inout) :: x 52 | real(r32), intent(in) :: mu, sig 53 | end subroutine random_gauss_r32 54 | end interface 55 | end module randoms 56 | -------------------------------------------------------------------------------- /test/test.ps1: -------------------------------------------------------------------------------- 1 | fpm clean --all 2 | fpm test --compiler gfortran --flag "-std=f2018 -mtune=generic -march=x86-64 -g -Wall -Wextra -Werror=implicit-interface -fPIC -fmax-errors=1 -fbounds-check -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -ffree-form -cpp -D R4 -D I1" 3 | fpm test --compiler gfortran --flag "-std=f2018 -mtune=generic -march=x86-64 -g -Wall -Wextra -Werror=implicit-interface -fPIC -fmax-errors=1 -fbounds-check -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -ffree-form -cpp -D R4 -D I2" 4 | fpm test --compiler gfortran --flag "-std=f2018 -mtune=generic -march=x86-64 -g -Wall -Wextra -Werror=implicit-interface -fPIC -fmax-errors=1 -fbounds-check -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -ffree-form -cpp -D R8 -D I4" 5 | fpm test --compiler gfortran --flag "-std=f2018 -mtune=generic -march=x86-64 -g -Wall -Wextra -Werror=implicit-interface -fPIC -fmax-errors=1 -fbounds-check -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -ffree-form -cpp -D R16 -D I8" 6 | 7 | fpm clean --all 8 | fpm test --compiler gfortran --flag "-std=f2018 -O3 -cpp -D R4 -D I1" 9 | fpm test --compiler gfortran --flag "-std=f2018 -O3 -cpp -D R4 -D I2" 10 | fpm test --compiler gfortran --flag "-std=f2018 -O3 -cpp -D R8 -D I4" 11 | fpm test --compiler gfortran --flag "-std=f2018 -O3 -cpp -D R16 -D I8" 12 | 13 | fpm clean --all 14 | fpm test --compiler ifx --flag "/stand:f18 /warn:all /check:all /error-limit:1 /Od /Z7 /traceback /assume:byterecl /fpp /define:R4 /define:I1" 15 | fpm test --compiler ifx --flag "/stand:f18 /warn:all /check:all /error-limit:1 /Od /Z7 /traceback /assume:byterecl /fpp /define:R4 /define:I2" 16 | fpm test --compiler ifx --flag "/stand:f18 /warn:all /check:all /error-limit:1 /Od /Z7 /traceback /assume:byterecl /fpp /define:R8 /define:I4" 17 | fpm test --compiler ifx --flag "/stand:f18 /warn:all /check:all /error-limit:1 /Od /Z7 /traceback /assume:byterecl /fpp /define:R16 /define:I8" 18 | 19 | fpm clean --all 20 | fpm test --compiler ifx --flag "/stand:f18 /O3 /fpp /define:R4 /define:I1" 21 | fpm test --compiler ifx --flag "/stand:f18 /O3 /fpp /define:R4 /define:I2" 22 | fpm test --compiler ifx --flag "/stand:f18 /O3 /fpp /define:R8 /define:I4" 23 | fpm test --compiler ifx --flag "/stand:f18 /O3 /fpp /define:R16 /define:I8" 24 | 25 | fpm clean --all 26 | fpm test --compiler ifort --flag "/stand:f18 /warn:all /check:all /error-limit:1 /Od /Z7 /traceback /assume:byterecl /fpp /define:R4 /define:I1" 27 | fpm test --compiler ifort --flag "/stand:f18 /warn:all /check:all /error-limit:1 /Od /Z7 /traceback /assume:byterecl /fpp /define:R4 /define:I2" 28 | fpm test --compiler ifort --flag "/stand:f18 /warn:all /check:all /error-limit:1 /Od /Z7 /traceback /assume:byterecl /fpp /define:R8 /define:I4" 29 | fpm test --compiler ifort --flag "/stand:f18 /warn:all /check:all /error-limit:1 /Od /Z7 /traceback /assume:byterecl /fpp /define:R16 /define:I8" 30 | 31 | fpm clean --all 32 | fpm test --compiler ifort --flag "/stand:f18 /O3 /fpp /define:R4 /define:I1" 33 | fpm test --compiler ifort --flag "/stand:f18 /O3 /fpp /define:R4 /define:I2" 34 | fpm test --compiler ifort --flag "/stand:f18 /O3 /fpp /define:R8 /define:I4" 35 | fpm test --compiler ifort --flag "/stand:f18 /O3 /fpp /define:R16 /define:I8" 36 | 37 | fpm clean --all 38 | -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | fpm clean --all 2 | fpm test --compiler gfortran --flag "-std=f2018 -mtune=generic -march=x86-64 -g -Wall -Wextra -Werror=implicit-interface -fPIC -fmax-errors=1 -fbounds-check -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -ffree-form -cpp -D R4 -D I1" 3 | fpm test --compiler gfortran --flag "-std=f2018 -mtune=generic -march=x86-64 -g -Wall -Wextra -Werror=implicit-interface -fPIC -fmax-errors=1 -fbounds-check -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -ffree-form -cpp -D R4 -D I2" 4 | fpm test --compiler gfortran --flag "-std=f2018 -mtune=generic -march=x86-64 -g -Wall -Wextra -Werror=implicit-interface -fPIC -fmax-errors=1 -fbounds-check -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -ffree-form -cpp -D R8 -D I4" 5 | fpm test --compiler gfortran --flag "-std=f2018 -mtune=generic -march=x86-64 -g -Wall -Wextra -Werror=implicit-interface -fPIC -fmax-errors=1 -fbounds-check -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -ffree-form -cpp -D R16 -D I8" 6 | 7 | fpm clean --all 8 | fpm test --compiler gfortran --flag "-std=f2018 -O3 -cpp -D R4 -D I1" 9 | fpm test --compiler gfortran --flag "-std=f2018 -O3 -cpp -D R4 -D I2" 10 | fpm test --compiler gfortran --flag "-std=f2018 -O3 -cpp -D R8 -D I4" 11 | fpm test --compiler gfortran --flag "-std=f2018 -O3 -cpp -D R16 -D I8" 12 | 13 | fpm clean --all 14 | fpm test --compiler ifx --flag "-stand f18 -warn all -check all -check nouninit -error-limit 1 -O0 -g -traceback -assume byterecl -fpp -D R4 -D I1" 15 | fpm test --compiler ifx --flag "-stand f18 -warn all -check all -check nouninit -error-limit 1 -O0 -g -traceback -assume byterecl -fpp -D R4 -D I2" 16 | fpm test --compiler ifx --flag "-stand f18 -warn all -check all -check nouninit -error-limit 1 -O0 -g -traceback -assume byterecl -fpp -D R8 -D I4" 17 | fpm test --compiler ifx --flag "-stand f18 -warn all -check all -check nouninit -error-limit 1 -O0 -g -traceback -assume byterecl -fpp -D R16 -D I8" 18 | 19 | fpm clean --all 20 | fpm test --compiler ifx --flag "-stand f18 -O3 -fpp -D R4 -D I1" 21 | fpm test --compiler ifx --flag "-stand f18 -O3 -fpp -D R4 -D I2" 22 | fpm test --compiler ifx --flag "-stand f18 -O3 -fpp -D R8 -D I4" 23 | fpm test --compiler ifx --flag "-stand f18 -O3 -fpp -D R16 -D I8" 24 | 25 | fpm clean --all 26 | fpm test --compiler ifort --flag "-stand f18 -warn all -check all -error-limit 1 -O0 -g -traceback -assume byterecl -fpp -D R4 -D I1" 27 | fpm test --compiler ifort --flag "-stand f18 -warn all -check all -error-limit 1 -O0 -g -traceback -assume byterecl -fpp -D R4 -D I2" 28 | fpm test --compiler ifort --flag "-stand f18 -warn all -check all -error-limit 1 -O0 -g -traceback -assume byterecl -fpp -D R8 -D I4" 29 | fpm test --compiler ifort --flag "-stand f18 -warn all -check all -error-limit 1 -O0 -g -traceback -assume byterecl -fpp -D R16 -D I8" 30 | 31 | fpm clean --all 32 | fpm test --compiler ifort --flag "-stand f18 -O3 -fpp -D R4 -D I1" 33 | fpm test --compiler ifort --flag "-stand f18 -O3 -fpp -D R4 -D I2" 34 | fpm test --compiler ifort --flag "-stand f18 -O3 -fpp -D R8 -D I4" 35 | fpm test --compiler ifort --flag "-stand f18 -O3 -fpp -D R16 -D I8" 36 | 37 | fpm clean --all 38 | -------------------------------------------------------------------------------- /test/units/array-test-i.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use, intrinsic :: iso_fortran_env, only: compiler_version, compiler_options 3 | use kinds, only: rk, ik 4 | use io_fortran_lib, only: String, cast, LF, SPACE, str, operator(+), operator(**) 5 | use randoms, only: random_gauss 6 | implicit none (type, external) 7 | 8 | integer, parameter :: n = 2000 9 | character(len=*), parameter :: logfile = "./test/tests.log" 10 | 11 | character(len=10) :: date="", time="" 12 | type(String) :: testlog, errlog 13 | logical :: test_succeeded=.true., all_passing=.true. 14 | 15 | character(len=512) :: errmsg="" 16 | integer :: stat=0 17 | 18 | type(String), allocatable :: string_var(:) 19 | real(rk), allocatable :: x(:) 20 | integer(ik), allocatable :: i(:), j(:) 21 | 22 | call random_init(repeatable=.false., image_distinct=.true.) 23 | call date_and_time(date=date, time=time) 24 | 25 | testlog = String("RUNNING TESTS (String/cast) | date: " + trim(adjustl(date)) + & 26 | " | time: " + time + & 27 | " | int kind: " + str(ik) ) 28 | call testlog%push(LF + "-"**testlog%len() + LF) 29 | 30 | errlog = String(" ERROR LOG" + LF + " ---------" + LF) 31 | 32 | allocate( string_var(n), stat=stat, errmsg=errmsg ) 33 | if ( stat /= 0 ) error stop LF + "FATAL: Allocation failure at line " + str(__LINE__ - 1) + & 34 | ' of file "' + __FILE__ + '".' 35 | allocate( x(n), source=0e0_rk, stat=stat, errmsg=errmsg ) 36 | if ( stat /= 0 ) error stop LF + "FATAL: Allocation failure at line " + str(__LINE__ - 1) + & 37 | ' of file "' + __FILE__ + '".' 38 | allocate( i(n), j(n), source=0_ik, stat=stat, errmsg=errmsg ) 39 | if ( stat /= 0 ) error stop LF + "FATAL: Allocation failure at line " + str(__LINE__ - 1) + & 40 | ' of file "' + __FILE__ + '".' 41 | 42 | 1 continue 43 | 44 | call random_gauss(x,0e0_rk,1e0_rk); i = floor(huge(1_ik)*x, ik) + 1_ik 45 | call cast(String(i, fmt="i"), into=j, fmt="i") 46 | test_succeeded = all(i == j) 47 | if ( .not. test_succeeded ) then 48 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 49 | end if 50 | 51 | 2 if ( .not. test_succeeded ) all_passing = .false. 52 | 53 | call random_gauss(x,0e0_rk,1e0_rk); i = floor(huge(1_ik)*x, ik) + 1_ik 54 | call cast(String(i, fmt="z"), into=j, fmt="z") 55 | test_succeeded = all(i == j) 56 | if ( .not. test_succeeded ) then 57 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 58 | end if 59 | 60 | 3 if ( .not. test_succeeded ) all_passing = .false. 61 | 62 | call random_gauss(x,0e0_rk,1e0_rk); i = floor(huge(1_ik)*x, ik) + 1_ik 63 | call cast(i, into=string_var, fmt="i"); call cast(string_var, into=j, fmt="i") 64 | test_succeeded = all(i == j) 65 | if ( .not. test_succeeded ) then 66 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 67 | end if 68 | 69 | 4 if ( .not. test_succeeded ) all_passing = .false. 70 | 71 | call random_gauss(x,0e0_rk,1e0_rk); i = floor(huge(1_ik)*x, ik) + 1_ik 72 | call cast(i, into=string_var, fmt="z"); call cast(string_var, into=j, fmt="z") 73 | test_succeeded = all(i == j) 74 | if ( .not. test_succeeded ) then 75 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 76 | end if 77 | 78 | 5 if ( .not. test_succeeded ) all_passing = .false. 79 | 80 | if ( all_passing ) then 81 | call testlog%push('All tests are "PASSING" with compiler "' + compiler_version() + '" ' + & 82 | 'using compiler options "' + compiler_options() + '".' + LF) 83 | else 84 | call testlog%push('Some tests are "FAILING" with compiler "' + compiler_version() + '" ' + & 85 | 'using compiler options "' + compiler_options() + '".' + LF) 86 | end if 87 | 88 | call testlog%echo(logfile) 89 | write(*,"(DT)") testlog 90 | 91 | if ( .not. all_passing ) then 92 | call errlog%echo(logfile) 93 | write(*,"(DT)") errlog 94 | end if 95 | end program main 96 | -------------------------------------------------------------------------------- /test/units/array-test-r.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use, intrinsic :: iso_fortran_env, only: compiler_version, compiler_options 3 | use kinds, only: rk 4 | use io_fortran_lib, only: String, cast, LF, SPACE, str, operator(+), operator(**) 5 | use randoms, only: random_gauss 6 | implicit none (type, external) 7 | 8 | real(rk), parameter :: tol = 3.0_rk*epsilon(1e0_rk) 9 | integer, parameter :: n = 2000 10 | character(len=*), parameter :: logfile = "./test/tests.log" 11 | 12 | character(len=10) :: date="", time="" 13 | type(String) :: testlog, errlog 14 | logical :: test_succeeded=.true., all_passing=.true. 15 | 16 | character(len=512) :: errmsg="" 17 | integer :: stat=0 18 | 19 | type(String), allocatable :: string_var(:) 20 | real(rk), allocatable :: x(:), y(:) 21 | 22 | call random_init(repeatable=.false., image_distinct=.true.) 23 | call date_and_time(date=date, time=time) 24 | 25 | testlog = String("RUNNING TESTS (String/cast) | date: " + trim(adjustl(date)) + & 26 | " | time: " + time + & 27 | " | real kind: " + str(rk) ) 28 | call testlog%push(LF + "-"**testlog%len() + LF) 29 | 30 | errlog = String(" ERROR LOG" + LF + " ---------" + LF) 31 | 32 | allocate( string_var(n), stat=stat, errmsg=errmsg ) 33 | if ( stat /= 0 ) error stop LF + "FATAL: Allocation failure at line " + str(__LINE__ - 1) + & 34 | ' of file "' + __FILE__ + '".' 35 | allocate( x(n), y(n), source=0e0_rk, stat=stat, errmsg=errmsg ) 36 | if ( stat /= 0 ) error stop LF + "FATAL: Allocation failure at line " + str(__LINE__ - 1) + & 37 | ' of file "' + __FILE__ + '".' 38 | 39 | 1 continue 40 | 41 | call random_gauss(x,0e0_rk,1e0_rk) 42 | call cast(String(x, locale="US", fmt="e"), into=y, locale="US", fmt="e") 43 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 44 | if ( .not. test_succeeded ) then 45 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 46 | end if 47 | 48 | 2 if ( .not. test_succeeded ) all_passing = .false. 49 | 50 | call random_gauss(x,0e0_rk,1e0_rk) 51 | call cast(String(x, locale="US", fmt="f"), into=y, locale="US", fmt="f") 52 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 53 | if ( .not. test_succeeded ) then 54 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 55 | end if 56 | 57 | 3 if ( .not. test_succeeded ) all_passing = .false. 58 | 59 | call random_gauss(x,0e0_rk,1e0_rk) 60 | call cast(String(x, locale="US", fmt="z"), into=y, locale="US", fmt="z") 61 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 62 | if ( .not. test_succeeded ) then 63 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 64 | end if 65 | 66 | 4 if ( .not. test_succeeded ) all_passing = .false. 67 | 68 | call random_gauss(x,0e0_rk,1e0_rk) 69 | call cast(String(x, locale="EU", fmt="e"), into=y, locale="EU", fmt="e") 70 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 71 | if ( .not. test_succeeded ) then 72 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 73 | end if 74 | 75 | 5 if ( .not. test_succeeded ) all_passing = .false. 76 | 77 | call random_gauss(x,0e0_rk,1e0_rk) 78 | call cast(String(x, locale="EU", fmt="f"), into=y, locale="EU", fmt="f") 79 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 80 | if ( .not. test_succeeded ) then 81 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 82 | end if 83 | 84 | 6 if ( .not. test_succeeded ) all_passing = .false. 85 | 86 | call random_gauss(x,0e0_rk,1e0_rk) 87 | call cast(String(x, locale="EU", fmt="z"), into=y, locale="EU", fmt="z") 88 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 89 | if ( .not. test_succeeded ) then 90 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 91 | end if 92 | 93 | 7 if ( .not. test_succeeded ) all_passing = .false. 94 | 95 | call random_gauss(x,0e0_rk,1e0_rk) 96 | call cast(x, into=string_var, locale="US", fmt="e"); call cast(string_var, into=y, locale="US", fmt="e") 97 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 98 | if ( .not. test_succeeded ) then 99 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 100 | end if 101 | 102 | 8 if ( .not. test_succeeded ) all_passing = .false. 103 | 104 | call random_gauss(x,0e0_rk,1e0_rk) 105 | call cast(x, into=string_var, locale="US", fmt="f"); call cast(string_var, into=y, locale="US", fmt="f") 106 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 107 | if ( .not. test_succeeded ) then 108 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 109 | end if 110 | 111 | 9 if ( .not. test_succeeded ) all_passing = .false. 112 | 113 | call random_gauss(x,0e0_rk,1e0_rk) 114 | call cast(x, into=string_var, locale="US", fmt="z"); call cast(string_var, into=y, locale="US", fmt="z") 115 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 116 | if ( .not. test_succeeded ) then 117 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 118 | end if 119 | 120 | 10 if ( .not. test_succeeded ) all_passing = .false. 121 | 122 | call random_gauss(x,0e0_rk,1e0_rk) 123 | call cast(x, into=string_var, locale="EU", fmt="e"); call cast(string_var, into=y, locale="EU", fmt="e") 124 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 125 | if ( .not. test_succeeded ) then 126 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 127 | end if 128 | 129 | 11 if ( .not. test_succeeded ) all_passing = .false. 130 | 131 | call random_gauss(x,0e0_rk,1e0_rk) 132 | call cast(x, into=string_var, locale="EU", fmt="f"); call cast(string_var, into=y, locale="EU", fmt="f") 133 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 134 | if ( .not. test_succeeded ) then 135 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 136 | end if 137 | 138 | 12 if ( .not. test_succeeded ) all_passing = .false. 139 | 140 | call random_gauss(x,0e0_rk,1e0_rk) 141 | call cast(x, into=string_var, locale="EU", fmt="z"); call cast(string_var, into=y, locale="EU", fmt="z") 142 | test_succeeded = (maxval( abs(x-y)/abs(x) ) < tol) 143 | if ( .not. test_succeeded ) then 144 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 145 | end if 146 | 147 | 13 if ( .not. test_succeeded ) all_passing = .false. 148 | 149 | if ( all_passing ) then 150 | call testlog%push('All tests are "PASSING" with compiler "' + compiler_version() + '" ' + & 151 | 'using compiler options "' + compiler_options() + '".' + LF) 152 | else 153 | call testlog%push('Some tests are "FAILING" with compiler "' + compiler_version() + '" ' + & 154 | 'using compiler options "' + compiler_options() + '".' + LF) 155 | end if 156 | 157 | call testlog%echo(logfile) 158 | write(*,"(DT)") testlog 159 | 160 | if ( .not. all_passing ) then 161 | call errlog%echo(logfile) 162 | write(*,"(DT)") errlog 163 | end if 164 | end program main 165 | -------------------------------------------------------------------------------- /test/units/kinds_mod.f90: -------------------------------------------------------------------------------- 1 | module kinds 2 | use, intrinsic :: iso_fortran_env, only: r128=>real128, r64=>real64, r32=>real32, & ! ISO standard real kinds 3 | i64=>int64, i32=>int32, i16=>int16, i8=>int8 ! ISO standard int kinds 4 | implicit none (type, external) 5 | private 6 | 7 | #ifdef R16 8 | integer, public, parameter :: rk = r128 9 | #elif R8 10 | integer, public, parameter :: rk = r64 11 | #else 12 | integer, public, parameter :: rk = r32 13 | #endif 14 | 15 | #ifdef I8 16 | integer, public, parameter :: ik = i64 17 | #elif I2 18 | integer, public, parameter :: ik = i16 19 | #elif I1 20 | integer, public, parameter :: ik = i8 21 | #else 22 | integer, public, parameter :: ik = i32 23 | #endif 24 | 25 | end module kinds 26 | -------------------------------------------------------------------------------- /test/units/scalar-test-i.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use, intrinsic :: iso_fortran_env, only: compiler_version, compiler_options 3 | use kinds, only: rk, ik 4 | use io_fortran_lib, only: String, cast, LF, SPACE, str, operator(+), operator(**) 5 | use randoms, only: random_gauss 6 | implicit none (type, external) 7 | 8 | character(len=*), parameter :: logfile = "./test/tests.log" 9 | 10 | character(len=10) :: date="", time="" 11 | type(String) :: testlog, errlog 12 | logical :: test_succeeded=.true., all_passing=.true. 13 | 14 | character(len=:), allocatable :: char_var 15 | 16 | real(rk) :: x = 0e0_rk 17 | integer(ik) :: i = 0_ik, j = 0_ik 18 | 19 | call random_init(repeatable=.false., image_distinct=.true.) 20 | call date_and_time(date=date, time=time) 21 | 22 | testlog = String("RUNNING TESTS (str/cast) | date: " + trim(adjustl(date)) + & 23 | " | time: " + time + & 24 | " | int kind: " + str(ik) ) 25 | call testlog%push(LF + "-"**testlog%len() + LF) 26 | 27 | errlog = String(" ERROR LOG" + LF + " ---------" + LF) 28 | 29 | 1 continue 30 | 31 | call random_gauss(x,0e0_rk,1e0_rk); i = floor(huge(1_ik)*x, ik) + 1_ik 32 | call cast(str(i, fmt="i"), into=j, fmt="i") 33 | test_succeeded = (i == j) 34 | if ( .not. test_succeeded ) then 35 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 36 | end if 37 | 38 | 2 if ( .not. test_succeeded ) all_passing = .false. 39 | 40 | call random_gauss(x,0e0_rk,1e0_rk); i = floor(huge(1_ik)*x, ik) + 1_ik 41 | call cast(str(i, fmt="z"), into=j, fmt="z") 42 | test_succeeded = (i == j) 43 | if ( .not. test_succeeded ) then 44 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 45 | end if 46 | 47 | 3 if ( .not. test_succeeded ) all_passing = .false. 48 | 49 | call random_gauss(x,0e0_rk,1e0_rk); i = floor(huge(1_ik)*x, ik) + 1_ik 50 | call cast(i, into=char_var, fmt="i"); call cast(char_var, into=j, fmt="i") 51 | test_succeeded = (i == j) 52 | if ( .not. test_succeeded ) then 53 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 54 | end if 55 | 56 | 4 if ( .not. test_succeeded ) all_passing = .false. 57 | 58 | call random_gauss(x,0e0_rk,1e0_rk); i = floor(huge(1_ik)*x, ik) + 1_ik 59 | call cast(i, into=char_var, fmt="z"); call cast(char_var, into=j, fmt="z") 60 | test_succeeded = (i == j) 61 | if ( .not. test_succeeded ) then 62 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 63 | end if 64 | 65 | 5 if ( .not. test_succeeded ) all_passing = .false. 66 | 67 | if ( all_passing ) then 68 | call testlog%push('All tests are "PASSING" with compiler "' + compiler_version() + '" ' + & 69 | 'using compiler options "' + compiler_options() + '".' + LF) 70 | else 71 | call testlog%push('Some tests are "FAILING" with compiler "' + compiler_version() + '" ' + & 72 | 'using compiler options "' + compiler_options() + '".' + LF) 73 | end if 74 | 75 | call testlog%echo(logfile) 76 | write(*,"(DT)") testlog 77 | 78 | if ( .not. all_passing ) then 79 | call errlog%echo(logfile) 80 | write(*,"(DT)") errlog 81 | end if 82 | end program main 83 | -------------------------------------------------------------------------------- /test/units/scalar-test-r.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use, intrinsic :: iso_fortran_env, only: compiler_version, compiler_options 3 | use kinds, only: rk 4 | use io_fortran_lib, only: String, cast, LF, SPACE, str, operator(+), operator(**) 5 | use randoms, only: random_gauss 6 | implicit none (type, external) 7 | 8 | real(rk), parameter :: tol = 3.0_rk*epsilon(1e0_rk) 9 | character(len=*), parameter :: logfile = "./test/tests.log" 10 | 11 | character(len=10) :: date="", time="" 12 | type(String) :: testlog, errlog 13 | logical :: test_succeeded=.true., all_passing=.true. 14 | 15 | character(len=:), allocatable :: char_var 16 | 17 | real(rk) :: x = 0e0_rk, y = 0e0_rk 18 | 19 | call random_init(repeatable=.false., image_distinct=.true.) 20 | call date_and_time(date=date, time=time) 21 | 22 | testlog = String("RUNNING TESTS (str/cast) | date: " + trim(adjustl(date)) + & 23 | " | time: " + time + & 24 | " | real kind: " + str(rk) ) 25 | call testlog%push(LF + "-"**testlog%len() + LF) 26 | 27 | errlog = String(" ERROR LOG" + LF + " ---------" + LF) 28 | 29 | 1 continue 30 | 31 | call random_gauss(x,0e0_rk,1e0_rk) 32 | call cast(str(x, locale="US", fmt="e"), into=y, locale="US", fmt="e") 33 | test_succeeded = (abs(x-y)/abs(x) < tol) 34 | if ( .not. test_succeeded ) then 35 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 36 | end if 37 | 38 | 2 if ( .not. test_succeeded ) all_passing = .false. 39 | 40 | call random_gauss(x,0e0_rk,1e0_rk) 41 | call cast(str(x, locale="US", fmt="f"), into=y, locale="US", fmt="f") 42 | test_succeeded = (abs(x-y)/abs(x) < tol) 43 | if ( .not. test_succeeded ) then 44 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 45 | end if 46 | 47 | 3 if ( .not. test_succeeded ) all_passing = .false. 48 | 49 | call random_gauss(x,0e0_rk,1e0_rk) 50 | call cast(str(x, locale="US", fmt="z"), into=y, locale="US", fmt="z") 51 | test_succeeded = (abs(x-y)/abs(x) < tol) 52 | if ( .not. test_succeeded ) then 53 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 54 | end if 55 | 56 | 4 if ( .not. test_succeeded ) all_passing = .false. 57 | 58 | call random_gauss(x,0e0_rk,1e0_rk) 59 | call cast(str(x, locale="EU", fmt="e"), into=y, locale="EU", fmt="e") 60 | test_succeeded = (abs(x-y)/abs(x) < tol) 61 | if ( .not. test_succeeded ) then 62 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 63 | end if 64 | 65 | 5 if ( .not. test_succeeded ) all_passing = .false. 66 | 67 | call random_gauss(x,0e0_rk,1e0_rk) 68 | call cast(str(x, locale="EU", fmt="f"), into=y, locale="EU", fmt="f") 69 | test_succeeded = (abs(x-y)/abs(x) < tol) 70 | if ( .not. test_succeeded ) then 71 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 72 | end if 73 | 74 | 6 if ( .not. test_succeeded ) all_passing = .false. 75 | 76 | call random_gauss(x,0e0_rk,1e0_rk) 77 | call cast(str(x, locale="EU", fmt="z"), into=y, locale="EU", fmt="z") 78 | test_succeeded = (abs(x-y)/abs(x) < tol) 79 | if ( .not. test_succeeded ) then 80 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 81 | end if 82 | 83 | 7 if ( .not. test_succeeded ) all_passing = .false. 84 | 85 | call random_gauss(x,0e0_rk,1e0_rk) 86 | call cast(x, into=char_var, locale="US", fmt="e"); call cast(char_var, into=y, locale="US", fmt="e") 87 | test_succeeded = (abs(x-y)/abs(x) < tol) 88 | if ( .not. test_succeeded ) then 89 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 90 | end if 91 | 92 | 8 if ( .not. test_succeeded ) all_passing = .false. 93 | 94 | call random_gauss(x,0e0_rk,1e0_rk) 95 | call cast(x, into=char_var, locale="US", fmt="f"); call cast(char_var, into=y, locale="US", fmt="f") 96 | test_succeeded = (abs(x-y)/abs(x) < tol) 97 | if ( .not. test_succeeded ) then 98 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 99 | end if 100 | 101 | 9 if ( .not. test_succeeded ) all_passing = .false. 102 | 103 | call random_gauss(x,0e0_rk,1e0_rk) 104 | call cast(x, into=char_var, locale="US", fmt="z"); call cast(char_var, into=y, locale="US", fmt="z") 105 | test_succeeded = (abs(x-y)/abs(x) < tol) 106 | if ( .not. test_succeeded ) then 107 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 108 | end if 109 | 110 | 10 if ( .not. test_succeeded ) all_passing = .false. 111 | 112 | call random_gauss(x,0e0_rk,1e0_rk) 113 | call cast(x, into=char_var, locale="EU", fmt="e"); call cast(char_var, into=y, locale="EU", fmt="e") 114 | test_succeeded = (abs(x-y)/abs(x) < tol) 115 | if ( .not. test_succeeded ) then 116 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 117 | end if 118 | 119 | 11 if ( .not. test_succeeded ) all_passing = .false. 120 | 121 | call random_gauss(x,0e0_rk,1e0_rk) 122 | call cast(x, into=char_var, locale="EU", fmt="f"); call cast(char_var, into=y, locale="EU", fmt="f") 123 | test_succeeded = (abs(x-y)/abs(x) < tol) 124 | if ( .not. test_succeeded ) then 125 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 126 | end if 127 | 128 | 12 if ( .not. test_succeeded ) all_passing = .false. 129 | 130 | call random_gauss(x,0e0_rk,1e0_rk) 131 | call cast(x, into=char_var, locale="EU", fmt="z"); call cast(char_var, into=y, locale="EU", fmt="z") 132 | test_succeeded = (abs(x-y)/abs(x) < tol) 133 | if ( .not. test_succeeded ) then 134 | call errlog%push(" Test bounds failed at line " + str(__LINE__ - 2) + ' of file "' + __FILE__ + '".' + LF) 135 | end if 136 | 137 | 13 if ( .not. test_succeeded ) all_passing = .false. 138 | 139 | if ( all_passing ) then 140 | call testlog%push('All tests are "PASSING" with compiler "' + compiler_version() + '" ' + & 141 | 'using compiler options "' + compiler_options() + '".' + LF) 142 | else 143 | call testlog%push('Some tests are "FAILING" with compiler "' + compiler_version() + '" ' + & 144 | 'using compiler options "' + compiler_options() + '".' + LF) 145 | end if 146 | 147 | call testlog%echo(logfile) 148 | write(*,"(DT)") testlog 149 | 150 | if ( .not. all_passing ) then 151 | call errlog%echo(logfile) 152 | write(*,"(DT)") errlog 153 | end if 154 | end program main 155 | --------------------------------------------------------------------------------