Marray
tutorial.cxx
1 // http://www.andres.sc/marray.html
2 //
3 #include <iostream>
4 
5 #include "andres/marray.hxx"
6 
7 int main() {
8  // how to construct arrays:
9  {
10  size_t shape[] = {3, 4, 2};
11  andres::Marray<int> i(shape, shape + 3);
12  // a 3-dimensional array with 2x4x3 entries initialized as int()
13 
14  andres::Marray<int> j(shape, shape + 3, 1);
15  // a 3-dimensional array with 2x4x3 entries initialized as 1
16 
18  // a 3-dimensional array with 2x4x3 entries which remain un-initialized
19 
20  andres::Marray<int> l(shape, shape + 3, andres::FirstMajorOrder);
21  // a 3-dimensional array with 2x4x3 entries which indexed in
22  // first coordinate major order
23 
26  // you get the point
27  }
28 
29  // how to access the entries of arrays:
30  {
31  size_t shape[] = {3, 4, 2};
32  andres::Marray<int> a(shape, shape + 3);
33 
34  // 1. by coordinates
35  a(0, 0, 0) = 1;
36  a(2, 3, 1) = 2;
37  std::cout << a.asString();
38 
39  // 2. by scalar indices
40  for(size_t j=0; j<a.size(); ++j) {
41  std::cout << a(j) << ' ';
42  }
43  std::cout << std::endl;
44 
45  // 3. by STL-compliant random access iterators
46  for(andres::Marray<int>::const_iterator it = a.begin(); it != a.end(); ++it) {
47  std::cout << *it << ' ';
48  }
49  std::cout << std::endl;
50  }
51 
52  // how to do arithmetics with arrays:
53  {
54  // simple arithmetics work just as you would expect.
55  {
56  size_t shape[] = {3, 4, 2};
57  andres::Marray<int> a(shape, shape + 3, 2);
58  andres::Marray<int> b(shape, shape + 3, 2);
60 
61  ++a;
62  --a;
63 
64  a += 2;
65  a -= 2;
66  a /= 2;
67  a *= 2;
68 
69  c = a + b;
70  c = a - b;
71  c = a * b;
72  c = a / b;
73 
74  c = a*a + 2*a*b + b*b;
75  }
76 
77  // different types (e.g. int and float) can be combined in one arithmetic expression.
78  // marray will promote the types according to the standard rules.
79  {
80  size_t shape[] = {4};
81  andres::Marray<int> a(shape, shape + 1, 1);
82  andres::Marray<float> b(shape, shape + 1, 1.1f);
83  andres::Marray<float> c = a + b;
84  std::cout << c.asString();
85  }
86 
87  // marray implements expression templates. the following expression is
88  // thus evaluated only for one entry at coordinate (2, 0, 0):
89  {
90  size_t shape[] = {3, 4, 2};
91  andres::Marray<int> a(shape, shape + 3, 2);
92  andres::Marray<float> b(shape, shape + 3, 3.1f);
93 
94  float c = (a*a*a + 2*a*a*b + 2*a*b*b + b*b*b)(2, 0, 0);
95  std::cout << c << std::endl;
96  }
97  }
98 
99  // how to construct and transform views
100  {
101  // a View makes a contiguous interval in memory look as if it was an
102  // Marray. In contrast to Marray which allocates and de-allocates
103  // memory via an STL-compliant allocator, a View provides an interface
104  // to a contiguous interval in memory that needs to be allocated by
105  // other means. One important use of View is to access sub-arrays.
106  {
107  // let's start with a simple matrix...
108  size_t shape[] = {15, 5};
109  andres::Marray<int> a(shape, shape + 2);
110  for(size_t j=0; j<a.size(); ++j) {
111  a(j) = 10 + static_cast<int>(j);
112  }
113 
114  // ... and define a view to a 7x3 sub-matrix that starts at
115  // coordinates (5, 1):
116  size_t base[] = {5, 1};
117  shape[0] = 7;
118  shape[1] = 3;
119  andres::View<int> b = a.view(base, shape); // construct a view
120  std::cout << a.asString();
121  std::cout << b.asString();
122 
123  // let's now manipulate the sub-matrix (i.e. the data under the
124  // view) and print the original matrix
125  b = 10;
126  std::cout << a.asString();
127 
128  // A View can also be used to make data look differently.
129  // here are two examples:
130 
131  // 1. transposition
133  // Construct a View. 'true' makes the data under the view constant,
134  // i.e., this particular view cannot be used to change the data.
135  std::cout << c.asString();
136  c.transpose();
137  std::cout << c.asString();
138 
139  // 2. reshaping
141  size_t newShape[] = {5, 3, 5};
142  d.reshape(newShape, newShape + 3);
143  std::cout << d.asString();
144 
145  // the doxygen reference documentation tells more about shifting,
146  // reshaping and squeezing views.
147  }
148 
149  // a more advanced use of views is to provide a convenient interface
150  // for the multi-dimensional arrays that are native to C:
151  {
152  int e[3][4][2];
153  for(size_t j=0; j<24; ++j) {
154  (**e)[j] = static_cast<int>(j);
155  }
156 
157  size_t shape[] = {3, 4, 2};
158  andres::View<int> f(shape, shape + 3, **e, andres::FirstMajorOrder,
160  // the first andres::FirstMajorOrder determines how coordinates
161  // are mapped to memory. The second andres::FirstMajorOrder
162  // determines how scalar indices into the view are mapped to
163  // coordinates in the view.
164 
165  // let's compare the element access by scalar indices...
166  for(size_t j=0; j<24; ++j) {
167  std::cout << (**e)[j] << ", " << f(j) << std::endl;
168  }
169 
170  // ...to the element access by coordinates
171  for(size_t x=0; x<shape[0]; ++x) {
172  for(size_t y=0; y<shape[1]; ++y) {
173  for(size_t z=0; z<shape[2]; ++z) {
174  std::cout << "e[" << x << "][" << y << "][" << z << "] = "
175  << e[x][y][z] << ", "
176  << "f(" << x << ", " << y << ", " << z << ") = "
177  << f(x, y, z) << std::endl;
178  }
179  }
180  }
181 
182  // all constructors of View are described in the doxygen reference
183  // documentation.
184  }
185  }
186 
187  // enjoy Marray!
188  return 0;
189 }