@@ -55,7 +55,7 @@ bool inners_share_one_slab(const outer_t& tot) {
5555 return true ;
5656}
5757
58- }
58+ } // namespace
5959
6060BOOST_AUTO_TEST_SUITE (arena_tot_trivial_suite, TA_UT_LABEL_SERIAL)
6161
@@ -88,8 +88,8 @@ BOOST_AUTO_TEST_CASE(add_bit_equal_and_one_slab) {
8888 for (std::size_t ord = 0 ; ord < L.range ().volume (); ++ord) {
8989 inner_t inner ((L.data () + ord)->range ());
9090 for (std::size_t i = 0 ; i < inner.range ().volume (); ++i)
91- inner.at_ordinal (i) = (L. data () + ord)-> at_ordinal (i) +
92- (R.data () + ord)->at_ordinal (i);
91+ inner.at_ordinal (i) =
92+ (L. data () + ord)-> at_ordinal (i) + (R.data () + ord)->at_ordinal (i);
9393 *(baseline.data () + ord) = std::move (inner);
9494 }
9595 BOOST_CHECK (tot_equal (arena_result, baseline));
@@ -104,8 +104,8 @@ BOOST_AUTO_TEST_CASE(subt_bit_equal_and_one_slab) {
104104 for (std::size_t ord = 0 ; ord < L.range ().volume (); ++ord) {
105105 inner_t inner ((L.data () + ord)->range ());
106106 for (std::size_t i = 0 ; i < inner.range ().volume (); ++i)
107- inner.at_ordinal (i) = (L. data () + ord)-> at_ordinal (i) -
108- (R.data () + ord)->at_ordinal (i);
107+ inner.at_ordinal (i) =
108+ (L. data () + ord)-> at_ordinal (i) - (R.data () + ord)->at_ordinal (i);
109109 *(baseline.data () + ord) = std::move (inner);
110110 }
111111 BOOST_CHECK (tot_equal (arena_result, baseline));
@@ -120,8 +120,8 @@ BOOST_AUTO_TEST_CASE(mult_elementwise_bit_equal_and_one_slab) {
120120 for (std::size_t ord = 0 ; ord < L.range ().volume (); ++ord) {
121121 inner_t inner ((L.data () + ord)->range ());
122122 for (std::size_t i = 0 ; i < inner.range ().volume (); ++i)
123- inner.at_ordinal (i) = (L. data () + ord)-> at_ordinal (i) *
124- (R.data () + ord)->at_ordinal (i);
123+ inner.at_ordinal (i) =
124+ (L. data () + ord)-> at_ordinal (i) * (R.data () + ord)->at_ordinal (i);
125125 *(baseline.data () + ord) = std::move (inner);
126126 }
127127 BOOST_CHECK (tot_equal (arena_result, baseline));
@@ -141,4 +141,94 @@ BOOST_AUTO_TEST_CASE(arena_outlives_source) {
141141 (9.0 + ord * 100.0 + i) * 2.0 );
142142}
143143
144+ // --- mismatched null-inner-cell coverage (non-arena inner) ---------------
145+ // Same kernel (arena_trivial_binary) backs Tensor<Tensor<double>>; exercise
146+ // the union-sparsity / implicit-zero path with mismatched per-cell nulls.
147+ // An unassigned outer cell is a default (empty) inner Tensor.
148+
149+ namespace {
150+
151+ // / `present[ord]==false` leaves cell `ord` a null (empty) inner tensor.
152+ outer_t make_tot_sparse (std::size_t N_outer, std::size_t n_inner, double base,
153+ const std::vector<bool >& present) {
154+ outer_t outer (TA::Range{static_cast <long >(N_outer)}, 1 );
155+ for (std::size_t ord = 0 ; ord < N_outer; ++ord) {
156+ if (!present[ord]) continue ; // leave default-constructed -> empty
157+ inner_t inner (TA::Range{static_cast <long >(n_inner)});
158+ for (std::size_t i = 0 ; i < n_inner; ++i)
159+ inner.at_ordinal (i) = base + ord * 100.0 + i;
160+ *(outer.data () + ord) = std::move (inner);
161+ }
162+ return outer;
163+ }
164+
165+ // 0 = lone-left, 1&2 = both, 3 = both-null, 4 = lone-right.
166+ const std::vector<bool > nz_L{true , true , true , false , false };
167+ const std::vector<bool > nz_R{false , true , true , false , true };
168+
169+ } // namespace
170+
171+ BOOST_AUTO_TEST_CASE (add_mismatched_null_inners) {
172+ outer_t L = make_tot_sparse (5 , 4 , 1.0 , nz_L);
173+ outer_t R = make_tot_sparse (5 , 4 , 0.5 , nz_R);
174+ outer_t sum = L.add (R); // must not segfault on lone-left cell 0
175+ for (std::size_t ord = 0 ; ord < 5 ; ++ord) {
176+ const inner_t & l = *(L.data () + ord);
177+ const inner_t & r = *(R.data () + ord);
178+ const inner_t & d = *(sum.data () + ord);
179+ const bool hl = !l.empty (), hr = !r.empty ();
180+ if (!hl && !hr) {
181+ BOOST_CHECK (d.empty ());
182+ } else {
183+ BOOST_REQUIRE (!d.empty ());
184+ for (std::size_t i = 0 ; i < d.range ().volume (); ++i) {
185+ const double lv = hl ? l.at_ordinal (i) : 0.0 ;
186+ const double rv = hr ? r.at_ordinal (i) : 0.0 ;
187+ BOOST_CHECK_EQUAL (d.at_ordinal (i), lv + rv);
188+ }
189+ }
190+ }
191+ }
192+
193+ BOOST_AUTO_TEST_CASE (subt_mismatched_null_inners) {
194+ outer_t L = make_tot_sparse (5 , 4 , 5.0 , nz_L);
195+ outer_t R = make_tot_sparse (5 , 4 , 1.0 , nz_R);
196+ outer_t diff = L.subt (R);
197+ for (std::size_t ord = 0 ; ord < 5 ; ++ord) {
198+ const inner_t & l = *(L.data () + ord);
199+ const inner_t & r = *(R.data () + ord);
200+ const inner_t & d = *(diff.data () + ord);
201+ const bool hl = !l.empty (), hr = !r.empty ();
202+ if (!hl && !hr) {
203+ BOOST_CHECK (d.empty ());
204+ } else {
205+ BOOST_REQUIRE (!d.empty ());
206+ for (std::size_t i = 0 ; i < d.range ().volume (); ++i) {
207+ const double lv = hl ? l.at_ordinal (i) : 0.0 ;
208+ const double rv = hr ? r.at_ordinal (i) : 0.0 ;
209+ BOOST_CHECK_EQUAL (d.at_ordinal (i), lv - rv);
210+ }
211+ }
212+ }
213+ }
214+
215+ BOOST_AUTO_TEST_CASE (mult_mismatched_null_inners) {
216+ outer_t L = make_tot_sparse (5 , 4 , 2.0 , nz_L);
217+ outer_t R = make_tot_sparse (5 , 4 , 0.5 , nz_R);
218+ outer_t prod = L.mult (R);
219+ for (std::size_t ord = 0 ; ord < 5 ; ++ord) {
220+ const inner_t & l = *(L.data () + ord);
221+ const inner_t & r = *(R.data () + ord);
222+ const inner_t & d = *(prod.data () + ord);
223+ if (!l.empty () && !r.empty ()) {
224+ BOOST_REQUIRE (!d.empty ());
225+ for (std::size_t i = 0 ; i < d.range ().volume (); ++i)
226+ BOOST_CHECK_EQUAL (d.at_ordinal (i), l.at_ordinal (i) * r.at_ordinal (i));
227+ } else if (!d.empty ()) {
228+ for (std::size_t i = 0 ; i < d.range ().volume (); ++i)
229+ BOOST_CHECK_EQUAL (d.at_ordinal (i), 0.0 );
230+ }
231+ }
232+ }
233+
144234BOOST_AUTO_TEST_SUITE_END ()
0 commit comments