Full Code of JuliaArrays/FillArrays.jl for AI

master 27bf96383e9e cached
28 files
239.9 KB
85.4k tokens
1 requests
Download .txt
Showing preview only (252K chars total). Download the full file or copy to clipboard to get everything.
Repository: JuliaArrays/FillArrays.jl
Branch: master
Commit: 27bf96383e9e
Files: 28
Total size: 239.9 KB

Directory structure:
gitextract_tzhpayfy/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── CompatHelper.yml
│       ├── Invalidations.yml
│       ├── TagBot.yml
│       ├── ci.yml
│       ├── docs.yml
│       └── downstream.yml
├── .gitignore
├── LICENSE
├── Project.toml
├── README.md
├── appveyor.yml
├── benchmark/
│   └── benchmarks.jl
├── docs/
│   ├── Project.toml
│   ├── make.jl
│   └── src/
│       └── index.md
├── ext/
│   ├── FillArraysPDMatsExt.jl
│   ├── FillArraysSparseArraysExt.jl
│   ├── FillArraysStaticArraysExt.jl
│   └── FillArraysStatisticsExt.jl
├── src/
│   ├── FillArrays.jl
│   ├── fillalgebra.jl
│   ├── fillbroadcast.jl
│   ├── oneelement.jl
│   └── trues.jl
└── test/
    ├── aqua.jl
    ├── infinitearrays.jl
    └── runtests.jl

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/" # Location of package manifests
    schedule:
      interval: "weekly"


================================================
FILE: .github/workflows/CompatHelper.yml
================================================
name: CompatHelper
on:
  schedule:
    - cron: 0 0 * * *
  workflow_dispatch:
permissions:
  contents: write
  pull-requests: write
jobs:
  CompatHelper:
    runs-on: ubuntu-latest
    steps:
      - name: Check if Julia is already available in the PATH
        id: julia_in_path
        run: which julia
        continue-on-error: true
      - name: Install Julia, but only if it is not already available in the PATH
        uses: julia-actions/setup-julia@v3
        with:
          version: '1'
          arch: ${{ runner.arch }}
        if: steps.julia_in_path.outcome != 'success'
      - name: "Add the General registry via Git"
        run: |
          import Pkg
          ENV["JULIA_PKG_SERVER"] = ""
          Pkg.Registry.add("General")
        shell: julia --color=yes {0}
      - name: "Install CompatHelper"
        run: |
          import Pkg
          name = "CompatHelper"
          uuid = "aa819f21-2bde-4658-8897-bab36330d9b7"
          version = "3"
          Pkg.add(; name, uuid, version)
        shell: julia --color=yes {0}
      - name: "Run CompatHelper"
        run: |
          import CompatHelper
          CompatHelper.main()
        shell: julia --color=yes {0}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }}
          COMPATHELPER_PRIV: ${{ secrets.COMPATHELPER_PRIV }}


================================================
FILE: .github/workflows/Invalidations.yml
================================================
name: Invalidations

on:
  pull_request:
    paths-ignore:
      - 'LICENSE'
      - 'README.md'
      - '.github/workflows/TagBot.yml'

concurrency:
  # Skip intermediate builds: always.
  # Cancel intermediate builds: always.
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  evaluate:
    # Only run on PRs to the default branch.
    # In the PR trigger above branches can be specified only explicitly whereas this check should work for master, main, or any other default branch
    if: github.base_ref == github.event.repository.default_branch
    runs-on: ubuntu-latest
    steps:
    - uses: julia-actions/setup-julia@v3
      with:
        version: '1.10'
    - uses: actions/checkout@v6
    - uses: julia-actions/julia-buildpkg@v1
    - uses: julia-actions/julia-invalidations@v1
      id: invs_pr

    - uses: actions/checkout@v6
      with:
        ref: ${{ github.event.repository.default_branch }}
    - uses: julia-actions/julia-buildpkg@v1
    - uses: julia-actions/julia-invalidations@v1
      id: invs_default

    - name: Report invalidation counts
      run: |
        echo "Invalidations on default branch: ${{ steps.invs_default.outputs.total }} (${{ steps.invs_default.outputs.deps }} via deps)" >> $GITHUB_STEP_SUMMARY
        echo "This branch: ${{ steps.invs_pr.outputs.total }} (${{ steps.invs_pr.outputs.deps }} via deps)" >> $GITHUB_STEP_SUMMARY
    - name: Check if the PR does increase number of invalidations
      if: steps.invs_pr.outputs.total > steps.invs_default.outputs.total
      run: exit 1


================================================
FILE: .github/workflows/TagBot.yml
================================================
name: TagBot
on:
  issue_comment:
    types:
      - created
  workflow_dispatch:
    inputs:
      lookback:
        default: 3
permissions:
  contents: write
jobs:
  TagBot:
    if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
    runs-on: ubuntu-latest
    steps:
      - uses: JuliaRegistries/TagBot@v1
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          ssh: ${{ secrets.DOCUMENTER_KEY }}


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
  push:
    branches:
      - master
    tags: [v*]
    paths-ignore:
      - 'LICENSE'
      - 'README.md'
      - '.github/workflows/TagBot.yml'
  pull_request:
    paths-ignore:
      - 'LICENSE'
      - 'README.md'
      - '.github/workflows/TagBot.yml'


concurrency:
  group: build-${{ github.event.pull_request.number || github.ref }}-${{ github.workflow }}
  cancel-in-progress: true

jobs:
  pre_job:
    # continue-on-error: true # Uncomment once integration is finished
    runs-on: ubuntu-latest
    # Map a step output to a job output
    outputs:
      should_skip: ${{ steps.skip_check.outputs.should_skip }}
    steps:
      - id: skip_check
        uses: fkirc/skip-duplicate-actions@v5
  test:
    needs: pre_job
    if: needs.pre_job.outputs.should_skip != 'true'
    name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ github.event_name }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        version:
          - 'min'
          - 'lts'
          - '1'
          - 'pre'
        os:
          - ubuntu-latest
          - macOS-latest
          - windows-latest
    steps:
      - uses: actions/checkout@v6
      - uses: julia-actions/setup-julia@v3
        with:
          version: ${{ matrix.version }}
      - uses: julia-actions/cache@v3
      - uses: julia-actions/julia-buildpkg@v1
      - uses: julia-actions/julia-runtest@v1
      - uses: julia-actions/julia-processcoverage@v1
      - uses: codecov/codecov-action@v6
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: lcov.info


================================================
FILE: .github/workflows/docs.yml
================================================
name: Documentation

on:
  pull_request:
    paths-ignore:
      - 'LICENSE'
      - 'README.md'
      - '.github/workflows/TagBot.yml'
  push:
    branches:
      - 'master'
      - 'release-'
    tags: '*'
    paths-ignore:
      - 'LICENSE'
      - 'README.md'
      - '.github/workflows/TagBot.yml'
  release:
    types: [published]

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        julia-version: [1]
        os: [ubuntu-latest]
    steps:
      - uses: actions/checkout@v6
      - uses: julia-actions/setup-julia@latest
        with:
          version: ${{ matrix.julia-version }}
      - name: Cache artifacts
        uses: actions/cache@v5
        env:
          cache-name: cache-artifacts
        with:
          path: ~/.julia/artifacts
          key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
          restore-keys: |
            ${{ runner.os }}-test-${{ env.cache-name }}-
            ${{ runner.os }}-test-
            ${{ runner.os }}-
      - name: Install dependencies
        run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
      - name: Build and deploy
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key
        run: julia --project=docs/ docs/make.jl


================================================
FILE: .github/workflows/downstream.yml
================================================
name: IntegrationTest
on:
  push:
    branches: [master]
    tags: [v*]
    paths-ignore:
      - 'LICENSE'
      - 'README.md'
      - '.github/workflows/TagBot.yml'
  pull_request:
    paths-ignore:
      - 'LICENSE'
      - 'README.md'
      - '.github/workflows/TagBot.yml'

concurrency:
  group: build-${{ github.event.pull_request.number || github.ref }}-${{ github.workflow }}
  cancel-in-progress: true

