Compare commits
No commits in common. "master" and "sparse-array-0.1.0" have entirely different histories.
master
...
sparse-arr
|
@ -1,42 +0,0 @@
|
||||||
# Clojure CircleCI 2.0 configuration file
|
|
||||||
#
|
|
||||||
# Check https://circleci.com/docs/2.0/language-clojure/ for more details
|
|
||||||
#
|
|
||||||
version: 2
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
docker:
|
|
||||||
# specify the version you desire here
|
|
||||||
- image: circleci/clojure:lein-2.7.1
|
|
||||||
|
|
||||||
# Specify service dependencies here if necessary
|
|
||||||
# CircleCI maintains a library of pre-built images
|
|
||||||
# documented at https://circleci.com/docs/2.0/circleci-images/
|
|
||||||
# - image: circleci/postgres:9.4
|
|
||||||
|
|
||||||
working_directory: ~/repo
|
|
||||||
|
|
||||||
environment:
|
|
||||||
LEIN_ROOT: "true"
|
|
||||||
# Customize the JVM maximum heap limit
|
|
||||||
JVM_OPTS: -Xmx3200m
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
|
|
||||||
# Download and cache dependencies
|
|
||||||
- restore_cache:
|
|
||||||
keys:
|
|
||||||
- v1-dependencies-{{ checksum "project.clj" }}
|
|
||||||
# fallback to using the latest cache if no exact match is found
|
|
||||||
- v1-dependencies-
|
|
||||||
|
|
||||||
- run: lein deps
|
|
||||||
|
|
||||||
- save_cache:
|
|
||||||
paths:
|
|
||||||
- ~/.m2
|
|
||||||
key: v1-dependencies-{{ checksum "project.clj" }}
|
|
||||||
|
|
||||||
# run tests!
|
|
||||||
- run: lein test
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,5 +9,3 @@ pom.xml.asc
|
||||||
/.nrepl-port
|
/.nrepl-port
|
||||||
.hgignore
|
.hgignore
|
||||||
.hg/
|
.hg/
|
||||||
.clj-kondo/
|
|
||||||
.lsp/
|
|
105
README.md
105
README.md
|
@ -1,15 +1,12 @@
|
||||||
# sparse-array
|
# sparse-array
|
||||||
|
|
||||||
A Clojure library designed to manipulate sparse *arrays* - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you're better to use a *sparse matrix* library, [for example clojure.core.matrix](https://mikera.github.io/core.matrix/).
|
A Clojure library designed to manipulate sparse *arrays* - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you're better to use a *sparse matrix* library, for example [clojure.core.matrix](https://mikera.github.io/core.matrix/).
|
||||||
|
|
||||||
Arbitrary numbers of dimensions are supported, up to limits imposed by the JVM stack.
|
Arbitrary numbers of dimensions are supported, up to limits imposed by the JVM stack.
|
||||||
|
|
||||||
Available from Clojars: [](https://clojars.org/sparse-array)
|
[](https://clojars.org/sparse-array)
|
||||||
Tests currently [](https://circleci.com/gh/simon-brooke/sparse-array)
|
|
||||||
|
|
||||||
[](https://circleci.com/gh/simon-brooke/sparse-array)
|
## Conventions:
|
||||||
|
|
||||||
## Conventions
|
|
||||||
|
|
||||||
### Sparse arrays
|
### Sparse arrays
|
||||||
|
|
||||||
|
@ -56,7 +53,7 @@ However, if `*safe-sparse-operations*` is bound to `true`, exceptions will be th
|
||||||
ExceptionInfo Expected 3 coordinates; found 1 clojure.core/ex-info (core.clj:4617)
|
ExceptionInfo Expected 3 coordinates; found 1 clojure.core/ex-info (core.clj:4617)
|
||||||
```
|
```
|
||||||
|
|
||||||
Sanity checking data is potentially expensive; for this reason `*safe-sparse-operations*` defaults to `false`, but you may wish to bind it to `true` especially while debugging.
|
Sanity checking data is potentially expensive; for this reason `*safe-sparse-operations*` defaults to `false`, but you make wish to bind it to `true` especially while debugging.
|
||||||
|
|
||||||
### Dense arrays
|
### Dense arrays
|
||||||
|
|
||||||
|
@ -163,100 +160,6 @@ e.g.
|
||||||
[nil nil nil "goodbye" nil]]
|
[nil nil nil "goodbye" nil]]
|
||||||
```
|
```
|
||||||
|
|
||||||
### extract
|
|
||||||
|
|
||||||
The whole point of working with sparse arrays is because we wish to work with
|
|
||||||
interesting subsets of arrays the entirety of which would be too large to
|
|
||||||
conveniently handle; thus perhaps the most important operation is to be able
|
|
||||||
to extract a sparse subset of an array.
|
|
||||||
|
|
||||||
`sparse-array.extract/extract ([array function])`
|
|
||||||
|
|
||||||
Return a sparse subset of this `array` - which may be either sparse or
|
|
||||||
dense - comprising all those cells for which this `function` returns a
|
|
||||||
'truthy' value.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(extract [[[1 2 3][:one :two :three]["one" "two" "three"]]
|
|
||||||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
|
||||||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
|
||||||
#(if
|
|
||||||
(number? %)
|
|
||||||
(= % 3)
|
|
||||||
(= (name %) "three")))
|
|
||||||
|
|
||||||
=> {:dimensions 3,
|
|
||||||
:coord :i0,
|
|
||||||
:content (:i1 :i2),
|
|
||||||
0
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
0 {:dimensions 1, :coord :i2, :content :data, 2 3},
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 :three},
|
|
||||||
2 {:dimensions 1, :coord :i2, :content :data, 2 "three"}},
|
|
||||||
1
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
0 {:dimensions 1, :coord :i2, :content :data, 2 "three"},
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 :three},
|
|
||||||
2 {:dimensions 1, :coord :i2, :content :data, 2 3}},
|
|
||||||
2
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 3}}}
|
|
||||||
```
|
|
||||||
|
|
||||||
### extract-from-dense
|
|
||||||
|
|
||||||
Note that the above example returns the default axis sequence `{i0, i1, i2...}`;
|
|
||||||
extracting from a sparse array will always retain the axes of the array
|
|
||||||
extracted from. Dense arrays, obviously, do not have explicit axes.
|
|
||||||
|
|
||||||
You may wish to specify a sequence of axes when extracting from a dense array.
|
|
||||||
A function is provided:
|
|
||||||
|
|
||||||
`sparse-array.extract/extract-from-dense ([array function] [array function axes])`
|
|
||||||
|
|
||||||
Return a subset of this dense `array` comprising all those cells for which
|
|
||||||
this `function` returns a 'truthy' value. Use these `axes` if provided.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(extract-from-dense
|
|
||||||
[[[1 2 3][:one :two :three]["one" "two" "three"]]
|
|
||||||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
|
||||||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
|
||||||
integer?
|
|
||||||
'(:p :q :r))
|
|
||||||
|
|
||||||
=> {:dimensions 3,
|
|
||||||
:coord :p,
|
|
||||||
:content (:q :r),
|
|
||||||
0
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
0 {:dimensions 1, :coord :r, :content :data, 0 1, 1 2, 2 3}},
|
|
||||||
1
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
0 {:dimensions 1, :coord :r, :content :data, 0 1},
|
|
||||||
1 {:dimensions 1, :coord :r, :content :data, 1 2},
|
|
||||||
2 {:dimensions 1, :coord :r, :content :data, 2 3}},
|
|
||||||
2
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
1 {:dimensions 1, :coord :r, :content :data, 0 1, 1 2, 2 3}}}
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright © 2019 Simon Brooke
|
Copyright © 2019 Simon Brooke
|
||||||
|
|
264
doc/intro.md
264
doc/intro.md
|
@ -1,263 +1,3 @@
|
||||||
# Introduction to Sparse-array
|
# Introduction to sparse-array
|
||||||
|
|
||||||
A Clojure library designed to manipulate sparse *arrays* - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you're better to use a *sparse matrix* library, [for example clojure.core.matrix](https://mikera.github.io/core.matrix/).
|
TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)
|
||||||
|
|
||||||
Arbitrary numbers of dimensions are supported, up to limits imposed by the JVM stack.
|
|
||||||
|
|
||||||
Available from Clojars: [](https://clojars.org/sparse-array)
|
|
||||||
Tests currently [](https://circleci.com/gh/simon-brooke/sparse-array)
|
|
||||||
|
|
||||||
## Conventions
|
|
||||||
|
|
||||||
### Sparse arrays
|
|
||||||
|
|
||||||
For the purposes of this library, a sparse array shall be implemented as a map, such that all keys are non-negative members of the set of integers, except for the following keyword keys, all of which are expected to be present:
|
|
||||||
|
|
||||||
1. `:dimensions` The number of dimensions in this array, counting the present one (value expected to be a real number);
|
|
||||||
2. `:coord` The coordinate of the dimension represented by the current map (value expected to be a keyword);
|
|
||||||
3. `:content` What this map contains; if the value of `:dimensions` is one, then `:data`; otherwise, an ordered sequence of the coordinates of the dimensions below this one.
|
|
||||||
|
|
||||||
Thus an array with a single value 'hello' at coordinates x = 3, y = 4, z = 5 would be encoded:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
{:dimensions 3
|
|
||||||
:coord :x
|
|
||||||
:content [:y :z]
|
|
||||||
3 {:dimensions 2
|
|
||||||
:coord :y
|
|
||||||
:content [:z]
|
|
||||||
4 {:dimensions 1
|
|
||||||
:coord :z
|
|
||||||
:content :data
|
|
||||||
5 "hello"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Errors and error-reporting
|
|
||||||
|
|
||||||
A dynamic variable, `*safe-sparse-operations*`, is provided to handle behaviour in error conditions. If this is `false`, bad data will generally not cause an exception to be thrown, and corrupt structures may be returned, thus:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(put (make-sparse-array :x :y :z) "hello" 3) ;; insufficient coordinates specified
|
|
||||||
|
|
||||||
=> {:dimensions 3, :coord :x, :content (:y :z), 3 {:dimensions 2, :coord :y, :content (:z), nil {:dimensions 1, :coord :z, :content :data, nil nil}}}
|
|
||||||
```
|
|
||||||
|
|
||||||
However, if `*safe-sparse-operations*` is bound to `true`, exceptions will be thrown instead:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(binding [*safe-sparse-operations* true]
|
|
||||||
(put (make-sparse-array :x :y :z) "hello" 3))
|
|
||||||
|
|
||||||
ExceptionInfo Expected 3 coordinates; found 1 clojure.core/ex-info (core.clj:4617)
|
|
||||||
```
|
|
||||||
|
|
||||||
Sanity checking data is potentially expensive; for this reason `*safe-sparse-operations*` defaults to `false`, but you may wish to bind it to `true` especially while debugging.
|
|
||||||
|
|
||||||
### Dense arrays
|
|
||||||
|
|
||||||
For the purposes of conversion, a **dense array** is assumed to be a vector; a two dimensional dense array a vector of vectors; a three dimensional dense array a vector of vectors of vectors, and so on. For any depth `N`, all vectors at depth `N` must have the same arity. If these conventions are not respected conversion may fail.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### make-sparse-array
|
|
||||||
`sparse-array.core/make-sparse-array ([& dimensions])`
|
|
||||||
|
|
||||||
Make a sparse array with these `dimensions`. Every member of `dimensions` must be a keyword; otherwise, `nil` will be returned.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(make-sparse-array :x :y :z)
|
|
||||||
|
|
||||||
=> {:dimensions 3, :coord :x, :content (:y :z)}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### sparse-array?
|
|
||||||
|
|
||||||
`sparse-array.core/sparse-array? ([x])`
|
|
||||||
|
|
||||||
`true` if `x` is a sparse array conforming to the conventions established by this library, else `false`.
|
|
||||||
|
|
||||||
### put
|
|
||||||
|
|
||||||
`sparse-array.core/put ([array value & coordinates])`
|
|
||||||
|
|
||||||
Return a sparse array like this `array` but with this `value` at these `coordinates`. Returns `nil` if any coordinate is invalid.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(put (put (make-sparse-array :x :y) "hello" 3 4) "goodbye" 4 3)
|
|
||||||
|
|
||||||
=> {:dimensions 2,
|
|
||||||
:coord :x,
|
|
||||||
:content (:y),
|
|
||||||
3 {:dimensions 1, :coord :y, :content :data, 4 "hello"},
|
|
||||||
4 {:dimensions 1, :coord :y, :content :data, 3 "goodbye"}}
|
|
||||||
```
|
|
||||||
|
|
||||||
### get
|
|
||||||
|
|
||||||
`sparse-array.core/get ([array & coordinates])`
|
|
||||||
|
|
||||||
Return the value in this sparse `array` at these `coordinates`.
|
|
||||||
|
|
||||||
### merge-sparse-arrays
|
|
||||||
|
|
||||||
`sparse-array.core/merge-sparse-arrays ([a1 a2])`
|
|
||||||
|
|
||||||
Return a sparse array taking values from sparse arrays `a1` and `a2`, but preferring values from `a2` where there is a conflict. `a1` and `a2` must have the **same** dimensions in the **same** order, or `nil` will be returned.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(merge-sparse-arrays
|
|
||||||
(put (make-sparse-array :x) "hello" 3)
|
|
||||||
(put (make-sparse-array :x) "goodbye" 4)))
|
|
||||||
|
|
||||||
=> {:dimensions 1, :coord :x, :content :data, 3 "hello", 4 "goodbye"}
|
|
||||||
```
|
|
||||||
|
|
||||||
### dense-to-sparse
|
|
||||||
|
|
||||||
`sparse-array.core/dense-to-sparse ([x] [x coordinates])`
|
|
||||||
|
|
||||||
Return a sparse array representing the content of the dense array `x`, assuming these `coordinates` if specified. *NOTE THAT* if insufficient values of `coordinates` are specified, the resulting sparse array will be malformed.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(dense-to-sparse [nil nil nil "hello" nil "goodbye"])
|
|
||||||
|
|
||||||
=> {:dimensions 1, :coord :i0, :content :data, 3 "hello", 5 "goodbye"}
|
|
||||||
```
|
|
||||||
|
|
||||||
### sparse-to-dense
|
|
||||||
|
|
||||||
`sparse-array.core/sparse-to-dense ([x] [x arity])`
|
|
||||||
|
|
||||||
Return a dense array representing the content of the sparse array `x`.
|
|
||||||
|
|
||||||
**NOTE THAT** this has the potential to consume very large amounts of memory.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(sparse-to-dense
|
|
||||||
(put
|
|
||||||
(put
|
|
||||||
(make-sparse-array :x :y)
|
|
||||||
"hello" 3 4)
|
|
||||||
"goodbye" 4 3))
|
|
||||||
|
|
||||||
=> [[nil nil nil nil nil]
|
|
||||||
[nil nil nil nil nil]
|
|
||||||
[nil nil nil nil nil]
|
|
||||||
[nil nil nil nil "hello"]
|
|
||||||
[nil nil nil "goodbye" nil]]
|
|
||||||
```
|
|
||||||
|
|
||||||
### extract
|
|
||||||
|
|
||||||
The whole point of working with sparse arrays is because we wish to work with
|
|
||||||
interesting subsets of arrays the entirety of which would be too large to
|
|
||||||
conveniently handle; thus perhaps the most important operation is to be able
|
|
||||||
to extract a sparse subset of an array.
|
|
||||||
|
|
||||||
`sparse-array.extract/extract ([array function])`
|
|
||||||
|
|
||||||
Return a sparse subset of this `array` - which may be either sparse or
|
|
||||||
dense - comprising all those cells for which this `function` returns a
|
|
||||||
'truthy' value.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(extract [[[1 2 3][:one :two :three]["one" "two" "three"]]
|
|
||||||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
|
||||||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
|
||||||
#(if
|
|
||||||
(number? %)
|
|
||||||
(= % 3)
|
|
||||||
(= (name %) "three")))
|
|
||||||
|
|
||||||
=> {:dimensions 3,
|
|
||||||
:coord :i0,
|
|
||||||
:content (:i1 :i2),
|
|
||||||
0
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
0 {:dimensions 1, :coord :i2, :content :data, 2 3},
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 :three},
|
|
||||||
2 {:dimensions 1, :coord :i2, :content :data, 2 "three"}},
|
|
||||||
1
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
0 {:dimensions 1, :coord :i2, :content :data, 2 "three"},
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 :three},
|
|
||||||
2 {:dimensions 1, :coord :i2, :content :data, 2 3}},
|
|
||||||
2
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 3}}}
|
|
||||||
```
|
|
||||||
|
|
||||||
### extract-from-dense
|
|
||||||
|
|
||||||
Note that the above example returns the default axis sequence `{i0, i1, i2...}`;
|
|
||||||
extracting from a sparse array will always retain the axes of the array
|
|
||||||
extracted from. Dense arrays, obviously, do not have explicit axes.
|
|
||||||
|
|
||||||
You may wish to specify a sequence of axes when extracting from a dense array.
|
|
||||||
A function is provided:
|
|
||||||
|
|
||||||
`sparse-array.extract/extract-from-dense ([array function] [array function axes])`
|
|
||||||
|
|
||||||
Return a subset of this dense `array` comprising all those cells for which
|
|
||||||
this `function` returns a 'truthy' value. Use these `axes` if provided.
|
|
||||||
|
|
||||||
e.g.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(extract-from-dense
|
|
||||||
[[[1 2 3][:one :two :three]["one" "two" "three"]]
|
|
||||||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
|
||||||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
|
||||||
integer?
|
|
||||||
'(:p :q :r))
|
|
||||||
|
|
||||||
=> {:dimensions 3,
|
|
||||||
:coord :p,
|
|
||||||
:content (:q :r),
|
|
||||||
0
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
0 {:dimensions 1, :coord :r, :content :data, 0 1, 1 2, 2 3}},
|
|
||||||
1
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
0 {:dimensions 1, :coord :r, :content :data, 0 1},
|
|
||||||
1 {:dimensions 1, :coord :r, :content :data, 1 2},
|
|
||||||
2 {:dimensions 1, :coord :r, :content :data, 2 3}},
|
|
||||||
2
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
1 {:dimensions 1, :coord :r, :content :data, 0 1, 1 2, 2 3}}}
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Copyright © 2019 Simon Brooke
|
|
||||||
|
|
||||||
Distributed under the Eclipse Public License either version 1.0 or (at
|
|
||||||
your option) any later version.
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
.covered {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
background-color: #558B55;
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-covered {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.partial {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
background-color: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
.not-tracked {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blank {
|
|
||||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.with-bar {
|
|
||||||
width: 250px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.with-number {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.ns-name {
|
|
||||||
min-width: 150px;
|
|
||||||
padding-right: 25px;
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="./coverage.css"/>
|
|
||||||
<title>Coverage Summary</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<table>
|
|
||||||
<thead><tr>
|
|
||||||
<td class="ns-name"> Namespace </td>
|
|
||||||
<td class="with-bar"> Forms </td>
|
|
||||||
<td class="with-number">Forms %</td>
|
|
||||||
<td class="with-bar"> Lines </td>
|
|
||||||
<td class="with-number">Lines %</td>
|
|
||||||
<td class="with-number">Total</td><td class="with-number">Blank</td><td class="with-number">Instrumented</td>
|
|
||||||
</tr></thead>
|
|
||||||
<tr>
|
|
||||||
<td><a href="sparse_array/core.clj.html">sparse-array.core</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:85.14150943396227%;
|
|
||||||
float:left;"> 722 </div><div class="not-covered"
|
|
||||||
style="width:14.858490566037736%;
|
|
||||||
float:left;"> 126 </div></td>
|
|
||||||
<td class="with-number">85.14 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:82.37885462555066%;
|
|
||||||
float:left;"> 187 </div><div class="partial"
|
|
||||||
style="width:6.167400881057269%;
|
|
||||||
float:left;"> 14 </div><div class="not-covered"
|
|
||||||
style="width:11.45374449339207%;
|
|
||||||
float:left;"> 26 </div></td>
|
|
||||||
<td class="with-number">88.55 %</td>
|
|
||||||
<td class="with-number">337</td><td class="with-number">22</td><td class="with-number">227</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="sparse_array/extract.clj.html">sparse-array.extract</a></td><td class="with-bar"><div class="covered"
|
|
||||||
style="width:94.31818181818181%;
|
|
||||||
float:left;"> 166 </div><div class="not-covered"
|
|
||||||
style="width:5.681818181818182%;
|
|
||||||
float:left;"> 10 </div></td>
|
|
||||||
<td class="with-number">94.32 %</td>
|
|
||||||
<td class="with-bar"><div class="covered"
|
|
||||||
style="width:89.1304347826087%;
|
|
||||||
float:left;"> 41 </div><div class="partial"
|
|
||||||
style="width:2.1739130434782608%;
|
|
||||||
float:left;"> 1 </div><div class="not-covered"
|
|
||||||
style="width:8.695652173913043%;
|
|
||||||
float:left;"> 4 </div></td>
|
|
||||||
<td class="with-number">91.30 %</td>
|
|
||||||
<td class="with-number">71</td><td class="with-number">5</td><td class="with-number">46</td>
|
|
||||||
</tr>
|
|
||||||
<tr><td>Totals:</td>
|
|
||||||
<td class="with-bar"></td>
|
|
||||||
<td class="with-number">86.72 %</td>
|
|
||||||
<td class="with-bar"></td>
|
|
||||||
<td class="with-number">89.01 %</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,221 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link rel="stylesheet" href="../coverage.css"/> <title> sparse_array/extract.clj </title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
001 (ns sparse-array.extract
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
002 "Extracting interesting data from sparse arrays."
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
003 (:require [sparse-array.core :refer [*safe-sparse-operations*
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
004 dense-array? dense-dimensions
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
005 make-sparse-array sparse-array?]]))
|
|
||||||
</span><br/>
|
|
||||||
<span class="blank" title="0 out of 0 forms covered">
|
|
||||||
006
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
007 ;;; The whole point of working with sparse arrays is to work with interesting
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
008 ;;; subsets of arrays most of which are uninteresting. To extract an
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
009 ;;; interesting subset from an array, we're going to need an extract function.
|
|
||||||
</span><br/>
|
|
||||||
<span class="blank" title="0 out of 0 forms covered">
|
|
||||||
010
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
011 (defn- extract-from-sparse
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
012 "Return a subset of this sparse `array` comprising all those cells for which
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
013 this `function` returns a 'truthy' value."
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
014 [array function]
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
015 (reduce
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
016 merge
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
017 (apply
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
018 make-sparse-array
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
019 (cons
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="3 out of 3 forms covered">
|
|
||||||
020 (:coord array)
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="10 out of 10 forms covered">
|
|
||||||
021 (when (coll? (:content array)) (:content array))))
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="3 out of 3 forms covered">
|
|
||||||
022 (map
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
023 #(if
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="5 out of 5 forms covered">
|
|
||||||
024 (= :data (:content array))
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="12 out of 12 forms covered">
|
|
||||||
025 (when (function (array %)) {% (array %)})
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="7 out of 7 forms covered">
|
|
||||||
026 (let [v (extract-from-sparse (array %) function)]
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
027 (if
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
028 (empty?
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="6 out of 6 forms covered">
|
|
||||||
029 (filter integer? (keys v)))
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
030 nil
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="3 out of 3 forms covered">
|
|
||||||
031 {% v})))
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="6 out of 6 forms covered">
|
|
||||||
032 (filter integer? (keys array)))))
|
|
||||||
</span><br/>
|
|
||||||
<span class="blank" title="0 out of 0 forms covered">
|
|
||||||
033
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
034 (defn extract-from-dense
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
035 "Return a subset of this dense `array` comprising all those cells for which
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
036 this `function` returns a 'truthy' value. Use these `axes` if provided."
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
037 ([array function]
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="15 out of 15 forms covered">
|
|
||||||
038 (extract-from-dense array function (map #(keyword (str "i" %)) (range))))
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
039 ([array function axes]
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
040 (let
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="3 out of 3 forms covered">
|
|
||||||
041 [dimensions (dense-dimensions array)]
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
042 (reduce
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
043 merge
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="7 out of 7 forms covered">
|
|
||||||
044 (apply make-sparse-array (take dimensions axes))
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
045 (if
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="3 out of 3 forms covered">
|
|
||||||
046 (= dimensions 1)
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
047 (map
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="10 out of 10 forms covered">
|
|
||||||
048 (fn [i v] (when (function v) (hash-map i v)))
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
049 (range)
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
050 array)
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
051 (map
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="15 out of 15 forms covered">
|
|
||||||
052 (fn [i v] (if (empty? (filter integer? (keys v))) nil (hash-map i v)))
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="2 out of 2 forms covered">
|
|
||||||
053 (range)
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="11 out of 11 forms covered">
|
|
||||||
054 (map #(extract-from-dense % function (rest axes)) array)))))))
|
|
||||||
</span><br/>
|
|
||||||
<span class="blank" title="0 out of 0 forms covered">
|
|
||||||
055
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="1 out of 1 forms covered">
|
|
||||||
056 (defn extract
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
057 "Return a sparse subset of this `array` - which may be either sparse or
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
058 dense - comprising all those cells for which this `function` returns a
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
059 'truthy' value."
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
060 [array function]
|
|
||||||
</span><br/>
|
|
||||||
<span class="partial" title="2 out of 4 forms covered">
|
|
||||||
061 (cond
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="3 out of 3 forms covered">
|
|
||||||
062 (sparse-array? array)
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="4 out of 4 forms covered">
|
|
||||||
063 (extract-from-sparse array function)
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="3 out of 3 forms covered">
|
|
||||||
064 (dense-array? array)
|
|
||||||
</span><br/>
|
|
||||||
<span class="covered" title="4 out of 4 forms covered">
|
|
||||||
065 (extract-from-dense array function)
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-covered" title="0 out of 1 forms covered">
|
|
||||||
066 *safe-sparse-operations*
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-covered" title="0 out of 1 forms covered">
|
|
||||||
067 (throw
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-covered" title="0 out of 3 forms covered">
|
|
||||||
068 (ex-info
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
|
||||||
069 "Argument passed as `array` is neither sparse nor dense."
|
|
||||||
</span><br/>
|
|
||||||
<span class="not-covered" title="0 out of 3 forms covered">
|
|
||||||
070 {:array array}))))
|
|
||||||
</span><br/>
|
|
||||||
<span class="blank" title="0 out of 0 forms covered">
|
|
||||||
071
|
|
||||||
</span><br/>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,551 +0,0 @@
|
||||||
body {
|
|
||||||
font-family: Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, code {
|
|
||||||
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
|
|
||||||
font-size: 9pt;
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 29px;
|
|
||||||
margin: 10px 0 2px 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5.license {
|
|
||||||
margin: 9px 0 22px 0;
|
|
||||||
color: #555;
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 12px;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.document h1, .namespace-index h1 {
|
|
||||||
font-size: 32px;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header, #content, .sidebar {
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 22px;
|
|
||||||
color: #f5f5f5;
|
|
||||||
padding: 5px 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
top: 32px;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
overflow: auto;
|
|
||||||
background: #fff;
|
|
||||||
color: #333;
|
|
||||||
padding: 0 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
position: fixed;
|
|
||||||
top: 32px;
|
|
||||||
bottom: 0;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.primary {
|
|
||||||
background: #e2e2e2;
|
|
||||||
border-right: solid 1px #cccccc;
|
|
||||||
left: 0;
|
|
||||||
width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.secondary {
|
|
||||||
background: #f2f2f2;
|
|
||||||
border-right: solid 1px #d7d7d7;
|
|
||||||
left: 251px;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content.namespace-index, #content.document {
|
|
||||||
left: 251px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content.namespace-docs {
|
|
||||||
left: 452px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content.document {
|
|
||||||
padding-bottom: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
background: #3f3f3f;
|
|
||||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h1 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: lighter;
|
|
||||||
text-shadow: -1px -1px 0px #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h1 .project-version {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-version {
|
|
||||||
padding-left: 0.15em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header a, .sidebar a {
|
|
||||||
display: block;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header a {
|
|
||||||
color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar a {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h2 {
|
|
||||||
float: right;
|
|
||||||
font-size: 9pt;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 4px 3px;
|
|
||||||
padding: 0;
|
|
||||||
color: #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header h2 a {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar h3 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px 13px 0 13px;
|
|
||||||
font-size: 19px;
|
|
||||||
font-weight: lighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar h3 a {
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar h3.no-link {
|
|
||||||
color: #636363;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar ul {
|
|
||||||
padding: 7px 0 6px 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar ul.index-link {
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li {
|
|
||||||
display: block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li a, .sidebar li .no-link {
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
padding: 0 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .no-link {
|
|
||||||
display: block;
|
|
||||||
color: #777;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .inner {
|
|
||||||
display: inline-block;
|
|
||||||
padding-top: 7px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li a, .sidebar li .tree {
|
|
||||||
height: 31px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.depth-1 .inner { padding-left: 2px; }
|
|
||||||
.depth-2 .inner { padding-left: 6px; }
|
|
||||||
.depth-3 .inner { padding-left: 20px; }
|
|
||||||
.depth-4 .inner { padding-left: 34px; }
|
|
||||||
.depth-5 .inner { padding-left: 48px; }
|
|
||||||
.depth-6 .inner { padding-left: 62px; }
|
|
||||||
|
|
||||||
.sidebar li .tree {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
position: relative;
|
|
||||||
top: -10px;
|
|
||||||
margin: 0 4px 0 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li.depth-1 .tree {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .tree .top, .sidebar li .tree .bottom {
|
|
||||||
display: block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .tree .top {
|
|
||||||
border-left: 1px solid #aaa;
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
height: 19px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li .tree .bottom {
|
|
||||||
height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar li.branch .tree .bottom {
|
|
||||||
border-left: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.primary li.current a {
|
|
||||||
border-left: 3px solid #a33;
|
|
||||||
color: #a33;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar.secondary li.current a {
|
|
||||||
border-left: 3px solid #33a;
|
|
||||||
color: #33a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index h2 {
|
|
||||||
margin: 30px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index h3 {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index .topics {
|
|
||||||
padding-left: 30px;
|
|
||||||
margin: 11px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index .topics li {
|
|
||||||
padding: 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-docs h3 {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public h3 {
|
|
||||||
margin: 0;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public {
|
|
||||||
margin: 0;
|
|
||||||
border-top: 1px solid #e0e0e0;
|
|
||||||
padding-top: 14px;
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public:last-child {
|
|
||||||
margin-bottom: 20%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members .public:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members {
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members h4 {
|
|
||||||
color: #555;
|
|
||||||
font-weight: normal;
|
|
||||||
font-variant: small-caps;
|
|
||||||
margin: 0 0 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members .inner {
|
|
||||||
padding-top: 5px;
|
|
||||||
padding-left: 12px;
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-left: 7px;
|
|
||||||
border-left: 1px solid #bbb;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content .members .inner h3 {
|
|
||||||
font-size: 12pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members .public {
|
|
||||||
border-top: none;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-top: 6px;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members .public:first-child {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.type,
|
|
||||||
h4.dynamic,
|
|
||||||
h4.added,
|
|
||||||
h4.deprecated {
|
|
||||||
float: left;
|
|
||||||
margin: 3px 10px 15px 0;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
font-variant: small-caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public h4.type,
|
|
||||||
.public h4.dynamic,
|
|
||||||
.public h4.added,
|
|
||||||
.public h4.deprecated {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 3px 0 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members h4.type,
|
|
||||||
.members h4.added,
|
|
||||||
.members h4.deprecated {
|
|
||||||
margin-top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.type {
|
|
||||||
color: #717171;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.dynamic {
|
|
||||||
color: #9933aa;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.added {
|
|
||||||
color: #508820;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.deprecated {
|
|
||||||
color: #880000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace:last-child {
|
|
||||||
margin-bottom: 10%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index {
|
|
||||||
padding: 0;
|
|
||||||
font-size: 80%;
|
|
||||||
margin: 15px 0;
|
|
||||||
line-height: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index * {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index p {
|
|
||||||
padding-right: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index li {
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.index ul {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-sig {
|
|
||||||
clear: both;
|
|
||||||
color: #088;
|
|
||||||
}
|
|
||||||
|
|
||||||
.type-sig pre {
|
|
||||||
padding-top: 10px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage code {
|
|
||||||
display: block;
|
|
||||||
color: #008;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage code:first-child {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public p:first-child, .public pre.plaintext {
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc {
|
|
||||||
margin: 0 0 26px 0;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.public .doc {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index .doc {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.namespace-index .namespace .doc {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td {
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown li {
|
|
||||||
padding: 2px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown h2 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 25px;
|
|
||||||
margin: 30px 0 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown h3 {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 20px;
|
|
||||||
margin: 30px 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown h4 {
|
|
||||||
font-size: 15px;
|
|
||||||
margin: 22px 0 -4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc, .public, .namespace .index {
|
|
||||||
max-width: 680px;
|
|
||||||
overflow-x: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown pre > code {
|
|
||||||
display: block;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown pre > code, .src-link a {
|
|
||||||
border: 1px solid #e4e4e4;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown code:not(.hljs), .src-link a {
|
|
||||||
background: #f6f6f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.deps {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
border: 1px solid #e4e4e4;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #f6f6f6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.markdown hr {
|
|
||||||
border-style: solid;
|
|
||||||
border-top: none;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc ul, .doc ol {
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc table td, .doc table th {
|
|
||||||
border: 1px solid #dddddd;
|
|
||||||
padding: 4px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc table th {
|
|
||||||
background: #f2f2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc dl {
|
|
||||||
margin: 0 10px 20px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc dl dt {
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
padding: 3px 0;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc dl dd {
|
|
||||||
padding: 5px 0;
|
|
||||||
margin: 0 0 5px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.doc abbr {
|
|
||||||
border-bottom: 1px dotted #333;
|
|
||||||
font-variant: none;
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
.src-link {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.src-link a {
|
|
||||||
font-size: 70%;
|
|
||||||
padding: 1px 4px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #5555bb;
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
|
||||||
*/
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
display: block;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 0.5em;
|
|
||||||
color: #333;
|
|
||||||
background: #f8f8f8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-comment,
|
|
||||||
.hljs-quote {
|
|
||||||
color: #998;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-keyword,
|
|
||||||
.hljs-selector-tag,
|
|
||||||
.hljs-subst {
|
|
||||||
color: #333;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-number,
|
|
||||||
.hljs-literal,
|
|
||||||
.hljs-variable,
|
|
||||||
.hljs-template-variable,
|
|
||||||
.hljs-tag .hljs-attr {
|
|
||||||
color: #008080;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-string,
|
|
||||||
.hljs-doctag {
|
|
||||||
color: #d14;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-title,
|
|
||||||
.hljs-section,
|
|
||||||
.hljs-selector-id {
|
|
||||||
color: #900;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-subst {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-type,
|
|
||||||
.hljs-class .hljs-title {
|
|
||||||
color: #458;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-tag,
|
|
||||||
.hljs-name,
|
|
||||||
.hljs-attribute {
|
|
||||||
color: #000080;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-regexp,
|
|
||||||
.hljs-link {
|
|
||||||
color: #009926;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-symbol,
|
|
||||||
.hljs-bullet {
|
|
||||||
color: #990073;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-built_in,
|
|
||||||
.hljs-builtin-name {
|
|
||||||
color: #0086b3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-meta {
|
|
||||||
color: #999;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-deletion {
|
|
||||||
background: #fdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-addition {
|
|
||||||
background: #dfd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-emphasis {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>Sparse-array 0.3.0</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Sparse-array</span> <span class="project-version">0.3.0</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 current"><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to Sparse-array</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>sparse-array</span></div></div></li><li class="depth-2 branch"><a href="sparse-array.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2"><a href="sparse-array.extract.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>extract</span></div></a></li></ul></div><div class="namespace-index" id="content"><h1><span class="project-title"><span class="project-name">Sparse-array</span> <span class="project-version">0.3.0</span></span></h1><h5 class="license">Released under the <a href="http://www.eclipse.org/legal/epl-v10.html">Eclipse Public License</a></h5><div class="doc"><p>A Clojure library designed to manipulate sparse *arrays* - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you're better to use a *sparse matrix* library, for example [clojure.core.matrix](https://mikera.github.io/core.matrix/).</p></div><h2>Installation</h2><p>To install, add the following dependency to your project or build file:</p><pre class="deps">[sparse-array "0.3.0"]</pre><h2>Topics</h2><ul class="topics"><li><a href="intro.html">Introduction to Sparse-array</a></li></ul><h2>Namespaces</h2><div class="namespace"><h3><a href="sparse-array.core.html">sparse-array.core</a></h3><div class="doc"><div class="markdown"><p>Operations on sparse arrays.</p></div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="sparse-array.core.html#var-*safe-sparse-operations*">*safe-sparse-operations*</a> </li><li> <a href="sparse-array.core.html#var-arity">arity</a> </li><li> <a href="sparse-array.core.html#var-child-arity">child-arity</a> </li><li> <a href="sparse-array.core.html#var-dense-array.3F">dense-array?</a> </li><li> <a href="sparse-array.core.html#var-dense-dimensions">dense-dimensions</a> </li><li> <a href="sparse-array.core.html#var-dense-to-sparse">dense-to-sparse</a> </li><li> <a href="sparse-array.core.html#var-get">get</a> </li><li> <a href="sparse-array.core.html#var-make-sparse-array">make-sparse-array</a> </li><li> <a href="sparse-array.core.html#var-merge-arrays">merge-arrays</a> </li><li> <a href="sparse-array.core.html#var-merge-dense-with-sparse">merge-dense-with-sparse</a> </li><li> <a href="sparse-array.core.html#var-merge-sparse-arrays">merge-sparse-arrays</a> </li><li> <a href="sparse-array.core.html#var-put">put</a> </li><li> <a href="sparse-array.core.html#var-sparse-array.3F">sparse-array?</a> </li><li> <a href="sparse-array.core.html#var-sparse-to-dense">sparse-to-dense</a> </li></ul></div></div><div class="namespace"><h3><a href="sparse-array.extract.html">sparse-array.extract</a></h3><div class="doc"><div class="markdown"><p>Extracting interesting data from sparse arrays.</p></div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="sparse-array.extract.html#var-extract">extract</a> </li><li> <a href="sparse-array.extract.html#var-extract-from-dense">extract-from-dense</a> </li></ul></div></div></div></body></html>
|
|
|
@ -1,181 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>Introduction to Sparse-array</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Sparse-array</span> <span class="project-version">0.3.0</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>Introduction to Sparse-array</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>sparse-array</span></div></div></li><li class="depth-2 branch"><a href="sparse-array.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2"><a href="sparse-array.extract.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>extract</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#introduction-to-sparse-array" name="introduction-to-sparse-array"></a>Introduction to Sparse-array</h1>
|
|
||||||
<p>A Clojure library designed to manipulate sparse <em>arrays</em> - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you’re better to use a <em>sparse matrix</em> library, <a href="https://mikera.github.io/core.matrix/">for example clojure.core.matrix</a>.</p>
|
|
||||||
<p>Arbitrary numbers of dimensions are supported, up to limits imposed by the JVM stack.</p>
|
|
||||||
<p>Available from Clojars: <a href="https://clojars.org/sparse-array"><img src="https://img.shields.io/clojars/v/sparse-array.svg" alt="Clojars Project" /></a> Tests currently <a href="https://circleci.com/gh/simon-brooke/sparse-array"><img src="https://circleci.com/gh/simon-brooke/sparse-array.svg?style=svg" alt="CircleCI" /></a></p>
|
|
||||||
<h2><a href="#conventions" name="conventions"></a>Conventions</h2>
|
|
||||||
<h3><a href="#sparse-arrays" name="sparse-arrays"></a>Sparse arrays</h3>
|
|
||||||
<p>For the purposes of this library, a sparse array shall be implemented as a map, such that all keys are non-negative members of the set of integers, except for the following keyword keys, all of which are expected to be present:</p>
|
|
||||||
<ol>
|
|
||||||
<li><code>:dimensions</code> The number of dimensions in this array, counting the present one (value expected to be a real number);</li>
|
|
||||||
<li><code>:coord</code> The coordinate of the dimension represented by the current map (value expected to be a keyword);</li>
|
|
||||||
<li><code>:content</code> What this map contains; if the value of <code>:dimensions</code> is one, then <code>:data</code>; otherwise, an ordered sequence of the coordinates of the dimensions below this one.</li>
|
|
||||||
</ol>
|
|
||||||
<p>Thus an array with a single value ‘hello’ at coordinates x = 3, y = 4, z = 5 would be encoded:</p>
|
|
||||||
<pre><code class="clojure">{:dimensions 3
|
|
||||||
:coord :x
|
|
||||||
:content [:y :z]
|
|
||||||
3 {:dimensions 2
|
|
||||||
:coord :y
|
|
||||||
:content [:z]
|
|
||||||
4 {:dimensions 1
|
|
||||||
:coord :z
|
|
||||||
:content :data
|
|
||||||
5 "hello"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</code></pre>
|
|
||||||
<h3><a href="#errors-and-error-reporting" name="errors-and-error-reporting"></a>Errors and error-reporting</h3>
|
|
||||||
<p>A dynamic variable, <code>*safe-sparse-operations*</code>, is provided to handle behaviour in error conditions. If this is <code>false</code>, bad data will generally not cause an exception to be thrown, and corrupt structures may be returned, thus:</p>
|
|
||||||
<pre><code class="clojure">(put (make-sparse-array :x :y :z) "hello" 3) ;; insufficient coordinates specified
|
|
||||||
|
|
||||||
=> {:dimensions 3, :coord :x, :content (:y :z), 3 {:dimensions 2, :coord :y, :content (:z), nil {:dimensions 1, :coord :z, :content :data, nil nil}}}
|
|
||||||
</code></pre>
|
|
||||||
<p>However, if <code>*safe-sparse-operations*</code> is bound to <code>true</code>, exceptions will be thrown instead:</p>
|
|
||||||
<pre><code class="clojure">(binding [*safe-sparse-operations* true]
|
|
||||||
(put (make-sparse-array :x :y :z) "hello" 3))
|
|
||||||
|
|
||||||
ExceptionInfo Expected 3 coordinates; found 1 clojure.core/ex-info (core.clj:4617)
|
|
||||||
</code></pre>
|
|
||||||
<p>Sanity checking data is potentially expensive; for this reason <code>*safe-sparse-operations*</code> defaults to <code>false</code>, but you may wish to bind it to <code>true</code> especially while debugging.</p>
|
|
||||||
<h3><a href="#dense-arrays" name="dense-arrays"></a>Dense arrays</h3>
|
|
||||||
<p>For the purposes of conversion, a <strong>dense array</strong> is assumed to be a vector; a two dimensional dense array a vector of vectors; a three dimensional dense array a vector of vectors of vectors, and so on. For any depth <code>N</code>, all vectors at depth <code>N</code> must have the same arity. If these conventions are not respected conversion may fail.</p>
|
|
||||||
<h2><a href="#usage" name="usage"></a>Usage</h2>
|
|
||||||
<h3><a href="#make-sparse-array" name="make-sparse-array"></a>make-sparse-array</h3>
|
|
||||||
<p><code>sparse-array.core/make-sparse-array ([& dimensions])</code></p>
|
|
||||||
<p>Make a sparse array with these <code>dimensions</code>. Every member of <code>dimensions</code> must be a keyword; otherwise, <code>nil</code> will be returned.</p>
|
|
||||||
<p>e.g.</p>
|
|
||||||
<pre><code class="clojure">(make-sparse-array :x :y :z)
|
|
||||||
|
|
||||||
=> {:dimensions 3, :coord :x, :content (:y :z)}
|
|
||||||
|
|
||||||
</code></pre>
|
|
||||||
<h3><a href="#sparse-array-" name="sparse-array-"></a>sparse-array?</h3>
|
|
||||||
<p><code>sparse-array.core/sparse-array? ([x])</code></p>
|
|
||||||
<p><code>true</code> if <code>x</code> is a sparse array conforming to the conventions established by this library, else <code>false</code>.</p>
|
|
||||||
<h3><a href="#put" name="put"></a>put</h3>
|
|
||||||
<p><code>sparse-array.core/put ([array value & coordinates])</code></p>
|
|
||||||
<p>Return a sparse array like this <code>array</code> but with this <code>value</code> at these <code>coordinates</code>. Returns <code>nil</code> if any coordinate is invalid.</p>
|
|
||||||
<p>e.g.</p>
|
|
||||||
<pre><code class="clojure">(put (put (make-sparse-array :x :y) "hello" 3 4) "goodbye" 4 3)
|
|
||||||
|
|
||||||
=> {:dimensions 2,
|
|
||||||
:coord :x,
|
|
||||||
:content (:y),
|
|
||||||
3 {:dimensions 1, :coord :y, :content :data, 4 "hello"},
|
|
||||||
4 {:dimensions 1, :coord :y, :content :data, 3 "goodbye"}}
|
|
||||||
</code></pre>
|
|
||||||
<h3><a href="#get" name="get"></a>get</h3>
|
|
||||||
<p><code>sparse-array.core/get ([array & coordinates])</code></p>
|
|
||||||
<p>Return the value in this sparse <code>array</code> at these <code>coordinates</code>.</p>
|
|
||||||
<h3><a href="#merge-sparse-arrays" name="merge-sparse-arrays"></a>merge-sparse-arrays</h3>
|
|
||||||
<p><code>sparse-array.core/merge-sparse-arrays ([a1 a2])</code></p>
|
|
||||||
<p>Return a sparse array taking values from sparse arrays <code>a1</code> and <code>a2</code>, but preferring values from <code>a2</code> where there is a conflict. <code>a1</code> and <code>a2</code> must have the <strong>same</strong> dimensions in the <strong>same</strong> order, or <code>nil</code> will be returned.</p>
|
|
||||||
<p>e.g.</p>
|
|
||||||
<pre><code class="clojure">(merge-sparse-arrays
|
|
||||||
(put (make-sparse-array :x) "hello" 3)
|
|
||||||
(put (make-sparse-array :x) "goodbye" 4)))
|
|
||||||
|
|
||||||
=> {:dimensions 1, :coord :x, :content :data, 3 "hello", 4 "goodbye"}
|
|
||||||
</code></pre>
|
|
||||||
<h3><a href="#dense-to-sparse" name="dense-to-sparse"></a>dense-to-sparse</h3>
|
|
||||||
<p><code>sparse-array.core/dense-to-sparse ([x] [x coordinates])</code></p>
|
|
||||||
<p>Return a sparse array representing the content of the dense array <code>x</code>, assuming these <code>coordinates</code> if specified. <em>NOTE THAT</em> if insufficient values of <code>coordinates</code> are specified, the resulting sparse array will be malformed.</p>
|
|
||||||
<p>e.g.</p>
|
|
||||||
<pre><code class="clojure">(dense-to-sparse [nil nil nil "hello" nil "goodbye"])
|
|
||||||
|
|
||||||
=> {:dimensions 1, :coord :i0, :content :data, 3 "hello", 5 "goodbye"}
|
|
||||||
</code></pre>
|
|
||||||
<h3><a href="#sparse-to-dense" name="sparse-to-dense"></a>sparse-to-dense</h3>
|
|
||||||
<p><code>sparse-array.core/sparse-to-dense ([x] [x arity])</code></p>
|
|
||||||
<p>Return a dense array representing the content of the sparse array <code>x</code>.</p>
|
|
||||||
<p><strong>NOTE THAT</strong> this has the potential to consume very large amounts of memory.</p>
|
|
||||||
<p>e.g.</p>
|
|
||||||
<pre><code class="clojure">(sparse-to-dense
|
|
||||||
(put
|
|
||||||
(put
|
|
||||||
(make-sparse-array :x :y)
|
|
||||||
"hello" 3 4)
|
|
||||||
"goodbye" 4 3))
|
|
||||||
|
|
||||||
=> [[nil nil nil nil nil]
|
|
||||||
[nil nil nil nil nil]
|
|
||||||
[nil nil nil nil nil]
|
|
||||||
[nil nil nil nil "hello"]
|
|
||||||
[nil nil nil "goodbye" nil]]
|
|
||||||
</code></pre>
|
|
||||||
<h3><a href="#extract" name="extract"></a>extract</h3>
|
|
||||||
<p>The whole point of working with sparse arrays is because we wish to work with interesting subsets of arrays the entirety of which would be too large to conveniently handle; thus perhaps the most important operation is to be able to extract a sparse subset of an array.</p>
|
|
||||||
<p><code>sparse-array.extract/extract ([array function])</code></p>
|
|
||||||
<p>Return a sparse subset of this <code>array</code> - which may be either sparse or dense - comprising all those cells for which this <code>function</code> returns a ‘truthy’ value.</p>
|
|
||||||
<p>e.g.</p>
|
|
||||||
<pre><code class="clojure">(extract [[[1 2 3][:one :two :three]["one" "two" "three"]]
|
|
||||||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
|
||||||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
|
||||||
#(if
|
|
||||||
(number? %)
|
|
||||||
(= % 3)
|
|
||||||
(= (name %) "three")))
|
|
||||||
|
|
||||||
=> {:dimensions 3,
|
|
||||||
:coord :i0,
|
|
||||||
:content (:i1 :i2),
|
|
||||||
0
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
0 {:dimensions 1, :coord :i2, :content :data, 2 3},
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 :three},
|
|
||||||
2 {:dimensions 1, :coord :i2, :content :data, 2 "three"}},
|
|
||||||
1
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
0 {:dimensions 1, :coord :i2, :content :data, 2 "three"},
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 :three},
|
|
||||||
2 {:dimensions 1, :coord :i2, :content :data, 2 3}},
|
|
||||||
2
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :i1,
|
|
||||||
:content (:i2),
|
|
||||||
1 {:dimensions 1, :coord :i2, :content :data, 2 3}}}
|
|
||||||
</code></pre>
|
|
||||||
<h3><a href="#extract-from-dense" name="extract-from-dense"></a>extract-from-dense</h3>
|
|
||||||
<p>Note that the above example returns the default axis sequence <code>{i0, i1, i2...}</code>; extracting from a sparse array will always retain the axes of the array extracted from. Dense arrays, obviously, do not have explicit axes.</p>
|
|
||||||
<p>You may wish to specify a sequence of axes when extracting from a dense array. A function is provided:</p>
|
|
||||||
<p><code>sparse-array.extract/extract-from-dense ([array function] [array function axes])</code></p>
|
|
||||||
<p>Return a subset of this dense <code>array</code> comprising all those cells for which this <code>function</code> returns a ‘truthy’ value. Use these <code>axes</code> if provided.</p>
|
|
||||||
<p>e.g.</p>
|
|
||||||
<pre><code class="clojure">(extract-from-dense
|
|
||||||
[[[1 2 3][:one :two :three]["one" "two" "three"]]
|
|
||||||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
|
||||||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
|
||||||
integer?
|
|
||||||
'(:p :q :r))
|
|
||||||
|
|
||||||
=> {:dimensions 3,
|
|
||||||
:coord :p,
|
|
||||||
:content (:q :r),
|
|
||||||
0
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
0 {:dimensions 1, :coord :r, :content :data, 0 1, 1 2, 2 3}},
|
|
||||||
1
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
0 {:dimensions 1, :coord :r, :content :data, 0 1},
|
|
||||||
1 {:dimensions 1, :coord :r, :content :data, 1 2},
|
|
||||||
2 {:dimensions 1, :coord :r, :content :data, 2 3}},
|
|
||||||
2
|
|
||||||
{:dimensions 2,
|
|
||||||
:coord :q,
|
|
||||||
:content (:r),
|
|
||||||
1 {:dimensions 1, :coord :r, :content :data, 0 1, 1 2, 2 3}}}
|
|
||||||
</code></pre>
|
|
||||||
<h2><a href="#license" name="license"></a>License</h2>
|
|
||||||
<p>Copyright © 2019 Simon Brooke</p>
|
|
||||||
<p>Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.</p></div></div></div></body></html>
|
|
2
docs/codox/js/highlight.min.js
vendored
2
docs/codox/js/highlight.min.js
vendored
File diff suppressed because one or more lines are too long
4
docs/codox/js/jquery.min.js
vendored
4
docs/codox/js/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,112 +0,0 @@
|
||||||
function visibleInParent(element) {
|
|
||||||
var position = $(element).position().top
|
|
||||||
return position > -50 && position < ($(element).offsetParent().height() - 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasFragment(link, fragment) {
|
|
||||||
return $(link).attr("href").indexOf("#" + fragment) != -1
|
|
||||||
}
|
|
||||||
|
|
||||||
function findLinkByFragment(elements, fragment) {
|
|
||||||
return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first()
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollToCurrentVarLink(elements) {
|
|
||||||
var elements = $(elements);
|
|
||||||
var parent = elements.offsetParent();
|
|
||||||
|
|
||||||
if (elements.length == 0) return;
|
|
||||||
|
|
||||||
var top = elements.first().position().top;
|
|
||||||
var bottom = elements.last().position().top + elements.last().height();
|
|
||||||
|
|
||||||
if (top >= 0 && bottom <= parent.height()) return;
|
|
||||||
|
|
||||||
if (top < 0) {
|
|
||||||
parent.scrollTop(parent.scrollTop() + top);
|
|
||||||
}
|
|
||||||
else if (bottom > parent.height()) {
|
|
||||||
parent.scrollTop(parent.scrollTop() + bottom - parent.height());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setCurrentVarLink() {
|
|
||||||
$('.secondary a').parent().removeClass('current')
|
|
||||||
$('.anchor').
|
|
||||||
filter(function(index) { return visibleInParent(this) }).
|
|
||||||
each(function(index, element) {
|
|
||||||
findLinkByFragment(".secondary a", element.id).
|
|
||||||
parent().
|
|
||||||
addClass('current')
|
|
||||||
});
|
|
||||||
scrollToCurrentVarLink('.secondary .current');
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }())
|
|
||||||
|
|
||||||
function scrollPositionId(element) {
|
|
||||||
var directory = window.location.href.replace(/[^\/]+\.html$/, '')
|
|
||||||
return 'scroll::' + $(element).attr('id') + '::' + directory
|
|
||||||
}
|
|
||||||
|
|
||||||
function storeScrollPosition(element) {
|
|
||||||
if (!hasStorage) return;
|
|
||||||
localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft())
|
|
||||||
localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop())
|
|
||||||
}
|
|
||||||
|
|
||||||
function recallScrollPosition(element) {
|
|
||||||
if (!hasStorage) return;
|
|
||||||
$(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x"))
|
|
||||||
$(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y"))
|
|
||||||
}
|
|
||||||
|
|
||||||
function persistScrollPosition(element) {
|
|
||||||
recallScrollPosition(element)
|
|
||||||
$(element).scroll(function() { storeScrollPosition(element) })
|
|
||||||
}
|
|
||||||
|
|
||||||
function sidebarContentWidth(element) {
|
|
||||||
var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() })
|
|
||||||
return Math.max.apply(Math, widths)
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateSize(width, snap, margin, minimum) {
|
|
||||||
if (width == 0) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resizeSidebars() {
|
|
||||||
var primaryWidth = sidebarContentWidth('.primary')
|
|
||||||
var secondaryWidth = 0
|
|
||||||
|
|
||||||
if ($('.secondary').length != 0) {
|
|
||||||
secondaryWidth = sidebarContentWidth('.secondary')
|
|
||||||
}
|
|
||||||
|
|
||||||
// snap to grid
|
|
||||||
primaryWidth = calculateSize(primaryWidth, 32, 13, 160)
|
|
||||||
secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160)
|
|
||||||
|
|
||||||
$('.primary').css('width', primaryWidth)
|
|
||||||
$('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1)
|
|
||||||
|
|
||||||
if (secondaryWidth > 0) {
|
|
||||||
$('#content').css('left', primaryWidth + secondaryWidth + 2)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#content').css('left', primaryWidth + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(window).ready(resizeSidebars)
|
|
||||||
$(window).ready(setCurrentVarLink)
|
|
||||||
$(window).ready(function() { persistScrollPosition('.primary')})
|
|
||||||
$(window).ready(function() {
|
|
||||||
$('#content').scroll(setCurrentVarLink)
|
|
||||||
$(window).resize(setCurrentVarLink)
|
|
||||||
})
|
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
|
||||||
"">
|
|
||||||
<html><head><meta charset="UTF-8" /><title>sparse-array.extract documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Sparse-array</span> <span class="project-version">0.3.0</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to Sparse-array</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>sparse-array</span></div></div></li><li class="depth-2 branch"><a href="sparse-array.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 current"><a href="sparse-array.extract.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>extract</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="sparse-array.extract.html#var-extract"><div class="inner"><span>extract</span></div></a></li><li class="depth-1"><a href="sparse-array.extract.html#var-extract-from-dense"><div class="inner"><span>extract-from-dense</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">sparse-array.extract</h1><div class="doc"><div class="markdown"><p>Extracting interesting data from sparse arrays.</p></div></div><div class="public anchor" id="var-extract"><h3>extract</h3><div class="usage"><code>(extract array function)</code></div><div class="doc"><div class="markdown"><p>Return a sparse subset of this <code>array</code> - which may be either sparse or dense - comprising all those cells for which this <code>function</code> returns a ‘truthy’ value.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/sparse-array/blob/master/src/sparse_array/extract.clj#L56">view source</a></div></div><div class="public anchor" id="var-extract-from-dense"><h3>extract-from-dense</h3><div class="usage"><code>(extract-from-dense array function)</code><code>(extract-from-dense array function axes)</code></div><div class="doc"><div class="markdown"><p>Return a subset of this dense <code>array</code> comprising all those cells for which this <code>function</code> returns a ‘truthy’ value. Use these <code>axes</code> if provided.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/sparse-array/blob/master/src/sparse_array/extract.clj#L34">view source</a></div></div></div></body></html>
|
|
|
@ -1,14 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<title>Sparse Array: Documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="codox/css/default.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Sparse Array: Documentation</h1>
|
|
||||||
<ul>
|
|
||||||
<li><a href="codox/index.html">Primary documentaion</a></li>
|
|
||||||
<li><a href="cloverage/index.html">Test coverage</a></li>
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
22
project.clj
22
project.clj
|
@ -1,18 +1,11 @@
|
||||||
(defproject sparse-array "0.3.0"
|
(defproject sparse-array "0.1.0"
|
||||||
:aot :all
|
|
||||||
:cloverage {:output "docs/cloverage"}
|
|
||||||
:codox {:metadata {:doc "**TODO**: write docs"
|
|
||||||
:doc/format :markdown}
|
|
||||||
:output-path "docs/codox"
|
|
||||||
:source-uri "https://github.com/simon-brooke/sparse-array/blob/master/{filepath}#L{line}"}
|
|
||||||
|
|
||||||
:dependencies [[org.clojure/clojure "1.11.1"]]
|
|
||||||
|
|
||||||
:description "A Clojure library designed to manipulate sparse *arrays* - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you're better to use a *sparse matrix* library, for example [clojure.core.matrix](https://mikera.github.io/core.matrix/)."
|
:description "A Clojure library designed to manipulate sparse *arrays* - multi-dimensional spaces accessed by indices, but containing arbitrary values rather than just numbers. For sparse spaces which contain numbers only, you're better to use a *sparse matrix* library, for example [clojure.core.matrix](https://mikera.github.io/core.matrix/)."
|
||||||
|
:url "http://example.com/FIXME"
|
||||||
:license {:name "Eclipse Public License"
|
:license {:name "Eclipse Public License"
|
||||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||||
:plugins [[lein-cloverage "1.2.2"]
|
:dependencies [[org.clojure/clojure "1.8.0"]]
|
||||||
[lein-codox "0.10.7"]
|
|
||||||
|
:plugins [[lein-codox "0.10.4"]
|
||||||
[lein-release "1.0.5"]]
|
[lein-release "1.0.5"]]
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,8 +21,7 @@
|
||||||
;; ["vcs" "tag"] -- not working, problems with secret key
|
;; ["vcs" "tag"] -- not working, problems with secret key
|
||||||
["uberjar"]
|
["uberjar"]
|
||||||
["install"]
|
["install"]
|
||||||
;; ["deploy" "clojars"] -- also not working now. Security tightened at Clojars?
|
["deploy" "clojars"]
|
||||||
["change" "version" "leiningen.release/bump-version"]
|
["change" "version" "leiningen.release/bump-version"]
|
||||||
["vcs" "commit"]]
|
["vcs" "commit"]])
|
||||||
:url "https://simon-brooke.github.io/sparse-array/docs/")
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
(ns sparse-array.core
|
(ns sparse-array.core)
|
||||||
"Operations on sparse arrays.")
|
|
||||||
|
|
||||||
(declare put get)
|
(declare put get)
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@
|
||||||
"Make a sparse array with these `dimensions`. Every member of `dimensions`
|
"Make a sparse array with these `dimensions`. Every member of `dimensions`
|
||||||
must be a keyword; otherwise, `nil` will be returned."
|
must be a keyword; otherwise, `nil` will be returned."
|
||||||
[& dimensions]
|
[& dimensions]
|
||||||
(when
|
(if
|
||||||
(and (pos? (count dimensions))
|
(and (pos? (count dimensions))
|
||||||
(every? keyword? dimensions))
|
(every? keyword? dimensions))
|
||||||
{:dimensions (count dimensions)
|
{:dimensions (count dimensions)
|
||||||
|
@ -43,10 +42,6 @@
|
||||||
(defn sparse-array?
|
(defn sparse-array?
|
||||||
"`true` if `x` is a sparse array conforming to the conventions established
|
"`true` if `x` is a sparse array conforming to the conventions established
|
||||||
by this library, else `false`."
|
by this library, else `false`."
|
||||||
;; TODO: sparse-array? should not throw exceptions even when
|
|
||||||
;; *safe-sparse-operations* is true, since we may use to test
|
|
||||||
;; whether an object is a sparse array. The place to throw the exceptions
|
|
||||||
;; (if required) is after it has failed.
|
|
||||||
([x]
|
([x]
|
||||||
(apply
|
(apply
|
||||||
sparse-array?
|
sparse-array?
|
||||||
|
@ -54,7 +49,7 @@
|
||||||
x
|
x
|
||||||
(cons
|
(cons
|
||||||
(:coord x)
|
(:coord x)
|
||||||
(when
|
(if
|
||||||
(coll? (:content x))
|
(coll? (:content x))
|
||||||
(:content x))))))
|
(:content x))))))
|
||||||
([x & axes]
|
([x & axes]
|
||||||
|
@ -134,7 +129,9 @@
|
||||||
:coordinates coordinates
|
:coordinates coordinates
|
||||||
:invalid (remove #(and (pos? %) (integer? %)) coordinates)}))
|
:invalid (remove #(and (pos? %) (integer? %)) coordinates)}))
|
||||||
:else
|
:else
|
||||||
(unsafe-put array value coordinates)))
|
(unsafe-put array value coordinates)
|
||||||
|
value
|
||||||
|
*safe-sparse-operations*))
|
||||||
|
|
||||||
(defn- unsafe-get
|
(defn- unsafe-get
|
||||||
;; TODO: I am CERTAIN there is a more elegant solution to this.
|
;; TODO: I am CERTAIN there is a more elegant solution to this.
|
||||||
|
@ -174,30 +171,6 @@
|
||||||
:else
|
:else
|
||||||
(unsafe-get array coordinates)))
|
(unsafe-get array coordinates)))
|
||||||
|
|
||||||
(defn dense-dimensions
|
|
||||||
"How many usable dimensions (represented as vectors) does the dense array
|
|
||||||
`x` have?"
|
|
||||||
[x]
|
|
||||||
(if
|
|
||||||
(vector? x)
|
|
||||||
(if
|
|
||||||
(every? vector? x)
|
|
||||||
(inc (apply min (map dense-dimensions x)))
|
|
||||||
;; `min` is right here, not `max`, because otherwise
|
|
||||||
;; we will get malformed arrays. Be liberal with what you
|
|
||||||
;; consume, conservative with what you return!
|
|
||||||
1)
|
|
||||||
0))
|
|
||||||
|
|
||||||
(defn dense-array?
|
|
||||||
"Basically, any vector can be considered as a dense array of one dimension.
|
|
||||||
If we're seeking a dense array of more than one dimension, the number of
|
|
||||||
dimensions should be specified as `d`."
|
|
||||||
([x]
|
|
||||||
(vector? x))
|
|
||||||
([x d]
|
|
||||||
(and (vector? x) (< d (dense-dimensions x)))))
|
|
||||||
|
|
||||||
(defn merge-sparse-arrays
|
(defn merge-sparse-arrays
|
||||||
"Return a sparse array taking values from sparse arrays `a1` and `a2`,
|
"Return a sparse array taking values from sparse arrays `a1` and `a2`,
|
||||||
but preferring values from `a2` where there is a conflict. `a1` and `a2`
|
but preferring values from `a2` where there is a conflict. `a1` and `a2`
|
||||||
|
@ -225,75 +198,40 @@
|
||||||
(keys a1)
|
(keys a1)
|
||||||
(keys a2))))))))
|
(keys a2))))))))
|
||||||
|
|
||||||
(defn merge-dense-with-sparse
|
(defn dense-dimensions
|
||||||
"Merge this dense array `d` with this sparse array `s`, returning a new
|
"How many usable dimensions (represented as vectors) does the dense array
|
||||||
dense array with the same arity as `d`, preferring values from `s` where
|
`x` have?"
|
||||||
there is conflict"
|
[x]
|
||||||
[d s]
|
(if
|
||||||
(apply
|
(vector? x)
|
||||||
vector
|
(if
|
||||||
(map
|
(every? vector? x)
|
||||||
#(cond
|
(inc (apply min (map dense-dimensions x)))
|
||||||
(= :data (:content s))
|
;; `min` is right here, not `max`, because otherwise
|
||||||
(or (s %2) %1)
|
;; we will get malformed arrays. Be liberal with what you
|
||||||
(nil? (s %2))
|
;; consume, conservative with what you return!
|
||||||
%1
|
1)
|
||||||
:else
|
0))
|
||||||
(merge-dense-with-sparse %1 (s %2)))
|
|
||||||
d
|
|
||||||
(range))))
|
|
||||||
|
|
||||||
(defn merge-arrays
|
|
||||||
"Merge two arrays `a1`, `a2`, which may be either dense or sparse but which
|
|
||||||
should have the same number of axes and compatible dimensions, and return a
|
|
||||||
new dense array preferring values from `a2`."
|
|
||||||
[a1 a2]
|
|
||||||
(cond
|
|
||||||
(dense-array? a2)
|
|
||||||
a2 ;; if a2 is dense, no values from a1 will be returned
|
|
||||||
(sparse-array? a1)
|
|
||||||
(cond
|
|
||||||
(sparse-array? a2)
|
|
||||||
(merge-sparse-arrays a1 a2)
|
|
||||||
*safe-sparse-operations*
|
|
||||||
(throw
|
|
||||||
(ex-info
|
|
||||||
"Object passed as array is neither dense not sparse"
|
|
||||||
{:array a2})))
|
|
||||||
(dense-array? a1)
|
|
||||||
(cond
|
|
||||||
(sparse-array? a2)
|
|
||||||
(merge-dense-with-sparse a1 a2)
|
|
||||||
*safe-sparse-operations*
|
|
||||||
(throw
|
|
||||||
(ex-info
|
|
||||||
"Object passed as array is neither dense not sparse"
|
|
||||||
{:array a2})))
|
|
||||||
*safe-sparse-operations*
|
|
||||||
(throw
|
|
||||||
(ex-info
|
|
||||||
"Object passed as array is neither dense not sparse"
|
|
||||||
{:array a1}))))
|
|
||||||
|
|
||||||
(defn dense-to-sparse
|
(defn dense-to-sparse
|
||||||
"Return a sparse array representing the content of the dense array `x`,
|
"Return a sparse array representing the content of the dense array `x`,
|
||||||
assuming these `axes` if specified. *NOTE THAT* if insufficient
|
assuming these `coordinates` if specified. *NOTE THAT* if insufficient
|
||||||
values of `axes` are specified, the resulting sparse array will
|
values of `coordinates` are specified, the resulting sparse array will
|
||||||
be malformed."
|
be malformed."
|
||||||
([x]
|
([x]
|
||||||
(dense-to-sparse x (map #(keyword (str "i" %)) (range))))
|
(dense-to-sparse x (map #(keyword (str "i" %)) (range))))
|
||||||
([x axes]
|
([x coordinates]
|
||||||
(let
|
(let
|
||||||
[dimensions (dense-dimensions x)]
|
[dimensions (dense-dimensions x)]
|
||||||
(reduce
|
(reduce
|
||||||
merge
|
merge
|
||||||
(apply make-sparse-array (take dimensions axes))
|
(apply make-sparse-array (take dimensions coordinates))
|
||||||
(map
|
(map
|
||||||
(fn [i v] (if (nil? v) nil (hash-map i v)))
|
(fn [i v] (if (nil? v) nil (hash-map i v)))
|
||||||
(range)
|
(range)
|
||||||
(if
|
(if
|
||||||
(> dimensions 1)
|
(> dimensions 1)
|
||||||
(map #(dense-to-sparse % (rest axes)) x)
|
(map #(dense-to-sparse % (rest coordinates)) x)
|
||||||
x))))))
|
x))))))
|
||||||
|
|
||||||
(defn arity
|
(defn arity
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
(ns sparse-array.extract
|
|
||||||
"Extracting interesting data from sparse arrays."
|
|
||||||
(:require [sparse-array.core :refer [*safe-sparse-operations*
|
|
||||||
dense-array? dense-dimensions
|
|
||||||
make-sparse-array sparse-array?]]))
|
|
||||||
|
|
||||||
;;; The whole point of working with sparse arrays is to work with interesting
|
|
||||||
;;; subsets of arrays most of which are uninteresting. To extract an
|
|
||||||
;;; interesting subset from an array, we're going to need an extract function.
|
|
||||||
|
|
||||||
(defn- extract-from-sparse
|
|
||||||
"Return a subset of this sparse `array` comprising all those cells for which
|
|
||||||
this `function` returns a 'truthy' value."
|
|
||||||
[array function]
|
|
||||||
(reduce
|
|
||||||
merge
|
|
||||||
(apply
|
|
||||||
make-sparse-array
|
|
||||||
(cons
|
|
||||||
(:coord array)
|
|
||||||
(when (coll? (:content array)) (:content array))))
|
|
||||||
(map
|
|
||||||
#(if
|
|
||||||
(= :data (:content array))
|
|
||||||
(when (function (array %)) {% (array %)})
|
|
||||||
(let [v (extract-from-sparse (array %) function)]
|
|
||||||
(if
|
|
||||||
(empty?
|
|
||||||
(filter integer? (keys v)))
|
|
||||||
nil
|
|
||||||
{% v})))
|
|
||||||
(filter integer? (keys array)))))
|
|
||||||
|
|
||||||
(defn extract-from-dense
|
|
||||||
"Return a subset of this dense `array` comprising all those cells for which
|
|
||||||
this `function` returns a 'truthy' value. Use these `axes` if provided."
|
|
||||||
([array function]
|
|
||||||
(extract-from-dense array function (map #(keyword (str "i" %)) (range))))
|
|
||||||
([array function axes]
|
|
||||||
(let
|
|
||||||
[dimensions (dense-dimensions array)]
|
|
||||||
(reduce
|
|
||||||
merge
|
|
||||||
(apply make-sparse-array (take dimensions axes))
|
|
||||||
(if
|
|
||||||
(= dimensions 1)
|
|
||||||
(map
|
|
||||||
(fn [i v] (when (function v) (hash-map i v)))
|
|
||||||
(range)
|
|
||||||
array)
|
|
||||||
(map
|
|
||||||
(fn [i v] (if (empty? (filter integer? (keys v))) nil (hash-map i v)))
|
|
||||||
(range)
|
|
||||||
(map #(extract-from-dense % function (rest axes)) array)))))))
|
|
||||||
|
|
||||||
(defn extract
|
|
||||||
"Return a sparse subset of this `array` - which may be either sparse or
|
|
||||||
dense - comprising all those cells for which this `function` returns a
|
|
||||||
'truthy' value."
|
|
||||||
[array function]
|
|
||||||
(cond
|
|
||||||
(sparse-array? array)
|
|
||||||
(extract-from-sparse array function)
|
|
||||||
(dense-array? array)
|
|
||||||
(extract-from-dense array function)
|
|
||||||
*safe-sparse-operations*
|
|
||||||
(throw
|
|
||||||
(ex-info
|
|
||||||
"Argument passed as `array` is neither sparse nor dense."
|
|
||||||
{:array array}))))
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
(ns sparse-array.core-test
|
(ns sparse-array.core-test
|
||||||
(:require [clojure.test :refer [deftest is testing]]
|
(:require [clojure.test :refer :all]
|
||||||
[sparse-array.core :refer [*safe-sparse-operations*
|
[sparse-array.core :refer :all]))
|
||||||
dense-dimensions dense-to-sparse
|
|
||||||
get make-sparse-array
|
|
||||||
merge-arrays put
|
|
||||||
sparse-array? sparse-to-dense]]))
|
|
||||||
|
|
||||||
(deftest creation-and-testing
|
(deftest creation-and-testing
|
||||||
(testing "Creation and testing."
|
(testing "Creation and testing."
|
||||||
|
@ -176,30 +172,20 @@
|
||||||
(get (make-sparse-array :x :y :z) 3 4 5 6))))))
|
(get (make-sparse-array :x :y :z) 3 4 5 6))))))
|
||||||
|
|
||||||
(deftest merge-test
|
(deftest merge-test
|
||||||
(testing "merge, sparse arrays, one dimension"
|
(testing "merge, one dimension"
|
||||||
(let
|
(let
|
||||||
[merged (merge-arrays
|
[merged (merge-sparse-arrays
|
||||||
(put (make-sparse-array :x) "hello" 3)
|
(put (make-sparse-array :x) "hello" 3)
|
||||||
(put (make-sparse-array :x) "goodbye" 4))]
|
(put (make-sparse-array :x) "goodbye" 4))]
|
||||||
(is (= "hello" (get merged 3)))
|
(is (= "hello" (get merged 3)))
|
||||||
(is (= "goodbye" (get merged 4)))))
|
(is (= "goodbye" (get merged 4)))))
|
||||||
(testing "merge, sparse arrays, two dimensions"
|
(testing "merge, two dimensions"
|
||||||
(let
|
(let
|
||||||
[merged (merge-arrays
|
[merged (merge-sparse-arrays
|
||||||
(put (make-sparse-array :x :y) "hello" 3 4)
|
(put (make-sparse-array :x :y) "hello" 3 4)
|
||||||
(put (make-sparse-array :x :y) "goodbye" 4 3))]
|
(put (make-sparse-array :x :y) "goodbye" 4 3))]
|
||||||
(is (= "hello" (get merged 3 4)))
|
(is (= "hello" (get merged 3 4)))
|
||||||
(is (= "goodbye" (get merged 4 3)))))
|
(is (= "goodbye" (get merged 4 3))))))
|
||||||
(testing "merge, dense with sparse, two dimensions")
|
|
||||||
(let [dense [[[nil nil nil][nil nil nil][nil nil nil]]
|
|
||||||
[[nil nil nil][nil nil nil][nil nil nil]]
|
|
||||||
[[nil nil nil][nil nil nil][nil nil nil]]]
|
|
||||||
sparse (put (put (make-sparse-array :x :y :z) "hello" 0 0 0) "goodbye" 2 2 2)
|
|
||||||
expected [[["hello" nil nil] [nil nil nil] [nil nil nil]]
|
|
||||||
[[nil nil nil] [nil nil nil] [nil nil nil]]
|
|
||||||
[[nil nil nil] [nil nil nil] [nil nil "goodbye"]]]
|
|
||||||
actual (merge-arrays dense sparse)]
|
|
||||||
(is (= actual expected))))
|
|
||||||
|
|
||||||
(deftest dense-to-sparse-tests
|
(deftest dense-to-sparse-tests
|
||||||
(testing "dense-to-sparse, one dimension"
|
(testing "dense-to-sparse, one dimension"
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
(ns sparse-array.extract-test
|
|
||||||
(:require [clojure.test :refer :all]
|
|
||||||
[sparse-array.core :refer [dense-to-sparse get]]
|
|
||||||
[sparse-array.extract :refer :all]))
|
|
||||||
|
|
||||||
|
|
||||||
(deftest sparse-tests
|
|
||||||
(testing "extraction from sparse array"
|
|
||||||
(let [dense [[[1 2 3][:one :two :three]["one" "two" "three"]]
|
|
||||||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
|
||||||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
|
||||||
sparse (dense-to-sparse dense '(:a :b :c))
|
|
||||||
integers (extract sparse integer?)
|
|
||||||
strings (extract sparse string?)
|
|
||||||
keywords (extract sparse keyword?)
|
|
||||||
threes (extract sparse #(if
|
|
||||||
(number? %)
|
|
||||||
(= % 3)
|
|
||||||
(= (name %) "three")))]
|
|
||||||
(map
|
|
||||||
#(let [expected nil
|
|
||||||
actual (get (extract sparse map?) %1 %2 %3)]
|
|
||||||
(is (= actual expected) "there are no cells of which `map?` is true"))
|
|
||||||
(range 3)
|
|
||||||
(range 3)
|
|
||||||
(range 3))
|
|
||||||
(let [expected 1
|
|
||||||
actual (get integers 0 0 0)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get keywords 0 0 0)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get strings 0 0 0)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get threes 0 0 0)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected 3
|
|
||||||
actual (get integers 0 0 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get keywords 0 0 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected :three
|
|
||||||
actual (get keywords 0 1 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get strings 0 0 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected 3
|
|
||||||
actual (get threes 0 0 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected :three
|
|
||||||
actual (get threes 0 1 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected "three"
|
|
||||||
actual (get threes 0 2 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
)))
|
|
||||||
|
|
||||||
(deftest dense-tests
|
|
||||||
(testing "extraction from dense array"
|
|
||||||
(let [dense [[[1 2 3][:one :two :three]["one" "two" "three"]]
|
|
||||||
[[1 :two "three"]["one" 2 :three][:one "two" 3]]
|
|
||||||
[[1.0 2.0 3.0][2/2 4/2 6/2]["I" "II" "III"]]]
|
|
||||||
integers (extract dense integer?)
|
|
||||||
strings (extract dense string?)
|
|
||||||
keywords (extract dense keyword?)
|
|
||||||
threes (extract dense #(if
|
|
||||||
(number? %)
|
|
||||||
(= % 3)
|
|
||||||
(= (name %) "three")))]
|
|
||||||
(map
|
|
||||||
#(let [expected nil
|
|
||||||
actual (get (extract dense map?) %1 %2 %3)]
|
|
||||||
(is (= actual expected) "there are no cells of which `map?` is true"))
|
|
||||||
(range 3)
|
|
||||||
(range 3)
|
|
||||||
(range 3))
|
|
||||||
(let [expected 1
|
|
||||||
actual (get integers 0 0 0)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get keywords 0 0 0)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get strings 0 0 0)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get threes 0 0 0)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected 3
|
|
||||||
actual (get integers 0 0 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get keywords 0 0 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected :three
|
|
||||||
actual (get keywords 0 1 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected nil
|
|
||||||
actual (get strings 0 0 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected 3
|
|
||||||
actual (get threes 0 0 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected :three
|
|
||||||
actual (get threes 0 1 2)]
|
|
||||||
(is (= actual expected)))
|
|
||||||
(let [expected "three"
|
|
||||||
actual (get threes 0 2 2)]
|
|
||||||
(is (= actual expected))))))
|
|
Loading…
Reference in a new issue