interpn 0.2.0__tar.gz → 0.2.2__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 (51) hide show
  1. {interpn-0.2.0 → interpn-0.2.2}/.github/workflows/CI.yml +3 -3
  2. {interpn-0.2.0 → interpn-0.2.2}/.github/workflows/test.yml +1 -1
  3. interpn-0.2.2/CHANGELOG.md +9 -0
  4. {interpn-0.2.0 → interpn-0.2.2}/Cargo.lock +23 -15
  5. {interpn-0.2.0 → interpn-0.2.2}/Cargo.toml +4 -4
  6. {interpn-0.2.0 → interpn-0.2.2}/LICENSE-MIT +1 -1
  7. {interpn-0.2.0 → interpn-0.2.2}/PKG-INFO +50 -12
  8. {interpn-0.2.0 → interpn-0.2.2}/README.md +44 -7
  9. interpn-0.2.2/docs/1d_quality_of_fit_Rectilinear.svg +5356 -0
  10. interpn-0.2.2/docs/1d_quality_of_fit_Regular.svg +4771 -0
  11. interpn-0.2.2/docs/2d_quality_of_fit_Rectilinear.svg +3435 -0
  12. interpn-0.2.2/docs/2d_quality_of_fit_Regular.svg +3458 -0
  13. interpn-0.2.0/docs/3d_no_prealloc.svg → interpn-0.2.2/docs/3d_throughput_vs_nobs.svg +367 -395
  14. interpn-0.2.2/docs/3d_throughput_vs_nobs_prealloc.svg +2754 -0
  15. interpn-0.2.0/docs/6d_no_prealloc.svg → interpn-0.2.2/docs/6d_throughput_vs_nobs.svg +457 -507
  16. interpn-0.2.2/docs/6d_throughput_vs_nobs_prealloc.svg +2740 -0
  17. interpn-0.2.2/docs/index.md +114 -0
  18. {interpn-0.2.0 → interpn-0.2.2}/docs/perf.md +9 -12
  19. interpn-0.2.0/docs/nd_memory.svg → interpn-0.2.2/docs/ram_vs_dims.svg +236 -185
  20. interpn-0.2.0/docs/nd_throughput_1000obs.svg → interpn-0.2.2/docs/throughput_vs_dims_1000_obs.svg +358 -307
  21. interpn-0.2.0/docs/nd_throughput_1obs.svg → interpn-0.2.2/docs/throughput_vs_dims_1_obs.svg +372 -307
  22. interpn-0.2.2/examples/cubic_comparison.py +120 -0
  23. {interpn-0.2.0 → interpn-0.2.2}/interpn/__init__.py +2 -0
  24. interpn-0.2.2/interpn/multicubic_rectilinear.py +221 -0
  25. {interpn-0.2.0 → interpn-0.2.2}/interpn/multicubic_regular.py +1 -1
  26. {interpn-0.2.0 → interpn-0.2.2}/interpn/multilinear_rectilinear.py +1 -1
  27. {interpn-0.2.0 → interpn-0.2.2}/interpn/raw.py +4 -0
  28. {interpn-0.2.0 → interpn-0.2.2}/pyproject.toml +7 -7
  29. {interpn-0.2.0 → interpn-0.2.2}/src/lib.rs +37 -0
  30. {interpn-0.2.0 → interpn-0.2.2}/test/bench_cpu.py +128 -51
  31. {interpn-0.2.0 → interpn-0.2.2}/test/bench_mem.py +35 -9
  32. interpn-0.2.2/test/test_multicubic_rectilinear.py +75 -0
  33. interpn-0.2.0/docs/1d_cubic_quality_of_fit.svg +0 -5004
  34. interpn-0.2.0/docs/2d_cubic_quality_of_fit.svg +0 -3538
  35. interpn-0.2.0/docs/3d_no_prealloc_cubic.svg +0 -1748
  36. interpn-0.2.0/docs/6d_no_prealloc_cubic.svg +0 -1797
  37. interpn-0.2.0/docs/index.md +0 -68
  38. interpn-0.2.0/examples/cubic_comparison.py +0 -94
  39. {interpn-0.2.0 → interpn-0.2.2}/.gitignore +0 -0
  40. {interpn-0.2.0 → interpn-0.2.2}/.readthedocs.yml +0 -0
  41. {interpn-0.2.0 → interpn-0.2.2}/LICENSE-APACHE +0 -0
  42. {interpn-0.2.0 → interpn-0.2.2}/docs/API_Docs.md +0 -0
  43. {interpn-0.2.0 → interpn-0.2.2}/docs/requirements.txt +0 -0
  44. {interpn-0.2.0 → interpn-0.2.2}/interpn/multilinear_regular.py +0 -0
  45. {interpn-0.2.0 → interpn-0.2.2}/interpn/py.typed +0 -0
  46. {interpn-0.2.0 → interpn-0.2.2}/interpn/serialization.py +0 -0
  47. {interpn-0.2.0 → interpn-0.2.2}/mkdocs.yml +0 -0
  48. {interpn-0.2.0 → interpn-0.2.2}/test/test_docs.py +0 -0
  49. {interpn-0.2.0 → interpn-0.2.2}/test/test_multicubic_regular.py +0 -0
  50. {interpn-0.2.0 → interpn-0.2.2}/test/test_multilinear_rectilinear.py +0 -0
  51. {interpn-0.2.0 → interpn-0.2.2}/test/test_multilinear_regular.py +0 -0
