cpp-pd-code-simplify-interface 0.1.10__py3-none-any.whl → 0.1.12__py3-none-any.whl

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.
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import sys
5
+ from typing import Any
6
+
7
+ from .main import _run_one_direct
8
+
9
+
10
+ def main() -> int:
11
+ try:
12
+ request: dict[str, Any] = json.loads(sys.stdin.read())
13
+ result = _run_one_direct(
14
+ str(request.get("pd_text", "")),
15
+ max_paths=int(request.get("max_paths", -1)),
16
+ ban_heuristic=bool(request.get("ban_heuristic", False)),
17
+ reduction_round=int(request.get("reduction_round", -1)),
18
+ max_thread=int(request.get("max_thread", -1)),
19
+ timeout=int(request.get("timeout", -1)),
20
+ verbose=bool(request.get("verbose", False)),
21
+ known_crossingless_components=int(
22
+ request.get("known_crossingless_components", 0)
23
+ ),
24
+ remove_crossings=request.get("remove_crossings") or [],
25
+ )
26
+ print(json.dumps({"ok": True, "result": result}, separators=(",", ":")))
27
+ return 0
28
+ except KeyboardInterrupt:
29
+ print(
30
+ json.dumps(
31
+ {"ok": False, "error": "interrupted by Ctrl+C"},
32
+ separators=(",", ":"),
33
+ )
34
+ )
35
+ return 130
36
+ except BaseException as exc: # noqa: BLE001 - return errors through JSON.
37
+ print(
38
+ json.dumps(
39
+ {"ok": False, "error": str(exc)},
40
+ separators=(",", ":"),
41
+ )
42
+ )
43
+ return 0
44
+
45
+
46
+ if __name__ == "__main__":
47
+ raise SystemExit(main())
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include <array>
4
+ #include <chrono>
4
5
  #include <cstddef>
5
6
  #include <functional>
6
7
  #include <iosfwd>
