Compare commits

..

No commits in common. "master" and "sparse-array-0.2.1" have entirely different histories.

22 changed files with 51 additions and 2744 deletions

View file

@ -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
View file

@ -9,5 +9,3 @@ pom.xml.asc
/.nrepl-port
.hgignore
.hg/
.clj-kondo/
.lsp/

View file

@ -1,15 +1,14 @@
# 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.
Available from Clojars: [![Clojars Project](https://img.shields.io/clojars/v/sparse-array.svg)](https://clojars.org/sparse-array)
Tests currently [![CircleCI](https://circleci.com/gh/simon-brooke/sparse-array.svg?style=svg)](https://circleci.com/gh/simon-brooke/sparse-array)
[![Clojars Project](https://img.shields.io/clojars/v/sparse-array.svg)](https://clojars.org/sparse-array)
[![CircleCI](https://circleci.com/gh/simon-brooke/sparse-array.svg?style=svg)](https://circleci.com/gh/simon-brooke/sparse-array)
## Conventions
## Conventions:
### Sparse arrays

View file

@ -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/).
Arbitrary numbers of dimensions are supported, up to limits imposed by the JVM stack.
Available from Clojars: [![Clojars Project](https://img.shields.io/clojars/v/sparse-array.svg)](https://clojars.org/sparse-array)
Tests currently [![CircleCI](https://circleci.com/gh/simon-brooke/sparse-array.svg?style=svg)](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.
TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)

View file

@ -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;
}

View file

@ -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

View file

@ -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&nbsp;&nbsp;(ns&nbsp;sparse-array.extract
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
002&nbsp;&nbsp;&nbsp;&nbsp;&quot;Extracting&nbsp;interesting&nbsp;data&nbsp;from&nbsp;sparse&nbsp;arrays.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
003&nbsp;&nbsp;&nbsp;&nbsp;(:require&nbsp;[sparse-array.core&nbsp;:refer&nbsp;[*safe-sparse-operations*
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dense-array?&nbsp;dense-dimensions
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
005&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;make-sparse-array&nbsp;sparse-array?]]))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
006&nbsp;&nbsp;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
007&nbsp;&nbsp;;;;&nbsp;The&nbsp;whole&nbsp;point&nbsp;of&nbsp;working&nbsp;with&nbsp;sparse&nbsp;arrays&nbsp;is&nbsp;to&nbsp;work&nbsp;with&nbsp;interesting
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
008&nbsp;&nbsp;;;;&nbsp;subsets&nbsp;of&nbsp;arrays&nbsp;most&nbsp;of&nbsp;which&nbsp;are&nbsp;uninteresting.&nbsp;To&nbsp;extract&nbsp;an
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
009&nbsp;&nbsp;;;;&nbsp;interesting&nbsp;subset&nbsp;from&nbsp;an&nbsp;array,&nbsp;we&#x27;re&nbsp;going&nbsp;to&nbsp;need&nbsp;an&nbsp;extract&nbsp;function.
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
010&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
011&nbsp;&nbsp;(defn-&nbsp;extract-from-sparse
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
012&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;subset&nbsp;of&nbsp;this&nbsp;sparse&nbsp;`array`&nbsp;comprising&nbsp;all&nbsp;those&nbsp;cells&nbsp;for&nbsp;which
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
013&nbsp;&nbsp;&nbsp;&nbsp;this&nbsp;`function`&nbsp;returns&nbsp;a&nbsp;&#x27;truthy&#x27;&nbsp;value.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
014&nbsp;&nbsp;&nbsp;&nbsp;[array&nbsp;function]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
015&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merge
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
017&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(apply
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;make-sparse-array
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
019&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(cons
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(:coord&nbsp;array)
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
021&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(when&nbsp;(coll?&nbsp;(:content&nbsp;array))&nbsp;(:content&nbsp;array))))
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#(if
</span><br/>
<span class="covered" title="5 out of 5 forms covered">
024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(=&nbsp;:data&nbsp;(:content&nbsp;array))
</span><br/>
<span class="covered" title="12 out of 12 forms covered">
025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(when&nbsp;(function&nbsp;(array&nbsp;%))&nbsp;{%&nbsp;(array&nbsp;%)})
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let&nbsp;[v&nbsp;(extract-from-sparse&nbsp;(array&nbsp;%)&nbsp;function)]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(empty?
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter&nbsp;integer?&nbsp;(keys&nbsp;v)))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nil
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{%&nbsp;v})))
</span><br/>
<span class="covered" title="6 out of 6 forms covered">
032&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(filter&nbsp;integer?&nbsp;(keys&nbsp;array)))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
033&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
034&nbsp;&nbsp;(defn&nbsp;extract-from-dense
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
035&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;subset&nbsp;of&nbsp;this&nbsp;dense&nbsp;`array`&nbsp;comprising&nbsp;all&nbsp;those&nbsp;cells&nbsp;for&nbsp;which
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
036&nbsp;&nbsp;&nbsp;&nbsp;this&nbsp;`function`&nbsp;returns&nbsp;a&nbsp;&#x27;truthy&#x27;&nbsp;value.&nbsp;Use&nbsp;these&nbsp;`axes`&nbsp;if&nbsp;provided.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
037&nbsp;&nbsp;&nbsp;&nbsp;([array&nbsp;function]
</span><br/>
<span class="covered" title="15 out of 15 forms covered">
038&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(extract-from-dense&nbsp;array&nbsp;function&nbsp;(map&nbsp;#(keyword&nbsp;(str&nbsp;&quot;i&quot;&nbsp;%))&nbsp;(range))))
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
039&nbsp;&nbsp;&nbsp;&nbsp;([array&nbsp;function&nbsp;axes]
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
040&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(let
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
041&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[dimensions&nbsp;(dense-dimensions&nbsp;array)]
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
042&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(reduce
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
043&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;merge
</span><br/>
<span class="covered" title="7 out of 7 forms covered">
044&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(apply&nbsp;make-sparse-array&nbsp;(take&nbsp;dimensions&nbsp;axes))
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
045&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(if
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
046&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(=&nbsp;dimensions&nbsp;1)
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
047&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="covered" title="10 out of 10 forms covered">
048&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(fn&nbsp;[i&nbsp;v]&nbsp;(when&nbsp;(function&nbsp;v)&nbsp;(hash-map&nbsp;i&nbsp;v)))
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
049&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(range)
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
050&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;array)
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
051&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map
</span><br/>
<span class="covered" title="15 out of 15 forms covered">
052&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(fn&nbsp;[i&nbsp;v]&nbsp;(if&nbsp;(empty?&nbsp;(filter&nbsp;integer?&nbsp;(keys&nbsp;v)))&nbsp;nil&nbsp;(hash-map&nbsp;i&nbsp;v)))
</span><br/>
<span class="covered" title="2 out of 2 forms covered">
053&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(range)
</span><br/>
<span class="covered" title="11 out of 11 forms covered">
054&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(map&nbsp;#(extract-from-dense&nbsp;%&nbsp;function&nbsp;(rest&nbsp;axes))&nbsp;array)))))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
055&nbsp;&nbsp;
</span><br/>
<span class="covered" title="1 out of 1 forms covered">
056&nbsp;&nbsp;(defn&nbsp;extract
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
057&nbsp;&nbsp;&nbsp;&nbsp;&quot;Return&nbsp;a&nbsp;sparse&nbsp;subset&nbsp;of&nbsp;this&nbsp;`array`&nbsp;-&nbsp;which&nbsp;may&nbsp;be&nbsp;either&nbsp;sparse&nbsp;or
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
058&nbsp;&nbsp;&nbsp;&nbsp;dense&nbsp;-&nbsp;comprising&nbsp;all&nbsp;those&nbsp;cells&nbsp;for&nbsp;which&nbsp;this&nbsp;`function`&nbsp;returns&nbsp;a
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
059&nbsp;&nbsp;&nbsp;&nbsp;&#x27;truthy&#x27;&nbsp;value.&quot;
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
060&nbsp;&nbsp;&nbsp;&nbsp;[array&nbsp;function]
</span><br/>
<span class="partial" title="2 out of 4 forms covered">
061&nbsp;&nbsp;&nbsp;&nbsp;(cond
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
062&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(sparse-array?&nbsp;array)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
063&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(extract-from-sparse&nbsp;array&nbsp;function)
</span><br/>
<span class="covered" title="3 out of 3 forms covered">
064&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(dense-array?&nbsp;array)
</span><br/>
<span class="covered" title="4 out of 4 forms covered">
065&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(extract-from-dense&nbsp;array&nbsp;function)
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
066&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*safe-sparse-operations*
</span><br/>
<span class="not-covered" title="0 out of 1 forms covered">
067&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(throw
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
068&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(ex-info
</span><br/>
<span class="not-tracked" title="0 out of 0 forms covered">
069&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;Argument&nbsp;passed&nbsp;as&nbsp;`array`&nbsp;is&nbsp;neither&nbsp;sparse&nbsp;nor&nbsp;dense.&quot;
</span><br/>
<span class="not-covered" title="0 out of 3 forms covered">
070&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{:array&nbsp;array}))))
</span><br/>
<span class="blank" title="0 out of 0 forms covered">
071&nbsp;&nbsp;
</span><br/>
</body>
</html>

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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>

View file

@ -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, youre 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
=&gt; {: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 ([&amp; 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)
=&gt; {: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 &amp; 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)
=&gt; {: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 &amp; 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)))
=&gt; {: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"])
=&gt; {: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))
=&gt; [[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")))
=&gt; {: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))
=&gt; {: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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -1,18 +1,11 @@
(defproject sparse-array "0.3.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"]]
(defproject sparse-array "0.2.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/)."
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:plugins [[lein-cloverage "1.2.2"]
[lein-codox "0.10.7"]
:dependencies [[org.clojure/clojure "1.8.0"]]
:plugins [[lein-codox "0.10.4"]
[lein-release "1.0.5"]]
@ -28,8 +21,7 @@
;; ["vcs" "tag"] -- not working, problems with secret key
["uberjar"]
["install"]
;; ["deploy" "clojars"] -- also not working now. Security tightened at Clojars?
["deploy" "clojars"]
["change" "version" "leiningen.release/bump-version"]
["vcs" "commit"]]
:url "https://simon-brooke.github.io/sparse-array/docs/")
["vcs" "commit"]])

View file

@ -1,5 +1,4 @@
(ns sparse-array.core
"Operations on sparse arrays.")
(ns sparse-array.core)
(declare put get)
@ -18,7 +17,7 @@
"Make a sparse array with these `dimensions`. Every member of `dimensions`
must be a keyword; otherwise, `nil` will be returned."
[& dimensions]
(when
(if
(and (pos? (count dimensions))
(every? keyword? dimensions))
{:dimensions (count dimensions)
@ -43,10 +42,6 @@
(defn sparse-array?
"`true` if `x` is a sparse array conforming to the conventions established
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]
(apply
sparse-array?
@ -54,7 +49,7 @@
x
(cons
(:coord x)
(when
(if
(coll? (:content x))
(:content x))))))
([x & axes]
@ -134,7 +129,9 @@
:coordinates coordinates
:invalid (remove #(and (pos? %) (integer? %)) coordinates)}))
:else
(unsafe-put array value coordinates)))
(unsafe-put array value coordinates)
value
*safe-sparse-operations*))
(defn- unsafe-get
;; TODO: I am CERTAIN there is a more elegant solution to this.
@ -174,30 +171,6 @@
:else
(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
"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`
@ -225,55 +198,29 @@
(keys a1)
(keys a2))))))))
(defn merge-dense-with-sparse
"Merge this dense array `d` with this sparse array `s`, returning a new
dense array with the same arity as `d`, preferring values from `s` where
there is conflict"
[d s]
(apply
vector
(map
#(cond
(= :data (:content s))
(or (s %2) %1)
(nil? (s %2))
%1
:else
(merge-dense-with-sparse %1 (s %2)))
d
(range))))
(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 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-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 dense-to-sparse
"Return a sparse array representing the content of the dense array `x`,

View file

@ -1,8 +1,5 @@
(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?]]))
(:require [sparse-array.core :refer :all]))
;;; The whole point of working with sparse arrays is to work with interesting
;;; subsets of arrays most of which are uninteresting. To extract an
@ -18,11 +15,11 @@
make-sparse-array
(cons
(:coord array)
(when (coll? (:content array)) (:content array))))
(if (coll? (:content array)) (:content array))))
(map
#(if
(= :data (:content array))
(when (function (array %)) {% (array %)})
(if (function (array %)) {% (array %)})
(let [v (extract-from-sparse (array %) function)]
(if
(empty?
@ -45,7 +42,7 @@
(if
(= dimensions 1)
(map
(fn [i v] (when (function v) (hash-map i v)))
(fn [i v] (if (function v) (hash-map i v)))
(range)
array)
(map

View file

@ -1,10 +1,6 @@
(ns sparse-array.core-test
(:require [clojure.test :refer [deftest is testing]]
[sparse-array.core :refer [*safe-sparse-operations*
dense-dimensions dense-to-sparse
get make-sparse-array
merge-arrays put
sparse-array? sparse-to-dense]]))
(:require [clojure.test :refer :all]
[sparse-array.core :refer :all]))
(deftest creation-and-testing
(testing "Creation and testing."
@ -176,30 +172,20 @@
(get (make-sparse-array :x :y :z) 3 4 5 6))))))
(deftest merge-test
(testing "merge, sparse arrays, one dimension"
(testing "merge, one dimension"
(let
[merged (merge-arrays
[merged (merge-sparse-arrays
(put (make-sparse-array :x) "hello" 3)
(put (make-sparse-array :x) "goodbye" 4))]
(is (= "hello" (get merged 3)))
(is (= "goodbye" (get merged 4)))))
(testing "merge, sparse arrays, two dimensions"
(testing "merge, two dimensions"
(let
[merged (merge-arrays
[merged (merge-sparse-arrays
(put (make-sparse-array :x :y) "hello" 3 4)
(put (make-sparse-array :x :y) "goodbye" 4 3))]
(is (= "hello" (get merged 3 4)))
(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))))
(is (= "goodbye" (get merged 4 3))))))
(deftest dense-to-sparse-tests
(testing "dense-to-sparse, one dimension"