autoforge 1.9.2__tar.gz → 1.9.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.
- {autoforge-1.9.2 → autoforge-1.9.2.2}/PKG-INFO +1 -1
- {autoforge-1.9.2 → autoforge-1.9.2.2}/pyproject.toml +1 -1
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/AutoForge.egg-info/PKG-INFO +1 -1
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/AutoForge.egg-info/SOURCES.txt +2 -0
- autoforge-1.9.2.2/src/autoforge/Helper/AmpUtils.py +123 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/OptimizerHelper.py +18 -14
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/auto_forge.py +1 -4
- autoforge-1.9.2.2/tests/test_amp_autocast_probe.py +52 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/README.md +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/setup.cfg +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/AutoForge.egg-info/dependency_links.txt +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/AutoForge.egg-info/entry_points.txt +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/AutoForge.egg-info/requires.txt +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/AutoForge.egg-info/top_level.txt +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/CAdamW.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/FilamentHelper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/Heightmaps/ChristofidesHeightMap.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/Heightmaps/DepthEstimateHeightMap.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/Heightmaps/__init__.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/ImageHelper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/OtherHelper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/OutputHelper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/PruningHelper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/__init__.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/filamentcolors_library.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Init_comparer/__init__.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Init_comparer/auto_forge_init_check.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Init_comparer/plot_results.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Loss/LossFunctions.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Loss/PerceptionLoss.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Loss/__init__.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Misc/__init__.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Misc/color_fit_all.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Modules/Optimizer.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Modules/__init__.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/__init__.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/__main__.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/priority_mask_editor.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_cadamw.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_christofides_height_map.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_christofides_heightmap.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_depth_adjusted_height_map.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_depth_estimate_height_map.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_end_to_end_optimizer.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_filament_and_output.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_filament_helper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_flatforge.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_heightmap_init.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_image_helper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_loss_functions.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_optimizer_helper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_optimizer_module.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_optimizer_smoke.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_output_helper.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_output_helper_extended.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_perception_and_pruning.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_property_based.py +0 -0
- {autoforge-1.9.2 → autoforge-1.9.2.2}/tests/test_pruning_helper.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "AutoForge"
|
|
7
|
-
version="1.9.2"
|
|
7
|
+
version="1.9.2.2"
|
|
8
8
|
description = "AutoForge is a Python tool for generating 3D printed layered models from an input image."
|
|
9
9
|
authors = [{ name = "Hendric Voss", email = "hvoss@techfak.uni-bielefeld.de" }]
|
|
10
10
|
readme = "README.md"
|
|
@@ -10,6 +10,7 @@ src/autoforge/__init__.py
|
|
|
10
10
|
src/autoforge/__main__.py
|
|
11
11
|
src/autoforge/auto_forge.py
|
|
12
12
|
src/autoforge/priority_mask_editor.py
|
|
13
|
+
src/autoforge/Helper/AmpUtils.py
|
|
13
14
|
src/autoforge/Helper/CAdamW.py
|
|
14
15
|
src/autoforge/Helper/FilamentHelper.py
|
|
15
16
|
src/autoforge/Helper/ImageHelper.py
|
|
@@ -32,6 +33,7 @@ src/autoforge/Misc/__init__.py
|
|
|
32
33
|
src/autoforge/Misc/color_fit_all.py
|
|
33
34
|
src/autoforge/Modules/Optimizer.py
|
|
34
35
|
src/autoforge/Modules/__init__.py
|
|
36
|
+
tests/test_amp_autocast_probe.py
|
|
35
37
|
tests/test_cadamw.py
|
|
36
38
|
tests/test_christofides_height_map.py
|
|
37
39
|
tests/test_christofides_heightmap.py
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import nullcontext, contextmanager
|
|
4
|
+
from typing import Optional, Tuple, Dict
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
# Cache the selection per device key so CPU/CUDA/MPS don't interfere
|
|
10
|
+
_SELECTED_MAP: Dict[str, Tuple[Optional[torch.dtype], str]] = {}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _device_key(device: torch.device) -> str:
|
|
14
|
+
if device.type == "cuda":
|
|
15
|
+
idx = device.index if device.index is not None else torch.cuda.current_device()
|
|
16
|
+
return f"cuda:{idx}"
|
|
17
|
+
return device.type
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _probe_backward(device: torch.device, dtype: torch.dtype) -> bool:
|
|
21
|
+
"""Try a tiny forward+backward under autocast for given dtype on the device.
|
|
22
|
+
|
|
23
|
+
Returns True if successful, False if a RuntimeError occurs (e.g., oneDNN bf16/f16 backward not supported).
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
if device.type == "mps":
|
|
27
|
+
return False # no stable autocast on MPS yet
|
|
28
|
+
x = torch.randn(8, 8, device=device, dtype=torch.float32, requires_grad=False)
|
|
29
|
+
w = torch.randn(8, 8, device=device, dtype=torch.float32, requires_grad=True)
|
|
30
|
+
target = torch.randn(8, 8, device=device, dtype=torch.float32)
|
|
31
|
+
with torch.autocast(device_type=device.type, dtype=dtype):
|
|
32
|
+
y = x @ w
|
|
33
|
+
loss = torch.nn.functional.mse_loss(y, target)
|
|
34
|
+
loss.backward()
|
|
35
|
+
# Step with a dummy optimizer to ensure gradients are usable
|
|
36
|
+
opt = torch.optim.SGD([w], lr=1e-3)
|
|
37
|
+
opt.step()
|
|
38
|
+
return True
|
|
39
|
+
except Exception:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _select_dtype_for_device(device: torch.device) -> Tuple[Optional[torch.dtype], str]:
|
|
44
|
+
"""Decide the best autocast dtype for the current device, probing backward support.
|
|
45
|
+
|
|
46
|
+
Returns (dtype or None, reason string).
|
|
47
|
+
None means: do not use autocast; stick to full float32.
|
|
48
|
+
"""
|
|
49
|
+
# Allow override via env for debugging
|
|
50
|
+
mode = os.getenv("AUTOFORGE_AMP", default="auto").lower()
|
|
51
|
+
|
|
52
|
+
if mode in {"off", "fp32", "float32"}:
|
|
53
|
+
print("AUTOFORGE_AMP=off: disabling automatic mixed precision")
|
|
54
|
+
return None, "AUTOFORGE_AMP=off"
|
|
55
|
+
if mode in {"bf16", "bfloat16"}:
|
|
56
|
+
# Only honor if probe succeeds
|
|
57
|
+
if _probe_backward(device, torch.bfloat16):
|
|
58
|
+
return torch.bfloat16, "forced bf16"
|
|
59
|
+
print("AUTOFORGE_AMP=bf16: disabling bf16 as probe failed")
|
|
60
|
+
return None, "forced bf16 but probe failed"
|
|
61
|
+
if mode in {"fp16", "float16", "half"}:
|
|
62
|
+
if device.type == "cuda" and _probe_backward(device, torch.float16):
|
|
63
|
+
return torch.float16, "forced f16"
|
|
64
|
+
print("AUTOFORGE_AMP=fp16: disabling fp16 as probe failed or non-CUDA")
|
|
65
|
+
return None, "forced f16 but probe failed or non-CUDA"
|
|
66
|
+
|
|
67
|
+
# Automatic selection
|
|
68
|
+
if device.type == "cuda":
|
|
69
|
+
# Prefer bf16 if it actually works end-to-end
|
|
70
|
+
if torch.cuda.is_bf16_supported() and _probe_backward(device, torch.bfloat16):
|
|
71
|
+
print("Using bf16 autocast on CUDA device")
|
|
72
|
+
return torch.bfloat16, "cuda bf16"
|
|
73
|
+
# Next try fp16 if the GPU supports it
|
|
74
|
+
major, minor = torch.cuda.get_device_capability(device)
|
|
75
|
+
if (major * 10 + minor) >= 53 and _probe_backward(device, torch.float16):
|
|
76
|
+
print("Using fp16 autocast on CUDA device")
|
|
77
|
+
return torch.float16, "cuda f16"
|
|
78
|
+
# Fall back to fp32; enable TF32 to speed up matmuls if available
|
|
79
|
+
torch.backends.cuda.matmul.allow_tf32 = True
|
|
80
|
+
torch.backends.cudnn.allow_tf32 = True
|
|
81
|
+
print("Falling back to fp32 autocast on CUDA device with TF32 enabled")
|
|
82
|
+
return None, "cuda fp32 (tf32 on)"
|
|
83
|
+
|
|
84
|
+
if device.type == "cpu":
|
|
85
|
+
# Some CPUs (AVX512) can do bf16 forward/backward with oneDNN, but many cannot.
|
|
86
|
+
# Probe and use bf16 only if backward succeeds; otherwise stay fp32.
|
|
87
|
+
if _probe_backward(device, torch.bfloat16):
|
|
88
|
+
print("Using bf16 autocast on CPU device")
|
|
89
|
+
return torch.bfloat16, "cpu bf16"
|
|
90
|
+
# No reliable fp16 on CPU for backward; use fp32
|
|
91
|
+
print("Falling back to fp32 autocast on CPU device")
|
|
92
|
+
return None, "cpu fp32"
|
|
93
|
+
|
|
94
|
+
if device.type == "mps":
|
|
95
|
+
print("MPS device detected: autocast not reliably supported, using fp32")
|
|
96
|
+
return None, "mps fp32"
|
|
97
|
+
|
|
98
|
+
# Default: no autocast
|
|
99
|
+
print(f"Unknown device type '{device.type}': disabling autocast, using fp32")
|
|
100
|
+
return None, f"{device.type} fp32"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_selected_autocast(device: torch.device) -> Tuple[Optional[torch.dtype], str]:
|
|
104
|
+
key = _device_key(device)
|
|
105
|
+
if key not in _SELECTED_MAP:
|
|
106
|
+
_SELECTED_MAP[key] = _select_dtype_for_device(device)
|
|
107
|
+
return _SELECTED_MAP[key]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@contextmanager
|
|
111
|
+
def safe_autocast(device: torch.device):
|
|
112
|
+
"""Context manager that uses autocast only if the runtime probe succeeded.
|
|
113
|
+
|
|
114
|
+
Falls back to a no-op context on unsupported platforms to avoid runtime errors like:
|
|
115
|
+
RuntimeError: DNNL does not support bf16/f16 backward on the platform with avx2_vnni_2.
|
|
116
|
+
"""
|
|
117
|
+
dtype, _reason = get_selected_autocast(device)
|
|
118
|
+
if dtype is None:
|
|
119
|
+
with nullcontext():
|
|
120
|
+
yield
|
|
121
|
+
else:
|
|
122
|
+
with torch.autocast(device_type=device.type, dtype=dtype):
|
|
123
|
+
yield
|
|
@@ -2,6 +2,7 @@ from contextlib import contextmanager
|
|
|
2
2
|
|
|
3
3
|
import torch
|
|
4
4
|
import torch.nn.functional as F
|
|
5
|
+
from autoforge.Helper.AmpUtils import get_selected_autocast
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
@torch.jit.script
|
|
@@ -343,22 +344,25 @@ class PrecisionManager:
|
|
|
343
344
|
self.autocast_dtype = None
|
|
344
345
|
self.enabled = False
|
|
345
346
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
347
|
+
# Decide dtype once using the shared runtime probe
|
|
348
|
+
dtype, _reason = get_selected_autocast(device)
|
|
349
|
+
self.autocast_dtype = dtype
|
|
350
|
+
# Enable only when a non-None dtype is selected and device supports native CUDA autocast
|
|
351
|
+
self.enabled = dtype is not None and device.type == "cuda"
|
|
352
|
+
|
|
353
|
+
# Use GradScaler only for CUDA float16; bf16 does not need scaling
|
|
354
|
+
if self.enabled and dtype == torch.float16:
|
|
355
|
+
self.scaler = torch.cuda.amp.GradScaler()
|
|
356
|
+
else:
|
|
357
|
+
self.scaler = None
|
|
358
|
+
|
|
359
|
+
# Optional: If CUDA but no AMP selected, allow TF32 for speed on Ampere+
|
|
360
|
+
if device.type == "cuda" and dtype is None:
|
|
361
|
+
try:
|
|
360
362
|
torch.backends.cuda.matmul.allow_tf32 = True
|
|
361
363
|
torch.backends.cudnn.allow_tf32 = True
|
|
364
|
+
except Exception:
|
|
365
|
+
pass
|
|
362
366
|
|
|
363
367
|
@contextmanager
|
|
364
368
|
def autocast(self):
|
|
@@ -191,7 +191,6 @@ def parse_args() -> argparse.Namespace:
|
|
|
191
191
|
|
|
192
192
|
parser.add_argument(
|
|
193
193
|
"--perform_pruning",
|
|
194
|
-
type=bool,
|
|
195
194
|
default=True,
|
|
196
195
|
help="Perform pruning after optimization",
|
|
197
196
|
action=argparse.BooleanOptionalAction,
|
|
@@ -199,7 +198,6 @@ def parse_args() -> argparse.Namespace:
|
|
|
199
198
|
|
|
200
199
|
parser.add_argument(
|
|
201
200
|
"--fast_pruning",
|
|
202
|
-
type=bool,
|
|
203
201
|
default=True,
|
|
204
202
|
help="Use fast pruning method",
|
|
205
203
|
action=argparse.BooleanOptionalAction,
|
|
@@ -289,7 +287,6 @@ def parse_args() -> argparse.Namespace:
|
|
|
289
287
|
|
|
290
288
|
parser.add_argument(
|
|
291
289
|
"--flatforge",
|
|
292
|
-
type=bool,
|
|
293
290
|
default=False,
|
|
294
291
|
help="Enable FlatForge mode to generate separate STL files for each color",
|
|
295
292
|
action=argparse.BooleanOptionalAction,
|
|
@@ -461,7 +458,7 @@ def _prepare_background_and_materials(
|
|
|
461
458
|
Returns:
|
|
462
459
|
(bgr_tuple_uint8, background_tensor, material_colors_tensor, material_TDs_tensor)
|
|
463
460
|
"""
|
|
464
|
-
bgr_tuple = hex_to_rgb(args.background_color)
|
|
461
|
+
bgr_tuple = tuple(hex_to_rgb(args.background_color))
|
|
465
462
|
background = torch.tensor(bgr_tuple, dtype=torch.float32, device=device)
|
|
466
463
|
material_colors = torch.tensor(
|
|
467
464
|
material_colors_np, dtype=torch.float32, device=device
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import torch
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from autoforge.Helper.AmpUtils import safe_autocast, get_selected_autocast
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _device_from_env():
|
|
9
|
+
if torch.cuda.is_available():
|
|
10
|
+
return torch.device("cuda")
|
|
11
|
+
if torch.backends.mps.is_available():
|
|
12
|
+
return torch.device("mps")
|
|
13
|
+
return torch.device("cpu")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_safe_autocast_backward_does_not_crash():
|
|
17
|
+
# Ensure automatic mode
|
|
18
|
+
os.environ.pop("AUTOFORGE_AMP", None)
|
|
19
|
+
device = _device_from_env()
|
|
20
|
+
|
|
21
|
+
x = torch.randn(16, 16, device=device, dtype=torch.float32)
|
|
22
|
+
w = torch.randn(16, 16, device=device, dtype=torch.float32, requires_grad=True)
|
|
23
|
+
target = torch.randn(16, 16, device=device, dtype=torch.float32)
|
|
24
|
+
|
|
25
|
+
dtype, _reason = get_selected_autocast(device)
|
|
26
|
+
# Should not raise regardless of dtype selection
|
|
27
|
+
with safe_autocast(device):
|
|
28
|
+
y = x @ w
|
|
29
|
+
loss = torch.nn.functional.mse_loss(y, target)
|
|
30
|
+
loss.backward()
|
|
31
|
+
|
|
32
|
+
# If CUDA fp16 was selected, ensure grads exist and are finite
|
|
33
|
+
if device.type == "cuda" and dtype == torch.float16:
|
|
34
|
+
assert w.grad is not None
|
|
35
|
+
assert torch.isfinite(w.grad).all()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("force", ["off", "bf16", "fp16"]) # exercise overrides
|
|
39
|
+
def test_env_override_modes(monkeypatch, force):
|
|
40
|
+
monkeypatch.setenv("AUTOFORGE_AMP", force)
|
|
41
|
+
device = _device_from_env()
|
|
42
|
+
|
|
43
|
+
x = torch.randn(8, 8, device=device, dtype=torch.float32)
|
|
44
|
+
w = torch.randn(8, 8, device=device, dtype=torch.float32, requires_grad=True)
|
|
45
|
+
target = torch.randn(8, 8, device=device, dtype=torch.float32)
|
|
46
|
+
|
|
47
|
+
with safe_autocast(device):
|
|
48
|
+
y = x @ w
|
|
49
|
+
loss = torch.nn.functional.mse_loss(y, target)
|
|
50
|
+
loss.backward()
|
|
51
|
+
assert w.grad is not None
|
|
52
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/Heightmaps/ChristofidesHeightMap.py
RENAMED
|
File without changes
|
{autoforge-1.9.2 → autoforge-1.9.2.2}/src/autoforge/Helper/Heightmaps/DepthEstimateHeightMap.py
RENAMED
|
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
|