@@ -28,7 +28,7 @@ jobs:
28
28
  strategy:
29
29
  matrix:
30
30
  target: [x86_64, x86, aarch64, armv7, s390x, ppc64le]
31
- python-version: ["3.9", "3.10", "3.11"]
31
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
32
32
  steps:
33
33
  - uses: actions/checkout@v3
34
34
  - uses: actions/setup-python@v4
@@ -52,7 +52,7 @@ jobs:
52
52
  strategy:
53
53
  matrix:
54
54
  target: [x64, x86]
55
- python-version: ["3.9", "3.10", "3.11"]
55
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
56
56
  steps:
57
57
  - uses: actions/checkout@v3
58
58
  - uses: actions/setup-python@v4
@@ -76,7 +76,7 @@ jobs:
76
76
  strategy:
77
77
  matrix:
78
78
  target: [x86_64, aarch64]
79
- python-version: ["3.9", "3.10", "3.11"]
79
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
80
80
  steps:
81
81
  - uses: actions/checkout@v3
82
82
  - uses: actions/setup-python@v4
@@ -11,7 +11,7 @@ jobs:
11
11
  strategy:
12
12
  matrix:
13
13
  target: [x86_64]
14
- python-version: ["3.9", "3.10", "3.11"]
14
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
15
15
  steps:
16
16
  - uses: actions/checkout@v3
17
17
  - uses: actions/setup-python@v4
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.2.2 2024-07-08
4
+
5
+ ### Changed
6
+
7
+ * Update python deps incl. numpy >2
8
+ * Update rust deps
9
+ * Support python 3.12
@@ -4,7 +4,7 @@ version = 3
4
4
 
5
5
  [[package]]
