Skip to content

Instantly share code, notes, and snippets.

@arrieta
Created November 1, 2017 22:59
Show Gist options
  • Save arrieta/1d2f4bf34de2ca89626209aba2587723 to your computer and use it in GitHub Desktop.
Save arrieta/1d2f4bf34de2ca89626209aba2587723 to your computer and use it in GitHub Desktop.
C++ implementation of linspace.
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*-
/// @file example-linspace.cpp
/// @brief Sample use of Nabla Zero Labs' linspace utilities.
/// @author J. Arrieta <Juan.Arrieta@nablazerolabs.com>
/// @date November 01, 2017
/// @copyright (c) 2017 Nabla Zero Labs
/// @license MIT License
// C++ Standard Library
#include <iomanip>
#include <iostream>
#include <list>
#include <vector>
// Nabla Zero Labs Library
#include "linspace.hpp"
template <typename T>
void run_example() {
auto p = std::numeric_limits<T>::max_digits10;
auto w = p + 7;
std::cout << std::setprecision(p) << std::scientific;
// low-level iterator interface
auto it = nzl::linspace_iterator<T>(-10, 10, 5);
auto it_end = nzl::linspace_iterator<T>{}; // sentinel
std::cout << "linspace_iterator\n";
for (auto count = 0; it != it_end; ++it) {
std::cout << " linspace[" << count++ << "] = " << std::setw(w) << *it
<< "\n";
}
// range-based for (also ilustrating decreasing values)
std::cout << "range-based\n";
for (auto&& value : nzl::linspace<T>(10, -5, 6)) {
std::cout << " " << std::setw(w) << value << "\n";
}
// fill exising containers
std::vector<T> v;
v.resize(7);
nzl::fill_linspace(v, -8, 3);
std::cout << "fill vector\n";
for (auto&& vi : v) std::cout << " " << std::setw(w) << vi << "\n";
// only the first five elements
std::list<T> l;
l.resize(10);
nzl::fill_linspace_n(l.begin(), -1, 3, 5);
std::cout << "fill elements [0, 5) in list\n";
for (auto&& li : l) std::cout << " " << std::setw(w) << li << "\n";
// put a linspace in a new container
auto x = nzl::as_linspace<std::vector<T>>(-3, 8, 10);
std::cout << "create container\n";
for (auto&& xi : x) std::cout << " " << std::setw(w) << xi << "\n";
}
int main() {
std::cout << "float\n";
run_example<float>();
std::cout << "double\n";
run_example<double>();
std::cout << "long double\n";
run_example<long double>();
}
// -*- coding:utf-8; mode:c++; mode:auto-fill; fill-column:80; -*-
/// @file nzl/linspace.hpp
/// @brief Generate an linear space of floating point numbers.
/// @author J. Arrieta <Juan.Arrieta@nablazerolabs.com>
/// @date November 01, 2017
/// @copyright (c) 2017 Nabla Zero Labs
/// @license MIT License
#pragma once
#ifndef NZL_LINSPACE_HPP_HEADER_GUARD
#define NZL_LINSPACE_HPP_HEADER_GUARD
// C++ Standard Library
#include <cstddef>
#include <iterator>
#include <limits>
namespace nzl {
/// @brief Iterator producing elements of a linear space.
template <typename T> // T is any floating point type
class linspace_iterator {
public:
using iterator_category = std::input_iterator_tag;
using value_type = T;
using reference = T&;
using pointer = T*;
using difference_type = std::size_t;
static_assert(
std::is_floating_point<T>::value,
"nzl::linspace_iterator is only defined for floating point types");
explicit linspace_iterator() noexcept(true)
: m_remaining{std::numeric_limits<std::size_t>::max()} {}
linspace_iterator(T first, T last, std::size_t n) noexcept(true)
: m_remaining{n - 1},
m_current{std::move(first)},
m_step{(last - m_current) / m_remaining},
m_final{std::move(last)} {}
linspace_iterator<T>& operator++() {
this->next();
return *this;
}
linspace_iterator<T> operator++(int) {
auto tmp = *this;
this->next();
return tmp;
}
const value_type& operator*() const { return m_current; }
const value_type* operator->() const { return std::addressof(*this); }
bool operator!=(const linspace_iterator<T>& other) {
return not(*this == other);
}
bool operator==(const linspace_iterator<T>& other) {
return ((this->is_invalid() and other.is_invalid()) or
std::memcmp(this, &other, sizeof(other)) == 0);
}
std::size_t size() const noexcept(true) { return m_remaining + 1; }
private:
bool is_invalid() const {
return m_remaining == std::numeric_limits<std::size_t>::max();
}
void next() {
switch (m_remaining) {
case 1:
m_current = m_final;
--m_remaining;
break;
case 0:
m_remaining = std::numeric_limits<std::size_t>::max();
break;
case std::numeric_limits<std::size_t>::max():
break;
default:
m_current += m_step;
--m_remaining;
break;
}
}
std::size_t m_remaining;
T m_current;
T m_step;
T m_final;
};
/// @brief Range containing a linear space of floating point numbers.
template <typename T>
class linspace {
public:
linspace(T first, T last, std::size_t n = 100) noexcept(true)
: m_it{first, last, n}, m_it_end{} {}
linspace_iterator<T> begin() const noexcept(true) { return m_it; }
linspace_iterator<T> end() const noexcept(true) { return m_it_end; }
std::size_t size() const noexcept(true) { return m_it.size(); }
private:
linspace_iterator<T> m_it;
linspace_iterator<T> m_it_end;
};
/// @brief Fill a range with a linear space.
template <typename I> // I models InputIterator
inline void fill_linspace_n(I begin, typename I::value_type first,
typename I::value_type last, std::size_t n) {
std::copy(linspace_iterator<typename I::value_type>(first, last, n),
linspace_iterator<typename I::value_type>(), begin);
}
/// @brief Fill a range with a linear space.
template <typename I> // I models InputIterator
inline void fill_linspace(I begin, I end, typename I::value_type first,
typename I::value_type last) {
return fill_linspace_n(begin, first, last, std::distance(begin, end));
}
/// @brief Fill an existing sequence with a linear space.
template <typename S> // S models SequenceContainer
inline S& fill_linspace(S& sequence_container, typename S::value_type first,
typename S::value_type last) {
fill_linspace_n(sequence_container.begin(), first, last,
sequence_container.size());
return sequence_container;
}
/// @brief Create a sequence filled with a linear space.
template <typename S> // S models SequenceContainer
inline S as_linspace(typename S::value_type first, typename S::value_type last,
std::size_t n) {
return S(linspace_iterator<typename S::value_type>(first, last, n),
linspace_iterator<typename S::value_type>());
}
} // namespace nzl
#endif // NZL_LINSPACE_HPP_HEADER_GUARD
$ clang++ example-linspace.cpp -std=c++17 -Wall -Wextra -Ofast -march=native
$ ./a.out
float
linspace_iterator
linspace[0] = -1.000000000e+01
linspace[1] = -5.000000000e+00
linspace[2] = 0.000000000e+00
linspace[3] = 5.000000000e+00
linspace[4] = 1.000000000e+01
range-based
1.000000000e+01
7.000000000e+00
4.000000000e+00
1.000000000e+00
-2.000000000e+00
-5.000000000e+00
fill vector
-8.000000000e+00
-6.166666508e+00
-4.333333015e+00
-2.499999523e+00
-6.666661501e-01
1.166667223e+00
3.000000000e+00
fill elements [0, 5) in list
-1.000000000e+00
0.000000000e+00
1.000000000e+00
2.000000000e+00
3.000000000e+00
0.000000000e+00
0.000000000e+00
0.000000000e+00
0.000000000e+00
0.000000000e+00
create container
-3.000000000e+00
-1.777777791e+00
-5.555555820e-01
6.666666269e-01
1.888888836e+00
3.111111164e+00
4.333333492e+00
5.555555820e+00
6.777778149e+00
8.000000000e+00
double
linspace_iterator
linspace[0] = -1.00000000000000000e+01
linspace[1] = -5.00000000000000000e+00
linspace[2] = 0.00000000000000000e+00
linspace[3] = 5.00000000000000000e+00
linspace[4] = 1.00000000000000000e+01
range-based
1.00000000000000000e+01
7.00000000000000000e+00
4.00000000000000000e+00
1.00000000000000000e+00
-2.00000000000000000e+00
-5.00000000000000000e+00
fill vector
-8.00000000000000000e+00
-6.16666666666666696e+00
-4.33333333333333393e+00
-2.50000000000000089e+00
-6.66666666666667629e-01
1.16666666666666563e+00
3.00000000000000000e+00
fill elements [0, 5) in list
-1.00000000000000000e+00
0.00000000000000000e+00
1.00000000000000000e+00
2.00000000000000000e+00
3.00000000000000000e+00
0.00000000000000000e+00
0.00000000000000000e+00
0.00000000000000000e+00
0.00000000000000000e+00
0.00000000000000000e+00
create container
-3.00000000000000000e+00
-1.77777777777777768e+00
-5.55555555555555358e-01
6.66666666666666963e-01
1.88888888888888928e+00
3.11111111111111160e+00
4.33333333333333393e+00
5.55555555555555625e+00
6.77777777777777857e+00
8.00000000000000000e+00
long double
linspace_iterator
linspace[0] = -1.000000000000000000000e+01
linspace[1] = -5.000000000000000000000e+00
linspace[2] = 0.000000000000000000000e+00
linspace[3] = 5.000000000000000000000e+00
linspace[4] = 1.000000000000000000000e+01
range-based
1.000000000000000000000e+01
7.000000000000000000000e+00
4.000000000000000000000e+00
1.000000000000000000000e+00
-2.000000000000000000000e+00
-5.000000000000000000000e+00
fill vector
-8.000000000000000000000e+00
-6.166666666666666666522e+00
-4.333333333333333333044e+00
-2.499999999999999999566e+00
-6.666666666666666661968e-01
1.166666666666666667173e+00
3.000000000000000000000e+00
fill elements [0, 5) in list
-1.000000000000000000000e+00
0.000000000000000000000e+00
1.000000000000000000000e+00
2.000000000000000000000e+00
3.000000000000000000000e+00
0.000000000000000000000e+00
0.000000000000000000000e+00
0.000000000000000000000e+00
0.000000000000000000000e+00
0.000000000000000000000e+00
create container
-3.000000000000000000000e+00
-1.777777777777777777754e+00
-5.555555555555555555074e-01
6.666666666666666667389e-01
1.888888888888888888985e+00
3.111111111111111111232e+00
4.333333333333333333478e+00
5.555555555555555555941e+00
6.777777777777777777971e+00
8.000000000000000000000e+00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment