rustnx 0.1.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. rustnx-0.1.0a1/.github/workflows/ci.yml +59 -0
  2. rustnx-0.1.0a1/.github/workflows/wheels.yml +128 -0
  3. rustnx-0.1.0a1/.gitignore +8 -0
  4. rustnx-0.1.0a1/CHANGELOG.md +81 -0
  5. rustnx-0.1.0a1/Cargo.lock +184 -0
  6. rustnx-0.1.0a1/Cargo.toml +23 -0
  7. rustnx-0.1.0a1/LICENSE +29 -0
  8. rustnx-0.1.0a1/PKG-INFO +299 -0
  9. rustnx-0.1.0a1/README.md +273 -0
  10. rustnx-0.1.0a1/benchmarks/bench.py +102 -0
  11. rustnx-0.1.0a1/benchmarks/compare.py +106 -0
  12. rustnx-0.1.0a1/pyproject.toml +43 -0
  13. rustnx-0.1.0a1/python/rustnx/__init__.py +45 -0
  14. rustnx-0.1.0a1/python/rustnx/_info.py +65 -0
  15. rustnx-0.1.0a1/python/rustnx/algorithms.py +1018 -0
  16. rustnx-0.1.0a1/python/rustnx/graph.py +350 -0
  17. rustnx-0.1.0a1/python/rustnx/interface.py +178 -0
  18. rustnx-0.1.0a1/python/rustnx/rx.py +904 -0
  19. rustnx-0.1.0a1/src/algorithms/centrality.rs +353 -0
  20. rustnx-0.1.0a1/src/algorithms/cluster.rs +82 -0
  21. rustnx-0.1.0a1/src/algorithms/directed.rs +150 -0
  22. rustnx-0.1.0a1/src/algorithms/distance.rs +205 -0
  23. rustnx-0.1.0a1/src/algorithms/link_analysis.rs +106 -0
  24. rustnx-0.1.0a1/src/algorithms/mod.rs +7 -0
  25. rustnx-0.1.0a1/src/algorithms/paths.rs +184 -0
  26. rustnx-0.1.0a1/src/algorithms/traversal.rs +178 -0
  27. rustnx-0.1.0a1/src/graph.rs +483 -0
  28. rustnx-0.1.0a1/src/lib.rs +650 -0
  29. rustnx-0.1.0a1/src/native.rs +351 -0
  30. rustnx-0.1.0a1/src/rx.rs +279 -0
  31. rustnx-0.1.0a1/src/serialize.rs +304 -0
  32. rustnx-0.1.0a1/tests/conftest.py +4 -0
  33. rustnx-0.1.0a1/tests/test_against_networkx.py +938 -0
  34. rustnx-0.1.0a1/tests/test_native.py +279 -0
  35. rustnx-0.1.0a1/tests/test_rx_api.py +283 -0
