Find dirac in wave file - c++

This is about room correction.
I have a reference wave file, which is played back via a stereo amplifier and recorded using a microphone (umik 1).
Now my problem is that I have to manually find the diracs (loud „clicks“) inserted into the reference audio, and manually calculate the clock drift of the microphone. For example, in the reference file the click is at 0.5s and another one at 62s.
In the file recorded the clicks are slightly off. I‘m currently checking the wave file and calculate the actual clock / sample rate from the actual recorded distance between the two clicks.
How can I recognize this click in code, and get required info to do the calculations?

You can:
go forward in the audio signal and find the first sample whose absolute value is above the noise_level threshold
then, walk back from that location to find the first zero-crossing.
find_relevant_start implements this, and you can use it like so:
// You may need to adjust `noise_level` based on the characteristics of your audio.
constexpr auto noise_level = 0.1f;
std::vector<float> audio;
auto it = find_relevant_start(audio.begin(), audio.end(), noise_level);
if(it==audio.end()) {
// not found, maybe 'noise_level' was too high?
}
This is the code (from a personal project):
/*
* #param abs_relevant_level : if the absolute value of the signal
* is bigger than this value, it is considered to be relevant.
* Use a non-zero value if your signal contains noise.
*/
template<typename ITER, typename VAL = typename ITER::value_type>
ITER first_relevant_value(ITER it, ITER end, VAL abs_relevant_level) {
for(; it != end; ++ it ) {
if(std::abs(*it) > abs_relevant_level) {
break;
}
}
return it;
}
template<typename ITER>
ITER first_zero_crossing(ITER it, ITER end) {
using VAL = typename ITER::value_type;
using Tr = NumTraits<VAL>;
bool first = true;
VAL prev;
while(it != end) {
auto cur = *it;
if(cur == Tr::zero()) {
break;
}
if(first) {
first = false;
}
else if(prev * cur < Tr::zero()) {
break;
}
prev = cur;
++it;
}
return it;
}
template<typename ITER, typename VAL = typename ITER::value_type>
ITER find_relevant_start(ITER it, ITER end, VAL abs_relevant_level) {
auto it_relevant_value = first_relevant_value(it, end, abs_relevant_level);
if(it_relevant_value == end) {
return end;
}
using REVERSE_ITER = std::reverse_iterator<ITER>;
auto rit = REVERSE_ITER(it_relevant_value + 1);
auto rend = REVERSE_ITER(it);
auto rzero = first_zero_crossing( rit, rend);
// first_zero_crossing returns the iterator after the zero crossing (in the reverse direction)
// so rzero.base() is the iterator on the other side of the zero crossing
return rzero.base();
}
These unit tests will show you how it performs on simple arrays:
TEST(Peaks, find_relevant_start)
{
using namespace imajuscule;
{
std::vector<float> v{ -0.04f, -0.03f, -0.02f, -0.01f, 0.1f, 0.2f, 0.3f };
auto it = find_relevant_start(v.begin(), v.end(), 0.15f);
ASSERT_EQ(0.1f, *it);
}
{
std::vector<float> v{ -0.04f, -0.03f, -0.02f, -0.01f, 0.1f, 0.2f, 0.3f };
auto it = find_relevant_start(v.begin(), v.end(), 0.25f);
// the algorithm finds the first relevant value, and goes backward from there to find the first sign change and returns the sample just after
ASSERT_EQ(0.1f, *it);
}
{
std::vector<float> v{ 0.04f, 0.03f, 0.02f, 0.01f, -0.1f, -0.2f, -0.3f };
auto it = find_relevant_start(v.begin(), v.end(), 0.25f);
// the algorithm finds the first relevant value, and goes backward from there to find the first sign change and returns the sample just after
ASSERT_EQ(-0.1f, *it);
}
{
std::vector<float> v{ -0.04f, -0.03f, -0.02f, -0.01f, 0.1f, 0.2f, 0.3f };
auto it = find_relevant_start(v.begin(), v.end(), 0.5f);
// the level is too high and was never reached so "end" should be returned
ASSERT_EQ(v.end(), it);
}
{
std::vector<float> v{ 1.f, 2.f, 1.f };
auto it = find_relevant_start(v.begin(), v.end(), 0.5f);
ASSERT_EQ(v.begin(), it);
}
{
std::vector<float> v{ -1.f, -2.f, -1.f };
auto it = find_relevant_start(v.begin(), v.end(), 0.5f);
ASSERT_EQ(v.begin(), it);
}
{
std::vector<float> v;
auto it = find_relevant_start(v.begin(), v.end(), 0.5f);
ASSERT_EQ(v.end(), it);
}
{
std::vector<float> v{.1f};
auto it = find_relevant_start(v.begin(), v.end(), 0.5f);
ASSERT_EQ(v.end(), it);
}
{
std::vector<float> v{1.f};
auto it = find_relevant_start(v.begin(), v.end(), 0.5f);
ASSERT_EQ(1.f, *it);
}
{
std::vector<float> v{-1.f};
auto it = find_relevant_start(v.begin(), v.end(), 0.5f);
ASSERT_EQ(-1.f, *it);
}
}

Related

How does one obtain the area of a general polygon set in CGAL?

In the following CGAL example, drawn from here, I have a union of circles and rectangles. It looks like so:
How do I get the area of the resulting union using CGAL?
// Compile with: clang++ -DBOOST_ALL_NO_LIB -DCGAL_USE_GMPXX=1 -O2 -g -DNDEBUG -Wall -Wextra -pedantic -march=native -frounding-math bob.cpp -lgmpxx -lmpfr -lgmp
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Gps_circle_segment_traits_2.h>
#include <CGAL/General_polygon_set_2.h>
#include <CGAL/Lazy_exact_nt.h>
#include <list>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2 Point_2;
typedef Kernel::Circle_2 Circle_2;
typedef CGAL::Gps_circle_segment_traits_2<Kernel> Traits_2;
typedef CGAL::General_polygon_set_2<Traits_2> Polygon_set_2;
typedef Traits_2::General_polygon_2 Polygon_2;
typedef Traits_2::General_polygon_with_holes_2 Polygon_with_holes_2;
typedef Traits_2::Curve_2 Curve_2;
typedef Traits_2::X_monotone_curve_2 X_monotone_curve_2;
// Construct a polygon from a circle.
Polygon_2 construct_polygon (const Circle_2& circle)
{
// Subdivide the circle into two x-monotone arcs.
Traits_2 traits;
Curve_2 curve (circle);
std::list<CGAL::Object> objects;
traits.make_x_monotone_2_object() (curve, std::back_inserter(objects));
CGAL_assertion (objects.size() == 2);
// Construct the polygon.
Polygon_2 pgn;
X_monotone_curve_2 arc;
std::list<CGAL::Object>::iterator iter;
for (iter = objects.begin(); iter != objects.end(); ++iter) {
CGAL::assign (arc, *iter);
pgn.push_back (arc);
}
return pgn;
}
// Construct a polygon from a rectangle.
Polygon_2 construct_polygon (const Point_2& p1, const Point_2& p2,
const Point_2& p3, const Point_2& p4)
{
Polygon_2 pgn;
X_monotone_curve_2 s1(p1, p2); pgn.push_back(s1);
X_monotone_curve_2 s2(p2, p3); pgn.push_back(s2);
X_monotone_curve_2 s3(p3, p4); pgn.push_back(s3);
X_monotone_curve_2 s4(p4, p1); pgn.push_back(s4);
return pgn;
}
// The main program:
int main ()
{
// Insert four non-intersecting circles.
Polygon_set_2 S;
Polygon_2 circ1, circ2, circ3, circ4;
circ1 = construct_polygon(Circle_2(Point_2(1, 1), 1)); S.insert(circ1);
circ2 = construct_polygon(Circle_2(Point_2(5, 1), 1)); S.insert(circ2);
circ3 = construct_polygon(Circle_2(Point_2(5, 5), 1)); S.insert(circ3);
circ4 = construct_polygon(Circle_2(Point_2(1, 5), 1)); S.insert(circ4);
// Compute the union with four rectangles incrementally.
Polygon_2 rect1, rect2, rect3, rect4;
rect1 = construct_polygon(Point_2(1, 0), Point_2(5, 0),
Point_2(5, 2), Point_2(1, 2));
S.join (rect1);
rect2 = construct_polygon(Point_2(1, 4), Point_2(5, 4),
Point_2(5, 6), Point_2(1, 6));
S.join (rect2);
rect3 = construct_polygon(Point_2(0, 1), Point_2(2, 1),
Point_2(2, 5), Point_2(0, 5));
S.join (rect3);
rect4 = construct_polygon(Point_2(4, 1), Point_2(6, 1),
Point_2(6, 5), Point_2(4, 5));
S.join (rect4);
// Print the output.
std::list<Polygon_with_holes_2> res;
S.polygons_with_holes (std::back_inserter (res));
std::copy (res.begin(), res.end(),
std::ostream_iterator<Polygon_with_holes_2>(std::cout, "\n"));
std::cout << std::endl;
return 0;
}
General polygons with possibly circular edges don't have the area function - so we need to get into details of their representation and write this kind of functions ourselves (sigh!!). An example of such an area function is below:
// ------ return signed area under the linear segment (P1, P2)
auto area(Traits_2::Point_2 const& P1, Traits_2::Point_2 const& P2)
{
auto const dx = CGAL::to_double(P1.x()) - CGAL::to_double(P2.x());
auto const sy = CGAL::to_double(P1.y()) + CGAL::to_double(P2.y());
return dx * sy / 2;
}
// ------ return signed area under the circular segment (P1, P2, C)
auto area(Traits_2::Point_2 const& P1, Traits_2::Point_2 const& P2, Circle const& C)
{
auto const dx = CGAL::to_double(P1.x()) - CGAL::to_double(P2.x());
auto const dy = CGAL::to_double(P1.y()) - CGAL::to_double(P2.y());
auto const squaredChord = dx * dx + dy * dy;
auto const chord = std::sqrt(squaredChord);
auto const squaredRadius = CGAL::to_double(C.squared_radius());
auto const areaSector = squaredRadius * std::asin(std::min(1.0, chord / (std::sqrt(squaredRadius) * 2)));
auto const areaTriangle = chord * std::sqrt(std::max(0.0, squaredRadius * 4 - squaredChord)) / 4;
auto const areaCircularSegment = areaSector - areaTriangle;
return area(P1, P2) + C.orientation() * areaCircularSegment;
}
// ------ return signed area under the X-monotone curve
auto area(X_monotone_curve_2 const& XCV)
{
if (XCV.is_linear())
{
return area(XCV.source(), XCV.target());
}
else if (XCV.is_circular())
{
return area(XCV.source(), XCV.target(), XCV.supporting_circle());
}
else
{
return 0.0;
}
}
// ------ return area of the simple polygon
auto area(Polygon_2 const& P)
{
auto res = 0.0;
for (auto it = P.curves_begin(); it != P.curves_end(); ++it) res += area(*it);
return res;
}
// ------ return area of the polygon with (optional) holes
auto area(Polygon_with_holes_2 const& P)
{
auto res = area(P.outer_boundary());
for (auto it = P.holes_begin(); it != P.holes_end(); ++it) res += area(*it);
return res;
}
As you can see I used here Polygon_with_holes_2 access functions outer_boundary, holes_begin and holes_end. For Polygon_2 I used only curves_begin and curves_end access functions. Much more efforts are required to process edges of this polygon, which are of the X_monotone_curve_2 type. All these access functions will be necessary for any other functions you'll need to develop for general polygons.
A full MWE incorporating your example is below:
// Compile with: clang++ -DBOOST_ALL_NO_LIB -DCGAL_USE_GMPXX=1 -O2 -g -DNDEBUG -Wall -Wextra -pedantic -march=native -frounding-math bob.cpp -lgmpxx -lmpfr -lgmp
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Gps_circle_segment_traits_2.h>
#include <CGAL/General_polygon_set_2.h>
#include <CGAL/Lazy_exact_nt.h>
#include <list>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2 Point_2;
typedef Kernel::Circle_2 Circle_2;
typedef CGAL::Gps_circle_segment_traits_2<Kernel> Traits_2;
typedef CGAL::General_polygon_set_2<Traits_2> Polygon_set_2;
typedef Traits_2::General_polygon_2 Polygon_2;
typedef Traits_2::General_polygon_with_holes_2 Polygon_with_holes_2;
typedef Traits_2::Curve_2 Curve_2;
typedef Traits_2::X_monotone_curve_2 X_monotone_curve_2;
//For two circles of radii R and r and centered at (0,0) and (d,0) intersecting
//in a region shaped like an asymmetric lens.
constexpr double lens_area(const double r, const double R, const double d){
return r*r*std::acos((d*d+r*r-R*R)/2/d/r) + R*R*std::acos((d*d+R*R-r*r)/2/d/R) - 0.5*std::sqrt((-d+r+R)*(d+r-R)*(d-r+R)*(d+r+R));
}
// ------ return signed area under the linear segment (P1, P2)
auto area(const Traits_2::Point_2& P1, const Traits_2::Point_2& P2){
auto const dx = CGAL::to_double(P1.x()) - CGAL::to_double(P2.x());
auto const sy = CGAL::to_double(P1.y()) + CGAL::to_double(P2.y());
return dx * sy / 2;
}
// ------ return signed area under the circular segment (P1, P2, C)
auto area(const Traits_2::Point_2& P1, const Traits_2::Point_2& P2, const Circle_2& C){
auto const dx = CGAL::to_double(P1.x()) - CGAL::to_double(P2.x());
auto const dy = CGAL::to_double(P1.y()) - CGAL::to_double(P2.y());
auto const squaredChord = dx * dx + dy * dy;
auto const chord = std::sqrt(squaredChord);
auto const squaredRadius = CGAL::to_double(C.squared_radius());
auto const areaSector = squaredRadius * std::asin(std::min(1.0, chord / (std::sqrt(squaredRadius) * 2)));
auto const areaTriangle = chord * std::sqrt(std::max(0.0, squaredRadius * 4 - squaredChord)) / 4;
auto const areaCircularSegment = areaSector - areaTriangle;
return area(P1, P2) + C.orientation() * areaCircularSegment;
}
// ------ return signed area under the X-monotone curve
auto area(const X_monotone_curve_2& XCV){
if (XCV.is_linear())
{
return area(XCV.source(), XCV.target());
}
else if (XCV.is_circular())
{
return area(XCV.source(), XCV.target(), XCV.supporting_circle());
}
else
{
return 0.0;
}
}
// ------ return area of the simple polygon
auto area(const Polygon_2 P){
auto res = 0.0;
for (auto it = P.curves_begin(); it != P.curves_end(); ++it) {
res += area(*it);
}
return res;
}
// ------ return area of the polygon with (optional) holes
auto area(const Polygon_with_holes_2& P){
auto res = area(P.outer_boundary());
for (auto it = P.holes_begin(); it != P.holes_end(); ++it) {
res += area(*it);
}
return res;
}
// Construct a polygon from a circle.
Polygon_2 construct_polygon (const Circle_2& circle){
// Subdivide the circle into two x-monotone arcs.
Traits_2 traits;
Curve_2 curve (circle);
std::list<CGAL::Object> objects;
traits.make_x_monotone_2_object() (curve, std::back_inserter(objects));
CGAL_assertion (objects.size() == 2);
// Construct the polygon.
Polygon_2 pgn;
X_monotone_curve_2 arc;
std::list<CGAL::Object>::iterator iter;
for (iter = objects.begin(); iter != objects.end(); ++iter) {
CGAL::assign (arc, *iter);
pgn.push_back (arc);
}
return pgn;
}
// Construct a polygon from a rectangle.
Polygon_2 construct_polygon (
const Point_2& p1,
const Point_2& p2,
const Point_2& p3,
const Point_2& p4
){
Polygon_2 pgn;
X_monotone_curve_2 s1(p1, p2); pgn.push_back(s1);
X_monotone_curve_2 s2(p2, p3); pgn.push_back(s2);
X_monotone_curve_2 s3(p3, p4); pgn.push_back(s3);
X_monotone_curve_2 s4(p4, p1); pgn.push_back(s4);
return pgn;
}
double area(const Polygon_set_2& Pset){
std::list<Polygon_with_holes_2> res;
Pset.polygons_with_holes(std::back_inserter(res));
double ret = 0;
for(const auto &x : res){
const auto temp = area(x);
if(!std::isnan(temp)){ //TODO: This seems to be necessary, but I'm not sure why
ret += area(x);
}
}
return ret;
}
void test1(){
// Insert four non-intersecting circles.
Polygon_set_2 S;
const auto circ1 = construct_polygon(Circle_2(Point_2(1, 1), 1));
const auto circ2 = construct_polygon(Circle_2(Point_2(5, 1), 1));
const auto circ3 = construct_polygon(Circle_2(Point_2(5, 5), 1));
const auto circ4 = construct_polygon(Circle_2(Point_2(1, 5), 1));
S.insert(circ1);
S.insert(circ2);
S.insert(circ3);
S.insert(circ4);
// Compute the union with four rectangles incrementally.
const auto rect1 = construct_polygon(Point_2(1, 0), Point_2(5, 0),
Point_2(5, 2), Point_2(1, 2));
const auto rect2 = construct_polygon(Point_2(1, 4), Point_2(5, 4),
Point_2(5, 6), Point_2(1, 6));
const auto rect3 = construct_polygon(Point_2(0, 1), Point_2(2, 1),
Point_2(2, 5), Point_2(0, 5));
const auto rect4 = construct_polygon(Point_2(4, 1), Point_2(6, 1),
Point_2(6, 5), Point_2(4, 5));
S.join(rect1);
S.join(rect2);
S.join(rect3);
S.join(rect4);
// Print the output.
std::list<Polygon_with_holes_2> res;
S.polygons_with_holes (std::back_inserter (res));
std::copy (res.begin(), res.end(),
std::ostream_iterator<Polygon_with_holes_2>(std::cout, "\n"));
std::cout << std::endl;
for(const auto &x: res){
std::cout << "Area = "<< area(x)<< " and should be "<<(4*8-4+M_PI)<<std::endl;
}
}
void test2(){
Polygon_set_2 S;
const auto circ1 = construct_polygon(Circle_2(Point_2(0, 0), 1));
const auto circ2 = construct_polygon(Circle_2(Point_2(0.5, 0), 1));
S.join(circ1);
S.join(circ2);
std::cout<<"Number of holes was "<<(S.number_of_polygons_with_holes()==1)<<" should be 1"<<std::endl;
std::cout<<"Area was "<<CGAL::to_double(area(S))<<" should be "<<(2*M_PI - lens_area(1, 1, 0.5))<<std::endl;
}
void test3(){
Polygon_set_2 S;
const auto circ1 = construct_polygon(Circle_2(Point_2(0, 0), 1));
const auto circ2 = construct_polygon(Circle_2(Point_2(0.5, 0), 1));
const auto circ3 = construct_polygon(Circle_2(Point_2(-1.8, 0), 1));
S.join(circ1);
S.join(circ2);
S.join(circ3);
std::cout<<"Number of holes was "<<(S.number_of_polygons_with_holes()==1)<<" should be 1"<<std::endl;
std::cout<<"Area was "<<CGAL::to_double(area(S))<<" should be "<<(3*M_PI - lens_area(1, 1, 0.5) - lens_area(1, 1, 1.8))<<std::endl;
}
// The main program:
int main (){
std::cout<<"test1"<<std::endl;
test1();
std::cout<<"test2"<<std::endl;
test2();
std::cout<<"test3"<<std::endl;
test3();
return 0;
}
Running the test1 gives an output of 31.1416. You have four rectangles of area 8 each with four overlapping regions of area 1 and 4 quarter circles giving: 4*8-4+π=31.141592653589793, so the results match the theory. Other two tests also work OK.

C++ Merge Sort using iterators

Currently, I am trying to create a simple C++ Merge Sort Program.
using namespace std;
using Iterator = std::vector<int>::iterator;
using CIterator = std::vector<int>::const_iterator;
std::vector<int> merge(CIterator left_begin, CIterator left_end, CIterator right_begin, CIterator right_end) {
std::vector<int> result;
CIterator left = left_begin;
CIterator right = right_begin;
while (left != left_end && right != right_end) {
if (*left <= *right) {
result.push_back(*left);
left++;
} else {
result.push_back(*right);
right++;
}
}
while (left != left_end) {
result.push_back(*left);
left++;
}
while (right != right_end) {
result.push_back(*right);
right++;
}
return result;
}
I created a merge function that basically connects two sorted vectors into one and returns it (I am bound to use the following return type of the function merge). Then Trying to write the driver function merge sort I have the following code, that I think works correctly
void merge_sort(Iterator begin, Iterator end) {
auto difference = distance(begin, end);
if (difference <= 1) {
return;
}
Iterator middle = begin;
advance(middle, difference / 2);
merge_sort(begin, middle);
merge_sort(middle, end);
vector<int> result = merge(begin, middle, middle, end);
// But what to put here?
}
At the place of the comment mark, I don't understand what to write in order to move the sorted array a step up in the recursion. I tried
begin = result.begin();
end = result.end();
but this obviously doesnt work
The problem is that the type signature for merge_sort assumes an in-place algorithm:
void merge_sort(Iterator begin, Iterator end);
But your merge procedure isn't in-place but returns a merged copy of the arrays. You either need to change merge to be in-place, or you need to change merge_sort to return the sorted array. The latter solution (easier but less efficient) would be like this:
std::vector<int> merge_sort(Iterator begin, Iterator end) {
auto difference = distance(begin, end);
if (difference <= 1) {
return;
}
Iterator middle = begin;
advance(middle, difference / 2);
std::vector<int> left = merge_sort(begin, middle);
std::vector<int> right = merge_sort(middle, end);
return merge(left.begin(), left.end(), right.begin(), right.end());
}
An optimized top down merge sort that does a one time allocation of a second vector, then uses a pair of mutually recursive functions (...AtoA, ...AtoB) to alternate the direction of merge based on level of recursion. (I left out the prototypes).
void MergeSort( typename std::vector<int>::iterator &ab,
typename std::vector<int>::iterator &ae)
{
size_t sz = ae - ab;
if (sz < 2)
return;
std::vector<int> vb(sz); // temp vector
std::vector<int>::iterator bb = vb.begin();
std::vector<int>::iterator be = vb.end();
MergeSortAtoA(ab, ae, bb, be);
}
void MergeSortAtoA( typename std::vector<int>::iterator &ab,
typename std::vector<int>::iterator &ae,
typename std::vector<int>::iterator &bb,
typename std::vector<int>::iterator &be)
{
size_t sz = ae - ab;
if(sz < 2) // if 1 element return
return;
std::vector<int>::iterator am = ab+(sz/2);
std::vector<int>::iterator bm = bb+(sz/2);
MergeSortAtoB(ab, am, bb, bm);
MergeSortAtoB(am, ae, bm, be);
Merge(bb, bm, be, ab);
}
void MergeSortAtoB( typename std::vector<int>::iterator &ab,
typename std::vector<int>::iterator &ae,
typename std::vector<int>::iterator &bb,
typename std::vector<int>::iterator &be)
{
size_t sz = ae - ab;
if(sz < 2){ // if 1 element, copy it
*bb = *ab;
return;
}
std::vector<int>::iterator am = ab+(sz/2);
std::vector<int>::iterator bm = bb+(sz/2);
MergeSortAtoA(ab, am, bb, bm);
MergeSortAtoA(am, ae, bm, be);
Merge(ab, am, ae, bb);
}
void Merge( typename std::vector<int>::iterator &ab,
typename std::vector<int>::iterator &am,
typename std::vector<int>::iterator &ae,
typename std::vector<int>::iterator &bb)
{
std::vector<int>::iterator mb = ab; // left run iterator
std::vector<int>::iterator mm = am; // right run iterator
std::vector<int>::iterator bi = bb; // merge run iterator
while(1){ // merge data
if(*mb <= *mm){ // if mb <= mm
*bi++ = *mb++; // copy mb
if(mb < am) // if not end left run
continue; // continue (back to while)
while(mm < ae) // else copy rest of right run
*bi++ = *mm++;
break; // and return
} else { // else mb > mm
*bi++ = *mm++; // copy mm
if(mm < ae) // if not end of right run
continue; // continue (back to while)
while(mb < am) // else copy rest of left run
*bi++ = *mb++;
break; // and return
}
}
}

C++ filtration using lazy iterator 2

I have written this code to print out 17.5, 25.0
template<typename Iterator, typename Callable>
struct FilteringIterator : Iterator {
using OriginalIterator = Iterator; // required for accessing the used Iterator type from other locations
Iterator m_end;
Callable callable;
FilteringIterator(Iterator m_begin, Iterator m_end, Callable callable) : Iterator{m_begin}, m_end{m_end}, callable{callable} { }
Iterator& get_orig_iter() { return ((Iterator&)*this); }
double operator*() {
while (true) {
if (get_orig_iter() != m_end) {
if(callable(*get_orig_iter()) == 1) {
return *get_orig_iter();
} else {
get_orig_iter()++;
}
} else {
break;
}
}
if(callable(*get_orig_iter()) == 1) {
return *get_orig_iter();
}
return *get_orig_iter();
}
};
auto filter = [](auto action) {
return [=](auto& container) {
using Container = std::decay_t<decltype(container)>;
using Iterator = typename Container::iterator;
using IteratorX = FilteringIterator<Iterator, decltype(action)>;
return Range{IteratorX{container.begin(), container.end(), action}, IteratorX{container.end(), container.end(), action}};
};
};
I need a lazy iterator that will traverse over a range and filter it lazily. For example,
auto v = std::vector<double>{};
auto odd_gen = views::odds();
for(int i=0; i<5; ++i)
v.push_back(odd_gen() * 2.5);
// v contains {2.5, 7.5, 12.5, 17.5, 22.5} here
new_line();
for(auto a : v | filter(greater_than(15))) // filter is applied lazily as the range is traversed
std::cout << a << std::endl;
// print { 17.5, 22.5} here
new_line();
for(auto a : v | filter(less_than(15))) // filter is applied lazily as the range is traversed
std::cout << a << std::endl;
// prints 2.5, 7.5 and 12.5
but now, I want it to print out 2.5, 7.5, 12.5 but i couldn't manage to do that. What can I do?

Efficient way to find intersection of two vectors with respect to two members of vector objects

I have two vectors holding data objects. Each data object is holding coordinates and some other data. The vectors will always be sorted (first for the x coordinates and then for the y coordinates). I'm trying to delete all objects from both vectors that have coordinates that can not be found in both of the vectors. Here's an MWE of what I'm currently doing:
#include <iostream>
#include <vector>
#include <algorithm>
struct foo{
foo()=default;
foo(int x, int y, double data):x(x),y(y),data(data){}
int x;
int y;
double data;
};
int main()
{
std::vector<foo> vec1=std::vector<foo>(7);
std::vector<foo> vec2=std::vector<foo>(4);
vec1={foo(1,1,0.),foo(1,2,0.),foo(2,1,0.),foo(2,2,0.),foo(2,3,0.),foo(3,1,0.),foo(3,2,0.)};
vec2={foo(1,2,0.),foo(1,3,0.),foo(2,1,0.),foo(3,1,0.)};
for(auto it1=vec1.begin(); it1!=vec1.end();){
auto cur_element=*it1;
auto intersec = std::find_if(vec2.begin(),vec2.end(),[cur_element]
(foo & comp_element)->bool{
return((cur_element.x==comp_element.x) && (cur_element.y==comp_element.y));
});
if(intersec==vec2.end()) it1=vec1.erase(it1);
else ++it1;
}
for(auto it2=vec2.begin(); it2!=vec2.end();){
auto cur_element=*it2;
auto intersec = std::find_if(vec1.begin(),vec1.end(),[cur_element]
(foo & comp_element)->bool{
return((cur_element.x==comp_element.x) && (cur_element.y==comp_element.y));
});
if(intersec==vec1.end()) it2=vec2.erase(it2);
else ++it2;
}
std::cout<<"vec1:\n";
for(auto i: vec1) std::cout<<i.x<<" "<<i.y<<"\n";
std::cout<<"\nvec2:\n";
for(auto i: vec2) std::cout<<i.x<<" "<<i.y<<"\n";
return 0;
}
It works and gives me the expected output.
Anyway it seems really unefficient having to loop through both of the vectors. Is there a more efficient way to achieve the same output?
EDIT: It's not enough to obtain the coordinates that are represented in both vectors. What I need is an efficient way to delete the "wrong" objects from both vectors.
Your two vectors are sorted already – perfect!
First, assuming a comparison function (with up-coming C++20, this would get the space-ship operator...):
int compare(foo const& l, foo const& r)
{
return l.x != r.x ? l.x - r.x : l.y - r.y;
}
Now you can use it in the algorithm:
auto i1 = v1.begin();
auto i2 = v2.begin();
auto end1 = i1;
auto end2 = i2;
while(i1 != v1.end() && i2 != v2.end())
{
int cmp = compare(*i1, *i2);
if(cmp < 0)
{
// skip element
++i1;
}
else if(cmp > 0)
{
++i2;
}
else
{
// matching element found, keep in both vectors...
if(i1 != end1)
*end1 = std::move(*i1);
++i1;
++end1;
if(i2 != end2)
*end2 = std::move(*i2);
++i2;
++end2;
// if you can rely on move (or fallback copy) assignment
// checking for self assignment, the following simpler
// alternative can be used instead:
//*end1++ = std::move(*i1++);
//*end2++ = std::move(*i2++);
}
}
v1.erase(end1, v1.end());
v2.erase(end2, v2.end());
Linear in both vectors...
The algorithm just moves the elements to be kept to front and finally drops all the overdue ones – similarly as would std::remove_if do...
I think this solution is linear and does what you want.
Possible further enhancement:
for large vectors with large areas of non-intersection, it may be worth caching regions to erase.
another strategy if data is cheap to move, is to conditionally build output vectors from input vectors and swap
struct foo_less
{
bool operator()(foo const&l, foo const& r) const
{
return std::tie(l.x, l.y) < std::tie(r.x, r.y);
}
};
void remove_non_matching(std::vector<foo>& l, std::vector<foo>& r)
{
constexpr auto less = foo_less();
assert(std::is_sorted(l.begin(), l.end(), less));
assert(std::is_sorted(r.begin(), r.end(), less));
auto lcurrent = l.begin(), rcurrent = r.begin();
while (lcurrent != l.end() && rcurrent != r.end())
{
if (less(*lcurrent, *rcurrent))
lcurrent = l.erase(lcurrent);
else if(less(*rcurrent, *lcurrent))
rcurrent = r.erase(rcurrent);
else
{
++lcurrent;
++rcurrent;
}
}
l.erase(lcurrent, l.end());
r.erase(rcurrent, r.end());
}
alternative approach will cost more memory but is theoretically more efficient:
void remove_non_matching_alt(std::vector<foo>& l, std::vector<foo>& r)
{
constexpr auto less = foo_less();
assert(std::is_sorted(l.begin(), l.end(), less));
assert(std::is_sorted(r.begin(), r.end(), less));
auto lresult = std::vector<foo>(), rresult = std::vector<foo>();
auto sz = std::min(l.size(), r.size());
lresult.reserve(sz);
rresult.reserve(sz);
auto lcurrent = l.begin(), rcurrent = r.begin();
while (lcurrent != l.end() && rcurrent != r.end())
{
if (less(*lcurrent, *rcurrent))
++lcurrent;
else if(less(*rcurrent, *lcurrent))
++rcurrent;
else
{
lresult.push_back(std::move(*lcurrent++));
rresult.push_back(std::move(*rcurrent++));
}
}
l.swap(lresult);
r.swap(rresult);
}
Similar but uses a thread_local persistent cache to avoid un-necessary memory allocations:
void remove_non_matching_alt_faster(std::vector<foo>& l, std::vector<foo>& r)
{
constexpr auto less = foo_less();
assert(std::is_sorted(l.begin(), l.end(), less));
assert(std::is_sorted(r.begin(), r.end(), less));
// optimisation - minimise memory allocations on subsequent calls while maintaining
// thread-safety
static thread_local auto lresult = std::vector<foo>(), rresult = std::vector<foo>();
auto sz = std::min(l.size(), r.size());
lresult.reserve(sz);
rresult.reserve(sz);
auto lcurrent = l.begin(), rcurrent = r.begin();
while (lcurrent != l.end() && rcurrent != r.end())
{
if (less(*lcurrent, *rcurrent))
++lcurrent;
else if(less(*rcurrent, *lcurrent))
++rcurrent;
else
{
lresult.push_back(std::move(*lcurrent++));
rresult.push_back(std::move(*rcurrent++));
}
}
l.swap(lresult);
r.swap(rresult);
// ensure destructors of discarded 'data' are called and prep for next call
lresult.clear();
rresult.clear();
}
This is my approach, in a erase–remove idiom style, iterating only once through the vectors:
#include <iostream>
#include <vector>
#include <iterator>
#include <utility>
struct foo
{
foo() = default;
foo(int x, int y, double data) : x(x), y(y), data(data) {}
int x;
int y;
double data;
};
// Maybe better as overloaded operators
int compare_foo(const foo& foo1, const foo& foo2)
{
if (foo1.x < foo2.x) return -1;
if (foo1.x > foo2.x) return +1;
if (foo1.y < foo2.y) return -1;
if (foo1.y > foo2.y) return +1;
return 0;
}
std::tuple<std::vector<foo>::iterator, std::vector<foo>::iterator>
remove_difference(std::vector<foo>& vec1, std::vector<foo>& vec2)
{
typedef std::vector<foo>::iterator iterator;
iterator it1 = vec1.begin();
size_t shift1 = 0;
iterator it2 = vec2.begin();
size_t shift2 = 0;
while (it1 != vec1.end() && it2 != vec2.end())
{
int cmp = compare_foo(*it1, *it2);
if (cmp < 0)
{
++it1;
shift1++;
}
else if (cmp > 0)
{
++it2;
shift2++;
}
else
{
std::iter_swap(it1, std::prev(it1, shift1));
++it1;
std::iter_swap(it2, std::prev(it2, shift2));
++it2;
}
}
return std::make_tuple(std::prev(it1, shift1), std::prev(it2, shift2));
}
int main()
{
std::vector<foo> vec1=std::vector<foo>(7);
std::vector<foo> vec2=std::vector<foo>(4);
vec1={foo(1,1,0.),foo(1,2,0.),foo(2,1,0.),foo(2,2,0.),foo(2,3,0.),foo(3,1,0.),foo(3,2,0.)};
vec2={foo(1,2,0.),foo(1,3,0.),foo(2,1,0.),foo(3,1,0.)};
auto remove_iters = remove_difference(vec1, vec2);
vec1.erase(std::get<0>(remove_iters), vec1.end());
vec2.erase(std::get<1>(remove_iters), vec2.end());
std::cout<<"vec1:\n";
for(auto i: vec1) std::cout<<i.x<<" "<<i.y<<"\n";
std::cout<<"\nvec2:\n";
for(auto i: vec2) std::cout<<i.x<<" "<<i.y<<"\n";
return 0;
}
Output:
vec1:
1 2
2 1
3 1
vec2:
1 2
2 1
3 1
The only thing to not is that this assumes that there are no repeated coordinates, or more specifically, that they are repeated the same number of times on both vectors, and "extra" repetitions would be removed (you could adapt the algorithm to change that if you needed, although it would make the code a bit uglier).
Maybe something like this? You choose first which vector is bigger then iterate (mainly) over the bigger one and check inside the other one.
int main()
{
std::vector<foo> vec1=std::vector<foo>(7);
std::vector<foo> vec2=std::vector<foo>(4);
vec1={foo(1,1,0.),foo(1,2,0.),foo(2,1,0.),foo(2,2,0.),foo(2,3,0.),foo(3,1,0.),foo(3,2,0.)};
vec2={foo(1,2,0.),foo(1,3,0.),foo(2,1,0.),foo(3,1,0.)};
std::vector<foo>::iterator it_begin;
std::vector<foo>::iterator it_end;
std::vector<foo>* main;
std::vector<foo>* other;
if( vec1.size() > vec2.size() ) {
it_begin = vec1.begin();
it_end = vec1.end();
main = &vec1;
other = &vec2;
}
else {
it_begin = vec2.begin();
it_end = vec2.end();
main = &vec2;
other = &vec1;
}
std::vector<foo> new_vec;
for( it_begin; it_begin != it_end; ++it_begin ) {
auto cur_element = *it_begin;
auto intersec = std::find_if( other->begin(),other->end(),[cur_element]
(foo & comp_element)->bool{
return( (cur_element.x==comp_element.x ) && ( cur_element.y==comp_element.y ) );
});
if( intersec != other->end() )
{
new_vec.push_back( cur_element );
}
}
vec1 = new_vec;
vec2 = new_vec;
std::cout<<"vec1:\n";
for(auto i: vec1) std::cout<<i.x<<" "<<i.y<<"\n";
std::cout<<"\nvec2:\n";
for(auto i: vec2) std::cout<<i.x<<" "<<i.y<<"\n";
return 0;
}

BGL : get vertex descriptor with data

I want to get the vertex descriptor with the composant of the vertex, like this :
struct WayPoint{
std::pair<float, float> pos; // with this composant
};
the adjency list :
typedef boost::adjacency_list<
boost::listS,
boost::vecS,
boost::undirectedS,
WayPoint,
WayPointConnection
> WayPointGraph;
typedef WayPointGraph::vertex_descriptor WayPointID;
typedef WayPointGraph::edge_descriptor WayPointConnectionID;
I built my graph and created all the vertices / edges .... the aim is to apply an astar on the graph.
void PathFinding::findMeAPath(std::pair<float, float>begin, std::pair<float, float>end)
{
std::vector<WayPointID> p(boost::num_vertices(graphe));
std::vector<float> d(boost::num_vertices(graphe));
WayPointID start = // I want to find the WayPointID with begin
WayPointID goal = //same with end;
shortest_path.clear();
try {
boost::astar_search
(
graphe,
start,
boost::astar_heuristic<WayPointGraph, float>(),
boost::predecessor_map(&p[0]).distance_map(&d[0]).visitor(astar_goal_visitor(goal)).weight_map(boost::get(&WayPointConnection::dist, graphe))
);
} catch(found_goal fg) {
for(WayPointID v = goal;; v = p[v]) {
shortest_path.push_front(v);
if(p[v] == v)
break;
}
}
}
You need to write a function to find a vertex given a position. The graph type that you have defined uses std::vector to store vertices, so the function will have to iterate through it and compare the queried position to each WayPoint. Something like this could do:
std::pair<WayPointID, bool> find_vertex(const WayPoint& wp, const WayPointGraph& graph)
{
for (WayPointID id = 0; id < boost::num_vertices(graph); ++id)
{
if (equal(graph[id], wp))
return std::make_pair(id, true);
}
return std::make_pair(0, false);
}
Note that the function returns a pair (Id + boolean flag) to indicate whether the search succeeded or not, so you would use it as follows:
bool vertex_found;
WayPointID start;
std::tie (start, vertex_found) = find_vertex(begin, graphe);
if (!vertex_found)
// do something about it
Also the function uses the following to compare positions:
bool equal(const std::pair<float, float>& p1, const std::pair<float, float>& p2)
{
const float EPS = 1e-6;
return (std::fabs(p1.first - p2.first) < EPS &&
std::fabs(p1.second - p2.second) < EPS);
}