コンパイル時Brainfuckコンパイラ ――C++14 constexpr の進歩と限界――
中3女子です。
この記事は C++ Advent Claneder2014 23日の参加記事です。
constexpr とは
この記事を読んでいる層には、いまさら言及するまでもないと思われるので省略する。
必要であれば このあたり の資料を参照のこと。
C++14 シンタックス上の進歩とセマンティクス上の進歩
巷では C++14 になり constexpr が簡単になったという話をしばしば耳にする。たしかに簡単になった。
では何が簡単になったかというと、もっともわかりやすいのは複文とローカル変数、制御構文の制限緩和だろう。
条件分岐やループを if, while, for, switch 等でガリガリ書くことができるようになった(ただし goto は不可だ)。
非常に簡単になったといえるが、これはあくまでシンタックス上の進歩だ。
これによって何ができるかというセマンティクス上の問題については、C++11 の時とほとんど変わっていない。
これら制御構文によるコードは、条件演算子、再帰、Index-tupleイディオム、分割統治といったイディオムによって、見た目はともかく挙動と効率においてほとんど変わらずに C++11 constexpr の制約を満たすように書きかえることができる。
constexpr の進歩を語るには、単に簡単になったと言うだけでなく、何ができるようになったかという論点でも語るべきである。
さて、C++14 constexpr で大きなセマンティクス上の進歩をもたらしたのは、オブジェクト書き換えの制限緩和だ。
C++14 では、constexpr関数内で、ローカル変数や一時オブジェクトの内容を書き換えることができるようになった(ただし寿命が定数式評価の中で完結するものに限る)。
これによって、テンポラリバッファを計算途中の値で書き換えながら実行するようなアルゴリズムが記述できるようになった。
C++11 では不可能であるか、もしくは非常に非効率にしか記述できなかったことである。
DFT と FFT
DFT(離散フーリエ変換)をコンパイル時に行いたいという需要は多い[要出典]。
Sprout にもコンパイル時DFTの機能がある。
/*============================================================================= Copyright (c) 2011-2014 Bolero MURAKAMI https://github.com/bolero-MURAKAMI/Sprout Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #ifndef SPROUT_NUMERIC_DFT_FIXED_DFT_HPP #define SPROUT_NUMERIC_DFT_FIXED_DFT_HPP #include <iterator> #include <type_traits> #include <sprout/config.hpp> #include <sprout/index_tuple/metafunction.hpp> #include <sprout/container/traits.hpp> #include <sprout/container/functions.hpp> #include <sprout/container/indexes.hpp> #include <sprout/iterator/operation.hpp> #include <sprout/iterator/dft_iterator.hpp> #include <sprout/algorithm/fixed/results.hpp> #include <sprout/pit/pit.hpp> #include <sprout/math/less.hpp> #include <sprout/numeric/dft/dft_element.hpp> #include <sprout/detail/container_complate.hpp> namespace sprout { namespace fixed { namespace detail { template<typename RandomAccessIterator, typename Result, sprout::index_t... Indexes> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type dft_impl_ra( RandomAccessIterator first, RandomAccessIterator last, Result const& result, sprout::index_tuple<Indexes...>, typename sprout::container_traits<Result>::difference_type offset, typename sprout::container_traits<Result>::size_type size, typename sprout::container_traits<Result>::size_type input_size ) { return sprout::remake<Result>( result, size, (Indexes >= offset && sprout::math::less(Indexes, offset + size) && sprout::math::less(Indexes, offset + input_size) ? sprout::detail::dft_element_impl(first, last, Indexes - offset, input_size) : *sprout::next(sprout::internal_begin(result), Indexes) )... ); } template<typename RandomAccessIterator, typename Result> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type dft( RandomAccessIterator first, RandomAccessIterator last, Result const& result, std::random_access_iterator_tag* ) { return sprout::fixed::detail::dft_impl_ra( first, last, result, sprout::container_indexes<Result>::make(), sprout::internal_begin_offset(result), sprout::size(result), sprout::distance(first, last) ); } template<typename ForwardIterator, typename Result, typename... Args> inline SPROUT_CONSTEXPR typename std::enable_if< sprout::container_traits<Result>::static_size == sizeof...(Args), typename sprout::fixed::results::algorithm<Result>::type >::type dft_impl( ForwardIterator, ForwardIterator, Result const& result, typename sprout::container_traits<Result>::size_type, ForwardIterator, typename sprout::container_traits<Result>::difference_type, Args const&... args ) { return sprout::remake<Result>(result, sprout::size(result), args...); } template<typename ForwardIterator, typename Result, typename... Args> inline SPROUT_CONSTEXPR typename std::enable_if< sprout::container_traits<Result>::static_size != sizeof...(Args), typename sprout::fixed::results::algorithm<Result>::type >::type dft_impl( ForwardIterator first, ForwardIterator last, Result const& result, typename sprout::container_traits<Result>::size_type size, ForwardIterator first_, typename sprout::container_traits<Result>::difference_type i, Args const&... args ) { return first != last && sizeof...(Args) < size ? sprout::fixed::detail::dft_impl( sprout::next(first), last, result, size, first_, i + 1, args..., sprout::detail::dft_element_impl(first_, last, i, size) ) : sprout::detail::container_complate(result, args...) ; } template<typename ForwardIterator, typename Result> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type dft( ForwardIterator first, ForwardIterator last, Result const& result, std::forward_iterator_tag* ) { return sprout::fixed::detail::dft_impl(first, last, result, sprout::size(result), first, 0); } template<typename ForwardIterator, typename Result> inline SPROUT_CONSTEXPR typename std::enable_if< sprout::is_fixed_container<Result>::value, typename sprout::fixed::results::algorithm<Result>::type >::type dft(ForwardIterator first, ForwardIterator last, Result const& result) { typedef typename std::iterator_traits<ForwardIterator>::iterator_category* category; return sprout::fixed::detail::dft(first, last, result, category()); } template<typename ForwardIterator, typename Result> inline SPROUT_CONSTEXPR typename std::enable_if< !sprout::is_fixed_container<Result>::value, typename sprout::fixed::results::algorithm<Result>::type >::type dft(ForwardIterator first, ForwardIterator last, Result const& result) { return sprout::remake<Result>( result, sprout::size(result), sprout::make_dft_iterator(first, last), sprout::make_dft_iterator(first, last, sprout::distance(first, last)) ); } } // namespace detail // // dft // template<typename ForwardIterator, typename Result> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type dft(ForwardIterator first, ForwardIterator last, Result const& result) { return sprout::fixed::detail::dft(first, last, result); } template<typename Result, typename ForwardIterator> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Result>::type dft(ForwardIterator first, ForwardIterator last) { return sprout::fixed::dft(first, last, sprout::pit<Result>()); } } // namespace fixed using sprout::fixed::dft; } // namespace sprout #endif // #ifndef SPROUT_NUMERIC_DFT_FIXED_DFT_HPP
ディスパッチ処理等が含まれているのでコードがいささか煩雑だが、ようするに離散フーリエ変換の定義
における各 t に対する F(t) を逐次求めている。
時間計算量は標本点 N に対して Ο(N^2) である。
このほか、Sprout.Numeric.DFT にはスペクトル解析といった機能も用意されている。
実際に使ってみると以下のような感じになる。
値の計算はすべてコンパイル時に行われ、実行時には結果のファイルへの出力のみが行われる。
- コンパイル時DFTの例
#include <iostream> #include <fstream> #include <sprout/config.hpp> #include <sprout/array.hpp> #include <sprout/pit.hpp> #include <sprout/complex.hpp> #include <sprout/range/numeric/dft.hpp> #include <sprout/range/adaptor.hpp> #include <sprout/range/algorithm.hpp> #include <sprout/functional.hpp> template<typename Elem, typename Traits, typename InputRange> std::basic_ostream<Elem, Traits>& output_plot(std::basic_ostream<Elem, Traits>& os, InputRange const& range) { os << std::fixed; int x = 0; for (auto const& e : range) { os << x++ << ',' << e << '\n'; } return os; } template<typename Elem, typename Traits, typename InputRange> std::basic_ostream<Elem, Traits>& output_plot_real(std::basic_ostream<Elem, Traits>& os, InputRange const& range) { os << std::fixed; int x = 0; for (auto const& e : range) { os << x++ << ',' << real(e) << '\n'; } return os; } int main() { using namespace sprout; namespace adp = sprout::adaptors; constexpr std::size_t size = 256; typedef array<complex<double>, size> container_t; typedef array<double, size> spectrum_t; // 周波数 10, 25, 35 の正弦波の重ね合わせを生成 SPROUT_CONSTEXPR container_t src_data = adp::sinusoidal(10. / size, 10000.) | adp::transformed(adp::sinusoidal(25. / size, 5000.), plus<double>()) | adp::transformed(adp::sinusoidal(35. / size, 7500.), plus<double>()) | adp::copied ; { std::ofstream os("src.txt"); output_plot_real(os, src_data); } // DFT(離散フーリエ変換) SPROUT_CONSTEXPR auto dft_data = range::dft(src_data, pit<container_t>()); // IDFT(逆離散フーリエ変換) SPROUT_CONSTEXPR auto idft_data = range::idft(dft_data, pit<container_t>()); { std::ofstream os("dft_idft.txt"); output_plot_real(os, idft_data); } // 周波数スペクトルに変換 SPROUT_CONSTEXPR auto amp_spec_data = range::amplitude_spectrum(dft_data, pit<spectrum_t>()); { std::ofstream os("amp_spec.txt"); output_plot(os, amp_spec_data); } // 位相スペクトルに変換 SPROUT_CONSTEXPR auto pha_spec_data = range::phase_spectrum(dft_data, pit<spectrum_t>()); { std::ofstream os("pha_spec.txt"); output_plot(os, pha_spec_data); } }
なお、このプログラムを完全にコンパイルできるのは現状で gcc 5.0.0(experimental) しかないことに注意されたし。
gcc[trunk] のない読者の環境で結果だけ確認したい場合は、コード中の SPROUT_CONSTEXPR をコメントアウトすればよい。
出力を gnuplot でグラフ化すると以下のようになる。
- ソース
- 変換して再度戻す(DFT → IDFT)
- 位相スペクトル
さて、離散フーリエ変換の効率的なアルゴリズムとして非常に有名な FFT(高速フーリエ変換)があるが、C++11 constexpr の制約下では不可能である。
なぜなら、FFTのアルゴリズムは空間の再利用を行うため、オブジェクトの書き換えなしには実装できないからだ。
(同等の挙動を記述することは不可能ではないが、その場合時間計算量は Ο(N^2 log N)よりも悪くなるため意味がない)
C++14 では、constexpr FFT アルゴリズムも実装できる。
もちろん時間計算量も Ο(N log N) になる。
/*============================================================================= Copyright (c) 2011-2014 Bolero MURAKAMI https://github.com/bolero-MURAKAMI/Sprout Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #ifndef SPROUT_NUMERIC_FFT_FFT_HPP #define SPROUT_NUMERIC_FFT_FFT_HPP #include <iterator> #include <sprout/config.hpp> #include <sprout/complex.hpp> #include <sprout/math/cos.hpp> #include <sprout/math/sin.hpp> #include <sprout/math/constants.hpp> #include <sprout/utility/swap.hpp> namespace sprout { // // fft // template<typename RandomAccessIterator> inline SPROUT_CXX14_CONSTEXPR void fft(RandomAccessIterator first, RandomAccessIterator last) { typedef typename std::iterator_traits<RandomAccessIterator>::difference_type difference_type; typedef typename std::iterator_traits<RandomAccessIterator>::value_type value_type; typedef typename value_type::value_type elem_type; using sprout::real; using sprout::imag; difference_type const size = last - first; // scrambler for (difference_type i = 0, j = 1; j < size - 1; ++j) { for (difference_type k = size >> 1; k > (i ^= k); k >>= 1) ; if (j < i) { sprout::swap(first[i], first[j]); } } // L shaped butterflies elem_type const theta = -(sprout::math::half_pi<elem_type>() / size); for (difference_type m = 4; m <= size; m <<= 1) { difference_type mq = m >> 2; // W == 1 for (difference_type k = mq; k >= 1; k >>= 2) { for (difference_type j = mq - k; j < mq - (k >> 1); ++j) { difference_type j1 = j + mq; difference_type j2 = j1 + mq; difference_type j3 = j2 + mq; value_type x1 = first[j] - first[j1]; first[j] += first[j1]; value_type x3 = first[j3] - first[j2]; first[j2] += first[j3]; first[j1] = value_type( real(x1) - imag(x3), imag(x1) + real(x3) ); first[j3] = value_type( real(x1) + imag(x3), imag(x1) - real(x3) ); } } if (m == size) { continue; } difference_type irev = size >> 1; elem_type w1r = sprout::cos(theta * irev); for (difference_type k = mq; k >= 1; k >>= 2) { for (difference_type j = m + mq - k; j < m + mq - (k >> 1); ++j) { difference_type j1 = j + mq; difference_type j2 = j1 + mq; difference_type j3 = j2 + mq; value_type x1 = first[j] - first[j1]; first[j] += first[j1]; value_type x3 = first[j3] - first[j2]; first[j2] += first[j3]; elem_type x0r = real(x1) - imag(x3); elem_type x0i = imag(x1) + real(x3); first[j1] = value_type( w1r * (x0r + x0i), w1r * (x0i - x0r) ); x0r = real(x1) + imag(x3); x0i = imag(x1) - real(x3); first[j3] = value_type( w1r * (-x0r + x0i), w1r * (-x0i - x0r) ); } } for (difference_type i = 2 * m; i < size; i += m) { for (difference_type k = size >> 1; k > (irev ^= k); k >>= 1) ; elem_type w1r = sprout::cos(theta * irev); elem_type w1i = sprout::sin(theta * irev); elem_type w3r = sprout::cos(theta * 3 * irev); elem_type w3i = sprout::sin(theta * 3 * irev); for (difference_type k = mq; k >= 1; k >>= 2) { for (difference_type j = i + mq - k; j < i + mq - (k >> 1); ++j) { difference_type j1 = j + mq; difference_type j2 = j1 + mq; difference_type j3 = j2 + mq; value_type x1 = first[j] - first[j1]; first[j] += first[j1]; value_type x3 = first[j3] - first[j2]; first[j2] += first[j3]; elem_type x0r = real(x1) - imag(x3); elem_type x0i = imag(x1) + real(x3); first[j1] = value_type( w1r * x0r - w1i * x0i, w1r * x0i + w1i * x0r ); x0r = real(x1) + imag(x3); x0i = imag(x1) - real(x3); first[j3] = value_type( w3r * x0r - w3i * x0i, w3r * x0i + w3i * x0r ); } } } } // radix 2 butterflies difference_type mq = size >> 1; for (difference_type k = mq; k >= 1; k >>= 2) { for (difference_type j = mq - k; j < mq - (k >> 1); ++j) { difference_type j1 = mq + j; value_type x0 = first[j] - first[j1]; first[j] += first[j1]; first[j1] = x0; } } } } // namespace sprout #endif // #ifndef SPROUT_NUMERIC_FFT_FFT_HPP
使用例はこのようになる。
#include <iostream> #include <fstream> #include <sprout/array.hpp> #include <sprout/pit.hpp> #include <sprout/complex.hpp> #include <sprout/range/numeric/dft.hpp> #include <sprout/range/numeric/fft.hpp> #include <sprout/range/adaptor.hpp> #include <sprout/range/algorithm.hpp> #include <sprout/functional.hpp> template<typename Elem, typename Traits, typename InputRange> std::basic_ostream<Elem, Traits>& output_plot(std::basic_ostream<Elem, Traits>& os, InputRange const& range) { os << std::fixed; int x = 0; for (auto const& e : range) { os << x++ << ',' << e << '\n'; } return os; } template<typename Elem, typename Traits, typename InputRange> std::basic_ostream<Elem, Traits>& output_plot_real(std::basic_ostream<Elem, Traits>& os, InputRange const& range) { os << std::fixed; int x = 0; for (auto const& e : range) { os << x++ << ',' << real(e) << '\n'; } return os; } SPROUT_CONSTEXPR std::size_t size = 256; typedef sprout::array<sprout::complex<double>, size> container_t; typedef sprout::array<double, size> spectrum_t; SPROUT_CXX14_CONSTEXPR container_t fft(container_t const& src_data) { using namespace sprout; auto result = src_data; range::fft(result); return result; } SPROUT_CXX14_CONSTEXPR container_t ifft(container_t const& src_data) { using namespace sprout; auto result = src_data; range::ifft(result); return result; } SPROUT_CXX14_CONSTEXPR spectrum_t amplitude_spectrum(container_t const& dft_data) { using namespace sprout; auto result = spectrum_t(); range::amplitude_spectrum(dft_data, sprout::begin(result)); return result; } SPROUT_CXX14_CONSTEXPR spectrum_t phase_spectrum(container_t const& dft_data) { using namespace sprout; auto result = spectrum_t(); range::phase_spectrum(dft_data, sprout::begin(result)); return result; } int main() { using namespace sprout; namespace adp = sprout::adaptors; // 周波数 10, 25, 35 の正弦波の重ね合わせを生成 SPROUT_CXX14_CONSTEXPR container_t src_data = adp::sinusoidal(10. / ::size, 10000.) | adp::transformed(adp::sinusoidal(25. / ::size, 5000.), plus<double>()) | adp::transformed(adp::sinusoidal(35. / ::size, 7500.), plus<double>()) | adp::copied ; { std::ofstream os("src.txt"); output_plot_real(os, src_data); } // DFT(離散フーリエ変換) SPROUT_CXX14_CONSTEXPR auto dft_data = ::fft(src_data); // IDFT(逆離散フーリエ変換) SPROUT_CXX14_CONSTEXPR auto idft_data = ::ifft(dft_data); { std::ofstream os("dft_idft.txt"); output_plot_real(os, idft_data); } // 周波数スペクトルに変換 SPROUT_CXX14_CONSTEXPR auto amp_spec_data = ::amplitude_spectrum(dft_data); { std::ofstream os("amp_spec.txt"); output_plot(os, amp_spec_data); } // 位相スペクトルに変換 SPROUT_CXX14_CONSTEXPR auto pha_spec_data = ::phase_spectrum(dft_data); { std::ofstream os("pha_spec.txt"); output_plot(os, pha_spec_data); } }
出力は最初と同様なので割愛する。
このように、C++14 では、C++11 で実装できなかったコンパイル時アルゴリズムを実装できるようになった。
これが進歩というものである。
コンパイル時Brainfuckコンパイラ (x86)
多くのプログラマが一度はBrainfuck処理系を実装したことがあるだろう。
C++11 constexpr の制約下でも、「Brainfuckインタプリタ」を実装することは可能である。
/*============================================================================= Copyright (c) 2011-2014 Bolero MURAKAMI https://github.com/bolero-MURAKAMI/Sprout Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #ifndef SPROUT_BRAINFUCK_BRAINFUCK_HPP #define SPROUT_BRAINFUCK_BRAINFUCK_HPP #include <iterator> #include <stdexcept> #include <sprout/config.hpp> #include <sprout/workaround/std/cstddef.hpp> #include <sprout/array/array.hpp> #include <sprout/pit/pit.hpp> #include <sprout/iterator/operation.hpp> #include <sprout/iterator/value_iterator.hpp> #include <sprout/container/traits.hpp> #include <sprout/container/functions.hpp> #include <sprout/algorithm/fixed/results.hpp> #include <sprout/algorithm/fixed/copy.hpp> #include <sprout/operation/fixed/set.hpp> #include <sprout/detail/char_literal.hpp> #include HDR_ALGORITHM_MIN_MAX_SSCRISK_CEL_OR_SPROUT namespace sprout { namespace brainfuck { namespace detail { template<typename InputIterator> inline SPROUT_CONSTEXPR InputIterator find_scope_end(InputIterator first, std::size_t count = 0) { typedef typename std::iterator_traits<InputIterator>::value_type value_type; return *first == SPROUT_CHAR_LITERAL('[', value_type) ? sprout::brainfuck::detail::find_scope_end(sprout::next(first), count + 1) : *first == SPROUT_CHAR_LITERAL(']', value_type) ? count == 0 ? first : sprout::brainfuck::detail::find_scope_end(sprout::next(first), count - 1) : sprout::brainfuck::detail::find_scope_end(sprout::next(first), count) ; } template<typename BidirectionalIterator> inline SPROUT_CONSTEXPR BidirectionalIterator find_scope_start(BidirectionalIterator first, std::size_t count = 0) { typedef typename std::iterator_traits<BidirectionalIterator>::value_type value_type; return *first == SPROUT_CHAR_LITERAL(']', value_type) ? sprout::brainfuck::detail::find_scope_start(sprout::prev(first), count + 1) : *first == SPROUT_CHAR_LITERAL('[', value_type) ? count == 0 ? first : sprout::brainfuck::detail::find_scope_start(sprout::prev(first), count - 1) : sprout::brainfuck::detail::find_scope_start(sprout::prev(first), count) ; } template<typename InputIterator> inline SPROUT_CONSTEXPR bool is_well_formed(InputIterator first, InputIterator last, std::size_t count = 0) { typedef typename std::iterator_traits<InputIterator>::value_type value_type; return first == last ? count == 0 : *first == SPROUT_CHAR_LITERAL('[', value_type) ? sprout::brainfuck::detail::is_well_formed(sprout::next(first), last, count + 1) : *first == SPROUT_CHAR_LITERAL(']', value_type) ? count != 0 && sprout::brainfuck::detail::is_well_formed(sprout::next(first), last, count - 1) : sprout::brainfuck::detail::is_well_formed(sprout::next(first), last, count) ; } template< typename BidirectionalIteratorSource, typename Output, typename InputIteratorInput, typename Buffer, typename OutputBuffer > inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type exec_impl( BidirectionalIteratorSource first, BidirectionalIteratorSource last, Output const& output, InputIteratorInput in_first, InputIteratorInput in_last, Buffer const& buffer, OutputBuffer const& out_buffer, std::size_t pos = 0, std::size_t out_pos = 0 ) { typedef typename std::iterator_traits<BidirectionalIteratorSource>::value_type value_type; typedef typename sprout::container_traits<OutputBuffer>::value_type out_value_type; return first == last ? sprout::fixed::copy( sprout::begin(out_buffer), sprout::next(sprout::begin(out_buffer), NS_SSCRISK_CEL_OR_SPROUT::min(out_pos, sprout::size(out_buffer))), output ) : *first == SPROUT_CHAR_LITERAL('>', value_type) ? sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, in_first, in_last, buffer, out_buffer, pos + 1, out_pos ) : *first == SPROUT_CHAR_LITERAL('<', value_type) ? sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, in_first, in_last, buffer, out_buffer, pos - 1, out_pos ) : *first == SPROUT_CHAR_LITERAL('+', value_type) ? sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, in_first, in_last, sprout::fixed::set(buffer, pos, value_type(buffer.at(pos) + 1)), out_buffer, pos, out_pos ) : *first == SPROUT_CHAR_LITERAL('+', value_type) ? sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, in_first, in_last, sprout::fixed::set(buffer, pos, value_type(buffer.at(pos) - 1)), out_buffer, pos, out_pos ) : *first == SPROUT_CHAR_LITERAL('.', value_type) ? out_pos != out_buffer.size() ? sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, in_first, in_last, buffer, sprout::fixed::set(out_buffer, out_pos, out_value_type(buffer.at(pos))), pos, out_pos + 1 ) : throw std::out_of_range("output out of range") : *first == SPROUT_CHAR_LITERAL(',', value_type) ? in_first != in_last ? sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, sprout::next(in_first), in_last, sprout::fixed::set(buffer, pos, value_type(*in_first)), out_buffer, pos, out_pos ) : throw std::out_of_range("input out of range") : *first == SPROUT_CHAR_LITERAL('[', value_type) ? buffer.at(pos) == 0 ? sprout::brainfuck::detail::exec_impl( sprout::next(sprout::brainfuck::detail::find_scope_end(sprout::next(first))), last, output, in_first, in_last, buffer, out_buffer, pos, out_pos ) : sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, in_first, in_last, buffer, out_buffer, pos, out_pos ) : *first == SPROUT_CHAR_LITERAL(']', value_type) ? buffer.at(pos) != 0 ? sprout::brainfuck::detail::exec_impl( sprout::next(sprout::brainfuck::detail::find_scope_start(sprout::prev(first))), last, output, in_first, in_last, buffer, out_buffer, pos, out_pos ) : sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, in_first, in_last, buffer, out_buffer, pos, out_pos ) : sprout::brainfuck::detail::exec_impl( sprout::next(first), last, output, in_first, in_last, buffer, out_buffer, pos, out_pos ) ; } } // namespace detail // // exec // template<std::size_t BufferSize = 32, typename BidirectionalIteratorSource, typename Output, typename InputIteratorInput> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type exec( BidirectionalIteratorSource first, BidirectionalIteratorSource last, Output const& output, InputIteratorInput in_first, InputIteratorInput in_last ) { typedef typename std::iterator_traits<BidirectionalIteratorSource>::value_type value_type; typedef sprout::container_traits<Output> out_traits; return sprout::brainfuck::detail::exec_impl( first, last, output, in_first, in_last, sprout::array<value_type, BufferSize>{{}}, sprout::array<typename out_traits::value_type, out_traits::static_size>{{}} ); } template<std::size_t BufferSize = 32, typename BidirectionalIteratorSource, typename Output> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type exec( BidirectionalIteratorSource first, BidirectionalIteratorSource last, Output const& output ) { typedef typename std::iterator_traits<BidirectionalIteratorSource>::value_type value_type; return sprout::brainfuck::exec<BufferSize>( first, last, output, sprout::value_iterator<value_type>(value_type()), sprout::value_iterator<value_type>() ); } template<std::size_t BufferSize = 32, typename BidirectionalIteratorSource> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm< sprout::array<typename std::iterator_traits<BidirectionalIteratorSource>::value_type, BufferSize> >::type exec( BidirectionalIteratorSource first, BidirectionalIteratorSource last ) { typedef typename std::iterator_traits<BidirectionalIteratorSource>::value_type value_type; return sprout::brainfuck::exec<BufferSize>( first, last, sprout::pit<sprout::array<value_type, BufferSize> >() ); } // // exec_range // template<std::size_t BufferSize = 32, typename BidirectionalRangeSource, typename Output, typename InputRangeInput> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type exec_range(BidirectionalRangeSource const& source, Output const& output, InputRangeInput const& input) { return sprout::brainfuck::exec<BufferSize>( sprout::begin(source), sprout::end(source), output, sprout::begin(input), sprout::end(input) ); } template<std::size_t BufferSize = 32, typename BidirectionalRangeSource, typename Output> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm<Output>::type exec_range(BidirectionalRangeSource const& source, Output const& output) { return sprout::brainfuck::exec<BufferSize>( sprout::begin(source), sprout::end(source), output ); } template<std::size_t BufferSize = 32, typename BidirectionalRangeSource> inline SPROUT_CONSTEXPR typename sprout::fixed::results::algorithm< sprout::array<typename sprout::container_traits<BidirectionalRangeSource>::value_type, BufferSize> >::type exec_range(BidirectionalRangeSource const& source) { return sprout::brainfuck::exec<BufferSize>( sprout::begin(source), sprout::end(source) ); } // // is_well_formed // template<typename InputIterator> inline SPROUT_CONSTEXPR bool is_well_formed(InputIterator first, InputIterator last) { return sprout::brainfuck::detail::is_well_formed(first, last); } } // namespace brainfuck } // namespace sprout #endif // #ifndef SPROUT_BRAINFUCK_BRAINFUCK_HPP
しかし「Brainfuckコンパイラ」となると、C++11 constexpr では困難なうえに、実装できたとしても非常に非効率な処理にならざるをえない。
実際、筆者も以前一度実装しようとして挫折している。
C++14 では、コンパイル時Brainfuckコンパイラも容易に実装できる。
#include <cstddef> #include <cstdint> #include <iterator> #include <iostream> #include <fstream> #include <sprout/config.hpp> #include <sprout/endian_traits.hpp> #include <sprout/string.hpp> #include <sprout/array.hpp> #include <sprout/sub_array.hpp> #include <sprout/container/functions.hpp> #include <sprout/range/algorithm.hpp> #include <sprout/preprocessor/stringize.hpp> #include <sprout/assert.hpp> // // PE関連の構造体 // <windows.h> からコピペ // typedef unsigned long DWORD; typedef int BOOL; typedef unsigned char BYTE; typedef unsigned short WORD; // // File header format. // typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; // // Directory format. // typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 // // Optional header format. // typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; // // NT additional fields. // DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; // // Section header format. // #define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // union { // DWORD PhysicalAddress; // DWORD VirtualSize; // } Misc; struct { DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; // // 出力バイナリサイズ // #ifndef BRAINFUCK_BINARY_SIZE # define BRAINFUCK_BINARY_SIZE (16 * 1024) #endif // // ループ制限 // #ifndef BRAINFUCK_LOOP_LIMIT # define BRAINFUCK_LOOP_LIMIT 256 #endif // // 入力ファイル名 // #ifndef BRAINFUCK_SOURCE_FILE # define BRAINFUCK_SOURCE_FILE a.bf #endif // // 定数 // SPROUT_STATIC_CONSTEXPR std::size_t bin_size = BRAINFUCK_BINARY_SIZE; SPROUT_STATIC_CONSTEXPR std::size_t loop_limit = BRAINFUCK_LOOP_LIMIT; SPROUT_STATIC_CONSTEXPR auto source = sprout::to_string( # include SPROUT_PP_STRINGIZE(BRAINFUCK_SOURCE_FILE) ); SPROUT_STATIC_CONSTEXPR std::int32_t addr_putchar = 0x00405044; SPROUT_STATIC_CONSTEXPR std::int32_t addr_getchar = addr_putchar + 4; SPROUT_STATIC_CONSTEXPR std::int32_t addr_buf = 0x00406000; // // 出力用関数 // template<typename OutputIterator, typename T> SPROUT_CXX14_CONSTEXPR std::size_t write_bytes(OutputIterator& out, T const& val) { typedef sprout::little_endian_traits<T> traits; for (std::size_t i = 0; i != traits::size(); ++i) { *out++ = sprout::little_endian_traits<T>::get_byte(val, i); } return traits::size(); } template<typename OutputIterator, typename T, std::size_t N> SPROUT_CXX14_CONSTEXPR std::size_t write_bytes(OutputIterator& out, T const(& vals)[N]) { typedef sprout::little_endian_traits<T> traits; for (std::size_t i = 0; i != N; ++i) { ::write_bytes(out, vals[i]); } return traits::size() * N; } template<typename OutputIterator, typename Head, typename... Tail> SPROUT_CXX14_CONSTEXPR std::size_t write_bytes(OutputIterator& out, Head const& head, Tail const&... tail) { return ::write_bytes(out, head) + ::write_bytes(out, tail...); } template<typename OutputIterator> SPROUT_CXX14_CONSTEXPR std::size_t write_pe_header(OutputIterator& out, std::size_t n = 0) { SPROUT_CONSTEXPR unsigned char stub[] = { // 00-3b: DOS Header 'M', 'Z', 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0f, 0x00, 0xff, 0xff, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3c-3f: Pointer to PE Header (=80) 0x80, 0x00, 0x00, 0x00, // 40-7f: DOS Stub 0xba, 0x10, 0x00, 0x0e, 0x1f, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 0x21, 0x90, 0x90, 'T', 'h', 'i', 's', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm', ' ', 'c', 'a', 'n', 'n', 'o', 't', ' ', 'b', 'e', ' ', 'r', 'u', 'n', ' ', 'i', 'n', ' ', 'D', 'O', 'S', ' ', 'm', 'o', 'd', 'e', '.', '\r', '\n', '$', 0, 0, 0, 0, 0, 0, // 80-83: PE Signature 'P', 'E', 0, 0 }; n += ::write_bytes( out, stub ); SPROUT_CONSTEXPR ::IMAGE_FILE_HEADER coff = { 0x014c, // Machine 3, // NumberOfSections 0, // TimeDateStamp 0, // PointerToSymbolTable 0, // NumberOfSymbols sizeof(::IMAGE_OPTIONAL_HEADER32), // SizeOfOptionalHeader 0x030f // Characteristics }; n += ::write_bytes( out, coff.Machine, coff.NumberOfSections, coff.TimeDateStamp, coff.PointerToSymbolTable, coff.NumberOfSymbols, coff.SizeOfOptionalHeader, coff.Characteristics ); SPROUT_CONSTEXPR ::IMAGE_OPTIONAL_HEADER32 opt = { 0x010b, // Magic 6, 0, // MajorLinkerVersion, MinorLinkerVersion ::bin_size, // SizeOfCode 0, // SizeOfInitializedData 65536, // SizeOfUninitializedData 0x1000, // AddressOfEntryPoint 0x1000, // BaseOfCode 0x6000, // BaseOfData 0x00400000, // ImageBase 0x1000, // SectionAlignment 0x0200, // FileAlignment 4, 0, // MajorOperatingSystemVersion, MinorOperatingSystemVersion 0, 0, // MajorImageVersion, MinorImageVersion 4, 0, // MajorSubsystemVersion, MinorSubsystemVersion 0, // Win32VersionValue 0x16000, // SizeOfImage 0x200, // SizeOfHeaders 0, // CheckSum 3, // Subsystem 0, // DllCharacteristics 1024 * 1024, // SizeOfStackReserve 8 * 1024, // SizeOfStackCommit 1024 * 1024, // SizeOfHeapReserve 4 * 1024, // SizeOfHeapCommit 0, // LoaderFlags 16, // NumberOfRvaAndSizes { {}, { 0x5000, // VirtualAddress (import table) 100 // Size }, {} } }; n += ::write_bytes( out, opt.Magic, opt.MajorLinkerVersion, opt.MinorLinkerVersion, opt.SizeOfCode, opt.SizeOfInitializedData, opt.SizeOfUninitializedData, opt.AddressOfEntryPoint, opt.BaseOfCode, opt.BaseOfData, opt.ImageBase, opt.SectionAlignment, opt.FileAlignment, opt.MajorOperatingSystemVersion, opt.MinorOperatingSystemVersion, opt.MajorImageVersion, opt.MinorImageVersion, opt.MajorSubsystemVersion, opt.MinorSubsystemVersion, opt.Win32VersionValue, opt.SizeOfImage, opt.SizeOfHeaders, opt.CheckSum, opt.Subsystem, opt.DllCharacteristics, opt.SizeOfStackReserve, opt.SizeOfStackCommit, opt.SizeOfHeapReserve, opt.SizeOfHeapCommit, opt.LoaderFlags, opt.NumberOfRvaAndSizes, opt.DataDirectory[0].VirtualAddress, opt.DataDirectory[0].Size, opt.DataDirectory[1].VirtualAddress, opt.DataDirectory[1].Size ); for (std::size_t i = 2; i != IMAGE_NUMBEROF_DIRECTORY_ENTRIES; ++i) { n += ::write_bytes( out, opt.DataDirectory[i].VirtualAddress, opt.DataDirectory[i].Size ); } SPROUT_CONSTEXPR ::IMAGE_SECTION_HEADER sects[3] = { { ".text", // Name {::bin_size}, // Misc.VirtualSize 0x1000, // VirtualAddress ::bin_size, // SizeOfRawData 0x400, // PointerToRawData 0, // PointerToRelocations 0, // PointerToLinenumbers 0, // NumberOfRelocations 0, // NumberOfLinenumbers 0x60500060 // Characteristics }, { ".idata", // Name {100}, // Misc.VirtualSize 0x5000, // VirtualAddress 512, // SizeOfRawData 0x200, // PointerToRawData 0, // PointerToRelocations 0, // PointerToLinenumbers 0, // NumberOfRelocations 0, // NumberOfLinenumbers 0xc0300040 // Characteristics }, { ".bss", // Name {65536}, // Misc.VirtualSize 0x6000, // VirtualAddress 0, // SizeOfRawData 0, // PointerToRawData 0, // PointerToRelocations 0, // PointerToLinenumbers 0, // NumberOfRelocations 0, // NumberOfLinenumbers 0xc0400080 // Characteristics } }; for (std::size_t i = 0; i != 3; ++i) { n += ::write_bytes( out, sects[i].Name, sects[i].Misc.VirtualSize, sects[i].VirtualAddress, sects[i].SizeOfRawData, sects[i].PointerToRawData, sects[i].PointerToRelocations, sects[i].PointerToLinenumbers, sects[i].NumberOfRelocations, sects[i].NumberOfLinenumbers, sects[i].Characteristics ); } for (; n != 0x200; ) { n += ::write_bytes( out, (unsigned char)0 ); } return n; } template<typename OutputIterator> SPROUT_CXX14_CONSTEXPR std::size_t write_idata(OutputIterator& out, std::size_t n = 0) { SPROUT_CONSTEXPR int idt[] = { // IDT 1 0x5028, 0, 0, 0x5034, 0x5044, // IDT (終端) 0, 0, 0, 0, 0 }; n += ::write_bytes( out, idt ); SPROUT_CONSTEXPR int ilt_iat[] = { 0x5050, 0x505a, 0 }; // ILT n += ::write_bytes( out, ilt_iat ); n += ::write_bytes( out, "msvcrt.dll\0\0\0\0\0" ); // IAT n += ::write_bytes( out, ilt_iat ); n += ::write_bytes( out, (std::int16_t)0, "putchar", (std::int16_t)0, "getchar" ); for (; n != 0x400; ) { n += ::write_bytes( out, (unsigned char)0 ); } return n; } // // コンパイル // template<std::size_t LoopLimit = 256, typename InputIterator, typename RandomAccessIterator> SPROUT_CXX14_CONSTEXPR std::size_t compile(InputIterator first, InputIterator last, RandomAccessIterator& out_first) { sprout::array<int, LoopLimit> loops {{}}; // ループを保存するスタック auto loop_first = sprout::begin(loops); auto loop_last = sprout::end(loops); auto loop = loop_first; int idx = 0; auto out = out_first; idx += ::write_bytes( out, (unsigned char)0x31, (unsigned char)0xc9, // xor ecx, ecx (unsigned char)0x57, // push edi (unsigned char)0xbf, ::addr_buf // mov edi, addr_buf ); for (; first != last; ++first) { switch (*first) { case '>': idx += ::write_bytes( out, (unsigned char)0x66, (unsigned char)0x41 // inc cx ); break; case '<': idx += ::write_bytes( out, (unsigned char)0x66, (unsigned char)0x49 // dec cx ); break; case '+': idx += ::write_bytes( out, (unsigned char)0xfe, (unsigned char)0x04, (unsigned char)0x0f // inc byte [edi+ecx] ); break; case '-': idx += ::write_bytes( out, (unsigned char)0xfe, (unsigned char)0x0c, (unsigned char)0x0f // dec byte [edi+ecx] ); break; case '.': idx += ::write_bytes( out, (unsigned char)0x51, // push ecx (unsigned char)0x0f, (unsigned char)0xb6, (unsigned char)0x04, (unsigned char)0x0f, // movzx eax,byte [edi+ecx] (unsigned char)0x50, // push eax (unsigned char)0xa1, ::addr_putchar, // mov eax, [addr_putchar] (unsigned char)0xff, (unsigned char)0xd0, // call eax (unsigned char)0x58, // pop eax (unsigned char)0x59 // pop ecx ); break; case ',': idx += ::write_bytes( out, (unsigned char)0x51, // push ecx (unsigned char)0xa1, ::addr_getchar, // mov eax, [addr_getchar] (unsigned char)0xff, (unsigned char)0xd0, // call eax (unsigned char)0x59, // pop ecx (unsigned char)0x88, (unsigned char)0x04, (unsigned char)0x0f // mov [edi+ecx],al ); break; case '[': SPROUT_ASSERT_MSG(loop != loop_last, "loop overflow"); *loop++ = idx; // インデックスをスタックに積む idx += ::write_bytes( out, (unsigned char)0x80, (unsigned char)0x3c, (unsigned char)0x0f, (unsigned char)0x00, // cmp byte [edi+ecx],0 (unsigned char)0x0f, (unsigned char)0x84, // jz 対応する]の直後 (unsigned char)0xde, (unsigned char)0xad, (unsigned char)0xbe, (unsigned char)0xef // (アドレスは後で決定) ); break; case ']': { SPROUT_ASSERT_MSG(loop != loop_first, "missing '['"); int idx_loop = *--loop; idx += ::write_bytes( out, (unsigned char)0xe9, (std::int32_t)(idx_loop - (idx + 5)) // jmp idx_loop ); auto out_loop = out_first + (idx_loop + 6); ::write_bytes( out_loop, (std::int32_t)(idx - (idx_loop + 10)) // (アドレス) ); break; } } SPROUT_ASSERT_MSG(idx <= (int)::bin_size - 32, "buffer overflow"); } SPROUT_ASSERT_MSG(loop == loop_first, "missing ']'"); // 終了処理 idx += ::write_bytes( out, (unsigned char)0x5f, // pop edi (unsigned char)0x31, (unsigned char)0xc0, // xor eax, eax (unsigned char)0xc3 // ret ); return idx; } // // ビルド // template<std::size_t LoopLimit = 256, typename InputIterator, typename RandomAccessIterator> SPROUT_CXX14_CONSTEXPR std::size_t build(InputIterator first, InputIterator last, RandomAccessIterator& out) { std::size_t n = 0; n += ::write_pe_header(out, n); // ヘッダ出力 n += ::write_idata(out, n); // .idataセクション出力 n += ::compile<LoopLimit>(first, last, out); // コンパイル return n; } SPROUT_CXX14_CONSTEXPR sprout::sub_array<sprout::array<unsigned char, ::bin_size> > build_brainfuck() { sprout::array<unsigned char, ::bin_size> bin{{}}; auto out = sprout::begin(bin); auto n = ::build<::loop_limit>(sprout::begin(::source), sprout::end(::source), out); return sprout::sub_copy(bin, 0, n); } int main(int argc, char* argv[]) { SPROUT_CXX14_CONSTEXPR auto bin = ::build_brainfuck(); char const* filename = argc >= 2 ? argv[1] : "a.exe" ; std::ofstream ofs(filename); if (!ofs) { std::cout << "could not open file" << std::endl ; return -1; } sprout::range::copy(bin, std::ostreambuf_iterator<char>(ofs)); }
このとおりだ。
実装にあたっては 七誌 氏による実行時Brainfuckコンパイラのコードを参考にした。
C++14 constexpr の制約を満たす上では、出力先を FILE* ではなく OutputIterator で渡すようにしたり、バイト毎に(C++処理系のエンディアンにかかわらず)リトルエンディアンで出力されるようにした。
また、非リテラル型である std::vector 等も使わないようにした。
その他関数の切り分け方などいくつかの相違点があるが、オペコード生成とPEヘッダ付加の処理の流れは実行時コンパイラとほとんど変わりない。
見てのとおり、非常に明快でわかりやすいコードになっている。
C++14 constexpr の制約を満たすよう実装したことによる、メリットと言えることがいくつかある。
- エンディアンといった処理系依存の挙動が除去された
- (なぜなら、処理系依存の挙動は基本的に定数式になれない)
- (非コンパイル時定数の)グローバル変数が除去された
- (グローバル変数は非定数式である)
- 関数のインタフェースがよりジェネリックプログラミング仕様になった
- (std::fwrite() といった非constexpr関数が使えないため、特定の副作用や型に依存しないデザインパターンを用いる必要がある)
- 副作用のない処理部分がコードから切り出された
これらのメリットは、単にコンパイル時処理の範疇だけに留まるものではない。
総称的で、変更に強く、副作用にまつわるバグのリスクを低減する、モダンなプログラミングスタイルの指針のいくつかを満足するものだ。
とはいえデメリットもある。
C++14 でも相変わらず定数式で動的オブジェクトを扱うことができないため、コンパイル時処理の場合は固定長バッファを用いるほかない。
しかしながら、インタフェースを工夫することで、array
さて、このコンパイル時コンパイラでBrainfuckソースをコンパイルしたバイナリをWindows上で実行した結果をいろいろ見てみる。
なお、Brainfuckソースは C++ の文字列リテラルとして記述する必要がある。これは、#include でソースをそのまま読み込めるようにするためだ。
- hello.bf
// Hello, world! "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-." "------------.<++++++++.--------.+++.------.--------.>+."
- 出力
Hello, world!
- echo.bf (入力した文字列をそのまま出力する)
// echo "+[>,.<]"
- 入力
foobar
- 出力
foobar
- fizzBuzz.bf
// FizzBuzz "++++++++++++[->++++++>+++++++++>+++++>++++++++++>++++++++++>+++>>>>>>++++++++<<<<<<<<<<<<]>-->--->++" "++++>--->++>---->>>>+++>+++++>++++[>>>+[-<<[->>+>+<<<]>>>[-<<<+>>>]+<[[-]>-<<[->+>+<<]>>[-<<+>>]+<[[" "-]>-<<<+>->]>[-<<<--------->+++++++++>>>>>+<<<]<]>[-<+++++++[<<+++++++>>-]<++++++++>>]>>>]<<<<<<[<<<" "<]>-[-<<+>+>]<[->+<]+<[[-]>-<]>[->+++<<<<<<<<<.>.>>>..>>+>>]>>-[-<<<+>+>>]<<[->>+<<]+<[[-]>-<]>[->>+" "++++<<<<<<<<.>.>..>>+>>]<+<[[-]>-<]>[->>>>>[>>>>]<<<<[.<<<<]<]<<.>>>>>>-]"
- 出力
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz Buzz
// Quine "-" ">++>+++>+>+>+++>>>>>>>>>>>>>>>>>>>>>>+>+>++>+++>++>>+++" ">+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+>+>>+++>>>>+++>>>+++" ">+>>>>>>>++>+++>+++>+>>+++>+++>+>+++>+>+++>+>++>+++>>>+" ">+>+>+>++>+++>+>+>>+++>>>>>>>+>+>>>+>+>++>+++>+++>+>>+++" ">+++>+>+++>+>++>+++>++>>+>+>++>+++>+>+>>+++>>>+++>+>>>++" ">+++>+++>+>>+++>>>+++>+>+++>+>>+++>>+++>>" "+[[>>+[>]+>+[<]<-]>>[>]<+<+++[<]<<+]" ">>>[>]+++>+" "[+[<++++++++++++++++>-]<++++++++++.<]"
- 出力
->++>+++>+>+>+++>>>>>>>>>>>>>>>>>>>>>>+>+>++>+++>++>>+++>+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+>+>>+++>>>>+++>>>+++>+>>>>>>>++>+++>+++>+>>+++>+++>+>+++>+>+++>+>++>+++>>>+>+>+>+>++>+++>+>+>>+++>>>>>>>+>+>>>+>+>++>+++>+++>+>>+++>+++>+>+++>+>++>+++>++>>+>+>++>+++>+>+>>+++>>>+++>+>>>++>+++>+++>+>>+++>>>+++>+>+++>+>>+++>>+++>>+[[>>+[>]+>+[<]<-]>>[>]<+<+++[<]<<+]>>>[>]+++>+[+[<++++++++++++++++>-]<++++++++++.<]
- bottles.bf (99本のボトルが0本になるまで飲み干していくカウントダウン)
// 99 Bottles of Beer ">+++++++++[<+++++++++++>-]<[>[-]>[-]<<[>+>+<<-]>>[<<+>>-]>>>" "[-]<<<+++++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+<" "-]<<-<-]+++++++++>[<->-]>>+>[<[-]<<+>>>-]>[-]+<<[>+>-<<-]<<<" "[>>+>+<<<-]>>>[<<<+>>>-]>[<+>-]<<-[>[-]<[-]]>>+<[>[-]<-]<+++" "+++++[<++++++<++++++>>-]>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>-" "]<<<<<<.>>[-]>[-]++++[<++++++++>-]<.>++++[<++++++++>-]<++.>+" "++++[<+++++++++>-]<.><+++++..--------.-------.>>[>>+>+<<<-]>" ">>[<<<+>>>-]<[<<<<++++++++++++++.>>>>-]<<<<[-]>++++[<+++++++" "+>-]<.>+++++++++[<+++++++++>-]<--.---------.>+++++++[<------" "---->-]<.>++++++[<+++++++++++>-]<.+++..+++++++++++++.>++++++" "++[<---------->-]<--.>+++++++++[<+++++++++>-]<--.-.>++++++++" "[<---------->-]<++.>++++++++[<++++++++++>-]<++++.-----------" "-.---.>+++++++[<---------->-]<+.>++++++++[<+++++++++++>-]<-." ">++[<----------->-]<.+++++++++++..>+++++++++[<---------->-]<" "-----.---.>>>[>+>+<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>>+++" "+[<++++++>-]<--.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<." "><+++++..--------.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++" "++++++++++++.>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<++" "+++++++>-]<--.---------.>+++++++[<---------->-]<.>++++++[<++" "+++++++++>-]<.+++..+++++++++++++.>++++++++++[<---------->-]<" "-.---.>+++++++[<++++++++++>-]<++++.+++++++++++++.++++++++++." "------.>+++++++[<---------->-]<+.>++++++++[<++++++++++>-]<-." "-.---------.>+++++++[<---------->-]<+.>+++++++[<++++++++++>-" "]<--.+++++++++++.++++++++.---------.>++++++++[<---------->-]" "<++.>+++++[<+++++++++++++>-]<.+++++++++++++.----------.>++++" "+++[<---------->-]<++.>++++++++[<++++++++++>-]<.>+++[<----->" "-]<.>+++[<++++++>-]<..>+++++++++[<--------->-]<--.>+++++++[<" "++++++++++>-]<+++.+++++++++++.>++++++++[<----------->-]<++++" ".>+++++[<+++++++++++++>-]<.>+++[<++++++>-]<-.---.++++++.----" "---.----------.>++++++++[<----------->-]<+.---.[-]<<<->[-]>[" "-]<<[>+>+<<-]>>[<<+>>-]>>>[-]<<<+++++++++<[>>>+<<[>+>[-]<<-]" ">[<+>-]>[<<++++++++++>>>+<-]<<-<-]+++++++++>[<->-]>>+>[<[-]<" "<+>>>-]>[-]+<<[>+>-<<-]<<<[>>+>+<<<-]>>>[<<<+>>>-]<>>[<+>-]<" "<-[>[-]<[-]]>>+<[>[-]<-]<++++++++[<++++++<++++++>>-]>>>[>+>+" "<<-]>>[<<+>>-]<[<<<<<.>>>>>-]<<<<<<.>>[-]>[-]++++[<++++++++>" "-]<.>++++[<++++++++>-]<++.>+++++[<+++++++++>-]<.><+++++..---" "-----.-------.>>[>>+>+<<<-]>>>[<<<+>>>-]<[<<<<++++++++++++++" ".>>>>-]<<<<[-]>++++[<++++++++>-]<.>+++++++++[<+++++++++>-]<-" "-.---------.>+++++++[<---------->-]<.>++++++[<+++++++++++>-]" "<.+++..+++++++++++++.>++++++++[<---------->-]<--.>+++++++++[" "<+++++++++>-]<--.-.>++++++++[<---------->-]<++.>++++++++[<++" "++++++++>-]<++++.------------.---.>+++++++[<---------->-]<+." ">++++++++[<+++++++++++>-]<-.>++[<----------->-]<.+++++++++++" "..>+++++++++[<---------->-]<-----.---.+++.---.[-]<<<]" >|| - 出力
99 Bottles of beer on the wall
99 Bottles of beer
Take one down and pass it around
98 Bottles of beer on the wall
98 Bottles of beer on the wall
98 Bottles of beer
Take one down and pass it around
97 Bottles of beer on the wall
97 Bottles of beer on the wall
97 Bottles of beer
Take one down and pass it around
96 Bottles of beer on the wall
96 Bottles of beer on the wall
96 Bottles of beer
Take one down and pass it around
95 Bottles of beer on the wall
95 Bottles of beer on the wall
95 Bottles of beer
Take one down and pass it around
94 Bottles of beer on the wall
94 Bottles of beer on the wall
94 Bottles of beer
Take one down and pass it around
93 Bottles of beer on the wall
93 Bottles of beer on the wall
93 Bottles of beer
Take one down and pass it around
92 Bottles of beer on the wall
92 Bottles of beer on the wall
92 Bottles of beer
Take one down and pass it around
91 Bottles of beer on the wall
91 Bottles of beer on the wall
91 Bottles of beer
Take one down and pass it around
90 Bottles of beer on the wall
90 Bottles of beer on the wall
90 Bottles of beer
Take one down and pass it around
89 Bottles of beer on the wall
89 Bottles of beer on the wall
89 Bottles of beer
Take one down and pass it around
88 Bottles of beer on the wall
88 Bottles of beer on the wall
88 Bottles of beer
Take one down and pass it around
87 Bottles of beer on the wall
87 Bottles of beer on the wall
87 Bottles of beer
Take one down and pass it around
86 Bottles of beer on the wall
86 Bottles of beer on the wall
86 Bottles of beer
Take one down and pass it around
85 Bottles of beer on the wall
85 Bottles of beer on the wall
85 Bottles of beer
Take one down and pass it around
84 Bottles of beer on the wall
84 Bottles of beer on the wall
84 Bottles of beer
Take one down and pass it around
83 Bottles of beer on the wall
83 Bottles of beer on the wall
83 Bottles of beer
Take one down and pass it around
82 Bottles of beer on the wall
82 Bottles of beer on the wall
82 Bottles of beer
Take one down and pass it around
81 Bottles of beer on the wall
81 Bottles of beer on the wall
81 Bottles of beer
Take one down and pass it around
80 Bottles of beer on the wall
80 Bottles of beer on the wall
80 Bottles of beer
Take one down and pass it around
79 Bottles of beer on the wall
79 Bottles of beer on the wall
79 Bottles of beer
Take one down and pass it around
78 Bottles of beer on the wall
78 Bottles of beer on the wall
78 Bottles of beer
Take one down and pass it around
77 Bottles of beer on the wall
77 Bottles of beer on the wall
77 Bottles of beer
Take one down and pass it around
76 Bottles of beer on the wall
76 Bottles of beer on the wall
76 Bottles of beer
Take one down and pass it around
75 Bottles of beer on the wall
75 Bottles of beer on the wall
75 Bottles of beer
Take one down and pass it around
74 Bottles of beer on the wall
74 Bottles of beer on the wall
74 Bottles of beer
Take one down and pass it around
73 Bottles of beer on the wall
73 Bottles of beer on the wall
73 Bottles of beer
Take one down and pass it around
72 Bottles of beer on the wall
72 Bottles of beer on the wall
72 Bottles of beer
Take one down and pass it around
71 Bottles of beer on the wall
71 Bottles of beer on the wall
71 Bottles of beer
Take one down and pass it around
70 Bottles of beer on the wall
70 Bottles of beer on the wall
70 Bottles of beer
Take one down and pass it around
69 Bottles of beer on the wall
69 Bottles of beer on the wall
69 Bottles of beer
Take one down and pass it around
68 Bottles of beer on the wall
68 Bottles of beer on the wall
68 Bottles of beer
Take one down and pass it around
67 Bottles of beer on the wall
67 Bottles of beer on the wall
67 Bottles of beer
Take one down and pass it around
66 Bottles of beer on the wall
66 Bottles of beer on the wall
66 Bottles of beer
Take one down and pass it around
65 Bottles of beer on the wall
65 Bottles of beer on the wall
65 Bottles of beer
Take one down and pass it around
64 Bottles of beer on the wall
64 Bottles of beer on the wall
64 Bottles of beer
Take one down and pass it around
63 Bottles of beer on the wall
63 Bottles of beer on the wall
63 Bottles of beer
Take one down and pass it around
62 Bottles of beer on the wall
62 Bottles of beer on the wall
62 Bottles of beer
Take one down and pass it around
61 Bottles of beer on the wall
61 Bottles of beer on the wall
61 Bottles of beer
Take one down and pass it around
60 Bottles of beer on the wall
60 Bottles of beer on the wall
60 Bottles of beer
Take one down and pass it around
59 Bottles of beer on the wall
59 Bottles of beer on the wall
59 Bottles of beer
Take one down and pass it around
58 Bottles of beer on the wall
58 Bottles of beer on the wall
58 Bottles of beer
Take one down and pass it around
57 Bottles of beer on the wall
57 Bottles of beer on the wall
57 Bottles of beer
Take one down and pass it around
56 Bottles of beer on the wall
56 Bottles of beer on the wall
56 Bottles of beer
Take one down and pass it around
55 Bottles of beer on the wall
55 Bottles of beer on the wall
55 Bottles of beer
Take one down and pass it around
54 Bottles of beer on the wall
54 Bottles of beer on the wall
54 Bottles of beer
Take one down and pass it around
53 Bottles of beer on the wall
53 Bottles of beer on the wall
53 Bottles of beer
Take one down and pass it around
52 Bottles of beer on the wall
52 Bottles of beer on the wall
52 Bottles of beer
Take one down and pass it around
51 Bottles of beer on the wall
51 Bottles of beer on the wall
51 Bottles of beer
Take one down and pass it around
50 Bottles of beer on the wall
50 Bottles of beer on the wall
50 Bottles of beer
Take one down and pass it around
49 Bottles of beer on the wall
49 Bottles of beer on the wall
49 Bottles of beer
Take one down and pass it around
48 Bottles of beer on the wall
48 Bottles of beer on the wall
48 Bottles of beer
Take one down and pass it around
47 Bottles of beer on the wall
47 Bottles of beer on the wall
47 Bottles of beer
Take one down and pass it around
46 Bottles of beer on the wall
46 Bottles of beer on the wall
46 Bottles of beer
Take one down and pass it around
45 Bottles of beer on the wall
45 Bottles of beer on the wall
45 Bottles of beer
Take one down and pass it around
44 Bottles of beer on the wall
44 Bottles of beer on the wall
44 Bottles of beer
Take one down and pass it around
43 Bottles of beer on the wall
43 Bottles of beer on the wall
43 Bottles of beer
Take one down and pass it around
42 Bottles of beer on the wall
42 Bottles of beer on the wall
42 Bottles of beer
Take one down and pass it around
41 Bottles of beer on the wall
41 Bottles of beer on the wall
41 Bottles of beer
Take one down and pass it around
40 Bottles of beer on the wall
40 Bottles of beer on the wall
40 Bottles of beer
Take one down and pass it around
39 Bottles of beer on the wall
39 Bottles of beer on the wall
39 Bottles of beer
Take one down and pass it around
38 Bottles of beer on the wall
38 Bottles of beer on the wall
38 Bottles of beer
Take one down and pass it around
37 Bottles of beer on the wall
37 Bottles of beer on the wall
37 Bottles of beer
Take one down and pass it around
36 Bottles of beer on the wall
36 Bottles of beer on the wall
36 Bottles of beer
Take one down and pass it around
35 Bottles of beer on the wall
35 Bottles of beer on the wall
35 Bottles of beer
Take one down and pass it around
34 Bottles of beer on the wall
34 Bottles of beer on the wall
34 Bottles of beer
Take one down and pass it around
33 Bottles of beer on the wall
33 Bottles of beer on the wall
33 Bottles of beer
Take one down and pass it around
32 Bottles of beer on the wall
32 Bottles of beer on the wall
32 Bottles of beer
Take one down and pass it around
31 Bottles of beer on the wall
31 Bottles of beer on the wall
31 Bottles of beer
Take one down and pass it around
30 Bottles of beer on the wall
30 Bottles of beer on the wall
30 Bottles of beer
Take one down and pass it around
29 Bottles of beer on the wall
29 Bottles of beer on the wall
29 Bottles of beer
Take one down and pass it around
28 Bottles of beer on the wall
28 Bottles of beer on the wall
28 Bottles of beer
Take one down and pass it around
27 Bottles of beer on the wall
27 Bottles of beer on the wall
27 Bottles of beer
Take one down and pass it around
26 Bottles of beer on the wall
26 Bottles of beer on the wall
26 Bottles of beer
Take one down and pass it around
25 Bottles of beer on the wall
25 Bottles of beer on the wall
25 Bottles of beer
Take one down and pass it around
24 Bottles of beer on the wall
24 Bottles of beer on the wall
24 Bottles of beer
Take one down and pass it around
23 Bottles of beer on the wall
23 Bottles of beer on the wall
23 Bottles of beer
Take one down and pass it around
22 Bottles of beer on the wall
22 Bottles of beer on the wall
22 Bottles of beer
Take one down and pass it around
21 Bottles of beer on the wall
21 Bottles of beer on the wall
21 Bottles of beer
Take one down and pass it around
20 Bottles of beer on the wall
20 Bottles of beer on the wall
20 Bottles of beer
Take one down and pass it around
19 Bottles of beer on the wall
19 Bottles of beer on the wall
19 Bottles of beer
Take one down and pass it around
18 Bottles of beer on the wall
18 Bottles of beer on the wall
18 Bottles of beer
Take one down and pass it around
17 Bottles of beer on the wall
17 Bottles of beer on the wall
17 Bottles of beer
Take one down and pass it around
16 Bottles of beer on the wall
16 Bottles of beer on the wall
16 Bottles of beer
Take one down and pass it around
15 Bottles of beer on the wall
15 Bottles of beer on the wall
15 Bottles of beer
Take one down and pass it around
14 Bottles of beer on the wall
14 Bottles of beer on the wall
14 Bottles of beer
Take one down and pass it around
13 Bottles of beer on the wall
13 Bottles of beer on the wall
13 Bottles of beer
Take one down and pass it around
12 Bottles of beer on the wall
12 Bottles of beer on the wall
12 Bottles of beer
Take one down and pass it around
11 Bottles of beer on the wall
11 Bottles of beer on the wall
11 Bottles of beer
Take one down and pass it around
10 Bottles of beer on the wall
10 Bottles of beer on the wall
10 Bottles of beer
Take one down and pass it around
9 Bottles of beer on the wall
9 Bottles of beer on the wall
9 Bottles of beer
Take one down and pass it around
8 Bottles of beer on the wall
8 Bottles of beer on the wall
8 Bottles of beer
Take one down and pass it around
7 Bottles of beer on the wall
7 Bottles of beer on the wall
7 Bottles of beer
Take one down and pass it around
6 Bottles of beer on the wall
6 Bottles of beer on the wall
6 Bottles of beer
Take one down and pass it around
5 Bottles of beer on the wall
5 Bottles of beer on the wall
5 Bottles of beer
Take one down and pass it around
4 Bottles of beer on the wall
4 Bottles of beer on the wall
4 Bottles of beer
Take one down and pass it around
3 Bottles of beer on the wall
3 Bottles of beer on the wall
3 Bottles of beer
Take one down and pass it around
2 Bottles of beer on the wall
2 Bottles of beer on the wall
2 Bottles of beer
Take one down and pass it around
1 Bottle of beer on the wall
1 Bottle of beer on the wall
1 Bottle of beer
Take one down and pass it around
0 Bottles of beer on the wall
|
// Self-describing "+++++[>+++++++++<-],[[>--.++>+<<-]>+.->[<.>-]<<,]"
- 入力
+++++[>+++++++++<-],[[>--.++>+<<-]>+.->[<.>-]<<,]
- 出力
+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++.----------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++.----------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++.----------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++++++.--------------------------------------------+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------------------------------------++++++++++.----------
素晴らしい。