jobs:
  pre_job:
    # continue-on-error: true # Uncomment once integration is finished
    runs-on: ubuntu-latest
    # Map a step output to a job output
    outputs:
      should_skip: ${{ steps.skip_check.outputs.should_skip }}
    steps:
      - id: skip_check
        uses: fkirc/skip-duplicate-actions@v5
  test:
    needs: pre_job
    if: needs.pre_job.outputs.should_skip != 'true'
    name: ${{ matrix.package.group }}/${{ matrix.package.repo }}/${{ matrix.julia-version }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        julia-version: ['1']
        os: [ubuntu-latest]
        package:
          - {repo: Distributions.jl, group: JuliaStats}
          - {repo: BlockArrays.jl, group: JuliaArrays}
          - {repo: LazyArrays.jl, group: JuliaArrays}
          - {repo: InfiniteArrays.jl, group: JuliaArrays}
          - {repo: ArrayLayouts.jl, group: JuliaLinearAlgebra}
          - {repo: LazyBandedMatrices.jl, group: JuliaLinearAlgebra}
          - {repo: BandedMatrices.jl, group: JuliaLinearAlgebra}
          - {repo: BlockBandedMatrices.jl, group: JuliaLinearAlgebra}
          - {repo: InfiniteLinearAlgebra.jl, group: JuliaLinearAlgebra}
          - {repo: Optim.jl, group: JuliaNLSolvers}

    steps:
      - uses: actions/checkout@v6
      - uses: julia-actions/setup-julia@v3
        with:
          version: ${{ matrix.julia-version }}
          arch: x64
      - uses: julia-actions/julia-buildpkg@latest
      - name: Clone Downstream
        uses: actions/checkout@v6
        with:
          repository: ${{ matrix.package.group }}/${{ matrix.package.repo }}
          path: downstream
      - name: Load this and run the downstream tests
        shell: julia --color=yes --project=downstream {0}
        run: |
          using Pkg
          try
            # force it to use this PR's version of the package
            Pkg.develop(PackageSpec(path="."))  # resolver may fail with main deps
            Pkg.update()
            Pkg.test(; coverage = true, test_args=["--downstream_integration_test"])  # resolver may fail with test time deps
          catch err
            err isa Pkg.Resolve.ResolverError || rethrow()
            # If we can't resolve that means this is incompatible by SemVer and this is fine
            # It means we marked this as a breaking change, so we don't need to worry about
            # Mistakenly introducing a breaking change, as we have intentionally made one
            @info "Not compatible with this release. No problem." exception=err
            exit(0)  # Exit immediately, as a success
          end
      - uses: julia-actions/julia-processcoverage@v1
      - uses: codecov/codecov-action@v6
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: lcov.info


================================================
FILE: .gitignore
================================================
*.jl.cov
*.jl.*.cov
*.jl.mem
deps/deps.jl
.DS_Store
Manifest.toml
Manifest-v*.*.toml
docs/build


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 Sheehan Olver

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: Project.toml
================================================
name = "FillArrays"
uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
version = "1.16.0"

[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[weakdeps]
PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[extensions]
FillArraysPDMatsExt = "PDMats"
FillArraysSparseArraysExt = "SparseArrays"
FillArraysStatisticsExt = "Statistics"
FillArraysStaticArraysExt = "StaticArrays"

[compat]
Aqua = "0.8"
Documenter = "1"
Infinities = "0.1"
LinearAlgebra = "1"
PDMats = "0.11.17"
Quaternions = "0.7"
Random = "1"
ReverseDiff = "1"
SparseArrays = "1"
StaticArrays = "1"
Statistics = "1"
Test = "1"
julia = "1.10"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Infinities = "e1ba4f0e-776d-440f-acd9-e1d2e9742647"
PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150"
Quaternions = "94ee1d12-ae83-5a48-8b1c-48b8ff168ae0"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Aqua", "Test", "Infinities", "PDMats", "ReverseDiff", "SparseArrays", "StaticArrays", "Statistics", "Quaternions", "Documenter", "Random"]


================================================
FILE: README.md
================================================
# FillArrays.jl

[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaArrays.github.io/FillArrays.jl/stable)
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaArrays.github.io/FillArrays.jl/dev)
[![Build Status](https://github.com/JuliaArrays/FillArrays.jl/workflows/CI/badge.svg)](https://github.com/JuliaArrays/FillArrays.jl/actions)
[![codecov](https://codecov.io/gh/JuliaArrays/FillArrays.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaArrays/FillArrays.jl)
[![deps](https://juliahub.com/docs/FillArrays/deps.svg)](https://juliahub.com/ui/Packages/FillArrays/2Dg1l?t=2)
[![version](https://juliahub.com/docs/FillArrays/version.svg)](https://juliahub.com/ui/Packages/FillArrays/2Dg1l)
[![pkgeval](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/F/FillArrays.svg)](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/report.html)
[![Aqua](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)

Julia package to lazily represent matrices filled with a single entry,
as well as identity matrices.  This package exports the following types:
`Eye`, `Fill`, `Ones`, `Zeros`, `Trues`, `Falses`, and `OneElement`.


The primary purpose of this package is to present a unified way of constructing
matrices. 
For example, to construct a 5-by-5 `BandedMatrix` of all zeros with bandwidths `(1,2)`, one would use  
```julia
julia> BandedMatrix(Zeros(5,5), (1, 2))
```

## Usage

Here are the matrix types:
```julia
julia> Zeros(5, 6)
5×6 Zeros{Float64}

julia> Zeros{Int}(2, 3)
2×3 Zeros{Int64}

julia> Zeros(Int, 2, 3) # can also specify the type as an argument
2×3 Zeros{Int64}

julia> Ones{Int}(5)
5-element Ones{Int64}

julia> Eye{Int}(5)
 5×5 Diagonal{Int64,Ones{Int64,1,Tuple{Base.OneTo{Int64}}}}:
  1  ⋅  ⋅  ⋅  ⋅
  ⋅  1  ⋅  ⋅  ⋅
  ⋅  ⋅  1  ⋅  ⋅
  ⋅  ⋅  ⋅  1  ⋅
  ⋅  ⋅  ⋅  ⋅  1

julia> Fill(7.0f0, 3, 2)
3×2 Fill{Float32}: entries equal to 7.0

julia> Trues(2, 3)
2×3 Ones{Bool}

julia> Falses(2)
2-element Zeros{Bool}

julia> OneElement(3.0, (2,1), (5,6))
5×6 OneElement{Float64, 2, Tuple{Int64, Int64}, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}:
  ⋅    ⋅    ⋅    ⋅    ⋅    ⋅ 
 3.0   ⋅    ⋅    ⋅    ⋅    ⋅ 
  ⋅    ⋅    ⋅    ⋅    ⋅    ⋅ 
  ⋅    ⋅    ⋅    ⋅    ⋅    ⋅ 
  ⋅    ⋅    ⋅    ⋅    ⋅    ⋅ 
```

They support conversion to other matrix types like `Array`, `SparseVector`, `SparseMatrix`, and `Diagonal`:
```julia
julia> Matrix(Zeros(5, 5))
5×5 Array{Float64,2}:
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0

julia> SparseMatrixCSC(Zeros(5, 5))
5×5 SparseMatrixCSC{Float64,Int64} with 0 stored entries

julia> Array(Fill(7, (2,3)))
2×3 Array{Int64,2}:
 7  7  7
 7  7  7
```

There is also support for offset index ranges,
and the type includes the `axes`:
```julia
julia> Ones((-3:2, 1:2))
6×2 Ones{Float64,2,Tuple{UnitRange{Int64},UnitRange{Int64}}} with indices -3:2×1:2

julia> Fill(7, ((0:2), (-1:0)))
3×2 Fill{Int64,2,Tuple{UnitRange{Int64},UnitRange{Int64}}} with indices 0:2×-1:0: entries equal to 7

julia> typeof(Zeros(5,6))
Zeros{Float64,2,Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}}
```

These types have methods that perform many operations efficiently,
including elementary algebra operations like multiplication and addition,
as well as linear algebra methods like
`norm`, `adjoint`, `transpose` and `vec`.

## Warning!

Broadcasting operations and `map`, `mapreduce` are also done efficiently, by evaluating the function being applied only once:

```julia
julia> map(sqrt, Fill(4, 2,5))  # one evaluation, not 10, to save time
2×5 Fill{Float64}: entries equal to 2.0

julia> println.(Fill(pi, 10))
π
10-element Fill{Nothing}: entries equal to nothing
```

Notice that this will only match the behaviour of a dense matrix from `fill` if the function is pure. And that this shortcut is taken *before* any other fused broadcast:

```julia
julia> map(_ -> rand(), Fill("pi", 2,5))  # not a pure function!
2×5 Fill{Float64}: entries equal to 0.7201617100284206

julia> map(_ -> rand(), fill("4", 2,5))  # 10 evaluations, different answer!
2×5 Matrix{Float64}:
 0.43675   0.270809  0.56536   0.0948089  0.24655
 0.959363  0.79598   0.238662  0.401909   0.317716

julia> ones(1,5) .+ (_ -> rand()).(Fill("vec", 2))  # Fill broadcast is done first
2×5 Matrix{Float64}:
 1.51796  1.51796  1.51796  1.51796  1.51796
 1.51796  1.51796  1.51796  1.51796  1.51796

julia> ones(1,5) .+ (_ -> rand()).(fill("vec", 2))  # fused, 10 evaluations
2×5 Matrix{Float64}:
 1.51337  1.17578  1.19815  1.43035  1.2987
 1.30253  1.21909  1.61755  1.02645  1.77681
```


================================================
FILE: appveyor.yml
================================================
environment:
  matrix:
  - julia_version: 1
  - julia_version: 1.5
  - julia_version: nightly

platform:
  - x86 # 32-bit
  - x64 # 64-bit

# # Uncomment the following lines to allow failures on nightly julia
# # (tests will run but not make your overall status red)
matrix:
  allow_failures:
  - julia_version: nightly

branches:
  only:
    - master
    - /release-.*/

notifications:
  - provider: Email
    on_build_success: false
    on_build_failure: false
    on_build_status_changed: false

install:
  - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))

build_script:
  - echo "%JL_BUILD_SCRIPT%"
  - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"

test_script:
  - echo "%JL_TEST_SCRIPT%"
  - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"

# # Uncomment to support code coverage upload. Should only be enabled for packages
# # which would have coverage gaps without running on Windows
# on_success:
#   - echo "%JL_CODECOV_SCRIPT%"
#   - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%"


================================================
FILE: benchmark/benchmarks.jl
================================================
using BenchmarkTools
using FillArrays
using LinearAlgebra: triu, tril

# Subtract the overhead from benchmark times
BenchmarkTools.DEFAULT_PARAMETERS.overhead = BenchmarkTools.estimate_overhead()

const SUITE = BenchmarkGroup()

###
### Eye
###

g = addgroup!(SUITE, "Eye", [])

eye1float = Eye{Float64}(1)
eye10float = Eye{Float64}(10)
eye1000float = Eye{Float64}(1000)
eye10int = Eye{Int}(10)
eye1000int = Eye{Int}(1000)

r1 = addgroup!(g, "reduction", [])
r2 = addgroup!(g, "properties", ["properties"])
r3 = addgroup!(g, "any/all", [])
r4 = addgroup!(g, "iterate", [])
r5 = addgroup!(g, "identities", [])

dimstring(a) = string("n=", size(a, 1))
fulldimstring(a) = string("size=", size(a))
funop(fun, op) = string(fun,"(", op, ", a)")

for a in (eye10float, eye1000float, eye10int, eye1000int)
    for fun in (sum,)
        r1[string(fun), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($a)
    end
    for fun in (isone, iszero)
        r2[string(fun), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($a)
    end
    for (fun, op) in ((any, isone), (all, isone))
        r3[funop(fun, op), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($op, $a)
    end
    for fun in (collect,)
        r4[string(fun), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($a)
    end
    for fun in (permutedims, triu, tril, inv)
        r5[string(fun), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($a)
    end
end

for a in (eye1float,)
    for (fun, op) in ((any, isone), (all, isone))
        r3[funop(fun, op), string(eltype(a)), dimstring(a)] = @benchmarkable $fun($op, $a)
    end
end

###
### Zeros
###

g1 = addgroup!(SUITE, "Zeros", [])

zeros10 = Zeros(10)
zeros10x10 = Zeros(10, 10)

z1 = addgroup!(g1, "properties", ["properties"])

for a in (zeros10, zeros10x10)
    for fun in (iszero, )
        z1[string(fun), string(eltype(a)), fulldimstring(a)] = @benchmarkable $fun($a)
    end
end

# If a cache of tuned parameters already exists, use it, otherwise, tune and cache
# the benchmark parameters. Reusing cached parameters is faster and more reliable
# than re-tuning `SUITE` every time the file is included.
paramspath = joinpath(dirname(@__FILE__), "params.json")

if isfile(paramspath)
    loadparams!(SUITE, BenchmarkTools.load(paramspath)[1], :evals);
else
    tune!(SUITE)
    BenchmarkTools.save(paramspath, params(SUITE));
end

result = run(SUITE, verbose = true)


================================================
FILE: docs/Project.toml
================================================
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
Documenter = "1"
Random = "1"
SparseArrays = "1"
StaticArrays = "1"


================================================
FILE: docs/make.jl
================================================
using Documenter
using FillArrays

# Setup for doctests in docstrings
DocMeta.setdocmeta!(FillArrays, :DocTestSetup, :(using FillArrays))

makedocs(;
    format = Documenter.HTML(
        canonical = "https://JuliaArrays.github.io/FillArrays.jl/stable/",
    ),
    pages = [
        "Home" => "index.md",
        ],
    sitename = "FillArrays.jl",
)

deploydocs(; repo = "github.com/JuliaArrays/FillArrays.jl")


================================================
FILE: docs/src/index.md
================================================
```@meta
DocTestSetup  = quote
    using FillArrays
end
```

# Introduction

`FillArrays` allows one to lazily represent arrays filled with a single entry, as well as identity matrices. This package exports the following types: `Eye`, `Fill`, `Ones`, `Zeros`, `Trues` and `Falses`. Among these, the [`FillArrays.AbstractFill`](@ref) types represent lazy versions of dense arrays where all elements have the same value. `Eye`, on the other hand, represents a `Diagonal` matrix with ones along the principal diagonal. All these types accept sizes or axes as arguments, so one may create arrays of arbitrary sizes and dimensions. A rectangular `Eye` matrix may be constructed analogously, by passing the size of the matrix to `Eye`.

## Quick Start

Create a 2x2 zero matrix

```jldoctest
julia> z = Zeros(2,2)
2×2 Zeros{Float64}

julia> Array(z)
2×2 Matrix{Float64}:
 0.0  0.0
 0.0  0.0
```

We may specify the element type as

```jldoctest
julia> z = Zeros{Int}(2,2)
2×2 Zeros{Int64}

julia> Array(z)
2×2 Matrix{Int64}:
 0  0
 0  0
```

We may create arrays with any number of dimensions. A `Vector` of ones may be created as

```jldoctest
julia> a = Ones(4)
4-element Ones{Float64}

julia> Array(a)
4-element Vector{Float64}:
 1.0
 1.0
 1.0
 1.0
```

Similarly, a `2x3x2` array, where every element is equal to `10`, may be created as

```jldoctest
julia> f = Fill(10, 2,3,2)
2×3×2 Fill{Int64}, with entries equal to 10

julia> Array(f)
2×3×2 Array{Int64, 3}:
[:, :, 1] =
 10  10  10
 10  10  10

[:, :, 2] =
 10  10  10
 10  10  10
```

The elements of a `Fill` array don't need to be restricted to numbers, and these may be any Julia object. For example, we may construct an array of strings using

```jldoctest
julia> f = Fill("hello", 2,5)
2×5 Fill{String}, with entries equal to "hello"

julia> Array(f)
2×5 Matrix{String}:
 "hello"  "hello"  "hello"  "hello"  "hello"
 "hello"  "hello"  "hello"  "hello"  "hello"
```

### Conversion to a sparse form

These `Fill` array types may be converted to sparse arrays as well, which might be useful in certain cases
```jldoctest sparse
julia> using SparseArrays

julia> z = Zeros{Int}(2,2)
2×2 Zeros{Int64}

julia> sparse(z)
2×2 SparseMatrixCSC{Int64, Int64} with 0 stored entries:
 ⋅  ⋅
 ⋅  ⋅
```
Note, however, that most `Fill` arrays are not sparse, despite being lazily evaluated.

These types have methods that perform many operations efficiently, including elementary algebra operations like multiplication and addition, as well as linear algebra methods like `norm`, `adjoint`, `transpose` and `vec`.

### Custom axes

The various `Fill` equivalents all support offset or custom axes, where instead of the size, one may pass a `Tuple` of axes. So, for example, one may use a `SOneTo` axis from [`StaticArrays.jl`](https://github.com/JuliaArrays/StaticArrays.jl) to construct a statically sized `Fill`.

```jldoctest
julia> using StaticArrays

julia> f = Fill(2, (SOneTo(4), SOneTo(5)))
4×5 Fill{Int64, 2, Tuple{SOneTo{4}, SOneTo{5}}} with indices SOneTo(4)×SOneTo(5), with entries equal to 2
```

The size of such an array would be known at compile time, permitting compiler optimizations.

We may construct infinite fill arrays by passing infinite-sized axes, see [`InfiniteArrays.jl`](https://github.com/JuliaArrays/InfiniteArrays.jl).

### Other lazy types

A lazy representation of an identity matrix may be constructured using `Eye`. For example, a `4x4` identity matrix with `Float32` elements may be constructed as

```jldoctest sparse
julia> id = Eye{Float32}(4)
4×4 Eye{Float32}

julia> Array(id)
4×4 Matrix{Float32}:
 1.0  0.0  0.0  0.0
 0.0  1.0  0.0  0.0
 0.0  0.0  1.0  0.0
 0.0  0.0  0.0  1.0

julia> sparse(id)
4×4 SparseMatrixCSC{Float32, Int64} with 4 stored entries:
 1.0   ⋅    ⋅    ⋅
  ⋅   1.0   ⋅    ⋅
  ⋅    ⋅   1.0   ⋅
  ⋅    ⋅    ⋅   1.0

julia> idrect = Eye(2,5) # rectangular matrix
2×5 Eye{Float64}

julia> sparse(idrect)
2×5 SparseMatrixCSC{Float64, Int64} with 2 stored entries:
 1.0   ⋅    ⋅    ⋅    ⋅
  ⋅   1.0   ⋅    ⋅    ⋅
```

Note that an `Eye` actually returns a `Diagonal` matrix, where the diagonal is a `Ones` vector.

## Warning about map and broadcasting

Broadcasting operations, and `map` and `mapreduce`, are also done efficiently, by evaluating the function being applied only once:

```jldoctest
julia> map(sqrt, Fill(4, 2,5))  # one evaluation, not 10, to save time
2×5 Fill{Float64}, with entries equal to 2.0

julia> println.(Fill(pi, 10))
π
10-element Fill{Nothing}, with entries equal to nothing
```

Notice that this will only match the behaviour of a dense matrix from `fill` if the function is pure. And that this shortcut is taken before any other fused broadcast:

```jldoctest; setup=:(using Random; Random.seed!(1234))
julia> map(_ -> rand(), Fill("pi", 2,5))  # not a pure function!
2×5 Fill{Float64}, with entries equal to 0.32597672886359486

julia> map(_ -> rand(), fill("4", 2,5))  # 10 evaluations, different answer!
2×5 Matrix{Float64}:
 0.549051  0.894245  0.394255  0.795547  0.748415
 0.218587  0.353112  0.953125  0.49425   0.578232

julia> ones(1,5) .+ (_ -> rand()).(Fill("vec", 2))  # Fill broadcast is done first
2×5 Matrix{Float64}:
 1.72794  1.72794  1.72794  1.72794  1.72794
 1.72794  1.72794  1.72794  1.72794  1.72794

julia> ones(1,5) .+ (_ -> rand()).(fill("vec", 2))  # fused, 10 evaluations
2×5 Matrix{Float64}:
 1.00745  1.43924  1.95674  1.99667  1.11008
 1.19938  1.68253  1.64786  1.74919  1.49138
```

# API

```@autodocs
Modules = [FillArrays]
```


================================================
FILE: ext/FillArraysPDMatsExt.jl
================================================
module FillArraysPDMatsExt

import FillArrays
import FillArrays.LinearAlgebra
import PDMats
using FillArrays: mult_zeros, AbstractZeros
using PDMats: ScalMat

function PDMats.AbstractPDMat(a::LinearAlgebra.Diagonal{T,<:FillArrays.AbstractFill{T,1}}) where {T<:Real}
    dim = size(a, 1)
    return ScalMat(dim, FillArrays.getindex_value(a.diag))
end

Base.:*(a::ScalMat, b::AbstractZeros{T, 1} where T) = mult_zeros(a, b)
Base.:*(a::ScalMat, b::AbstractZeros{T, 2} where T) = mult_zeros(a, b)
Base.:*(a::AbstractZeros{T, 2} where T, b::ScalMat) = mult_zeros(a, b) # This is implemented in case ScalMat implements right multiplication

end # module


================================================
FILE: ext/FillArraysSparseArraysExt.jl
================================================
module FillArraysSparseArraysExt

using SparseArrays
using SparseArrays: SparseVectorUnion
import Base: convert, kron
using FillArrays
using FillArrays: RectDiagonalFill, RectOrDiagonalFill, ZerosVector, ZerosMatrix, getindex_value, AbstractFillVector, _fill_dot
# Specifying the full namespace is necessary because of https://github.com/JuliaLang/julia/issues/48533
# See https://github.com/JuliaStats/LogExpFunctions.jl/pull/63
using FillArrays.LinearAlgebra
import LinearAlgebra: dot, kron, I

##################
## Sparse arrays
##################
SparseVector{T}(Z::ZerosVector) where T = spzeros(T, length(Z))
SparseVector{Tv,Ti}(Z::ZerosVector) where {Tv,Ti} = spzeros(Tv, Ti, length(Z))

convert(::Type{AbstractSparseVector}, Z::ZerosVector{T}) where T = spzeros(T, length(Z))
convert(::Type{AbstractSparseVector{T}}, Z::ZerosVector) where T= spzeros(T, length(Z))

SparseMatrixCSC{T}(Z::ZerosMatrix) where T = spzeros(T, size(Z)...)
SparseMatrixCSC{Tv,Ti}(Z::Zeros{T,2,Axes}) where {Tv,Ti<:Integer,T,Axes} = spzeros(Tv, Ti, size(Z)...)

convert(::Type{AbstractSparseMatrix}, Z::ZerosMatrix{T}) where T = spzeros(T, size(Z)...)
convert(::Type{AbstractSparseMatrix{T}}, Z::ZerosMatrix) where T = spzeros(T, size(Z)...)

convert(::Type{AbstractSparseArray}, Z::Zeros{T}) where T = spzeros(T, size(Z)...)
convert(::Type{AbstractSparseArray{Tv}}, Z::Zeros{T}) where {T,Tv} = spzeros(Tv, size(Z)...)
convert(::Type{AbstractSparseArray{Tv,Ti}}, Z::Zeros{T}) where {T,Tv,Ti} = spzeros(Tv, Ti, size(Z)...)
convert(::Type{AbstractSparseArray{Tv,Ti,N}}, Z::Zeros{T,N}) where {T,Tv,Ti,N} = spzeros(Tv, Ti, size(Z)...)

SparseMatrixCSC{Tv}(Z::Eye{T}) where {T,Tv} = SparseMatrixCSC{Tv}(I, size(Z)...)
# works around missing `speye`:
SparseMatrixCSC{Tv,Ti}(Z::Eye{T}) where {T,Tv,Ti<:Integer} =
    convert(SparseMatrixCSC{Tv,Ti}, SparseMatrixCSC{Tv}(I, size(Z)...))

convert(::Type{AbstractSparseMatrix}, Z::Eye{T}) where {T} = SparseMatrixCSC{T}(I, size(Z)...)
convert(::Type{AbstractSparseMatrix{Tv}}, Z::Eye{T}) where {T,Tv} = SparseMatrixCSC{Tv}(I, size(Z)...)

convert(::Type{AbstractSparseArray}, Z::Eye{T}) where T = SparseMatrixCSC{T}(I, size(Z)...)
convert(::Type{AbstractSparseArray{Tv}}, Z::Eye{T}) where {T,Tv} = SparseMatrixCSC{Tv}(I, size(Z)...)


convert(::Type{AbstractSparseArray{Tv,Ti}}, Z::Eye{T}) where {T,Tv,Ti} =
    convert(SparseMatrixCSC{Tv,Ti}, Z)
convert(::Type{AbstractSparseArray{Tv,Ti,2}}, Z::Eye{T}) where {T,Tv,Ti} =
    convert(SparseMatrixCSC{Tv,Ti}, Z)

function SparseMatrixCSC{Tv}(R::RectOrDiagonalFill) where {Tv}
    SparseMatrixCSC{Tv,eltype(axes(R,1))}(R)
end
function SparseMatrixCSC{Tv,Ti}(R::RectOrDiagonalFill) where {Tv,Ti}
    Base.require_one_based_indexing(R)
    v = parent(R)
    J = getindex_value(v)*I
    SparseMatrixCSC{Tv,Ti}(J, size(R))
end

# TODO: remove in v2.0
@deprecate kron(E1::RectDiagonalFill, E2::RectDiagonalFill) kron(sparse(E1), sparse(E2))

# Ambiguity. see #178
dot(x::AbstractFillVector, y::SparseVectorUnion) = _fill_dot(x, y)

end # module


================================================
FILE: ext/FillArraysStaticArraysExt.jl
================================================
module FillArraysStaticArraysExt

using FillArrays
using StaticArrays

import Base: promote_op
import FillArrays: elconvert

# Disambiguity methods for StaticArrays

function Base.:+(a::FillArrays.Zeros, b::StaticArray)
    promote_shape(a,b)
    return elconvert(promote_op(+,eltype(a),eltype(b)),b)
end
function Base.:+(a::StaticArray, b::FillArrays.Zeros)
    promote_shape(a,b)
    return elconvert(promote_op(+,eltype(a),eltype(b)),a)
end
function Base.:-(a::StaticArray, b::FillArrays.Zeros)
    promote_shape(a,b)
    return elconvert(promote_op(-,eltype(a),eltype(b)),a)
end
function Base.:-(a::FillArrays.Zeros, b::StaticArray)
    promote_shape(a,b)
    return elconvert(promote_op(-,eltype(a),eltype(b)),-b)
end

end # module

================================================
FILE: ext/FillArraysStatisticsExt.jl
================================================
module FillArraysStatisticsExt

import Statistics: mean, var, cov, cor
using LinearAlgebra: diagind

using FillArrays
using FillArrays: AbstractFill, AbstractFillVector, AbstractFillMatrix, getindex_value

mean(A::AbstractFill; dims=(:)) = mean(identity, A; dims=dims)
function mean(f::Union{Function, Type}, A::AbstractFill; dims=(:))
    val = float(f(getindex_value(A)))
    dims isa Colon ? val :
        Fill(val, ntuple(d -> d in dims ? 1 : size(A,d), ndims(A))...)
end


function var(A::AbstractFill{T}; corrected::Bool=true, mean=nothing, dims=(:)) where {T<:Number}
    dims isa Colon ? zero(float(T)) :
        Zeros{float(T)}(ntuple(d -> d in dims ? 1 : size(A,d), ndims(A))...)
end

cov(::AbstractFillVector{T}; corrected::Bool=true) where {T<:Number} = zero(float(T))
cov(A::AbstractFillMatrix{T}; corrected::Bool=true, dims::Integer=1) where {T<:Number} =
    Zeros{float(T)}(size(A, 3-dims), size(A, 3-dims))

cor(::AbstractFillVector{T}) where {T<:Number} = one(float(T))
function cor(A::AbstractFillMatrix{T}; dims::Integer=1) where {T<:Number}
    out = fill(float(T)(NaN), size(A, 3-dims), size(A, 3-dims))
    out[diagind(out)] .= 1
    out
end

end # module


================================================
FILE: src/FillArrays.jl
================================================
""" `FillArrays` module to lazily represent matrices with a single value """
module FillArrays

using LinearAlgebra
import Base: size, getindex, setindex!, IndexStyle, checkbounds, convert,
    +, -, *, /, \, diff, sum, cumsum, maximum, minimum, sort, sort!,
    any, all, axes, isone, iszero, iterate, unique, allunique, permutedims, inv,
    copy, vec, setindex!, count, ==, reshape, map, zero,
    show, view, in, mapreduce, one, reverse, promote_op, promote_rule, repeat,
    parent, similar, issorted, add_sum, accumulate, OneTo, permutedims

import LinearAlgebra: rank, svdvals!, tril, triu, tril!, triu!, diag, transpose, adjoint, fill!,
    dot, norm2, norm1, normInf, normMinusInf, normp, lmul!, rmul!, diagzero, AdjointAbsVec, TransposeAbsVec,
    issymmetric, ishermitian, AdjOrTransAbsVec, checksquare, mul!, kron, AbstractTriangular


import Base.Broadcast: broadcasted, DefaultArrayStyle, broadcast_shape, BroadcastStyle, Broadcasted

export Zeros, Ones, Fill, Eye, Trues, Falses, OneElement

import Base: oneto

"""
    AbstractFill{T, N, Axes} <: AbstractArray{T, N}

Supertype for lazy array types whose entries are all equal.
Subtypes of `AbstractFill` should implement [`FillArrays.getindex_value`](@ref) to return the value of the entries.
"""
abstract type AbstractFill{T, N, Axes} <: AbstractArray{T, N} end
const AbstractFillVector{T} = AbstractFill{T,1}
const AbstractFillMatrix{T} = AbstractFill{T,2}
const AbstractFillVecOrMat{T} = Union{AbstractFillVector{T},AbstractFillMatrix{T}}

==(a::AbstractFill, b::AbstractFill) = axes(a) == axes(b) && getindex_value(a) == getindex_value(b)

@inline function Base.isassigned(F::AbstractFill, i::Integer...)
    @boundscheck checkbounds(Bool, F, to_indices(F, i)...) || return false
    return true
end

@inline function _fill_getindex(F::AbstractFill, kj::Integer...)
    @boundscheck checkbounds(F, kj...)
    getindex_value(F)
end

Base.@propagate_inbounds getindex(F::AbstractFill, k::Integer) = _fill_getindex(F, k)
Base.@propagate_inbounds getindex(F::AbstractFill{T, N}, kj::Vararg{Integer, N}) where {T, N} = _fill_getindex(F, kj...)

@inline function setindex!(F::AbstractFill, v, k::Integer)
    @boundscheck checkbounds(F, k)
    v == getindex_value(F) || throw(ArgumentError(LazyString("Cannot setindex! to ", v, " for an AbstractFill with value ", getindex_value(F), ".")))
    F
end

@inline function setindex!(F::AbstractFill{T, N}, v, kj::Vararg{Integer, N}) where {T, N}
    @boundscheck checkbounds(F, kj...)
    v == getindex_value(F) || throw(ArgumentError(LazyString("Cannot setindex! to ", v, " for an AbstractFill with value ", getindex_value(F), ".")))
    F
end

@inline function fill!(F::AbstractFill, v)
    v == getindex_value(F) || throw(ArgumentError(LazyString("Cannot fill! with ", v, " an AbstractFill with value ", getindex_value(F), ".")))
    F
end

rank(F::AbstractFill) = iszero(getindex_value(F)) ? 0 : 1
IndexStyle(::Type{<:AbstractFill{<:Any,N,<:NTuple{N,Base.OneTo{Int}}}}) where N = IndexLinear()

issymmetric(F::AbstractFillMatrix) = axes(F,1) == axes(F,2) && (isempty(F) || issymmetric(getindex_value(F)))
ishermitian(F::AbstractFillMatrix) = axes(F,1) == axes(F,2) && (isempty(F) || ishermitian(getindex_value(F)))

Base.IteratorSize(::Type{<:AbstractFill{T,N,Axes}}) where {T,N,Axes} = _IteratorSize(Axes)
_IteratorSize(::Type{Tuple{}}) = Base.HasShape{0}()
_IteratorSize(::Type{Tuple{T}}) where {T} = Base.IteratorSize(T)
function _IteratorSize(::Type{T}) where {T<:Tuple}
    N = fieldcount(T)
    s = ntuple(i-> Base.IteratorSize(fieldtype(T, i)), N)
    any(x -> x isa Base.IsInfinite, s) ? Base.IsInfinite() : Base.HasShape{N}()
end


"""
    Fill{T, N, Axes} where {T,N,Axes<:Tuple{Vararg{AbstractUnitRange,N}}}

A lazy representation of an array of dimension `N`
whose entries are all equal to a constant of type `T`,
with axes of type `Axes`.
Typically created by `Fill` or `Zeros` or `Ones`

# Examples

```jldoctest
julia> Fill(7, (2,3))
2×3 Fill{Int64}, with entries equal to 7

julia> Fill{Float64, 1, Tuple{UnitRange{Int64}}}(7.0, (1:2,))
2-element Fill{Float64, 1, Tuple{UnitRange{Int64}}} with indices 1:2, with entries equal to 7.0
```
"""
struct Fill{T, N, Axes} <: AbstractFill{T, N, Axes}
    value::T
    axes::Axes

    Fill{T,N,Axes}(x::T, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} =
        new{T,N,Axes}(x,sz)
    Fill{T,0,Tuple{}}(x::T, sz::Tuple{}) where T = new{T,0,Tuple{}}(x,sz)
end
const FillVector{T} = Fill{T,1}
const FillMatrix{T} = Fill{T,2}
const FillVecOrMat{T} = Union{FillVector{T},FillMatrix{T}}

Fill{T,N,Axes}(x, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} =
    Fill{T,N,Axes}(convert(T, x)::T, sz)

Fill{T,0}(x, ::Tuple{}) where T = Fill{T,0,Tuple{}}(convert(T, x)::T, ()) # ambiguity fix

@inline Fill{T, N}(x, sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} =
    Fill{T,N,Axes}(convert(T, x)::T, sz)

@inline Fill{T, N}(x, sz::SZ) where SZ<:Tuple{Vararg{Integer,N}} where {T, N} =
    Fill{T,N}(x, oneto.(sz))
@inline Fill{T, N}(x, sz::Vararg{Integer, N}) where {T, N} = Fill{T,N}(convert(T, x)::T, sz)


@inline Fill{T}(x, sz::Vararg{Integer,N}) where {T, N} = Fill{T, N}(x, sz)
@inline Fill{T}(x, sz::Tuple{Vararg{Any,N}}) where {T, N} = Fill{T, N}(x, sz)
""" `Fill(x, dims...)` construct lazy version of `fill(x, dims...)` """
@inline Fill(x::T, sz::Vararg{Integer,N}) where {T, N}  = Fill{T, N}(x, sz)
""" `Fill(x, dims)` construct lazy version of `fill(x, dims)` """
@inline Fill(x::T, sz::Tuple{Vararg{Any,N}}) where {T, N}  = Fill{T, N}(x, sz)

# We restrict to  when T is specified to avoid ambiguity with a Fill of a Fill
@inline Fill{T}(F::Fill{T}) where T = F
@inline Fill{T,N}(F::Fill{T,N}) where {T,N} = F
@inline Fill{T,N,Axes}(F::Fill{T,N,Axes}) where {T,N,Axes} = F

@inline axes(F::Fill) = F.axes
@inline size(F::Fill) = map(length, F.axes)

"""
    FillArrays.getindex_value(F::AbstractFill)

Return the value that `F` is filled with.

# Examples

```jldoctest
julia> f = Ones(3);

julia> FillArrays.getindex_value(f)
1.0

julia> g = Fill(2, 10);

julia> FillArrays.getindex_value(g)
2
```
"""
getindex_value

@inline getindex_value(F::Fill) = F.value

AbstractArray{T,N}(F::Fill{V,N}) where {T,V,N} = Fill{T}(convert(T, F.value)::T, F.axes)
AbstractFill{T}(F::AbstractFill) where T = AbstractArray{T}(F)
AbstractFill{T,N}(F::AbstractFill) where {T,N} = AbstractArray{T,N}(F)
AbstractFill{T,N,Ax}(F::AbstractFill{<:Any,N,Ax}) where {T,N,Ax} = AbstractArray{T,N}(F)

convert(::Type{AbstractFill{T}}, F::AbstractFill) where T = convert(AbstractArray{T}, F)
convert(::Type{AbstractFill{T,N}}, F::AbstractFill) where {T,N} = convert(AbstractArray{T,N}, F)
convert(::Type{AbstractFill{T,N,Ax}}, F::AbstractFill{<:Any,N,Ax}) where {T,N,Ax} = convert(AbstractArray{T,N}, F)

copy(F::Fill) = Fill(F.value, F.axes)

"""
    unique_value(arr::AbstractArray)

Return `only(unique(arr))` without intermediate allocations.
Throws an error if `arr` does not contain one and only one unique value.
"""
function unique_value(arr::AbstractArray)
    if isempty(arr) error("Cannot convert empty array to Fill") end
    val = first(arr)
    for x in arr
        if x !== val
            error(LazyString("Input array contains both ", x, " and ", val, ". Cannot convert to Fill"))
        end
    end
    return val
end
unique_value(f::AbstractFill) = getindex_value(f)
convert(::Type{Fill}, arr::AbstractArray{T}) where T = Fill{T}(unique_value(arr), axes(arr))
convert(::Type{Fill{T}}, arr::AbstractArray) where T = Fill{T}(unique_value(arr), axes(arr))
convert(::Type{Fill{T,N}}, arr::AbstractArray{<:Any,N}) where {T,N} = Fill{T,N}(unique_value(arr), axes(arr))
convert(::Type{Fill{T,N,Axes}}, arr::AbstractArray{<:Any,N}) where {T,N,Axes} = Fill{T,N,Axes}(unique_value(arr), axes(arr))
# ambiguity fix
convert(::Type{Fill}, arr::Fill{T}) where T = Fill{T}(unique_value(arr), axes(arr))
convert(::Type{T}, F::T) where T<:Fill = F



getindex(F::Fill{<:Any,0}) = getindex_value(F)

Base.@propagate_inbounds @inline function _fill_getindex(A::AbstractFill, I::Vararg{Union{Real, AbstractArray}, N}) where N
    @boundscheck checkbounds(A, I...)
    shape = Base.index_shape(I...)
    fillsimilar(A, shape)
end

Base.@propagate_inbounds @inline function _fill_getindex(A::AbstractFill, kr::AbstractArray{Bool})
   @boundscheck checkbounds(A, kr)
   fillsimilar(A, count(kr))
end

Base.@propagate_inbounds @inline Base._unsafe_getindex(::IndexStyle, F::AbstractFill, I::Vararg{Union{Real, AbstractArray}}) =
    _fill_getindex(F, I...)



Base.@propagate_inbounds getindex(A::AbstractFill, kr::AbstractVector{Bool}) = _fill_getindex(A, kr)
Base.@propagate_inbounds getindex(A::AbstractFill, kr::AbstractArray{Bool}) = _fill_getindex(A, kr)

@inline Base.iterate(F::AbstractFill) = length(F) == 0 ? nothing : (v = getindex_value(F); (v, (v, 1)))
@inline function Base.iterate(F::AbstractFill, (v, n))
    1 <= n < length(F) || return nothing
    v, (v, n+1)
end

# Iterators
Iterators.rest(F::AbstractFill, (_,n)) = fillsimilar(F, n <= 0 ? 0 : max(length(F)-n, 0))
function Iterators.drop(F::AbstractFill, n::Integer)
    n >= 0 || throw(ArgumentError("drop length must be nonnegative"))
    fillsimilar(F, max(length(F)-n, 0))
end
function Iterators.take(F::AbstractFill, n::Integer)
    n >= 0 || throw(ArgumentError("take length must be nonnegative"))
    fillsimilar(F, min(n, length(F)))
end
Base.rest(F::AbstractFill, s) = Iterators.rest(F, s)

#################
# Sorting
#################
function issorted(f::AbstractFill; kw...)
    v = getindex_value(f)
    issorted((v, v); kw...)
end
function sort(a::AbstractFill; kwds...)
    issorted(a; kwds...) # ensure that the values may be compared
    return a
end
function sort!(a::AbstractFill; kwds...)
    issorted(a; kwds...) # ensure that the values may be compared
    return a
end

svdvals!(a::AbstractFillMatrix) = [getindex_value(a)*sqrt(prod(size(a))); Zeros(min(size(a)...)-1)]

function fill_reshape(parent, dims::Integer...)
    n = length(parent)
    prod(dims) == n || throw(DimensionMismatch(LazyString("parent has ", n, " elements, which is incompatible with size ", dims)))
    fillsimilar(parent, dims...)
end

if VERSION < v"1.12.0-DEV.1726"
    # resolve ambiguity with Base
    reshape(parent::AbstractFillVector, ::Colon) = parent
    reshape(parent::AbstractFill, dims::Integer...) = reshape(parent, dims)
    reshape(parent::AbstractFill, dims::Union{Int,Colon}...) = reshape(parent, dims)
    reshape(parent::AbstractFill, dims::Union{Integer,Colon}...) = reshape(parent, dims)
    reshape(parent::AbstractFill, dims::Tuple{Vararg{Union{Integer,Colon}}}) =
        fill_reshape(parent, Base._reshape_uncolon(parent, dims)...)
    reshape(parent::AbstractFill, dims::Tuple{Vararg{Union{Int,Colon}}}) =
        fill_reshape(parent, Base._reshape_uncolon(parent, dims)...)
    reshape(parent::AbstractFill, shp::Tuple{Union{Integer,Base.OneTo}, Vararg{Union{Integer,Base.OneTo}}}) =
        reshape(parent, Base.to_shape(shp))
    # resolve ambiguity with Base
    reshape(parent::AbstractFillVector, ::Tuple{Colon}) = parent
end
reshape(parent::AbstractFill, dims::Dims) = fill_reshape(parent, dims...)
reshape(parent::AbstractFill, dims::Tuple{Integer, Vararg{Integer}}) = fill_reshape(parent, dims...)


for (AbsTyp, Typ, funcs, func) in ((:AbstractZeros, :Zeros, :zeros, :zero), (:AbstractOnes, :Ones, :ones, :one))
    @eval begin
        abstract type $AbsTyp{T, N, Axes} <: AbstractFill{T, N, Axes} end
        $(Symbol(AbsTyp,"Vector")){T} = $AbsTyp{T,1}
        $(Symbol(AbsTyp,"Matrix")){T} = $AbsTyp{T,2}
        $(Symbol(AbsTyp,"VecOrMat")){T} = Union{$(Symbol(AbsTyp,"Vector")){T},$(Symbol(AbsTyp,"Matrix"))}

        """ `$($Typ){T, N, Axes} <: AbstractFill{T, N, Axes}` (lazy `$($funcs)` with axes)"""
        struct $Typ{T, N, Axes} <: $AbsTyp{T, N, Axes}
            axes::Axes
            @inline $Typ{T,N,Axes}(sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} =
                new{T,N,Axes}(sz)
            @inline $Typ{T,N}(sz::Axes) where Axes<:Tuple{Vararg{AbstractUnitRange,N}} where {T, N} =
                new{T,N,Axes}(sz)
            @inline $Typ{T,0,Tuple{}}(sz::Tuple{}) where T = new{T,0,Tuple{}}(sz)
        end
        const $(Symbol(Typ,"Vector")){T} = $Typ{T,1}
        const $(Symbol(Typ,"Matrix")){T} = $Typ{T,2}
        const $(Symbol(Typ,"VecOrMat")){T} = Union{$Typ{T,1},$Typ{T,2}}


        @inline $Typ{T, 0}(sz::Tuple{}) where {T} = $Typ{T,0,Tuple{}}(sz)
        @inline $Typ{T, N}(sz::Tuple{Vararg{Integer, N}}) where {T, N} = $Typ{T,N}(oneto.(sz))
        @inline $Typ{T, N}(sz::Vararg{Integer, N}) where {T, N} = $Typ{T,N}(sz)
        """ `$($Typ){T}(dims...)` construct lazy version of `$($funcs)(dims...)`"""
        @inline $Typ{T}(sz::Vararg{Integer,N}) where {T, N} = $Typ{T, N}(sz)
        @inline $Typ{T}(sz::SZ) where SZ<:Tuple{Vararg{Any,N}} where {T, N} = $Typ{T, N}(sz)
        @inline $Typ(sz::Vararg{Any,N}) where N = $Typ{Float64,N}(sz)
        @inline $Typ(sz::SZ) where SZ<:Tuple{Vararg{Any,N}} where N = $Typ{Float64,N}(sz)
        @inline $Typ{T}(n::Integer) where T = $Typ{T,1}(n)
        @inline $Typ(n::Integer) = $Typ{Float64,1}(n)

        @inline $Typ{T,N,Axes}(A::AbstractArray{V,N}) where{T,V,N,Axes} = $Typ{T,N,Axes}(axes(A))
        @inline $Typ{T,N}(A::AbstractArray{V,N}) where{T,V,N} = $Typ{T,N}(size(A))
        @inline $Typ{T}(A::AbstractArray) where{T} = $Typ{T}(size(A))
        @inline $Typ(A::AbstractArray) = $Typ{eltype(A)}(A)
        @inline $Typ(::Type{T}, m...) where T = $Typ{T}(m...)

        @inline axes(Z::$Typ) = Z.axes
        @inline size(Z::$AbsTyp) = length.(axes(Z))
        @inline getindex_value(Z::$AbsTyp{T}) where T = $func(T)

        AbstractArray{T}(F::$AbsTyp) where T = $Typ{T}(axes(F))
        AbstractArray{T,N}(F::$AbsTyp{V,N}) where {T,V,N} = $Typ{T}(axes(F))

        copy(F::$AbsTyp) = F

        getindex(F::$AbsTyp{T,0}) where T = getindex_value(F)

        promote_rule(::Type{$Typ{T, N, Axes}}, ::Type{$Typ{V, N, Axes}}) where {T,V,N,Axes} = $Typ{promote_type(T,V),N,Axes}
        function convert(::Type{Typ}, A::$AbsTyp{V,N,Axes}) where {T,V,N,Axes,Typ<:$AbsTyp{T,N,Axes}}
            convert(T, getindex_value(A)) # checks that the types are convertible
            Typ(axes(A))
        end
        convert(::Type{$Typ{T,N}}, A::$AbsTyp{V,N,Axes}) where {T,V,N,Axes} = convert($Typ{T,N,Axes}, A)
        convert(::Type{$Typ{T}}, A::$AbsTyp{V,N,Axes}) where {T,V,N,Axes} = convert($Typ{T,N,Axes}, A)
        function convert(::Type{Typ}, A::AbstractFill{V,N}) where {T,V,N,Axes,Typ<:$AbsTyp{T,N,Axes}}
            axes(A) isa Axes || throw(ArgumentError(LazyString("cannot convert, as axes of array are not ", Axes)))
            val = getindex_value(A)
            y = convert(T, val)
            y == $func(T) || throw(ArgumentError(LazyString("cannot convert an array containinig ", val, " to ", Typ)))
            Typ(axes(A))
        end
        function convert(::Type{$Typ{T,N}}, A::AbstractFill{<:Any,N}) where {T,N}
            convert($Typ{T,N,typeof(axes(A))}, A)
        end
        function convert(::Type{$Typ{T}}, A::AbstractFill{<:Any,N}) where {T,N}
            convert($Typ{T,N}, A)
        end
        function convert(::Type{$Typ}, A::AbstractFill{V,N}) where {V,N}
            convert($Typ{V,N}, A)
        end
    end
end

# conversions
for TYPE in (:Fill, :AbstractFill, :Ones, :Zeros), STYPE in (:AbstractArray, :AbstractFill)
    @eval begin
        @inline $STYPE{T}(F::$TYPE{T}) where T = F
        @inline $STYPE{T,N}(F::$TYPE{T,N}) where {T,N} = F
    end
end

promote_rule(::Type{<:AbstractFill{T, N, Axes}}, ::Type{<:AbstractFill{V, N, Axes}}) where {T,V,N,Axes} = Fill{promote_type(T,V),N,Axes}

"""
    fillsimilar(a::AbstractFill, axes...)

creates a fill object that has the same fill value as `a` but
with the specified axes.
For example, if `a isa Zeros` then so is the returned object.
"""
fillsimilar(a::Ones{T}, axes...) where T = Ones{T}(axes...)
fillsimilar(a::Zeros{T}, axes...) where T = Zeros{T}(axes...)
fillsimilar(a::AbstractFill, axes...) = Fill(getindex_value(a), axes...)

# functions
function Base.sqrt(a::AbstractFillMatrix{<:Union{Real, Complex}})
    Base.require_one_based_indexing(a)
    size(a,1) == size(a,2) || throw(DimensionMismatch(LazyString("matrix is not square: dimensions are ", size(a))))
    _sqrt(a)
end
_sqrt(a::AbstractZerosMatrix) = float(a)
function _sqrt(a::AbstractFillMatrix)
    n = size(a,1)
    n == 0 && return float(a)
    v = getindex_value(a)
    Fill(√(v/n), axes(a))
end
function Base.cbrt(a::AbstractFillMatrix{<:Real})
    Base.require_one_based_indexing(a)
    size(a,1) == size(a,2) || throw(DimensionMismatch(LazyString("matrix is not square: dimensions are ", size(a))))
    _cbrt(a)
end
_cbrt(a::AbstractZerosMatrix) = float(a)
function _cbrt(a::AbstractFillMatrix)
    n = size(a,1)
    n == 0 && return float(a)
    v = getindex_value(a)
    Fill(cbrt(v)/cbrt(n)^2, axes(a))
end

struct RectDiagonal{T,V<:AbstractVector{T},Axes<:Tuple{Vararg{AbstractUnitRange,2}}} <: AbstractMatrix{T}
    diag::V
    axes::Axes

    @inline function RectDiagonal{T,V}(A::V, axes::Axes) where {T,V<:AbstractVector{T},Axes<:Tuple{Vararg{AbstractUnitRange,2}}}
        Base.require_one_based_indexing(A)
        @assert any(length(ax) == length(A) for ax in axes)
        rd = new{T,V,Axes}(A, axes)
        Base.require_one_based_indexing(rd)
        return rd
    end
end

@inline RectDiagonal{T,V}(A::V, sz::Tuple{Vararg{Integer, 2}}) where {T,V} = RectDiagonal{T,V}(A, oneto.(sz))
@inline RectDiagonal{T,V}(A::V, axes::Vararg{Any, 2}) where {T,V} = RectDiagonal{T,V}(A, axes)
@inline RectDiagonal{T,V}(A::V, sz::Vararg{Integer, 2}) where {T,V} = RectDiagonal{T,V}(A, sz)
@inline RectDiagonal{T,V}(A::V) where {T,V} = RectDiagonal{T,V}(A, (axes(A, 1), axes(A, 1)))
@inline RectDiagonal{T}(A::V, args...) where {T,V} = RectDiagonal{T,V}(A, args...)
@inline RectDiagonal(A::V, args...) where {V} = RectDiagonal{eltype(V),V}(A, args...)

const UpperOrUnitUpperTriangular{T,S} = Union{UpperTriangular{T,S}, UnitUpperTriangular{T,S}}
const LowerOrUnitLowerTriangular{T,S} = Union{LowerTriangular{T,S}, UnitLowerTriangular{T,S}}
const UpperOrLowerTriangular{T,S} = Union{UpperOrUnitUpperTriangular{T,S}, LowerOrUnitLowerTriangular{T,S}}

# patch missing overload from Base
axes(rd::Diagonal{<:Any,<:AbstractFill}) = (axes(rd.diag,1),axes(rd.diag,1))
axes(T::UpperOrLowerTriangular{<:Any,<:AbstractFill}) = axes(parent(T))

axes(rd::RectDiagonal) = rd.axes
size(rd::RectDiagonal) = map(length, rd.axes)

parent(rd::RectDiagonal) = rd.diag

@inline function getindex(rd::RectDiagonal{T}, i::Integer, j::Integer) where T
    @boundscheck checkbounds(rd, i, j)
    if i == j
        @inbounds r = rd.diag[i]
    else
        r = zero(T)
    end
    return r
end

function setindex!(rd::RectDiagonal, v, i::Integer, j::Integer)
    @boundscheck checkbounds(rd, i, j)
    if i == j
        @inbounds rd.diag[i] = v
    elseif !iszero(v)
        throw(ArgumentError(LazyString("cannot set off-diagonal entry (", i, ", ", j, ") to a nonzero value (", v, ")")))
    end
    return v
end

diag(rd::RectDiagonal) = rd.diag

for f in (:triu, :triu!, :tril, :tril!)
    @eval ($f)(M::RectDiagonal) = M
end

# Due to default definitions in LinearAlgebra only the following implementations are needed
# (see above for more details)
function +(a::RectDiagonal, b::UniformScaling)
    LinearAlgebra.checksquare(a)
    return Diagonal(a.diag .+ b.λ)
end
function -(a::UniformScaling, b::RectDiagonal)
    LinearAlgebra.checksquare(b)
    return Diagonal(a.λ .- b.diag)
end

Base.replace_in_print_matrix(A::RectDiagonal, i::Integer, j::Integer, s::AbstractString) =
    i == j ? s : Base.replace_with_centered_mark(s)


const RectOrDiagonal{T,V,Axes} = Union{RectDiagonal{T,V,Axes}, Diagonal{T,V}}
const RectOrDiagonalFill{T,V<:AbstractFillVector{T},Axes} = RectOrDiagonal{T,V,Axes}
const RectDiagonalFill{T,V<:AbstractFillVector{T}} = RectDiagonal{T,V}
const SquareEye{T,Axes} = Diagonal{T,Ones{T,1,Tuple{Axes}}}
const Eye{T,Axes} = RectOrDiagonal{T,Ones{T,1,Tuple{Axes}}}

@inline SquareEye{T}(n::Integer) where T = Diagonal(Ones{T}(n))
@inline SquareEye(n::Integer) = Diagonal(Ones(n))
@inline SquareEye{T}(ax::Tuple{AbstractUnitRange{Int}}) where T = Diagonal(Ones{T}(ax))
@inline SquareEye(ax::Tuple{AbstractUnitRange{Int}}) = Diagonal(Ones(ax))

@inline Eye{T}(n::Integer) where T = SquareEye{T}(n)
@inline Eye(n::Integer) = SquareEye(n)
@inline Eye{T}(ax::Tuple{AbstractUnitRange{Int}}) where T = SquareEye{T}(ax)
@inline Eye(ax::Tuple{AbstractUnitRange{Int}}) = SquareEye(ax)

# function iterate(iter::Eye, istate = (1, 1))
#     (i::Int, j::Int) = istate
#     m = size(iter, 1)
#     return i > m ? nothing :
#         ((@inbounds getindex(iter, i, j)),
#          j == m ? (i + 1, 1) : (i, j + 1))
# end

isone(::SquareEye) = true

function diag(E::Eye, k::Integer=0)
    v = k == 0 ? oneunit(eltype(E)) : zero(eltype(E))
    len = length(diagind(E, k))
    Fill(v, len)
end

# These should actually be in StdLib, LinearAlgebra.jl, for all Diagonal
for f in (:permutedims, :triu, :triu!, :tril, :tril!, :copy)
    @eval ($f)(IM::Diagonal{<:Any,<:AbstractFill}) = IM
end

inv(IM::SquareEye) = IM
inv(IM::Diagonal{<:Any,<:AbstractFill}) = Diagonal(map(inv, IM.diag))

Eye(n::Integer, m::Integer) = RectDiagonal(Ones(min(n,m)), n, m)
Eye{T}(n::Integer, m::Integer) where T = RectDiagonal{T}(Ones{T}(min(n,m)), n, m)
function Eye{T}((a,b)::NTuple{2,AbstractUnitRange{Int}}) where T
    ab = length(a) ≤ length(b) ? a : b
    RectDiagonal{T}(Ones{T}((ab,)), (a,b))
end
function Eye((a,b)::NTuple{2,AbstractUnitRange{Int}})
    ab = length(a) ≤ length(b) ? a : b
    RectDiagonal(Ones((ab,)), (a,b))
end

@inline Eye{T}(A::AbstractMatrix) where T = Eye{T}(size(A)...)
@inline Eye(A::AbstractMatrix) = Eye{eltype(A)}(size(A)...)

# This may break, as it uses undocumented internals of LinearAlgebra
# Ideally this should be copied over to this package
# Also, maybe this should reuse the broadcasting behavior of the parent,
# once AbstractFill types implement their own BroadcastStyle
BroadcastStyle(::Type{<:RectDiagonal}) = LinearAlgebra.StructuredMatrixStyle{RectDiagonal}()
function LinearAlgebra.structured_broadcast_alloc(bc, ::Type{<:RectDiagonal}, ::Type{ElType}, n) where {ElType}
    RectDiagonal(Array{ElType}(undef, minimum(n)), axes(bc))
end
@inline LinearAlgebra.fzero(S::RectDiagonal{T}) where {T} = zero(T)

#########
#  Special matrix types
#########



## Array
Base.Array{T,N}(F::AbstractFill{V,N}) where {T,V,N} =
    convert(Array{T,N}, fill(convert(T, getindex_value(F)), size(F)))

# These are in case `zeros` or `ones` are ever faster than `fill`
for (Typ, funcs, func) in ((:AbstractZeros, :zeros, :zero), (:AbstractOnes, :ones, :one))
    @eval begin
        Base.Array{T,N}(F::$Typ{V,N}) where {T,V,N} = $funcs(T,size(F))
    end
end

if VERSION < v"1.11-"
    # temporary patch. should be a PR(#48895) to LinearAlgebra
    Diagonal{T}(A::AbstractFillMatrix) where T = Diagonal{T}(diag(A))
    function convert(::Type{T}, A::AbstractFillMatrix) where T<:Diagonal
        checksquare(A)
        isdiag(A) ? T(diag(A)) : throw(InexactError(:convert, T, A))
    end
end

Base.StepRangeLen(F::AbstractFillVector{T}) where T = StepRangeLen(getindex_value(F), zero(T), length(F))
convert(::Type{SL}, F::AbstractFillVector) where SL<:AbstractRange = convert(SL, StepRangeLen(F))

#################
# Structured matrix types
#################

for SMT in (:Diagonal, :Bidiagonal, :Tridiagonal, :SymTridiagonal)
    @eval function diag(D::$SMT{T,<:AbstractFillVector{T}}, k::Integer=0) where {T<:Number}
        inds = (1,1) .+ (k >= 0 ? (0,k) : (-k,0))
        v = get(D, inds, zero(eltype(D)))
        Fill(v, length(diagind(D, k)))
    end
end


#########
# maximum/minimum
#########

for op in (:maximum, :minimum)
    @eval $op(x::AbstractFill) = getindex_value(x)
end


#########
# Cumsum
#########

# These methods are necessary to deal with infinite arrays
sum(x::AbstractFill) = getindex_value(x)*length(x)
sum(f, x::AbstractFill) = length(x) * f(getindex_value(x))
sum(x::AbstractZeros) = getindex_value(x)

# needed to support infinite case
steprangelen(st...) = StepRangeLen(st...)
function cumsum(x::AbstractFill{T,1}) where T
    V = promote_op(add_sum, T, T)
    steprangelen(convert(V,getindex_value(x)), getindex_value(x), length(x))
end

cumsum(x::AbstractZerosVector{T}) where T = _range_convert(AbstractVector{promote_op(add_sum, T, T)}, x)
cumsum(x::AbstractZerosVector{Bool}) = _range_convert(AbstractVector{Int}, x)
cumsum(x::AbstractOnesVector{T}) where T<:Integer = _range_convert(AbstractVector{promote_op(add_sum, T, T)}, oneto(length(x)))
cumsum(x::AbstractOnesVector{Bool}) = oneto(length(x))


for op in (:+, :-)
    @eval begin
        function accumulate(::typeof($op), x::AbstractFill{T,1}) where T
            V = promote_op($op, T, T)
            steprangelen(convert(V,getindex_value(x)), $op(getindex_value(x)), length(x))
        end

        accumulate(::typeof($op), x::AbstractZerosVector{T}) where T = _range_convert(AbstractVector{promote_op($op, T, T)}, x)
        accumulate(::typeof($op), x::AbstractZerosVector{Bool}) = _range_convert(AbstractVector{Int}, x)
    end
end

accumulate(::typeof(+), x::AbstractOnesVector{T}) where T<:Integer = _range_convert(AbstractVector{promote_op(+, T, T)}, oneto(length(x)))
accumulate(::typeof(+), x::AbstractOnesVector{Bool}) = oneto(length(x))

#########
# Diff
#########

diff(x::AbstractFillVector{T}) where T = Zeros{T}(length(x)-1)

#########
# unique
#########

unique(x::AbstractFill) = fillsimilar(x, Int(!isempty(x)))
allunique(x::AbstractFill) = length(x) < 2

#########
# zero
#########

zero(r::AbstractZeros{T,N}) where {T,N} = r
# TODO: Make this required?
zero(r::AbstractOnes{T,N}) where {T,N} = Zeros{T,N}(axes(r))
zero(r::Fill{T,N}) where {T,N} = Zeros{T,N}(r.axes)

#########
# oneunit
#########

function one(A::AbstractFillMatrix{T}) where {T}
    Base.require_one_based_indexing(A)
    m, n = size(A)
    m == n || throw(ArgumentError("multiplicative identity defined only for square matrices"))
    SquareEye{T}(m)
end

#########
# any/all/isone/iszero
#########

function isone(AF::AbstractFillMatrix)
    (n,m) = size(AF)
    n != m && return false
    (n == 0 || m == 0) && return true
    isone(getindex_value(AF)) || return false
    n == 1 && return true
    return false
end

# all(isempty, []) and any(isempty, []) have non-generic behavior.
# We do not follow it here for Eye(0).
function any(f::Function, IM::Eye{T}) where T
    d1, d2 = size(IM)
    (d1 < 1 || d2 < 1) && return false
    (d1 > 1 || d2 > 1) && return f(zero(T)) || f(one(T))
    return any(f(one(T)))
end

function all(f::Function, IM::Eye{T}) where T
    d1, d2 = size(IM)
    (d1 < 1 || d2 < 1) && return false
    (d1 > 1 || d2 > 1) && return f(zero(T)) && f(one(T))
    return all(f(one(T)))
end

# In particular, these make iszero(Eye(n))  efficient.
# use any/all on scalar to get Boolean error message
function any(f::Function, x::AbstractFill)
    isempty(x) && return false
    # If the condition is true for one value, then it's true for all
    fval = f(getindex_value(x))
    any((fval,))
end
function all(f::Function, x::AbstractFill)
    isempty(x) && return true
    # If the condition is true for one value, then it's true for all
    fval = f(getindex_value(x))
    return all((fval,))
end
any(x::AbstractFill) = any(identity, x)
all(x::AbstractFill) = all(identity, x)

count(x::AbstractOnes{Bool}) = length(x)
count(x::AbstractZeros{Bool}) = 0
count(f, x::AbstractFill) = f(getindex_value(x)) ? length(x) : 0

#########
# in
#########
in(x, A::AbstractFill) = x == getindex_value(A)
function in(x, A::RectDiagonal{<:Number})
    any(iszero, size(A)) && return false # Empty matrix
    all(isone, size(A)) && return x == A.diag[1] # A 1x1 matrix has only one element
    x == zero(eltype(A)) || x in A.diag
end

#########
# include
#########

include("fillalgebra.jl")
include("fillbroadcast.jl")
include("trues.jl")

##
# print
##
Base.replace_in_print_matrix(::AbstractZeros, ::Integer, ::Integer, s::AbstractString) =
    Base.replace_with_centered_mark(s)

# following support blocked fill array printing via
# BlockArrays.jl
axes_print_matrix_row(lay, io, X, A, i, cols, sep, idxlast::Integer=last(axes(X, 2))) =
    Base.invoke(Base.print_matrix_row, Tuple{IO,AbstractVecOrMat,Vector,Integer,AbstractVector,AbstractString,Integer},
                    io, X, A, i, cols, sep, idxlast)

Base.print_matrix_row(io::IO,
        X::Union{AbstractFillVector,
                 AbstractFillMatrix,
                 Diagonal{<:Any,<:AbstractFillVector},
                 RectDiagonal,
                 UpperOrLowerTriangular{<:Any,<:AbstractFillMatrix}
                 }, A::Vector,
        i::Integer, cols::AbstractVector, sep::AbstractString, idxlast::Integer=last(axes(X, 2))) =
        axes_print_matrix_row(axes(X), io, X, A, i, cols, sep)


# Display concise description of a Fill.

function Base.show(io::IO, ::MIME"text/plain", x::Union{Eye, AbstractFill})
    if get(IOContext(io), :compact, false)  # for example [Fill(i==j,2,2) for i in 1:3, j in 1:4]
        return show(io, x)
    end
    summary(io, x)
    if !(x isa Union{AbstractZeros, AbstractOnes, Eye})
        print(io, ", with ", length(x) > 1 ? "entries" : "entry", " equal to ")
        show(io, getindex_value(x))
    end
end

function Base.show(io::IO, x::AbstractFill)  # for example (Fill(π,3),)
    print(io, nameof(typeof(x)))
    sz = size(x)
    args = if x isa Union{AbstractZeros, AbstractOnes}
        T = eltype(x)
        if T != Float64
            print(io,"{", T, "}")
        end
        print(io, "(")
    else
        # show, not print, to handle (Fill(1f0,2),)
        print(io, "(")
        show(io, getindex_value(x))
        ndims(x) == 0 || print(io, ", ")
    end
    join(io, size(x), ", ")
    print(io, ")")
end
function Base.show(io::IO, x::Eye)
    print(io, "Eye(", size(x,1))
    if size(x,1) != size(x,2)
        print(io, ",", size(x,2))
    end
    print(io, ")")
end

Base.array_summary(io::IO, ::Zeros{T}, inds::Tuple{Vararg{Base.OneTo}}) where T =
    print(io, Base.dims2string(length.(inds)), " Zeros{$T}")
Base.array_summary(io::IO, ::Ones{T}, inds::Tuple{Vararg{Base.OneTo}}) where T =
    print(io, Base.dims2string(length.(inds)), " Ones{$T}")
Base.array_summary(io::IO, a::Fill{T}, inds::Tuple{Vararg{Base.OneTo}}) where T =
    print(io, Base.dims2string(length.(inds)), " Fill{$T}")
Base.array_summary(io::IO, a::Eye{T}, inds::Tuple{Vararg{Base.OneTo}}) where T =
    print(io, Base.dims2string(length.(inds)), " Eye{$T}")


##
# interface
##

getindex_value(a::LinearAlgebra.Adjoint) = adjoint(getindex_value(parent(a)))
getindex_value(a::LinearAlgebra.Transpose) = transpose(getindex_value(parent(a)))
getindex_value(a::SubArray) = getindex_value(parent(a))

copy(a::LinearAlgebra.Adjoint{<:Any,<:AbstractFill}) = copy(parent(a))'
copy(a::LinearAlgebra.Transpose{<:Any,<:AbstractFill}) = transpose(parent(a))

##
# view
##

Base.@propagate_inbounds view(A::AbstractFill{<:Any,N}, kr::AbstractArray{Bool,N}) where N = _fill_getindex(A, kr)
Base.@propagate_inbounds view(A::AbstractFill{<:Any,1}, kr::AbstractVector{Bool}) = _fill_getindex(A, kr)
Base.@propagate_inbounds view(A::AbstractFill, I...) =
    _fill_getindex(A, Base.to_indices(A,I)...)

# not getindex since we need array-like indexing
Base.@propagate_inbounds function view(A::AbstractFill, I::Vararg{Real})
    @boundscheck checkbounds(A, I...)
    fillsimilar(A)
end

# repeat

_first(t::Tuple) = t[1]
_first(t::Tuple{}) = 1

_maybetail(t::Tuple) = Base.tail(t)
_maybetail(t::Tuple{}) = t

_match_size(sz::Tuple{}, inner::Tuple{}, outer::Tuple{}) = ()
function _match_size(sz::Tuple, inner::Tuple, outer::Tuple)
    t1 = (_first(sz), _first(inner), _first(outer))
    t2 = _match_size(_maybetail(sz), _maybetail(inner), _maybetail(outer))
    (t1, t2...)
end

function _repeat_size(sz::Tuple, inner::Tuple, outer::Tuple)
    t = _match_size(sz, inner, outer)
    map(*, getindex.(t, 1), getindex.(t, 2), getindex.(t, 3))
end

function _repeat(A; inner=ntuple(x->1, ndims(A)), outer=ntuple(x->1, ndims(A)))
    Base.require_one_based_indexing(A)
    length(inner) >= ndims(A) ||
        throw(ArgumentError(LazyString("number of inner repetitions ", length(inner), " cannot be less than number of dimensions of input array ", ndims(A))))
    length(outer) >= ndims(A) ||
        throw(ArgumentError(LazyString("number of outer repetitions ", length(outer), " cannot be less than number of dimensions of input array ", ndims(A))))
    sz = _repeat_size(size(A), Tuple(inner), Tuple(outer))
    fillsimilar(A, sz)
end

repeat(A::AbstractFill, count::Integer...) = _repeat(A, outer=count)
function repeat(A::AbstractFill; inner=ntuple(x->1, ndims(A)), outer=ntuple(x->1, ndims(A)))
    _repeat(A, inner=inner, outer=outer)
end

include("oneelement.jl")

end # module


================================================
FILE: src/fillalgebra.jl
================================================
## vec

vec(a::AbstractFill) = fillsimilar(a, length(a))

## Transpose/Adjoint
# cannot do this for vectors since that would destroy scalar dot product


for OP in (:transpose, :adjoint)
    @eval begin
        function $OP(a::AbstractZerosMatrix)
            v = getindex_value(a)
            T = typeof($OP(v))
            Zeros{T}(reverse(axes(a)))
        end
        $OP(a::AbstractOnesMatrix) = fillsimilar(a, reverse(axes(a)))
        $OP(a::FillMatrix) = Fill($OP(a.value), reverse(a.axes))
    end
end

permutedims(a::AbstractFillVector) = fillsimilar(a, (1, length(a)))
permutedims(a::AbstractFillMatrix) = fillsimilar(a, reverse(axes(a)))

function permutedims(B::AbstractFill, perm)
    dimsB = size(B)
    ndimsB = length(dimsB)
    (ndimsB == length(perm) && isperm(perm)) || throw(ArgumentError("no valid permutation of dimensions"))
    dimsP = ntuple(i->dimsB[perm[i]], ndimsB)::typeof(dimsB)
    fillsimilar(B, dimsP)
end

Base.@propagate_inbounds function reverse(A::AbstractFill, start::Integer, stop::Integer=lastindex(A))
    @boundscheck checkbounds(A, start)
    @boundscheck checkbounds(A, stop)
    A
end
reverse(A::AbstractFill; dims=:) = A

## Algebraic identities

# Default outputs, can overload to customize
mult_fill(a, b, val, ax) = Fill(val, ax)
mult_zeros(a, b, elt, ax) = Zeros{elt}(ax)
mult_ones(a, b, elt, ax) = Ones{elt}(ax)

function mult_fill(a::AbstractFill, b::AbstractFill, ax)
    val = getindex_value(a)*getindex_value(b)*size(a,2)
    return mult_fill(a, b, val, ax)
end

function mult_zeros(a, b, ax)
    # This is currently only used in contexts where zero is defined
    # might need a rethink
    elt = typeof(zero(eltype(a)) * zero(eltype(b)))
    return mult_zeros(a, b, elt, ax)
end

function mult_ones(a, b, ax)
    # This is currently only used in contexts where zero is defined
    # might need a rethink
    elt = typeof(zero(eltype(a)) * zero(eltype(b)))
    return mult_ones(a, b, elt, ax)
end

function mult_axes(a, b)
    Base.require_one_based_indexing(a, b)
    size(a, 2) ≠ size(b, 1) &&
        throw(DimensionMismatch(LazyString("A has dimensions ", size(a), " but B has dimensions ", size(b))))
    return (axes(a, 1), axes(b)[2:end]...)
end

mult_fill(a, b) = mult_fill(a, b, mult_axes(a, b))
# for arrays of numbers, we assume that zero is defined for the result
# in this case, we may express the result as a Zeros
mult_zeros(a::AbstractArray{<:Number}, b::AbstractArray{<:Number}) = mult_zeros(a, b, mult_axes(a, b))
# In general, we create a Fill that doesn't assume anything about the
# properties of the element type
mult_zeros(a, b) = mult_fill(a, b, mult_axes(a, b))
mult_ones(a, b) = mult_ones(a, b, mult_axes(a, b))

*(a::AbstractFillMatrix, b::AbstractFillMatrix) = mult_fill(a,b)
*(a::AbstractFillMatrix, b::AbstractFillVector) = mult_fill(a,b)

# this treats a size (n,) vector as a nx1 matrix, so b needs to have 1 row
# special cased, as OnesMatrix * OnesMatrix isn't a Ones
*(a::AbstractOnesVector, b::AbstractOnesMatrix) = mult_ones(a, b)

*(a::AbstractZerosMatrix, b::AbstractZerosMatrix) = mult_zeros(a, b)
*(a::AbstractZerosMatrix, b::AbstractZerosVector) = mult_zeros(a, b)

*(a::AbstractZerosMatrix, b::AbstractFillMatrix) = mult_zeros(a, b)
*(a::AbstractZerosMatrix, b::AbstractFillVector) = mult_zeros(a, b)
*(a::AbstractFillMatrix, b::AbstractZerosMatrix) = mult_zeros(a, b)
*(a::AbstractFillMatrix, b::AbstractZerosVector) = mult_zeros(a, b)

for MT in (:AbstractMatrix, :AbstractTriangular)
    @eval *(a::AbstractZerosMatrix, b::$MT) = mult_zeros(a, b)
    @eval *(a::$MT, b::AbstractZerosMatrix) = mult_zeros(a, b)
end
# Odd way to deal with the type-parameters to avoid ambiguities
for MT in (:(AbstractMatrix{T}), :(Transpose{<:Any, <:AbstractMatrix{T}}), :(Adjoint{<:Any, <:AbstractMatrix{T}}),
            :(AbstractTriangular{T}))
    @eval *(a::$MT, b::AbstractZerosVector) where {T} = mult_zeros(a, b)
end
for T in (:AbstractZerosMatrix, :AbstractFillMatrix)
    @eval begin
        *(a::Transpose{<:Any, <:AbstractVector}, b::$T) = transpose(transpose(b) * parent(a))
        *(a::Adjoint{<:Any, <:AbstractVector}, b::$T) = adjoint(adjoint(b) * parent(a))
    end
end
*(a::AbstractZerosMatrix, b::AbstractVector) = mult_zeros(a, b)
function *(F::AbstractFillMatrix, v::AbstractVector)
    check_matmul_sizes(F, v)
    Fill(getindex_value(F) * sum(v), (axes(F,1),))
end

function lmul_diag(a::Diagonal, b)
    size(a,2) == size(b,1) || throw(DimensionMismatch(LazyString("A has dimensions ", size(a), " but B has dimensions ", size(b))))
    parent(a) .* b # use special broadcast
end
function rmul_diag(a, b::Diagonal)
    size(a,2) == size(b,1) || throw(DimensionMismatch(LazyString("A has dimensions ", size(a), " but B has dimensions ", size(b))))
    a .* permutedims(parent(b)) # use special broadcast
end

*(a::AbstractZerosMatrix, b::Diagonal) = rmul_diag(a, b)
*(a::Diagonal, b::AbstractZerosVector) = lmul_diag(a, b)
*(a::Diagonal, b::AbstractZerosMatrix) = lmul_diag(a, b)
*(a::Diagonal, b::AbstractFillMatrix) = lmul_diag(a, b)
*(a::AbstractFillMatrix, b::Diagonal) = rmul_diag(a, b)

@noinline function check_matmul_sizes(A::AbstractMatrix, x::AbstractVector)
    Base.require_one_based_indexing(A, x)
    size(A,2) == size(x,1) ||
        throw(DimensionMismatch(LazyString("second dimension of A, ", size(A,2), ", does not match length of x, ", length(x))))
end
@noinline function check_matmul_sizes(A::AbstractMatrix, B::AbstractMatrix)
    Base.require_one_based_indexing(A, B)
    size(A,2) == size(B,1) ||
        throw(DimensionMismatch(LazyString("second dimension of A, ", size(A,2), ", does not match first dimension of B, ", size(B,1))))
end
@noinline function check_matmul_sizes(y::AbstractVector, A::AbstractMatrix, x::AbstractVector)
    Base.require_one_based_indexing(A, x, y)
    size(A,2) == size(x,1) ||
        throw(DimensionMismatch(LazyString("second dimension of A, ", size(A,2), ", does not match length of x, ", length(x))))
    size(y,1) == size(A,1) ||
        throw(DimensionMismatch(LazyString("first dimension of A, ", size(A,1), ", does not match length of y, ", length(y))))
end
@noinline function check_matmul_sizes(C::AbstractMatrix, A::AbstractMatrix, B::AbstractMatrix)
    Base.require_one_based_indexing(A, B, C)
    size(A,2) == size(B,1) ||
        throw(DimensionMismatch(LazyString("second dimension of A, ", size(A,2), ", does not match first dimension of B, ", size(B,1))))
    size(C,1) == size(A,1) && size(C,2) == size(B,2) ||
        throw(DimensionMismatch(LazyString("A has size ", size(A), ", B has size ", size(B), ", C has size ", size(C))))
end

function mul!(y::AbstractVector, A::AbstractFillMatrix, b::AbstractFillVector, alpha::Number, beta::Number)
    check_matmul_sizes(y, A, b)

    Abα = Ref(getindex_value(A) * getindex_value(b) * alpha * length(b))

    if iszero(beta)
        y .= Abα
    else
        y .= Abα .+ y .* beta
    end
    y
end

function mul!(y::StridedVector, A::StridedMatrix, b::AbstractFillVector, alpha::Number, beta::Number)
    check_matmul_sizes(y, A, b)

    bα = Ref(getindex_value(b) * alpha)

    if iszero(beta)
        y .= Ref(zero(eltype(y)))
    else
        rmul!(y, beta)
    end
    for Acol in eachcol(A)
        @. y += Acol * bα
    end
    y
end

function mul!(y::StridedVector, A::AbstractFillMatrix, b::StridedVector, alpha::Number, beta::Number)
    check_matmul_sizes(y, A, b)

    Abα = Ref(getindex_value(A) * sum(b) * alpha)

    if iszero(beta)
        y .= Abα
    else
        y .= Abα .+ y .* beta
    end
    y
end

function _mul_adjtrans!(y::AbstractVector, A::AbstractMatrix, b::AbstractFillVector, alpha, beta, f)
    bα = getindex_value(b) * alpha
    At = f(A)

    if iszero(beta)
        for (ind, Atcol) in zip(eachindex(y), eachcol(At))
            y[ind] = f(sum(Atcol)) * bα
        end
    else
        for (ind, Atcol) in zip(eachindex(y), eachcol(At))
            y[ind] = f(sum(Atcol)) * bα .+ y[ind] .* beta
        end
    end
    y
end

for (T, f) in ((:Adjoint, :adjoint), (:Transpose, :transpose))
    @eval function mul!(y::StridedVector, A::$T{<:Any, <:StridedMatrix}, b::AbstractFillVector, alpha::Number, beta::Number)
        check_matmul_sizes(y, A, b)
        _mul_adjtrans!(y, A, b, alpha, beta, $f)
    end
end

# unnecessary indirection, added for ambiguity resolution
function _mulfill!(C::AbstractMatrix, A::AbstractFillMatrix, B::AbstractFillMatrix, alpha, beta)
    check_matmul_sizes(C, A, B)
    ABα = getindex_value(A) * getindex_value(B) * alpha * size(B,1)
    if iszero(beta)
        C .= ABα
    else
        C .= ABα .+ C .* beta
    end
    return C
end

function mul!(C::AbstractMatrix, A::AbstractFillMatrix, B::AbstractFillMatrix, alpha::Number, beta::Number)
    _mulfill!(C, A, B, alpha, beta)
    return C
end

function copyfirstcol!(C)
    @views for i in axes(C,2)[2:end]
        C[:, i] .= C[:, 1]
    end
    return C
end

_firstcol(C::AbstractMatrix) = first(eachcol(C))

function copyfirstrow!(C)
    # C[begin+1:end, ind] .= permutedims(_firstrow(C))
    # we loop here as the aliasing check isn't smart enough to
    # detect that the two sides don't alias, and ends up materializing the RHS
    for (ind, v) in pairs(_firstrow(C))
        C[begin+1:end, ind] .= Ref(v)
    end
    return C
end
_firstrow(C::AbstractMatrix) = first(eachrow(C))

function _mulfill!(C::AbstractMatrix, A::AbstractMatrix, B::AbstractFillMatrix, alpha, beta)
    check_matmul_sizes(C, A, B)
    iszero(size(B,2)) && return C # no columns in B and C, empty matrix
    if iszero(beta)
        # the mat-vec product sums along the rows of A
        mul!(_firstcol(C), A, _firstcol(B), alpha, beta)
        copyfirstcol!(C)
    else
        # the mat-vec product sums along the rows of A, which produces the first column of ABα
        # allocate a temporary column vector to store the result
        v = A * (_firstcol(B) * alpha)
        C .= v .+ C .* beta
    end
    return C
end
function _mulfill!(C::AbstractMatrix, A::AbstractFillMatrix, B::AbstractMatrix, alpha, beta)
    check_matmul_sizes(C, A, B)
    iszero(size(A,1)) && return C # no rows in A and C, empty matrix
    Aval = getindex_value(A)
    if iszero(beta)
        Crow = _firstrow(C)
        # sum along the columns of B
        Crow .= Ref(Aval) .* sum.(eachcol(B)) .* alpha
        copyfirstrow!(C)
    else
        # sum along the columns of B, and allocate the result.
        # This is the first row of ABα
        ABα_row = Ref(Aval) .* sum.(eachcol(B)) .* alpha
        C .= permutedims(ABα_row) .+ C .* beta
    end
    return C
end

function mul!(C::StridedMatrix, A::StridedMatrix, B::AbstractFillMatrix, alpha::Number, beta::Number)
    _mulfill!(C, A, B, alpha, beta)
    return C
end
function mul!(C::StridedMatrix, A::AbstractFillMatrix, B::StridedMatrix, alpha::Number, beta::Number)
    _mulfill!(C, A, B, alpha, beta)
    return C
end

for T in (:Adjoint, :Transpose)
    @eval begin
        function mul!(C::StridedMatrix, A::$T{<:Any, <:StridedMatrix}, B::AbstractFillMatrix, alpha::Number, beta::Number)
            _mulfill!(C, A, B, alpha, beta)
            return C
        end
        function mul!(C::StridedMatrix, A::AbstractFillMatrix, B::$T{<:Any, <:StridedMatrix}, alpha::Number, beta::Number)
            _mulfill!(C, A, B, alpha, beta)
            return C
        end
    end
end

function _adjvec_mul_zeros(a, b)
    la, lb = length(a), length(b)
    if la ≠ lb
        throw(DimensionMismatch(LazyString("dot product arguments have lengths ", la, " and ", lb)))
    end
    # ensure that all the elements of `a` are of the same size,
    # so that ∑ᵢaᵢbᵢ = b₁∑ᵢaᵢ makes sense
    if la == 0
        # this errors if a is a nested array, and zero isn't well-defined
        return zero(eltype(a)) * zero(eltype(b))
    end
    a1 = a[1]
    sza1 = size(a1)
    all(x -> size(x) == sza1, a) || throw(DimensionMismatch(LazyString("not all elements of A are of size ", sza1)))
    # we replace b₁∑ᵢaᵢ by b₁a₁, as we know that b₁ is zero.
    # Each term in the summation is zero, so the sum is equal to the first term
    return a1 * b[1]
end

for MT in (:AbstractMatrix, :AbstractTriangular, :(Adjoint{<:Any,<:TransposeAbsVec}), :AbstractFillMatrix)
    @eval *(a::AdjointAbsVec{<:Any,<:AbstractZerosVector}, b::$MT) = (b' * a')'
end
# ambiguity
function *(a::AdjointAbsVec{<:Any,<:AbstractZerosVector}, b::TransposeAbsVec{<:Any,<:AdjointAbsVec})
    # change from Transpose ∘ Adjoint to Adjoint ∘ Transpose
    b2 = adjoint(transpose(adjoint(transpose(b))))
    a * b2
end
*(a::AdjointAbsVec{<:Any,<:AbstractZerosVector}, b::AbstractZerosMatrix) = (b' * a')'
for MT in (:AbstractMatrix, :AbstractTriangular, :(Transpose{<:Any,<:AdjointAbsVec}), :AbstractFillMatrix)
    @eval *(a::TransposeAbsVec{<:Any,<:AbstractZerosVector}, b::$MT) = transpose(transpose(b) * transpose(a))
end
*(a::TransposeAbsVec{<:Any,<:AbstractZerosVector}, b::AbstractZerosMatrix) = transpose(transpose(b) * transpose(a))

*(a::AbstractVector, b::AdjOrTransAbsVec{<:Any,<:AbstractZerosVector}) = a * permutedims(parent(b))
for MT in (:AbstractMatrix, :AbstractTriangular)
    @eval *(a::$MT, b::AdjOrTransAbsVec{<:Any,<:AbstractZerosVector}) = a * permutedims(parent(b))
end
*(a::AbstractZerosVector, b::AdjOrTransAbsVec{<:Any,<:AbstractZerosVector}) = a * permutedims(parent(b))
*(a::AbstractZerosMatrix, b::AdjOrTransAbsVec{<:Any,<:AbstractZerosVector}) = a * permutedims(parent(b))

*(a::AdjointAbsVec, b::AbstractZerosVector) = _adjvec_mul_zeros(a, b)
*(a::AdjointAbsVec{<:Number}, b::AbstractZerosVector{<:Number}) = _adjvec_mul_zeros(a, b)
*(a::TransposeAbsVec, b::AbstractZerosVector) = _adjvec_mul_zeros(a, b)
*(a::TransposeAbsVec{<:Number}, b::AbstractZerosVector{<:Number}) = _adjvec_mul_zeros(a, b)

*(a::Adjoint{T, <:AbstractMatrix{T}} where T, b::AbstractZeros{<:Any, 1}) = mult_zeros(a, b)

*(D::Diagonal, a::Adjoint{<:Any,<:AbstractZerosVector}) = (a' * D')'
*(D::Diagonal, a::Transpose{<:Any,<:AbstractZerosVector}) = transpose(transpose(a) * transpose(D))
*(a::AdjointAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal) = (D' * a')'
*(a::TransposeAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal) = transpose(D*transpose(a))
function _triple_zeromul(x, D::Diagonal, y)
    if !(length(x) == length(D.diag) == length(y))
        throw(DimensionMismatch(LazyString("x has length ", length(x), ", D has size ", size(D), ", and y has ", length(y))))
    end
    zero(promote_type(eltype(x), eltype(D), eltype(y)))
end

*(x::AdjointAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal, y::AbstractVector) = _triple_zeromul(x, D, y)
*(x::TransposeAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal, y::AbstractVector) = _triple_zeromul(x, D, y)
*(x::AdjointAbsVec, D::Diagonal, y::AbstractZerosVector) = _triple_zeromul(x, D, y)
*(x::TransposeAbsVec, D::Diagonal, y::AbstractZerosVector) = _triple_zeromul(x, D, y)
*(x::AdjointAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal, y::AbstractZerosVector) = _triple_zeromul(x, D, y)
*(x::TransposeAbsVec{<:Any,<:AbstractZerosVector}, D::Diagonal, y::AbstractZerosVector) = _triple_zeromul(x, D, y)


function *(a::Transpose{T, <:AbstractVector}, b::AbstractZerosVector{T}) where T<:Real
    la, lb = length(a), length(b)
    if la ≠ lb
        throw(DimensionMismatch(LazyString("dot product arguments have lengths ", la, " and ", lb)))
    end
    return zero(T)
end
*(a::Transpose{T, <:AbstractMatrix{T}}, b::AbstractZerosVector{T}) where T<:Real = mult_zeros(a, b)

# support types with fast sum
# infinite cases should be supported in InfiniteArrays.jl
# type issues of Bool dot are ignored at present.
function _fill_dot(a::AbstractFillVector{T}, b::AbstractVector{V}) where {T,V}
    axes(a) == axes(b) || throw(DimensionMismatch(LazyString("dot product arguments have lengths ", length(a), " and ", length(b))))
    dot(getindex_value(a), sum(b))
end

function _fill_dot_rev(a::AbstractVector{T}, b::AbstractFillVector{V}) where {T,V}
    axes(a) == axes(b) || throw(DimensionMismatch(LazyString("dot product arguments have lengths ", length(a), " and ", length(b))))
    dot(sum(a), getindex_value(b))
end

dot(a::AbstractFillVector, b::AbstractFillVector) = _fill_dot(a, b)
dot(a::AbstractFillVector, b::AbstractVector) = _fill_dot(a, b)
dot(a::AbstractVector, b::AbstractFillVector) = _fill_dot_rev(a, b)

function dot(u::AbstractVector, E::Eye, v::AbstractVector)
    length(u) == size(E,1) && length(v) == size(E,2) ||
        throw(DimensionMismatch(LazyString("dot product arguments have dimensions ", length(u), "×", size(E), "×", length(v))))
    d = dot(u,v)
    T = typeof(one(eltype(E)) * d)
    convert(T, d)
end

function dot(u::AbstractVector, D::Diagonal{<:Any,<:Fill}, v::AbstractVector)
    length(u) == size(D,1) && length(v) == size(D,2) ||
        throw(DimensionMismatch(LazyString("dot product arguments have dimensions ", length(u), "×", size(D), "×", length(v))))
    D.diag.value*dot(u, v)
end

function dot(u::AbstractVector{T}, D::Diagonal{U,<:Zeros}, v::AbstractVector{V}) where {T,U,V}
    length(u) == size(D,1) && length(v) == size(D,2) ||
        throw(DimensionMismatch(LazyString("dot product arguments have dimensions ", length(u), "×", size(D), "×", length(v))))
    zero(promote_type(T,U,V))
end

# Addition and Subtraction
+(a::AbstractFill) = a
-(a::AbstractZeros) = a
-(a::AbstractFill) = Fill(-getindex_value(a), size(a))

# special-cased for type-stability, as Ones + Ones is not a Ones
Base.reduce_first(::typeof(+), x::AbstractOnes) = Fill(Base.reduce_first(+, getindex_value(x)), axes(x))

function +(a::AbstractZeros{T}, b::AbstractZeros{V}) where {T, V} # for disambiguity
    promote_shape(a,b)
    return elconvert(promote_op(+,T,V),a)
end

function -(a::AbstractZeros{T}, b::AbstractZeros{V}) where {T, V} # for disambiguity
    promote_shape(a,b)
    return elconvert(promote_op(-,T,V),-b)
end

# AbstractFill and Array for disambiguity
for TYPE in (:Array, :AbstractFill, :AbstractRange, :AbstractArray)
    @eval function +(a::$TYPE{T}, b::AbstractZeros{V}) where {T, V}
        promote_shape(a,b)
        return elconvert(promote_op(+,T,V),a)
    end
     @eval function -(a::$TYPE{T}, b::AbstractZeros{V}) where {T, V}
        promote_shape(a,b)
        return elconvert(promote_op(-,T,V),a)
    end
    @eval function -(a::AbstractZeros{T}, b::$TYPE{V}) where {T, V}
        promote_shape(a,b)
        return elconvert(promote_op(-,T,V),-b)
    end
    @eval +(a::AbstractZeros, b::$TYPE) = b + a
end

# for VERSION other than 1.6, could use ZerosMatrix only
function +(a::AbstractFillMatrix{T}, b::UniformScaling) where {T}
    n = checksquare(a)
    return a + Diagonal(Fill(zero(T) + b.λ, n))
end

# LinearAlgebra defines `-(a::AbstractMatrix, b::UniformScaling) = a + (-b)`,
# so the implementation of `-(a::UniformScaling, b::AbstractFill{<:Any,2})` is sufficient
-(a::UniformScaling, b::AbstractFill) = -b + a # @test I-Zeros(3,3) === Diagonal(Ones(3))

# TODO: How to do this conversion generically?
-(a::AbstractOnes, b::AbstractOnes) = broadcasted_zeros(+, a, eltype(a), axes(a)) + broadcasted_zeros(-, b, eltype(a), axes(a))

# no AbstractArray. Otherwise incompatible with StaticArrays.jl
for TYPE in (:Array, :AbstractRange)
    @eval begin
        +(a::$TYPE, b::AbstractFill) = fill_add(a, b)
        -(a::$TYPE, b::AbstractFill) = a + (-b)
        +(a::AbstractFill, b::$TYPE) = fill_add(b, a)
        -(a::AbstractFill, b::$TYPE) = a + (-b)
    end
end
+(a::AbstractFill, b::AbstractFill) = Fill(getindex_value(a) + getindex_value(b), promote_shape(a,b))
-(a::AbstractFill, b::AbstractFill) = a + (-b)

@inline function fill_add(a::AbstractArray, b::AbstractFill)
    promote_shape(a, b)
    a .+ (getindex_value(b),)
end
@inline function fill_add(a::AbstractArray{<:Number}, b::AbstractFill)
    promote_shape(a, b)
    a .+ getindex_value(b)
end

# following needed since as of Julia v1.8 convert(AbstractArray{T}, ::AbstractRange) might return a Vector
@inline elconvert(::Type{T}, A::AbstractRange) where T = T(first(A)):T(step(A)):T(last(A))
@inline elconvert(::Type{T}, A::AbstractUnitRange) where T<:Integer = AbstractUnitRange{T}(A)
@inline elconvert(::Type{T}, A::AbstractArray) where T = AbstractArray{T}(A)

####
# norm
####

for op in (:norm1, :norm2, :normInf, :normMinusInf)
    @eval $op(a::AbstractZeros) = norm(getindex_value(a))
end

normp(a::AbstractZeros, p) = norm(getindex_value(a))

norm1(a::AbstractFill) = length(a)*norm(getindex_value(a))
function norm2(a::AbstractFill)
    nrm1 = norm(getindex_value(a))
    sqrt(oftype(nrm1, length(a)))*nrm1
end
function normp(a::AbstractFill, p)
    nrm1 = norm(getindex_value(a))
    (length(a))^(1/oftype(nrm1, p))*nrm1
end
normInf(a::AbstractFill) = norm(getindex_value(a))
normMinusInf(a::AbstractFill) = norm(getindex_value(a))


###
# lmul!/rmul!
###

function lmul!(x::Number, z::AbstractFill)
    λ = getindex_value(z)
    # Following check ensures consistency w/ lmul!(x, Array(z))
    # for, e.g., lmul!(NaN, z)
    x*λ == λ || throw(ArgumentError(LazyString("Cannot scale by ", x)))
    z
end

function rmul!(z::AbstractFill, x::Number)
    λ = getindex_value(z)
    # Following check ensures consistency w/ lmul!(x, Array(z))
    # for, e.g., lmul!(NaN, z)
    λ*x == λ || throw(ArgumentError(LazyString("Cannot scale by ", x)))
    z
end

fillzero(::Type{Fill{T,N,AXIS}}, n, m) where {T,N,AXIS} = Fill{T,N,AXIS}(zero(T), (n, m))
fillzero(::Type{<:AbstractZeros{T,N,AXIS}}, n, m) where {T,N,AXIS} = Zeros{T,N,AXIS}((n, m))
fillzero(::Type{F}, n, m) where F = throw(ArgumentError(LazyString("Cannot create a zero array of type ", F)))

diagzero(D::Diagonal{F}, i, j) where F<:AbstractFill = fillzero(F, axes(D.diag[i], 1), axes(D.diag[j], 2))

# kron

# Default outputs, can overload to customize
kron_fill(a, b, val, ax) = Fill(val, ax)
kron_zeros(a, b, elt, ax) = Zeros{elt}(ax)
kron_ones(a, b, elt, ax) = Ones{elt}(ax)

_kronsize(f::AbstractFillVector, g::AbstractFillVector) = (size(f,1)*size(g,1),)
_kronsize(f::AbstractFillVecOrMat, g::AbstractFillVecOrMat) = (size(f,1)*size(g,1), size(f,2)*size(g,2))
function _kron(f::AbstractFill, g::AbstractFill, sz)
    v = getindex_value(f)*getindex_value(g)
    return kron_fill(f, g, v, sz)
end
function _kron(f::AbstractZeros, g::AbstractZeros, sz)
    elt = promote_type(eltype(f), eltype(g))
    return kron_zeros(f, g, elt, sz)
end
function _kron(f::AbstractOnes, g::AbstractOnes, sz)
    elt = promote_type(eltype(f), eltype(g))
    return kron_ones(f, g, elt, sz)
end
function kron(f::AbstractFillVecOrMat, g::AbstractFillVecOrMat)
    sz = _kronsize(f, g)
    return _kron(f, g, sz)
end

# bandedness
function LinearAlgebra.istriu(A::AbstractFillMatrix, k::Integer = 0)
    iszero(A) || k <= -(size(A,1)-1)
end
function LinearAlgebra.istril(A::AbstractFillMatrix, k::Integer = 0)
    iszero(A) || k >= size(A,2)-1
end

triu(A::AbstractZerosMatrix, k::Integer=0) = A
tril(A::AbstractZerosMatrix, k::Integer=0) = A


================================================
FILE: src/fillbroadcast.jl
================================================
### map

map(f::Function, r::AbstractFill) = Fill(f(getindex_value(r)), axes(r))

function map(f::Function, v::AbstractFillVector, ws::AbstractFillVector...)
    stop = mapreduce(length, min, (v, ws...))
    val = f(map(getindex_value, (v, ws...))...)
    Fill(val, stop)
end

function map(f::Function, q::AbstractFill, rs::AbstractFill...)
    if _maplinear(q, rs...)
        map(f, map(vec, (q, rs...))...)
    else
        val = f(map(getindex_value, (q, rs...))...)
        Fill(val, axes(q))
    end
end

function _maplinear(rs...) # tries to match Base's behaviour, could perhaps hook in more deeply
    if any(ndims(r)==1 for r in rs)
        return true
    else
        r1 = axes(first(rs))
        for r in rs
            axes(r) == r1 || throw(DimensionMismatch(
            LazyString("dimensions must match: a has dims ", r1, ", b has dims ", axes(r))))
        end
        return false
    end
end

### mapreduce

function Base._mapreduce_dim(f, op, ::Base._InitialValue, A::AbstractFill, ::Colon)
    fval = f(getindex_value(A))
    out = fval
    for _ in 2:length(A)
        out = op(out, fval)
    end
    out
end

function Base._mapreduce_dim(f, op, ::Base._InitialValue, A::AbstractFill, dims)
    fval = f(getindex_value(A))
    red = *(ntuple(d -> d in dims ? size(A,d) : 1, ndims(A))...)
    out = fval
    for _ in 2:red
        out = op(out, fval)
    end
    Fill(out, ntuple(d -> d in dims ? Base.OneTo(1) : axes(A,d), ndims(A)))
end

function mapreduce(f, op, A::AbstractFill, B::AbstractFill; kw...)
    val(_...) = f(getindex_value(A), getindex_value(B))
    reduce(op, map(val, A, B); kw...)
end

# These are particularly useful because mapreduce(*, +, A, B; dims) is slow in Base,
# but can be re-written as some mapreduce(g, +, C; dims) which is fast.

function mapreduce(f, op, A::AbstractFill, B::AbstractArray, Cs::AbstractArray...; kw...)
    g(b, cs...) = f(getindex_value(A), b, cs...)
    mapreduce(g, op, B, Cs...; kw...)
end
function mapreduce(f, op, A::AbstractArray, B::AbstractFill, Cs::AbstractArray...; kw...)
    h(a, cs...) = f(a, getindex_value(B), cs...)
    mapreduce(h, op, A, Cs...; kw...)
end
function mapreduce(f, op, A::AbstractFill, B::AbstractFill, Cs::AbstractArray...; kw...)
    gh(cs...) = f(getindex_value(A), getindex_value(B), cs...)
    mapreduce(gh, op, Cs...; kw...)
end


### Unary broadcasting

function broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}) where {T,N}
    return Fill(op(getindex_value(r)), axes(r))
end

broadcasted(::DefaultArrayStyle, ::typeof(+), r::AbstractZeros) = r
broadcasted(::DefaultArrayStyle, ::typeof(-), r::AbstractZeros) = r
broadcasted(::DefaultArrayStyle, ::typeof(+), r::AbstractOnes) = r

broadcasted(::DefaultArrayStyle{N}, ::typeof(conj), r::AbstractZeros{T,N}) where {T,N} = r
broadcasted(::DefaultArrayStyle{N}, ::typeof(conj), r::AbstractOnes{T,N}) where {T,N} = r
broadcasted(::DefaultArrayStyle{N}, ::typeof(real), r::AbstractZeros{T,N}) where {T,N} = Zeros{real(T)}(axes(r))
broadcasted(::DefaultArrayStyle{N}, ::typeof(real), r::AbstractOnes{T,N}) where {T,N} = Ones{real(T)}(axes(r))
broadcasted(::DefaultArrayStyle{N}, ::typeof(imag), r::AbstractZeros{T,N}) where {T,N} = Zeros{real(T)}(axes(r))
broadcasted(::DefaultArrayStyle{N}, ::typeof(imag), r::AbstractOnes{T,N}) where {T,N} = Zeros{real(T)}(axes(r))

### Binary broadcasting

# Default outputs, can overload to customize
broadcasted_fill(f, a, val, ax) = Fill(val, ax)
broadcasted_fill(f, a, b, val, ax) = Fill(val, ax)
broadcasted_zeros(f, a, elt, ax) = Zeros{elt}(ax)
broadcasted_zeros(f, a, b, elt, ax) = Zeros{elt}(ax)
broadcasted_ones(f, a, elt, ax) = Ones{elt}(ax)
broadcasted_ones(f, a, b, elt, ax) = Ones{elt}(ax)

function broadcasted(::DefaultArrayStyle, op, a::AbstractFill, b::AbstractFill)
    val = op(getindex_value(a), getindex_value(b))
    ax = broadcast_shape(axes(a), axes(b))
    return broadcasted_fill(op, a, b, val, ax)
end

function _broadcasted_zeros(f, a, b)
  elt = Base.Broadcast.combine_eltypes(f, (a, b))
  ax = broadcast_shape(axes(a), axes(b))
  return broadcasted_zeros(f, a, b, elt, ax)
end
function _broadcasted_ones(f, a, b)
  elt = Base.Broadcast.combine_eltypes(f, (a, b))
  ax = broadcast_shape(axes(a), axes(b))
  return broadcasted_ones(f, a, b, elt, ax)
end
function _broadcasted_nan(f, a, b)
  val = convert(Base.Broadcast.combine_eltypes(f, (a, b)), NaN)
  ax = broadcast_shape(axes(a), axes(b))
  return broadcasted_fill(f, a, b, val, ax)
end

broadcasted(::DefaultArrayStyle, ::typeof(+), a::AbstractZeros, b::AbstractZeros) = _broadcasted_zeros(+, a, b)
broadcasted(::DefaultArrayStyle, ::typeof(+), a::AbstractOnes, b::AbstractZeros) = _broadcasted_ones(+, a, b)
broadcasted(::DefaultArrayStyle, ::typeof(+), a::AbstractZeros, b::AbstractOnes) = _broadcasted_ones(+, a, b)

broadcasted(::DefaultArrayStyle, ::typeof(-), a::AbstractZeros, b::AbstractZeros) = _broadcasted_zeros(-, a, b)
broadcasted(::DefaultArrayStyle, ::typeof(-), a::AbstractOnes, b::AbstractZeros) = _broadcasted_ones(-, a, b)
broadcasted(::DefaultArrayStyle, ::typeof(-), a::AbstractOnes, b::AbstractOnes) = _broadcasted_zeros(-, a, b)

broadcasted(::DefaultArrayStyle{1}, ::typeof(+), a::AbstractZerosVector, b::AbstractZerosVector) = _broadcasted_zeros(+, a, b)
broadcasted(::DefaultArrayStyle{1}, ::typeof(+), a::AbstractOnesVector, b::AbstractZerosVector) = _broadcasted_ones(+, a, b)
broadcasted(::DefaultArrayStyle{1}, ::typeof(+), a::AbstractZerosVector, b::AbstractOnesVector) = _broadcasted_ones(+, a, b)

broadcasted(::DefaultArrayStyle{1}, ::typeof(-), a::AbstractZerosVector, b::AbstractZerosVector) = _broadcasted_zeros(-, a, b)
broadcasted(::DefaultArrayStyle{1}, ::typeof(-), a::AbstractOnesVector, b::AbstractZerosVector) = _broadcasted_ones(-, a, b)


broadcasted(::DefaultArrayStyle, ::typeof(*), a::AbstractZeros, b::AbstractZeros) = _broadcasted_zeros(*, a, b)

# In following, need to restrict to <: Number as otherwise we cannot infer zero from type
# TODO: generalise to things like SVector
for op in (:*, :/)
    @eval begin
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::AbstractOnes) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::AbstractFill{<:Number}) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::Number) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::AbstractRange) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::AbstractArray{<:Number}) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros, b::Base.Broadcast.Broadcasted) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractZeros, b::AbstractRange) = _broadcasted_zeros($op, a, b)
    end
end

for op in (:*, :\)
    @eval begin
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractOnes, b::AbstractZeros) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractFill{<:Number}, b::AbstractZeros) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::Number, b::AbstractZeros) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractRange, b::AbstractZeros) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractArray{<:Number}, b::AbstractZeros) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle, ::typeof($op), a::Base.Broadcast.Broadcasted, b::AbstractZeros) = _broadcasted_zeros($op, a, b)
        broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractRange, b::AbstractZeros) = _broadcasted_zeros($op, a, b)
    end
end

for op in (:*, :/, :\)
    @eval broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractOnes, b::AbstractOnes) = _broadcasted_ones($op, a, b)
end

for op in (:/, :\)
    @eval broadcasted(::DefaultArrayStyle, ::typeof($op), a::AbstractZeros{<:Number}, b::AbstractZeros{<:Number}) = _broadcasted_nan($op, a, b)
end

# special case due to missing converts for ranges
_range_convert(::Type{AbstractVector{T}}, a::AbstractRange{T}) where T = a
_range_convert(::Type{AbstractVector{T}}, a::AbstractUnitRange) where T = convert(T,first(a)):convert(T,last(a))
_range_convert(::Type{AbstractVector{T}}, a::OneTo) where T = OneTo(convert(T, a.stop))
_range_convert(::Type{AbstractVector{T}}, a::AbstractRange) where T = convert(T,first(a)):step(a):convert(T,last(a))
_range_convert(::Type{AbstractVector{T}}, a::ZerosVector) where T = ZerosVector{T}(length(a))


# TODO: replacing with the following will support more general broadcasting.
# function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractFill, b::AbstractRange)
#     broadcast_shape(axes(a), axes(b)) # check axes
#     r1 = b[1] * getindex_value(a)
#     T = typeof(r1)
#     if length(b) == 1 # Need a fill, but for type stability use StepRangeLen
#         StepRangeLen{T}(r1, zero(T), length(a))
#     else
#         StepRangeLen{T}(r1, convert(T, getindex_value(a) * step(b)), length(b))
#     end
# end

# function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractRange, b::AbstractFill)
#     broadcast_shape(axes(a), axes(b)) # check axes
#     r1 = a[1] * getindex_value(b)
#     T = typeof(r1)
#     if length(a) == 1 # Need a fill, but for type stability use StepRangeLen
#         StepRangeLen{T}(r1, zero(T), length(b))
#     else
#         StepRangeLen{T}(r1, convert(T, step(a) * getindex_value(b)), length(a))
#     end
# end

function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractOnesVector, b::AbstractRange)
    broadcast_shape(axes(a), axes(b)) == axes(b) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first.")))
    TT = typeof(zero(eltype(a)) * zero(eltype(b)))
    return _range_convert(AbstractVector{TT}, b)
end

function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractRange, b::AbstractOnesVector)
    broadcast_shape(axes(a), axes(b)) == axes(a) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first.")))
    TT = typeof(zero(eltype(a)) * zero(eltype(b)))
    return _range_convert(AbstractVector{TT}, a)
end

for op in (:+, :-)
    @eval begin
        function broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractVector, b::AbstractZerosVector)
            broadcast_shape(axes(a), axes(b)) == axes(a) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first.")))
            TT = typeof($op(zero(eltype(a)), zero(eltype(b))))
            # Use `TT ∘ (+)` to fix AD issues with `broadcasted(TT, x)`
            eltype(a) === TT ? a : broadcasted(TT ∘ (+), a)
        end
        function broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractZerosVector, b::AbstractVector)
            broadcast_shape(axes(a), axes(b)) == axes(b) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", a, " to a Vector first.")))
            TT = typeof($op(zero(eltype(a)), zero(eltype(b))))
            $op === (+) && eltype(b) === TT ? b : broadcasted(TT ∘ ($op), b)
        end

        broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractFillVector, b::AbstractZerosVector) =
            Base.invoke(broadcasted, Tuple{DefaultArrayStyle, typeof($op), AbstractFill, AbstractFill}, DefaultArrayStyle{1}(), $op, a, b)

        broadcasted(::DefaultArrayStyle{1}, ::typeof($op), a::AbstractZerosVector, b::AbstractFillVector) =
            Base.invoke(broadcasted, Tuple{DefaultArrayStyle, typeof($op), AbstractFill, AbstractFill}, DefaultArrayStyle{1}(), $op, a, b)
    end
end

# Need to prevent array-valued fills from broadcasting over entry
_broadcast_getindex_value(a::AbstractFill{<:Number}) = getindex_value(a)
_broadcast_getindex_value(a::AbstractFill) = Ref(getindex_value(a))


function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractFill, b::AbstractRange)
    broadcast_shape(axes(a), axes(b)) == axes(b) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first.")))
    return broadcasted(*, _broadcast_getindex_value(a), b)
end

function broadcasted(::DefaultArrayStyle{1}, ::typeof(*), a::AbstractRange, b::AbstractFill)
    broadcast_shape(axes(a), axes(b)) == axes(a) || throw(ArgumentError(LazyString("Cannot broadcast ", a, " and ", b, ". Convert ", b, " to a Vector first.")))
    return broadcasted(*, a, _broadcast_getindex_value(b))
end

broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}, x::Number) where {T,N} = broadcasted_fill(op, r, op(getindex_value(r),x), axes(r))
broadcasted(::DefaultArrayStyle{N}, op, x::Number, r::AbstractFill{T,N}) where {T,N} = broadcasted_fill(op, r, op(x, getindex_value(r)), axes(r))
broadcasted(::DefaultArrayStyle{N}, op, r::AbstractFill{T,N}, x::Ref) where {T,N} = broadcasted_fill(op, r, op(getindex_value(r),x[]), axes(r))
broadcasted(::DefaultArrayStyle{N}, op, x::Ref, r::AbstractFill{T,N}) where {T,N} = broadcasted_fill(op, r, op(x[], getindex_value(r)), axes(r))

# support AbstractFill .^ k
broadcasted(::DefaultArrayStyle{N}, op::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::AbstractFill{T,N}, ::Base.RefValue{Val{k}}) where {T,N,k} = broadcasted_fill(op, r, getindex_value(r)^k, axes(r))
broadcasted(::DefaultArrayStyle{N}, op::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::AbstractOnes{T,N}, ::Base.RefValue{Val{k}}) where {T,N,k} = broadcasted_ones(op, r, T, axes(r))
broadcasted(::DefaultArrayStyle{N}, op::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::AbstractZeros{T,N}, ::Base.RefValue{Val{0}}) where {T,N} = broadcasted_ones(op, r, T, axes(r))
broadcasted(::DefaultArrayStyle{N}, op::typeof(Base.literal_pow), ::Base.RefValue{typeof(^)}, r::AbstractZeros{T,N}, ::Base.RefValue{Val{k}}) where {T,N,k} = broadcasted_zeros(op, r, T, axes(r))

# supports structured broadcast
if isdefined(LinearAlgebra, :fzero)
    LinearAlgebra.fzero(x::AbstractZeros) = zero(eltype(x))
end


================================================
FILE: src/oneelement.jl
================================================
"""
    OneElement(val, ind, axesorsize) <: AbstractArray

Represents an array with the specified axes (if its a tuple of `AbstractUnitRange`s)
or size (if its a tuple of `Integer`s), with a single entry set to `val` and all others equal to zero,
specified by `ind``.
"""
struct OneElement{T,N,I,A} <: AbstractArray{T,N}
  val::T
  ind::I
  axes::A
  OneElement(val::T, ind::I, axes::A) where {T, I<:NTuple{N,Int}, A<:NTuple{N,AbstractUnitRange}} where {N} = new{T,N,I,A}(val, ind, axes)
  OneElement(val::T, ind::Tuple{}, axes::Tuple{}) where {T} = new{T,0,Tuple{},Tuple{}}(val, ind, axes)
end

const OneElementVector{T,I,A} = OneElement{T,1,I,A}
const OneElementMatrix{T,I,A} = OneElement{T,2,I,A}
const OneElementVecOrMat{T,I,A} = Union{OneElementVector{T,I,A}, OneElementMatrix{T,I,A}}

OneElement(val, inds::NTuple{N,Int}, sz::NTuple{N,Integer}) where N = OneElement(val, inds, oneto.(sz))
"""
    OneElement(val, ind::Int, n::Integer)

Creates a length `n` vector where the `ind` entry is equal to `val`, and all other entries are zero.
"""
OneElement(val, ind::Int, len::Integer) = OneElement(val, (ind,), (len,))
"""
    OneElement(ind::Int, n::Integer)

Creates a length `n` vector where the `ind` entry is equal to `1`, and all other entries are zero.
"""
OneElement(inds::Int, sz::Integer) = OneElement(1, inds, sz)
OneElement{T}(val, inds::NTuple{N,Int}, sz::NTuple{N,Integer}) where {T,N} = OneElement(convert(T,val), inds, oneto.(sz))
OneElement{T}(val, inds::Int, sz::Integer) where T = OneElement{T}(val, (inds,), (sz,))

"""
    OneElement{T}(ind::Int, n::Int)

Creates a length `n` vector where the `ind` entry is equal to `one(T)`, and all other entries are zero.
"""
OneElement{T}(inds::Int, sz::Integer) where T = OneElement(one(T), inds, sz)

Base.size(A::OneElement) = map(length, A.axes)
Base.axes(A::OneElement) = A.axes
Base.getindex(A::OneElement{T,0}) where {T} = getindex_value(A)
Base.@propagate_inbounds function Base.getindex(A::OneElement{T,N}, kj::Vararg{Int,N}) where {T,N}
    @boundscheck checkbounds(A, kj...)
    ifelse(kj == A.ind, A.val, zero(T))
end
const VectorInds = Union{AbstractUnitRange{<:Integer}, Integer} # no index is repeated for these indices
const VectorIndsWithColon = Union{VectorInds, Colon}
# retain the values from Ainds corresponding to the vector indices in inds
_index_shape(Ainds, inds::Tuple{Integer, Vararg{Any}}) = _index_shape(Base.tail(Ainds), Base.tail(inds))
_index_shape(Ainds, inds::Tuple{AbstractVector, Vararg{Any}}) = (Ainds[1], _index_shape(Base.tail(Ainds), Base.tail(inds))...)
_index_shape(::Tuple{}, ::Tuple{}) = ()
Base.@propagate_inbounds function Base.getindex(A::OneElement{T,N}, inds::Vararg{VectorIndsWithColon,N}) where {T,N}
    I = to_indices(A, inds) # handle Bool, and convert to compatible index types
    @boundscheck checkbounds(A, I...)
    shape = _index_shape(I, I)
    nzind = _index_shape(A.ind, I) .- first.(shape) .+ firstindex.(shape)
    containsval = all(in.(A.ind, I))
    OneElement(getindex_value(A), containsval ? Int.(nzind) : Int.(lastindex.(shape,1)).+1, axes.(shape,1))
end

"""
    nzind(A::OneElement{T,N}) -> CartesianIndex{N}

Return the index where `A` contains a non-zero value.

!!! note
    The indices are not guaranteed to lie within the valid index bounds for `A`,
    and if `FillArrays.nzind(A) ∉ CartesianIndices(A)` then `all(iszero, A)`.
    On the other hand, if `FillArrays.nzind(A) in CartesianIndices(A)` then
    `A[FillArrays.nzind(A)] == FillArrays.getindex_value(A)`

# Examples
```jldoctest
julia> A = OneElement(2, (1,2), (2,2))
2×2 OneElement{Int64, 2, Tuple{Int64, Int64}, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}:
 ⋅  2
 ⋅  ⋅

julia> FillArrays.nzind(A)
CartesianIndex(1, 2)

julia> A[FillArrays.nzind(A)]
2
```
"""
nzind(f::OneElement) = CartesianIndex(f.ind)

"""
    getindex_value(A::OneElement)

Return the only non-zero value stored in `A`.

!!! note
    If the index at which the value is stored doesn't lie within the valid indices of `A`, then
    this returns `zero(eltype(A))`.

# Examples
```jldoctest
julia> A = OneElement(2, 3)
3-element OneElement{Int64, 1, Tuple{Int64}, Tuple{Base.OneTo{Int64}}}:
 ⋅
 1
 ⋅

julia> FillArrays.getindex_value(A)
1
```
"""
getindex_value(A::OneElement) = all(in.(A.ind, axes(A))) ? A.val : zero(eltype(A))

@inline function Base.isassigned(F::OneElement, i::Integer...)
    @boundscheck checkbounds(Bool, F, to_indices(F, i)...) || return false
    return true
end

Base.AbstractArray{T,N}(A::OneElement{<:Any,N}) where {T,N} = OneElement(T(A.val), A.ind, A.axes)

Base.replace_in_print_matrix(o::OneElementVector, k::Integer, j::Integer, s::AbstractString) =
    o.ind == (k,) ? s : Base.replace_with_centered_mark(s)

