Fortranの標準ライブラリstdlibの紹介

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

本記事はstdlibの紹介を主眼にしており，その狙いやインストール方法などを解説します．stdlibの各機能の使用例は別の記事で紹介し，本記事にリンクしていきます．

- [組込の代替・補足](https://qiita.com/implicit_none/items/2d77e26265d94599de0a)（stdlib_error, stdlib_kinds, stdlib_io, stdlib_optval）
- [ロガー](https://qiita.com/implicit_none/items/b7f325a4219d8f268b8b)（stdlib_logger）
- 型
    - [文字・文字列操作](https://qiita.com/implicit_none/items/903cddb978363ece0f5f)（stdlib_ascii, stdlib_strings）
    - ユーザ定義型（[stdlib_string_type, stdlib_stringlist_type](https://qiita.com/implicit_none/items/a6a2bf0e7ad916f25068)）
- 数値処理
    - 数学（stdlib_math, stdlib_linalg, stdlib_specialfunctions, stdlib_quadrature）
    - 統計（stdlib_random, stdlib_stats, stdlib_stats_distribution_uniform, stdlib_stats_distribution_normal）
    - ソート（[stdlib_sorting](https://qiita.com/implicit_none/items/13acee82941a7542b1cc), stdlib_selection）
- stdlibの情報
    - バージョン情報（[stdlib_version](#stdlib_versionの使用例)）

# stdlibのねらい
Fortranには，規格で定められた標準ライブラリがありません．そのため，必須と思われる処理があちこちで再発明されていました．stdlibは，コミュニティ主導で，コミュニティによって合意された**事実上の**標準ライブラリを提供することを目的としています．

Issueでも議論されていましたが，stdlib自身は車輪の再発明を含んでいます．しかし，コミュニティによって合意された車輪を開発することにより，それが多くの人に適合し，個別の再発明を回避できます．

また，stdlibに実装された機能のうち，いくつかを標準に取り込もうという動きもあるようです．

# stdlibの対象範囲
stdlibは，三つの対象を掲げています．各対象での開発項目と，それらの現状を列挙します．

- Utilities
    - containers<br>文字列を扱うためのユーザ定義派生型`string_type`，`string_type`のリスト`stringlist_type`が実装されている．連結リストもレビュー中である．
    - strings<br>文字および文字列に対する処理が`stdlib_ascii`, `stdlib_strings`に実装されている．
    - files<br>`stdlib_io`に，ファイルオープンや簡潔な入出力を実行する処理が実装されている．
    - OS/environment integration<br>機能は多くないが，`stdlib_system`や`stdlib_error`に標準よりも柔軟な機能が実装されている．型のkindが定義されたモジュール`stdlib_kinds`も用意されている．
    - unit testing & assertions<br>単体テストの機能は，stdlibとは切り離された状態で開発されているが，stdlibのテストに利用されている．stdlibに取り込むかどうかの議論もされている．assertionsに関する簡単か機能が`stdlib_error`に実装されている．
    - logging<br>`stdlib_logger`にロギングのための派生型が定義されている．
    - その他<br>`optional`引数の取り扱いを改善する`optval`が実装されている．
- Algorithms
    - searching<br>現状該当なし
    - sorting<br>`stdlib_sorting`に2種類のソートが実装されている．
    - merging<br>現状該当なし
- Mathematics
    - linear algebra<br>`stdlib_linalg`に簡単な処理が実装されている．
    - sparse matrices<br>現状該当なし．議論はされており，stdlibリポジトリのWikiに[Sparse matrix API](https://github.com/fortran-lang/stdlib/wiki/Stdlib-Sparse-matrix-API)が確認できる．
    - special functions<br>`stdlib_specialfunctions`にLegendre多項式が実装されている．
    - fast Fourier transform<br>現状該当なし
    - random numbers<br>`stdlib_random`に整数擬似乱数生成器が実装されている．
    - statistics<br>`stdlib_stats`に平均値，中央値，分散，共分散，相関係数，モーメントの計算が実装されている． <br>`stdlib_stats_distribution_uniform`に一様分布，確率密度関数，累積分布関数などが実装されている．
    - ordinary differential equations<br>現状該当なし
    - numerical integration<br>`stdlib_quadrature`に台形則，シンプソン則，Gauss-Legendre求積法，Gauss-Legendre-Lobatto求積法が実装されている．
    - optimization<br>現状該当なし

# stdlibのビルドとインストール
## 要求ソフトウェア
stdlibのビルドには，Fortran 2008以降のコンパイラが必須です．その他は，ビルドする環境によって異なります．

ビルドにCMakeを利用する場合は，

- CMake 3.14以降
- makeもしくはNinja
- fypp

が必要です．CMakeを用いず，makeのみでビルドすることも可能ですが，その場合でもfyppは必要です．

ビルドに[fpm](https://github.com/fortran-lang/fpm)（Fortran-langによって開発されているFortran用のパッケージマネージャ兼ビルドツール）を用いる場合は，

- fpm 0.4.0以降
- リンカ
    - 例えばWindows版Intel FortranであればVisual Studio 

が必要です．

## ソースの取得
GitHubからソースを取得します．

```
git clone https://github.com/fortran-lang/stdlib
```

以降の作業は，stdlibディレクトリで行うことを前提としています．

```
cd stdlib
```

## ビルドとインストール
### CMakeによるビルドとインストール
CMakeは，2段階でビルドを行います．まず，下記のコマンドを実行してビルドの設定を行います．

```
cmake -B build
```

設定段階では，いくつかオプションが指定できます．

- CMakeのオプション
    - `-DCMAKE_Fortran_COMPILER=コンパイラ`<br>ビルドに用いるコンパイラを指定する．`コンパイラ`の部分はコンパイラの実行コマンド（gfortranの場合は`gfortran`，Intel Fortranの場合は`ifort`）に置き換える．
    - `-G バックエンド`<br>ビルド・リンクに用いるバックエンド（ジェネレータ）を指定する．WindowsでVisual Studioをインストールしていると，Visual Studioが標準のバックエンドとなる．明示的にmakeを指定するには，`-G "Unix Makefiles"`を与える．
- stdlibのオプション
    - `-DCMAKE_MAXIMUM_RANK:String=ランク`<br>fyppによって生成される，手続の引数となる配列の最大ランクを指定する．最大値は15，標準値は4である. `ランク`を15としてコンパイルすると，4 GB程度のメモリを消費する．並列ビルドの場合は，並列数に比例して使用メモリ量が増加する．<br>利用者は多くないと思うが，CMakeLists.txtを編集してVisual Studioをバックエンドに利用すると，`ランク`15としてビルドした際に完了まで数時間を要する．
    - `-DBUILD_SHARED_LIBS={ON|OFF}`<br>出力するライブラリの種類を制御する．`ON`の場合は共有ライブラリ，それ以外では静的ライブラリを生成する．

設定が完了した後，ターゲットをビルドします．

```
cmake --build build
```

ビルドしたライブラリおよびmodファイルをインストールするには，

```
cmake --install build
```

を実行します．このとき，`--prefix`オプションでインストールディレクトリを指定しないと，標準のインストールディレクトリにインストールされます．標準ディレクトリは，Unix系OSの場合は`/usr/local`，Windowsの場合は`C:/Program Files/`です．Unix系OSでもWindowsでも，標準のディレクトリへのファイルコピーに管理者権限が必要なので，管理者として作業していない場合はインストールに失敗します．

```
CMake Error at build/config/cmake_install.cmake:41 (file):
  file cannot create directory: /usr/local/lib/pkgconfig.  Maybe need
  administrative privileges.
```

CMakeを利用してインストールすると，ライブラリ`libfortran_stdlib.a`がインストールディレクトリ直下の`lib`ディレクトリにコピーされます．また，インストールディレクトリ直下の`include/fortran_stdlib/コンパイラベンダ-バージョン`に`.mod`および`.smod`ファイルがコピーされます．

`コンパイラベンダ-バージョン`については，例えばgfortran 9.3.0でビルドすると`GNU-9.3.0`となります．

### fpmによるビルドとインストール
stdlibには，fpmでビルドするための設定が追加されたブランチstdlib-fpmが存在します．当該ブランチをチェックアウトして，ビルド・インストールを行うことになります．

```
git switch stdlib-fpm
```

stdlib-fpmブランチに用意されているビルド設定（マニフェスト）`fpm.toml`には，インストールの設定が書かれておらず，インストールに失敗します．そのため，次の項目をfpm.tomlに追記します．

```toml
[install]
library = true
```

追加した後，ビルドを行います．

```
fpm build --profile release
```

ビルドが完了したら

```
fpm install --profile release --prefix インストールディレクトリ
```

を実行してインストールします．このとき，インストールディレクトリ直下の`lib`ディレクトリにライブラリ`libstdlib.a`が，`include`に`.mod`ファイルがコピーされます．CMakeでビルドする時とライブラリの名前が異なることに注意してください．

# stdlibの利用
CMakeを利用してstdlibをインストールし，stdlibを参照するプログラムをCMakeでビルドする場合は，CMakeLists.txt内で`find_package(fortran_stdlib REQUIRED)`を呼び出した後，`target_link_libraries`の項目に`fortran_stdlib::fortran_stdlib`を追加します．

CMakeおよびfpm以外を用いてビルドする場合は，`lib`および`include`ディレクトリにパスを通しておき，ライブラリとして`fortran_stdlib`を参照します．

fpmを利用してビルドする場合は，`lib`および`include`ディレクトリにパスを通しておき，fpm.tomlの`build`セクションに，`link`と`external-modules`の設定を追加します．

```toml
[build]
link = "stdlib"
external-modules = ["stdlib_error", "stdlib_kinds"]
```

上記の例は，`stdlib_error`および`stdlib_kinds`モジュールを`use`している場合の設定です．他にもstdlibのモジュールを`use`している場合は，そのモジュールの名前を追加する必要があります．

fpmの設定ファイルに下記の設定を記述すると，stdlibをシステムにインストールしていなくても，stdlibをGitHubリポジトリから取得して参照できます．この設定を利用すると，上記の`link`や`external-modules`の設定が不要になるので，非常に便利です．

```toml
[dependencies]
stdlib = { git="https://github.com/fortran-lang/stdlib", branch="stdlib-fpm" }
```


fpmの使い方に関しては，[fpm (Fortran Package Manager)の使い方](https://qiita.com/implicit_none/items/6a65ac1b83efefc782db)や[fpm (Fortran Package Manager)の設定ファイルの解説](https://qiita.com/implicit_none/items/99c5d12f5c18733c66fd)等をご覧ください．

## stdlib_versionの使用例
stdlibに新しく追加されたモジュール`stdlib_version`の紹介も兼ねて，利用方法の例を示します．

`stdlib_version`は，Fortranの標準ライブラリstdlibのバージョンを提供します．バージョン番号はセマンティックバージョニングに基づいており，定数として参照することも，ゲッターから取得することもできます．

```Fortran:stdlib.f90
module stdlib
    use :: stdlib_version
    implicit none
    private
    public :: version

contains

    subroutine version()
        implicit none
        block
            ! バージョン番号の定数を参照する．
            print *, stdlib_version_string
            ! 0.1.0

            ! バージョン番号を単一の整数として取得する．
            ! version_compact = major*10000 + minor*100 + patchで取得する
            print '(I6.6)', stdlib_version_compact
            ! 000100
        end block
        block
            character(:), allocatable :: ver
            ! stdlibのバージョンを文字列で取得する．
            call get_stdlib_version(string=ver)
            print *, ver
            ! 0.1.0
        end block

        block
            use, intrinsic :: iso_fortran_env
            integer(int32) :: major, minor, patch

            ! stdlibのバージョン番号（major, minor, patch）を個別に取得する．
            call get_stdlib_version(major=major, minor=minor, patch=patch)
            print '(*(g0:,"."))', major, minor, patch
            !0.1.0
        end block
    end subroutine version
end module stdlib
```

```Fortran:main.f90
program main
    use :: stdlib
    implicit none

    call version()
end program main
```

### CMake
CMakeを用いてstdlibをビルド・インストールしてある場合に，上記のソースファイルをCMakeによってビルドするには，上述のとおりCMakeLists.txt内で`find_package(fortran_stdlib REQUIRED)`を呼び出した後，`target_link_libraries`の項目に`fortran_stdlib::fortran_stdlib`を追加します．

stdlibのインストールディレクトリにパスが通ってい場合，変数`CMAKE_PREFIX_PATH`にstdlibのインストールディレクトリを追加するか，変数`fortran_stdlib_DIR`に`fortran_stdlib-config.cmake`が存在するディレクトリパスを指定します．

例えば，下記のようにインストールした場合，

```console
cmake --install build --prefix C:\Users\ユーザ名\AppData\Local\fortran_stdlib\
```

CMakeLists.txtは次のように作成します．

```cmake:CMAKELists.txt
cmake_minimum_required(VERSION 3.14.0)
project(stdlib_ver LANGUAGES Fortran)

set(fortran_stdlib_DIR "C:/Users/ユーザ名/AppData/Local/fortran_stdlib/lib/cmake/fortran_stdlib")
find_package(fortran_stdlib REQUIRED)

add_executable(${PROJECT_NAME}
    main.f90
    stdlib.f90
)

target_link_libraries(${PROJECT_NAME}
    PRIVATE
    fortran_stdlib::fortran_stdlib
)
```

```console
cmake -B build
cmake --build build
build\stdlib_ver.exe
 0.1.0
000100
 0.1.0
0.1.0
```

### fpm
fpmを用いると，設定が簡単になります．下記コマンドでfpmのプロジェクトを新規作成し，上記の`main.f90`および`stdlib.f90`を，それぞれ`app`, `src`ディレクトリにコピーします．

```console
fpm new stdlib_ver --app --lib
```

その後，設定ファイル`fpm.toml`に`[dependencies]`の設定を追記してビルド・実行します．

```toml
name = "stdlib_ver"
version = "0.1.0"
license = "license"
author = "Jane Doe"
maintainer = "jane.doe@example.com"
copyright = "Copyright 2021, Jane Doe"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[install]
library = false
[dependencies]
stdlib = { git="https://github.com/fortran-lang/stdlib", branch="stdlib-fpm" }
```

```console
fpm build
fpm run
 0.1.0
000100
 0.1.0
0.1.0
```