Re: [major] struct array indexing in tip

classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

Re: [major] struct array indexing in tip

John W. Eaton
Administrator
[moved from the bug list.  --jwe]

On 21-Jan-2009, Jaroslav Hajek wrote:

| Okay, I've seen a couple of bugs myself, so here's another shot:
| http://hg.savannah.gnu.org/hgweb/octave/rev/906f976d35a8
|
| This time, I didn't try to allow further indexing on cs-lists, like
| a(1:2).x(1), because Matlab doesn't allow it either, and it
| complicates things a little.
|
| octave:1> [a(1:2).x(2)] = deal(3,4)
| error: a cs-list cannot be further indexed
| error: evaluating argument list element number 1

OK.

| I've revamped the tree_index_expression::lvalue code to avoid useless
| evaluation of the trailing indexing expr just for getting dimensions,
| so things should be somewhat faster now. Also, re-evaluation of an
| already evaluated portion of the index chain is avoided in both lvalue
| and rvalue, so that the following code calls testfun only once:
|
| function y = testfun (x)
|   disp ('called');
|   for i = 1:5
|     y{i} = (1:i) * x;
|   endfor
| endfunction
|
| testfun (5) {end} (end)
|
| instead of 3 times (prior to this patch).

OK, I think this was the intent all along, but I'm not surprised that
it was not doing the right thing...

| Also, I've disallowed automatic resizing in the normal subsref
| function, so that the following code will now produce a proper error:
|
| a(1).x.x = 1
| a(2).x

OK.

| > Maybe someone (other than Jaroslav) would like to generate some tests
| > so these problems don't resurface later?
| >
|
| That would be great. I *may* do it eventually myself, but no promise :)

We need the tests, so I'm hoping someone else will have the time to do
it.  Anyone?

| I'd still like to make more changes to the indexing code:
|
| First, an indexed assignment to an out-of-bounds element that does not
| occur last in the indexing chain
| does unnecessarily resize the array three times via the indexing with
| resize_ok. I don't think that Array<T>::index (..., resize_ok) is
| actually used by any instance than Cell (i.e. Array<octave_value>),
| and probably the only correct usage is with scalar-resulting indices,
| so I'd like to move the methods away from Array<T> to Cell and
| optimize the all-scalars case to avoid the unnecessary resizes.

OK.

| Second, given that octave_value_lists are often converted to Cells and
| vice versa, I'd like to consider rebasing octave_value_list as a Cell
| subclass. That would allow virtually all argument-passing code benefit
| from the shallow copy mechanism of Array<T> (now even for contiguous
| subranges), and thus reduce the amount of copying octave_values
| around. That would allow things like func (args{:}) to work without
| copying.

I think this is OK too, but should octave_value_list be derived from
Cell, or contain a Cell?  Does it need to inherit all the operations
of a Cell?  If not, I think maybe it would be better if it simply
contained a Cell instead of a std::vector<octave_value> object.  But
I haven't given it a lot of thought so maybe I'm missing something.

jwe
Reply | Threaded
Open this post in threaded view
|

Re: [major] struct array indexing in tip

Thorsten Meyer
John W. Eaton wrote:
> | > Maybe someone (other than Jaroslav) would like to generate some tests
> | > so these problems don't resurface later?
> | >
> |
> | That would be great. I *may* do it eventually myself, but no promise :)
>
> We need the tests, so I'm hoping someone else will have the time to do
> it.  Anyone?
>
I would like to learn how to use struct arrays anyway. So I'll try to find time to do it over the
weekend. Where should they be placed in the source tree?

Thorsten

Reply | Threaded
Open this post in threaded view
|

Re: [major] struct array indexing in tip

Jaroslav Hajek-2
In reply to this post by John W. Eaton
On Wed, Jan 21, 2009 at 4:28 PM, John W. Eaton <[hidden email]> wrote:

