batplot 1.8.33__tar.gz → 1.8.35__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 (42) hide show
  1. {batplot-1.8.33/batplot.egg-info → batplot-1.8.35}/PKG-INFO +1 -1
  2. {batplot-1.8.33 → batplot-1.8.35}/batplot/__init__.py +1 -1
  3. {batplot-1.8.33 → batplot-1.8.35}/batplot/data/CHANGELOG.md +8 -0
  4. {batplot-1.8.33 → batplot-1.8.35}/batplot/electrochem_interactive.py +75 -41
  5. {batplot-1.8.33 → batplot-1.8.35/batplot.egg-info}/PKG-INFO +1 -1
  6. {batplot-1.8.33 → batplot-1.8.35}/pyproject.toml +1 -1
  7. {batplot-1.8.33 → batplot-1.8.35}/LICENSE +0 -0
  8. {batplot-1.8.33 → batplot-1.8.35}/MANIFEST.in +0 -0
  9. {batplot-1.8.33 → batplot-1.8.35}/NOTICE +0 -0
  10. {batplot-1.8.33 → batplot-1.8.35}/README.md +0 -0
  11. {batplot-1.8.33 → batplot-1.8.35}/batplot/args.py +0 -0
  12. {batplot-1.8.33 → batplot-1.8.35}/batplot/batch.py +0 -0
  13. {batplot-1.8.33 → batplot-1.8.35}/batplot/batplot.py +0 -0
  14. {batplot-1.8.33 → batplot-1.8.35}/batplot/canvas_interactive.py +0 -0
  15. {batplot-1.8.33 → batplot-1.8.35}/batplot/cif.py +0 -0
  16. {batplot-1.8.33 → batplot-1.8.35}/batplot/cli.py +0 -0
  17. {batplot-1.8.33 → batplot-1.8.35}/batplot/color_utils.py +0 -0
  18. {batplot-1.8.33 → batplot-1.8.35}/batplot/config.py +0 -0
  19. {batplot-1.8.33 → batplot-1.8.35}/batplot/converters.py +0 -0
  20. {batplot-1.8.33 → batplot-1.8.35}/batplot/cpc_interactive.py +0 -0
  21. {batplot-1.8.33 → batplot-1.8.35}/batplot/data/USER_MANUAL.md +0 -0
  22. {batplot-1.8.33 → batplot-1.8.35}/batplot/dev_upgrade.py +0 -0
  23. {batplot-1.8.33 → batplot-1.8.35}/batplot/interactive.py +0 -0
  24. {batplot-1.8.33 → batplot-1.8.35}/batplot/manual.py +0 -0
  25. {batplot-1.8.33 → batplot-1.8.35}/batplot/modes.py +0 -0
  26. {batplot-1.8.33 → batplot-1.8.35}/batplot/operando.py +0 -0
  27. {batplot-1.8.33 → batplot-1.8.35}/batplot/operando_ec_interactive.py +0 -0
  28. {batplot-1.8.33 → batplot-1.8.35}/batplot/plotting.py +0 -0
  29. {batplot-1.8.33 → batplot-1.8.35}/batplot/readers.py +0 -0
  30. {batplot-1.8.33 → batplot-1.8.35}/batplot/session.py +0 -0
  31. {batplot-1.8.33 → batplot-1.8.35}/batplot/showcol.py +0 -0
  32. {batplot-1.8.33 → batplot-1.8.35}/batplot/style.py +0 -0
  33. {batplot-1.8.33 → batplot-1.8.35}/batplot/ui.py +0 -0
  34. {batplot-1.8.33 → batplot-1.8.35}/batplot/utils.py +0 -0
  35. {batplot-1.8.33 → batplot-1.8.35}/batplot/version_check.py +0 -0
  36. {batplot-1.8.33 → batplot-1.8.35}/batplot.egg-info/SOURCES.txt +0 -0
  37. {batplot-1.8.33 → batplot-1.8.35}/batplot.egg-info/dependency_links.txt +0 -0
  38. {batplot-1.8.33 → batplot-1.8.35}/batplot.egg-info/entry_points.txt +0 -0
  39. {batplot-1.8.33 → batplot-1.8.35}/batplot.egg-info/requires.txt +0 -0
  40. {batplot-1.8.33 → batplot-1.8.35}/batplot.egg-info/top_level.txt +0 -0
  41. {batplot-1.8.33 → batplot-1.8.35}/setup.cfg +0 -0
  42. {batplot-1.8.33 → batplot-1.8.35}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.33
