Repository: invenia/NamedDims.jl
Pull request #72 (2019-10-03 17:00:41)

sum!, prod! etc
These give an error if names don’t match. I guess the other option would be to permute things to make it work if possible.

codecov[bot]: # [Codecov](https://codecov.io/gh/invenia/NamedDims.jl/pull/72?src=pr&el=h1) Report
> Merging [#72](https://codecov.io/gh/invenia/NamedDims.jl/pull/72?src=pr&el=desc) into [master](https://codecov.io/gh/invenia/NamedDims.jl/commit/2f355d7ac11f1e9bee4ecfe35a57eab74363be2a?src=pr&el=desc) will **increase** coverage by `0.28%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/invenia/NamedDims.jl/pull/72/graphs/tree.svg?width=650&token=M5MW3xa0xM&height=150&src=pr)](https://codecov.io/gh/invenia/NamedDims.jl/pull/72?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #72      +/-   ##
==========================================
+ Coverage   86.72%   87.01%   +0.28%     
==========================================
  Files           8        8              
  Lines         226      231       +5     
==========================================
+ Hits          196      201       +5     
  Misses         30       30
```


| [Impacted Files](https://codecov.io/gh/invenia/NamedDims.jl/pull/72?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/functions.jl](https://codecov.io/gh/invenia/NamedDims.jl/pull/72/diff?src=pr&el=tree#diff-c3JjL2Z1bmN0aW9ucy5qbA==) | `97.05% <100%> (+0.5%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/invenia/NamedDims.jl/pull/72?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/invenia/NamedDims.jl/pull/72?src=pr&el=footer). Last update [2f355d7...3cfa1fd](https://codecov.io/gh/invenia/NamedDims.jl/pull/72?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/functions.jl b/src/functions.jl
index 48d31eb..2a01374 100644
--- a/src/functions.jl
+++ b/src/functions.jl
@@ -102,6 +102,34 @@ for (mod, funs) in (
     end
 end
 
+# Two arrays
+for (mod, funs) in (
+    (:Base, (:sum!, :prod!, :maximum!, :minimum!)),
+)
+    for fun in funs
+        @eval begin
+
+            function $mod.$fun(a::NamedDimsArray{L}, b::AbstractArray) where L
+                data = $mod.$fun(parent(a), b)
+                return NamedDimsArray{L}(data)
+            end
+
+            function $mod.$fun(a::AbstractArray, b::NamedDimsArray{L}) where L
+                data = $mod.$fun(a, parent(b))
+                newL = unify_names_shortest(L, ntuple(_ -> :_, ndims(a)))
+                return NamedDimsArray{newL}(data)
+            end
+
+            function $mod.$fun(a::NamedDimsArray{La}, b::NamedDimsArray{Lb}) where {La, Lb}
+                newL = unify_names_shortest(La, Lb)
+                data = $mod.$fun(parent(a), parent(b))
+                return NamedDimsArray{newL}(data)
+            end
+
+        end
+    end
+end
+
 Base.pop!(A::NamedDimsArray) = pop!(parent(A))
 Base.popfirst!(A::NamedDimsArray) = popfirst!(parent(A))
 
diff --git a/src/name_core.jl b/src/name_core.jl
index 3af6790..f575a61 100644
--- a/src/name_core.jl
+++ b/src/name_core.jl
@@ -215,6 +215,26 @@ function unify_names_longest(names_a, names_b)
     return compile_time_return_hack(ret)
 end
 
+unify_names_shortest(names, ::Tuple{}) = ()
+unify_names_shortest(::Tuple{}, names) = ()
+unify_names_shortest(::Tuple{}, ::Tuple{}) = ()
+function unify_names_shortest(names_a, names_b)
+    # 0 Allocations: @btime (()-> unify_names_shortest((:a,:b), (:a,)))()
+
+    length(names_a) == length(names_b) && return unify_names(names_a, names_b)
+    long, short = length(names_a) > length(names_b) ? (names_a, names_b) : (names_b, names_a)
+    ret = ntuple(length(short)) do ii
+        a = getfield(long, ii)
+        b = getfield(short, ii)
+        a === :_ && return b
+        b === :_ && return a
+        a === b && return a
+        return false  # mismatch occured, we mark this with a nonSymbol result
+    end
+    ret isa Tuple{Vararg{Symbol}} || incompatible_dimension_error(names_a, names_b)
+    return compile_time_return_hack(ret)
+end
+
 # The following are helpers for remaining_dimnames_from_indexing
 # as a generated function it can get unhappy if asked to use anon functions
 # and it can only call function declared before it. So we declare them explictly here.


Test patch:
diff --git a/test/functions.jl b/test/functions.jl
index fdc77ca..d9050e3 100644
--- a/test/functions.jl
+++ b/test/functions.jl
@@ -44,6 +44,35 @@ using Statistics
         end
     end
 
+    @testset "$f!" for (f,f!) in zip((sum, prod, maximum, minimum), (sum!, prod!, maximum!, minimum!))
+        a = [10 20; 31 40]
+        nda = NamedDimsArray(a, (:x, :y)) # size (2,2)
+
+        nda1 = sum(nda, dims=1)           # size (1,2)
+        nda2 = sum(nda, dims=2)           # size (2,1)
+        @testset "ndims==2" begin
+            @test f!(nda1, nda) == f!(nda1, a) == f(a, dims=1)
+            @test f!(nda2, nda) == f!(nda2, a) == f(a, dims=2)
+
+            @test NamedDims.names(f!(nda1, nda)) == (:x, :y) == NamedDims.names(f!(nda1, a))
+            @test NamedDims.names(f!(nda2, nda)) == (:x, :y) == NamedDims.names(f!(nda2, a))
+
+            @test_throws DimensionMismatch f!(nda1, transpose(nda)) # names wrong way around
+        end
+        @testset "ndims==1 too" begin
+            ndx = NamedDimsArray([3,4], :x)
+            ndy = NamedDimsArray([5,6], :y)
+            nd_ = NamedDimsArray([7,8], :_)
+
+            @test f!(ndx, nda) == f!([0,0], nda) == dropdims(f(a, dims=2), dims=2)
+            @test f!(nd_, nda) == f!(ndx, a)
+
+            @test f!(ndy', nda) == f!([0 0], nda) == f(a, dims=1)
+
+            @test_throws DimensionMismatch f!(ndy, nda) # name y on wrong dimension
+        end
+    end
+
     @testset "eachslice" begin
         if VERSION > v"1.1-"
             slices = [[111 121; 211 221], [112 122; 212 222]]
@@ -134,8 +163,8 @@ using Statistics
         nda = NamedDimsArray(a, (:x, :y))
 
         @test count(nda) == count(a) == 3
-        @test_throws ErrorException count(nda; dims=:x)
-        @test_throws ErrorException count(a; dims=1)
+        @test_throws Exception count(nda; dims=:x)
+        @test_throws Exception count(a; dims=1)
     end
 
     @testset "push!, pop!, etc" begin
@@ -163,7 +192,7 @@ using Statistics
 
         @test names(empty!(ndv)) == (:i,)
         @test length(ndv) == 0
-end
+    end
 
 end  # Base
 
diff --git a/test/name_core.jl b/test/name_core.jl
index eb3cde0..ba07da2 100644
--- a/test/name_core.jl
+++ b/test/name_core.jl
@@ -3,6 +3,7 @@ using NamedDims:
     names,
     unify_names,
     unify_names_longest,
+    unify_names_shortest,
     order_named_inds,
     permute_dimnames,
     remaining_dimnames_from_indexing,
@@ -31,9 +32,13 @@ end
     @test unify_names_longest((:a,), (:a, :_)) == (:a, :_)
     @test unify_names_longest((:a, :b), (:a, :_, :c)) == (:a, :b, :c)
 
+    @test unify_names_shortest((:a,), (:a, :b,)) == (:a,)
+    @test unify_names_shortest((:a,), (:a, :_)) == (:a,)
+    @test unify_names_shortest((:a, :b), (:a, :_, :c)) == (:a, :b)
+
     @test_throws DimensionMismatch unify_names_longest((:a, :b, :c), (:b, :a))
 
-    for unify in (unify_names, unify_names_longest)
+    for unify in (unify_names, unify_names_longest, unify_names_shortest)
         @test unify((:a,), (:a,)) == (:a,)
         @test unify((:a, :b), (:a, :b)) == (:a, :b)
         @test unify((:a, :_), (:a, :b)) == (:a, :b)