Fortranの標準ライブラリstdlibの紹介（組込機能の代替・補足）

# 概要
[Fortran-lang](https://fortran-lang.org)コミュニティが主導して開発しているFortraの標準ライブラリ[stdlib](https://github.com/fortran-lang/stdlib)について紹介します．紹介する内容は，stdlibの現行バージョン0.1.0に基づいています．

本記事では，組込機能の代替・補足として使える機能を紹介します．

- stdlib_error
- stdlib_kinds
- stdlib_io
- stdlib_optval

# stdlib_error
`stdlib_error`モジュールでは，エラー処理関わる手続を定義しています．モジュール内には，手続が二つ定義されています．

- `check`<br>条件式の結果を調べ，条件が成立していない場合にプログラムを停止する．
- `error_stop`<br>プログラムを停止する．

## `check`
`check`は，引数に論理値を取り，論理値が`.false.`の場合にプログラムを停止します．実用上は，論理値ではなく条件式を渡して条件成立の成否を確認する，assertionに用いられます．

サブルーチンであるため，`call check(condition[, msg][, code][, warn])`として呼び出します．引数はそれぞれ下記の意味があります．

|引数名|型|用途|
|:--|:--|:--|
|`condition`|`logical,intent(in)`|条件式の結果|
|`msg`|`character(*),intent(in),optional`|出力されるエラーメッセージ．標準値は`Check failed.`|
|`code`|`integer,intent(in),optional`|終了コード．標準値は`1`|
|`warn`|`logical,intent(in),optional`|プログラムを終了しない．標準値は`.false.`|

### 使い方

- 引数`condition`が`.false.`のとき，`Check failed.`を出力し，終了コード1でプログラムを終了する．

```Fortran
call check(condition=.false.)
!  Check failed.
! ERROR STOP 1
```

- 論理値ではなく条件式を渡して条件成立の成否を確認する．

```Fortran
integer :: a = 1

call check(a == 0)
!  Check failed.
! ERROR STOP 1
```

- `a==0`が`.false.`のとき，引数`msg`に渡した文字列を出力し，終了コード1でプログラムを終了する．

```Fortran
integer :: a = 1

call check(a == 0, msg="error: actual value of a is not equal to expected value 0")
!  error: actual value of a is not equal to expected value 0
! ERROR STOP 1
```


- 条件式が`.false.`のとき，引数`msg`に渡した文字列を出力し，終了コード77でプログラムを終了する．

```Fortran
integer :: a = 1

call check(a == 0, "error: actual value of a is not equal to expected value 0", code=77)
!  error: actual value of a is not equal to expected value 0
! ERROR STOP 77
```

- 引数warnに`.true.`を渡すと，プログラムは終了しない．

```Fortran
integer :: a = 1

call check(a == 0, "warning: actual value of a is not equal to expected value 0", warn=.true.)
!  warning: actual value of a is not equal to expected value 0
```

## `error_stop`
`error_stop`は，エラーメッセージを表示して，非ゼロの終了コードでプログラムを終了します．Fortran 2018に`error stop`文があり，それと機能は同じです．一方で，Fortran 2008の`error stop`文は定数式を指定できますが，変数を指定できません．`error_stop`は，この機能不足を補い，Fortran 2008でも2018でも同じように書けるようにするために導入されています．

`call error_stop(msg[, code])`として呼び出します．引数はそれぞれ下記の意味があります．

|引数名|型|用途|
|:--|:--|:--|
|`msg`|`character(*),intent(in)`|出力されるエラーメッセージ．|
|`code`|`integer,intent(in),optional`|終了コード．標準値は`1`|

### 使い方

- 引数msgに渡した文字列を出力し，非ゼロの終了コードでプログラムを終了する．

```Fortran
call error_stop(msg="Fatal error")
!  Fatal error
! ERROR STOP
```

- 引数に渡した文字列を出力し，終了コード999でプログラムを終了する．

```Fortran
call error_stop("Fatal error", code=999)
!  Fatal error
!  ERROR: code          999  was specified.
! ERROR STOP
```

# stdlib_kinds
`stdlib_kinds`モジュールでは，Fortranのデータ型の種別kindを指定する定数が定義されています．

Fortran 2008で単精度および倍精度のkindを指定するパラメータ`real32`および`real64`が定められました．しかし，単精度のkindを`sp`，倍精度を`dp`とする習慣があったので，それを踏襲したのだと考えられます．

`lk`は論理型変数のkindです．

## 定義されている定数

- `sp`<br>単精度浮動小数点数のkind（=`real32`）．`selected_real_kind(6)`の戻り値．
- `dp`<br>倍精度浮動小数点数のkind（=`real64`）．`selected_real_kind(15)`の戻り値．
- `xdp`<br>拡張倍精度浮動小数点数のkind．`selected_real_kind(18)`の戻り値．利用できない場合`-1`．
- `qp`<br>4倍精度浮動小数点数のkind（=`real128`）．`selected_real_kind(33)`の戻り値．利用できない場合`-1`．
- `lk`<br>論理値のkind．

定義されている定数以外に，`iso_fortran_env`モジュールに定義されている`int8`, `int16`, `int32`, `int64`が読み込まれ，公開されています．同様に，`iso_c_binding`モジュールに定義されている`c_bool`も公開されています．

## stdlib-fpm
`xdp`および`qp`は，cmakeおよびfyppの機能を用いて値が定められます．fpmを用いてビルドする場合，プリプロセスができないため，`xdp`, `qp`ともに`-1`となっています．

```Fortran:stdlib_kinds.f90
  !> Extended double precision real numbers
  integer, parameter :: xdp = -1

  !> Quadruple precision real numbers
  integer, parameter :: qp = -1
```

これらは，`selected_real_kind`を用いるように変更した方がよいでしょう．コンパイラが拡張倍精度や4倍精度浮動小数点数をサポートしていなければ，`selected_real_kind`は`-1`を返します．

```Fortran:stdlib_kinds.f90
  !> Extended double precision real numbers
  integer, parameter :: xdp = selected_real_kind(18)

  !> Quadruple precision real numbers
  integer, parameter :: qp = selected_real_kind(33)
```

# stdlib_io
`stdlib_io`モジュールでは，入出力の利便性を改善する手続が定義されています．

- `open`<br>ファイルを開き，その装置番号を返す．
- `savetxt`<br>2次元配列をテキスト形式でファイルに書き出す．
- `loadtxt`<br>2次元配列をテキスト形式でファイルから読み込む．

## `open`
`open`関数は，ファイルを開き，その装置番号を返します．

`unit_number = open(filename[, mode][, iostat])`として利用します．戻り値`unit_number`は`integer`です．引数はそれぞれ下記の意味があります．

|引数名|型|用途|
|:--|:--|:--|
|`filename`|`character(*),intent(in)`|ファイル名．|
|`mode`|`character(*),intent(in),optional`|ファイルを開くモード．標準は`rt`|
|`iostat`|`integer,intent(out),optional`|`open`関数のエラー状態．エラーがなければ0|

### モード
モードは，テキストで指定します．

|テキスト|モード|
|:--|:--|
|`"r"`|読み取り専用として開く．（標準）|
|`"w"`|書き込み用に新規に開く．（既存ファイルの中は消去される）|
|`"x"`|ファイルを新規作成する．ファイルが既に存在する場合は処理が失敗する．|
|`"a"`|ファイルを追記するために開く．|
|`"+"`|ファイル更新（読み取り，書き込み）用に開く|
|`"b"`|バイナリモードで開く．|
|`"t"`|テキストモードで開く（標準）|

### 使い方

```Fortran
integer(int32) :: unit
character(128) :: str

! 書き込み＋テキストモードで開く．
unit = open ("example_io.txt", "wt")
close (unit)

! 読み取り＋テキストモードで開く
unit = open ("example_io.txt", "rt")

! 書き込みをしようとするとエラー．
! write (unit, '(A)') "append to example_io.txt"
close (unit)

! 追記モードで開く
unit = open ("example_io.txt", "a")
close (unit)
```

## `savetxt`
サブルーチン`savetxt`は，2次元配列をテキストモードでファイルに書き出します．

`call savetxt(filename, array)`として利用します．引数はそれぞれ下記の意味があります．

|引数名|型|用途|
|:--|:--|:--|
|`filename`|`character(*),intent(in)`|ファイル名．|
|`array`|`integer,dimension(:,:),intent(in)`|出力する2次元配列|
||`real,dimension(:,:),intent(in)`||
||`complex,dimension(:,:),intent(in)`||

### 使い方

```Fortran
use :: stdlib_math
use :: iso_c_binding

real(real32) :: array(16, 2)

enum, bind(c)
    enumerator :: x = 1
    enumerator :: f
end enum

! stdlib_mathで定義されているlinspaceを利用してx = [0, 2π]を設定し，sin(x)を計算．
array(:, x) = linspace(0., 2.*acos(-1.), 16)
array(:, f) = sin(array(:, x))

! 2次元配列をテキストモードで出力する．
call savetxt("sin.txt", array)
```

```text:sin.txt
0.00000000E+00  0.00000000E+00
4.18879032E-01  4.06736642E-01
8.37758064E-01  7.43144870E-01
1.25663710E+00  9.51056540E-01
1.67551613E+00  9.94521916E-01
2.09439516E+00  8.66025388E-01
2.51327419E+00  5.87785184E-01
2.93215322E+00  2.07911611E-01
3.35103226E+00 -2.07911789E-01
3.76991129E+00 -5.87785363E-01
4.18879032E+00 -8.66025448E-01
4.60766935E+00 -9.94521916E-01
5.02654839E+00 -9.51056480E-01
5.44542742E+00 -7.43144751E-01
5.86430645E+00 -4.06736493E-01
6.28318548E+00  1.74845553E-07
```

## `loadtxt`
サブルーチン`loadtxt`は，テキストファイルから2次元配列を読み取ります．

`call savetxt(filename, array)`として利用します．引数はそれぞれ下記の意味があります．

|引数名|型|用途|
|:--|:--|:--|
|`filename`|`character(*),intent(in)`|ファイル名．|
|`array`|`integer,dimension(:,:),allocatable,intent(out)`|読み取ったファイル内容を保持する2次元配列|
||`real,dimension(:,:),allocatable,intent(in)`||
||`complex,dimension(:,:),allocatable,intent(in)`||

### 使い方

```Fortran
real(real32), allocatable :: array(:, :)

! テキストファイルから2次元配列を読み取る．
call loadtxt("sin.txt", array)
print *, array(:, 1)
print *, array(:, 2)
!   0.00000000      0.418879032      0.837758064       1.25663710       1.67551613       2.09439516       2.51327419       2.93215322       3.35103226       3.76991129       4.18879032       4.60766935       5.02654839       5.44542742       5.86430645       6.28318548
!   0.00000000      0.406736642      0.743144870      0.951056540      0.994521916      0.866025388      0.587785184      0.207911611     -0.207911789     -0.587785363     -0.866025448     -0.994521916     -0.951056480     -0.743144751     -0.406736493       1.74845553E-07
```

# `stdlib_optval`
`stdlib_optval`モジュールでは，`optional`引数の取り扱いを改善する手続`optval`を定義しています．

## `optval`
`optval`は，`optional`引数が存在しない場合に標準値を定めるための関数です．

`result = optval([x,] default)`として呼び出します．引数はそれぞれ下記の意味があります．

|引数名|型|用途|
|:--|:--|:--|
|`x`|`integer,intent(in),optional`|`optional`引数|
||`real,intent(in),optional`||
||`complex,intent(in),optional`||
||`logical,intent(in),optional`||
||`character,intent(in),optional`||
|`default`|`x`と同じ|`x`が存在しない場合の標準値|

### 使い方

- 整数

```Fortran
print *, optval(1, default=0), optval(default=0)
! 1 0
```

- 単精度実数

```Fortran
print *, optval(1., default=0.), optval(default=0.)
! 1.00000000 0.00000000
```

- 倍精度実数

```Fortran
print *, optval(1d0, default=0d0), optval(default=0d0)
! 1.0000000000000000 0.0000000000000000
```

- 論理値

```Fortran
print *, optval(.true., default=.false.), optval(default=.false.)
! T F
```

- 複素数

```Fortran
print *, optval((1., 2.), default=(0., 0.)), optval(default=(0., 0.))
! (1.00000000,2.00000000) (0.00000000,0.00000000)
```

- xのn乗根を計算する関数root．nの標準値は2

```Fortran
real(real64) :: x = 64.

print *, root(x), root(x, 3)
! 8.0000000000000000 3.9999999999999996

contains

!| xのn乗根を計算する．
real(real64) function root(x, n)
    implicit none
    real(real64), intent(in) :: x !! 被開平数
    integer(int32), intent(in), optional :: n !! 指数

    root = x**(1d0/optval(n, 2)) ! nが無いときは2が使われる
end function root
```