Base.replace_in_print_matrix(o::OneElementMatrix, k::Integer, j::Integer, s::AbstractString) =
    o.ind == (k,j) ? s : Base.replace_with_centered_mark(s)

Base.@propagate_inbounds function Base.setindex(A::AbstractZeros{T,N}, v, kj::Vararg{Int,N}) where {T,N}
    @boundscheck checkbounds(A, kj...)
    OneElement(convert(T, v), kj, axes(A))
end

zero(A::OneElement) = OneElement(zero(A.val), A.ind, A.axes)

iszero(A::OneElement) = iszero(getindex_value(A))

function isone(A::OneElementMatrix)
    lenA = length(A)
    lenA == 0 && return true
    lenA > 1 && return false
    isone(getindex_value(A))
end

-(O::OneElement) = OneElement(-O.val, O.ind, O.axes)

*(x::OneElement, b::Number) = OneElement(x.val * b, x.ind, x.axes)
*(b::Number, x::OneElement) = OneElement(b * x.val, x.ind, x.axes)
/(x::OneElement, b::Number) = OneElement(x.val / b, x.ind, x.axes)
\(b::Number, x::OneElement) = OneElement(b \ x.val, x.ind, x.axes)

# matrix-vector and matrix-matrix multiplication

# Fill and OneElement
function *(A::OneElementMatrix, B::OneElementVecOrMat)
    check_matmul_sizes(A, B)
    valA = getindex_value(A)
    valB = getindex_value(B)
    val = valA * valB * (A.ind[2] == B.ind[1])
    OneElement(val, (A.ind[1], B.ind[2:end]...), (axes(A,1), axes(B)[2:end]...))
end

*(A::OneElementMatrix, x::AbstractZerosVector) = mult_zeros(A, x)

function *(A::OneElementMatrix, B::AbstractFillVector)
    check_matmul_sizes(A, B)
    val = getindex_value(A) * getindex_value(B)
    OneElement(val, A.ind[1], size(A,1))
end

# Special matrix types

function *(A::OneElementMatrix, D::Diagonal)
    check_matmul_sizes(A, D)
    nzcol = A.ind[2]
    val = if nzcol in axes(D,1)
        A.val * D[nzcol, nzcol]
    else
        A.val * zero(eltype(D))
    end
    OneElement(val, A.ind, size(A))
end
function *(D::Diagonal, A::OneElementMatrix)
    check_matmul_sizes(D, A)
    nzrow = A.ind[1]
    val = if nzrow in axes(D,2)
        D[nzrow, nzrow] * A.val
    else
        zero(eltype(D)) * A.val
    end
    OneElement(val, A.ind, size(A))
end

# Inplace multiplication

# We use this for out overloads for _mul! for OneElement because its more efficient
# due to how efficient 2 arg mul is when one or more of the args are OneElement
function __mulonel!(C, A, B, alpha, beta)
    ABα = A * B * alpha
    if iszero(beta)
        C .= ABα
    else
        C .= ABα .+ C .* beta
    end
    return C
end
# These methods remove the ambituity in _mul!. This isn't strictly necessary, but this makes Aqua happy.
function _mul!(C::AbstractVector, A::OneElementMatrix, B::OneElementVector, alpha, beta)
    __mulonel!(C, A, B, alpha, beta)
end
function _mul!(C::AbstractMatrix, A::OneElementMatrix, B::OneElementMatrix, alpha, beta)
    __mulonel!(C, A, B, alpha, beta)
end

function mul!(C::AbstractMatrix, A::OneElementMatrix, B::OneElementMatrix, alpha::Number, beta::Number)
    _mul!(C, A, B, alpha, beta)
end
function mul!(C::AbstractVector, A::OneElementMatrix, B::OneElementVector, alpha::Number, beta::Number)
    _mul!(C, A, B, alpha, beta)
end

@inline function __mul!(y, A::AbstractMatrix, x::OneElement, alpha, beta)
    xα = Ref(x.val * alpha)
    ind1 = x.ind[1]
    if iszero(beta)
        y .= view(A, :, ind1) .* xα
    else
        y .= view(A, :, ind1) .* xα .+ y .* beta
    end
    return y
end

function _mul!(y::AbstractVector, A::AbstractMatrix, x::OneElementVector, alpha, beta)
    check_matmul_sizes(y, A, x)
    if iszero(getindex_value(x))
        mul!(y, A, Zeros{eltype(x)}(axes(x)), alpha, beta)
        return y
    end
    __mul!(y, A, x, alpha, beta)
    y
end

function _mul!(C::AbstractMatrix, A::AbstractMatrix, B::OneElementMatrix, alpha, beta)
    check_matmul_sizes(C, A, B)
    if iszero(getindex_value(B))
        mul!(C, A, Zeros{eltype(B)}(axes(B)), alpha, beta)
        return C
    end
    nzrow, nzcol = B.ind
    if iszero(beta)
        C .= Ref(zero(eltype(C)))
    else
        view(C, :, 1:nzcol-1) .*= beta
        view(C, :, nzcol+1:size(C,2)) .*= beta
    end
    y = view(C, :, nzcol)
    __mul!(y, A, B, alpha, beta)
    C
end
function _mul!(C::AbstractMatrix, A::Diagonal, B::OneElementMatrix, alpha, beta)
    check_matmul_sizes(C, A, B)
    if iszero(getindex_value(B))
        mul!(C, A, Zeros{eltype(B)}(axes(B)), alpha, beta)
        return C
    end
    nzrow, nzcol = B.ind
    ABα = A * B * alpha
    if iszero(beta)
        C .= Ref(zero(eltype(C)))
        C[nzrow, nzcol] = ABα[nzrow, nzcol]
    else
        view(C, :, 1:nzcol-1) .*= beta
        view(C, :, nzcol+1:size(C,2)) .*= beta
        y = view(C, :, nzcol)
        y .= view(ABα, :, nzcol) .+ y .* beta
    end
    C
end

function _mul!(C::AbstractMatrix, A::OneElementMatrix, B::AbstractMatrix, alpha, beta)
    check_matmul_sizes(C, A, B)
    if iszero(getindex_value(A))
        mul!(C, Zeros{eltype(A)}(axes(A)), B, alpha, beta)
        return C
    end
    nzrow, nzcol = A.ind
    y = view(C, nzrow, :)
    Aval = A.val
    if iszero(beta)
        C .= Ref(zero(eltype(C)))
        y .= Ref(Aval) .* view(B, nzcol, :) .* alpha
    else
        view(C, 1:nzrow-1, :) .*= beta
        view(C, nzrow+1:size(C,1), :) .*= beta
        y .= Ref(Aval) .* view(B, nzcol, :) .* alpha .+ y .* beta
    end
    C
end
function _mul!(C::AbstractMatrix, A::OneElementMatrix, B::Diagonal, alpha, beta)
    check_matmul_sizes(C, A, B)
    if iszero(getindex_value(A))
        mul!(C, Zeros{eltype(A)}(axes(A)), B, alpha, beta)
        return C
    end
    nzrow, nzcol = A.ind
    ABα = A * B * alpha
    if iszero(beta)
        C .= Ref(zero(eltype(C)))
        C[nzrow, nzcol] = ABα[nzrow, nzcol]
    else
        view(C, 1:nzrow-1, :) .*= beta
        view(C, nzrow+1:size(C,1), :) .*= beta
        y = view(C, nzrow, :)
        y .= view(ABα, nzrow, :) .+ y .* beta
    end
    C
end

function _mul!(C::AbstractVector, A::OneElementMatrix, B::AbstractVector, alpha, beta)
    check_matmul_sizes(C, A, B)
    if iszero(getindex_value(A))
        mul!(C, Zeros{eltype(A)}(axes(A)), B, alpha, beta)
        return C
    end
    nzrow, nzcol = A.ind
    Aval = A.val
    if iszero(beta)
        C .= Ref(zero(eltype(C)))
        C[nzrow] = Aval * B[nzcol] * alpha
    else
        view(C, 1:nzrow-1) .*= beta
        view(C, nzrow+1:size(C,1)) .*= beta
        C[nzrow] = Aval * B[nzcol] * alpha + C[nzrow] * beta
    end
    C
end

for MT in (:StridedMatrix, :(Transpose{<:Any, <:StridedMatrix}), :(Adjoint{<:Any, <:StridedMatrix}))
    @eval function mul!(y::StridedVector, A::$MT, x::OneElementVector, alpha::Number, beta::Number)
        _mul!(y, A, x, alpha, beta)
    end
end
for MT in (:StridedMatrix, :(Transpose{<:Any, <:StridedMatrix}), :(Adjoint{<:Any, <:StridedMatrix}),
            :Diagonal)
    @eval function mul!(C::StridedMatrix, A::$MT, B::OneElementMatrix, alpha::Number, beta::Number)
        _mul!(C, A, B, alpha, beta)
    end
    @eval function mul!(C::StridedMatrix, A::OneElementMatrix, B::$MT, alpha::Number, beta::Number)
        _mul!(C, A, B, alpha, beta)
    end
end
function mul!(C::StridedVector, A::OneElementMatrix, B::StridedVector, alpha::Number, beta::Number)
    _mul!(C, A, B, alpha, beta)
end

function mul!(y::AbstractVector, A::AbstractFillMatrix, x::OneElementVector, alpha::Number, beta::Number)
    _mul!(y, A, x, alpha, beta)
end
function mul!(C::AbstractMatrix, A::AbstractFillMatrix, B::OneElementMatrix, alpha::Number, beta::Number)
    _mul!(C, A, B, alpha, beta)
end
function mul!(C::AbstractVector, A::OneElementMatrix, B::AbstractFillVector, alpha::Number, beta::Number)
    _mul!(C, A, B, alpha, beta)
end
function mul!(C::AbstractMatrix, A::OneElementMatrix, B::AbstractFillMatrix, alpha::Number, beta::Number)
    _mul!(C, A, B, alpha, beta)
end

# adjoint/transpose

adjoint(A::OneElementMatrix) = OneElement(adjoint(A.val), reverse(A.ind), reverse(A.axes))
transpose(A::OneElementMatrix) = OneElement(transpose(A.val), reverse(A.ind), reverse(A.axes))

# isbanded
function LinearAlgebra.isbanded(A::OneElementMatrix, kl::Integer, ku::Integer)
    iszero(getindex_value(A)) || kl <= A.ind[2] - A.ind[1] <= ku
end

# tril/triu

function tril(A::OneElementMatrix, k::Integer=0)
    nzband = A.ind[2] - A.ind[1]
    OneElement(nzband > k ? zero(A.val) : A.val, A.ind, axes(A))
end

function triu(A::OneElementMatrix, k::Integer=0)
    nzband = A.ind[2] - A.ind[1]
    OneElement(nzband < k ? zero(A.val) : A.val, A.ind, axes(A))
end


# issymmetric
issymmetric(O::OneElement) = axes(O,1) == axes(O,2) && isdiag(O) && issymmetric(getindex_value(O))
ishermitian(O::OneElement) = axes(O,1) == axes(O,2) && isdiag(O) && ishermitian(getindex_value(O))

# diag
function diag(O::OneElementMatrix, k::Integer=0)
    Base.require_one_based_indexing(O)
    len = length(diagind(O, k))
    ind = O.ind[2] - O.ind[1] == k ? (k >= 0 ? O.ind[2] - k : O.ind[1] + k) : len + 1
    OneElement(getindex_value(O), ind, len)
end

# broadcast

for f in (:abs, :abs2, :conj, :real, :imag)
    @eval function broadcasted(::DefaultArrayStyle{N}, ::typeof($f), r::OneElement{<:Any,N}) where {N}
        OneElement($f(r.val), r.ind, axes(r))
    end
end
function broadcasted(::DefaultArrayStyle{N}, ::typeof(^), r::OneElement{<:Any,N}, x::Number) where {N}
    OneElement(r.val^x, r.ind, axes(r))
end
function broadcasted(::DefaultArrayStyle{N}, ::typeof(*), r::OneElement{<:Any,N}, x::Number) where {N}
    OneElement(r.val*x, r.ind, axes(r))
end
function broadcasted(::DefaultArrayStyle{N}, ::typeof(/), r::OneElement{<:Any,N}, x::Number) where {N}
    OneElement(r.val/x, r.ind, axes(r))
end
function broadcasted(::DefaultArrayStyle{N}, ::typeof(\), x::Number, r::OneElement{<:Any,N}) where {N}
    OneElement(x \ r.val, r.ind, axes(r))
end

# reshape

function Base.reshape(A::OneElement, shape::Tuple{Vararg{Int}})
    prod(shape) == length(A) || throw(DimensionMismatch(LazyString("new dimension ", shape, " must be consistent with array size ", length(A))))
    if all(in.(A.ind, axes(A)))
        # we use the fact that the linear index of the non-zero value is preserved
        oldlinind = LinearIndices(A)[A.ind...]
        newcartind = CartesianIndices(shape)[oldlinind]
    else
        # arbitrarily set to some value outside the domain
        newcartind = shape .+ 1
    end
    OneElement(A.val, Tuple(newcartind), shape)
end

#permute
_permute(x, p) = ntuple(i -> x[p[i]], length(x))
permutedims(o::OneElementMatrix) = OneElement(o.val, reverse(o.ind), reverse(o.axes))
permutedims(o::OneElementVector) = reshape(o, (1, length(o)))
permutedims(o::OneElement, dims) = OneElement(o.val, _permute(o.ind, dims), _permute(o.axes, dims))

# unique
function unique(O::OneElement)
    v = getindex_value(O)
    len = iszero(v) ? 1 : min(2, length(O))
    OneElement(getindex_value(O), len, len)
end
allunique(O::OneElement) = length(O) <= 1 || (length(O) < 3 && !iszero(getindex_value(O)))

# show
_maybesize(t::Tuple{Base.OneTo{Int}, Vararg{Base.OneTo{Int}}}) = size.(t,1)
_maybesize(t) = t
Base.show(io::IO, A::OneElement) = print(io, OneElement, "(", A.val, ", ", A.ind, ", ", _maybesize(axes(A)), ")")
Base.show(io::IO, A::OneElement{<:Any,1,Tuple{Int},Tuple{Base.OneTo{Int}}}) =
    print(io, OneElement, "(", A.val, ", ", A.ind[1], ", ", size(A,1), ")")

# mapreduce
Base.sum(O::OneElement; dims=:, kw...) = _sum(O, dims; kw...)
_sum(O::OneElement, ::Colon; kw...) = sum((getindex_value(O),); kw...)
function _sum(O::OneElement, dims; kw...)
    v = _sum(O, :; kw...)
    ax = Base.reduced_indices(axes(O), dims)
    ind = ntuple(x -> x in dims ? first(ax[x]) + (O.ind[x] in axes(O)[x]) - 1 : O.ind[x], ndims(O))
    OneElement(v, ind, ax)
end


================================================
FILE: src/trues.jl
================================================

"""
    Trues = Ones{Bool, N, Axes} where {N, Axes}

Lazy version of `trues` with axes.
Typically created using `Trues(dims)` or `Trues(dims...)`

# Example
```jldoctest
julia> T = Trues(1,3)
1×3 Ones{Bool}

julia> Array(T)
1×3 Matrix{Bool}:
 1  1  1
```
"""
const Trues = Ones{Bool, N, Axes} where {N, Axes}


"""
    Falses = Zeros{Bool, N, Axes}

Lazy version of `falses` with axes.

See also: [`Trues`](@ref)
"""
const Falses = Zeros{Bool, N, Axes} where {N, Axes}


# y[mask] = x when mask isa Trues (cf y[:] = x)

function Base.to_indices(A::AbstractArray{T,N}, inds, I::Tuple{Trues{N}}) where {T,N}
    @boundscheck axes(A) == axes(I[1]) || Base.throw_boundserror(A, I[1])
    (vec(LinearIndices(A)),)
end


================================================
FILE: test/aqua.jl
================================================
using Aqua
using FillArrays
using Test
downstream_test = "--downstream_integration_test" in ARGS
@testset "Project quality" begin
    Aqua.test_all(FillArrays;
        # https://github.com/JuliaArrays/FillArrays.jl/issues/105#issuecomment-1582516319
        ambiguities=(; broken=true),
        stale_deps = !downstream_test,
    )
end


================================================
FILE: test/infinitearrays.jl
================================================
# Infinite Arrays implementation from
# https://github.com/JuliaLang/julia/blob/master/test/testhelpers/InfiniteArrays.jl
module InfiniteArrays
    using Infinities
    export OneToInf

    abstract type AbstractInfUnitRange{T<:Real} <: AbstractUnitRange{T} end
    Base.length(r::AbstractInfUnitRange) = ℵ₀
    Base.size(r::AbstractInfUnitRange) = (ℵ₀,)
    Base.last(r::AbstractInfUnitRange) = ℵ₀
    Base.axes(r::AbstractInfUnitRange) = (OneToInf(),)

    Base.IteratorSize(::Type{<:AbstractInfUnitRange}) = Base.IsInfinite()

    """
        OneToInf(n)
    Define an `AbstractInfUnitRange` that behaves like `1:∞`, with the added
    distinction that the limits are guaranteed (by the type system) to
    be 1 and ∞.
    """
    struct OneToInf{T<:Integer} <: AbstractInfUnitRange{T} end

    OneToInf() = OneToInf{Int}()

    Base.axes(r::OneToInf) = (r,)
    Base.first(r::OneToInf{T}) where {T} = oneunit(T)
    Base.oneto(::InfiniteCardinal{0}) = OneToInf()

    struct InfUnitRange{T<:Real} <: AbstractInfUnitRange{T}
        start::T
    end
    Base.first(r::InfUnitRange) = r.start
    InfUnitRange(a::InfUnitRange) = a
    InfUnitRange{T}(a::AbstractInfUnitRange) where T<:Real = InfUnitRange{T}(first(a))
    InfUnitRange(a::AbstractInfUnitRange{T}) where T<:Real = InfUnitRange{T}(first(a))
    Base.:(:)(start::T, stop::InfiniteCardinal{0}) where {T<:Integer} = InfUnitRange{T}(start)
    function getindex(v::InfUnitRange{T}, i::Integer) where T
        @boundscheck i > 0 || Base.throw_boundserror(v, i)
        convert(T, first(v) + i - 1)
    end
end


================================================
FILE: test/runtests.jl
================================================
using FillArrays, LinearAlgebra, PDMats, SparseArrays, StaticArrays, ReverseDiff, Random, Test, Statistics, Quaternions

import FillArrays: AbstractFill, RectDiagonal, SquareEye

using Documenter
DocMeta.setdocmeta!(FillArrays, :DocTestSetup, :(using FillArrays))
doctest(FillArrays; manual = false)

include("aqua.jl")

include("infinitearrays.jl")
import .InfiniteArrays

# we may use this instead of rand(n) to generate deterministic arrays
oneton(T::Type, sz...) = reshape(T.(1:prod(sz)), sz)
oneton(sz...) = oneton(Float64, sz...)

stringmime(args...) = sprint(show, args...)

@testset "fill array constructors and convert" begin
    for (Typ, funcs) in ((Zeros, zeros), (Ones, ones))
        @test Typ((-1,5)) == Typ((0,5))
        @test Typ(5) isa AbstractVector{Float64}
        @test Typ(5,5) isa AbstractMatrix{Float64}
        @test Typ(5) == Typ((5,))
        @test Typ(5,5) == Typ((5,5))
        @test eltype(Typ(5,5)) == Float64

        for T in (Int, Float64)
            Z = Typ{T}(5)
            @test Typ(T, 5) ≡ Z
            @test eltype(Z) == T
            @test Array(Z) == funcs(T,5)
            @test Array{T}(Z) == funcs(T,5)
            @test Array{T,1}(Z) == funcs(T,5)

            @test convert(AbstractArray,Z) ≡ Z
            @test convert(AbstractArray{T},Z) ≡ AbstractArray{T}(Z) ≡ Z
            @test convert(AbstractVector{T},Z) ≡ AbstractVector{T}(Z) ≡ Z
            @test convert(AbstractFill{T},Z) ≡ AbstractFill{T}(Z) ≡ Z

            @test Typ{T,1}(2ones(T,5)) == Z
            @test Typ{T}(2ones(T,5)) == Z
            @test Typ(2ones(T,5)) == Z

            Z = Typ{T}(5, 5)
            @test Typ(T, 5, 5) ≡ Z
            @test eltype(Z) == T
            @test Array(Z) == funcs(T,5,5)
            @test Array{T}(Z) == funcs(T,5,5)
            @test Array{T,2}(Z) == funcs(T,5,5)

            @test convert(AbstractArray,Z) ≡ convert(AbstractFill,Z) ≡ Z
            @test convert(AbstractArray{T},Z) ≡ convert(AbstractFill{T},Z) ≡ AbstractArray{T}(Z) ≡ Z
            @test convert(AbstractMatrix{T},Z) ≡ convert(AbstractFill{T,2},Z) ≡ AbstractMatrix{T}(Z) ≡ Z

            @test_throws Exception convert(Fill{Float64}, [1,1,2])
            @test_throws Exception convert(Fill, [])
            @test convert(Fill{Float64}, [1,1,1]) ≡ Fill(1.0, 3)
            @test convert(Fill, Float64[1,1,1]) ≡ Fill(1.0, 3)
            @test convert(Fill{Float64}, Fill(1.0,2)) ≡ Fill(1.0, 2) # was ambiguous
            @test convert(Fill{Int}, Ones(20)) ≡ Fill(1, 20)
            @test convert(Fill{Int,1}, Ones(20)) ≡ Fill(1, 20)
            @test convert(Fill{Int,1,Tuple{Base.OneTo{Int}}}, Ones(20)) ≡ Fill(1, 20)
            @test convert(AbstractFill{Int}, Ones(20)) ≡ AbstractFill{Int}(Ones(20)) ≡ Ones{Int}(20)
            @test convert(AbstractFill{Int,1}, Ones(20)) ≡ AbstractFill{Int,1}(Ones(20)) ≡ Ones{Int}(20)
            @test convert(AbstractFill{Int,1,Tuple{Base.OneTo{Int}}}, Ones(20)) ≡ AbstractFill{Int,1,Tuple{Base.OneTo{Int}}}(Ones(20)) ≡ Ones{Int}(20)

            @test Typ{T,2}(2ones(T,5,5)) ≡ Typ{T}(5,5)
            @test Typ{T}(2ones(T,5,5)) ≡ Typ{T}(5,5)
            @test Typ(2ones(T,5,5)) ≡ Typ{T}(5,5)

            z = Typ{T}()[]
            @test convert(Typ, Fill(z,2)) === Typ{T}(2)
            z = Typ{Int8}()[]
            @test convert(Typ{T}, Fill(z,2)) === Typ{T}(2)
            @test convert(Typ{T,1}, Fill(z,2)) === Typ{T}(2)
            @test convert(Typ{T,1,Tuple{Base.OneTo{Int}}}, Fill(z,2)) === Typ{T}(2)
            @test_throws ArgumentError convert(Typ, Fill(2,2))

            @test Typ(Z) ≡ Typ{T}(Z) ≡ Typ{T,2}(Z) ≡ typeof(Z)(Z) ≡ Z

            @test AbstractArray{Float32}(Z) ≡ Typ{Float32}(5,5)
            @test AbstractArray{Float32,2}(Z) ≡ Typ{Float32}(5,5)
        end
    end

    @test Fill(1) ≡ Fill{Int}(1) ≡ Fill{Int,0}(1) ≡ Fill{Int,0,Tuple{}}(1,())
    @test Fill(1,(-1,5)) ≡ Fill(1,(0,5))
    @test Fill(1.0,5) isa AbstractVector{Float64}
    @test Fill(1.0,5,5) isa AbstractMatrix{Float64}
    @test Fill(1,5) ≡ Fill(1,(5,))
    @test Fill(1,5,5) ≡ Fill(1,(5,5))
    @test eltype(Fill(1.0,5,5)) == Float64

    @test Matrix{Float64}(Zeros{ComplexF64}(10,10)) == zeros(10,10)
    @test_throws InexactError Matrix{Float64}(Fill(1.0+1.0im,10,10))


    for T in (Int, Float64)
        F = Fill{T, 0}(2)
        @test size(F) == ()
        @test F[] === T(2)

        F = Fill{T}(1, 5)

        @test eltype(F) == T
        @test Array(F) == fill(one(T),5)
        @test Array{T}(F) == fill(one(T),5)
        @test Array{T,1}(F) == fill(one(T),5)

        F = Fill{T}(1, 5, 5)
        @test eltype(F) == T
        @test Array(F) == fill(one(T),5,5)
        @test Array{T}(F) == fill(one(T),5,5)
        @test Array{T,2}(F) == fill(one(T),5,5)

        @test convert(AbstractArray,F) ≡ F
        @test convert(AbstractArray{T},F) ≡ AbstractArray{T}(F) ≡ F
        @test convert(AbstractMatrix{T},F) ≡ AbstractMatrix{T}(F) ≡ F

        @test convert(AbstractArray{Float32},F) ≡ AbstractArray{Float32}(F) ≡
                Fill{Float32}(one(Float32),5,5)
        @test convert(AbstractMatrix{Float32},F) ≡ AbstractMatrix{Float32}(F) ≡
                Fill{Float32}(one(Float32),5,5)

        @test Fill{T}(F) ≡ Fill{T,2}(F) ≡ typeof(F)(F) ≡ F

        show(devnull, MIME("text/plain"), F) # for codecov
    end

    @test Eye(5) isa Diagonal{Float64}
    @test SquareEye(5) isa Diagonal{Float64}
    @test Eye(5) == Eye{Float64}(5) == SquareEye(5) == SquareEye{Float64}(5)
    @test Eye(5,6) == Eye{Float64}(5,6)
    @test Eye(Ones(5,6)) == Eye{Float64}(5,6)
    @test eltype(Eye(5)) == Float64
    @test eltype(Eye(5,6)) == Float64

    @test Eye((Base.OneTo(5),)) ≡ SquareEye((Base.OneTo(5),)) ≡ Eye(5)
    @test Eye((Base.OneTo(5),Base.OneTo(6))) ≡ Eye(5,6)

    for T in (Int, Float64)
        E = Eye{T}(5)
        M = Matrix{T}(I, 5, 5)

        @test eltype(E) == T
        @test Array(E) == M
        @test Array{T}(E) == M
        @test Array{T,2}(E) == M

        @test convert(AbstractArray,E) === E
        @test convert(AbstractArray{T},E) === E
        @test convert(AbstractMatrix{T},E) === E


        @test AbstractArray{Float32}(E) == Eye{Float32}(5)
        @test AbstractArray{Float32}(E) == Eye{Float32}(5, 5)

        @test Eye{T}(randn(4,5)) ≡ Eye{T}(4,5) ≡ Eye{T}((Base.OneTo(4),Base.OneTo(5)))
        @test Eye{T}((Base.OneTo(5),)) ≡ SquareEye{T}((Base.OneTo(5),)) ≡ Eye{T}(5)
    end

    @testset "Bool should change type" begin
        x = Fill(true,5)
        y = x + x
        @test y isa Fill{Int,1}
        @test y[1] == 2

        x = Ones{Bool}(5)
        y = x + x
        @test y isa Fill{Int,1}
        @test y[1] == 2
        @test x + Zeros{Bool}(5) ≡ Ones{Int}(5)
        @test x - Zeros{Bool}(5) ≡ Ones{Int}(5)
        @test Zeros{Bool}(5) + x ≡ Ones{Int}(5)
        @test -x ≡ Fill(-1,5)
    end

    @testset "copy should return Fill" begin
        x = Fill(1.0,10)
        @test copy(x) ≡ x
        x = Zeros(10)
        @test copy(x) ≡ x
        x = Fill([1.,2.],10)
        @test copy(x) == x
        @test copy(x) === x   # because isbits(x)
        @test copy(x) isa Fill
        @test copy(Fill(:a, 4)) == fill(:a, 4)    # FillArrays#63
    end

    @testset "vec" begin
        @test vec(Ones{Int}(5,10)) ≡ Ones{Int}(50)
        @test vec(Zeros{Int}(5,10)) ≡ Zeros{Int}(50)
        @test vec(Zeros{Int}(5,10,20)) ≡ Zeros{Int}(1000)
        @test vec(Fill(1,5,10)) ≡ Fill(1,50)
    end

    @testset "in" begin
        for T in [Zeros, Ones, Fill, Trues, Falses]
            A = T(4, 4)
            @test FillArrays.getindex_value(A) in A
            @test !(FillArrays.getindex_value(A) + 1 in A)
        end
        A = FillArrays.RectDiagonal([1, 2, 3])
        @test 3 in A
        @test 0 in A
        @test !(4 in A)
        A = FillArrays.RectDiagonal([1])
        @test 1 in A
        @test !(0 in A)
        A = FillArrays.RectDiagonal([2], (1:1, 1:4))
        @test 2 in A
        @test 0 in A
        @test !(1 in A)
        @test !(Zeros(1,1) in A)
        A = FillArrays.RectDiagonal(Int[])
        @test !(0 in A)
        A = FillArrays.RectDiagonal(Int[], (1:0, 1:4))
        @test !(0 in A)
    end

    @testset "promotion" begin
        Z = Zeros{Int}(5)
        Zf = Zeros(5)
        @test promote_type(typeof(Z), typeof(Zf)) == typeof(Zf)
        O = Ones{Int}(5)
        Of = Ones{Float64}(5)
        @test promote_type(typeof(O), typeof(Of)) == typeof(Of)
        @test [Z,O] isa Vector{Fill{Int,1,Tuple{Base.OneTo{Int}}}}
        @test [Z,Of] isa Vector{Fill{Float64,1,Tuple{Base.OneTo{Int}}}}
        @test [O,O] isa Vector{Ones{Int,1,Tuple{Base.OneTo{Int}}}}
        @test [O,Of] isa Vector{Ones{Float64,1,Tuple{Base.OneTo{Int}}}}
        @test [Z,Zf] isa Vector{Zeros{Float64,1,Tuple{Base.OneTo{Int}}}}

        @test convert(Ones{Int}, Of) ≡ convert(Ones{Int,1}, Of) ≡ convert(typeof(O), Of) ≡ O
        @test convert(Zeros{Int}, Zf) ≡ convert(Zeros{Int,1}, Zf) ≡ convert(typeof(Z), Zf) ≡ Z

        F = Fill(1, 2)
        Ff = Fill(1.0, 2)
        @test promote_type(typeof(F), typeof(Ff)) == typeof(Ff)

        @test_throws MethodError convert(Zeros{SVector{2,Int}}, Zf)
    end
end

@testset "interface" begin
    struct Twos{T,N} <: FillArrays.AbstractFill{T,N,NTuple{N,Base.OneTo{Int}}}
        sz :: NTuple{N,Int}
    end
    Twos{T}(sz::NTuple{N,Int}) where {T,N} = Twos{T,N}(sz)
    Twos{T}(sz::Vararg{Int,N}) where {T,N} = Twos{T,N}(sz)
    Base.size(A::Twos) = A.sz
    FillArrays.getindex_value(A::Twos{T}) where {T} = oneunit(T) + oneunit(T)

    @testset "broadcasting ambiguities" begin
        A = Twos{Int}(3)
        B = Zeros{Int}(size(A))
        @test A .* B === B
        @test B .* A === B
        @test B ./ A === Zeros{Float64}(size(A))
        @test A .\ B === Zeros{Float64}(size(A))
        @test A ./ B === Fill(Inf, size(A))
    end
end

@testset "isassigned" begin
    for f in (Fill("", 3, 4), Zeros(3,4), Ones(3,4))
        @test !isassigned(f, 0, 0)
        @test isassigned(f, 2, 2)
        @test !isassigned(f, 10, 10)
        @test_throws ArgumentError isassigned(f, true)
    end
end

