Dear all,

In swift (just as in many other languages) I have been terribly

missing the operators like .* ./ .^ as I know them from

MATLAB/Scilab. These operators are very handy and do element-wise

operations on vectors or matrices of the same size.

So for instance A*B is a matrix multiplication (and the number of

columns for A must correspond to the number of rows in B), whereas A*B

(with A and B of same size) returns the matrix of that size whose

elements are obtained by making the product of each pair of elements

at the same location in A and B.

So just a small example:

[1.0 , 2.5 , 3.0] .* [2.0 , 5.0 , -1.0] -> [2.0 , 12.5 , -3.0]

The same exists for the division (./) or for instance for the power

function (.^). Here another example with *, .* , ^ , and .^ to show

the difference in behaviour in MATLAB/Scilab

A = [1 2 3 ; 4 5 6 ; 7 8 9];

A*A

ans =

30 36 42

66 81 96

102 126 150

A.*A

ans =

1 4 9

16 25 36

49 64 81

A^2

ans =

30 36 42

66 81 96

102 126 150

A.^3

ans =

1 8 27

64 125 216

343 512 729

For addition and subtraction the regular operator (+ and -) and their

counterparts (.+ and .-) are actually doing the same. However note

that since the + operator on arrays is defined differently (it does an

append operation), there is a clear use for a .+ operation in swift.

Version 1:

In principle, we can define it recursively, for instance ...+ would be

the element-wise application of the ..+ operator, which is itself the

element-wise application of the .+ operator, which is also the

element-wise application of the + operator.

Version 2:

Alternatively we could have a concept where .+ is the element-wise

application of the .+ operator and finally when reaching the basic

type (e.g. Double when starting from [[[[Double]]]]) the .+ operator

needs to be defined as identical to the + operator. I do prefer this

version since it does not need to define various operators depending

on the "level" (i.e. Double -> level 0, [Double] -> level 1,

[[Double]] -> level 2, etc.). I could make this option work without

generics, but as I tried it with generics it generated a runtime error

as the call stack grew indefinitely (which does not seem as something

that should actually happen since at each call the level gets lower

and when reaching 0 it all solvable).

Anyway, I would like to discuss first the basic idea of defining these

element-wise operators for Arrays, before seeing how far it would be

interesting to go on this and how the implementation should exactly

look like. As a support for the discussion, you will find hereunder a

first shot for a generics-based solution for the aforementioned

Version 1 and going up to level 3 and for the 4 basic operators + - *

/

(BTW you can see that I have twice the same code, once with the

protocol conformance to my own protocols and once for FloatingPoint:

is there a way to specific the protocol conformance to protocol A or

to protocol B at once?)

I personally think that these operators are very practical and helping

programmers to directly "vectorize" the way they write their

operations. Often these element-wise operations replace loops and I

think that the required syntax analysis (on compiler's side) to

vectorize the code is much simpler then. In swift, I have been using

map, flatMap, zip + map with a closure to make these type of

operations, but I think that the proposed operators would be a much

clearer and expressive way of coding this for most basic operations.

Note that I mention and consider only Arrays here, but the idea might

be extended to other collections/containers.

I am very curious to see the feedback of the community on this!

Nicolas

infix operator .+

infix operator ..+

infix operator ...+

infix operator .-

infix operator ..-

infix operator ...-

infix operator .*

infix operator ..*

infix operator ...*

infix operator ./

infix operator ../

infix operator .../

protocol ImplementsInnerAddition { static func + (_: Self,_: Self)->Self }

protocol ImplementsInnerSubtraction { static func - (_: Self,_: Self)->Self }

protocol ImplementsInnerMultiplication { static func * (_: Self,_: Self)->Self }

protocol ImplementsInnerDivision { static func / (_: Self,_: Self)->Self }

func .+<T> (lhs: [T], rhs: [T]) -> [T] where T:ImplementsInnerAddition {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

T,b: T)->T in return a + b }) }

guard (lhs.count != 1) else { return rhs.map({(b: T)->T

in return lhs[0] + b }) }

guard (rhs.count != 1) else { return lhs.map({(a: T)->T

in return a + rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func .+<T> (lhs: [T], rhs: T ) -> [T] where T:ImplementsInnerAddition

{ return lhs.map({(a: T)->T in return a + rhs }) }

func .+<T> (lhs: T , rhs: [T]) -> [T] where T:ImplementsInnerAddition

{ return rhs.map({(b: T)->T in return lhs + b }) }

func ..+<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:ImplementsInnerAddition {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[T],b: [T])->[T] in return a .+ b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[T])->[T] in return lhs[0] .+ b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[T])->[T] in return a .+ rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ..+<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where

T:ImplementsInnerAddition { return lhs.map({(a: [T])->[T] in return

a .+ rhs }) }

func ..+<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where

T:ImplementsInnerAddition { return rhs.map({(b: [T])->[T] in return

lhs .+ b }) }

func ...+<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where

T:ImplementsInnerAddition {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[[T]],b: [[T]])->[[T]] in return a ..+ b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[[T]])->[[T]] in return lhs[0] ..+ b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[[T]])->[[T]] in return a ..+ rhs[0] })

}

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ...+<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where

T:ImplementsInnerAddition { return lhs.map({(a: [[T]])->[[T]] in

return a ..+ rhs }) }

func ...+<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where

T:ImplementsInnerAddition { return rhs.map({(b: [[T]])->[[T]] in

return lhs ..+ b }) }

func .-<T> (lhs: [T], rhs: [T]) -> [T] where T:ImplementsInnerSubtraction {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

T,b: T)->T in return a - b }) }

guard (lhs.count != 1) else { return rhs.map({(b: T)->T

in return lhs[0] - b }) }

guard (rhs.count != 1) else { return lhs.map({(a: T)->T

in return a - rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func .-<T> (lhs: [T], rhs: T ) -> [T] where

T:ImplementsInnerSubtraction { return lhs.map({(a: T)->T in return a

- rhs }) }

func .-<T> (lhs: T , rhs: [T]) -> [T] where

T:ImplementsInnerSubtraction { return rhs.map({(b: T)->T in return

lhs - b }) }

func ..-<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where

T:ImplementsInnerSubtraction {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[T],b: [T])->[T] in return a .- b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[T])->[T] in return lhs[0] .- b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[T])->[T] in return a .- rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ..-<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where

T:ImplementsInnerSubtraction { return lhs.map({(a: [T])->[T] in

return a .- rhs }) }

func ..-<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where

T:ImplementsInnerSubtraction { return rhs.map({(b: [T])->[T] in

return lhs .- b }) }

func ...-<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where

T:ImplementsInnerSubtraction {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[[T]],b: [[T]])->[[T]] in return a ..- b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[[T]])->[[T]] in return lhs[0] ..- b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[[T]])->[[T]] in return a ..- rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ...-<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where

T:ImplementsInnerSubtraction { return lhs.map({(a: [[T]])->[[T]] in

return a ..- rhs }) }

func ...-<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where

T:ImplementsInnerSubtraction { return rhs.map({(b: [[T]])->[[T]] in

return lhs ..- b }) }

func .*<T> (lhs: [T], rhs: [T]) -> [T] where T:ImplementsInnerMultiplication {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

T,b: T)->T in return a * b }) }

guard (lhs.count != 1) else { return rhs.map({(b: T)->T

in return lhs[0] * b }) }

guard (rhs.count != 1) else { return lhs.map({(a: T)->T

in return a * rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func .*<T> (lhs: [T], rhs: T ) -> [T] where

T:ImplementsInnerMultiplication { return lhs.map({(a: T)->T in return

a * rhs }) }

func .*<T> (lhs: T , rhs: [T]) -> [T] where

T:ImplementsInnerMultiplication { return rhs.map({(b: T)->T in return

lhs * b }) }

func ..*<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where

T:ImplementsInnerMultiplication {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[T],b: [T])->[T] in return a .* b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[T])->[T] in return lhs[0] .* b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[T])->[T] in return a .* rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ..*<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where

T:ImplementsInnerMultiplication { return lhs.map({(a: [T])->[T] in

return a .* rhs }) }

func ..*<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where

T:ImplementsInnerMultiplication { return rhs.map({(b: [T])->[T] in

return lhs .* b }) }

func ...*<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where

T:ImplementsInnerMultiplication {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[[T]],b: [[T]])->[[T]] in return a ..* b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[[T]])->[[T]] in return lhs[0] ..* b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[[T]])->[[T]] in return a ..* rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ...*<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where

T:ImplementsInnerMultiplication { return lhs.map({(a: [[T]])->[[T]]

in return a ..* rhs }) }

func ...*<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where

T:ImplementsInnerMultiplication { return rhs.map({(b: [[T]])->[[T]]

in return lhs ..* b }) }

func ./<T> (lhs: [T], rhs: [T]) -> [T] where T:ImplementsInnerDivision {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

T,b: T)->T in return a / b }) }

guard (lhs.count != 1) else { return rhs.map({(b: T)->T

in return lhs[0] / b }) }

guard (rhs.count != 1) else { return lhs.map({(a: T)->T

in return a / rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ./<T> (lhs: [T], rhs: T ) -> [T] where T:ImplementsInnerDivision

{ return lhs.map({(a: T)->T in return a / rhs }) }

func ./<T> (lhs: T , rhs: [T]) -> [T] where T:ImplementsInnerDivision

{ return rhs.map({(b: T)->T in return lhs / b }) }

func ../<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:ImplementsInnerDivision {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[T],b: [T])->[T] in return a ./ b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[T])->[T] in return lhs[0] ./ b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[T])->[T] in return a ./ rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ../<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where

T:ImplementsInnerDivision { return lhs.map({(a: [T])->[T] in return

a ./ rhs }) }

func ../<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where

T:ImplementsInnerDivision { return rhs.map({(b: [T])->[T] in return

lhs ./ b }) }

func .../<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where

T:ImplementsInnerDivision {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[[T]],b: [[T]])->[[T]] in return a ../ b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[[T]])->[[T]] in return lhs[0] ../ b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[[T]])->[[T]] in return a ../ rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func .../<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where

T:ImplementsInnerDivision { return lhs.map({(a: [[T]])->[[T]] in

return a ../ rhs }) }

func .../<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where

T:ImplementsInnerDivision { return rhs.map({(b: [[T]])->[[T]] in

return lhs ../ b }) }

func .+<T> (lhs: [T], rhs: [T]) -> [T] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

T,b: T)->T in return a + b }) }

guard (lhs.count != 1) else { return rhs.map({(b: T)->T

in return lhs[0] + b }) }

guard (rhs.count != 1) else { return lhs.map({(a: T)->T

in return a + rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func .+<T> (lhs: [T], rhs: T ) -> [T] where T:FloatingPoint { return

lhs.map({(a: T)->T in return a + rhs }) }

func .+<T> (lhs: T , rhs: [T]) -> [T] where T:FloatingPoint { return

rhs.map({(b: T)->T in return lhs + b }) }

func ..+<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[T],b: [T])->[T] in return a .+ b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[T])->[T] in return lhs[0] .+ b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[T])->[T] in return a .+ rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ..+<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:FloatingPoint {

return lhs.map({(a: [T])->[T] in return a .+ rhs }) }

func ..+<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:FloatingPoint {

return rhs.map({(b: [T])->[T] in return lhs .+ b }) }

func ...+<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[[T]],b: [[T]])->[[T]] in return a ..+ b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[[T]])->[[T]] in return lhs[0] ..+ b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[[T]])->[[T]] in return a ..+ rhs[0] })

}

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ...+<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where

T:FloatingPoint { return lhs.map({(a: [[T]])->[[T]] in return a ..+

rhs }) }

func ...+<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where

T:FloatingPoint { return rhs.map({(b: [[T]])->[[T]] in return lhs ..+

b }) }

func .-<T> (lhs: [T], rhs: [T]) -> [T] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

T,b: T)->T in return a - b }) }

guard (lhs.count != 1) else { return rhs.map({(b: T)->T

in return lhs[0] - b }) }

