Skip to content

Instantly share code, notes, and snippets.

@willkill07
Last active February 28, 2019 21:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save willkill07/3c77df53f43fbed4c796585c1cf99e5d to your computer and use it in GitHub Desktop.
Save willkill07/3c77df53f43fbed4c796585c1cf99e5d to your computer and use it in GitHub Desktop.
// Expressive Pi
// -------------
//
// Fluent C++ Blog Post: The Pi Day Challenge for Expressive Code
// http://www.fluentcpp.com/2017/03/02/the-pi-day-challenge-for-expressive-code-in-c/
//
// Author: William Killian
// Email: william.killian@gmail.com
// Github: http://www.github.com/willkill07
// Website: https://www.eecis.udel.edu/~wkillian
//
// ================================================================================
//
// Copyright 2017 William Killian
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// Notes:
// - requires a C++1z compiler with support for WG21 P0184 (http://wg21.link/P0184)
// - This was my first time using Range-V3 (https://github.com/ericniebler/range-v3)
// - I thought the most expressive way to solve this problem was to:
// 1. Eliminate functions from doing more than one thing
// 2. Eliminate confusing chains of expressions as a single line of code
// * In general, breaking up expressions is perfectly valid, and the compiler
// often sees no difference (and sometimes it even helps!)
// 3. Give sensible variable names to avoid any potential confusion
// 4. Use const wherever data does not need mutated
#include <array>
#include <random>
#include <cmath>
#include <cstdio>
#include <range/v3/all.hpp>
using RealType = double;
using RandomNumberGenerator = std::mt19937;
using Distribution = std::uniform_real_distribution<RealType>;
using DistributionParams = typename Distribution::param_type;
using Point = std::array<RealType, 2>;
auto generatePoint(RealType radius) {
static RandomNumberGenerator rng(std::random_device{}());
static Distribution dist;
dist.param(DistributionParams{-radius, std::nexttoward(radius, 2 * radius)});
return [radius] {
return Point{{dist(rng), dist(rng)}};
};
}
auto checkWithinCircle(RealType radius) {
return [radius] (const Point &p) noexcept -> bool {
return std::hypot(std::get<0>(p), std::get<1>(p)) <= radius;
};
}
int pow10(int raised) {
return std::pow(10, raised);
}
int main() {
const auto RADIUS_SIZES = ranges::view::ints(0, 10) | ranges::view::transform(pow10);
const auto POINTS_COUNT = ranges::view::ints(0, 8) | ranges::view::transform(pow10);
for (int radius : RADIUS_SIZES) {
for (int points : POINTS_COUNT) {
auto GENERATED_POINTS = ranges::view::generate_n(generatePoint(radius), points);
const int POINTS_IN_CIRCLE = ranges::count_if(GENERATED_POINTS, checkWithinCircle(radius));
const RealType MY_PI = 4.0 * static_cast<RealType>(POINTS_IN_CIRCLE) / points;
const RealType PI_ERROR = std::abs(MY_PI - M_PI);
printf(" %0.6lf", PI_ERROR);
}
putchar('\n');
}
return EXIT_SUCCESS;
}
CXX := /usr/local/opt/llvm/bin/clang++
CXXFLAGS := -O3 -march=native -std=c++1z -Wall -Wextra -pedantic-errors
CPPFLAGS := -I/opt/range-v3/include
.PHONY: all
all : ExpressivePi
@willkill07
Copy link
Author

willkill07 commented Mar 14, 2017

I made a small update since the PiDay contest.

The uniform_real_distribution is now static and I set the params each time I call generate. This removes a lot of boilerplate code generation during each loop iteration. Runtime went from ~5.2s on my laptop down to 3.7s

I also attempted to make the power-of-ten generation easier to understand (view::ints(0,upper) | view::transform(pow10)) instead of creating an infinite range and taking the first 8 (or 10).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment