Sorting packed vertices with thrust - c++

So I have an device array of PackedVertex structs:
struct PackedVertex {
glm::vec3 Vertex;
glm::vec2 UV;
glm::vec3 Normal;
}
I'm trying to sort them so that duplicates are clustered together in the array; I don't care about overall order at all.
I've tried sorting them by comparing the lengths of the vectors which ran but didn't sort them correctly so now I'm trying per variable using 3 stable_sorts with the binary_operators:
__thrust_hd_warning_disable__
struct sort_packed_verts_by_vertex : public thrust::binary_function < PackedVertex, PackedVertex, bool >
{
__host__ __device__ bool operator()(const PackedVertex &lhs, const PackedVertex &rhs)
{
return lhs.Vertex.x < rhs.Vertex.x || lhs.Vertex.y < rhs.Vertex.y || lhs.Vertex.z < rhs.Vertex.z;
}
};
__thrust_hd_warning_disable__
struct sort_packed_verts_by_uv : public thrust::binary_function < PackedVertex, PackedVertex, bool >
{
__host__ __device__ bool operator()(const PackedVertex &lhs, const PackedVertex &rhs)
{
return lhs.UV.x < rhs.UV.x || lhs.UV.y < rhs.UV.y;
}
};
__thrust_hd_warning_disable__
struct sort_packed_verts_by_normal : public thrust::binary_function < PackedVertex, PackedVertex, bool >
{
__host__ __device__ bool operator()(const PackedVertex &lhs, const PackedVertex &rhs)
{
return lhs.Normal.x < rhs.Normal.x || lhs.Normal.y < rhs.Normal.y || lhs.Normal.z < rhs.Normal.z;
}
};
Trouble is I'm getting a thrust error now: "launch_closure_by_value" which hazarding a guess means that my sort isn't converging due to my operators.
That being said I'm also pretty sure this is not the best way for me to be doing this kind of sort so any feedback would be greatly appreciated.

I don't believe your sort functors are correct.
A sort functor must give a consistent ordering. Let's just consider this one:
return lhs.UV.x < rhs.UV.x || lhs.UV.y < rhs.UV.y;
Suppose I have two UV quantites like this:
UV1.x: 1
UV1.y: 0
UV2.x: 0
UV2.y: 1
This functor will return true no matter which order I present UV1 and UV2. Your other functors are similarly defective.
In thrust speak, these are not valid StrictWeakOrdering functors. If we wish to order UV1 and UV2, we must provide a functor which (consistently) returns true for one presentation order and false for the other presentation order. (The only exception to this is if the two presented quantities are truly equal, then the functor should always return just one answer, either true or false, consistently, regardless of presentation order. However the UV1 and UV2 presented here are not "equal" for the purposes of your desired ordering, i.e. grouping of identical structs.)
The following simple test seems to work for me:
$ cat t717.cu
#include <thrust/sort.h>
#include <thrust/device_ptr.h>
#include <iostream>
#include <stdlib.h>
#define DSIZE 64
#define RNG 10
struct PackedVertex {
float3 Vertex;
float2 UV;
float3 Normal;
};
struct my_PV_grouper {
template <typename T>
__host__ __device__
bool operator()(const T &lhs, const T &rhs) const {
if (lhs.Vertex.x > rhs.Vertex.x) return true;
else if (lhs.Vertex.x < rhs.Vertex.x) return false;
else if (lhs.Vertex.y > rhs.Vertex.y) return true;
else if (lhs.Vertex.y < rhs.Vertex.y) return false;
else if (lhs.Vertex.z > rhs.Vertex.z) return true;
else if (lhs.Vertex.z < rhs.Vertex.z) return false;
else if (lhs.UV.x > rhs.UV.x) return true;
else if (lhs.UV.x < rhs.UV.x) return false;
else if (lhs.UV.y > rhs.UV.y) return true;
else if (lhs.UV.y < rhs.UV.y) return false;
else if (lhs.Normal.x > rhs.Normal.x) return true;
else if (lhs.Normal.x < rhs.Normal.x) return false;
else if (lhs.Normal.y > rhs.Normal.y) return true;
else if (lhs.Normal.y < rhs.Normal.y) return false;
else if (lhs.Normal.z > rhs.Normal.z) return true;
else return false;
}
};
int main(){
PackedVertex h_data[DSIZE];
PackedVertex *d_data;
for (int i =0; i < DSIZE; i++)
h_data[i].Vertex.x = h_data[i].Vertex.y = h_data[i].Vertex.z = h_data[i].UV.x = h_data[i].UV.y = h_data[i].Normal.x = h_data[i].Normal.y = h_data[i].Normal.z = rand()%RNG;
cudaMalloc(&d_data, DSIZE*sizeof(PackedVertex));
cudaMemcpy(d_data, h_data, DSIZE*sizeof(PackedVertex), cudaMemcpyHostToDevice);
thrust::device_ptr<PackedVertex> d_ptr(d_data);
thrust::sort(d_ptr, d_ptr+DSIZE, my_PV_grouper());
cudaMemcpy(h_data, d_data, DSIZE*sizeof(PackedVertex), cudaMemcpyDeviceToHost);
for (int i =0; i < DSIZE; i++)
std::cout << h_data[i].Vertex.x << " ";
std::cout << std::endl;
}
$ nvcc -o t717 t717.cu
$ ./t717
9 9 9 9 9 9 9 8 8 8 7 7 7 7 7 7 7 6 6 6 6 6 6 6 6 6 5 5 5 5 5 5 4 4 4 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 0 0 0 0 0 0
$
In case it's not clear, there is nothing particularly specific to the usage of thrust and functors here; the fundamental logic used to order these items needs to be correct for a valid sort. Even if you wrote a simple serial bubble-sort, it would have to use similar logic. The logic presented in your functors cannot be used to provide a sensible ordering.
If there are other problems with your approach, I can't say, as you have not provided a proper MCVE, which is expected for questions like this.

Related

C++ EIGEN: How to create triangular matrix map from a vector?

I would like to use data stored into an Eigen (https://eigen.tuxfamily.org) vector
Eigen::Vector<double, 6> vec({1,2,3,4,5,6});
as if they were a triangular matrix
1 2 3
0 4 5
0 0 6
I know how to do it for a full matrix using Eigen's Map
Eigen::Vector<double, 9> vec({1,2,3,4,5,6,7,8,9});
std::cout << Eigen::Map<Eigen::Matrix<double, 3, 3, RowMajor>>(vec.data());
which produces
1 2 3
4 5 6
7 8 9
However I do not know how to make a Map to a triangular matrix.
Is it possible?
Thanks!
[Edited for clarity]
In my opinion this cannot be done using Map only: The implementation of Map as it is relies on stride sizes that remain constant no matter their index positions, see https://eigen.tuxfamily.org/dox/classEigen_1_1Stride.html.
To implement a triangular matrix map you would have to have a Map that changes its inner stride depending on the actual column number. The interfaces in Eigen do not allow that at the moment, see https://eigen.tuxfamily.org/dox/Map_8h_source.html.
But if you are just concerned about the extra memory you can just use Eigen's sparse matrix representation:
https://eigen.tuxfamily.org/dox/group__TutorialSparse.html
(Refer to section "Filling a sparse matrix".)
This is not a direct solution to your problem but a way how to calculate the std::vector to fill in the 0 at the correct place. I think it is also possible to calculate it as a std::array if needed. I am not sure if that helps, but I guess you could use the calculated vector to fill the Eigen::Map
#include <array>
#include <cstddef>
#include <iostream>
#include <vector>
template<typename T, size_t N>
class EigenVector
{
static constexpr int CalculateRowColSize(size_t n)
{
size_t i = 1;
size_t inc = 1;
do
{
if (inc == n)
{
return static_cast<int>(i);
}
i++;
inc += i;
} while (i < n);
return -1;
}
static constexpr bool IsValid(size_t n)
{
if(CalculateRowColSize(n) == -1)
{
return false;
}
return true;
}
static_assert(IsValid(N));
public:
EigenVector() = delete;
static std::vector<T> Calculate(std::array<T, N> values)
{
constexpr size_t mRowColSize = CalculateRowColSize(N);
std::vector<T> ret;
auto count = 0;
auto valueCounter = 0;
for (size_t i = 0; i < mRowColSize; i++)
{
for (auto j = 0; j < count; j++)
{
ret.push_back(T());
}
for (size_t j = 0; j < mRowColSize - count; j++)
{
ret.push_back(values[valueCounter]);
valueCounter++;
}
count++;
}
return ret;
}
};
int main()
{
{
const std::array<int, 6> arr{ 1,2,3,4,5,6 };
const auto values = EigenVector<int, 6>::Calculate(arr);
for (auto& val : values)
{
std::cout << val << " ";
}
}
std::cout << std::endl << std::endl;
{
const std::array<int, 10> arr{ 1,2,3,4,5,6,7,8,9,10 };
const auto values = EigenVector<int, 10>::Calculate(arr);
for (auto& val : values)
{
std::cout << val << " ";
}
}
return 0;
}
Output:
1 2 3 0 4 5 0 0 6
1 2 3 4 0 5 6 7 0 0 8 9 0 0 0 10
Note that the algorithm is written that only possible matrix sizes are valid as input

How to sort std::set according to the second element?

Given n points in a two-dimensional space, sort all the points in ascending order.
(x1,y1) > (x2,y2) if and only if (x1>x2) or (x1==x2 && y1<y2)
Input specification:
The first line consists of an integer t, the number of test cases. Then for each test case, the first line consists of an integer n, the number of points. Then the next n lines contain two integers xi, yi which represents the point.
Output Specification:
For each test case print the sorted order of the points.
Input constraints:
1 <= t <= 10
1 <= n <= 100000
- 10 ^ 9 <= co - ordinates <= 10 ^ 9
NOTE: Strict time limit. Prefer scanf/printf/BufferedReader instead of cin/cout/Scanner.
Sample Input:
1
5
3 4
-1 2
5 -3
3 3
-1 -2
Sample Output:
-1 2
-1 -2
3 4
3 3
5 -3
I declared a set, now I want to sort descendingly(values) if the keys are equal. Here is my code:
int main()
{
int n, i, hold = 0;
set<pair<int, int>>s;
int x, y, t;
set<pair<int, int>>::iterator it;
SF(t)
while (t--)
{
SF(n) while (n--) {
SF(x) SF(y)
s.insert({ x,y });
}
for (it = s.begin(); it != s.end(); it++) {
PF(it->first) printf(" "); PF(it->second); printf("\n");
}
s.clear();
}
return 0;
}
my output
-1 -2
-1 2
3 3
3 4
5 -3
I want the key values to be sorted descendingly if the keys are same.
The std::set uses by default std::less as default comparator for comparing the elements inserting to it.
In your case, you have std::pair<int,int> as your element type hence, the std::set uses the default operator< of std::pair defined in the standard and hence you are not getting the result you want.
In order to achieve your custom style comparison, you need to provide a custom comparator
template<
class Key,
class Compare = std::less<Key>,
// ^^^^^^^^^^^^^^^ --> instead of this
class Allocator = std::allocator<Key>
> class set;
which should meet the requirements of compare.
Since C++11 you could also use a lambda function for this:
Following is a sample example code: (See Online)
#include <iostream>
#include <set>
using pairs = std::pair<int, int>;
int main()
{
// custom compare
const auto compare = [](const pairs &lhs, const pairs &rhs)
{
return lhs.first < rhs.first || (lhs.first == rhs.first && lhs.second > rhs.second);
};
std::set<pairs, decltype(compare)> mySet(compare);
mySet.emplace(3, 4);
mySet.emplace(-1, 2);
mySet.emplace(5, -3);
mySet.emplace(3, 3);
mySet.emplace(-1, -2);
for (const auto& it : mySet)
std::cout << it.first << " " << it.second << std::endl;
}
Output:
-1 2
-1 -2
3 4
3 3
5 -3
Set doesn't sort the way you want by default, so you have to supply your own comparison function.
struct MyComp
{
bool operator()(const pair<int,int>& x, const pair<int,int>& y) const
{
return x.first < y.first || (x.first == y.first && x.second > y.second);
}
};
set<pair<int,int>, MyComp> s;
As Jejo and others have answered, you can create a custom comparitor to specify how you want your points sorted:
// custom compare
const auto compare = [](const pairs &lhs, const pairs &rhs)
{
return lhs.first < rhs.first || (lhs.first == rhs.first && lhs.second > rhs.second);
};
set<pair<int, int>, decltype(compare)> mySet(compare);
However, if performance is your concern, you will probably find that using a std::vector and calling std::sort is much faster than the std::set/insert alternative:
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n, i, hold = 0;
vector<pair<int, int>> v;
int x, y, t;
SF(t)
while (t--)
{
SF(n)
v.reserve(n);
while (n--) {
SF(x) SF(y)
v.emplace_back( x,y );
}
// custom comparitor
const auto comp = [](const pairs &lhs, const pairs &rhs)
{
return lhs.first < rhs.first || (lhs.first == rhs.first && lhs.second > rhs.second);
};
sort(v.begin(), v.end(), comp);
for (const auto &p : v) {
PF(p.first) printf(" "); PF(p.second); printf("\n");
}
v.clear();
}
return 0;
}
A couple reasons why inserting into a set is slower than inserting into a vector and then sorting:
std::set implementations involve binary trees, usually red-black trees. See here for details.
Iterating over the range of elements in a std::set is much slower
Note that both methods require n allocations and require on the order of nlog(n) operations for insertion + sorting.

Find if path exist or not in maze c++

My code is giving me runtime error. I can't figure it out how to resolve it?
It's not even working for smaller matrix 4 X 4 . Matrix size for the problem is not more than 20 x 20.
Code:
#include <iostream>
using namespace std;
int a[20][20];
bool findpath(int ar[][20],int i,int j,int size)
{
if (ar[i][j]==0 || i>(size-1) || j>(size-1) || i<0 || j<0)
return false;
if (ar[i][j]==2)
return true;
if ((findpath(ar,i+1,j,size)) || (findpath(ar,i,j+1,size))
|| (findpath(ar,i-1,j,size)) || (findpath(ar,i,j-1,size)))
return true;
return false;
}
int main() {
int t;
cin>>t;
while(t--)
{ int n;
cin>>n;
int r,c;
//size = n;
for(int i =0 ;i<n;i++)
{
for(int j=0;j<n;j++)
{
cin>>a[i][j];
if (a[i][j]==1)
{ r=i;
c=j;
}
}
}
//cout<<r<<c;
bool b = findpath(a,r,c,n);
if (b)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
Input:
1
4
3 0 0 0 0 3 3 0 0 1 0 3 0 2 3 3
Output:
YES
But I am getting Segmentation Fault (SIGSEGV)
Check the order of evaluation of your statement if (ar[i][j]==0 || i>(size-1) || j>(size-1) || i<0 || j<0). You will access ar[i][j] to evaluate the first expression even if i is out of bounds or j is out of bounds. It should be in the order so that when a short circuit does happen in the if condition you are safe/does not result in undefined behaviour for example:
if (i < 0 || i >= size || j < 0 || j >= size || ar[i][j]==0). Now if i < 0 it shorcircuits and does not need to check the rest and does not evaluate ar[i][j].
As you mentioned this is not working, here is a working version which I will explain. First I have changed your C style arrays to vectors and rather I use those to get row and col sizes. I also removed your inputs from users which you can add in later and helps keep the problem simple.
#include <vector>
bool findpath(vector<vector<int>>ar,int i,int j,vector<vector<int>>& visited)
{
if (i < 0 || i >= ar.size() || j < 0 || j >= ar[0].size() || ar[i][j] == 0 || visited[i][j]) return false;
if (ar[i][j]==2) return true;
visited[i][j] = true;
if(findpath(ar,i+1,j,visited) || findpath(ar,i,j+1,visited) || findpath(ar,i-1,j,visited) || findpath(ar,i,j-1,visited)) return true;
visited[i][j] = false;
return false;
}
int main() {
const int rows = 3;
const int cols = 3;
vector<vector<int>> arr = {{ 0 , 3 , 2 },
{ 3 , 3 , 0 },
{ 1 , 3 , 0 }};
vector<vector<int>> visited(rows,vector<int>(cols,false));
bool b = findpath(arr,1,1,visited);
if (b)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
return 0;
}
In the main function I have just used a vector<vector<int>> to describe a maze which was in the link you posted. The i and j are the starting points in the example below that is 1 and 1. There is also a visited 2D array same as the maze. This stops you from going in an infinite recursion by marking the spots you have already covered and if they dont work out you set vector[i][j] = false to backtrack. Lastly if any of your arrangements returns a valid result we return else we just return false.
You can view this demo live here
You also mention that 1 is the starting point. In the example I have already started from 1. You can add a loop in main to first figure out the coordinates for the starting point. Again this should be enough to get you going.

C++ set: storing duplicates: confused about < operator

I'm quite new to C++ (but know my way around C) so I'm probably missing something obvious.
TLDR: I use a std::set which stores elements twice, which is definitely not what I want.
Long story:
I've defined a class Clique and I need to store elements of this class in a set, so I've defined the < operator for Clique:
class Clique{
public :
int b;
int e;
int l;
std::set<int> X;
bool operator <( const Clique &rhs ) const
{
if( b < rhs.b)
return true;
if( e < rhs.e)
return true;
if( X.size() < rhs.X.size() )
return true;
std::set<int>::iterator itX = X.begin();
std::set<int>::iterator itrhs = rhs.X.begin();
// both sets have same size, need only to check end for one of them
while( (*itX == *itrhs) && ( itX != X.end() ) ){
++itX;
++itrhs;
}
if( itX == X.end() ){
//both sets are equal
return false;
}
else
return ( *itX < *itrhs );
}
void print_clique(FILE *F) const ;
};
(I wasn't sure how set comparison is done, so I wrote a routine for comparing them first by size, then element by element).
Now I want to store Clique elements in a set and this is where the problem appears.
My std::set
(1) does not appear to store Clique elements in the order I've defined;
(2) stores several copies of the same Clique
I've written a function to print a set of Clique:
void print_cliqueset(std::set<Clique> mySet){
int setsize = 0;
std::set<Clique>::iterator it = mySet.begin();
Clique cur_c = *it;
Clique prev_c = *it;
while( it != mySet.end() ){
// for( std::set<Clique>::iterator it = mySet.begin(); it != mySet.end(); ++it ){
it->print_clique(stdout);
setsize ++;
++it;
if( it != mySet.end() ){
cur_c = *it;
assert ( prev_c < cur_c);
gassert( prev_c.b <= cur_c.b );
prev_c = *it;
}
}
assert( setsize == mySet.size() );
}
My function is more complicated than needed but I wanted to make sure I understood what was going on.
Here is a typical output of printing such a set:
There's a line for each Clique, in which I print first b, then e, then the elements in the set X.
6829 9716 1 2 3 5 8 9 10
6792 9687 1 2 3 7 8 9 10
606 6531 1 2 3 5 6 7 8 9
6829 9687 1 2 3 5 7 8 9 10
410 9951 2 6
484 9805 1 2 4 6
494 9805 2 4 6 10
506 9805 1 2 5 6
484 9821 1 2 4
484 9871 2 3 4 6
506 9821 1 2 5
484 9802 1 2 3 4 6
486 9805 1 2 4 6 9
486 9802 1 2 3 4 6 9
507 9802 1 2 3 4 6 9 10
502 9802 1 2 3 4 6 10
506 9802 1 2 3 5 6
507 9806 1 2 4 9 10
507 9805 1 2 5 6 9
527 9806 1 2 5 9 10
As we can see, the cliques are not at all sorted on the order I defined (or wanted to define). They should be sorted first by member b (which is the first of each line), and this is not the case at all.
Then I have some duplicate lines in the output (not appearing in the example above but present in the full output). I guess the fact that I have duplicates is not surprising given that it seems confused about the order...
I guess the answer is something fairly obvious but I fail to see it. Any help would be appreciated!
Your bool operator <( const Clique &rhs ) const is wrong as it doesn't respect strict ordering.
It may simply be:
bool operator <(const Clique& rhs) const
{
return std::tie(b, e, X) < std::tie(rhs.b, rhs.e, rhs.X);
}
Your operator< is broken. Consider two Cliques:
c1 is {b = 0, e = 1, ...}
c2 is {b = 1, e = 0, ...}
Your code will return true for both c1 < c2 and c2 < c1.
Obviously, in such situation std::set shows strange behavior.
I would fix your operator< in the following way:
bool operator <( const Clique &rhs ) const
{
if( b != rhs.b)
return b < rhs.b;
if( e != rhs.e)
return e < rhs.e;
if( X.size() != rhs.X.size() )
return X.size() < rhs.X.size();
std::set<int>::iterator itX = X.begin();
std::set<int>::iterator itrhs = rhs.X.begin();
// both sets have same size, need only to check end for one of them
while((itX != X.end()) && (itX == *itrhs)){
++itX;
++itrhs;
}
if( itX == X.end() ){
//both sets are equal
return false;
}
else
return ( *itX < *itrhs );
}
The definition of operator< should be such that for each pair of elements 'b' and 'e' the relationship b < e should be used to determine any kind of relationship. The following equivalences are in force here:
a > b <==> b < a
a == b <==> !(a < b) && !(b < a)
a >= b <==> `!(a < b)
And so on. If you use multiple fields to be checked for every relationship check, then you have a kind-of multidimensional ranges. Making a flat range out of that can be only done this way:
More significant field is checked first; if in this field values aren't equal, you return the result immediately
Otherwise - if they are equal - you check the next field in the significance order and so on.
The requirement of using this complicated relationship definition in the set makes things actually harder for you because all you should do is to state whether one element is less than the other. So in your case you'll have to check for equality inside by yourself. Your procedure checks the fields "next in significance chain" also if lhs.b > rhs.b.
Operator < must provide strict weak ordering. I.e. if x < y then !(y < x) and !(y == x).
In the case of Clique, the requirements seem to be that the elements b, e, and X are compared lexographically.
The idiomatic way to represent this is to do all comparisons in terms of operator<:
#include <set>
class Clique{
public :
int b;
int e;
int l;
std::set<int> X;
bool operator <( const Clique &r ) const
{
auto const& l = *this;
if (l.b < r.b) return true;
if (r.b < l.b) return false;
if (l.e < r.e) return true;
if (r.e < l.e) return false;
if (l.X < r.X) return true;
if (r.X < l.X) return false;
return false;
}
void print_clique(FILE *F) const ;
};
And yes, std::set really does provide operator< when the key type provides it.
Another way to write this, as Jarod was alluding to is this:
#include <set>
#include <tuple>
class Clique{
public :
int b;
int e;
int l;
std::set<int> X;
bool operator <( const Clique &r ) const
{
auto const& l = *this;
return std::tie(l.b, l.e, l.X) < std::tie(r.b, r.e, r.X);
}
void print_clique(FILE *F) const ;
};
Which I think you'll agree is concise, expressive, correct and idiomatic.

How to make double sort integer arrays using C++?

I have 3-column integer arrays, whose last 2 elements are for sorting. For example
10 0 1
11 0 2
12 1 2
13 0 1
I want them to become:
10 0 1
13 0 1
11 0 2
12 1 2
The arrays are first sorted according to the 2nd column, and then again according to 3rd column.
I have over 3000 rows, so I need something also fast. How can you do this in c++?
Note: The array will be allocated dynamically using the following templates:
template <typename T>
T **AllocateDynamic2DArray(int nRows, int nCols){
T **dynamicArray;
dynamicArray = new T*[nRows];
for( int i = 0 ; i < nRows ; i++ ){
dynamicArray[i] = new T[nCols];
for ( int j=0; j<nCols;j++){
dynamicArray[i][j]= 0;
}
}
return dynamicArray;
}
in main,
int ** lineFilter = AllocateDynamic2DArray(2*numberOfLines,3);
you can use std::sort(); however, this is complicated by your array being 2D.
In general, std::sort() can't eat 2D arrays; you have to create a class to cast around the compiler warnings and complaints:
#include <iostream>
#include <algorithm>
int data[4][3] = {
{10,0,1},
{11,0,2},
{12,1,2},
{13,0,1}
};
struct row_t { // our type alias for sorting; we know this is compatible with the rows in data
int data[3];
bool operator<(const row_t& rhs) const {
return (data[1]<rhs.data[1]) || ((data[1]==rhs.data[1]) && (data[2]<rhs.data[2]));
}
};
int main() {
std::sort((row_t*)data,(row_t*)(data+4));
for(int i=0; i<4; i++)
std::cout << i << '=' << data[i][0] << ',' << data[i][1] << ',' << data[i][2] << ';' << std::endl;
return 0;
}
It becomes much easier if you use a std::vector to hold your items that really are of type row_t or such. Vectors are dynamically sized and sortable.
I think this should work:
template<typename T>
struct compareRows {
bool operator() (T * const & a, T * const & b) {
if (a[1] == b[1])
return a[2] < b[2];
else
return a[1] < b[1];
}
};
std::sort(dynamicArray, dynamicArray+nrows, compareRows<int>());
Use a functor to implement the comparison between the rows. The sort will take pointers to the beginning of each row and swap them according to the contents of the rows. The rows will stay in the same places in memory.
OK, the OP has a three-column integer arrays, which is not straightforward to sort, because you can't assign arrays.
One option is to have arrays of structs, where the struct contains one element for each column, write a custom compare routine and use std::sort.
Another option is to pretend we have such an array of structs and employ the evilness of reinterpret_cast, like below:
#include <algorithm>
#include <iostream>
struct elt_t
{
int e0;
int e1;
int e2;
};
int
compare (const elt_t &a, const elt_t &b)
{
if (a.e1 == b.e1)
return a.e2 < b.e2;
else
return a.e1 < b.e1;
}
int a [10][3] =
{
{ 10, 0, 1 },
{ 11, 0, 2 },
{ 12, 1, 2 },
{ 13, 0, 1 }
};
int
main ()
{
std::sort (reinterpret_cast<elt_t *>(&a[0]),
reinterpret_cast<elt_t *>(&a[4]), compare);
int i, j;
for (i = 0; i < 4; ++i)
std::cout << a [i][0] << ", " << a [i][1] << ", " << a [i][2] << std::endl;
return 0;
}
Of course, whether or not this is standards compliant is highly debatable :)
EDIT:
With the added requirement for the matrix to by dynamically allocated, you can use an array of std::vector, or a vector of std::vector:
#include <algorithm>
#include <iostream>
#include <vector>
int
compare (const std::vector<int> &a, const std::vector<int> &b)
{
if (a[1] == b[1])
return a[2] < b[2];
else
return a[1] < b[1];
}
std::vector<int> *
make_vec (unsigned int r, unsigned int c)
{
std::vector<int> *v = new std::vector<int> [r];
/* Don't care for column count for the purposes of the example. */
v [0].push_back (10); v [0].push_back (0); v [0].push_back (1);
v [1].push_back (11); v [1].push_back (0); v [1].push_back (2);
v [2].push_back (12); v [2].push_back (1); v [2].push_back (2);
v [3].push_back (13); v [3].push_back (0); v [3].push_back (1);
return v;
}
int
main ()
{
std::vector<int> *v = make_vec (4, 3);
std::sort (&v[0], &v[4], compare);
int i, j;
for (i = 0; i < 4; ++i)
std::cout << v[i][0] << ", " << v [i][1] << ", " << v [i][2] << std::endl;
delete [] v;
return 0;
}
use this for the second column and then for the third. Now it works for single dim arrays
int *toplace(int *start, int *end)
{
int *i = start+1, *j= end-1;
while(i<=j)
{
while(*i<=*start && i<=j) {i++;}
while(*j>=*start && i<=j) {j--;}
if (i<j) std::swap(*i++,*j--);
}
std::swap(*start,*(i-1));
return i-1;
}
void quicksort(int *start, int *end)
{
if (start >= end) return;
int *temp = start;
temp = toplace(start,end);
quicksort(start,temp);
quicksort(temp+1,end);
}
You can do this using the bubble sort algorithm (http://en.wikipedia.org/wiki/Bubble_sort)
Basically iterate through all records, comparing the current record, with the next. If the current record's 2nd column is higher then swap these records. If the current record's 2nd column is equal but the 3rd column is higher, then swap also.
Continue iterating until no more swaps are made.
To use your example:
10 0 1
11 0 2
12 1 2 (swap with next)
13 0 1
10 0 1
11 0 2(swap with next)
13 0 1
12 1 2
10 0 1
13 0 1
11 0 2
12 1 2
And done!