halib 0.2.19__tar.gz → 0.2.21__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.
- {halib-0.2.19 → halib-0.2.21}/PKG-INFO +6 -1
- {halib-0.2.19 → halib-0.2.21}/README.md +5 -0
- {halib-0.2.19 → halib-0.2.21}/halib/__init__.py +11 -5
- {halib-0.2.19 → halib-0.2.21}/halib/common/common.py +29 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/perf/profiler.py +131 -12
- {halib-0.2.19 → halib-0.2.21}/halib.egg-info/PKG-INFO +6 -1
- {halib-0.2.19 → halib-0.2.21}/setup.py +1 -1
- {halib-0.2.19 → halib-0.2.21}/.gitignore +0 -0
- {halib-0.2.19 → halib-0.2.21}/GDriveFolder.txt +0 -0
- {halib-0.2.19 → halib-0.2.21}/LICENSE.txt +0 -0
- {halib-0.2.19 → halib-0.2.21}/MANIFEST.in +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/common/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/common/rich_color.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/core/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/core/base_config.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/core/base_exp.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/core/param_gen.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/core/wandb_op.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/data/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/data/dataclass_util.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/data/dataset.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/data/torchloader.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/perf/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/perf/flop_calc.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/perf/gpu_mon.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/perf/perfcalc.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/perf/perfmetrics.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/perf/perftb.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/viz/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/exp/viz/plot.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/filetype/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/filetype/csvfile.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/filetype/ipynb.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/filetype/jsonfile.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/filetype/textfile.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/filetype/videofile.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/filetype/yamlfile.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/online/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/online/gdrive.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/online/gdrive_mkdir.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/online/projectmake.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/online/tele_noti.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/system/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/system/_list_pc.csv +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/system/cmd.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/system/filesys.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/system/path.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/utils/__init__.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/utils/dict.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib/utils/list.py +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib.egg-info/SOURCES.txt +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib.egg-info/dependency_links.txt +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib.egg-info/requires.txt +0 -0
- {halib-0.2.19 → halib-0.2.21}/halib.egg-info/top_level.txt +0 -0
- {halib-0.2.19 → halib-0.2.21}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: halib
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.21
|
|
4
4
|
Summary: Small library for common tasks
|
|
5
5
|
Author: Hoang Van Ha
|
|
6
6
|
Author-email: hoangvanhauit@gmail.com
|
|
@@ -56,6 +56,11 @@ Dynamic: summary
|
|
|
56
56
|
|
|
57
57
|
## v0.2.x (Experiment & Core Updates)
|
|
58
58
|
|
|
59
|
+
### **v0.2.21**
|
|
60
|
+
- ✨ **New Feature:** Added `common.common.pprint_stack_trace` to print stack traces with optional custom messages and force stop capability.
|
|
61
|
+
|
|
62
|
+
- 🚀 **Improvement:** `exp.perf.profiler` - allow to export *report dict* as csv files for further analysis
|
|
63
|
+
|
|
59
64
|
### **v0.2.19**
|
|
60
65
|
- ✨ **New Feature:** Added `exp.core.param_gen` to facilitate fast generation of parameter combination sweeps (grid search) using YAML configurations.
|
|
61
66
|
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## v0.2.x (Experiment & Core Updates)
|
|
4
4
|
|
|
5
|
+
### **v0.2.21**
|
|
6
|
+
- ✨ **New Feature:** Added `common.common.pprint_stack_trace` to print stack traces with optional custom messages and force stop capability.
|
|
7
|
+
|
|
8
|
+
- 🚀 **Improvement:** `exp.perf.profiler` - allow to export *report dict* as csv files for further analysis
|
|
9
|
+
|
|
5
10
|
### **v0.2.19**
|
|
6
11
|
- ✨ **New Feature:** Added `exp.core.param_gen` to facilitate fast generation of parameter combination sweeps (grid search) using YAML configurations.
|
|
7
12
|
|
|
@@ -19,11 +19,11 @@ __all__ = [
|
|
|
19
19
|
"os",
|
|
20
20
|
"pd",
|
|
21
21
|
"plt",
|
|
22
|
-
"pprint",
|
|
23
22
|
"pprint_box",
|
|
24
23
|
"pprint_local_path",
|
|
24
|
+
"pprint_stack_trace",
|
|
25
|
+
"pprint",
|
|
25
26
|
"px",
|
|
26
|
-
"pprint_local_path",
|
|
27
27
|
"rcolor_all_str",
|
|
28
28
|
"rcolor_palette_all",
|
|
29
29
|
"rcolor_palette",
|
|
@@ -32,10 +32,10 @@ __all__ = [
|
|
|
32
32
|
"rprint",
|
|
33
33
|
"sns",
|
|
34
34
|
"tcuda",
|
|
35
|
+
"time",
|
|
35
36
|
"timebudget",
|
|
36
37
|
"tqdm",
|
|
37
38
|
"warnings",
|
|
38
|
-
"time",
|
|
39
39
|
]
|
|
40
40
|
import warnings
|
|
41
41
|
|
|
@@ -64,7 +64,8 @@ from .common.common import (
|
|
|
64
64
|
norm_str,
|
|
65
65
|
pprint_box,
|
|
66
66
|
pprint_local_path,
|
|
67
|
-
|
|
67
|
+
pprint_stack_trace,
|
|
68
|
+
tcuda,
|
|
68
69
|
)
|
|
69
70
|
|
|
70
71
|
# for log
|
|
@@ -76,7 +77,12 @@ from timebudget import timebudget
|
|
|
76
77
|
import omegaconf
|
|
77
78
|
from omegaconf import OmegaConf
|
|
78
79
|
from omegaconf.dictconfig import DictConfig
|
|
79
|
-
from .common.rich_color import
|
|
80
|
+
from .common.rich_color import (
|
|
81
|
+
rcolor_str,
|
|
82
|
+
rcolor_palette,
|
|
83
|
+
rcolor_palette_all,
|
|
84
|
+
rcolor_all_str,
|
|
85
|
+
)
|
|
80
86
|
|
|
81
87
|
# for visualization
|
|
82
88
|
import seaborn as sns
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import sys
|
|
2
3
|
import re
|
|
3
4
|
import arrow
|
|
4
5
|
import importlib
|
|
@@ -10,6 +11,8 @@ from rich.console import Console
|
|
|
10
11
|
from rich.pretty import pprint, Pretty
|
|
11
12
|
|
|
12
13
|
from pathlib import Path, PureWindowsPath
|
|
14
|
+
from typing import Optional
|
|
15
|
+
from loguru import logger
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
console = Console()
|
|
@@ -114,6 +117,32 @@ def linux_to_wins_path(path: str) -> str:
|
|
|
114
117
|
return str(PureWindowsPath(win_path))
|
|
115
118
|
|
|
116
119
|
|
|
120
|
+
DEFAULT_STACK_TRACE_MSG = "Caused by halib.common.common.pprint_stack_trace"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def pprint_stack_trace(
|
|
124
|
+
msg: str = DEFAULT_STACK_TRACE_MSG,
|
|
125
|
+
e: Optional[Exception] = None,
|
|
126
|
+
force_stop: bool = False,
|
|
127
|
+
):
|
|
128
|
+
"""
|
|
129
|
+
Print the current stack trace or the stack trace of an exception.
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
if e is not None:
|
|
133
|
+
raise e
|
|
134
|
+
else:
|
|
135
|
+
raise Exception("pprint_stack_trace called")
|
|
136
|
+
except Exception as e:
|
|
137
|
+
# attach the exception trace to a warning
|
|
138
|
+
global DEFAULT_STACK_TRACE_MSG
|
|
139
|
+
if len(msg.strip()) == 0:
|
|
140
|
+
msg = DEFAULT_STACK_TRACE_MSG
|
|
141
|
+
logger.opt(exception=e).warning(msg)
|
|
142
|
+
if force_stop:
|
|
143
|
+
console.rule("[red]Force Stop Triggered in <halib.common.pprint_stack_trace>[/red]")
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
|
|
117
146
|
def pprint_local_path(
|
|
118
147
|
local_path: str, get_wins_path: bool = False, tag: str = ""
|
|
119
148
|
) -> str:
|
|
@@ -182,9 +182,9 @@ class zProfiler:
|
|
|
182
182
|
│ │ │ [278091.230944486, 278091.251378469],
|
|
183
183
|
│ }
|
|
184
184
|
"""
|
|
185
|
-
assert (
|
|
186
|
-
|
|
187
|
-
)
|
|
185
|
+
assert len(ctx_step_dict.keys()) > 0, (
|
|
186
|
+
"step_dict must have only one key (step_name) for detail."
|
|
187
|
+
)
|
|
188
188
|
normed_ctx_step_dict = {}
|
|
189
189
|
for step_name, time_list in ctx_step_dict.items():
|
|
190
190
|
if not isinstance(ctx_step_dict[step_name], list):
|
|
@@ -246,14 +246,126 @@ class zProfiler:
|
|
|
246
246
|
report_dict[ctx_name]["step_dict"]["summary"]["avg_time"][
|
|
247
247
|
f"avg_{step_name}"
|
|
248
248
|
] = avg_time
|
|
249
|
-
report_dict[ctx_name]["step_dict"]["summary"][
|
|
250
|
-
|
|
251
|
-
|
|
249
|
+
report_dict[ctx_name]["step_dict"]["summary"]["total_avg_time"] = (
|
|
250
|
+
total_avg_time
|
|
251
|
+
)
|
|
252
252
|
report_dict[ctx_name]["step_dict"]["summary"] = dict(
|
|
253
253
|
sorted(report_dict[ctx_name]["step_dict"]["summary"].items())
|
|
254
254
|
)
|
|
255
255
|
return report_dict
|
|
256
256
|
|
|
257
|
+
def get_report_dataframes(self):
|
|
258
|
+
"""
|
|
259
|
+
Returns two pandas DataFrames containing profiling data.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
tuple: (df_summary, df_detail)
|
|
263
|
+
- df_summary: Aggregated stats (Context, Step, Avg, %, Count)
|
|
264
|
+
- df_detail: Raw duration for every single iteration
|
|
265
|
+
"""
|
|
266
|
+
try:
|
|
267
|
+
import pandas as pd
|
|
268
|
+
except ImportError:
|
|
269
|
+
logger.error(
|
|
270
|
+
"Pandas is required for get_pandas_dfs(). Please pip install pandas."
|
|
271
|
+
)
|
|
272
|
+
return None, None
|
|
273
|
+
|
|
274
|
+
# Get full data structure
|
|
275
|
+
data = self.get_report_dict(with_detail=True)
|
|
276
|
+
|
|
277
|
+
summary_rows = []
|
|
278
|
+
detail_rows = []
|
|
279
|
+
|
|
280
|
+
for ctx_name, ctx_data in data.items():
|
|
281
|
+
summary = ctx_data["step_dict"]["summary"]
|
|
282
|
+
detail_dict = ctx_data["step_dict"]["detail"]
|
|
283
|
+
|
|
284
|
+
# --- 1. Build Summary Data ---
|
|
285
|
+
# Iterate keys in 'avg_time' to ensure we capture all steps
|
|
286
|
+
for avg_key, avg_val in summary["avg_time"].items():
|
|
287
|
+
step_name = avg_key.replace("avg_", "")
|
|
288
|
+
|
|
289
|
+
# Get corresponding percent
|
|
290
|
+
per_key = f"per_{step_name}"
|
|
291
|
+
percent_val = summary["percent_time"].get(per_key, 0.0)
|
|
292
|
+
|
|
293
|
+
# Get sample count from detail list length
|
|
294
|
+
count = len(detail_dict.get(step_name, []))
|
|
295
|
+
|
|
296
|
+
summary_rows.append(
|
|
297
|
+
{
|
|
298
|
+
"context_name": ctx_name,
|
|
299
|
+
"step_name": step_name,
|
|
300
|
+
"avg_time_sec": avg_val,
|
|
301
|
+
"percent_total": percent_val,
|
|
302
|
+
"sample_count": count,
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# --- 2. Build Detail Data ---
|
|
307
|
+
for step_name, time_list in detail_dict.items():
|
|
308
|
+
# time_list format: [(idx, duration), (idx, duration)...]
|
|
309
|
+
for item in time_list:
|
|
310
|
+
if len(item) == 2:
|
|
311
|
+
idx, duration = item
|
|
312
|
+
detail_rows.append(
|
|
313
|
+
{
|
|
314
|
+
"context_name": ctx_name,
|
|
315
|
+
"step_name": step_name,
|
|
316
|
+
"iteration_idx": idx,
|
|
317
|
+
"duration_sec": duration,
|
|
318
|
+
}
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# Create DataFrames
|
|
322
|
+
df_summary = pd.DataFrame(summary_rows)
|
|
323
|
+
df_detail = pd.DataFrame(detail_rows)
|
|
324
|
+
|
|
325
|
+
# Reorder columns for readability (optional but nice)
|
|
326
|
+
if not df_summary.empty:
|
|
327
|
+
df_summary = df_summary[
|
|
328
|
+
[
|
|
329
|
+
"context_name",
|
|
330
|
+
"step_name",
|
|
331
|
+
"avg_time_sec",
|
|
332
|
+
"percent_total",
|
|
333
|
+
"sample_count",
|
|
334
|
+
]
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
if not df_detail.empty:
|
|
338
|
+
df_detail = df_detail[
|
|
339
|
+
["context_name", "step_name", "iteration_idx", "duration_sec"]
|
|
340
|
+
]
|
|
341
|
+
|
|
342
|
+
return df_summary, df_detail
|
|
343
|
+
|
|
344
|
+
def get_report_csv_files(self, outdir, tag="profiler"):
|
|
345
|
+
"""
|
|
346
|
+
Exports profiling data to two CSV files:
|
|
347
|
+
1. {tag}_summary.csv: Aggregated stats (Avg time, %)
|
|
348
|
+
2. {tag}_detailed_logs.csv: Raw duration for every iteration
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
outdir (str): Directory to save files.
|
|
352
|
+
tag (str): Optional prefix for filenames.
|
|
353
|
+
"""
|
|
354
|
+
|
|
355
|
+
if not os.path.exists(outdir):
|
|
356
|
+
os.makedirs(outdir)
|
|
357
|
+
tag_str = f"{tag}_" if tag else ""
|
|
358
|
+
summary_file = os.path.join(outdir, f"{tag_str}summary.csv")
|
|
359
|
+
detail_file = os.path.join(outdir, f"{tag_str}detailed_logs.csv")
|
|
360
|
+
|
|
361
|
+
df_summary, df_detail = self.get_report_dataframes()
|
|
362
|
+
if df_summary is not None:
|
|
363
|
+
df_summary.to_csv(summary_file, index=False, sep=";", encoding="utf-8")
|
|
364
|
+
logger.info(f"Saved summary CSV to: {summary_file}")
|
|
365
|
+
if df_detail is not None:
|
|
366
|
+
df_detail.to_csv(detail_file, index=False, sep=";", encoding="utf-8")
|
|
367
|
+
logger.info(f"Saved detailed logs CSV to: {detail_file}")
|
|
368
|
+
|
|
257
369
|
@classmethod
|
|
258
370
|
def plot_formatted_data(
|
|
259
371
|
cls, profiler_data, outdir=None, file_format="png", do_show=False, tag=""
|
|
@@ -282,9 +394,13 @@ class zProfiler:
|
|
|
282
394
|
|
|
283
395
|
assert n_steps > 0, "No steps found for context: {}".format(ctx)
|
|
284
396
|
# Generate dynamic colors
|
|
285
|
-
colors =
|
|
286
|
-
|
|
287
|
-
|
|
397
|
+
colors = (
|
|
398
|
+
px.colors.sample_colorscale(
|
|
399
|
+
"Viridis", [i / (n_steps - 1) for i in range(n_steps)]
|
|
400
|
+
)
|
|
401
|
+
if n_steps > 1
|
|
402
|
+
else [px.colors.sample_colorscale("Viridis", [0])[0]]
|
|
403
|
+
)
|
|
288
404
|
# pprint(f'{len(colors)} colors generated for {n_steps} steps')
|
|
289
405
|
color_map = dict(zip(step_names, colors))
|
|
290
406
|
|
|
@@ -301,7 +417,7 @@ class zProfiler:
|
|
|
301
417
|
go.Bar(
|
|
302
418
|
x=step_names,
|
|
303
419
|
y=list(avg_times.values()),
|
|
304
|
-
text=[f"{v*1000:.2f} ms" for v in avg_times.values()],
|
|
420
|
+
text=[f"{v * 1000:.2f} ms" for v in avg_times.values()],
|
|
305
421
|
textposition="outside",
|
|
306
422
|
marker=dict(color=[color_map[s] for s in step_names]),
|
|
307
423
|
name="", # unified legend
|
|
@@ -345,7 +461,9 @@ class zProfiler:
|
|
|
345
461
|
# Save figure
|
|
346
462
|
if outdir is not None:
|
|
347
463
|
file_prefix = ctx if len(tag_str) == 0 else f"{tag_str}_{ctx}"
|
|
348
|
-
file_path = os.path.join(
|
|
464
|
+
file_path = os.path.join(
|
|
465
|
+
outdir, f"{file_prefix}_summary.{file_format.lower()}"
|
|
466
|
+
)
|
|
349
467
|
fig.write_image(file_path)
|
|
350
468
|
pprint(f"Saved figure to: 🔽")
|
|
351
469
|
pprint_local_path(file_path)
|
|
@@ -368,6 +486,7 @@ class zProfiler:
|
|
|
368
486
|
return self.plot_formatted_data(
|
|
369
487
|
report, outdir=outdir, file_format=file_format, do_show=do_show, tag=tag
|
|
370
488
|
)
|
|
489
|
+
|
|
371
490
|
def meta_info(self):
|
|
372
491
|
"""
|
|
373
492
|
Print the structure of the profiler's time dictionary.
|
|
@@ -375,7 +494,7 @@ class zProfiler:
|
|
|
375
494
|
"""
|
|
376
495
|
for ctx_name, ctx_dict in self.time_dict.items():
|
|
377
496
|
with ConsoleLog(f"Context: {ctx_name}"):
|
|
378
|
-
step_names = list(ctx_dict[
|
|
497
|
+
step_names = list(ctx_dict["step_dict"].keys())
|
|
379
498
|
for step_name in step_names:
|
|
380
499
|
pprint(f"Step: {step_name}")
|
|
381
500
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: halib
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.21
|
|
4
4
|
Summary: Small library for common tasks
|
|
5
5
|
Author: Hoang Van Ha
|
|
6
6
|
Author-email: hoangvanhauit@gmail.com
|
|
@@ -56,6 +56,11 @@ Dynamic: summary
|
|
|
56
56
|
|
|
57
57
|
## v0.2.x (Experiment & Core Updates)
|
|
58
58
|
|
|
59
|
+
### **v0.2.21**
|
|
60
|
+
- ✨ **New Feature:** Added `common.common.pprint_stack_trace` to print stack traces with optional custom messages and force stop capability.
|
|
61
|
+
|
|
62
|
+
- 🚀 **Improvement:** `exp.perf.profiler` - allow to export *report dict* as csv files for further analysis
|
|
63
|
+
|
|
59
64
|
### **v0.2.19**
|
|
60
65
|
- ✨ **New Feature:** Added `exp.core.param_gen` to facilitate fast generation of parameter combination sweeps (grid search) using YAML configurations.
|
|
61
66
|
|
|
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
|