@testset "indexing" begin
    A = Fill(3.0,5)
    @test A[1:3] ≡ Fill(3.0,3)
    @test A[1:3,1:1] ≡ Fill(3.0,3,1)
    @test_throws BoundsError A[1:3,2]
    @test_throws BoundsError A[1:26]
    @test A[[true, false, true, false, false]] ≡ Fill(3.0, 2)
    A = Fill(3.0, 2, 2)
    @test A[[true true; true false]] ≡ Fill(3.0, 3)
    @test_throws BoundsError A[[true, false]]

    A = Ones{Int}(5,5)
    @test A[1:3] ≡ Ones{Int}(3)
    @test A[1:3,1:2] ≡ Ones{Int}(3,2)
    @test A[1:3,2] ≡ Ones{Int}(3)
    @test_throws BoundsError A[1:26]
    A = Ones{Int}(2,2)
    @test A[[true false; true false]] ≡ Ones{Int}(2)
    @test A[[true, false, true, false]] ≡ Ones{Int}(2)
    @test_throws BoundsError A[[true false false; true false false]]

    A = Zeros{Int}(5,5)
    @test A[1:3] ≡ Zeros{Int}(3)
    @test A[1:3,1:2] ≡ Zeros{Int}(3,2)
    @test A[1:3,2] ≡ Zeros{Int}(3)
    @test_throws BoundsError A[1:26]
    A = Zeros{Int}(2,2)
    @test A[[true false; true false]] ≡ Zeros{Int}(2)
    @test A[[true, false, true, false]] ≡ Zeros{Int}(2)
    @test_throws BoundsError A[[true false false; true false false]]

    @testset "colon" begin
        @test Ones(2)[:] ≡ Ones(2)[Base.Slice(Base.OneTo(2))] ≡ Ones(2)
        @test Zeros(2)[:] ≡ Zeros(2)[Base.Slice(Base.OneTo(2))] ≡ Zeros(2)
        @test Fill(3.0,2)[:] ≡ Fill(3.0,2)[Base.Slice(Base.OneTo(2))] ≡ Fill(3.0,2)

        @test Ones(2,2)[:,:] ≡ Ones(2,2)[Base.Slice(Base.OneTo(2)),Base.Slice(Base.OneTo(2))] ≡ Ones(2,2)
        @test Zeros(2,2)[:,:] ≡ Zeros(2)[Base.Slice(Base.OneTo(2)),Base.Slice(Base.OneTo(2))] ≡ Zeros(2,2)
        @test Fill(3.0,2,2)[:,:] ≡ Fill(3.0,2,2)[Base.Slice(Base.OneTo(2)),Base.Slice(Base.OneTo(2))] ≡ Fill(3.0,2,2)
    end

    @testset "mixed integer / vector /colon" begin
        a = Fill(2.0,5)
        z = Zeros(5)
        @test a[1:5] ≡ a[:] ≡ a
        @test z[1:5] ≡ z[:] ≡ z

        A = Fill(2.0,5,6)
        Z = Zeros(5,6)
        @test A[:,1] ≡ A[1:5,1] ≡ Fill(2.0,5)
        @test A[1,:] ≡ A[1,1:6] ≡ Fill(2.0,6)
        @test A[:,:] ≡ A[1:5,1:6] ≡ A[1:5,:] ≡ A[:,1:6] ≡ A
        @test Z[:,1] ≡ Z[1:5,1] ≡ Zeros(5)
        @test Z[1,:] ≡ Z[1,1:6] ≡ Zeros(6)
        @test Z[:,:] ≡ Z[1:5,1:6] ≡ Z[1:5,:] ≡ Z[:,1:6] ≡ Z

        A = Fill(2.0,5,6,7)
        Z = Zeros(5,6,7)
        @test A[:,1,1] ≡ A[1:5,1,1] ≡ Fill(2.0,5)
        @test A[1,:,1] ≡ A[1,1:6,1] ≡ Fill(2.0,6)
        @test A[:,:,:] ≡ A[1:5,1:6,1:7] ≡ A[1:5,:,1:7] ≡ A[:,1:6,1:7] ≡ A
    end

    @testset "StepRangeLen convert" begin
        for (z,s) in  ((Zeros{Int}(5), StepRangeLen(0, 0, 5)), (Ones{Int}(5), StepRangeLen(1, 0, 5)), (Fill(2,5), StepRangeLen(2, 0, 5)))
            @test s == z
            @test StepRangeLen(z) ≡ convert(StepRangeLen, z) ≡ convert(StepRangeLen{Int}, z) ≡ convert(typeof(s), z) ≡ convert(AbstractRange, z) ≡ s
        end
    end
end

@testset "RectDiagonal" begin
    data = 1:3
    expected_size = (5, 3)
    expected_axes = Base.OneTo.(expected_size)
    expected_matrix = [1 0 0; 0 2 0; 0 0 3; 0 0 0; 0 0 0]
    expected = RectDiagonal{Int, UnitRange{Int}}(data, expected_axes)

    @test axes(expected) == expected_axes
    @test size(expected) == expected_size
    @test (axes(expected, 1), axes(expected, 2)) == expected_axes
    @test (size(expected, 1), size(expected, 2)) == expected_size

    @test expected == expected_matrix
    @test Matrix(expected) == expected_matrix
    @test expected[:, 2] == expected_matrix[:, 2]
    @test expected[2, :] == expected_matrix[2, :]
    @test expected[5, :] == expected_matrix[5, :]


    for Typ in (RectDiagonal, RectDiagonal{Int}, RectDiagonal{Int, UnitRange{Int}})
        @test Typ(data) == expected[1:3, 1:3]
        @test Typ(data, expected_axes) == expected
        @test Typ(data, expected_axes...) == expected
        @test Typ(data, expected_size) == expected
        @test Typ(data, expected_size...) == expected
    end

    @test diag(expected) === expected.diag

    mut = RectDiagonal(collect(data), expected_axes)
    @test mut == expected
    @test mut == expected_matrix
    mut[1, 1] = 5
    @test mut[1] == 5
    @test diag(mut) == [5, 2, 3]
    mut[2, 1] = 0
    @test_throws ArgumentError mut[2, 1] = 9

    D = RectDiagonal([1.,2.], (Base.OneTo(3),Base.OneTo(2)))
    @test stringmime("text/plain", D) == "3×2 RectDiagonal{Float64, Vector{Float64}, Tuple{Base.OneTo{$Int}, Base.OneTo{$Int}}}:\n 1.0   ⋅ \n  ⋅   2.0\n  ⋅    ⋅ "
end

# Check that all pair-wise combinations of + / - elements of As and Bs yield the correct
# type, and produce numerically correct results.
as_array(x::AbstractArray) = Array(x)
as_array(x::UniformScaling) = x
isapprox_or_undef(a::Number, b::Number) = (a ≈ b) || isequal(a, b)
isapprox_or_undef(a, b) = all(((x,y),) -> isapprox_or_undef(x,y), zip(a, b))
function test_addition_subtraction_dot(As, Bs, Tout::Type)
    for A in As, B in Bs
        @testset "$(typeof(A)) and $(typeof(B))" begin
            @test @inferred(A + B) isa Tout{promote_type(eltype(A), eltype(B))}
            @test isapprox_or_undef(as_array(A + B), as_array(A) + as_array(B))

            @test @inferred(A - B) isa Tout{promote_type(eltype(A), eltype(B))}
            @test isapprox_or_undef(as_array(A - B), as_array(A) - as_array(B))

            @test @inferred(B + A) isa Tout{promote_type(eltype(B), eltype(A))}
            @test isapprox_or_undef(as_array(B + A), as_array(B) + as_array(A))

            @test @inferred(B - A) isa Tout{promote_type(eltype(B), eltype(A))}
            @test isapprox_or_undef(as_array(B - A), as_array(B) - as_array(A))

            d1 = dot(A, B)
            d2 = dot(as_array(A), as_array(B))
            d3 = dot(B, A)
            d4 = dot(as_array(B), as_array(A))
            @test d1 ≈ d2 || d1 ≡ d2
            @test d3 ≈ d4 || d3 ≡ d4
        end
    end
end

# Check that all permutations of + / - throw a `DimensionMismatch` exception.
function test_addition_and_subtraction_dim_mismatch(a, b)
    @testset "$(typeof(a)) ± $(typeof(b))" begin
        @test_throws DimensionMismatch a + b
        @test_throws DimensionMismatch a - b
        @test_throws DimensionMismatch b + a
        @test_throws DimensionMismatch b - a
    end
end

@testset "FillArray addition and subtraction" begin
    test_addition_and_subtraction_dim_mismatch(Zeros(5), Zeros(6))
    test_addition_and_subtraction_dim_mismatch(Zeros(5), Zeros{Int}(6))
    test_addition_and_subtraction_dim_mismatch(Zeros(5), Zeros(6,6))
    test_addition_and_subtraction_dim_mismatch(Zeros(5), Zeros{Int}(6,5))

    # Construct FillArray for repeated use.
    rng = MersenneTwister(123456)
    A_fill, B_fill = Fill(randn(rng, Float64), 5), Fill(4, 5)

    # Unary +/- constructs a new FillArray.
    @test +A_fill === A_fill
    @test -A_fill === Fill(-A_fill.value, 5)

    # FillArray +/- FillArray should construct a new FillArray.
    test_addition_subtraction_dot((A_fill, B_fill), (A_fill, B_fill), Fill)
    test_addition_and_subtraction_dim_mismatch(A_fill, Fill(randn(rng), 5, 2))

    # FillArray + Array (etc) should construct a new Array using `getindex`.
    B_dense = (randn(rng, 5), [5, 4, 3, 2, 1], fill(Inf, 5), fill(NaN, 5))
    test_addition_subtraction_dot((A_fill, B_fill), B_dense, Array)
    test_addition_and_subtraction_dim_mismatch(A_fill, randn(rng, 5, 2))

    # FillArray + StepLenRange / UnitRange (etc) should yield an AbstractRange.
    A_ur, B_ur = 1.0:5.0, 6:10
    test_addition_subtraction_dot((A_fill, B_fill), (A_ur, B_ur), AbstractRange)
    test_addition_and_subtraction_dim_mismatch(A_fill, 1.0:6.0)
    test_addition_and_subtraction_dim_mismatch(A_fill, 5:10)

    # FillArray + UniformScaling should yield a Matrix in general
    As_fill_square = (Fill(randn(rng, Float64), 3, 3), Fill(5, 4, 4))
    Bs_us = (UniformScaling(2.3), UniformScaling(3))
    test_addition_subtraction_dot(As_fill_square, Bs_us, Matrix)
    As_fill_nonsquare = (Fill(randn(rng, Float64), 3, 2), Fill(5, 3, 4))
    for A in As_fill_nonsquare, B in Bs_us
        test_addition_and_subtraction_dim_mismatch(A, B)
    end

    # FillArray + StaticArray should not have ambiguities
    A_svec, B_svec = SVector{5}(rand(5)), SVector(1, 2, 3, 4, 5)
    test_addition_subtraction_dot((A_fill, B_fill, Zeros(5)), (A_svec, B_svec), SVector{5})

    # Issue #224
    A_matmat, B_matmat = Fill(rand(3,3),5), [rand(3,3) for n=1:5]
    test_addition_subtraction_dot((A_matmat,), (A_matmat,), Fill)
    test_addition_subtraction_dot((B_matmat,), (A_matmat,), Vector)

    # Optimizations for Zeros and RectOrDiagonal{<:Any, <:AbstractFill}
    As_special_square = (
        Zeros(3, 3), Zeros{Int}(4, 4),
        Eye(3), Eye{Int}(4), Eye(3, 3), Eye{Int}(4, 4),
        Diagonal(Fill(randn(rng, Float64), 3)), Diagonal(Fill(3, 4)),
        RectDiagonal(Fill(randn(rng, Float64), 3), 3, 3), RectDiagonal(Fill(3, 4), 4, 4)
    )
    DiagonalAbstractFill{T} = Diagonal{T, <:AbstractFill{T, 1}}
    test_addition_subtraction_dot(As_special_square, Bs_us, DiagonalAbstractFill)
    As_special_nonsquare = (
        Zeros(3, 2), Zeros{Int}(3, 4),
        Eye(3, 2), Eye{Int}(3, 4),
        RectDiagonal(Fill(randn(rng, Float64), 2), 3, 2), RectDiagonal(Fill(3, 3), 3, 4)
    )
    for A in As_special_nonsquare, B in Bs_us
        test_addition_and_subtraction_dim_mismatch(A, B)
    end

    @testset "Zeros" begin
        As = ([1,2], Float64[1,2], Int8[1,2], ComplexF16[2,4])
        Zs = (TZ -> Zeros{TZ}(2)).((Int, Float64, Int8, ComplexF64))
        test_addition_subtraction_dot(As, Zs, Vector)
        for A in As, Z in (TZ -> Zeros{TZ}(3)).((Int, Float64, Int8, ComplexF64))
            test_addition_and_subtraction_dim_mismatch(A, Z)
        end

        As = (@SArray([1,2]), @SArray(Float64[1,2]), @SArray(Int8[1,2]), @SArray(ComplexF16[2,4]))
        test_addition_subtraction_dot(As, Zs, SVector{2})
        for A in As, Z in (TZ -> Zeros{TZ}(3)).((Int, Float64, Int8, ComplexF64))
            test_addition_and_subtraction_dim_mismatch(A, Z)
        end

        # Zeros should act as an additive identity
        # Arbitrary AbstractMatrix
        D = Diagonal([1, 1])
        Z = Zeros(2, 2)
        @test D + Z isa Diagonal
        @test D + Z == D
        @test D - Z == D
        @test Z - D == -D

        @test Z + Z isa Zeros
        @test Z + Z == Z
        @test Z - Z == Z
    end
end

@testset "Other matrix types" begin
    @test Diagonal(Zeros(5)) == Diagonal(zeros(5))

    @test Diagonal(Zeros(8,5)) == Diagonal(zeros(5))
    @test convert(Diagonal, Zeros(5,5)) == Diagonal(zeros(5))
    @test_throws DimensionMismatch convert(Diagonal, Zeros(8,5))

    @test convert(Diagonal{Int}, Zeros(5,5)) == Diagonal(zeros(Int,5))
    @test_throws DimensionMismatch convert(Diagonal{Int}, Zeros(8,5))


    @test Diagonal(Eye(8,5)) == Diagonal(ones(5))
    @test convert(Diagonal, Eye(5)) == Diagonal(ones(5))
    @test convert(Diagonal{Int}, Eye(5)) == Diagonal(ones(Int,5))

    for E in (Eye(2,4), Eye(3))
        M = collect(E)
        for i in -5:5
            @test diag(E, i) == diag(M, i)
        end
    end
end

@testset "one" begin
    @testset for A in Any[Eye(4), Zeros(4,4), Ones(4,4), Fill(3,4,4)]
        B = one(A)
        @test B * A == A * B == A
    end
    @test_throws ArgumentError one(Ones(3,4))
    @test_throws ArgumentError one(Ones((3:5,4:5)))
end

@testset "Sparse vectors and matrices" begin
    @test SparseVector(Zeros(5)) ==
            SparseVector{Int}(Zeros(5)) ==
            SparseVector{Float64}(Zeros(5)) ==
            SparseVector{Float64,Int}(Zeros(5)) ==
            convert(AbstractSparseArray,Zeros(5)) ==
            convert(AbstractSparseVector,Zeros(5)) ==
            convert(AbstractSparseArray{Float64},Zeros(5)) ==
            convert(AbstractSparseVector{Float64},Zeros(5)) ==
            convert(AbstractSparseVector{Float64,Int},Zeros(5)) ==
            spzeros(5)

    for (Mat, SMat) in ((Zeros(5,5), spzeros(5,5)), (Zeros(6,5), spzeros(6,5)),
                        (Eye(5), sparse(I,5,5)), (Eye(6,5), sparse(I,6,5)))
        @test SparseMatrixCSC(Mat) ==
                SparseMatrixCSC{Int}(Mat) ==
                SparseMatrixCSC{Float64}(Mat) ==
                SparseMatrixCSC{Float64,Int}(Mat) ==
                convert(AbstractSparseArray,Mat) ==
                convert(AbstractSparseMatrix,Mat) ==
                convert(AbstractSparseArray{Float64},Mat) ==
                convert(AbstractSparseArray{Float64,Int},Mat) ==
                convert(AbstractSparseMatrix{Float64},Mat) ==
                convert(AbstractSparseMatrix{Float64,Int},Mat) ==
                SMat
    end

    function testsparsediag(E)
        S = @inferred SparseMatrixCSC(E)
        @test S == E
        S = @inferred SparseMatrixCSC{Float64}(E)
        @test S == E
        @test S isa SparseMatrixCSC{Float64}
        @test convert(SparseMatrixCSC{Float64}, E) == S
        S = @inferred SparseMatrixCSC{Float64,Int32}(E)
        @test S == E
        @test S isa SparseMatrixCSC{Float64,Int32}
        @test convert(SparseMatrixCSC{Float64,Int32}, E) == S
    end

    for f in (Fill(Int8(4),3), Ones{Int8}(3), Zeros{Int8}(3))
        E = Diagonal(f)
        testsparsediag(E)
        for sz in ((3,6), (6,3), (3,3))
            E = RectDiagonal(f, sz)
            testsparsediag(E)
        end
    end
end

@testset "==" begin
    @test Zeros(5,4) == Fill(0,5,4)
    @test Zeros(5,4) ≠ Zeros(3)
    @test Ones(5,4) == Fill(1,5,4)
end

@testset "Rank" begin
    @test rank(Zeros(5,4)) == 0
    @test rank(Ones(5,4)) == 1
    @test rank(Fill(2,5,4)) == 1
    @test rank(Fill(0,5,4)) == 0
    @test rank(Eye(2)) == 2
end

@testset "ishermitian" begin
    @testset for el in (2, 3+0im, 4+5im, [1 2; 3 4], fill(2, 2, 2)), size in [(3,3), (3,4), (0,0), (0,1)]
        @test issymmetric(Fill(el, size...)) == issymmetric(fill(el, size...))
        @test ishermitian(Fill(el, size...)) == ishermitian(fill(el, size...))
    end
end

@testset "BigInt indices" begin
    for A in (Zeros(BigInt(100)), Ones(BigInt(100)), Fill(2, BigInt(100)))
        @test length(A) isa BigInt
        @test axes(A) == tuple(Base.OneTo{BigInt}(BigInt(100)))
        @test size(A) isa Tuple{BigInt}
    end
    for A in (Eye(BigInt(100)), Eye(BigInt(100), BigInt(100)))
        @test length(A) isa BigInt
        @test axes(A) == tuple(Base.OneTo{BigInt}(BigInt(100)),Base.OneTo{BigInt}(BigInt(100)))
        @test size(A) isa Tuple{BigInt,BigInt}
    end
    for A in (Zeros(BigInt(10), 10), Ones(BigInt(10), 10), Fill(2.0, (BigInt(10), 10)), Eye(BigInt(10), 8))
        @test size(A) isa Tuple{BigInt,Int}
    end

end

@testset "IndexStyle" begin
    @test IndexStyle(Zeros(5,5)) == IndexStyle(typeof(Zeros(5,5))) == IndexLinear()
end