> [moved from the bug list.  --jwe]
>
> On 21-Jan-2009, Jaroslav Hajek wrote:
>
> | Okay, I've seen a couple of bugs myself, so here's another shot:
> | http://hg.savannah.gnu.org/hgweb/octave/rev/906f976d35a8
> |
> | This time, I didn't try to allow further indexing on cs-lists, like
> | a(1:2).x(1), because Matlab doesn't allow it either, and it
> | complicates things a little.
> |
> | octave:1> [a(1:2).x(2)] = deal(3,4)
> | error: a cs-list cannot be further indexed
> | error: evaluating argument list element number 1
>
> OK.
>
> | I've revamped the tree_index_expression::lvalue code to avoid useless
> | evaluation of the trailing indexing expr just for getting dimensions,
> | so things should be somewhat faster now. Also, re-evaluation of an
> | already evaluated portion of the index chain is avoided in both lvalue
> | and rvalue, so that the following code calls testfun only once:
> |
> | function y = testfun (x)
> |   disp ('called');
> |   for i = 1:5
> |     y{i} = (1:i) * x;
> |   endfor
> | endfunction
> |
> | testfun (5) {end} (end)
> |
> | instead of 3 times (prior to this patch).
>
> OK, I think this was the intent all along, but I'm not surprised that
> it was not doing the right thing...
>
> | Also, I've disallowed automatic resizing in the normal subsref
> | function, so that the following code will now produce a proper error:
> |
> | a(1).x.x = 1
> | a(2).x
>
> OK.
>
> | > Maybe someone (other than Jaroslav) would like to generate some tests
> | > so these problems don't resurface later?
> | >
> |
> | That would be great. I *may* do it eventually myself, but no promise :)
>
> We need the tests, so I'm hoping someone else will have the time to do
> it.  Anyone?
>
> | I'd still like to make more changes to the indexing code:
> |
> | First, an indexed assignment to an out-of-bounds element that does not
> | occur last in the indexing chain
> | does unnecessarily resize the array three times via the indexing with
> | resize_ok. I don't think that Array<T>::index (..., resize_ok) is
> | actually used by any instance than Cell (i.e. Array<octave_value>),
> | and probably the only correct usage is with scalar-resulting indices,
> | so I'd like to move the methods away from Array<T> to Cell and
> | optimize the all-scalars case to avoid the unnecessary resizes.
>
> OK.
>
> | Second, given that octave_value_lists are often converted to Cells and
> | vice versa, I'd like to consider rebasing octave_value_list as a Cell
> | subclass. That would allow virtually all argument-passing code benefit
> | from the shallow copy mechanism of Array<T> (now even for contiguous
> | subranges), and thus reduce the amount of copying octave_values
> | around. That would allow things like func (args{:}) to work without
> | copying.
>
> I think this is OK too, but should octave_value_list be derived from
> Cell, or contain a Cell?  Does it need to inherit all the operations
> of a Cell?  If not, I think maybe it would be better if it simply
> contained a Cell instead of a std::vector<octave_value> object.  But
> I haven't given it a lot of thought so maybe I'm missing something.
>
> jwe
>

The changesets are uploaded. octave_value_list is now implemented
using Array<octave_value>, allowing data sharing,
and shallow conversions to and from Cells. I've also dug for various
unoptimized constructions of octave_value_lists using plain loops and
replaced them by shallow copying and slicing where possible. Also,
I've added const qualifiers to several temporary variables (Cells and
octave_value_lists) that were unnecessarily forcing copies.

cf this simple benchmark (set n to a suitable value):
n = 1e6;
a = zeros (n, 2);
a = mat2cell (a, ones (n, 1), 2);
tic; [b(1:n).x] = a{:}; toc
tic; [b(1:n).y] = a{:}; toc
tic; [c{1:n}] = b.x; toc
tic; horzcat (a{:}); toc

#now fun...
function fun (varargin)
endfunction
tic; fun (c{:}); toc
tic; size_equal (a{:}); toc

with some older tip, I get:
Elapsed time is 0.772284 seconds.
Elapsed time is 0.76441 seconds.
Elapsed time is 0.25703 seconds.
Elapsed time is 1.31502 seconds.
Elapsed time is 0.249905 seconds.
Elapsed time is 0.687679 seconds.

with the recent patches, I get:
Elapsed time is 0.000152111 seconds.
Elapsed time is 6.91414e-05 seconds.
Elapsed time is 6.60419e-05 seconds.
Elapsed time is 0.899748 seconds.
Elapsed time is 0.03069 seconds.
Elapsed time is 0.191001 seconds.


In particular, you can see that cells can now be cheaply transformed
to struct components and vice versa.
The fourth case is actually realistic - there may be situations when
passing very long argument lists to a function is meaningful. The last
two benchmarks are not really serious, they just illustrate that
significantly less overhead with copying octave_values is now done.

