Skip to content

Commit

Permalink
refactor: Add fibonacci sequence functions
Browse files Browse the repository at this point in the history
  • Loading branch information
oboukli committed Sep 14, 2023
1 parent 652a3f9 commit 45c1821
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 44 deletions.
65 changes: 27 additions & 38 deletions benchmark/fibonacci_sequence_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,79 +4,68 @@

// SPDX-License-Identifier: MIT

#include <concepts>
#include <cstdint>

#include <catch2/catch_test_macros.hpp>

#include <nanobench.h>

#include <nameof.hpp>

#include "forfun/fibonacci_sequence.hpp"

inline constexpr int const f{514229};
inline constexpr std::uint_fast32_t const uf{f};

static_assert(uf == f);
template <typename T, typename TState>
requires std::integral<T> && (sizeof(T) < sizeof(TState))
void dummy_callback(T const n, TState* const state) noexcept
{
*state += n;
}

TEST_CASE(
"fibonacci sequence loops benchmarking", "[benchmark][fibonacci_sequence]")
TEST_CASE("fibonacci::sequence benchmarking", "[benchmark][fibonacci_sequence]")
{
using namespace forfun::fibonacci::sequence;

using result_t = std::size_t;

ankerl::nanobench::Bench()
.title("Fibonacci sequence loops")
.title("forfun::fibonacci::sequence")

.run(
"Three variables",
NAMEOF_RAW(slow::fib_seq<int, result_t>).c_str(),
[]() {
result_t r{};
for (auto i{0}, j{1}, tmp{0}; i <= f;)
{
r += i;

tmp = j + i;
i = j;
j = tmp;
}
forfun::fibonacci::sequence::slow::fib_seq(
f, dummy_callback, &r);
ankerl::nanobench::doNotOptimizeAway(r);
})

.run(
"Two variables",
NAMEOF_RAW(fast::fib_seq<int, result_t>).c_str(),
[]() {
result_t r{};
for (auto i{0}, j{1}; i <= f;)
{
r += i;

j += i;
i = j - i;
}
forfun::fibonacci::sequence::fast::fib_seq(
f, dummy_callback, &r);
ankerl::nanobench::doNotOptimizeAway(r);
})

.run(
"Two unsigned variables",
NAMEOF_RAW(fast::fib_seq<std::uint_fast32_t, result_t>).c_str(),
[]() {
result_t r{};
for (std::uint_fast32_t i{0}, j{1}; i <= uf;)
{
r += i;

j += i;
i = j - i;
}
forfun::fibonacci::sequence::fast::fib_seq(
std::uint_fast32_t{f}, dummy_callback, &r);
ankerl::nanobench::doNotOptimizeAway(r);
})

.run(
"Two variables by Creel",
NAMEOF_RAW(creel::fib_seq<int, result_t>).c_str(),
[]() {
result_t r{};
// Adapted from: https://youtu.be/IZc4Odd3K2Q?t=949
for (auto i{0}, j{1}; i <= f;)
{
r += i;

j = (i += j) - j;
}
forfun::fibonacci::sequence::creel::fib_seq(
f, dummy_callback, &r);
ankerl::nanobench::doNotOptimizeAway(r);
})

Expand Down
61 changes: 60 additions & 1 deletion include/forfun/fibonacci_sequence.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,68 @@
#ifndef FORFUN_FIBONACCI_SEQUENCE_HPP_
#define FORFUN_FIBONACCI_SEQUENCE_HPP_

#include <concepts>

namespace forfun::fibonacci::sequence {

// See benchmark/fibonacci_benchmark.cpp
template <typename T, typename TState>
requires std::integral<T>
using callback_t = void(*)(T const, TState* const state) noexcept;

namespace slow {

template <typename T, typename TState>
requires std::integral<T>
void fib_seq(
T const max, callback_t<T, TState> const cb, TState* const state) noexcept
{
for (auto i{0}, j{1}, tmp{0}; i <= max;)
{
cb(i, state);

tmp = j + i;
i = j;
j = tmp;
}
}

} // namespace slow

namespace fast {

template <typename T, typename TState>
requires std::integral<T>
void fib_seq(
T const max, callback_t<T, TState> const cb, TState* const state) noexcept
{
for (auto i{0}, j{1}; i <= max;)
{
cb(i, state);

j += i;
i = j - i;
}
}

} // namespace fast

namespace creel {

template <typename T, typename TState>
requires std::integral<T>
void fib_seq(
T const max, callback_t<T, TState> const cb, TState* const state) noexcept
{
// Adapted from: https://youtu.be/IZc4Odd3K2Q?t=949
for (auto i{0}, j{1}; i <= max;)
{
cb(i, state);

j = (i += j) - j;
}
}

} // namespace creel

} // namespace forfun::fibonacci::sequence

Expand Down
2 changes: 0 additions & 2 deletions src/fibonacci_sequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,3 @@
// SPDX-License-Identifier: MIT

#include "forfun/fibonacci_sequence.hpp"

// See benchmark/fibonacci_sequence_benchmark.cpp
70 changes: 67 additions & 3 deletions test/fibonacci_sequence_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,78 @@

// SPDX-License-Identifier: MIT

#include <vector>

#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>

#include "forfun/fibonacci_sequence.hpp"

TEST_CASE("fibonacci_sequence")
void dummy_callback(int const n, std::vector<int>* const seq) noexcept
{
seq->push_back(n);
}

TEMPLATE_TEST_CASE_SIG(
"fibonacci_sequence",
"[fibonacci_sequence]",
((auto sut), sut),
(forfun::fibonacci::sequence::slow::fib_seq<int, std::vector<int>>),
(forfun::fibonacci::sequence::fast::fib_seq<int, std::vector<int>>),
(forfun::fibonacci::sequence::creel::fib_seq<int, std::vector<int>>))
{
SECTION("Loops")
SECTION("Valid input")
{
SKIP("See benchmark/fibonacci_sequence_benchmark.cpp");
GIVEN("a positive integer (incl. zero) as an upper bound")
{
auto const [max, expected_seq]{
GENERATE(table<int, std::vector<int>>({
{0, {0}},
{1, {0, 1, 1}},
{13, {0, 1, 1, 2, 3, 5, 8, 13}},
{239, {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233}},
}))};

CAPTURE(max);

WHEN("generating a Fibonacci sequence from zero")
{
std::vector<int> seq;
seq.reserve(expected_seq.size());

sut(max, dummy_callback, &seq);

THEN("sequence is valid")
{
REQUIRE(seq == expected_seq);
}
}
}
}

SECTION("Negative input")
{
GIVEN("a negative integer as an upper bound")
{
auto const [max]{GENERATE(table<int>({
{-13},
{-1},
}))};

CAPTURE(max);

WHEN("generating a Fibonacci sequence from zero")
{
std::vector<int> seq;

sut(max, dummy_callback, &seq);

THEN("sequence is empty")
{
REQUIRE(seq == std::vector<int>{});
}
}
}
}
}

0 comments on commit 45c1821

Please sign in to comment.