@testset "Identities" begin
    @test Zeros(3,4) * randn(4,5) === randn(3,4) * Zeros(4,5) === Zeros(3, 5)
    @test_throws DimensionMismatch randn(3,4) * Zeros(3, 3)
    @test eltype(Zeros{Int}(3,4) * fill(1, 4, 5)) == Int
    @test eltype(Zeros{Int}(3,4) * fill(3.4, 4, 5)) == Float64
    @test Zeros(3, 4) * randn(4) == Zeros(3, 4) * Zeros(4) == Zeros(3)
    @test Zeros(3, 4) * Zeros(4, 5) === Zeros(3, 5)

    @test_throws MethodError [1,2,3]*Zeros(1) # Not defined for [1,2,3]*[0] either
    @test [1,2,3]*Zeros(1,3) ≡ Zeros(3,3)
    @test_throws MethodError [1,2,3]*Zeros(3) # Not defined for [1,2,3]*[0,0,0] either

    @testset "Matrix multiplication with array elements" begin
        x = [1 2; 3 4]
        z = zero(SVector{2,Int})
        ZV = Zeros{typeof(z)}(2)
        A = Fill(x, 3, 2) * ZV
        @test A isa Fill
        @test size(A) == (3,)
        @test A[1] == x * z
        @test_throws DimensionMismatch Fill(x, 1, 1) * ZV
        @test_throws DimensionMismatch Fill(oneton(1,1), 1, length(ZV)) * ZV

        @test_throws DimensionMismatch Ones(SMatrix{3,3,Int,9},2) * Ones(SMatrix{2,2,Int,4},1,2)
    end

    @testset "Check multiplication by Adjoint vectors works as expected." begin
        @test @inferred(oneton(4, 3)' * Zeros(4)) ≡ Zeros(3)
        @test @inferred(oneton(4)' * Zeros(4)) ≡ @inferred(transpose(oneton(4)) * Zeros(4)) == 0.0
        @test [1, 2, 3]' * Zeros{Int}(3) ≡ zero(Int)
        @test [SVector(1,2)', SVector(2,3)', SVector(3,4)']' * Zeros{Int}(3) === SVector(0,0)
        @test_throws DimensionMismatch oneton(4)' * Zeros(3)
        @test Zeros(5)' * oneton(5,3) ≡ Zeros(5)'*Zeros(5,3) ≡ Zeros(5)'*Ones(5,3) ≡ Zeros(3)'
        @test abs(Zeros(5)' * oneton(5)) == abs(Zeros(5)' * Zeros(5)) ≡ abs(Zeros(5)' * Ones(5)) == 0.0
        @test Zeros(5) * Zeros(6)' ≡ Zeros(5,1) * Zeros(6)' ≡ Zeros(5,6)
        @test oneton(5) * Zeros(6)' ≡ oneton(5,1) * Zeros(6)' ≡ Zeros(5,6)
        @test Zeros(5) * oneton(6)' ≡ Zeros(5,6)

        @test @inferred(Zeros{Int}(0)' * Zeros{Int}(0)) === zero(Int)

        @test Any[1,2.0]' * Zeros{Int}(2) == 0
        @test Real[1,2.0]' * Zeros{Int}(2) == 0

        @test @inferred(([[1,2]])' * Zeros{SVector{2,Int}}(1)) ≡ 0
        @test ([[1,2], [1,2]])' * Zeros{SVector{2,Int}}(2) ≡ 0
        @test_throws DimensionMismatch ([[1,2,3]])' * Zeros{SVector{2,Int}}(1)
        @test_throws DimensionMismatch ([[1,2,3], [1,2]])' * Zeros{SVector{2,Int}}(2)

        A = SMatrix{2,1,Int,2}[]'
        B = Zeros(SVector{2,Int},0)
        C = collect(B)
        @test @inferred(A * B) == @inferred(A * C)
    end

    @testset "Check multiplication by Transpose-d vectors works as expected." begin
        @test transpose(oneton(4, 3)) * Zeros(4) === Zeros(3)
        @test transpose(oneton(4)) * Zeros(4) == 0.0
        @test transpose([1, 2, 3]) * Zeros{Int}(3) === zero(Int)
        @test_throws DimensionMismatch transpose(oneton(4)) * Zeros(3)
        @test transpose(Zeros(5)) * oneton(5,3) ≡ transpose(Zeros(5))*Zeros(5,3) ≡ transpose(Zeros(5))*Ones(5,3) ≡ transpose(Zeros(3))
        @test abs(transpose(Zeros(5)) * oneton(5)) ≡ abs(transpose(Zeros(5)) * Zeros(5)) ≡ abs(transpose(Zeros(5)) * Ones(5)) ≡ 0.0
        @test oneton(5) * transpose(Zeros(6)) ≡ oneton(5,1) * transpose(Zeros(6)) ≡ Zeros(5,6)
        @test Zeros(5) * transpose(oneton(6)) ≡ Zeros(5,6)
        @test transpose(oneton(5)) * Zeros(5) == 0.0
        @test transpose(oneton(5) .+ im) * Zeros(5) == 0.0 + 0im

        @test @inferred(transpose(Zeros{Int}(0)) * Zeros{Int}(0)) === zero(Int)

        @test transpose(Any[1,2.0]) * Zeros{Int}(2) == 0
        @test transpose(Real[1,2.0]) * Zeros{Int}(2) == 0

        @test @inferred(transpose([[1,2]]) * Zeros{SVector{2,Int}}(1)) ≡ 0
        @test transpose([[1,2], [1,2]]) * Zeros{SVector{2,Int}}(2) ≡ 0
        @test_throws DimensionMismatch transpose([[1,2,3]]) * Zeros{SVector{2,Int}}(1)
        @test_throws DimensionMismatch transpose([[1,2,3], [1,2]]) * Zeros{SVector{2,Int}}(2)

        A = transpose(SMatrix{2,1,Int,2}[])
        B = Zeros(SVector{2,Int},0)
        C = collect(B)
        @test @inferred(A * B) == @inferred(A * C)

        @testset "Diagonal mul introduced in v1.9" begin
            @test Zeros(5)'*Diagonal(1:5) ≡ Zeros(5)'
            @test transpose(Zeros(5))*Diagonal(1:5) ≡ transpose(Zeros(5))
            @test Zeros(5)'*Diagonal(1:5)*(1:5) ==
                (1:5)'*Diagonal(1:5)*Zeros(5) ==
                transpose(1:5)*Diagonal(1:5)*Zeros(5) ==
                Zeros(5)'*Diagonal(1:5)*Zeros(5) ==
                transpose(Zeros(5))*Diagonal(1:5)*Zeros(5) ==
                transpose(Zeros(5))*Diagonal(1:5)*(1:5)

            @test_throws DimensionMismatch Zeros(6)'*Diagonal(1:5)*Zeros(5)
            @test_throws DimensionMismatch Zeros(5)'*Diagonal(1:6)*Zeros(5)
            @test_throws DimensionMismatch Zeros(5)'*Diagonal(1:5)*Zeros(6)
        end
    end

    z1, z2 = Zeros{Float64}(4), Zeros{Int}(4)

    @testset "`Zeros` are closed under addition and subtraction (both unary and binary)." begin
        @test +(Zeros{Float64}(3, 5)) === Zeros{Float64}(3, 5)
        @test -(Zeros{Float32}(5, 2)) === Zeros{Float32}(5, 2)

        @test +(z1) === z1
        @test -(z1) === z1

        test_addition_subtraction_dot((z1, z2), (z1, z2), Zeros)
        test_addition_and_subtraction_dim_mismatch(z1, Zeros{Float64}(4, 2))
    end

    # `Zeros` +/- `Fill`s should yield `Fills`.
    fill1, fill2 = Fill(5.0, 4), Fill(5, 4)
    test_addition_subtraction_dot((z1, z2), (fill1, fill2), Fill)
    test_addition_and_subtraction_dim_mismatch(z1, Fill(5, 5))

    X = randn(3, 5)
    for op in [+, -]

        # Addition / subtraction with same eltypes.
        @test op(Zeros(6, 4), Zeros(6, 4)) === Zeros(6, 4)
        @test_throws DimensionMismatch op(X, Zeros(4, 6))
        @test eltype(op(Zeros(3, 5), X)) == Float64

        # Different eltypes, the other way around.
        @test op(X, Zeros{Float32}(3, 5)) isa Matrix{Float64}
        @test !(op(X, Zeros{Float32}(3, 5)) === X)
        @test op(X, Zeros{Float32}(3, 5)) == X
        @test !(op(X, Zeros{ComplexF64}(3, 5)) === X)
        @test op(X, Zeros{ComplexF64}(3, 5)) == X

        # Addition / subtraction of Zeros.
        @test eltype(op(Zeros{Float64}(4, 5), Zeros{Int}(4, 5))) == Float64
        @test eltype(op(Zeros{Int}(5, 4), Zeros{Float32}(5, 4))) == Float32
        @test op(Zeros{Float64}(4, 5), Zeros{Int}(4, 5)) isa Zeros{Float64}
        @test op(Zeros{Float64}(4, 5), Zeros{Int}(4, 5)) === Zeros{Float64}(4, 5)
    end

    @testset "Zeros +/- dense where + / - have different results." begin
        @test +(Zeros(3, 5), X) == X && +(X, Zeros(3, 5)) == X
        @test !(Zeros(3, 5) + X === X) && !(X + Zeros(3, 5) === X)
        @test -(Zeros(3, 5), X) == -X
    end

    @testset "Addition with different eltypes." begin
        @test +(Zeros{Float32}(3, 5), X) isa Matrix{Float64}
        @test !(+(Zeros{Float32}(3, 5), X) === X)
        @test +(Zeros{Float32}(3, 5), X) == X
        @test !(+(Zeros{ComplexF64}(3, 5), X) === X)
        @test +(Zeros{ComplexF64}(3, 5), X) == X
    end

    @testset "Subtraction with different eltypes." begin
        @test -(Zeros{Float32}(3, 5), X) isa Matrix{Float64}
        @test -(Zeros{Float32}(3, 5), X) == -X
        @test -(Zeros{ComplexF64}(3, 5), X) == -X
    end

    @testset "Tests for ranges." begin
        X = randn(5)
        @test !(Zeros(5) + X ≡ X)
        @test Zeros{Int}(5) + (1:5) ≡ (1:5) + Zeros{Int}(5) ≡ (1:5)
        @test Zeros(5) + (1:5) ≡ (1:5) + Zeros(5) ≡ (1.0:1.0:5.0)
        @test (1:5) - Zeros{Int}(5) ≡ (1:5)
        @test Zeros{Int}(5) - (1:5) ≡ -1:-1:-5
        @test Zeros(5) - (1:5) ≡ -1.0:-1.0:-5.0
        @test Zeros{Int}(5) + (1.0:5) ≡ (1.0:5) + Zeros{Int}(5) ≡ 1.0:5
    end

    @testset "test Base.zero" begin
        @test zero(Zeros(10)) == Zeros(10)
        @test zero(Ones(10,10)) == Zeros(10,10)
        @test zero(Fill(0.5, 10, 10)) == Zeros(10,10)
    end

    @testset "Matrix ±" begin
        x = Fill([1,2], 5)
        z = Zeros{SVector{2,Int}}(5)
        @test +(z) ≡ -(z) ≡ z
        @test +(x) == x
        @test -(x) == Fill(-[1,2], 5)
    end
end

@testset "maximum/minimum/svd/sort" begin
    @test maximum(Fill(1, 1_000_000_000)) == minimum(Fill(1, 1_000_000_000)) == 1
    @test svdvals(fill(2,5,6)) ≈ svdvals(Fill(2,5,6))
    @test svdvals(Eye(5)) === Fill(1.0,5)
    @test sort(Ones(5)) == sort!(Ones(5))

    @test_throws MethodError issorted(Fill(im, 2))
    @test_throws MethodError sort(Fill(im, 2))
    @test_throws MethodError sort!(Fill(im, 2))
end

@testset "Cumsum, accumulate and diff" begin
    @test @inferred(sum(Fill(3,10))) ≡ 30
    @test @inferred(reduce(+, Fill(3,10))) ≡ 30
    @test @inferred(sum(x -> x + 1, Fill(3,10))) ≡ 40
    @test @inferred(cumsum(Fill(3,10))) ≡ @inferred(accumulate(+, Fill(3,10))) ≡ StepRangeLen(3,3,10)
    @test @inferred(accumulate(-, Fill(3,10))) ≡ StepRangeLen(3,-3,10)

    @test @inferred(sum(Ones(10))) ≡ 10.0
    @test @inferred(sum(x -> x + 1, Ones(10))) ≡ 20.0
    @test @inferred(cumsum(Ones(10))) ≡ @inferred(accumulate(+, Ones(10))) ≡ StepRangeLen(1.0, 1.0, 10)
    @test @inferred(accumulate(-, Ones(10))) ≡ StepRangeLen(1.0,-1.0,10)

    @test sum(Ones{Int}(10)) ≡ 10
    @test sum(x -> x + 1, Ones{Int}(10)) ≡ 20
    @test cumsum(Ones{Int}(10)) ≡ accumulate(+,Ones{Int}(10)) ≡ Base.OneTo(10)
    @test accumulate(-, Ones{Int}(10)) ≡ StepRangeLen(1,-1,10)

    @test sum(Zeros(10)) ≡ 0.0
    @test sum(x -> x + 1, Zeros(10)) ≡ 10.0
    @test cumsum(Zeros(10)) ≡ accumulate(+,Zeros(10)) ≡ accumulate(-,Zeros(10)) ≡ Zeros(10)

    @test sum(Zeros{Int}(10)) ≡ 0
    @test sum(x -> x + 1, Zeros{Int}(10)) ≡ 10
    @test cumsum(Zeros{Int}(10)) ≡ accumulate(+,Zeros{Int}(10)) ≡ accumulate(-,Zeros{Int}(10)) ≡ Zeros{Int}(10)

    # we want cumsum of fills to match the types of the standard cusum
    @test all(cumsum(Zeros{Bool}(10)) .≡ cumsum(zeros(Bool,10)))
    @test all(accumulate(+, Zeros{Bool}(10)) .≡ accumulate(+, zeros(Bool,10)) .≡ accumulate(-, zeros(Bool,10)))
    @test cumsum(Zeros{Bool}(10)) ≡ accumulate(+, Zeros{Bool}(10)) ≡ accumulate(-, Zeros{Bool}(10)) ≡ Zeros{Int}(10)
    @test cumsum(Ones{Bool}(10)) ≡ accumulate(+, Ones{Bool}(10)) ≡ Base.OneTo{Int}(10)
    @test all(cumsum(Fill(true,10)) .≡ cumsum(fill(true,10)))
    @test cumsum(Fill(true,10)) ≡ StepRangeLen(1, true, 10)

    @test all(cumsum(Zeros{UInt8}(10)) .≡ cumsum(zeros(UInt8,10)))
    @test all(accumulate(+, Zeros{UInt8}(10)) .≡ accumulate(+, zeros(UInt8,10)))
    @test cumsum(Zeros{UInt8}(10)) ≡ Zeros{UInt64}(10)
    @test accumulate(+, Zeros{UInt8}(10)) ≡ accumulate(-, Zeros{UInt8}(10)) ≡ Zeros{UInt8}(10)
    
    @test all(cumsum(Ones{UInt8}(10)) .≡ cumsum(ones(UInt8,10)))
    @test all(accumulate(+, Ones{UInt8}(10)) .≡ accumulate(+, ones(UInt8,10)))
    @test cumsum(Ones{UInt8}(10)) ≡ Base.OneTo(UInt64(10))
    @test accumulate(+, Ones{UInt8}(10)) ≡ Base.OneTo(UInt8(10))
    
    @test all(cumsum(Fill(UInt8(2),10)) .≡ cumsum(fill(UInt8(2),10)))
    @test all(accumulate(+,  Fill(UInt8(2))) .≡ accumulate(+, fill(UInt8(2))))
    @test cumsum(Fill(UInt8(2),10)) ≡ StepRangeLen(UInt64(2), UInt8(2), 10)
    @test accumulate(+, Fill(UInt8(2),10)) ≡ StepRangeLen(UInt8(2), UInt8(2), 10)

    @test diff(Fill(1,10)) ≡ Zeros{Int}(9)
    @test diff(Ones{Float64}(10)) ≡ Zeros{Float64}(9)
    @test_throws UndefKeywordError cumsum(Fill(1,1,5))

    @test @inferred(sum([Ones(4)])) ≡ Fill(1.0, 4)
    @test @inferred(sum([Trues(4)])) ≡ Fill(1, 4)

    @testset "infinite arrays" begin
        r = InfiniteArrays.OneToInf()
        A = Ones{Int}((r,))
        @test isinf(sum(A))
        @test sum(A) == length(A)
        @test sum(x->x^2, A) == sum(A.^2)
        @testset "IteratorSize" begin
            @test (@inferred Base.IteratorSize(Ones())) == Base.IteratorSize(ones())
            @test (@inferred Base.IteratorSize(Ones(2))) == Base.IteratorSize(ones(2))
            @test (@inferred Base.IteratorSize(Ones(r))) == Base.IsInfinite()
            @test (@inferred Base.IteratorSize(Fill(2, (1:2, 1:2)))) == Base.HasShape{2}()
            @test (@inferred Base.IteratorSize(Fill(2, (1:2, r)))) == Base.IsInfinite()
            @test (@inferred Base.IteratorSize(Fill(2, (r, 1:2)))) == Base.IsInfinite()
            @test (@inferred Base.IteratorSize(Fill(2, (r, r)))) == Base.IsInfinite()
        end

        @test issorted(Fill(2, (InfiniteArrays.OneToInf(),)))
    end
end

@testset "iterators" begin
    @testset "invalid state" begin
        @test isnothing(iterate(Ones(4), (1,-3)))
        @test isempty(Iterators.rest(Ones(4), (1,-3)))
    end
    @testset "Iterators.rest" begin
        @test Iterators.rest(Fill(4, 10), (4, 3)) === Fill(4, 7)
        # Base.rest
        a, b... = Fill(3, 4)
        @test a === 3
        @test b === Fill(3, 3)
        a, b... = Ones(3, 4)
        @test a === 1.0
        @test b === Ones(11)
    end
    @testset "Iterators.drop/take" begin
        @test Iterators.drop(Fill(4, 10), 3) === Fill(4, 7)
        @test Iterators.take(Fill(4, 10), 3) === Fill(4, 3)
        @test Iterators.drop(Fill(4, 10), 0) === Fill(4, 10)
        @test Iterators.take(Fill(4, 10), 0) === Fill(4, 0)
        @test Iterators.drop(Fill(4, 10), 11) === Fill(4, 0)
        @test Iterators.take(Fill(4, 10), 11) === Fill(4, 10)
        @test_throws ArgumentError Iterators.drop(Fill(4, 10), -11)
        @test_throws ArgumentError Iterators.take(Fill(4, 10), -11)
        @test Iterators.drop(Ones(4, 10), 3) === Ones(37)
        @test Iterators.take(Ones(4, 10), 3) === Ones(3)
    end
end

@testset "Broadcast" begin
    x = Fill(5,5)
    @test (.+)(x) ≡ x
    @test (.-)(x) ≡ -x
    @test exp.(x) ≡ Fill(exp(5),5)
    @test x .+ 1 ≡ Fill(6,5)
    @test 1 .+ x ≡ Fill(6,5)
    @test x .+ x ≡ Fill(10,5)
    @test x .+ Ones(5) ≡ Fill(6.0,5)
    f = (x,y) -> cos(x*y)
    @test f.(x, Ones(5)) ≡ Fill(f(5,1.0),5)
    @test x .^ 1 ≡ Fill(5,5)

    y = Ones(5,5)
    @test (.+)(y) ≡ Ones(5,5)
    @test (.-)(y) ≡ Fill(-1.0,5,5)
    @test exp.(y) ≡ Fill(exp(1),5,5)
    @test y .+ 1 ≡ Fill(2.0,5,5)
    @test y .+ y ≡ Fill(2.0,5,5)
    @test y .* y ≡ y ./ y ≡ y .\ y ≡ y
    @test y .^ 1 ≡ y .^ 0 ≡ Ones(5,5)

    rng = MersenneTwister(123456)
    sizes = [(5, 4), (5, 1), (1, 4), (1, 1), (5,)]
    for sx in sizes, sy in sizes
        x, y = Fill(randn(rng), sx), Fill(randn(rng), sy)
        x_one, y_one = Ones(sx), Ones(sy)
        x_zero, y_zero = Zeros(sx), Zeros(sy)
        x_dense, y_dense = randn(rng, sx), randn(rng, sy)

        for x in [x, x_one, x_zero, x_dense], y in [y, y_one, y_zero, y_dense]
            @test x .+ y == collect(x) .+ collect(y)
        end
        @test x_zero .+ y_zero isa Zeros
        @test x_zero .+ y_one isa Ones
        @test x_one .+ y_zero isa Ones

        for x in [x, x_one, x_zero, x_dense], y in [y, y_one, y_zero, y_dense]
            @test x .* y == collect(x) .* collect(y)
        end
        for x in [x, x_one, x_zero, x_dense]
            @test x .* y_zero isa Zeros
        end
        for y in [y, y_one, y_zero, y_dense]
            @test x_zero .* y isa Zeros
        end
    end

    @test Zeros{Int}(5) .^ 0 ≡ Ones{Int}(5)
    @test Zeros{Int}(5) .^ 1 ≡ Zeros{Int}(5)
    @test Zeros{Int}(5) .+ Zeros(5) isa Zeros{Float64}

    # Test for conj, real and imag with complex element types
    @test conj(Zeros{ComplexF64}(10)) isa Zeros{ComplexF64}
    @test conj(Zeros{ComplexF64}(10,10)) isa Zeros{ComplexF64}
    @test conj(Ones{ComplexF64}(10)) isa Ones{ComplexF64}
    @test conj(Ones{ComplexF64}(10,10)) isa Ones{ComplexF64}
    @test real(Zeros{Float64}(10)) isa Zeros{Float64}
    @test real(Zeros{Float64}(10,10)) isa Zeros{Float64}
    @test real(Zeros{ComplexF64}(10)) isa Zeros{Float64}
    @test real(Zeros{ComplexF64}(10,10)) isa Zeros{Float64}
    @test real(Ones{Float64}(10)) isa Ones{Float64}
    @test real(Ones{Float64}(10,10)) isa Ones{Float64}
    @test real(Ones{ComplexF64}(10)) isa Ones{Float64}
    @test real(Ones{ComplexF64}(10,10)) isa Ones{Float64}
    @test imag(Zeros{Float64}(10)) isa Zeros{Float64}
    @test imag(Zeros{Float64}(10,10)) isa Zeros{Float64}
    @test imag(Zeros{ComplexF64}(10)) isa Zeros{Float64}
    @test imag(Zeros{ComplexF64}(10,10)) isa Zeros{Float64}
    @test imag(Ones{Float64}(10)) isa Zeros{Float64}
    @test imag(Ones{Float64}(10,10)) isa Zeros{Float64}
    @test imag(Ones{ComplexF64}(10)) isa Zeros{Float64}
    @test imag(Ones{ComplexF64}(10,10)) isa Zeros{Float64}

    @testset "range broadcast" begin
        rnge = range(-5.0, step=1.0, length=10)
        @test broadcast(*, Fill(5.0, 10), rnge) == broadcast(*, 5.0, rnge)
        @test broadcast(*, Zeros(10, 10), rnge) ≡ Zeros{Float64}(10, 10)
        @test broadcast(*, rnge, Zeros(10, 10)) ≡ Zeros{Float64}(10, 10)
        @test broadcast(*, Ones{Int}(10), rnge) ≡ rnge
        @test broadcast(*, rnge, Ones{Int}(10)) ≡ rnge
        @test broadcast(*, Ones(10), -5:4) ≡ broadcast(*, -5:4, Ones(10)) ≡ rnge
        @test broadcast(*, Ones(10), -5:1:4) ≡ broadcast(*, -5:1:4, Ones(10)) ≡ rnge
        @test_throws DimensionMismatch broadcast(*, Fill(5.0, 11), rnge)
        @test broadcast(*, rnge, Fill(5.0, 10)) == broadcast(*, rnge, 5.0)
        @test_throws DimensionMismatch broadcast(*, rnge, Fill(5.0, 11))

        # following should pass using alternative implementation in code
        deg = 5:5
        @test_throws ArgumentError @inferred(broadcast(*, Fill(5.0, 10), deg)) == broadcast(*, fill(5.0,10), deg)
        @test_throws ArgumentError @inferred(broadcast(*, deg, Fill(5.0, 10))) == broadcast(*, deg, fill(5.0,10))

        @test rnge .+ Zeros(10) ≡ rnge .- Zeros(10) ≡ Zeros(10) .+ rnge ≡ rnge

        @test_throws DimensionMismatch rnge .+ Zeros(5)
        @test_throws DimensionMismatch rnge .- Zeros(5)
        @test_throws DimensionMismatch Zeros(5) .+ rnge

        @test Fill(2,10) + (1:10) isa UnitRange
        @test (1:10) + Fill(2,10) isa UnitRange

        f = Fill(1+im,10)
        @test f + rnge isa AbstractRange
        @test f + rnge == rnge + f
        @test f + (1:10) isa AbstractRange
    end

    @testset "Special Zeros/Ones" begin
        @test broadcast(+,Zeros(5)) ≡ broadcast(-,Zeros(5)) ≡ Zeros(5)
        @test broadcast(+,Ones(5)) ≡ Ones(5)

        @test Zeros(5) .* Ones(5) ≡ Zeros(5) .* 1 ≡ Zeros(5)
        @test Zeros(5) .* Fill(5.0, 5) ≡ Zeros(5) .* 5.0 ≡ Zeros(5)
        @test Ones(5) .* Zeros(5) ≡ 1 .* Zeros(5) ≡ Zeros(5)
        @test Fill(5.0, 5) .* Zeros(5) ≡ 5.0 .* Zeros(5) ≡ Zeros(5)

        @test Zeros(5) ./ Ones(5) ≡ Zeros(5) ./ 1 ≡ Zeros(5)
        @test Zeros(5) ./ Fill(5.0, 5) ≡ Zeros(5) ./ 5.0 ≡ Zeros(5)
        @test Ones(5) .\ Zeros(5) ≡ 1 .\ Zeros(5) ≡ Zeros(5)
        @test Fill(5.0, 5) .\ Zeros(5) ≡ 5.0 .\ Zeros(5) ≡ Zeros(5)

        @test conj.(Zeros(5)) ≡ Zeros(5)
        @test conj.(Zeros{ComplexF64}(5)) ≡ Zeros{ComplexF64}(5)

        @test_throws DimensionMismatch broadcast(*, Ones(3), 1:6)
        @test_throws DimensionMismatch broadcast(*, 1:6, Ones(3))
        @test_throws DimensionMismatch broadcast(*, Fill(1,3), 1:6)
        @test_throws DimensionMismatch broadcast(*, 1:6, Fill(1,3))

        @testset "Number" begin
            @test broadcast(*, Zeros(5), 2) ≡ broadcast(*, 2, Zeros(5)) ≡ Zeros(5)
        end

        @testset "Nested" begin
            @test randn(5) .\ rand(5) .* Zeros(5) ≡ Zeros(5)
            @test broadcast(*, Zeros(5), Base.Broadcast.broadcasted(\, randn(5), rand(5))) ≡ Zeros(5)
        end

        @testset "array-valued" begin
            @test broadcast(*, Fill([1,2],3), 1:3) == broadcast(*, 1:3, Fill([1,2],3)) == broadcast(*, 1:3, fill([1,2],3))
            @test broadcast(*, Fill([1,2],3), Zeros(3)) == broadcast(*, Zeros(3), Fill([1,2],3)) == broadcast(*, zeros(3), fill([1,2],3))
            @test broadcast(*, Fill([1,2],3), Zeros(3)) isa Fill{Vector{Float64}}
            @test broadcast(*, [[1,2], [3,4,5]], Zeros(2)) == broadcast(*, Zeros(2), [[1,2], [3,4,5]]) == broadcast(*, zeros(2), [[1,2], [3,4,5]])
        end

        @testset "NaN" begin
            @test Zeros(5) ./ Zeros(5) ≡ Zeros(5) .\ Zeros(5) ≡ Fill(NaN,5)
            @test Zeros{Int}(5,6) ./ Zeros{Int}(5) ≡ Zeros{Int}(5) .\ Zeros{Int}(5,6) ≡ Fill(NaN,5,6)
        end

        @testset "Addition/Subtraction" begin
            @test Zeros{Int}(5) .+ (1:5) ≡ (1:5) .+ Zeros{Int}(5) ≡ (1:5) .- Zeros{Int}(5) ≡ 1:5
            @test Zeros{Int}(1) .+ (1:5) ≡ (1:5) .+ Zeros{Int}(1) ≡ (1:5) .- Zeros{Int}(1) ≡ 1:5
            @test Zeros(5) .+ (1:5) == (1:5) .+ Zeros(5) == (1:5) .- Zeros(5) == 1:5
            @test Zeros{Int}(5) .+ Fill(1,5) ≡ Fill(1,5) .+ Zeros{Int}(5) ≡ Fill(1,5) .- Zeros{Int}(5) ≡ Fill(1,5)
            @test_throws DimensionMismatch Zeros{Int}(2) .+ (1:5)
            @test_throws DimensionMismatch (1:5) .+ Zeros{Int}(2)

            for v in (rand(Bool, 5), [1:5;], SVector{5}(1:5), SVector{5,ComplexF16}(1:5)), T in (Bool, Int, Float64)
                TT = eltype(v + zeros(T, 5))
                S = v isa SVector ? SVector{5,TT} : Vector{TT}

                a = @inferred(Zeros{T}(5) .+ v)
                b = @inferred(v .+ Zeros{T}(5))
                c = @inferred(v .- Zeros{T}(5))
                @test a == b == c == v
                d = @inferred(Zeros{T}(5) .- v)
                @test d == -v
                @test all(Base.Fix2(isa, S), (a,b,c,d))
            end
        end
    end

    @testset "support Ref" begin
        @test Fill(1,10) .- 1 ≡ Fill(1,10) .- Ref(1) ≡ Fill(1,10) .- Ref(1I)
        @test Fill([1 2; 3 4],10) .- Ref(1I) == Fill([0 2; 3 3],10)
        @test Ref(1I) .+ Fill([1 2; 3 4],10) == Fill([2 2; 3 5],10)
    end

    @testset "Special Ones" begin
        @test Ones{Int}(5) .* (1:5) ≡ (1:5) .* Ones{Int}(5) ≡ 1:5
        @test Ones(5) .* (1:5) ≡ (1:5) .* Ones(5) ≡ 1.0:5
        @test Ones{Int}(5) .* Ones{Int}(5) ≡ Ones{Int}(5)
        @test Ones{Int}(5,2) .* (1:5) == Array(Ones{Int}(5,2)) .* Array(1:5)
        @test (1:5) .* Ones{Int}(5,2)  == Array(1:5) .* Array(Ones{Int}(5,2))
        @test (1:0.5:5) .* Ones{Int}(9,2)  == Array(1:0.5:5) .* Array(Ones{Int}(9,2))
        @test Ones{Int}(9,2) .* (1:0.5:5)  == Array(Ones{Int}(9,2)) .* Array(1:0.5:5)
        @test_throws DimensionMismatch Ones{Int}(6) .* (1:5)
        @test_throws DimensionMismatch (1:5) .* Ones{Int}(6)
        @test_throws DimensionMismatch Ones{Int}(5) .* Ones{Int}(6)
    end

    @testset "Zeros -" begin
        @test Zeros(10) - Zeros(10) ≡ Zeros(10)
        @test Ones(10) - Zeros(10) ≡ Ones(10)
        @test Ones(10) - Ones(10) ≡ Zeros(10)
        @test Fill(1,10) - Zeros(10) ≡ Fill(1.0,10)

        @test Zeros(10) .- Zeros(10) ≡ Zeros(10)
        @test Ones(10) .- Zeros(10) ≡ Ones(10)
        @test Ones(10) .- Ones(10) ≡ Zeros(10)
        @test Fill(1,10) .- Zeros(10) ≡ Fill(1.0,10)

        @test Zeros(10) .- Zeros(1,9) ≡ Zeros(10,9)
        @test Ones(10) .- Zeros(1,9) ≡ Ones(10,9)
        @test Ones(10) .- Ones(1,9) ≡ Zeros(10,9)

    end

    @testset "issue #208" begin
        TS = (Bool, Int, Float32, Float64)
        for S in TS, T in TS
            u = rand(S, 2)
            v = Zeros(T, 2)
            if zero(S) + zero(T) isa S
                @test @inferred(Broadcast.broadcasted(-, u, v)) === u
                @test @inferred(Broadcast.broadcasted(+, u, v)) === u
                @test @inferred(Broadcast.broadcasted(+, v, u)) === u
            else
                @test @inferred(Broadcast.broadcasted(-, u, v)) isa Broadcast.Broadcasted
                @test @inferred(Broadcast.broadcasted(+, u, v)) isa Broadcast. Broadcasted
                @test @inferred(Broadcast.broadcasted(+, v, u)) isa Broadcast.Broadcasted
            end
            @test @inferred(Broadcast.broadcasted(-, v, u)) isa Broadcast.Broadcasted
        end
    end

    @testset "Zero .*" begin
        TS = (Bool, Int, Float32, Float64)
        for S in TS, T in TS
            U = typeof(zero(S) * zero(T))
            @test Zeros{S}(10) .* Zeros{T}(10) ≡ Zeros{U}(10)
            @test rand(S, 10) .* Zeros(T, 10) ≡ Zeros(U, 10)
            @test Zeros(S, 10) .* rand(T, 10) ≡ Zeros(U, 10)
            if S !== Bool
                @test (S(1):S(10)) .* Zeros(T, 10) ≡ Zeros(U, 10)
                @test_throws DimensionMismatch (S(1):S(11)) .* Zeros(T, 10)
            end
            if T !== Bool
                @test Zeros(S, 10) .* (T(1):T(10)) ≡ Zeros(U, 10)
                @test_throws DimensionMismatch Zeros(S, 10) .* (T(1):T(11))
            end
        end
    end
end

@testset "map" begin
    x1 = Ones(5)
    @test map(exp,x1) === Fill(exp(1.0),5)
    @test map(isone,x1) === Fill(true,5)

    x0 = Zeros(5)
    @test map(exp,x0) === exp.(x0)

    x2 = Fill(2,5,3)
    @test map(exp,x2) === Fill(exp(2),5,3)

    @test map(+, x1, x2) === Fill(3.0, 5)
    @test map(+, x2, x2) === x2 .+ x2
    @test_throws DimensionMismatch map(+, x2', x2)

    # Issue https://github.com/JuliaArrays/FillArrays.jl/issues/179
    if VERSION < v"1.11.0-"  # In 1.11, 1-arg map & mapreduce was removed
        @test map(() -> "ok") == "ok"  # was MethodError: reducing over an empty collection is not allowed
        @test mapreduce(() -> "ok", *) == "ok"
    else
        @test_throws "no method matching map" map(() -> "ok")
        @test_throws "no method matching map" mapreduce(() -> "ok", *)
    end
end

@testset "mapreduce" begin
    x = rand(3, 4)
    y = fill(1.0, 3, 4)
    Y = Fill(1.0, 3, 4)
    O = Ones(3, 4)

    @test mapreduce(exp, +, Y) == mapreduce(exp, +, y)
    @test mapreduce(exp, +, Y; dims=2) == mapreduce(exp, +, y; dims=2)
    @test mapreduce(identity, +, Y) == sum(y) == sum(Y)
    @test mapreduce(identity, +, Y, dims=1) == sum(y, dims=1) == sum(Y, dims=1)

    @test mapreduce(exp, +, Y; dims=(1,), init=5.0) == mapreduce(exp, +, y; dims=(1,), init=5.0)

    # Two arrays
    @test mapreduce(*, +, x, Y) == mapreduce(*, +, x, y)
    @test mapreduce(*, +, Y, x) == mapreduce(*, +, y, x)
    @test mapreduce(*, +, x, O) == mapreduce(*, +, x, y)
    @test mapreduce(*, +, Y, O) == mapreduce(*, +, y, y)

    f2(x,y) = 1 + x/y
    op2(x,y) = x^2 + 3y
    @test mapreduce(f2, op2, x, Y) == mapreduce(f2, op2, x, y)

    @test mapreduce(f2, op2, x, Y, dims=1, init=5.0) == mapreduce(f2, op2, x, y, dims=1, init=5.0)
    @test mapreduce(f2, op2, Y, x, dims=1, init=5.0) == mapreduce(f2, op2, y, x, dims=1, init=5.0)
    @test mapreduce(f2, op2, x, O, dims=1, init=5.0) == mapreduce(f2, op2, x, y, dims=1, init=5.0)
    @test mapreduce(f2, op2, Y, O, dims=1, init=5.0) == mapreduce(f2, op2, y, y, dims=1, init=5.0)

    # More than two
    @test mapreduce(+, +, x, Y, x) == mapreduce(+, +, x, y, x)
    @test mapreduce(+, +, Y, x, x) == mapreduce(+, +, y, x, x)
    @test mapreduce(+, +, x, O, Y) == mapreduce(+, +, x, y, y)
    @test mapreduce(+, +, Y, O, Y) == mapreduce(+, +, y, y, y)
    @test mapreduce(+, +, Y, O, Y, x) == mapreduce(+, +, y, y, y, x)
end

@testset "Offset indexing" begin
    A = Fill(3, (Base.Slice(-1:1),))
    @test axes(A)  == (Base.Slice(-1:1),)
    @test A[0] == 3
    @test_throws BoundsError A[2]
    @test_throws BoundsError A[-2]

    A = Zeros((Base.Slice(-1:1),))
    @test axes(A)  == (Base.Slice(-1:1),)
    @test A[0] == 0
    @test_throws BoundsError A[2]
    @test_throws BoundsError A[-2]
end

@testset "0-dimensional" begin
    A = Fill{Int,0,Tuple{}}(3, ())

    @test A[] ≡ A[1] ≡ 3
    @test A ≡ Fill{Int,0}(3, ()) ≡ Fill(3, ()) ≡ Fill(3)
    @test size(A) == ()
    @test axes(A) == ()

    A = Ones{Int,0,Tuple{}}(())
    @test A[] ≡ A[1] ≡ 1
    @test A ≡ Ones{Int,0}(()) ≡ Ones{Int}(()) ≡ Ones{Int}()

    A = Zeros{Int,0,Tuple{}}(())
    @test A[] ≡ A[1] ≡ 0
    @test A ≡ Zeros{Int,0}(()) ≡ Zeros{Int}(()) ≡ Zeros{Int}()
end

@testset "unique" begin
    @test unique(Fill(12, 20)) == unique(fill(12, 20))
    @test unique(Fill(1, 0)) == []
    @test unique(Zeros(0)) == Zeros(0)
    @test !allunique(Fill("a", 2))
    @test allunique(Ones(0))
end

@testset "iterate" begin
    for d in (0, 1, 2, 100)
        for T in (Float64, Int)
            m = Eye(d)
            mcp = [x for x in m]
            @test mcp == m
            @test eltype(mcp) == eltype(m)
        end
    end
end

@testset "properties" begin
    for d in (0, 1, 2, 100)
        @test isone(Eye(d))
    end
end

@testset "any all iszero isone" begin
    for T in (Int, Float64, ComplexF64)
        for m in (Eye{T}(0), Eye{T}(0, 0), Eye{T}(0, 1), Eye{T}(1, 0))
            @test ! any(isone, m)
            @test ! any(iszero, m)
            @test ! all(iszero, m)
            @test ! all(isone, m)
        end
        @testset for d in (0, 1)
            for m in (Eye{T}(d), Eye{T}(d, d))
                M = Array(m)
                @test ! any(iszero, m)
                @test ! all(iszero, m)
                @test any(isone, m) == !isempty(m)
                @test all(isone, m) == !isempty(m)
                if !isempty(m)
                    @test ! any(iszero, m) == ! any(iszero, M)
                    @test ! all(iszero, m) == ! all(iszero, M)
                    @test any(isone, m) == any(isone, M)
                    @test all(isone, m) == all(isone, M)
                end
            end

            for m in (Eye{T}(d, d + 1), Eye{T}(d + 1, d))
                M = Array(m)
                @test any(iszero, m) == !isempty(m)
                @test ! all(iszero, m)
                @test any(isone, m) == !isempty(m)
                @test ! all(isone, m)
                if !isempty(M)
                    @test any(iszero, m) == any(iszero, M)
                    @test ! all(iszero, m) == ! all(iszero, M)
                    @test any(isone, m) == any(isone, M)
                    @test ! all(isone, m) == ! all(isone, M)
                end
            end

            onem = Ones{T}(d, d)
            @test isone(onem) == isone(Array(onem))
            @test iszero(onem) == isempty(onem) == iszero(Array(onem))

            if d > 0
                @test !isone(Ones{T}(d, 2d))
            end

            zerom = Zeros{T}(d, d)
            @test  isone(zerom) == isempty(zerom) == isone(Array(zerom))
            @test  iszero(zerom) == iszero(Array(zerom))

            if d > 0
                @test iszero(Zeros{T}(d, 2d))
            end

            fillm0 = Fill(T(0), d, d)
            @test   isone(fillm0) == isempty(fillm0) == isone(Array(fillm0))
            @test   iszero(fillm0) == iszero(Array(fillm0))

            fillm1 = Fill(T(1), d, d)
            @test isone(fillm1) == isone(Array(fillm1))
            @test iszero(fillm1) == isempty(fillm1) == iszero(Array(fillm1))

            fillm2 = Fill(T(2), d, d)
            @test isone(fillm2) == isempty(fillm2) == isone(Array(fillm2))
            @test iszero(fillm2) == isempty(fillm2) == iszero(Array(fillm2))
        end
        for d in (2, 3)
            for m in (Eye{T}(d), Eye{T}(d, d), Eye{T}(d, d + 2), Eye{T}(d + 2, d))
                @test any(iszero, m)
                @test ! all(iszero, m)
                @test any(isone, m)
                @test ! all(isone, m)
            end

            m1 = Ones{T}(d, d)
            @test ! isone(m1)
            @test ! iszero(m1)
            @test all(isone, m1)
            @test ! all(iszero, m1)

            m2 = Zeros{T}(d, d)
            @test ! isone(m2)
            @test iszero(m2)
            @test ! all(isone, m2)
            @test  all(iszero, m2)

            m3 = Fill(T(2), d, d)
            @test ! isone(m3)
            @test ! iszero(m3)
            @test ! all(isone, m3)
            @test ! all(iszero, m3)
            @test ! any(iszero, m3)

            m4 = Fill(T(1), d, d)
            @test ! isone(m4)
            @test ! iszero(m4)
        end
    end

    @test iszero(Zeros{SMatrix{2,2,Int,4}}(2))
    @test iszero(Fill(SMatrix{2,2}(0,0,0,0), 2))
    @test iszero(Fill(SMatrix{2,2}(0,0,0,1), 0))

    # compile-time evaluation
    @test @inferred((Z -> Val(iszero(Z)))(Zeros(3,3))) == Val(true)

    @testset "all/any" begin
        @test any(Ones{Bool}(10)) === all(Ones{Bool}(10)) === any(Fill(true,10)) === all(Fill(true,10)) === true
        @test any(Zeros{Bool}(10)) === all(Zeros{Bool}(10)) === any(Fill(false,10)) === all(Fill(false,10)) === false
        @test all(b -> ndims(b) ==  1, Fill([1,2],10))
        @test any(b -> ndims(b) ==  1, Fill([1,2],10))

        @test all(Fill(2,0))
        @test !any(Fill(2,0))
        @test any(Trues(2,0)) == any(trues(2,0))
        @test_throws TypeError all(Fill(2,2))
        @test all(iszero, Fill(missing,0)) === all(iszero, fill(missing,0)) === true
        @test all(iszero, Fill(missing,2)) === all(iszero, fill(missing,2)) === missing
        @test any(iszero, Fill(missing,0)) === any(iszero, fill(missing,0)) === false
        @test any(iszero, Fill(missing,2)) === any(iszero, fill(missing,2)) === missing
    end

    @testset "Error" begin
        @test_throws TypeError any(exp, Fill(1,5))
        @test_throws TypeError all(exp, Fill(1,5))
        @test_throws TypeError any(exp, Eye(5))
        @test_throws TypeError all(exp, Eye(5))
        @test_throws TypeError any(Fill(1,5))
        @test_throws TypeError all(Fill(1,5))
        @test_throws TypeError any(Zeros(5))
        @test_throws TypeError all(Zeros(5))
        @test_throws TypeError any(Ones(5))
        @test_throws TypeError all(Ones(5))
        @test_throws TypeError any(Eye(5))
        @test_throws TypeError all(Eye(5))
    end
end

@testset "Eye identity ops" begin
    m = Eye(10)
    D = Diagonal(Fill(2,10))

    for op in (permutedims, inv)
        @test op(m) === m
    end
    @test permutedims(D) ≡ D
    @test inv(D) ≡ Diagonal(Fill(1/2,10))

    for m in (Eye(10), Eye(10, 10), Eye(10, 8), Eye(8, 10), D)
        for op in (tril, triu, tril!, triu!)
            @test op(m) === m
        end
    end

    @test copy(m) ≡ m
    @test copy(D) ≡ D
end

@testset "Eye broadcast" begin
    E = Eye(2,3)
    M = Matrix(E)
    F = E .+ E
    @test F isa FillArrays.RectDiagonal
    @test F == M + M

    F = E .+ 1
    @test F == M .+ 1

    E = Eye((SOneTo(2), SOneTo(2)))
    @test axes(E .+ E) === axes(E)
end

@testset "Issues" begin
    @testset "#31" begin
        @test convert(SparseMatrixCSC{Float64,Int64}, Zeros{Float64}(3, 3)) == spzeros(3, 3)
        @test sparse(Zeros(4, 2)) == spzeros(4, 2)
    end
    @testset "#178" begin
        @test Zeros(10)'*spzeros(10) == 0
    end
end

@testset "Adjoint/Transpose/permutedims" begin
    @test Ones{ComplexF64}(5,6)' ≡ transpose(Ones{ComplexF64}(5,6)) ≡ Ones{ComplexF64}(6,5)
    @test Zeros{ComplexF64}(5,6)' ≡ transpose(Zeros{ComplexF64}(5,6)) ≡ Zeros{ComplexF64}(6,5)
    @test Fill(1+im, 5, 6)' ≡ Fill(1-im, 6,5)
    @test transpose(Fill(1+im, 5, 6)) ≡ Fill(1+im, 6,5)
    @test Ones(5)' isa Adjoint # Vectors still need special dot product
    @test copy(Ones(5)') ≡ Ones(5)'
    @test copy(transpose(Ones(5))) ≡ transpose(Ones(5))
    @test Fill([1+im 2; 3 4; 5 6], 2,3)' == Fill([1+im 2; 3 4; 5 6]', 3,2)
    @test transpose(Fill([1+im 2; 3 4; 5 6], 2,3)) == Fill(transpose([1+im 2; 3 4; 5 6]), 3,2)

    @test permutedims(Ones(10)) ≡ Ones(1,10)
    @test permutedims(Zeros(10)) ≡ Zeros(1,10)
    @test permutedims(Fill(2.0,10)) ≡ Fill(2.0,1,10)
    @test permutedims(Ones(10,3)) ≡ Ones(3,10)
    @test permutedims(Zeros(10,3)) ≡ Zeros(3,10)
    @test permutedims(Fill(2.0,10,3)) ≡ Fill(2.0,3,10)

    @test permutedims(Ones(2,4,5), [3,2,1]) == permutedims(Array(Ones(2,4,5)), [3,2,1])
    @test permutedims(Ones(2,4,5), [3,2,1]) ≡ Ones(5,4,2)
    @test permutedims(Zeros(2,4,5), [3,2,1]) ≡ Zeros(5,4,2)
    @test permutedims(Fill(2.0,2,4,5), [3,2,1]) ≡ Fill(2.0,5,4,2)

    @testset "recursive" begin
        S = SMatrix{2,3}(1:6)
        Z = Zeros(typeof(S), 2, 3)
        Y = zeros(typeof(S), 2, 3)
        @test Z' == Y'
        @test transpose(Z) == transpose(Y)

        F = Fill(S, 2, 3)
        G = fill(S, 2, 3)
        @test F' == G'
        @test transpose(F) == transpose(G)
    end
end

@testset "reverse" begin
    for A in (Zeros{Int}(6), Ones(2,3), Fill("abc", 2, 3, 4))
        @test reverse(A) == reverse(Array(A))
        @test reverse(A, dims=1) == reverse(Array(A), dims=1)
    end
    A = Ones{Int}(6)
    @test reverse(A, 2, 4) == reverse(Array(A), 2, 4)
    @test_throws BoundsError reverse(A, 1, 10)
end

@testset "setindex!/fill!" begin
    F = Fill(1,10)
    @test (F[1] = 1) == 1
    @test_throws BoundsError (F[11] = 1)
    @test_throws ArgumentError (F[10] = 2)


    F = Fill(1,10,5)
    @test (F[1] = 1) == 1
    @test (F[3,3] = 1) == 1
    @test_throws BoundsError (F[51] = 1)
    @test_throws BoundsError (F[1,6] = 1)
    @test_throws ArgumentError (F[10] = 2)
    @test_throws ArgumentError (F[10,1] = 2)

    @test (F[:,1] .= 1) == fill(1,10)
    @test_throws ArgumentError (F[:,1] .= 2)

    @test fill!(F,1) == F
    @test_throws ArgumentError fill!(F,2)
end

@testset "mult" begin
    @test Fill(2,10)*Fill(3,1,12) == Vector(Fill(2,10))*Matrix(Fill(3,1,12))
    @test Fill(2,10)*Fill(3,1,12) ≡ Fill(6,10,12)
    @test Fill(2,3,10)*Fill(3,10,12) ≡ Fill(60,3,12)
    @test Fill(2,3,10)*Fill(3,10) ≡ Fill(60,3)
    @test_throws DimensionMismatch Fill(2,10)*Fill(3,2,12)
    @test_throws DimensionMismatch Fill(2,3,10)*Fill(3,2,12)

    f = Fill(1, (Base.IdentityUnitRange(1:3), Base.IdentityUnitRange(1:3)))
    @test f * f === Fill(size(f,2), axes(f))

    f = Fill(2, (Base.IdentityUnitRange(2:3), Base.IdentityUnitRange(2:3)))
    @test_throws ArgumentError f * f

    @test Ones(10)*Fill(3,1,12) ≡ Fill(3.0,10,12)
    @test Ones(10,3)*Fill(3,3,12) ≡ Fill(9.0,10,12)
    @test Ones(10,3)*Fill(3,3) ≡ Fill(9.0,10)

    @test Fill(2,10)*Ones(1,12) ≡ Fill(2.0,10,12)
    @test Fill(2,3,10)*Ones(10,12) ≡ Fill(20.0,3,12)
    @test Fill(2,3,10)*Ones(10) ≡ Fill(20.0,3)

    @test Ones(10)*Ones(1,12) ≡ Ones(10,12)
    @test Ones(3,10)*Ones(10,12) ≡ Fill(10.0,3,12)
    @test Ones(3,10)*Ones(10) ≡ Fill(10.0,3)

    @test Zeros(10)*Fill(3,1,12) ≡   Zeros(10,12)
    @test Zeros(10,3)*Fill(3,3,12) ≡ Zeros(10,12)
    @test Zeros(10,3)*Fill(3,3) ≡    Zeros(10)

    @test Fill(2,10)*  Zeros(1,12) ≡  Zeros(10,12)
    @test Fill(2,3,10)*Zeros(10,12) ≡ Zeros(3,12)
    @test Fill(2,3,10)*Zeros(10) ≡    Zeros(3)

    @test Zeros(10)*Zeros(1,12) ≡ Zeros(10,12)
    @test Zeros(3,10)*Zeros(10,12) ≡ Zeros(3,12)
    @test Zeros(3,10)*Zeros(10) ≡ Zeros(3)

    f = Zeros((Base.IdentityUnitRange(1:4), Base.IdentityUnitRange(1:4)))
    @test f * f === f

    f = Zeros((Base.IdentityUnitRange(3:4), Base.IdentityUnitRange(3:4)))
    @test_throws ArgumentError f * f

    @testset "Arrays as elements" begin
        SMT = SMatrix{2,2,Int,4}
        SVT = SVector{2,Int}
        @test @inferred(Zeros{SMT}(0,0) * Fill([1 2; 3 4], 0, 0)) == Zeros{SMT}(0,0)
        @test @inferred(Zeros{SMT}(4,2) * Fill([1 2; 3 4], 2, 3)) == Zeros{SMT}(4,3)
        @test @inferred(Fill([1 2; 3 4], 2, 3) * Zeros{SMT}(3, 4)) == Zeros{SMT}(2,4)
        @test @inferred(Zeros{SMT}(4,2) * Fill([1, 2], 2, 3)) == Zeros{SVT}(4,3)
        @test @inferred(Fill([1 2], 2, 3) * Zeros{SMT}(3,4)) == Zeros{SMatrix{1,2,Int,2}}(2,4)

        TSM = SMatrix{2,2,Int,4}
        s = TSM(1:4)
        for n in 0:3
            v = fill(s, 1)
            z = zeros(TSM, n)
            A = @inferred Zeros{TSM}(n) * Diagonal(v)
            B = z * Diagonal(v)
            @test A == B

            w = fill(s, n)
            A = @inferred Diagonal(w) * Zeros{TSM}(n)
            B = Diagonal(w) * z
            @test A == B

            A = @inferred Zeros{TSM}(2n, n) * Diagonal(w)
            B = zeros(TSM, 2n, n) * Diagonal(w)
            @test A == B

            A = @inferred Diagonal(w) * Zeros{TSM}(n, 2n)
            B = Diagonal(w) * zeros(TSM, n, 2n)
            @test A == B
        end

        D = Diagonal([[1 2; 3 4], [1 2 3; 4 5 6]])
        @test @inferred(Zeros(TSM, 2,2) * D) == zeros(TSM, 2,2) * D

        D = Diagonal(fill(SMatrix{2,3}(fill(im,6)),1))
        Z = Zeros(SMatrix{2,3,ComplexF64,6},1)
        @test D * Z' == fill(zero(SMatrix{2,2,ComplexF64,4}),1,1)

        D = Diagonal(fill(zeros(2,3), 2))
        Z = Zeros(SMatrix{2,3,Float64,6}, 2)
        @test Z' * D == Array(Z)' * D

        S = SMatrix{2,3}(1:6)
        A = reshape([S,2S,3S,4S],2,2)
        F = Fill(S',2,2)
        @test A * F == A * fill(S',size(F))
        @test mul!(A * F, A, F, 2, 1) == 3 * A * fill(S',size(F))
        @test F * A == fill(S',size(F)) * A
        @test mul!(F * A, F, A, 2, 1) == 3 * fill(S',size(F)) * A

        # doubly nested
        A = [[[1,2]]]'
        Z = Zeros(SMatrix{1,1,SMatrix{2,2,Int,4},1},1)
        Z2 = zeros(SMatrix{1,1,SMatrix{2,2,Int,4},1},1)
        @test A * Z == A * Z2

        x = [1 2 3; 4 5 6]
        A = reshape([x,2x,3x,4x],2,2)
        F = Fill(x,2,2)
        @test A' * F == A' * fill(x,size(F))
        @test mul!(A' * F, A', F, 2, 1) == 3 * A' * fill(x,size(F))
    end

    for W in (zeros(3,4), @MMatrix zeros(3,4))
        mW, nW = size(W)
        @test mul!(W, Fill(2,mW,5), Fill(3,5,nW)) ≈ Fill(30,mW,nW) ≈ fill(2,mW,5) * fill(3,5,nW)
        W .= 2
        @test mul!(W, Fill(2,mW,5), Fill(3,5,nW), 1.0, 2.0) ≈ Fill(30,mW,nW) .+ 4 ≈ fill(2,mW,5) * fill(3,5,nW) .+ 4
    end

    for w in (zeros(5), @MVector zeros(5))
        mw = size(w,1)
        @test mul!(w, Fill(2,mw,5), Fill(3,5)) ≈ Fill(30,mw) ≈ fill(2,mw,5) * fill(3,5)
        w .= 2
        @test mul!(w, Fill(2,mw,5), Fill(3,5), 1.0, 2.0) ≈ Fill(30,mw) .+ 4 ≈ fill(2,mw,5) * fill(3,5) .+ 4
    end

    @testset "strided" begin
        @testset for (la, (mA, nA)) in [(3, (1,4)), (0, (1,4)), (3, (1, 0))]

            a = randn(la)
            na = 1
            A = randn(mA,nA)

            @test Fill(2,3)*A ≈ Vector(Fill(2,3))*A
            @test Fill(2,0)*A ≈ Vector(Fill(2,0))*A
            @test Fill(2,3,mA)*A ≈ mul!(similar(A, 3,nA), Fill(2,3,mA), A) ≈ Matrix(Fill(2,3,mA))*A
            @test Fill(2,3,la)*a ≈ mul!(similar(a, 3), Fill(2,3,la), a) ≈ Matrix(Fill(2,3,la))*a
            @test Fill(2,3,la)*a isa Fill
            @test Ones(3)*A ≈ Vector(Ones(3))*A
            @test Ones(3,mA)*A ≈ mul!(similar(A, 3, nA), Ones(3,mA), A) ≈ Matrix(Ones(3,mA))*A
            @test Ones(3,la)*a ≈ mul!(similar(a, 3), Ones(3,la), a) ≈ Matrix(Ones(3,la))*a
            @test Ones(3,la)*a isa Fill
            @test Zeros(3)*A  ≡ Zeros(3,nA)
            @test Zeros(3,mA)*A == mul!(similar(A, 3, nA), Zeros(3,mA), A) == Zeros(3,nA)
            @test Zeros(3,la)*a == mul!(similar(A, 3), Zeros(3,la), a) == Zeros(3)

            @test A*Fill(2,nA) ≈ A*Vector(Fill(2,nA))
            @test A*Fill(2,nA,1) ≈ A*Matrix(Fill(2,nA,1))
            @test a*Fill(2,na,3) ≈ a*Matrix(Fill(2,na,3))
            @test A*Fill(2,nA,0) ≈ A*Matrix(Fill(2,nA,0))
            @test a*Fill(2,na,0) ≈ a*Matrix(Fill(2,na,0))
            @test A*Ones(nA) ≈ A*Vector(Ones(nA))
            @test A*Ones(nA,1) ≈ A*Matrix(Ones(nA,1))
            @test a*Ones(na,3) ≈ a*Matrix(Ones(na,3))
            @test A*Zeros(nA)  ≡ Zeros(mA)
            @test A*Zeros(nA,1) ≡ Zeros(mA,1)
            @test a*Zeros(na,3) ≡ Zeros(la,3)

            @test transpose(A) * Zeros(mA) ≡ Zeros(nA)
            @test A' * Zeros(mA) ≡ Zeros(nA)

            @test transpose(a) * Zeros(la, 3) ≡ transpose(Zeros(3))
            @test a' * Zeros(la,3) ≡ adjoint(Zeros(3))

            @test Zeros(la)' * Transpose(Adjoint(a)) == 0.0

            F = Fill(2, mA, 3)
            @test transpose(A) * F ≈ transpose(Fill(2, 3, mA) * A)
            F = Fill(2, la, 3)
            FS = Fill(2, (Base.OneTo(la), SOneTo(3)))
            @testset for (adjf, adjT) in ((transpose, Transpose), (adjoint, Adjoint))
                @test adjf(a) * F ≈ adjf(Fill(2, 3, la) * a)
                @test adjf(a) * F isa adjT{<:Any, <:Fill{<:Any,1}}
                @test adjf(a) * FS ≈ adjf(Fill(2, 3, la) * a)
                @test axes(adjf(a) * FS, 2) == SOneTo(3)
            end

            w = zeros(mA)
            @test mul!(w, A, Fill(2,nA), true, false) ≈ A * fill(2,nA)
            w .= 2
            @test mul!(w, A, Fill(2,nA), 1.0, 1.0) ≈ A * fill(2,nA) .+ 2

            nW = 3
            W = zeros(mA, nW)
            @test mul!(W, A, Fill(2,nA,nW), true, false) ≈ A * fill(2,nA,nW)
            W .= 2
            @test mul!(W, A, Fill(2,nA,nW), 1.0, 1.0) ≈ A * fill(2,nA,nW) .+ 2

            mW = 5
            W = zeros(mW, nA)
            @test mul!(W, Fill(2,mW,mA), A, true, false) ≈ fill(2,mW,mA) * A
            W .= 2
            @test mul!(W, Fill(2,mW,mA), A, 1.0, 1.0) ≈ fill(2,mW,mA) * A .+ 2

            mw = 5
            w = zeros(mw)
            @test mul!(w, Fill(2,mw,la), a, true, false) ≈ fill(2,mw,la) * a
            w .= 2
            @test mul!(w, Fill(2,mw,la), a, 1.0, 1.0) ≈ fill(2,mw,la) * a .+ 2

            @testset for f in [adjoint, transpose]
                w = zeros(nA)
                @test mul!(w, f(A), Fill(2,mA), true, false) ≈ f(A) * fill(2,mA)
                w .= 2
                @test mul!(w, f(A), Fill(2,mA), 1.0, 1.0) ≈ f(A) * fill(2,mA) .+ 2

                W = zeros(nA, nW)
                @test mul!(W, f(A), Fill(2,mA,nW), true, false) ≈ f(A) * fill(2,mA,nW)
                W .= 2
                @test mul!(W, f(A), Fill(2,mA,nW), 1.0, 1.0) ≈ f(A) * fill(2,mA,nW) .+ 2

                W = zeros(mW, mA)
                @test mul!(W, Fill(2,mW,nA), f(A), true, false) ≈ fill(2,mW,nA) * f(A)
                W .= 2
                @test mul!(W, Fill(2,mW,nA), f(A), 1.0, 1.0) ≈ fill(2,mW,nA) * f(A) .+ 2
            end
        end
    end

    D = Diagonal(randn(1))
    @test Zeros(1,1)*D ≡ Zeros(1,1)
    @test Zeros(1)*D ≡ Zeros(1,1)
    @test D*Zeros(1,1) ≡ Zeros(1,1)
    @test D*Zeros(1) ≡ Zeros(1)

    D = Diagonal(Fill(2,10))
    @test D * Ones(10) ≡ Fill(2.0,10)
    @test D * Ones(10,5) ≡ Fill(2.0,10,5)
    @test Ones(5,10) * D ≡ Fill(2.0,5,10)

    # following test is broken in Base as of Julia v1.5
    @test_throws DimensionMismatch Diagonal(Fill(1,1)) * Ones(10)
    @test_throws DimensionMismatch Diagonal(Fill(1,1)) * Ones(10,5)
    @test_throws DimensionMismatch Ones(5,10) * Diagonal(Fill(1,1))

    E = Eye(5)
    @test E*(1:5) ≡ 1.0:5.0
    @test (1:5)'E == (1.0:5)'
    @test E*E ≡ E

    n  = 10
    k  = 12
    m  = 15
    for T in (Float64, ComplexF64)
        Ank  = rand(T, n, k)
        Akn = rand(T, k, n)
        Ak = rand(T, k)
        onesm = ones(m)
        zerosm = zeros(m)

        fv = T == Float64 ? T(1.6) : T(1.6, 1.3)

        for (fillvec, fillmat) in ((Fill(fv, k), Fill(fv, k, m)),
                                    (Ones(T, k), Ones(T, k, m)),
                                    (Zeros(T, k), Zeros(T, k, m)))

            Afillvec = Array(fillvec)
            Afillmat = Array(fillmat)
            @test Ank * fillvec ≈ Ank * Afillvec
            @test Ank * fillmat ≈ Ank * Afillmat

            for A  in (Akn, Ak)
                @test transpose(A)*fillvec ≈ transpose(A)*Afillvec
                AtF = transpose(A)*fillmat
                AtM = transpose(A)*Afillmat
                @test AtF ≈ AtM
                @test AtF * Ones(m) ≈ AtM * onesm
                @test AtF * Zeros(m) ≈ AtM * zerosm
                @test adjoint(A)*fillvec ≈ adjoint(A)*Afillvec
                AadjF = adjoint(A)*fillmat
                AadjM = adjoint(A)*Afillmat
                @test AadjF ≈ AadjM
                @test AadjF * Ones(m) ≈ AadjM * onesm
                @test AadjF * Zeros(m) ≈ AadjM * zerosm
            end
        end

        # inplace C = F * A' * alpha + C * beta
        F = Fill(fv, m, k)
        M = Array(F)
        C = rand(T, m, n)
        @testset for f in (adjoint, transpose)
            @test mul!(copy(C), F, f(Ank)) ≈ mul!(copy(C), M, f(Ank))
            @test mul!(copy(C), F, f(Ank), 1.0, 2.0) ≈ mul!(copy(C), M, f(Ank), 1.0, 2.0)
        end
    end

    @testset "non-commutative" begin
        A = Fill(quat(rand(4)...), 2, 2)
        M = Array(A)
        α, β = quat(0,1,1,0), quat(1,0,0,1)
        @testset "matvec" begin
            f = Fill(quat(rand(4)...), size(A,2))
            v = Array(f)
            D = copy(v)
            exp_res = M * v * α + D * β
            @test mul!(copy(D), A, f, α, β) ≈ mul!(copy(D), M, v, α, β) ≈ exp_res
            @test mul!(copy(D), M, f, α, β) ≈ mul!(copy(D), M, v, α, β) ≈ exp_res
            @test mul!(copy(D), A, v, α, β) ≈ mul!(copy(D), M, v, α, β) ≈ exp_res

            @test mul!(copy(D), M', f, α, β) ≈ mul!(copy(D), M', v, α, β) ≈ M' * v * α + D * β
            @test mul!(copy(D), transpose(M), f, α, β) ≈ mul!(copy(D), transpose(M), v, α, β) ≈ transpose(M) * v * α + D * β
        end

        @testset "matmat" begin
            B = Fill(quat(rand(4)...), 2, 2)
            N = Array(B)
            D = copy(N)
            exp_res = M * N * α + D * β
            @test mul!(copy(D), A, B, α, β) ≈ mul!(copy(D), M, N, α, β) ≈ exp_res
            @test mul!(copy(D), M, B, α, β) ≈ mul!(copy(D), M, N, α, β) ≈ exp_res
            @test mul!(copy(D), A, N, α, β) ≈ mul!(copy(D), M, N, α, β) ≈ exp_res

            @test mul!(copy(D), M', B, α, β) ≈ mul!(copy(D), M', N, α, β) ≈ M' * N * α + D * β
            @test mul!(copy(D), transpose(M), B, α, β) ≈ mul!(copy(D), transpose(M), N, α, β) ≈ transpose(M) * N * α + D * β

            @test mul!(copy(D), A, N', α, β) ≈ mul!(copy(D), M, N', α, β) ≈ M * N' * α + D * β
            @test mul!(copy(D), A, transpose(N), α, β) ≈ mul!(copy(D), M, transpose(N), α, β) ≈ M * transpose(N) * α + D * β
        end
    end

    @testset "ambiguities" begin
        UT33 = UpperTriangular(ones(3,3))
        UT11 = UpperTriangular(ones(1,1))
        @test transpose(Zeros(3)) * Transpose(Adjoint([1,2,3])) == 0
        @test Zeros(3)' * Adjoint(Transpose([1,2,3])) == 0
        @test Zeros(3)' * UT33 == Zeros(3)'
        @test transpose(Zeros(3)) * UT33 == transpose(Zeros(3))
        @test UT11 * Zeros(3)' == Zeros(1,3)
        @test UT11 *
Download .txt
gitextract_tzhpayfy/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── CompatHelper.yml
│       ├── Invalidations.yml
│       ├── TagBot.yml
│       ├── ci.yml
│       ├── docs.yml
│       └── downstream.yml
├── .gitignore
├── LICENSE
├── Project.toml
├── README.md
├── appveyor.yml
├── benchmark/
│   └── benchmarks.jl
├── docs/
│   ├── Project.toml
│   ├── make.jl
│   └── src/
│       └── index.md
├── ext/
│   ├── FillArraysPDMatsExt.jl
│   ├── FillArraysSparseArraysExt.jl
│   ├── FillArraysStaticArraysExt.jl
│   └── FillArraysStatisticsExt.jl
├── src/
│   ├── FillArrays.jl
│   ├── fillalgebra.jl
│   ├── fillbroadcast.jl
│   ├── oneelement.jl
│   └── trues.jl
└── test/
    ├── aqua.jl
    ├── infinitearrays.jl
    └── runtests.jl
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (256K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 255,
    "preview": "# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\nversion: 2\nupda"
  },
  {
    "path": ".github/workflows/CompatHelper.yml",
    "chars": 1381,
    "preview": "name: CompatHelper\non:\n  schedule:\n    - cron: 0 0 * * *\n  workflow_dispatch:\npermissions:\n  contents: write\n  pull-requ"
  },
  {
    "path": ".github/workflows/Invalidations.yml",
    "chars": 1566,
    "preview": "name: Invalidations\n\non:\n  pull_request:\n    paths-ignore:\n      - 'LICENSE'\n      - 'README.md'\n      - '.github/workfl"
  },
  {
    "path": ".github/workflows/TagBot.yml",
    "chars": 440,
    "preview": "name: TagBot\non:\n  issue_comment:\n    types:\n      - created\n  workflow_dispatch:\n    inputs:\n      lookback:\n        de"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1589,
    "preview": "name: CI\non:\n  push:\n    branches:\n      - master\n    tags: [v*]\n    paths-ignore:\n      - 'LICENSE'\n      - 'README.md'"
  },
  {
    "path": ".github/workflows/docs.yml",
    "chars": 1405,
    "preview": "name: Documentation\n\non:\n  pull_request:\n    paths-ignore:\n      - 'LICENSE'\n      - 'README.md'\n      - '.github/workfl"
  },
  {
    "path": ".github/workflows/downstream.yml",
    "chars": 3206,
    "preview": "name: IntegrationTest\non:\n  push:\n    branches: [master]\n    tags: [v*]\n    paths-ignore:\n      - 'LICENSE'\n      - 'REA"
  },
  {
    "path": ".gitignore",
    "chars": 96,
    "preview": "*.jl.cov\n*.jl.*.cov\n*.jl.mem\ndeps/deps.jl\n.DS_Store\nManifest.toml\nManifest-v*.*.toml\ndocs/build\n"
  },
  {
    "path": "LICENSE",
    "chars": 1070,
    "preview": "MIT License\n\nCopyright (c) 2017 Sheehan Olver\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
  },
  {
    "path": "Project.toml",
    "chars": 1495,
    "preview": "name = \"FillArrays\"\nuuid = \"1a297f60-69ca-5386-bcde-b61e274b549b\"\nversion = \"1.16.0\"\n\n[deps]\nLinearAlgebra = \"37e2e46d-f"
  },
  {
    "path": "README.md",
    "chars": 4682,
    "preview": "# FillArrays.jl\n\n[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://JuliaArrays.github.io/FillArrays"
  },
  {
    "path": "appveyor.yml",
    "chars": 1067,
    "preview": "environment:\n  matrix:\n  - julia_version: 1\n  - julia_version: 1.5\n  - julia_version: nightly\n\nplatform:\n  - x86 # 32-bi"
  },
  {
    "path": "benchmark/benchmarks.jl",
    "chars": 2413,
    "preview": "using BenchmarkTools\nusing FillArrays\nusing LinearAlgebra: triu, tril\n\n# Subtract the overhead from benchmark times\nBenc"
  },
  {
    "path": "docs/Project.toml",
    "chars": 345,
    "preview": "[deps]\nDocumenter = \"e30172f5-a6a5-5a46-863b-614d45cd2de4\"\nFillArrays = \"1a297f60-69ca-5386-bcde-b61e274b549b\"\nRandom = "
  },
  {
    "path": "docs/make.jl",
    "chars": 412,
    "preview": "using Documenter\nusing FillArrays\n\n# Setup for doctests in docstrings\nDocMeta.setdocmeta!(FillArrays, :DocTestSetup, :(u"
  },
  {
    "path": "docs/src/index.md",
    "chars": 5492,
    "preview": "```@meta\nDocTestSetup  = quote\n    using FillArrays\nend\n```\n\n# Introduction\n\n`FillArrays` allows one to lazily represent"
  },
  {
    "path": "ext/FillArraysPDMatsExt.jl",
    "chars": 648,
    "preview": "module FillArraysPDMatsExt\n\nimport FillArrays\nimport FillArrays.LinearAlgebra\nimport PDMats\nusing FillArrays: mult_zeros"
  },
  {
    "path": "ext/FillArraysSparseArraysExt.jl",
    "chars": 3011,
    "preview": "module FillArraysSparseArraysExt\n\nusing SparseArrays\nusing SparseArrays: SparseVectorUnion\nimport Base: convert, kron\nus"
  },
  {
    "path": "ext/FillArraysStaticArraysExt.jl",
    "chars": 736,
    "preview": "module FillArraysStaticArraysExt\n\nusing FillArrays\nusing StaticArrays\n\nimport Base: promote_op\nimport FillArrays: elconv"
  },
  {
    "path": "ext/FillArraysStatisticsExt.jl",
    "chars": 1179,
    "preview": "module FillArraysStatisticsExt\n\nimport Statistics: mean, var, cov, cor\nusing LinearAlgebra: diagind\n\nusing FillArrays\nus"
  },
  {
    "path": "src/FillArrays.jl",
    "chars": 33088,
    "preview": "\"\"\" `FillArrays` module to lazily represent matrices with a single value \"\"\"\nmodule FillArrays\n\nusing LinearAlgebra\nimpo"
  },
  {
    "path": "src/fillalgebra.jl",
    "chars": 22919,
    "preview": "## vec\n\nvec(a::AbstractFill) = fillsimilar(a, length(a))\n\n## Transpose/Adjoint\n# cannot do this for vectors since that w"
  },
  {
    "path": "src/fillbroadcast.jl",
    "chars": 14299,
    "preview": "### map\n\nmap(f::Function, r::AbstractFill) = Fill(f(getindex_value(r)), axes(r))\n\nfunction map(f::Function, v::AbstractF"
  },
  {
    "path": "src/oneelement.jl",
    "chars": 16316,
    "preview": "\"\"\"\n    OneElement(val, ind, axesorsize) <: AbstractArray\n\nRepresents an array with the specified axes (if its a tuple o"
  },
  {
    "path": "src/trues.jl",
    "chars": 714,
    "preview": "\n\"\"\"\n    Trues = Ones{Bool, N, Axes} where {N, Axes}\n\nLazy version of `trues` with axes.\nTypically created using `Trues("
  },
  {
    "path": "test/aqua.jl",
    "chars": 336,
    "preview": "using Aqua\nusing FillArrays\nusing Test\ndownstream_test = \"--downstream_integration_test\" in ARGS\n@testset \"Project quali"
  },
  {
    "path": "test/infinitearrays.jl",
    "chars": 1572,
    "preview": "# Infinite Arrays implementation from\n# https://github.com/JuliaLang/julia/blob/master/test/testhelpers/InfiniteArrays.j"
  },
  {
    "path": "test/runtests.jl",
    "chars": 123946,
    "preview": "using FillArrays, LinearAlgebra, PDMats, SparseArrays, StaticArrays, ReverseDiff, Random, Test, Statistics, Quaternions\n"
  }
]

About this extraction

This page contains the full source code of the JuliaArrays/FillArrays.jl GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (239.9 KB), approximately 85.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!