cheers

--
RNDr. Jaroslav Hajek
computing expert
Aeronautical Research and Test Institute (VZLU)
Prague, Czech Republic
url: www.highegg.matfyz.cz
Reply | Threaded
Open this post in threaded view
|

Re: [major] struct array indexing in tip

John W. Eaton
Administrator
On 23-Jan-2009, Jaroslav Hajek wrote:

| The changesets are uploaded. octave_value_list is now implemented
| using Array<octave_value>, allowing data sharing,
| and shallow conversions to and from Cells. I've also dug for various
| unoptimized constructions of octave_value_lists using plain loops and
| replaced them by shallow copying and slicing where possible. Also,

This looks great.  Thanks for working on this.

| I've added const qualifiers to several temporary variables (Cells and
| octave_value_lists) that were unnecessarily forcing copies.

There are probably many more places in Octave that could benefit from
const, so if anyone wants to work on that, I'd be happy to have
changes.

jwe
Reply | Threaded
Open this post in threaded view
|

Re: [major] struct array indexing in tip

Thorsten Meyer
In reply to this post by John W. Eaton
John W. Eaton wrote:
> We need the tests, so I'm hoping someone else will have the time to do
> it.  Anyone?
While generating some tests for structure indexing, I came across this behaviour
  in octave (revision 83b8c739d626):

octave:165> clear
octave:166> b=struct ("name", {"a", "b", "c"; "d", "e", "f"}, "value", 0);
octave:167> b(1, [1,3]).name
ans = a
ans = c
octave:168> [b(1, [1,3]).name] = deal("aa", "cc");
octave:169> b(1, [1,3]).name
ans = aa
ans = cc

this works as I would have expected. However:

octave:173> [b(1:2, [1,3]).name] = deal("aaa", "ddd", "ccc", "fff");
error: A(I,J,...) = X: dimensions mismatch

Is that intended behaviour?

Now trying to nest struct arrays:

octave:173> b(3,1).value = b;
octave:174> b(3,1).value(1, [1,3]).name
ans = aa
ans = cc
octave:175> [b(3,1).value(1, [1,3]).name] = deal("aaa", "ccc")
error: invalid cs-list length in assignment
error: assignment to structure element failed
error: assignment failed, or no method for `struct = cs-list'

Should that work or have I misunderstood something?

regards

Thorsten
Reply | Threaded
Open this post in threaded view
|

Re: [major] struct array indexing in tip

Thorsten Meyer
Hi,

Jaroslav Hajek wrote:
While generating some tests for structure indexing, I came across this behaviour
 in octave (revision 83b8c739d626):

octave:165> clear
octave:166> b=struct ("name", {"a", "b", "c"; "d", "e", "f"}, "value", 0);
octave:167> b(1, [1,3]).name
ans = a
ans = c
octave:168> [b(1, [1,3]).name] = deal("aa", "cc");
octave:169> b(1, [1,3]).name
ans = aa
ans = cc

this works as I would have expected. However:

octave:173> [b(1:2, [1,3]).name] = deal("aaa", "ddd", "ccc", "fff");
error: A(I,J,...) = X: dimensions mismatch

Is that intended behaviour?

    

This stuff never worked correctly, so it's hard to judge by older
versions, but I think that given that cs-lists aren't allowed to have
shape, they should automatically reshape when assigned to lvalues like
a{1:2,3:4}. This following patch should guarantee this.
http://hg.savannah.gnu.org/hgweb/octave/rev/35656d6ad061
  
With my freshly built octave, the above bug is gone. Thanks.
  
Now trying to nest struct arrays:

octave:173> b(3,1).value = b;
octave:174> b(3,1).value(1, [1,3]).name
ans = aa
ans = cc
octave:175> [b(3,1).value(1, [1,3]).name] = deal("aaa", "ccc")
error: invalid cs-list length in assignment
error: assignment to structure element failed
error: assignment failed, or no method for `struct = cs-list'

Should that work or have I misunderstood something?

    

It should work. I don't see this behaviour; can you upgrade to the tip?

  
During the last couple of days I found it really difficult to upgrade to the tip: more patches kept coming in while I was building octave. :-)
Anyway. With octave freshly built this morning I no longer see the above behaviour.

regards

Thorsten
Reply | Threaded
Open this post in threaded view
|

Re: [major] struct array indexing in tip

Thorsten Meyer
In reply to this post by Thorsten Meyer
Hi,

Jaroslav Hajek wrote:
octave:173> [b(1:2, [1,3]).name] = deal("aaa", "ddd", "ccc", "fff");
error: A(I,J,...) = X: dimensions mismatch

Is that intended behaviour?

    

This stuff never worked correctly, so it's hard to judge by older
versions, but I think that given that cs-lists aren't allowed to have
shape, they should automatically reshape when assigned to lvalues like
a{1:2,3:4}. This following patch should guarantee this.
http://hg.savannah.gnu.org/hgweb/octave/rev/35656d6ad061
  
Does that mean, that  [b(1:2, [1,3]).name] internally still has shape? I thought that
     b(1:2, [1,3]).name
is a cs-list without shape, that gets converted to a vector (of some kind of pointers into the b structure array) by the [] operator.

regards

Thorsten
Reply | Threaded
Open this post in threaded view
|

Re: [major] struct array indexing in tip

Jaroslav Hajek-2
On Sun, Jan 25, 2009 at 12:55 PM, Thorsten Meyer <[hidden email]> wrote:

> Hi,
>
> Jaroslav Hajek wrote:
>
> octave:173> [b(1:2, [1,3]).name] = deal("aaa", "ddd", "ccc", "fff");
> error: A(I,J,...) = X: dimensions mismatch
>
> Is that intended behaviour?
>
>
>
> This stuff never worked correctly, so it's hard to judge by older
> versions, but I think that given that cs-lists aren't allowed to have
> shape, they should automatically reshape when assigned to lvalues like
> a{1:2,3:4}. This following patch should guarantee this.
> http://hg.savannah.gnu.org/hgweb/octave/rev/35656d6ad061
>
>
> Does that mean, that  [b(1:2, [1,3]).name] internally still has shape? I
> thought that
>      b(1:2, [1,3]).name
> is a cs-list without shape, that gets converted to a vector (of some kind of
> pointers into the b structure array) by the [] operator.
>
> regards

No, the code doesn't work like that. b(1:2, [1,3]) used as an
expression yields a linear cs-list (see octave_cell::subsref and the
octave_value_list::octave_value_list (const Cell&) constructor).
However, when used on lhs of assignment, the indexing expression do
*not* evaluate to some kind of lists of references or pointers;
instead, the octave_lvalue object retains the index chain and a
pointer to the "root" object. To allow further processing, it also
computes the element count for the expression and eventually evaluates
subscripts with "magic ends" (see tree_index_expression::lvalue).
The assigment handling code (tree_multi_assign::rvalue) builds up all
the lvalue expressions, sums their size (to get proper nargout for
rhs), evaluates rhs, partitions the cs-list yielded by rhs and calls
octave_lvalue::assign, that in turn calls subsasgn on the root object.
In fact, I think it's better than building up an array of references
because this allows us to optimize stuff like [a(1:n).b] = c{:} to be
performed using a shallow copy.
However, the bulk of work is passed down to the (optimized)
Array<T>::assign methods (via the Cell interface), and these respect
the shape matching rules of normal array assignments. That's why the
portion of cs-list needs to be reshaped.

OK, sorry for the technical bogus. The short answer is yes, internally
the expression has shape, but when evaluated, it loses it.

cheers

--
RNDr. Jaroslav Hajek
computing expert
Aeronautical Research and Test Institute (VZLU)
Prague, Czech Republic
url: www.highegg.matfyz.cz
Reply | Threaded
Open this post in threaded view
|

[changeset] Re: [major] struct array indexing in tip

Thorsten Meyer
In reply to this post by Thorsten Meyer
Thorsten Meyer wrote:

> John W. Eaton wrote:
>> | > Maybe someone (other than Jaroslav) would like to generate some tests
>> | > so these problems don't resurface later?
>> | >
>> |
>> | That would be great. I *may* do it eventually myself, but no promise :)
>>
>> We need the tests, so I'm hoping someone else will have the time to do
>> it.  Anyone?
>>
> I would like to learn how to use struct arrays anyway. So I'll try to find time to do it over the
> weekend. Where should they be placed in the source tree?
>
Here is a patch containing a few tests for struct array indexing. What I am not
sure about: is test/test_struct.m the right place to put them?

regards

Thorsten



