Actually, one steam can be infinite, but it should be left-hand one for the expected results.

```
(0..2) +.x [0, 10] // -> [ 0, 10, 1, 11, 2, 12 ]
(Pseries(0, 1) +.x Pseq([0, 10])).asStream.nextN(6)
// -> [ 0, 10, 1, 11, 2, 12 ]
(Pseq([0, 10]) +.x Pseries(0, 1)).asStream.nextN(6)
// -> [ 0, 1, 2, 3, 4, 5 ]
```

The even more confusing behavior is when you apply it to a combo of an array and an infinite stream

```
(Pseries(0, 1) +.x [0, 10]).asStream.nextN(3)
// -> [ [ 0, 10 ], [ 0, 10 ], [ 0, 10 ] ]
([0, 10] +.x Pseries(0, 1)).asStream.nextN(3)
// -> [ [ 0, 10 ], [ 1, 11 ], [ 2, 12 ] ]
```

You can [P]flatten the latter for the desired result…

```
([0, 10] +.x Pseries(0, 1)).flatten.asStream.nextN(6)
// -> [ 0, 10, 1, 11, 2, 12 ]
```

but there’s the confusing usability issue that a finite Pseq needs to go “on the right”, but a (finite, obvious) array needs to “go on the left” . Also, in the latter case, since you have to re-flatten, it’s no different than using a plain ‘+’

```
([0, 10] + Pseries(0, 1)).flatten.asStream.nextN(6)
// -> [ 0, 10, 1, 11, 2, 12 ]
```

There’s a special implementation for cross adverbs in `BinaryOpXStream`

: when the right-hand side stream yields `nil`

, it gets `reset`

. (Cross adverbs are detected by `Pbinop.asStream`

which uses BinaryOpXStream for the latter instead of BinaryOpStream.) That implementation makes it a non-commutative operation, but then so is its finite counterpart on arrays… BinaryOpXStream also works more like a flatMap in Java etc. As “proof”:

```
(0..2) +.x [0, 10] // same result as next line
(0..2).collect({|a| a +.x [0, 10]}).flatten // -> [ 0, 10, 1, 11, 2, 12 ]
// so, "patternizing" that idea
Pflatten(1, Pcollect({|a| a +.x [0, 10]}, Pseries(0, 1))).asStream.nextN(6) // or
Pcollect({|a| a +.x [0, 10]}, Pseries(0, 1)).flatten.asStream.nextN(6) // or
Pseries(0, 1).collect({|a| a +.x [0, 10]}).flatten.asStream.nextN(6)
// -> [ 0, 10, 1, 11, 2, 12 ]
```

Because of the wrapping behavior of adding a single scalar to an array, that’s the same as

```
(0..2).collect({|a| a + [0, 10]}).flatten //-> [ 0, 10, 1, 11, 2, 12 ]; also
Pflatten(1, Pcollect({|a| a + [0, 10]}, Pseries(0, 1))).asStream.nextN(6) // etc.
```

But in the more general case where Pseries returned an array, there’s a difference where you flatten, or equivalently commuting the arguments of `+.x`

(on an array), i.e row vs column “major order”, e.g.

```
[0, 10] +.x [0, 0.1] // -> [ 0, 0.1, 10, 10.1 ]
[0, 0.1] +.x [0, 10] // -> [ 0, 10, 0.1, 10.1 ]
(Pseries([0, 0.1], 1) +.x Pseq([0, 10])).flatten.asStream.nextN(12) // same as
Pcollect({|a| [0, 10] +.x a}, Pseries([0, 0.1], 1)).flatten.asStream.nextN(12)
// -> [ 0, 0.1, 10, 10.1, 1, 1.1, 11, 11.1, 2, 2.1, 12, 12.1 ]
(Pseries([0, 0.1], 1).flatten +.x Pseq([0, 10])).asStream.nextN(12) // same as
Pcollect({|a| a +.x [0, 10]}, Pseries([0, 0.1], 1)).flatten.asStream.nextN(12)
// -> [ 0, 10, 0.1, 10.1, 1, 11, 1.1, 11.1, 2, 12, 2.1, 12.1 ]
```

N.B., `*.x`

is mostly called an “outer” rather than “cross” product/operation is most non-SC contexts. Actually, “Kronecker product” is probably more appropriate since the result of `.x`

is flattened. The `.t`

adverb in SC gives the true (unflattened) outer product.