6
6
  name = "_interpn"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  dependencies = [
9
9
  "interpn",
10
10
  "numpy",
@@ -43,9 +43,9 @@ checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
43
43
 
44
44
  [[package]]
45
45
  name = "interpn"
46
- version = "0.4.1"
46
+ version = "0.4.2"
47
47
  source = "registry+https://github.com/rust-lang/crates.io-index"
48
- checksum = "82ec859d62d17054a7314d5bb47d7e4eeb4648b87b7c1e0e33de1d4df21e0375"
48
+ checksum = "f37cc48f61030a3bed5eb01e583af8ae1248f3e3bf63d05d87a18f27a98a426b"
49
49
  dependencies = [
50
50
  "num-traits",
51
51
  ]
@@ -135,9 +135,9 @@ dependencies = [
135
135
 
136
136
  [[package]]
137
137
  name = "numpy"
138
- version = "0.20.0"
138
+ version = "0.21.0"
139
139
  source = "registry+https://github.com/rust-lang/crates.io-index"
140
- checksum = "bef41cbb417ea83b30525259e30ccef6af39b31c240bda578889494c5392d331"
140
+ checksum = "ec170733ca37175f5d75a5bea5911d6ff45d2cd52849ce98b685394e4f2f37f4"
141
141
  dependencies = [
142
142
  "libc",
143
143
  "ndarray",
@@ -177,6 +177,12 @@ dependencies = [
177
177
  "windows-targets",
178
178
  ]
179
179
 
180
+ [[package]]
181
+ name = "portable-atomic"
182
+ version = "1.6.0"
183
+ source = "registry+https://github.com/rust-lang/crates.io-index"
184
+ checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
185
+
180
186
  [[package]]
181
187
  name = "proc-macro2"
182
188
  version = "1.0.69"
@@ -188,15 +194,16 @@ dependencies = [
188
194
 
189
195
  [[package]]
190
196
  name = "pyo3"
191
- version = "0.20.0"
197
+ version = "0.21.2"
192
198
  source = "registry+https://github.com/rust-lang/crates.io-index"
193
- checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b"
199
+ checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8"
194
200
  dependencies = [
195
201
  "cfg-if",
196
202
  "indoc",
197
203
  "libc",
198
204
  "memoffset",
199
205
  "parking_lot",
206
+ "portable-atomic",
200
207
  "pyo3-build-config",
201
208
  "pyo3-ffi",
202
209
  "pyo3-macros",
@@ -205,9 +212,9 @@ dependencies = [
205
212
 
206
213
  [[package]]
207
214
  name = "pyo3-build-config"
208
- version = "0.20.0"
215
+ version = "0.21.2"
209
216
  source = "registry+https://github.com/rust-lang/crates.io-index"
210
- checksum = "a96fe70b176a89cff78f2fa7b3c930081e163d5379b4dcdf993e3ae29ca662e5"
217
+ checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50"
211
218
  dependencies = [
212
219
  "once_cell",
213
220
  "target-lexicon",
@@ -215,9 +222,9 @@ dependencies = [
215
222
 
216
223
  [[package]]
217
224
  name = "pyo3-ffi"
218
- version = "0.20.0"
225
+ version = "0.21.2"
219
226
  source = "registry+https://github.com/rust-lang/crates.io-index"
220
- checksum = "214929900fd25e6604661ed9cf349727c8920d47deff196c4e28165a6ef2a96b"
227
+ checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403"
221
228
  dependencies = [
222
229
  "libc",
223
230
  "pyo3-build-config",
@@ -225,9 +232,9 @@ dependencies = [
225
232
 
226
233
  [[package]]
227
234
  name = "pyo3-macros"
228
- version = "0.20.0"
235
+ version = "0.21.2"
229
236
  source = "registry+https://github.com/rust-lang/crates.io-index"
230
- checksum = "dac53072f717aa1bfa4db832b39de8c875b7c7af4f4a6fe93cdbf9264cf8383b"
237
+ checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c"
231
238
  dependencies = [
232
239
  "proc-macro2",
233
240
  "pyo3-macros-backend",
@@ -237,12 +244,13 @@ dependencies = [
237
244
 
238
245
  [[package]]
239
246
  name = "pyo3-macros-backend"
240
- version = "0.20.0"
247
+ version = "0.21.2"
241
248
  source = "registry+https://github.com/rust-lang/crates.io-index"
242
- checksum = "7774b5a8282bd4f25f803b1f0d945120be959a36c72e08e7cd031c792fdfd424"
249
+ checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c"
243
250
  dependencies = [
244
251
  "heck",
245
252
  "proc-macro2",
253
+ "pyo3-build-config",
246
254
  "quote",
247
255
  "syn",
248
256
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "_interpn"
3
- version = "0.1.2"
3
+ version = "0.1.3"
4
4
  edition = "2021"
5
5
 
6
6
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -9,9 +9,9 @@ name = "_interpn"
9
9
  crate-type = ["cdylib"]
10
10
 
11
11
  [dependencies]
12
- pyo3 = "0.20.0"
13
- numpy = "0.20.0"
14
- interpn = {version = "0.4.1", default_features = false}
12
+ pyo3 = "^0.21.0"
13
+ numpy = "^0.21.0"
14
+ interpn = {version = "0.4.2", default_features = false}
15
15
  #interpn = { path = "../interpn/interpn" }
16
16
 
17
17
  [profile.release]
@@ -1,4 +1,4 @@
1
- Copyright (c) 2023 James Logan
1
+ Copyright (c) 2024 James Logan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any
4
4
  person obtaining a copy of this software and associated
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: interpn
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
7
- Requires-Dist: numpy >=1
7
+ Requires-Dist: numpy >=2
8
8
  Requires-Dist: pydantic >=2.5.2
9
- Requires-Dist: pytest >=7.4.3 ; extra == 'test'
9
+ Requires-Dist: pytest >=8.0 ; extra == 'test'
10
10
  Requires-Dist: coverage >=7.3.2 ; extra == 'test'
11
- Requires-Dist: ruff >=0.1.6 ; extra == 'test'
12
- Requires-Dist: pyright >=1.1.337 ; extra == 'test'
11
+ Requires-Dist: ruff >=0.4.10 ; extra == 'test'
12
+ Requires-Dist: pyright ==1.1.337 ; extra == 'test'
13
13
  Requires-Dist: mktestdocs >=0.2.1 ; extra == 'test'
14
14
  Requires-Dist: scipy >=1.11.4 ; extra == 'test'
15
15
  Requires-Dist: matplotlib >=3.8 ; extra == 'test'
@@ -26,7 +26,7 @@ License-File: LICENSE-APACHE
26
26
  License-File: LICENSE-MIT
27
27
  Summary: N-dimensional interpolation/extrapolation methods
28
28
  Author-email: jlogan03@gmail.com
29
- Requires-Python: >=3.9, <3.12
29
+ Requires-Python: >=3.9, <3.13
30
30
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
31
31
 
32
32
  # interpn
@@ -39,29 +39,67 @@ Python bindings to the `interpn` Rust library for N-dimensional interpolation an
39
39
  [Rust Docs (docs.rs)](https://docs.rs/interpn/latest/interpn/)
40
40
 
41
41
  ## Features
42
+
42
43
  | Feature →<br>↓ Interpolant Method | Regular<br>Grid | Rectilinear<br>Grid | Json<br>Serialization |
43
44
  |-----------------------------------|-----------------|---------------------|-----------------------|
44
45
  | Linear | ✅ | ✅ | ✅ |
45
- | Cubic | ✅ | 💡 | ✅ |
46
+ | Cubic | ✅ | | ✅ |
46
47
 
47
48
  The methods provided here, while more limited in scope than scipy's, are
49
+
48
50
  * significantly faster for higher dimensions (1-3 orders of magnitude under most conditions)
49
51
  * use almost no RAM (and perform no heap allocations at all)
50
52
  * produce significantly improved floating-point error (by 1-2 orders of magnitude)
51
53
  * are json-serializable using Pydantic
52
54
  * can also be used easily in web and embedded applications via the Rust library
55
+ * are permissively licensed
56
+
57
+ ![ND throughput 1 obs](./docs/throughput_vs_dims_1_obs.svg)
53
58
 
54
59
  See [here](https://interpnpy.readthedocs.io/en/latest/perf/) for more info about quality-of-fit, throughput, and memory usage.
55
60
 
61
+ ## Installation
62
+
63
+ ```bash
64
+ pip install interpn
65
+ ```
66
+
67
+ ## Example: Available Methods
68
+
69
+ ```python
70
+ import interpn
71
+ import numpy as np
72
+
73
+ # Build grid
74
+ x = np.linspace(0.0, 10.0, 5)
75
+ y = np.linspace(20.0, 30.0, 4)
76
+ grids = [x, y]
77
+
78
+ xgrid, ygrid = np.meshgrid(x, y, indexing="ij")
79
+ zgrid = (xgrid + 2.0 * ygrid) # Values at grid points
80
+
81
+ # Grid inputs for true regular grid
82
+ dims = [x.size, y.size]
83
+ starts = np.array([x[0], y[0]])
84
+ steps = np.array([x[1] - x[0], y[1] - y[0]])
85
+
86
+ # Initialize different interpolators
87
+ # Call like `linear_regular.eval([xs, ys])`
88
+ linear_regular = interpn.MultilinearRegular.new(dims, starts, steps, zgrid)
89
+ cubic_regular = interpn.MulticubicRegular.new(dims, starts, steps, zgrid)
90
+ linear_rectilinear = interpn.MultilinearRectilinear.new(grids, zgrid)
91
+ cubic_rectilinear = interpn.MulticubicRectilinear.new(grids, zgrid)
92
+ ```
56
93
 
57
94
  ## Example: Multilinear Interpolation on a Regular Grid
95
+
58
96
  ```python
59
97
  import interpn
60
98
  import numpy as np
61
99
 
62
100
  # Build grid
63
101
  x = np.linspace(0.0, 10.0, 5)
64
- y = np.linspace(20.0, 30.0, 3)
102
+ y = np.linspace(20.0, 30.0, 4)
65
103
 
66
104
  xgrid, ygrid = np.meshgrid(x, y, indexing="ij")
67
105
  zgrid = (xgrid + 2.0 * ygrid) # Values at grid points
@@ -75,15 +113,13 @@ steps = np.array([x[1] - x[0], y[1] - y[0]])
75
113
  obs = [xgrid.flatten(), ygrid.flatten()]
76
114
 
77
115
  # Initialize
78
- interpolator = interpn.MultilinearRegular.new(
79
- dims, starts, steps, zgrid.flatten()
80
- )
116
+ interpolator = interpn.MultilinearRegular.new(dims, starts, steps, zgrid.flatten())
81
117
 
82
118
  # Interpolate
83
119
  out = interpolator.eval(obs)
84
120
 
85
121
  # Check result
86
- assert np.all(out == zgrid.flatten())
122
+ assert np.allclose(out, zgrid.flatten(), rtol=1e-13)
87
123
 
88
124
  # Serialize and deserialize
89
125
  roundtrip_interpolator = interpn.MultilinearRegular.model_validate_json(
@@ -97,9 +133,11 @@ assert np.all(out == out2)
97
133
 
98
134
 
99
135
  # License
136
+
100
137
  Licensed under either of
101
138
 
102
139
  - Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
103
140
  - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
104
141
 
105
142
  at your option.
143
+
@@ -8,29 +8,67 @@ Python bindings to the `interpn` Rust library for N-dimensional interpolation an
8
8
  [Rust Docs (docs.rs)](https://docs.rs/interpn/latest/interpn/)
9
9
 
10
10
  ## Features
11
+
11
12
  | Feature →<br>↓ Interpolant Method | Regular<br>Grid | Rectilinear<br>Grid | Json<br>Serialization |
12
13
  |-----------------------------------|-----------------|---------------------|-----------------------|
13
14
  | Linear | ✅ | ✅ | ✅ |
14
- | Cubic | ✅ | 💡 | ✅ |
15
+ | Cubic | ✅ | | ✅ |
15
16
 
16
17
  The methods provided here, while more limited in scope than scipy's, are
18
+
17
19
  * significantly faster for higher dimensions (1-3 orders of magnitude under most conditions)
18
20
  * use almost no RAM (and perform no heap allocations at all)
19
21
  * produce significantly improved floating-point error (by 1-2 orders of magnitude)
20
22
  * are json-serializable using Pydantic
21
23
  * can also be used easily in web and embedded applications via the Rust library
24
+ * are permissively licensed
25
+
26
+ ![ND throughput 1 obs](./docs/throughput_vs_dims_1_obs.svg)
22
27
 
23
28
  See [here](https://interpnpy.readthedocs.io/en/latest/perf/) for more info about quality-of-fit, throughput, and memory usage.
24
29
 
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install interpn
34
+ ```
35
+
36
+ ## Example: Available Methods
37
+
38
+ ```python
39
+ import interpn
40
+ import numpy as np
41
+
42
+ # Build grid
43
+ x = np.linspace(0.0, 10.0, 5)
44
+ y = np.linspace(20.0, 30.0, 4)
45
+ grids = [x, y]
46
+
47
+ xgrid, ygrid = np.meshgrid(x, y, indexing="ij")
48
+ zgrid = (xgrid + 2.0 * ygrid) # Values at grid points
49
+
50
+ # Grid inputs for true regular grid
51
+ dims = [x.size, y.size]
52
+ starts = np.array([x[0], y[0]])
53
+ steps = np.array([x[1] - x[0], y[1] - y[0]])
54
+
55
+ # Initialize different interpolators
56
+ # Call like `linear_regular.eval([xs, ys])`
57
+ linear_regular = interpn.MultilinearRegular.new(dims, starts, steps, zgrid)
58
+ cubic_regular = interpn.MulticubicRegular.new(dims, starts, steps, zgrid)
59
+ linear_rectilinear = interpn.MultilinearRectilinear.new(grids, zgrid)
60
+ cubic_rectilinear = interpn.MulticubicRectilinear.new(grids, zgrid)
61
+ ```
25
62
 
26
63
  ## Example: Multilinear Interpolation on a Regular Grid
64
+
27
65
  ```python
28
66
  import interpn
29
67
  import numpy as np
30
68
 
31
69
  # Build grid
32
70
  x = np.linspace(0.0, 10.0, 5)
33
- y = np.linspace(20.0, 30.0, 3)
71
+ y = np.linspace(20.0, 30.0, 4)
34
72
 
35
73
  xgrid, ygrid = np.meshgrid(x, y, indexing="ij")
36
74
  zgrid = (xgrid + 2.0 * ygrid) # Values at grid points
@@ -44,15 +82,13 @@ steps = np.array([x[1] - x[0], y[1] - y[0]])
44
82
  obs = [xgrid.flatten(), ygrid.flatten()]
45
83
 
46
84
  # Initialize
47
- interpolator = interpn.MultilinearRegular.new(
48
- dims, starts, steps, zgrid.flatten()
49
- )
85
+ interpolator = interpn.MultilinearRegular.new(dims, starts, steps, zgrid.flatten())
50
86
 
51
87
  # Interpolate
52
88
  out = interpolator.eval(obs)
53
89
 
54
90
  # Check result
55
- assert np.all(out == zgrid.flatten())
91
+ assert np.allclose(out, zgrid.flatten(), rtol=1e-13)
56
92
 
57
93
  # Serialize and deserialize
58
94
  roundtrip_interpolator = interpn.MultilinearRegular.model_validate_json(
@@ -66,9 +102,10 @@ assert np.all(out == out2)
66
102
 
67
103
 
68
104
  # License
105
+
69
106
  Licensed under either of
70
107
 
71
108
  - Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
72
109
  - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
73
110
 
74
- at your option.
111
+ at your option.