3
+ Version: 1.8.35
4
4
  Summary: Interactive plotting tool for material science (1D plot) and electrochemistry (GC, CV, dQ/dV, CPC, operando) with batch processing
5
5
  Author-email: Tian Dai <tianda@uio.no>
6
6
  License: MIT License
@@ -1,5 +1,5 @@
1
1
  """batplot: Interactive plotting for battery data visualization."""
2
2
 
3
- __version__ = "1.8.33"
3
+ __version__ = "1.8.35"
4
4
 
5
5
  __all__ = ["__version__"]
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.8.35] - 2026-03-26
4
+ - Bug fixes
5
+
6
+
7
+ ## [1.8.34] - 2026-03-24
8
+ - Bug fixes
9
+
10
+
3
11
  ## [1.8.32] - 2026-03-24
4
12
  - Improved multi-file support for EC mode
5
13
 
@@ -281,12 +281,24 @@ def _apply_stored_smooth_settings(cycle_lines: Dict[int, Dict[str, Optional[Any]
281
281
  continue
282
282
  xdata = np.asarray(ln.get_xdata(), float)
283
283
  ydata = np.asarray(ln.get_ydata(), float)
284
+ if xdata.size != ydata.size:
285
+ n = int(min(xdata.size, ydata.size))
286
+ if n < 3:
287
+ continue
288
+ xdata = xdata[:n]
289
+ ydata = ydata[:n]
284
290
  if xdata.size < 3:
285
291
  continue
286
292
  # Get original data if available, otherwise use current data
287
293
  if hasattr(ln, '_original_xdata'):
288
294
  xdata = np.asarray(ln._original_xdata, float)
289
295
  ydata = np.asarray(ln._original_ydata, float)
296
+ if xdata.size != ydata.size:
297
+ n = int(min(xdata.size, ydata.size))
298
+ if n < 3:
299
+ continue
300
+ xdata = xdata[:n]
301
+ ydata = ydata[:n]
290
302
  else:
291
303
  ln._original_xdata = np.array(xdata, copy=True)
292
304
  ln._original_ydata = np.array(ydata, copy=True)
@@ -309,12 +321,24 @@ def _apply_stored_smooth_settings(cycle_lines: Dict[int, Dict[str, Optional[Any]
309
321
  continue
310
322
  xdata = np.asarray(ln.get_xdata(), float)
311
323
  ydata = np.asarray(ln.get_ydata(), float)
324
+ if xdata.size != ydata.size:
325
+ n = int(min(xdata.size, ydata.size))
326
+ if n < 3:
327
+ continue
328
+ xdata = xdata[:n]
329
+ ydata = ydata[:n]
312
330
  if xdata.size < 3:
313
331
  continue
314
332
  # Get original data if available, otherwise use current data
315
333
  if hasattr(ln, '_original_xdata'):
316
334
  xdata = np.asarray(ln._original_xdata, float)
317
335
  ydata = np.asarray(ln._original_ydata, float)
336
+ if xdata.size != ydata.size:
337
+ n = int(min(xdata.size, ydata.size))
338
+ if n < 3:
339
+ continue
340
+ xdata = xdata[:n]
341
+ ydata = ydata[:n]
318
342
  else:
319
343
  ln._original_xdata = np.array(xdata, copy=True)
320
344
  ln._original_ydata = np.array(ydata, copy=True)
@@ -341,12 +365,24 @@ def _apply_stored_smooth_settings(cycle_lines: Dict[int, Dict[str, Optional[Any]
341
365
  continue
342
366
  xdata = np.asarray(ln.get_xdata(), float)
343
367
  ydata = np.asarray(ln.get_ydata(), float)
368
+ if xdata.size != ydata.size:
369
+ n = int(min(xdata.size, ydata.size))
370
+ if n < 5:
371
+ continue
372
+ xdata = xdata[:n]
373
+ ydata = ydata[:n]
344
374
  if xdata.size < 5:
345
375
  continue
346
376
  # Get original data if available, otherwise use current data
347
377
  if hasattr(ln, '_original_xdata'):
348
378
  xdata = np.asarray(ln._original_xdata, float)
349
379
  ydata = np.asarray(ln._original_ydata, float)
380
+ if xdata.size != ydata.size:
381
+ n = int(min(xdata.size, ydata.size))
382
+ if n < 5:
383
+ continue
384
+ xdata = xdata[:n]
385
+ ydata = ydata[:n]
350
386
  else:
351
387
  ln._original_xdata = np.array(xdata, copy=True)
352
388
  ln._original_ydata = np.array(ydata, copy=True)
@@ -904,11 +940,6 @@ def _format_cycles_compact(cycles: List[int]) -> str:
904
940
  return ", ".join(parts)
905
941
 
906
942
 
907
- def _print_ec_main_menu_options_tip():
908
- """Reminder shown in top-level EC submenus: main menu includes p/i/s/b."""
909
- print(" " + _colorize_menu("Tip: q → main menu (p i s b: export/import style, save session, undo)"))
910
-
911
-
912
943
  def _parse_cycle_tokens(tokens: List[str], fig=None) -> Tuple[str, List[int], dict, Optional[str], bool]:
913
944
  """Classify and parse tokens for the cycle command.
914
945
 
@@ -2370,7 +2401,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
2370
2401
  print(" " + _colorize_menu("d: show only discharge curves (hide charge)"))
2371
2402
  print(" " + _colorize_menu("b: show both charge and discharge"))
2372
2403
  print(" " + _colorize_menu("Which cycles are shown & colors: main menu c (e.g. 2-30 1 = cycles 2–30, palette 1 / tab10)"))
2373
- _print_ec_main_menu_options_tip()
2374
2404
  print(" " + _colorize_menu("q: back"))
2375
2405
  sub = _safe_input(_colorize_prompt("Display (c/d/b/q): ")).strip().lower()
2376
2406
  if not sub or sub == 'q':
@@ -2602,7 +2632,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
2602
2632
  print("Legend:")
2603
2633
  print(" " + _colorize_menu("t: toggle"))
2604
2634
  print(" " + _colorize_menu("p: set position"))
2605
- _print_ec_main_menu_options_tip()
2606
2635
  print(" " + _colorize_menu("q: back"))
2607
2636
  sub = _safe_input(_colorize_prompt("Legend (t/p/q): ")).strip().lower()
2608
2637
  if not sub:
@@ -2805,7 +2834,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
2805
2834
  cfg = _get_style_snapshot(fig, ax, cycle_lines, tick_state, file_data=file_data if is_multi_file else None)
2806
2835
  cfg['kind'] = 'ec_style' # Default, will be updated if psg is chosen
2807
2836
  _print_style_snapshot(cfg)
2808
- _print_ec_main_menu_options_tip()
2809
2837
 
2810
2838
  # List available style files (.bps, .bpsg, .bpcfg) in Styles/ subdirectory
2811
2839
  style_file_list = list_files_in_subdirectory(('.bps', '.bpsg', '.bpcfg'), 'style')
@@ -3545,7 +3573,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
3545
3573
  print(f" {_colorize_menu('l : show only lines (no markers) for all curves')}")
3546
3574
  print(f" {_colorize_menu('ld : show line and dots (markers) for all curves')}")
3547
3575
  print(f" {_colorize_menu('d : show only dots (no connecting line) for all curves')}")
3548
- _print_ec_main_menu_options_tip()
3549
3576
  print(f" {_colorize_menu('q : return')}")
3550
3577
  sub = _safe_input(_colorize_prompt("Choose (c/f/g/l/ld/d/q): ")).strip().lower()
3551
3578
  if not sub:
@@ -3740,7 +3767,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
3740
3767
  for idx, color in enumerate(user_colors, 1):
3741
3768
  print(" " + _colorize_menu(f"{idx}: {color_block(color)} {color}"))
3742
3769
  print(" " + _colorize_menu("u: edit saved colors"))
3743
- _print_ec_main_menu_options_tip()
3744
3770
  print(" " + _colorize_menu("q: back to main menu"))
3745
3771
  line = _safe_input(_colorize_prompt("Enter mappings (e.g., a:red d:blue, q=back): ")).strip()
3746
3772
  if not line or line.lower() == 'q':
@@ -3831,7 +3857,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
3831
3857
  print(" " + _colorize_menu("y: y-axis"))
3832
3858
  if file_data:
3833
3859
  print(" " + _colorize_menu("f: file names (legend)"))
3834
- _print_ec_main_menu_options_tip()
3835
3860
  print(" " + _colorize_menu("q: back"))
3836
3861
  opts = "x/y" + ("/tx" if (is_dual_xaxis and secax) else "") + ("/f" if file_data else "") + "/q"
3837
3862
  sub = _safe_input(_colorize_prompt(f"Rename ({opts}): ")).strip().lower()
@@ -3962,7 +3987,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
3962
3987
  continue
3963
3988
  while True:
3964
3989
  _print_file_list(file_data)
3965
- _print_ec_main_menu_options_tip()
3966
3990
  print("Current legend order (top to bottom):")
3967
3991
  order = getattr(fig, '_ec_legend_file_order', None) or list(range(len(file_data)))
3968
3992
  for i, idx in enumerate(order):
@@ -4167,7 +4191,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
4167
4191
  print(f" Minor count : {_C}m{_R}=minor ticks per interval e.g. {_C}x 4{_R} {_C}y 1{_R} {_C}all 0{_R}=off {_C}x auto{_R}")
4168
4192
  print(f" Title offsets : {_C}p{_R}=adjust ({_C}w{_R}=top {_C}s{_R}=bottom {_C}a{_R}=left {_C}d{_R}=right)")
4169
4193
  print(f" Other : {_C}list{_R}=show state {_C}q{_R}=back")
4170
- _print_ec_main_menu_options_tip()
4171
4194
  while True:
4172
4195
  cmd = _safe_input(_colorize_prompt("Enter code(s): ")).strip().lower()
4173
4196
  if not cmd:
@@ -4521,7 +4544,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
4521
4544
  for idx, color in enumerate(user_colors, 1):
4522
4545
  print(" " + _colorize_menu(f"{idx}: {color_block(color)} {color}"))
4523
4546
  print(" " + _colorize_menu("u: edit saved colors before assigning"))
4524
- _print_ec_main_menu_options_tip()
4525
4547
  print(" " + _colorize_menu("q: back"))
4526
4548
  line = _safe_input(_colorize_prompt("Selection: ")).strip()
4527
4549
  if not line or line.lower() == 'q':
@@ -4827,7 +4849,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
4827
4849
  print(" " + _colorize_menu("s : swap axes (switch top/bottom)"))
4828
4850
  if c_th:
4829
4851
  print(" " + _colorize_menu("u : update theoretical capacity"))
4830
- _print_ec_main_menu_options_tip()
4831
4852
  print(" " + _colorize_menu("q : back to main menu"))
4832
4853
 
4833
4854
  sub = _safe_input("X> ").strip().lower()
@@ -5367,7 +5388,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
5367
5388
  print(f"\nFont (current: family='{cur_family}', size={cur_size})")
5368
5389
  print(" " + _colorize_menu("f: family"))
5369
5390
  print(" " + _colorize_menu("s: size"))
5370
- _print_ec_main_menu_options_tip()
5371
5391
  print(" " + _colorize_menu("q: back"))
5372
5392
  sub = _safe_input(_colorize_prompt("Font (f/s/q): ")).strip().lower()
5373
5393
  if not sub:
@@ -5442,7 +5462,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
5442
5462
  print(" " + _colorize_menu("w: upper only"))
5443
5463
  print(" " + _colorize_menu("s: lower only"))
5444
5464
  print(" " + _colorize_menu("a: auto (restore original)"))
5445
- _print_ec_main_menu_options_tip()
5446
5465
  print(" " + _colorize_menu("q: back"))
5447
5466
  lim = _safe_input(_colorize_prompt("X (w/s/a/q): ")).strip()
5448
5467
  if not lim or lim.lower() == 'q':
@@ -5532,7 +5551,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
5532
5551
  print(" " + _colorize_menu("w: upper only"))
5533
5552
  print(" " + _colorize_menu("s: lower only"))
5534
5553
  print(" " + _colorize_menu("a: auto (restore original)"))
5535
- _print_ec_main_menu_options_tip()
5536
5554
  print(" " + _colorize_menu("q: back"))
5537
5555
  lim = _safe_input(_colorize_prompt("Y (w/s/a/q): ")).strip()
5538
5556
  if not lim or lim.lower() == 'q':
@@ -5623,7 +5641,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
5623
5641
  while True:
5624
5642
  print(" " + _colorize_menu("p: plot frame size"))
5625
5643
  print(" " + _colorize_menu("c: canvas size"))
5626
- _print_ec_main_menu_options_tip()
5627
5644
  print(" " + _colorize_menu("q: back"))
5628
5645
  sub = _safe_input(_colorize_prompt("Geom (p/c/q): ")).strip().lower()
5629
5646
  if not sub:
@@ -5685,7 +5702,6 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
5685
5702
  print(" " + _colorize_menu("d: DiffCap smooth (≥1 mV ΔV + Savitzky–Golay, order 3, window 9)"))
5686
5703
  print(" " + _colorize_menu("o: remove outliers (removes abrupt dQ/dV spikes)"))
5687
5704
  print(" " + _colorize_menu("r: reset to original data"))
5688
- _print_ec_main_menu_options_tip()
5689
5705
  print(" " + _colorize_menu("q: back to main menu"))
5690
5706
  sub = _safe_input(_colorize_prompt("dQ/dV (a/d/o/r/q): ")).strip().lower()
5691
5707
  if not sub:
@@ -5761,27 +5777,33 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
5761
5777
  if ln is None or not ln.get_visible():
5762
5778
  continue
5763
5779
  xdata = np.asarray(ln.get_xdata(), float)
5764
- ydata = np.asarray(ln.get_ydata(), float)
5765
- if xdata.size < 3:
5766
- continue
5767
- if not hasattr(ln, '_original_xdata'):
5768
- ln._original_xdata = np.array(xdata, copy=True)
5769
- ln._original_ydata = np.array(ydata, copy=True)
5770
- dv = np.abs(np.diff(xdata))
5771
- mask = np.ones_like(xdata, dtype=bool)
5772
- mask[1:] &= dv >= threshold_v
5773
- mask[:-1] &= dv >= threshold_v
5774
- filtered_x = xdata[mask]
5775
- filtered_y = ydata[mask]
5776
- before = len(xdata)
5777
- after = len(filtered_x)
5778
- if after < before:
5779
- ln.set_xdata(filtered_x)
5780
- ln.set_ydata(filtered_y)
5781
- ln._smooth_applied = True
5782
- filtered += 1
5783
- total_before += before
5784
- total_after += after
5780
+ ydata = np.asarray(ln.get_ydata(), float)
5781
+ if xdata.size != ydata.size:
5782
+ n = int(min(xdata.size, ydata.size))
5783
+ if n < 3:
5784
+ continue
5785
+ xdata = xdata[:n]
5786
+ ydata = ydata[:n]
5787
+ if xdata.size < 3:
5788
+ continue
5789
+ if not hasattr(ln, '_original_xdata'):
5790
+ ln._original_xdata = np.array(xdata, copy=True)
5791
+ ln._original_ydata = np.array(ydata, copy=True)
5792
+ dv = np.abs(np.diff(xdata))
5793
+ mask = np.ones_like(xdata, dtype=bool)
5794
+ mask[1:] &= dv >= threshold_v
5795
+ mask[:-1] &= dv >= threshold_v
5796
+ filtered_x = xdata[mask]
5797
+ filtered_y = ydata[mask]
5798
+ before = len(xdata)
5799
+ after = len(filtered_x)
5800
+ if after < before:
5801
+ ln.set_xdata(filtered_x)
5802
+ ln.set_ydata(filtered_y)
5803
+ ln._smooth_applied = True
5804
+ filtered += 1
5805
+ total_before += before
5806
+ total_after += after
5785
5807
  if filtered:
5786
5808
  removed = total_before - total_after
5787
5809
  pct = 100 * removed / total_before if total_before else 0
@@ -5887,6 +5909,12 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
5887
5909
  continue
5888
5910
  xdata = np.asarray(ln.get_xdata(), float)
5889
5911
  ydata = np.asarray(ln.get_ydata(), float)
5912
+ if xdata.size != ydata.size:
5913
+ n = int(min(xdata.size, ydata.size))
5914
+ if n < 3:
5915
+ continue
5916
+ xdata = xdata[:n]
5917
+ ydata = ydata[:n]
5890
5918
  if xdata.size < 3:
5891
5919
  continue
5892
5920
  if not hasattr(ln, '_original_xdata'):
@@ -5996,6 +6024,12 @@ def electrochem_interactive_menu(fig, ax, cycle_lines: Optional[Dict[int, Dict[s
5996
6024
  continue
5997
6025
  xdata = np.asarray(ln.get_xdata(), float)
5998
6026
  ydata = np.asarray(ln.get_ydata(), float)
6027
+ if xdata.size != ydata.size:
6028
+ n = int(min(xdata.size, ydata.size))
6029
+ if n < 5:
6030
+ continue
6031
+ xdata = xdata[:n]
6032
+ ydata = ydata[:n]
5999
6033
  if xdata.size < 5:
6000
6034
  continue
6001
6035
  if not hasattr(ln, '_original_xdata'):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: batplot
3
- Version: 1.8.33
3
+ Version: 1.8.35
4
4
  Summary: Interactive plotting tool for material science (1D plot) and electrochemistry (GC, CV, dQ/dV, CPC, operando) with batch processing
5
5
  Author-email: Tian Dai <tianda@uio.no>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "batplot"
7
- version = "1.8.33"
7
+ version = "1.8.35"
8
8
  description = "Interactive plotting tool for material science (1D plot) and electrochemistry (GC, CV, dQ/dV, CPC, operando) with batch processing"
9
9
  authors = [
10
10
  { name = "Tian Dai", email = "tianda@uio.no" }
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