Ubuntuでc++17を試す。

# UbuntuでC++17を試す。
Ubuntu16.04でC++17を試そうとすると機能が少なすぎた...orz。
動かなかった機能は`namespace`のネスト、要は

```c++
namespace my::math{
}
```
が出来ない。後は特殊関数、名前空間の名前からやりたいことはだいたいわかると思います。
原因はgccのバージョン、Ubuntuはgcc5系が標準らしいが最新版はgcc7があるらしいのでそれをインストールする。特殊関数自体はgcc6でサポートしてるらしいですがどうせなら最新版が欲しかったので、あとgcc5でも`-std=c++17`フラッグは使えますし多くの`c++17`の機能は使えるはず、少なくともconstexprラムダ式は使えたので今の機能に満足している人はいりません。

## gcc7の入手
https://askubuntu.com/questions/859256/how-to-install-gcc-7-or-clang-4-0
の丸パクリです。要はリポジトリを足してインストール

```
sudo add-apt-repository ppa:jonathonf/gcc-7.1
sudo apt update
sudo apt install gcc-7 g++-7
```
小ネタですが`apt-get`の`-get`が必要なくなってます。`repository`がどうかは知りません。`-cache`も必要ありません。が、`apt search "hoge" | xargs ~~~`とかすると

```
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
```
と安定的なCLIでないと怒られます。結果は一応出してくれます。`apt-cache search "hoge" | xargs`なら大丈夫です。以上、小ネタでした。

インストールが終了すると`g++-7`コマンドが使えます。普通のgccのメインバージョンは5のままです。
因みにこのリポジトリではgcc6はインストールできませんでした。gcc6用は

```
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
```
です。こっちはリポジトリに`ubuntu`がついてるのでgcc7はより実験的なんでしょうね。
ついでにgcc6もインストールしておきます。もちろんメインバージョンはgcc5のままです。

## 実行
c++特殊関数一覧
http://en.cppreference.com/w/cpp/experimental/special_math
今回は、ルジャンドルの倍多項式を試します。公式からコピペ

```c++
#include <cmath>
#include <iostream>

double P20(double x) { return 0.5*(3*x*x-1); }
double P21(double x) { return -3.0*x*std::sqrt(1-x*x); }
double P22(double x) { return 3*(1-x*x); }

//constexpr double legendre(int n, int i, double x){ return std::assoc_legendre(n, i, x); };
int main()
{
  // spot-checks
  std::cout << std::assoc_legendre(2, 0, 0.5) << '=' << P20(0.5) << '\n'
            << std::assoc_legendre(2, 1, 0.5) << '=' << P21(0.5) << '\n'
            << std::assoc_legendre(2, 2, 0.5) << '=' << P22(0.5) << '\n';
}
```
後は、`g++-7 -std=c++17 main.cc`でコンパイルです。`-std=c++17`フラッグは勝手につけてくれると期待しましたが必要でした。コメントアウト部分は特殊関数がconstexprとして使えるか試して見た部分ですがどうやら無理っぽいです。複素数対応もしていません。
ということでc++17の特殊関数使ってみましたが、微妙に使えない子っぽいです。実装が後回しにされるのがよくわかりました。
特殊関数は使えない子ですがc++17の機能をフルに使いたい人はgcc6/7をインストールしてもいいんじゃないでしょうか、ただし自己責任で。
まあ簡単ですしgccのメインバージョンも保たれるのでそれほど悪さをするとも思えないのでインストールしてもいいとは思います。
そういや`g++-7`とか++して-してとかC++の複雑さを表していますね。Makefileに`g++7`と書いてしまって混乱してしまった:joy:
あとちょっとだけエラーメッセージが親切になります。エラー部分の赤色表示が強化され、矢印も強調形式になります。

# 追記
## float, double, long doubleについて
公式からヘッダーをコピペ。

```c++:
double      assoc_legendre( unsigned int n, unsigned int m, double x );
double      assoc_legendre( unsigned int n, unsigned int m, float x );
double      assoc_legendre( unsigned int n, unsigned int m, long double x );
float       assoc_legendref( unsigned int n, unsigned int m, float x );
long double assoc_legendrel( unsigned int n, unsigned int m, long double x );

double      assoc_legendre( unsigned int n, unsigned int m, Integral x );
```
引数はfloatでもlong doubleでも受けられるようになっています。intでも型変換でうけられます。返り値は同じ関数名だと判断できないので関数名にf(float)やl(long)をつけるようです。同じ規則は初等関数のsin、cos等でもそうなっています。
精度が欲しい場合はこっちを使いましょう。doubleで大体15桁、数値計算で誤差の累積とか考えると大体8桁精度が限界、4倍精度使えば30桁程度、きちんと言えば33桁ですがまあ多少は安全ファクターを取って、で多分15桁打ち切りぐらいは実現できるんじゃないかと思います。C++で精度が足りないとかいう奴はとりあえず4倍精度使え、sinはsinlに書き換えろ、話はそれからだ。
後なんか`Integral`とかいう怪しげ?嬉しそう?な奴が居る。後で調べてみる。

# 追記2
`assoc_legendre`なのでルジャンドルの陪多項式でした、すいません。ルジャンドル関数は`legendre`です。ルジャンドルの多項式が2つの整数を引数に取るわけ無いじゃん...orz
`Integral`はまんま整数のようです。英語でIntegralは他にもありますが「整数の、積分の」らしいです。頼むから分けてくれ...

# 追記3
`legendre`ですが定義域は`[-1:1]`のようです。それ以外の値を渡すと`std::domain_error`を飛ばします。

```
  what():  Argument out of range in __poly_legendre_p.
```
なので一応、意味はわかります。何度もいうけどただの多項式なんだから大げさな実装はいらないのに...
Boost版は

```c++:boost/math/special_functions/legendre.hpp
namespace boost{ namespace math{

template <class T>
calculated-result-type legendre_p(int n, T x);

template <class T, class Policy>
calculated-result-type legendre_p(int n, T x, const Policy&);

template <class T>
calculated-result-type legendre_p(int n, int m, T x);

template <class T, class Policy>
calculated-result-type legendre_p(int n, int m, T x, const Policy&);

template <class T>
calculated-result-type legendre_q(unsigned n, T x);

template <class T, class Policy>
calculated-result-type legendre_q(unsigned n, T x, const Policy&);

template <class T1, class T2, class T3>
calculated-result-type legendre_next(unsigned l, T1 x, T2 Pl, T3 Plm1);

template <class T1, class T2, class T3>
calculated-result-type legendre_next(unsigned l, unsigned m, T1 x, T2 Pl, T3 Plm1);
}} // namespaces
```
とテンプレートを使っているので全く同じ実装ではなさそうです。