@@ -0,0 +1,59 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ # Weekly run against the latest NetworkX, to catch breaking releases
7
+ # (like 3.7's new closeness_centrality parameter) before users do.
8
+ schedule:
9
+ - cron: "17 6 * * 1" # Mondays 06:17 UTC
10
+ workflow_dispatch:
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ name: test (Python ${{ matrix.python-version }}, NetworkX ${{ matrix.networkx }})
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ # Oldest supported NetworkX, each release since, and the latest.
20
+ include:
21
+ - python-version: "3.10"
22
+ networkx: "3.4.*"
23
+ - python-version: "3.11"
24
+ networkx: "3.5.*"
25
+ - python-version: "3.13"
26
+ networkx: "latest"
27
+ steps:
28
+ - uses: actions/checkout@v7
29
+ - uses: actions/setup-python@v7
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+ - uses: dtolnay/rust-toolchain@stable
33
+ with:
34
+ components: clippy, rustfmt
35
+ - name: Rust lint
36
+ run: |
37
+ cargo fmt --check
38
+ cargo clippy --release -- -D warnings
39
+ - name: Build and install
40
+ run: |
41
+ python -m venv .venv
42
+ source .venv/bin/activate
43
+ if [ "${{ matrix.networkx }}" = latest ]; then
44
+ pip install maturin networkx pytest numpy scipy rustworkx
45
+ else
46
+ pip install maturin "networkx==${{ matrix.networkx }}" pytest numpy scipy rustworkx
47
+ fi
48
+ python -c "import networkx; print('NetworkX', networkx.__version__)"
49
+ maturin develop --release
50
+ - name: Compare against NetworkX
51
+ run: |
52
+ source .venv/bin/activate
53
+ pytest -q
54
+ - name: NetworkX test suite with rustnx backend
55
+ run: |
56
+ source .venv/bin/activate
57
+ cd /tmp
58
+ NETWORKX_TEST_BACKEND=rustnx NETWORKX_FALLBACK_TO_NX=True \
59
+ python -m pytest -q -p no:cacheprovider --pyargs networkx
@@ -0,0 +1,128 @@
1
+ name: Wheels
2
+
3
+ # Builds installable packages for every platform and tests each one on a
4
+ # real machine of that platform. Publishing to PyPI only happens when a
5
+ # version tag (e.g. v0.1.0a1) is pushed.
6
+
7
+ on:
8
+ push:
9
+ tags: ["v*"]
10
+ pull_request:
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ linux:
18
+ runs-on: ubuntu-latest
19
+ strategy:
20
+ matrix:
21
+ target: [x86_64, aarch64]
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+ - uses: PyO3/maturin-action@v1
25
+ with:
26
+ target: ${{ matrix.target }}
27
+ manylinux: auto
28
+ args: --release --out dist
29
+ - uses: actions/upload-artifact@v7
30
+ with:
31
+ name: wheels-linux-${{ matrix.target }}
32
+ path: dist
33
+
34
+ macos:
35
+ runs-on: macos-latest
36
+ strategy:
37
+ matrix:
38
+ target: [x86_64, aarch64]
39
+ steps:
40
+ - uses: actions/checkout@v7
41
+ - uses: PyO3/maturin-action@v1
42
+ with:
43
+ target: ${{ matrix.target }}
44
+ args: --release --out dist
45
+ - uses: actions/upload-artifact@v7
46
+ with:
47
+ name: wheels-macos-${{ matrix.target }}
48
+ path: dist
49
+
50
+ windows:
51
+ runs-on: windows-latest
52
+ steps:
53
+ - uses: actions/checkout@v7
54
+ - uses: actions/setup-python@v7
55
+ with:
56
+ python-version: "3.13"
57
+ - uses: PyO3/maturin-action@v1
58
+ with:
59
+ target: x64
60
+ args: --release --out dist
61
+ - uses: actions/upload-artifact@v7
62
+ with:
63
+ name: wheels-windows-x64
64
+ path: dist
65
+
66
+ sdist:
67
+ runs-on: ubuntu-latest
68
+ steps:
69
+ - uses: actions/checkout@v7
70
+ - uses: PyO3/maturin-action@v1
71
+ with:
72
+ command: sdist
73
+ args: --out dist
74
+ - uses: actions/upload-artifact@v7
75
+ with:
76
+ name: sdist
77
+ path: dist
78
+
79
+ # Install each wheel (not a local build) and run the test suite with it.
80
+ test:
81
+ needs: [linux, macos, windows]
82
+ strategy:
83
+ fail-fast: false
84
+ matrix:
85
+ include:
86
+ - os: ubuntu-latest
87
+ python-version: "3.10"
88
+ - os: ubuntu-24.04-arm
89
+ python-version: "3.13"
90
+ - os: macos-latest
91
+ python-version: "3.13"
92
+ - os: windows-latest
93
+ python-version: "3.13"
94
+ runs-on: ${{ matrix.os }}
95
+ defaults:
96
+ run:
97
+ shell: bash
98
+ steps:
99
+ - uses: actions/checkout@v7
100
+ - uses: actions/setup-python@v7
101
+ with:
102
+ python-version: ${{ matrix.python-version }}
103
+ - uses: actions/download-artifact@v8
104
+ with:
105
+ pattern: wheels-*
106
+ path: dist
107
+ merge-multiple: true
108
+ - name: Install the built wheel
109
+ run: |
110
+ python -m pip install networkx pytest numpy scipy rustworkx
111
+ python -m pip install --no-index --find-links dist rustnx
112
+ python -c "import rustnx, networkx; print('rustnx', rustnx.__version__, 'on NetworkX', networkx.__version__)"
113
+ - name: Run tests against the installed wheel
114
+ run: python -m pytest -q tests
115
+
116
+ publish:
117
+ if: startsWith(github.ref, 'refs/tags/v')
118
+ needs: [linux, macos, windows, sdist, test]
119
+ runs-on: ubuntu-latest
120
+ environment: pypi
121
+ permissions:
122
+ id-token: write # PyPI trusted publishing; no API token stored in the repo
123
+ steps:
124
+ - uses: actions/download-artifact@v8
125
+ with:
126
+ path: dist
127
+ merge-multiple: true
128
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ /target
2
+ /.venv
3
+ __pycache__/
4
+ *.so
5
+ *.pyd
6
+ *.egg-info/
7
+ /dist
8
+ .pytest_cache/
@@ -0,0 +1,81 @@
1
+ # Changelog
2
+
3
+ All notable changes to rustnx are listed here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
5
+ [Semantic Versioning](https://semver.org/). While rustnx is at 0.x, minor
6
+ versions may change behavior.
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0a1] - 2026-10-02
11
+
12
+ First public pre-release.
13
+
14
+ ### Added
15
+ - `rustnx.rx`, a rustworkx-compatible API (`PyGraph`, `PyDiGraph`, and
16
+ rustworkx-named algorithm functions) on the same Rust core. It matches
17
+ rustworkx's index reuse, multigraph rules, neighbor order and exceptions,
18
+ and is tested against rustworkx itself.
19
+ - Native graphs: `rustnx.Graph` and `rustnx.DiGraph` are built directly in
20
+ Rust from edge tuples, or from NumPy arrays with `from_arrays`. No
21
+ conversion is needed, they use about 5× less memory than NetworkX graphs,
22
+ and they give exactly the results of the equivalent `networkx.Graph`.
23
+ They're read-only, with a small NetworkX-like query API and
24
+ `to_networkx()`.
25
+ - `rustnx.enable()` sets NetworkX's backend priority to rustnx, falling back
26
+ to NetworkX for everything else.
27
+ - Distance measures: `eccentricity`, `diameter`, `radius`, `center`,
28
+ `periphery`, `average_shortest_path_length` and `wiener_index`. Unweighted
29
+ graphs use bit-parallel BFS (about 800× faster than NetworkX); weighted ones
30
+ use parallel Dijkstra (about 40×).
31
+ - `all_pairs_shortest_path_length` and `all_pairs_dijkstra_path_length`,
32
+ computed in parallel batches.
33
+ - Shortest paths that return the paths: `shortest_path`,
34
+ `shortest_path_length`, `single_source_shortest_path`,
35
+ `single_target_shortest_path`, `bidirectional_shortest_path`, `has_path`,
36
+ `dijkstra_path`, `dijkstra_path_length`, `single_source_dijkstra`,
37
+ `single_source_dijkstra_path`, `all_pairs_shortest_path`,
38
+ `all_pairs_dijkstra_path` and `all_pairs_dijkstra`. They return the same
39
+ paths as NetworkX, ties included.
40
+ - `descendants` and `ancestors`.
41
+ - `triangles`, `clustering`, `average_clustering` and `transitivity`
42
+ (unweighted), with results identical to NetworkX.
43
+ - `edge_betweenness_centrality` (unweighted and weighted, `k` sampling), run
44
+ in parallel.
45
+
46
+ - NetworkX backend (opt in with `nx.config.backend_priority = ["rustnx"]`)
47
+ with Rust implementations of:
48
+ - `betweenness_centrality` (unweighted and weighted, `normalized`,
49
+ `endpoints`, `k` sampling), run in parallel
50
+ - `closeness_centrality` (unweighted and `distance=`, `wf_improved`, `u=`),
51
+ run in parallel, with results identical to NetworkX
52
+ - `single_source_shortest_path_length` and
53
+ `single_source_dijkstra_path_length`, with `cutoff`
54
+ - `connected_components`, `number_connected_components`, `is_connected`
55
+ - `pagerank`, with every NetworkX option
56
+ - `strongly_connected_components` and `weakly_connected_components`, plus
57
+ their `number_*` and `is_*` variants
58
+ - `topological_sort`, `topological_generations`, `is_directed_acyclic_graph`
59
+ - Generators (`topological_sort`, the component functions) notice when the
60
+ graph changes during iteration. `topological_sort` then behaves exactly as
61
+ NetworkX does; the component generators raise `RuntimeError` instead of
62
+ returning stale results.
63
+ - Automatic fallback to NetworkX for anything unsupported: other functions,
64
+ multigraphs, callable or non-numeric weights, and parameters rustnx doesn't
65
+ implement.
66
+ - On small graphs (under 500 nodes), the linear-time functions stay in
67
+ NetworkX, because it's faster there.
68
+ - Converting a graph to rustnx is up to 3× faster. Directed graphs are read
69
+ once, not twice, and node lookups skip the Python dict for integer-labelled
70
+ graphs and for neighbor keys that are the same objects as the node keys.
71
+ - `closeness_centrality` on unweighted graphs runs 64 BFS searches at once
72
+ (bit-parallel BFS): about 10× faster than before, with identical results.
73
+ - `benchmarks/compare.py` compares rustnx with nx-rustworkx and
74
+ FrankenNetworkX, checking every result against NetworkX.
75
+ - Graphs that rustnx has worked on can be pickled and deep-copied (e.g. sent
76
+ to `multiprocessing` workers). Previously this failed, because NetworkX
77
+ caches rustnx's converted graph on the original graph.
78
+ - Supports NetworkX 3.4 through 3.7 and Python 3.10+.
79
+ - Prebuilt wheels for Linux (x86-64, ARM), macOS (Intel, Apple Silicon) and
80
+ Windows, each tested on its platform, published to PyPI by pushing a
81
+ version tag.
@@ -0,0 +1,184 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "crossbeam-deque"
7
+ version = "0.8.8"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a"
10
+ dependencies = [
11
+ "crossbeam-epoch",
12
+ "crossbeam-utils",
13
+ ]
14
+
15
+ [[package]]
16
+ name = "crossbeam-epoch"
17
+ version = "0.9.21"
18
+ source = "registry+https://github.com/rust-lang/crates.io-index"
19
+ checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d"
20
+ dependencies = [
21
+ "crossbeam-utils",
22
+ ]
23
+
24
+ [[package]]
25
+ name = "crossbeam-utils"
26
+ version = "0.8.23"
27
+ source = "registry+https://github.com/rust-lang/crates.io-index"
28
+ checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
29
+
30
+ [[package]]
31
+ name = "either"
32
+ version = "1.18.0"
33
+ source = "registry+https://github.com/rust-lang/crates.io-index"
34
+ checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
35
+
36
+ [[package]]
37
+ name = "heck"
38
+ version = "0.5.0"
39
+ source = "registry+https://github.com/rust-lang/crates.io-index"
40
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
41
+
42
+ [[package]]
43
+ name = "libc"
44
+ version = "0.2.189"
45
+ source = "registry+https://github.com/rust-lang/crates.io-index"
46
+ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
47
+
48
+ [[package]]
49
+ name = "once_cell"
50
+ version = "1.21.4"
51
+ source = "registry+https://github.com/rust-lang/crates.io-index"
52
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
53
+
54
+ [[package]]
55
+ name = "portable-atomic"
56
+ version = "1.15.0"
57
+ source = "registry+https://github.com/rust-lang/crates.io-index"
58
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
59
+
60
+ [[package]]
61
+ name = "proc-macro2"
62
+ version = "1.0.107"
63
+ source = "registry+https://github.com/rust-lang/crates.io-index"
64
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
65
+ dependencies = [
66
+ "unicode-ident",
67
+ ]
68
+
69
+ [[package]]
70
+ name = "pyo3"
71
+ version = "0.29.2"
72
+ source = "registry+https://github.com/rust-lang/crates.io-index"
73
+ checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
74
+ dependencies = [
75
+ "libc",
76
+ "once_cell",
77
+ "portable-atomic",
78
+ "pyo3-build-config",
79
+ "pyo3-ffi",
80
+ "pyo3-macros",
81
+ ]
82
+
83
+ [[package]]
84
+ name = "pyo3-build-config"
85
+ version = "0.29.2"
86
+ source = "registry+https://github.com/rust-lang/crates.io-index"
87
+ checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
88
+ dependencies = [
89
+ "target-lexicon",
90
+ ]
91
+
92
+ [[package]]
93
+ name = "pyo3-ffi"
94
+ version = "0.29.2"
95
+ source = "registry+https://github.com/rust-lang/crates.io-index"
96
+ checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
97
+ dependencies = [
98
+ "libc",
99
+ "pyo3-build-config",
100
+ ]
101
+
102
+ [[package]]
103
+ name = "pyo3-macros"
104
+ version = "0.29.2"
105
+ source = "registry+https://github.com/rust-lang/crates.io-index"
106
+ checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
107
+ dependencies = [
108
+ "proc-macro2",
109
+ "pyo3-macros-backend",
110
+ "quote",
111
+ "syn",
112
+ ]
113
+
114
+ [[package]]
115
+ name = "pyo3-macros-backend"
116
+ version = "0.29.2"
117
+ source = "registry+https://github.com/rust-lang/crates.io-index"
118
+ checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
119
+ dependencies = [
120
+ "heck",
121
+ "proc-macro2",
122
+ "quote",
123
+ "syn",
124
+ ]
125
+
126
+ [[package]]
127
+ name = "quote"
128
+ version = "1.0.47"
129
+ source = "registry+https://github.com/rust-lang/crates.io-index"
130
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
131
+ dependencies = [
132
+ "proc-macro2",
133
+ ]
134
+
135
+ [[package]]
136
+ name = "rayon"
137
+ version = "1.12.0"
138
+ source = "registry+https://github.com/rust-lang/crates.io-index"
139
+ checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
140
+ dependencies = [
141
+ "either",
142
+ "rayon-core",
143
+ ]
144
+
145
+ [[package]]
146
+ name = "rayon-core"
147
+ version = "1.13.0"
148
+ source = "registry+https://github.com/rust-lang/crates.io-index"
149
+ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
150
+ dependencies = [
151
+ "crossbeam-deque",
152
+ "crossbeam-utils",
153
+ ]
154
+
155
+ [[package]]
156
+ name = "rustnx"
157
+ version = "0.1.0-alpha.1"
158
+ dependencies = [
159
+ "pyo3",
160
+ "rayon",
161
+ ]
162
+
163
+ [[package]]
164
+ name = "syn"
165
+ version = "2.0.119"
166
+ source = "registry+https://github.com/rust-lang/crates.io-index"
167
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
168
+ dependencies = [
169
+ "proc-macro2",
170
+ "quote",
171
+ "unicode-ident",
172
+ ]
173
+
174
+ [[package]]
175
+ name = "target-lexicon"
176
+ version = "0.13.5"
177
+ source = "registry+https://github.com/rust-lang/crates.io-index"
178
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
179
+
180
+ [[package]]
181
+ name = "unicode-ident"
182
+ version = "1.0.26"
183
+ source = "registry+https://github.com/rust-lang/crates.io-index"
184
+ checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
@@ -0,0 +1,23 @@
1
+ [package]
2
+ name = "rustnx"
3
+ version = "0.1.0-alpha.1"
4
+ edition = "2021"
5
+ license = "BSD-3-Clause"
6
+ authors = ["Fabuseless"]
7
+ description = "Rust-accelerated backend for NetworkX"
8
+ repository = "https://github.com/fabuseless/rustnx"
9
+ homepage = "https://github.com/fabuseless/rustnx"
10
+ readme = "README.md"
11
+ publish = false
12
+
13
+ [lib]
14
+ name = "_core"
15
+ crate-type = ["cdylib"]
16
+
17
+ [dependencies]
18
+ pyo3 = { version = "0.29", features = ["abi3-py310"] }
19
+ rayon = "1"
20
+
21
+ [profile.release]
22
+ lto = "fat"
23
+ codegen-units = 1
rustnx-0.1.0a1/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Fabuseless and the rustnx contributors
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.