unitforge 0.4.3__tar.gz → 0.5.1__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.
- {unitforge-0.4.3 → unitforge-0.5.1}/Cargo.lock +6 -6
- {unitforge-0.4.3 → unitforge-0.5.1}/Cargo.toml +2 -9
- {unitforge-0.4.3 → unitforge-0.5.1}/PKG-INFO +1 -1
- {unitforge-0.4.3 → unitforge-0.5.1}/scripts/generate_python_stubs.py +4 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/impl_macros.rs +22 -11
- {unitforge-0.4.3 → unitforge-0.5.1}/src/lib.rs +1 -32
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities.template.rs +24 -6
- {unitforge-0.4.3 → unitforge-0.5.1}/src/small_linalg/bindings/vector3_py.template.rs +59 -13
- unitforge-0.5.1/tests/test_quantities.py +86 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/test_small_linalg.py +55 -2
- {unitforge-0.4.3 → unitforge-0.5.1}/unitforge_macros/Cargo.toml +2 -2
- unitforge-0.4.3/tests/test_quantities.py +0 -38
- {unitforge-0.4.3 → unitforge-0.5.1}/.gitignore +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/.gitlab-ci.yml +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/Readme.md +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/Readme_py.md +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/install_hooks.sh +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/pyproject.toml +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/python/unitforge/__init__.py +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/errors.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/internal.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/prelude.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/acceleration.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/angle.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/angular_acceleration.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/angular_velocity.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/area.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/area_of_moment.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/charge.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/compliance.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/density.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/distance.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/force.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/force_area.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/force_distance.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/force_div_distance_power_four.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/force_per_volume.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/force_stress.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/force_volume.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/frequency.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/inverse_angle.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/inverse_area.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/inverse_distance.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/inverse_stress.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/mass.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/mass_per_distance.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/molar_mass.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/pressure_rate.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/rotational_stiffness.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/specific_gas_constant.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/stiffness.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/strain.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/stress.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/stress_squared.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/temperature.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/time.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/universal_gas_constant.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/velocity.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/velocity_squared.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/voltage.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/quantities/volume.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/relations.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/small_linalg/bindings/matrix3_py.template.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/small_linalg/matrix2.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/small_linalg/matrix2x3.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/small_linalg/matrix3.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/small_linalg/matrix3x2.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/small_linalg/vector2.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/src/small_linalg/vector3.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/test.sh +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/common/mod.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/matrix2.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/matrix2x3.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/matrix3.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/matrix3x2.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/ndarray_dot.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantities.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/constants.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/construction.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/enum_iter.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/metadata.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/normalization.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/plain_storage.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/relations.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/runtime_quantity.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core/scalar_edges.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_core.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/quantity_serde.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/small_linalg_edge_cases.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/small_linalg_interfaces.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/typecheck/mypy_smoke.py +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/vector2.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/tests/vector3.rs +0 -0
- {unitforge-0.4.3 → unitforge-0.5.1}/unitforge_macros/src/lib.rs +0 -0
|
@@ -231,9 +231,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
|
|
|
231
231
|
|
|
232
232
|
[[package]]
|
|
233
233
|
name = "regex"
|
|
234
|
-
version = "1.
|
|
234
|
+
version = "1.12.3"
|
|
235
235
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
236
|
-
checksum = "
|
|
236
|
+
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
|
237
237
|
dependencies = [
|
|
238
238
|
"aho-corasick",
|
|
239
239
|
"memchr",
|
|
@@ -243,9 +243,9 @@ dependencies = [
|
|
|
243
243
|
|
|
244
244
|
[[package]]
|
|
245
245
|
name = "regex-automata"
|
|
246
|
-
version = "0.4.
|
|
246
|
+
version = "0.4.14"
|
|
247
247
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
248
|
-
checksum = "
|
|
248
|
+
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
|
249
249
|
dependencies = [
|
|
250
250
|
"aho-corasick",
|
|
251
251
|
"memchr",
|
|
@@ -351,7 +351,7 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
|
|
351
351
|
|
|
352
352
|
[[package]]
|
|
353
353
|
name = "unitforge"
|
|
354
|
-
version = "0.
|
|
354
|
+
version = "0.5.1"
|
|
355
355
|
dependencies = [
|
|
356
356
|
"ndarray",
|
|
357
357
|
"num-traits",
|
|
@@ -367,7 +367,7 @@ dependencies = [
|
|
|
367
367
|
|
|
368
368
|
[[package]]
|
|
369
369
|
name = "unitforge_macros"
|
|
370
|
-
version = "0.
|
|
370
|
+
version = "0.5.1"
|
|
371
371
|
dependencies = [
|
|
372
372
|
"regex",
|
|
373
373
|
]
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
[lib]
|
|
2
|
-
name = "unitforge"
|
|
3
|
-
crate-type = ["cdylib", "rlib"]
|
|
4
|
-
|
|
5
1
|
[package]
|
|
6
2
|
name = "unitforge"
|
|
7
|
-
version = "0.
|
|
3
|
+
version = "0.5.1"
|
|
8
4
|
edition = "2021"
|
|
9
5
|
authors = ["Dr.Q team <info@askdrq.com>", "Henrik Stromberg <henrik@askdrq.com>"]
|
|
10
6
|
description = "A library for unit and quantity consistent computations in Rust"
|
|
@@ -24,7 +20,7 @@ pyo3 = { version = "0.28.3", features = ["extension-module", "auto-initialize",
|
|
|
24
20
|
numpy = { version = "0.28.0", optional = true }
|
|
25
21
|
strum = { version = "0.28.0", optional = true }
|
|
26
22
|
strum_macros = { version = "0.28.0", optional = true }
|
|
27
|
-
unitforge_macros = { version = "0.
|
|
23
|
+
unitforge_macros = { version = "0.5.1", path = "unitforge_macros" }
|
|
28
24
|
|
|
29
25
|
[features]
|
|
30
26
|
default = []
|
|
@@ -38,6 +34,3 @@ no_std = []
|
|
|
38
34
|
|
|
39
35
|
[workspace]
|
|
40
36
|
members = ["unitforge_macros"]
|
|
41
|
-
|
|
42
|
-
[profile.dev]
|
|
43
|
-
panic = "abort"
|
|
@@ -274,7 +274,9 @@ def render_vector3_stub() -> List[str]:
|
|
|
274
274
|
' def dot_vec(self, other: "Vector3") -> Any: ...',
|
|
275
275
|
' def cross(self, other: "Vector3") -> "Vector3": ...',
|
|
276
276
|
" def __mul__(self, rhs: Any) -> Vector3: ...",
|
|
277
|
+
" def __imul__(self, rhs: Any) -> None: ...",
|
|
277
278
|
" def __truediv__(self, rhs: Any) -> Vector3: ...",
|
|
279
|
+
" def __itruediv__(self, rhs: Any) -> None: ...",
|
|
278
280
|
]
|
|
279
281
|
|
|
280
282
|
|
|
@@ -341,6 +343,8 @@ def render_quantity_stub(
|
|
|
341
343
|
f' def __sub__(self, rhs: "{q}") -> "{q}": ...',
|
|
342
344
|
" @overload",
|
|
343
345
|
f' def __mul__(self, rhs: float) -> "{q}": ...',
|
|
346
|
+
" @overload",
|
|
347
|
+
" def __mul__(self, rhs: Vector3) -> Vector3: ...",
|
|
344
348
|
]
|
|
345
349
|
|
|
346
350
|
mul_ops = operator_maps.mul.get(q, {})
|
|
@@ -16,8 +16,11 @@ macro_rules! impl_quantity {
|
|
|
16
16
|
use pyo3::pyclass;
|
|
17
17
|
#[cfg(feature = "pyo3")]
|
|
18
18
|
use pyo3::{
|
|
19
|
-
basic::CompareOp,
|
|
20
|
-
|
|
19
|
+
basic::CompareOp,
|
|
20
|
+
exceptions::PyTypeError,
|
|
21
|
+
pymethods,
|
|
22
|
+
types::{PyAnyMethods, PyType},
|
|
23
|
+
Bound, IntoPyObject, Py, PyAny, PyRef, PyResult,
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
#[cfg(feature = "strum")]
|
|
@@ -395,13 +398,21 @@ macro_rules! impl_quantity {
|
|
|
395
398
|
let py = lhs.py();
|
|
396
399
|
let rhs_ref = rhs.bind(py);
|
|
397
400
|
let lhs_ref = lhs.into_pyobject(py)?;
|
|
398
|
-
let rhs_quantity = Quantity::from_py_any(rhs_ref)
|
|
399
|
-
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
400
401
|
let lhs_quantity = Quantity::from_py_any(&lhs_ref)
|
|
401
|
-
.map_err(|e|
|
|
402
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
403
|
+
if let Ok(rhs_vector) = rhs_ref.extract::<$crate::Vector3Py>() {
|
|
404
|
+
return Ok(rhs_vector
|
|
405
|
+
.mul_quantity_left(lhs_quantity)?
|
|
406
|
+
.into_pyobject(py)?
|
|
407
|
+
.into_any()
|
|
408
|
+
.unbind());
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let rhs_quantity = Quantity::from_py_any(rhs_ref)
|
|
412
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
402
413
|
match lhs_quantity * rhs_quantity {
|
|
403
414
|
Ok(value) => Ok(value.to_pyobject(py)?),
|
|
404
|
-
Err(_) => Err(
|
|
415
|
+
Err(_) => Err(PyTypeError::new_err(
|
|
405
416
|
"Multiplication of given objects is not possible.",
|
|
406
417
|
)),
|
|
407
418
|
}
|
|
@@ -412,12 +423,12 @@ macro_rules! impl_quantity {
|
|
|
412
423
|
let rhs_ref = rhs.bind(py);
|
|
413
424
|
let lhs_ref = lhs.into_pyobject(py)?;
|
|
414
425
|
let rhs_quantity = Quantity::from_py_any(rhs_ref)
|
|
415
|
-
.map_err(|e|
|
|
426
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
416
427
|
let lhs_quantity = Quantity::from_py_any(&lhs_ref)
|
|
417
|
-
.map_err(|e|
|
|
428
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
418
429
|
match lhs_quantity / rhs_quantity {
|
|
419
430
|
Ok(value) => Ok(value.to_pyobject(py)?),
|
|
420
|
-
Err(_) => Err(
|
|
431
|
+
Err(_) => Err(PyTypeError::new_err(
|
|
421
432
|
"Division of given objects is not possible.",
|
|
422
433
|
)),
|
|
423
434
|
}
|
|
@@ -435,10 +446,10 @@ macro_rules! impl_quantity {
|
|
|
435
446
|
let py = lhs.py();
|
|
436
447
|
let lhs_ref = lhs.into_pyobject(py)?;
|
|
437
448
|
let lhs_quantity = Quantity::from_py_any(&lhs_ref)
|
|
438
|
-
.map_err(|e|
|
|
449
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
439
450
|
match lhs_quantity.powi(rhs) {
|
|
440
451
|
Ok(value) => Ok(value.to_pyobject(py)?),
|
|
441
|
-
Err(_) => Err(
|
|
452
|
+
Err(_) => Err(PyTypeError::new_err(
|
|
442
453
|
"Power of given object is not possible.",
|
|
443
454
|
)),
|
|
444
455
|
}
|
|
@@ -1,37 +1,6 @@
|
|
|
1
|
-
#![cfg_attr(
|
|
2
|
-
all(
|
|
3
|
-
feature = "no_std",
|
|
4
|
-
any(feature = "storage-f64", feature = "storage-f32"),
|
|
5
|
-
not(all(feature = "storage-f64", feature = "storage-f32")),
|
|
6
|
-
not(any(
|
|
7
|
-
feature = "serde",
|
|
8
|
-
feature = "ndarray",
|
|
9
|
-
feature = "pyo3",
|
|
10
|
-
feature = "strum"
|
|
11
|
-
))
|
|
12
|
-
),
|
|
13
|
-
no_std
|
|
14
|
-
)]
|
|
1
|
+
#![cfg_attr(feature = "no_std", no_std)]
|
|
15
2
|
#![allow(non_camel_case_types)]
|
|
16
3
|
|
|
17
|
-
#[cfg(all(
|
|
18
|
-
feature = "no_std",
|
|
19
|
-
any(feature = "storage-f64", feature = "storage-f32"),
|
|
20
|
-
not(all(feature = "storage-f64", feature = "storage-f32")),
|
|
21
|
-
not(any(
|
|
22
|
-
feature = "serde",
|
|
23
|
-
feature = "ndarray",
|
|
24
|
-
feature = "pyo3",
|
|
25
|
-
feature = "strum"
|
|
26
|
-
)),
|
|
27
|
-
not(test),
|
|
28
|
-
not(target_os = "none")
|
|
29
|
-
))]
|
|
30
|
-
#[panic_handler]
|
|
31
|
-
fn panic(_info: &core::panic::PanicInfo<'_>) -> ! {
|
|
32
|
-
loop {}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
4
|
#[cfg(all(feature = "storage-f64", feature = "storage-f32"))]
|
|
36
5
|
compile_error!("features `storage-f64` and `storage-f32` are mutually exclusive");
|
|
37
6
|
|
|
@@ -207,10 +207,9 @@ fn extract_f64(v: &Bound<PyAny>) -> Option<f64> {
|
|
|
207
207
|
#[cfg(feature = "pyo3")]
|
|
208
208
|
impl Quantity {
|
|
209
209
|
pub fn from_py_any(v: &Bound<PyAny>) -> crate::QuantityConversionResult<Self> {
|
|
210
|
-
if let
|
|
211
|
-
Ok(
|
|
210
|
+
if let Ok(inner) = Self::from_py_any_direct(v) {
|
|
211
|
+
Ok(inner)
|
|
212
212
|
}
|
|
213
|
-
// __EXTRACT_QUANTITY_MATCHES__
|
|
214
213
|
else if let Ok(inner) = pyo3_capsule_api::extract_quantity_from_capsule(v) {
|
|
215
214
|
Ok(inner)
|
|
216
215
|
}
|
|
@@ -219,6 +218,16 @@ impl Quantity {
|
|
|
219
218
|
}
|
|
220
219
|
}
|
|
221
220
|
|
|
221
|
+
pub(crate) fn from_py_any_direct(v: &Bound<PyAny>) -> crate::QuantityConversionResult<Self> {
|
|
222
|
+
if let Some(inner) = extract_f64(v) {
|
|
223
|
+
Ok(Quantity::FloatQuantity(inner))
|
|
224
|
+
}
|
|
225
|
+
// __EXTRACT_QUANTITY_MATCHES__
|
|
226
|
+
else {
|
|
227
|
+
Err(crate::QuantityConversionError::InvalidPythonQuantity)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
222
231
|
pub fn to_pyobject(self, py: Python) -> PyResult<Py<PyAny>> {
|
|
223
232
|
Ok(match self {
|
|
224
233
|
Quantity::FloatQuantity(v) => v.into_pyobject(py).map(|obj| obj.into())?,
|
|
@@ -240,7 +249,9 @@ impl Display for Quantity {
|
|
|
240
249
|
impl Unit {
|
|
241
250
|
|
|
242
251
|
pub fn from_py_any(v: &Bound<PyAny>) -> crate::QuantityConversionResult<Self> {
|
|
243
|
-
|
|
252
|
+
if let Ok(inner) = Self::from_py_any_direct(v) {
|
|
253
|
+
Ok(inner)
|
|
254
|
+
}
|
|
244
255
|
else if let Ok(inner) = pyo3_capsule_api::extract_unit_from_capsule(v) {
|
|
245
256
|
Ok(inner)
|
|
246
257
|
}
|
|
@@ -248,6 +259,13 @@ impl Unit {
|
|
|
248
259
|
Err(crate::QuantityConversionError::InvalidPythonUnit)
|
|
249
260
|
}
|
|
250
261
|
}
|
|
262
|
+
|
|
263
|
+
pub(crate) fn from_py_any_direct(v: &Bound<PyAny>) -> crate::QuantityConversionResult<Self> {
|
|
264
|
+
// __EXTRACT_UNIT_MATCHES__
|
|
265
|
+
else {
|
|
266
|
+
Err(crate::QuantityConversionError::InvalidPythonUnit)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
251
269
|
}
|
|
252
270
|
|
|
253
271
|
#[cfg(feature = "pyo3")]
|
|
@@ -400,7 +418,7 @@ pub mod pyo3_capsule_api {
|
|
|
400
418
|
return Err(PyValueError::new_err("output pointer for quantity repr is null"));
|
|
401
419
|
}
|
|
402
420
|
let obj_bound = Bound::from_borrowed_ptr(py, obj);
|
|
403
|
-
let quantity = Quantity::
|
|
421
|
+
let quantity = Quantity::from_py_any_direct(&obj_bound)
|
|
404
422
|
.map_err(|err| PyValueError::new_err(err.to_string()))?;
|
|
405
423
|
unsafe {
|
|
406
424
|
*out = quantity_to_repr(quantity)
|
|
@@ -444,7 +462,7 @@ pub mod pyo3_capsule_api {
|
|
|
444
462
|
return Err(PyValueError::new_err("output pointer for unit repr is null"));
|
|
445
463
|
}
|
|
446
464
|
let obj_bound = Bound::from_borrowed_ptr(py, obj);
|
|
447
|
-
let unit = Unit::
|
|
465
|
+
let unit = Unit::from_py_any_direct(&obj_bound)
|
|
448
466
|
.map_err(|err| PyValueError::new_err(err.to_string()))?;
|
|
449
467
|
unsafe {
|
|
450
468
|
*out = unit_to_repr(unit);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use std::mem::discriminant;
|
|
2
|
-
use pyo3::{exceptions::PyValueError, types::PyList};
|
|
2
|
+
use pyo3::{exceptions::{PyTypeError, PyValueError}, types::PyList};
|
|
3
3
|
use numpy::{PyReadonlyArray1, PyArray1};
|
|
4
4
|
use crate::small_linalg::Vector3;
|
|
5
5
|
|
|
@@ -38,6 +38,36 @@ impl Vector3Py {
|
|
|
38
38
|
self.data[2]
|
|
39
39
|
)
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
fn mul_quantity_right_data(&self, rhs: Quantity) -> PyResult<[Quantity; 3]> {
|
|
43
|
+
Ok([
|
|
44
|
+
(self.data[0] * rhs).map_err(|_| PyTypeError::new_err("Cannot multiply given Quantities."))?,
|
|
45
|
+
(self.data[1] * rhs).map_err(|_| PyTypeError::new_err("Cannot multiply given Quantities."))?,
|
|
46
|
+
(self.data[2] * rhs).map_err(|_| PyTypeError::new_err("Cannot multiply given Quantities."))?,
|
|
47
|
+
])
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fn mul_quantity_left_data(&self, lhs: Quantity) -> PyResult<[Quantity; 3]> {
|
|
51
|
+
Ok([
|
|
52
|
+
(lhs * self.data[0]).map_err(|_| PyTypeError::new_err("Cannot multiply given Quantities."))?,
|
|
53
|
+
(lhs * self.data[1]).map_err(|_| PyTypeError::new_err("Cannot multiply given Quantities."))?,
|
|
54
|
+
(lhs * self.data[2]).map_err(|_| PyTypeError::new_err("Cannot multiply given Quantities."))?,
|
|
55
|
+
])
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn div_quantity_right_data(&self, rhs: Quantity) -> PyResult<[Quantity; 3]> {
|
|
59
|
+
Ok([
|
|
60
|
+
(self.data[0] / rhs).map_err(|_| PyTypeError::new_err("Cannot divide given Quantities."))?,
|
|
61
|
+
(self.data[1] / rhs).map_err(|_| PyTypeError::new_err("Cannot divide given Quantities."))?,
|
|
62
|
+
(self.data[2] / rhs).map_err(|_| PyTypeError::new_err("Cannot divide given Quantities."))?,
|
|
63
|
+
])
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pub fn mul_quantity_left(&self, lhs: Quantity) -> PyResult<Self> {
|
|
67
|
+
Ok(Vector3Py {
|
|
68
|
+
data: self.mul_quantity_left_data(lhs)?,
|
|
69
|
+
})
|
|
70
|
+
}
|
|
41
71
|
}
|
|
42
72
|
|
|
43
73
|
#[pymethods]
|
|
@@ -127,7 +157,7 @@ impl Vector3Py {
|
|
|
127
157
|
|
|
128
158
|
fn __add__(&self, other: &Vector3Py) -> PyResult<Self> {
|
|
129
159
|
if discriminant(&self.data[0]) != discriminant(&other.data[0]) {
|
|
130
|
-
Err(
|
|
160
|
+
Err(PyTypeError::new_err("The passed values must be of the same quantity.".to_string()))
|
|
131
161
|
} else {
|
|
132
162
|
Ok(Vector3Py {
|
|
133
163
|
data: [self.data[0].add(other.data[0]).unwrap(),
|
|
@@ -139,7 +169,7 @@ impl Vector3Py {
|
|
|
139
169
|
|
|
140
170
|
fn __sub__(&self, other: &Vector3Py) -> PyResult<Self> {
|
|
141
171
|
if discriminant(&self.data[0]) != discriminant(&other.data[0]) {
|
|
142
|
-
Err(
|
|
172
|
+
Err(PyTypeError::new_err("The passed values must be of the same quantity.".to_string()))
|
|
143
173
|
} else {
|
|
144
174
|
Ok(Vector3Py {
|
|
145
175
|
data: [self.data[0].sub(other.data[0]).unwrap(),
|
|
@@ -338,20 +368,36 @@ impl Vector3Py {
|
|
|
338
368
|
fn __mul__(&self, py: Python, rhs: Py<PyAny>) -> PyResult<Self> {
|
|
339
369
|
let rhs_ref = rhs.bind(py);
|
|
340
370
|
let rhs_quantity = Quantity::from_py_any(rhs_ref)
|
|
341
|
-
.map_err(|e|
|
|
342
|
-
Ok(Vector3Py {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
(self.data[2] * rhs_quantity).map_err(|_| PyValueError::new_err("Cannot multiply given Quantities."))?] })
|
|
371
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
372
|
+
Ok(Vector3Py {
|
|
373
|
+
data: self.mul_quantity_right_data(rhs_quantity)?,
|
|
374
|
+
})
|
|
346
375
|
}
|
|
347
376
|
|
|
348
377
|
fn __truediv__(&self, py: Python, rhs: Py<PyAny>) -> PyResult<Self> {
|
|
349
378
|
let rhs_ref = rhs.bind(py);
|
|
350
379
|
let rhs_quantity = Quantity::from_py_any(rhs_ref)
|
|
351
|
-
.map_err(|e|
|
|
352
|
-
Ok(Vector3Py {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
380
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
381
|
+
Ok(Vector3Py {
|
|
382
|
+
data: self.div_quantity_right_data(rhs_quantity)?,
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
fn __imul__(&mut self, py: Python, rhs: Py<PyAny>) -> PyResult<()> {
|
|
387
|
+
let rhs_ref = rhs.bind(py);
|
|
388
|
+
let rhs_quantity = Quantity::from_py_any(rhs_ref)
|
|
389
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
390
|
+
let data = self.mul_quantity_right_data(rhs_quantity)?;
|
|
391
|
+
self.data = data;
|
|
392
|
+
Ok(())
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
fn __itruediv__(&mut self, py: Python, rhs: Py<PyAny>) -> PyResult<()> {
|
|
396
|
+
let rhs_ref = rhs.bind(py);
|
|
397
|
+
let rhs_quantity = Quantity::from_py_any(rhs_ref)
|
|
398
|
+
.map_err(|e| PyTypeError::new_err(e.to_string()))?;
|
|
399
|
+
let data = self.div_quantity_right_data(rhs_quantity)?;
|
|
400
|
+
self.data = data;
|
|
401
|
+
Ok(())
|
|
356
402
|
}
|
|
357
403
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from unitforge import *
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_inverse_stress_from_stress_division():
|
|
6
|
+
stress = Stress(2.0, StressUnit.Pa)
|
|
7
|
+
inverse = 1 / stress
|
|
8
|
+
assert isinstance(inverse, InverseStress)
|
|
9
|
+
assert inverse.to(InverseStressUnit._Pa) == pytest.approx(0.5)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_stress_from_inverse_stress_division():
|
|
13
|
+
inverse = InverseStress(0.5, InverseStressUnit._Pa)
|
|
14
|
+
stress = 1 / inverse
|
|
15
|
+
assert isinstance(stress, Stress)
|
|
16
|
+
assert stress.to(StressUnit.Pa) == pytest.approx(2.0)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_stress_squared_from_force_stress_over_area():
|
|
20
|
+
force = Force(2.0, ForceUnit.N)
|
|
21
|
+
stress = Stress(3.0, StressUnit.Pa)
|
|
22
|
+
area = Area(6.0, AreaUnit.msq)
|
|
23
|
+
result = (force * stress) / area
|
|
24
|
+
assert isinstance(result, StressSquared)
|
|
25
|
+
assert result.to(StressSquaredUnit.Nsq_mmhc) == pytest.approx(1.0)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_force_stress_from_stress_squared_times_area():
|
|
29
|
+
stress_squared = StressSquared(2.0, StressSquaredUnit.Nsq_mmhc)
|
|
30
|
+
area = Area(3.0, AreaUnit.msq)
|
|
31
|
+
result = stress_squared * area
|
|
32
|
+
assert isinstance(result, ForceStress)
|
|
33
|
+
assert result.to(ForceStressUnit.Nsq_msq) == pytest.approx(6.0)
|
|
34
|
+
|
|
35
|
+
def test_power_operator():
|
|
36
|
+
a = Distance(2., DistanceUnit.mm)
|
|
37
|
+
b = a**2
|
|
38
|
+
assert b == Area(4., AreaUnit.mmsq)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_quantity_unsupported_rhs_object_raises_type_error():
|
|
42
|
+
force = Force(1., ForceUnit.N)
|
|
43
|
+
with pytest.raises(TypeError):
|
|
44
|
+
_ = force + object()
|
|
45
|
+
with pytest.raises(TypeError):
|
|
46
|
+
_ = force - object()
|
|
47
|
+
with pytest.raises(TypeError):
|
|
48
|
+
_ = force * object()
|
|
49
|
+
with pytest.raises(TypeError):
|
|
50
|
+
_ = force / object()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_quantity_unsupported_relation_raises_type_error():
|
|
54
|
+
force = Force(1., ForceUnit.N)
|
|
55
|
+
time = Time(1., TimeUnit.s)
|
|
56
|
+
with pytest.raises(TypeError):
|
|
57
|
+
_ = force * time
|
|
58
|
+
with pytest.raises(TypeError):
|
|
59
|
+
_ = force / time
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_quantity_invalid_power_raises_type_error():
|
|
63
|
+
force = Force(1., ForceUnit.N)
|
|
64
|
+
with pytest.raises(TypeError):
|
|
65
|
+
_ = force ** 2
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_quantity_left_vector_multiplication():
|
|
69
|
+
result = Force(1., ForceUnit.N) * Vector3.x()
|
|
70
|
+
assert result[0] == Force(1., ForceUnit.N)
|
|
71
|
+
assert result[1] == Force(0., ForceUnit.N)
|
|
72
|
+
assert result[2] == Force(0., ForceUnit.N)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_quantity_left_vector_multiplication_with_quantity_vector():
|
|
76
|
+
vec = Vector3(Distance(3., DistanceUnit.mm), Distance(4., DistanceUnit.mm), Distance(12., DistanceUnit.mm))
|
|
77
|
+
result = Force(2., ForceUnit.N) * vec
|
|
78
|
+
assert result[0].to(ForceDistanceUnit.Nm) == 0.006
|
|
79
|
+
assert result[1].to(ForceDistanceUnit.Nm) == 0.008
|
|
80
|
+
assert result[2].to(ForceDistanceUnit.Nm) == 0.024
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_quantity_left_vector_multiplication_unsupported_relation_raises_type_error():
|
|
84
|
+
vec = Vector3(Time(1., TimeUnit.s), Time(2., TimeUnit.s), Time(3., TimeUnit.s))
|
|
85
|
+
with pytest.raises(TypeError):
|
|
86
|
+
_ = Force(1., ForceUnit.N) * vec
|
|
@@ -53,7 +53,7 @@ class TestVectorAdd():
|
|
|
53
53
|
assert c[2] == 8.
|
|
54
54
|
|
|
55
55
|
def test_vector_add_failing(self):
|
|
56
|
-
with pytest.raises(
|
|
56
|
+
with pytest.raises(TypeError, match="The passed values must be of the same quantity."):
|
|
57
57
|
a = Vector3(1., 12., 3.)
|
|
58
58
|
b = Vector3(Distance(2., DistanceUnit.mm), Distance(2., DistanceUnit.mm), Distance(2., DistanceUnit.mm))
|
|
59
59
|
_ = a + b
|
|
@@ -80,7 +80,7 @@ def test_vector_shared_unit():
|
|
|
80
80
|
assert vec[2].to(ForceUnit.kN) == 3.
|
|
81
81
|
|
|
82
82
|
def test_vector_sub_failing():
|
|
83
|
-
with pytest.raises(
|
|
83
|
+
with pytest.raises(TypeError, match="The passed values must be of the same quantity."):
|
|
84
84
|
a = Vector3(1., 12., 3.)
|
|
85
85
|
b = Vector3(Distance(2., DistanceUnit.mm), Distance(2., DistanceUnit.mm), Distance(2., DistanceUnit.mm))
|
|
86
86
|
_ = a - b
|
|
@@ -185,6 +185,22 @@ def test_vector_mul():
|
|
|
185
185
|
assert res[1].to(ForceDistanceUnit.Nm) == 0.008
|
|
186
186
|
assert res[2].to(ForceDistanceUnit.Nm) == 0.024
|
|
187
187
|
|
|
188
|
+
def test_vector_float_mul_quantity():
|
|
189
|
+
res = Vector3.x() * Force(1., ForceUnit.N)
|
|
190
|
+
assert res[0] == Force(1., ForceUnit.N)
|
|
191
|
+
assert res[1] == Force(0., ForceUnit.N)
|
|
192
|
+
assert res[2] == Force(0., ForceUnit.N)
|
|
193
|
+
|
|
194
|
+
def test_vector_mul_unsupported_rhs_raises_type_error():
|
|
195
|
+
vec = Vector3(Distance(3., DistanceUnit.mm), Distance(4., DistanceUnit.mm), Distance(12., DistanceUnit.mm))
|
|
196
|
+
with pytest.raises(TypeError):
|
|
197
|
+
_ = vec * object()
|
|
198
|
+
|
|
199
|
+
def test_vector_mul_unsupported_quantity_relation_raises_type_error():
|
|
200
|
+
vec = Vector3(Force(3., ForceUnit.N), Force(4., ForceUnit.N), Force(12., ForceUnit.N))
|
|
201
|
+
with pytest.raises(TypeError):
|
|
202
|
+
_ = vec * Time(1., TimeUnit.s)
|
|
203
|
+
|
|
188
204
|
def test_vector_div():
|
|
189
205
|
vec = Vector3(Force(3., ForceUnit.N), Force(4., ForceUnit.N), Force(12., ForceUnit.N))
|
|
190
206
|
res = vec / Area(2., AreaUnit.mmsq)
|
|
@@ -192,6 +208,36 @@ def test_vector_div():
|
|
|
192
208
|
assert res[1].to(StressUnit.MPa) == 2.
|
|
193
209
|
assert res[2].to(StressUnit.MPa) == 6.
|
|
194
210
|
|
|
211
|
+
def test_vector_div_unsupported_rhs_raises_type_error():
|
|
212
|
+
vec = Vector3(Force(3., ForceUnit.N), Force(4., ForceUnit.N), Force(12., ForceUnit.N))
|
|
213
|
+
with pytest.raises(TypeError):
|
|
214
|
+
_ = vec / object()
|
|
215
|
+
|
|
216
|
+
def test_vector_imul_quantity_mutates_in_place():
|
|
217
|
+
vec = Vector3.x()
|
|
218
|
+
original_id = id(vec)
|
|
219
|
+
vec *= Force(2., ForceUnit.N)
|
|
220
|
+
assert id(vec) == original_id
|
|
221
|
+
assert vec[0] == Force(2., ForceUnit.N)
|
|
222
|
+
assert vec[1] == Force(0., ForceUnit.N)
|
|
223
|
+
assert vec[2] == Force(0., ForceUnit.N)
|
|
224
|
+
|
|
225
|
+
def test_vector_itruediv_quantity_mutates_in_place():
|
|
226
|
+
vec = Vector3(Force(3., ForceUnit.N), Force(4., ForceUnit.N), Force(12., ForceUnit.N))
|
|
227
|
+
original_id = id(vec)
|
|
228
|
+
vec /= Area(2., AreaUnit.mmsq)
|
|
229
|
+
assert id(vec) == original_id
|
|
230
|
+
assert vec[0].to(StressUnit.MPa) == 1.5
|
|
231
|
+
assert vec[1].to(StressUnit.MPa) == 2.
|
|
232
|
+
assert vec[2].to(StressUnit.MPa) == 6.
|
|
233
|
+
|
|
234
|
+
def test_vector_imul_unsupported_quantity_leaves_vector_unchanged():
|
|
235
|
+
vec = Vector3(Force(3., ForceUnit.N), Force(4., ForceUnit.N), Force(12., ForceUnit.N))
|
|
236
|
+
before = vec.to_list()
|
|
237
|
+
with pytest.raises(TypeError):
|
|
238
|
+
vec *= Time(1., TimeUnit.s)
|
|
239
|
+
assert vec.to_list() == before
|
|
240
|
+
|
|
195
241
|
def test_vector_matrix_zero():
|
|
196
242
|
mat = Matrix3.zero()
|
|
197
243
|
for i in range(3):
|
|
@@ -229,6 +275,13 @@ def test_matrix_str():
|
|
|
229
275
|
mat = Matrix3.from_list([[1, 3, 4], [5, 7, 3], [7, 8, 9]], ForceUnit.N)
|
|
230
276
|
assert str(mat) == 'Matrix3: [\n[ForceQuantity(1 N), ForceQuantity(3 N), ForceQuantity(4 N)]\n[ForceQuantity(5 N), ForceQuantity(7 N), ForceQuantity(3 N)]\n[ForceQuantity(7 N), ForceQuantity(8 N), ForceQuantity(9 N)]]'
|
|
231
277
|
|
|
278
|
+
def test_matrix_missing_binary_arithmetic_raises_type_error():
|
|
279
|
+
mat = Matrix3.zero()
|
|
280
|
+
with pytest.raises(TypeError):
|
|
281
|
+
_ = mat + mat
|
|
282
|
+
with pytest.raises(TypeError):
|
|
283
|
+
_ = mat * 2.
|
|
284
|
+
|
|
232
285
|
|
|
233
286
|
class TestMatrixListInterface:
|
|
234
287
|
def test_matrix_from_list(self):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "unitforge_macros"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.1"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Dr.Q team <info@askdrq.com>", "Henrik Stromberg <henrik@askdrq.com>"]
|
|
6
6
|
description = "Proc macros for generating units, quantities, and Python bindings in unitforge"
|
|
@@ -12,4 +12,4 @@ keywords = ["units", "quantities", "simulation", "physics"]
|
|
|
12
12
|
proc-macro = true
|
|
13
13
|
|
|
14
14
|
[dependencies]
|
|
15
|
-
regex = "1.
|
|
15
|
+
regex = "1.12.3"
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from unitforge import *
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def test_inverse_stress_from_stress_division():
|
|
6
|
-
stress = Stress(2.0, StressUnit.Pa)
|
|
7
|
-
inverse = 1 / stress
|
|
8
|
-
assert isinstance(inverse, InverseStress)
|
|
9
|
-
assert inverse.to(InverseStressUnit._Pa) == pytest.approx(0.5)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def test_stress_from_inverse_stress_division():
|
|
13
|
-
inverse = InverseStress(0.5, InverseStressUnit._Pa)
|
|
14
|
-
stress = 1 / inverse
|
|
15
|
-
assert isinstance(stress, Stress)
|
|
16
|
-
assert stress.to(StressUnit.Pa) == pytest.approx(2.0)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def test_stress_squared_from_force_stress_over_area():
|
|
20
|
-
force = Force(2.0, ForceUnit.N)
|
|
21
|
-
stress = Stress(3.0, StressUnit.Pa)
|
|
22
|
-
area = Area(6.0, AreaUnit.msq)
|
|
23
|
-
result = (force * stress) / area
|
|
24
|
-
assert isinstance(result, StressSquared)
|
|
25
|
-
assert result.to(StressSquaredUnit.Nsq_mmhc) == pytest.approx(1.0)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def test_force_stress_from_stress_squared_times_area():
|
|
29
|
-
stress_squared = StressSquared(2.0, StressSquaredUnit.Nsq_mmhc)
|
|
30
|
-
area = Area(3.0, AreaUnit.msq)
|
|
31
|
-
result = stress_squared * area
|
|
32
|
-
assert isinstance(result, ForceStress)
|
|
33
|
-
assert result.to(ForceStressUnit.Nsq_msq) == pytest.approx(6.0)
|
|
34
|
-
|
|
35
|
-
def test_power_operator():
|
|
36
|
-
a = Distance(2., DistanceUnit.mm)
|
|
37
|
-
b = a**2
|
|
38
|
-
assert b == Area(4., AreaUnit.mmsq)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|