guard (rhs.count != 1) else { return lhs.map({(a: T)->T

in return a - rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func .-<T> (lhs: [T], rhs: T ) -> [T] where T:FloatingPoint { return

lhs.map({(a: T)->T in return a - rhs }) }

func .-<T> (lhs: T , rhs: [T]) -> [T] where T:FloatingPoint { return

rhs.map({(b: T)->T in return lhs - b }) }

func ..-<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[T],b: [T])->[T] in return a .- b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[T])->[T] in return lhs[0] .- b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[T])->[T] in return a .- rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ..-<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:FloatingPoint {

return lhs.map({(a: [T])->[T] in return a .- rhs }) }

func ..-<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:FloatingPoint {

return rhs.map({(b: [T])->[T] in return lhs .- b }) }

func ...-<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[[T]],b: [[T]])->[[T]] in return a ..- b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[[T]])->[[T]] in return lhs[0] ..- b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[[T]])->[[T]] in return a ..- rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ...-<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where

T:FloatingPoint { return lhs.map({(a: [[T]])->[[T]] in return a ..-

rhs }) }

func ...-<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where

T:FloatingPoint { return rhs.map({(b: [[T]])->[[T]] in return lhs ..-

b }) }

func .*<T> (lhs: [T], rhs: [T]) -> [T] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

T,b: T)->T in return a * b }) }

guard (lhs.count != 1) else { return rhs.map({(b: T)->T

in return lhs[0] * b }) }

guard (rhs.count != 1) else { return lhs.map({(a: T)->T

in return a * rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func .*<T> (lhs: [T], rhs: T ) -> [T] where T:FloatingPoint { return

lhs.map({(a: T)->T in return a * rhs }) }

func .*<T> (lhs: T , rhs: [T]) -> [T] where T:FloatingPoint { return

rhs.map({(b: T)->T in return lhs * b }) }

func ..*<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[T],b: [T])->[T] in return a .* b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[T])->[T] in return lhs[0] .* b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[T])->[T] in return a .* rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ..*<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:FloatingPoint {

return lhs.map({(a: [T])->[T] in return a .* rhs }) }

func ..*<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:FloatingPoint {

return rhs.map({(b: [T])->[T] in return lhs .* b }) }

func ...*<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[[T]],b: [[T]])->[[T]] in return a ..* b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[[T]])->[[T]] in return lhs[0] ..* b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[[T]])->[[T]] in return a ..* rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ...*<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where

T:FloatingPoint { return lhs.map({(a: [[T]])->[[T]] in return a ..*

rhs }) }

func ...*<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where

T:FloatingPoint { return rhs.map({(b: [[T]])->[[T]] in return lhs ..*

b }) }

func ./<T> (lhs: [T], rhs: [T]) -> [T] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

T,b: T)->T in return a / b }) }

guard (lhs.count != 1) else { return rhs.map({(b: T)->T

in return lhs[0] / b }) }

guard (rhs.count != 1) else { return lhs.map({(a: T)->T

in return a / rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ./<T> (lhs: [T], rhs: T ) -> [T] where T:FloatingPoint { return

lhs.map({(a: T)->T in return a / rhs }) }

func ./<T> (lhs: T , rhs: [T]) -> [T] where T:FloatingPoint { return

rhs.map({(b: T)->T in return lhs / b }) }

func ../<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[T],b: [T])->[T] in return a ./ b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[T])->[T] in return lhs[0] ./ b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[T])->[T] in return a ./ rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func ../<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:FloatingPoint {

return lhs.map({(a: [T])->[T] in return a ./ rhs }) }

func ../<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:FloatingPoint {

return rhs.map({(b: [T])->[T] in return lhs ./ b }) }

func .../<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint {

guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a:

[[T]],b: [[T]])->[[T]] in return a ../ b }) }

guard (lhs.count != 1) else { return rhs.map({(b:

[[T]])->[[T]] in return lhs[0] ../ b }) }

guard (rhs.count != 1) else { return lhs.map({(a:

[[T]])->[[T]] in return a ../ rhs[0] }) }

assert(false,"Element-wise operation can only be applied to arrays

of same size or alternatively if one of the array is of size

1",file:#file,line:#line)

}

func .../<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where

T:FloatingPoint { return lhs.map({(a: [[T]])->[[T]] in return a ../

rhs }) }

func .../<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where

T:FloatingPoint { return rhs.map({(b: [[T]])->[[T]] in return lhs ../

b }) }