@@ -51,10 +52,14 @@ struct GreenCrossing {
51
52
  struct SimplifierOptions {
52
53
  int max_paths = -1;
53
54
  int max_threads = -1;
55
+ int timeout_seconds = -1;
56
+ bool has_timeout_deadline = false;
57
+ std::chrono::steady_clock::time_point timeout_deadline{};
54
58
  bool ban_heuristic = false;
55
59
  bool require_applicable = false;
56
60
  bool verbose = false;
57
61
  std::function<void(const std::string&)> progress;
62
+ std::function<bool()> should_cancel;
58
63
  };
59
64
 
60
65
  struct LinkComponentSummary {
@@ -121,6 +126,7 @@ struct ReductionResult {
121
126
  std::size_t tested_green_paths = 0;
122
127
  std::string last_path_search_mode;
123
128
  bool stopped_by_round_limit = false;
129
+ bool timed_out = false;
124
130
  };
125
131
 
126
132
  PDCODE_SIMPLIFY_API PDCode parse_pd_code(const std::string& text);
@@ -106,7 +106,8 @@ std::string result_to_json(
106
106
  out << "\"tested_green_paths\":" << result.tested_green_paths << ",";
107
107
  out << "\"last_path_search_mode\":\"" << json_escape(result.last_path_search_mode) << "\",";
108
108
  out << "\"stopped_by_round_limit\":"
109
- << (result.stopped_by_round_limit ? "true" : "false");
109
+ << (result.stopped_by_round_limit ? "true" : "false") << ",";
110
+ out << "\"timed_out\":" << (result.timed_out ? "true" : "false");
110
111
  out << "}";
111
112
  return out.str();
112
113
  }
@@ -133,6 +134,7 @@ char* pdcode_simplify_run_json(
133
134
  int ban_heuristic,
134
135
  int reduction_round,
135
136
  int max_thread,
137
+ int timeout_seconds,
136
138
  int verbose,
137
139
  unsigned long long known_crossingless_components,
138
140
  const int* removed_crossings,
@@ -146,6 +148,7 @@ char* pdcode_simplify_run_json(
146
148
  pdcode_simplify::SimplifierOptions options;
147
149
  options.max_paths = max_paths;
148
150
  options.max_threads = max_thread;
151
+ options.timeout_seconds = timeout_seconds;
149
152
  options.ban_heuristic = ban_heuristic != 0;
150
153
  options.verbose = verbose != 0;
151
154
  options.progress = [](const std::string& message) {
@@ -1,6 +1,7 @@
1
1
  #include "pdcode_simplify/pdcode_simplify.hpp"
2
2
 
3
3
  #include <algorithm>
4
+ #include <chrono>
4
5
  #include <cctype>
5
6
  #include <cstdlib>
6
7
  #include <deque>
@@ -510,8 +511,10 @@ std::vector<std::vector<int>> raw_faces_from_pd_code(const PDCode& code) {
510
511
  struct Diagram {
511
512
  PDCode code;
512
513
  std::vector<CrossingState> crossings;
514
+ std::vector<int> rotations;
513
515
 
514
- explicit Diagram(PDCode input) : code(std::move(input)), crossings(code.size()) {
516
+ explicit Diagram(PDCode input)
517
+ : code(std::move(input)), crossings(code.size()), rotations(code.size(), 0) {
515
518
  build_adjacency();
516
519
  auto starts = component_starts_from_pd();
517
520
  orient_crossings(starts);
@@ -533,6 +536,10 @@ struct Diagram {
533
536
  return Endpoint{endpoint.crossing, positive_mod(endpoint.strand + offset, 4)};
534
537
  }
535
538
 
539
+ int label_at(int crossing, int strand) const {
540
+ return code.at(crossing).at(positive_mod(strand + rotations.at(crossing), 4));
541
+ }
542
+
536
543
  std::vector<Endpoint> crossing_entries() const {
537
544
  std::vector<Endpoint> entries;
538
545
  entries.reserve(crossings.size() * 2);
@@ -715,6 +722,7 @@ private:
715
722
 
716
723
  void rotate_crossing_180(int crossing) {
717
724
  auto old_adjacent = crossings[crossing].adjacent;
725
+ rotations[crossing] = positive_mod(rotations[crossing] + 2, 4);
718
726
  bool old_directions[4][4]{};
719
727
  for (int a = 0; a < 4; ++a) {
720
728
  for (int b = 0; b < 4; ++b) {
@@ -980,7 +988,10 @@ std::set<int> normalized_removed_crossings(const PDCode& code, const std::vector
980
988
  std::vector<int> heuristic_distances_to_target(
981
989
  const DualGraph& graph,
982
990
  int target,
983
- int cutoff);
991
+ int cutoff,
992
+ const SimplifierOptions& options);
993
+
994
+ void check_timeout(const SimplifierOptions& options);
984
995
 
985
996
  void collect_simple_paths_dfs(
986
997
  const DualGraph& graph,
@@ -992,7 +1003,9 @@ void collect_simple_paths_dfs(
992
1003
  const std::vector<int>& distance,
993
1004
  std::vector<char>& visited,
994
1005
  std::vector<int>& current_path,
995
- std::vector<std::vector<int>>& paths) {
1006
+ std::vector<std::vector<int>>& paths,
1007
+ const SimplifierOptions& options) {
1008
+ check_timeout(options);
996
1009
  const int infinity = std::numeric_limits<int>::max() / 4;
997
1010
  if (static_cast<int>(current_path.size()) - 1 >= cutoff) {
998
1011
  return;
@@ -1038,7 +1051,8 @@ void collect_simple_paths_dfs(
1038
1051
  distance,
1039
1052
  visited,
1040
1053
  current_path,
1041
- paths);
1054
+ paths,
1055
+ options);
1042
1056
  if (max_paths != -1 && static_cast<int>(paths.size()) > max_paths) {
1043
1057
  visited[next] = false;
1044
1058
  current_path.pop_back();
@@ -1056,7 +1070,9 @@ std::vector<std::vector<int>> collect_simple_paths(
1056
1070
  int source,
1057
1071
  int target,
1058
1072
  int cutoff,
1059
- int max_paths) {
1073
+ int max_paths,
1074
+ const SimplifierOptions& options) {
1075
+ check_timeout(options);
1060
1076
  std::vector<std::vector<int>> paths;
1061
1077
  if (source == target || source < 0 || target < 0 ||
1062
1078
  source >= static_cast<int>(graph.faces.size()) ||
@@ -1067,7 +1083,7 @@ std::vector<std::vector<int>> collect_simple_paths(
1067
1083
 
1068
1084
  std::vector<char> visited(graph.faces.size(), false);
1069
1085
  std::vector<int> current_path{source};
1070
- const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
1086
+ const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff, options);
1071
1087
  visited[source] = true;
1072
1088
  collect_simple_paths_dfs(
1073
1089
  graph,
@@ -1079,14 +1095,16 @@ std::vector<std::vector<int>> collect_simple_paths(
1079
1095
  distance,
1080
1096
  visited,
1081
1097
  current_path,
1082
- paths);
1098
+ paths,
1099
+ options);
1083
1100
  return paths;
1084
1101
  }
1085
1102
 
1086
1103
  std::vector<int> heuristic_distances_to_target(
1087
1104
  const DualGraph& graph,
1088
1105
  int target,
1089
- int cutoff) {
1106
+ int cutoff,
1107
+ const SimplifierOptions& options) {
1090
1108
  const int face_count = static_cast<int>(graph.faces.size());
1091
1109
  const int infinity = std::numeric_limits<int>::max() / 4;
1092
1110
  std::vector<int> distance(face_count, infinity);
@@ -1095,6 +1113,7 @@ std::vector<int> heuristic_distances_to_target(
1095
1113
  queue.push_back(target);
1096
1114
 
1097
1115
  while (!queue.empty()) {
1116
+ check_timeout(options);
1098
1117
  const int current = queue.front();
1099
1118
  queue.pop_front();
1100
1119
  for (int edge_index : graph.adjacency[current]) {
@@ -1172,7 +1191,9 @@ std::vector<std::vector<int>> collect_heuristic_paths(
1172
1191
  const DualGraph& graph,
1173
1192
  int source,
1174
1193
  int target,
1175
- int cutoff) {
1194
+ int cutoff,
1195
+ const SimplifierOptions& options) {
1196
+ check_timeout(options);
1176
1197
  std::vector<std::vector<int>> paths;
1177
1198
  const int face_count = static_cast<int>(graph.faces.size());
1178
1199
  if (source == target || source < 0 || target < 0 ||
@@ -1180,7 +1201,7 @@ std::vector<std::vector<int>> collect_heuristic_paths(
1180
1201
  return paths;
1181
1202
  }
1182
1203
 
1183
- const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff);
1204
+ const std::vector<int> distance = heuristic_distances_to_target(graph, target, cutoff, options);
1184
1205
  const int infinity = std::numeric_limits<int>::max() / 4;
1185
1206
  if (distance[source] == infinity || distance[source] >= cutoff) {
1186
1207
  return paths;
@@ -1208,6 +1229,7 @@ std::vector<std::vector<int>> collect_heuristic_paths(
1208
1229
  int popped_states = 0;
1209
1230
  while (!queue.empty() && popped_states < state_budget &&
1210
1231
  static_cast<int>(paths.size()) < path_budget) {
1232
+ check_timeout(options);
1211
1233
  HeuristicState state = queue.top();
1212
1234
  queue.pop();
1213
1235
  ++popped_states;
@@ -1816,6 +1838,41 @@ std::string search_mode_for_options(const SimplifierOptions& options) {
1816
1838
  return "bounded";
1817
1839
  }
1818
1840
 
1841
+ void validate_timeout_options(const SimplifierOptions& options) {
1842
+ if (options.timeout_seconds < -1 || options.timeout_seconds == 0) {
1843
+ throw std::invalid_argument("timeout must be -1 or a positive integer");
1844
+ }
1845
+ }
1846
+
1847
+ class TimeoutError : public std::runtime_error {
1848
+ public:
1849
+ explicit TimeoutError(const std::string& message) : std::runtime_error(message) {}
1850
+ };
1851
+
1852
+ SimplifierOptions with_timeout_deadline(SimplifierOptions options) {
1853
+ validate_timeout_options(options);
1854
+ if (options.timeout_seconds > 0 && !options.has_timeout_deadline) {
1855
+ options.has_timeout_deadline = true;
1856
+ options.timeout_deadline =
1857
+ std::chrono::steady_clock::now() + std::chrono::seconds(options.timeout_seconds);
1858
+ }
1859
+ return options;
1860
+ }
1861
+
1862
+ void check_timeout(const SimplifierOptions& options) {
1863
+ if (options.should_cancel && options.should_cancel()) {
1864
+ throw std::runtime_error("interrupted by Ctrl+C");
1865
+ }
1866
+ if (!options.has_timeout_deadline) {
1867
+ return;
1868
+ }
1869
+ if (std::chrono::steady_clock::now() >= options.timeout_deadline) {
1870
+ std::ostringstream message;
1871
+ message << "timeout after " << options.timeout_seconds << " seconds";
1872
+ throw TimeoutError(message.str());
1873
+ }
1874
+ }
1875
+
1819
1876
  } // namespace
1820
1877
 
1821
1878
  PDCode parse_pd_code(const std::string& text) {
@@ -1878,14 +1935,9 @@ std::string format_final_pd_code(const PDCode& code) {
1878
1935
  std::set<int> labels;
1879
1936
  std::map<int, int> next_label;
1880
1937
 
1881
- auto label_at = [&](int crossing, int strand) {
1882
- const Endpoint other = diagram.crossings[crossing].adjacent[strand];
1883
- return diagram.code[other.crossing][other.strand];
1884
- };
1885
-
1886
1938
  for (int crossing = 0; crossing < static_cast<int>(code.size()); ++crossing) {
1887
1939
  for (int strand = 0; strand < 4; ++strand) {
1888
- const int label = label_at(crossing, strand);
1940
+ const int label = diagram.label_at(crossing, strand);
1889
1941
  oriented[crossing][strand] = label;
1890
1942
  labels.insert(label);
1891
1943
  }
@@ -1898,8 +1950,8 @@ std::string format_final_pd_code(const PDCode& code) {
1898
1950
  if (!diagram.crossings[crossing].directions[tail][head]) {
1899
1951
  continue;
1900
1952
  }
1901
- const int in_label = label_at(crossing, tail);
1902
- const int out_label = label_at(crossing, head);
1953
+ const int in_label = diagram.label_at(crossing, tail);
1954
+ const int out_label = diagram.label_at(crossing, head);
1903
1955
  const auto inserted = next_label.insert(std::make_pair(in_label, out_label));
1904
1956
  if (!inserted.second && inserted.first->second != out_label) {
1905
1957
  throw std::invalid_argument("Final PD component orientation is inconsistent");
@@ -1934,6 +1986,7 @@ std::string format_final_pd_code(const PDCode& code) {
1934
1986
  label = relabel.at(label);
1935
1987
  }
1936
1988
  }
1989
+ std::sort(oriented.begin(), oriented.end());
1937
1990
  return format_pd_code(oriented);
1938
1991
  }
1939
1992
 
@@ -2210,6 +2263,7 @@ RedPathSearchOutcome search_single_red_path(
2210
2263
  const std::string& path_search_mode,
2211
2264
  int red_index,
2212
2265
  const std::atomic<int>* best_found_index) {
2266
+ check_timeout(options);
2213
2267
  RedPathSearchOutcome outcome;
2214
2268
  outcome.witness.path_search_mode = path_search_mode;
2215
2269
  if (should_skip_parallel_red_path(red_index, best_found_index)) {
@@ -2228,6 +2282,7 @@ RedPathSearchOutcome search_single_red_path(
2228
2282
  const std::array<int, 2> destinations{end_face, end_opposite_face};
2229
2283
 
2230
2284
  for (std::size_t i = 1; i + 1 < red_path.size(); ++i) {
2285
+ check_timeout(options);
2231
2286
  const Endpoint endpoint = red_path[i];
2232
2287
  const int right_region = graph.edge_to_face[endpoint_key(endpoint)];
2233
2288
  const int left_region = graph.edge_to_face[endpoint_key(diagram.opposite(endpoint))];
@@ -2240,15 +2295,17 @@ RedPathSearchOutcome search_single_red_path(
2240
2295
  const int cutoff = static_cast<int>(red_path.size()) - 1;
2241
2296
  for (int source : sources) {
2242
2297
  for (int destination : destinations) {
2298
+ check_timeout(options);
2243
2299
  if (should_skip_parallel_red_path(red_index, best_found_index)) {
2244
2300
  outcome.skipped = true;
2245
2301
  return outcome;
2246
2302
  }
2247
2303
  std::vector<std::vector<int>> found_paths;
2248
2304
  if (options.max_paths == -1 && !options.ban_heuristic) {
2249
- found_paths = collect_heuristic_paths(graph, source, destination, cutoff);
2305
+ found_paths = collect_heuristic_paths(graph, source, destination, cutoff, options);
2250
2306
  } else {
2251
- found_paths = collect_simple_paths(graph, source, destination, cutoff, options.max_paths);
2307
+ found_paths = collect_simple_paths(
2308
+ graph, source, destination, cutoff, options.max_paths, options);
2252
2309
  }
2253
2310
  paths.insert(paths.end(), found_paths.begin(), found_paths.end());
2254
2311
  if (options.max_paths != -1 && static_cast<int>(paths.size()) > options.max_paths) {
@@ -2258,6 +2315,7 @@ RedPathSearchOutcome search_single_red_path(
2258
2315
  }
2259
2316
 
2260
2317
  for (const auto& green_path : paths) {
2318
+ check_timeout(options);
2261
2319
  if (should_skip_parallel_red_path(red_index, best_found_index)) {
2262
2320
  outcome.skipped = true;
2263
2321
  return outcome;
@@ -2398,36 +2456,49 @@ SimplificationResult find_simplification_parallel_bruteforce(
2398
2456
  SimplificationResult find_simplification(
2399
2457
  const PDCode& code,
2400
2458
  const SimplifierOptions& options) {
2459
+ const SimplifierOptions run_options = with_timeout_deadline(options);
2460
+ check_timeout(run_options);
2401
2461
  SimplificationResult result;
2402
- result.path_search_mode = search_mode_for_options(options);
2462
+ result.path_search_mode = search_mode_for_options(run_options);
2403
2463
  Diagram diagram(code);
2464
+ check_timeout(run_options);
2404
2465
  DualGraph base_graph(diagram);
2466
+ check_timeout(run_options);
2405
2467
  const auto red_lines = possible_red_lines(diagram);
2406
- const bool brute_force_mode = options.max_paths == -1 && options.ban_heuristic;
2468
+ check_timeout(run_options);
2469
+ const bool brute_force_mode = run_options.max_paths == -1 && run_options.ban_heuristic;
2407
2470
  const int worker_count = brute_force_mode
2408
2471
  ? selected_bruteforce_worker_count(
2409
- options.max_threads,
2472
+ run_options.max_threads,
2410
2473
  static_cast<int>(red_lines.size()))
2411
2474
  : 1;
2475
+ if (brute_force_mode && run_options.max_threads == -1) {
2476
+ std::ostringstream message;
2477
+ message << "bruteforce_threads max_thread=-1"
2478
+ << " actual_threads=" << worker_count
2479
+ << " red_paths=" << red_lines.size();
2480
+ emit_progress(run_options, message.str());
2481
+ }
2412
2482
  if (worker_count > 1) {
2413
2483
  return find_simplification_parallel_bruteforce(
2414
2484
  code,
2415
2485
  diagram,
2416
2486
  base_graph,
2417
2487
  red_lines,
2418
- options,
2488
+ run_options,
2419
2489
  result.path_search_mode,
2420
2490
  worker_count);
2421
2491
  }
2422
2492
 
2423
2493
  for (const auto& red_path : red_lines) {
2494
+ check_timeout(run_options);
2424
2495
  ++result.tested_red_paths;
2425
2496
  const RedPathSearchOutcome outcome = search_single_red_path(
2426
2497
  code,
2427
2498
  diagram,
2428
2499
  base_graph,
2429
2500
  red_path,
2430
- options,
2501
+ run_options,
2431
2502
  result.path_search_mode,
2432
2503
  -1,
2433
2504
  nullptr);
@@ -2449,125 +2520,155 @@ ReductionResult reduce_pd_code(
2449
2520
  std::size_t known_crossingless_components,
2450
2521
  const SimplifierOptions& options,
2451
2522
  int reduction_round) {
2452
- {
2453
- std::ostringstream message;
2454
- message << "start input_crossings=" << code.size()
2455
- << " known_crossingless_components=" << known_crossingless_components
2456
- << " reduction_round=" << reduction_round
2457
- << " max_paths=" << options.max_paths
2458
- << " max_thread=" << options.max_threads
2459
- << " heuristic=" << (options.ban_heuristic ? "off" : "on");
2460
- emit_progress(options, message.str());
2461
- }
2462
-
2463
- const PDSimplificationResult prepared = simplify_pd_code(code, known_crossingless_components);
2523
+ const SimplifierOptions run_options = with_timeout_deadline(options);
2464
2524
  ReductionResult output;
2465
- output.code = prepared.code;
2466
- output.crossingless_components = prepared.crossingless_components;
2467
- output.reidemeister_i_moves = prepared.reidemeister_i_moves;
2468
- output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
2469
- {
2470
- std::ostringstream message;
2471
- message << "pre_simplify input_crossings=" << code.size()
2472
- << " output_crossings=" << output.code.size()
2473
- << " crossingless_components=" << output.crossingless_components
2474
- << " r1_moves=" << prepared.reidemeister_i_moves
2475
- << " nugatory_moves=" << prepared.nugatory_crossing_moves;
2476
- emit_progress(options, message.str());
2477
- }
2525
+ output.code = code;
2526
+ output.crossingless_components = known_crossingless_components;
2478
2527
 
2479
- while (reduction_round < 0 || output.mid_simplification_rounds < reduction_round) {
2480
- SimplifierOptions search_options = options;
2481
- search_options.require_applicable = true;
2482
- const int round = output.mid_simplification_rounds + 1;
2528
+ try {
2529
+ check_timeout(run_options);
2483
2530
  {
2484
2531
  std::ostringstream message;
2485
- message << "round " << round
2486
- << " search_start crossings=" << output.code.size()
2487
- << " mode=" << search_mode_for_options(search_options)
2488
- << " max_thread=" << search_options.max_threads;
2489
- emit_progress(options, message.str());
2490
- }
2491
- SimplificationResult search = find_simplification(output.code, search_options);
2492
- output.tested_red_paths += search.tested_red_paths;
2493
- output.tested_green_paths += search.tested_green_paths;
2494
- output.last_path_search_mode = search.path_search_mode;
2532
+ message << "start input_crossings=" << code.size()
2533
+ << " known_crossingless_components=" << known_crossingless_components
2534
+ << " reduction_round=" << reduction_round
2535
+ << " max_paths=" << run_options.max_paths
2536
+ << " max_thread=" << run_options.max_threads
2537
+ << " timeout=" << run_options.timeout_seconds
2538
+ << " heuristic=" << (run_options.ban_heuristic ? "off" : "on");
2539
+ emit_progress(run_options, message.str());
2540
+ }
2541
+
2542
+ const PDSimplificationResult prepared =
2543
+ simplify_pd_code(code, known_crossingless_components);
2544
+ output.code = prepared.code;
2545
+ output.crossingless_components = prepared.crossingless_components;
2546
+ output.reidemeister_i_moves = prepared.reidemeister_i_moves;
2547
+ output.nugatory_crossing_moves = prepared.nugatory_crossing_moves;
2548
+ check_timeout(run_options);
2495
2549
  {
2496
2550
  std::ostringstream message;
2497
- message << "round " << round
2498
- << " search_done found=" << (search.found ? "yes" : "no")
2499
- << " mode=" << search.path_search_mode
2500
- << " tested_red=" << search.tested_red_paths
2501
- << " tested_green=" << search.tested_green_paths;
2502
- emit_progress(options, message.str());
2503
- }
2504
-
2505
- if (!search.found && reduction_round < 0 &&
2506
- options.max_paths == -1 && !options.ban_heuristic) {
2507
- SimplifierOptions brute_options = options;
2508
- brute_options.max_paths = -1;
2509
- brute_options.ban_heuristic = true;
2510
- brute_options.require_applicable = true;
2551
+ message << "pre_simplify input_crossings=" << code.size()
2552
+ << " output_crossings=" << output.code.size()
2553
+ << " crossingless_components=" << output.crossingless_components
2554
+ << " r1_moves=" << prepared.reidemeister_i_moves
2555
+ << " nugatory_moves=" << prepared.nugatory_crossing_moves;
2556
+ emit_progress(run_options, message.str());
2557
+ }
2558
+
2559
+ while (reduction_round < 0 || output.mid_simplification_rounds < reduction_round) {
2560
+ check_timeout(run_options);
2561
+ SimplifierOptions search_options = run_options;
2562
+ search_options.require_applicable = true;
2563
+ output.last_path_search_mode = search_mode_for_options(search_options);
2564
+ const int round = output.mid_simplification_rounds + 1;
2511
2565
  {
2512
2566
  std::ostringstream message;
2513
2567
  message << "round " << round
2514
- << " brute_fallback_start crossings=" << output.code.size()
2515
- << " max_thread=" << brute_options.max_threads;
2516
- emit_progress(options, message.str());
2517
- }
2518
- SimplificationResult brute = find_simplification(output.code, brute_options);
2519
- output.tested_red_paths += brute.tested_red_paths;
2520
- output.tested_green_paths += brute.tested_green_paths;
2521
- output.last_path_search_mode = brute.path_search_mode;
2568
+ << " search_start crossings=" << output.code.size()
2569
+ << " mode=" << output.last_path_search_mode
2570
+ << " max_thread=" << search_options.max_threads;
2571
+ emit_progress(run_options, message.str());
2572
+ }
2573
+ SimplificationResult search = find_simplification(output.code, search_options);
2574
+ output.tested_red_paths += search.tested_red_paths;
2575
+ output.tested_green_paths += search.tested_green_paths;
2576
+ output.last_path_search_mode = search.path_search_mode;
2522
2577
  {
2523
2578
  std::ostringstream message;
2524
2579
  message << "round " << round
2525
- << " brute_fallback_done found=" << (brute.found ? "yes" : "no")
2526
- << " tested_red=" << brute.tested_red_paths
2527
- << " tested_green=" << brute.tested_green_paths;
2528
- emit_progress(options, message.str());
2580
+ << " search_done found=" << (search.found ? "yes" : "no")
2581
+ << " mode=" << search.path_search_mode
2582
+ << " tested_red=" << search.tested_red_paths
2583
+ << " tested_green=" << search.tested_green_paths;
2584
+ emit_progress(run_options, message.str());
2585
+ }
2586
+
2587
+ if (!search.found && reduction_round < 0 &&
2588
+ run_options.max_paths == -1 && !run_options.ban_heuristic) {
2589
+ SimplifierOptions brute_options = run_options;
2590
+ brute_options.max_paths = -1;
2591
+ brute_options.ban_heuristic = true;
2592
+ brute_options.require_applicable = true;
2593
+ output.last_path_search_mode = search_mode_for_options(brute_options);
2594
+ {
2595
+ std::ostringstream message;
2596
+ message << "round " << round
2597
+ << " brute_fallback_start crossings=" << output.code.size()
2598
+ << " max_thread=" << brute_options.max_threads;
2599
+ emit_progress(run_options, message.str());
2600
+ }
2601
+ SimplificationResult brute = find_simplification(output.code, brute_options);
2602
+ output.tested_red_paths += brute.tested_red_paths;
2603
+ output.tested_green_paths += brute.tested_green_paths;
2604
+ output.last_path_search_mode = brute.path_search_mode;
2605
+ {
2606
+ std::ostringstream message;
2607
+ message << "round " << round
2608
+ << " brute_fallback_done found=" << (brute.found ? "yes" : "no")
2609
+ << " tested_red=" << brute.tested_red_paths
2610
+ << " tested_green=" << brute.tested_green_paths;
2611
+ emit_progress(run_options, message.str());
2612
+ }
2613
+ if (brute.found) {
2614
+ ++output.heuristic_failover_rounds;
2615
+ search = std::move(brute);
2616
+ }
2529
2617
  }
2530
- if (brute.found) {
2531
- ++output.heuristic_failover_rounds;
2532
- search = std::move(brute);
2618
+
2619
+ if (!search.found) {
2620
+ {
2621
+ std::ostringstream message;
2622
+ message << "round " << round
2623
+ << " stop_no_path crossings=" << output.code.size();
2624
+ emit_progress(run_options, message.str());
2625
+ }
2626
+ break;
2533
2627
  }
2534
- }
2535
2628
 
2536
- if (!search.found) {
2629
+ const std::size_t before_apply_crossings = output.code.size();
2630
+ check_timeout(run_options);
2631
+ const MidSimplificationApplyResult applied =
2632
+ apply_simplification_witness(output.code, search, output.crossingless_components);
2633
+ ++output.mid_simplification_rounds;
2634
+ output.code = applied.code;
2635
+ output.crossingless_components = applied.crossingless_components;
2636
+ check_timeout(run_options);
2637
+ const PDSimplificationResult simplified =
2638
+ simplify_pd_code(output.code, output.crossingless_components);
2639
+ output.code = simplified.code;
2640
+ output.crossingless_components = simplified.crossingless_components;
2641
+ output.reidemeister_i_moves += simplified.reidemeister_i_moves;
2642
+ output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
2643
+ check_timeout(run_options);
2537
2644
  {
2538
2645
  std::ostringstream message;
2539
2646
  message << "round " << round
2540
- << " stop_no_path crossings=" << output.code.size();
2541
- emit_progress(options, message.str());
2647
+ << " applied crossings=" << before_apply_crossings
2648
+ << " -> " << applied.code.size()
2649
+ << " -> " << output.code.size()
2650
+ << " crossingless_components=" << output.crossingless_components
2651
+ << " r1_moves=" << simplified.reidemeister_i_moves
2652
+ << " nugatory_moves=" << simplified.nugatory_crossing_moves;
2653
+ emit_progress(run_options, message.str());
2542
2654
  }
2543
- break;
2544
2655
  }
2545
-
2546
- const std::size_t before_apply_crossings = output.code.size();
2547
- const MidSimplificationApplyResult applied =
2548
- apply_simplification_witness(output.code, search, output.crossingless_components);
2549
- ++output.mid_simplification_rounds;
2550
- const PDSimplificationResult simplified =
2551
- simplify_pd_code(applied.code, applied.crossingless_components);
2552
- output.code = simplified.code;
2553
- output.crossingless_components = simplified.crossingless_components;
2554
- output.reidemeister_i_moves += simplified.reidemeister_i_moves;
2555
- output.nugatory_crossing_moves += simplified.nugatory_crossing_moves;
2656
+ } catch (const TimeoutError& error) {
2657
+ output.timed_out = true;
2556
2658
  {
2557
2659
  std::ostringstream message;
2558
- message << "round " << round
2559
- << " applied crossings=" << before_apply_crossings
2560
- << " -> " << applied.code.size()
2561
- << " -> " << output.code.size()
2660
+ message << error.what()
2661
+ << "; returning_current_best crossings=" << output.code.size()
2562
2662
  << " crossingless_components=" << output.crossingless_components
2563
- << " r1_moves=" << simplified.reidemeister_i_moves
2564
- << " nugatory_moves=" << simplified.nugatory_crossing_moves;
2565
- emit_progress(options, message.str());
2663
+ << " mid_rounds=" << output.mid_simplification_rounds;
2664
+ emit_progress(run_options, message.str());
2566
2665
  }
2567
2666
  }
2568
2667
 
2569
2668
  output.stopped_by_round_limit =
2570
- reduction_round >= 0 && output.mid_simplification_rounds >= reduction_round;
2669
+ !output.timed_out &&
2670
+ reduction_round >= 0 &&
2671
+ output.mid_simplification_rounds >= reduction_round;
2571
2672
  {
2572
2673
  std::ostringstream message;
2573
2674
  message << "done final_crossings=" << output.code.size()
@@ -2575,8 +2676,9 @@ ReductionResult reduce_pd_code(
2575
2676
  << " mid_rounds=" << output.mid_simplification_rounds
2576
2677
  << " heuristic_failover_rounds=" << output.heuristic_failover_rounds
2577
2678
  << " stopped_by_round_limit="
2578
- << (output.stopped_by_round_limit ? "yes" : "no");
2579
- emit_progress(options, message.str());
2679
+ << (output.stopped_by_round_limit ? "yes" : "no")
2680
+ << " timed_out=" << (output.timed_out ? "yes" : "no");
2681
+ emit_progress(run_options, message.str());
2580
2682
  }
2581
2683
  return output;
2582
2684
  }
@@ -762,6 +762,7 @@ def _load_library() -> ctypes.CDLL:
762
762
  ctypes.c_int,
763
763
  ctypes.c_int,
764
764
  ctypes.c_int,
765
+ ctypes.c_int,
765
766
  ctypes.c_ulonglong,
766
767
  ctypes.POINTER(ctypes.c_int),
767
768
  ctypes.c_ulonglong,
@@ -774,13 +775,14 @@ def _load_library() -> ctypes.CDLL:
774
775
  return library
775
776
 
776
777
 
777
- def _run_one(
778
+ def _run_one_direct(
778
779
  pd_text: str,
779
780
  *,
780
781
  max_paths: int = -1,
781
782
  ban_heuristic: bool = False,
782
783
  reduction_round: int = -1,
783
784
  max_thread: int = -1,
785
+ timeout: int = -1,
784
786
  verbose: bool = False,
785
787
  known_crossingless_components: int = 0,
786
788
  remove_crossings: Optional[Sequence[int]] = None,
@@ -789,6 +791,8 @@ def _run_one(
789
791
  raise ValueError("reduction_round must be -1 or a non-negative integer")
790
792
  if max_thread < -1 or max_thread == 0:
791
793
  raise ValueError("max_thread must be -1 or a positive integer")
794
+ if timeout < -1 or timeout == 0:
795
+ raise ValueError("timeout must be -1 or a positive integer")
792
796
  library = _load_library()
793
797
  removed_count = 0 if remove_crossings is None else len(remove_crossings)
794
798
  removed_array = None
@@ -801,6 +805,7 @@ def _run_one(
801
805
  1 if ban_heuristic else 0,
802
806
  int(reduction_round),
803
807
  int(max_thread),
808
+ int(timeout),
804
809
  1 if verbose else 0,
805
810
  int(known_crossingless_components),
806
811
  removed_array,
@@ -822,6 +827,93 @@ def _run_one(
822
827
  return result
823
828
 
824
829
 
830
+ def _terminate_process(proc: subprocess.Popen[str]) -> None:
831
+ if proc.poll() is not None:
832
+ return
833
+ proc.terminate()
834
+ try:
835
+ proc.wait(timeout=2.0)
836
+ except subprocess.TimeoutExpired:
837
+ proc.kill()
838
+ proc.wait()
839
+
840
+
841
+ def _run_one(
842
+ pd_text: str,
843
+ *,
844
+ max_paths: int = -1,
845
+ ban_heuristic: bool = False,
846
+ reduction_round: int = -1,
847
+ max_thread: int = -1,
848
+ timeout: int = -1,
849
+ verbose: bool = False,
850
+ known_crossingless_components: int = 0,
851
+ remove_crossings: Optional[Sequence[int]] = None,
852
+ ) -> dict[str, Any]:
853
+ if reduction_round < -1:
854
+ raise ValueError("reduction_round must be -1 or a non-negative integer")
855
+ if max_thread < -1 or max_thread == 0:
856
+ raise ValueError("max_thread must be -1 or a positive integer")
857
+ if timeout < -1 or timeout == 0:
858
+ raise ValueError("timeout must be -1 or a positive integer")
859
+
860
+ request = {
861
+ "pd_text": pd_text,
862
+ "max_paths": int(max_paths),
863
+ "ban_heuristic": bool(ban_heuristic),
864
+ "reduction_round": int(reduction_round),
865
+ "max_thread": int(max_thread),
866
+ "timeout": int(timeout),
867
+ "verbose": bool(verbose),
868
+ "known_crossingless_components": int(known_crossingless_components),
869
+ "remove_crossings": [int(value) for value in remove_crossings or []],
870
+ }
871
+ env = os.environ.copy()
872
+ project_root = str(pathlib.Path(__file__).resolve().parents[1])
873
+ env["PYTHONPATH"] = project_root + os.pathsep + env.get("PYTHONPATH", "")
874
+ proc = subprocess.Popen(
875
+ [
876
+ sys.executable,
877
+ "-m",
878
+ "cpp_pd_code_simplify_interface._worker",
879
+ ],
880
+ cwd=str(pathlib.Path.cwd()),
881
+ text=True,
882
+ stdin=subprocess.PIPE,
883
+ stdout=subprocess.PIPE,
884
+ stderr=None if verbose else subprocess.PIPE,
885
+ env=env,
886
+ )
887
+ try:
888
+ stdout, stderr = proc.communicate(json.dumps(request))
889
+ except KeyboardInterrupt:
890
+ _terminate_process(proc)
891
+ raise
892
+
893
+ if proc.returncode != 0:
894
+ detail = (stderr or "").strip()
895
+ raise PdCodeSimplifyInterfaceError(
896
+ f"C++ interface worker failed with exit code {proc.returncode}"
897
+ + (f": {detail}" if detail else "")
898
+ )
899
+ try:
900
+ envelope = json.loads(stdout)
901
+ except json.JSONDecodeError as exc:
902
+ raise PdCodeSimplifyInterfaceError(
903
+ f"invalid interface worker JSON output: {stdout!r}"
904
+ ) from exc
905
+ if not isinstance(envelope, dict):
906
+ raise PdCodeSimplifyInterfaceError(
907
+ f"invalid interface worker response type: {type(envelope)!r}"
908
+ )
909
+ if not envelope.get("ok", False):
910
+ raise PdCodeSimplifyInterfaceError(str(envelope.get("error", "unknown worker error")))
911
+ result = envelope.get("result")
912
+ if not isinstance(result, dict):
913
+ raise PdCodeSimplifyInterfaceError("interface worker did not return a JSON object result")
914
+ return result
915
+
916
+
825
917
  def simplify(
826
918
  pd_code: PdInput,
827
919
  *,
@@ -829,6 +921,7 @@ def simplify(
829
921
  ban_heuristic: bool = False,
830
922
  reduction_round: int = -1,
831
923
  max_thread: int = -1,
924
+ timeout: int = -1,
832
925
  verbose: bool = False,
833
926
  known_crossingless_components: int = 0,
834
927
  remove_crossings: Optional[Sequence[int]] = None,
@@ -841,6 +934,7 @@ def simplify(
841
934
  ban_heuristic=ban_heuristic,
842
935
  reduction_round=reduction_round,
843
936
  max_thread=max_thread,
937
+ timeout=timeout,
844
938
  verbose=verbose,
845
939
  known_crossingless_components=known_crossingless_components,
846
940
  remove_crossings=remove_crossings,
@@ -854,6 +948,7 @@ def simplify_many(
854
948
  ban_heuristic: bool = False,
855
949
  reduction_round: int = -1,
856
950
  max_thread: int = -1,
951
+ timeout: int = -1,
857
952
  verbose: bool = False,
858
953
  known_crossingless_components: int = 0,
859
954
  remove_crossings: Optional[Sequence[int]] = None,
@@ -867,6 +962,7 @@ def simplify_many(
867
962
  ban_heuristic=ban_heuristic,
868
963
  reduction_round=reduction_round,
869
964
  max_thread=max_thread,
965
+ timeout=timeout,
870
966
  verbose=verbose,
871
967
  known_crossingless_components=known_crossingless_components,
872
968
  remove_crossings=remove_crossings,
@@ -884,6 +980,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
884
980
  parser.add_argument("--ban-heuristic", action="store_true")
885
981
  parser.add_argument("--reduction-round", type=int, default=-1)
886
982
  parser.add_argument("--max-thread", type=int, default=-1)
983
+ parser.add_argument("--timeout", type=int, default=-1)
887
984
  parser.add_argument("--verbose", action="store_true")
888
985
  parser.add_argument("--known-crossingless-components", type=int, default=0)
889
986
  parser.add_argument("--remove-crossings", help="comma-separated zero-based crossing indices")
@@ -892,6 +989,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
892
989
  parser.error("--reduction-round must be -1 or a non-negative integer")
893
990
  if args.max_thread < -1 or args.max_thread == 0:
894
991
  parser.error("--max-thread must be -1 or a positive integer")
992
+ if args.timeout < -1 or args.timeout == 0:
993
+ parser.error("--timeout must be -1 or a positive integer")
895
994
  if args.pd_code and args.pd_code_option:
896
995
  parser.error("pass either a positional PD code or --pd-code, not both")
897
996
  pd_code_text = args.pd_code_option or args.pd_code
@@ -916,6 +1015,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
916
1015
  ban_heuristic=args.ban_heuristic,
917
1016
  reduction_round=args.reduction_round,
918
1017
  max_thread=args.max_thread,
1018
+ timeout=args.timeout,
919
1019
  verbose=args.verbose,
920
1020
  known_crossingless_components=args.known_crossingless_components,
921
1021
  remove_crossings=remove_crossings,
@@ -923,6 +1023,15 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
923
1023
  if show_labels:
924
1024
  item = {"label": label, **item}
925
1025
  batch_payload.append(item)
1026
+ if item.get("timed_out"):
1027
+ exit_code = 2
1028
+ except KeyboardInterrupt:
1029
+ exit_code = 130
1030
+ item = {"error": "interrupted by Ctrl+C"}
1031
+ if show_labels:
1032
+ item = {"label": label, **item}
1033
+ batch_payload.append(item)
1034
+ break
926
1035
  except Exception as exc:
927
1036
  exit_code = 2
928
1037
  item = {"error": str(exc)}
@@ -931,16 +1040,26 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
931
1040
  batch_payload.append(item)
932
1041
  payload: Any = batch_payload
933
1042
  else:
934
- payload = simplify(
935
- pd_code_text or "",
936
- max_paths=args.max_paths,
937
- ban_heuristic=args.ban_heuristic,
938
- reduction_round=args.reduction_round,
939
- max_thread=args.max_thread,
940
- verbose=args.verbose,
941
- known_crossingless_components=args.known_crossingless_components,
942
- remove_crossings=remove_crossings,
943
- )
1043
+ try:
1044
+ payload = simplify(
1045
+ pd_code_text or "",
1046
+ max_paths=args.max_paths,
1047
+ ban_heuristic=args.ban_heuristic,
1048
+ reduction_round=args.reduction_round,
1049
+ max_thread=args.max_thread,
1050
+ timeout=args.timeout,
1051
+ verbose=args.verbose,
1052
+ known_crossingless_components=args.known_crossingless_components,
1053
+ remove_crossings=remove_crossings,
1054
+ )
1055
+ if isinstance(payload, dict) and payload.get("timed_out"):
1056
+ exit_code = 2
1057
+ except KeyboardInterrupt:
1058
+ exit_code = 130
1059
+ payload = {"error": "interrupted by Ctrl+C"}
1060
+ except Exception as exc:
1061
+ exit_code = 2
1062
+ payload = {"error": str(exc)}
944
1063
 
945
1064
  print(json.dumps(payload, indent=2))
946
1065
  return exit_code
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cpp-pd-code-simplify-interface
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary: Python interface for cpp-pd-code-simplify with runtime C++ compilation.
5
5
  License-Expression: MIT
6
6
  Author: GGN_2015
@@ -60,16 +60,23 @@ print(result["final_pd_code"])
60
60
  ```
61
61
 
62
62
  Returned `final_pd_code` strings are normalized for display: each crossing is
63
- written from the under-incoming edge and labels are renumbered along oriented
64
- components from `1`. This is applied only at the final JSON boundary; the C++
65
- backend keeps its internal numbering unchanged while simplifying.
63
+ written from the under-incoming edge, labels are renumbered along oriented
64
+ components from `1`, and crossing rows are sorted lexicographically. This is
65
+ applied only at the final JSON boundary; the C++ backend keeps its internal
66
+ numbering unchanged while simplifying.
66
67
 
67
68
  The default `max_paths=-1` uses deterministic heuristic green-path sampling in
68
69
  the C++ backend. Use `ban_heuristic=True` to request exhaustive green-path
69
70
  enumeration for a manageable input. Use `reduction_round=K` to cap applied
70
71
  mid-simplification rounds; the default `-1` runs until stable. Use
71
- `verbose=True` to forward timestamped C++ progress logs to stderr. Verbose log
72
- lines use local wall-clock time in `YYYY-MM-DD HH:MM:SS` format.
72
+ `timeout=K` to cap a call at `K` seconds; the default `-1` has no timeout. Use
73
+ `verbose=True` to forward timestamped C++ progress logs to stderr. If a call
74
+ times out, the returned dictionary still contains the best PD code found so far
75
+ and sets `timed_out` to `True`. Verbose log lines use local wall-clock time in
76
+ `YYYY-MM-DD HH:MM:SS` format. When `max_thread=-1` reaches a brute-force search
77
+ phase, verbose logs also include `actual_threads`, the worker count selected by
78
+ the C++ backend for that phase. The backend call runs in a helper process, so
79
+ `Ctrl+C` can terminate active C++ work and its worker threads cleanly.
73
80
 
74
81
  Batch use:
75
82
 
@@ -0,0 +1,12 @@
1
+ cpp_pd_code_simplify_interface/__init__.py,sha256=aaNwD--zgQX13xiU1hpuvgXy8vVTFGRk-r5zLm8zIRY,447
2
+ cpp_pd_code_simplify_interface/__main__.py,sha256=fdA1QWBe5LNX0fbtaJXa1-dwEhrg_4sS_5UnjaajcMo,80
3
+ cpp_pd_code_simplify_interface/_worker.py,sha256=pgvL3zlAZkUVM033YKAT1geXxcI8TxTUXLp83sgrZgw,1477
4
+ cpp_pd_code_simplify_interface/main.py,sha256=QkhR_2Cq9Ep_8hT5qpFKML7bhq99SuQSKO0jePJHp3U,37007
5
+ cpp_pd_code_simplify_interface/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
+ cpp_pd_code_simplify_interface-0.1.12.dist-info/entry_points.txt,sha256=ThorC4Enxp317TKITsXm9pE0gUp7pt-pP6Dl5mEPUR8,91
7
+ cpp_pd_code_simplify_interface-0.1.12.dist-info/METADATA,sha256=LECLw_rBE_IhkBnc3fiaJjlqoIwd5TzW9tC4yb0zJe8,4553
8
+ cpp_pd_code_simplify_interface-0.1.12.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
9
+ cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp,sha256=ZWZVWuSXN9HgCYd88pfURgLqs-3_0xGDi_ZEPZrYUyY,5135
10
+ cpp_pd_code_simplify_interface/data/src/native_interface.cpp,sha256=-X9ztIyCl5y27Yhj2vCyC4bHCeDpFlcKm6ACPMDoxHs,6708
11
+ cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp,sha256=8XTpwAl5kSGsyZpHUIdWZC2NzGrhIMxp9LDcCqFSM8I,95614
12
+ cpp_pd_code_simplify_interface-0.1.12.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- cpp_pd_code_simplify_interface/__init__.py,sha256=aaNwD--zgQX13xiU1hpuvgXy8vVTFGRk-r5zLm8zIRY,447
2
- cpp_pd_code_simplify_interface/__main__.py,sha256=fdA1QWBe5LNX0fbtaJXa1-dwEhrg_4sS_5UnjaajcMo,80
3
- cpp_pd_code_simplify_interface/main.py,sha256=4t-6xvjQjsuEWP_oiwedGtgrLYu3316uATrl9t4Q8zs,32778
4
- cpp_pd_code_simplify_interface/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
5
- cpp_pd_code_simplify_interface-0.1.10.dist-info/entry_points.txt,sha256=ThorC4Enxp317TKITsXm9pE0gUp7pt-pP6Dl5mEPUR8,91
6
- cpp_pd_code_simplify_interface-0.1.10.dist-info/METADATA,sha256=mFNvOlxRrKm62IouC_r5wWyt7KuJ7XqcAdVq39goeDA,4028
7
- cpp_pd_code_simplify_interface-0.1.10.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
8
- cpp_pd_code_simplify_interface/data/include/pdcode_simplify/pdcode_simplify.hpp,sha256=0WflWW2MB2EAK8GbcKKr9SbnoByMV3hFxt4m4mAerzQ,4917
9
- cpp_pd_code_simplify_interface/data/src/native_interface.cpp,sha256=16HbKJBPA_EGdbyom4sSoQ7lisDBwaKrl7NsphmlA-k,6555
10
- cpp_pd_code_simplify_interface/data/src/pdcode_simplify.cpp,sha256=EUW5kLkhVxurDhdKZ4dhEXr1_7cUZL42ejXqr6vUo2E,91148
11
- cpp_pd_code_simplify_interface-0.1.10.dist-info/RECORD,,