# HG changeset patch
# User Thorsten Meyer <[hidden email]>
# Date 1232919156 -3600
# Node ID 0863daa9c738a1217b4f4446f7094fc459ffd470
# Parent  79845b1793cf079861840b800a51b1b399b2dd63
Prevent regression of struct array indexing

diff -r 79845b1793cf -r 0863daa9c738 test/ChangeLog
--- a/test/ChangeLog Sun Jan 25 08:04:56 2009 +0100
+++ b/test/ChangeLog Sun Jan 25 22:32:36 2009 +0100
@@ -0,0 +1,4 @@
+2009-01-25  Thorsten Meyer  <thorsten@hexe>
+
+ * test_struct.m: Add struct array tests.
+
diff -r 79845b1793cf -r 0863daa9c738 test/test_struct.m
--- a/test/test_struct.m Sun Jan 25 08:04:56 2009 +0100
+++ b/test/test_struct.m Sun Jan 25 22:32:36 2009 +0100
@@ -109,3 +109,93 @@
 %! s.a = 1;
 %! fail("isstruct (s, 1)","Invalid call to isstruct.*");
 
+## increment element of matrix stored in struct array field
+%!test
+%!  a = struct("c", {[1, 2, 3], [4, 5, 6], [7, 8, 9]});
+%!  a(2).c(3)++;
+%!  assert(a(2).c, [4, 5, 7]);
+
+## create struct array with assignment to cs-list
+%!test
+%!  [a(1:2).x] = deal (1, 3);
+%!  assert(a, struct("x", {1, 3}));
+%!  assert({a(1:2).x}, {1, 3});
+
+## assign to subrange of struct array field
+%!test
+%!  b = struct ("name", {"a", "b", "c"; "d", "e", "f"}, "value", 100);
+%!  [b(1:2, [1,3]).name] = deal("aaa", "ddd", "ccc", "fff");
+%!  assert ({b.name}, {"aaa", "ddd", "b", "e", "ccc", "fff"});
+
+## index into nested struct arrays
+%!test
+%!  a = struct ("name", {"a", "b", "c"; "d", "e", "f"}, "value", 0);
+%!  a(2).value = a;
+%!  assert (a(2).value(2,3).name, "f");
+
+## assign to subrange of field in nested struct array
+%!test
+%!  b = struct ("name", {"a", "b", "c"; "d", "e", "f"}, "value", 0);
+%!  b(3, 1).value = b;
+%!  [b(3, 1).value(1, [1, 3]).name] = deal ("aaa", "ccc");
+%!  assert (size (b), [3, 3]);
+%!  assert (b(3,1).value(1, 3).name, "ccc");
+
+## test 4 dimensional struct array
+%!test
+%!  c(4, 4, 4, 4).name  = "a";
+%!  c(3, 3, 3, 3).value = 1;
+%!  assert (c(2,2,2,2), struct ("name", [], "value", []));
+
+## assign to subrange of field or 4d struct array
+%!test
+%!  c(4, 4, 4, 4).name  = "a";
+%!  c(3, 3, 3, 3).value = 1;
+%!  [c([1, 3], 2, :, [3, 4]).value] = deal (1);
+%!  assert (length(find([c.value] == 1)), 17);
+%!  assert (length(find([c.value])), 17);
+
+## swap elements of struct array
+%!test
+%!  b = struct ("name", {"a", "b", "c"; "d", "e", "f"}, "value", 0);
+%!  [b([2, 1], [3, 1]).name] = deal(b([1, 2], [1, 2]).name);
+%!  assert ({b.name}, {"e", "b", "b", "e", "d", "a"});
+
+## test internal ordering of struct array fields
+%!test
+%!  c(4, 4, 4, 4).value = 3;
+%!  c(1, 2, 3, 4).value = 2;
+%!  c(3, 3, 3, 3).value = 1;
+%!  d = reshape ({c.value}, size(c));
+%!  assert ([d{4, 4, 4, 4}, d{1, 2, 3, 4}, d{3, 3, 3, 3}],
+%!          [3, 2, 1]);
+
+## test assignment to mixed cs-list of field element subranges
+%!test
+%!  b = struct ("name", {"a", "b", "c"; "d", "e", "f"}, "value", 100);
+%!  [b(1:2, [1, 3]).name, b(2, 1:3).value] = ...
+%!    deal (1, 2, 3, 4, "5", "6", "7");
+%!  assert ({b.name}, {1, 2, "b", "e", 3, 4});
+%!  assert ({b.value}, {100, "5", 100, "6", 100, "7"});
+
+%!error <a cs-list cannot be further indexed>
+%!  [a(1:3).x] = deal ([1, 5], [3, 7], [8, 9]);
+%!  [a(2:3).x(2)] = deal (10, 11);
+
+%!error <can't perform indexing operations for cs-list type>
+%!  [a(1:3).x] = deal ([1, 5], [3, 7], [8, 9]);
+%!  a(2:3).x(2);
+
+%!error <Index exceeds matrix dimension>
+%!  a(1).x.x = 1;
+%!  a(2).x;
+
+%!error <invalid number of output arguments for constant expression>
+%!  a = struct ("value", {1, 2, 3, 4, 5});
+%!  [a(2:4).value] = 1;
+
+%!error <invalid assignment to cs-list outside multiple assignment>
+%!  c(4, 4, 4, 4).name  = "a";
+%!  c(3, 3, 3, 3).value = 1;
+%!  c([1, 3], 2, :, [3, 4]).value = 1;
+
Reply | Threaded
Open this post in threaded view
|

Re: [changeset] Re: [major] struct array indexing in tip

Jaroslav Hajek-2
On Sun, Jan 25, 2009 at 10:37 PM, Thorsten Meyer <[hidden email]> wrote:

> Thorsten Meyer wrote:
>> John W. Eaton wrote:
>>> | > Maybe someone (other than Jaroslav) would like to generate some tests
>>> | > so these problems don't resurface later?
>>> | >
>>> |
>>> | That would be great. I *may* do it eventually myself, but no promise :)
>>>
>>> We need the tests, so I'm hoping someone else will have the time to do
>>> it.  Anyone?
>>>
>> I would like to learn how to use struct arrays anyway. So I'll try to find time to do it over the
>> weekend. Where should they be placed in the source tree?
>>
>
> Here is a patch containing a few tests for struct array indexing. What I am not
> sure about: is test/test_struct.m the right place to put them?
>

I think it's OK. Nice job.

cheers

--
RNDr. Jaroslav Hajek
computing expert
Aeronautical Research and Test Institute (VZLU)
Prague, Czech Republic
url: www.highegg.matfyz.cz
Reply | Threaded
Open this post in threaded view
|

[changeset] Re: [major] struct array indexing in tip

John W. Eaton
Administrator
In reply to this post by Thorsten Meyer
On 25-Jan-2009, Thorsten Meyer wrote:

| Thorsten Meyer wrote:
| > John W. Eaton wrote:
| >> | > Maybe someone (other than Jaroslav) would like to generate some tests
| >> | > so these problems don't resurface later?
| >> | >
| >> |
| >> | That would be great. I *may* do it eventually myself, but no promise :)
| >>
| >> We need the tests, so I'm hoping someone else will have the time to do
| >> it.  Anyone?
| >>
| > I would like to learn how to use struct arrays anyway. So I'll try to find time to do it over the
| > weekend. Where should they be placed in the source tree?
| >
|
| Here is a patch containing a few tests for struct array indexing. What I am not
| sure about: is test/test_struct.m the right place to put them?

I think that's OK.  Please commit this change.

Thanks,

jwe
Reply | Threaded
Open this post in threaded view
|

Re: [changeset] Re: [major] struct array indexing in tip

Thorsten Meyer
John W. Eaton wrote:

> On 25-Jan-2009, Thorsten Meyer wrote:
>
> | Thorsten Meyer wrote:
> | > John W. Eaton wrote:
> | >> | > Maybe someone (other than Jaroslav) would like to generate some tests
> | >> | > so these problems don't resurface later?
> | >> | >
> | >> |
> | >> | That would be great. I *may* do it eventually myself, but no promise :)
> | >>
> | >> We need the tests, so I'm hoping someone else will have the time to do
> | >> it.  Anyone?
> | >>
> | > I would like to learn how to use struct arrays anyway. So I'll try to find time to do it over the
> | > weekend. Where should they be placed in the source tree?
> | >
> |
> | Here is a patch containing a few tests for struct array indexing. What I am not
> | sure about: is test/test_struct.m the right place to put them?
>
> I think that's OK.  Please commit this change.
>  
done

Thorsten