/** * Author: F. Philipp * * Translation of * https://benchmarksgame.alioth.debian.org/u64q/program.php?test=mandelbrot&lang=rust&id=4 * with minor changes * * Contains a bug resulting in slightly different results */ #include // using std::array #include // using std::vector #include // using std::tuple #include // using std::stoi #include // using std::cout #include // using std::transform, std::all_of #include // using std::bind, std::placeholders, std::multiplies, std::plus, std::minus #include // using CHAR_BIT namespace { constexpr int MAX_ITER = 50; constexpr int VLEN = CHAR_BIT; typedef double double_t; struct Vecf64 { using array_type = std::array; array_type array; Vecf64() = default; explicit Vecf64(double_t s) noexcept { array.fill(s); } Vecf64& operator=(const Vecf64&) = default; Vecf64& operator=(double_t s) noexcept { array.fill(s); return *this; } double_t& operator[](int idx) noexcept { return array[idx]; } double_t operator[](int idx) const noexcept { return array[idx]; } template Vecf64& inplace_transform(const Vecf64& o, BinaryFunctor f) { array_type::iterator first = array.begin(); std::transform(first, array.end(), o.array.begin(), first, f); return *this; } template Vecf64 transform(const Vecf64& o, BinaryFunctor f) const { Vecf64 rtrn; std::transform(array.begin(), array.end(), o.array.begin(), rtrn.array.begin(), f); return rtrn; } template Vecf64& inplace_transform(UnaryFunctor f) { array_type::iterator first = array.begin(); std::transform(first, array.end(), first, f); return *this; } template Vecf64 transform(UnaryFunctor f) const { Vecf64 rtrn; std::transform(array.begin(), array.end(), rtrn.array.begin(), f); return rtrn; } Vecf64& operator*=(const Vecf64& o) noexcept { return inplace_transform(o, std::multiplies{}); } Vecf64 operator*(const Vecf64& o) const noexcept { return transform(o, std::multiplies{}); } Vecf64& operator*=(double_t s) noexcept { return inplace_transform(std::bind(std::multiplies{}, std::placeholders::_1, s)); } Vecf64 operator*(double_t s) const noexcept { return transform(std::bind(std::multiplies{}, std::placeholders::_1, s)); } friend Vecf64 operator*(double_t left, const Vecf64& right) noexcept { return right * left; } Vecf64& operator+=(const Vecf64& o) noexcept { return inplace_transform(o, std::plus{}); } Vecf64 operator+(const Vecf64& o) const noexcept { return transform(o, std::plus{}); } Vecf64& operator+=(double_t s) noexcept { return inplace_transform(std::bind(std::plus{}, std::placeholders::_1, s)); } Vecf64 operator+(double_t s) const noexcept { return transform(std::bind(std::plus{}, std::placeholders::_1, s)); } friend Vecf64 operator+(double_t left, const Vecf64& right) noexcept { return right + left; } Vecf64& operator-=(const Vecf64& o) noexcept { return inplace_transform(o, std::minus{}); } Vecf64 operator-(const Vecf64& o) const noexcept { return transform(o, std::minus{}); } Vecf64& operator-=(double_t s) noexcept { return inplace_transform(std::bind(std::minus{}, std::placeholders::_1, s)); } Vecf64 operator-(double_t s) const noexcept { return transform(std::bind(std::minus{}, std::placeholders::_1, s)); } friend Vecf64 operator-(double_t left, const Vecf64& right) noexcept { return right.transform(std::bind(std::minus{}, left, std::placeholders::_1)); } Vecf64& inplace_square() noexcept { return inplace_transform(std::bind(std::multiplies{}, std::placeholders::_1, std::placeholders::_1)); } Vecf64 square() const noexcept { return transform(std::bind(std::multiplies{}, std::placeholders::_1, std::placeholders::_1)); } template bool all(Predicate p) const { return std::all_of(array.begin(), array.end(), p); } }; struct CrCr2Chunk { Vecf64 cr, cr2; }; class Mandelbrot8 { Vecf64 zr, zi, tr, ti, cr; double_t ci, ci2; void advance(int iterations) noexcept { for(int i = 0; i < iterations; ++i) { zi = 2. * zr * zi + ci; zr = tr - ti + cr; tr = zr.square(); ti = zi.square(); } } bool all_diverged() const noexcept { auto diverged = std::bind(std::greater{}, std::placeholders::_1, 4.); return (tr + ti).all(diverged); } unsigned char to_byte() const noexcept { const Vecf64 trti = tr + ti; unsigned char accu = 0; for(int i = 0; i < VLEN; ++i) { if(trti[i] <= 4.) accu |= 0x80 >> i; } return accu; } public: explicit Mandelbrot8(double_t ci) noexcept : ci(ci), ci2(ci * ci) {} unsigned char operator()(const CrCr2Chunk& col_invariant) noexcept { zr = col_invariant.cr; zi = ci; tr = col_invariant.cr2; ti = ci2; cr = col_invariant.cr; advance(4); constexpr int n = MAX_ITER / 5 - 1; for(int i = 0; i < n; ++i) { if(all_diverged()) return 0; advance(4); } return to_byte(); } }; std::vector make_column_invariant(int bytes_per_line) { const int pixels_per_line = bytes_per_line * VLEN; const double_t inv = 2. / pixels_per_line; std::vector rtrn(bytes_per_line); # pragma omp parallel for for(int col_byte = 0; col_byte < bytes_per_line; ++col_byte) { CrCr2Chunk& chunk = rtrn[col_byte]; for(int bit = 0; bit < VLEN; ++bit) { const int col = col_byte * VLEN + bit; chunk.cr[bit] = col * inv - 1.5; } chunk.cr2 = chunk.cr.square(); } return rtrn; } std::vector make_bitmap(const std::vector& column_invariants) { const int bytes_per_line = static_cast(column_invariants.size()); const int pixels_per_line = bytes_per_line * VLEN; const double_t inv = 2. / pixels_per_line; std::vector bitmap(pixels_per_line * bytes_per_line); # pragma omp parallel for for(int row = 0; row < pixels_per_line; ++row) { const int start = row * bytes_per_line; const double_t ci = row * inv - 1.; std::transform(column_invariants.begin(), column_invariants.end(), bitmap.begin() + start, Mandelbrot8(ci)); } return bitmap; } void print_pbm(const std::vector& bitmap, int pixels_per_line, std::ostream& binary_output) { binary_output << "P4\n" << pixels_per_line << ' ' << pixels_per_line << '\n'; binary_output.write(reinterpret_cast(bitmap.data()), bitmap.size()); binary_output.flush(); } } int main(int argc, char** argv) { /* number of pixels in each dimension */ int pixels_per_line = 200; if(argc > 1) pixels_per_line = std::stoi(argv[1]); const int bytes_per_line = pixels_per_line / VLEN; pixels_per_line = bytes_per_line * VLEN; const std::vector column_invariants = make_column_invariant(bytes_per_line); const std::vector bitmap = make_bitmap(column_invariants); print_pbm(bitmap, pixels_per_line, std::cout); }