{"id":17113030,"url":"https://github.com/emmt/cartesianboxes.jl","last_synced_at":"2026-01-05T00:46:18.090Z","repository":{"id":71510208,"uuid":"121522522","full_name":"emmt/CartesianBoxes.jl","owner":"emmt","description":"Flexible and efficicient multi-dimensional index boxes in Julia","archived":false,"fork":false,"pushed_at":"2024-06-18T08:49:05.000Z","size":85,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-29T05:41:23.523Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/emmt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-14T14:53:03.000Z","updated_at":"2024-06-18T08:49:07.000Z","dependencies_parsed_at":"2023-07-30T18:16:23.517Z","dependency_job_id":null,"html_url":"https://github.com/emmt/CartesianBoxes.jl","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FCartesianBoxes.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FCartesianBoxes.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FCartesianBoxes.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emmt%2FCartesianBoxes.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emmt","download_url":"https://codeload.github.com/emmt/CartesianBoxes.jl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245177262,"owners_count":20573071,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T17:02:25.059Z","updated_at":"2026-01-05T00:46:18.061Z","avatar_url":"https://github.com/emmt.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flexible and efficient multi-dimensional index boxes in Julia\n\n[![License](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](LICENSE.md)\n[![Build Status](https://github.com/emmt/CartesianBoxes.jl/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/emmt/CartesianBoxes.jl/actions/workflows/CI.yml?query=branch%3Amaster)\n[![Build Status](https://ci.appveyor.com/api/projects/status/github/emmt/CartesianBoxes.jl?branch=master)](https://ci.appveyor.com/project/emmt/CartesianBoxes-jl/branch/master)\n[![codecov.io](http://codecov.io/github/emmt/CartesianBoxes.jl/coverage.svg?branch=master)](http://codecov.io/github/emmt/CartesianBoxes.jl?branch=master)\n\nThis module implements `CartesianBox{N}` to define rectangular regions of\n`N`-dimensional indices in Julia arrays. Cartesian boxes are similar to\n`CartesianIndices` but, being a different type, they can be used to\nspecifically extend methods without introducing unexpected behaviors in other\nJulia modules.\n\nFor multi-dimensional loops, instances of `CartesianBox{N}` are as fast as\n`CartesianIndices{N}`. They can thus be used as a *fast* and *portable*\nreplacement (see [*Rationale*](#rationale) below) of these different\nrepresentations of rectangular multi-dimensional regions.\n\n\n## Usage\n\n### Construction\n\nA Cartesian box is created by calling the constructor `CartesianBox(args...)`\nwith a variety of arguments. For example:\n\n```julia\nCartesianBox(A)\n```\n\nyields the Cartesian box which contains all indices of array `A`. An arbitrary\nregion whose first and last multi-dimensional indices are `(imin,jmin,...)` and\n`(imax,jmax,...)` can be defined by one of:\n\n```julia\nCartesianBox(CartesianIndex(imin,jmin,...), CartesianIndex(imax,jmax,...))\nCartesianBox((imin:imax, jmin:jmax, ...))\nCartesianBox((imin,jmin,...), (imax,jmax,...))\n```\n\nhence\n\n```julia\nCartesianBox(axes(A))\n```\n\nalso defines a Cartesian box containing all indices of array `A`. For normal\narrays (with 1-based indices), it is sufficient to provide the dimensions of\nthe array:\n\n```julia\nCartesianBox(size(A))\nCartesianBox((dim1, dim2, ...))\n```\n\nThis is however not recommended, `CartesianBox(axes(A))` or, for short,\n`CartesianBox(A)` are more likely to be correct for any kind of array `A`.\n\nIt is also possible to convert an instance `R` of `CartesianIndices` into a\n`CartesianBox` by calling the constructor:\n\n```julia\nB = CartesianBox(R)\n```\n\nThe reverse operation is also possible and is a lossless operation as shown by:\n\n```julia\nCartesianIndices(B) === R\n```\n\nwhich always holds.\n\nTo retrieve the `N`-tuple of ranges that constitute a Cartesian box `B`, call\n`Tuple(B)`. This is not the same as `axes(B)` which yields the ranges to index\n`B` itself.\n\n\n```julia\nB = CartesianBox(2:7, 3:5)\nTuple(B) -\u003e (2:7, 3:5)\naxes(B) -\u003e (Base.OneTo(6),Base.OneTo(3))\n```\n\n\n### Fast (and safe) iterations\n\nAn instance of `CartesianBox` can be used in a loop as follows:\n\n```julia\nfor i in CartesianBox(...)\n   A[i] = ...\nend\n```\n\nwhere `i` will be set to a `CartesianIndex` with all the multi-dimensional\nindices of the rectangular region defined by `B`. To benefit from faster loops\nyou may suppress bound checking and activate\n[SIMD](https://fr.wikipedia.org/wiki/Single_instruction_multiple_data)\nvectorization:\n\n```julia\n@inbounds @simd for i in CartesianBox(...)\n   A[i] = ...\nend\n```\n\nWhen at least one of `A` or `B` is a Cartesian box, the expression `A ∩ B`, or\nequivalently `intersect(A,B)`, yields the Cartesian box containing all indices\nin `A` and `B`. This may be used to write safe loops like:\n\n```julia\nA = ...               # some array\nB = CartesianBox(...) # some region of interest\n@inbounds for i in B ∩ A\n    A[i] = ...\nend\n```\n\nto operate on the indices of the Cartesian box `B` that are valid for `A`.\n\nWhen at least one of `A` or `B` is a Cartesian box, the expression `A ⊆ B`, or\nequivalently `intersect(A,B)`, yields whether all Cartesian indices defined by\n`A` are also indices of `B`. This may be used as:\n\n```julia\nA = ...               # some array\nB = CartesianBox(...) # some region of interest\nif B ⊆ A\n    @inbounds for i in B\n        A[i] = ...\n    end\nend\n```\n\nto only access the indices of the Cartesian box `B` if they are all valid for\n`A`.\n\n\n### Indexation and views\n\nYou may extract the region defined by a Cartesian box `B` from an array `A` by\ncalling:\n\n```julia\nC = A[B]\n```\n\nSetting values is also possible with\n\n```julia\nA[B] = C\n```\n\nwhere `C` is an array of same dimensions as the region defined by `B`. To fill\nthe region `B` of array `A` with a scalar `x`, just do:\n\n```julia\nfill!(A, B, x) -\u003e A\n```\n\nA *view* can be created by:\n\n```julia\nV = view(A, B)\n```\n\nwhich yields a sub-array `V` sharing its elements with `A` in the region\ndefined by `B`.\n\n### Arithmetic operations on a Cartesian box\n\nIf `B` is an instance of `CartesianBox{N}`, `-B` reverses the limits of `B`.\n\nExpressions like `B + I` or `B - I` can be used to shift `B`, an instance of\n`CartesianBox{N}`, by offset `I` specified as an instance of\n`CartesianIndex{N}` or as an `N`-tuple of integers. Expression like `I - B`\nshifts and negates the Cartesian box `B`.\n\n\n### Exported or extended methods\n\nThe call:\n\n```julia\nintersect(CartesianBox, A, B)\n```\n\nyields the Cartesian box given by the intersection of the Cartesian regions\ndefined by `A` and `B`. In this context, a Cartesian region can specified by a\nCartesian box, a list of integer valued ranges, a list of dimensions, or an\ninstance of `CartesianIndices`. This method is equivalent to `intersect(A,B)`,\nor `A ∩ B` for short, when at least one of `A` or `B` is a Cartesian box.\n\nThe call:\n\n```julia\nisnonemptypartof(A, B)\n```\n\nyields whether the region defined by `A` is nonempty and a valid part of the\nregion defined by `B` or of the contents of `B` if `B` is an array. This is\nequivalent to:\n\n```julia\n!isempty(CartesianBox(A)) \u0026\u0026 (CartesianBox(A) ⊆ CartesianBox(B))\n```\n\nexcept that `A` may not be an array.\n\nThe call:\n\n```julia\nboundingbox([pred,] A [, B])\n```\n\nyields the bounding-box of values in array `A` for which the predicate function\n`pred` is true. If the predicate function `pred` is omitted, the result is the\nbounding-box of non-zero values in array `A` or of the `true` values in `A` if\nits elements are of type `Bool`. Optional argument `B` is to only consider a\nsub-region `B` of `A` (`B` can be a `CartesianBox`, a `CartesianIndices`, or a\ntuple of integer valued unit ranges).\n\n\n## Restrictions\n\n* The algorithm for finding the bounding-box of valid values is pretty simple\n  and scales as `O(length(A))`.\n\n* There is no way to define an *empty* Cartesian box when `N=0`.\n\n\n## Installation\n\nThe easiest way to install `CartesianBoxes` is via Julia registry\n[`EmmtRegistry`](https://github.com/emmt/EmmtRegistry):\n\n```julia\nusing Pkg\npkg\"registry add General\" # if you have not yet any registries\npkg\"registry add https://github.com/emmt/EmmtRegistry\"\npkg\"add CartesianBoxes\"\n```\n\n\n## Rationale\n\nFor pre-0.7 Julia versions, rectangular regions of `N`-dimensional indices were\ndefined by instances of `CartesianRange{CardinalIndex{N}}` in Julia and have a\nnumber of related methods which make coding [multi-dimensional\nalgorithms](https://julialang.org/blog/2016/02/iteration) not only *easy* but\nalso *very efficient*. More recent Julia versions (≥ 0.7) introduced a change\nin the representation of such sets of multi-dimensional indices which are now\ncalled\n[`CartesianIndices{N}`](https://github.com/JuliaLang/julia/issues/20974). There\nhave been a few changes in the API but, in general, it is sufficient to replace\n`CartesianRange{CardinalIndex{N}}` by `CartesianIndices{N}` in the code. For\nbackward compatibility, [`using\nCompat`](https://github.com/JuliaLang/Compat.jl) let you use\n`CartesianIndices{N}` with Julia ≤ 0.6. However, while the performances have\nbeen maintained or even improved with `CartesianIndices{N}` in Julia ≥ 0.7\ncompared to `CartesianRange{CardinalIndex{N}}` in Julia ≤ 0.6, this is not true\nif you are using `CartesianIndices{N}` in Julia 0.6 via the\n[Compat](https://github.com/JuliaLang/Compat.jl) package. For instance, I\nmeasured (with [BenchmarkTools](http://github.com/JuliaCI/BenchmarkTools.jl))\nslowdowns worse than a factor of 30 for simple additions of arrays. My guess is\nthat this is because [Compat](https://github.com/JuliaLang/Compat.jl) does not\nextend `simd_outer_range()`, `simd_inner_length()` nor `simd_index()` methods\nfor `CartesianIndices{N}`.\n\nAnother motivation for this module, was that I wanted to add some\nfunctionalities in a such a way that is does not interfere with how\n`CartesianIndices` or `CartesianRange` are used by others.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femmt%2Fcartesianboxes.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femmt%2Fcartesianboxes.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femmt%2Fcartesianboxes.jl/lists"}